Login

Subversion Repositories NedoOS

Rev

Rev 1778 | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; KERNEL (system side) ;;;;;;;;;;;;;;;;;;;;;;;        
;при вызове 0x0005 в системе включены страницы: pgsystem, pgkillable, pgkillable, pgkillable (на случай порчи стеком)

MAXAPPS=16
bdosstack_sz=0;150 ;80 мало для загрузки файла, 110 мало для fopen (даже с INTSTACK2), 140 мало для чтения каталога (даже с INTSTACK2) ;0=отключить мьютекс BDOS

QUITSTACK=0x4000 ;<=0x4000

        macro BDOSSETPGSSCR
        call sys_setpgsscr
        endm

        macro BDOSSETPGFATFS
        call BDOS_setpgfatfs
        endm

        macro BDOSSETPGTRDOSFS
        call BDOS_setpgtrdosfs
        endm

        macro BDOSSETPGW5300
        call BDOS_setpgtrdosfs
        endm


fatfs.tabl=0x4000
        include "fatfs_h.asm"
       
wassyscode
        disp 0x0000
syscode
sys_time_date
        ds 4

        ds 0x0000+4-$
        jp sys_quit

sys_reter
        ret

callbdos_mutex
        db 0xc0
        ds 0x0005+4-$
        jp callbdos

        ds 0x0009+4-$
        jp sys_getchar

sys_farcall
        jp endsys_result_a

        ds 0x0015-2-$
endsys_result_aq
        out (0xfd),a
        display "kernel_result_a=",$
        ds 0x0010+5-$
        jp fastprchar

        ds 0x0018-$
;setpg4000=0x0018
        ld bc,memport4000
        out (c),a
        ret

        ds 0x0020-$
;setpg8000=0x0020
        ld bc,memport8000
        out (c),a
        ret

        ds 0x0028-$
;setpgc000=0x0028
        ld bc,memportc000
        out (c),a
        ret

sys_timer
        ds 4

        ds 0x0030+4-$
        jp sys_farcall

        ds 0x0038-$
        jp sys_sysint

        ds 0x0038+7-$ -3
sys_intq
;bc=memport0000
;d=pgmain
;a=screenpg
       di ;на время до включения нужной pg0000, чтобы не запороть чужой стек
        out (0xfd),a ;дальше попадаем в init_resident
;выход в конец юзерского обработчика прерываний

;вход из начала юзерского обработчика прерываний
        ;ds 0x0038+14-$ -4
        ;TODO захватить мьютекс (прерывание внутри прерывания должно попасть в простой обработчик без шедулера)
        jp sys_intgo ;нужно, чтобы можно было ставить точку останова на 0x0100

fastprchar
;e=char
        if bdosstack_sz==0
        ld (sys_prchar_sp),sp
        ld sp,BDOSSTACK ;до этого момента прерывание может запороть любое место памяти (user sp >=0x3b00)
        else
        exx
        ld hl,0
        add hl,sp
        ld iy,(appaddr)
        ;ld (iy+app.callbdos_sp),l
        ;ld (iy+app.callbdos_sp+1),h
        ld bc,app.bdosstack+bdosstack_sz
        add iy,bc
        ld sp,iy ;до этого момента прерывание может запороть любое место памяти (user sp >=0x3b00)
        exx
        endif
       
        ld iy,(appaddr)
        call BDOS_prchar ;портит только 0xc000+, но сама восстанавливает pgkillable
        if bdosstack_sz==0
sys_prchar_sp=$+1
        ld sp,0
        else
        exx
        ;ld iy,(appaddr)
        ;ld l,(iy+app.callbdos_sp)
        ;ld h,(iy+app.callbdos_sp+1)
        ld sp,hl
        exx
        endif
        jp endsys_result_a

        ds 0x0100-$ ;stack for CP/M programs
       
safestack_sz=16 +2 ;на запарывание прерыванием
        STRUCT app
