I'm planning on supporting Text Mode soon, and SMS' VDP, when mode 4 is not active, behaves mostly like a regular TMS9918. By having this behavior in a separate unit, we'll be able to use it in other systems.master
@@ -1,7 +1,8 @@ | |||
TARGETS = forth rc2014 sms ti84 | |||
OBJS = emul.o z80.o | |||
RC2014_OBJS = $(OBJS) sio.o acia.o sdc.o rc2014_spi.o | |||
SMS_OBJS = $(OBJS) sms_vdp.o sms_ports.o sms_pad.o ps2_kbd.o sdc.o sms_spi.o | |||
SMS_OBJS = $(OBJS) tms9918.o sms_vdp.o sms_ports.o sms_pad.o ps2_kbd.o sdc.o \ | |||
sms_spi.o | |||
TI84_OBJS = $(OBJS) t6a04.o ti84_kbd.o | |||
CDIR = ../../cvm | |||
STAGE = $(CDIR)/stage | |||
@@ -48,7 +48,7 @@ static SPI spi; | |||
static uint8_t iord_vdp_cmd() | |||
{ | |||
return vdp_cmd_rd(&vdp); | |||
return tms_cmd_rd(&vdp.tms); | |||
} | |||
static uint8_t iord_vdp_data() | |||
@@ -3,55 +3,47 @@ | |||
void vdp_init(VDP *vdp) | |||
{ | |||
memset(vdp->vram, 0, VDP_VRAM_SIZE); | |||
memset(vdp->regs, 0, 0x10); | |||
vdp->has_cmdlsb = false; | |||
vdp->curaddr = 0; | |||
} | |||
uint8_t vdp_cmd_rd(VDP *vdp) | |||
{ | |||
return 0; | |||
tms_init(&vdp->tms); | |||
memset(vdp->cram, 0, VDP_CRAM_SIZE); | |||
} | |||
void vdp_cmd_wr(VDP *vdp, uint8_t val) | |||
{ | |||
if (!vdp->has_cmdlsb) { | |||
vdp->cmdlsb = val; | |||
vdp->has_cmdlsb = true; | |||
return; | |||
} | |||
vdp->has_cmdlsb = false; | |||
if ((val & 0xc0) == 0x80) { | |||
// set register | |||
vdp->regs[val&0xf] = vdp->cmdlsb; | |||
} else if ((val & 0xc0) == 0xc0) { | |||
// palette RAM | |||
vdp->curaddr = 0x4000 + (vdp->cmdlsb&0x1f); | |||
if (!vdp->tms.has_cmdlsb) { | |||
tms_cmd_wr(&vdp->tms, val); | |||
} else { | |||
// VRAM | |||
vdp->curaddr = ((val&0x3f) << 8) + vdp->cmdlsb; | |||
if ((val & 0xc0) == 0xc0) { | |||
// palette RAM | |||
// curaddr > VRAM == addr in CRAM | |||
vdp->tms.curaddr = TMS_VRAM_SIZE + (vdp->tms.cmdlsb&0x1f); | |||
} else { | |||
tms_cmd_wr(&vdp->tms, val); | |||
} | |||
} | |||
} | |||
uint8_t vdp_data_rd(VDP *vdp) | |||
{ | |||
uint8_t res = vdp->vram[vdp->curaddr]; | |||
if (vdp->curaddr < VDP_VRAM_SIZE) { | |||
vdp->curaddr++; | |||
TMS9918 *tms = &vdp->tms; | |||
if (tms->curaddr < TMS_VRAM_SIZE) { | |||
return tms_data_rd(&vdp->tms); | |||
} else if (tms->curaddr - TMS_VRAM_SIZE < VDP_CRAM_SIZE) { | |||
return vdp->cram[tms->curaddr++-TMS_VRAM_SIZE]; | |||
} else { | |||
return 0; | |||
} | |||
return res; | |||
} | |||
void vdp_data_wr(VDP *vdp, uint8_t val) | |||
{ | |||
vdp->vram[vdp->curaddr] = val; | |||
if (vdp->curaddr < VDP_VRAM_SIZE) { | |||
vdp->curaddr++; | |||
TMS9918 *tms = &vdp->tms; | |||
if (tms->curaddr < TMS_VRAM_SIZE) { | |||
tms_data_wr(&vdp->tms, val); | |||
} else if (tms->curaddr - TMS_VRAM_SIZE < VDP_CRAM_SIZE) { | |||
vdp->cram[tms->curaddr++-TMS_VRAM_SIZE] = val; | |||
} | |||
} | |||
// Returns a 8-bit RGB value (0b00bbggrr) | |||
uint8_t vdp_pixel(VDP *vdp, uint16_t x, uint16_t y) | |||
{ | |||
if (x >= VDP_SCREENW) { | |||
@@ -60,10 +52,11 @@ uint8_t vdp_pixel(VDP *vdp, uint16_t x, uint16_t y) | |||
if (y >= VDP_SCREENH) { | |||
return 0; | |||
} | |||
TMS9918 *tms = &vdp->tms; | |||
// name table offset | |||
uint16_t offset = (vdp->regs[2] & 0xe) << 10; | |||
uint16_t offset = (tms->regs[2] & 0xe) << 10; | |||
offset += ((y/8) << 6) + ((x/8) << 1); | |||
uint16_t tableval = vdp->vram[offset] + (vdp->vram[offset+1] << 8); | |||
uint16_t tableval = tms->vram[offset] + (tms->vram[offset+1] << 8); | |||
uint16_t tilenum = tableval & 0x1ff; | |||
// is palette select bit on? if yes, use sprite palette instead | |||
uint8_t palettemod = tableval & 0x800 ? 0x10 : 0; | |||
@@ -74,10 +67,10 @@ uint8_t vdp_pixel(VDP *vdp, uint16_t x, uint16_t y) | |||
uint8_t bitnum = 7 - (x%8); | |||
// Now, let's compose the result by pushing the right bit of our 4 bytes | |||
// into our result. | |||
uint8_t palette_id = ((vdp->vram[offset] >> bitnum) & 1) + \ | |||
(((vdp->vram[offset+1] >> bitnum) & 1) << 1) + \ | |||
(((vdp->vram[offset+2] >> bitnum) & 1) << 2) + \ | |||
(((vdp->vram[offset+3] >> bitnum) & 1) << 3); | |||
uint8_t rgb = vdp->vram[0x4000+palettemod+palette_id]; | |||
uint8_t palette_id = ((tms->vram[offset] >> bitnum) & 1) + \ | |||
(((tms->vram[offset+1] >> bitnum) & 1) << 1) + \ | |||
(((tms->vram[offset+2] >> bitnum) & 1) << 2) + \ | |||
(((tms->vram[offset+3] >> bitnum) & 1) << 3); | |||
uint8_t rgb = vdp->cram[palettemod+palette_id]; | |||
return rgb; | |||
} |
@@ -1,26 +1,16 @@ | |||
#include <stdint.h> | |||
#include <stdbool.h> | |||
#include "tms9918.h" | |||
#define VDP_VRAM_SIZE 0x4020 | |||
#define VDP_CRAM_SIZE 0x20 | |||
#define VDP_SCREENW (32*8) | |||
#define VDP_SCREENH (24*8) | |||
// Offset of the name table | |||
#define VDP_NTABLE_OFFSET 0x3800 | |||
typedef struct { | |||
// the last 0x20 is palette RAM | |||
uint8_t vram[VDP_VRAM_SIZE]; | |||
uint8_t regs[0x10]; | |||
uint8_t cmdlsb; | |||
bool has_cmdlsb; | |||
uint16_t curaddr; | |||
TMS9918 tms; | |||
uint8_t cram[VDP_CRAM_SIZE]; | |||
} VDP; | |||
void vdp_init(VDP *vdp); | |||
uint8_t vdp_cmd_rd(VDP *vdp); | |||
void vdp_cmd_wr(VDP *vdp, uint8_t val); | |||
uint8_t vdp_data_rd(VDP *vdp); | |||
void vdp_data_wr(VDP *vdp, uint8_t val); | |||
// result is a RGB value | |||
uint8_t vdp_pixel(VDP *vdp, uint16_t x, uint16_t y); |
@@ -0,0 +1,54 @@ | |||
#include <string.h> | |||
#include "tms9918.h" | |||
void tms_init(TMS9918 *tms) | |||
{ | |||
memset(tms->vram, 0, TMS_VRAM_SIZE); | |||
memset(tms->regs, 0, 0x10); | |||
tms->has_cmdlsb = false; | |||
tms->curaddr = 0; | |||
} | |||
uint8_t tms_cmd_rd(TMS9918 *tms) | |||
{ | |||
return 0; | |||
} | |||
void tms_cmd_wr(TMS9918 *tms, uint8_t val) | |||
{ | |||
if (!tms->has_cmdlsb) { | |||
tms->cmdlsb = val; | |||
tms->has_cmdlsb = true; | |||
return; | |||
} | |||
tms->has_cmdlsb = false; | |||
if ((val & 0xc0) == 0x80) { | |||
// set register | |||
tms->regs[val&0xf] = tms->cmdlsb; | |||
} else { | |||
// VRAM | |||
tms->curaddr = ((val&0x3f) << 8) + tms->cmdlsb; | |||
} | |||
} | |||
uint8_t tms_data_rd(TMS9918 *tms) | |||
{ | |||
if (tms->curaddr < TMS_VRAM_SIZE) { | |||
return tms->vram[tms->curaddr++]; | |||
} else { | |||
return 0; | |||
} | |||
} | |||
void tms_data_wr(TMS9918 *tms, uint8_t val) | |||
{ | |||
if (tms->curaddr < TMS_VRAM_SIZE) { | |||
tms->vram[tms->curaddr++] = val; | |||
} | |||
} | |||
// Returns a 8-bit RGB value (0b00bbggrr) | |||
uint8_t tms_pixel(TMS9918 *tms, uint16_t x, uint16_t y) | |||
{ | |||
return 0; // no TMS9918 mode implemented yet | |||
} |
@@ -0,0 +1,24 @@ | |||
#pragma once | |||
#include <stdint.h> | |||
#include <stdbool.h> | |||
#define TMS_VRAM_SIZE 0x4000 | |||
// Offset of the name table | |||
#define TMS_NTABLE_OFFSET 0x3800 | |||
typedef struct { | |||
uint8_t vram[TMS_VRAM_SIZE]; | |||
uint8_t regs[0x10]; | |||
uint8_t cmdlsb; | |||
bool has_cmdlsb; | |||
uint16_t curaddr; | |||
} TMS9918; | |||
void tms_init(TMS9918 *tms); | |||
uint8_t tms_cmd_rd(TMS9918 *tms); | |||
void tms_cmd_wr(TMS9918 *tms, uint8_t val); | |||
uint8_t tms_data_rd(TMS9918 *tms); | |||
void tms_data_wr(TMS9918 *tms, uint8_t val); | |||
// result is a RGB value | |||
uint8_t tms_pixel(TMS9918 *tms, uint16_t x, uint16_t y); |