libc: сплит «1 функция = 1 модуль» — вся библиотека, wildcard-сборка
- bios/conio/env/errno/gfx/io/mem/mouse/stdio/stdlib/string/sys/time/ video разложены по модулям: общие статики и helpers — в internal _-модулях (_conio.h/_mouse.h/_gfx.h/_palette.h/_atexit.h/_time.h) - lib/Makefile: LIBC_C = wildcard libc/*/*.c — гранулярность файлов = гранулярность DCE линкера - эффект _CODE: gfx_text 6986→2568 Б, gfx_mous −1745, gfx_demo/d16 −542; ранее timedir −3270, ls −3098, stattest −2995 - комментарии оставшихся модулей переведены на русский; puts: убран мёртвый pchars; videomode_raw разложен на get/set - docs/libc-split-asm-cases.md — правила asm-связок между модулями; docs/libc-roadmap.md — план этапа - восстановлен examples/mdview/SAMPLE.MD (нужен make floppy) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* _bios_putchar — быстрый вывод символа через ESTEX PUTCHAR ($5B):
|
||||
* без атрибута, CR/LF/скролл и курсор ведёт сам ESTEX. Используется
|
||||
* (задуман) для пути g_text_attr == KEEP_EXIST_ATTR.
|
||||
* PUTCHAR не меняет IX (проверено), поэтому push/pop ix не нужен.
|
||||
*/
|
||||
|
||||
#include "_conio.h"
|
||||
|
||||
char _bios_putchar(char ch) __naked
|
||||
{
|
||||
(void)ch;
|
||||
__asm
|
||||
;; ch в A. push af сохраняет его через RST (который портит A).
|
||||
push af
|
||||
ld c, #0x5B ; ESTEX PUTCHAR
|
||||
rst #0x10
|
||||
pop af
|
||||
ret
|
||||
__endasm;
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* _conio.h — внутренние состояние и хелперы conio.
|
||||
*
|
||||
* НЕ публичный заголовок: живёт рядом с исходниками. После сплита
|
||||
* «1 функция = 1 модуль» каждый элемент лежит в своём модуле, чтобы
|
||||
* приложение тянуло из архива только то, что реально вызывает:
|
||||
*
|
||||
* _g_text_attr.c — g_text_attr (текущий атрибут, KEEP_EXIST_ATTR)
|
||||
* _pc_place.c — pc_place (курсор col:row; global — читают тесты)
|
||||
* _pc_raw_mode.c — pc_raw_mode (режим управляющих символов putch)
|
||||
* _get_cursor.c — чтение курсора BIOS → pc_place
|
||||
* _set_cursor.c — запись pc_place → курсор BIOS
|
||||
* _putch_wrchar.c — воркер вывода mode-0 (BS/TAB/LF/CR интерпретируются)
|
||||
* _putch_wrchar_raw.c — воркер mode-1 (всё как глифы)
|
||||
* _bios_putchar.c — быстрый вывод через ESTEX PUTCHAR (без атрибута)
|
||||
*/
|
||||
#ifndef _CONIO_INTERNAL_H
|
||||
#define _CONIO_INTERNAL_H
|
||||
|
||||
#include <conio.h> /* two_bytes, KEEP_EXIST_ATTR */
|
||||
#include <stdint.h>
|
||||
|
||||
/* Текущий текстовый атрибут: 0x00..0xFF — реальный байт атрибута,
|
||||
* KEEP_EXIST_ATTR (0xFFFF) — быстрый путь без атрибута. */
|
||||
extern two_bytes g_text_attr;
|
||||
|
||||
/* Позиция курсора: .byte.low = col, .byte.high = row.
|
||||
* Поддерживается gotoxy/wherex/wherey/wherexy и воркерами putch. */
|
||||
extern two_bytes pc_place;
|
||||
|
||||
/* 0 — BS/TAB/LF/CR интерпретируются; 1 — печатаются как глифы. */
|
||||
extern uint8_t pc_raw_mode;
|
||||
|
||||
/* Прочитать курсор BIOS (rst 8, $8E) в pc_place. */
|
||||
void _get_cursor(void);
|
||||
|
||||
/* Установить курсор BIOS (rst 8, $84) из pc_place. */
|
||||
void _set_cursor(void);
|
||||
|
||||
/* Быстрый вывод символа через ESTEX PUTCHAR ($5B) — атрибут не
|
||||
* трогается, CR/LF/скролл делает ESTEX. */
|
||||
char _bios_putchar(char ch);
|
||||
|
||||
/* Воркеры вывода в (pc_place) атрибутом attr; курсор BIOS НЕ трогают —
|
||||
* вызывающий делает _get_cursor()/_set_cursor() один раз на операцию. */
|
||||
void _putch_wrchar(char ch, uint8_t attr);
|
||||
void _putch_wrchar_raw(char ch, uint8_t attr);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,12 @@
|
||||
/*
|
||||
* g_text_attr — текущий текстовый атрибут conio (Turbo-C-стиль: stdio
|
||||
* putchar/puts быстрые и без атрибута; putch/cputs/cprintf применяют
|
||||
* этот атрибут). Модуль только с данными.
|
||||
*
|
||||
* 0x00..0xFF — реальный атрибут (4 бита FG | 3 бита BG | 1 бит blink)
|
||||
* KEEP_EXIST_ATTR (0xFFFF) — putch/cputs уходят на быстрый путь
|
||||
*/
|
||||
|
||||
#include "_conio.h"
|
||||
|
||||
two_bytes g_text_attr;
|
||||
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
* _get_cursor — прочитать текущий курсор BIOS GetCursor (rst 8, $8E)
|
||||
* в pc_place (D=row, E=col → сохраняются парой).
|
||||
*/
|
||||
|
||||
#include "_conio.h"
|
||||
|
||||
void _get_cursor(void) __naked
|
||||
{
|
||||
__asm
|
||||
ld c, #0x8e ; BIOS GetCursor
|
||||
rst #0x08
|
||||
ld (_pc_place), de
|
||||
ret
|
||||
__endasm;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
/*
|
||||
* pc_place — кэш позиции курсора conio: .byte.low = col (0..79),
|
||||
* .byte.high = row (0..31). Обновляется gotoxy/wherex/wherey/wherexy
|
||||
* и воркерами putch; глобальный (не `_`-имя) — читается и тестами
|
||||
* (tests/conio2). Модуль только с данными.
|
||||
*/
|
||||
|
||||
#include "_conio.h"
|
||||
|
||||
two_bytes pc_place;
|
||||
@@ -0,0 +1,13 @@
|
||||
/*
|
||||
* pc_raw_mode — режим обработки управляющих символов (< 0x20) в
|
||||
* воркерах putch/cputs:
|
||||
* 0 (по умолчанию) — BS/TAB/LF/CR интерпретируются (без глифа);
|
||||
* 1 — все символы печатаются как глифы CP437.
|
||||
* Действует только на WRCHAR-пути (attr ≤ 0xFF); при KEEP_EXIST_ATTR
|
||||
* курсором и управляющими символами занимается сам ESTEX.
|
||||
* Модуль только с данными.
|
||||
*/
|
||||
|
||||
#include "_conio.h"
|
||||
|
||||
uint8_t pc_raw_mode;
|
||||
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
* _putch_wrchar — воркер вывода mode-0: пишет символ в (pc_place)
|
||||
* через ESTEX WRCHAR ($58) атрибутом attr, интерпретируя управляющие:
|
||||
*
|
||||
* 0x08 BS → pc_col-- (если не 0)
|
||||
* 0x09 TAB → pc_col к следующему кратному 8 (потолок 80)
|
||||
* 0x0A LF → pc_row++ (потолок 32; без глифа)
|
||||
* 0x0D CR → pc_col = 0
|
||||
* прочее → WRCHAR + pc_col++
|
||||
*
|
||||
* WRCHAR подавляется при pc_col ≥ 80 или pc_row ≥ 32 (вне экрана) —
|
||||
* координаты только [0..79] × [0..31].
|
||||
*
|
||||
* Курсор BIOS НЕ трогает — вызывающий читает его один раз до серии
|
||||
* вызовов и записывает один раз после: плата BIOS за ОПЕРАЦИЮ,
|
||||
* а не за символ.
|
||||
*/
|
||||
|
||||
#include "_conio.h"
|
||||
|
||||
void _putch_wrchar(char ch, uint8_t attr) __naked
|
||||
{
|
||||
(void)ch; (void)attr;
|
||||
__asm
|
||||
;; __sdcccall(1): ch в A, attr в L.
|
||||
;; Диспетчеризация по управляющим, пока A держит ch (cp не
|
||||
;; трогает A). B/C грузятся только на пути вывода, поэтому
|
||||
;; ветки управляющих символов дешевле.
|
||||
cp #0x08
|
||||
jr z, _rp0_bs
|
||||
cp #0x09
|
||||
jr z, _rp0_tab
|
||||
cp #0x0A
|
||||
jr z, _rp0_lf
|
||||
cp #0x0D
|
||||
jr z, _rp0_cr
|
||||
;; Всё остальное (печатное или незнакомый ctrl) → глиф.
|
||||
|
||||
_rp0_pri:
|
||||
|
||||
ld c, a ; C = ch (сохранить до порчи A)
|
||||
ld a, (_pc_place + 1)
|
||||
cp #32
|
||||
ret nc ; за нижним краем — молча пропустить
|
||||
ld d, a ; D = row (конвенция ESTEX WRCHAR)
|
||||
ld a, (_pc_place)
|
||||
cp #80
|
||||
ret nc ; за правым краем — молча пропустить
|
||||
ld e, a ; E = col
|
||||
inc a
|
||||
ld (_pc_place), a ; pc_col++
|
||||
|
||||
_rp0_wr:
|
||||
ld b, l ; B = attr
|
||||
ld a, c ; A = ch
|
||||
push ix
|
||||
ld c, #0x58 ; ESTEX WRCHAR
|
||||
rst #0x10
|
||||
pop ix
|
||||
ret
|
||||
|
||||
_rp0_bs:
|
||||
ld a, (_pc_place)
|
||||
or a, a
|
||||
ret z ; уже в колонке 0 — без изменений
|
||||
dec a
|
||||
ld (_pc_place), a
|
||||
ld e, a ; E = col
|
||||
ld a, (_pc_place + 1)
|
||||
ld d, a ; D = row (конвенция ESTEX WRCHAR)
|
||||
ld c, #0x20
|
||||
jr _rp0_wr
|
||||
|
||||
_rp0_tab:
|
||||
ld a, (_pc_place)
|
||||
or #0x07 ; округлить вниз к кратному 8
|
||||
inc a ; → следующее кратное 8
|
||||
cp #81 ; сравнить A с 81 (0x51)
|
||||
jr c, _rp0_tab_skip ; если A < 81 (т.е. A ≤ 80), пропустить загрузку
|
||||
ld a, #80 ; иначе A > 80 → установить A = 80
|
||||
_rp0_tab_skip:
|
||||
ld (_pc_place), a
|
||||
ret
|
||||
|
||||
_rp0_lf:
|
||||
ld a, (_pc_place + 1)
|
||||
cp #32
|
||||
ret nc ; уже у нижнего края
|
||||
inc a
|
||||
ld (_pc_place + 1), a
|
||||
ret
|
||||
|
||||
_rp0_cr:
|
||||
xor a, a
|
||||
ld (_pc_place), a
|
||||
ret
|
||||
__endasm;
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* _putch_wrchar_raw — воркер вывода mode-1: КАЖДЫЙ байт идёт через
|
||||
* ESTEX WRCHAR ($58) как глиф CP437 (включая 0x08/0x09/0x0A/0x0D),
|
||||
* атрибутом attr, в позицию (pc_place) с pc_col++.
|
||||
* Вне экрана (col ≥ 80 / row ≥ 32) вывод молча подавляется.
|
||||
* Курсор BIOS не трогает — см. _putch_wrchar.
|
||||
*/
|
||||
|
||||
#include "_conio.h"
|
||||
|
||||
void _putch_wrchar_raw(char ch, uint8_t attr) __naked
|
||||
{
|
||||
(void)ch; (void)attr;
|
||||
__asm
|
||||
;; __sdcccall(1): ch в A, attr в L.
|
||||
ld c, a ; C = ch (сохранить)
|
||||
|
||||
ld a, (_pc_place + 1)
|
||||
cp #32
|
||||
ret nc ; за нижним краем — молча пропустить
|
||||
ld d, a ; D = row
|
||||
ld a, (_pc_place)
|
||||
cp #80
|
||||
ret nc ; за правым краем — молча пропустить
|
||||
ld e, a ; E = col
|
||||
inc a
|
||||
ld (_pc_place), a ; pc_col++
|
||||
|
||||
ld b, l ; B = attr
|
||||
ld a, c ; A = ch
|
||||
push ix
|
||||
ld c, #0x58 ; ESTEX WRCHAR
|
||||
rst #0x10
|
||||
pop ix
|
||||
ret
|
||||
__endasm;
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
* _set_cursor — установить курсор BIOS SetCursor (rst 8, $84) из
|
||||
* pc_place (D=row, E=col).
|
||||
*/
|
||||
|
||||
#include "_conio.h"
|
||||
|
||||
void _set_cursor(void) __naked
|
||||
{
|
||||
__asm
|
||||
ld de, (_pc_place)
|
||||
ld c, #0x84 ; BIOS SetCursor
|
||||
rst #0x08
|
||||
ret
|
||||
__endasm;
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* cgets — построчный ввод в стиле Solid-C / Turbo-C:
|
||||
* buf[0] = максимум символов (вход)
|
||||
* buf[1] = фактическое количество (выход)
|
||||
* buf[2..] = символы + NUL
|
||||
* Ввод через getche() (с эхом), Enter завершает строку (выводится
|
||||
* "\r\n"), Backspace откатывает символ. Возвращает &buf[2].
|
||||
*/
|
||||
|
||||
#include <conio.h>
|
||||
#include <stdint.h>
|
||||
|
||||
char *cgets(char *buf)
|
||||
{
|
||||
uint8_t maxlen = (uint8_t)buf[0];
|
||||
uint8_t n = 0;
|
||||
while (n < maxlen) {
|
||||
int ch = getche();
|
||||
if (ch == '\n' || ch == '\r') {
|
||||
putch('\r'); putch('\n');
|
||||
break;
|
||||
}
|
||||
if (ch == 8) { /* backspace */
|
||||
if (n > 0) { n--; }
|
||||
continue;
|
||||
}
|
||||
buf[2 + n] = (char)ch;
|
||||
n++;
|
||||
}
|
||||
buf[1] = (char)n;
|
||||
buf[2 + n] = 0;
|
||||
return &buf[2];
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
/*
|
||||
* clrscr — очистить экран атрибутом по умолчанию 0x0F (ярко-белый на
|
||||
* чёрном). Tail-call в clrscr_attr (jp по глобальному имени — работает
|
||||
* через границу модулей).
|
||||
*/
|
||||
|
||||
#include "_conio.h"
|
||||
|
||||
void clrscr(void) __naked
|
||||
{
|
||||
__asm
|
||||
ld a, #0x0F
|
||||
jp _clrscr_attr
|
||||
__endasm;
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* clrscr_attr — очистить экран (80×32) пробелами с заданным атрибутом.
|
||||
* ESTEX CLEAR ($56): A=символ-заполнитель, B=атрибут, DE=верхний левый
|
||||
* угол, H=строк, L=колонок.
|
||||
*/
|
||||
|
||||
#include "_conio.h"
|
||||
|
||||
void clrscr_attr(uint8_t attr) __naked
|
||||
{
|
||||
(void)attr;
|
||||
__asm
|
||||
push ix
|
||||
;; SDCC __sdcccall(1): первый uint8_t аргумент в A.
|
||||
ld b, a ; B = атрибут (цвет заливки)
|
||||
ld de, #0x0000 ; верхний левый угол
|
||||
ld hl, #0x2050 ; H=32 строки, L=80 колонок
|
||||
ld a, #0x20 ; заполнение пробелом
|
||||
ld c, #0x56 ; ESTEX CLEAR
|
||||
rst #0x10
|
||||
pop ix
|
||||
ret
|
||||
__endasm;
|
||||
}
|
||||
@@ -1,678 +0,0 @@
|
||||
/*
|
||||
* conio.c — console I/O wrappers around ESTEX kbd/screen syscalls.
|
||||
*
|
||||
* $30 WAITKEY — blocking read, returns scan / ASCII / modifiers
|
||||
* $31 SCANKEY — non-blocking poll
|
||||
* $32 ECHOKEY — blocking read + auto-echo to the screen
|
||||
* $52 LOCATE — set cursor to (D=row, E=col)
|
||||
* $56 CLEAR — fill a window with (A=char, B=attr)
|
||||
* $5B PUTCHAR — write single character (CR/LF/scroll handled by ESTEX)
|
||||
*
|
||||
* Every RST 10h is bracketed with push/pop IX (caller's frame pointer).
|
||||
*/
|
||||
|
||||
#include <conio.h>
|
||||
#include <stdint.h>
|
||||
#include <errno.h>
|
||||
|
||||
|
||||
/* Forward extern — definition is further down (after putch/cputs which
|
||||
* reference it from asm by linker-symbol name). */
|
||||
static two_bytes g_text_attr = {0};
|
||||
static uint8_t pc_ch = 0;
|
||||
extern two_bytes pc_place = {0};
|
||||
static uint8_t pc_raw_mode = 0;
|
||||
|
||||
|
||||
// TODO - проверить - ф-ии 30h-33h (kbhit/getch/getche/getkey)
|
||||
// не должны менять IX и им можно не делать push ix / pop ix
|
||||
//
|
||||
char kbhit(void) __naked
|
||||
{
|
||||
__asm
|
||||
push ix
|
||||
ld c, #0x33 ; ESTEX CTRLKEY — peeks without consuming
|
||||
rst #0x10
|
||||
pop ix
|
||||
;; A=0 → no key waiting; non-zero → there is one.
|
||||
or a, a
|
||||
ret z
|
||||
ld a, #0x01
|
||||
ret
|
||||
__endasm;
|
||||
}
|
||||
|
||||
char getch(void) __naked
|
||||
{
|
||||
__asm
|
||||
push ix
|
||||
ld c, #0x30 ; ESTEX WAITKEY (no echo)
|
||||
rst #0x10
|
||||
pop ix
|
||||
;; ESTEX returns ASCII in E (and copy in A)
|
||||
ret
|
||||
__endasm;
|
||||
}
|
||||
|
||||
char getche(void) __naked
|
||||
{
|
||||
__asm
|
||||
push ix
|
||||
ld c, #0x32 ; ESTEX ECHOKEY (echo to console)
|
||||
rst #0x10
|
||||
pop ix
|
||||
;; ESTEX returns ASCII in E (and copy in A)
|
||||
ret
|
||||
__endasm;
|
||||
}
|
||||
|
||||
/* getkey — like getch() but exposes BOTH the ASCII value and the
|
||||
* positional scan code, so callers can distinguish extended keys
|
||||
* (arrows, F1..F12, PgUp/PgDn, Home/End, Ins/Del — all of which carry
|
||||
* ASCII == 0 from ESTEX) from plain ASCII keys.
|
||||
*
|
||||
* return = (scan << 8) | ascii
|
||||
*
|
||||
* For plain keys: ascii holds the character, scan holds the positional
|
||||
* code (bit 7 set when Ctrl/Alt/Shift is held).
|
||||
* For extended keys: ascii == 0, scan identifies the key (see KEY_* in
|
||||
* <conio.h>).
|
||||
*/
|
||||
uint16_t getkey(void) __naked
|
||||
{
|
||||
__asm
|
||||
push ix
|
||||
ld c, #0x30 ; ESTEX WAITKEY: A=ASCII, D=scan, E=ASCII
|
||||
rst #0x10
|
||||
pop ix
|
||||
ld e, a ; E = ASCII (defensive: A is the canonical copy)
|
||||
ret ; __sdcccall(1) returns uint16_t in DE
|
||||
__endasm;
|
||||
}
|
||||
|
||||
/* ---- putch / cputs: Turbo-C conio convention ---------------------- *
|
||||
* Both APPLY the current text attribute (g_text_attr). When attr is
|
||||
* KEEP_EXIST_ATTR (>0xFF), they short-circuit to the FAST stdio path
|
||||
* (putchar / puts-like raw PCHARS).
|
||||
*
|
||||
* No '\n' to CR LF translation here — Turbo-C cputs/putch require the
|
||||
* caller to use "\r\n" explicitly. Stdio puts/putchar do translate.
|
||||
*/
|
||||
|
||||
|
||||
/* Controls how _raw_putch treats control characters (< 0x20):
|
||||
* 0 (default) — BS/TAB/LF/CR are interpreted (no glyph output);
|
||||
* other chars print as glyphs via WRCHAR.
|
||||
* 1 — all characters print as glyphs, no interpretation.
|
||||
*
|
||||
* Only takes effect on the WRCHAR (attr ≤ 0xFF) path. When
|
||||
* g_text_attr is KEEP_EXIST_ATTR, ESTEX's own PUTCHAR/PCHARS handle
|
||||
* cursor and control chars — pc_raw_mode is irrelevant. */
|
||||
|
||||
void set_putch_raw_mode(uint8_t mode) { pc_raw_mode = mode; }
|
||||
uint8_t get_putch_raw_mode(void) { return pc_raw_mode; }
|
||||
|
||||
/* ---- Internal helpers ------------------------------------------- */
|
||||
|
||||
/* Read current cursor into pc_row / pc_col via ESTEX CURSOR ($53). */
|
||||
static void _get_cursor(void) __naked
|
||||
{
|
||||
__asm
|
||||
; push ix
|
||||
ld c, #0x8e ; BIOS GetCursor
|
||||
rst #0x08
|
||||
ld (_pc_place), de
|
||||
; pop ix
|
||||
ret
|
||||
__endasm;
|
||||
}
|
||||
|
||||
/* Move cursor to (pc_col, pc_row) via ESTEX LOCATE ($52). */
|
||||
static void _set_cursor(void) __naked
|
||||
{
|
||||
__asm
|
||||
; push ix
|
||||
ld de, (_pc_place)
|
||||
ld c, #0x84 ; BIOS SetCursor
|
||||
rst #0x08
|
||||
; pop ix
|
||||
ret
|
||||
__endasm;
|
||||
}
|
||||
|
||||
/* ESTEX PUTCHAR ($5B) — fast no-attr path; ESTEX handles CR/LF/scroll
|
||||
* and cursor itself. Used when g_text_attr = KEEP_EXIST_ATTR. */
|
||||
static char _bios_putchar(char ch) __naked
|
||||
{
|
||||
(void)ch;
|
||||
__asm
|
||||
;; c in A. push af stashes it across the RST (which clobbers A).
|
||||
;; push ix ; PUTCHAR не меняет IX
|
||||
push af
|
||||
ld c, #0x5B ; ESTEX PUTCHAR
|
||||
rst #0x10
|
||||
pop af
|
||||
;; pop ix
|
||||
ret
|
||||
__endasm;
|
||||
}
|
||||
|
||||
/* Raw putch: low-level WRCHAR-based output at (pc_col, pc_row) using
|
||||
* the given attribute byte (caller has already verified that the high
|
||||
* byte of g_text_attr is zero — this function takes only the low byte).
|
||||
* Updates pc_col / pc_row per pc_raw_mode:
|
||||
*
|
||||
* pc_raw_mode == 0: BS/TAB/LF/CR are INTERPRETED:
|
||||
* 0x08 BS → pc_col-- (if not already 0)
|
||||
* 0x09 TAB → pc_col rounded up to next multiple of 8 (capped 80)
|
||||
* 0x0A LF → pc_row++ (capped at 32; no glyph)
|
||||
* 0x0D CR → pc_col = 0
|
||||
* other → WRCHAR + pc_col++
|
||||
*
|
||||
* pc_raw_mode == 1: ALL characters print as glyphs via WRCHAR
|
||||
* + pc_col++ (including 0x08, 0x09, 0x0A, 0x0D — they render as
|
||||
* their CP437 glyphs).
|
||||
*
|
||||
* WRCHAR itself is suppressed when pc_col ≥ 80 or pc_row ≥ 32 (off-
|
||||
* screen) — coordinates [0..79] × [0..31] only.
|
||||
*
|
||||
* Does NOT call CURSOR / LOCATE — caller is expected to fetch cursor
|
||||
* once before a sequence of _raw_putch calls and write it back once
|
||||
* after, so we pay the BIOS overhead per OPERATION instead of per CHAR. */
|
||||
/* Mode-0 worker: interprets BS/TAB/LF/CR, outputs other chars as glyphs. */
|
||||
static void _putch_wrchar(char ch, uint8_t attr) __naked
|
||||
{
|
||||
(void)ch; (void)attr;
|
||||
__asm
|
||||
;; __sdcccall(1): ch in A, attr in L.
|
||||
;; Dispatch on control chars while A still holds ch (cp does not
|
||||
;; modify A). B/C only get loaded on the output path so the
|
||||
;; ctrl-char paths are cheaper.
|
||||
|
||||
;; ld c, a
|
||||
;; ld a, (_pc_ch) ; A = ch (`ld a,(nn)` does not touch flags)
|
||||
;; jr nz, _rp0_pri
|
||||
|
||||
;; ld a, c
|
||||
cp #0x08
|
||||
jr z, _rp0_bs
|
||||
cp #0x09
|
||||
jr z, _rp0_tab
|
||||
cp #0x0A
|
||||
jr z, _rp0_lf
|
||||
cp #0x0D
|
||||
jr z, _rp0_cr
|
||||
;; Anything else (printable or unrecognised ctrl) → glyph.
|
||||
|
||||
_rp0_pri:
|
||||
|
||||
ld c, a ; C = ch (save before A is clobbered)
|
||||
ld a, (_pc_place + 1)
|
||||
cp #32
|
||||
ret nc ; off-screen bottom — silently skip
|
||||
ld d, a ; D = row (ESTEX WRCHAR convention)
|
||||
ld a, (_pc_place)
|
||||
cp #80
|
||||
ret nc ; off-screen right — silently skip
|
||||
ld e, a ; E = col
|
||||
inc a
|
||||
ld (_pc_place), a ; pc_col++
|
||||
|
||||
_rp0_wr:
|
||||
ld b, l ; B = attr
|
||||
ld a, c ; A = ch
|
||||
push ix
|
||||
ld c, #0x58 ; ESTEX WRCHAR
|
||||
rst #0x10
|
||||
pop ix
|
||||
ret
|
||||
|
||||
_rp0_bs:
|
||||
ld a, (_pc_place)
|
||||
or a, a
|
||||
ret z ; already at col 0 — no change
|
||||
dec a
|
||||
ld (_pc_place), a
|
||||
ld e, a ; E = col
|
||||
ld a, (_pc_place + 1)
|
||||
ld d, a ; D = row (ESTEX WRCHAR convention)
|
||||
ld c, #0x20
|
||||
jr _rp0_wr
|
||||
|
||||
_rp0_tab:
|
||||
ld a, (_pc_place)
|
||||
or #0x07 ; floor to mult of 8
|
||||
inc a ; → next mult of 8
|
||||
cp #81 ; сравнить A с 81 (0x51)
|
||||
jr c, _rp0_tab_skip ; если A < 81 (т.е. A ≤ 80), пропустить загрузку
|
||||
ld a, #80 ; иначе A > 80 → установить A = 80
|
||||
_rp0_tab_skip:
|
||||
ld (_pc_place), a
|
||||
ret
|
||||
|
||||
_rp0_lf:
|
||||
ld a, (_pc_place + 1)
|
||||
cp #32
|
||||
ret nc ; already at bottom edge
|
||||
inc a
|
||||
ld (_pc_place + 1), a
|
||||
ret
|
||||
|
||||
_rp0_cr:
|
||||
xor a, a
|
||||
ld (_pc_place), a
|
||||
ret
|
||||
__endasm;
|
||||
}
|
||||
|
||||
/* Mode-1 worker: every byte goes through WRCHAR as a glyph. */
|
||||
static void _putch_wrchar_raw(char ch, uint8_t attr) __naked
|
||||
{
|
||||
(void)ch; (void)attr;
|
||||
__asm
|
||||
;; __sdcccall(1): ch in A, attr in L.
|
||||
ld c, a ; C = ch (save)
|
||||
|
||||
ld a, (_pc_place + 1)
|
||||
cp #32
|
||||
ret nc ; off-screen bottom — silently skip
|
||||
ld d, a ; D = row
|
||||
ld a, (_pc_place)
|
||||
cp #80
|
||||
ret nc ; off-screen right — silently skip
|
||||
ld e, a ; E = col
|
||||
inc a
|
||||
ld (_pc_place), a ; pc_col++
|
||||
|
||||
ld b, l ; B = attr
|
||||
ld a, c ; A = ch
|
||||
push ix
|
||||
ld c, #0x58 ; ESTEX WRCHAR
|
||||
rst #0x10
|
||||
pop ix
|
||||
ret
|
||||
__endasm;
|
||||
}
|
||||
|
||||
|
||||
/* ---- Public putch / cputs --------------------------------------- *
|
||||
*
|
||||
* KEEP_EXIST_ATTR (high byte != 0) → fast PUTCHAR/PCHARS through
|
||||
* ESTEX, which manages its own cursor.
|
||||
*
|
||||
* Otherwise → fetch cursor ONCE via CURSOR ($53), run one or many
|
||||
* _raw_putch calls, write cursor back ONCE via LOCATE ($52). This
|
||||
* folds the per-char CURSOR/LOCATE pair from the old design into a
|
||||
* single pair per operation. */
|
||||
|
||||
|
||||
char putch(char ch) __naked
|
||||
{
|
||||
(void)ch;
|
||||
__asm
|
||||
;; A = ch on entry; char return → A.
|
||||
ld (_pc_ch), a ; stash c (for both return and re-load)
|
||||
|
||||
ld a, (_g_text_attr) ; A = low byte = attr
|
||||
ld l, a ; L = attr
|
||||
|
||||
ld a, (_pc_raw_mode)
|
||||
or a, a ; Z = (mode == 0)
|
||||
ld a, (_pc_ch) ; A = ch (`ld a,(nn)` does not touch flags)
|
||||
jr nz, _putch_use_raw
|
||||
call __putch_wrchar
|
||||
jr _putch_after_raw
|
||||
_putch_use_raw:
|
||||
call __putch_wrchar_raw
|
||||
_putch_after_raw:
|
||||
call __set_cursor
|
||||
ld a, (_pc_ch) ; return value
|
||||
ret
|
||||
|
||||
__endasm;
|
||||
}
|
||||
|
||||
|
||||
char cputs(const char *s) __naked
|
||||
{
|
||||
(void)s;
|
||||
__asm
|
||||
;; HL = s on entry; char return → A.
|
||||
|
||||
;; NULL-check: cputs(NULL) → return 0 immediately.
|
||||
ld a, h
|
||||
or a, l
|
||||
ret z
|
||||
|
||||
push ix
|
||||
|
||||
call __get_cursor
|
||||
|
||||
;; KEEP_EXIST_ATTR? high byte of g_text_attr != 0
|
||||
ld a, (_g_text_attr + 1)
|
||||
or a, a
|
||||
jr nz, _cputs_fast
|
||||
|
||||
ld a, (_pc_raw_mode)
|
||||
or a, a
|
||||
jr nz, _cputs_bios
|
||||
|
||||
push hl
|
||||
pop de
|
||||
|
||||
_cputs_wrloop:
|
||||
ld a, (_g_text_attr)
|
||||
ld l, a
|
||||
ld a, (de) ; загрузить байт
|
||||
or a ; установить флаг Z, если A == 0
|
||||
jr z, _cputs_ex ; завершить подпрограмму (конец строки)
|
||||
push de
|
||||
call __putch_wrchar ; вывести символ (A передаётся как аргумент)
|
||||
pop de
|
||||
inc de ; перейти к следующему байту
|
||||
jr _cputs_wrloop ; повторить
|
||||
|
||||
_cputs_bios:
|
||||
ld a, (_g_text_attr)
|
||||
ld b, #0xFF
|
||||
ld d, #0x0
|
||||
ld e, a
|
||||
ld c, #0x8B ; BIOS LP_PRINT_LN5
|
||||
rst #0x08
|
||||
jr _cputs_ex
|
||||
_cputs_fast:
|
||||
ld c, #0x5C ; ESTEX PCHARS
|
||||
rst #0x10
|
||||
_cputs_ex:
|
||||
call __set_cursor
|
||||
pop ix ; restore callers IX
|
||||
xor a, a ; return 0
|
||||
ret
|
||||
__endasm;
|
||||
}
|
||||
|
||||
void clrscr(void) __naked
|
||||
{
|
||||
__asm
|
||||
ld a, #0x0F
|
||||
jp _clrscr_attr
|
||||
__endasm;
|
||||
}
|
||||
|
||||
void clrscr_attr(uint8_t attr) __naked
|
||||
{
|
||||
(void)attr;
|
||||
__asm
|
||||
push ix
|
||||
;; SDCC __sdcccall(1): uint8_t 1st arg is in A.
|
||||
ld b, a ; B = attribute (mode-fill colour)
|
||||
ld de, #0x0000 ; top-left
|
||||
ld hl, #0x2050 ; H=32 rows, L=80 cols
|
||||
ld a, #0x20 ; space fill
|
||||
ld c, #0x56 ; ESTEX CLEAR
|
||||
rst #0x10
|
||||
pop ix
|
||||
ret
|
||||
__endasm;
|
||||
}
|
||||
|
||||
void gotoxy(uint8_t x, uint8_t y) __naked
|
||||
{
|
||||
(void)x; (void)y;
|
||||
__asm
|
||||
;; __sdcccall(1) 2 uint8 args: x in A, y in L.
|
||||
;; ESTEX LOCATE ($52) wants: D = row, E = col.
|
||||
;; push ix
|
||||
ld d, l ; D = row (y)
|
||||
ld e, a ; E = col (x)
|
||||
ld (_pc_place), de
|
||||
ld c, #0x84 ; BIOS SetCursor
|
||||
rst #0x08
|
||||
;; ld c, #0x52
|
||||
;; rst #0x10
|
||||
;; pop ix
|
||||
ret
|
||||
__endasm;
|
||||
}
|
||||
|
||||
uint8_t wherex(void) __naked
|
||||
{
|
||||
__asm
|
||||
;; ESTEX CURSOR ($53): D = row, E = col. Return col in DE.
|
||||
;; push ix
|
||||
ld c, #0x8e ; BIOS GetCursor
|
||||
rst #0x08
|
||||
;; ld c, #0x53
|
||||
;; rst #0x10
|
||||
;; pop ix
|
||||
ld a, e
|
||||
ld (_pc_place), de
|
||||
ret
|
||||
__endasm;
|
||||
}
|
||||
|
||||
uint8_t wherey(void) __naked
|
||||
{
|
||||
__asm
|
||||
;; push ix
|
||||
ld c, #0x8e ; BIOS GetCursor
|
||||
rst #0x08
|
||||
;; ld c, #0x53
|
||||
;; rst #0x10
|
||||
;; pop ix
|
||||
ld a, d
|
||||
ld (_pc_place), de
|
||||
ret
|
||||
__endasm;
|
||||
}
|
||||
|
||||
uint16_t wherexy(void) __naked
|
||||
{
|
||||
__asm
|
||||
;; ESTEX CURSOR ($53): D = row, E = col. Return col in DE.
|
||||
;; push ix
|
||||
ld c, #0x8e ; BIOS GetCursor
|
||||
rst #0x08
|
||||
;; ld c, #0x53
|
||||
;; rst #0x10
|
||||
;; pop ix
|
||||
ld (_pc_place), de
|
||||
ret
|
||||
__endasm;
|
||||
}
|
||||
|
||||
/* wrchar(uint8_t x, uint8_t y, char ch, uint8_t attr)
|
||||
*
|
||||
* SDCC __sdcccall(1): x in A, y in L (2 uint8 → A, L); ch and attr
|
||||
* packed and pushed on the stack as a single 16-bit value (caller does
|
||||
* `ld hl, #(attr<<8)|ch; push hl`). Layout after CALL:
|
||||
* [SP+0..1] = return address
|
||||
* [SP+2] = ch (low half of pushed pair)
|
||||
* [SP+3] = attr (high half)
|
||||
* Void return → callee-pops the 2 stack-arg bytes via `pop bc` + jp (iy).
|
||||
*/
|
||||
void scroll(uint8_t x, uint8_t y, uint8_t w, uint8_t h, uint8_t direction, uint8_t clear) __naked
|
||||
{
|
||||
(void)x; (void)y; (void)w; (void)h; (void)direction; (void)clear;
|
||||
__asm
|
||||
pop iy ; return address
|
||||
ld d, l ; D = row (y)
|
||||
ld e, a ; E = col (x)
|
||||
pop hl ; H = heigth(h), L = width(w)
|
||||
pop bc ; C = direction, B = clear
|
||||
ld a, b ; A = clear(B)
|
||||
ld b, c ; B = direction(C)
|
||||
push ix
|
||||
ld c, #0x55 ; ESTEX SCROLL
|
||||
rst #0x10
|
||||
pop ix
|
||||
jp (iy)
|
||||
__endasm;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/* wrchar(uint8_t x, uint8_t y, char ch, uint8_t attr)
|
||||
*
|
||||
* SDCC __sdcccall(1): x in A, y in L (2 uint8 → A, L); ch and attr
|
||||
* packed and pushed on the stack as a single 16-bit value (caller does
|
||||
* `ld hl, #(attr<<8)|ch; push hl`). Layout after CALL:
|
||||
* [SP+0..1] = return address
|
||||
* [SP+2] = ch (low half of pushed pair)
|
||||
* [SP+3] = attr (high half)
|
||||
* Void return → callee-pops the 2 stack-arg bytes via `pop bc` + jp (iy).
|
||||
*/
|
||||
void wrchar(uint8_t x, uint8_t y, char ch, uint8_t attr) __naked
|
||||
{
|
||||
(void)x; (void)y; (void)ch; (void)attr;
|
||||
__asm
|
||||
pop iy ; return address
|
||||
pop bc ; C = ch, B = attr
|
||||
push ix
|
||||
ld d, l ; D = row (y)
|
||||
ld e, a ; E = col (x)
|
||||
ld a, c ; A = ch
|
||||
ld c, #0x58 ; ESTEX WRCHAR
|
||||
rst #0x10
|
||||
pop ix
|
||||
jp (iy)
|
||||
__endasm;
|
||||
}
|
||||
|
||||
/* rdchar(int x, int y) → (attr << 8) | ch */
|
||||
uint16_t rdchar(uint8_t x, uint8_t y) __naked
|
||||
{
|
||||
(void)x; (void)y;
|
||||
__asm
|
||||
push ix
|
||||
ld d, l ; D = row
|
||||
ld e, a ; E = col
|
||||
ld c, #0x57 ; ESTEX RDCHAR
|
||||
rst #0x10
|
||||
;; A = ch, B = attr
|
||||
ld d, b ; high byte → attr
|
||||
ld e, a ; low byte → ch
|
||||
pop ix
|
||||
ret
|
||||
__endasm;
|
||||
}
|
||||
|
||||
/* Public text-mode video API — defined here so it's pulled in with the
|
||||
* rest of conio. The raw setters/getters live in videomode_raw.c so
|
||||
* pure graphics programs can pick them up without conio's other
|
||||
* dependencies. */
|
||||
extern uint8_t _videomode_raw_get(void);
|
||||
extern int _videomode_raw_set(uint8_t mode);
|
||||
|
||||
uint8_t gettextmode(void)
|
||||
{
|
||||
return _videomode_raw_get();
|
||||
}
|
||||
|
||||
int settextmode(uint8_t mode)
|
||||
{
|
||||
/* Refuse anything that isn't a known text mode — otherwise a stray
|
||||
* GFX_MODE_* value could swap the screen out from under text I/O. */
|
||||
if (mode != TEXT_MODE_40x32 && mode != TEXT_MODE_80x32) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
return _videomode_raw_set(mode);
|
||||
}
|
||||
|
||||
/* ---- text attribute state ----------------------------------------
|
||||
* g_text_attr is owned by conio.c now (Turbo-C-style: stdio putchar/puts
|
||||
* are fast and attribute-free; only conio's putch/cputs/cprintf apply
|
||||
* the attribute). Default = 0x0F (bright white on black).
|
||||
*
|
||||
* 0x00..0xFF — real attribute (4-bit FG | 3-bit BG | 1-bit blink)
|
||||
* KEEP_EXIST_ATTR (0xFFFF) — putch/cputs fall back to fast no-attr path */
|
||||
|
||||
int16_t set_text_attr(int16_t attr)
|
||||
{
|
||||
int16_t prev = g_text_attr.value;
|
||||
g_text_attr.value = attr;
|
||||
return prev;
|
||||
}
|
||||
|
||||
int16_t get_text_attr(void)
|
||||
{
|
||||
return g_text_attr.value;
|
||||
}
|
||||
|
||||
/* ---- Turbo-C-style palette helpers --------------------------------
|
||||
* textcolor / textbackground touch only their nibble; the other nibble
|
||||
* (and the blink bit) are preserved. textattr replaces the whole byte. */
|
||||
|
||||
void textcolor(uint8_t fg)
|
||||
{
|
||||
fg = (fg & 0x07);
|
||||
g_text_attr.value = (g_text_attr.byte.low & 0xF0) | fg;
|
||||
// g_text_attr.byte.high = 0;
|
||||
}
|
||||
|
||||
void textbackground(uint8_t bg)
|
||||
{
|
||||
bg = (bg & 0x07) << 4;
|
||||
g_text_attr.value = (g_text_attr.byte.low & 0x0F) | bg;
|
||||
// g_text_attr.byte.high = 0;
|
||||
}
|
||||
|
||||
void textattr(uint8_t attr)
|
||||
{
|
||||
g_text_attr.value = (uint16_t)attr;
|
||||
}
|
||||
|
||||
/* ---- Solid-C compatibility ---------------------------------------- */
|
||||
|
||||
/* Direct port I/O. Z80 has 256 IN/OUT ports; we wrap the Z80 IN/OUT
|
||||
* opcodes with a stable C API. Names match Solid-C / MS-DOS Turbo-C. */
|
||||
uint8_t z80_inp(uint8_t port) __naked
|
||||
{
|
||||
(void)port;
|
||||
__asm
|
||||
;; SDCC __sdcccall(1): single uint8_t arg in A; uint8_t return in A.
|
||||
ld c, a
|
||||
in a, (c)
|
||||
ret
|
||||
__endasm;
|
||||
}
|
||||
|
||||
void z80_outp(uint8_t port, uint8_t value) __naked
|
||||
{
|
||||
(void)port; (void)value;
|
||||
__asm
|
||||
;; __sdcccall(1): 2 uint8 args → arg1 in A, arg2 in L.
|
||||
ld c, a ; C = port
|
||||
out (c), l ; out (port), value
|
||||
ret
|
||||
__endasm;
|
||||
}
|
||||
|
||||
/* cgets — Solid-C / Turbo-C style line input.
|
||||
* buf[0] = max characters (in)
|
||||
* buf[1] = actual count (out)
|
||||
* buf[2..] = chars + NUL
|
||||
* Returns &buf[2]. */
|
||||
char *cgets(char *buf)
|
||||
{
|
||||
uint8_t maxlen = (uint8_t)buf[0];
|
||||
uint8_t n = 0;
|
||||
while (n < maxlen) {
|
||||
int ch = getche();
|
||||
if (ch == '\n' || ch == '\r') {
|
||||
putch('\r'); putch('\n');
|
||||
break;
|
||||
}
|
||||
if (ch == 8) { /* backspace */
|
||||
if (n > 0) { n--; }
|
||||
continue;
|
||||
}
|
||||
buf[2 + n] = (char)ch;
|
||||
n++;
|
||||
}
|
||||
buf[1] = (char)n;
|
||||
buf[2 + n] = 0;
|
||||
return &buf[2];
|
||||
}
|
||||
@@ -1,12 +1,13 @@
|
||||
/*
|
||||
* cprintf — printf for the conio output set. Formats into a static
|
||||
* buffer with vsprintf (from SDCC's stdlib), then emits via cputs which
|
||||
* applies the current text attribute per character.
|
||||
* cprintf — printf для conio-набора вывода. Форматирует в статический
|
||||
* буфер через vsprintf (из stdlib SDCC), затем выводит cputs'ом,
|
||||
* который применяет текущий текстовый атрибут посимвольно.
|
||||
*
|
||||
* No '\n' to CR LF translation — Turbo-C convention: callers write
|
||||
* "\r\n" explicitly in the format string for line breaks.
|
||||
* '\n' в CR LF НЕ транслируется — конвенция Turbo-C: перевод строки
|
||||
* пишется в format-строке явно как "\r\n".
|
||||
*
|
||||
* Not reentrant (single static buffer) but Z80 single-threaded is fine.
|
||||
* Не реентерабельно (один статический буфер), но на однопоточном Z80
|
||||
* это не проблема.
|
||||
*/
|
||||
|
||||
#include <conio.h>
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* cputs — вывод ASCIIZ-строки с текущим атрибутом (Turbo-C conio).
|
||||
*
|
||||
* Курсор BIOS читается один раз до строки и записывается один раз
|
||||
* после. Три пути:
|
||||
* g_text_attr == KEEP_EXIST_ATTR → быстрый ESTEX PCHARS ($5C);
|
||||
* pc_raw_mode == 1 → BIOS LP_PRINT_LN5 (глифы с атрибутом);
|
||||
* иначе → цикл _putch_wrchar (интерпретация
|
||||
* BS/TAB/LF/CR).
|
||||
* Трансляции '\n' → CR LF нет — вызывающий пишет "\r\n" сам.
|
||||
* cputs(NULL) — no-op. Возвращает 0.
|
||||
*/
|
||||
|
||||
#include "_conio.h"
|
||||
|
||||
char cputs(const char *s) __naked
|
||||
{
|
||||
(void)s;
|
||||
__asm
|
||||
;; HL = s на входе; возврат char → A.
|
||||
|
||||
;; NULL-check: cputs(NULL) → сразу вернуть 0.
|
||||
ld a, h
|
||||
or a, l
|
||||
ret z
|
||||
|
||||
push ix
|
||||
|
||||
call __get_cursor
|
||||
|
||||
;; KEEP_EXIST_ATTR? старший байт g_text_attr != 0
|
||||
ld a, (_g_text_attr + 1)
|
||||
or a, a
|
||||
jr nz, _cputs_fast
|
||||
|
||||
ld a, (_pc_raw_mode)
|
||||
or a, a
|
||||
jr nz, _cputs_bios
|
||||
|
||||
push hl
|
||||
pop de
|
||||
|
||||
_cputs_wrloop:
|
||||
ld a, (_g_text_attr)
|
||||
ld l, a
|
||||
ld a, (de) ; загрузить байт
|
||||
or a ; установить флаг Z, если A == 0
|
||||
jr z, _cputs_ex ; завершить подпрограмму (конец строки)
|
||||
push de
|
||||
call __putch_wrchar ; вывести символ (A передаётся как аргумент)
|
||||
pop de
|
||||
inc de ; перейти к следующему байту
|
||||
jr _cputs_wrloop ; повторить
|
||||
|
||||
_cputs_bios:
|
||||
ld a, (_g_text_attr)
|
||||
ld b, #0xFF
|
||||
ld d, #0x0
|
||||
ld e, a
|
||||
ld c, #0x8B ; BIOS LP_PRINT_LN5
|
||||
rst #0x08
|
||||
jr _cputs_ex
|
||||
_cputs_fast:
|
||||
ld c, #0x5C ; ESTEX PCHARS
|
||||
rst #0x10
|
||||
_cputs_ex:
|
||||
call __set_cursor
|
||||
pop ix ; восстановить IX вызывающего
|
||||
xor a, a ; вернуть 0
|
||||
ret
|
||||
__endasm;
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
/*
|
||||
* get_putch_raw_mode — текущий режим обработки управляющих символов
|
||||
* putch/cputs (см. set_putch_raw_mode).
|
||||
*/
|
||||
|
||||
#include "_conio.h"
|
||||
|
||||
uint8_t get_putch_raw_mode(void)
|
||||
{
|
||||
return pc_raw_mode;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
/*
|
||||
* get_text_attr — текущий 16-битный атрибут conio (см. set_text_attr).
|
||||
*/
|
||||
|
||||
#include "_conio.h"
|
||||
|
||||
int16_t get_text_attr(void)
|
||||
{
|
||||
return g_text_attr.value;
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* getch — блокирующее чтение клавиши БЕЗ эха на экран.
|
||||
* ESTEX WAITKEY ($30); ASCII возвращается в A.
|
||||
*/
|
||||
|
||||
#include "_conio.h"
|
||||
|
||||
char getch(void) __naked
|
||||
{
|
||||
__asm
|
||||
push ix
|
||||
ld c, #0x30 ; ESTEX WAITKEY (без эха)
|
||||
rst #0x10
|
||||
pop ix
|
||||
;; ESTEX возвращает ASCII в E (и копию в A)
|
||||
ret
|
||||
__endasm;
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* getche — блокирующее чтение клавиши С эхом на экран.
|
||||
* ESTEX ECHOKEY ($32); ASCII возвращается в A.
|
||||
*/
|
||||
|
||||
#include "_conio.h"
|
||||
|
||||
char getche(void) __naked
|
||||
{
|
||||
__asm
|
||||
push ix
|
||||
ld c, #0x32 ; ESTEX ECHOKEY (эхо на консоль)
|
||||
rst #0x10
|
||||
pop ix
|
||||
;; ESTEX возвращает ASCII в E (и копию в A)
|
||||
ret
|
||||
__endasm;
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* getkey — как getch(), но возвращает И ASCII, И позиционный скан-код,
|
||||
* чтобы различать расширенные клавиши (стрелки, F1..F12, PgUp/PgDn,
|
||||
* Home/End, Ins/Del — у них ASCII == 0 из ESTEX) и обычные символы.
|
||||
*
|
||||
* возврат = (scan << 8) | ascii
|
||||
*
|
||||
* Обычные клавиши: ascii — символ, scan — позиционный код (бит 7
|
||||
* взведён при Ctrl/Alt/Shift).
|
||||
* Расширенные: ascii == 0, клавишу определяет scan (см. KEY_* в
|
||||
* <conio.h>).
|
||||
*/
|
||||
|
||||
#include "_conio.h"
|
||||
|
||||
uint16_t getkey(void) __naked
|
||||
{
|
||||
__asm
|
||||
push ix
|
||||
ld c, #0x30 ; ESTEX WAITKEY: A=ASCII, D=scan, E=ASCII
|
||||
rst #0x10
|
||||
pop ix
|
||||
ld e, a ; E = ASCII (защитно: канонична копия в A)
|
||||
ret ; __sdcccall(1) возвращает uint16_t в DE
|
||||
__endasm;
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
/*
|
||||
* gettextmode — текущий видеорежим (обёртка _videomode_raw_get из
|
||||
* video/videomode_raw.c: сырые геттер/сеттер живут отдельно, чтобы
|
||||
* чисто графические программы не тянули conio).
|
||||
*/
|
||||
|
||||
#include <conio.h>
|
||||
#include <stdint.h>
|
||||
|
||||
extern uint8_t _videomode_raw_get(void);
|
||||
|
||||
uint8_t gettextmode(void)
|
||||
{
|
||||
return _videomode_raw_get();
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* gotoxy — установить курсор в (x=col, y=row), 0-based.
|
||||
* Обновляет pc_place и ставит курсор BIOS SetCursor (rst 8, $84).
|
||||
*/
|
||||
|
||||
#include "_conio.h"
|
||||
|
||||
void gotoxy(uint8_t x, uint8_t y) __naked
|
||||
{
|
||||
(void)x; (void)y;
|
||||
__asm
|
||||
;; __sdcccall(1), 2 аргумента uint8: x в A, y в L.
|
||||
ld d, l ; D = row (y)
|
||||
ld e, a ; E = col (x)
|
||||
ld (_pc_place), de
|
||||
ld c, #0x84 ; BIOS SetCursor
|
||||
rst #0x08
|
||||
ret
|
||||
__endasm;
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* kbhit — 1, если в буфере клавиатуры есть символ, иначе 0.
|
||||
* ESTEX CTRLKEY ($33) подглядывает, не забирая символ из буфера.
|
||||
*/
|
||||
|
||||
#include "_conio.h"
|
||||
|
||||
// TODO - проверить - ф-ии 30h-33h (kbhit/getch/getche/getkey)
|
||||
// не должны менять IX и им можно не делать push ix / pop ix
|
||||
char kbhit(void) __naked
|
||||
{
|
||||
__asm
|
||||
push ix
|
||||
ld c, #0x33 ; ESTEX CTRLKEY — подглядывает без изъятия
|
||||
rst #0x10
|
||||
pop ix
|
||||
;; A=0 → клавиши нет; ненулевой → есть.
|
||||
or a, a
|
||||
ret z
|
||||
ld a, #0x01
|
||||
ret
|
||||
__endasm;
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* putch — вывод символа с текущим атрибутом (Turbo-C conio).
|
||||
*
|
||||
* Применяет g_text_attr; при pc_raw_mode == 1 управляющие символы
|
||||
* печатаются как глифы (воркер _putch_wrchar_raw), иначе
|
||||
* интерпретируются (_putch_wrchar). После вывода записывает курсор
|
||||
* BIOS один раз (_set_cursor). Трансляции '\n' → CR LF нет —
|
||||
* Turbo-C-конвенция, вызывающий пишет "\r\n" сам.
|
||||
* Возвращает выведенный символ.
|
||||
*/
|
||||
|
||||
#include "_conio.h"
|
||||
|
||||
/* Стэш символа на время вызовов воркеров (только для putch). */
|
||||
static uint8_t pc_ch;
|
||||
|
||||
char putch(char ch) __naked
|
||||
{
|
||||
(void)ch;
|
||||
__asm
|
||||
;; A = ch на входе; возврат char → A.
|
||||
ld (_pc_ch), a ; спрятать ch (для возврата и перезагрузки)
|
||||
|
||||
ld a, (_g_text_attr) ; A = младший байт = attr
|
||||
ld l, a ; L = attr
|
||||
|
||||
ld a, (_pc_raw_mode)
|
||||
or a, a ; Z = (mode == 0)
|
||||
ld a, (_pc_ch) ; A = ch (`ld a,(nn)` не трогает флаги)
|
||||
jr nz, _putch_use_raw
|
||||
call __putch_wrchar
|
||||
jr _putch_after_raw
|
||||
_putch_use_raw:
|
||||
call __putch_wrchar_raw
|
||||
_putch_after_raw:
|
||||
call __set_cursor
|
||||
ld a, (_pc_ch) ; возвращаемое значение
|
||||
ret
|
||||
|
||||
__endasm;
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* rdchar — прочитать символ и атрибут из позиции (x, y):
|
||||
* возврат (attr << 8) | ch. ESTEX RDCHAR ($57).
|
||||
*/
|
||||
|
||||
#include "_conio.h"
|
||||
|
||||
uint16_t rdchar(uint8_t x, uint8_t y) __naked
|
||||
{
|
||||
(void)x; (void)y;
|
||||
__asm
|
||||
push ix
|
||||
ld d, l ; D = row
|
||||
ld e, a ; E = col
|
||||
ld c, #0x57 ; ESTEX RDCHAR
|
||||
rst #0x10
|
||||
;; A = ch, B = attr
|
||||
ld d, b ; старший байт → attr
|
||||
ld e, a ; младший байт → ch
|
||||
pop ix
|
||||
ret
|
||||
__endasm;
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* scroll — прокрутка окна (x, y, w, h) на 1 позицию в направлении
|
||||
* direction, с очисткой освободившейся строки/колонки атрибутом clear.
|
||||
* ESTEX SCROLL ($55).
|
||||
*
|
||||
* SDCC __sdcccall(1): x в A, y в L; w,h и direction,clear запушены
|
||||
* парами на стек. Void-возврат → callee снимает стековые аргументы
|
||||
* (pop до RST) и уходит через jp (iy).
|
||||
*/
|
||||
|
||||
#include "_conio.h"
|
||||
|
||||
void scroll(uint8_t x, uint8_t y, uint8_t w, uint8_t h, uint8_t direction, uint8_t clear) __naked
|
||||
{
|
||||
(void)x; (void)y; (void)w; (void)h; (void)direction; (void)clear;
|
||||
__asm
|
||||
pop iy ; адрес возврата
|
||||
ld d, l ; D = row (y)
|
||||
ld e, a ; E = col (x)
|
||||
pop hl ; H = высота (h), L = ширина (w)
|
||||
pop bc ; C = direction, B = clear
|
||||
ld a, b ; A = clear(B)
|
||||
ld b, c ; B = direction(C)
|
||||
push ix
|
||||
ld c, #0x55 ; ESTEX SCROLL
|
||||
rst #0x10
|
||||
pop ix
|
||||
jp (iy)
|
||||
__endasm;
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
/*
|
||||
* set_putch_raw_mode — режим обработки управляющих символов в
|
||||
* putch/cputs: 0 — BS/TAB/LF/CR интерпретируются, 1 — все байты
|
||||
* печатаются как глифы CP437 (см. pc_raw_mode).
|
||||
*/
|
||||
|
||||
#include "_conio.h"
|
||||
|
||||
void set_putch_raw_mode(uint8_t mode)
|
||||
{
|
||||
pc_raw_mode = mode;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
* set_text_attr — установить полный 16-битный атрибут conio
|
||||
* (0x00..0xFF — реальный атрибут; KEEP_EXIST_ATTR — быстрый путь без
|
||||
* атрибута). Возвращает прежнее значение.
|
||||
*/
|
||||
|
||||
#include "_conio.h"
|
||||
|
||||
int16_t set_text_attr(int16_t attr)
|
||||
{
|
||||
int16_t prev = g_text_attr.value;
|
||||
g_text_attr.value = attr;
|
||||
return prev;
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* settextmode — установить ТЕКСТОВЫЙ видеорежим (TEXT_MODE_40x32 /
|
||||
* TEXT_MODE_80x32). Любое другое значение отвергается с EINVAL —
|
||||
* иначе случайный GFX_MODE_* мог бы выдернуть экран из-под текстового
|
||||
* I/O. Возвращает 0 или -1 + errno.
|
||||
*/
|
||||
|
||||
#include <conio.h>
|
||||
#include <stdint.h>
|
||||
#include <errno.h>
|
||||
|
||||
extern int _videomode_raw_set(uint8_t mode);
|
||||
|
||||
int settextmode(uint8_t mode)
|
||||
{
|
||||
/* Отвергаем всё, что не известный текстовый режим. */
|
||||
if (mode != TEXT_MODE_40x32 && mode != TEXT_MODE_80x32) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
return _videomode_raw_set(mode);
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
/*
|
||||
* text_pal_get — прочитать блок записей текстовой палитры;
|
||||
* план 0..3 транслируется в палитровую страницу BIOS 4..7.
|
||||
*/
|
||||
|
||||
#include <conio.h>
|
||||
#include <palette.h>
|
||||
|
||||
void text_pal_get(uint8_t plane, uint8_t start, uint8_t count, uint8_t *data)
|
||||
{
|
||||
pal_get((uint8_t)(plane + 4u), start, count, data);
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
/*
|
||||
* text_pal_get_color — прочитать одну запись текстовой палитры в R/G/B;
|
||||
* план 0..3 транслируется в палитровую страницу BIOS 4..7.
|
||||
*/
|
||||
|
||||
#include <conio.h>
|
||||
#include <palette.h>
|
||||
|
||||
void text_pal_get_color(uint8_t plane, uint8_t attr,
|
||||
uint8_t *r, uint8_t *g, uint8_t *b)
|
||||
{
|
||||
pal_get_color((uint8_t)(plane + 4u), attr, r, g, b);
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
* text_pal_load — загрузить блок записей текстовой палитры.
|
||||
* Текстовый «план» 0..3 (paper/ink/blink-paper/blink-ink) транслируется
|
||||
* в палитровую страницу BIOS 4..7 (см. <palette.h>).
|
||||
*/
|
||||
|
||||
#include <conio.h>
|
||||
#include <palette.h>
|
||||
|
||||
void text_pal_load(uint8_t plane, uint8_t start, uint8_t count,
|
||||
const uint8_t *data)
|
||||
{
|
||||
pal_load((uint8_t)(plane + 4u), start, count, data);
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
/*
|
||||
* text_pal_reset — восстановить системную текстовую палитру
|
||||
* (BIOS $A6, type = PAL_CGA, страницы 4..7).
|
||||
*/
|
||||
|
||||
#include <conio.h>
|
||||
#include <palette.h>
|
||||
|
||||
void text_pal_reset(void)
|
||||
{
|
||||
pal_reset(PAL_CGA);
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
/*
|
||||
* text_pal_set_color — одна запись текстовой палитры из RGB;
|
||||
* план 0..3 транслируется в палитровую страницу BIOS 4..7.
|
||||
*/
|
||||
|
||||
#include <conio.h>
|
||||
#include <palette.h>
|
||||
|
||||
void text_pal_set_color(uint8_t plane, uint8_t attr,
|
||||
uint8_t r, uint8_t g, uint8_t b)
|
||||
{
|
||||
pal_set_color((uint8_t)(plane + 4u), attr, r, g, b);
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
/*
|
||||
* text_palette.c — text-mode palette wrappers.
|
||||
*
|
||||
* Thin layer over libc/video/palette.c. Translates the text "plane
|
||||
* number" 0..3 (paper/ink/blink-paper/blink-ink) into the underlying
|
||||
* BIOS palette page 4..7 used by $A4.
|
||||
*
|
||||
* For an introduction to the four-plane text colour model see
|
||||
* <palette.h> (top-of-file doc-block).
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <conio.h>
|
||||
#include <palette.h>
|
||||
|
||||
void text_pal_load(uint8_t plane, uint8_t start, uint8_t count,
|
||||
const uint8_t *data)
|
||||
{
|
||||
pal_load((uint8_t)(plane + 4u), start, count, data);
|
||||
}
|
||||
|
||||
void text_pal_set_color(uint8_t plane, uint8_t attr,
|
||||
uint8_t r, uint8_t g, uint8_t b)
|
||||
{
|
||||
pal_set_color((uint8_t)(plane + 4u), attr, r, g, b);
|
||||
}
|
||||
|
||||
void text_pal_get(uint8_t plane, uint8_t start, uint8_t count, uint8_t *data)
|
||||
{
|
||||
pal_get((uint8_t)(plane + 4u), start, count, data);
|
||||
}
|
||||
|
||||
void text_pal_get_color(uint8_t plane, uint8_t attr,
|
||||
uint8_t *r, uint8_t *g, uint8_t *b)
|
||||
{
|
||||
pal_get_color((uint8_t)(plane + 4u), attr, r, g, b);
|
||||
}
|
||||
|
||||
void text_pal_reset(void)
|
||||
{
|
||||
pal_reset(PAL_CGA);
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
/*
|
||||
* textattr — заменить атрибут целиком одним байтом (fg | bg | blink).
|
||||
* Turbo-C-стиль. Сбрасывает KEEP_EXIST_ATTR (старший байт = 0).
|
||||
*/
|
||||
|
||||
#include "_conio.h"
|
||||
|
||||
void textattr(uint8_t attr)
|
||||
{
|
||||
g_text_attr.value = (uint16_t)attr;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
/*
|
||||
* textbackground — установить цвет фона (биты 4..6 атрибута),
|
||||
* цвет символов и бит blink сохраняются. Turbo-C-стиль. Попутно
|
||||
* сбрасывает KEEP_EXIST_ATTR (старший байт становится 0).
|
||||
*/
|
||||
|
||||
#include "_conio.h"
|
||||
|
||||
void textbackground(uint8_t bg)
|
||||
{
|
||||
bg = (bg & 0x07) << 4;
|
||||
g_text_attr.value = (g_text_attr.byte.low & 0x0F) | bg;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
/*
|
||||
* textcolor — установить цвет символов (нижний нибл атрибута),
|
||||
* фон и бит blink сохраняются. Turbo-C-стиль. Попутно сбрасывает
|
||||
* KEEP_EXIST_ATTR (старший байт становится 0).
|
||||
*/
|
||||
|
||||
#include "_conio.h"
|
||||
|
||||
void textcolor(uint8_t fg)
|
||||
{
|
||||
fg = (fg & 0x07);
|
||||
g_text_attr.value = (g_text_attr.byte.low & 0xF0) | fg;
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* wherex — текущая колонка курсора (0..79).
|
||||
* Читает курсор BIOS GetCursor (rst 8, $8E), попутно обновляя pc_place.
|
||||
*/
|
||||
|
||||
#include "_conio.h"
|
||||
|
||||
uint8_t wherex(void) __naked
|
||||
{
|
||||
__asm
|
||||
ld c, #0x8e ; BIOS GetCursor: D = row, E = col
|
||||
rst #0x08
|
||||
ld a, e
|
||||
ld (_pc_place), de
|
||||
ret
|
||||
__endasm;
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* wherexy — обе координаты курсора одним вызовом:
|
||||
* (row << 8) | col в DE. Читает курсор BIOS GetCursor (rst 8, $8E),
|
||||
* попутно обновляя pc_place.
|
||||
*/
|
||||
|
||||
#include "_conio.h"
|
||||
|
||||
uint16_t wherexy(void) __naked
|
||||
{
|
||||
__asm
|
||||
ld c, #0x8e ; BIOS GetCursor: D = row, E = col
|
||||
rst #0x08
|
||||
ld (_pc_place), de
|
||||
ret ; __sdcccall(1): uint16_t в DE
|
||||
__endasm;
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* wherey — текущая строка курсора (0..31).
|
||||
* Читает курсор BIOS GetCursor (rst 8, $8E), попутно обновляя pc_place.
|
||||
*/
|
||||
|
||||
#include "_conio.h"
|
||||
|
||||
uint8_t wherey(void) __naked
|
||||
{
|
||||
__asm
|
||||
ld c, #0x8e ; BIOS GetCursor: D = row, E = col
|
||||
rst #0x08
|
||||
ld a, d
|
||||
ld (_pc_place), de
|
||||
ret
|
||||
__endasm;
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* wrchar — записать символ ch с атрибутом attr в позицию (x, y), не
|
||||
* трогая курсор. ESTEX WRCHAR ($58).
|
||||
*
|
||||
* SDCC __sdcccall(1): x в A, y в L (2 uint8 → A, L); ch и attr
|
||||
* упакованы и запушены на стек одной 16-битной парой (вызывающий
|
||||
* делает `ld hl, #(attr<<8)|ch; push hl`). Раскладка после CALL:
|
||||
* [SP+0..1] = адрес возврата
|
||||
* [SP+2] = ch (младшая половина пары)
|
||||
* [SP+3] = attr (старшая половина)
|
||||
* Void-возврат → callee снимает 2 байта стековых аргументов через
|
||||
* `pop bc` + jp (iy).
|
||||
*/
|
||||
|
||||
#include "_conio.h"
|
||||
|
||||
void wrchar(uint8_t x, uint8_t y, char ch, uint8_t attr) __naked
|
||||
{
|
||||
(void)x; (void)y; (void)ch; (void)attr;
|
||||
__asm
|
||||
pop iy ; адрес возврата
|
||||
pop bc ; C = ch, B = attr
|
||||
push ix
|
||||
ld d, l ; D = row (y)
|
||||
ld e, a ; E = col (x)
|
||||
ld a, c ; A = ch
|
||||
ld c, #0x58 ; ESTEX WRCHAR
|
||||
rst #0x10
|
||||
pop ix
|
||||
jp (iy)
|
||||
__endasm;
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* z80_inp — прочитать байт из порта Z80 (опкод IN). Обёртка с
|
||||
* стабильным C API; имя как в Solid-C / MS-DOS Turbo-C.
|
||||
*/
|
||||
|
||||
#include "_conio.h"
|
||||
|
||||
uint8_t z80_inp(uint8_t port) __naked
|
||||
{
|
||||
(void)port;
|
||||
__asm
|
||||
;; SDCC __sdcccall(1): единственный uint8_t аргумент в A; возврат в A.
|
||||
ld c, a
|
||||
in a, (c)
|
||||
ret
|
||||
__endasm;
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* z80_outp — записать байт value в порт Z80 (опкод OUT). Обёртка с
|
||||
* стабильным C API; имя как в Solid-C / MS-DOS Turbo-C.
|
||||
*/
|
||||
|
||||
#include "_conio.h"
|
||||
|
||||
void z80_outp(uint8_t port, uint8_t value) __naked
|
||||
{
|
||||
(void)port; (void)value;
|
||||
__asm
|
||||
;; __sdcccall(1): 2 аргумента uint8 → arg1 в A, arg2 в L.
|
||||
ld c, a ; C = port
|
||||
out (c), l ; out (port), value
|
||||
ret
|
||||
__endasm;
|
||||
}
|
||||
Reference in New Issue
Block a user