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
