38static void fail(
const char* what,
unsigned long wanted,
unsigned long got)
41 printf(
" FAIL %s: want %lx got %lx\n", what, wanted, got);
44static void expectRegion(
const char* what, uint32_t addr, uint32_t wantFlags, uint32_t wantEnd)
46 uint32_t end = 0xffffffffu;
47 uint32_t flags = pico9918_debug_region(addr, &end);
49 if (flags != wantFlags) fail(what, wantFlags, flags);
50 if (end != wantEnd) fail(what, wantEnd, end);
64 expectRegion(
"pram-first", 0x5000,
66 expectRegion(
"pram-last", 0x5fff,
79 if (size <= 0x10000) fail(
"map-stops-at-64k", 0x10001, size);
82 expectRegion(
"past-end", size, 0, size);
83 expectRegion(
"far-past-end", 0xfffffff0u, 0, 0xfffffff0u);
88 uint32_t at = 0, regions = 0;
92 if (pico9918_debug_region(at, &end) == 0) fail(
"walk-hole", 1, at);
95 fail(
"walk-stuck", at + 1, end);
101 if (at != size) fail(
"walk-end", size, at);
102 if (regions != 6) fail(
"walk-regions", 6, regions);
106 if (pico9918_debug_region(0x5000, NULL) !=
108 fail(
"region-null-end", 1, 0);
113 static const uint32_t probes[] = {0x0000, 0x3fff, 0x4000, 0x5000, 0x6000,
114 0x6040, 0xb000, 0xb010, 0xffff};
115 for (
unsigned i = 0; i <
sizeof(probes) /
sizeof(probes[0]); ++i)
119 ((uint8_t*)&tms9918->vram)[probes[i]] = (uint8_t)(0x5a + i);
120 if (pico9918_debug_read(
PICO9918_INST probes[i], &got, 1) != 1) fail(
"probe-short", 1, 0);
121 if (got != (uint8_t)(0x5a + i)) fail(
"probe-value", (uint8_t)(0x5a + i), got);
128 tms9918->vram.map.wrksp[4] = 0xa5;
132 if (pico9918_debug_read(
PICO9918_INST 0x10004, &got, 1) != 1) fail(
"wrksp-short", 1, 0);
133 if (got != 0xa5) fail(
"wrksp-value", 0xa5, got);
141 memset(buf, 0,
sizeof(buf));
142 if (pico9918_debug_read(
PICO9918_INST 0x5ff0, buf,
sizeof(buf)) !=
sizeof(buf))
143 fail(
"read-across-window", (
unsigned long)
sizeof(buf), 0);
150 if (pico9918_debug_read(
PICO9918_INST 0, buf, 0) != 0) fail(
"read-zero-len", 0, 1);
151 if (pico9918_debug_read(
PICO9918_INST 0, NULL,
sizeof(buf)) != 0) fail(
"read-null", 0, 1);
152 if (pico9918_debug_read(
PICO9918_INST size, buf,
sizeof(buf)) != 0) fail(
"read-at-end", 0, 1);
153 if (pico9918_debug_read(
PICO9918_INST size + 1, buf,
sizeof(buf)) != 0)
154 fail(
"read-past-end", 0, 1);
157 const size_t got = pico9918_debug_read(
PICO9918_INST size - 4, buf,
sizeof(buf));
158 if (got != 4) fail(
"read-short-at-end", 4, (
unsigned long)got);
166 memset(buf, 0x77,
sizeof(buf));
167 if (pico9918_debug_write(
PICO9918_INST 0x1000, buf,
sizeof(buf)) !=
sizeof(buf))
168 fail(
"write-vram", (
unsigned long)
sizeof(buf), 0);
169 if (((uint8_t*)&tms9918->vram)[0x1000] != 0x77) fail(
"write-vram-first", 0x77, 0);
170 if (((uint8_t*)&tms9918->vram)[0x101f] != 0x77) fail(
"write-vram-last", 0x77, 0);
173 ((uint8_t*)&tms9918->vram)[0x6000] = 0x11;
174 if (pico9918_debug_write(
PICO9918_INST 0x5ff0, buf,
sizeof(buf)) != 0x10)
175 fail(
"write-into-regs", 0x10, 0);
176 if (((uint8_t*)&tms9918->vram)[0x6000] != 0x11) fail(
"write-regs-leaked", 0x11, 0x77);
179 if (pico9918_debug_write(
PICO9918_INST 0x6010, buf,
sizeof(buf)) != 0)
180 fail(
"write-in-regs", 0, 1);
183 ((uint8_t*)&tms9918->vram)[0xb000] = 0x22;
184 if (pico9918_debug_write(
PICO9918_INST 0xaff0, buf,
sizeof(buf)) != 0x10)
185 fail(
"write-into-status", 0x10, 0);
186 if (((uint8_t*)&tms9918->vram)[0xb000] != 0x22) fail(
"write-status-leaked", 0x22, 0x77);
187 if (pico9918_debug_write(
PICO9918_INST 0xb008, buf,
sizeof(buf)) != 0)
188 fail(
"write-in-status", 0, 1);
192 if (pico9918_debug_write(
PICO9918_INST 0x4ff0, buf,
sizeof(buf)) !=
sizeof(buf))
193 fail(
"write-across-pram", (
unsigned long)
sizeof(buf), 0);
196 if (pico9918_debug_write(
PICO9918_INST 0, buf, 0) != 0) fail(
"write-zero-len", 0, 1);
197 if (pico9918_debug_write(
PICO9918_INST 0, NULL,
sizeof(buf)) != 0) fail(
"write-null", 0, 1);
198 if (pico9918_debug_write(
PICO9918_INST size, buf,
sizeof(buf)) != 0) fail(
"write-at-end", 0, 1);
200 const size_t tail = pico9918_debug_write(
PICO9918_INST size - 4, buf,
sizeof(buf));
201 if (tail != 4) fail(
"write-short-at-end", 4, (
unsigned long)tail);
207 const uint8_t entry[2] = {0x0f, 0x0a};
209 tms9918->palDirty = 0;
210 if (pico9918_debug_write(
PICO9918_INST 0x5000, entry,
sizeof(entry)) !=
sizeof(entry))
211 fail(
"write-pram", 2, 0);
212 if (!tms9918->palDirty) fail(
"write-pram-not-dirty", 1, 0);
215 tms9918->palDirty = 0;
216 if (pico9918_debug_write(
PICO9918_INST 0x1000, entry,
sizeof(entry)) !=
sizeof(entry))
217 fail(
"write-vram-again", 2, 0);
218 if (tms9918->palDirty) fail(
"write-vram-dirtied-palette", 0, 1);
221 tms9918->palDirty = 0;
222 if (pico9918_debug_write(
PICO9918_INST 0x6000, entry,
sizeof(entry)) != 0)
223 fail(
"write-regs-again", 0, 1);
224 if (tms9918->palDirty) fail(
"write-regs-dirtied-palette", 0, 1);
230 TMS_REGISTER(tms9918, 6) = 0x66;
231 TMS_REGISTER(tms9918, 30) = 0x30;
232 if (tms9918->isUnlocked) fail(
"locked-precondition", 0, 1);
234 fail(
"reg-locked-30", 0x30, pico9918_debug_reg(
PICO9918_INST 30));
237 if (pico9918_debug_reg(
PICO9918_INST 64) != 0) fail(
"reg-out-of-range", 0, 1);
245 tms9918->vram.map.pram[7] = __builtin_bswap16(0x0abc);
247 fail(
"palette-host-order", 0x0abc, pico9918_debug_palette(
PICO9918_INST 7));
248 if (pico9918_debug_palette(
PICO9918_INST 64) != 0) fail(
"palette-out-of-range", 0, 1);
254 const uint8_t entry[2] = {0x0a, 0xcb};
256 if (pico9918_debug_write(
PICO9918_INST 0x5000 + 7 * 2, entry,
sizeof(entry)) != 2)
257 fail(
"palette-write", 2, 0);
259 fail(
"palette-round-trip", 0x0acb, pico9918_debug_palette(
PICO9918_INST 7));
265 tms9918->currentAddress = 0x1234;
273 tms9918->currentAddress = 0x1ffff;
274 if (pico9918_debug_vram_address(
PICO9918_INST_ONLY) > 0xffff) fail(
"vram-addr-wide", 0, 1);
282 tms9918->restart = 0;
296 tms9918->restart = 1;
300 tms9918->restart = 0;
309 TMS_REGISTER(tms9918, 6) = 0x66;
310 if (tms9918->isUnlocked) fail(
"write-locked-precondition", 0, 1);
311 if (!pico9918_debug_reg_write(
PICO9918_INST 30, 0x3c)) fail(
"write-reg-30", 1, 0);
313 fail(
"write-reg-30-value", 0x3c, pico9918_debug_reg(
PICO9918_INST 30));
315 fail(
"write-reg-30-hit-6", 0x66, pico9918_debug_reg(
PICO9918_INST 6));
318 TMS_REGISTER(tms9918, 0) = 0x0d;
319 if (pico9918_debug_reg_write(
PICO9918_INST 64, 0xff)) fail(
"write-reg-64", 0, 1);
320 if (pico9918_debug_reg(
PICO9918_INST 0) != 0x0d) fail(
"write-reg-64-wrapped", 0x0d, 0);
323 tms9918->palDirty = 0;
325 if (!tms9918->palDirty) fail(
"write-reg-not-dirty", 1, 0);
347 if (!tms9918->frameInt) fail(
"write-reg-int-not-raised", 1, 0);
349 if (tms9918->frameInt) fail(
"write-reg-int-not-dropped", 0, 1);
356 if (tms9918->isUnlocked) fail(
"write-r57-once-unlocked", 0, 1);
358 if (tms9918->isUnlocked) fail(
"write-r57-twice-unlocked", 0, 1);
359 if (tms9918->lockedMask != 0x07) fail(
"write-r57-moved-mask", 0x07, tms9918->lockedMask);
366 tms9918->restart = 0;
368 tms9918->configDirty =
false;
369 TMS_REGISTER(tms9918, 9) = 0x99;
380 if (tms9918->flash) fail(
"write-r63-flashed", 0, 1);
384 fail(
"write-r50-reset-file", 0x99, pico9918_debug_reg(
PICO9918_INST 9));
385 if (tms9918->configDirty) fail(
"write-r50-config-dirty", 0, 1);
390 fail(
"write-r30-zero-folded", 0,
394 TMS_STATUS(tms9918, 0x0f) = 0xee;
396 if (TMS_STATUS(tms9918, 0x0f) != 0xee) fail(
"write-r15-snapped", 0xee, TMS_STATUS(tms9918, 0x0f));
405 uint8_t*
const before = malloc(
sizeof(pico9918_t));
406 const uint8_t*
const live = (
const uint8_t*)tms9918;
407 const uint32_t regByte = 0x6000 + 20;
408 const uint32_t dirty = (uint32_t)offsetof(pico9918_t, palDirty);
411 if (!before)
return fail(
"snapshot-alloc", 1, 0), 1;
413 TMS_REGISTER(tms9918, 20) = 0x00;
414 tms9918->palDirty = 0;
415 memcpy(before, tms9918,
sizeof(pico9918_t));
417 if (!pico9918_debug_reg_write(
PICO9918_INST 20, 0x5e)) fail(
"snapshot-write", 1, 0);
419 for (uint32_t i = 0; i < (uint32_t)
sizeof(pico9918_t); ++i)
421 if (before[i] == live[i])
continue;
423 if (i != regByte && i != dirty) fail(
"write-reg-touched-offset", regByte, i);
425 if (moved != 2) fail(
"write-reg-moved-count", 2, moved);
430 printf(
"%s: debugger surface, %d failure(s)\n", failures ?
"FAIL" :
"PASS", failures);
431 return failures ? 1 : 0;
uint16_t pico9918_gpu_pc(pico9918_t *tms9918)
see the header.
uint32_t pico9918_gpu_mem_size(void)
see the header.
uint8_t pico9918_gpu_mem_value(pico9918_t *tms9918, uint32_t addr)
see the header.
pico9918_mode_t pico9918_display_mode(pico9918_t *tms9918)
current display mode
uint8_t pico9918_reg_value(pico9918_t *tms9918, pico9918_register_t reg)
return a register value - see the header for the locked-device aliasing
#define TMS_R1_MODE_TEXT
40-column text
#define PICO9918_R56_GPU_RUN
register 56 bit: the GPU trigger
#define TMS_R0_MODE_TEXT_80
80-column text, with R1's text mode.
#define PICO9918_INST_ONLY
pass the instance as the only argument
#define PICO9918_R50_RESET
register 50 bits: GPU triggers and the remaining layer controls
#define TMS_R1_DISP_ACTIVE
render the active display
#define PICO9918_SR0_INT
status register 0 bits.
pico9918_register_t
the eight TMS9918 registers, by number and by what each one holds
@ PICO9918_REG_GPU_PC_MSB
GPU program counter, high byte.
@ PICO9918_REG_STATUS_SELECT
which status register S1 reads back, and the counter controls
@ PICO9918_REG_ENHANCED2
GPU triggers, per-position attributes, layer priority.
@ PICO9918_REG_UNLOCK
0x1c twice unlocks the F18A personality; any other value locks
@ PICO9918_REG_MAX_SCAN_SPRITES
sprites drawn per scanline before the limit bites
@ PICO9918_REG_GPU_PC_LSB
GPU program counter, low byte - writing it also starts the GPU.
@ PICO9918_REG_GPU_CONTROL
GPU load and trigger.
@ PICO9918_REG_FLASH_CONTROL
PICO9918 only: flash operation control.
@ PICO9918_SR_STATUS
the TMS9918A status: interrupt, 5th sprite, collision, sprite number
#define TMS_R1_INT_ENABLE
assert /INT at end of frame
#define PICO9918_INST
pass the instance ahead of other arguments
pico9918-core - debugger access
#define PICO9918_DEBUG_STATUS
the status file, which the library owns
#define PICO9918_DEBUG_WRITABLE
pico9918_debug_write stores here
#define PICO9918_DEBUG_REGISTERS
the register file - use pico9918_debug_reg_write
#define PICO9918_DEBUG_READABLE
pico9918_debug_read returns real bytes here
#define PICO9918_DEBUG_PALETTE
PRAM - a write here republishes the palette.
pico9918-core - the private instance layout
PICO9918_INLINE_HOT uint32_t pico9918_cpu_vram_addr_impl(pico9918_t *tms9918, uint32_t addr)
where a CPU-side VRAM access lands