pico9918-core
1.3.0
TMS9918A / F18A video display processor emulation in C99
Loading...
Searching...
No Matches
tms9900.h
Go to the documentation of this file.
1
/**
2
* \file
3
* \brief pico9918-core - TMS9900 CPU interpreter (portable C)
4
*
5
* Copyright (c) 2026 Troy Schrapel
6
*
7
* This code is licensed under the MIT license
8
*
9
* https://github.com/visrealm/pico9918-core
10
*
11
* This is a full reimplementation of JasonACT's RP2040 thumb assembly core
12
* found in thumb9900_m0.S / thumb9900_m33.S. It is not cycle-accurate and
13
* intentionally mirrors the status-flag encoding used in the assembly
14
* (bits: LG=0x80, AG=0x40, EQ=0x20, C=0x10, OV=0x08, P=0x04) so existing
15
* GPU glue can remain unchanged. run9900_c takes a Tms9900Cpu the caller has
16
* initialised with tms9900_init; gpu.c adapts it to the assembly core's
17
* four-argument run9900 signature. PICO9918_GPU_C_CORE selects between them.
18
*/
19
20
#pragma once
21
22
#include <stdbool.h>
23
#include <stdint.h>
24
25
#ifdef __cplusplus
26
extern
"C"
27
{
28
#endif
29
30
/* Status flag bits (mirrors the assembly core's layout) */
31
#define TMS_ST_LGT 0x80
/* Logic greater-than */
32
#define TMS_ST_AGT 0x40
/* Arithmetic greater-than */
33
#define TMS_ST_EQ 0x20
/* Equal */
34
#define TMS_ST_C 0x10
/* Carry */
35
#define TMS_ST_OV 0x08
/* Overflow */
36
#define TMS_ST_P 0x04
/* Parity (odd) */
37
38
/*
39
* Off-target, nothing watches memory for the library the way a Pico's MPU does, so
40
* a write to the GPU's DMA port is not seen until the run returns - which is far
41
* too late for a program that triggers a transfer and then keeps going. The
42
* interpreter reports writes instead. On a Pico the hardware does it and none of
43
* this is compiled.
44
*/
45
#if !defined(PICO_BUILD)
46
#define TMS9900_WATCH_WRITES 1
47
#endif
48
49
typedef
struct
Tms9900Cpu
50
{
51
uint8_t* mem;
/* Pointer to memory backing the CPU */
52
uint8_t* regx38;
/* Pointer to the GPU control byte (TMS register 0x38) */
53
uint32_t pc;
/* Program counter (uint32_t to handle WP=0xFFFE overflow) */
54
uint16_t wp;
/* Workspace pointer */
55
uint16_t st;
/* Status register (flag layout matches assembly core) */
56
57
/* Decode memory the way an F18A does: a few small windows above 16KB, each mirrored
58
across its 4KB and most of the space absent. False, which is what tms9900_init
59
leaves, is the PICO9918's own map - 64KB of memory, and what the assembly cores
60
see. Only a build that can answer as a plain F18A ever sets it. */
61
bool
f18aMemory;
62
#if defined(TMS9900_WATCH_WRITES)
63
/*
64
* Called after a write to an address the running program chose, or null.
65
*
66
* LIMITATION: workspace-relative writes are not reported - register stores and
67
* the context saves BLWP and XOP make. Those land at WP+n, and WP is >FFFE, so
68
* they can only reach a watched address if a program moves its workspace onto
69
* one with LWPI. Nothing does. Widening this means routing set_reg and the
70
* context saves through the same watch, which costs the CPU core's hot path a
71
* call per register write.
72
*/
73
void (*onWrite)(uint8_t* mem, uint32_t addr);
74
#endif
75
}
Tms9900Cpu
;
76
77
/* Initialize a CPU context */
78
void
tms9900_init(
Tms9900Cpu
* cpu, uint8_t* mem, uint8_t* regx38, uint16_t pc, uint16_t wp);
79
80
/* Execute until regx38 bit0 is cleared or an IDLE occurs. Returns final PC. */
81
uint16_t run9900_c(
Tms9900Cpu
* cpu);
82
83
/* The same, giving up after at most `budget` instructions - zero means no limit.
84
Returns the PC either way, which is what makes it resumable: a host with one
85
thread interleaves this with its renderer, and a program that waits on the
86
raster gets a raster that moves.
87
88
A budget that runs out and a program that parks on an IDLE or a self-jump both
89
leave the run flag set, so the flag cannot tell them apart. `outOfBudget`, if
90
given, does: only that one has work still to do.
91
92
The PC is not the whole of what a resume needs. A budget expires BETWEEN
93
instructions, which includes between a compare and the jump that reads what it
94
set, so `cpu->st` has to come back too: build the next call's Tms9900Cpu from the
95
one this returned rather than from tms9900_init, which starts the status at zero
96
and would have that jump decide on flags nothing set. */
97
uint16_t run9900_budget_c(
Tms9900Cpu
* cpu, uint32_t budget,
bool
* outOfBudget);
98
99
#ifdef __cplusplus
100
}
101
#endif
Tms9900Cpu
Definition
tms9900.h:50
src
gpu
tms9900.h
Generated by
1.9.8