|
pico9918-core 1.3.0
TMS9918A / F18A video display processor emulation in C99
|
pico9918-core - debugger access More...
Include dependency graph for pico9918_debug.h:
This graph shows which files directly or indirectly include this file:Go to the source code of this file.
Macros | |
| #define | PICO9918_DEBUG_READABLE 0x01 |
| pico9918_debug_read returns real bytes here | |
| #define | PICO9918_DEBUG_WRITABLE 0x02 |
| pico9918_debug_write stores here | |
| #define | PICO9918_DEBUG_REGISTERS 0x04 |
| the register file - use pico9918_debug_reg_write | |
| #define | PICO9918_DEBUG_STATUS 0x08 |
| the status file, which the library owns | |
| #define | PICO9918_DEBUG_PALETTE 0x10 |
| PRAM - a write here republishes the palette. | |
Functions | |
| uint32_t | pico9918_debug_region (uint32_t addr, uint32_t *end) |
what the byte at addr is, and how far that stays true | |
| size_t | pico9918_debug_read (pico9918_t *tms9918, uint32_t addr, uint8_t *out, size_t len) |
| a span of the map, without disturbing anything | |
| size_t | pico9918_debug_write (pico9918_t *tms9918, uint32_t addr, const uint8_t *in, size_t len) |
| a span of the map, written without the machine noticing | |
| uint8_t | pico9918_debug_reg (pico9918_t *tms9918, uint8_t reg) |
| a register, as the register file actually holds it | |
| bool | pico9918_debug_reg_write (pico9918_t *tms9918, uint8_t reg, uint8_t value) |
| a register, stored where its number says, with no device behaviour | |
| uint16_t | pico9918_debug_palette (pico9918_t *tms9918, uint8_t index) |
| a live palette entry, in host byte order | |
| uint16_t | pico9918_debug_vram_address (pico9918_t *tms9918) |
| where the next guest access would land | |
| bool | pico9918_debug_gpu_armed (pico9918_t *tms9918) |
| whether a GPU program is waiting to run | |
| void | pico9918_debug_gpu_set_pc (pico9918_t *tms9918, uint16_t pc) |
| move the GPU's PC without starting it | |
pico9918-core - debugger access
Copyright (c) 2026 Troy Schrapel
This code is licensed under the MIT license
https://github.com/visrealm/pico9918-core
Purpose: what a host's memory pane, register editor and disassembler need, so that none of them has to include impl/.
These read and write the library's BACKING STATE - the instance's memory as it is stored, every byte appearing exactly once, the GPU's workspace overflow included. That is not the map a GPU program observes: a running personality mirrors 0x4xxx, 0x5xxx, 0x6xxx and 0x7xxx across 4KB and answers 0 in the holes. Backing state is what an emulator's debugger wants, because it re-lays-out nothing when the user switches chip, and the decoded view is derivable from it.
Nothing here disturbs the machine. No address latch moves, no read-ahead is consumed, no status is cleared, no interrupt is acknowledged, and the guest cannot tell that any of it happened - which is the entire difference between this and driving the host bus.
The scalar half of the read side is already published: pico9918_gpu_mem_size() is the size of the map these address, and pico9918_gpu_mem_value() is one byte of it. They are in gpu/gpu.h for historical reasons and are part of this surface.
Definition in file pico9918_debug.h.
| #define PICO9918_DEBUG_READABLE 0x01 |
pico9918_debug_read returns real bytes here
Definition at line 51 of file pico9918_debug.h.
| #define PICO9918_DEBUG_WRITABLE 0x02 |
pico9918_debug_write stores here
Definition at line 52 of file pico9918_debug.h.
| #define PICO9918_DEBUG_REGISTERS 0x04 |
the register file - use pico9918_debug_reg_write
Definition at line 53 of file pico9918_debug.h.
| #define PICO9918_DEBUG_STATUS 0x08 |
the status file, which the library owns
Definition at line 54 of file pico9918_debug.h.
| #define PICO9918_DEBUG_PALETTE 0x10 |
PRAM - a write here republishes the palette.
Definition at line 55 of file pico9918_debug.h.
| uint32_t pico9918_debug_region | ( | uint32_t | addr, |
| uint32_t * | end | ||
| ) |
what the byte at addr is, and how far that stays true
The map itself, so a pane can colour protected spans and split bulk work without carrying its own copy of the layout. Returns the PICO9918_DEBUG_* flags for the region containing addr, and through end, which may be null, the EXCLUSIVE address the region stops at - so a walk is addr = end until the flags come back 0.
Past the end of the map returns 0 with *end = addr, which terminates such a walk.
No instance: the layout is the build's, not the selected personality's, the same reason pico9918_gpu_mem_size() takes none.
what the byte at addr is, and how far that stays true
The flags at addr, and where they stop being true.
Definition at line 53 of file pico9918_debug.c.
| size_t pico9918_debug_read | ( | pico9918_t * | tms9918, |
| uint32_t | addr, | ||
| uint8_t * | out, | ||
| size_t | len | ||
| ) |
a span of the map, without disturbing anything
Copies up to len bytes from addr into out and returns how many, which is short at the end of the map and 0 past it. A null out, or a len of 0, copies nothing and returns 0 rather than faulting, so a caller may probe with either.
Every readable byte here is the byte pico9918_gpu_mem_value() returns for the same address. This exists because a 64KB pane one call at a time across a shared-library boundary is 65572 calls.
a span of the map, without disturbing anything
A span of the backing state, disturbing nothing.
Definition at line 71 of file pico9918_debug.c.
References pico9918_gpu_mem_size().
| size_t pico9918_debug_write | ( | pico9918_t * | tms9918, |
| uint32_t | addr, | ||
| const uint8_t * | in, | ||
| size_t | len | ||
| ) |
a span of the map, written without the machine noticing
Copies up to len bytes from in to addr and returns how many landed. A null in, or a len of 0, writes nothing and returns 0.
SHORT AT THE FIRST BYTE IT WILL NOT WRITE, which is the end of the map, the register window and the status window - the two PICO9918_DEBUG_REGISTERS and PICO9918_DEBUG_STATUS report. So a bulk loader scrubbing memory cannot start a GPU program or strand a firmware update, and a caller that wants a register has pico9918_debug_reg_write, which is a different operation with a different contract. A run that stops immediately returns 0, which is how a caller tells a refused window from an accepted one.
A span landing in PRAM republishes the palette, because the write contract is "no host-bus side effects" rather than "no effects" - without it a debugger edits the palette successfully and the picture does not change.
a span of the map, written without the machine noticing
The same span, writing, stopping at the first byte it will not.
Definition at line 88 of file pico9918_debug.c.
References PICO9918_DEBUG_PALETTE, and PICO9918_DEBUG_WRITABLE.
| uint8_t pico9918_debug_reg | ( | pico9918_t * | tms9918, |
| uint8_t | reg | ||
| ) |
a register, as the register file actually holds it
NOT what pico9918_reg_value() answers, which is the guest's read and folds the number to three bits on a locked device - so a pane showing R30 there is showing R6. This is the byte at reg. Above 63 returns 0, the file being 64 entries.
a register, as the register file actually holds it
The register file's own byte, not the guest's folded read.
Definition at line 120 of file pico9918_debug.c.
| bool pico9918_debug_reg_write | ( | pico9918_t * | tms9918, |
| uint8_t | reg, | ||
| uint8_t | value | ||
| ) |
a register, stored where its number says, with no device behaviour
NOT the device's write. pico9918_write_register_value() is a protocol: it folds the number to three bits on a locked device, so asking it for R30 stores R6; it drops the write entirely on a locked M4; and R55, R56, R50, R63 and R15 each set something in motion. A register editor wants none of that - it wants R30 to mean R30.
So this is the physical store, and its contract is a list rather than a principle. For reg 0-63 it does EXACTLY four things:
value at register reg - not reg & lockedMask, not reg & 7Everything else is untouched: the unlock latch, the GPU's address and armed state, the flash and config-dirty flags, the host address latch, every other register, every status byte, every config byte. No GPU program starts, no firmware update begins, no register file resets, no timer snaps.
The unlock latch is PRESERVED rather than recomputed, and that is deliberate: the device unlocks on the value arriving TWICE, so the byte stored in R57 does not determine the state - after one write and after two it is the same byte and the same count, differing only in the latch. Recomputing it would have to guess. Typing into a register pane is not performing the handshake, so it does not move it.
Returns false, changing nothing, for reg above 63.
a register, stored where its number says, with no device behaviour
The store itself, plus the four things it owes and nothing else.
Definition at line 129 of file pico9918_debug.c.
References pico9918_debug_sync_mode_impl(), PICO9918_INST_ONLY, and pico9918_write_reconcile_int_impl().
| uint16_t pico9918_debug_palette | ( | pico9918_t * | tms9918, |
| uint8_t | index | ||
| ) |
a live palette entry, in host byte order
PRAM as the renderer reads it, with the big-endian storage undone - so the value is the 0x0rgb an F18A program wrote, not the byte-swapped word underneath. The F18A defines the low twelve bits; anything above them is whatever is stored there, because this is the backing state and a debugger that wrote a raw byte should see it back.
Above index 63 returns 0, PRAM being 64 entries.
a live palette entry, in host byte order
PRAM with the big-endian storage undone.
Definition at line 144 of file pico9918_debug.c.
References PICO9918_R47_INDEX.
| uint16_t pico9918_debug_vram_address | ( | pico9918_t * | tms9918 | ) |
where the next guest access would land
The host address latch made EFFECTIVE, which is not the counter it is kept in: that one is 32 bits and runs past the bus width between accesses, and on a 4K chip with R1's 16K bit clear the machine permutes the address rather than merely masking it. So a pane wanting "the byte the next read returns" cannot get there with a mask.
where the next guest access would land
The latch as an access would use it, permutation included.
Definition at line 153 of file pico9918_debug.c.
References pico9918_cpu_vram_addr_impl(), and PICO9918_INST.
| bool pico9918_debug_gpu_armed | ( | pico9918_t * | tms9918 | ) |
whether a GPU program is waiting to run
Armed, not executing: a host pacing the GPU itself asks this to find out whether there is anything to step. Whether a program is still going after a slice is pico9918_gpu_step_n()'s return, which is a different question.
whether a GPU program is waiting to run
Whether a program is armed, not whether one is executing.
Definition at line 160 of file pico9918_debug.c.
| void pico9918_debug_gpu_set_pc | ( | pico9918_t * | tms9918, |
| uint16_t | pc | ||
| ) |
move the GPU's PC without starting it
Masked even, the way the register path masks it. Leaves the armed state exactly as it found it, so this redirects a program that was going to run and does not start one that was not. Writes neither R54/R55 - which would be a second, visible effect on the register file - nor the status.
pico9918_gpu_pc() reads it back.
move the GPU's PC without starting it
The PC alone - not the registers it is loaded from, not the run.
Definition at line 167 of file pico9918_debug.c.