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
+169
View File
@@ -0,0 +1,169 @@
/*
* 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;
}