47#define COLS TMS9918_PIXELS_X
48#define CELLS_X (COLS / 8)
49#define CELLS_Y (ROWS / 8)
50#define CELLS (CELLS_X * CELLS_Y)
56#define SPRITE_PATT 0x0800
57#define SPRITE_ATTR 0x0C00
63#define VRAM_SIZE 0x4000
67#define NUM_TILES (TILE_STRIPE + 1)
74static void writeVram(
PICO9918_INST_ARG uint16_t addr,
const uint8_t* data,
size_t len)
85static void buildTiles(uint8_t* plane1, uint8_t* plane2,
size_t len)
87 memset(plane1, 0, len);
88 memset(plane2, 0, len);
89 for (
int y = 0; y < 8; ++y)
91 for (
int x = 0; x < 8; ++x)
93 const int edge = (x == 0 || x == 7 || y == 0 || y == 7);
94 const int highlight = (x >= 1 && x <= 2 && y >= 1 && y <= 2);
95 const int field = edge ? 1 : highlight ? 2 : 3;
96 if (field & 1) plane1[TILE_FIELD * 8 + y] |= 0x80 >> x;
97 if (field & 2) plane2[TILE_FIELD * 8 + y] |= 0x80 >> x;
101 plane1[TILE_STRIPE * 8 + y] |= 0x80 >> x;
102 plane2[TILE_STRIPE * 8 + y] |= 0x80 >> x;
110 static uint8_t rgb[(size_t)COLS * ROWS * 3];
111 for (uint16_t y = 0; y < ROWS; ++y)
116 uint8_t* px = rgb + (size_t)y * COLS * 3;
117 for (
int x = 0; x < COLS; ++x)
121 *px++ = (uint8_t)(((argb >> 8) & 0xF) * 17);
122 *px++ = (uint8_t)(((argb >> 4) & 0xF) * 17);
123 *px++ = (uint8_t)((argb & 0xF) * 17);
127 FILE* f = fopen(path,
"wb");
133 fprintf(f,
"P6\n%d %d\n255\n", COLS, ROWS);
134 fwrite(rgb, 3, (
size_t)COLS * ROWS, f);
136 printf(
"wrote %s\n", path);
139int main(
int argc,
char** argv)
141 const char* lockedOut = argc > 1 ? argv[1] :
"locked.ppm";
142 const char* f18aOut = argc > 2 ? argv[2] :
"f18a.ppm";
144 static uint8_t zeros[VRAM_SIZE];
145 uint8_t plane1[NUM_TILES * 8], plane2[NUM_TILES * 8];
146 uint8_t cells[CELLS];
148#if PICO9918_SINGLE_INSTANCE
152 if (!tms9918)
return 1;
159 buildTiles(plane1, plane2,
sizeof plane1);
163 memset(cells, TILE_FIELD,
sizeof cells);
171 writeVram(
PICO9918_INST SPRITE_ATTR, (
const uint8_t[]){0xD0}, 1);
196 for (
int row = 0; row < CELLS_Y; ++row)
198 for (
int col = 0; col < CELLS_X; ++col)
200 const int ring = (abs(col - CELLS_X / 2) + abs(row - CELLS_Y / 2)) / 2;
201 cells[row * CELLS_X + col] = (uint8_t)(1 + ring % 3);
208 for (
int row = 0; row < CELLS_Y; ++row)
209 for (
int col = 0; col < CELLS_X; ++col)
210 cells[row * CELLS_X + col] = (uint8_t)((((col - row) & 0x1F) < 4) ? TILE_STRIPE : 0);
215 memset(cells, 0x10,
sizeof cells);
232 printf(
"unlocked: R49 reads back 0x%02X\n",
237#if !PICO9918_SINGLE_INSTANCE
void pico9918_destroy(pico9918_t *tms9918)
destroy a TMS9918
const uint8_t * pico9918_line_source(pico9918_t *tms9918)
where the scanline just generated actually is.
uint8_t pico9918_scan_line(pico9918_t *tms9918, uint16_t y)
generate a scanline
void pico9918_reset(pico9918_t *tms9918)
reset the new TMS9918
uint16_t pico9918_default_palette(int index)
a default palette entry, 0xargb
uint8_t pico9918_reg_value(pico9918_t *tms9918, pico9918_register_t reg)
return a register value - see the header for the locked-device aliasing
pico9918-core - core interface
pico9918_t * pico9918_new(void)
create a new TMS9918
#define PICO9918_R49_TILE2_ENABLE
register 49 bits: tile layer 2, row count, and the enhanced colour modes
#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 TMS_R1_DISP_ACTIVE
render the active display
pico9918_register_t
the eight TMS9918 registers, by number and by what each one holds
@ PICO9918_REG_COLOR_TABLE2
tile layer 2 colour table base
@ PICO9918_REG_NAME_TABLE2
tile layer 2 name table base
@ PICO9918_REG_ENHANCED1
tile layer 2, 30-row mode, ECM levels, real Y
@ PICO9918_REG_T1_VSCROLL
tile layer 1 vertical scroll
@ PICO9918_REG_ENHANCED2
GPU triggers, per-position attributes, layer priority.
@ PICO9918_REG_T2_HSCROLL
tile layer 2 horizontal scroll
@ PICO9918_REG_T2_VSCROLL
tile layer 2 vertical scroll
@ PICO9918_REG_PAGE_SIZE
scroll page sizes, and the ECM pattern plane stride
@ PICO9918_REG_UNLOCK
0x1c twice unlocks the F18A personality; any other value locks
@ PICO9918_REG_T1_HSCROLL
tile layer 1 horizontal scroll
#define PICO9918_R49_ECM_TILE_2
tiles take two bitplanes, four colours
#define PICO9918_R57_UNLOCK
the value register 57 takes, twice in a row, to unlock
#define PICO9918_R50_POS_ATTR
tile attributes come per position, not per tile
#define PICO9918_INST
pass the instance ahead of other arguments
pico9918-core - utility / helper functions
static void pico9918_set_address_write(pico9918_t *tms9918, uint16_t addr)
point the VRAM address register at addr for writing, ie.
static void pico9918_set_fg_bg_color(pico9918_t *tms9918, pico9918_color_t fg, pico9918_color_t bg)
set register 7, the text-mode foreground colour and the backdrop
static void pico9918_set_color_table_addr(pico9918_t *tms9918, uint16_t addr)
set the colour table address, which register 3 holds in 64-byte units
static void pico9918_set_sprite_patt_table_addr(pico9918_t *tms9918, uint16_t addr)
set the sprite pattern table address, which register 6 holds in 2KB units
static void pico9918_set_name_table_addr(pico9918_t *tms9918, uint16_t addr)
set the name table address, which register 2 holds in 1KB units
static void pico9918_write_bytes(pico9918_t *tms9918, const uint8_t *bytes, size_t numBytes)
write a block of bytes to VRAM from the current address
static void pico9918_set_sprite_attr_table_addr(pico9918_t *tms9918, uint16_t addr)
set the sprite attribute table address, which register 5 holds in 128-byte units
static void pico9918_write_register_value(pico9918_t *tms9918, pico9918_register_t reg, uint8_t value)
write a VDP register as a host would, value byte first
static uint8_t pico9918_fg_bg_color(pico9918_color_t fg, pico9918_color_t bg)
pack fg into the high nibble and bg into the low nibble of one colour byte
static void pico9918_set_pattern_table_addr(pico9918_t *tms9918, uint16_t addr)
set the pattern table address, which register 4 holds in 2KB units