flags           BYTE ;флаги (всегда в начале структуры)
;priority        BYTE ;TODO приоритет (0=конец списка)
id              BYTE ;номер задачи (0=свободно)
parentid        BYTE ;номер родительской задачи
mainpg          BYTE ;главная страница задачи (там userkernel)
;callbdos_sp     WORD ;сюда сохраняется стек при вызове BDOS
;curmsg          WORD ;TODO адрес текущего сообщения этой задаче
;endmsg          WORD ;TODO адрес конца очереди сообщений этой задаче
;sp              WORD ;текущий адрес стека (лежит в mainpg:intsp)
;next            WORD ;TODO указатель на следущую задачу (следующая за выполняемой внутри того же приоритета)
stdin           BYTE
stdout          BYTE
stderr          BYTE ;TODO не нужен?
lasttime        BYTE
border          BYTE ;текущий цвет бордера 0..15
screen          BYTE ;текущий номер экрана ;fd_user + 8*screen
gfxmode         BYTE ;текущий видеорежим ;значение для 0xbd77
gfxkeep         BYTE ;b7 = keep gfx pages
scr0low         BYTE ;pages
scr0high        BYTE ;pages
scr1low         BYTE ;pages
scr1high        BYTE ;pages
childresult     WORD ;filled by closed child
textcuraddr     WORD ;адрес курсора на экране
curcolor        BYTE ;текущий атрибут при печати
dta             WORD ;data transfer address
vol             BYTE ;текущий драйв (volume)
dircluster      DWORD ;текущая директория
dir             BLOCK DIR_sz ;временный буфер для чтения каталога
bdosstack       BLOCK bdosstack_sz ;стек при вызове BDOS
pal             BLOCK 32
;safestack       BLOCK safestack_sz ;de,hl,af',af,ix,hl',de',bc',iy
        ENDS

        display "apps start=",/h,$
safestack
        ds safestack_sz
app1    app
app_sz=$-safestack

        ds (MAXAPPS-1)*app_sz
        display "MAXAPPS=",/h,MAXAPPS

app_afterlast=$+safestack_sz
app_last=app_afterlast-app_sz
        display "app1=",/h,app1
        display "app_last=",/h,app_last

sys_intgo
        ex de,hl
        ld hl,0
        add hl,sp
appaddr=$+1
        ld sp,app1 ;safestack_end
        push hl ;"sp"
        push de ;"hl"
        push iy
        exx
        push bc
        push de
        push hl
        push ix
        ex af,af' ;'
        push af

        ld bc,memport4000
        ld a,pgtrdosfs;pagexor-5
        out (c),a ;там INTSTACK
        ld sp,INTSTACK2
        call setgfxpal_focus
        call on_int ;тикает таймер
        call schedule ;out: iy=app
        ld a,pgkillable
        ld bc,memport4000
        ld (sys_curpg4000),a ;не надо? (если di)
        out (c),a

sys_int_popregs ;только для выхода из yield
;iy=app
        ld de,-(safestack_sz-2) ;2 на запарывание прерыванием
        add iy,de
        ld sp,iy ;di!!!

        pop af
        ex af,af' ;'
        pop ix
        pop hl
        pop de
        pop bc
        exx
        ld d,(iy+app.mainpg+safestack_sz-2) ;2 на запарывание прерыванием
        ld iy,(focusappaddr)
        ld a,(iy+app.screen)
        pop iy
        pop bc ;"hl"
        pop hl ;"sp"
        ld sp,hl
        ld h,b
        ld l,c
        ld bc,memport0000
        ;TODO освободить мьютекс, можно включить прерывания
        jp sys_intq ;out (0xfd),a ;дальше попадаем в init_resident

schedule
;find next app, set iy
;out: iy=app, ix=(focusappaddr)
        ld iy,(appaddr)
        ld bc,-app_last;app_afterlast
        ld de,app_last+app_sz;app_sz
         ld a,(sys_timer) ;ok
        ld l,MAXAPPS
findnextapp0
        add iy,bc
        jr nc,$+6
        ld iy,app1 -(app_last+app_sz)
        add iy,de
         cp (iy+app.lasttime)
         jr z,findnextappskip
         ;bit fyield,(iy)
         ;jr nz,findnextappq
        bit factive,(iy)
        jr nz,findnextappq
findnextappskip
        dec l
        jr nz,findnextapp0
;no active apps (или каждая в данном фрейме уже вызывалась)
        ld iy,app1 ;idle
