mirror of
https://git.k-space.ee/arti/vt1200.git
synced 2026-11-01 12:14:56 +02:00
The harness forced the VR150 monitor switch, so the firmware wrote the OS screen at 1024 px / 128-byte rows. VT1200_VRAM_REAL then re-read that framebuffer at the VR319 pitch, which sheared the OS and wrapped the right quarter of every row. Model VR319 (switch 0x60) by default -- the real unit's mode -- so the firmware itself programs 1280x1024 with a 256-byte row pitch (CONVDP=0x14). The renderer's pitch follows VT1200_MONITOR, and the destination span now comes from HEBLNK..HSBLNK instead of x=0, placing the active 1280 px at x=336 in MAME's 1696-wide raster. Verified: clean Terminal Manager at frame 600, legible faithful diagnostics menu and level-1 error screens, tile mode PASS with tile_check 1280/1280 tiles, tablet/mouse pixel-identical. VT1200_MONITOR=VR150 reproduces the old geometry; the regression baseline moves 198 -> 199 (2 PCs) and is recorded in harness/mame/README.md.
105 lines
3.7 KiB
C
105 lines
3.7 KiB
C
/*
|
|
* hello.c -- the VT1200's first C program.
|
|
*
|
|
* Writes the word HELLO straight into video RAM, no X11 involved:
|
|
*
|
|
* framebuffer bit 0x04000000, 256-byte row pitch on the real unit
|
|
* (the harness now defaults to the same VR319 layout;
|
|
* only VT1200_MONITOR=VR150 uses 128)
|
|
* pixels 1 bpp, LSB-first within each byte
|
|
* text area black 256x72 box at x=40 y=800, white 5x7 glyphs scaled 8x
|
|
*
|
|
* The program also leaves a marker word at 0x018A8000 so the host can tell the
|
|
* C runtime reached main(): 0x48454C4C == 'HELL'.
|
|
*
|
|
* K&R C only: no prototypes, no '//', no unsigned suffixes, declarations first.
|
|
* The tables are filled in code, not by static initializers, because this image
|
|
* is entered through entry.asm and does not run boot.obj's .cinit pass.
|
|
*/
|
|
|
|
#define VRAM_BASE 0x04000000 /* bit address of the framebuffer */
|
|
#ifndef ROW_BYTES
|
|
#define ROW_BYTES 256 /* real unit: 256-byte row pitch */
|
|
#endif
|
|
#define ROW_BITS (ROW_BYTES * 8)
|
|
#define BOX_X 40 /* box left edge, pixels (1 bit per pixel) */
|
|
#define BOX_Y 800 /* box top edge, pixels */
|
|
#define BOX_W 256 /* 32 bytes */
|
|
#define BOX_H 72
|
|
#define MARKER 0x018A8000 /* free RAM, outside .text and .bss */
|
|
|
|
static unsigned char font[4][7]; /* H E L O (HELLO uses L twice) */
|
|
static unsigned char order[5];
|
|
static unsigned char box[BOX_H][BOX_W / 8];
|
|
|
|
/* 5x7 glyphs, bit 4 = leftmost pixel */
|
|
static void build_font()
|
|
{
|
|
font[0][0] = 17; font[0][1] = 17; font[0][2] = 31; font[0][3] = 17;
|
|
font[0][4] = 17; font[0][5] = 17; font[0][6] = 17; /* H */
|
|
|
|
font[1][0] = 31; font[1][1] = 16; font[1][2] = 16; font[1][3] = 30;
|
|
font[1][4] = 16; font[1][5] = 16; font[1][6] = 31; /* E */
|
|
|
|
font[2][0] = 16; font[2][1] = 16; font[2][2] = 16; font[2][3] = 16;
|
|
font[2][4] = 16; font[2][5] = 16; font[2][6] = 31; /* L */
|
|
|
|
font[3][0] = 14; font[3][1] = 17; font[3][2] = 17; font[3][3] = 17;
|
|
font[3][4] = 17; font[3][5] = 17; font[3][6] = 14; /* O */
|
|
|
|
order[0] = 0; order[1] = 1; order[2] = 2; order[3] = 2; order[4] = 3;
|
|
}
|
|
|
|
int main()
|
|
{
|
|
int c, r, gx, dx, dy, px, py;
|
|
unsigned int v;
|
|
unsigned int addr;
|
|
|
|
build_font();
|
|
|
|
/* 1. prove we are here */
|
|
*(unsigned int *)MARKER = 0x48454C4C;
|
|
|
|
/* 2. clear the box (black) */
|
|
for (py = 0; py < BOX_H; py++)
|
|
for (px = 0; px < BOX_W / 8; px++)
|
|
box[py][px] = 0;
|
|
|
|
/* 3. draw HELLO, each glyph pixel as an 8x8 block */
|
|
for (c = 0; c < 5; c++) {
|
|
for (r = 0; r < 7; r++) {
|
|
v = font[order[c]][r];
|
|
for (gx = 0; gx < 5; gx++) {
|
|
if (!(v & (16 >> gx)))
|
|
continue;
|
|
for (dy = 0; dy < 8; dy++) {
|
|
for (dx = 0; dx < 8; dx++) {
|
|
px = 8 + c * 48 + gx * 8 + dx;
|
|
py = 8 + r * 8 + dy;
|
|
box[py][px >> 3] |= 1 << (px & 7);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* 4. blit the box into video RAM, one byte at a time.
|
|
*
|
|
* Pointers here are BIT addresses and pointer arithmetic scales by the
|
|
* object size, so a `char *` step is 8 bits: p + n is n bytes, not n*8.
|
|
* Address the bytes as addr + px*8 bits and cast, which is unambiguous.
|
|
*/
|
|
for (py = 0; py < BOX_H; py++) {
|
|
addr = VRAM_BASE + (BOX_Y + py) * ROW_BITS + BOX_X;
|
|
for (px = 0; px < BOX_W / 8; px++) {
|
|
*(unsigned char *)(addr + px * 8) = box[py][px];
|
|
}
|
|
}
|
|
|
|
/* 5. second marker: the blit finished */
|
|
*(unsigned int *)(MARKER + 0x20) = 0x444F4E45; /* 'DONE' */
|
|
|
|
return 0;
|
|
}
|