intLibrary assert intLibrary >= #8000 INT_TABLE_HI equ #be INT_TABLE equ 256 * INT_TABLE_HI ; useLib: keybLibrary, memLibrary ; useRam: INT_TABLE_HI ; export var: ; intMode ; intFlagPlayAyMus ; intFlagPlayAyMusPageAdr ; export fn: ; intLibInit ; intSetGameInterrupt ; intRestoreDefaultInterrupt ; intWaitVsync ; Векторы прерываний: ; клавиатура - #ff ; кадровое - #ff ; CovoxBlaster - #ff (признак — порт #fe.bit7 = 1 и оно висит до захвата прерывания процом) ; CTC - #06 (LSB) (висит до захвата прерывания процом) ; возможно есть и другие - надо изучить ; СТС имеет 4 счётчика. ; идея в том, что: ; - счётчик №2 тактируется по знакоместам (1 тик = 1 знакоместо) с частотой 875 Кгц, ; - счётчик №3 тактируется от №2. ; эти два счётчика всегда работают в связке ; если один счётчик заставить отсчитать 2 пиксельных линии экрана (112 знакомест), ; а другой счётчик отсчитать 160 раз от первого, то получим одно срабатывание на 320 пиксельных линий — это полностью идентично кадровому прерыванию. ; Режимы прерываний INT_MODE_SYS equ 0 INT_MODE_GAME equ 1 INT_MODE_VSYNC_HOOK equ 2 ; Здесь храним текущий режим прерываний intMode db INT_MODE_SYS ; Инициализация библиотеки ; запоминаем текущее состояние регистра i ; use: af intLibInit ld a,i ld (_intDefaultRegI),a ret ; Установка прерываний игры ; use: hl,af,i intSetGameInterrupt ; Перед установкой CTC нам необходимо поймать кадровое прерывание ; (это важно т.к. у нас некоторые циклы работают с одним экраном до прихода луча развертки) ; Просто ei:halt не подойдет т.к. общий вектор для кадрового и клавиатурного прерываний ; и если в данный момент будут нажаты кнопки мы поймаем прерывание от них и окажемся в случайном месте кадра ; Делаем в несколько шагов: ; 1. Отключаем системные прерывания чтобы не мешались ; (т.к. они в том числе обрабатывают прерывания от клавиатуры и читают из неё данные) ; и устанавливаем пустой обработчик прерываний ; также сразу устанавливаем адрес обработки CTC di ld hl,_intHandlerCtc ld (INT_TABLE + #06),hl ld hl,_intHandlerNull ld (INT_TABLE + #ff),hl ld a,INT_TABLE_HI ld i,a im 2 ld a,INT_MODE_VSYNC_HOOK ld (intMode),a ; 2. Далее ждем прерываний - если есть данные от клавиатуры, то это не кадровое ; если одновременно будет и кадровое и клавиатура - пропустим кадровое но тут нам это не страшно (дождемся следующего) _vSyncHookLoop ei halt di in a,(COM_A) bit 0,a ; проверить наличие данных от клавиатуры jr z,_vSyncHookSuccess ; если нет данных значит поймали кадровое in a,(DAT_A) ; считать байт jr _vSyncHookLoop ; 3. Поймали кадровое прерывание ; Настраиваем CTC и устанавливаем прерывания игры ; Почему именно такие значения - надо читать доку по z84c15 _vSyncHookSuccess ld a,#57 : out (CTC.Ch_2),a ld a,112 : out (CTC.Ch_2),a ld a,#d7 : out (CTC.Ch_3),a ld a,160 : out (CTC.Ch_3),a ld a,0 : out (CTC.Ch_0),a ld hl,_intHandlerKeybOrFrame ld (INT_TABLE + #ff),hl ld a,INT_MODE_GAME ld (intMode),a ;------- ; инициализируем CovoxBlaster jp soundDeviceCovoxBlasterLib.init ; Пустой обработчик прерываний - нужен для определения от кого прерывание (см. intSetGameInterrupt) ; use: - _intHandlerNull di call _intReti ei: ret _intReti ei reti ; Восстановление прерываний ; (обязательно делать перед использованием функций дос и биос) ; use: af, i intRestoreDefaultInterrupt di ld a,1 out (CTC.Ch_3),a ld a,INT_MODE_SYS ld (intMode),a ld a,#00 _intDefaultRegI equ $-1 ld i,a im 1 ret ; Обработчик "кадровых" прерываний от CTC ; на выходе нужно именно reti если заменить на ret то прерывание от CTC больше не придет ; use: save & restore all used regs to stack _intHandlerCtc di push hl,de,bc,af ex af,af push af ;- ld a,1 ld (_vsyncFlag),a ; Устанавливаем флаг что пришло кадровое прерывание ;- in a,(EmmWin.P1) ld (_intEmmWinP1),a call soundLib.intHandler ld a,0 _intEmmWinP1 equ $-1 out (EmmWin.P1),a ;--- pop af ex af,af pop af,bc,de,hl call _intReti ei: ret ; Обработчик прерываний от клавиатуры и кадровых ; тут делаем только обработку прерываний от клавиатуры ; use: save & restore all used regs to stack _intHandlerKeybOrFrame di push hl,de,bc,af ;--- call keybIntHandler ;di ;--- call soundDeviceCovoxBlasterLib.intHandler ;--- pop af,bc,de,hl call _intReti ei: ret ; Ожидаем начало фрейма (прерывание от СТС) ; Для этого сбрасываем (_vsyncFlag), делаем ei:halt и проверяем что дождались (_vsyncFlag)=1 - кадровое ; use: af intWaitVsync ld a,(intMode) cp INT_MODE_SYS jr z,_intWaitVsyncSys ;- xor a ld (_vsyncFlag),a ;- if DEBUG_BORDER = 1 xor a: out(#fe),a endif ;- _intWaitVsyncLoop ei halt ;- ld a,0 _vsyncFlag equ $-1 and a jr z,_intWaitVsyncLoop ;- if DEBUG_BORDER = 1 ld a,2: out(#fe),a endif ;- ret ; Ожидаем начало фрейма в режиме системных прерываний (ei:halt) ; можем поймать прерывание от клавиатуры вместо кадрового ; Наверное надо бы запретить вызывать intWaitVsync при системных прерываниях, например так: ; if DEBUG_IS_ON = 1 ; ld hl,txtErrorIntWaitVsync ; jp debugShowError ; endif ; txtErrorIntWaitVsync db "ERROR: Call IntWaitVsync with system int", #00 _intWaitVsyncSys call memCacheOffTemporary ;временно выключаем fastRam ei halt jp memCacheRestoryState ;восстанавливаем состояние подключения fastRam