My idea of plugging a RC2014 bridge directly onto a Sega Master System cartridge doesn't work. The SMS eats all I/O addr space, we can't use it. Therefore, this naive idea, in the emulator, of reusing sdc.c in sms.c as-is, doesn't work either. I'll have to find another way of communicating to a SPI device on the SMS. I'll probably do it through a controller port. Meanwhile, I need to decouple SPI from SDC in the emulator code so that I can reuse sdc.c. This is what is done here.master
@@ -1,6 +1,6 @@ | |||
TARGETS = forth rc2014 sms ti84 | |||
OBJS = emul.o z80.o | |||
RC2014_OBJS = $(OBJS) sio.o acia.o sdc.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 | |||
TI84_OBJS = $(OBJS) t6a04.o ti84_kbd.o | |||
CDIR = ../../cvm | |||
@@ -5,6 +5,7 @@ | |||
typedef byte (*IORD) (); | |||
typedef void (*IOWR) (byte data); | |||
typedef byte (*EXCH) (byte data); | |||
typedef struct { | |||
Z80Context cpu; | |||
@@ -15,6 +15,7 @@ | |||
#include "acia.h" | |||
#include "sio.h" | |||
#include "sdc.h" | |||
#include "rc2014_spi.h" | |||
#define RAMSTART 0x8000 | |||
#define ACIA_CTL_PORT 0x80 | |||
@@ -29,6 +30,7 @@ bool use_sio = false; | |||
static ACIA acia; | |||
static SIO sio; | |||
static SDC sdc; | |||
static SPI spi; | |||
static uint8_t iord_acia_ctl() | |||
{ | |||
@@ -70,26 +72,28 @@ static void iowr_sio_data(uint8_t val) | |||
sio_adata_wr(&sio, val); | |||
} | |||
static uint8_t iord_sdc_spi() | |||
static uint8_t iord_spi() | |||
{ | |||
return sdc_spi_rd(&sdc); | |||
return spi_rd(&spi); | |||
} | |||
static void iowr_sdc_spi(uint8_t val) | |||
static void iowr_spi(uint8_t val) | |||
{ | |||
sdc_spi_wr(&sdc, val); | |||
spi_wr(&spi, val); | |||
} | |||
byte spix_sdc(byte val) { return sdc_spix(&sdc, val); } | |||
// in emulation, exchanges are always instantaneous, so we | |||
// always report as ready. | |||
static uint8_t iord_sdc_ctl() | |||
static uint8_t iord_spi_ctl() | |||
{ | |||
return 0; | |||
} | |||
static void iowr_sdc_ctl(uint8_t val) | |||
static void iowr_spi_ctl(uint8_t val) | |||
{ | |||
sdc_ctl_wr(&sdc, val); | |||
spi_ctl_wr(&spi, val); | |||
} | |||
static bool has_irq() | |||
@@ -134,6 +138,7 @@ int main(int argc, char *argv[]) | |||
acia_init(&acia); | |||
sio_init(&sio); | |||
sdc_init(&sdc); | |||
spi_init(&spi, spix_sdc); | |||
while ((ch = getopt(argc, argv, "sc:")) != -1) { | |||
switch (ch) { | |||
@@ -198,10 +203,10 @@ int main(int argc, char *argv[]) | |||
m->iowr[ACIA_CTL_PORT] = iowr_acia_ctl; | |||
m->iowr[ACIA_DATA_PORT] = iowr_acia_data; | |||
} | |||
m->iord[SDC_SPI] = iord_sdc_spi; | |||
m->iowr[SDC_SPI] = iowr_sdc_spi; | |||
m->iord[SDC_CTL] = iord_sdc_ctl; | |||
m->iowr[SDC_CTL] = iowr_sdc_ctl; | |||
m->iord[SDC_SPI] = iord_spi; | |||
m->iowr[SDC_SPI] = iowr_spi; | |||
m->iord[SDC_CTL] = iord_spi_ctl; | |||
m->iowr[SDC_CTL] = iowr_spi_ctl; | |||
char tosend = 0; | |||
while (emul_step()) { | |||
@@ -0,0 +1,28 @@ | |||
#include "rc2014_spi.h" | |||
void spi_init(SPI *spi, EXCH spixfn) | |||
{ | |||
spi->selected = false; | |||
spi->resp = 0xff; | |||
spi->spixfn = spixfn; | |||
} | |||
// TODO: for now, any nonzero value enables the SPI. To allow | |||
// emulation of systems with multi-devices SPI relay, change | |||
// this. | |||
void spi_ctl_wr(SPI *spi, byte val) | |||
{ | |||
spi->selected = val; | |||
} | |||
void spi_wr(SPI *spi, byte val) | |||
{ | |||
if (spi->selected) { | |||
spi->resp = spi->spixfn(val); | |||
} | |||
} | |||
byte spi_rd(SPI *spi) | |||
{ | |||
return spi->selected ? spi->resp : 0xff; | |||
} |
@@ -0,0 +1,17 @@ | |||
#include "emul.h" | |||
/* Emulates a SPI relay designed for the RC2014, enabled by poking on the CTL | |||
port, then allowing a SPI exchange by writing to, then reading from, the | |||
data port. | |||
*/ | |||
typedef struct { | |||
bool selected; | |||
byte resp; | |||
EXCH spixfn; | |||
} SPI; | |||
void spi_init(SPI *spi, EXCH spixfn); | |||
void spi_ctl_wr(SPI *spi, byte val); | |||
void spi_wr(SPI *spi, byte val); | |||
byte spi_rd(SPI *spi); |
@@ -13,47 +13,34 @@ static uint16_t crc16(uint16_t crc, uint8_t data) | |||
void sdc_init(SDC *sdc) | |||
{ | |||
sdc->selected = false; | |||
sdc->initstat = 0; | |||
sdc->recvidx = 0; | |||
sdc->sendidx = -1; | |||
sdc->resp = 0xff; | |||
sdc->fp = NULL; | |||
sdc->cmd17bytes = -1; | |||
sdc->cmd24bytes = -2; | |||
} | |||
// TODO: for now, any nonzero value enables the SDC. To allow | |||
// emulation of systems with multi-devices SPI relay, change | |||
// this. | |||
void sdc_ctl_wr(SDC *sdc, uint8_t val) | |||
byte sdc_spix(SDC *sdc, byte val) | |||
{ | |||
sdc->selected = val; | |||
} | |||
void sdc_spi_wr(SDC *sdc, uint8_t val) | |||
{ | |||
if (!sdc->selected) { | |||
return; | |||
} | |||
sdc->resp = 0xff; | |||
byte resp = 0xff; | |||
if (sdc->initstat < 8) { | |||
// not woken yet. | |||
sdc->initstat++; | |||
return; | |||
return resp; | |||
} | |||
if (sdc->sendidx >= 0) { | |||
sdc->resp = sdc->sendbuf[sdc->sendidx++]; | |||
resp = sdc->sendbuf[sdc->sendidx++]; | |||
if (sdc->sendidx == 5) { | |||
sdc->sendidx = -1; | |||
} | |||
return; | |||
return resp; | |||
} | |||
if (sdc->cmd17bytes >= 0) { | |||
if (sdc->fp) { | |||
sdc->resp = getc(sdc->fp); | |||
resp = getc(sdc->fp); | |||
} | |||
sdc->crc16 = crc16(sdc->crc16, sdc->resp); | |||
sdc->crc16 = crc16(sdc->crc16, resp); | |||
sdc->cmd17bytes++; | |||
if (sdc->cmd17bytes == 512) { | |||
sdc->sendbuf[3] = sdc->crc16 >> 8; | |||
@@ -61,12 +48,12 @@ void sdc_spi_wr(SDC *sdc, uint8_t val) | |||
sdc->sendidx = 3; | |||
sdc->cmd17bytes = -1; | |||
} | |||
return; | |||
return resp; | |||
} | |||
if (sdc->cmd24bytes == -1) { | |||
if (val == 0xff) { | |||
// it's ok to receive idle bytes before the data token. | |||
return; | |||
return resp; | |||
} | |||
if (val == 0xfe) { | |||
// data token, good | |||
@@ -75,7 +62,7 @@ void sdc_spi_wr(SDC *sdc, uint8_t val) | |||
// something is wrong, cancel cmd24 | |||
sdc->cmd24bytes = -2; | |||
} | |||
return; | |||
return resp; | |||
} | |||
if (sdc->cmd24bytes >= 0) { | |||
if (sdc->cmd24bytes < 512) { | |||
@@ -102,16 +89,16 @@ void sdc_spi_wr(SDC *sdc, uint8_t val) | |||
sdc->cmd24bytes = -3; | |||
} | |||
sdc->cmd24bytes++; | |||
return; | |||
return resp; | |||
} | |||
if ((sdc->recvidx == 0) && ((val > 0x7f) || (val < 0x40))) { | |||
// not a command | |||
return; | |||
return resp; | |||
} | |||
sdc->recvbuf[sdc->recvidx++] = val; | |||
if (sdc->recvidx < 6) { | |||
// incomplete command | |||
return; | |||
return resp; | |||
} | |||
// Command complete | |||
val &= 0x3f; | |||
@@ -128,7 +115,7 @@ void sdc_spi_wr(SDC *sdc, uint8_t val) | |||
sdc->sendbuf[4] = 0x01; | |||
sdc->sendidx = 4; | |||
} | |||
return; | |||
return resp; | |||
} | |||
if (sdc->initstat == 9) { | |||
// At this stage, we're expecting CMD8 with 0x1aa arg2 | |||
@@ -143,7 +130,7 @@ void sdc_spi_wr(SDC *sdc, uint8_t val) | |||
} else { | |||
sdc-> initstat = 8; | |||
} | |||
return; | |||
return resp; | |||
} | |||
if (sdc->initstat == 10) { | |||
// At this stage, we're expecting CMD55 | |||
@@ -154,7 +141,7 @@ void sdc_spi_wr(SDC *sdc, uint8_t val) | |||
} else { | |||
sdc->initstat = 8; | |||
} | |||
return; | |||
return resp; | |||
} | |||
if (sdc->initstat == 11) { | |||
// At this stage, we're expecting CMD41 | |||
@@ -165,7 +152,7 @@ void sdc_spi_wr(SDC *sdc, uint8_t val) | |||
} else { | |||
sdc->initstat = 8; | |||
} | |||
return; | |||
return resp; | |||
} | |||
// We have a fully initialized card. | |||
if (cmd == 17) { | |||
@@ -178,7 +165,7 @@ void sdc_spi_wr(SDC *sdc, uint8_t val) | |||
sdc->sendidx = 3; | |||
sdc->cmd17bytes = 0; | |||
sdc->crc16 = 0; | |||
return; | |||
return resp; | |||
} | |||
if (cmd == 24) { | |||
if (sdc->fp) { | |||
@@ -188,17 +175,10 @@ void sdc_spi_wr(SDC *sdc, uint8_t val) | |||
sdc->sendidx = 4; | |||
sdc->cmd24bytes = -1; | |||
sdc->crc16 = 0; | |||
return; | |||
return resp; | |||
} | |||
// Simulate success for any unknown command. | |||
sdc->sendbuf[4] = 0x00; | |||
sdc->sendidx = 4; | |||
} | |||
uint8_t sdc_spi_rd(SDC *sdc) | |||
{ | |||
if (!sdc->selected) { | |||
return 0xff; | |||
} | |||
return sdc->resp; | |||
return resp; | |||
} |
@@ -1,24 +1,19 @@ | |||
#include <stdint.h> | |||
#include <stdbool.h> | |||
#include "emul.h" | |||
typedef struct { | |||
bool selected; | |||
// Initialization status. 0 == not woken 8 == woken 9 == CMD0 received | |||
// 10 == CMD8 received, 11 == CMD55 received, 12 == CMD41 received (fully | |||
// initialized). | |||
unsigned int initstat; | |||
// We receive commands into this buffer. | |||
uint8_t recvbuf[6]; | |||
byte recvbuf[6]; | |||
// Where the next SPI byte should be stored in recvbuf. | |||
unsigned int recvidx; | |||
// Buffer to the arguments for a response | |||
uint8_t sendbuf[5]; | |||
byte sendbuf[5]; | |||
// Index of the next byte from sendbuf we should return. If -1, buffer is | |||
// empty. | |||
int sendidx; | |||
// One byte response. When all other response buffers are empty, return | |||
// this. | |||
uint8_t resp; | |||
// File used for contents read/write | |||
FILE *fp; | |||
// number of bytes read into the current CMD17. -1 means no CMD17 active. | |||
@@ -31,6 +26,4 @@ typedef struct { | |||
} SDC; | |||
void sdc_init(SDC *sdc); | |||
void sdc_ctl_wr(SDC *sdc, uint8_t val); | |||
void sdc_spi_wr(SDC *sdc, uint8_t val); | |||
uint8_t sdc_spi_rd(SDC *sdc); | |||
byte sdc_spix(SDC *sdc, byte val); |
@@ -90,7 +90,8 @@ static void iowr_ports_ctl(uint8_t val) | |||
ports_ctl_wr(&ports, val); | |||
} | |||
static uint8_t iord_sdc_spi() | |||
// TODO: re-add as controller-based SPI | |||
/* static uint8_t iord_sdc_spi() | |||
{ | |||
return sdc_spi_rd(&sdc); | |||
} | |||
@@ -110,7 +111,7 @@ static uint8_t iord_sdc_ctl() | |||
static void iowr_sdc_ctl(uint8_t val) | |||
{ | |||
sdc_ctl_wr(&sdc, val); | |||
} | |||
}*/ | |||
void create_window() | |||
{ | |||
@@ -356,10 +357,12 @@ int main(int argc, char *argv[]) | |||
m->iowr[VDP_CMD_PORT] = iowr_vdp_cmd; | |||
m->iowr[VDP_DATA_PORT] = iowr_vdp_data; | |||
m->iowr[PORTS_CTL_PORT] = iowr_ports_ctl; | |||
/* TODO: re-add | |||
m->iord[SDC_SPI] = iord_sdc_spi; | |||
m->iowr[SDC_SPI] = iowr_sdc_spi; | |||
m->iord[SDC_CTL] = iord_sdc_ctl; | |||
m->iowr[SDC_CTL] = iowr_sdc_ctl; | |||
*/ | |||
conn = xcb_connect(NULL, NULL); | |||
screen = xcb_setup_roots_iterator(xcb_get_setup(conn)).data; | |||
@@ -1,13 +1,15 @@ | |||
0xff00 CONSTANT RS_ADDR 0xfffa CONSTANT PS_ADDR | |||
RS_ADDR 0x80 - CONSTANT SYSVARS | |||
0x8000 CONSTANT HERESTART | |||
4 CONSTANT SPI_DATA 5 CONSTANT SPI_CTL 1 CONSTANT SDC_DEVID | |||
602 LOAD ( acia decl ) | |||
5 LOAD ( z80 assembler ) | |||
262 LOAD ( xcomp ) 282 LOAD ( boot.z80.decl ) | |||
270 LOAD ( xcomp overrides ) 283 335 LOADR ( boot.z80 ) | |||
353 LOAD ( xcomp core low ) 603 605 LOADR ( acia ) | |||
419 LOAD 423 436 LOADR | |||
390 LOAD ( xcomp core high ) | |||
(entry) _ | |||
( Update LATEST ) | |||
PC ORG @ 8 + ! | |||
," ACIA$ " EOT, | |||
," ACIA$ BLK$ " EOT, |