Theoretically, it works. I can access an emulated SD card on it. Will it work on real hardware? I've also made SMS emulation faster. It was unbearably slow for SDC access.master
@@ -1,7 +1,7 @@ | |||
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_OBJS = $(OBJS) 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 | |||
@@ -11,6 +11,7 @@ | |||
#include "sms_vdp.h" | |||
#include "sms_ports.h" | |||
#include "sms_pad.h" | |||
#include "sms_spi.h" | |||
#include "ps2_kbd.h" | |||
#include "sdc.h" | |||
@@ -43,6 +44,7 @@ static Pad pad; | |||
static Kbd kbd; | |||
static bool use_kbd = false; | |||
static SDC sdc; | |||
static SPI spi; | |||
static uint8_t iord_vdp_cmd() | |||
{ | |||
@@ -90,28 +92,12 @@ static void iowr_ports_ctl(uint8_t val) | |||
ports_ctl_wr(&ports, val); | |||
} | |||
// TODO: re-add as controller-based SPI | |||
/* static uint8_t iord_sdc_spi() | |||
static byte iord_spi() | |||
{ | |||
return sdc_spi_rd(&sdc); | |||
return spi_rd(&spi); | |||
} | |||
static void iowr_sdc_spi(uint8_t val) | |||
{ | |||
sdc_spi_wr(&sdc, val); | |||
} | |||
// in emulation, exchanges are always instantaneous, so we | |||
// always report as ready. | |||
static uint8_t iord_sdc_ctl() | |||
{ | |||
return 0; | |||
} | |||
static void iowr_sdc_ctl(uint8_t val) | |||
{ | |||
sdc_ctl_wr(&sdc, val); | |||
}*/ | |||
static byte spix_sdc(byte val) { return sdc_spix(&sdc, val); } | |||
void create_window() | |||
{ | |||
@@ -246,15 +232,25 @@ static bool _handle_keypress(xcb_generic_event_t *e) | |||
void event_loop() | |||
{ | |||
while (1) { | |||
if (!emul_step()) { | |||
fprintf(stderr, "CPU halted, quitting\n"); | |||
usleep(1000 * 1000); | |||
break; | |||
for (int i=0; i<100; i++) { | |||
if (!emul_step()) { | |||
fprintf(stderr, "CPU halted, quitting\n"); | |||
usleep(1000 * 1000); | |||
break; | |||
} | |||
spi_pulse(&spi); | |||
} | |||
if (vdp_changed) { | |||
// To avoid overdrawing, we'll let the CPU run a bit to finish its | |||
// drawing operation. | |||
emul_steps(10000); | |||
for (int i=0; i<10000; i++) { | |||
if (!emul_step()) { | |||
fprintf(stderr, "CPU halted, quitting\n"); | |||
usleep(1000 * 1000); | |||
break; | |||
} | |||
spi_pulse(&spi); | |||
} | |||
draw_pixels(); | |||
} | |||
// A low tech way of checking when the window was closed. The proper way | |||
@@ -293,6 +289,12 @@ static void usage() | |||
fprintf(stderr, "Usage: ./sms [-k] [-c sdcard.img] /path/to/rom\n"); | |||
} | |||
static byte spi_dbg(byte val) | |||
{ | |||
fprintf(stderr, "SPI XCH: %x\n", val); | |||
return val+1; | |||
} | |||
int main(int argc, char *argv[]) | |||
{ | |||
if (argc < 2) { | |||
@@ -305,6 +307,7 @@ int main(int argc, char *argv[]) | |||
pad_init(&pad, &ports.THA); | |||
kbd_init(&kbd, &ports.THA); | |||
sdc_init(&sdc); | |||
spi_init(&spi, &ports.THB, &ports.TRB, spix_sdc); | |||
int ch; | |||
while ((ch = getopt(argc, argv, "kc:")) != -1) { | |||
@@ -348,6 +351,7 @@ int main(int argc, char *argv[]) | |||
} else { | |||
ports.portA_rd = iord_pad; | |||
} | |||
ports.portB_rd = iord_spi; | |||
m->iord[VDP_CMD_PORT] = iord_vdp_cmd; | |||
m->iord[VDP_DATA_PORT] = iord_vdp_data; | |||
@@ -357,12 +361,6 @@ 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; | |||
@@ -0,0 +1,39 @@ | |||
#include "sms_spi.h" | |||
void spi_init(SPI *spi, Tristate *TH, Tristate *TR, EXCH spixfn) | |||
{ | |||
spi->TH = TH; | |||
spi->TR = TR; | |||
spi->lastTH = *TH; | |||
spi->bitcnt = 0; | |||
spi->bit = false; | |||
spi->rx = 0; | |||
spi->tx = 0xff; | |||
spi->spixfn = spixfn; | |||
} | |||
byte spi_rd(SPI *spi) | |||
{ | |||
return (byte)spi->bit; // return in bit 0 | |||
} | |||
void spi_pulse(SPI *spi) | |||
{ | |||
if (*spi->TH != spi->lastTH) { | |||
spi->lastTH = *spi->TH; | |||
if (spi->lastTH == TRI_HIGH) { | |||
spi->rx <<= 1; | |||
if (*spi->TR == TRI_HIGH) { | |||
spi->rx++; | |||
} | |||
spi->bit = spi->tx >> 7; | |||
spi->tx <<= 1; | |||
spi->bitcnt++; | |||
if (spi->bitcnt == 8) { | |||
spi->tx = spi->spixfn(spi->rx); | |||
spi->bitcnt = 0; | |||
} | |||
} | |||
} | |||
} |
@@ -0,0 +1,30 @@ | |||
#include "emul.h" | |||
/* This emulates a SPI device being connected directly on a controller port. | |||
TH and TR control CLK and DI, input bit 0 (BTN_UP) is DO. | |||
This device has to "pulse" frequently so that it checks whether there's | |||
been a change in CLK. | |||
We have a bit of a challenge here with regards to an important limitation | |||
of the SMS: only 2 pins can be set as output. Without the ability to | |||
programatically select the SPI slave, we have to keep it selected at all | |||
times. This makes us vulnerable to going out of sync. This is a known | |||
limitation, the user will have to re-insert the SD card when that happens. | |||
*/ | |||
typedef struct { | |||
Tristate *TH; // CLK | |||
Tristate *TR; // DI | |||
Tristate lastTH; // value of TH on last pulse | |||
byte bitcnt; // current xchange bit count | |||
bool bit; // bit to return in spi_rd() | |||
byte rx; | |||
byte tx; | |||
EXCH spixfn; | |||
} SPI; | |||
void spi_init(SPI *spi, Tristate *TH, Tristate *TR, EXCH spixfn); | |||
byte spi_rd(SPI *spi); // for sms_ports' portX_rd | |||
void spi_pulse(SPI *spi); |
@@ -0,0 +1,12 @@ | |||
: (spie) DROP ; ( always enabled ) | |||
: (spix) ( x -- x, for port B ) | |||
0 SWAP ( rx tx ) 8 0 DO | |||
( send bit 7 to bit 6, TR's output bit ) | |||
DUP 1 RSHIFT 0x40 AND ( rx tx bits ) 0x80 OR ( CLK hi ) | |||
0x3f PC@ OR 0xf3 AND ( TH and TR output ) | |||
DUP 0x3f PC! ( rx tx bits ) | |||
( CLK low ) 0x7f AND 0x3f PC! ( rx tx ) | |||
( shift tx ) 1 LSHIFT ( rx tx<< ) | |||
( read into rx ) SWAP 1 LSHIFT 0xdc PC@ ( tx<< rx<< x ) | |||
( out bit is the 6th ) 6 RSHIFT 1 AND OR | |||
SWAP LOOP ( rx tx ) DROP ; |