c71e249a4e
First substantive commit: the entire Sprinter C compiler tree on top of
the bare README+gitignore initial commit.
What's in here:
bin/sprinter-cc — driver script invoking SDCC + linker + mkexe
libc/ — Sprinter-specific libc layer over ESTEX/BIOS
(conio, gfx, io, mem, stdio + headers)
runtime/ — crt0 variants (default/small/banked/minimal)
+ heap + bank trampolines
toolchain/ — mkexe (SprintEXE packer, C + tests)
examples/ — 30 demo programs (gfx, file I/O, env, time, …)
lib/Makefile — builds the libc archive (sprinter.lib)
docs/ — converted Sprinter manuals + asm reference samples
third_party/ — solid-c reference compiler dump + sdcc setup script
release_docs/ — packaging / release notes
gitignore overhaul:
• Drop dangerous blanket patterns: *.asm (would hide docs/samples/*.asm)
and *.exe (case-insensitive match was hiding third_party/solid-c/*.EXE
on macOS APFS). Replaced with examples/*/*.{asm,exe,…} and lib/*.lib.
• Restore tracking of toolchain/mkexe/tests/{one,big}.bin — those are
INPUT fixtures, not build outputs.
• Collapse the duplicated SDCC/C/Sdcc sections into one section per
concern (build outputs / vendored / OS-junk).
• Add .sprinter-cc-*/, build/ (catches lib/build/ too), .claude/.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
218 lines
7.8 KiB
NASM
Executable File
218 lines
7.8 KiB
NASM
Executable File
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
|