diff --git a/libc/conio/conio.c b/libc/conio/conio.c index 0b1373a..6f37196 100644 --- a/libc/conio/conio.c +++ b/libc/conio/conio.c @@ -15,10 +15,18 @@ #include #include + /* Forward extern — definition is further down (after putch/cputs which * reference it from asm by linker-symbol name). */ -extern int16_t g_text_attr; +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 @@ -91,10 +99,6 @@ uint16_t getkey(void) __naked * caller to use "\r\n" explicitly. Stdio puts/putchar do translate. */ -static uint8_t pc_ch = 0; -static uint8_t pc_attr = 0; -static uint8_t pc_row = 0; -static uint8_t pc_col = 0; /* Controls how _raw_putch treats control characters (< 0x20): * 0 (default) — BS/TAB/LF/CR are interpreted (no glyph output); @@ -104,7 +108,6 @@ static uint8_t pc_col = 0; * 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. */ -static uint8_t pc_raw_mode = 0; void set_putch_raw_mode(uint8_t mode) { pc_raw_mode = mode; } uint8_t get_putch_raw_mode(void) { return pc_raw_mode; } @@ -115,14 +118,11 @@ uint8_t get_putch_raw_mode(void) { return pc_raw_mode; } static void _get_cursor(void) __naked { __asm - push ix - ld c, #0x53 ; ESTEX CURSOR - rst #0x10 - ld a, d - ld (_pc_row), a - ld a, e - ld (_pc_col), a - pop ix + ; push ix + ld c, #0x8e ; BIOS GetCursor + rst #0x08 + ld (_pc_place), de + ; pop ix ret __endasm; } @@ -131,14 +131,11 @@ static void _get_cursor(void) __naked static void _set_cursor(void) __naked { __asm - push ix - ld a, (_pc_row) - ld d, a - ld a, (_pc_col) - ld e, a - ld c, #0x52 ; ESTEX LOCATE - rst #0x10 - pop ix + ; push ix + ld de, (_pc_place) + ld c, #0x84 ; BIOS SetCursor + rst #0x08 + ; pop ix ret __endasm; } @@ -183,7 +180,7 @@ static char _bios_putchar(char ch) __naked * 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 _raw_putch_raw0(char ch, uint8_t attr) __naked +static void _putch_wrchar(char ch, uint8_t attr) __naked { (void)ch; (void)attr; __asm @@ -191,6 +188,12 @@ static void _raw_putch_raw0(char ch, uint8_t attr) __naked ;; 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 @@ -201,18 +204,21 @@ static void _raw_putch_raw0(char ch, uint8_t attr) __naked 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_row) + 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_col) + ld a, (_pc_place) cp #80 - ret nc ; off-screen right — silently skip + ret nc ; off-screen right — silently skip ld e, a ; E = col inc a - ld (_pc_col), a ; pc_col++ + ld (_pc_place), a ; pc_col++ + _rp0_wr: ld b, l ; B = attr ld a, c ; A = ch push ix @@ -222,56 +228,61 @@ static void _raw_putch_raw0(char ch, uint8_t attr) __naked ret _rp0_bs: - ld a, (_pc_col) + ld a, (_pc_place) or a, a ret z ; already at col 0 — no change dec a - ld (_pc_col), a - ret + 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_col) - and #0xF8 ; floor to mult of 8 - add a, #8 ; → next mult of 8 - cp #80 - jr c, _rp0_tab_store - ld a, #80 ; cap at off-screen right - _rp0_tab_store: - ld (_pc_col), a + 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_row) + ld a, (_pc_place + 1) cp #32 ret nc ; already at bottom edge inc a - ld (_pc_row), a + ld (_pc_place + 1), a ret _rp0_cr: xor a, a - ld (_pc_col), a + ld (_pc_place), a ret __endasm; } /* Mode-1 worker: every byte goes through WRCHAR as a glyph. */ -static void _raw_putch_raw1(char ch, uint8_t attr) __naked +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_row) + + ld a, (_pc_place + 1) cp #32 ret nc ; off-screen bottom — silently skip ld d, a ; D = row - ld a, (_pc_col) + ld a, (_pc_place) cp #80 - ret nc ; off-screen right — silently skip + ret nc ; off-screen right — silently skip ld e, a ; E = col inc a - ld (_pc_col), a ; pc_col++ + ld (_pc_place), a ; pc_col++ ld b, l ; B = attr ld a, c ; A = ch @@ -283,18 +294,6 @@ static void _raw_putch_raw1(char ch, uint8_t attr) __naked __endasm; } -/* PCHARS (no attr) — used by cputs when KEEP_EXIST_ATTR is in effect. */ -static void _cputs_pchars(const char *s) __naked -{ - (void)s; - __asm - push ix - ld c, #0x5C ; ESTEX PCHARS - rst #0x10 - pop ix - ret - __endasm; -} /* ---- Public putch / cputs --------------------------------------- * * @@ -306,6 +305,7 @@ static void _cputs_pchars(const char *s) __naked * folds the per-char CURSOR/LOCATE pair from the old design into a * single pair per operation. */ + char putch(char ch) __naked { (void)ch; @@ -313,38 +313,26 @@ char putch(char ch) __naked ;; A = ch on entry; char return → A. ld (_pc_ch), a ; stash c (for both return and re-load) - ;; KEEP_EXIST_ATTR? high byte of g_text_attr != 0 - ld a, (_g_text_attr + 1) - or a, a - jr nz, _putch_fast - - ;; --- WRCHAR path: cursor → worker → cursor --- - call __get_cursor - - ;; Worker ABI (2 char/uint8 args): ch in A, attr in L. 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_raw1 - call __raw_putch_raw0 + jr nz, _putch_use_raw + call __putch_wrchar jr _putch_after_raw - _putch_use_raw1: - call __raw_putch_raw1 + _putch_use_raw: + call __putch_wrchar_raw _putch_after_raw: call __set_cursor ld a, (_pc_ch) ; return value ret - _putch_fast: - ld a, (_pc_ch) - call __bios_putchar ; __bios_putchar keeps AF - ret __endasm; } + char cputs(const char *s) __naked { (void)s; @@ -356,60 +344,48 @@ char cputs(const char *s) __naked or a, l ret z - ;; IX is callee-saved under SDCC's z80 ABI. We use it as a - ;; function pointer below (ld ix, #__raw_putch_raw0/1) so save - ;; the caller's value up front and restore before every ret. 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 - ;; --- WRCHAR path: cursor → worker → cursor --- - call __get_cursor - - ;; Worker walks the string via DE; HL is used to carry attr in L - ;; across iterations (RST 10 inside worker clobbers it, so we - ;; push/pop hl around each call). - ex de, hl ; DE = s - ld a, (_g_text_attr) ; A = attr (low byte) - ld l, a ; L = attr (worker ABI: arg2 in L) - - ;; Pick worker once based on pc_raw_mode; IX = function pointer. ld a, (_pc_raw_mode) or a, a - jr z, _cputs_use_raw0 - ld ix, #__raw_putch_raw1 - jr _cputs_loop - _cputs_use_raw0: - ld ix, #__raw_putch_raw0 + jr nz, _cputs_bios - _cputs_loop: - ld a, (de) - or a, a - jr z, _cputs_loop_end - inc de - ;; Z80 has no "call (ix)" — emulate via push-of-ret + jp (ix). - push de ; save string pointer - push hl ; save attr (in L) - ld de, #_cputs_after_worker - push de ; push return address - jp (ix) ; "call" worker - _cputs_after_worker: - pop hl + push hl pop de - jr _cputs_loop - _cputs_loop_end: + + _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 caller's IX - xor a, a ; return 0 - ret - - _cputs_fast: - call __cputs_pchars - pop ix ; restore caller's IX + pop ix ; restore callers IX xor a, a ; return 0 ret __endasm; @@ -446,12 +422,15 @@ void gotoxy(uint8_t x, uint8_t y) __naked __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 c, #0x52 - rst #0x10 - pop ix + ;; 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; } @@ -460,11 +439,14 @@ uint8_t wherex(void) __naked { __asm ;; ESTEX CURSOR ($53): D = row, E = col. Return col in DE. - push ix - ld c, #0x53 - rst #0x10 - pop ix - ld a, e + ;; 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; } @@ -472,11 +454,14 @@ uint8_t wherex(void) __naked uint8_t wherey(void) __naked { __asm - push ix - ld c, #0x53 - rst #0x10 - pop ix - ld a, d + ;; 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; } @@ -485,10 +470,13 @@ uint16_t wherexy(void) __naked { __asm ;; ESTEX CURSOR ($53): D = row, E = col. Return col in DE. - push ix - ld c, #0x53 - rst #0x10 - pop ix + ;; push ix + ld c, #0x8e ; BIOS GetCursor + rst #0x08 + ;; ld c, #0x53 + ;; rst #0x10 + ;; pop ix + ld (_pc_place), de ret __endasm; } @@ -577,12 +565,12 @@ uint16_t rdchar(uint8_t x, uint8_t y) __naked extern uint8_t _videomode_raw_get(void); extern int _videomode_raw_set(uint8_t mode); -uint8_t get_videotextmode(void) +uint8_t gettextmode(void) { return _videomode_raw_get(); } -int set_videotextmode(uint8_t mode) +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. */ @@ -600,18 +588,17 @@ int set_videotextmode(uint8_t mode) * * 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 g_text_attr = 0x0F; int16_t set_text_attr(int16_t attr) { - int16_t prev = g_text_attr; - g_text_attr = 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; + return g_text_attr.value; } /* ---- Turbo-C-style palette helpers -------------------------------- @@ -620,21 +607,21 @@ int16_t get_text_attr(void) void textcolor(uint8_t fg) { - /* If we were KEEP_EXIST_ATTR, switch to a real attr first. */ - uint8_t cur = ((uint16_t)g_text_attr > 0xFF) ? 0x00 : (uint8_t)g_text_attr; - g_text_attr = (int16_t)((cur & 0xF0) | (fg & 0x0F)); + 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) { - uint8_t cur = ((uint16_t)g_text_attr > 0xFF) ? 0x00 : (uint8_t)g_text_attr; - /* Background uses 3 bits (4..6); preserve blink (bit 7) too. */ - g_text_attr = (int16_t)((cur & 0x8F) | ((bg & 0x07) << 4)); + 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 = (int16_t)attr; + g_text_attr.value = (uint16_t)attr; } /* ---- Solid-C compatibility ---------------------------------------- */ diff --git a/libc/include/conio.h b/libc/include/conio.h index d3c57fe..a562a55 100644 --- a/libc/include/conio.h +++ b/libc/include/conio.h @@ -74,12 +74,24 @@ uint16_t getkey(void); #define KEY_PGUP 0x59 #define KEY_INS 0x50 /* numpad 0; not verified */ #define KEY_DEL 0x55 /* numpad 5/.; not verified */ + +typedef union { + uint16_t value; + struct { + uint8_t low; + uint8_t high; + } byte; +} two_bytes; + + + char putch (char c); char cputs (const char *s); int cprintf(const char *fmt, ...); void clrscr(void); void gotoxy(uint8_t x, uint8_t y); + /* Solid-C compatibility helpers. */ #define home() gotoxy(0, 0) #define inp(port) z80_inp(port) @@ -124,8 +136,8 @@ void clrscr_attr(uint8_t attr); #define TEXT_MODE_40x32 0x02 #define TEXT_MODE_80x32 0x03 -uint8_t get_videotextmode(void); -int set_videotextmode(uint8_t mode); /* 0 OK, -1 + errno on bad mode */ +uint8_t gettextmode(void); +int settextmode(uint8_t mode); /* 0 OK, -1 + errno on bad mode */ /* ------------------------------------------------------------------ * * Text-output attribute (used by the conio set: putch / cputs / cprintf). diff --git a/tests/conio2/conio2.c b/tests/conio2/conio2.c index 5d3705b..0740fa1 100644 --- a/tests/conio2/conio2.c +++ b/tests/conio2/conio2.c @@ -1,6 +1,7 @@ #include #include #include /* sleep */ +#include /* * Exercises every conio function: @@ -16,6 +17,8 @@ * (0,0), (79,0), (0,31), (79,31). */ +extern two_bytes pc_place; + int main(void) { /* 1. clrscr + diagnostic probes + a centered banner. @@ -27,9 +30,16 @@ int main(void) */ clrscr(); - gotoxy(0, 0); putch('0'); + gotoxy(1, 0); + textattr(0x07); + putch('0'); textattr(0x70); - gotoxy(1, 0); putch('1'); + putch('1'); + // gotoxy(10, 20); + printf("X = %u, Y = %u\n", pc_place.byte.low, pc_place.byte.high); + uint8_t x = wherex(); + uint8_t y = wherey(); + printf("X = %u, Y = %u\n", x, y); set_putch_raw_mode(1); @@ -51,11 +61,23 @@ int main(void) } } + set_text_attr(0x011D); set_putch_raw_mode(0); gotoxy(0,22); - set_text_attr(0x000E); -// cputs("Test message line"); - cputs("Test message line 1\nTest message line2\rline3\n\r\n\r1234\t5678\b90"); + cputs("Test message line 1\nTest message line2\rline3\n\r1234\t5678\b90\n\r"); + + set_putch_raw_mode(1); + gotoxy(0,25); + cputs("Test message line 1\nTest message line2\rline3\n\r1234\t5678\b90\n\r"); + + set_text_attr(0x001D); + set_putch_raw_mode(0); + gotoxy(0,28); + cputs("Test message line 1\nTest message line2\rline3\n\r1234\t5678\b90\n\r"); + + set_putch_raw_mode(1); + gotoxy(0,31); + cputs("Test message line 1\nTest message line2\rline3\n\r1234\t5678\b90\n\r"); (void)getch();