e761d21505
Функционально-совместимый с Turbo-C <graphics.h> поверх gfx.h. Архитектура: mode-agnostic математика (libc/bgi/*.c → sprinter.lib) + driver-leaf'ы per-режим (libc/bgi/drv256/*.c → sprinter_gfx256.lib). Режим выбирается линковкой: sprinter-cc --gfx 256 (16 — позже, тем же leaf-split'ом). Один код работает в любом режиме без правок. API: initgraph/closegraph/graphresult/cleardevice, set/get color+bkcolor, getmaxx/y/color, put/getpixel, moveto/moverel/getx/gety, line/lineto/ linerel, rectangle, bar, circle, outtext/outtextxy. initgraph грузит EGA-палитру 0..15. Пакетные примитивы (circle) — одна W3-скобка на примитив (иначе на порядок медленнее). Проверено в MAME (tests/bgitest). Попутно: gfx_getpixel256 в libc/gfx. size-check без регресса, baseline обновлён. Детали: memory/bgi_two_lib_design, docs/TODO.md. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
346 lines
24 KiB
Markdown
346 lines
24 KiB
Markdown
# libc — справочник API (2026-07-06)
|
||
|
||
Сводка по заголовкам: сигнатура + одна строка + особенности ABI.
|
||
Детали дизайна: docs/libc-headers.md (контракт затенения SDCC),
|
||
docs/file-buffering-design.md (FILE*), docs/solid_c_compatibility.md.
|
||
|
||
Общие соглашения:
|
||
- ошибки: возврат -1/NULL/EOF + `errno` (код DSS as-is, см. errno.h);
|
||
- SDCC `__sdcccall(1)`: 1-й аргумент HL (8-битный — A), 2-й — DE,
|
||
остальные на стеке; **int/указатель возвращается в DE**;
|
||
- строки для BIOS-вызовов (rst 8) должны лежать в #4000–#BFFF;
|
||
- стек при любых ESTEX/BIOS-вызовах — в W2 (обеспечено crt0).
|
||
|
||
## <stdio.h> — полная замена SDCC (контракт: printf-семейство из z80.lib)
|
||
|
||
Из SDCC z80.lib: `printf sprintf vprintf vsprintf`.
|
||
|
||
Консоль (ESTEX, без атрибутов — быстрый путь; цветной вывод — conio):
|
||
| Сигнатура | Описание |
|
||
|---|---|
|
||
| `int putchar(int c)` | символ через PUTCHAR $5B; '\n'→CR LF |
|
||
| `int getchar(void)` | блокирующий WAITKEY $30, ASCII |
|
||
| `char puts(const char *s)` | строка + '\n' (посимвольно через putchar) |
|
||
| `char *gets(char *buf)` | строка с консоли, без контроля длины |
|
||
| `void dec8/dec16/dec32(v)` | десятичная печать без ведущих нулей |
|
||
| `void hex8/hex16/hex32(v)` | hex-печать фиксированной ширины |
|
||
|
||
FILE* (буферизованный, вариант B+ — единый ленивый буфер BUFSIZ=512
|
||
на чтение/запись с автопереключением; таблица `OPEN_MAX=8` слотов;
|
||
exit() сбрасывает всё через atexit; **ошибки записи отложенные —
|
||
проверять fclose**):
|
||
| Сигнатура | Описание |
|
||
|---|---|
|
||
| `FILE *fopen(path, mode)` | "r/w/a" + '+', 'b/t' игнорируются |
|
||
| `FILE *fdopen(fd, mode)` | завернуть готовый fd (закрывать fclose!) |
|
||
| `FILE *freopen(path, mode, fp)` | переоткрыть тот же FILE* |
|
||
| `int fclose(FILE*)` / `void fclosall(void)` | сброс+закрытие / все потоки |
|
||
| `int fflush(FILE*)` | сброс записи / откат readahead; NULL = все |
|
||
| `int fgetc/fputc(...)` | горячий путь на asm; getc/putc — макро-алиасы |
|
||
| `char *fgets(buf, n, fp)` | до '\n' (сохраняется); блочный LDI-сканер |
|
||
| `int fputs(s, fp)` | без '\n'; через fwrite |
|
||
| `size_t fread/fwrite(p, sz, n, fp)` | блоки ≥ 512 идут мимо буфера |
|
||
| `int ungetc(c, fp)` | 1 байт putback (и на stdin) |
|
||
| `int fseek(fp, off, whence)` / `long ftell(fp)` | ftell без побочных эффектов |
|
||
| `void rewind(fp)` | fseek(0) + сброс EOF/ERROR |
|
||
| `int fgetpos/fsetpos(fp, &pos)` | fpos_t = long |
|
||
| `int feof/ferror(fp)`, `void clearerr(fp)` | флаги потока |
|
||
| `int fprintf/vfprintf(fp, fmt, ...)` | vsprintf в статический буфер 256 |
|
||
| `int scanf/fscanf/sscanf(...)` | %d %u %x %o %c %s, `l`, ширина, %*, %% |
|
||
| `int rename(old, new)` | ESTEX RENAME $10 |
|
||
|
||
`stdin/stdout/stderr` — консольные псевдопотоки (fd 0/-1/-2), не
|
||
буферизуются; freopen на них меняет только FILE*-операции (printf
|
||
идёт в ESTEX напрямую).
|
||
|
||
## <stdlib.h> — include_next + добавки
|
||
|
||
Из SDCC: malloc/free/calloc/realloc (heap в W2), atoi/atol/strtol/
|
||
strtoul, rand/srand, qsort/bsearch, abs/labs, div/ldiv, exit-типы.
|
||
Наше: `int16_t min(a,b)`, `int16_t max(a,b)` (функции, как в Solid-C).
|
||
|
||
## <string.h> — include_next + добавки
|
||
|
||
Из SDCC: mem*/str* полностью. Наше: `char *strlwr/strupr(char *s)` —
|
||
in-place регистр, латиница + кириллица CP866.
|
||
|
||
## <time.h> — полная замена (struct tm в SDCC-ABI, __TIME_UNSIGNED=1)
|
||
|
||
| Сигнатура | Описание |
|
||
|---|---|
|
||
| `void getdatetime(datetime_t*)` | RTC как есть (ESTEX SYSTIME $21) |
|
||
| `int setdatetime(const datetime_t*)` | установка RTC ($22) |
|
||
| `time_t time(time_t*)` | Unix-эпоха из RTC |
|
||
| `mktime/gmtime/localtime/asctime/ctime` | POSIX поверх RTC (без TZ) |
|
||
|
||
`datetime_t`: day/month/year(полный)/hour/minute/second/dow (1=Вс).
|
||
|
||
## <unistd.h>, <fcntl.h> — fd-уровень (манипуляторы DSS)
|
||
|
||
| Сигнатура | Описание |
|
||
|---|---|
|
||
| `int open(path, flags)` | O_RDONLY/WRONLY/RDWR + O_CREAT/TRUNC/EXCL/APPEND (ESTEX $11/$0A/$0B) |
|
||
| `int creat(path, mode)` | open(W|CREAT|TRUNC); mode игнорируется |
|
||
| `int read/write(fd, buf, n)` | ESTEX $13/$14. **Квирк WRITE: DE-возврат ненадёжен, успех = CF=0&A=0** (см. memory/estex_write_de_quirk) |
|
||
| `int close(fd)` | ESTEX $12 |
|
||
| `long lseek(fd, off, whence)` | 32-битная позиция (MOVE_FP $15) |
|
||
| `int unlink(path)` | удалить (DELETE $0E) |
|
||
| `int isatty(fd)` | fd <= 0 (файловые манипуляторы DSS с 1) |
|
||
| `int mkdir/rmdir/chdir(path)` | ESTEX $1B/$1C/$1D |
|
||
| `char *getcwd(buf, size)` | буфер 256 байт, size игнорируется |
|
||
| `void sleep(seconds)` | калиброванный busy-wait (см. ниже) |
|
||
| `void delayms(uint16_t ms)` | то же, гранулярность — миллисекунды (см. ниже) |
|
||
|
||
**Лимит: 8 одновременных манипуляторов**; 9-й OPEN вешает DSS —
|
||
libc отказывает сама (EMFILE, предохранитель _fd_guard).
|
||
|
||
**`sleep()` (2026-07-07, редизайн)**: старая версия считала halt-
|
||
пробуждения (50 = 1 c), предполагая, что КАЖДОЕ прерывание — кадровый
|
||
тик; с CBL/клавиатурой на векторе 0xFF это уже не так (CBL прерывает
|
||
намного чаще кадра — sleep() возвращался бы раньше срока). Теперь:
|
||
лениво, один раз калибруется кратковременным `irq_install()` против
|
||
РЕАЛЬНОГО кадрового тика (трамплин зовёт хук только на настоящих
|
||
кадровых прерываниях — клавиатура/CBL уходят в свои ветки раньше),
|
||
считая, сколько итераций тесного цикла умещается в один КАДР (не в
|
||
секунду — калибровка на секунду переполняла `uint16_t`, был баг:
|
||
`sleep(5)` отрабатывал быстрее секунды, найден пользователем на
|
||
реальном прогоне и исправлен). `sleep(seconds)` — вложенный цикл:
|
||
внешний по секундам, внутренний ровно 50 раз калиброванный busy-wait —
|
||
без единого прерывания и БЕЗ умножения/32-битной арифметики (по духу
|
||
`docs/samples/delayms.asm`, который тоже калибрует на 1 мс, а не на
|
||
1 с). Погрешность ~5-10% (калибровочный и рабочий циклы не тактово-
|
||
идентичны). Fallback на старое "50 halt" поведение, если фрейм-хук уже
|
||
занят другим `irq_install()`-клиентом (EBUSY).
|
||
**Cтоит дороже по размеру** (~+580 Б) — тянет весь модуль `irq`
|
||
(install/remove/трамплин/IM2-таблицу), даже если программа больше
|
||
ничего из irq не использует.
|
||
|
||
**`delayms(ms)`** — тот же движок (`libc/time/_sleep_calib.c`), общий с
|
||
`sleep()`: калибровка одна на двоих (первый вызов ЛЮБОЙ из функций
|
||
калибрует, вторая просто использует готовое). Производная величина
|
||
"итераций на 1 мс" — одно 16-битное деление (`__divuint`, НЕ
|
||
`__mullong`) на константу 20, вычисляется один раз при калибровке;
|
||
`delayms(ms)` — простой цикл `ms` раз, без умножения вообще. Fallback
|
||
при EBUSY — грубый (одно кадровое `halt`), точной альтернативы для
|
||
миллисекундной гранулярности без калибровки нет.
|
||
|
||
**Квирк общий для sleep()/delayms()**: калибровка сама стоит ~20-40 мс
|
||
(синхронизация на границу кадра + сам замер) — ПЕРВЫЙ вызов ЛЮБОЙ из
|
||
двух функций в программе превысит запрошенное время на эту величину;
|
||
для `delayms()` с маленьким `ms` это заметно (`delayms(5)` на первом
|
||
вызове может растянуться на ~25-45 мс). Все последующие вызовы точны.
|
||
Оставлено как есть по решению пользователя — не стали усложнять API
|
||
отдельным calibrate()-примитивом.
|
||
|
||
## <errno.h>
|
||
|
||
`errno` (int), коды = коды DSS (EOK..EUNKERR) + POSIX-имена
|
||
(ENOENT/EBADF/EMFILE/…) + алиасы Solid-C (EZERO/EINVFNC/ENOFILE/…).
|
||
`const char *strerror(int)`, `void perror(const char*)`.
|
||
|
||
## <dos.h> — DOS-слой Solid-C
|
||
|
||
| Сигнатура | Описание |
|
||
|---|---|
|
||
| `void getdate/gettime(&d)` | struct date/time (Turbo-C; ti_hund=0) |
|
||
| `int setdate/settime(&d)` | RMW полного datetime |
|
||
| `uint8_t getdisk(void)` | текущий диск, 0=A (ESTEX $02) |
|
||
| `int setdisk(uint8_t)` | смена диска; возврат = число дисков ($01) |
|
||
| `int absread/abswrite(disk, sect, cnt, buf)` | секторы ЛОГИЧЕСКОГО диска (BIOS $55/$56); буфер в #4000–#BFFF; abswrite минует ФС! |
|
||
|
||
## <dir.h>
|
||
|
||
`int ffirst(pattern, ffblk_t*, attrib)` / `int fnext(ffblk_t*)` —
|
||
поиск по шаблону (ESTEX $19/$1A). Квирк: "."/".." находятся только
|
||
итерацией "*.*" (memory/estex_ffirst_dotdot). FA_*-атрибуты.
|
||
|
||
## <sys/stat.h>
|
||
|
||
`int stat(path, struct stat*)` / `int fstat(fd, ...)` — st_mode
|
||
(S_ISREG/S_ISDIR), st_size, st_mtime (Unix-эпоха).
|
||
|
||
## <conio.h> — текстовый экран с атрибутами (Turbo-C стиль)
|
||
|
||
Клавиатура: `kbhit getch getche getkey` (+KEY_* коды позиций),
|
||
`char *cgets(buf)`.
|
||
Вывод с атрибутом: `putch cputs cprintf` (~10× медленнее stdio-пути;
|
||
'\n' НЕ транслируется — писать "\r\n").
|
||
Атрибуты: `textcolor textbackground textattr`, `set/get_text_attr`,
|
||
COLOR_*-enum, `COLOR(fg,bg)`, COLOR_BLINK; `set/get_putch_raw_mode`.
|
||
Экран: `clrscr clrscr_attr gotoxy home() wherex wherey wherexy scroll
|
||
wrchar rdchar`; режимы `gettextmode/settextmode` (0x02=40×32,
|
||
0x03=80×32).
|
||
Порты/IRQ: `inp outp enable() disable()`.
|
||
Текстовая палитра: `text_pal_load/set_color/get/get_color/reset`
|
||
(план 0..3 → страница BIOS 4..7).
|
||
|
||
## <bios/text.h> — быстрый BIOS-вывод (rst 8, place-based)
|
||
|
||
`bios_set_place/get_place`, `bios_write[attr][_until|_stop]`,
|
||
`bios_fillchar/fillattr/fillcharattr`, `bios_clearwin[_ch]`,
|
||
`bios_scrollwin`. Строка s — в #4000–#BFFF; place продвигается.
|
||
|
||
## <gfx.h> — графика (0x81: 320×256×256; 0x82: 640×256×16)
|
||
|
||
Setup: `gfx_init(mode,page)→prev`, `gfx_done(prev)`.
|
||
Страницы/банк: `gfx_set/get_visible_page`, `gfx_set/get_draw_page`
|
||
(double buffering), `gfx_set/get_bank` (0x50..0x5F, 0x58 = FF-
|
||
прозрачность).
|
||
|
||
`gfx_wait_vsync()` (2026-07-07, редизайн): ждёт переход бита 5 порта
|
||
0xFE из 1 в 0 — реальное аппаратное состояние луча (Y>256 → начало
|
||
кадра, см. MAME sprinter.cpp kbd_fe_r), а не прерывание — поэтому не
|
||
путается с клавиатурой/CBL/CTC, деляющими вектор 0xFF. Бит доступен
|
||
только пока включён `cbl_mode()` (bit7 порта 0x004E) — если приложение
|
||
уже играет через `cbl_open()`, бит достаётся бесплатно; иначе
|
||
`gfx_wait_vsync()` лениво занимает bit7 "немым" кодом частоты через
|
||
`_cbl_port_ref()`/`_cbl_port_unref()` (см. `<cbl.h>`, `_cbl_port.c`) —
|
||
разделяемое владение портом 0x004E, безопасное при любом порядке
|
||
использования с реальным CBL-звуком. Фолбэк на одно кадровое
|
||
прерывание (`halt`), если бит не ведёт себя как ожидается за разумное
|
||
число попыток.
|
||
Примитивы (суффикс _256 / _16): `clear putpixel hline vline line
|
||
rect fill_rect` — через акселератор (hline/vline burst до 256 байт;
|
||
в 0x82 vline через RMW). Координаты int, клиппинг по краям.
|
||
Текст: `gfx_putchar256/16, gfx_text256/16` (8×8, fg/bg; в 0x82 x —
|
||
чётный), `gfx_load_default_font`, `gfx_set_font(ptr)` (interleaved
|
||
font[row*256+char]).
|
||
Палитра: `gfx_pal_load/set/get/get_color/reset` (страницы 0..3).
|
||
`gfx_getpixel256(x,y)→color` — чтение пикселя (0x81).
|
||
|
||
## <graphics.h> — Turbo-C BGI (функц. совместимость), Фаза 1, режим 256
|
||
|
||
Слой поверх `<gfx.h>` со «текущим» цветом/позицией. Режим задаётся
|
||
driver-либой на линковке: `sprinter-cc --gfx 256` (→ 320×256×256; 16 —
|
||
позже). API mode-agnostic: код не меняется при смене режима.
|
||
Setup: `initgraph()` (без аргументов — режим фиксирован либой; грузит
|
||
EGA-палитру 0..15, цвет=WHITE, фон=BLACK, CP=(0,0)),
|
||
`closegraph()`, `graphresult()`, `cleardevice()`.
|
||
Границы/цвет: `getmaxx/getmaxy` (319/255), `getmaxcolor` (255),
|
||
`setcolor/getcolor`, `setbkcolor/getbkcolor`. Константы BLACK..WHITE.
|
||
Точки: `putpixel(x,y,c)`, `getpixel(x,y)`.
|
||
Позиция/линии: `moveto/moverel/getx/gety`, `lineto/linerel` (двигают
|
||
CP), `line(x1,y1,x2,y2)` (не двигает).
|
||
Фигуры: `rectangle` (контур), `bar` (заливка тек. цветом — паттерны в
|
||
Ф2), `circle`. Текст 8×8: `outtextxy(x,y,s)`, `outtext(s)` (двигает CP).
|
||
Реализация: libc/bgi/*.c (math) + libc/bgi/drv256/*.c (leaf →
|
||
sprinter_gfx256.lib). Пакетные примитивы — одна W3-скобка на примитив.
|
||
Ф2: ellipse/arc/fillpoly/floodfill/setfillstyle/getimage/putimage/
|
||
viewport/settextstyle — см. docs/TODO.md.
|
||
|
||
## <irq.h> — user-ISR кадрового прерывания (IM 2)
|
||
|
||
| Сигнатура | Описание |
|
||
|---|---|
|
||
| `int irq_install(isr_t h)` | h зовётся ~50 Гц на кадровых прерываниях; DSS-обработчик чейнится всегда (клавиатура/SYSTIME/мышь живы). 0 / -1+errno (EBUSY повтор, EINVAL — код не в W2: только tiny/big) |
|
||
| `void irq_remove(void)` | вернуть таблицу DSS; идемпотентно; висит на atexit |
|
||
| `int irq_ctc_install(h, div2, div3)` | периодический таймер CTC (вектор 0x06): f = 875000/(div2×div3), div 0=256; пресет кадра IRQ_CTC_VSYNC_DIV2/3 (112×160, ~48.8 Гц); независим от кадрового; трамплин завершает RETI |
|
||
| `void irq_ctc_remove(void)` | глушит CTC (обязательно; atexit подстрахует) |
|
||
| `IRQ_DISABLE()/IRQ_ENABLE()` | di/ei — скобки для чтения shared-переменных из main |
|
||
|
||
Handler'у нельзя: ESTEX/BIOS-вызовы, gfx_*/своп окон, акселератор,
|
||
banked-функции; только volatile-глобалы и быстрая работа (<1 мс).
|
||
|
||
## <cbl.h> — потоковый звук CBL/COVOX (вектор 0xFF, свой ISR)
|
||
|
||
Без собственного кольца (2026-07-07): у CBL уже есть аппаратный буфер
|
||
256 Б (2×128, двойная буферизация на стороне железа — см. официальную
|
||
доку "5.3 COVOX-Blaster"); библиотека просто зовёт `fill()` приложения
|
||
ИЗ ISR, а оно само пропихивает данные (откуда угодно) через
|
||
`cbl_push_otir/accel` — без промежуточной копии.
|
||
|
||
| Сигнатура | Описание |
|
||
|---|---|
|
||
| `int cbl_open(freq_code, fmt, pump_mode, underrun_mode, fill)` | включить CBL (`0x90\|fmt\|freq`), зарегистрировать callback; 0 / -1+errno (EBUSY повтор, EINVAL — freq/fmt/pump_mode/underrun_mode плохие или OTIR+16-бит, ENOMEM — буфер тишины) |
|
||
| `void cbl_close(void)` | выключить CBL, снять хук, освободить буфер тишины (если был); висит на atexit |
|
||
| `void cbl_push_otir(const void *src, uint16_t n)` | пропихнуть n байт через `otir` в порт 0x4F; звать ИЗ fill() |
|
||
| `void cbl_push_accel(const void *src, uint16_t n)` | то же акселератором (страница EMM 0xFD@0xC000); единственный путь для 16-бит |
|
||
| `uint16_t cbl_requests(void)` | счётчик запросов блока ISR (~fs/(сэмплов в блоке) в секунду) |
|
||
| `uint16_t cbl_underruns(void)` | счётчик недоливов (`fill` вернул 0 или не задан) |
|
||
| `CBL_FREQ_7K8 .. CBL_FREQ_109K` | коды частоты (биты 3..0 control-порта) |
|
||
| `CBL_FMT_MONO8/MONO16/STEREO8/STEREO16` | формат (биты 5/6 control-порта); блок 128 Б (8-бит) или 256 Б (16-бит), не зависит от моно/стерео; тишина 0x80 (8-бит) / 0x0000 (16-бит, знаковый) |
|
||
| `CBL_PUMP_OTIR / CBL_PUMP_ACCEL` | способ выдачи — см. ниже |
|
||
| `CBL_UNDERRUN_APP / CBL_UNDERRUN_SILENCE` | поведение при недоливе — см. ниже |
|
||
|
||
`typedef int (*cbl_fill_fn)(uint16_t n);` — callback, зовётся ИЗ ISR за
|
||
очередным блоком (n = `_cbl_block`, 128/256). **Обязан быть быстрым**
|
||
— никаких ESTEX/BIOS/gfx-вызовов (тот же констрейнт, что у
|
||
`irq_install()`-хендлера); вернуть ненулевое, если реально пропихнул n
|
||
байт. Диск (read()) читать из fill() НЕЛЬЗЯ — см. tests/cblstream,
|
||
где под это заведено кольцо уровня приложения.
|
||
|
||
Два насоса (3-й параметр `cbl_open`):
|
||
- **OTIR** (`_cbl_pump_otir`) — `cbl_push_otir()` в порт 0x4F; базовый.
|
||
**НЕ умеет 16-бит** — `cbl_open(..., MONO16/STEREO16, CBL_PUMP_OTIR,
|
||
...)` вернёт EINVAL: по исходнику MAME (sprinter.cpp) порт данных
|
||
ВСЕГДА кладёт байт как есть в один слот, не собирая пару байт в
|
||
16-бит значение и не сверяясь с 16-бит флагом вообще.
|
||
- **ACCEL** (`_cbl_pump_accel`) — `cbl_push_accel()` через акселератор
|
||
в спец-страницу EMM 0xFD, замапленную в окно W3 на 0xC000 (см.
|
||
docs/converted/accel_r.txt, Forum.txt); размер блока патчится SMC
|
||
(`LD D,D` + immediate `LD A,n` + `LD L,L` — тот же паттерн, что и в
|
||
libc/gfx/_gfx_hfill256.c); единственный путь для 16-бит.
|
||
**Verified в MAME 2026-07-07** (tests/cbltest, вся accel-половина
|
||
матрицы прошла без ошибок/underrun).
|
||
|
||
Поведение при недоливе (4-й параметр `cbl_open`):
|
||
- **CBL_UNDERRUN_APP** (по умолчанию, 0) — не забота библиотеки, буфер
|
||
тишины НЕ аллоцируется, в CBL доигрывает то, что уже лежало в его
|
||
аппаратном буфере.
|
||
- **CBL_UNDERRUN_SILENCE** (1) — насос сам пропихивает тишину; буфер
|
||
(128/256 Б по формату) аллоцируется malloc'ом ВНУТРИ `cbl_open()`
|
||
только в этом режиме.
|
||
`cbl_underruns()` считает недоливы в обоих режимах — диагностика,
|
||
поведение не меняет.
|
||
|
||
Приватное прерывание, к DSS не чейнится; бит 7 порта 0xFE (запрос
|
||
блока) читается только пока `cbl_open` не закрыт (при выключенном CBL
|
||
бит всегда 1 — MAME-квирк).
|
||
|
||
**Разделяемое владение портом 0x004E** (2026-07-07): бит 5 порта 0xFE
|
||
(позиция луча — см. `<gfx.h>` `gfx_wait_vsync()`) доступен только пока
|
||
включён bit7 порта 0x004E, независимо от того, играет ли реальный
|
||
звук. `_cbl_port_ref()`/`_cbl_port_unref()` (internal, `_cbl_port.c`)
|
||
дают gfx-модулю занять bit7 "немым" кодом частоты (не заводящим таймер
|
||
CBL — без звука/прерываний), не мешая реальной `cbl_open()`-сессии,
|
||
если она уже идёт (и наоборот — `cbl_close()` возвращает "немой" режим
|
||
вместо полного выключения порта, если gfx его ещё держит).
|
||
|
||
## <palette.h> — низкий уровень (BIOS $A4/$A6)
|
||
|
||
`pal_load pal_get pal_set_color pal_get_color` (страница 0..7,
|
||
записи B,G,R,0), `pal_reset(type)` / `pal_reset_at(type,page,graph)`;
|
||
PAL_GRAPH/PAL_SINCLAIR/PAL_CGA.
|
||
|
||
## <mouse.h> — драйвер RST 30h
|
||
|
||
`mouse_init show hide refresh read(mouse_state_t*) goto bounds_x/y
|
||
text_cursor load_cursor/get_cursor(mouse_cursor_t*) set_sensitivity
|
||
get_sensitivity_x/y video_mode_changed`. Sensitivity = делитель
|
||
(меньше = быстрее). Solid-C алиасы ms_* включены.
|
||
|
||
## <sprinter.h> — платформа
|
||
|
||
Константы портов (PORT_PAGE_W0..W3, PORT_RGADR, PORT_RGMOD), номера
|
||
всех ESTEX-функций (ESTEX_*), BIOS EMM ($C0..$C7); `__sfr`-доступ и
|
||
inline `sprinter_page_w0..w3(page)`; ENV: `getenv putenv sysenv`.
|
||
|
||
## <sprinter_mem.h> — EMM-страницы и банковый I/O
|
||
|
||
`mem_alloc_pages(n)→blk_id, mem_free_block, mem_get_page(blk,idx),
|
||
mem_info(&total,&free)` (реализации _bios/_estex; макро-выбор
|
||
MEM_MANAGE_MODE_*). HOME-резидентный доступ к чужим страницам:
|
||
`bank_load_byte/store_byte/read/write` (своп W3 внутри; *_w1 —
|
||
вариант через окно W1).
|
||
|
||
## <sprinter_exit.h>
|
||
|
||
`atexit` (LIFO, 8 слотов), `exit` (хендлеры+сброс FILE), `_exit`.
|
||
|
||
## <sprinter_compat.h> / <sprinter_solid.h>
|
||
|
||
Типы (BYTE/BOOL/WORD/uint/FD/f_point), TRUE/FALSE/OK/ERROR,
|
||
`setmem movmem` (порядок аргументов!), `strerr seek tell ltell
|
||
remove _ffirst _setargv abort()`, isascii. `<sprinter_solid.h>` —
|
||
зонтичный: один include для портирования Solid-C программ.
|