findnextappq
;TODO при fwaitfocus пропускать app, если у него нет фокуса
;или пусть term (хозяин пайпа) получает factive только после появления чего-то в пайпе или при нажатии клавиши (когда он в фокусе)
        ld (appaddr),iy
         ld (iy+app.lasttime),a
          ;ld iy,(appaddr)
          ld a,(iy+app.mainpg)
          ld bc,memport4000
          ld (sys_curpg4000),a ;нужно (могут вызвать из yield - в любой момент)
          out (c),a
          ld ix,(focusappaddr)
          ld a,(ix+app.screen)
          or fd_system
          ;ld (user_fdvalue1+0x4000),a ;for QUIT, мешает делать многозадачность с 0xffff: jp nn
          ld (user_fdvalue2+0x4000),a
          ld (user_fdvalue3+0x4000),a
          ld (user_fdvalue4+0x4000),a
          ;ld (user_fdvalue5+0x4000),a ;not supported yet
          ld (user_fdvalue6+0x4000),a
          ld a,pgtrdosfs
          ;ld (sys_curpg4000),a ;нужно (могут вызвать из yield - в любой момент - но там не важно, там стек в нулях, а потом само ввключает pgkillable)
          out (c),a ;там INTSTACK
        ;ld iy,(appaddr)
        ret

setgfxpal_focus
;если в yield не поставить палитру второй задаче, то она никогда не поставится, если первая задача в цикле делает yield
;потому что все прерывания будут ставить первую задачу
;если же палитру ставить в самом yield, то могут быть проблемы с выставлением палитры, если yield вызывать в случайных местах или если все задачи неактивны
;поэтому обработчик прерываний должен выставлять палитру и видеорежим задачи, которая в фокусе, независимо от её активности
;менять палитру только после смены фокуса или записи палитры
                display "setgfxpal_focus ",setgfxpal_focus
palettechanged=$
        or a
        jp c,focusappborder
        ld a,55 ;"scf"
        ld (palettechanged),a
       ifndef NOPAL
        ld hl,(focusappaddr)
        ld bc,app.pal+31 ;-app.gfxmode
        add hl,bc
        if 0;atm==1
                ld bc,0x07ff
                dup 8
                ld a,b
                out (0xf6),a
                outd
                dec hl
                edup
                ld b,0x07
                dup 7
                ld a,b
                out (0xfe),a
                outd
                dec hl
                edup
                ld a,b
                out (0xfe),a
                outd
        else

        ld c,0xff
        ld a,7
        dup 8
        OUT (0xF6),A
        ld d,(hl)
        dec hl
        ld b,(hl) ;DDp palette low bits
        OUT (c),d;(0xFF),A
        dec hl
        dec a
        edup
        ld a,7
        dup 7
        OUT (0xFE),A
        ld d,(hl)
        dec hl
        ld b,(hl) ;DDp palette low bits
        OUT (c),d;(0xFF),A
        dec hl
        dec a
        edup
        OUT (0xFE),A ;0
        ld d,(hl)
        dec hl
        ld b,(hl) ;DDp palette low bits
        OUT (c),d;(0xFF),A
        endif
       endif
focusappborder
         ld ix,(focusappaddr)
       ifndef NOPAL
         ld a,(ix+app.border)
         cp 8
         res 3,a ;tapeout sound
         out (0xfe),a
         ret c
         out (0xf6),a
       endif
        ret

sys_sysint
;TODO schedule (для RTOS), но тогда надо реентерабельность всех процедур BDOS (даже без этого шедулинга они всё равно не должны иметь состояния!)
;как шедулить, когда мы в kernelspace???
;TODO проверка критической секции (в обычном прерывании не нужно)
        ex de,hl
        ex (sp),hl ;восстановили стек из de
        ld (sys_sysint_jp),hl
        ld (sys_sysint_sp),sp
        ld sp,INTSTACK1
        push af
        push bc
        push de ;"hl"
        ;push hl
        exx
        ex af,af' ;'
        push af
        push bc
        push de
        push hl
        push ix
        push iy
        ld sp,INTSTACK2

        ld bc,memport4000
        ld a,pgtrdosfs;pagexor-5 ;там INTSTACK
        out (c),a

        call setgfxpal_focus

        call on_int
sys_curpg4000=$+1
        ld a,pgkillable
        ld bc,memport4000
        out (c),a

        ld sp,INTSTACK1-18
        pop iy
        pop ix
        pop hl
        pop de
        pop bc
        pop af
        ex af,af' ;'
        exx
        pop hl ;"hl"
        ;pop de
        pop bc
        pop af
sys_sysint_sp=$+1
        ld sp,0
        pop de
        ei
        ;ret
sys_sysint_jp=$+1
        jp 0
       
on_int
;в 0x4000 сейчас pgtrdosfs;5, там стек
focusappaddr=$+1
        ld hl,app1
        ld bc,app.gfxmode
        add hl,bc
        ld e,(hl)
