70#include "pico/stdlib.h"
73#include "../pico9918.h"
74#include "../pico9918_config.h"
77#define GRAPHICS_NUM_COLS 32
78#define GRAPHICS_NUM_ROWS 24
79#define GRAPHICS_CHAR_WIDTH 8
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
93#ifndef PICO9918_TEXT80_8BPP
94#define PICO9918_TEXT80_8BPP 0
99#if PICO9918_TEXT80_8BPP
100#define SCANLINE_BYTES_MAX (TMS9918_PIXELS_X * 2)
103#define TEXT80_PADDING_PX (TEXT_PADDING_PX * 2)
105#define SCANLINE_BYTES_MAX TMS9918_PIXELS_X
106#define TEXT80_PADDING_PX TEXT_PADDING_PX
114 "PICO9918_TEXT80_8BPP disagrees with the archive's PICO9918_BUILD_TEXT80_8BPP");
117#define SCANLINE_BUFFER_BYTES (SCANLINE_BYTES_MAX + 8)
118#define SCANLINE_MASK_WORDS ((SCANLINE_BUFFER_BYTES + 31) / 32)
120#define PATTERN_BYTES 8
121#define GFXI_COLOR_GROUP_SIZE 8
123#define MAX_SPRITES 32
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
133#define BASE_VRAM_SIZE (1 << 14)
134#define VRAM_SIZE (1 << 16)
138#define TMS_REGISTERS 64
139#define TMS_STATUS_REGISTERS 16
141#define VRAM_MASK (BASE_VRAM_SIZE - 1)
145#define PICO9918_CPU_VRAM_MASK(tms) VRAM_MASK
149#if PICO9918_BUILD_RUNTIME_CHIP
150#define PICO9918_VRAM_4K_CHIP(T) PICO9918_HAS(T, PICO9918_FEAT_VRAM_4K)
152#define PICO9918_VRAM_4K_CHIP(T) false
158 uint8_t base[BASE_VRAM_SIZE];
160 uint8_t gram1[0x1000];
161 uint16_t pram[0x0800];
164 uint8_t registers[TMS_REGISTERS];
166 uint8_t gram2[0x1000 - TMS_REGISTERS];
169 uint8_t gram3[0x4000 - 2];
172 uint8_t status[TMS_STATUS_REGISTERS];
174 uint8_t gram4[0x5000 - TMS_STATUS_REGISTERS];
182#if defined(PICO_BUILD) && !defined(PICO9918_GPU_C_CORE)
183#define PICO9918_GPU_BUDGETED 0
185#define PICO9918_GPU_BUDGETED 1
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)
202#define PICO9918_FEAT_UNLOCK 0x01
203#define PICO9918_FEAT_CONFIG 0x02
204#define PICO9918_FEAT_OVERLAY 0x04
205#define PICO9918_FEAT_BITMAP 0x08
206#define PICO9918_FEAT_VRAM_4K 0x10
207#define PICO9918_FEAT_WIDE_T80 0x20
208#define PICO9918_FEAT_GPU_RAM 0x40
210#if PICO9918_BUILD_RUNTIME_CHIP
211#define PICO9918_HAS(T, F) (((T)->features & (F)) != 0)
220#define PICO9918_SR1_ID(T) (((T)->chip >= PICO9918_CHIP_PICO9918) ? 0xE8 : 0xE0)
222#define PICO9918_HAS(T, F) true
223#define PICO9918_SR1_ID(T) 0xE8
226#define TMS_REGISTER(T, R) (T->vram.map.registers[R])
227#define TMS_STATUS(T, R) (T->vram.map.status[R])
231#define PICO9918_GM2(T) PICO9918_HAS(T, PICO9918_FEAT_BITMAP)
235#define PICO9918_WIDE_T80(T) PICO9918_HAS(T, PICO9918_FEAT_WIDE_T80)
239#define PICO9918_GPU_FLAT_MEM(T) PICO9918_HAS(T, PICO9918_FEAT_GPU_RAM)
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))
256 uint8_t bytes[VRAM_SIZE];
261 uint32_t currentAddress;
273 uint8_t regWriteStage;
276 uint8_t regWriteStage0Value;
279 uint8_t readAheadBuffer;
285 volatile uint8_t restart;
286 volatile uint8_t flash;
288#if PICO9918_GPU_BUDGETED
295 uint8_t palWriteStage;
296 uint8_t palWriteStage0Value;
302#if PICO9918_BUILD_RUNTIME_CHIP
310 bool scanlineHasSprites;
314 uint32_t currentTime;
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];
337 uint32_t __aligned(4) finalMask[SCANLINE_MASK_WORDS];
346#if !PICO9918_SINGLE_INSTANCE
352 pico9918_config_applied_fn fn;
358 pico9918_config_reload_fn fn;
364 pico9918_gpu_flash_fn fn;
370 pico9918_gpu_config_save_fn fn;
381_Static_assert(offsetof(
struct pico9918_s, vram) == 0,
382 "vram offset moved - the GPU MPU guard ranges could cross a page boundary");
385#if PICO9918_SINGLE_INSTANCE
386extern pico9918_t*
const tms9918;
400extern const uint8_t* pico9918_cached_line_source;
407#if PICO9918_TEXT80_8BPP
408#define TEXT80_WIDE_ROW \
409 (pico9918_cached_mode == TMS_MODE_TEXT80 && PICO9918_UNLOCKED(tms9918) && PICO9918_WIDE_T80(tms9918))
411#define TEXT80_WIDE_ROW false
417 return pico9918_cached_mode;
429 return pico9918_cached_line_source;
446 addr &= PICO9918_CPU_VRAM_MASK(tms9918);
448 if (PICO9918_VRAM_4K_CHIP(tms9918) && !(TMS_REGISTER(tms9918, TMS_REG_1) &
TMS_R1_RAM_16K))
451 addr = (addr & 0x203f) | ((addr & 0x0fc0) << 1) | ((addr & 0x1000) >> 6);
477 if (tms9918->regWriteStage == 0)
481 tms9918->regWriteStage0Value = data;
482 tms9918->regWriteStage = 1;
490 if ((data & 0x40) == 0)
497 tms9918->currentAddress = tms9918->regWriteStage0Value | ((data & 0x3f) << 8);
498 if ((data & 0x40) == 0)
500 tms9918->readAheadBuffer =
505 tms9918->regWriteStage = 0;
544PICO9918_INLINE_HOT
void pico9918_status_read_core(
PICO9918_INST_ARG uint8_t readReg, uint8_t readVal)
549 tms9918->frameStatus &= ~readVal;
551 tms9918->frameStatus |= 0x1f;
555 tms9918->frameInt =
false;
556 PICO9918_HOST_SET_INT(
false);
566 if (stillInt != tms9918->frameInt)
568 tms9918->frameInt = stillInt;
569 PICO9918_HOST_SET_INT(stillInt);
582 tms9918->regWriteStage = 0;
617 tms9918->regWriteStage = 0;
619 tms9918->palWriteStage = 0;
624 const uint8_t readVal = TMS_STATUS(tms9918, readReg);
647 if (tms9918->palWriteStage == 0)
649 tms9918->palWriteStage0Value = data & 0x0f;
650 ++tms9918->palWriteStage;
654 tms9918->palWriteStage = 0;
658 (tms9918->palWriteStage0Value) | (data << 8);
659 tms9918->palDirty = 1;
674 tms9918->regWriteStage = 0;
675 tms9918->readAheadBuffer = data;
685 tms9918->regWriteStage = 0;
686 uint8_t currentValue = tms9918->readAheadBuffer;
687 tms9918->readAheadBuffer =
696 tms9918->regWriteStage = 0;
697 tms9918->readAheadBuffer =
700 return tms9918->readAheadBuffer;
706 return tms9918->readAheadBuffer;
743 tms9918->frameStatus = status;
757 return tms9918->frameStatus;
763 return tms9918->frameInt;
769 return tms9918->frameDoneInt;
772PICO9918_INLINE
void pico9918_set_frame_done_int_impl(
PICO9918_INST_ARG bool done)
774 tms9918->frameDoneInt = done;
797extern int pico9918_frame_count;
798extern int pico9918_dropped_frames_count;
800#if PICO9918_DIAG_GPU_FRAME_COUNTER
804extern uint32_t pico9918_gpu_frame_count;
813 return pico9918_frame_count;
823 pico9918_frame_count = 0;
824#if PICO9918_DIAG_GPU_FRAME_COUNTER
825 pico9918_gpu_frame_count = 0;
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;
856extern uint32_t pico9918_border_bg;
866 return pico9918_v_pixels;
871 return pico9918_v_border;
880 return pico9918_valid_writes;
892 return pico9918_dropped_frames_count;
923PICO9918_INLINE uint16_t pico9918_frame_map_line_impl(
PICO9918_INST_ARG uint16_t y, uint8_t field,
924 bool interlaced, uint8_t fieldOrder)
928 tmsY = y * 2 + (field ^ fieldOrder);
941 if (newInt != tms9918->frameInt)
943 tms9918->frameInt = newInt;
944 PICO9918_HOST_SET_INT(newInt);
975 tms9918->frameInt =
false;
976 tms9918->frameDoneInt =
true;
997extern PICO9918_PALETTE_LUT_T pico9918_palette_lut[256];
999#if !PICO9918_SINGLE_INSTANCE
1000extern const pico9918_t* pico9918_palette_owner;
1005#if PICO9918_BUILD_DEBUG_API
1021#if !PICO9918_SINGLE_INSTANCE
1024 if (pico9918_palette_owner != tms9918)
return true;
1026 return tms9918->palDirty || (TMS_STATUS(tms9918,
PICO9918_SR_GPU) & 0x80);
#define PICO9918_SR0_5S
more sprites on a line than the limit allows
#define PICO9918_R47_DATA_PORT
register 47 bits: the palette data port
#define TMS_R1_RAM_16K
register 1 bits: VRAM size, blanking, interrupt, mode and sprite size
#define PICO9918_INST_ARG
declare the instance ahead of other parameters
#define PICO9918_INST_ONLY
pass the instance as the only argument
#define PICO9918_R47_INDEX
first palette index to write
#define PICO9918_SCANLINE_BYTES_MAX
the widest active line this build renders, in bytes
#define PICO9918_R15_STATUS_NUM
which status register S1 reads back
#define PICO9918_SR0_INT
status register 0 bits.
@ PICO9918_REG_VRAM_INC
signed VRAM address increment per access
@ PICO9918_REG_STATUS_SELECT
which status register S1 reads back, and the counter controls
@ PICO9918_REG_PALETTE_CONTROL
palette data port mode, auto-increment and index
@ PICO9918_SR_STATUS
the TMS9918A status: interrupt, 5th sprite, collision, sprite number
@ PICO9918_SR_IDENT
chip identity, blanking, and the scanline interrupt flag
@ PICO9918_SR_GPU
GPU running and its status byte.
#define PICO9918_R47_AUTO_INC
step the palette index after each entry
#define PICO9918_INST_ONLY_ARG
declare the instance as the only parameter
#define TMS9918_PIXELS_X
active display width, every mode
#define TMS_R0_DOUBLE_ROWS
PICO9918 only: twice the rows, drawn interlaced.
#define TMS_R0_INT_SCANLINE
assert /INT when the raster reaches the line in R19.
#define PICO9918_SR1_HF
status register 1 bits.
#define TMS_R1_INT_ENABLE
assert /INT at end of frame
#define PICO9918_SR0_COLLISION
two sprites overlapped on an opaque pixel
#define PICO9918_INST
pass the instance ahead of other arguments
#define PICO9918_DLLEXPORT
the linkage every public entry point carries - see LINKAGE MODES above
pico9918_mode_t
the display modes the VDP can be in, TMS9918A modes and F18A alike
#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
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.
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