2020-01-01 14:03:48 +11:00
|
|
|
/* RC2014 classic
|
|
|
|
*
|
|
|
|
* - 8K of ROM in range 0x0000-0x2000
|
|
|
|
* - 32K of RAM in range 0x8000-0xffff
|
|
|
|
* - ACIA in ports 0x80 (ctl) and 0x81 (data)
|
|
|
|
*
|
|
|
|
* ACIA is hooked to stdin/stdout. CTRL+D exits when in TTY mode.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <stdint.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <termios.h>
|
2020-10-26 06:58:00 +11:00
|
|
|
#include "emul.h"
|
2020-01-01 14:03:48 +11:00
|
|
|
#include "acia.h"
|
2020-09-25 11:01:14 +10:00
|
|
|
#include "sio.h"
|
2020-04-18 05:11:32 +10:00
|
|
|
#include "sdc.h"
|
2020-10-30 06:01:25 +11:00
|
|
|
#include "rc2014_spi.h"
|
2020-01-01 14:03:48 +11:00
|
|
|
|
|
|
|
#define RAMSTART 0x8000
|
|
|
|
#define ACIA_CTL_PORT 0x80
|
|
|
|
#define ACIA_DATA_PORT 0x81
|
2020-09-25 11:01:14 +10:00
|
|
|
#define SIO_ACTL_PORT 0x80
|
|
|
|
#define SIO_ADATA_PORT 0x81
|
2020-09-17 23:36:16 +10:00
|
|
|
#define SDC_CTL 0x05
|
2020-04-18 05:11:32 +10:00
|
|
|
#define SDC_SPI 0x04
|
2020-01-01 14:03:48 +11:00
|
|
|
#define MAX_ROMSIZE 0x2000
|
|
|
|
|
2020-09-25 11:01:14 +10:00
|
|
|
bool use_sio = false;
|
2020-01-01 14:03:48 +11:00
|
|
|
static ACIA acia;
|
2020-09-25 11:01:14 +10:00
|
|
|
static SIO sio;
|
2020-04-18 05:11:32 +10:00
|
|
|
static SDC sdc;
|
2020-10-30 06:01:25 +11:00
|
|
|
static SPI spi;
|
2020-01-01 14:03:48 +11:00
|
|
|
|
|
|
|
static uint8_t iord_acia_ctl()
|
|
|
|
{
|
|
|
|
return acia_ctl_rd(&acia);
|
|
|
|
}
|
|
|
|
|
|
|
|
static uint8_t iord_acia_data()
|
|
|
|
{
|
|
|
|
return acia_data_rd(&acia);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void iowr_acia_ctl(uint8_t val)
|
|
|
|
{
|
|
|
|
acia_ctl_wr(&acia, val);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void iowr_acia_data(uint8_t val)
|
|
|
|
{
|
|
|
|
acia_data_wr(&acia, val);
|
|
|
|
}
|
|
|
|
|
2020-09-25 11:01:14 +10:00
|
|
|
static uint8_t iord_sio_ctl()
|
|
|
|
{
|
|
|
|
return sio_actl_rd(&sio);
|
|
|
|
}
|
|
|
|
|
|
|
|
static uint8_t iord_sio_data()
|
|
|
|
{
|
|
|
|
return sio_adata_rd(&sio);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void iowr_sio_ctl(uint8_t val)
|
|
|
|
{
|
|
|
|
sio_actl_wr(&sio, val);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void iowr_sio_data(uint8_t val)
|
|
|
|
{
|
|
|
|
sio_adata_wr(&sio, val);
|
|
|
|
}
|
|
|
|
|
2020-10-30 06:01:25 +11:00
|
|
|
static uint8_t iord_spi()
|
2020-04-18 05:11:32 +10:00
|
|
|
{
|
2020-10-30 06:01:25 +11:00
|
|
|
return spi_rd(&spi);
|
2020-04-18 05:11:32 +10:00
|
|
|
}
|
|
|
|
|
2020-10-30 06:01:25 +11:00
|
|
|
static void iowr_spi(uint8_t val)
|
2020-04-18 05:11:32 +10:00
|
|
|
{
|
2020-10-30 06:01:25 +11:00
|
|
|
spi_wr(&spi, val);
|
2020-04-18 05:11:32 +10:00
|
|
|
}
|
|
|
|
|
2020-10-30 06:01:25 +11:00
|
|
|
byte spix_sdc(byte val) { return sdc_spix(&sdc, val); }
|
|
|
|
|
2020-09-17 23:36:16 +10:00
|
|
|
// in emulation, exchanges are always instantaneous, so we
|
|
|
|
// always report as ready.
|
2020-10-30 06:01:25 +11:00
|
|
|
static uint8_t iord_spi_ctl()
|
2020-04-18 05:11:32 +10:00
|
|
|
{
|
2020-09-17 23:36:16 +10:00
|
|
|
return 0;
|
2020-04-18 05:11:32 +10:00
|
|
|
}
|
|
|
|
|
2020-10-30 06:01:25 +11:00
|
|
|
static void iowr_spi_ctl(uint8_t val)
|
2020-04-18 05:11:32 +10:00
|
|
|
{
|
2020-10-30 06:01:25 +11:00
|
|
|
spi_ctl_wr(&spi, val);
|
2020-04-18 05:11:32 +10:00
|
|
|
}
|
|
|
|
|
2020-09-25 11:01:14 +10:00
|
|
|
static bool has_irq()
|
|
|
|
{
|
|
|
|
return use_sio ? sio_has_irq(&sio) : acia_has_irq(&acia);
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool hastx()
|
|
|
|
{
|
|
|
|
return use_sio ? sio_hastx(&sio) : acia_hastx(&acia);
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool hasrx()
|
|
|
|
{
|
|
|
|
return use_sio ? sio_hasrx(&sio) : acia_hasrx(&acia);
|
|
|
|
}
|
|
|
|
|
|
|
|
static uint8_t _read()
|
|
|
|
{
|
|
|
|
return use_sio ? sio_read(&sio) : acia_read(&acia);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void _write(uint8_t val)
|
|
|
|
{
|
|
|
|
if (use_sio) { sio_write(&sio, val); } else { acia_write(&acia, val); }
|
|
|
|
}
|
|
|
|
|
|
|
|
static void usage()
|
|
|
|
{
|
|
|
|
fprintf(stderr, "Usage: ./classic [-s] [-c sdcard.img] /path/to/rom\n");
|
|
|
|
}
|
|
|
|
|
2020-01-01 14:03:48 +11:00
|
|
|
int main(int argc, char *argv[])
|
|
|
|
{
|
2020-09-25 11:01:14 +10:00
|
|
|
FILE *fp = NULL;
|
|
|
|
int ch;
|
|
|
|
|
2020-04-18 09:10:22 +10:00
|
|
|
if (argc < 2) {
|
2020-09-25 11:01:14 +10:00
|
|
|
usage();
|
2020-01-01 14:03:48 +11:00
|
|
|
return 1;
|
|
|
|
}
|
2020-09-25 11:01:14 +10:00
|
|
|
acia_init(&acia);
|
|
|
|
sio_init(&sio);
|
|
|
|
sdc_init(&sdc);
|
2020-10-30 06:01:25 +11:00
|
|
|
spi_init(&spi, spix_sdc);
|
2020-09-25 11:01:14 +10:00
|
|
|
|
|
|
|
while ((ch = getopt(argc, argv, "sc:")) != -1) {
|
|
|
|
switch (ch) {
|
|
|
|
case 's':
|
|
|
|
use_sio = true;
|
|
|
|
break;
|
|
|
|
case 'c':
|
|
|
|
fprintf(stderr, "Setting up SD card image with %s\n", optarg);
|
|
|
|
sdc.fp = fopen(optarg, "r+");
|
2020-10-26 07:53:58 +11:00
|
|
|
if (sdc.fp == NULL) {
|
|
|
|
fprintf(stderr, "Can't open file\n");
|
|
|
|
return 1;
|
|
|
|
}
|
2020-09-25 11:01:14 +10:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (optind != argc-1) {
|
|
|
|
usage();
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
fp = fopen(argv[optind], "r");
|
2020-01-01 14:03:48 +11:00
|
|
|
if (fp == NULL) {
|
2020-01-02 14:48:01 +11:00
|
|
|
fprintf(stderr, "Can't open %s\n", argv[1]);
|
2020-01-01 14:03:48 +11:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
Machine *m = emul_init();
|
|
|
|
m->ramstart = RAMSTART;
|
|
|
|
int i = 0;
|
|
|
|
int c;
|
|
|
|
while ((c = fgetc(fp)) != EOF && i < MAX_ROMSIZE) {
|
|
|
|
m->mem[i++] = c & 0xff;
|
|
|
|
}
|
|
|
|
pclose(fp);
|
|
|
|
if (i == MAX_ROMSIZE) {
|
|
|
|
fprintf(stderr, "ROM image too large.\n");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
bool tty = isatty(fileno(stdin));
|
|
|
|
struct termios term, saved_term;
|
|
|
|
if (tty) {
|
|
|
|
// Turn echo off: the shell takes care of its own echoing.
|
|
|
|
if (tcgetattr(0, &term) == -1) {
|
|
|
|
printf("Can't setup terminal.\n");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
saved_term = term;
|
|
|
|
term.c_lflag &= ~ECHO;
|
|
|
|
term.c_lflag &= ~ICANON;
|
|
|
|
term.c_cc[VMIN] = 0;
|
|
|
|
term.c_cc[VTIME] = 0;
|
|
|
|
tcsetattr(0, TCSADRAIN, &term);
|
|
|
|
}
|
|
|
|
|
2020-09-25 11:01:14 +10:00
|
|
|
if (use_sio) {
|
|
|
|
m->iord[SIO_ACTL_PORT] = iord_sio_ctl;
|
|
|
|
m->iord[SIO_ADATA_PORT] = iord_sio_data;
|
|
|
|
m->iowr[SIO_ACTL_PORT] = iowr_sio_ctl;
|
|
|
|
m->iowr[SIO_ADATA_PORT] = iowr_sio_data;
|
|
|
|
} else {
|
|
|
|
m->iord[ACIA_CTL_PORT] = iord_acia_ctl;
|
|
|
|
m->iord[ACIA_DATA_PORT] = iord_acia_data;
|
|
|
|
m->iowr[ACIA_CTL_PORT] = iowr_acia_ctl;
|
|
|
|
m->iowr[ACIA_DATA_PORT] = iowr_acia_data;
|
2020-04-18 09:10:22 +10:00
|
|
|
}
|
2020-10-30 06:01:25 +11:00
|
|
|
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;
|
2020-01-01 14:03:48 +11:00
|
|
|
|
|
|
|
char tosend = 0;
|
|
|
|
while (emul_step()) {
|
|
|
|
// Do we have an interrupt?
|
2020-09-25 11:01:14 +10:00
|
|
|
if (has_irq()) {
|
2020-01-01 14:03:48 +11:00
|
|
|
Z80INT(&m->cpu, 0);
|
|
|
|
}
|
|
|
|
// Is the RC2014 transmitting?
|
2020-09-25 11:01:14 +10:00
|
|
|
if (hastx()) {
|
|
|
|
putchar(_read());
|
2020-01-01 14:03:48 +11:00
|
|
|
fflush(stdout);
|
|
|
|
}
|
|
|
|
// Do we have something to send?
|
|
|
|
if (!tosend) {
|
|
|
|
char c;
|
|
|
|
if (read(fileno(stdin), &c, 1) == 1) {
|
2020-04-08 03:37:52 +10:00
|
|
|
if (c == 5) {
|
2020-04-09 22:26:41 +10:00
|
|
|
emul_memdump();
|
2020-04-08 03:37:52 +10:00
|
|
|
c = 0; // don't send to RC2014
|
|
|
|
}
|
2020-01-01 14:03:48 +11:00
|
|
|
if (c == 4) { // CTRL+D
|
|
|
|
// Stop here
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
tosend = c;
|
|
|
|
} else if (!tty) {
|
|
|
|
// This means we reached EOF
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2020-09-25 11:01:14 +10:00
|
|
|
if (tosend && !hasrx()) {
|
|
|
|
_write(tosend);
|
2020-01-01 14:03:48 +11:00
|
|
|
tosend = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (tty) {
|
|
|
|
printf("Done!\n");
|
|
|
|
tcsetattr(0, TCSADRAIN, &saved_term);
|
|
|
|
emul_printdebug();
|
|
|
|
}
|
2020-04-18 09:10:22 +10:00
|
|
|
if (sdc.fp) {
|
|
|
|
fclose(sdc.fp);
|
|
|
|
}
|
2020-01-01 14:03:48 +11:00
|
|
|
return 0;
|
|
|
|
}
|