emul/z80/sms: add SPI exchange through controller port

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.
This commit is contained in:
Virgil Dupras 2020-10-29 19:40:13 -04:00
父節點 97a46a7b9b
當前提交 ce10320030
共有 5 個檔案被更改,包括 110 行新增31 行删除

查看文件

@ -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;

39
emul/z80/sms_spi.c Normal file
查看文件

@ -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;
}
}
}
}

30
emul/z80/sms_spi.h Normal file
查看文件

@ -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);

12
recipes/sms/blk/622 Normal file
查看文件

@ -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 ;