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:
父節點
97a46a7b9b
當前提交
ce10320030
@ -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
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
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
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 ;
|
載入中…
新增問題並參考
Block a user