;sys_curgfxmode=$+1
        ;ld e,%10101000 ;320x200 mode
                if atm==1
                        ld bc,0xfadf ;buttons
                        in a,(c)
                        ld (sys_mousebuttons),a
                        inc b ;ld bc,0xfbdf ;x
                        in l,(c)
                        ld b,0xff ;y
                        in h,(c)
                         ld bc,0xff77   ;shadow ports and palette off
                         out (c),e
                        in a,(0x1f)
                        ld (sys_kempstonbuttons),a
                         ld bc,0x01bf
                         out (c),b
                         ld bc,0xbd77   ;shadow ports and palette remain on
                         out (c),e
                         ld a,32 ;xor a ;D5=444 palette
                         out (0xbf),a
                else
                        push de ;e=gfxmode
                        ifdef NOMOUSE
                        ld hl,0
                        ld de,0x0f00
                        else
                        call readmouse ;resident >=0x4000
                        endif
                        ld a,d
                        ld (sys_mousebuttons),a
                        ld a,e
                        ld (sys_kempstonbuttons),a
                        pop de ;e=gfxmode
                endif
        ld (sys_mousecoords),hl
sys_settime_do=$
                or a ;/scf=set time
sys_settime_time=$+1
                ld bc,0
sys_settime_date=$+1
                ld hl,0
                call c,writetime ;keep de for atm != 1
                ld a,55+128
                ld (sys_settime_do),a

                if atm != 1
                        ld a,(sys_timer) ;ok
                        and 7
                        jr nz,on_int_noreadtime
                        ifdef NOCMOS
                        else
                        call readtime ;in: e=gfxmode, out: hl=date, de=time
                        ld (sys_time_date),de
                        ld (sys_time_date+2),hl
                        endif
on_int_noreadtime
                endif
        ld hl,sys_timer
        inc (hl)
        inc hl
        jr nz,on_int_timerq
        inc (hl)
        inc hl
        jr nz,on_int_timerq
        inc (hl)
        inc hl
        jr nz,on_int_timerq
        inc (hl)
on_int_timerq
       
                if PS2KBD==0
                        ld a,pgtrdosfs
                        ld bc,memport4000
                        out (c),a ;don't keep in sys_curpg4000!
                        call KEYSCAN
                else
KEYSCAN
.rep_wait=$+1
                        ld a,0
                        dec a
                        jp m,.end_keyscan
                        ld (.rep_wait),a
.end_keyscan                   
                endif
;TODO при наличии клавиши или события мышки поставить factive для задачи в фокусе
;пока что просто ставим factive для задачи в фокусе, если у неё есть fgfx (т.е. это не idle) - этого достаточно для ускорения игр
        ld iy,(focusappaddr)
        bit fgfx,(iy+app.flags)
        jr z,keyscan_nosetactive
        set factive,(iy+app.flags)
keyscan_nosetactive

        ;call PEEKKEY ;ld a,(curkey)
        ;cp ssEnter
       
        ld a,0x7f
        in a,(0xfe)
        rra
        ld c,a ;c0=ss
        ld a,0xbf
        in a,(0xfe)
        or c
        cpl
        ld c,a
        cpl
        ;a0=c0=0: ssEnter pressed
on_int_oldssEnter=$+1
        or 0 ;=0: ssEnter was released
        rra
        ld a,c
        ld (on_int_oldssEnter),a
        jr c,sys_int_noselectapp
         call KEY_PUTREDRAW

       ld a,(sys_curpg8000)
       push af
       ld a,(sys_curpgc000)
       push af

        if 1==1
        ld iy,(focusappaddr)
        ;di
        call disablescrpgs_setc000 ;портит sys_curpg8000, sys_curpgc000
        ;ei
        ld bc,-app_last;app_afterlast
        ld de,app_last+app_sz;app_sz
        ld l,MAXAPPS
findnextgfxapp0
        add iy,bc
        jr nc,$+6
        ld iy,app1 -(app_last+app_sz)
        add iy,de
        ld a,(iy+app.flags)
        bit fgfx,a
        jr z,findnextgfxappskip
         bit fwaiting,a
         jr z,findnextgfxappq
findnextgfxappskip
        dec l
        jr nz,findnextgfxapp0
;no active apps (или каждая в данном фрейме уже вызывалась)
        ld iy,app1 ;idle
