Add mdview markdown viewer, reorganize tests/examples and libc layout

- Split tests/ (libc feature tests) and examples/ (real apps); shared
  app.mk in repo root, was examples/example.mk
- libc/io/* split into libc/{conio,env,errno,file,mouse,string,sys,
  time,video}/ — clearer module boundaries
- New examples/mdview/: markdown viewer (Phases 1-5 + light nested
  lists). Headers (H1-H4), HR, ulist/olist/quote with nesting via
  leading spaces, fenced code blocks, inline emphasis (bold/italic/
  underscore/code), wrap/unwrap mode with soft wrap (F2), horizontal
  pan (← →) with '>' truncation indicator
- libc additions: scroll() in conio (ESTEX SCROLL), strlwr/strupr,
  gets() test
- Makefile updates across tests/ for the new shared app.mk path

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-06-04 22:23:36 +03:00
parent b851e22fa6
commit 737c974400
104 changed files with 2485 additions and 223 deletions
-5
View File
@@ -1,5 +0,0 @@
# Build argv_test.exe — uses lib/sprinter.lib in TINY memory mode.
PROJ_ROOT := $(abspath $(CURDIR)/../..)
EXAMPLE := argv
include $(PROJ_ROOT)/examples/example.mk
-22
View File
@@ -1,22 +0,0 @@
#include <stdio.h>
/*
* Prints every command-line argument, then exits.
*
* Try in MAME / ESTEX shell:
* ARGV_TES -> argc=1, argv[0] = ""
* ARGV_TES one two three -> argc=4, argv[1..3] = "one","two","three"
* ARGV_TES spaced args -> multiple spaces are squashed
*/
int main(int argc, char **argv)
{
printf("argc = %d\n", argc);
for (int i = 0; i < argc; i++) {
printf("argv[%d] = \"%s\"\n", i, argv[i]);
}
puts("");
puts("Press any key to exit...");
(void)getchar();
return 0;
}
-3
View File
@@ -1,3 +0,0 @@
PROJ_ROOT := $(abspath $(CURDIR)/../..)
EXAMPLE := assrtest
include $(PROJ_ROOT)/examples/example.mk
-43
View File
@@ -1,43 +0,0 @@
/*
* assrtest — exercise <assert.h> from SDCC's z80.lib.
*
* Tests both the disabled-debug (NDEBUG) form and the active form.
* The failing assert at the end prints an error message via printf
* (which routes through our putchar) and then halts.
*/
#include <stdio.h>
#include <assert.h>
static int divide(int a, int b)
{
assert(b != 0); /* must succeed for the test cases */
return a / b;
}
int main(int argc, char **argv)
{
(void)argc; (void)argv;
puts("assrtest: exercising <assert.h>");
/* These pass. */
int q = divide(10, 2);
printf(" divide(10, 2) = %d\n", q);
assert(q == 5);
q = divide(-9, 3);
printf(" divide(-9, 3) = %d\n", q);
assert(q == -3);
puts(" all passing asserts OK");
/* Final, failing assert. Prints "Assert(...) failed in function ..."
* then enters an infinite loop (SDCC __assert convention). */
puts(" triggering deliberate failing assert (system will hang)...");
int zero = 0;
(void)divide(1, zero);
/* unreachable */
return 0;
}
-5
View File
@@ -1,5 +0,0 @@
# Build attr_probe.exe — uses lib/sprinter.lib in TINY memory mode.
PROJ_ROOT := $(abspath $(CURDIR)/../..)
EXAMPLE := attrprob
include $(PROJ_ROOT)/examples/example.mk
-79
View File
@@ -1,79 +0,0 @@
#include <stdio.h>
#include <conio.h>
#include <stdint.h>
/*
* attr_probe — visualise every value of the ESTEX attribute byte.
*
* Layout:
* row 2 16 cells: low nibble 0..F, high nibble 0 (varying fg)
* row 4 16 cells: low nibble 7, high nibble 0..F (varying bg)
* row 6 bit-7 toggle test: 8 pairs comparing N with N|0x80
* row 8 full 16x16 grid: bg outer, fg inner — every attribute byte
* row 26 RDCHAR readback of a known cell
*
* Each cell is drawn with wrchar() so the attribute is set exactly,
* independent of ESTEX's hidden "current" attribute used by PCHARS.
*/
int main(void)
{
clrscr();
gotoxy(20, 0); cputs("== attr probe ==");
/* Row 2: vary fg (low nibble), bg = 0 (black). */
gotoxy(0, 2); cputs("fg 0..F /bg0:");
for (int i = 0; i < 16; i++) {
wrchar(15 + i, 2, 'A' + i, (uint8_t)i);
}
/* Row 4: vary bg (high nibble), fg = 7 (light gray). */
gotoxy(0, 4); cputs("bg 0..F /fg7:");
for (int i = 0; i < 16; i++) {
wrchar(15 + i, 4, 'A' + i, (uint8_t)((i << 4) | 0x07));
}
/* Row 6: compare attr N with N|0x80 — bit 7 may be blink or extra bg. */
gotoxy(0, 6); cputs("bit7: N|N|0x80 pairs (1F,2E,3D,4C,5B,6A,79,F0):");
{
static const uint8_t probes[] = { 0x1F, 0x2E, 0x3D, 0x4C,
0x5B, 0x6A, 0x79, 0xF0 };
for (int i = 0; i < 8; i++) {
wrchar(2 + i*4, 7, '*', probes[i]);
wrchar(2 + i*4 + 1, 7, '*', probes[i] | 0x80);
/* hex labels under each pair */
gotoxy(2 + i*4, 8);
printf("%02X", probes[i]);
}
}
/* Row 10..25: full 16x16 grid of every attr byte 0x00..0xFF.
* Cell (col, row) = (col_idx + offset, row_idx + 10) holds char '#'
* with attr = (row_idx << 4) | col_idx. */
gotoxy(0, 10); cputs("Full 16x16 grid (row=bg, col=fg):");
for (int row = 0; row < 16; row++) {
for (int col = 0; col < 16; col++) {
uint8_t attr = (uint8_t)((row << 4) | col);
wrchar(20 + col, 11 + row, '#', attr);
}
/* label rows */
gotoxy(15, 11 + row);
printf("%X:", row);
}
gotoxy(20, 27);
cputs("0123456789ABCDEF (fg)");
/* RDCHAR readback test. */
{
uint16_t got = rdchar(15, 2);
gotoxy(0, 29);
printf("rdchar(15,2) -> ch=0x%02X attr=0x%02X (expected 'A'=0x41 attr=0x00)",
got & 0xFF, (got >> 8) & 0xFF);
}
gotoxy(0, 31);
cputs("press any key");
getch();
return 0;
}
-11
View File
@@ -1,11 +0,0 @@
# Build banked.exe — HUGE memory mode (small + banked code in W3).
#
# Layout:
# CODE/HOME at 0x4100 (W1), DATA at 0x8000 (W2), banks at 0x{N}C000 (W3).
# crt0_banked.s loads N banks from the .EXE before calling main.
PROJ_ROOT := $(abspath $(CURDIR)/../..)
EXAMPLE := banked
MEMORY := huge
EXTRA_FLAGS := --bank 1=bank1.c --bank 2=bank2.c
include $(PROJ_ROOT)/examples/example.mk
-17
View File
@@ -1,17 +0,0 @@
#include <stdio.h>
#include <stdint.h>
#include <sprinter.h>
void bank1_func(int x) __banked
{
(void)x;
puts("BANK1: hello from a banked function!");
puts("BANK1: window 3 phys page = ");
hex8(_io_page_w3); /* should be BANK1's phys page */
putchar('\n');
putchar('1');
putchar('=');
putchar('0' + (x / 10) % 10);
putchar('0' + x % 10);
putchar('\n');
}
-18
View File
@@ -1,18 +0,0 @@
#include <stdio.h>
#include <stdint.h>
#include <sprinter.h>
void bank2_func(int x) __banked
{
(void)x;
puts("BANK2: hello from the second bank!");
puts("BANK2: window 3 phys page = ");
hex8(_io_page_w3); /* should be BANK2's phys page */
putchar('\n');
putchar('2');
putchar('=');
putchar('0' + (x / 100) % 10);
putchar('0' + (x / 10) % 10);
putchar('0' + x % 10);
putchar('\n');
}
-47
View File
@@ -1,47 +0,0 @@
#include <stdio.h>
#include <stdint.h>
#include <sprinter.h>
/* crt0_banked.s reads this constant to know how many banks to load. */
const uint8_t n_banks = 2;
void bank1_func(int x) __banked;
void bank2_func(int x) __banked;
extern uint8_t bank_pages[]; /* filled by crt0_banked.s */
static void show_w3(const char *who)
{
puts(who);
hex8(_io_page_w3); /* current physical page mapped in window 3 */
putchar('\n');
}
int main(void)
{
puts("HOME: program start.");
#ifdef DEBUG_RT
printf("HOME: w2_self_allocated = %u\n", w2_self_allocated);
#endif
show_w3("HOME: window 3 phys page = ");
puts("HOME: bank_pages[1] (BANK1 phys) = ");
hex8(bank_pages[1]);
putchar('\n');
puts("HOME: bank_pages[2] (BANK2 phys) = ");
hex8(bank_pages[2]);
putchar('\n');
puts("HOME: calling bank1_func(42)...");
bank1_func(42);
show_w3("HOME: back, window 3 phys = ");
puts("HOME: calling bank2_func(123)...");
bank2_func(123);
show_w3("HOME: back, window 3 phys = ");
puts("HOME: done.");
puts("Press any key to exit...");
(void)getchar();
return 0;
}
-12
View File
@@ -1,12 +0,0 @@
# Build banked_big.exe — BIG memory mode (tiny + banked code in W1).
#
# Layout:
# CODE/HOME at 0x8100 (W2), DATA chained after CODE (W2)
# Banks at virtual 0x{N}4000 — physical pages mapped into W1
# crt0_banked.s + bank.s built with BANK_W1=1 by sprinter-cc
PROJ_ROOT := $(abspath $(CURDIR)/../..)
EXAMPLE := bankedbg
MEMORY := big
EXTRA_FLAGS := --bank 1=bank1.c --bank 2=bank2.c
include $(PROJ_ROOT)/examples/example.mk
-17
View File
@@ -1,17 +0,0 @@
#include <stdio.h>
#include <stdint.h>
#include <sprinter.h>
void bank1_func(int x) __banked
{
(void)x;
puts("BANK1: hello from a banked function (W1)!");
puts("BANK1: window 1 phys page = ");
hex8(_io_page_w1); /* should be BANK1's phys page */
putchar('\n');
putchar('1');
putchar('=');
putchar('0' + (x / 10) % 10);
putchar('0' + x % 10);
putchar('\n');
}
-18
View File
@@ -1,18 +0,0 @@
#include <stdio.h>
#include <stdint.h>
#include <sprinter.h>
void bank2_func(int x) __banked
{
(void)x;
puts("BANK2: hello from the second bank (W1)!");
puts("BANK2: window 1 phys page = ");
hex8(_io_page_w1); /* should be BANK2's phys page */
putchar('\n');
putchar('2');
putchar('=');
putchar('0' + (x / 100) % 10);
putchar('0' + (x / 10) % 10);
putchar('0' + x % 10);
putchar('\n');
}
-58
View File
@@ -1,58 +0,0 @@
/*
* banked_big — BIG memory mode: base CODE+DATA in W2, banked code in W1.
*
* Layout:
* _CODE at 0x8100 (W2), DATA chained after CODE (W2)
* _BANK1 / _BANK2 at virtual 0x{N}4000 — physical page mapped into W1
* trampoline ___sdcc_bcall_ehl swaps port 0xA2 (W1) to call bank funcs
*
* Mirrors examples/banked (HUGE mode) for direct comparison.
*/
#include <stdio.h>
#include <stdint.h>
#include <sprinter.h>
/* crt0_banked.s reads this constant to know how many banks to load. */
const uint8_t n_banks = 2;
void bank1_func(int x) __banked;
void bank2_func(int x) __banked;
extern uint8_t bank_pages[]; /* filled by crt0_banked.s */
static void show_w1(const char *who)
{
puts(who);
hex8(_io_page_w1); /* current physical page mapped in window 1 */
putchar('\n');
}
int main(void)
{
puts("HOME: program start (BIG mode).");
#ifdef DEBUG_RT
printf("HOME: w2_self_allocated = %u\n", w2_self_allocated);
#endif
show_w1("HOME: window 1 phys page = ");
puts("HOME: bank_pages[1] (BANK1 phys) = ");
hex8(bank_pages[1]);
putchar('\n');
puts("HOME: bank_pages[2] (BANK2 phys) = ");
hex8(bank_pages[2]);
putchar('\n');
puts("HOME: calling bank1_func(42)...");
bank1_func(42);
show_w1("HOME: back, window 1 phys = ");
puts("HOME: calling bank2_func(123)...");
bank2_func(123);
show_w1("HOME: back, window 1 phys = ");
puts("HOME: done.");
puts("Press any key to exit...");
(void)getchar();
return 0;
}
-12
View File
@@ -1,12 +0,0 @@
# bank_local_data — code AND static data live in BANK1.
#
# HUGE mode: HOME at 0x4100 (W1), DATA at 0x8000 (W2), BANK1 in W3.
# sprinter-cc compiles bank1.c with --codeseg/--constseg/--dataseg BANK1
# automatically (via --bank 1=bank1.c). mkexe gets `-p 0` so BSS pages
# in the bank land as zero-filled — no runtime zeroing needed.
PROJ_ROOT := $(abspath $(CURDIR)/../..)
EXAMPLE := banklocl
MEMORY := huge
EXTRA_FLAGS := --bank 1=bank1.c --mkexe -p --mkexe 0
include $(PROJ_ROOT)/examples/example.mk
-85
View File
@@ -1,85 +0,0 @@
/*
* bank1.c — code, const tables AND writable static data all live in the
* BANK1 page mapped into window 3 while these functions run.
*
* Compile flags (see Makefile):
* --codeseg BANK1 functions go to _BANK1 area
* --constseg BANK1 `static const` tables go to _BANK1
* --dataseg BANK1 uninitialized data (BSS) goes to _BANK1
*
* mkexe -p 0 zero-pads the bank's image, so any BSS bytes that don't
* appear in the IHX file are 0x00 when the bank is loaded — free zero
* init for `uint8_t buf[N];`.
*
* Note: a CRITICAL trampoline fix is required (see memory/sdcc_banking)
* — the old bcall used `pop af; out (n), a` which clobbered the uint8_t
* return value. Without that fix, banked functions returning a byte
* would silently return the saved page number instead.
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
uint8_t bank1_buf[16];
static const uint16_t bank1_tab[5] = { 100, 200, 300, 400, 500 };
void bank1_fill(int seed) __banked
{
uint8_t *p = bank1_buf;
for (int i = 0; i < 16; i++)
p[i] = (uint8_t)(seed + i);
}
uint8_t bank1_peek(int idx) __banked
{
uint8_t *p = bank1_buf;
if (idx < 0 || idx >= 16) return 0xFF;
return p[idx];
}
uint16_t bank1_sumtab(void) __banked
{
uint16_t s = 0;
for (int i = 0; i < 5; i++) s += bank1_tab[i];
return s;
}
void bank1_map_print(void) __banked
{
printf( "bank1_buf = %p\n", bank1_buf);
printf( "bank1_tab = %p\n", bank1_tab);
printf( "bank1_fill = %p\n", bank1_fill);
printf( "bank1_peek = %p\n", bank1_peek);
printf( "bank1_sumtab = %p\n", bank1_sumtab);
}
/*
* malloc-from-banked test.
*
* SDCC's heap lives in HOME RAM (W2 = 0x8000-0xBFFF, see runtime/heap.s).
* Window 2 is fixed and stays mapped regardless of W3 page swaps. So
* malloc() called from a banked function should:
* 1. Return a pointer somewhere in 0x80xx..0xBFxx (W2 heap)
* 2. The pointer is usable both inside the bank (W2 still mapped) and
* after returning to HOME (W2 unchanged across trampoline).
*
* The function allocates N bytes, fills them with a known pattern, and
* returns the pointer to the caller for verification.
*/
uint8_t *bank1_alloc_and_fill(int n, uint8_t pattern) __banked
{
uint8_t *p = (uint8_t *)malloc(n);
if (p == NULL) return NULL;
for (int i = 0; i < n; i++)
p[i] = (uint8_t)(pattern + i);
printf(" bank1_alloc_and_fill: malloc(%d) -> %p\n", n, p);
return p;
}
void bank1_free(void *p) __banked
{
free(p);
}
-87
View File
@@ -1,87 +0,0 @@
/*
* bank_local_data — demonstrates that a banked function can own its own
* static (BSS) array and a const lookup table without burning HOME
* memory. When the function executes, BANK1 is mapped into window 3
* and both code and data are directly addressable.
*
* No bank_read / bank_write needed — the trampoline maps the bank for
* us and the C code touches the arrays normally.
*/
#include <stdio.h>
#include <stdint.h>
extern void bank1_fill (int seed) __banked;
extern uint16_t bank1_sumtab (void) __banked;
extern uint8_t bank1_peek (int idx) __banked;
extern void bank1_map_print(void) __banked;
extern uint8_t *bank1_alloc_and_fill(int n, uint8_t pattern) __banked;
extern void bank1_free (void *p) __banked;
/* MUST be `const`: crt0_banked reads _n_banks BEFORE gsinit, so
* non-const initialized variables haven't been copied yet and would
* read 0 → bank loading is skipped → garbage in window 3. */
const uint8_t n_banks = 1;
uint8_t local0_buf[16];
int main(int argc, char **argv)
{
puts("bank_local_data: testing static data in BANK1");
bank1_map_print();
/* Verify crt0_banked parsed argv correctly. */
printf("argc=%d, argv[0]=\"%s\"\n", argc, argv[0]);
printf("local0_buf = %p\n", local0_buf);
for (int i = 1; i < argc; i++)
printf(" argv[%d]=\"%s\"\n", i, argv[i]);
/* Seed the BSS array in BANK1. */
bank1_fill(0x10);
/* Read it back through another banked function — verifies the data
* survived after the trampoline returned to HOME and re-entered. */
int ok = 1;
for (int i = 0; i < 8; i++) {
uint8_t v = bank1_peek(i);
uint8_t expected = (uint8_t)(0x10 + i);
if (v != expected) {
printf(" bank1_buf[%d] = 0x%02X, expected 0x%02X\n",
i, v, expected);
ok = 0;
}
}
if (ok)
puts(" buf[0..7] OK");
/* Use the const lookup table that lives in BANK1. */
uint16_t sum = bank1_sumtab();
printf(" bank1_sumtab() = %u (expected 1500)\n", sum);
/* malloc from a banked function — the heap lives in W2 (HOME),
* which stays mapped across W3 page swaps. Returned pointer
* should be usable both inside the bank AND from HOME afterwards. */
uint8_t *heap_p = bank1_alloc_and_fill(32, 0xA0);
if (heap_p == NULL) {
puts(" malloc from bank FAILED");
} else {
printf(" caller sees pointer = %p\n", heap_p);
int ok = 1;
for (int i = 0; i < 32; i++) {
uint8_t expected = (uint8_t)(0xA0 + i);
if (heap_p[i] != expected) {
printf(" heap_p[%d] = 0x%02X, expected 0x%02X\n",
i, heap_p[i], expected);
ok = 0;
}
}
if (ok) puts(" bank-allocated heap [0..31] OK from HOME");
bank1_free(heap_p);
}
puts("done");
return 0;
}
-6
View File
@@ -1,6 +0,0 @@
# Build cat.exe — uses lib/sprinter.lib in TINY memory mode.
PROJ_ROOT := $(abspath $(CURDIR)/../..)
EXAMPLE := cat
EXTRA_DATA := test.txt
include $(PROJ_ROOT)/examples/example.mk
-31
View File
@@ -1,31 +0,0 @@
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
int main(void)
{
int fd = open("TEST.TXT", O_RDONLY);
if (fd < 0) {
puts("cat: cannot open TEST.TXT");
puts("Press any key to exit...");
(void)getchar();
return 1;
}
puts("--- TEST.TXT ---");
char buf[64];
int n;
while ((n = read(fd, buf, sizeof(buf))) > 0) {
for (int i = 0; i < n; i++) {
putchar((unsigned char)buf[i]);
}
}
close(fd);
puts("");
puts("--- end of file. Press any key to exit. ---");
(void)getchar();
return 0;
}
-13
View File
@@ -1,13 +0,0 @@
Hello from a file on disk!
This text was read by cat.exe through
the ESTEX file I/O syscalls:
open ($11)
read ($13)
close ($12)
The whole loop hits real DSS code on
the Sprinter (in MAME, just an image).
Line 9.
Line 10.
Final line.
-5
View File
@@ -1,5 +0,0 @@
# Build conio_test.exe — uses lib/sprinter.lib in TINY memory mode.
PROJ_ROOT := $(abspath $(CURDIR)/../..)
EXAMPLE := conio
include $(PROJ_ROOT)/examples/example.mk
-128
View File
@@ -1,128 +0,0 @@
#include <stdio.h>
#include <conio.h>
#include <unistd.h> /* sleep */
/*
* Exercises every conio function:
* clrscr — clear the screen
* gotoxy — move cursor to (x, y), 0-based
* putch — single character
* cputs — string without auto-newline
* kbhit — non-blocking key probe
* getch — blocking, no echo
* getche — blocking, with echo
*
* Sprinter native text mode is 80×32, so we anchor corners at
* (0,0), (79,0), (0,31), (79,31).
*/
int main(void)
{
/* 1. clrscr + diagnostic probes + a centered banner.
*
* Row-0 probe: figure out why [NW]/[NE] don't paint.
* '0','1','2','3' — single putch at row 0, cols 0/5/40/79
* "R0" — cputs at row 0, col 10
* 'a','b','c','R1'— same pattern on row 1 (control)
*/
clrscr();
gotoxy(0, 0); putch('0');
gotoxy(5, 0); putch('1');
gotoxy(40, 0); putch('2');
gotoxy(79, 0); putch('3');
gotoxy(10, 0); cputs("R0");
gotoxy(0, 1); putch('a');
gotoxy(5, 1); putch('b');
gotoxy(79, 1); putch('c');
gotoxy(10, 1); cputs("R1");
/* Bottom corners moved to row 30 to avoid the auto-wrap-scroll issue —
* cputs("[SE]") at (76, 31) ends with cursor at col 80 on the last row,
* which scrolls the screen up by one and eats everything we wrote at
* row 0. By staying on row 30, no wraparound is triggered. */
gotoxy(0, 30); cputs("[SW]");
gotoxy(76, 30); cputs("[SE]");
/* Row 31 single-char probes (col 0 + col 78, no col-80 reach). */
gotoxy(0, 31); putch('L');
gotoxy(78, 31); putch('R');
gotoxy(30, 4);
cputs("== conio test ==");
/* wherex/wherey probe — place cursor at a known spot, read it back. */
gotoxy(15, 5);
int cx = wherex();
int cy = wherey();
gotoxy(0, 6);
printf("wherex/wherey after gotoxy(15,5) -> x=%d y=%d", cx, cy);
/* 2. putch — single chars in a diagonal. */
for (int i = 0; i < 16; i++) {
gotoxy(10 + i, 8 + i);
putch('*');
}
/* 3. kbhit polling loop with a spinning indicator. */
gotoxy(0, 14);
cputs("kbhit polling - press any key to interrupt");
static const char spin[] = "|/-\\";
int frame = 0;
while (!kbhit()) {
gotoxy(50, 14);
putch(spin[frame & 3]);
frame++;
/* small delay so the spin is visible (sleep is in 1-sec units;
* we want ~10 fps — implemented with a busy "halts" later, but
* here we just rely on kbhit being slow enough to be visible). */
}
int probed = kbhit(); /* re-read to clear / display */
gotoxy(0, 16);
printf("kbhit captured 0x%02X ('%c')", probed,
(probed >= 0x20 && probed < 0x7F) ? probed : '?');
/* 4. getch — drain the key + ask for one with no echo. */
gotoxy(0, 18);
cputs("getch (no echo) — press a key: ");
int k1 = getch();
gotoxy(0, 19);
printf("you pressed 0x%02X ('%c')", k1,
(k1 >= 0x20 && k1 < 0x7F) ? k1 : '?');
/* 5. getche — same but the keypress should appear on screen. */
gotoxy(0, 21);
cputs("getche (with echo) — press a key: ");
gotoxy(36, 21);
int k2 = getche();
gotoxy(0, 22);
printf("you pressed 0x%02X (echoed above)", k2);
/* 6. Finish at the bottom; do NOT scroll. */
gotoxy(0, 28);
cputs("All conio functions exercised. Press any key to exit.");
set_videotextmode(TEXT_MODE_40x32);
for (int i = 0; i < 16; i++) {
wrchar(4 + i * 2, 3, i < 10 ? '0' + i : 'A' - 10 + i, 0x1F);
wrchar(4 + i * 2 + 1, 3, ' ', 0x1F);
}
for (int j = 0; j < 16; j++) {
wrchar(2, 4 + j, j < 10 ? '0' + j : 'A' - 10 + j, 0x1F);
wrchar(2 + 1, 4 + j, ' ', 0x1F);
for (int i = 0; i < 16; i++) {
wrchar(4 + i * 2, 4 + j, j * 16 + i, 0x2F);
wrchar(4 + i * 2 + 1, 4 + j, ' ', 0x2F);
}
}
(void)getch();
set_videotextmode(TEXT_MODE_80x32);
return 0;
}
-31
View File
@@ -1,31 +0,0 @@
Задача -
меняем алгоритм работы putch (и соответственно остальных ф-ий работающих через putch - cputs/cprintf и т.д.) -
вводится еще одна глобальная переменная (с сеттером и геттером) - char -
как обрабатываются управляющие символы ф-иями вывода в conio - выводятся через wrchar или отрабатываются как управляющие
если она 0 - то отрабатваются как управляющие -
это символы
0x08 - BS - уменьшаем pc_col на 1 если она еще не равна 0 (если 0 - не меняется)
0x09 - TAB - увеличиваем pc_col до ближайшего кратного 8-ми
0x0A - LF - увеличиваем pc_row на 1 кроме случая когда pc_row = 32 - тогда уже не увеличиваем
0x0D - CR - сбрасываем pc_col до 0
в остальных случаях после вывода символа увеличиваем pc_col на 1 (но не более чем до 80)
если же глобальная переменная 1 - то выводим ВСЕ символы as is через wrchar так же увеличивая pc_col на 1 (но не более чем до 80)
Замечание - если pc_col == 80 или pc_row == 32 - можем просто не выводить символ
(символы выводятся только если координаты X / Y = [0..79]/[0..31])
такой режим только если attr <= 0x00FF, в противном случае - вывод через быстрый вывод без управления координат и цветом
для ускорения предлагается создать raw putch который будет принимать в параметрах только символ и аттрибут.
этой функции уже не надо будет проверять старший байт аттрибутов (а в аттрибуты будет принимать младший байт)
этот raw putch всегда будет выводить через WRCHAR (но будет отрабатывать новый параметр - как отрабатывать управляющие сигналы)
обычный putch вначале будет проверять attr что бы понять выводить через PUTCHAR (5B) или WRCHAR (58 - raw putch).
аналогично cputs - вначале будет проверять attr что бы понять - выводить через PCHARS (5C - _cputs_pchars) или через WRCHAR (58 - raw putch)
так же перед выводом и cputs и putcg будут получать текущие координаты для вывода и сохрнять их (если вывод идет через WRCHAR)
то есть еще один пункт для оптимизации - мы должны только один раз получить кооринаты для вывода и один раз их сохранить
(если вывод через WRCHAR, если же через PUTCHAR/PCHARS - то за координаты отвечает уже сам ESTEX).
-5
View File
@@ -1,5 +0,0 @@
# Build conio_test.exe — uses lib/sprinter.lib in TINY memory mode.
PROJ_ROOT := $(abspath $(CURDIR)/../..)
EXAMPLE := conio2
include $(PROJ_ROOT)/examples/example.mk
-63
View File
@@ -1,63 +0,0 @@
#include <stdio.h>
#include <conio.h>
#include <unistd.h> /* sleep */
/*
* Exercises every conio function:
* clrscr — clear the screen
* gotoxy — move cursor to (x, y), 0-based
* putch — single character
* cputs — string without auto-newline
* kbhit — non-blocking key probe
* getch — blocking, no echo
* getche — blocking, with echo
*
* Sprinter native text mode is 80×32, so we anchor corners at
* (0,0), (79,0), (0,31), (79,31).
*/
int main(void)
{
/* 1. clrscr + diagnostic probes + a centered banner.
*
* Row-0 probe: figure out why [NW]/[NE] don't paint.
* '0','1','2','3' — single putch at row 0, cols 0/5/40/79
* "R0" — cputs at row 0, col 10
* 'a','b','c','R1'— same pattern on row 1 (control)
*/
clrscr();
gotoxy(0, 0); putch('0');
textattr(0x70);
gotoxy(1, 0); putch('1');
set_putch_raw_mode(1);
textattr(0x0F);
gotoxy(4, 3);
for (int i = 0; i < 16; i++) {
putch(i < 10 ? '0' + i : 'A' - 10 + i);
putch(' ');
}
for (int j = 0; j < 16; j++) {
gotoxy(2, 4 + j);
textattr(0x0F);
putch(j < 10 ? '0' + j : 'A' - 10 + j);
putch(' ');
for (int i = 0; i < 16; i++) {
textattr((j << 4) + i);
putch((j << 4) + i); putch(' ');
}
}
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");
(void)getch();
return 0;
}
-3
View File
@@ -1,3 +0,0 @@
PROJ_ROOT := $(abspath $(CURDIR)/../..)
EXAMPLE := dec_test
include $(PROJ_ROOT)/examples/example.mk
-39
View File
@@ -1,39 +0,0 @@
#include <stdio.h>
extern void dec8 (unsigned char);
extern void dec16(unsigned int);
extern void dec32(unsigned long);
extern void hex8 (unsigned char);
extern void hex16(unsigned int);
extern void hex32(unsigned long);
int main(void) {
puts("hex8:");
hex8(0x00); puts("");
hex8(0x5A); puts("");
hex8(0xFF); puts("");
puts("hex16:");
hex16(0x0000); puts("");
hex16(0xCAFE); puts("");
hex16(0xFFFF); puts("");
puts("hex32:");
hex32(0x00000000UL); puts("");
hex32(0xDEADBEEFUL); puts("");
hex32(0xFFFFFFFFUL); puts("");
puts("dec8:");
dec8(0); puts("");
dec8(42); puts("");
dec8(255); puts("");
puts("dec16:");
dec16(0); puts("");
dec16(1234); puts("");
dec16(65535); puts("");
puts("dec32:");
dec32(0UL); puts("");
dec32(98765UL); puts("");
dec32(123456UL); puts("");
dec32(123456789UL); puts("");
dec32(4294967295UL); puts("");
puts("done — any key");
(void)getchar();
return 0;
}
-5
View File
@@ -1,5 +0,0 @@
# Build errno_test.exe — uses lib/sprinter.lib in TINY memory mode.
PROJ_ROOT := $(abspath $(CURDIR)/../..)
EXAMPLE := errno
include $(PROJ_ROOT)/examples/example.mk
-50
View File
@@ -1,50 +0,0 @@
#include <stdio.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
/*
* Exercises errno + strerror + perror by triggering each failure path.
* No file system mutations are required — every call here is expected
* to fail by design.
*/
int main(void)
{
puts("errno test:");
puts("");
/* 1. Open a file that does not exist. */
errno = 0;
int fd = open("NOSUCH.TXT", O_RDONLY);
printf(" open(\"NOSUCH.TXT\"): fd=%d errno=%d %s\n",
fd, errno, strerror(errno));
/* 2. Close a bogus file descriptor. */
errno = 0;
int r = close(0x7F);
printf(" close(0x7F): ret=%d errno=%d %s\n", r, errno, strerror(errno));
/* 3. perror() — convenience wrapper. */
errno = 0;
fd = open("ANOTHER.MIS", O_RDONLY);
perror(" open(\"ANOTHER.MIS\")");
/* 4. Delete something that isn't there. */
errno = 0;
r = unlink("PHANTOM.TMP");
perror(" unlink(\"PHANTOM.TMP\")");
/* 5. A few raw codes for the table. */
puts("");
puts(" raw codes:");
for (int i = 0; i <= 5; i++) {
printf(" %2d → %s\n", i, strerror(i));
}
printf(" %2d → %s (out of range)\n", 99, strerror(99));
puts("");
puts("Press any key to exit.");
(void)getchar();
return 0;
}
-76
View File
@@ -1,76 +0,0 @@
# example.mk — shared Makefile fragment for simple Sprinter ESTEX examples.
#
# Usage in an example's Makefile:
#
# PROJ_ROOT := $(abspath $(CURDIR)/../..)
# EXAMPLE := my_example # base name (matches my_example.c)
#
# # Optional overrides (any combination):
# # MEMORY := small # tiny | small | big | huge | manual
# # STACK_SIZE := 2048 # bytes reserved for the stack
# # EXTRA_SRCS := helper.c util.c # additional .c files in this dir
# # EXTRA_FLAGS := --crt0=minimal # passed through to sprinter-cc
# # EXTRA_DATA := test.txt # extra files to add to `make floppy`
#
# include $(PROJ_ROOT)/examples/example.mk
#
# Pipeline (all driven by sprinter-cc):
# crt0 + EXAMPLE.c + EXTRA_SRCS --sdcc--> .ihx
# .ihx --mkexe--> EXAMPLE.exe
# linking against lib/sprinter.lib for libc.
#
# Targets provided:
# all build $(EXAMPLE).exe (default)
# clean remove build artefacts
# floppy build the example and pack it (alone, plus EXTRA_DATA) into
# mame/v306/IMG/mc.img — useful for trying a single program
# without rebuilding every example. Top-level `make floppy`
# (in the repo root) still packs all examples.
# run floppy + launch MAME
SPRINTER_CC := $(PROJ_ROOT)/bin/sprinter-cc
MKEXE := $(PROJ_ROOT)/toolchain/mkexe/mkexe
LIB := $(PROJ_ROOT)/lib/sprinter.lib
MAME_DIR := $(PROJ_ROOT)/mame/v306
FLOPPY_IMG := $(MAME_DIR)/IMG/mc.img
MAKE_DISK := $(MAME_DIR)/make_disk.py
RUN_MAME := $(MAME_DIR)/run_mame.sh
# Optional knobs — see top of file.
MEMORY ?= tiny
SOURCES := $(EXAMPLE).c $(EXTRA_SRCS)
CC_FLAGS := --memory $(MEMORY)
ifneq ($(STACK_SIZE),)
CC_FLAGS += --stack-size $(STACK_SIZE)
endif
CC_FLAGS += $(EXTRA_FLAGS)
all: $(EXAMPLE).exe
$(EXAMPLE).exe: $(SOURCES) $(MKEXE) $(LIB)
$(SPRINTER_CC) $(CC_FLAGS) -o $@ $(SOURCES)
$(MKEXE):
$(MAKE) -C $(PROJ_ROOT)/toolchain/mkexe
$(LIB):
$(MAKE) -C $(PROJ_ROOT)/lib
clean:
rm -rf .sprinter-cc-* $(EXAMPLE).exe
# `make floppy` packs ONLY this example (+ optional EXTRA_DATA files) into
# the MAME floppy image, replacing whatever was there. Handy for trying a
# single program without rebuilding all 27 examples.
floppy: $(EXAMPLE).exe
python3 $(MAKE_DISK) $(FLOPPY_IMG) $(EXAMPLE).exe $(EXTRA_DATA)
@echo
@echo "Floppy ready: $(FLOPPY_IMG) (with $(EXAMPLE).exe$(if $(EXTRA_DATA), + $(EXTRA_DATA))) "
@echo "Run: cd $(MAME_DIR) && ./run_mame.sh"
run: floppy
cd $(MAME_DIR) && ./run_mame.sh
.PHONY: all clean floppy run
-3
View File
@@ -1,3 +0,0 @@
PROJ_ROOT := $(abspath $(CURDIR)/../..)
EXAMPLE := filetest
include $(PROJ_ROOT)/examples/example.mk
-74
View File
@@ -1,74 +0,0 @@
/*
* filetest — exercise minimal FILE * stream API (fopen/fclose/fputs/
* fgets/fread/fwrite/fseek/ftell/feof + stdin/stdout/stderr).
*
* Steps:
* 1. fopen("FILE.TXT","w"), fputs+fputc lines, fclose.
* 2. fopen("FILE.TXT","r"), fgets each line, fclose.
* 3. fopen("r"), fread block, fseek, ftell, feof, fread again.
* 4. fputs to stdout (sanity check console pseudo-stream).
*/
#include <stdio.h>
#include <string.h>
#include <errno.h>
int main(void)
{
const char *path = "FILE.TXT";
/* ----- 1. Write via fputs + fputc ----- */
{
FILE *fp = fopen(path, "w");
if (!fp) {
printf("fopen(w) failed: errno=%d (%s)\n", errno, strerror(errno));
return 1;
}
fputs("Hello from FILE*\n", fp);
fputs("Second line via fputs\n", fp);
for (int i = '0'; i <= '9'; i++) fputc(i, fp);
fputc('\n', fp);
if (fclose(fp) != 0) puts("fclose: error");
}
/* ----- 2. Read via fgets ----- */
{
FILE *fp = fopen(path, "r");
if (!fp) {
printf("fopen(r) failed: errno=%d\n", errno);
return 1;
}
char buf[128];
int n = 0;
while (fgets(buf, sizeof buf, fp)) {
printf(" line %d: %s", ++n, buf);
if (buf[strlen(buf)-1] != '\n') putchar('\n');
}
printf(" total lines: %d, feof=%d\n", n, feof(fp));
fclose(fp);
}
/* ----- 3. fread / fseek / ftell ----- */
{
FILE *fp = fopen(path, "r");
if (!fp) return 1;
char buf[16];
size_t got = fread(buf, 1, 16, fp);
printf(" fread(16) -> %u bytes, ftell=%ld\n",
(unsigned)got, ftell(fp));
if (fseek(fp, 0L, SEEK_END) == 0) {
printf(" file size via fseek(END)+ftell = %ld\n", ftell(fp));
}
rewind(fp);
printf(" rewind: ftell=%ld, feof=%d\n", ftell(fp), feof(fp));
fclose(fp);
}
/* ----- 4. Console pseudo-streams ----- */
fputs(" fputs to stdout works\n", stdout);
fputs(" fputs to stderr works\n", stderr);
fputc('!', stdout); fputc('\n', stdout);
puts("filetest done.");
return 0;
}
-5
View File
@@ -1,5 +0,0 @@
# Build gfx_demo16.exe — exercises libc/gfx 640x256x16 primitives.
PROJ_ROOT := $(abspath $(CURDIR)/../..)
EXAMPLE := gfx_d16
include $(PROJ_ROOT)/examples/example.mk
-116
View File
@@ -1,116 +0,0 @@
/*
* gfx_demo16 — exercises the 640×256×16 (mode 0x82) primitives.
*
* Stages (press a key to advance):
* 1. 1-pixel frame via hline/vline16 — verifies edge alignment
* (odd-x and odd-(x+len) RMW paths)
* 2. Nested rectangles in cycled colours
* 3. Colour bars — 16 vertical stripes, each colour 0..15
* (palette readability test)
* 4. Diagonal star + axis-aligned cross via line16
*/
#include <stdio.h>
#include <conio.h>
#include <gfx.h>
#include <stdint.h>
static uint8_t palette[16 * 4];
static void wait_key(void)
{
__asm
ld c, #0x30
rst #0x10
__endasm;
}
static void build_palette(void)
{
/* 16-color palette: index 0 = black, 1..7 = primary ramp,
* 8..15 = mid/light variants. Order chosen so neighbouring
* indices contrast in the colour-bars test. */
static const uint8_t bgr[16][3] = {
{0x00, 0x00, 0x00}, /* 0 black */
{0x00, 0x00, 0xFF}, /* 1 red */
{0x00, 0xFF, 0x00}, /* 2 green */
{0xFF, 0x00, 0x00}, /* 3 blue */
{0x00, 0xFF, 0xFF}, /* 4 yellow */
{0xFF, 0x00, 0xFF}, /* 5 magenta */
{0xFF, 0xFF, 0x00}, /* 6 cyan */
{0xFF, 0xFF, 0xFF}, /* 7 white */
{0x40, 0x40, 0x40}, /* 8 dim grey */
{0x40, 0x40, 0xFF}, /* 9 light red */
{0x40, 0xFF, 0x40}, /* A light green */
{0xFF, 0x40, 0x40}, /* B light blue */
{0x40, 0xFF, 0xFF}, /* C light yellow */
{0xFF, 0x40, 0xFF}, /* D light magenta */
{0xFF, 0xFF, 0x40}, /* E light cyan */
{0xA0, 0xA0, 0xA0}, /* F grey */
};
for (int i = 0; i < 16; i++) {
palette[i * 4 + 0] = bgr[i][0];
palette[i * 4 + 1] = bgr[i][1];
palette[i * 4 + 2] = bgr[i][2];
palette[i * 4 + 3] = 0;
}
}
int main(void)
{
build_palette();
uint8_t prev = gfx_init(GFX_MODE_640x256x16, 0);
gfx_pal_load(0, 0, 16, palette);
/* --- Stage 1: 1-px frame at edges -------------------------------- */
gfx_clear16(8); /* dim grey */
gfx_hline16(0, 0, GFX_WIDTH_16, 7); /* white top */
gfx_hline16(0, GFX_HEIGHT_16 - 1, GFX_WIDTH_16, 7); /* white bottom */
gfx_vline16(0, 0, GFX_HEIGHT_16, 7);
gfx_vline16(GFX_WIDTH_16 - 1, 0, GFX_HEIGHT_16, 7);
/* 1-px inset purple frame — exercises odd-x edges. */
gfx_hline16(1, 1, GFX_WIDTH_16 - 2, 5);
gfx_hline16(1, GFX_HEIGHT_16 - 2, GFX_WIDTH_16 - 2, 5);
gfx_vline16(1, 2, GFX_HEIGHT_16 - 4, 5);
gfx_vline16(GFX_WIDTH_16 - 2, 2, GFX_HEIGHT_16 - 4, 5);
wait_key();
/* --- Stage 2: nested rectangles --------------------------------- */
gfx_clear16(0);
for (int i = 0; i < 16; i++)
gfx_rect16(i * 20, i * 8,
GFX_WIDTH_16 - i * 40,
GFX_HEIGHT_16 - i * 16,
(uint8_t)((i + 1) & 0x0F));
wait_key();
/* --- Stage 3: vertical color bars ------------------------------- */
gfx_clear16(0);
/* 16 bars × 40 px wide = 640 px exactly. */
for (int i = 0; i < 16; i++)
gfx_fill_rect16(i * 40, 0, 40, GFX_HEIGHT_16, (uint8_t)i);
wait_key();
/* --- Stage 4: diagonal star + cross ----------------------------- */
gfx_clear16(0);
int cx = GFX_WIDTH_16 / 2, cy = GFX_HEIGHT_16 / 2;
for (int i = 0; i < 16; i++) {
int ex = i * (GFX_WIDTH_16 - 1) / 15;
gfx_line16(cx, cy, ex, 0, 7);
gfx_line16(cx, cy, ex, GFX_HEIGHT_16 - 1, 3);
}
for (int i = 0; i < 16; i++) {
int ey = i * (GFX_HEIGHT_16 - 1) / 15;
gfx_line16(cx, cy, 0, ey, 1);
gfx_line16(cx, cy, GFX_WIDTH_16 - 1, ey, 2);
}
/* axis-aligned cross to verify hline/vline alignment at runtime. */
gfx_hline16(0, cy, GFX_WIDTH_16, 6);
gfx_vline16(cx, 0, GFX_HEIGHT_16, 6);
wait_key();
gfx_done(prev);
puts("done");
return 0;
}
-3
View File
@@ -1,3 +0,0 @@
PROJ_ROOT := $(abspath $(CURDIR)/../..)
EXAMPLE := gfx_dbuf
include $(PROJ_ROOT)/examples/example.mk
-120
View File
@@ -1,120 +0,0 @@
/*
* gfx_dbuf — double-buffering demo.
*
* Renders a moving rectangle by alternating between page 0 and page 1:
* - Each frame: draw to the HIDDEN page, then flip visible to it.
* - The page currently being displayed is never written to, so the
* user only sees fully-rendered frames (no half-drawn artefacts,
* no flicker from the per-frame clear).
*
* Indicators help verify the swap is actually happening:
* - A small colour swatch in the top-right alternates each frame:
* one shade when page 0 is shown, another when page 1. If the
* box motion looks smooth and the swatch flickers between the two
* shades at the animation rate, the swap is working.
*
* NOTE: each graphics screen has its OWN palette page (screen 0 →
* palette 0, screen 1 → palette 1). We load the same palette into
* both so the colours look identical regardless of which page is
* currently visible. See memory/sprinter_graphics.md.
*
* Press any key to exit.
*/
#include <stdio.h>
#include <conio.h>
#include <gfx.h>
#include <stdint.h>
static uint8_t palette[256 * 4];
#define COL_BLACK 0x00
#define COL_BG 0x20
#define COL_STRIPE 0x60
#define COL_TEXT 0xC0
#define COL_BOX 0xFF
#define COL_PAGE0 0x40
#define COL_PAGE1 0xE0
static void make_palette(void)
{
for (int i = 0; i < 256; i++) {
palette[i*4+0] = (uint8_t)i;
palette[i*4+1] = (uint8_t)i;
palette[i*4+2] = (uint8_t)i;
palette[i*4+3] = 0;
}
}
static void draw_frame(int box_x, int box_y, uint8_t page_indicator)
{
gfx_clear256(COL_BG);
/* Static decoration — identical on both pages so the swap doesn't
* flicker the chrome. */
gfx_fill_rect256(0, 0, GFX_WIDTH, 8, COL_STRIPE);
gfx_fill_rect256(0, GFX_HEIGHT - 8, GFX_WIDTH, 8, COL_STRIPE);
gfx_text256(8, 16, "double-buffering demo", COL_TEXT, COL_BG);
gfx_text256(8, GFX_HEIGHT - 24,
"press a key to exit", COL_TEXT, COL_BG);
/* Page indicator — different shade for each page so a flickering
* box here proves the swap is happening. */
gfx_fill_rect256(GFX_WIDTH - 24, 16, 12, 12,
page_indicator ? COL_PAGE1 : COL_PAGE0);
/* The animated box. */
gfx_fill_rect256(box_x, box_y, 40, 30, COL_BOX);
gfx_rect256 (box_x, box_y, 40, 30, COL_BLACK);
}
int main(void)
{
make_palette();
uint8_t prev = gfx_init(GFX_MODE_320x256x256, 0);
/* Each graphics screen has its own palette page (0→pal 0, 1→pal 1).
* Load the same data into both so the visual swap is seamless. */
gfx_pal_load(0, 0, 0, palette);
gfx_pal_load(1, 0, 0, palette);
/* Clear both pages so the very first flip doesn't reveal garbage. */
gfx_set_draw_page(0);
gfx_clear256(COL_BG);
gfx_set_draw_page(1);
gfx_clear256(COL_BG);
/* Bouncing rect state. */
int box_x = 10;
int box_y = 100;
int dx = 3;
int dy = 2;
const int box_w = 40, box_h = 30;
while (!kbhit()) {
uint8_t hidden = 1 - gfx_get_visible_page();
gfx_set_draw_page(hidden);
draw_frame(box_x, box_y, hidden);
/* Wait for the next frame interrupt before flipping, so the
* page swap lands during vertical retrace and the user never
* sees a half-drawn frame. */
gfx_wait_vsync();
gfx_set_visible_page(hidden);
/* Step the box; bounce off all four walls. */
box_x += dx;
if (box_x < 0) { box_x = 0; dx = -dx; }
if (box_x + box_w > GFX_WIDTH) { box_x = GFX_WIDTH - box_w; dx = -dx; }
box_y += dy;
if (box_y < 8) { box_y = 8; dy = -dy; }
if (box_y + box_h > GFX_HEIGHT-8) { box_y = GFX_HEIGHT-8 - box_h; dy = -dy; }
}
(void)getch();
gfx_done(prev);
puts("done");
return 0;
}
-3
View File
@@ -1,3 +0,0 @@
PROJ_ROOT := $(abspath $(CURDIR)/../..)
EXAMPLE := gfx_demo
include $(PROJ_ROOT)/examples/example.mk
-100
View File
@@ -1,100 +0,0 @@
/*
* gfx_demo — exercises the libc/gfx primitives. Shows:
* 1. accelerator-backed gfx_clear256 / gfx_hline256 / gfx_vline256 / gfx_fill_rect256
* 2. gfx_rect256 outline, gfx_line256 diagonals (Bresenham via putpixel)
* 3. a grey-ramp palette
*
* Press a key to advance through each stage.
*/
#include <stdio.h>
#include <conio.h>
#include <gfx.h>
#include <stdint.h>
static uint8_t palette_data[256 * 4];
static void wait_key(void)
{
__asm
ld c, #0x30
rst #0x10
__endasm;
}
int main(void)
{
/* Grey ramp: (B=i, G=i, R=i, 0). */
for (int i = 0; i < 256; i++) {
palette_data[i * 4 + 0] = (uint8_t)i;
palette_data[i * 4 + 1] = (uint8_t)i;
palette_data[i * 4 + 2] = (uint8_t)i;
palette_data[i * 4 + 3] = 0;
}
uint8_t prev = gfx_init(GFX_MODE_320x256x256, 0);
gfx_pal_load(0, 0, 0, palette_data);
/* --- Stage 1: orthogonal frame via hline / vline ----------------- */
gfx_clear256(0x40);
gfx_hline256(1, 1, GFX_WIDTH - 2, 0xFF);
gfx_hline256(1, GFX_HEIGHT - 2, GFX_WIDTH - 2, 0xFF);
gfx_vline256(1, 2, GFX_HEIGHT - 4, 0xFF);
gfx_vline256(GFX_WIDTH - 2, 2, GFX_HEIGHT - 4, 0xFF);
wait_key();
/* --- Stage 2: nested rectangles -------------------------------- */
gfx_clear256(0x20);
for (int i = 0; i < 16; i++)
gfx_rect256(i * 10, i * 8,
GFX_WIDTH - i * 20,
GFX_HEIGHT - i * 16,
(uint8_t)(0x40 + i * 8));
wait_key();
/* --- Stage 3: filled-rect colour bars --------------------------- */
gfx_clear256(0);
/* Tall-narrow rects (w=20, h=128) — heuristic picks vertical orient. */
for (int i = 0; i < 16; i++) {
gfx_fill_rect256(i * 20, 0, 20, 128, (uint8_t)(i * 16));
gfx_fill_rect256(i * 20 + 0, 128, 20, 128, (uint8_t)(255 - i * 16));
}
wait_key();
/* --- Stage 3b: wide-short rects + a grid of small squares -------- */
gfx_clear256(0x08);
/* Wide-short stripes (w=320, h=16) — heuristic picks horizontal. */
for (int i = 0; i < 8; i++)
gfx_fill_rect256(0, i * 32, GFX_WIDTH, 16, (uint8_t)(0x40 + i * 24));
/* 8×8 small squares grid (w=h, heuristic picks either — same cost). */
for (int row = 0; row < 4; row++)
for (int col = 0; col < 16; col++)
gfx_fill_rect256(col * 20 + 4, 256 - 80 + row * 20, 12, 12,
(uint8_t)((row * 16 + col) * 4));
wait_key();
/* --- Stage 4: diagonal lines via Bresenham ---------------------- */
gfx_clear256(0x18);
/* "Star" of lines from centre to a circle of endpoints. */
int cx = GFX_WIDTH / 2, cy = GFX_HEIGHT / 2;
for (int i = 0; i < 16; i++) {
int ex = i * (GFX_WIDTH - 1) / 15;
gfx_line256(cx, cy, ex, 0, 0xFF);
gfx_line256(cx, cy, ex, GFX_HEIGHT - 1, 0xC0);
}
for (int i = 0; i < 16; i++) {
int ey = i * (GFX_HEIGHT - 1) / 15;
gfx_line256(cx, cy, 0, ey, 0x80);
gfx_line256(cx, cy, GFX_WIDTH - 1, ey, 0x40);
}
/* Box outline using gfx_line256. */
gfx_line256(0, 0, GFX_WIDTH - 1, 0, 0xFF);
gfx_line256(0, GFX_HEIGHT - 1, GFX_WIDTH - 1, GFX_HEIGHT - 1, 0xFF);
gfx_line256(0, 0, 0, GFX_HEIGHT - 1, 0xFF);
gfx_line256(GFX_WIDTH - 1, 0, GFX_WIDTH - 1, GFX_HEIGHT - 1, 0xFF);
wait_key();
gfx_done(prev);
puts("done");
return 0;
}
-3
View File
@@ -1,3 +0,0 @@
PROJ_ROOT := $(abspath $(CURDIR)/../..)
EXAMPLE := gfx_mous
include $(PROJ_ROOT)/examples/example.mk
-169
View File
@@ -1,169 +0,0 @@
/*
* gfx_mouse_test — exercises the graphics-mode mouse cursor API:
* $09 LOAD CURSOR — install a custom cursor bitmap
* $81 CHANGE VIDEO MODE — let the driver re-sync after switching modes
* $0B RETURN CURSOR — read the cursor back (may be a stub in MAME)
*
* Flow:
* 1. Init the mouse driver in text mode.
* 2. Switch to 320x256x256, notify the driver.
* 3. Build an 8x8 cursor — solid white border + transparent middle —
* and load it. Show it. Move the mouse to verify it tracks.
* 4. Press a key: query the driver's view of the cursor and dump the
* result so we can compare against what we loaded.
* 5. Hide cursor, return to text mode, print the round-trip dump.
*
* The driver's bitmap format is undocumented — best guess is one byte
* per pixel (palette index, with 0 meaning transparent). We load 0xFF
* for opaque cells and 0 for transparent so the cursor stands out
* against any background.
*/
#include <stdio.h>
#include <conio.h>
#include <mouse.h>
#include <gfx.h>
#include <stdint.h>
#define CURSOR_W 12
#define CURSOR_H 12
#define CURSOR_BUF_BYTES (CURSOR_W * CURSOR_H)
/* Cursor format (verified empirically 2026-05-31):
* ONE BYTE per pixel — row-major, width*height bytes total.
* 0xFF = transparent (background shows through)
* any other byte = palette index (drawn opaque)
*
* Cursor bitmap lives in a dedicated video bank, not the 0x50 page used
* by gfx_clear256 / gfx_putpixel256 — that's why 0xFF reads as transparent
* even though in the main visible page 0xFF is a regular colour.
*/
static uint8_t cursor_bitmap[CURSOR_BUF_BYTES];
static uint8_t roundtrip_buf[CURSOR_BUF_BYTES];
static uint8_t palette[256 * 4];
/* Arrow shape, 1 byte per pixel. Each cell is either:
* _ transparent (0xFF, lets the background through)
* O black solid pixel (0x00)
* W white solid pixel (0xFF would mean transparent, so use 0xFF
* palette entry by hand if you want a white pixel — for the
* default greyscale palette in this demo, W = bright grey 0xE0)
*
* Feel free to edit the 12×12 grid below to draw a nicer cursor. */
#define _ 0xFF
#define O 0x00
#define W 0xE0
static const uint8_t cursor_arrow[CURSOR_BUF_BYTES] = {
O,_,_,_,_,_,_,_,_,_,_,_,
O,O,_,_,_,_,_,_,_,_,_,_,
O,W,O,_,_,_,_,_,_,_,_,_,
O,W,W,O,_,_,_,_,_,_,_,_,
O,W,W,W,O,_,_,_,_,_,_,_,
O,W,W,W,W,O,_,_,_,_,_,_,
O,W,W,W,W,W,O,_,_,_,_,_,
O,W,W,W,O,O,O,O,_,_,_,_,
O,O,O,W,O,_,_,_,_,_,_,_,
O,_,_,O,W,O,_,_,_,_,_,_,
_,_,_,O,W,O,_,_,_,_,_,_,
_,_,_,_,O,O,_,_,_,_,_,_,
};
#undef _
#undef O
#undef W
static void make_palette(void)
{
for (int i = 0; i < 256; i++) {
palette[i*4+0] = (uint8_t)i;
palette[i*4+1] = (uint8_t)i;
palette[i*4+2] = (uint8_t)i;
palette[i*4+3] = 0;
}
}
/* Copy the arrow pattern into our scratch (one place for the driver
* to point IX at). */
static void build_cursor(void)
{
for (int i = 0; i < CURSOR_BUF_BYTES; i++)
cursor_bitmap[i] = cursor_arrow[i];
}
static void wait_key(void)
{
while (!kbhit()) { /* spin */ }
(void)getch();
}
int main(void)
{
textattr(COLOR(COLOR_LIGHTGRAY, COLOR_BLACK));
clrscr();
cputs("gfx_mouse_test\r\n");
if (mouse_init() < 0) {
cputs("mouse_init failed - no driver\r\n");
return 1;
}
mouse_set_sensitivity(2, 2);
/* --- enter graphics, load custom cursor, then show -------------- */
make_palette();
build_cursor();
uint8_t prev = gfx_init(GFX_MODE_320x256x256, 0);
gfx_pal_load(0, 0, 0, palette);
gfx_clear256(0x40); /* dim grey background */
/* Coordinate grid every 32 px so the cursor position reads easily. */
for (int x = 0; x < GFX_WIDTH; x += 32) gfx_vline256(x, 0, GFX_HEIGHT, 0x80);
for (int y = 0; y < GFX_HEIGHT; y += 32) gfx_hline256(0, y, GFX_WIDTH, 0x80);
mouse_video_mode_changed(GFX_MODE_320x256x256);
mouse_bounds_x(0, GFX_WIDTH - 1);
mouse_bounds_y(0, GFX_HEIGHT - 1);
mouse_cursor_t custom = {
.image = cursor_bitmap,
.width = CURSOR_W,
.height = CURSOR_H,
.hot_x = 0,
.hot_y = 0,
};
mouse_load_cursor(&custom);
mouse_show();
wait_key();
/* --- query driver to see what it has now ------------------------ */
mouse_cursor_t readback = {
.image = roundtrip_buf,
.width = 0, .height = 0, .hot_x = 0, .hot_y = 0,
};
mouse_get_cursor(&readback);
mouse_hide();
gfx_done(prev);
mouse_video_mode_changed(prev); /* tell driver we're back */
/* --- back in text mode: report what the driver returned --------- */
textattr(COLOR(COLOR_LIGHTGRAY, COLOR_BLACK));
clrscr();
cprintf("loaded : %ux%u hot (%u,%u)\r\n",
CURSOR_W, CURSOR_H, 0, 0);
cprintf("read back: %ux%u hot (%u,%u)\r\n",
readback.width, readback.height, readback.hot_x, readback.hot_y);
uint16_t n = (uint16_t)readback.width * (uint16_t)readback.height;
if (n > CURSOR_BUF_BYTES) n = CURSOR_BUF_BYTES;
cprintf("bitmap (%u bytes):\r\n", n);
for (uint16_t i = 0; i < n; i++) {
cprintf("%02X ", roundtrip_buf[i]);
if ((i % readback.width) == readback.width - 1) cputs("\r\n");
}
cputs("\r\n");
cputs("press any key to exit");
wait_key();
return 0;
}
-5
View File
@@ -1,5 +0,0 @@
# Build gfx_text_demo.exe — bitmap-font text in both graphics modes.
PROJ_ROOT := $(abspath $(CURDIR)/../..)
EXAMPLE := gfx_text
include $(PROJ_ROOT)/examples/example.mk
-113
View File
@@ -1,113 +0,0 @@
/*
* gfx_text_demo — bitmap-font text rendering test.
*
* Stages (press a key to advance):
* 1. 320×256×256 mode — text in multiple colors using BIOS default font.
* 2. 640×256×16 mode — same text with the 16-color palette.
* Verifies the same font renders correctly in both pixel formats.
*/
#include <stdio.h>
#include <conio.h>
#include <gfx.h>
#include <stdint.h>
static uint8_t pal256[256 * 4];
static uint8_t pal16[16 * 4];
static void wait_key(void)
{
__asm
ld c, #0x30
rst #0x10
__endasm;
}
int main(void)
{
/* Grey ramp for 256-color mode. */
for (int i = 0; i < 256; i++) {
pal256[i * 4 + 0] = (uint8_t)i;
pal256[i * 4 + 1] = (uint8_t)i;
pal256[i * 4 + 2] = (uint8_t)i;
pal256[i * 4 + 3] = 0;
}
/* Simple 16-color palette: 0=black, 1=red, 2=green, ..., 7=white,
* 8..15 = dimmed variants. */
static const uint8_t bgr16[16][3] = {
{0x00,0x00,0x00}, {0x00,0x00,0xFF}, {0x00,0xFF,0x00}, {0xFF,0x00,0x00},
{0x00,0xFF,0xFF}, {0xFF,0x00,0xFF}, {0xFF,0xFF,0x00}, {0xFF,0xFF,0xFF},
{0x40,0x40,0x40}, {0x40,0x40,0xFF}, {0x40,0xFF,0x40}, {0xFF,0x40,0x40},
{0x40,0xFF,0xFF}, {0xFF,0x40,0xFF}, {0xFF,0xFF,0x40}, {0xA0,0xA0,0xA0},
};
for (int i = 0; i < 16; i++) {
pal16[i*4+0] = bgr16[i][0];
pal16[i*4+1] = bgr16[i][1];
pal16[i*4+2] = bgr16[i][2];
pal16[i*4+3] = 0;
}
/* --- Stage 1: text in 320×256×256 ----------------------------- */
uint8_t prev = gfx_init(GFX_MODE_320x256x256, 0);
gfx_pal_load(0, 0, 0, pal256);
gfx_clear256(0x20);
gfx_text256(8, 8, "Hello from Sprinter!", 0xFF, 0x20);
gfx_text256(8, 24, "320x256x256 mode", 0xC0, 0x20);
gfx_text256(8, 40, "Bitmap font from BIOS WIN_GET_ZG", 0x80, 0x20);
/* Color showcase — same string in 8 different greys. */
for (int i = 0; i < 8; i++) {
char buf[16] = "Color ramp test";
gfx_text256(8, 64 + i * 12, buf, (uint8_t)(0x40 + i * 24), 0x20);
}
/* Adjacent FG/BG variants. */
gfx_text256(8, 176, "FG=white BG=red", 0xFF, 0x30);
gfx_text256(8, 192, "FG=black BG=white", 0x00, 0xFF);
gfx_text256(8, 208, "Negative space:", 0xFF, 0x20);
/* Large block to test grid-aligned text. */
for (int row = 0; row < 3; row++)
for (int col = 0; col < 8; col++) {
char ch[2] = { (char)('A' + row * 8 + col), 0 };
gfx_text256(176 + col * 16, 176 + row * 16, ch, 0xFF, 0x20);
}
wait_key();
/* --- Stage 2: text in 640×256×16 ------------------------------ */
gfx_done(prev);
prev = gfx_init(GFX_MODE_640x256x16, 0);
gfx_pal_load(0, 0, 16, pal16);
gfx_clear16(0);
gfx_text16(8, 8, "Hello from Sprinter!", 7, 0);
gfx_text16(8, 24, "640x256x16 mode (16 colours)", 6, 0);
gfx_text16(8, 40, "Same font, packed two pixels/byte", 4, 0);
/* Color demo: each line in a different palette index. */
for (int i = 1; i < 16; i++) {
char buf[20];
/* Simple manual itoa to avoid stdio bloat. */
buf[0] = 'C'; buf[1] = 'o'; buf[2] = 'l'; buf[3] = 'o'; buf[4] = 'r';
buf[5] = ' '; buf[6] = (char)('0' + (i / 10));
buf[7] = (char)('0' + (i % 10));
buf[8] = 0;
gfx_text16(8, 64 + (i - 1) * 12, buf, (uint8_t)i, 0);
}
/* Show on right side — paragraph in white-on-black, then inverted. */
gfx_text16(280, 64, "ABCDEFGHIJKLMNOP", 7, 0);
gfx_text16(280, 76, "QRSTUVWXYZ", 7, 0);
gfx_text16(280, 92, "abcdefghijklmnop", 7, 0);
gfx_text16(280, 104, "qrstuvwxyz", 7, 0);
gfx_text16(280, 120, "0123456789", 7, 0);
gfx_text16(280, 132, "!@#$%^&*()_+-=", 7, 0);
gfx_text16(280, 148, "Inverted text", 0, 7);
gfx_text16(280, 164, "FG=red BG=cyan", 1, 6);
wait_key();
gfx_done(prev);
puts("done");
return 0;
}
-5
View File
@@ -1,5 +0,0 @@
# Build hello.exe — uses lib/sprinter.lib in TINY memory mode.
PROJ_ROOT := $(abspath $(CURDIR)/../..)
EXAMPLE := hello
include $(PROJ_ROOT)/examples/example.mk
-65
View File
@@ -1,65 +0,0 @@
/*
* hello — smoke test for the two output APIs:
*
* stdio (printf/puts/putchar) — FAST, no attribute (ambient colour)
* conio (cprintf/cputs/putch) — SLOWER, applies textcolor/textbackground
*
* Demonstrates textcolor/textbackground (Turbo-C-compatible names).
*/
#include <stdio.h>
#include <conio.h>
#include <gfx.h>
#include <sprinter.h>
#include <sprinter_exit.h>
#include <errno.h>
int main(void)
{
errno = -1;
int16_t a0 = 0, a1 = 0;
a0 = get_text_attr();
uint8_t vMode = get_videotextmode();
set_videotextmode(TEXT_MODE_80x32);
/* Wipe the screen with a known attribute so stdio printf/puts inherit
* a clean colour for the cursor. */
clrscr_attr(COLOR(COLOR_LIGHTGRAY, COLOR_BLACK));
/* --- stdio set: fast, no attribute ------------------------------- */
puts("hello (stdio) - fast PCHARS, ambient colour");
printf("printf: a0=%d (initial attr)\n", a0);
/* --- conio set: applies textcolor / textbackground --------------- */
textcolor(COLOR_YELLOW);
textbackground(COLOR_BLUE);
cputs("conio: yellow on blue\r\n");
textcolor(COLOR_LIGHTRED);
textbackground(COLOR_BLACK);
cprintf("cprintf %d %d %s\r\n", 42, -7, "args");
/* set_text_attr / textattr replace the whole byte. */
textattr(COLOR(COLOR_LIGHTGREEN, COLOR_BLACK));
cputs("conio: light-green via textattr\r\n");
/* Opting out of attribute control — conio falls back to fast path. */
set_text_attr(KEEP_EXIST_ATTR);
cputs("conio with KEEP_EXIST_ATTR (fast path)\r\n");
a1 = get_text_attr();
/* Back to a normal attribute so the goodbye reads cleanly. */
textattr(COLOR(COLOR_LIGHTGRAY, COLOR_BLACK));
printf("a0=%d a1=%d now=%d errno=%d\n",
a0, a1, get_text_attr(), errno);
#ifdef DEBUG_RT
printf("w2_self_allocated = %u\n", w2_self_allocated);
#endif
cputs("Press any key to exit...");
(void)getchar();
set_videotextmode(vMode);
return 0;
}
-5
View File
@@ -1,5 +0,0 @@
# Build ls.exe — uses lib/sprinter.lib in TINY memory mode.
PROJ_ROOT := $(abspath $(CURDIR)/../..)
EXAMPLE := ls
include $(PROJ_ROOT)/examples/example.mk
-96
View File
@@ -1,96 +0,0 @@
#include <stdio.h>
#include <string.h>
#include <conio.h>
#include <dir.h>
#include <errno.h>
/*
* ls — list files on the current drive that match the given pattern.
*
* LS -> "*.*" (everything in current dir)
* LS *.TXT -> just .TXT files
* LS C: -> auto-rewritten to "C:*.*"
* LS C:\BIN -> "C:\BIN\*.*"
* LS C:\BIN\ -> "C:\BIN\*.*"
* LS /A -> include hidden / system / label entries
*
* ESTEX F_FIRST itself returns errno 16 (EINAME) when given a bare path
* with no wildcard, so we patch the pattern up before calling it.
*/
/*
* Sprinter ESTEX F_FIRST refuses bare path components — it needs a real
* wildcard. We do the minimum that DOS-style shells do for the user:
* - "C:" → "C:\*.*" (drive without slash)
* - "C:\" → "C:\*.*" (root-like, just append the wildcard)
* - anything else: pass through. A more correct implementation would
* stat the path and decide between "single file" / "directory" — out
* of scope for this example.
*/
static char *normalize_pattern(const char *src, char *dst, int cap)
{
int len = (int)strlen(src);
if (len == 0) {
strcpy(dst, "*.*");
return dst;
}
char last = src[len - 1];
const char *suffix = NULL;
if (last == ':') suffix = "\\*.*";
else if (last == '\\' || last == '/') suffix = "*.*";
if (suffix && len + (int)strlen(suffix) + 1 <= cap) {
strcpy(dst, src);
strcat(dst, suffix);
} else {
strcpy(dst, src);
}
return dst;
}
int main(int argc, char **argv)
{
const char *pattern_in = "*.*";
uint8_t attr = FA_NORMAL | FA_DIREC | FA_RDONLY | FA_ARCH;
for (int i = 1; i < argc; i++) {
if (argv[i][0] == '/' && (argv[i][1] | 0x20) == 'a') {
attr |= FA_HIDDEN | FA_SYSTEM | FA_LABEL;
} else {
pattern_in = argv[i];
}
}
char pattern[80];
normalize_pattern(pattern_in, pattern, sizeof(pattern));
printf("ls: pattern=\"%s\" attr=0x%02X\n\n", pattern, attr);
ffblk_t blk;
int r = ffirst(pattern, &blk, attr);
if (r < 0) {
printf("no match (errno=%d %s)\n", errno, strerror(errno));
cputs("\nPress any key to exit.\n");
(void)getch();
return 1;
}
int count = 0;
do {
const char *kind;
if (blk.found_attr & FA_DIREC) kind = "<DIR>";
else if (blk.found_attr & FA_LABEL) kind = "<VOL>";
else if (blk.found_attr & FA_SYSTEM) kind = "[SYS]";
else if (blk.found_attr & FA_HIDDEN) kind = "[HID]";
else kind = " ";
printf(" %s %-14s %8lu\n", kind, blk.found_name, blk.size);
count++;
} while (fnext(&blk) == 0);
printf("\n%d entries\n\n", count);
cputs("Press any key to exit.\n");
(void)getch();
return 0;
}
-5
View File
@@ -1,5 +0,0 @@
# Build malloc_test.exe — uses lib/sprinter.lib in TINY memory mode.
PROJ_ROOT := $(abspath $(CURDIR)/../..)
EXAMPLE := malloc
include $(PROJ_ROOT)/examples/example.mk
-63
View File
@@ -1,63 +0,0 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/*
* Exercises SDCC's malloc / free. The library pulls in heap.rel + malloc.rel
* from z80.lib automatically and inits the heap from crt0's _GSINIT chain.
* Default heap size is 1023 bytes — see docs/TODO.md for plans to grow it.
*/
int main(void)
{
puts("malloc test:");
/* small allocations */
char *a = (char *)malloc(20);
char *b = (char *)malloc(40);
char *c = (char *)malloc(60);
printf(" a = %p\n", a);
printf(" b = %p\n", b);
printf(" c = %p\n", c);
if (a && b && c) {
strcpy(a, "first");
strcpy(b, "second-medium-length");
strcpy(c, "third-and-the-longest-of-them-all");
printf(" a: \"%s\"\n", a);
printf(" b: \"%s\"\n", b);
printf(" c: \"%s\"\n", c);
} else {
puts(" one of the allocations FAILED");
}
/* free b, alloc smaller — should fit in the b-shaped hole */
free(b);
char *d = (char *)malloc(15);
printf(" after free(b), d = %p (expected near b)\n", d);
if (d) {
strcpy(d, "reused");
printf(" d: \"%s\"\n", d);
}
/* drain a larger allocation to see when malloc actually says no */
int total = 0;
int count = 0;
for (;;) {
void *p = malloc(64);
if (!p) break;
total += 64;
count++;
}
printf(" alloc'd %d x 64-byte blocks before failure (total %d bytes)\n",
count, total);
free(a);
free(c);
free(d);
puts("");
puts("Press any key to exit.");
(void)getchar();
return 0;
}
+10
View File
@@ -0,0 +1,10 @@
# Build mdview.exe — Markdown viewer for Sprinter.
#
# small memory mode: code in W1, data/stack/heap in W2 (32 KB total).
# W3 stays free for the file buffer (EMM-mapped) and Phase 3+ render cache.
PROJ_ROOT := $(abspath $(CURDIR)/../..)
EXAMPLE := mdview
MEMORY := small
EXTRA_DATA := SAMPLE.MD
include $(PROJ_ROOT)/app.mk
+527
View File
@@ -0,0 +1,527 @@
# План: текстовый Markdown viewer для Sprinter (`examples/mdview`)
## Context
Тестовая крупная задача — проверить нашу libc на нетривиальном interactive-приложении (полноэкранный UI, файловый I/O, парсер). Параллельно даст хороший showcase платформы и поможет вытащить недоделки в conio/io. Конечная цель: viewer для `.md` файлов с подсветкой синтаксиса, навигацией по тексту и постраничным скроллингом.
**Ограничения v1 (зафиксированы пользователем):**
- POSIX file API (`open/read/lseek/close`); FILE*/fread/fgets — не использовать
- Максимум 16 KB на один файл (одна EMM-страница); многостраничный режим — в v2
- Подсветка через цвет: **размер заголовка** → цвет шрифта; **bold/italic** → цвет фона (моноширинный фонт без жирного/курсивного начертания)
---
## Архитектура
### Раскладка экрана (80×32, текст mode 0x03)
```
Row 0: ┃ mdview.md L 1-30 / 142 21% ┃ ← status bar (BG=blue, FG=white)
Row 1: ┃ ┃
... ┃ document viewport (30 rows) ┃
Row 30: ┃ ┃
Row 31: ┃ F1 Help F10 Exit┃ ← menu bar (BG=blue, FG=cyan)
```
- Viewport = 30 строк × 80 столбцов.
- Status / menu рендерятся через `wrchar()` (без авто-скролла), viewport — через `LOCATE` + посимвольный `wrchar()` (тоже без авто-скролла, ставит и char и attr за один call).
### Память
Memory mode: **`small`** — DSS отводит под наш образ **два банка (W1 + W2)** = 32 KB суммарно (CODE в W1, DATA + STACK + HEAP в W2). Этого должно хватить чтобы НЕ заводить `__banked` функции. W3 остаётся полностью свободным для маппинга больших буферов:
```
W0 (0x0000-0x3FFF): ESTEX (system, untouchable)
W1 (0x4000-0x7FFF): CODE (small mode page 1)
W2 (0x8000-0xBFFF): DATA + STACK + HEAP (small mode page 2)
W3 (0xC000-0xFFFF): paged window — переключается между EMM-страницами:
├── file page: исходный текст .md файла (16 KB)
└── cache page: кэш отформатированных строк (Phase 3+)
```
Почему small + W3:
- 32 KB на код+данные с большим запасом → нет банкинга
- W3 — стандартный paged window, под него у нас уже есть `bank_io_w3` API
- v2-расширение (multi-page файлы >16 KB): просто `mem_alloc_pages(N)` и переключение страниц в W3 через `sprinter_page_w3()`
Статики (в W2):
- `line_offset[MAX_LINES]` — uint16_t смещение каждой строки в файловой странице (4 KB на 2048 строк)
- `cache_tag[CACHE_N]` — uint16_t тег слота (200 байт на 100 слотов, появляется в Phase 3)
- `filename[64]`, `top_line`, `total_lines`, `file_size`, `file_blk`, `cache_blk` — единицы байт
**FILE_BUF**: `((char*)0xC000)` — фиксированная адресация в W3 после маппинга нужной страницы.
### Поток данных
```
main → open() → read() chunks 1KB → write to W1 (mapped EMM page) → close()
→ index_lines() (одно сканирование, заполняет line_offset[])
→ render_viewport() + main loop { getkey(); handle(); render_status(); render_viewport() }
```
**Тонкость с `read()`**: ESTEX READ записывает по dst-указателю в адресном пространстве вызывающего. Поскольку мы маппим EMM-страницу в W3 (0xC000) ДО вызова read(), указатель 0xC000+offset валиден. Если выяснится что BIOS трогает W3 во время read (графический видеобуфер по умолчанию в W3 при графических режимах, но в текстовом — должен быть свободен) — fallback: читать в 1 KB буфер в W2 и копировать в W3 через `bank_write_w3()`.
---
## Реализация — поэтапная
### Phase 1 — Plain text viewer (MVP)
**Что работает:**
- Загрузка файла (`open/read/close`) в W1-страницу
- Индексация строк (LF / CRLF разделители)
- Status bar: имя файла, L N-M / Total, процент скроллинга
- Menu bar: `F1 Help F10 Exit`
- Навигация: ↑/↓ (1 строка), PgUp/PgDn (30 строк), Home/End (начало/конец), Esc/F10 (выход), F1 (help screen)
- Обрезка строк длиннее 80 символов (без word-wrap)
- Цвета: текст белый на чёрном; status/menu — белый на синем
**Критичные файлы:**
- `examples/mdview/mdview.c` — main, key loop, rendering, indexing (one-file MVP)
- `examples/mdview/Makefile`
- `examples/mdview/SAMPLE.MD` — тестовый markdown файл
### Phase 2 — Headers и горизонтальная линия
**MD фичи:**
- `# H1` → ярко-жёлтый (COLOR_YELLOW = 14) на чёрном
- `## H2` → ярко-голубой (COLOR_LBLUE = 11)
- `### H3` → ярко-зелёный (COLOR_LGREEN = 10)
- `#### H4+` → серый (COLOR_GREY = 8)
- `---` / `***` на отдельной строке → линия 0xC4 (горизонтальная рамка ASCII) во всю ширину
### Phase 3 — Inline emphasis
**Парсер inline (per-line, runs в одну строку):**
- `**bold**` → ATTR_TEXT_BOLD
- `*italic*` → ATTR_TEXT_ITALIC
- `_underscore_` → ATTR_TEXT_UNDERSORE
- `` `code` `` → ATTR_TEXT_CODE
- Маркеры `**`/`*`/`_`/`` ` `` НЕ рендерятся (съедаются)
State machine: один активный стиль одновременно (без вложенности); конфликтующий маркер при чужом активном стиле уходит литералом. Состояние сбрасывается на каждой строке.
> **Кэш отформатированных строк** — отложен в самый конец, см. "Phase ∞: оптимизации".
> Скорости текущего наивного рендера хватает на 80×30 = 2400 wrchar / кадр; PgUp/PgDn визуально мгновенен.
### Phase 4 — Block elements
- Маркированные списки: `- foo`, `* foo`, `+ foo` → префикс `•` (0x07) + пробел; цвет маркера ярче основного
- Нумерованные списки: `1. foo`, `2. foo` → как есть (число оставляем)
- Blockquote: строки с `> ` → префикс `│` (0xB3) серого цвета, остальной текст слегка приглушённый
- Fenced code blocks: `` ``` `` открывает/закрывает блок; все строки между — bg=серый, моноширинно (без inline-парсинга)
- Indented code blocks (4+ пробелов): аналогично fenced, но без явного маркера
**Light nested lists (v1 — реализовано):**
- `classify_line()` пропускает leading spaces перед ulist/olist/quote маркером,
возвращает `content_off` после маркера → `content_off - p_start` = indent + marker
бит в visible col.
- `render_line()` рисует leading-spaces в `ATTR_TEXT`, потом маркер на сдвинутой
позиции (col = indent). Marker всё ещё фиксирован при горизонтальном pan'е.
- HR / header / fence delim остаются строго col-0 (CommonMark разрешает до 3
ведущих пробелов для них — упростили).
- Tab-indent → не распознаётся как nesting (только spaces).
**Phase 4-full — полная поддержка вложенности (deferred):**
- **Tab-indent**: считать tab = 4 пробела для определения уровня.
- **Quote nesting** (`> > foo`): каждый `>` подряд = +1 уровень, каждый рисуется
отдельным `│` в `ATTR_QUOTE_MARKER` (визуальная "лестница" слева).
- **Hanging indent в wrap-continuation**: когда `- some very long bullet text
that wraps...` — continuation seg должен начинаться от content-col (после
маркера), а не от col 0. Сейчас continuation идёт от col 0 (v1 simplification).
Требует хранить `marker_width` per логическая строка (8 бит) или re-classify
first seg при рендере continuation.
- **Lazy continuation**: строки без маркера, но с правильным indent под
предыдущим bullet'ом, должны считаться продолжением того bullet'а
(визуально — общий attr).
- **Strict CommonMark indent rules**: вложенный пункт должен быть на indent
≥ content_col родителя, иначе считается breakout. Нужен мини-stack
активных списков при индексации.
### Phase 5 — Wrap / Unwrap длинных строк
Дефолт: **wrap on**. F2 переключает; в меню-баре подпись отражает действие
("Unwrap" когда wrap включён, "Wrap" когда выключен).
**v1 — реализовано:**
- Один массив `line_offset[2048]` хранит ВИДИМЫЕ сегменты (а не логические
строки); биты 0..13 — байтовое смещение, бит 15 — CONT-флаг continuation.
- Wrap-режим: soft wrap на последнем пробеле ≤ 80; hard fallback если
пробела нет.
- Маркеры эмфазиса (`**`/`*`/`_`/`` ` ``) и header-префиксы (`#`/`##`/...) не
учитываются в visible-col при поиске точки переноса.
- "Специальные" логические строки не wrap'аются вообще (одна seg-запись на
логическую строку): fence delim, table row (header/separator/body), HR.
- F2 toggle сохраняет визуальную позицию через `top_offset` в FILE_BUF.
- Bitmaps (`in_code`/`in_table`/`is_tab_hdr`) перестраиваются вместе с
сегментами, индексируются seg-индексом, биты ставятся только на первом
seg'е логической строки.
- Continuation-сегменты рендерятся в стиле "v1: плоско" — plain text,
никаких markdown-классификаций; padding до конца строки `ATTR_TEXT`.
**v1.5 — отложено для полной картины wrap:**
- **Hanging indent**: continuation от ulist/olist/quote должен выравниваться
под content, а не от col 0. Требует хранить marker_width per логическая
строка ИЛИ re-classify первого seg'а при рендере continuation.
- **Наследование base_attr**: continuation от header'а должен сохранять
цвет; continuation от code body — фон ATTR_TEXT_CODE. Требует хранить
1 байт `base_attr` per seg ИЛИ lookup первого seg'а.
- **Inline emphasis через границу**: эмфазис, открытый в первом seg'е и не
закрытый, должен продолжаться во втором. Требует хранить emph state per
seg (3 бита).
- Compact way: добавить параллельный массив `seg_meta[MAX_SEGS]` по 1 байту
— пакует marker_width (4 бита) + emph_state (3 бита) + base_attr_idx
(4 бита из таблицы → нужен 2-байтовый seg_meta).
- **Hpan для длинных строк**: если wrap выключен, добавить ←/→ для
горизонтального скролла >80 cols. Общий механизм с tables (Phase 6).
**РЕАЛИЗОВАНО** (light) — `viewport_x` + полный re-render на каждое ←/→.
- **Ускорение hpan через ESTEX WINCOPY/WINREST** (deferred): сейчас pan
делает полный `render_viewport()` = 30 строк × 80 wrchar. Можно
скопировать существующее содержимое viewport'а на N cols влево/вправо
через win-copy, потом рендерить только узкую полосу справа/слева
(HPAN_STEP cols × 30 rows ≈ 240 wrchar вместо 2400). ESTEX SCROLL
горизонталь не поддерживает — нужна именно WINCOPY-операция или
rdchar/wrchar loop. Активировать когда ощутится тормоз; сейчас на
типовом markdown'е не заметно.
**v2 — отдельная фича, мимо wrap:**
- Toggle подсветки целиком (F3?)
- Search по тексту (Ctrl+F / F4)
- Links `[text](url)` → синий подчёркнутый text, url прячется
- Images `![alt](path)` → `[IMG: alt]`
### Phase 7 — Links и поиск (post-v1)
- `[text](url)` → отрисовать только `text` с ярко-синим FG (визуально подчёркнутое)
- `![alt](path)` → `[IMG: alt]` в скобках
- Search по тексту (F3 / Ctrl+F): инкрементальный, подсветка совпадений
### Phase 8 — F8 Raw / Render toggle
Переключатель режима отображения: при включённом Raw показывается исходный
текст файла как есть — все markdown-маркеры (`#`, `**`, `_`, `` ` ``, `|`,
`>`, `-`, etc.) рендерятся литералами с `ATTR_TEXT`, без классификации.
Полезно когда:
- нужно увидеть точную разметку (отлаживание .md, скриншоты, копирование)
- markdown-классификатор ошибся и хочется увидеть оригинал
- хочется быстро сравнить "до/после" рендера
**Поведение:**
- F8 переключает `render_mode` (1=render, 0=raw); меню показывает обратное
действие ("Raw" когда сейчас render, "Render" когда сейчас raw) — той же
логикой что F2/Wrap/Unwrap.
- В Raw режиме: `render_line()` идёт по короткому пути — никакого
`classify_line`, `is_fence_delim`, `is_code_body`, inline-эмфазиса; просто
байтовый дамп FILE_BUF от seg-offset до next-seg/EOL с tab-expansion и
ATTR_TEXT.
- Раздельно от F2: оба режима независимы (можно Raw+Wrap, Raw+Truncate,
Render+Wrap, Render+Truncate). Wrap-логика в `index_lines` работает в
обоих случаях одинаково (опирается на визуальные колонки независимо от
раскраски).
- Статус-бар: добавить индикатор `[R]` / `[V]` (Raw / View) или текстом
`RAW` рядом с именем файла.
**Минимальная реализация:**
- Один новый static `uint8_t render_mode = 1;`
- В `render_line()`: на самом верху `if (!render_mode) { … raw render … return; }`
- В `render_menu()`: добавить ярлык F8 рядом с F2.
- В главном цикле: `case KEY_F8: toggle_render(); break;`
- `toggle_render()` отличается от `toggle_wrap()` тем, что НЕ перестраивает
`line_offset[]` (wrap-сегментация не меняется), только перерендерит экран.
---
## API-новинки в libc (минимальные)
### `getkey()` — extended key reader (в libc)
Текущий `getch()` теряет scan code расширенных клавиш (возвращает только E=ASCII). Добавляем **сразу в `libc/conio/conio.c`** новую функцию рядом с `getch()`:
```c
// Returns scan in high byte, ASCII in low byte.
// Extended keys (arrows, F-keys, PgUp/PgDn, Home/End): ASCII=0, scan code в high byte.
// Plain keys: ASCII в low byte; high byte содержит positional scan (бит 7 = Ctrl/Alt/Shift modifier).
uint16_t getkey(void) __naked {
__asm
ld c, #0x30 ; ESTEX WAITKEY
rst #0x10 ; A=ASCII, D=scan, E=ASCII
ld e, a ; ensure E=ASCII even if E clobbered
ret ; SDCC __sdcccall(1): возврат uint16_t в DE (D=scan, E=ASCII)
__endasm;
}
```
Дополнительно — **в `libc/include/conio.h`** прописать прототип и константы scan-кодов:
```c
uint16_t getkey(void);
/* Scan codes for getkey() high byte when ASCII=0 (extended keys). */
#define KEY_F1 0x0E
#define KEY_F2 0x0F
#define KEY_F3 0x10
#define KEY_F4 0x11
#define KEY_F5 0x12
#define KEY_F6 0x13
#define KEY_F7 0x14
#define KEY_F8 0x15
#define KEY_F9 0x16
#define KEY_F10 0x17
#define KEY_F11 0x18
#define KEY_F12 0x19
#define KEY_END 0x24
#define KEY_DOWN 0x25
#define KEY_PGDN 0x26
#define KEY_LEFT 0x27
#define KEY_RIGHT 0x29
#define KEY_HOME 0x2A
#define KEY_UP 0x2B
#define KEY_PGUP 0x2C
#define KEY_INS 0x23
#define KEY_DEL 0x22
```
**Скан-коды (из docs/converted/ProgrammerManual.txt:2143-2323):**
| Клавиша | scan | Клавиша | scan |
|---|---|---|---|
| F1 | 0x0E | Up | 0x2B |
| F10 | 0x17 | Down | 0x25 |
| F11 | 0x18 | Left | 0x27 |
| F12 | 0x19 | Right | 0x29 |
| | | PgUp | 0x2C |
| | | PgDn | 0x26 |
| | | Home | 0x2A |
| | | End | 0x24 |
### Что **переиспользуем** из существующей libc
- `open/read/lseek/close` — `libc/io/{open,read,lseek}.c` (POSIX wrappers)
- `mem_alloc_pages/mem_free_block/mem_get_page` — `libc/mem/mem_alloc.c`
- `sprinter_page_w3()` — inline `__sfr` write в `libc/include/sprinter.h:113`
- `bank_read_w3/bank_write_w3` — `libc/mem/bank_io_w3.c` (для fallback или v2 multi-page)
- `wrchar(x, y, ch, attr)` — `libc/conio/conio.c:476` (без auto-scroll, идеально для viewport)
- `clrscr_attr(attr)` — `libc/conio/conio.c:395`
- `gotoxy/wherex/wherey` — `libc/conio/conio.c:412-462` (если нужно)
- `kbhit()` — `libc/conio/conio.c:22` (для non-blocking опроса, опционально)
- `dec16/dec8` — `libc/stdio/dec_print.c` (для status bar: текущая строка / total / %)
- `COLOR(fg, bg)` макрос — `libc/include/conio.h:152`
- Цветовые константы `COLOR_*` — `libc/include/conio.h:145`
- `strlen/memcpy/memset` — z80.lib (НЕ переписывать)
---
## Структура исходников
```
examples/mdview/
├── Makefile # стандартный pattern (см. examples/cat/Makefile)
├── mdview.c # Phase 1: всё в одном файле (main, keys, indexing, render)
├── SAMPLE.MD # тестовый markdown
└── README.md # описание и controls
```
После Phase 3 раскидать по модулям (если суммарный размер > ~6KB):
```
mdview.c — main loop, status/menu bars, key dispatch
mdrender.c — line rendering with MD inline parser
mdindex.c — file load + line indexing
```
### Сборка
```makefile
PROJ ?= ../..
SPRINTER_CC := $(PROJ)/bin/sprinter-cc
mdview.exe: mdview.c
$(SPRINTER_CC) --memory small -o $@ mdview.c
```
`--memory small`: код+данные в W2 (DSS даёт нужное число страниц); файл — отдельная EMM-страница в W3.
---
## Структура mdview.c (Phase 1, эскиз)
```c
#include <stdint.h>
#include <stdio.h>
#include <conio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sprinter.h>
#include <sprinter_mem.h>
#define VIEW_TOP 1
#define VIEW_BOT 30 // inclusive
#define VIEW_H 30
#define SCREEN_W 80
#define MAX_LINES 2048
#define FILE_BUF ((char*)0xC000) /* W3 — EMM page mapped here */
#define TAB_STOP 4
#define ATTR_TEXT COLOR(COLOR_WHITE, COLOR_BLACK)
#define ATTR_BAR COLOR(COLOR_WHITE, COLOR_BLUE)
#define ATTR_MENU_K COLOR(COLOR_YELLOW, COLOR_BLUE)
#define ATTR_MENU_T COLOR(COLOR_WHITE, COLOR_BLUE)
static uint16_t line_off[MAX_LINES];
static uint16_t n_lines;
static uint16_t top_line;
static uint16_t file_size;
static uint8_t file_blk;
static char filename[64];
static int load_file(const char *path); // open, alloc EMM page, map W3, read, close
static void index_lines(void); // scan FILE_BUF, fill line_off[]
static void render_status(void); // row 0
static void render_menu(void); // row 31
static void render_line(uint16_t idx, uint8_t row); // one line @ row
static void render_viewport(void); // VIEW_H lines starting from top_line
static void scroll_up(uint16_t n);
static void scroll_down(uint16_t n);
static void help_screen(void); // F1
int main(int argc, char **argv) {
if (argc < 2) { puts("Usage: mdview <file.md>"); return 1; }
if (load_file(argv[1]) < 0) { puts("load error"); return 1; }
index_lines();
clrscr_attr(ATTR_TEXT);
render_menu();
render_status();
render_viewport();
for (;;) {
uint16_t k = getkey();
uint8_t ascii = k & 0xFF;
uint8_t scan = (k >> 8) & 0x7F; // strip mod bit
if (ascii) {
if (ascii == 0x1B) break; // Esc → exit
continue;
}
switch (scan) {
case KEY_F10: goto exit;
case KEY_F1: help_screen(); break;
case KEY_UP: scroll_up(1); break;
case KEY_DOWN: scroll_down(1); break;
case KEY_PGUP: scroll_up(VIEW_H); break;
case KEY_PGDN: scroll_down(VIEW_H); break;
case KEY_HOME: top_line = 0;
render_viewport(); break;
case KEY_END: /* clamp to last viewport */ break;
}
render_status();
}
exit:
mem_free_block(file_blk);
clrscr_attr(ATTR_TEXT);
return 0;
}
```
---
## Verification
### Phase 1
1. Подготовить `SAMPLE.MD` ~5 KB (заголовки, абзацы, списки) — рендериться будет plain.
2. `cd examples/mdview && make`
3. `python make_disk.py mdview.exe SAMPLE.MD → mc.img && ./run_mame.sh`
4. Проверить:
- Status bar показывает `SAMPLE.MD L 1-30 / N X%`
- Menu bar внизу
- ↑/↓: 1 строка
- PgUp/PgDn: 30 строк, корректное clamp на границах
- Home: top_line=0
- End: top_line = total_lines - VIEW_H
- F1: показывает help, любая клавиша возвращает
- F10 / Esc: выход, экран очищен
5. Edge cases: пустой файл, файл из одной строки, файл с очень длинной строкой (>80), CRLF и LF mixed.
### Phase 2-4
Расширять `SAMPLE.MD` с фичами по мере добавления, визуально верифицировать в MAME. Скриншот-сравнение опционально.
### Регрессии
- Никаких изменений в libc на Phase 1 кроме (потенциально) добавления `getkey()` в `libc/conio/conio.c` — если так, прогнать `examples/conio2` и `examples/filetest` чтобы убедиться что ничего не сломалось.
---
## Решения по неоднозначностям
1. **Word-wrap vs truncate**: v1 = truncate (просто). v2 — F2 toggle wrap.
2. **Горизонтальный скроллинг**: v1 — нет; v2 — `←/→` сдвиг viewport по столбцам.
3. **Tab handling**: преобразование при рендере, **tabstop = 4** (стандарт MD). Оригинал в W3 не трогаем.
4. **UTF-8**: рендерим байты как есть. Если файл в CP866 — отрисуется кириллицей через системный фонт. UTF-8 — пока не поддерживаем (визуально будет каша на не-ASCII символах; детект и предупреждение — в v2).
---
### Phase 6 — Полный layout таблиц (deferred)
Сейчас (Phase 4-tables) таблицы рисуются "декоративно" — пайпы и тире
заменяются на box-drawing chars, но ширины колонок берутся как есть из
исходника. Цель Phase 6 — пересчитать таблицу в нормализованный вид:
- **Pre-scan таблицы**: пройти все строки одного table-блока, найти
максимальную ширину каждой колонки (с учётом съеденных inline-маркеров —
визуальный размер, не байтовый).
- **Re-emit в буфере**: при загрузке файла (или при первой встрече таблицы)
переписать строки в FILE_BUF так, чтобы все ячейки одной колонки имели
одинаковую ширину; добавить top/bottom рамки (`┌─┬─┐` / `└─┴─┘`) как
синтетические строки. Это позволит сохранить 1:1 соответствие "логическая
строка → одна viewport row" без специальной логики при рендере.
- **Память**: re-emit может УВЕЛИЧИТЬ файл за счёт padding и доп.рамок.
Если буфер близок к 16KB — отрезать таблицу и пометить её overflow'ом.
- **Горизонтальный скроллинг**: если итоговая ширина таблицы (или любой
строки) > SCREEN_W = 80 — добавить ←/→ для horizontal pan. Это будет
общий механизм для длинных строк (см. также wrap mode), не только таблиц.
- **Выравнивание из separator-row**: `:-` → left, `-:` → right, `:-:` →
center; учитывать при padding'е содержимого ячейки.
- **Шаги реализации**:
1. Walking pass по фенсам/таблицам прямо в `index_lines()` — собрать
extents всех таблиц.
2. Для каждой таблицы — определить ширины колонок.
3. Решение: rewrite-in-buffer (проще для рендера, но мутирует исходник)
vs render-time layout (cleaner, но требует отдельной структуры
описания layout'а на каждую таблицу).
4. Hpan: общий `viewport_x_offset` для всего экрана, или отдельный
"широкий режим" только внутри таблиц.
> Не блокирующая фича. Запускать когда станет понятен типовой источник
> markdown-файлов (узкие читалки → достаточно текущего декоратора;
> широкие README с большими таблицами → нужен полный layout).
---
### Phase ∞ — Кэш рендеренных строк (low priority)
Отложено: текущая скорость более чем достаточна. Активировать если появится
сценарий, где видна задержка PgUp/PgDn (например, при тяжёлом inline-парсере
v2 с UTF-8 / linkifier / таблицами).
**Кэш отформатированных строк** (W3, отдельная EMM-страница):
```
Cache layout (16 KB EMM page, всего 16000 байт используется):
slot 0: 80 chars + 80 attrs = 160 bytes @ offset 0
...
slot 99: 80 chars + 80 attrs = 160 bytes @ offset 15840
Cache tags (W2 static): uint16_t cache_tag[100] = 200 bytes
cache_tag[i] = line_id, или 0xFFFF = invalid
```
Стратегия — **direct map (no LRU)**: `slot = line_id % 100`. Коллизия → вытеснение.
**Batched viewport render**: 2 page-swap'а на ВЕСЬ viewport (cache → file → cache),
не 60 как при наивной реализации.
При сборке: `cache_blk = mem_alloc_pages(1)` после `file_blk`; `mem_free_block` на exit.
---
## Что отложено в v2
- Файлы >16 KB: многостраничное хранение через `mem_alloc_pages(N)`, ленивая загрузка страниц в W3 при доступе.
- Search (Find / Find next) — F3 / F4.
- Toggle highlight on/off — F2.
- Word wrap.
- Image alt-text rendering.
- Tables.
+527
View File
@@ -0,0 +1,527 @@
# « ­: ⥪áâ®¢ë© Markdown viewer ¤«ï Sprinter (`examples/mdview`)
## Context
’¥á⮢ ï ªà㯭 ï § ¤ ç  - ¯à®¢¥à¨âì ­ èã libc ­  ­¥âਢ¨ «ì­®¬ interactive-¯à¨«®¦¥­¨¨ (¯®«­®íªà ­­ë© UI, ä ©«®¢ë© I/O, ¯ àá¥à).  à ««¥«ì­® ¤ áâ å®à®è¨© showcase ¯« âä®à¬ë ¨ ¯®¬®¦¥â ¢ëâ é¨âì ­¥¤®¤¥«ª¨ ¢ conio/io. Š®­¥ç­ ï 楫ì: viewer ¤«ï `.md` ä ©«®¢ á ¯®¤á¢¥âª®© ᨭ⠪á¨á , ­ ¢¨£ æ¨¥© ¯® ⥪áâã ¨ ¯®áâà ­¨ç­ë¬ áªà®««¨­£®¬.
**Ž£à ­¨ç¥­¨ï v1 (§ ä¨ªá¨à®¢ ­ë ¯®«ì§®¢ â¥«¥¬):**
- POSIX file API (`open/read/lseek/close`); FILE*/fread/fgets - ­¥ ¨á¯®«ì§®¢ âì
- Œ ªá¨¬ã¬ 16 KB ­  ®¤¨­ ä ©« (®¤­  EMM-áâà ­¨æ ); ¬­®£®áâà ­¨ç­ë© ०¨¬ - ¢ v2
- ®¤á¢¥âª  ç¥à¥§ 梥â: **à §¬¥à § £®«®¢ª ** > 梥â èà¨äâ ; **bold/italic** > 梥â ä®­  (¬®­®è¨à¨­­ë© ä®­â ¡¥§ ¦¨à­®£®/ªãàᨢ­®£® ­ ç¥àâ ­¨ï)
---
## €àå¨â¥ªâãà 
###  áª« ¤ª  íªà ­  (80x32, ⥪áâ mode 0x03)
```
Row 0: ³ mdview.md L 1-30 / 142 21% ³ < status bar (BG=blue, FG=white)
Row 1: ³ ³
... ³ document viewport (30 rows) ³
Row 30: ³ ³
Row 31: ³ F1 Help F10 Exit³ < menu bar (BG=blue, FG=cyan)
```
- Viewport = 30 áâப x 80 á⮫¡æ®¢.
- Status / menu ७¤¥àïâáï ç¥à¥§ `wrchar()` (¡¥§  ¢â®-áªà®«« ), viewport - ç¥à¥§ `LOCATE` + ¯®á¨¬¢®«ì­ë© `wrchar()` (⮦¥ ¡¥§  ¢â®-áªà®«« , áâ ¢¨â ¨ char ¨ attr §  ®¤¨­ call).
###  ¬ïâì
Memory mode: **`small`** - DSS ®â¢®¤¨â ¯®¤ ­ è ®¡à § **¤¢  ¡ ­ª  (W1 + W2)** = 32 KB á㬬 à­® (CODE ¢ W1, DATA + STACK + HEAP ¢ W2). ⮣® ¤®«¦­® 墠â¨âì çâ®¡ë … § ¢®¤¨âì `__banked` ä㭪樨. W3 ®áâ ñâáï ¯®«­®áâìî ᢮¡®¤­ë¬ ¤«ï ¬ ¯¯¨­£  ¡®«ìè¨å ¡ãä¥à®¢:
```
W0 (0x0000-0x3FFF): ESTEX (system, untouchable)
W1 (0x4000-0x7FFF): CODE (small mode page 1)
W2 (0x8000-0xBFFF): DATA + STACK + HEAP (small mode page 2)
W3 (0xC000-0xFFFF): paged window - ¯¥à¥ª«îç ¥âáï ¬¥¦¤ã EMM-áâà ­¨æ ¬¨:
ÃÄÄ file page: ¨á室­ë© ⥪áâ .md ä ©«  (16 KB)
ÀÄÄ cache page: ªíè ®âä®à¬ â¨à®¢ ­­ëå áâப (Phase 3+)
```
®ç¥¬ã small + W3:
- 32 KB ­  ª®¤+¤ ­­ë¥ á ¡®«ì訬 § ¯ á®¬ > ­¥â ¡ ­ª¨­£ 
- W3 - áâ ­¤ àâ­ë© paged window, ¯®¤ ­¥£® ã ­ á 㦥 ¥áâì `bank_io_w3` API
- v2-à áè¨à¥­¨¥ (multi-page ä ©«ë >16 KB): ¯à®áâ® `mem_alloc_pages(N)` ¨ ¯¥à¥ª«î祭¨¥ áâà ­¨æ ¢ W3 ç¥à¥§ `sprinter_page_w3()`
‘â â¨ª¨ (¢ W2):
- `line_offset[MAX_LINES]` - uint16_t ᬥ饭¨¥ ª ¦¤®© áâப¨ ¢ ä ©«®¢®© áâà ­¨æ¥ (4 KB ­  2048 áâப)
- `cache_tag[CACHE_N]` - uint16_t ⥣ á«®â  (200 ¡ ©â ­  100 ᫮⮢, ¯®ï¢«ï¥âáï ¢ Phase 3)
- `filename[64]`, `top_line`, `total_lines`, `file_size`, `file_blk`, `cache_blk` - ¥¤¨­¨æë ¡ ©â
**FILE_BUF**: `((char*)0xC000)` - 䨪á¨à®¢ ­­ ï  ¤à¥á æ¨ï ¢ W3 ¯®á«¥ ¬ ¯¯¨­£  ­ã¦­®© áâà ­¨æë.
### ®â®ª ¤ ­­ëå
```
main > open() > read() chunks 1KB > write to W1 (mapped EMM page) > close()
> index_lines() (®¤­® ᪠­¨à®¢ ­¨¥, § ¯®«­ï¥â line_offset[])
> render_viewport() + main loop { getkey(); handle(); render_status(); render_viewport() }
```
**’®­ª®áâì á `read()`**: ESTEX READ § ¯¨á뢠¥â ¯® dst-㪠§ â¥«î ¢  ¤à¥á­®¬ ¯à®áâà ­á⢥ ¢ë§ë¢ î饣®. ®áª®«ìªã ¬ë ¬ ¯¯¨¬ EMM-áâà ­¨æã ¢ W3 (0xC000) „Ž ¢ë§®¢  read(), 㪠§ â¥«ì 0xC000+offset ¢ «¨¤¥­. …᫨ ¢ëïá­¨âáï çâ® BIOS âண ¥â W3 ¢® ¢à¥¬ï read (£à ä¨ç¥áª¨© ¢¨¤¥®¡ãä¥à ¯® 㬮«ç ­¨î ¢ W3 ¯à¨ £à ä¨ç¥áª¨å ०¨¬ å, ­® ¢ ⥪á⮢®¬ - ¤®«¦¥­ ¡ëâì ᢮¡®¤¥­) - fallback: ç¨â âì ¢ 1 KB ¡ãä¥à ¢ W2 ¨ ª®¯¨à®¢ âì ¢ W3 ç¥à¥§ `bank_write_w3()`.
---
## ¥ «¨§ æ¨ï - ¯®íâ ¯­ ï
### Phase 1 - Plain text viewer (MVP)
**—â® à ¡®â ¥â:**
- ‡ £à㧪  ä ©«  (`open/read/close`) ¢ W1-áâà ­¨æã
- ˆ­¤¥ªá æ¨ï áâப (LF / CRLF à §¤¥«¨â¥«¨)
- Status bar: ¨¬ï ä ©« , L N-M / Total, ¯à®æ¥­â áªà®««¨­£ 
- Menu bar: `F1 Help F10 Exit`
-  ¢¨£ æ¨ï: ^/v (1 áâப ), PgUp/PgDn (30 áâப), Home/End (­ ç «®/ª®­¥æ), Esc/F10 (¢ë室), F1 (help screen)
- Ž¡à¥§ª  áâப ¤«¨­­¥¥ 80 ᨬ¢®«®¢ (¡¥§ word-wrap)
- –¢¥â : ⥪áâ ¡¥«ë© ­  çñà­®¬; status/menu - ¡¥«ë© ­  ᨭ¥¬
**Šà¨â¨ç­ë¥ ä ©«ë:**
- `examples/mdview/mdview.c` - main, key loop, rendering, indexing (one-file MVP)
- `examples/mdview/Makefile`
- `examples/mdview/SAMPLE.MD` - â¥áâ®¢ë© markdown ä ©«
### Phase 2 - Headers ¨ £®à¨§®­â «ì­ ï «¨­¨ï
**MD ä¨ç¨:**
- `# H1` > ïમ-¦ñ«âë© (COLOR_YELLOW = 14) ­  çñà­®¬
- `## H2` > ïમ-£®«ã¡®© (COLOR_LBLUE = 11)
- `### H3` > ïમ-§¥«ñ­ë© (COLOR_LGREEN = 10)
- `#### H4+` > á¥àë© (COLOR_GREY = 8)
- `---` / `***` ­  ®â¤¥«ì­®© áâப¥ > «¨­¨ï 0xC4 (£®à¨§®­â «ì­ ï à ¬ª  ASCII) ¢® ¢áî è¨à¨­ã
### Phase 3 - Inline emphasis
** àá¥à inline (per-line, runs ¢ ®¤­ã áâபã):**
- `**bold**` > ATTR_TEXT_BOLD
- `*italic*` > ATTR_TEXT_ITALIC
- `_underscore_` > ATTR_TEXT_UNDERSORE
- `` `code` `` > ATTR_TEXT_CODE
- Œ àª¥àë `**`/`*`/`_`/`` ` `` … ७¤¥àïâáï (áꥤ îâáï)
State machine: ®¤¨­  ªâ¨¢­ë© áâ¨«ì ®¤­®¢à¥¬¥­­® (¡¥§ ¢«®¦¥­­®áâ¨); ª®­ä«¨ªâãî騩 ¬ àª¥à ¯à¨ ç㦮¬  ªâ¨¢­®¬ á⨫¥ ã室¨â «¨â¥à «®¬. ‘®áâ®ï­¨¥ á¡à á뢠¥âáï ­  ª ¦¤®© áâப¥.
> **Šíè ®âä®à¬ â¨à®¢ ­­ëå áâப** - ®â«®¦¥­ ¢ á ¬ë© ª®­¥æ, á¬. "Phase ?: ®¯â¨¬¨§ æ¨¨".
> ‘ª®à®á⨠⥪ã饣® ­ ¨¢­®£® ७¤¥à  墠⠥⠭  80x30 = 2400 wrchar / ª ¤à; PgUp/PgDn ¢¨§ã «ì­® ¬£­®¢¥­¥­.
### Phase 4 - Block elements
- Œ àª¨à®¢ ­­ë¥ ᯨ᪨: `- foo`, `* foo`, `+ foo` > ¯à¥ä¨ªá `ù` (0x07) + ¯à®¡¥«; 梥⠬ àª¥à  ïàç¥ ®á­®¢­®£®
- 㬥஢ ­­ë¥ ᯨ᪨: `1. foo`, `2. foo` > ª ª ¥áâì (ç¨á«® ®áâ ¢«ï¥¬)
- Blockquote: áâப¨ á `> ` > ¯à¥ä¨ªá `³` (0xB3) á¥à®£® 梥â , ®áâ «ì­®© ⥪áâ á«¥£ª  ¯à¨£«ãèñ­­ë©
- Fenced code blocks: `` ``` `` ®âªà뢠¥â/§ ªà뢠¥â ¡«®ª; ¢á¥ áâப¨ ¬¥¦¤ã - bg=á¥àë©, ¬®­®è¨à¨­­® (¡¥§ inline-¯ àᨭ£ )
- Indented code blocks (4+ ¯à®¡¥«®¢):  ­ «®£¨ç­® fenced, ­® ¡¥§ ®£® ¬ àª¥à 
**Light nested lists (v1 - ॠ«¨§®¢ ­®):**
- `classify_line()` ¯à®¯ã᪠¥â leading spaces ¯¥à¥¤ ulist/olist/quote ¬ àª¥à®¬,
¢®§¢à é ¥â `content_off` ¯®á«¥ ¬ àª¥à  > `content_off - p_start` = indent + marker
¡¨â ¢ visible col.
- `render_line()` à¨áã¥â leading-spaces ¢ `ATTR_TEXT`, ¯®â®¬ ¬ àª¥à ­  ᤢ¨­ã⮩
¯®§¨æ¨¨ (col = indent). Marker ¢áñ ¥éñ 䨪á¨à®¢ ­ ¯à¨ £®à¨§®­â «ì­®¬ pan'¥.
- HR / header / fence delim ®áâ îâáï áâண® col-0 (CommonMark à §à¥è ¥â ¤® 3
¢¥¤ãé¨å ¯à®¡¥«®¢ ¤«ï ­¨å - ã¯à®á⨫¨).
- Tab-indent > ­¥ à á¯®§­ ñâáï ª ª nesting (⮫쪮 spaces).
**Phase 4-full - ¯®«­ ï ¯®¤¤¥à¦ª  ¢«®¦¥­­®á⨠(deferred):**
- **Tab-indent**: áç¨â âì tab = 4 ¯à®¡¥«  ¤«ï ®¯à¥¤¥«¥­¨ï ã஢­ï.
- **Quote nesting** (`> > foo`): ª ¦¤ë© `>` ¯®¤àï¤ = +1 ã஢¥­ì, ª ¦¤ë© à¨áã¥âáï
®â¤¥«ì­ë¬ `³` ¢ `ATTR_QUOTE_MARKER` (¢¨§ã «ì­ ï "«¥áâ­¨æ " á«¥¢ ).
- **Hanging indent ¢ wrap-continuation**: ª®£¤  `- some very long bullet text
that wraps...` - continuation seg ¤®«¦¥­ ­ ç¨­ âìáï ®â content-col (¯®á«¥
¬ àª¥à ),   ­¥ ®â col 0. ‘¥©ç á continuation ¨¤ñâ ®â col 0 (v1 simplification).
’ॡã¥â åà ­¨âì `marker_width` per «®£¨ç¥áª ï áâப  (8 ¡¨â) ¨«¨ re-classify
first seg ¯à¨ ७¤¥à¥ continuation.
- **Lazy continuation**: áâப¨ ¡¥§ ¬ àª¥à , ­® á ¯à ¢¨«ì­ë¬ indent ¯®¤
¯à¥¤ë¤ã騬 bullet'®¬, ¤®«¦­ë áç¨â âìáï ¯à®¤®«¦¥­¨¥¬ ⮣® bullet' 
(¢¨§ã «ì­® - ®¡é¨© attr).
- **Strict CommonMark indent rules**: ¢«®¦¥­­ë© ¯ã­ªâ ¤®«¦¥­ ¡ëâì ­  indent
? content_col த¨â¥«ï, ¨­ ç¥ áç¨â ¥âáï breakout. 㦥­ ¬¨­¨-stack
 ªâ¨¢­ëå ᯨ᪮¢ ¯à¨ ¨­¤¥ªá æ¨¨.
### Phase 5 - Wrap / Unwrap ¤«¨­­ëå áâப
„¥ä®«â: **wrap on**. F2 ¯¥à¥ª«îç ¥â; ¢ ¬¥­î-¡ à¥ ¯®¤¯¨áì ®âà ¦ ¥â ¤¥©á⢨¥
("Unwrap" ª®£¤  wrap ¢ª«îçñ­, "Wrap" ª®£¤  ¢ëª«î祭).
**v1 - ॠ«¨§®¢ ­®:**
- ޤ¨­ ¬ áᨢ `line_offset[2048]` åà ­¨â ‚ˆ„ˆŒ›… ᥣ¬¥­âë (  ­¥ «®£¨ç¥áª¨¥
áâப¨); ¡¨âë 0..13 - ¡ ©â®¢®¥ ᬥ饭¨¥, ¡¨â 15 - CONT-ä« £ continuation.
- Wrap-०¨¬: soft wrap ­  ¯®á«¥¤­¥¬ ¯à®¡¥«¥ ? 80; hard fallback ¥á«¨
¯à®¡¥«  ­¥â.
- Œ àª¥àë í¬ä §¨á  (`**`/`*`/`_`/`` ` ``) ¨ header-¯à¥ä¨ªáë (`#`/`##`/...) ­¥
ãç¨â뢠îâáï ¢ visible-col ¯à¨ ¯®¨áª¥ â®çª¨ ¯¥à¥­®á .
- "‘¯¥æ¨ «ì­ë¥" «®£¨ç¥áª¨¥ áâப¨ ­¥ wrap' îâáï ¢®®¡é¥ (®¤­  seg-§ ¯¨áì ­ 
«®£¨ç¥áªãî áâபã): fence delim, table row (header/separator/body), HR.
- F2 toggle á®åà ­ï¥â ¢¨§ã «ì­ãî ¯®§¨æ¨î ç¥à¥§ `top_offset` ¢ FILE_BUF.
- Bitmaps (`in_code`/`in_table`/`is_tab_hdr`) ¯¥à¥áâà ¨¢ îâáï ¢¬¥á⥠á
ᥣ¬¥­â ¬¨, ¨­¤¥ªá¨àãîâáï seg-¨­¤¥ªá®¬, ¡¨âë áâ ¢ïâáï ⮫쪮 ­  ¯¥à¢®¬
seg'¥ «®£¨ç¥áª®© áâப¨.
- Continuation-ᥣ¬¥­âë ७¤¥àïâáï ¢ á⨫¥ "v1: ¯«®áª®" - plain text,
­¨ª ª¨å markdown-ª« áá¨ä¨ª æ¨©; padding ¤® ª®­æ  áâப¨ `ATTR_TEXT`.
**v1.5 - ®â«®¦¥­® ¤«ï ¯®«­®© ª à⨭ë wrap:**
- **Hanging indent**: continuation ®â ulist/olist/quote ¤®«¦¥­ ¢ëà ¢­¨¢ âìáï
¯®¤ content,   ­¥ ®â col 0. ’ॡã¥â åà ­¨âì marker_width per «®£¨ç¥áª ï
áâப  ˆ‹ˆ re-classify ¯¥à¢®£® seg'  ¯à¨ ७¤¥à¥ continuation.
- ** á«¥¤®¢ ­¨¥ base_attr**: continuation ®â header'  ¤®«¦¥­ á®åà ­ïâì
梥â; continuation ®â code body - ä®­ ATTR_TEXT_CODE. ’ॡã¥â åà ­¨âì
1 ¡ ©â `base_attr` per seg ˆ‹ˆ lookup ¯¥à¢®£® seg' .
- **Inline emphasis ç¥à¥§ £à ­¨æã**: í¬ä §¨á, ®âªàëâë© ¢ ¯¥à¢®¬ seg'¥ ¨ ­¥
§ ªàëâë©, ¤®«¦¥­ ¯à®¤®«¦ âìáï ¢® ¢â®à®¬. ’ॡã¥â åà ­¨âì emph state per
seg (3 ¡¨â ).
- Compact way: ¤®¡ ¢¨âì ¯ à ««¥«ì­ë© ¬ áᨢ `seg_meta[MAX_SEGS]` ¯® 1 ¡ ©âã
- ¯ ªã¥â marker_width (4 ¡¨â ) + emph_state (3 ¡¨â ) + base_attr_idx
(4 ¡¨â  ¨§ â ¡«¨æë > ­ã¦¥­ 2-¡ ©â®¢ë© seg_meta).
- **Hpan ¤«ï ¤«¨­­ëå áâப**: ¥á«¨ wrap ¢ëª«î祭, ¤®¡ ¢¨âì </> ¤«ï
£®à¨§®­â «ì­®£® áªà®««  >80 cols. ޡ鍩 ¬¥å ­¨§¬ á tables (Phase 6).
**…€‹ˆ‡Ž‚€Ž** (light) - `viewport_x` + ¯®«­ë© re-render ­  ª ¦¤®¥ </>.
- **“᪮७¨¥ hpan ç¥à¥§ ESTEX WINCOPY/WINREST** (deferred): ᥩç á pan
¤¥« ¥â ¯®«­ë© `render_viewport()` = 30 áâப x 80 wrchar. Œ®¦­®
᪮¯¨à®¢ âì áãé¥áâ¢ãî饥 ᮤ¥à¦¨¬®¥ viewport'  ­  N cols ¢«¥¢®/¢¯à ¢®
ç¥à¥§ win-copy, ¯®â®¬ ७¤¥à¨âì ⮫쪮 㧪ãî ¯®«®áã á¯à ¢ /á«¥¢ 
(HPAN_STEP cols x 30 rows ~ 240 wrchar ¢¬¥áâ® 2400). ESTEX SCROLL
£®à¨§®­â «ì ­¥ ¯®¤¤¥à¦¨¢ ¥â - ­ã¦­  ¨¬¥­­® WINCOPY-®¯¥à æ¨ï ¨«¨
rdchar/wrchar loop. €ªâ¨¢¨à®¢ âì ª®£¤  ®éãâ¨âáï â®à¬®§; ᥩç á ­ 
⨯®¢®¬ markdown'¥ ­¥ § ¬¥â­®.
**v2 - ®â¤¥«ì­ ï ä¨ç , ¬¨¬® wrap:**
- Toggle ¯®¤á¢¥âª¨ 楫¨ª®¬ (F3?)
- Search ¯® ⥪áâã (Ctrl+F / F4)
- Links `[text](url)` > ᨭ¨© ¯®¤çñભãâë© text, url ¯àïç¥âáï
- Images `![alt](path)` > `[IMG: alt]`
### Phase 7 - Links ¨ ¯®¨áª (post-v1)
- `[text](url)` > ®âà¨á®¢ âì ⮫쪮 `text` á ïમ-ᨭ¨¬ FG (¢¨§ã «ì­® ¯®¤çñભã⮥)
- `![alt](path)` > `[IMG: alt]` ¢ ᪮¡ª å
- Search ¯® ⥪áâã (F3 / Ctrl+F): ¨­ªà¥¬¥­â «ì­ë©, ¯®¤á¢¥âª  ᮢ¯ ¤¥­¨©
### Phase 8 - F8 Raw / Render toggle
¥à¥ª«îç â¥«ì ०¨¬  ®â®¡à ¦¥­¨ï: ¯à¨ ¢ª«îçñ­­®¬ Raw ¯®ª §ë¢ ¥âáï ¨á室­ë©
⥪áâ ä ©«  ª ª ¥áâì - ¢á¥ markdown-¬ àª¥àë (`#`, `**`, `_`, `` ` ``, `|`,
`>`, `-`, etc.) ७¤¥àïâáï «¨â¥à « ¬¨ á `ATTR_TEXT`, ¡¥§ ª« áá¨ä¨ª æ¨¨.
®«¥§­® ª®£¤ :
- ­ã¦­® 㢨¤¥âì â®ç­ãî à §¬¥âªã (®â« ¦¨¢ ­¨¥ .md, áªà¨­è®âë, ª®¯¨à®¢ ­¨¥)
- markdown-ª« áá¨ä¨ª â®à ®è¨¡áï ¨ å®ç¥âáï 㢨¤¥âì ®à¨£¨­ «
- å®ç¥âáï ¡ëáâà® áà ¢­¨âì "¤®/¯®á«¥" ७¤¥à 
**®¢¥¤¥­¨¥:**
- F8 ¯¥à¥ª«îç ¥â `render_mode` (1=render, 0=raw); ¬¥­î ¯®ª §ë¢ ¥â ®¡à â­®¥
¤¥©á⢨¥ ("Raw" ª®£¤  ᥩç á render, "Render" ª®£¤  ᥩç á raw) - ⮩ ¦¥
«®£¨ª®© çâ® F2/Wrap/Unwrap.
- Raw ०¨¬¥: `render_line()` ¨¤ñâ ¯® ª®à®âª®¬ã ¯ã⨠- ­¨ª ª®£®
`classify_line`, `is_fence_delim`, `is_code_body`, inline-í¬ä §¨á ; ¯à®áâ®
¡ ©â®¢ë© ¤ ¬¯ FILE_BUF ®â seg-offset ¤® next-seg/EOL á tab-expansion ¨
ATTR_TEXT.
-  §¤¥«ì­® ®â F2: ®¡  ०¨¬  ­¥§ ¢¨á¨¬ë (¬®¦­® Raw+Wrap, Raw+Truncate,
Render+Wrap, Render+Truncate). Wrap-«®£¨ª  ¢ `index_lines` à ¡®â ¥â ¢
®¡®¨å á«ãç ïå ®¤¨­ ª®¢® (®¯¨à ¥âáï ­  ¢¨§ã «ì­ë¥ ª®«®­ª¨ ­¥§ ¢¨á¨¬® ®â
à áªà áª¨).
- ‘â âãá-¡ à: ¤®¡ ¢¨âì ¨­¤¨ª â®à `[R]` / `[V]` (Raw / View) ¨«¨ ⥪á⮬
`RAW` à冷¬ á ¨¬¥­¥¬ ä ©« .
**Œ¨­¨¬ «ì­ ï ॠ«¨§ æ¨ï:**
- ޤ¨­ ­®¢ë© static `uint8_t render_mode = 1;`
- `render_line()`: ­  á ¬®¬ ¢¥àåã `if (!render_mode) { ? raw render ? return; }`
- `render_menu()`: ¤®¡ ¢¨âì ïà«ëª F8 à冷¬ á F2.
- ‚ £« ¢­®¬ 横«¥: `case KEY_F8: toggle_render(); break;`
- `toggle_render()` ®â«¨ç ¥âáï ®â `toggle_wrap()` ⥬, çâ® … ¯¥à¥áâà ¨¢ ¥â
`line_offset[]` (wrap-ᥣ¬¥­â æ¨ï ­¥ ¬¥­ï¥âáï), ⮫쪮 ¯¥à¥à¥­¤¥à¨â íªà ­.
---
## API-­®¢¨­ª¨ ¢ libc (¬¨­¨¬ «ì­ë¥)
### `getkey()` - extended key reader (¢ libc)
’¥ªã騩 `getch()` â¥àï¥â scan code à áè¨à¥­­ëå ª« ¢¨è (¢®§¢à é ¥â ⮫쪮 E=ASCII). „®¡ ¢«ï¥¬ **áà §ã ¢ `libc/conio/conio.c`** ­®¢ãî äã­ªæ¨î à冷¬ á `getch()`:
```c
// Returns scan in high byte, ASCII in low byte.
// Extended keys (arrows, F-keys, PgUp/PgDn, Home/End): ASCII=0, scan code ¢ high byte.
// Plain keys: ASCII ¢ low byte; high byte ᮤ¥à¦¨â positional scan (¡¨â 7 = Ctrl/Alt/Shift modifier).
uint16_t getkey(void) __naked {
__asm
ld c, #0x30 ; ESTEX WAITKEY
rst #0x10 ; A=ASCII, D=scan, E=ASCII
ld e, a ; ensure E=ASCII even if E clobbered
ret ; SDCC __sdcccall(1): ¢®§¢à â uint16_t ¢ DE (D=scan, E=ASCII)
__endasm;
}
```
„®¯®«­¨â¥«ì­® - **¢ `libc/include/conio.h`** ¯à®¯¨á âì ¯à®â®â¨¯ ¨ ª®­áâ ­âë scan-ª®¤®¢:
```c
uint16_t getkey(void);
/* Scan codes for getkey() high byte when ASCII=0 (extended keys). */
#define KEY_F1 0x0E
#define KEY_F2 0x0F
#define KEY_F3 0x10
#define KEY_F4 0x11
#define KEY_F5 0x12
#define KEY_F6 0x13
#define KEY_F7 0x14
#define KEY_F8 0x15
#define KEY_F9 0x16
#define KEY_F10 0x17
#define KEY_F11 0x18
#define KEY_F12 0x19
#define KEY_END 0x24
#define KEY_DOWN 0x25
#define KEY_PGDN 0x26
#define KEY_LEFT 0x27
#define KEY_RIGHT 0x29
#define KEY_HOME 0x2A
#define KEY_UP 0x2B
#define KEY_PGUP 0x2C
#define KEY_INS 0x23
#define KEY_DEL 0x22
```
**‘ª ­-ª®¤ë (¨§ docs/converted/ProgrammerManual.txt:2143-2323):**
| Š« ¢¨è  | scan | Š« ¢¨è  | scan |
|---|---|---|---|
| F1 | 0x0E | Up | 0x2B |
| F10 | 0x17 | Down | 0x25 |
| F11 | 0x18 | Left | 0x27 |
| F12 | 0x19 | Right | 0x29 |
| | | PgUp | 0x2C |
| | | PgDn | 0x26 |
| | | Home | 0x2A |
| | | End | 0x24 |
### —â® **¯¥à¥¨á¯®«ì§ã¥¬** ¨§ áãé¥áâ¢ãî饩 libc
- `open/read/lseek/close` - `libc/io/{open,read,lseek}.c` (POSIX wrappers)
- `mem_alloc_pages/mem_free_block/mem_get_page` - `libc/mem/mem_alloc.c`
- `sprinter_page_w3()` - inline `__sfr` write ¢ `libc/include/sprinter.h:113`
- `bank_read_w3/bank_write_w3` - `libc/mem/bank_io_w3.c` (¤«ï fallback ¨«¨ v2 multi-page)
- `wrchar(x, y, ch, attr)` - `libc/conio/conio.c:476` (¡¥§ auto-scroll, ¨¤¥ «ì­® ¤«ï viewport)
- `clrscr_attr(attr)` - `libc/conio/conio.c:395`
- `gotoxy/wherex/wherey` - `libc/conio/conio.c:412-462` (¥á«¨ ­ã¦­®)
- `kbhit()` - `libc/conio/conio.c:22` (¤«ï non-blocking ®¯à®á , ®¯æ¨®­ «ì­®)
- `dec16/dec8` - `libc/stdio/dec_print.c` (¤«ï status bar: ⥪ãé ï áâப  / total / %)
- `COLOR(fg, bg)` ¬ ªà®á - `libc/include/conio.h:152`
- –¢¥â®¢ë¥ ª®­áâ ­âë `COLOR_*` - `libc/include/conio.h:145`
- `strlen/memcpy/memset` - z80.lib (… ¯¥à¥¯¨á뢠âì)
---
## ‘âàãªâãà  ¨á室­¨ª®¢
```
examples/mdview/
ÃÄÄ Makefile # áâ ­¤ àâ­ë© pattern (á¬. examples/cat/Makefile)
ÃÄÄ mdview.c # Phase 1: ¢áñ ¢ ®¤­®¬ ä ©«¥ (main, keys, indexing, render)
ÃÄÄ SAMPLE.MD # â¥áâ®¢ë© markdown
ÀÄÄ README.md # ®¯¨á ­¨¥ ¨ controls
```
®á«¥ Phase 3 à áª¨¤ âì ¯® ¬®¤ã«ï¬ (¥á«¨ á㬬 à­ë© à §¬¥à > ~6KB):
```
mdview.c - main loop, status/menu bars, key dispatch
mdrender.c - line rendering with MD inline parser
mdindex.c - file load + line indexing
```
### ‘¡®àª 
```makefile
PROJ ?= ../..
SPRINTER_CC := $(PROJ)/bin/sprinter-cc
mdview.exe: mdview.c
$(SPRINTER_CC) --memory small -o $@ mdview.c
```
`--memory small`: ª®¤+¤ ­­ë¥ ¢ W2 (DSS ¤ ñ⠭㦭®¥ ç¨á«® áâà ­¨æ); ä ©« - ®â¤¥«ì­ ï EMM-áâà ­¨æ  ¢ W3.
---
## ‘âàãªâãà  mdview.c (Phase 1, í᪨§)
```c
#include <stdint.h>
#include <stdio.h>
#include <conio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sprinter.h>
#include <sprinter_mem.h>
#define VIEW_TOP 1
#define VIEW_BOT 30 // inclusive
#define VIEW_H 30
#define SCREEN_W 80
#define MAX_LINES 2048
#define FILE_BUF ((char*)0xC000) /* W3 - EMM page mapped here */
#define TAB_STOP 4
#define ATTR_TEXT COLOR(COLOR_WHITE, COLOR_BLACK)
#define ATTR_BAR COLOR(COLOR_WHITE, COLOR_BLUE)
#define ATTR_MENU_K COLOR(COLOR_YELLOW, COLOR_BLUE)
#define ATTR_MENU_T COLOR(COLOR_WHITE, COLOR_BLUE)
static uint16_t line_off[MAX_LINES];
static uint16_t n_lines;
static uint16_t top_line;
static uint16_t file_size;
static uint8_t file_blk;
static char filename[64];
static int load_file(const char *path); // open, alloc EMM page, map W3, read, close
static void index_lines(void); // scan FILE_BUF, fill line_off[]
static void render_status(void); // row 0
static void render_menu(void); // row 31
static void render_line(uint16_t idx, uint8_t row); // one line @ row
static void render_viewport(void); // VIEW_H lines starting from top_line
static void scroll_up(uint16_t n);
static void scroll_down(uint16_t n);
static void help_screen(void); // F1
int main(int argc, char **argv) {
if (argc < 2) { puts("Usage: mdview <file.md>"); return 1; }
if (load_file(argv[1]) < 0) { puts("load error"); return 1; }
index_lines();
clrscr_attr(ATTR_TEXT);
render_menu();
render_status();
render_viewport();
for (;;) {
uint16_t k = getkey();
uint8_t ascii = k & 0xFF;
uint8_t scan = (k >> 8) & 0x7F; // strip mod bit
if (ascii) {
if (ascii == 0x1B) break; // Esc > exit
continue;
}
switch (scan) {
case KEY_F10: goto exit;
case KEY_F1: help_screen(); break;
case KEY_UP: scroll_up(1); break;
case KEY_DOWN: scroll_down(1); break;
case KEY_PGUP: scroll_up(VIEW_H); break;
case KEY_PGDN: scroll_down(VIEW_H); break;
case KEY_HOME: top_line = 0;
render_viewport(); break;
case KEY_END: /* clamp to last viewport */ break;
}
render_status();
}
exit:
mem_free_block(file_blk);
clrscr_attr(ATTR_TEXT);
return 0;
}
```
---
## Verification
### Phase 1
1. ®¤£®â®¢¨âì `SAMPLE.MD` ~5 KB (§ £®«®¢ª¨,  ¡§ æë, ᯨ᪨) - ७¤¥à¨âìáï ¡ã¤¥â plain.
2. `cd examples/mdview && make`
3. `python make_disk.py mdview.exe SAMPLE.MD > mc.img && ./run_mame.sh`
4. ஢¥à¨âì:
- Status bar ¯®ª §ë¢ ¥â `SAMPLE.MD L 1-30 / N X%`
- Menu bar ¢­¨§ã
- ^/v: 1 áâப 
- PgUp/PgDn: 30 áâப, ª®à४⭮¥ clamp ­  £à ­¨æ å
- Home: top_line=0
- End: top_line = total_lines - VIEW_H
- F1: ¯®ª §ë¢ ¥â help, «î¡ ï ª« ¢¨è  ¢®§¢à é ¥â
- F10 / Esc: ¢ë室, íªà ­ ®ç¨é¥­
5. Edge cases: ¯ãá⮩ ä ©«, ä ©« ¨§ ®¤­®© áâப¨, ä ©« á ®ç¥­ì ¤«¨­­®© áâப®© (>80), CRLF ¨ LF mixed.
### Phase 2-4
 áè¨àïâì `SAMPLE.MD` á ä¨ç ¬¨ ¯® ¬¥à¥ ¤®¡ ¢«¥­¨ï, ¢¨§ã «ì­® ¢¥à¨ä¨æ¨à®¢ âì ¢ MAME. ‘ªà¨­è®â-áà ¢­¥­¨¥ ®¯æ¨®­ «ì­®.
### ¥£à¥áᨨ
- ¨ª ª¨å ¨§¬¥­¥­¨© ¢ libc ­  Phase 1 ªà®¬¥ (¯®â¥­æ¨ «ì­®) ¤®¡ ¢«¥­¨ï `getkey()` ¢ `libc/conio/conio.c` - ¥á«¨ â ª, ¯à®£­ âì `examples/conio2` ¨ `examples/filetest` ç⮡ë ã¡¥¤¨âìáï çâ® ­¨ç¥£® ­¥ á«®¬ «®áì.
---
## ¥è¥­¨ï ¯® ­¥®¤­®§­ ç­®áâï¬
1. **Word-wrap vs truncate**: v1 = truncate (¯à®áâ®). v2 - F2 toggle wrap.
2. **ƒ®à¨§®­â «ì­ë© áªà®««¨­£**: v1 - ­¥â; v2 - `</>` ᤢ¨£ viewport ¯® á⮫¡æ ¬.
3. **Tab handling**: ¯à¥®¡à §®¢ ­¨¥ ¯à¨ ७¤¥à¥, **tabstop = 4** (áâ ­¤ àâ MD). Žà¨£¨­ « ¢ W3 ­¥ âண ¥¬.
4. **UTF-8**: ७¤¥à¨¬ ¡ ©âë ª ª ¥áâì. …᫨ ä ©« ¢ CP866 - ®âà¨áã¥âáï ª¨à¨««¨æ¥© ç¥à¥§ á¨á⥬­ë© ä®­â. UTF-8 - ¯®ª  ­¥ ¯®¤¤¥à¦¨¢ ¥¬ (¢¨§ã «ì­® ¡ã¤¥â ª è  ­  ­¥-ASCII ᨬ¢®« å; ¤¥â¥ªâ ¨ ¯à¥¤ã¯à¥¦¤¥­¨¥ - ¢ v2).
---
### Phase 6 - ®«­ë© layout â ¡«¨æ (deferred)
‘¥©ç á (Phase 4-tables) â ¡«¨æë à¨áãîâáï "¤¥ª®à â¨¢­®" - ¯ ©¯ë ¨ â¨à¥
§ ¬¥­ïîâáï ­  box-drawing chars, ­® è¨à¨­ë ª®«®­®ª ¡¥àãâáï ª ª ¥áâì ¨§
¨á室­¨ª . –¥«ì Phase 6 - ¯¥à¥áç¨â âì â ¡«¨æã ¢ ­®à¬ «¨§®¢ ­­ë© ¢¨¤:
- **Pre-scan â ¡«¨æë**: ¯à®©â¨ ¢á¥ áâப¨ ®¤­®£® table-¡«®ª , ­ ©â¨
¬ ªá¨¬ «ì­ãî è¨à¨­ã ª ¦¤®© ª®«®­ª¨ (á ãçñ⮬ áꥤ¥­­ëå inline-¬ àª¥à®¢ -
¢¨§ã «ì­ë© à §¬¥à, ­¥ ¡ ©â®¢ë©).
- **Re-emit ¢ ¡ãä¥à¥**: ¯à¨ § £à㧪¥ ä ©«  (¨«¨ ¯à¨ ¯¥à¢®© ¢áâà¥ç¥ â ¡«¨æë)
¯¥à¥¯¨á âì áâப¨ ¢ FILE_BUF â ª, çâ®¡ë ¢á¥ ï祩ª¨ ®¤­®© ª®«®­ª¨ ¨¬¥«¨
®¤¨­ ª®¢ãî è¨à¨­ã; ¤®¡ ¢¨âì top/bottom à ¬ª¨ (`ÚÄÂÄ¿` / `ÀÄÁÄÙ`) ª ª
ᨭâ¥â¨ç¥áª¨¥ áâப¨. â® ¯®§¢®«¨â á®åà ­¨âì 1:1 ᮮ⢥âá⢨¥ "«®£¨ç¥áª ï
áâப  > ®¤­  viewport row" ¡¥§ ᯥ樠«ì­®© «®£¨ª¨ ¯à¨ ७¤¥à¥.
- ** ¬ïâì**: re-emit ¬®¦¥â “‚…‹ˆ—ˆ’œ ä ©« §  áçñâ padding ¨ ¤®¯.à ¬®ª.
…᫨ ¡ãä¥à ¡«¨§®ª ª 16KB - ®â१ âì â ¡«¨æã ¨ ¯®¬¥â¨âì ¥ñ overflow'®¬.
- **ƒ®à¨§®­â «ì­ë© áªà®««¨­£**: ¥á«¨ ¨â®£®¢ ï è¨à¨­  â ¡«¨æë (¨«¨ «î¡®©
áâப¨) > SCREEN_W = 80 - ¤®¡ ¢¨âì </> ¤«ï horizontal pan. â® ¡ã¤¥â
®¡é¨© ¬¥å ­¨§¬ ¤«ï ¤«¨­­ëå áâப (á¬. â ª¦¥ wrap mode), ­¥ ⮫쪮 â ¡«¨æ.
- **‚ëà ¢­¨¢ ­¨¥ ¨§ separator-row**: `:-` > left, `-:` > right, `:-:` >
center; ãç¨â뢠âì ¯à¨ padding'¥ ᮤ¥à¦¨¬®£® ï祩ª¨.
- **˜ £¨ ॠ«¨§ æ¨¨**:
1. Walking pass ¯® 䥭ᠬ/â ¡«¨æ ¬ ¯àאַ ¢ `index_lines()` - ᮡà âì
extents ¢á¥å â ¡«¨æ.
2. „«ï ª ¦¤®© â ¡«¨æë - ®¯à¥¤¥«¨âì è¨à¨­ë ª®«®­®ª.
3. ¥è¥­¨¥: rewrite-in-buffer (¯à®é¥ ¤«ï ७¤¥à , ­® ¬ãâ¨àã¥â ¨á室­¨ª)
vs render-time layout (cleaner, ­® âॡã¥â ®â¤¥«ì­®© áâàãªâãàë
®¯¨á ­¨ï layout'  ­  ª ¦¤ãî â ¡«¨æã).
4. Hpan: ®¡é¨© `viewport_x_offset` ¤«ï ¢á¥£® íªà ­ , ¨«¨ ®â¤¥«ì­ë©
"è¨à®ª¨© ०¨¬" ⮫쪮 ¢­ãâਠ⠡«¨æ.
> ¥ ¡«®ª¨àãîé ï ä¨ç . ‡ ¯ã᪠âì ª®£¤  áâ ­¥â ¯®­ï⥭ ⨯®¢®© ¨áâ®ç­¨ª
> markdown-ä ©«®¢ (㧪¨¥ ç¨â «ª¨ > ¤®áâ â®ç­® ⥪ã饣® ¤¥ª®à â®à ;
> è¨à®ª¨¥ README á ¡®«ì訬¨ â ¡«¨æ ¬¨ > ­ã¦¥­ ¯®«­ë© layout).
---
### Phase ? - Šíè ७¤¥à¥­­ëå áâப (low priority)
Žâ«®¦¥­®: ⥪ãé ï ᪮à®áâì ¡®«¥¥ 祬 ¤®áâ â®ç­ . €ªâ¨¢¨à®¢ âì ¥á«¨ ¯®ï¢¨âáï
áæ¥­ à¨©, £¤¥ ¢¨¤­  § ¤¥à¦ª  PgUp/PgDn (­ ¯à¨¬¥à, ¯à¨ âï¦ñ«®¬ inline-¯ àá¥à¥
v2 á UTF-8 / linkifier / â ¡«¨æ ¬¨).
**Šíè ®âä®à¬ â¨à®¢ ­­ëå áâப** (W3, ®â¤¥«ì­ ï EMM-áâà ­¨æ ):
```
Cache layout (16 KB EMM page, ¢á¥£® 16000 ¡ ©â ¨á¯®«ì§ã¥âáï):
slot 0: 80 chars + 80 attrs = 160 bytes @ offset 0
...
slot 99: 80 chars + 80 attrs = 160 bytes @ offset 15840
Cache tags (W2 static): uint16_t cache_tag[100] = 200 bytes
cache_tag[i] = line_id, ¨«¨ 0xFFFF = invalid
```
‘âà â¥£¨ï - **direct map (no LRU)**: `slot = line_id % 100`. Š®««¨§¨ï > ¢ëâ¥á­¥­¨¥.
**Batched viewport render**: 2 page-swap'  ­  ‚…‘œ viewport (cache > file > cache),
­¥ 60 ª ª ¯à¨ ­ ¨¢­®© ॠ«¨§ æ¨¨.
ਠᡮથ: `cache_blk = mem_alloc_pages(1)` ¯®á«¥ `file_blk`; `mem_free_block` ­  exit.
---
## —â® ®â«®¦¥­® ¢ v2
- ” ©«ë >16 KB: ¬­®£®áâà ­¨ç­®¥ åà ­¥­¨¥ ç¥à¥§ `mem_alloc_pages(N)`, «¥­¨¢ ï § £à㧪  áâà ­¨æ ¢ W3 ¯à¨ ¤®áâ㯥.
- Search (Find / Find next) - F3 / F4.
- Toggle highlight on/off - F2.
- Word wrap.
- Image alt-text rendering.
- Tables.
+165
View File
@@ -0,0 +1,165 @@
# MDVIEW Sample Document
This is a sample Markdown file for testing the Sprinter `mdview` text
viewer. Phase 3 adds inline emphasis: **bold**, *italic* and _underscore_
runs render with distinct background colours.
---
## Section: Inline emphasis
Plain words mixed with **bold words**, *italic words*, _underscore
words_ and `code words` to verify all four styles render with their
own colours.
A single **bold** stretch, then a single *italic* stretch, then a single
_underscore_ stretch, then a single `code` stretch, all on the same line.
A *long italic run that spans multiple words and several columns before
it closes here* and continues plain.
Inline code with punctuation: call `printf("%d\n", x)` then check the
result; or use `argv[0]` to grab the program name.
Unclosed emphasis (open **bold left dangling) — should auto-close at the
end of the line so the next line starts clean.
Conflict cases: **bold with a stray * inside** stays bold, and *italic
with a stray _ inside* stays italic, and `code with **bold** inside`
stays code.
## Section: Lorem ipsum
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim
veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea
commodo consequat.
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum
dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
## Section: Lists
Unordered (dash):
- alpha
- bravo
- charlie
Unordered (asterisk):
* delta
* echo
Unordered (plus):
+ foxtrot
+ golf
Numbered list (digits + period highlighted):
1. first
2. second
3. third
10. tenth — verifies multi-digit numbering
99. ninety-ninth
Numbered with parens:
1) parenthesised one
2) parenthesised two
Nested lists (light v1 — leading spaces enable detection at non-zero col;
no hanging indent in wrap continuation yet):
- top level alpha
- nested level 1 bravo
- nested level 1 charlie
- nested level 2 delta
- nested level 2 echo
- nested level 3 foxtrot
- nested level 1 golf
- top level hotel
Mixed nested:
1. first top
1. nested numeric
2. nested numeric
- mixed bullet under numeric
- another bullet
3. nested numeric
2. second top
## Section: Blockquote
> Single-line blockquote.
> Multi-line blockquote starts here.
> Each line keeps the > prefix highlighted
> while the body stays plain text.
## Section: Code
Inline code: `printf("hello")`.
Fenced block (lines inside should render with code attribute on the
full row, no inline parsing — note the **stars** and *italics* below
remain literal):
```c
int main(void) {
/* **not bold**, *not italic*, `not nested` */
return 42;
}
```
After the block, normal **bold** parsing resumes.
## Section: Long line truncation
This line is intentionally long to verify that the viewer truncates at 80 columns instead of wrapping or scrolling horizontally. Anything past the 80th visible column should simply not appear on screen.
## Section: Tabs
Indented with tabs:
tab-indent level 1
tab-indent level 2
tab-indent level 3
## Section: Blockquote
> Markdown blockquotes start with a greater-than sign at column zero.
> Multiple lines look like this.
### Subsection: deeper heading (H3)
The line above is an H3 — it should render in a colour distinct from
H1 and H2.
#### Subsubsection: H4 and below
H4 (and the rarely-seen H5/H6) all share the H4 colour slot.
***
## Section: Filler
The remaining content exists to make the document scroll past one viewport.
Line 50 ........... approx.
Line 51 ........... .
Line 52 ........... ..
Line 53 ........... ...
Line 54 ........... ....
Line 55 ........... .....
Line 56 ........... ......
Line 57 ........... .......
Line 58 ........... ........
Line 59 ........... .........
Line 60 ........... ..........
End-of-document marker. If you can see this line you can use Home / End
to bounce between the start and finish of the file.
+909
View File
@@ -0,0 +1,909 @@
/*
* mdview — markdown text viewer for Sprinter (Phase 1: plain text).
*
* Layout (80x32):
* row 0 — status bar (filename / line range / total / %)
* rows 1..30 — viewport (30 lines of document text)
* row 31 — menu bar (F1 Help ... F10 Exit)
*
* Navigation: arrows, PgUp/PgDn, Home/End, F1 help, F10/Esc exit.
*
* Memory:
* CODE → W1 (sprinter-cc --memory small)
* STACK/HEAP/DATA → W2
* File buffer → EMM page mapped into W3 at 0xC000 (≤ 16 KB)
*/
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <conio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sprinter.h>
#include <sprinter_mem.h>
/* ---- screen geometry ---------------------------------------------- */
#define SCREEN_W 80
#define SCREEN_H 32
#define VIEW_TOP_ROW 1
#define VIEW_H 30 /* rows 1..30 inclusive */
#define MENU_ROW 31
#define TAB_STOP 4
/* ---- file/memory layout ------------------------------------------- */
#define MAX_FILE 16384
#define MAX_LINES 2048
#define FILE_BUF ((char *)0xC000) /* W3-mapped EMM page */
/* ---- attribute palette -------------------------------------------- */
#define ATTR_TEXT COLOR(COLOR_LIGHTGRAY, COLOR_BLACK)
#define ATTR_TEXT_TITLE1 COLOR(COLOR_YELLOW, COLOR_BLACK)
#define ATTR_TEXT_TITLE2 COLOR(COLOR_LIGHTBLUE, COLOR_BLACK)
#define ATTR_TEXT_TITLE3 COLOR(COLOR_LIGHTGREEN, COLOR_BLACK)
#define ATTR_TEXT_TITLE4 COLOR(COLOR_DARKGRAY, COLOR_BLACK)
#define ATTR_TEXT_BOLD COLOR(COLOR_WHITE, COLOR_BLUE)
#define ATTR_TEXT_ITALIC COLOR(COLOR_LIGHTGREEN, COLOR_BROWN)
#define ATTR_TEXT_UNDERSORE COLOR(COLOR_LIGHTBLUE, COLOR_GREEN)
#define ATTR_TEXT_CODE COLOR(COLOR_WHITE, COLOR_LIGHTGRAY)
#define ATTR_LIST_MARKER COLOR(COLOR_LIGHTCYAN, COLOR_BLACK)
#define ATTR_QUOTE_MARKER COLOR(COLOR_LIGHTMAGENTA,COLOR_BLACK)
#define ATTR_HR COLOR(COLOR_DARKGRAY, COLOR_BLACK)
#define ATTR_TRUNC COLOR(COLOR_WHITE, COLOR_BLACK)
#define ATTR_BAR COLOR(COLOR_WHITE, COLOR_BLUE)
#define ATTR_MENU_T COLOR(COLOR_WHITE, COLOR_BLUE)
#define ATTR_MENU_K COLOR(COLOR_YELLOW, COLOR_BLUE)
/* ---- global state ------------------------------------------------- */
/* line_offset[] semantics depend on wrap_mode:
* wrap_mode == 0 (truncate): one entry per logical line, value = byte
* offset inside FILE_BUF.
* wrap_mode == 1 (wrap): one entry per VISIBLE viewport row. Low
* 14 bits = byte offset of segment start;
* bit 15 (OFF_CONT) marks a continuation of
* the previous logical line — rendered as
* plain text without markdown classification.
* MAX_FILE = 16 KB so offsets fit in 14 bits.
*/
#define OFF_MASK 0x3FFFu
#define OFF_CONT 0x8000u
/* NOTE: explicit `= 0` initialisers are MANDATORY on SDCC z80.
* Uninitialised static storage (`static T x;`) doesn't actually allocate
* space — multiple such declarations end up at the SAME address and
* silently overwrite each other. See memory/sdcc_static_storage_gotcha. */
static uint16_t line_offset[MAX_LINES] = {0};
/* Bitmap: seg i (non-CONT) is inside a fenced code block (between two
* ``` lines). The fence delimiter lines themselves are NOT marked here —
* they are detected dynamically via a 3-byte prefix check. */
static uint8_t in_code[MAX_LINES / 8] = {0};
static uint16_t n_lines = 0;
static uint16_t top_line = 0;
static uint16_t file_size = 0;
static uint8_t file_blk = 0;
static uint8_t wrap_mode = 1; /* default: wrap on */
static uint8_t viewport_x = 0; /* horizontal pan (truncate mode only) */
static char filename[64] = {0};
#define HPAN_STEP 8u
/* ==================================================================
* tiny helpers
* ================================================================== */
static void put_str_attr(uint8_t x, uint8_t y, const char *s, uint8_t attr)
{
while (*s && x < SCREEN_W) {
wrchar(x++, y, *s++, attr);
}
}
static void put_str(uint8_t x, uint8_t y, const char *s)
{
gotoxy(x, y);
puts(s);
}
static void fill_row(uint8_t y, uint8_t attr)
{
for (uint8_t c = 0; c < SCREEN_W; c++) {
wrchar(c, y, ' ', attr);
}
}
/* ==================================================================
* file loading
* ================================================================== */
static int load_file(const char *path)
{
int fd = open(path, O_RDONLY);
if (fd < 0) return -1;
long sz = lseek(fd, 0, SEEK_END);
if (sz < 0 || sz > MAX_FILE) {
close(fd);
return -2;
}
file_size = (uint16_t)sz;
lseek(fd, 0, SEEK_SET);
file_blk = mem_alloc_pages(1);
if (file_blk == 0) {
close(fd);
return -3;
}
uint8_t phys = mem_get_page(file_blk, 0);
sprinter_page_w3(phys);
int n = read(fd, FILE_BUF, file_size);
close(fd);
if (n != (int)file_size) {
return -4;
}
return 0;
}
static void unload_file(void)
{
if (file_blk) {
mem_free_block(file_blk);
file_blk = 0;
}
}
/* ==================================================================
* line indexing
* ================================================================== */
/* Returns 1 iff the seg at index `idx` is the start of a logical line
* that begins with ``` (a fenced-code block delimiter). Continuation
* segs are never fence delimiters. */
static uint8_t is_fence_delim(uint16_t idx)
{
if (idx >= n_lines) return 0;
uint16_t raw = line_offset[idx];
if (raw & OFF_CONT) return 0;
uint16_t off = raw & OFF_MASK;
if ((uint32_t)off + 2 >= (uint32_t)file_size) return 0;
return (uint8_t)(FILE_BUF[off] == '`' &&
FILE_BUF[off+1] == '`' &&
FILE_BUF[off+2] == '`');
}
static uint8_t is_code_body(uint16_t idx)
{
if (idx >= n_lines) return 0;
return (uint8_t)((in_code[idx >> 3] >> (idx & 7)) & 1u);
}
/* Returns 1 iff the logical line at file offset `p_start` should NOT be
* wrapped — it must render on a single seg even if longer than SCREEN_W.
* Currently covers fence delimiters and horizontal rules; tables will be
* added when Phase 4 tables / Phase 6 table layout lands. */
static uint8_t is_nowrap_line(uint16_t p_start)
{
if ((uint32_t)p_start + 2 < (uint32_t)file_size &&
FILE_BUF[p_start] == '`' &&
FILE_BUF[p_start + 1] == '`' &&
FILE_BUF[p_start + 2] == '`') {
return 1;
}
char c0 = FILE_BUF[p_start];
if (c0 == '-' || c0 == '*' || c0 == '_') {
char marker = 0;
uint8_t count = 0;
uint8_t ok = 1;
uint16_t p = p_start;
while (p < file_size) {
char ch = FILE_BUF[p++];
if (ch == '\n' || ch == '\r') break;
if (ch == ' ' || ch == '\t') continue;
if (ch != '-' && ch != '*' && ch != '_') { ok = 0; break; }
if (marker == 0) marker = ch;
else if (ch != marker) { ok = 0; break; }
count++;
}
if (ok && count >= 3) return 1;
}
return 0;
}
/* Line kind returned by classify_line(). */
#define LK_PLAIN 0
#define LK_H1 1
#define LK_H2 2
#define LK_H3 3
#define LK_H4 4 /* also used for H5/H6 */
#define LK_HR 5
#define LK_ULIST 6 /* "- ", "* ", "+ " bullet */
#define LK_OLIST 7 /* "12. " or "12) " number */
#define LK_QUOTE 8 /* "> " blockquote */
/* Classify a logical line.
* - Headers: 1..4 leading '#' followed by space/tab (H5+/H6 collapse to H4).
* - Horizontal rule: line containing only '-', '*' or '_' (one kind, >=3
* of them), optionally separated by spaces/tabs. Matches `---`, `***`,
* `___`, `- - -`, etc. Checked BEFORE ulist so that `- - -` wins.
* - Unordered list: "- ", "* " or "+ " followed by content.
* - Ordered list: one or more digits followed by '.' or ')' then space.
* - Blockquote: '>' optionally followed by space.
* - Otherwise plain text.
* For headers/lists/quotes, *out_content is updated to the offset of the
* first content byte (past the marker and its trailing space).
*/
static uint8_t classify_line(uint16_t p_start, uint16_t *out_content)
{
if (p_start >= file_size) return LK_PLAIN;
char c0 = FILE_BUF[p_start];
/* Header? */
if (c0 == '#') {
uint8_t lvl = 0;
uint16_t p = p_start;
while (p < file_size && FILE_BUF[p] == '#' && lvl < 6) {
lvl++; p++;
}
if (p < file_size && (FILE_BUF[p] == ' ' || FILE_BUF[p] == '\t')) {
p++;
*out_content = p;
if (lvl > 4) lvl = 4;
return lvl; /* LK_H1 .. LK_H4 */
}
/* Bare '#' with no space → not a header, fall through. */
}
/* Horizontal rule? (must precede ulist so `- - -` wins over `- `) */
if (c0 == '-' || c0 == '*' || c0 == '_') {
char marker = 0;
uint8_t count = 0;
uint8_t ok = 1;
uint16_t p = p_start;
while (p < file_size) {
char ch = FILE_BUF[p++];
if (ch == '\n' || ch == '\r') break;
if (ch == ' ' || ch == '\t') continue;
if (ch != '-' && ch != '*' && ch != '_') { ok = 0; break; }
if (marker == 0) marker = ch;
else if (ch != marker) { ok = 0; break; }
count++;
}
if (ok && count >= 3) return LK_HR;
}
/* For lists and blockquotes we additionally allow leading spaces
* (light nested-list support). HR / header / fence delim stay
* strict col-0 — done above with `c0`. */
uint16_t lp = p_start;
while (lp < file_size && FILE_BUF[lp] == ' ') lp++;
char cl = (lp < file_size) ? FILE_BUF[lp] : 0;
/* Unordered list? */
if ((cl == '-' || cl == '*' || cl == '+') &&
(lp + 1) < file_size &&
FILE_BUF[lp + 1] == ' ') {
*out_content = (uint16_t)(lp + 2);
return LK_ULIST;
}
/* Ordered list? */
if (cl >= '0' && cl <= '9') {
uint16_t p = lp;
while (p < file_size && FILE_BUF[p] >= '0' && FILE_BUF[p] <= '9') p++;
if (p < file_size && (FILE_BUF[p] == '.' || FILE_BUF[p] == ')') &&
(p + 1) < file_size && FILE_BUF[p + 1] == ' ') {
*out_content = (uint16_t)(p + 2);
return LK_OLIST;
}
}
/* Blockquote? */
if (cl == '>') {
uint16_t p = (uint16_t)(lp + 1);
if (p < file_size && FILE_BUF[p] == ' ') p++;
*out_content = p;
return LK_QUOTE;
}
return LK_PLAIN;
}
/* Visible column count for the marker prefix when rendered, including
* any leading indent for nested lists/quotes (the render side keeps the
* 1 byte = 1 visible col invariant for those, so content_off - p_start
* is the visible width). */
static uint8_t marker_visible_col(uint8_t kind, uint16_t p_start, uint16_t content_off)
{
switch (kind) {
case LK_ULIST:
case LK_QUOTE:
case LK_OLIST: return (uint8_t)(content_off - p_start);
default: return 0; /* PLAIN / headers (skipped) */
}
}
static void index_lines(void)
{
n_lines = 0;
memset(in_code, 0, sizeof(in_code));
if (file_size == 0) return;
if (!wrap_mode) {
/* Truncate: one entry per logical line. */
line_offset[n_lines++] = 0;
for (uint16_t p = 0; p < file_size; p++) {
if (FILE_BUF[p] == '\n' &&
p + 1 < file_size && n_lines < MAX_LINES) {
line_offset[n_lines++] = (uint16_t)(p + 1);
}
}
} else {
/* Wrap: walk logical lines, split each into one or more segs. */
uint16_t p = 0;
while (p < file_size && n_lines < MAX_LINES) {
/* Find end of logical line (\n or EOF). */
uint16_t line_end = p;
while (line_end < file_size && FILE_BUF[line_end] != '\n') line_end++;
/* Emit the first seg of this logical line (no CONT). */
line_offset[n_lines++] = p;
if (!is_nowrap_line(p)) {
/* Determine marker prefix and starting visible column. */
uint16_t content_off = p;
uint8_t kind = classify_line(p, &content_off);
uint8_t col = marker_visible_col(kind, p, content_off);
uint16_t q;
if (kind == LK_H1 || kind == LK_H2 || kind == LK_H3 || kind == LK_H4 ||
kind == LK_ULIST || kind == LK_OLIST || kind == LK_QUOTE) {
q = content_off;
} else {
q = p;
}
uint16_t last_space = 0xFFFFu; /* byte pos AFTER last space */
while (q < line_end && n_lines < MAX_LINES) {
char ch = FILE_BUF[q];
/* Inline emphasis markers are zero-width. */
if (ch == '*' || ch == '_' || ch == '`') {
q++;
continue;
}
if (ch == '\t') {
uint8_t tgt = (uint8_t)((col & (uint8_t)~(TAB_STOP - 1)) + TAB_STOP);
if (tgt > SCREEN_W) tgt = SCREEN_W;
col = tgt;
q++;
} else {
if (ch == ' ') last_space = (uint16_t)(q + 1);
col++;
q++;
}
if (col >= SCREEN_W && q < line_end) {
uint16_t wrap_at = (last_space != 0xFFFFu &&
last_space > (line_offset[n_lines - 1] & OFF_MASK))
? last_space : q;
if (wrap_at >= line_end) break;
line_offset[n_lines++] = (uint16_t)(wrap_at | OFF_CONT);
col = 0;
last_space = 0xFFFFu;
q = wrap_at;
}
}
}
p = (line_end < file_size) ? (uint16_t)(line_end + 1) : line_end;
}
}
/* Fence/code-body bitmap pass — only non-CONT segs participate. */
{
uint8_t in_block = 0;
for (uint16_t i = 0; i < n_lines; i++) {
if (line_offset[i] & OFF_CONT) continue;
if (is_fence_delim(i)) {
in_block = (uint8_t)!in_block;
} else if (in_block) {
in_code[i >> 3] |= (uint8_t)(1u << (i & 7));
}
}
}
}
/* ==================================================================
* rendering
* ================================================================== */
/* Inline emphasis tracking (one style active at a time, no nesting). */
#define EM_NONE 0
#define EM_BOLD 1
#define EM_ITALIC 2
#define EM_UNDER 3
#define EM_CODE 4
static void render_line(uint16_t line_idx, uint8_t row)
{
uint8_t col = 0;
uint8_t base_attr = ATTR_TEXT;
uint8_t cur_attr = ATTR_TEXT;
uint8_t emph = EM_NONE;
uint8_t parse_inline = 1; /* skip inline parsing for fenced code body */
if (line_idx < n_lines) {
uint16_t raw = line_offset[line_idx];
uint16_t p = raw & OFF_MASK;
uint16_t content_off = p;
uint8_t is_cont = (raw & OFF_CONT) ? 1 : 0;
/* Right bound for this seg: start of next seg, or file end. */
uint16_t seg_end = (line_idx + 1 < n_lines)
? (uint16_t)(line_offset[line_idx + 1] & OFF_MASK)
: file_size;
if (seg_end > file_size) seg_end = file_size;
uint8_t kind;
/* Continuation seg → render plain text from the seg's offset up to
* either the next seg's offset or end-of-line. No markdown
* classification, no marker prefixes, inline markers stay literal
* (we drop them silently so the visible output matches the wrap
* algorithm's column count). */
if (is_cont) {
while (p < seg_end && col < SCREEN_W) {
char ch = FILE_BUF[p++];
if (ch == '\n' || ch == '\r') break;
if (ch == '*' || ch == '_' || ch == '`') continue;
if (ch == '\t') {
uint8_t tgt = (uint8_t)((col & (uint8_t)~(TAB_STOP - 1)) + TAB_STOP);
if (tgt > SCREEN_W) tgt = SCREEN_W;
while (col < tgt) wrchar(col++, row, ' ', ATTR_TEXT);
} else {
wrchar(col++, row, ch, ATTR_TEXT);
}
}
while (col < SCREEN_W) wrchar(col++, row, ' ', ATTR_TEXT);
return;
}
/* Fenced code block: delimiter line → blank row; body lines render
* verbatim in ATTR_TEXT_CODE without any inline-emphasis parsing. */
if (is_fence_delim(line_idx)) {
while (col < SCREEN_W) wrchar(col++, row, ' ', ATTR_TEXT);
return;
}
if (is_code_body(line_idx)) {
base_attr = ATTR_TEXT_CODE;
cur_attr = ATTR_TEXT_CODE;
parse_inline = 0;
kind = LK_PLAIN;
} else {
kind = classify_line(p, &content_off);
}
if (kind == LK_HR) {
while (col < SCREEN_W) wrchar(col++, row, 0xC4, ATTR_HR);
return;
}
if (kind >= LK_H1 && kind <= LK_H4) {
switch (kind) {
case LK_H1: base_attr = ATTR_TEXT_TITLE1; break;
case LK_H2: base_attr = ATTR_TEXT_TITLE2; break;
case LK_H3: base_attr = ATTR_TEXT_TITLE3; break;
default: base_attr = ATTR_TEXT_TITLE4; break;
}
cur_attr = base_attr;
p = content_off;
} else if (kind == LK_ULIST) {
/* Light nested-list support: any leading spaces before '-' / '*'
* / '+' render as plain spaces (preserves nesting visually);
* then a bullet + a content space. */
uint16_t q = p;
while (q + 2 < content_off && col < SCREEN_W && FILE_BUF[q] == ' ') {
wrchar(col++, row, ' ', ATTR_TEXT);
q++;
}
if (col < SCREEN_W) wrchar(col++, row, 0x07, ATTR_LIST_MARKER); /* • */
if (col < SCREEN_W) wrchar(col++, row, ' ', ATTR_TEXT);
p = content_off;
} else if (kind == LK_OLIST) {
/* Indent (if any) → spaces; digits and '.' or ')' in marker
* colour; trailing space in plain. */
uint16_t q = p;
while (q < content_off && col < SCREEN_W && FILE_BUF[q] == ' ') {
wrchar(col++, row, ' ', ATTR_TEXT);
q++;
}
while (q < content_off - 1 && col < SCREEN_W) {
wrchar(col++, row, FILE_BUF[q++], ATTR_LIST_MARKER);
}
if (col < SCREEN_W) wrchar(col++, row, ' ', ATTR_TEXT);
p = content_off;
} else if (kind == LK_QUOTE) {
/* Indent (if any) → spaces; '│' (0xB3) prefix in marker
* colour, then a space, then content. */
uint16_t q = p;
while (q + 1 < content_off && col < SCREEN_W && FILE_BUF[q] == ' ') {
wrchar(col++, row, ' ', ATTR_TEXT);
q++;
}
if (col < SCREEN_W) wrchar(col++, row, 0xB3, ATTR_QUOTE_MARKER);
if (col < SCREEN_W) wrchar(col++, row, ' ', ATTR_TEXT);
p = content_off;
}
/* `cc` is the VISIBLE column inside the content area (the part
* after any fixed marker). When viewport_x > 0 (only possible in
* truncate mode), chars at cc < viewport_x are skipped silently
* so the user can pan past the marker. The marker itself was
* already rendered above at fixed screen cols 0..(col-1). */
uint8_t cc = 0;
while (p < seg_end && col < SCREEN_W) {
char ch = FILE_BUF[p];
if (ch == '\n' || ch == '\r') break;
/* --- inline emphasis markers (consumed, not rendered) ---
* Only one style active at a time. A marker that would conflict
* with the currently active style is rendered as a literal char.
* Skipped entirely inside fenced code blocks (parse_inline=0).
*/
if (parse_inline) {
if (ch == '*' && (p + 1) < file_size && FILE_BUF[p + 1] == '*') {
if (emph == EM_NONE) {
emph = EM_BOLD; cur_attr = ATTR_TEXT_BOLD;
p += 2; continue;
}
if (emph == EM_BOLD) {
emph = EM_NONE; cur_attr = base_attr;
p += 2; continue;
}
/* conflict (italic/under active) — fall through, treat
* the first '*' as literal. */
}
if (ch == '*') {
if (emph == EM_NONE) {
emph = EM_ITALIC; cur_attr = ATTR_TEXT_ITALIC;
p++; continue;
}
if (emph == EM_ITALIC) {
emph = EM_NONE; cur_attr = base_attr;
p++; continue;
}
}
if (ch == '_') {
if (emph == EM_NONE) {
emph = EM_UNDER; cur_attr = ATTR_TEXT_UNDERSORE;
p++; continue;
}
if (emph == EM_UNDER) {
emph = EM_NONE; cur_attr = base_attr;
p++; continue;
}
}
if (ch == '`') {
if (emph == EM_NONE) {
emph = EM_CODE; cur_attr = ATTR_TEXT_CODE;
p++; continue;
}
if (emph == EM_CODE) {
emph = EM_NONE; cur_attr = base_attr;
p++; continue;
}
}
}
p++;
if (ch == '\t') {
/* Tab expands in CONTENT-col space (cc), each generated
* space is then conditionally rendered through hpan. */
uint8_t tgt = (uint8_t)((cc & (uint8_t)~(TAB_STOP - 1)) + TAB_STOP);
while (cc < tgt && col < SCREEN_W) {
if (cc >= viewport_x) wrchar(col++, row, ' ', cur_attr);
cc++;
}
} else {
if (cc >= viewport_x && col < SCREEN_W) {
wrchar(col++, row, ch, cur_attr);
}
cc++;
}
}
/* Right-edge truncation indicator: when in truncate mode and the
* main loop stopped because the screen ran out (col == SCREEN_W),
* peek forward past any zero-width inline markers — if there's
* still real visible content before the end of the line, overlay
* the last screen cell with a bright '>'. */
if (!wrap_mode && col >= SCREEN_W) {
uint16_t pp = p;
while (pp < seg_end) {
char c = FILE_BUF[pp];
if (c == '\n' || c == '\r') break;
if (c == '*' || c == '_' || c == '`') { pp++; continue; }
wrchar(SCREEN_W - 1, row, '>', ATTR_TRUNC);
break;
}
}
}
/* Pad rest of the row so previous render content is wiped. For code-
* body rows use the code attribute so the block extends to the right
* edge — visually distinguishes the block from surrounding text. */
{
uint8_t pad_attr = (parse_inline ? ATTR_TEXT : ATTR_TEXT_CODE);
while (col < SCREEN_W) wrchar(col++, row, ' ', pad_attr);
}
}
static void render_viewport(void)
{
for (uint8_t i = 0; i < VIEW_H; i++) {
render_line((uint16_t)(top_line + i), (uint8_t)(VIEW_TOP_ROW + i));
}
}
static uint8_t calc_pct(void)
{
uint8_t pct;
if (n_lines <= VIEW_H)
pct = 100;
else
pct = (uint8_t)((uint32_t)top_line * 100UL / (uint32_t)(n_lines - VIEW_H));
return pct;
}
static void render_full_status(void)
{
uint16_t last = top_line + VIEW_H;
if (last > n_lines) last = n_lines;
uint8_t pct = calc_pct();
fill_row(0, ATTR_BAR);
gotoxy(1, 0);
printf("MDVIEW %s", filename);
gotoxy(45, 0);
printf("\xB3 L %5u-%u / %u", (top_line + 1), last, n_lines);
gotoxy(70, 0);
printf("\xB3 %3u%%", pct);
}
static void render_updated_status(void)
{
uint16_t last = top_line + VIEW_H;
if (last > n_lines) last = n_lines;
uint8_t pct = calc_pct();
gotoxy(45, 0);
printf("\xB3 L %5u-%u / %u", (top_line + 1), last, n_lines);
gotoxy(70, 0);
printf("\xB3 %3u%%", pct);
}
static void render_menu(void)
{
fill_row(MENU_ROW, ATTR_MENU_T);
put_str_attr(1, MENU_ROW, "F1", ATTR_MENU_K);
put_str_attr(4, MENU_ROW, "Help", ATTR_MENU_T);
put_str_attr(10, MENU_ROW, "F2", ATTR_MENU_K);
/* Label reflects what F2 will DO: when wrap is on, F2 turns it off. */
put_str_attr(13, MENU_ROW, wrap_mode ? "Unwrap" : "Wrap ", ATTR_MENU_T);
put_str_attr(SCREEN_W - 10, MENU_ROW, "F10", ATTR_MENU_K);
put_str_attr(SCREEN_W - 5, MENU_ROW, "Exit", ATTR_MENU_T);
}
/* ==================================================================
* scrolling
* ================================================================== */
static void clamp_top(void)
{
if (n_lines <= VIEW_H) {
top_line = 0;
} else if (top_line > n_lines - VIEW_H) {
top_line = (uint16_t)(n_lines - VIEW_H);
}
}
static void scroll_up(uint16_t n)
{
uint16_t new_top_line = (top_line >= n) ? (uint16_t)(top_line - n) : 0;
if( new_top_line != top_line) {
top_line = new_top_line;
if (n == 1) {
scroll(0, VIEW_TOP_ROW, SCREEN_W, VIEW_H, 2, 0);
render_line((uint16_t)(top_line), (uint8_t)(VIEW_TOP_ROW));
} else {
render_viewport();
}
}
}
static void scroll_down(uint16_t n)
{
uint16_t new_top_line = (top_line + n < n_lines - VIEW_H) ?
(uint16_t)(top_line + n) : n_lines - VIEW_H;
if( new_top_line != top_line) {
top_line = new_top_line;
if (n == 1) {
scroll(0, VIEW_TOP_ROW, SCREEN_W, VIEW_H, 1, 0);
render_line((uint16_t)(top_line + VIEW_H - 1), (uint8_t)(VIEW_TOP_ROW + VIEW_H - 1));
} else {
clamp_top();
render_viewport();
}
}
}
/* Horizontal pan (truncate mode only). Each step is HPAN_STEP visible
* columns; clamped to [0, 240] — enough for any real-world long line
* within a 16 KB file. */
static void scroll_h(int8_t delta)
{
if (wrap_mode) return;
int16_t nx = (int16_t)viewport_x + delta;
if (nx < 0) nx = 0;
if (nx > 240) nx = 240;
if ((uint8_t)nx != viewport_x) {
viewport_x = (uint8_t)nx;
render_viewport();
}
}
/* Toggle wrap/truncate and rebuild the seg index. Tries to preserve
* the user's scroll position by remembering the byte offset of the
* current top seg and finding the seg that contains that offset after
* the rebuild. Also resets horizontal pan — wrap mode doesn't use it. */
static void toggle_wrap(void)
{
uint16_t top_off = (top_line < n_lines)
? (uint16_t)(line_offset[top_line] & OFF_MASK) : 0;
wrap_mode = wrap_mode ? 0u : 1u;
viewport_x = 0;
index_lines();
/* Largest i such that the i-th seg's offset is ≤ top_off. */
uint16_t i = 0;
while ((uint16_t)(i + 1) < n_lines &&
(uint16_t)(line_offset[i + 1] & OFF_MASK) <= top_off) {
i++;
}
top_line = i;
clamp_top();
render_menu();
render_full_status();
render_viewport();
}
/* ==================================================================
* help screen
* ================================================================== */
static const char * const help_lines[] = {
"",
" MDVIEW v0.1 - Markdown text viewer for Sprinter",
" =================================================",
"",
" Navigation:",
" Up / Down - scroll one line",
" PgUp / PgDn - scroll one page",
" Home - go to top of document",
" End - go to bottom of document",
" Left / Right - pan horizontally (Unwrap mode only)",
"",
" Other:",
" F1 - show this help",
" F2 - toggle wrap / unwrap of long lines",
" F10 / Esc - exit",
"",
" Press any key to return...",
(const char *)0
};
static void help_screen(void)
{
clrscr_attr(ATTR_TEXT);
for (uint8_t i = 0; help_lines[i]; i++) {
put_str_attr(0, (uint8_t)(i + 1), help_lines[i], ATTR_TEXT);
}
(void)getkey();
clrscr_attr(ATTR_TEXT);
render_menu();
render_full_status();
render_viewport();
}
/* ==================================================================
* error
* ================================================================== */
static void die(const char *msg)
{
clrscr_attr(ATTR_TEXT);
puts(msg);
puts("Press any key to exit...");
(void)getkey();
}
/* ==================================================================
* main
* ================================================================== */
int main(int argc, char **argv)
{
const char *path;
if (argc >= 2) path = argv[1];
else path = "SAMPLE.MD";
/* Copy path → static filename[] for the status bar. */
{
uint8_t i = 0;
while (i < (uint8_t)(sizeof(filename) - 1) && path[i]) {
filename[i] = path[i];
i++;
}
filename[i] = 0;
}
int rc = load_file(path);
if (rc < 0) {
clrscr_attr(ATTR_TEXT);
puts("mdview: cannot load file");
puts(path);
switch (rc) {
case -1: puts("(open failed)"); break;
case -2: puts("(size > 16K or seek failed)"); break;
case -3: puts("(mem_alloc_pages failed)"); break;
case -4: puts("(short read)"); break;
}
puts("Press any key to exit...");
(void)getkey();
return 1;
}
index_lines();
if (n_lines == 0) {
die("mdview: empty file");
unload_file();
return 1;
}
set_videotextmode(TEXT_MODE_80x32);
clrscr_attr(ATTR_TEXT);
render_menu();
render_full_status();
render_viewport();
for (;;) {
uint16_t k = getkey();
uint8_t ascii = (uint8_t)(k & 0xFF);
uint8_t scan = (uint8_t)((k >> 8) & 0x7F);
if (ascii) {
if (ascii == 0x1B) break; /* Esc */
continue;
}
switch (scan) {
case KEY_F10: goto exit_loop;
case KEY_F1: help_screen(); break;
case KEY_F2: toggle_wrap(); break;
case KEY_UP: scroll_up(1); break;
case KEY_DOWN: scroll_down(1); break;
case KEY_LEFT: scroll_h(-(int8_t)HPAN_STEP); break;
case KEY_RIGHT: scroll_h(+(int8_t)HPAN_STEP); break;
case KEY_PGUP: scroll_up(VIEW_H); break;
case KEY_PGDN: scroll_down(VIEW_H); break;
case KEY_HOME: top_line = 0;
render_viewport(); break;
case KEY_END: top_line = (n_lines > VIEW_H)
? (uint16_t)(n_lines - VIEW_H) : 0;
render_viewport(); break;
default: continue;
}
render_updated_status();
}
exit_loop:
unload_file();
clrscr_attr(ATTR_TEXT);
return 0;
}
-5
View File
@@ -1,5 +0,0 @@
# Build mem_test.exe — uses lib/sprinter.lib in TINY memory mode.
PROJ_ROOT := $(abspath $(CURDIR)/../..)
EXAMPLE := mem_test
include $(PROJ_ROOT)/examples/example.mk
-87
View File
@@ -1,87 +0,0 @@
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <sprinter.h>
#include <sprinter_mem.h>
/*
* Exercises the ESTEX EMM page allocator + bank_read/bank_write helpers.
*
* Plan:
* 1. Show free-page count via mem_info.
* 2. Allocate 3 pages → block id.
* 3. For each page in the block, write a recognizable pattern via
* bank_write, then read it back via bank_read into a near buffer.
* 4. Cross-check the readback to prove pages are independent.
* 5. Free the block, show the free-page count again.
*/
static void show_mem(const char *label)
{
uint16_t total, free_pages;
mem_info(&total, &free_pages);
printf(" %s total=%u free=%u (each page = 16 KB)\n",
label, total, free_pages);
}
int main(void)
{
puts("Sprinter page allocator demo");
puts("");
show_mem("before:");
uint8_t blk = mem_alloc_pages(3);
if (blk == 0) {
puts(" mem_alloc_pages(3) failed");
(void)getchar();
return 1;
}
printf(" allocated block id = %u (3 pages)\n", blk);
show_mem("after alloc:");
/* Show the physical page numbers we got. */
for (uint8_t i = 0; i < 3; i++) {
uint8_t phys = mem_get_page(blk, i);
printf(" page %u of block = phys 0x%02X\n", i, phys);
}
/* Write a 32-byte pattern at offset 0 of each page; pattern depends
* on page index so we can distinguish them. */
char buf[33];
for (uint8_t i = 0; i < 3; i++) {
uint8_t phys = mem_get_page(blk, i);
for (int j = 0; j < 32; j++) {
buf[j] = 'A' + (char)i; /* page 0 -> 'A', page 1 -> 'B', page 2 -> 'C' */
}
buf[32] = 0;
bank_write(phys, 0, buf, 32);
printf(" wrote 32 x '%c' to page %u (phys 0x%02X)\n",
'A' + i, i, phys);
}
/* Read back and verify each page has its own pattern. */
puts("");
puts("readback:");
for (uint8_t i = 0; i < 3; i++) {
uint8_t phys = mem_get_page(blk, i);
memset(buf, 0, sizeof(buf));
bank_read(phys, 0, buf, 32);
buf[32] = 0;
printf(" page %u (phys 0x%02X) first 32 bytes: \"%s\"\n",
i, phys, buf);
}
/* Single-byte API spot check. */
bank_store_byte(mem_get_page(blk, 1), 100, 0x42);
uint8_t got = bank_load_byte(mem_get_page(blk, 1), 100);
printf("\n store/load single byte at page1[100]: 0x%02X (expect 0x42)\n", got);
mem_free_block(blk);
show_mem("after free:");
puts("");
puts("Press any key to exit.");
(void)getchar();
return 0;
}
-5
View File
@@ -1,5 +0,0 @@
# Build mouse_test.exe — uses lib/sprinter.lib in TINY memory mode.
PROJ_ROOT := $(abspath $(CURDIR)/../..)
EXAMPLE := mouse
include $(PROJ_ROOT)/examples/example.mk
-80
View File
@@ -1,80 +0,0 @@
/*
* mouse_test — exercises the libc/io/mouse driver wrappers.
*
* Loop: shows live cursor state (pixel + char coordinates + buttons)
* and the current sensitivity values. Press +/- to change horizontal
* sensitivity, [/] to change vertical. ESC quits.
*/
#include <stdio.h>
#include <conio.h>
#include <mouse.h>
int main(void)
{
textattr(COLOR(COLOR_LIGHTGRAY, COLOR_BLACK));
clrscr();
cputs("Mouse test - move mouse and click.\r\n"
" 1 / 2 : decrease / increase horizontal sensitivity\r\n"
" 3 / 4 : decrease / increase vertical sensitivity\r\n"
" ESC : exit\r\n");
if (mouse_init() < 0) {
cputs("mouse_init failed - no driver installed\r\n");
return 1;
}
/* Text mode 03h: 80 chars * 8 = 640 px wide, 32 * 8 = 256 px tall. */
mouse_bounds_x(0, 639);
mouse_bounds_y(0, 255);
mouse_show();
mouse_state_t st;
int last_x = -1, last_y = -1;
uint8_t last_btn = 0xFF;
/* Sensitivity is a "raw steps per cursor pixel" divider: smaller =
* faster cursor. Force a known starting value so the display
* matches what's active in the driver even when GET returns 0
* (some drivers / MAME stub). */
uint8_t sens_x = 2;
uint8_t sens_y = 2;
mouse_set_sensitivity(sens_x, sens_y);
int sens_dirty = 1;
while (1) {
mouse_read(&st);
if (st.x != last_x || st.y != last_y || st.buttons != last_btn) {
gotoxy(0, 6);
cprintf("x=%4u y=%4u text(%2u,%2u) buttons=0x%02X L%c R%c ",
st.x, st.y,
st.x / 8, st.y / 8,
st.buttons,
(st.buttons & 1) ? '*' : '.',
(st.buttons & 2) ? '*' : '.');
last_x = st.x;
last_y = st.y;
last_btn = st.buttons;
}
if (sens_dirty) {
gotoxy(0, 8);
cprintf("sensitivity horz=%3u vert=%3u ", sens_x, sens_y);
sens_dirty = 0;
}
if (!kbhit()) continue;
int k = getch();
if (k == 27) break; /* ESC */
if (k == '1' && sens_x > 1) { sens_x -= 1; sens_dirty = 1; }
if (k == '2' && sens_x < 254) { sens_x += 1; sens_dirty = 1; }
if (k == '3' && sens_y > 1) { sens_y -= 1; sens_dirty = 1; }
if (k == '4' && sens_y < 254) { sens_y += 1; sens_dirty = 1; }
if (sens_dirty)
mouse_set_sensitivity(sens_x, sens_y);
}
mouse_hide();
gotoxy(0, 10);
cputs("press any key to exit");
(void)getch();
return 0;
}
-7
View File
@@ -1,7 +0,0 @@
# Build open_env_test.exe — uses lib/sprinter.lib in TINY memory mode.
PROJ_ROOT := $(abspath $(CURDIR)/../..)
EXAMPLE := openenv
MEMORY := big
EXTRA_FLAGS :=
include $(PROJ_ROOT)/examples/example.mk
-153
View File
@@ -1,153 +0,0 @@
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <sprinter.h>
/*
* Exercises the new open() state machine + ESTEX env API.
*
* Touches the floppy: creates TMP1.TXT and TMP2.TXT, writes a few bytes,
* verifies O_CREAT / O_EXCL / O_TRUNC / O_APPEND behaviour, then deletes.
*/
extern uint8_t estex_file_handle;
static void show_errno(const char *label)
{
printf(" %s: errno=%d \"%s\"\n", label, errno, strerror(errno));
}
int main(void)
{
puts("Sprinter open() + env API test");
puts("");
printf("estex_file_handle: fd=%u\n", estex_file_handle);
/* --- open() state machine ------------------------------------- */
/* 1. O_CREAT|O_EXCL: must succeed first time, must fail on retry. */
unlink("TMP1.TXT"); /* clean slate */
errno = 0;
int fd = open("TMP1.TXT", O_WRONLY | O_CREAT | O_EXCL);
printf("create-new TMP1.TXT (1st): fd=%d\n", fd);
show_errno(" after 1st");
if (fd >= 0) {
write(fd, "abcdef", 6);
close(fd);
}
errno = 0;
fd = open("TMP1.TXT", O_WRONLY | O_CREAT | O_EXCL);
printf("create-new TMP1.TXT (2nd, should fail with EEXIST): fd=%d\n", fd);
show_errno(" after 2nd");
/* 2. O_CREAT|O_TRUNC: file is reset. */
errno = 0;
fd = open("TMP1.TXT", O_WRONLY | O_CREAT | O_TRUNC);
printf("create-trunc TMP1.TXT: fd=%d\n", fd);
if (fd >= 0) {
write(fd, "XYZ", 3);
close(fd);
}
/* Read back to confirm truncation overwrote "abcdef". */
char buf[16];
fd = open("TMP1.TXT", O_RDONLY);
int n = read(fd, buf, sizeof(buf) - 1);
if (n < 0) n = 0;
buf[n] = 0;
close(fd);
printf("contents after trunc+write: \"%s\" (expect \"XYZ\")\n", buf);
/* 3. O_APPEND: writes go to end. */
fd = open("TMP1.TXT", O_WRONLY | O_APPEND);
if (fd >= 0) {
write(fd, "+APP", 4);
close(fd);
}
fd = open("TMP1.TXT", O_RDONLY);
n = read(fd, buf, sizeof(buf) - 1);
if (n < 0) n = 0;
buf[n] = 0;
close(fd);
printf("contents after append: \"%s\" (expect \"XYZ+APP\")\n", buf);
/* 4. O_CREAT alone (no EXCL/TRUNC): existing file opens, new file is
* created. We open TMP1 (must succeed without truncating) then
* TMP2 (must be created fresh). */
fd = open("TMP1.TXT", O_RDONLY | O_CREAT);
n = read(fd, buf, sizeof(buf) - 1);
if (n < 0) n = 0;
buf[n] = 0;
close(fd);
printf("O_CREAT on existing TMP1: \"%s\" (expect \"XYZ+APP\")\n", buf);
unlink("TMP2.TXT");
fd = open("TMP2.TXT", O_WRONLY | O_CREAT);
printf("O_CREAT on missing TMP2: fd=%d\n", fd);
if (fd >= 0) {
write(fd, "fresh", 5);
close(fd);
}
/* cleanup */
unlink("TMP1.TXT");
unlink("TMP2.TXT");
/* --- env API -------------------------------------------------- */
puts("");
puts("env API:");
errno = 0;
char *v = getenv("PATH");
printf(" getenv(\"PATH\") = %p", v);
if (v) {
printf(" -> \"%s\"", v);
} else {
printf(" (NULL, errno=%d)", errno);
}
putchar('\n');
errno = 0;
int rc = putenv("SPRINTER_HELLO=world");
printf(" putenv(\"SPRINTER_HELLO=world\"): rc=%d\n", rc);
if (rc != 0) show_errno(" after putenv");
errno = 0;
v = getenv("SPRINTER_HELLO");
printf(" getenv(\"SPRINTER_HELLO\") = %p", v);
if (v) {
printf(" -> \"%s\"", v);
} else {
printf(" (NULL, errno=%d)", errno);
}
putchar('\n');
/* --- sysenv: dump the whole environment block ----------------- */
puts("");
puts("sysenv:");
{
static char envbuf[512];
errno = 0;
char *r = sysenv(envbuf);
if (r == (char *)-1) {
show_errno(" sysenv failed");
} else {
printf(" sysenv(%p) returned %p\n", envbuf, r);
int n = 0;
for (char *p = r; *p; p += strlen(p) + 1) {
printf(" [%d] %s\n", n++, p);
}
printf(" total: %d variable(s)\n", n);
}
}
puts("");
puts("Press any key to exit.");
(void)getchar();
return 0;
}
-3
View File
@@ -1,3 +0,0 @@
PROJ_ROOT := $(abspath $(CURDIR)/../..)
EXAMPLE := ptime
include $(PROJ_ROOT)/examples/example.mk
-44
View File
@@ -1,44 +0,0 @@
/*
* ptime — exercise POSIX <time.h>. All wired through SDCC's z80.lib
* time.rel which calls our RtcRead() (bridged to ESTEX SYSTIME).
*/
#include <stdio.h>
#include <time.h>
int main(void)
{
/* time() returns seconds since 1970-01-01 (Unix epoch). */
time_t now;
time(&now);
printf("time() = %lu epoch seconds\n", now);
/* localtime() decomposes into struct tm. */
struct tm *lt = localtime(&now);
printf("localtime() = %04d-%02d-%02d %02d:%02d:%02d (wday=%u)\n",
lt->tm_year + 1900,
lt->tm_mon + 1,
lt->tm_mday,
lt->tm_hour,
lt->tm_min,
lt->tm_sec,
(unsigned)lt->tm_wday);
/* asctime() formats it as a 25-char string. */
printf("asctime() = %s", asctime(lt)); /* asctime adds \n */
/* mktime() round-trip — should recover the same epoch. */
time_t round = mktime(lt);
printf("mktime() = %lu (round-trip %s original)\n",
round, (round == now ? "==" : "!="));
/* Native datetime_t API still works alongside. */
datetime_t dt;
getdatetime(&dt);
printf("native = %04u-%02u-%02u %02u:%02u:%02u (dow=%u)\n",
dt.year, dt.month, dt.day,
dt.hour, dt.minute, dt.second,
(unsigned)dt.dow);
return 0;
}
-5
View File
@@ -1,5 +0,0 @@
# Build rt_test.exe — uses lib/sprinter.lib in TINY memory mode.
PROJ_ROOT := $(abspath $(CURDIR)/../..)
EXAMPLE := rt_test
include $(PROJ_ROOT)/examples/example.mk
-63
View File
@@ -1,63 +0,0 @@
#include <stdio.h>
#include <unistd.h>
#include <setjmp.h>
#include <sprinter_exit.h>
static void bye1(void) { puts("[atexit] bye1 (registered first, runs last)"); }
static void bye2(void) { puts("[atexit] bye2 (registered second)"); }
static void bye3(void) { puts("[atexit] bye3 (registered third, runs first)"); }
static jmp_buf env;
static int attempt;
static void might_fail(int n)
{
printf(" might_fail(%d) running...\n", n);
if (n == 2) {
puts(" -> longjmp(env, 42)");
longjmp(env, 42);
/* not reached */
}
printf(" might_fail(%d) returned normally\n", n);
}
int main(void)
{
puts("Sprinter runtime features test:");
puts("");
/* --- atexit registration --- */
atexit(bye1);
atexit(bye2);
atexit(bye3);
/* --- setjmp / longjmp --- */
puts("setjmp/longjmp:");
int r = setjmp(env);
if (r == 0) {
attempt = 1;
puts(" first arrival from setjmp (r=0)");
might_fail(attempt);
attempt = 2;
might_fail(attempt);
puts(" -- this line is NOT reached");
} else {
printf(" resumed via longjmp, setjmp returned %d (attempt=%d)\n",
r, attempt);
}
/* --- sleep --- */
puts("");
puts("sleep(5): pausing 5 seconds...");
sleep(5);
puts(" done.");
puts("");
puts("Press any key, atexit chain will run after.");
(void)getchar();
/* Use exit() to drive the atexit chain (vs returning from main, which
* doesn't necessarily go through exit() under SDCC). */
exit(0);
return 0;
}
-6
View File
@@ -1,6 +0,0 @@
# Build seek_demo.exe — uses lib/sprinter.lib in TINY memory mode.
PROJ_ROOT := $(abspath $(CURDIR)/../..)
EXAMPLE := seek
EXTRA_DATA := big.txt
include $(PROJ_ROOT)/examples/example.mk
-400
View File
@@ -1,400 +0,0 @@
[off=0000000] block #0000 .....................................................................................................................................................................................................................................
[off=0000256] block #0001 .....................................................................................................................................................................................................................................
[off=0000512] block #0002 .....................................................................................................................................................................................................................................
[off=0000768] block #0003 .....................................................................................................................................................................................................................................
[off=0001024] block #0004 .....................................................................................................................................................................................................................................
[off=0001280] block #0005 .....................................................................................................................................................................................................................................
[off=0001536] block #0006 .....................................................................................................................................................................................................................................
[off=0001792] block #0007 .....................................................................................................................................................................................................................................
[off=0002048] block #0008 .....................................................................................................................................................................................................................................
[off=0002304] block #0009 .....................................................................................................................................................................................................................................
[off=0002560] block #0010 .....................................................................................................................................................................................................................................
[off=0002816] block #0011 .....................................................................................................................................................................................................................................
[off=0003072] block #0012 .....................................................................................................................................................................................................................................
[off=0003328] block #0013 .....................................................................................................................................................................................................................................
[off=0003584] block #0014 .....................................................................................................................................................................................................................................
[off=0003840] block #0015 .....................................................................................................................................................................................................................................
[off=0004096] block #0016 .....................................................................................................................................................................................................................................
[off=0004352] block #0017 .....................................................................................................................................................................................................................................
[off=0004608] block #0018 .....................................................................................................................................................................................................................................
[off=0004864] block #0019 .....................................................................................................................................................................................................................................
[off=0005120] block #0020 .....................................................................................................................................................................................................................................
[off=0005376] block #0021 .....................................................................................................................................................................................................................................
[off=0005632] block #0022 .....................................................................................................................................................................................................................................
[off=0005888] block #0023 .....................................................................................................................................................................................................................................
[off=0006144] block #0024 .....................................................................................................................................................................................................................................
[off=0006400] block #0025 .....................................................................................................................................................................................................................................
[off=0006656] block #0026 .....................................................................................................................................................................................................................................
[off=0006912] block #0027 .....................................................................................................................................................................................................................................
[off=0007168] block #0028 .....................................................................................................................................................................................................................................
[off=0007424] block #0029 .....................................................................................................................................................................................................................................
[off=0007680] block #0030 .....................................................................................................................................................................................................................................
[off=0007936] block #0031 .....................................................................................................................................................................................................................................
[off=0008192] block #0032 .....................................................................................................................................................................................................................................
[off=0008448] block #0033 .....................................................................................................................................................................................................................................
[off=0008704] block #0034 .....................................................................................................................................................................................................................................
[off=0008960] block #0035 .....................................................................................................................................................................................................................................
[off=0009216] block #0036 .....................................................................................................................................................................................................................................
[off=0009472] block #0037 .....................................................................................................................................................................................................................................
[off=0009728] block #0038 .....................................................................................................................................................................................................................................
[off=0009984] block #0039 .....................................................................................................................................................................................................................................
[off=0010240] block #0040 .....................................................................................................................................................................................................................................
[off=0010496] block #0041 .....................................................................................................................................................................................................................................
[off=0010752] block #0042 .....................................................................................................................................................................................................................................
[off=0011008] block #0043 .....................................................................................................................................................................................................................................
[off=0011264] block #0044 .....................................................................................................................................................................................................................................
[off=0011520] block #0045 .....................................................................................................................................................................................................................................
[off=0011776] block #0046 .....................................................................................................................................................................................................................................
[off=0012032] block #0047 .....................................................................................................................................................................................................................................
[off=0012288] block #0048 .....................................................................................................................................................................................................................................
[off=0012544] block #0049 .....................................................................................................................................................................................................................................
[off=0012800] block #0050 .....................................................................................................................................................................................................................................
[off=0013056] block #0051 .....................................................................................................................................................................................................................................
[off=0013312] block #0052 .....................................................................................................................................................................................................................................
[off=0013568] block #0053 .....................................................................................................................................................................................................................................
[off=0013824] block #0054 .....................................................................................................................................................................................................................................
[off=0014080] block #0055 .....................................................................................................................................................................................................................................
[off=0014336] block #0056 .....................................................................................................................................................................................................................................
[off=0014592] block #0057 .....................................................................................................................................................................................................................................
[off=0014848] block #0058 .....................................................................................................................................................................................................................................
[off=0015104] block #0059 .....................................................................................................................................................................................................................................
[off=0015360] block #0060 .....................................................................................................................................................................................................................................
[off=0015616] block #0061 .....................................................................................................................................................................................................................................
[off=0015872] block #0062 .....................................................................................................................................................................................................................................
[off=0016128] block #0063 .....................................................................................................................................................................................................................................
[off=0016384] block #0064 .....................................................................................................................................................................................................................................
[off=0016640] block #0065 .....................................................................................................................................................................................................................................
[off=0016896] block #0066 .....................................................................................................................................................................................................................................
[off=0017152] block #0067 .....................................................................................................................................................................................................................................
[off=0017408] block #0068 .....................................................................................................................................................................................................................................
[off=0017664] block #0069 .....................................................................................................................................................................................................................................
[off=0017920] block #0070 .....................................................................................................................................................................................................................................
[off=0018176] block #0071 .....................................................................................................................................................................................................................................
[off=0018432] block #0072 .....................................................................................................................................................................................................................................
[off=0018688] block #0073 .....................................................................................................................................................................................................................................
[off=0018944] block #0074 .....................................................................................................................................................................................................................................
[off=0019200] block #0075 .....................................................................................................................................................................................................................................
[off=0019456] block #0076 .....................................................................................................................................................................................................................................
[off=0019712] block #0077 .....................................................................................................................................................................................................................................
[off=0019968] block #0078 .....................................................................................................................................................................................................................................
[off=0020224] block #0079 .....................................................................................................................................................................................................................................
[off=0020480] block #0080 .....................................................................................................................................................................................................................................
[off=0020736] block #0081 .....................................................................................................................................................................................................................................
[off=0020992] block #0082 .....................................................................................................................................................................................................................................
[off=0021248] block #0083 .....................................................................................................................................................................................................................................
[off=0021504] block #0084 .....................................................................................................................................................................................................................................
[off=0021760] block #0085 .....................................................................................................................................................................................................................................
[off=0022016] block #0086 .....................................................................................................................................................................................................................................
[off=0022272] block #0087 .....................................................................................................................................................................................................................................
[off=0022528] block #0088 .....................................................................................................................................................................................................................................
[off=0022784] block #0089 .....................................................................................................................................................................................................................................
[off=0023040] block #0090 .....................................................................................................................................................................................................................................
[off=0023296] block #0091 .....................................................................................................................................................................................................................................
[off=0023552] block #0092 .....................................................................................................................................................................................................................................
[off=0023808] block #0093 .....................................................................................................................................................................................................................................
[off=0024064] block #0094 .....................................................................................................................................................................................................................................
[off=0024320] block #0095 .....................................................................................................................................................................................................................................
[off=0024576] block #0096 .....................................................................................................................................................................................................................................
[off=0024832] block #0097 .....................................................................................................................................................................................................................................
[off=0025088] block #0098 .....................................................................................................................................................................................................................................
[off=0025344] block #0099 .....................................................................................................................................................................................................................................
[off=0025600] block #0100 .....................................................................................................................................................................................................................................
[off=0025856] block #0101 .....................................................................................................................................................................................................................................
[off=0026112] block #0102 .....................................................................................................................................................................................................................................
[off=0026368] block #0103 .....................................................................................................................................................................................................................................
[off=0026624] block #0104 .....................................................................................................................................................................................................................................
[off=0026880] block #0105 .....................................................................................................................................................................................................................................
[off=0027136] block #0106 .....................................................................................................................................................................................................................................
[off=0027392] block #0107 .....................................................................................................................................................................................................................................
[off=0027648] block #0108 .....................................................................................................................................................................................................................................
[off=0027904] block #0109 .....................................................................................................................................................................................................................................
[off=0028160] block #0110 .....................................................................................................................................................................................................................................
[off=0028416] block #0111 .....................................................................................................................................................................................................................................
[off=0028672] block #0112 .....................................................................................................................................................................................................................................
[off=0028928] block #0113 .....................................................................................................................................................................................................................................
[off=0029184] block #0114 .....................................................................................................................................................................................................................................
[off=0029440] block #0115 .....................................................................................................................................................................................................................................
[off=0029696] block #0116 .....................................................................................................................................................................................................................................
[off=0029952] block #0117 .....................................................................................................................................................................................................................................
[off=0030208] block #0118 .....................................................................................................................................................................................................................................
[off=0030464] block #0119 .....................................................................................................................................................................................................................................
[off=0030720] block #0120 .....................................................................................................................................................................................................................................
[off=0030976] block #0121 .....................................................................................................................................................................................................................................
[off=0031232] block #0122 .....................................................................................................................................................................................................................................
[off=0031488] block #0123 .....................................................................................................................................................................................................................................
[off=0031744] block #0124 .....................................................................................................................................................................................................................................
[off=0032000] block #0125 .....................................................................................................................................................................................................................................
[off=0032256] block #0126 .....................................................................................................................................................................................................................................
[off=0032512] block #0127 .....................................................................................................................................................................................................................................
[off=0032768] block #0128 .....................................................................................................................................................................................................................................
[off=0033024] block #0129 .....................................................................................................................................................................................................................................
[off=0033280] block #0130 .....................................................................................................................................................................................................................................
[off=0033536] block #0131 .....................................................................................................................................................................................................................................
[off=0033792] block #0132 .....................................................................................................................................................................................................................................
[off=0034048] block #0133 .....................................................................................................................................................................................................................................
[off=0034304] block #0134 .....................................................................................................................................................................................................................................
[off=0034560] block #0135 .....................................................................................................................................................................................................................................
[off=0034816] block #0136 .....................................................................................................................................................................................................................................
[off=0035072] block #0137 .....................................................................................................................................................................................................................................
[off=0035328] block #0138 .....................................................................................................................................................................................................................................
[off=0035584] block #0139 .....................................................................................................................................................................................................................................
[off=0035840] block #0140 .....................................................................................................................................................................................................................................
[off=0036096] block #0141 .....................................................................................................................................................................................................................................
[off=0036352] block #0142 .....................................................................................................................................................................................................................................
[off=0036608] block #0143 .....................................................................................................................................................................................................................................
[off=0036864] block #0144 .....................................................................................................................................................................................................................................
[off=0037120] block #0145 .....................................................................................................................................................................................................................................
[off=0037376] block #0146 .....................................................................................................................................................................................................................................
[off=0037632] block #0147 .....................................................................................................................................................................................................................................
[off=0037888] block #0148 .....................................................................................................................................................................................................................................
[off=0038144] block #0149 .....................................................................................................................................................................................................................................
[off=0038400] block #0150 .....................................................................................................................................................................................................................................
[off=0038656] block #0151 .....................................................................................................................................................................................................................................
[off=0038912] block #0152 .....................................................................................................................................................................................................................................
[off=0039168] block #0153 .....................................................................................................................................................................................................................................
[off=0039424] block #0154 .....................................................................................................................................................................................................................................
[off=0039680] block #0155 .....................................................................................................................................................................................................................................
[off=0039936] block #0156 .....................................................................................................................................................................................................................................
[off=0040192] block #0157 .....................................................................................................................................................................................................................................
[off=0040448] block #0158 .....................................................................................................................................................................................................................................
[off=0040704] block #0159 .....................................................................................................................................................................................................................................
[off=0040960] block #0160 .....................................................................................................................................................................................................................................
[off=0041216] block #0161 .....................................................................................................................................................................................................................................
[off=0041472] block #0162 .....................................................................................................................................................................................................................................
[off=0041728] block #0163 .....................................................................................................................................................................................................................................
[off=0041984] block #0164 .....................................................................................................................................................................................................................................
[off=0042240] block #0165 .....................................................................................................................................................................................................................................
[off=0042496] block #0166 .....................................................................................................................................................................................................................................
[off=0042752] block #0167 .....................................................................................................................................................................................................................................
[off=0043008] block #0168 .....................................................................................................................................................................................................................................
[off=0043264] block #0169 .....................................................................................................................................................................................................................................
[off=0043520] block #0170 .....................................................................................................................................................................................................................................
[off=0043776] block #0171 .....................................................................................................................................................................................................................................
[off=0044032] block #0172 .....................................................................................................................................................................................................................................
[off=0044288] block #0173 .....................................................................................................................................................................................................................................
[off=0044544] block #0174 .....................................................................................................................................................................................................................................
[off=0044800] block #0175 .....................................................................................................................................................................................................................................
[off=0045056] block #0176 .....................................................................................................................................................................................................................................
[off=0045312] block #0177 .....................................................................................................................................................................................................................................
[off=0045568] block #0178 .....................................................................................................................................................................................................................................
[off=0045824] block #0179 .....................................................................................................................................................................................................................................
[off=0046080] block #0180 .....................................................................................................................................................................................................................................
[off=0046336] block #0181 .....................................................................................................................................................................................................................................
[off=0046592] block #0182 .....................................................................................................................................................................................................................................
[off=0046848] block #0183 .....................................................................................................................................................................................................................................
[off=0047104] block #0184 .....................................................................................................................................................................................................................................
[off=0047360] block #0185 .....................................................................................................................................................................................................................................
[off=0047616] block #0186 .....................................................................................................................................................................................................................................
[off=0047872] block #0187 .....................................................................................................................................................................................................................................
[off=0048128] block #0188 .....................................................................................................................................................................................................................................
[off=0048384] block #0189 .....................................................................................................................................................................................................................................
[off=0048640] block #0190 .....................................................................................................................................................................................................................................
[off=0048896] block #0191 .....................................................................................................................................................................................................................................
[off=0049152] block #0192 .....................................................................................................................................................................................................................................
[off=0049408] block #0193 .....................................................................................................................................................................................................................................
[off=0049664] block #0194 .....................................................................................................................................................................................................................................
[off=0049920] block #0195 .....................................................................................................................................................................................................................................
[off=0050176] block #0196 .....................................................................................................................................................................................................................................
[off=0050432] block #0197 .....................................................................................................................................................................................................................................
[off=0050688] block #0198 .....................................................................................................................................................................................................................................
[off=0050944] block #0199 .....................................................................................................................................................................................................................................
[off=0051200] block #0200 .....................................................................................................................................................................................................................................
[off=0051456] block #0201 .....................................................................................................................................................................................................................................
[off=0051712] block #0202 .....................................................................................................................................................................................................................................
[off=0051968] block #0203 .....................................................................................................................................................................................................................................
[off=0052224] block #0204 .....................................................................................................................................................................................................................................
[off=0052480] block #0205 .....................................................................................................................................................................................................................................
[off=0052736] block #0206 .....................................................................................................................................................................................................................................
[off=0052992] block #0207 .....................................................................................................................................................................................................................................
[off=0053248] block #0208 .....................................................................................................................................................................................................................................
[off=0053504] block #0209 .....................................................................................................................................................................................................................................
[off=0053760] block #0210 .....................................................................................................................................................................................................................................
[off=0054016] block #0211 .....................................................................................................................................................................................................................................
[off=0054272] block #0212 .....................................................................................................................................................................................................................................
[off=0054528] block #0213 .....................................................................................................................................................................................................................................
[off=0054784] block #0214 .....................................................................................................................................................................................................................................
[off=0055040] block #0215 .....................................................................................................................................................................................................................................
[off=0055296] block #0216 .....................................................................................................................................................................................................................................
[off=0055552] block #0217 .....................................................................................................................................................................................................................................
[off=0055808] block #0218 .....................................................................................................................................................................................................................................
[off=0056064] block #0219 .....................................................................................................................................................................................................................................
[off=0056320] block #0220 .....................................................................................................................................................................................................................................
[off=0056576] block #0221 .....................................................................................................................................................................................................................................
[off=0056832] block #0222 .....................................................................................................................................................................................................................................
[off=0057088] block #0223 .....................................................................................................................................................................................................................................
[off=0057344] block #0224 .....................................................................................................................................................................................................................................
[off=0057600] block #0225 .....................................................................................................................................................................................................................................
[off=0057856] block #0226 .....................................................................................................................................................................................................................................
[off=0058112] block #0227 .....................................................................................................................................................................................................................................
[off=0058368] block #0228 .....................................................................................................................................................................................................................................
[off=0058624] block #0229 .....................................................................................................................................................................................................................................
[off=0058880] block #0230 .....................................................................................................................................................................................................................................
[off=0059136] block #0231 .....................................................................................................................................................................................................................................
[off=0059392] block #0232 .....................................................................................................................................................................................................................................
[off=0059648] block #0233 .....................................................................................................................................................................................................................................
[off=0059904] block #0234 .....................................................................................................................................................................................................................................
[off=0060160] block #0235 .....................................................................................................................................................................................................................................
[off=0060416] block #0236 .....................................................................................................................................................................................................................................
[off=0060672] block #0237 .....................................................................................................................................................................................................................................
[off=0060928] block #0238 .....................................................................................................................................................................................................................................
[off=0061184] block #0239 .....................................................................................................................................................................................................................................
[off=0061440] block #0240 .....................................................................................................................................................................................................................................
[off=0061696] block #0241 .....................................................................................................................................................................................................................................
[off=0061952] block #0242 .....................................................................................................................................................................................................................................
[off=0062208] block #0243 .....................................................................................................................................................................................................................................
[off=0062464] block #0244 .....................................................................................................................................................................................................................................
[off=0062720] block #0245 .....................................................................................................................................................................................................................................
[off=0062976] block #0246 .....................................................................................................................................................................................................................................
[off=0063232] block #0247 .....................................................................................................................................................................................................................................
[off=0063488] block #0248 .....................................................................................................................................................................................................................................
[off=0063744] block #0249 .....................................................................................................................................................................................................................................
[off=0064000] block #0250 .....................................................................................................................................................................................................................................
[off=0064256] block #0251 .....................................................................................................................................................................................................................................
[off=0064512] block #0252 .....................................................................................................................................................................................................................................
[off=0064768] block #0253 .....................................................................................................................................................................................................................................
[off=0065024] block #0254 .....................................................................................................................................................................................................................................
[off=0065280] block #0255 .....................................................................................................................................................................................................................................
[off=0065536] block #0256 .....................................................................................................................................................................................................................................
[off=0065792] block #0257 .....................................................................................................................................................................................................................................
[off=0066048] block #0258 .....................................................................................................................................................................................................................................
[off=0066304] block #0259 .....................................................................................................................................................................................................................................
[off=0066560] block #0260 .....................................................................................................................................................................................................................................
[off=0066816] block #0261 .....................................................................................................................................................................................................................................
[off=0067072] block #0262 .....................................................................................................................................................................................................................................
[off=0067328] block #0263 .....................................................................................................................................................................................................................................
[off=0067584] block #0264 .....................................................................................................................................................................................................................................
[off=0067840] block #0265 .....................................................................................................................................................................................................................................
[off=0068096] block #0266 .....................................................................................................................................................................................................................................
[off=0068352] block #0267 .....................................................................................................................................................................................................................................
[off=0068608] block #0268 .....................................................................................................................................................................................................................................
[off=0068864] block #0269 .....................................................................................................................................................................................................................................
[off=0069120] block #0270 .....................................................................................................................................................................................................................................
[off=0069376] block #0271 .....................................................................................................................................................................................................................................
[off=0069632] block #0272 .....................................................................................................................................................................................................................................
[off=0069888] block #0273 .....................................................................................................................................................................................................................................
[off=0070144] block #0274 .....................................................................................................................................................................................................................................
[off=0070400] block #0275 .....................................................................................................................................................................................................................................
[off=0070656] block #0276 .....................................................................................................................................................................................................................................
[off=0070912] block #0277 .....................................................................................................................................................................................................................................
[off=0071168] block #0278 .....................................................................................................................................................................................................................................
[off=0071424] block #0279 .....................................................................................................................................................................................................................................
[off=0071680] block #0280 .....................................................................................................................................................................................................................................
[off=0071936] block #0281 .....................................................................................................................................................................................................................................
[off=0072192] block #0282 .....................................................................................................................................................................................................................................
[off=0072448] block #0283 .....................................................................................................................................................................................................................................
[off=0072704] block #0284 .....................................................................................................................................................................................................................................
[off=0072960] block #0285 .....................................................................................................................................................................................................................................
[off=0073216] block #0286 .....................................................................................................................................................................................................................................
[off=0073472] block #0287 .....................................................................................................................................................................................................................................
[off=0073728] block #0288 .....................................................................................................................................................................................................................................
[off=0073984] block #0289 .....................................................................................................................................................................................................................................
[off=0074240] block #0290 .....................................................................................................................................................................................................................................
[off=0074496] block #0291 .....................................................................................................................................................................................................................................
[off=0074752] block #0292 .....................................................................................................................................................................................................................................
[off=0075008] block #0293 .....................................................................................................................................................................................................................................
[off=0075264] block #0294 .....................................................................................................................................................................................................................................
[off=0075520] block #0295 .....................................................................................................................................................................................................................................
[off=0075776] block #0296 .....................................................................................................................................................................................................................................
[off=0076032] block #0297 .....................................................................................................................................................................................................................................
[off=0076288] block #0298 .....................................................................................................................................................................................................................................
[off=0076544] block #0299 .....................................................................................................................................................................................................................................
[off=0076800] block #0300 .....................................................................................................................................................................................................................................
[off=0077056] block #0301 .....................................................................................................................................................................................................................................
[off=0077312] block #0302 .....................................................................................................................................................................................................................................
[off=0077568] block #0303 .....................................................................................................................................................................................................................................
[off=0077824] block #0304 .....................................................................................................................................................................................................................................
[off=0078080] block #0305 .....................................................................................................................................................................................................................................
[off=0078336] block #0306 .....................................................................................................................................................................................................................................
[off=0078592] block #0307 .....................................................................................................................................................................................................................................
[off=0078848] block #0308 .....................................................................................................................................................................................................................................
[off=0079104] block #0309 .....................................................................................................................................................................................................................................
[off=0079360] block #0310 .....................................................................................................................................................................................................................................
[off=0079616] block #0311 .....................................................................................................................................................................................................................................
[off=0079872] block #0312 .....................................................................................................................................................................................................................................
[off=0080128] block #0313 .....................................................................................................................................................................................................................................
[off=0080384] block #0314 .....................................................................................................................................................................................................................................
[off=0080640] block #0315 .....................................................................................................................................................................................................................................
[off=0080896] block #0316 .....................................................................................................................................................................................................................................
[off=0081152] block #0317 .....................................................................................................................................................................................................................................
[off=0081408] block #0318 .....................................................................................................................................................................................................................................
[off=0081664] block #0319 .....................................................................................................................................................................................................................................
[off=0081920] block #0320 .....................................................................................................................................................................................................................................
[off=0082176] block #0321 .....................................................................................................................................................................................................................................
[off=0082432] block #0322 .....................................................................................................................................................................................................................................
[off=0082688] block #0323 .....................................................................................................................................................................................................................................
[off=0082944] block #0324 .....................................................................................................................................................................................................................................
[off=0083200] block #0325 .....................................................................................................................................................................................................................................
[off=0083456] block #0326 .....................................................................................................................................................................................................................................
[off=0083712] block #0327 .....................................................................................................................................................................................................................................
[off=0083968] block #0328 .....................................................................................................................................................................................................................................
[off=0084224] block #0329 .....................................................................................................................................................................................................................................
[off=0084480] block #0330 .....................................................................................................................................................................................................................................
[off=0084736] block #0331 .....................................................................................................................................................................................................................................
[off=0084992] block #0332 .....................................................................................................................................................................................................................................
[off=0085248] block #0333 .....................................................................................................................................................................................................................................
[off=0085504] block #0334 .....................................................................................................................................................................................................................................
[off=0085760] block #0335 .....................................................................................................................................................................................................................................
[off=0086016] block #0336 .....................................................................................................................................................................................................................................
[off=0086272] block #0337 .....................................................................................................................................................................................................................................
[off=0086528] block #0338 .....................................................................................................................................................................................................................................
[off=0086784] block #0339 .....................................................................................................................................................................................................................................
[off=0087040] block #0340 .....................................................................................................................................................................................................................................
[off=0087296] block #0341 .....................................................................................................................................................................................................................................
[off=0087552] block #0342 .....................................................................................................................................................................................................................................
[off=0087808] block #0343 .....................................................................................................................................................................................................................................
[off=0088064] block #0344 .....................................................................................................................................................................................................................................
[off=0088320] block #0345 .....................................................................................................................................................................................................................................
[off=0088576] block #0346 .....................................................................................................................................................................................................................................
[off=0088832] block #0347 .....................................................................................................................................................................................................................................
[off=0089088] block #0348 .....................................................................................................................................................................................................................................
[off=0089344] block #0349 .....................................................................................................................................................................................................................................
[off=0089600] block #0350 .....................................................................................................................................................................................................................................
[off=0089856] block #0351 .....................................................................................................................................................................................................................................
[off=0090112] block #0352 .....................................................................................................................................................................................................................................
[off=0090368] block #0353 .....................................................................................................................................................................................................................................
[off=0090624] block #0354 .....................................................................................................................................................................................................................................
[off=0090880] block #0355 .....................................................................................................................................................................................................................................
[off=0091136] block #0356 .....................................................................................................................................................................................................................................
[off=0091392] block #0357 .....................................................................................................................................................................................................................................
[off=0091648] block #0358 .....................................................................................................................................................................................................................................
[off=0091904] block #0359 .....................................................................................................................................................................................................................................
[off=0092160] block #0360 .....................................................................................................................................................................................................................................
[off=0092416] block #0361 .....................................................................................................................................................................................................................................
[off=0092672] block #0362 .....................................................................................................................................................................................................................................
[off=0092928] block #0363 .....................................................................................................................................................................................................................................
[off=0093184] block #0364 .....................................................................................................................................................................................................................................
[off=0093440] block #0365 .....................................................................................................................................................................................................................................
[off=0093696] block #0366 .....................................................................................................................................................................................................................................
[off=0093952] block #0367 .....................................................................................................................................................................................................................................
[off=0094208] block #0368 .....................................................................................................................................................................................................................................
[off=0094464] block #0369 .....................................................................................................................................................................................................................................
[off=0094720] block #0370 .....................................................................................................................................................................................................................................
[off=0094976] block #0371 .....................................................................................................................................................................................................................................
[off=0095232] block #0372 .....................................................................................................................................................................................................................................
[off=0095488] block #0373 .....................................................................................................................................................................................................................................
[off=0095744] block #0374 .....................................................................................................................................................................................................................................
[off=0096000] block #0375 .....................................................................................................................................................................................................................................
[off=0096256] block #0376 .....................................................................................................................................................................................................................................
[off=0096512] block #0377 .....................................................................................................................................................................................................................................
[off=0096768] block #0378 .....................................................................................................................................................................................................................................
[off=0097024] block #0379 .....................................................................................................................................................................................................................................
[off=0097280] block #0380 .....................................................................................................................................................................................................................................
[off=0097536] block #0381 .....................................................................................................................................................................................................................................
[off=0097792] block #0382 .....................................................................................................................................................................................................................................
[off=0098048] block #0383 .....................................................................................................................................................................................................................................
[off=0098304] block #0384 .....................................................................................................................................................................................................................................
[off=0098560] block #0385 .....................................................................................................................................................................................................................................
[off=0098816] block #0386 .....................................................................................................................................................................................................................................
[off=0099072] block #0387 .....................................................................................................................................................................................................................................
[off=0099328] block #0388 .....................................................................................................................................................................................................................................
[off=0099584] block #0389 .....................................................................................................................................................................................................................................
[off=0099840] block #0390 .....................................................................................................................................................................................................................................
[off=0100096] block #0391 .....................................................................................................................................................................................................................................
[off=0100352] block #0392 .....................................................................................................................................................................................................................................
[off=0100608] block #0393 .....................................................................................................................................................................................................................................
[off=0100864] block #0394 .....................................................................................................................................................................................................................................
[off=0101120] block #0395 .....................................................................................................................................................................................................................................
[off=0101376] block #0396 .....................................................................................................................................................................................................................................
[off=0101632] block #0397 .....................................................................................................................................................................................................................................
[off=0101888] block #0398 .....................................................................................................................................................................................................................................
[off=0102144] block #0399 .....................................................................................................................................................................................................................................
-35
View File
@@ -1,35 +0,0 @@
#!/usr/bin/env python3
"""
Generate a >64 KB test file with predictable position-encoded content.
Every 256-byte block starts with a 32-character ASCII line
"[off=NNNNNNN] block #BBBB filler...\n"
where NNNNNNN is the absolute byte offset of the block start (padded to 7
decimal digits) and BBBB is the 4-digit block index. The line is padded
to 256 bytes with '.' so that seeking to any 256-aligned offset gives an
immediately-readable label.
"""
import sys
import os
def main():
path = sys.argv[1] if len(sys.argv) > 1 else "big.txt"
target_size = int(sys.argv[2]) if len(sys.argv) > 2 else 100 * 1024
block_size = 256
nblocks = (target_size + block_size - 1) // block_size
with open(path, "wb") as f:
for b in range(nblocks):
offset = b * block_size
header = f"[off={offset:07d}] block #{b:04d} "
body = header + ("." * (block_size - len(header) - 1)) + "\n"
assert len(body) == block_size, len(body)
f.write(body.encode("ascii"))
actual = os.path.getsize(path)
print(f"wrote {path}: {actual} bytes ({nblocks} blocks of {block_size} each)")
if __name__ == "__main__":
main()
-66
View File
@@ -1,66 +0,0 @@
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
/*
* Exercises lseek() with offsets that span the 16-bit boundary.
* big.txt is 102400 bytes (100 KB); every 256-byte block opens with a
* "[off=NNNNNNN] block #BBBB " label, so any seek aligned to 256 lands
* on a self-identifying marker.
*/
static void show_marker(int fd, long off)
{
long pos = lseek(fd, off, SEEK_SET);
printf("\nseek %ld -> pos=%ld\n", off, pos);
char buf[28];
int n = read(fd, buf, 27);
if (n <= 0) {
printf(" read failed: %d\n", n);
return;
}
buf[n] = 0;
printf(" bytes: \"%s\"\n", buf);
}
int main(void)
{
int fd = open("BIG.TXT", O_RDONLY);
if (fd < 0) {
printf("open failed: %d\n", fd);
(void)getchar();
return 1;
}
/* Total file size. */
long size = lseek(fd, 0L, SEEK_END);
printf("file size = %ld bytes\n", size);
/* Seek inside the first 64 KB (high16 = 0). */
show_marker(fd, 0L);
show_marker(fd, 256L);
show_marker(fd, 32768L);
/* Right at the 64 KB boundary (offset 0x00010000, high16 = 1). */
show_marker(fd, 65536L);
/* Past 64 KB (high16 still 1, low16 != 0). */
show_marker(fd, 65536L + 256L);
show_marker(fd, 81920L);
/* Near end of file (size = 102400 = 0x19000, high16 = 1). */
show_marker(fd, 102400L - 256L);
/* SEEK_CUR: from current position, +256. After the previous SEEK_SET
* to 102144, then read of 27 bytes, pos is 102171. +256 -> 102427,
* which is past EOF — ESTEX should clamp or error. */
long after_cur = lseek(fd, 256L, SEEK_CUR);
printf("\nSEEK_CUR +256 -> pos=%ld (past EOF)\n", after_cur);
close(fd);
puts("");
puts("Press any key to exit...");
(void)getchar();
return 0;
}
-3
View File
@@ -1,3 +0,0 @@
PROJ_ROOT := $(abspath $(CURDIR)/../..)
EXAMPLE := stattest
include $(PROJ_ROOT)/examples/example.mk
-89
View File
@@ -1,89 +0,0 @@
/*
* stattest — exercise POSIX stat() and fstat() over ESTEX file metadata.
*
* Calls:
* stat() on a file by path
* stat() on a directory (current dir)
* stat() on a non-existent file (expects errno=ENOENT)
* fstat() on a freshly opened file
*/
#include <stdio.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <time.h>
static const char *mode_str(uint16_t m)
{
if (S_ISDIR(m)) return "DIR";
if (S_ISREG(m)) return "REG";
return "???";
}
static void print_stat(const char *what, const struct stat *st)
{
char buf[32];
/* Reuse asctime by converting epoch → struct tm. */
time_t t = st->st_mtime;
struct tm *tm = localtime(&t);
sprintf(buf, "%04d-%02d-%02d %02d:%02d:%02d",
tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
tm->tm_hour, tm->tm_min, tm->tm_sec);
printf(" %s: mode=%s perm=0%o size=%lu mtime=%s\n",
what,
mode_str(st->st_mode),
(unsigned)(st->st_mode & 0777),
(unsigned long)st->st_size,
buf);
}
int main(int argc, char **argv)
{
struct stat st;
puts("stattest: stat() / fstat() smoke test");
/* stat on this very file (the .exe). */
if (stat(argv[0], &st) == 0)
print_stat("self.exe", &st);
else
printf(" stat(\"%s\") failed: errno=%d (%s)\n",
argv[0], errno, strerror(errno));
/* stat on a definitely-missing file. */
errno = 0;
if (stat("NOSUCH.XXX", &st) == 0)
puts(" stat(NOSUCH.XXX) unexpectedly succeeded?!");
else
printf(" stat(NOSUCH.XXX) -> errno=%d (%s) [expected ENOENT]\n",
errno, strerror(errno));
/* fstat on a fresh handle (open the running exe read-only). */
int fd = open(argv[0], O_RDONLY);
if (fd < 0) {
printf(" open(\"%s\") failed: errno=%d (%s)\n",
argv[0], errno, strerror(errno));
} else {
if (fstat(fd, &st) == 0)
print_stat("fstat(self)", &st);
else
printf(" fstat failed: errno=%d (%s)\n",
errno, strerror(errno));
close(fd);
}
/* Extra argv-passed path. */
if (argc > 1) {
if (stat(argv[1], &st) == 0)
print_stat(argv[1], &st);
else
printf(" stat(\"%s\") failed: errno=%d (%s)\n",
argv[1], errno, strerror(errno));
}
return 0;
}
-3
View File
@@ -1,3 +0,0 @@
PROJ_ROOT := $(abspath $(CURDIR)/../..)
EXAMPLE := stdlib
include $(PROJ_ROOT)/examples/example.mk
-63
View File
@@ -1,63 +0,0 @@
/*
* stdlib_test — verifies that SDCC's z80.lib stdlib functions (atoi,
* strtol, rand, qsort, bsearch, abs, ldiv) work in our environment.
* If they all behave correctly, we can save ourselves the labour of
* reimplementing them in libc/stdlib.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static int cmp_int(const void *a, const void *b)
{
int ia = *(const int *)a;
int ib = *(const int *)b;
return (ia > ib) - (ia < ib);
}
int main(int argc, char **argv)
{
/* atoi / strtol */
int v_atoi = atoi("12345");
long v_strtol = strtol("-9876", NULL, 10);
long v_hex = strtol("ff", NULL, 16);
printf("atoi(\"12345\") = %d (expected 12345)\n", v_atoi);
printf("strtol(\"-9876\",,10) = %ld (expected -9876)\n", v_strtol);
printf("strtol(\"ff\",,16) = %ld (expected 255)\n", v_hex);
/* abs / ldiv */
printf("abs(-7) = %d (expected 7)\n", abs(-7));
{
ldiv_t q = ldiv(100L, 7L);
printf("ldiv(100,7) = {q=%ld, r=%ld} (expected {14, 2})\n",
q.quot, q.rem);
}
/* rand / srand — deterministic with fixed seed */
srand(42);
printf("rand x3 (seed 42): %d %d %d\n", rand(), rand(), rand());
/* qsort */
{
int arr[] = { 5, 1, 4, 2, 8, 3, 7, 6, 0, 9 };
qsort(arr, 10, sizeof(int), cmp_int);
printf("qsort:");
for (int i = 0; i < 10; i++) printf(" %d", arr[i]);
printf(" (expected 0..9 sorted)\n");
/* bsearch */
int key = 7;
int *p = (int *)bsearch(&key, arr, 10, sizeof(int), cmp_int);
printf("bsearch(7) -> %s (expected found)\n",
(p && *p == 7) ? "found" : "MISS");
}
/* argv-as-int: typical CLI use */
if (argc > 1) {
printf("argv[1] as int: %d\n", atoi(argv[1]));
}
puts("\nall SDCC stdlib functions reachable.");
return 0;
}
-3
View File
@@ -1,3 +0,0 @@
PROJ_ROOT := $(abspath $(CURDIR)/../..)
EXAMPLE := stest2
include $(PROJ_ROOT)/examples/example.mk
-43
View File
@@ -1,43 +0,0 @@
/*
* stattest — exercise POSIX stat() and fstat() over ESTEX file metadata.
*
* Calls:
* stat() on a file by path
* stat() on a directory (current dir)
* stat() on a non-existent file (expects errno=ENOENT)
* fstat() on a freshly opened file
*/
#include <stdio.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <time.h>
#include <dir.h>
int main(int argc, char **argv)
{
struct stat st;
puts("stattest: stat() / fstat() smoke test");
ffblk_t ffb;
if (ffirst(argv[0], &ffb, FA_DIREC) == 0) {
printf("File %s, size = %d\n", argv[0], ffb.size);
} else {
printf("File %s, error\n", argv[0]);
}
if (argc > 1) {
if (ffirst(argv[1], &ffb, FA_DIREC) == 0) {
printf("File %s, size = %d\n", argv[1], ffb.size);
} else {
printf("File %s, error\n", argv[1]);
}
}
return 0;
}
-5
View File
@@ -1,5 +0,0 @@
# Build strtest.exe — uses lib/sprinter.lib in TINY memory mode.
PROJ_ROOT := $(abspath $(CURDIR)/../..)
EXAMPLE := strtest
include $(PROJ_ROOT)/examples/example.mk
-53
View File
@@ -1,53 +0,0 @@
#include <stdio.h>
#include <string.h>
static void check(const char *label, int ok)
{
puts(label);
puts(ok ? " OK" : " FAIL");
}
int main(void)
{
char buf[32];
/* strlen */
check("strlen(\"\") == 0", strlen("") == 0);
check("strlen(\"hello\") == 5", strlen("hello") == 5);
/* strcpy + strlen */
strcpy(buf, "Sprinter");
check("strcpy + strlen == 8", strlen(buf) == 8);
/* strcmp */
check("strcmp(\"a\",\"a\") == 0", strcmp("a", "a") == 0);
check("strcmp(\"a\",\"b\") < 0", strcmp("a", "b") < 0);
check("strcmp(\"b\",\"a\") > 0", strcmp("b", "a") > 0);
check("strcmp(buf,\"Sprinter\")", strcmp(buf, "Sprinter") == 0);
/* strcat */
strcpy(buf, "Hello ");
strcat(buf, "Sprinter!");
check("strcat -> \"Hello Sprinter!\"", strcmp(buf, "Hello Sprinter!") == 0);
/* memcpy */
memset(buf, 'X', 16);
memcpy(buf + 4, "ABCD", 4);
buf[16] = 0;
check("memset+memcpy -> \"XXXXABCDXXXXXXXX\"",
strcmp(buf, "XXXXABCDXXXXXXXX") == 0);
/* memset */
memset(buf, 'q', 5);
buf[5] = 0;
check("memset(buf,'q',5) -> \"qqqqq\"", strcmp(buf, "qqqqq") == 0);
/* memcmp */
check("memcmp equal", memcmp("abcd", "abcd", 4) == 0);
check("memcmp diff", memcmp("abcd", "abxd", 4) != 0);
puts("");
puts("All checks done. Press any key to exit.");
(void)getchar();
return 0;
}
-5
View File
@@ -1,5 +0,0 @@
# Build time_dir_test.exe — uses lib/sprinter.lib in TINY memory mode.
PROJ_ROOT := $(abspath $(CURDIR)/../..)
EXAMPLE := timedir
include $(PROJ_ROOT)/examples/example.mk
-108
View File
@@ -1,108 +0,0 @@
#include <stdio.h>
#include <unistd.h>
#include <time.h>
#include <errno.h>
#include <string.h>
#include <conio.h>
static const char *dow_name(uint8_t d)
{
/* Sprinter SYSTIME returns dow 1-based: 1=Sun..7=Sat. */
static const char *names[] = { "?", "Sun", "Mon", "Tue",
"Wed", "Thu", "Fri", "Sat" };
return (d >= 1 && d <= 7) ? names[d] : names[0];
}
int main(void)
{
/* --- time ----------------------------------------------------------- */
datetime_t dt;
getdatetime(&dt);
printf("getdatetime: %04u-%02u-%02u %02u:%02u:%02u (dow=%u %s)\n",
dt.year, dt.month, dt.day,
dt.hour, dt.minute, dt.second,
dt.dow, dow_name(dt.dow));
/* dow convention probe — set to known dates, read back.
* Discriminating test: today (Thu) gave dow=5, which fits either
* (a) Sun=1, Mon=2, ..., Sat=7 (DOS 1-based starting Sun)
* (b) Sat=0, Sun=1, ..., Fri=6 (week starts Saturday)
* Saturday's number tells us which one. */
datetime_t saved = dt;
datetime_t probe;
probe = dt; probe.year = 2026; probe.month = 5; probe.day = 30; /* Sat */
if (setdatetime(&probe) == 0) {
getdatetime(&probe);
printf("probe 2026-05-30 (Sat expected): dow=%u %s\n",
probe.dow, dow_name(probe.dow));
}
probe = dt; probe.year = 2026; probe.month = 5; probe.day = 31; /* Sun */
if (setdatetime(&probe) == 0) {
getdatetime(&probe);
printf("probe 2026-05-31 (Sun expected): dow=%u %s\n",
probe.dow, dow_name(probe.dow));
}
probe = dt; probe.year = 2026; probe.month = 6; probe.day = 1; /* Mon */
if (setdatetime(&probe) == 0) {
getdatetime(&probe);
printf("probe 2026-06-01 (Mon expected): dow=%u %s\n",
probe.dow, dow_name(probe.dow));
}
/* Restore original. */
if (setdatetime(&saved) == 0)
printf("restored original date\n");
else
printf("setdatetime(restore) failed: errno=%d\n", errno);
/* --- getcwd --------------------------------------------------------- */
char cwd[256];
if (getcwd(cwd, sizeof cwd) == NULL) {
printf("getcwd failed: errno=%d (%s)\n", errno, strerror(errno));
} else {
printf("cwd before: \"%s\"\n", cwd);
}
/* --- mkdir / chdir / getcwd / rmdir --------------------------------- */
const char *test_dir = "TMPTEST";
/* If a previous run left it behind, ignore the mkdir failure. */
if (mkdir(test_dir) < 0) {
printf("mkdir(%s) -> errno=%d (%s) [continuing]\n",
test_dir, errno, strerror(errno));
} else {
printf("mkdir(%s) OK\n", test_dir);
}
if (chdir(test_dir) < 0) {
printf("chdir(%s) failed: errno=%d (%s)\n",
test_dir, errno, strerror(errno));
return 1;
}
printf("chdir(%s) OK\n", test_dir);
if (getcwd(cwd, sizeof cwd) != NULL)
printf("cwd after: \"%s\"\n", cwd);
/* Back out, then remove. */
if (chdir("\\") < 0)
printf("chdir(\\) failed: errno=%d\n", errno);
else
printf("chdir(\\) OK\n");
if (rmdir(test_dir) < 0)
printf("rmdir(%s) failed: errno=%d (%s)\n",
test_dir, errno, strerror(errno));
else
printf("rmdir(%s) OK\n", test_dir);
if (getcwd(cwd, sizeof cwd) != NULL)
printf("cwd final: \"%s\"\n", cwd);
puts("\npress any key to exit");
(void)getch();
return 0;
}