diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..56b47da --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,35 @@ +--- +name: CI + +# yamllint disable-line rule:truthy +on: + push: + pull_request: + schedule: + - cron: "0 0 22 * *" + +jobs: + build: + name: Build and test + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 0 + - name: Fix Checkout + run: | + git fetch --force --tags + + - name: Run minimal test build + run: | + make all + + - name: Upload artifacts + uses: actions/upload-artifact@v3 + with: + name: z180emu + path: | + p112 + markiv + makedisk diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8404162 --- /dev/null +++ b/.gitignore @@ -0,0 +1,14 @@ + +# Intermediate objects generated during compile +*.o + +# Compiled output +makedisk +markiv +p112 +sc126 + +# Binary ROMs needed for the emulators +markivrom.bin +p112rom.bin +sc126rom.bin diff --git a/Makefile b/Makefile index 5efeade..e39cb23 100644 --- a/Makefile +++ b/Makefile @@ -4,7 +4,20 @@ endif CCOPTS ?= -O3 -DSOCKETCONSOLE -std=gnu89 -all: p112 markiv makedisk +CFLAGS ?= -DSOCKETCONSOLE -g +LDFLAGS ?= $(SOCKLIB) + +all: sc126 p112 markiv makedisk + + +sc126.o: sc126.c sconsole.h z180dbg.h z180/z180.h z180/z80daisy.h z180/z80common.h ds1202_1302/ds1202_1302.h sdcard/sdcard.h +sc126: sc126.o +sc126: z180/z180.o z180/z180dasm.o z180/z80daisy.o z180/z80scc.o z180/z180asci.o +sc126: ds1202_1302/rtc_sc126.o ds1202_1302/ds1202_1302.o +sc126: sdcard/sdcard.o + +ds1202_1302/rtc_sc126.o: ds1202_1302/rtc.c ds1202_1302/rtc.h + $(CC) $(CFLAGS) -Dmachine_name=\"sc126\" -DHAVE_SYS_TIME_H -DHAVE_GETTIMEOFDAY -o $@ -c $< markiv: ide.o z180.o z180dasm.o z80daisy.o z80scc.o z180asci.o markiv.o rtc_markiv.o ds1202_1302.o $(CC) $(CCOPTS) -s -o markiv $^ $(SOCKLIB) diff --git a/ds1202_1302/ds1202_1302.c b/ds1202_1302/ds1202_1302.c index 93e5b2d..5240dd5 100644 --- a/ds1202_1302/ds1202_1302.c +++ b/ds1202_1302/ds1202_1302.c @@ -42,7 +42,7 @@ #define lib_calloc calloc #define lib_free free -int VERBOSE; +extern int VERBOSE; #define mon_out(x) if (VERBOSE) printf x /* The DS1202 and DS1302 are serial line based RTCs, they have the following features: diff --git a/fdc/86box.h b/fdc/86box.h index 37c65d8..0b7b9b3 100644 --- a/fdc/86box.h +++ b/fdc/86box.h @@ -26,7 +26,7 @@ #define wcscasecmp strcasecmp #define wcslen strlen -int VERBOSE; +extern int VERBOSE; #define LOG(...) do { if (VERBOSE) printf (__VA_ARGS__); } while (0) char *plat_get_extension(char *s); diff --git a/ins8250/ins8250.h b/ins8250/ins8250.h index 34019d2..d6049bb 100644 --- a/ins8250/ins8250.h +++ b/ins8250/ins8250.h @@ -11,7 +11,7 @@ #pragma once -int VERBOSE; +extern int VERBOSE; #include #ifndef LOG #define LOG(...) do { if (VERBOSE) printf (__VA_ARGS__); } while (0) diff --git a/markiv.c b/markiv.c index 87cf306..bf27b6e 100644 --- a/markiv.c +++ b/markiv.c @@ -301,7 +301,7 @@ int main(int argc, char** argv) atexit(destroy_rtc); cpu = cpu_create_z180("Z180",Z180_TYPE_Z180,18432000,&ram,NULL,&iospace,irq0ackcallback,NULL/*daisychain*/, - asci_rx,asci_tx,NULL,NULL,NULL,NULL); + asci_rx,asci_tx,NULL,NULL,NULL,NULL,NULL,NULL); //printf("1\n");fflush(stdout); cpu_reset_z180(cpu); //printf("2\n");fflush(stdout); diff --git a/p112.c b/p112.c index 8478977..860445e 100644 --- a/p112.c +++ b/p112.c @@ -406,7 +406,7 @@ int main(int argc, char** argv) fdd_load(BOOT_FDD,"p112-fdd1.img"); cpu = cpu_create_z180("Z182",Z180_TYPE_Z182,16000000,&ram,&rom,&iospace,irq0ackcallback,NULL/*daisychain*/, - NULL,NULL,escc_rx,escc_tx,parport_read,parport_write); + NULL,NULL,NULL,NULL,escc_rx,escc_tx,parport_read,parport_write); //printf("1\n");fflush(stdout); cpu_reset_z180(cpu); //printf("2\n");fflush(stdout); diff --git a/sc126.c b/sc126.c new file mode 100644 index 0000000..677e75b --- /dev/null +++ b/sc126.c @@ -0,0 +1,368 @@ +/* + * markiv.c - Mark IV emulation. + * + * Copyright (c) Michal Tomek 2018-2019 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 USA. + * + */ + +#include +#include +#include +#include +#include + +#ifdef SOCKETCONSOLE +#define BASE_PORT 10180 +#define MAX_SOCKET_PORTS 1 +#include "sconsole.h" +#endif + +#ifdef _WIN32 +#include +#include +#include +#define fileno _fileno +#else +#include +#endif + +#include "z180/z180.h" +#include "ds1202_1302/ds1202_1302.h" +#include "sdcard/sdcard.h" + +// so far only 512k EEPROM+512k RAM is supported +UINT8 _ram[1048576]; // lo 512k is ROM + +#define RAMARRAY _ram +#define ROMARRAY NULL +#include "z180dbg.h" + +unsigned int asci_clock = 16; + +rtc_ds1202_1302_t *rtc; + +UINT8 xmem_bank; + +struct z180_device *cpu; + +UINT8 ram_read(offs_t A) { + A &= 0xfffff; + return _ram[A]; +} + +void ram_write(offs_t A,UINT8 V) { + A &= 0xfffff; + + // low 512k is eprom + if (A >= 524288) { + _ram[A]=V; + } +} + +int char_available() { +#ifdef SOCKETCONSOLE + return char_available_socket_port(0); +#else + return _kbhit(); +#endif +} + +void asci_tx(device_t *device, int channel, UINT8 Value) { + if (channel==0) { + //printf("TX: %c", Value); +#ifdef SOCKETCONSOLE + tx_socket_port(0, Value); +#else + fputc(Value,stdout); +#endif + //printf("\n"); + } +} + +int asci_rx(device_t *device, int channel) { + int ioData; + if (channel==0) { + //ioData = 0xFF; + if(char_available()) { +#ifdef SOCKETCONSOLE + ioData = rx_socket_port(0); +#else + //printf("RX\n"); + ioData = getch(); +#endif + return ioData; + } + } + return -1; +} + +int irq0ackcallback(device_t *device,int irqnum) { +} + +void print_bin_u8(UINT8 v) { + int bits = 8; + while(bits) { + if (v & 0x80) { + printf("1"); + } else { + printf("0"); + } + v <<= 1; + bits--; + } +} + +// The serial port on the Z180 is backwards for SPI sdcards, +// so here is a quick table to bitswap a byte +UINT8 mirtab[256] = { + 0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0, 0x10, 0x90, 0x50, 0xD0, + 0x30, 0xB0, 0x70, 0xF0, 0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8, + 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8, 0x04, 0x84, 0x44, 0xC4, + 0x24, 0xA4, 0x64, 0xE4, 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4, + 0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC, 0x1C, 0x9C, 0x5C, 0xDC, + 0x3C, 0xBC, 0x7C, 0xFC, 0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2, + 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2, 0x0A, 0x8A, 0x4A, 0xCA, + 0x2A, 0xAA, 0x6A, 0xEA, 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA, + 0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6, 0x16, 0x96, 0x56, 0xD6, + 0x36, 0xB6, 0x76, 0xF6, 0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE, + 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE, 0x01, 0x81, 0x41, 0xC1, + 0x21, 0xA1, 0x61, 0xE1, 0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1, + 0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9, 0x19, 0x99, 0x59, 0xD9, + 0x39, 0xB9, 0x79, 0xF9, 0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5, + 0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5, 0x0D, 0x8D, 0x4D, 0xCD, + 0x2D, 0xAD, 0x6D, 0xED, 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD, + 0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3, 0x13, 0x93, 0x53, 0xD3, + 0x33, 0xB3, 0x73, 0xF3, 0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB, + 0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB, 0x07, 0x87, 0x47, 0xC7, + 0x27, 0xA7, 0x67, 0xE7, 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7, + 0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF, 0x1F, 0x9F, 0x5F, 0xDF, + 0x3F, 0xBF, 0x7F, 0xFF, +}; + +UINT8 rtc_latch; +struct sdcard_device sd0; + +void sci_write(device_t *device, int channel, UINT8 data) { + int cs0 = (rtc_latch & 0x04) == 0; + sdcard_write(&sd0, cs0, mirtab[data]); +} + +int sci_read(device_t *device, int channel) { + int cs0 = (rtc_latch & 0x04) == 0; + int result = 0xff; + result = mirtab[sdcard_read(&sd0, cs0, 0xff)]; + // TODO: multiple cards requires knowing if DO was driven + + return result; +} + +UINT8 io_read (offs_t Port) { + Port &= 0xff; + uint8_t ioData = 0; + switch (Port) { + case 0x0c: + ioData |= ds1202_1302_read_data_line(rtc); + // D7 = I2C SDA read TODO + break; + // case 0x0d: LED port, but doesnt support read + + default: + printf("IO:READ: 0x%04x\n",Port); + break; + } + return ioData; +} + +void io_write (offs_t Port,UINT8 Value) { + Port &= 0xff; + switch (Port) { + case 0x0c: + // D0 = I2C SCL + // D1 = Flash Select + // D2 = /SD_CS1 + // D3 = /SD_CS2 + + ds1202_1302_set_lines( + rtc, + Value&0x10?1:0, // D4 = /CE + Value&0x40?1:0, // D6 = SCLK + Value&0x20?0:(Value&0x80?1:0) // D5 = /WE, D7 = Input + ); + + rtc_latch = Value; + + // Try to avoid printing all RTC accesses + if ((Value & 0x10) && (Value & 0xf)!=0) { + printf("IO:CTRL: "); + print_bin_u8(Value); + printf("\n"); + } + + break; + + case 0x0d: + //printf("IO:LED: "); + //print_bin_u8(Value); + //printf("\n"); + break; + + case 0x12: + case 0x13: + case 0x14: + case 0x15: + case 0x16: + // IDE IO=0x10 + break; + + case 0x20: + case 0x22: + case 0x23: + // PPIDE IO=0x20 + break; + + default: + printf("IO:WRITE: 0x%04x = 0x%02x\n",Port,Value); + break; + } +} + +void do_timers() { + //16X clock for ASCI + //printf("asci_clk:%d\n",asci_clock); + if (!--asci_clock) { + z180asci_channel_device_timer(cpu->z180asci->m_chan0); + z180asci_channel_device_timer(cpu->z180asci->m_chan1); + asci_clock = 16; + } +} + +void boot1dma () { + FILE* f; + if (!(f=fopen("sc126rom.bin","rb"))) { + printf("No ROM found.\n"); + g_quit = 1; + } else { + fread(&_ram[0],1,524288,f); + fclose(f); + } +} + +void io_device_update() { +#ifdef SOCKETCONSOLE + // check socket open and optionally reopen it + if (!is_connected_socket_port(0)) open_socket_port(0); +#endif +} + +#ifndef _WIN32 +void sigint_handler(int s) { + // POSIX SIGINT handler + // do nothing +} + +void sigquit_handler(int s) { + // POSIX SIGQUIT handler + // make sure atexit is called + printf("\nExiting emulation.\n"); + shutdown_socket_ports(); // close sockets to prevent waiting for a connection + g_quit = 1; // make sure atexit is called +} +#endif + +void disableCTRLC() { +#ifdef _WIN32 + HANDLE consoleHandle = GetStdHandle(STD_INPUT_HANDLE); + DWORD consoleMode; + GetConsoleMode(consoleHandle,&consoleMode); + SetConsoleMode(consoleHandle,consoleMode&~ENABLE_PROCESSED_INPUT); +#else + signal(SIGINT, sigint_handler); +#endif +} + +struct address_space ram = {ram_read,ram_write,ram_read}; +//struct address_space rom = {rom_read,NULL,rom_read}; +struct address_space iospace = {io_read,io_write,NULL}; + +void destroy_rtc() +{ + ds1202_1302_destroy(rtc,1); +} + +int main(int argc, char** argv) +{ + printf("z180emu v1.0 sc126\n"); + + disableCTRLC(); +#ifndef _WIN32 + // on POSIX, route SIGQUIT (CTRL+\) to graceful shutdown + signal(SIGQUIT, sigquit_handler); +#endif + // on MINGW, keep CTRL+Break (and window close button) enabled + // MINGW always calls atexit in these cases + +#ifdef SOCKETCONSOLE + init_TCPIP(); + init_socket_port(0); // ASCI Console + atexit(shutdown_socket_ports); +#endif + io_device_update(); // wait for serial socket connections + + if (argc > 1 && !strcmp(argv[1],"d")) { + if (argc == 3) { + starttrace = atoll(argv[2]); + } else { + starttrace = 0; + } + } + VERBOSE = starttrace==0?1:0; + +#ifdef _WIN32 + setmode(fileno(stdout), O_BINARY); +#endif + + boot1dma(); + sdcard_init(&sd0,"sd00.img"); + + rtc = ds1202_1302_init("RTC",1302); + ds1202_1302_reset(rtc); + atexit(destroy_rtc); + + cpu = cpu_create_z180("Z180",Z180_TYPE_Z180,18432000,&ram,NULL,&iospace,irq0ackcallback,NULL/*daisychain*/, + asci_rx,asci_tx,sci_read,sci_write,NULL,NULL,NULL,NULL); + //printf("1\n");fflush(stdout); + cpu_reset_z180(cpu); + //printf("2\n");fflush(stdout); + + struct timeval t0; + struct timeval t1; + gettimeofday(&t0, 0); + int runtime=50000; + + //g_quit = 0; + while(!g_quit) { + if(instrcnt>=starttrace) VERBOSE=1; + cpu_execute_z180(cpu,10000); + //printf("3\n");fflush(stdout); + io_device_update(); + /*if (!(--runtime)) + g_quit=1;*/ + } + gettimeofday(&t1, 0); + printf("instrs:%llu, time:%g\n",instrcnt, (t1.tv_sec - t0.tv_sec) * 1000.0f + (t1.tv_usec - t0.tv_usec) / 1000.0f); +} diff --git a/sdcard/sdcard.c b/sdcard/sdcard.c new file mode 100644 index 0000000..8e3f94b --- /dev/null +++ b/sdcard/sdcard.c @@ -0,0 +1,498 @@ +/* + * Simple sdcard in SPI mode emulation + * (c)2023 Hamish Coleman + * + * TODO: + * - hook up writes + * - improve the CSD to reflect the actual size of the backing file + */ + +#include +#include +#include +#include +#include + +typedef uint8_t UINT8; + +#include "sdcard.h" + +#define dprint(level, ...) do { \ + if (sdcard_trace >= level) printf (__VA_ARGS__); \ +} while(0) + +int sdcard_trace = 1; + +char *sdcard_state_names[] = { + [IDLE] = "IDLE", + + [RX_CMD] = "RX_CMD", + [RX_BLOCK] = "RX_BLOCK", + + [TX_BUSY] = "TX_BUSY", + [TX_R1] = "TX_R1", + [TX_R3] = "TX_R3", + [TX_R7] = "TX_R7", + [TX_IMM32] = "TX_IMM32", + [TX_BLOCK_R1] = "TX_BLOCK_R1", + [TX_BLOCK_TOKEN] = "TX_BLOCK_TOKEN", + [TX_BLOCK_BUF] = "TX_BLOCK_BUF", + [TX_RX_BLOCK_R1] = "TX_RX_BLOCK_R1", + [TX_RX_BLOCK_STAT] = "TX_RX_BLOCK_STAT", + + [NEXT] = "next", +}; + +void sdcard_setstate(struct sdcard_device *sd, enum sdcard_state newstate) { + if (newstate == NEXT) { + newstate = sd->state_next; + sd->state_next = IDLE; + } + if (sd->state != newstate) { + dprint(1,"SD: %s -> %s\n", + sdcard_state_names[sd->state], + sdcard_state_names[newstate] + ); + } + sd->state = newstate; +} + +void sdcard_reset_ptr(struct sdcard_device *sd) { + sd->cmd_ptr = 0; + sd->resp_ptr = 0; + sd->tx_len = 0; +} + +void sdcard_reset(struct sdcard_device *sd) { + sdcard_setstate(sd, IDLE); + sdcard_reset_ptr(sd); +} + +int sdcard_init(struct sdcard_device *sd, char *filename) { + memset((void *)sd, 0, sizeof(*sd)); + sdcard_reset_ptr(sd); + + if ((sd->fd = open(filename, O_RDWR))==-1) { + return -1; + } + return 1; +} + +void sdcard_dump(struct sdcard_device *sd) { + printf("SD:DUMP: s%i,t%x,r%x, ", + sd->state, + sd->cmd_ptr, + sd->resp_ptr + ); + + int i; + for (i=0; icmd); i++) { + printf("%02x", sd->cmd[i]); + } + printf("-"); + for (i=0; i<20; i++) { + printf("%02x", sd->resp[i]); + } + printf("\n"); + +} + +#define R1_0 0x00 +#define R1_OK 0x01 +#define R1_ILL_CMD 0x05 +void sdcard_resp_r1(struct sdcard_device *sd, UINT8 r1) { + sd->resp_ptr = 0; + // TODO: sd->tx_len = 0; + sd->r1 = r1; + sd->state_next = TX_R1; + sdcard_setstate(sd, TX_BUSY); +} + +void sdcard_resp_reply32(struct sdcard_device *sd, int reply) { + sd->resp[0] = (reply >> 24) & 0xff; + sd->resp[1] = (reply >> 16) & 0xff; + sd->resp[2] = (reply >> 8) & 0xff; + sd->resp[3] = reply & 0xff; +} + +void sdcard_resp_r3(struct sdcard_device *sd, UINT8 r1, int value) { + sd->resp_ptr = 0; + sd->r1 = r1; + // TODO: sd->tx_len = 4; + sdcard_resp_reply32(sd, value); + sd->state_next = TX_R3; + sdcard_setstate(sd, TX_BUSY); +} + +// TODO: r7 is almost identical to r3, coalesce them +void sdcard_resp_r7(struct sdcard_device *sd, UINT8 r1, int value) { + sd->resp_ptr = 0; + sd->r1 = r1; + // TODO: sd->tx_len = 4; + sdcard_resp_reply32(sd, value); + sd->state_next = TX_R7; + sdcard_setstate(sd, TX_BUSY); +} + +void sdcard_resp_tx_block(struct sdcard_device *sd, UINT8 r1, int tx_len) { + sd->resp_ptr = 0; + sd->tx_len = tx_len; + sd->r1 = r1; + sd->state_next = TX_BLOCK_R1; + sdcard_setstate(sd, TX_BUSY); +} + +void sdcard_resp_rx_block(struct sdcard_device *sd) { + sd->resp_ptr = 0; + sd->tx_len = 0; // This overwrites the buffer, so mark it as empty + sd->r1 = 0; // R1 should indicate not idle + sd->state_next = TX_RX_BLOCK_R1; + sdcard_setstate(sd, TX_BUSY); +} + +int sdcard_write(struct sdcard_device *sd, int cs, UINT8 data) { + if (!cs) { + // Not for us, dont drive any bits in reply + return 0; + // TODO: detect init sequence + } + + if (sdcard_trace>1) { + sdcard_dump(sd); + printf("SD:WR: d=0x%02x\n", data); + } + + switch (sd->state) { + case TX_R1: + case TX_R3: + case TX_R7: + case TX_IMM32: + case TX_BLOCK_R1: + case TX_BLOCK_TOKEN: + case TX_BLOCK_BUF: + case TX_RX_BLOCK_R1: + case TX_RX_BLOCK_STAT: + // got a write when we thought we needed a read + dprint(2,"SD: got write, expected read (data=0x%02x)\n", data); + /* FALL THROUGH */ + + case IDLE: + if ((data & 0xc0) != 0x40) { + // The first two bits of a command must be 0,1 + // otherwise, we stay in IDLE + dprint(2,"SD: got dummy write?\n"); + return 0xff; // TODO: signal that we didnt drive DO + } + + sdcard_setstate(sd, RX_CMD); + sdcard_reset_ptr(sd); + /* FALL THROUGH */ + + case RX_CMD: + sd->cmd[sd->cmd_ptr++] = data; + if (sd->cmd_ptr < 6) { + return 0xff; // in write state, return is ignored + } + break; + + case RX_BLOCK: + if (sd->resp_ptr == 0) { + // data token + if (data != 0xfe) { + dprint(1,"SD: unexpected data token 0x%02x\n", data); + sdcard_reset(sd); + } + } else if (sd->resp_ptr < 514) { + int index = sd->resp_ptr-1; + sd->resp[index] = data; + } else if (sd->resp_ptr == 515) { + // ignore CRC1 + } else { + // ignore CRC2 + + // process entire block + int block = ( + sd->cmd[1]<<24 | + sd->cmd[2]<<16 | + sd->cmd[3]<<8 | + sd->cmd[4] + ); + dprint(1,"SD:WRITE: 0x%04x RX_BUFFER\n", block); + + // TODO: wire up write command here + + sd->r1 = 0x05; // Data accepted + sdcard_setstate(sd, TX_RX_BLOCK_STAT); + } + sd->resp_ptr++; + return 0xff; + + default: + dprint(1,"SD:WR: unexpected state %i\n", sd->state); + return 0xff; + } + + // If we got here, we have an entire command packet + + UINT8 cmd = sd->cmd[0]; + + // should start with 0b01xxxxxx (x = cmd) + // should end with 0bccccccc1 (c = crc) + + // TODO: check cmd[5] CRC + + int rp = 0; + switch (cmd) { + case 0x40: + dprint(1,"SD:CMD0 GO_IDLE_STATE\n"); + sdcard_resp_r1(sd, R1_OK); + break; + + case 0x48: + dprint(1,"SD:CMD8 SEND_IF_COND\n"); + + // We accept any conditions (cmd[1],cmd[2],cmd[3]) + // And echo the check byte back (cmd[4]) + // Should check CRC (eg, 48000001aa87 is valid) + int reply; + reply = sd->cmd[1] << 24; + reply |= sd->cmd[2] << 16; + reply |= sd->cmd[3] << 8; + reply |= sd->cmd[4]; + + sdcard_resp_r7(sd, R1_OK, reply); + break; + + case 0x49: + dprint(1,"SD:CMD9 SEND_CSD\n"); + + sd->resp[rp++] = 0x40; // CSD_STRUCT and reserved bits + sd->resp[rp++] = 0x0e; // TAAC + sd->resp[rp++] = 0x00; // NSAC + sd->resp[rp++] = 0x32; // TRAN_SPEED 24MHz + sd->resp[rp++] = 0x5b; // CCC - guess + sd->resp[rp++] = 0x59; // CCC, READ_BL_LEN + sd->resp[rp++] = 0x00; // 4 bits flags, 4 bits reserved + sd->resp[rp++] = 0x00; // 2 bits reserved, 6 bits LBA + sd->resp[rp++] = 0x08; // . + sd->resp[rp++] = 0x00; // Approx 1Gig + sd->resp[rp++] = 0x7f; + sd->resp[rp++] = 0x80; + sd->resp[rp++] = 0x0a; + sd->resp[rp++] = 0x40; + sd->resp[rp++] = 0x00; + sd->resp[rp++] = 0x01; // last bit is stop bit + // TODO: use real backing store for size + + sdcard_resp_tx_block(sd, R1_0,16); + break; + + case 0x4a: + dprint(1,"SD:CMD10 SEND_CID\n"); + + sd->resp[rp++] = 0x01; // Manuf + sd->resp[rp++] = 'A'; // App + sd->resp[rp++] = 'A'; + sd->resp[rp++] = 'B'; // Name + sd->resp[rp++] = 'B'; + sd->resp[rp++] = 'B'; + sd->resp[rp++] = 'B'; + sd->resp[rp++] = 'B'; + sd->resp[rp++] = 0x10; // Rev + sd->resp[rp++] = 0x12; // Serial + sd->resp[rp++] = 0x34; + sd->resp[rp++] = 0x56; + sd->resp[rp++] = 0x78; + sd->resp[rp++] = 0x01; // m=1 + sd->resp[rp++] = 0x01; // y=2001 + sd->resp[rp++] = 0x01; // last bit is stop bit + + sdcard_resp_tx_block(sd, R1_0, 16); + break; + + case 0x50: + dprint(1,"SD:CMD16 SET_BLOCKLEN\n"); + // TODO: do a real check of the value + if (sd->cmd[3] != 2) { + dprint(1,"SD: unexpected blocklen\n"); + sdcard_resp_r1(sd, R1_ILL_CMD); + break; + } + sdcard_resp_r1(sd, R1_OK); + break; + + case 0x51: { + dprint(1,"SD:CMD17 READ_SINGLE_BLOCK\n"); + int block = ( + sd->cmd[1]<<24 | + sd->cmd[2]<<16 | + sd->cmd[3]<<8 | + sd->cmd[4] + ); + dprint(1,"SD:READ: 0x%04x\n", block); + + if (lseek(sd->fd, block*0x200, SEEK_SET) != -1) { + read(sd->fd, &sd->resp, 0x200); + } + + // TODO: could use result of read as source of r1 status + sdcard_resp_tx_block(sd, R1_0, 512); + break; + } + + case 0x58: { + dprint(1,"SD:CMD24 WRITE_BLOCK\n"); + int block = ( + sd->cmd[1]<<24 | + sd->cmd[2]<<16 | + sd->cmd[3]<<8 | + sd->cmd[4] + ); + dprint(1,"SD:WRITE: 0x%04x\n", block); + + sdcard_resp_rx_block(sd); + break; + } + + case 0x77: + dprint(1,"SD:CMD55 APP_CMD\n"); + sdcard_resp_r1(sd, R1_OK); + break; + + case 0x7a: + dprint(1,"SD:CMD58 READ_OCR\n"); + sdcard_resp_r3(sd, R1_0, 0x40000000); + break; + + // TODO: gate ACMD values on a previous CMD55 APP_CMD + case 0x69: + dprint(1,"SD:ACMD41 SEND_OP_COND\n"); + sdcard_resp_r1(sd, R1_0); + break; + + case 0x73: + dprint(1,"SD:ACMD51 SEND_SCR\n"); + sd->resp[rp++] = 0x01; // SD_SPEC v1.10 + sd->resp[rp++] = 0xB1; // SD_SEC == SDHC, 1bit width + sd->resp[rp++] = 0x00; + sd->resp[rp++] = 0x00; + sd->resp[rp++] = 0x0c; // CMD_SUPPORT 58,59 + 48/49 + sd->resp[rp++] = 0x00; + sd->resp[rp++] = 0x00; + sd->resp[rp++] = 0x00; + sdcard_resp_tx_block(sd, R1_0, 8); + break; + + default: + sdcard_resp_r1(sd, R1_ILL_CMD); + + dprint(1,"unknown\n"); + dprint(1,"SD:CMD: cmd=0x%02x\n",cmd); + break; + } + if (sdcard_trace>1) { + printf("\n"); + } + return 0xff; +} + +int sdcard_read(struct sdcard_device *sd, int cs, UINT8 data) { + if (!cs) { + // Not for us, dont drive any bits in reply + return 0; + } + + UINT8 result; + if (sdcard_trace>1) { + sdcard_dump(sd); + } + + switch (sd->state) { + case IDLE: + // in idle mode, the card just outputs ones? + dprint(1,"SD: read while IDLE\n"); + result = 0xff; + break; + + case RX_CMD: + // got a read when we expected more writes + sdcard_reset(sd); + dprint(1,"SD: read when RX_CMD\n"); + break; + + case TX_BUSY: + result = 0xff; // first resp, indicate busy + sdcard_setstate(sd, NEXT); + break; + + case TX_R3: + case TX_R7: + sd->state_next = TX_IMM32; + /* FALL THROUGH */ + + case TX_R1: + result = sd->r1; // second, return response code + sdcard_setstate(sd, NEXT); + break; + + case TX_IMM32: + result = sd->resp[sd->resp_ptr]; + sd->resp_ptr++; + + if (sd->resp_ptr==4) { + sdcard_setstate(sd, IDLE); + } + + break; + + // TODO? with a deeper queue of state_next values, the TX_BLOCK + // could be simpler + case TX_BLOCK_R1: + result = sd->r1; + sdcard_setstate(sd, TX_BLOCK_TOKEN); + break; + + case TX_BLOCK_TOKEN: + result = 0xfe; // data token + sdcard_setstate(sd, TX_BLOCK_BUF); + break; + + case TX_BLOCK_BUF: { + int index = sd->resp_ptr; + + if (index < sd->tx_len) { + // Send the actual data + result = sd->resp[index]; + } else if (index == sd->tx_len) { + result = 0; // CRC1; + } else { + result = 0; // CRC2; + sdcard_setstate(sd, IDLE); + } + sd->resp_ptr++; + break; + } + + case TX_RX_BLOCK_R1: + result = sd->r1; + sdcard_reset_ptr(sd); + sdcard_setstate(sd, RX_BLOCK); + break; + + case TX_RX_BLOCK_STAT: + result = sd->r1; + sdcard_setstate(sd, IDLE); + break; + + default: + dprint(1,"SD:RD: unexpected state %i\n", sd->state); + result = 0xff; + break; + } + + if (sdcard_trace>1) { + printf("SD:RD: r=0x%02x\n", result); + } + return result; +} + diff --git a/sdcard/sdcard.h b/sdcard/sdcard.h new file mode 100644 index 0000000..12986f5 --- /dev/null +++ b/sdcard/sdcard.h @@ -0,0 +1,53 @@ +/* + * Simple sdcard in SPI mode emulation + * (c)2023 Hamish Coleman + * + * Public interface definitions + */ +#ifndef SDCARD_H +#define SDCARD_H + +// States are named from the SDcard's point of view (thus "TX" is the card +// intends to transmit) +// +// Remember to update sdcard_state_names too +enum sdcard_state { + IDLE = 0, + + RX_CMD, + RX_BLOCK, + + TX_BUSY, + TX_R1, + TX_R3, + TX_R7, + TX_IMM32, + TX_BLOCK_R1, + TX_BLOCK_TOKEN, + TX_BLOCK_BUF, + TX_RX_BLOCK_R1, + TX_RX_BLOCK_STAT, + + NEXT, // Pseudo state, requesting to shift to the "next" state +}; + +struct sdcard_device { + int fd; + enum sdcard_state state; + enum sdcard_state state_next; + int cmd_ptr; + int resp_ptr; + int tx_len; // size of valid data in resp to TX + UINT8 r1; // result code to send with R1 + UINT8 cmd[8]; + UINT8 resp[512+6]; +}; + +extern int sdcard_trace; + +// TODO: technically, SPI is simultaneous readwrite +int sdcard_read(struct sdcard_device *device, int cs, UINT8 data); +int sdcard_write(struct sdcard_device *device, int cs, UINT8 data); +int sdcard_init(struct sdcard_device *sd, char *filename); + +#endif diff --git a/z180/z180.c b/z180/z180.c index 1553150..2cb556e 100644 --- a/z180/z180.c +++ b/z180/z180.c @@ -23,6 +23,8 @@ * *****************************************************************************/ +int VERBOSE; + /* Revision: 2019-01-26 Michal Tomek z180emu */ /***************************************************************************** @@ -1097,12 +1099,17 @@ UINT8 z180_readcontrol(struct z180_state *cpustate, offs_t port) case Z180_CNTR: data = cpustate->IO_CNTR & Z180_CNTR_RMASK; data &= ~0x10; // Super Famicom Box sets the TE bit then wants it to be toggled after 8 bits transmitted + data &= ~0x20; // Clear the RE LOG("Z180 '%s' CNTR rd $%02x ($%02x)\n", cpustate->device->m_tag, data, cpustate->io[port]); break; case Z180_TRDR: - data = cpustate->IO_TRDR & Z180_TRDR_RMASK; - logerror("Z180 '%s' TRDR rd $%02x ($%02x)\n", cpustate->device->m_tag, data, cpustate->io[port]); + if (cpustate->device->z180csi_rx_cb) + data = cpustate->device->z180csi_rx_cb(cpustate->device,0); + else { + logerror("Z180 '%s' TRDR rd $%02x ($%02x)\n", cpustate->device->m_tag, data, cpustate->io[port]); + data = cpustate->IO_TRDR & Z180_TRDR_RMASK; + } break; case Z180_TMDR0L: @@ -1664,8 +1671,11 @@ void z180_writecontrol(struct z180_state *cpustate, offs_t port, UINT8 data) break; case Z180_TRDR: - LOG("Z180 '%s' TRDR wr $%02x ($%02x)\n", cpustate->device->m_tag, data, data & Z180_TRDR_WMASK); cpustate->IO_TRDR = (cpustate->IO_TRDR & ~Z180_TRDR_WMASK) | (data & Z180_TRDR_WMASK); + if (cpustate->device->z180csi_tx_cb) + cpustate->device->z180csi_tx_cb(cpustate->device,0,data); + else + LOG("Z180 '%s' TRDR wr $%02x ($%02x)\n", cpustate->device->m_tag, data, data & Z180_TRDR_WMASK); break; case Z180_TMDR0L: @@ -2422,6 +2432,7 @@ struct z180_device *cpu_create_z180(char *tag, UINT32 type, UINT32 clock, struct address_space *ram, struct address_space *rom /* only on Z182 */, struct address_space *iospace, device_irq_acknowledge_callback irqcallback, struct z80daisy_interface *daisy_init, rx_callback_t z180asci_rx_cb,tx_callback_t z180asci_tx_cb, + rx_callback_t z180csi_rx_cb, tx_callback_t z180csi_tx_cb, rx_callback_t z80scc_rx_cb,tx_callback_t z80scc_tx_cb /* only on Z182 */, parport_read_callback_t parport_read_cb, parport_write_callback_t parport_write_cb /* only on Z182 */) { @@ -2467,6 +2478,9 @@ struct z180_device *cpu_create_z180(char *tag, UINT32 type, UINT32 clock, d->z180asci = z180asci_device_create(d,d->z180asci_tag,clock, z180asci_rx_cb, z180asci_tx_cb); + d->z180csi_rx_cb = z180csi_rx_cb; + d->z180csi_tx_cb = z180csi_tx_cb; + SZHVC_add = malloc(2*256*256); SZHVC_sub = malloc(2*256*256); diff --git a/z180/z180.h b/z180/z180.h index aec76cf..600dde2 100644 --- a/z180/z180.h +++ b/z180/z180.h @@ -185,6 +185,8 @@ struct z180_device { struct z180asci_device *z180asci; parport_read_callback_t m_parport_read_cb; parport_write_callback_t m_parport_write_cb; + rx_callback_t z180csi_rx_cb; + tx_callback_t z180csi_tx_cb; }; //void cpu_get_info_z180(device_t *device, UINT32 state, cpuinfo *info); @@ -202,6 +204,7 @@ struct z180_device *cpu_create_z180(char *tag, UINT32 type, UINT32 clock, struct address_space *ram, struct address_space *rom /* only on Z182 */, struct address_space *iospace, device_irq_acknowledge_callback irqcallback, struct z80daisy_interface *daisy_init, rx_callback_t z180asci_rx_cb,tx_callback_t z180asci_tx_cb, + rx_callback_t z180csi_rx_cb, tx_callback_t z180csi_tx_cb, rx_callback_t z80scc_rx_cb,tx_callback_t z80scc_tx_cb /* only on Z182 */, parport_read_callback_t parport_read_cb, parport_write_callback_t parport_write_cb /* only on Z182 */); void cpu_reset_z180(device_t *device); diff --git a/z180/z80common.h b/z180/z80common.h index 23c3129..a859d0b 100644 --- a/z180/z80common.h +++ b/z180/z80common.h @@ -13,7 +13,7 @@ #ifndef __Z80COMMON_H__ #define __Z80COMMON_H__ -int VERBOSE; +extern int VERBOSE; #include #define logerror printf #define LOG(...) do { if (VERBOSE) logerror (__VA_ARGS__); } while (0)