findnextgfxappq
        ld (focusappaddr),iy
        call setpalettechanged

        else
       
        ld hl,(focusappaddr)
        ;отключить страницы экрана этой задаче и выключить их в памяти задачи
        push hl
        pop iy
        call disablescrpgs_setc000 ;портит sys_curpg8000, sys_curpgc000
        ld bc,-app_last;app_afterlast
        ld de,app_last+app_sz;app_sz
        ld a,MAXAPPS
findnextgfxapp0
        ;add hl,de
        ;sbc hl,bc
        ;add hl,bc
        ;jr nz,$+5
        ;ld hl,app1
        add hl,bc
        jr nc,$+5 ;hl < app_last
        ld hl,app1 -(app_last+app_sz)
        add hl,de
        bit fgfx,(hl)
        jr z,findnextgfxappskip
         bit fwaiting,(hl)
         jr z,findnextgfxappq
findnextgfxappskip
        dec a
        jr nz,findnextgfxapp0
        ld hl,app1
findnextgfxappq
        ld (focusappaddr),hl
        call setpalettechanged
        ;включить страницы экрана этой задаче
        ;push iy
        push hl
        pop iy
        endif
       
        call enablescreeninapp_setc000 ;портит sys_curpg8000, sys_curpgc000
       
        ;pop iy

       pop af
       ld (sys_curpgc000),a
       pop af
       ld (sys_curpg8000),a
       
sys_int_noselectapp
muzpg=$+1
        ld a,pgkillable
        ld bc,memport4000
        out (c),a
muzpg8000=$+1
         ld a,pgkillable
         ld b,memport8000_hi
         out (c),a
muzpgc000=$+1
         ld a,pgkillable
         ld b,memportc000_hi
         out (c),a

        ;jr $
        ld sp,INTMUZSTACK

         ld e,pgsys
        ld ix,(focusappaddr)
        ld a,(ix+app.gfxmode)
muzcall=$+1
        call sys_reter;pt3player.PLAY ;TODO call drivers
       
        ld sp,INTSTACK2-2

        ld ix,(focusappaddr)
        ld a,(ix+app.gfxmode)
        ld bc,0xbd77
        out (c),a ;set gfx mode
       
        ld a,pgtrdosfs;pagexor-5
        ;ld a,(sys_curpg4000)
        ld bc,memport4000
        out (c),a ;там INTSTACK
sys_curpg8000=$+1
         ld a,pgkillable
         ld b,memport8000_hi
         out (c),a
sys_curpgc000=$+1
         ld a,pgkillable
         ld b,memportc000_hi
         out (c),a
        ret

copypage_a_to_e
        call sys_setpg8000
        ld a,e
        call sys_setpgc000
        ld hl,0x8000
        ld de,0xc000
        ld bc,0x4000
        ldir
        ret

disablescrpgs_setc000
        bit 7,(iy+app.gfxkeep)
        jr z,disablescrpgs_nokeep
        ld a,pgscr0_0
        ld e,(iy+app.scr0low)
        call copypage_a_to_e
        ld a,pgscr0_1
        ld e,(iy+app.scr0high)
        call copypage_a_to_e
        ld a,pgscr1_0
        ld e,(iy+app.scr1low)
        call copypage_a_to_e
        ld a,pgscr1_1
        ld e,(iy+app.scr1high)
        call copypage_a_to_e
        call setmainpg_c000
        if 1==0
        ds 6*4
        else
       display "===",$
        ld a,(iy+app.scr0low)
        ld (0xc000+user_scr0_low),a
        ld a,(iy+app.scr0high)
        ld (0xc000+user_scr0_high),a
        ld a,(iy+app.scr1low)
        ld (0xc000+user_scr1_low),a
        ld a,(iy+app.scr1high)
        ld (0xc000+user_scr1_high),a
        endif
        jr disablescrpgs_keepok
disablescrpgs_nokeep
        call setmainpg_c000
        call disablescreeninapp ;set user_scr0_low etc to pgkillable
disablescrpgs_keepok
        ld de,curpg16k+0xc000
        call disablescrpg
        ld  e,0xff&(curpg32klow+0xc000)
        call disablescrpg
        ld  e,0xff&(curpg32khigh+0xc000)
        ;call disablescrpg
disablescrpg
;de=page keeping addr
        ld a,(de)
        ;or 7&(pgscr0_0|pgscr0_1|pgscr1_0|pgscr1_1)
        ;cp pgscr0_0
        ;ret nz;jr z,disablescrpg_ok
