pico9918-core 1.3.0
TMS9918A / F18A video display processor emulation in C99
Loading...
Searching...
No Matches
pico9918_priv.h
Go to the documentation of this file.
1/**
2 * \file
3 * \brief pico9918-core - the private instance layout
4 *
5 * Copyright (c) 2021 Troy Schrapel
6 *
7 * This code is licensed under the MIT license
8 *
9 * https://github.com/visrealm/pico9918-core
10 *
11 * The instance struct, the register and status accessors, and the privileged inline
12 * surface: everything a host needs that a plain consumer must not reach for. An
13 * emulator includes pico9918.h and its platform header; a host that drives the chip
14 * from an interrupt includes this, and takes on the ordering rules each entry states.
15 *
16 * The Impl entries are inline rather than calls because their callers are the host bus
17 * handlers and the per-scanline path, where a `bl` is not free.
18 *
19 * A DEBUGGER IS THE SECOND AUDIENCE, and the rule for it is which surface, not which
20 * operation.
21 *
22 * Reads are public because reads are safe, and the published set is meant to be
23 * complete. pico9918.h has the chip - pico9918_peek_status, pico9918_status_value,
24 * pico9918_read_data_no_inc, pico9918_reg_value, pico9918_vram_value - and gpu/gpu.h
25 * has the GPU: pico9918_gpu_pc, pico9918_gpu_mem_value, pico9918_gpu_mem_size,
26 * pico9918_gpu_reg_value, pico9918_gpu_status. A bridge that finds itself in this
27 * header for a READ has taken a wrong turn rather than made a judgement call, and if
28 * something genuinely has no public read then the gap is the bug.
29 *
30 * pico9918_debug.h is the rest of that set where a build asks for it - the span read and
31 * write, the map itself, the register file's own byte, and the register STORE that used
32 * to be the crossing described below.
33 *
34 * Two of those pairs look alike and are not:
35 *
36 * pico9918_vram_value | the guest's view, so it stops at 0x3FFF
37 * pico9918_gpu_mem_value | the BACKING STATE, not the map a GPU program observes: it
38 * | reaches GRAM, the palette, the register and status windows
39 * | and the workspace above 0xFFFF, each byte exactly once,
40 * | where a running personality mirrors four windows across
41 * | 4KB and answers 0 in the holes
42 * pico9918_reg_value | VR0-VR63, and the guest's view of them: on a locked device
43 * | it decodes three address bits, so reg 30 reads R6. The
44 * | physical register behind that is TMS_REGISTER, below
45 * pico9918_gpu_reg_value | the GPU's own R0-R15, out of its workspace
46 *
47 * WRITES that must not behave like the guest were the reason to be here, and the one
48 * that mattered has since been published. Every public register write but that one goes
49 * through the bus and so takes the unlock gate and the locked-mask aliasing with it - a
50 * locked device redirects VR30 to R6 rather than refusing it. A register editor writing
51 * what the operator typed wants pico9918_debug_reg_write, which is `TMS_REGISTER(tms9918,
52 * reg) = value` plus the four things a store still owes the instance.
53 *
54 * And the invariant that removal established: a PUBLIC entry must not silently write
55 * somewhere other than where its parameter names. The engine below takes the raw select
56 * byte, `0x80 | reg`, and is here rather than published for exactly that reason - typed
57 * as a register enum it turned PICO9918_REG_UNLOCK into a write of R1. Anything moved
58 * out to the public surface has to be honest about its own argument first.
59 *
60 * Nothing here is stable. It is versioned with the library and moves when the library
61 * does, so a tool that reaches in is pinned to a commit. That is the trade, and it is
62 * why anything on the guest's normal path belongs on the public surface instead.
63 */
64
65#pragma once
66
67#include "platform.h"
68
69#ifdef PICO_BUILD
70#include "pico/stdlib.h"
71#endif
72
73#include "../pico9918.h"
74#include "../pico9918_config.h"
75
76
77#define GRAPHICS_NUM_COLS 32
78#define GRAPHICS_NUM_ROWS 24
79#define GRAPHICS_CHAR_WIDTH 8
80
81#define TEXT_NUM_COLS 40
82#define TEXT_NUM_ROWS 24
83#define TEXT_CHAR_WIDTH 6
84#define TEXT_PADDING_PX 8
85#define TEXT80_NUM_COLS 80
86
87/* 80 columns at eight bits a pixel. It buys the tile palette select, ECM, the bitmap layer and
88 the shared composite in 80-column text, none of which a four-bit line can represent at all.
89 This is the scanline buffer's width, so it is settled at build time, and it is what decides
90 whether PICO9918_CHIP_PICO9918_PRO is a personality this build can be at all. The host default
91 is wide; firmware selects it from the board. The tier then chooses within that - see
92 PICO9918_WIDE_T80. */
93#ifndef PICO9918_TEXT80_8BPP
94#define PICO9918_TEXT80_8BPP 0
95#endif
96
97/* The widest line any mode on this build renders. 80 columns show 480 pixels inside a 512-pixel
98 line, which is 256 bytes at four bits a pixel and 512 at eight. */
99#if PICO9918_TEXT80_8BPP
100#define SCANLINE_BYTES_MAX (TMS9918_PIXELS_X * 2)
101/* the picture begins at sprite pixel 8 in either depth, so its byte offset doubles with the depth
102 while the sprite grid it is measured in does not */
103#define TEXT80_PADDING_PX (TEXT_PADDING_PX * 2)
104#else
105#define SCANLINE_BYTES_MAX TMS9918_PIXELS_X
106#define TEXT80_PADDING_PX TEXT_PADDING_PX
107#endif
108
109/* This one is keyed off the library's own compile flag and the published
110 PICO9918_SCANLINE_BYTES_MAX off the value recorded in the archive, which is what a
111 consumer must size against. Inside this build they are the same width, and a vendored
112 build that set only one of the two flags is the way that stops being true. */
113_Static_assert(SCANLINE_BYTES_MAX == PICO9918_SCANLINE_BYTES_MAX,
114 "PICO9918_TEXT80_8BPP disagrees with the archive's PICO9918_BUILD_TEXT80_8BPP");
115
116/* room for the cell a fine scroll uncovers past the picture's far edge */
117#define SCANLINE_BUFFER_BYTES (SCANLINE_BYTES_MAX + 8)
118#define SCANLINE_MASK_WORDS ((SCANLINE_BUFFER_BYTES + 31) / 32)
119
120#define PATTERN_BYTES 8
121#define GFXI_COLOR_GROUP_SIZE 8
122
123#define MAX_SPRITES 32
124
125#define SPRITE_ATTR_Y 0
126#define SPRITE_ATTR_X 1
127#define SPRITE_ATTR_NAME 2
128#define SPRITE_ATTR_COLOR 3
129#define SPRITE_ATTR_BYTES 4
130#define LAST_SPRITE_YPOS 0xD0
131#define MAX_SCANLINE_SPRITES 4
132
133#define BASE_VRAM_SIZE (1 << 14) /* 16kB */
134#define VRAM_SIZE (1 << 16) /* 64kB */
135
136/* Enhanced registers are read outside the unlock gate - the backdrop's R24, the frame
137 module's R19 - so the mapped register file keeps the full F18A width. */
138#define TMS_REGISTERS 64
139#define TMS_STATUS_REGISTERS 16
140
141#define VRAM_MASK (BASE_VRAM_SIZE - 1) /* 0x3fff */
142
143/* CPU-side VRAM mask. TODO: base-dependent, for 16K TMS9918A/F18A mirroring
144 against the V9938's 128K */
145#define PICO9918_CPU_VRAM_MASK(tms) VRAM_MASK
146
147/* R1 bit 7 is the 4K/16K DRAM select, and only a part that drives DRAM has one. A
148 fixed PICO9918 build has SRAM, so the transform folds away entirely there. */
149#if PICO9918_BUILD_RUNTIME_CHIP
150#define PICO9918_VRAM_4K_CHIP(T) PICO9918_HAS(T, PICO9918_FEAT_VRAM_4K)
151#else
152#define PICO9918_VRAM_4K_CHIP(T) false
153#endif
154
155
156typedef struct
157{
158 uint8_t base[BASE_VRAM_SIZE]; // 0x0000-0x3FFF (16KB)
159 /* video ram */
160 uint8_t gram1[0x1000]; // 0x4000-0x4fff (4KB) 2x repeated 2KB
161 uint16_t pram[0x0800]; // 0x5000-0x5fff (4KB) 32x repeated 128B
162
163 /* 64 write-only registers */
164 uint8_t registers[TMS_REGISTERS]; // 0x6000-0x6040
165
166 uint8_t gram2[0x1000 - TMS_REGISTERS]; // 0x6040-0x6FFF (~4KB)
167 uint8_t scanline; // 0x7000
168 uint8_t blanking; // 0x7001
169 uint8_t gram3[0x4000 - 2]; // 0x7002-0xAFFF (~16KB)
170
171 /* status registers (read-only) */
172 uint8_t status[TMS_STATUS_REGISTERS]; // 0xB000
173
174 uint8_t gram4[0x5000 - TMS_STATUS_REGISTERS]; // 0xB010-0xFFFF (~20KB)
175 uint8_t wrksp[36]; // 0x10000 overflow for hidden workspace
177
178/* Whether a GPU pass can be capped, and so whether the library can pace one itself. The
179 hand-written Thumb cores run a program to completion and ignore a cap; the builds that
180 have them are boards, which run the GPU on a core of their own. Here rather than in
181 gpu.c because the scanline path has to fold the pacing away, not test for it. */
182#if defined(PICO_BUILD) && !defined(PICO9918_GPU_C_CORE)
183#define PICO9918_GPU_BUDGETED 0
184#else
185#define PICO9918_GPU_BUDGETED 1
186#endif
187
188/* Has the F18A been unlocked, and is this a write to the register that decides it? The
189 runtime personality gate decides whether the write is honoured. */
190#define PICO9918_UNLOCKED(T) ((T)->isUnlocked)
191#define PICO9918_UNLOCK_REG(R) ((R) == (0x80 | PICO9918_REG_UNLOCK))
192#define PICO9918_UNLOCK_VALUE(V) (((V) & 0xfc) == PICO9918_R57_UNLOCK)
193
194/* What a personality answers to. Derived once, in pico9918_set_chip, so each site reads
195 one bit rather than re-deriving the ladder. Only what a personality can be asked to do
196 differently is a bit: the GPU is not one, because a program can only be started
197 through registers the unlock gate already covers.
198
199 Without the runtime switch there is nothing to gate - the build is one chip, and what
200 that chip can do it already does - so every one of them folds to a literal true and
201 the gates cost a board exactly what they cost it before they existed. */
202#define PICO9918_FEAT_UNLOCK 0x01 /* the F18A unlock write is honoured */
203#define PICO9918_FEAT_CONFIG 0x02 /* the VR58/59 config port and R63 firmware update */
204#define PICO9918_FEAT_OVERLAY 0x04 /* the splash and diagnostics overlays */
205#define PICO9918_FEAT_BITMAP 0x08 /* R0 M3 is decoded, so Graphics II exists */
206#define PICO9918_FEAT_VRAM_4K 0x10 /* R1 bit 7 is decoded, so 4K DRAM addressing exists */
207#define PICO9918_FEAT_WIDE_T80 0x20 /* 80-column text is a byte a pixel, not a nibble */
208#define PICO9918_FEAT_GPU_RAM 0x40 /* the GPU's whole 64KB is memory, not the F18A's windows */
209
210#if PICO9918_BUILD_RUNTIME_CHIP
211#define PICO9918_HAS(T, F) (((T)->features & (F)) != 0)
212/* SR1 is what software probing for an F18A reads: 0xE0 is the F18A ID, and the PICO9918
213 sets 0x08 for anyone who cares that it is not a real one. The base personality shares
214 the F18A value and never shows it: reaching SR1 needs a write to R15, which is above
215 the eight a locked device admits.
216
217 TRAP: >=, not ==. A PRO is a PICO9918 to software probing for one, so every
218 personality at or above PICO9918 answers 0xE8 and a tier added above must not fall
219 through to the real-F18A value by being numbered past the test. */
220#define PICO9918_SR1_ID(T) (((T)->chip >= PICO9918_CHIP_PICO9918) ? 0xE8 : 0xE0)
221#else
222#define PICO9918_HAS(T, F) true
223#define PICO9918_SR1_ID(T) 0xE8
224#endif
225
226#define TMS_REGISTER(T, R) (T->vram.map.registers[R])
227#define TMS_STATUS(T, R) (T->vram.map.status[R])
228
229/* Graphics II is the A in TMS9918A: the pre-A personality does not decode R0 M3.
230 PICO9918_HAS folds to a literal true without the runtime switch. */
231#define PICO9918_GM2(T) PICO9918_HAS(T, PICO9918_FEAT_BITMAP)
232
233/* The PRO tier's wide 80-column line. Only asked where the build has the buffer for it,
234 so a narrow build never reaches this and a board folds it to a literal true. */
235#define PICO9918_WIDE_T80(T) PICO9918_HAS(T, PICO9918_FEAT_WIDE_T80)
236
237/* An F18A's GPU reaches a few small windows above 16KB and mirrors each across its 4KB;
238 a PICO9918 backs the whole address space with RAM, which is what the assembly cores do. */
239#define PICO9918_GPU_FLAT_MEM(T) PICO9918_HAS(T, PICO9918_FEAT_GPU_RAM)
240
241/* A TMS9918A does not decode R0 bit 2. */
242#define PICO9918_CAN_UNLOCK(T) PICO9918_HAS(T, PICO9918_FEAT_UNLOCK)
243#define PICO9918_M4(T) \
244 (PICO9918_CAN_UNLOCK(T) && (TMS_REGISTER(T, TMS_REG_0) & TMS_R0_MODE_TEXT_80))
245
246
247/* PRIVATE DATA STRUCTURE
248 * ---------------------- */
250{
251 /* First: every VRAM, register and status access goes through this union, so at offset
252 zero the instance pointer doubles as its base, and the GPU MPU guard's two ranges
253 are page-aligned by construction rather than by an offset-derived mask. */
254 union
255 {
256 uint8_t bytes[VRAM_SIZE];
258 } vram;
259
260 /* current address for cpu access (auto-increments) */
261 uint32_t currentAddress;
262
263 uint16_t gpuAddress;
264
265 /* The GPU's status register, across a bounded step and nothing else. run9900 keeps
266 it in a local, which is all a run to completion needs; a budget can expire between
267 the compare that sets a flag and the jump that reads it, so a resume that started
268 from zero would take the wrong branch. Only the portable core takes a budget, so
269 only the portable core reads this. */
270 uint16_t gpuStatus;
271
272 /* address or register write stage (0 or 1) */
273 uint8_t regWriteStage;
274
275 /* holds first stage of write to address/register port */
276 uint8_t regWriteStage0Value;
277
278 /* buffered value */
279 uint8_t readAheadBuffer;
280
281 uint8_t lockedMask; // 0x07 when locked, 0x3F when unlocked
282 uint8_t unlockCount; // number of unlock steps taken
283 bool isUnlocked; // boolean version of lockedMask - read through PICO9918_UNLOCKED
284
285 volatile uint8_t restart;
286 volatile uint8_t flash;
287
288#if PICO9918_GPU_BUDGETED
289 /* Zero leaves an armed program to whoever else runs it. See pico9918_gpu_set_clock. */
290 uint32_t gpuIps;
291 uint32_t gpuSlice;
292#endif
293
294 /* palette writes are done in two stages too */
295 uint8_t palWriteStage;
296 uint8_t palWriteStage0Value;
297 uint8_t palDirty;
298
299 /* runtime base VDP selection (independent of F18A unlock) */
300 uint8_t vdpBase; /* PICO9918_BASE_TMS9918 or PICO9918_BASE_V9938 */
301
302#if PICO9918_BUILD_RUNTIME_CHIP
303 /* which chip this instance answers as, and the same thing as the bit per feature the
304 gates read. Both survive a reset - see pico9918_set_chip. Absent from a board's
305 build, which is one chip and gates nothing. */
306 uint8_t chip; /* pico9918_chip_t */
307 uint8_t features; /* PICO9918_FEAT_* - read through PICO9918_HAS */
308#endif
309
310 bool scanlineHasSprites;
311
312 uint32_t startTime;
313 uint32_t stopTime;
314 uint32_t currentTime;
315
316 struct
317 {
318 uint16_t y; /* raw scanline */
319 uint16_t y1; /* T1 layer Y after scroll (= y when locked or scroll=0) */
320 uint16_t y2; /* T2 layer Y after scroll (= y when T2 disabled or locked) */
321 bool swapY1Page; /* T1 name-table page swap flag */
322 bool swapY2Page; /* T2 name-table page swap flag */
323 uint8_t* pixels; /* pointer to caller-owned output pixel buffer */
324 } scanCtx;
325
326 uint8_t config[256];
327 bool configDirty;
328
329 /* the VDP state the configuration seeds - palette, sprite limit, scanlines - is
330 owed. Not palDirty above, which is the converted palette copy owing PRAM. */
331 bool configVdpDirty;
332
333 /* Aligned tile rendering optimization buffers - one extra tile for the scroll offset */
334 uint8_t __aligned(4) tileLayer2Buffer[SCANLINE_BUFFER_BYTES];
335 uint8_t __aligned(4) tileLayer1Buffer[SCANLINE_BUFFER_BYTES];
336 uint32_t __aligned(4) layerSelectionMask[SCANLINE_MASK_WORDS]; // 1 bit per pixel: 0=T1, 1=T2
337 uint32_t __aligned(4) finalMask[SCANLINE_MASK_WORDS]; // 1 bit per pixel: 0=T1, 1=T2
338
339 /* Frame interrupt / status state. Not volatile, and touched from both the CPU-interface
340 handlers and the frame path; the critical section around updateInterrupts is what
341 orders them. */
342 bool frameInt; /* current /INT pin state (true = asserted) */
343 uint8_t frameStatus; /* SR0 shadow - the latch the frame path merges into */
344 bool frameDoneInt; /* interrupt already raised this frame? */
345
346#if !PICO9918_SINGLE_INSTANCE
347 /* The integration layer's host callbacks, per instance. Absent from the single-instance
348 struct entirely: that build keeps them in file statics, so a board's layout is what it
349 was and the MPU guard's offset assertion above still holds. */
350 struct
351 {
352 pico9918_config_applied_fn fn;
353 void* userdata;
354 } configApplied;
355
356 struct
357 {
358 pico9918_config_reload_fn fn;
359 void* userdata;
360 } configReload;
361
362 struct
363 {
364 pico9918_gpu_flash_fn fn;
365 void* userdata;
366 } gpuFlash;
367
368 struct
369 {
370 pico9918_gpu_config_save_fn fn;
371 void* userdata;
372 } gpuConfigSave;
373#endif
374};
375
376#ifdef PICO_BUILD
377/* MPU guard anchor (see guard() in gpu/gpu.c): the guarded windows are derived
378 from vram's address. At offset zero, and with the instance 256-aligned, both land at the
379 start of their own 256-byte page and neither can cross one. Move vram and that stops
380 being true by construction - restore the layout rather than shift the window. */
381_Static_assert(offsetof(struct pico9918_s, vram) == 0,
382 "vram offset moved - the GPU MPU guard ranges could cross a page boundary");
383#endif
384
385#if PICO9918_SINGLE_INSTANCE
386extern pico9918_t* const tms9918;
387#endif
388
389/* The line's own metadata (pico9918.c), on the impl surface because pico9918_frame.c reads all
390 * of it every active line and each public accessor is a real `bl` across the TU boundary.
391 *
392 * Shared between instances, like the palette LUT: pico9918_scan_line sets the mode from the
393 * instance's own registers on entry, and a mismatch there is what marks that LUT dirty.
394 *
395 * TRAP: this block must stay below the tms9918 declaration above. Placed before it, a
396 * single-instance build fails on an identifier that has not been declared yet, and the error
397 * names the inline body rather than the ordering.
398 */
399extern pico9918_mode_t pico9918_cached_mode;
400extern const uint8_t* pico9918_cached_line_source;
401
402/* Is this row 80 columns at one byte a pixel - twice as wide a line, on two pixel grids?
403 Without the tier it is a literal false, so every count and shift below it folds away.
404 Unlocked only, and that is not a restriction: all four things the tier buys are F18A features
405 that need the unlock anyway, so locked 80-column text keeps the packed line and its own
406 emitter. */
407#if PICO9918_TEXT80_8BPP
408#define TEXT80_WIDE_ROW \
409 (pico9918_cached_mode == TMS_MODE_TEXT80 && PICO9918_UNLOCKED(tms9918) && PICO9918_WIDE_T80(tms9918))
410#else
411#define TEXT80_WIDE_ROW false
412#endif
413
414PICO9918_INLINE pico9918_mode_t pico9918_display_mode_impl(PICO9918_INST_ONLY_ARG)
415{
416 (void)tms9918;
417 return pico9918_cached_mode;
418}
419
420PICO9918_INLINE uint32_t pico9918_line_bytes_impl(PICO9918_INST_ONLY_ARG)
421{
422 (void)tms9918;
423 return TEXT80_WIDE_ROW ? SCANLINE_BYTES_MAX : TMS9918_PIXELS_X;
424}
425
426PICO9918_INLINE const uint8_t* pico9918_line_source_impl(PICO9918_INST_ONLY_ARG)
427{
428 (void)tms9918;
429 return pico9918_cached_line_source;
430}
431
432/**
433 * \brief where a CPU-side VRAM access lands
434 *
435 * A part that drives DRAM multiplexes the address as a row and a column, and R1 bit 7
436 * says how wide each half is. At 16K it is seven bits of each and the address is used
437 * raw. At 4K it is six of each, driven into the seven that the 16K DRAMs on the board
438 * still want, which rotates the middle seven bits up one place and leaves the low six
439 * and the top one where they were.
440 *
441 * Only the CPU side. Display fetches take the address the tables name, because an F18A
442 * has no such bit at all and nothing drives a picture out of 4K on a 16K machine.
443 */
444PICO9918_INLINE_HOT uint32_t pico9918_cpu_vram_addr_impl(PICO9918_INST_ARG uint32_t addr)
445{
446 addr &= PICO9918_CPU_VRAM_MASK(tms9918);
447
448 if (PICO9918_VRAM_4K_CHIP(tms9918) && !(TMS_REGISTER(tms9918, TMS_REG_1) & TMS_R1_RAM_16K))
449 {
450 /* static bits | shifted bits | rotated bit */
451 addr = (addr & 0x203f) | ((addr & 0x0fc0) << 1) | ((addr & 0x1000) >> 6);
452 }
453
454 return addr;
455}
456
457/**
458 * \brief set a register from the second byte of a host register write
459 *
460 * \p regSelect is that byte, not a register number: bit 7 set, the register in the low
461 * six. The locked-mask aliasing and the M4 rule are both defined on it, which is why it
462 * is not a pico9918_register_t. Carries the unlock sequence, the GPU arming writes, the
463 * palette rebuild, and the /INT reconcile on the three writes that can change the pin -
464 * R0, R1 and the unlock latch. Out of line, unlike its neighbours here: it is large, and
465 * the inline entry below is its only hot caller.
466 */
468void pico9918_write_reg_value_impl(PICO9918_INST_ARG uint8_t regSelect, uint8_t value);
469
470/**
471 * \brief write an address (mode = 1) to the tms9918
472 *
473 * data: the data (DB0 -> DB7) to send
474 */
475PICO9918_INLINE_HOT void pico9918_write_addr_impl(PICO9918_INST_ARG uint8_t data)
476{
477 if (tms9918->regWriteStage == 0)
478 {
479 /* first stage byte - either an address LSB or a register value */
480
481 tms9918->regWriteStage0Value = data;
482 tms9918->regWriteStage = 1;
483 }
484 else
485 {
486 /* second byte - either a register number or an address MSB */
487
488 if (data & 0x80) /* register */
489 {
490 if ((data & 0x40) == 0) // 64 registers, so only bit 6 is reserved
491 {
492 pico9918_write_reg_value_impl(PICO9918_INST data, tms9918->regWriteStage0Value);
493 }
494 }
495 else /* address */
496 {
497 tms9918->currentAddress = tms9918->regWriteStage0Value | ((data & 0x3f) << 8);
498 if ((data & 0x40) == 0)
499 {
500 tms9918->readAheadBuffer =
501 tms9918->vram.bytes[pico9918_cpu_vram_addr_impl(PICO9918_INST tms9918->currentAddress)];
502 tms9918->currentAddress += (int8_t)TMS_REGISTER(tms9918, PICO9918_REG_VRAM_INC); // increment register
503 }
504 }
505 tms9918->regWriteStage = 0;
506 }
507}
508
509/**
510 * \brief is R#15's status-register select live?
511 *
512 * True on an F18A-unlocked device, and also on a locked V9938-base one - the V9938 has
513 * R#15 in its base register set, so status select is not an unlock privilege there.
514 *
515 * The second disjunct is dead today and folds away: only the unlock sequence widens
516 * `lockedMask` past 0x07, so a locked device cannot have had R#15 written whatever its
517 * base. It is written base-aware so that V9938 support swaps a base rather than a
518 * scattered condition.
519 */
521{
522 return PICO9918_UNLOCKED(tms9918) || (tms9918->vdpBase == PICO9918_BASE_V9938);
523}
524
525/**
526 * \brief THE single implementation of "a status register was just read" - shared by the
527 * public read (pico9918_read_status_impl) and the CPU-interface read reconcile
528 * (pico9918_status_read_reconcile_impl); see the entry shims for which side
529 * effects each one owns.
530 *
531 * readReg: the selected status register (0..15)
532 * readVal: the value the reader received
533 *
534 * SR0: clear only the flags that were actually seen set, out of the SR0 shadow
535 * the frame path latches into. A seen 5S additionally restores the sprite
536 * number field to 31 (the reset value) - the flag and its ID clear together.
537 * SR1: bit 0 is the R#19 line-interrupt flag, clear-on-read.
538 */
539/* Defined below, and needed here: reading SR1 clears the scanline flag, which can be the
540 only thing holding /INT down. An ISR that acknowledged and returned with the pin still
541 asserted would re-enter immediately. */
543
544PICO9918_INLINE_HOT void pico9918_status_read_core(PICO9918_INST_ARG uint8_t readReg, uint8_t readVal)
545{
546 if (readReg == PICO9918_SR_STATUS)
547 {
549 tms9918->frameStatus &= ~readVal; // Clear only the flags that were set
550 if (readVal & PICO9918_SR0_5S) // Was 5th Sprite flag set?
551 tms9918->frameStatus |= 0x1f; // Set sprite number to 31
552 TMS_STATUS(tms9918, PICO9918_SR_STATUS) = tms9918->frameStatus;
553 if (readVal & PICO9918_SR0_INT) // Was Interrupt flag set?
554 {
555 tms9918->frameInt = false;
556 PICO9918_HOST_SET_INT(false);
557 }
558 }
559 else if (readReg == PICO9918_SR_IDENT)
560 {
561 if (readVal & PICO9918_SR1_HF)
562 {
563 TMS_STATUS(tms9918, PICO9918_SR_IDENT) &= (uint8_t)~PICO9918_SR1_HF;
564 /* the frame source may still be asserting, so re-derive rather than clearing */
566 if (stillInt != tms9918->frameInt)
567 {
568 tms9918->frameInt = stillInt;
569 PICO9918_HOST_SET_INT(stillInt);
570 }
571 }
572 }
573}
574
575/**
576 * \brief CPU-interface entry: the host's read-ahead already handed the CPU a value, so
577 * this only applies the read's side effects. The host supplies both the value the
578 * CPU saw and the register it came from (zero while status select is inactive).
579 */
580PICO9918_INLINE_HOT void pico9918_status_read_reconcile_impl(PICO9918_INST_ARG uint8_t readReg, uint8_t readVal)
581{
582 tms9918->regWriteStage = 0;
583
585
586 pico9918_status_read_core(PICO9918_INST readReg, readVal);
587}
588
589/**
590 * \brief read from the status register
591 *
592 * Emulator-facing entry: fetches the value itself, then runs the same core. It
593 * additionally resets the data-port palette staging, which the CPU-interface
594 * path never did (that port is written through a different host seam).
595 *
596 * PICO9918_INLINE, not PICO9918_INLINE_HOT. The core it calls reaches
597 * PICO9918_HOST_SET_INT, which on Pico is an SDK `static inline` gpio_put, and a
598 * non-static inline may not call a static one - which is why every entry on this
599 * surface is static. Its only caller is pico9918_read_status() in pico9918.c, the
600 * external definition consumers link against.
601 *
602 * TWO BEHAVIOURS worth stating outright, because this is a public entry point and
603 * no gate makes either visible:
604 *
605 * 1. Reading SR0 does NOT scrub the sprite-number field. It clears only the
606 * (INT|5S|COL) flags actually seen set, restoring the number to 31 only when 5S
607 * was among them. That follows the documented register layout: F is
608 * clear-on-read, but SP4-SP0 is a data field, not a flag.
609 * 2. On a host that defines PICO9918_HOST_SET_INT, reading SR0 with F set RELEASES
610 * THE /INT LINE and clears the library's interrupt shadow - so every input with
611 * bit 7 set writes the pin. A host running its own /INT plumbing alongside this
612 * call will see an unrequested pin write; such a host should drive the line from
613 * the library's state rather than in parallel with it.
614 */
616{
617 tms9918->regWriteStage = 0;
618
619 tms9918->palWriteStage = 0;
620 TMS_REGISTER(tms9918, PICO9918_REG_PALETTE_CONTROL) &=
621 (uint8_t)~PICO9918_R47_DATA_PORT; // reset data port palette mode
622
623 const uint8_t readReg = TMS_REGISTER(tms9918, PICO9918_REG_STATUS_SELECT) & PICO9918_R15_STATUS_NUM;
624 const uint8_t readVal = TMS_STATUS(tms9918, readReg);
625
626 pico9918_status_read_core(PICO9918_INST readReg, readVal);
627
628 return readVal;
629}
630
631/** \brief read from the status register without resetting it */
633{
634 return TMS_STATUS(tms9918, PICO9918_SR_STATUS);
635}
636
637/**
638 * \brief write data (mode = 0) to the tms9918
639 *
640 * data: the data (DB0 -> DB7) to send
641 */
642PICO9918_INLINE_HOT void pico9918_write_data_impl(PICO9918_INST_ARG uint8_t data)
643{
644 if (TMS_REGISTER(tms9918, PICO9918_REG_PALETTE_CONTROL) &
645 PICO9918_R47_DATA_PORT) // data port is in palette mode
646 {
647 if (tms9918->palWriteStage == 0)
648 {
649 tms9918->palWriteStage0Value = data & 0x0f;
650 ++tms9918->palWriteStage;
651 }
652 else
653 {
654 tms9918->palWriteStage = 0;
655
656 // this looks backwards because ARM is little-endian, TMS9900 is big-endian.
657 tms9918->vram.map.pram[TMS_REGISTER(tms9918, PICO9918_REG_PALETTE_CONTROL) & PICO9918_R47_INDEX] =
658 (tms9918->palWriteStage0Value) | (data << 8);
659 tms9918->palDirty = 1;
660
661 // reset data port palette mode
662 if (TMS_REGISTER(tms9918, PICO9918_REG_PALETTE_CONTROL) & PICO9918_R47_AUTO_INC)
663 {
664 ++TMS_REGISTER(tms9918, PICO9918_REG_PALETTE_CONTROL);
665 }
666 else
667 {
668 TMS_REGISTER(tms9918, PICO9918_REG_PALETTE_CONTROL) &= (uint8_t)~PICO9918_R47_DATA_PORT;
669 }
670 }
671 }
672 else
673 {
674 tms9918->regWriteStage = 0;
675 tms9918->readAheadBuffer = data;
676 tms9918->vram.bytes[pico9918_cpu_vram_addr_impl(PICO9918_INST tms9918->currentAddress)] = data;
677 tms9918->currentAddress += (int8_t)TMS_REGISTER(tms9918, PICO9918_REG_VRAM_INC); // increment register
678 }
679}
680
681
682/** \brief read data (mode = 0) from the tms9918 */
684{
685 tms9918->regWriteStage = 0;
686 uint8_t currentValue = tms9918->readAheadBuffer;
687 tms9918->readAheadBuffer =
688 tms9918->vram.bytes[pico9918_cpu_vram_addr_impl(PICO9918_INST tms9918->currentAddress)];
689 tms9918->currentAddress += (int8_t)TMS_REGISTER(tms9918, PICO9918_REG_VRAM_INC); // increment register
690 return currentValue;
691}
692
693/** \brief refill the read-ahead buffer from the current address and return the new value */
695{
696 tms9918->regWriteStage = 0;
697 tms9918->readAheadBuffer =
698 tms9918->vram.bytes[pico9918_cpu_vram_addr_impl(PICO9918_INST tms9918->currentAddress)];
699 tms9918->currentAddress += (int8_t)TMS_REGISTER(tms9918, PICO9918_REG_VRAM_INC); // increment register
700 return tms9918->readAheadBuffer;
701}
702
703/** \brief return the buffered value without reading VRAM or advancing the address */
705{
706 return tms9918->readAheadBuffer;
707}
708
709/** \brief return true if both INT status and INT control set */
710/**
711 * \brief whether /INT should be asserted
712 *
713 * Two independent sources, as on the F18A: the end-of-frame flag under R1's enable, and
714 * the scanline flag under R0's. Neither gates the other - a program that wants only the
715 * scanline interrupt turns R1's off - so the horizontal source cannot be folded into SR0.
716 * The scanline term leads with the flag because it is clear on almost every line, and the
717 * whole term folds away in a TMS9918A build, which has no R19 to arm it.
718 */
720{
721 return ((TMS_REGISTER(tms9918, TMS_REG_1) & TMS_R1_INT_ENABLE) &&
722 (TMS_STATUS(tms9918, PICO9918_SR_STATUS) & PICO9918_SR0_INT)) ||
723 (PICO9918_UNLOCKED(tms9918) && (TMS_STATUS(tms9918, PICO9918_SR_IDENT) & PICO9918_SR1_HF) &&
724 (TMS_REGISTER(tms9918, TMS_REG_0) & TMS_R0_INT_SCANLINE));
725}
726
727/** \brief raise the interrupt flag in SR0 and the frame shadow */
729{
730 tms9918->frameStatus |= PICO9918_SR0_INT;
731 TMS_STATUS(tms9918, PICO9918_SR_STATUS) |= PICO9918_SR0_INT;
732}
733
734/**
735 * \brief set status flag
736 *
737 * Writes the SR0 shadow too. SR0 has exactly one authoritative value: the frame
738 * path latches into the shadow and publishes it, so a setter that moved only the
739 * register would leave the next latch merging into a stale value.
740 */
741PICO9918_INLINE_HOT void pico9918_set_status_impl(PICO9918_INST_ARG uint8_t status)
742{
743 tms9918->frameStatus = status;
744 TMS_STATUS(tms9918, PICO9918_SR_STATUS) = status;
745}
746
747/* Frame interrupt / status state accessors
748 * ----------------------------------------
749 * frameInt / frameStatus / frameDoneInt are NOT exposed as extern variables: both
750 * the CPU-interface handlers and the frame path mutate them, so going through the
751 * Impl layer keeps ownership in one place.
752 */
753
754/* the SR0 latch the frame path merges into */
755PICO9918_INLINE uint8_t pico9918_frame_status_impl(PICO9918_INST_ONLY_ARG)
756{
757 return tms9918->frameStatus;
758}
759
760/* current /INT pin state */
761PICO9918_INLINE bool pico9918_frame_int_impl(PICO9918_INST_ONLY_ARG)
762{
763 return tms9918->frameInt;
764}
765
766/* has an interrupt been raised this frame? */
767PICO9918_INLINE bool pico9918_frame_done_int_impl(PICO9918_INST_ONLY_ARG)
768{
769 return tms9918->frameDoneInt;
770}
771
772PICO9918_INLINE void pico9918_set_frame_done_int_impl(PICO9918_INST_ARG bool done)
773{
774 tms9918->frameDoneInt = done;
775}
776
777/* Frame counter and dropped-frame accounting
778 * ------------------------------------------
779 * Defined in pico9918_frame.c, which owns every write. The host only READS them,
780 * which is what makes plain module globals behind inline accessors the right shape
781 * here, rather than the instance fields the interrupt/status state uses: that state
782 * is mutated by BOTH the bus-interface handlers and the frame path, so ownership has
783 * to be forced through one place. Nothing outside this module writes these, so there
784 * is no such split to arbitrate, and `pico9918_palette_lut` below is the precedent
785 * for a library-owned global on the privileged Impl surface.
786 *
787 * The choice is measured, not stylistic. The scanline path reads the count three
788 * times per border scanline and must not gain work. As instance fields the offsets
789 * land past the 64KB vram union, so each read needs a literal-pool offset AND the
790 * instance base - two instructions more in the scanline path and two in the GPIO IRQ
791 * handler, verified by building it both ways. As globals the pool holds the address
792 * directly.
793 *
794 * Not volatile and not guarded: all writes are on core 1 (the frame path and the
795 * reset IRQ, which is also core 1), and every reader is on core 1 too.
796 */
797extern int pico9918_frame_count;
798extern int pico9918_dropped_frames_count;
799
800#if PICO9918_DIAG_GPU_FRAME_COUNTER
801/* GPU frames observed at end of frame, read by the diag overlay's GPU-frames row -
802 which is why it is on this surface rather than a file static: the frame module
803 writes it and the overlay reads it directly. */
804extern uint32_t pico9918_gpu_frame_count;
805#endif
806
807/* Read-only from the host's point of view: the frame module owns the increment and
808 the reset, and advances the global directly - the end-of-frame sequence is
809 internal to it. The host's scanline reads this as the splash animation clock and
810 the startup-diagnostics threshold. */
811PICO9918_INLINE int pico9918_frame_count_impl(PICO9918_INST_ONLY_ARG)
812{
813 return pico9918_frame_count;
814}
815
816/* console-reset entry for the frame counter. Deliberately does NOT clear the
817 dropped-frame window, and does NOT clear pico9918_valid_writes: the dropped-frame
818 count is a rolling 16-frame average that is allowed to span a reset, and
819 validWrites is a once-per-run latch whose consumers (the splash hand-off, the
820 startup diagnostics screen) must not be re-armed by a console reset. */
821PICO9918_INLINE void pico9918_frame_reset_count_impl(PICO9918_INST_ONLY_ARG)
822{
823 pico9918_frame_count = 0;
824#if PICO9918_DIAG_GPU_FRAME_COUNTER
825 pico9918_gpu_frame_count = 0;
826#endif
827}
828
829/* Vertical geometry and the display-enable latch
830 * ----------------------------------------------
831 * Defined in pico9918_frame.c alongside the frame counter, for consistency of
832 * ownership. Unlike the frame counter, globals are NOT cheaper here: MEASURED both
833 * ways, instance fields make the scanline path two instructions SHORTER, and
834 * inspecting the two disassemblies that difference is register-allocation churn in
835 * the prologue rather than address arithmetic at the read sites. A codegen
836 * coin-flip, not a structural cost - so do not assume either form is free without
837 * looking.
838 *
839 * Globals are used because they match the frame counter these sit beside: one module
840 * owns one kind of state one way.
841 *
842 * Not volatile and not guarded: the frame module is the only writer and the host's
843 * scanline the only reader, both on core 1. */
844extern int pico9918_v_pixels;
845extern uint32_t pico9918_v_border;
846extern bool pico9918_valid_writes;
847extern uint8_t pico9918_v_scale;
848extern uint16_t pico9918_v_virtual;
849
850/* The border colour word the border-fill DMA instance reads. Declared here only so
851 pico9918.c's initLookups() can point the fill
852 instance at it: the frame module owns the storage, the placement and every write,
853 and the init site owns every fill instance. Not an accessor, because there is
854 nothing to accessorise - the one out-of-module user needs its ADDRESS, once, at
855 init. Placement is .scratch_y; see the definition. */
856extern uint32_t pico9918_border_bg;
857
858/* Active VDP display lines, and the top border offset in virtual lines. Written
859 once per frame by pico9918_frame_geometry; read by the host's scanline for its
860 border test and for the overlay geometry it forwards.
861
862 vBorder is UNSIGNED, and that is load-bearing rather than incidental - see the
863 narrowing note on pico9918_frame_geometry_t in pico9918_frame.h. */
864PICO9918_INLINE int pico9918_v_pixels_impl(PICO9918_INST_ONLY_ARG)
865{
866 return pico9918_v_pixels;
867}
868
869PICO9918_INLINE uint32_t pico9918_v_border_impl(PICO9918_INST_ONLY_ARG)
870{
871 return pico9918_v_border;
872}
873
874/* Has the VDP display been enabled at all since power-on or the last console
875 reset? Latched by pico9918_frame_end on the first frame that sees R1 bit 6 set,
876 and never cleared - the splash hand-off and the startup diagnostics screen both
877 hang off it and are once-per-run. */
878PICO9918_INLINE bool pico9918_valid_writes_impl(PICO9918_INST_ONLY_ARG)
879{
880 return pico9918_valid_writes;
881}
882
883/* Dropped frames over the trailing 16-frame window.
884 *
885 * RETAINED with no in-tree caller, deliberately: the frame module owns the counter
886 * and runs the diagnostics refresh, so it reads its own global directly. The
887 * accessor stays because the counter is a documented part of what this module
888 * accounts for, and an integrator driving frames has no other way to read it.
889 * Compare pico9918_frame_count_impl, which the host does read. */
890PICO9918_INLINE int pico9918_dropped_frames_impl(PICO9918_INST_ONLY_ARG)
891{
892 return pico9918_dropped_frames_count;
893}
894
895/* Function: pico9918_frame_map_line_impl
896 * ---------------------------------------
897 * the interlace field mapping: which VDP line a given display line renders.
898 *
899 * y: the line WITHIN THE DISPLAY REGION, i.e. after the top border has been
900 * subtracted. Not the raw VGA-encoded line: the scanline path applies the
901 * mapping only on its active arm, where y is already border-relative, and the
902 * mapping must see the same value it saw before the move.
903 * field: the field number, which the caller has already separated out of the raw y's
904 * bit 12 - it needs it on the border arm too.
905 * interlaced / fieldOrder: the mode's interlace parameters.
906 *
907 * Applied ONLY under interlace AND double-rows; otherwise the VDP line is y unchanged
908 * and the field number is DISCARDED. The two fields interleave the doubled VDP line
909 * space, and fieldOrder selects which field takes the even lines.
910 *
911 * An inline function rather than four lines inside pico9918_frame_scanline, and the
912 * reason is the gate, not tidiness. The golden frame surface's mapping group calls
913 * THIS, so it compares the shipping code against its own independent model. The
914 * scanline as a whole cannot serve that purpose - it does DMA and renders a line.
915 * This is the smallest thing that can.
916 *
917 * PICO9918_INLINE, so its one library caller inlines it and the per-scanline path is
918 * unchanged.
919 *
920 * The `y * 2 + (field ^ fieldOrder)` FUSION IS DELIBERATE and must not be decomposed
921 * into explicit even/odd cases. The harness's independent reference does exactly that
922 * decomposition, and its whole value is that it does not share this algebra. */
923PICO9918_INLINE uint16_t pico9918_frame_map_line_impl(PICO9918_INST_ARG uint16_t y, uint8_t field,
924 bool interlaced, uint8_t fieldOrder)
925{
926 uint16_t tmsY = y;
927 if (interlaced && (TMS_REGISTER(tms9918, TMS_REG_0) & TMS_R0_DOUBLE_ROWS))
928 tmsY = y * 2 + (field ^ fieldOrder);
929 return tmsY;
930}
931
932/**
933 * \brief recompute the interrupt state and, only if it changed, drive the pin.
934 *
935 * The single place the /INT pin is asserted or released from a recomputation.
936 * Both the post-write reconcile and updateInterrupts' tail need exactly this.
937 */
939{
941 if (newInt != tms9918->frameInt)
942 {
943 tms9918->frameInt = newInt;
944 PICO9918_HOST_SET_INT(newInt);
945 }
946}
947
948/**
949 * \brief bring /INT into agreement after a write that can change the predicate. An R1
950 * interrupt enable/disable must take effect at once - updateInterrupts only runs on
951 * active scanlines and at the trigger line, so without this a border-time R1 mask would
952 * leave /INT stuck asserted.
953 *
954 * Called from pico9918_write_reg_value_impl, which is the one place that knows which
955 * register a write actually landed on. A staged first byte and an address set cannot
956 * reach the predicate, so neither pays for this.
957 */
962
963/**
964 * \brief console-reset entry for the interrupt/status state. frameDoneInt resets to
965 * TRUE, not false: it suppresses the end-of-frame fallback interrupt until the
966 * next frame starts cleanly.
967 *
968 * Does NOT drive the pin - the reset handler's tail order is load-bearing (host
969 * read-ahead push and other non-VDP work run between this and the pin write), so
970 * the caller issues PICO9918_HOST_SET_INT at its own point.
971 */
973{
975 tms9918->frameInt = false;
976 tms9918->frameDoneInt = true;
977}
978
979
980/* Palette LUT (pico9918_palette.c)
981 * -----------------------------------
982 * 256 entries consumed by PICO9918_EXPAND_INDEXED. Regular SRAM, not a scratch
983 * bank - placement preserved from the firmware.
984 *
985 * They are here, on the library-internal impl surface, because that is where
986 * library-internal state belongs and they have in-library consumers: the frame module's
987 * scanline reads the LUT and drives both rebuild triggers, and the golden harness drives
988 * the same rebuild decision directly (test/golden/golden.c, the post-palette scene
989 * surface - which is what makes palDirty observable at all).
990 *
991 * HOST CODE MAY REFERENCE THE LUT FOR EXACTLY ONE THING: pointing an RP2040's
992 * interpolators at it, which PICO9918_EXPAND_INIT does. They are per-core state and the
993 * library does not know which core will expand lines, so the call belongs to whoever
994 * starts that core - pico9918's src/palette.c. A library entry point for it would put a
995 * boot-time call in this RAM-resident TU to save a host two lines, and the scanline path
996 * pays for what lands here. */
997extern PICO9918_PALETTE_LUT_T pico9918_palette_lut[256];
998
999#if !PICO9918_SINGLE_INSTANCE
1000extern const pico9918_t* pico9918_palette_owner;
1001#endif
1002
1003void pico9918_palette_regenerate(PICO9918_INST_ONLY_ARG);
1004
1005#if PICO9918_BUILD_DEBUG_API
1006/* The mode cache is refreshed on entry to a scanline and read before one by
1007 * pico9918_frame.c, so a debug register write landing between the two would leave
1008 * pico9918_display_mode answering with the old geometry for a line. In pico9918.c
1009 * because the cache and the decode are both file-static there. */
1011#endif
1012
1013/* Does the LUT need rebuilding?
1014 *
1015 * SR2 bit 7 is F18A-SPECIFIC (the GPU busy flag): the GPU may have written
1016 * palette registers behind our back, so a rebuild is forced while it runs.
1017 * V9938's S#2 bit 7 is TR, which idles at 1 - under the V9938 base this must
1018 * be gated per base (step 6) or the palette would rebuild every scanline. */
1019PICO9918_INLINE bool pico9918_palette_dirty(PICO9918_INST_ONLY_ARG)
1020{
1021#if !PICO9918_SINGLE_INSTANCE
1022 /* the converted palette is one module-level LUT, so it belongs to whoever rebuilt it
1023 last: a second instance in the same mode would otherwise draw the first one's colours */
1024 if (pico9918_palette_owner != tms9918) return true;
1025#endif
1026 return tms9918->palDirty || (TMS_STATUS(tms9918, PICO9918_SR_GPU) & 0x80);
1027}
#define PICO9918_SR0_5S
more sprites on a line than the limit allows
Definition pico9918.h:276
#define PICO9918_R47_DATA_PORT
register 47 bits: the palette data port
Definition pico9918.h:337
#define TMS_R1_RAM_16K
register 1 bits: VRAM size, blanking, interrupt, mode and sprite size
Definition pico9918.h:297
#define PICO9918_INST_ARG
declare the instance ahead of other parameters
Definition pico9918.h:71
#define PICO9918_INST_ONLY
pass the instance as the only argument
Definition pico9918.h:74
#define PICO9918_R47_INDEX
first palette index to write
Definition pico9918.h:339
#define PICO9918_SCANLINE_BYTES_MAX
the widest active line this build renders, in bytes
Definition pico9918.h:508
#define PICO9918_R15_STATUS_NUM
which status register S1 reads back
Definition pico9918.h:374
#define PICO9918_SR0_INT
status register 0 bits.
Definition pico9918.h:275
@ PICO9918_REG_VRAM_INC
signed VRAM address increment per access
Definition pico9918.h:235
@ PICO9918_REG_STATUS_SELECT
which status register S1 reads back, and the counter controls
Definition pico9918.h:219
@ PICO9918_REG_PALETTE_CONTROL
palette data port mode, auto-increment and index
Definition pico9918.h:234
@ PICO9918_SR_STATUS
the TMS9918A status: interrupt, 5th sprite, collision, sprite number
Definition pico9918.h:256
@ PICO9918_SR_IDENT
chip identity, blanking, and the scanline interrupt flag
Definition pico9918.h:257
@ PICO9918_SR_GPU
GPU running and its status byte.
Definition pico9918.h:258
#define PICO9918_R47_AUTO_INC
step the palette index after each entry
Definition pico9918.h:338
#define PICO9918_INST_ONLY_ARG
declare the instance as the only parameter
Definition pico9918.h:72
#define TMS9918_PIXELS_X
active display width, every mode
Definition pico9918.h:376
#define TMS_R0_DOUBLE_ROWS
PICO9918 only: twice the rows, drawn interlaced.
Definition pico9918.h:293
#define TMS_R0_INT_SCANLINE
assert /INT when the raster reaches the line in R19.
Definition pico9918.h:294
#define PICO9918_SR1_HF
status register 1 bits.
Definition pico9918.h:281
#define TMS_R1_INT_ENABLE
assert /INT at end of frame
Definition pico9918.h:301
#define PICO9918_SR0_COLLISION
two sprites overlapped on an opaque pixel
Definition pico9918.h:277
#define PICO9918_INST
pass the instance ahead of other arguments
Definition pico9918.h:73
#define PICO9918_DLLEXPORT
the linkage every public entry point carries - see LINKAGE MODES above
Definition pico9918.h:41
pico9918_mode_t
the display modes the VDP can be in, TMS9918A modes and F18A alike
Definition pico9918.h:104
#define PICO9918_BASE_V9938
the V9938 base
PICO9918_INLINE_HOT uint32_t pico9918_cpu_vram_addr_impl(pico9918_t *tms9918, uint32_t addr)
where a CPU-side VRAM access lands
PICO9918_INLINE void pico9918_frame_sync_int_impl(pico9918_t *tms9918)
recompute the interrupt state and, only if it changed, drive the pin.
PICO9918_INLINE_HOT void pico9918_write_addr_impl(pico9918_t *tms9918, uint8_t data)
write an address (mode = 1) to the tms9918
PICO9918_INLINE uint8_t pico9918_read_status_impl(pico9918_t *tms9918)
read from the status register
PICO9918_INLINE void pico9918_write_reconcile_int_impl(pico9918_t *tms9918)
bring /INT into agreement after a write that can change the predicate.
PICO9918_INLINE_HOT void pico9918_set_status_impl(pico9918_t *tms9918, uint8_t status)
set status flag
PICO9918_INLINE void pico9918_frame_reset_int_impl(pico9918_t *tms9918)
console-reset entry for the interrupt/status state.
PICO9918_INLINE_HOT void pico9918_interrupt_set_impl(pico9918_t *tms9918)
raise the interrupt flag in SR0 and the frame shadow
void pico9918_write_reg_value_impl(pico9918_t *tms9918, uint8_t regSelect, uint8_t value)
set a register from the second byte of a host register write
Definition pico9918.c:3342
PICO9918_INLINE_HOT uint8_t pico9918_peek_status_impl(pico9918_t *tms9918)
read from the status register without resetting it
void pico9918_debug_sync_mode_impl(pico9918_t *tms9918)
see impl/pico9918_priv.h.
Definition pico9918.c:3506
PICO9918_INLINE bool pico9918_status_select_active(pico9918_t *tms9918)
is R#15's status-register select live?
PICO9918_INLINE_HOT uint8_t pico9918_read_ahead_data_impl(pico9918_t *tms9918)
refill the read-ahead buffer from the current address and return the new value
PICO9918_INLINE_HOT void pico9918_status_read_reconcile_impl(pico9918_t *tms9918, uint8_t readReg, uint8_t readVal)
CPU-interface entry: the host's read-ahead already handed the CPU a value, so this only applies the r...
PICO9918_INLINE_HOT uint8_t pico9918_read_data_no_inc_impl(pico9918_t *tms9918)
return the buffered value without reading VRAM or advancing the address
PICO9918_INLINE_HOT bool pico9918_interrupt_status_impl(pico9918_t *tms9918)
THE single implementation of "a status register was just read" - shared by the public read (pico9918_...
PICO9918_INLINE_HOT uint8_t pico9918_read_data_impl(pico9918_t *tms9918)
read data (mode = 0) from the tms9918
PICO9918_INLINE_HOT void pico9918_write_data_impl(pico9918_t *tms9918, uint8_t data)
write data (mode = 0) to the tms9918
pico9918-core - Platform Abstraction