;disablescrpg_ok
        ;ld a,pgkillable
        ld c,(iy+app.scr0low)
        cp pgscr0_0
        jr z,disablescrpg_ok
        ld c,(iy+app.scr0high)
        cp pgscr0_1
        jr z,disablescrpg_ok
        ld c,(iy+app.scr1low)
        cp pgscr1_0
        jr z,disablescrpg_ok
        ld c,(iy+app.scr1high)
        cp pgscr1_1
        ret nz;jr z,disablescrpg_ok
disablescrpg_ok
        ld a,c
        ld (de),a
        ret
enablescrpg
;de=page keeping addr
        ld a,(de)
        cp (iy+app.scr0high) ;this order for pg5 games not switching pages
        ld c,pgscr0_1
        jr z,disablescrpg_ok
        cp (iy+app.scr0low)
        ld c,pgscr0_0
        jr z,disablescrpg_ok
        cp (iy+app.scr1high)
        ld c,pgscr1_1
        ret nz;jr z,enablescrpg_ok
        cp (iy+app.scr1low)
        ld c,pgscr1_0
        jr z,disablescrpg_ok
enablescrpg_ok
        ld a,c
        ld (de),a
        ret

sys_getchar
;out:
;de=mouse yx, l=buttons, A=key, H=high bits of key, bc=keynolang, lx=joystick 0bP2JFUDLR, nz=no focus (mouse position=0, ignore it!)
        ;call checkfocus_getkbdmouse
;checkfocus_getkbdmouse
;out: nz=fail
        ld de,(focusappaddr)
        ld hl,(appaddr)
        xor a
        sbc hl,de
        jr nz,sys_getchar_fail ;nz
;sys_oldmousecoords=$+1
;        ld de,0
;        ld (sys_oldmousecoords),hl
;        ld a,l
;        sub e ;a=dx
;        ld e,a ;e=dx
;        ld a,d
;        sub h ;a=dy
;        ld d,a ;d=dy
                if PS2KBD==1
                        display "ps2_sp ",$
                        ld (ps2_sp),sp
                        ld sp,BDOSSTACK ;это может затереть стек возврата! поэтому возвращаемся через jp
                        call BDOS_setpgtrdosfs
                        call GETKEY ;A=key, H=high bits of key, BC=keynolang
                        ld e,a
                        push bc ;ld (ps2_bc),bc
                        ld a,pgkillable
                        ld bc,memport4000
                        ld (sys_curpg4000),a
                        out (c),a
                        pop bc
ps2_sp=$+1
                        ld sp,0
;ps2_bc=$+1
;                       ld bc,0
                        ld a,e
                else
                        call GETKEY ;A=key, H=high bits of key, BC=keynolang
                endif
        cp a ;z
sys_mousecoords=$+1
        ld de,0;hl,0
sys_mousebuttons=$+1
        ld l,0xff
sys_kempstonbuttons=$+2
        ld lx,0
        ;ret ;z
        ;jp endsys_result_a
endsys_result_a
         ld iy,(focusappaddr)
        ex af,af' ;'
        ld a,(iy+app.screen)
        ;ld iy,(appaddr)
        jp endsys_result_aq

;TODO брать номер экрана у задачи с фокусом и при шедулинге ставить этот номер в userkernel новой задачи
       
sys_getchar_fail
;a=0, nz
        ;ld a,NOKEY ;no key
         ;ld h,a
         ;ld b,a
         ld c,a ;no keynolang
        ld d,a;0
        ld e,a;0 ;no mouse movement
         ld lx,a ;no joystick buttons
        ld l,0xff ;no buttons
        jr endsys_result_a ;ret ;nz ;jp endsys_result_a

callbdos
;при вызове bdos надо включить:
;0x0000 - syscode (уже включено)
;[0x4000 - pgfatfs или bdospg2]
;защита от одновременного доступа двум задачам
;занято a,bc,de,hl
;свободно iy
        if bdosstack_sz==0

        ld (callbdos_sp),sp
        ld sp,BDOSSTACK ;до этого момента прерывание может запороть любое место памяти (user sp >=0x3b00) ;это может затереть стек возврата! поэтому возвращаемся через jp

        else
       
        exx
callbdos_lock        
        ld hl,callbdos_mutex ;изначально 0xc0
        sla (hl)
        jr z,callbdos_lock ;был занят
       
        ld hl,0
        add hl,sp
        ld iy,(appaddr)
        ;ld (iy+app.callbdos_sp),l
        ;ld (iy+app.callbdos_sp+1),h
        ld bc,app.bdosstack+bdosstack_sz
        add iy,bc
        ld sp,iy ;до этого момента прерывание может запороть любое место памяти (user sp >=0x3b00)
        push hl
        exx
       
        endif
       
            ;ld iy,(focusappaddr)
            ;ld a,(iy+app.screen)
            ;xor 0x10 ;fd_user^fd_system
            ;out (0xfd),a
        ld iy,(appaddr)
       if 1
        ;push hl
        ;ld l,c
        ;ld h,tbdoscmds/256
        ;ld a,(hl)
        ;inc h
        ;ld h,(hl)
        ;ld l,a
        ;ex (sp),hl
        ;ret ;73t+call ;TODO jp (hl)..pop hl..jp NN ;58t, no call
        push hl
        ld l,c
        ld h,tbdoscmds/256
        ld a,(hl)
        inc h
        ld h,(hl)
        ld l,a
        ld ($+3+1+1),hl
        pop hl ;70t+call
        call 0
       else
        call BDOShandler
       endif
         push af
         push bc
         call setpgs_killable
        if bdosstack_sz !=0
        ld a,0xc0
        ld (callbdos_mutex),a ;то же самое делают те функции BDOS, которые не собираются возвращаться
        endif
         pop bc
         pop af
         
        if bdosstack_sz==0
callbdos_sp=$+1
        ld sp,0
        else
        exx
        pop hl
        ld iy,(appaddr)
        ;ld l,(iy+app.callbdos_sp)
        ;ld h,(iy+app.callbdos_sp+1)
        ld sp,hl
        exx
        endif
        jp endsys_result_a

       if 0
BDOShandler
        push hl
        ld a,c
        ld hl,tbdoscmds
        push bc
        ld bc,nbdoscmds
        cpir
        jp nz,BDOS_pop2fail
;bc=nbdoscmds-(cmdnumber+1) = 0..(nbdoscmds-1)
        add hl,bc
;hl=tbdoscmds+nbdoscmds
        add hl,bc
        add hl,bc
;hl=tbdoscmds+nbdoscmds+ 2*(nbdoscmds-(cmdnumber+1))
        pop bc
        ld a,(hl)
        inc hl
        ld h,(hl)
        ld l,a
        ;hl=jump addr
        ex (sp),hl
        ret
       endif

sys_quit
;снять текущую задачу
;hl=result
        ld sp,QUITSTACK ;если не сделать, то всё ещё стек задачи, и мы не вернёмся из schedule
        ld iy,(appaddr)
        ld e,(iy+app.id)
       if 1==0
        push de
        push iy
        call BDOS_freezeapp
        pop iy
;если установлен muzcall в пространстве задачи, снимаем его
        ld a,(muzpg)
        call addrpage
        pop de
        ld a,(hl)
        cp e
        jr nz,sys_quit_nomuzcall
       ld hl,sys_reter
       ld (muzcall),hl ;есть в delapppages тоже!!! TODO выбросить?
sys_quit_nomuzcall
        call BDOS_delapppages ;глушит сокеты и музыку, удаляет страницы
       endif
;hl=result
       call BDOS_dropapp ;будит родителя, глушит сокеты и музыку, удаляет страницы
        jp BDOS_yield_q ;переходим на какую-нибудь задачу (там же ставим pgkillable в 4000,8000,c000)
       
setkernelpages_go
;di!!!
;sp=0x3ffx
;сейчас включена 5-я страница
        BDOSSETPGTRDOSFS
        call makeidle
;setkernelpages_go_iy
        ;ld sp,BDOSSTACK
        call setpgs_killable
        ld sp,-8

        ;ld iy,(appaddr)
        ld d,(iy+app.mainpg)
;d=pgmain
;[e=значение для аккумулятора]
        ld bc,memport0000
        ld a,(iy+app.screen)
        jp sys_intq ;там ei


sys_findfreeappstruct
;out: nz=error, iy=free struct
        ld iy,app1
        ld de,app_sz
        ld b,MAXAPPS
        xor a
sys_findfreeappstruct0
        cp (iy+app.id)
        ret z ;iy = free app struct
        add iy,de
        djnz sys_findfreeappstruct0
;too many apps!!!
        ret ;nz
       
sys_findfreeid
        xor a
sys_findfreeid_next
        inc a ;a!=0 (0 и 0xff нельзя - см. BDOS_newpage)
        ld iy,app1
        ld de,app_sz
        ld b,MAXAPPS
sys_findfreeid0
        cp (iy+app.id)
        jr z,sys_findfreeid_next
        add iy,de
        djnz sys_findfreeid0
;a=free id
        ret

f_clos_curdrv_pp
;de=fil !=0
        push de
        call BDOS_setpgstructs
        inc de
        inc de
        inc de
        inc de
        inc de
        xor a
        ld (de),a
        pop de
        ld hl,ffsfunc.f_close
        jp call_ffs_curvol
               
                if atm==1
NVRAM_REG=0xde
NVRAM_VAL=0xbe
readtime
;sp=0x7fxx
;e=gfxmode
;out: hl=date, de=time
;TODO атомарно
                ld bc,0xf7 + (NVRAM_REG<<8)
                ld a,0x0b
                out (c),a
                ld b,NVRAM_VAL
                in a,(c)
                or 0x04
                out (c),a
                xor a           ;sec
                call bcd2bin
                srl a
                ld l,a
               
                ld a,2          ;min
                call bcd2bin
                call minmes
                add a,l
                ld (sys_time_date),a    ;ld e,a
                ld l,h
               
                ld a,4          ;h
                call bcd2bin
                add a,a
                add a,a
                add a,a
                add a,l
                ld (sys_time_date+1),a  ;ld d,a
               
                ld a,7          ;day
                call bcd2bin
                ld l,a
               
                ld a,8          ;mes
                call bcd2bin
                call minmes
                add a,l
                ld (sys_time_date+2),a  ;ld l,a
               
                ld a,9          ;god
                call bcd2bin
                add a,20
                add a,a
                add a,h
                ld (sys_time_date+3),a  ;ld h,a
                ret
minmes
                ld h,a
                xor a
                srl h
                rra
                srl h
                rra
                srl h
                rra
                ret

bcd2bin
                ld b,NVRAM_REG
                out (c),a
                ld b,NVRAM_VAL
                in a,(c)
                ret

writetime ;keep de only needed for atm != 1
         ld d,b
         ld e,c
        ld a,e
        add a,a
        and 63
        ld b,0          ;sec
        call bin2cmos

        ld a,d
        rra
        rra
        rra
        and 31 ;h
        ld b,4
        call bin2cmos

        ex de,hl
        add hl,hl
        add hl,hl
        add hl,hl
        ex de,hl
        ld a,d
        and 63 ;m
        ld b,2
        call bin2cmos

        ld a,h
        srl a
        sub 20
        ld b,9          ;year
        call bin2cmos

        ld a,l
        and 31
        ld b,7          ;day
        call bin2cmos
   
        add hl,hl
        add hl,hl
        add hl,hl
        ld a,h
        and 15
        ld b,8          ;month
        ;call bin2cmos
        ;ret
bin2cmos ;a to cmos cell b (BCD)
        push af
        ld a,b
        ld bc,0xf7 + (NVRAM_REG<<8)
        out (c),a
        pop af
        ld b,NVRAM_VAL
        out (c),a ;BCD
        ret
   
                endif


                if PS2KBD==1
KEY_PUTREDRAW
                ld bc,0xdef7
                out (c),c
                ld b,0xbe
        ld a,2
        out (c),a
                in a,(c)
                jr z,.buf_empty
                inc a
                jr nz,KEY_PUTREDRAW
                ld b,0xde               ;переполнено
                ld a,0x0c               ;сбросить буфер
                out (c),a
                ld b,0xbe
                ld a,1
                out (c),a
.buf_empty             
                xor a
                ld (KEYSCAN.rep_wait),a
                ld b,a
                ld c,a
                ld (.rep_key),bc
                ld bc,key_redraw
                ld (.redrawkey),bc
                ret
.rep_key
                defw 0x0000
.redrawkey
                defw 0x0000
                else
                        include "syskey1.asm"
                endif
               
        ds 50  
INTSTACK1;!=0x3f00 ;kernelspace (для входа в обработчик без порчи стека) (не пересекается с возможным стеком задачи!!!)

        include "fatfsdrv.asm"
        include "sysbdos.asm" ;в конце есть align 256
syskrnl_end=$
        ent