mirror of
https://github.com/hsoft/collapseos.git
synced 2024-11-27 12:18:07 +11:00
emul: add TRS-80 emulator
Not complete yet, but has @KEY and @DSP, enough to get prompt.
This commit is contained in:
parent
b12ac4b672
commit
23885dac33
@ -3,6 +3,8 @@ TARGET = os.bin
|
|||||||
BASE = ../../..
|
BASE = ../../..
|
||||||
BLKPACK = $(BASE)/tools/blkpack
|
BLKPACK = $(BASE)/tools/blkpack
|
||||||
STAGE = $(BASE)/cvm/stage
|
STAGE = $(BASE)/cvm/stage
|
||||||
|
EDIR = $(BASE)/emul/z80
|
||||||
|
EMUL = $(EDIR)/trs80
|
||||||
|
|
||||||
.PHONY: all
|
.PHONY: all
|
||||||
all: $(TARGET)
|
all: $(TARGET)
|
||||||
@ -12,8 +14,15 @@ $(TARGET): xcomp.fs $(STAGE) blkfs
|
|||||||
$(BLKPACK):
|
$(BLKPACK):
|
||||||
$(MAKE) -C ../tools
|
$(MAKE) -C ../tools
|
||||||
|
|
||||||
blkfs: $(BLKPACK)
|
blkfs: blk.fs $(BLKPACK)
|
||||||
cat $(BASE)/blk.fs blk.fs | $(BLKPACK) > $@
|
cat $(BASE)/blk.fs blk.fs | $(BLKPACK) > $@
|
||||||
|
|
||||||
$(STAGE):
|
$(STAGE):
|
||||||
$(MAKE) -C $(BASE)/cvm stage
|
$(MAKE) -C $(BASE)/cvm stage
|
||||||
|
|
||||||
|
$(EMUL):
|
||||||
|
$(MAKE) -C $(EDIR)
|
||||||
|
|
||||||
|
.PHONY: emul
|
||||||
|
emul: $(EMUL) $(TARGET)
|
||||||
|
$(EMUL) $(TARGET)
|
||||||
|
1
emul/z80/.gitignore
vendored
1
emul/z80/.gitignore
vendored
@ -3,3 +3,4 @@
|
|||||||
/ti84
|
/ti84
|
||||||
/sms
|
/sms
|
||||||
/rc2014
|
/rc2014
|
||||||
|
/trs80
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
TARGETS = forth rc2014 sms ti84
|
TARGETS = forth rc2014 sms ti84 trs80
|
||||||
OBJS = emul.o z80.o
|
OBJS = emul.o z80.o
|
||||||
RC2014_OBJS = $(OBJS) sio.o acia.o sdc.o rc2014_spi.o
|
RC2014_OBJS = $(OBJS) sio.o acia.o sdc.o rc2014_spi.o
|
||||||
SMS_OBJS = $(OBJS) tms9918.o sms_vdp.o sms_ports.o sms_pad.o ps2_kbd.o sdc.o \
|
SMS_OBJS = $(OBJS) tms9918.o sms_vdp.o sms_ports.o sms_pad.o ps2_kbd.o sdc.o \
|
||||||
sms_spi.o
|
sms_spi.o
|
||||||
TI84_OBJS = $(OBJS) t6a04.o ti84_kbd.o
|
TI84_OBJS = $(OBJS) t6a04.o ti84_kbd.o
|
||||||
|
TRS80_OBJS = $(OBJS)
|
||||||
CDIR = ../../cvm
|
CDIR = ../../cvm
|
||||||
STAGE = $(CDIR)/stage
|
STAGE = $(CDIR)/stage
|
||||||
BLKFS = $(CDIR)/blkfs
|
BLKFS = $(CDIR)/blkfs
|
||||||
@ -23,6 +24,9 @@ sms: sms.c $(SMS_OBJS)
|
|||||||
ti84: ti84.c $(TI84_OBJS)
|
ti84: ti84.c $(TI84_OBJS)
|
||||||
$(CC) ti84.c $(TI84_OBJS) -o $@ `pkg-config --cflags --libs xcb`
|
$(CC) ti84.c $(TI84_OBJS) -o $@ `pkg-config --cflags --libs xcb`
|
||||||
|
|
||||||
|
trs80: trs80.c $(TRS80_OBJS)
|
||||||
|
$(CC) trs80.c $(TRS80_OBJS) -lcurses -o $@
|
||||||
|
|
||||||
emul.o: emul.c forth.bin $(BLKFS)
|
emul.o: emul.c forth.bin $(BLKFS)
|
||||||
$(CC) -DFBIN_PATH=\"`pwd`/forth.bin\" -DBLKFS_PATH=\"`pwd`/$(BLKFS)\" -c -o emul.o emul.c
|
$(CC) -DFBIN_PATH=\"`pwd`/forth.bin\" -DBLKFS_PATH=\"`pwd`/$(BLKFS)\" -c -o emul.o emul.c
|
||||||
|
|
||||||
|
@ -82,7 +82,7 @@ static void mem_write(int unused, uint16_t addr, uint8_t val)
|
|||||||
m.mem[addr] = val;
|
m.mem[addr] = val;
|
||||||
}
|
}
|
||||||
|
|
||||||
Machine* emul_init()
|
Machine* emul_init(char *binpath, ushort binoffset)
|
||||||
{
|
{
|
||||||
fprintf(stderr, "Using blkfs %s\n", BLKFS_PATH);
|
fprintf(stderr, "Using blkfs %s\n", BLKFS_PATH);
|
||||||
blkfp = fopen(BLKFS_PATH, "r+");
|
blkfp = fopen(BLKFS_PATH, "r+");
|
||||||
@ -99,14 +99,24 @@ Machine* emul_init()
|
|||||||
fseek(blkfp, 0, SEEK_SET);
|
fseek(blkfp, 0, SEEK_SET);
|
||||||
// initialize memory
|
// initialize memory
|
||||||
memset(m.mem, 0, 0x10000);
|
memset(m.mem, 0, 0x10000);
|
||||||
FILE *bfp = fopen(FBIN_PATH, "r");
|
if (binpath == NULL) {
|
||||||
|
binpath = FBIN_PATH;
|
||||||
|
}
|
||||||
|
FILE *bfp = fopen(binpath, "r");
|
||||||
if (!bfp) {
|
if (!bfp) {
|
||||||
fprintf(stderr, "Can't open forth.bin\n");
|
fprintf(stderr, "Can't open %s\n", binpath);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
int i = 0;
|
int i = binoffset;
|
||||||
int c = getc(bfp);
|
int c = getc(bfp);
|
||||||
while (c != EOF) {
|
while (c != EOF) {
|
||||||
|
#ifdef MAX_ROMSIZE
|
||||||
|
if (i >= MAX_ROMSIZE) {
|
||||||
|
fprintf(stderr, "ROM image too large.\n");
|
||||||
|
fclose(fp);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
m.mem[i++] = c;
|
m.mem[i++] = c;
|
||||||
c = getc(bfp);
|
c = getc(bfp);
|
||||||
}
|
}
|
||||||
@ -118,6 +128,7 @@ Machine* emul_init()
|
|||||||
m.iord[i] = NULL;
|
m.iord[i] = NULL;
|
||||||
m.iowr[i] = NULL;
|
m.iowr[i] = NULL;
|
||||||
}
|
}
|
||||||
|
m.pchooks_cnt = 0;
|
||||||
Z80RESET(&m.cpu);
|
Z80RESET(&m.cpu);
|
||||||
m.cpu.memRead = mem_read;
|
m.cpu.memRead = mem_read;
|
||||||
m.cpu.memWrite = mem_write;
|
m.cpu.memWrite = mem_write;
|
||||||
@ -143,6 +154,12 @@ bool emul_step()
|
|||||||
if (m.cpu.R1.wr.IX > m.maxix) {
|
if (m.cpu.R1.wr.IX > m.maxix) {
|
||||||
m.maxix = m.cpu.R1.wr.IX;
|
m.maxix = m.cpu.R1.wr.IX;
|
||||||
}
|
}
|
||||||
|
for (int i=0; i<m.pchooks_cnt; i++) {
|
||||||
|
if (m.cpu.PC == m.pchooks[i]) {
|
||||||
|
m.pchookfunc(&m);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
|
@ -3,11 +3,13 @@
|
|||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include "z80.h"
|
#include "z80.h"
|
||||||
|
|
||||||
|
#define MAX_PCHOOK_COUNT 8
|
||||||
|
|
||||||
typedef byte (*IORD) ();
|
typedef byte (*IORD) ();
|
||||||
typedef void (*IOWR) (byte data);
|
typedef void (*IOWR) (byte data);
|
||||||
typedef byte (*EXCH) (byte data);
|
typedef byte (*EXCH) (byte data);
|
||||||
|
|
||||||
typedef struct {
|
typedef struct _Machine {
|
||||||
Z80Context cpu;
|
Z80Context cpu;
|
||||||
byte mem[0x10000];
|
byte mem[0x10000];
|
||||||
// Set to non-zero to specify where ROM ends. Any memory write attempt
|
// Set to non-zero to specify where ROM ends. Any memory write attempt
|
||||||
@ -21,6 +23,11 @@ typedef struct {
|
|||||||
// NULL when IO port is unhandled.
|
// NULL when IO port is unhandled.
|
||||||
IORD iord[0x100];
|
IORD iord[0x100];
|
||||||
IOWR iowr[0x100];
|
IOWR iowr[0x100];
|
||||||
|
// function to call when PC falls in one of the hooks
|
||||||
|
void (*pchookfunc) (struct _Machine *m);
|
||||||
|
// List of PC values at which we want to call pchookfunc
|
||||||
|
ushort pchooks[MAX_PCHOOK_COUNT];
|
||||||
|
byte pchooks_cnt;
|
||||||
} Machine;
|
} Machine;
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
@ -29,7 +36,7 @@ typedef enum {
|
|||||||
TRI_HIGHZ
|
TRI_HIGHZ
|
||||||
} Tristate;
|
} Tristate;
|
||||||
|
|
||||||
Machine* emul_init();
|
Machine* emul_init(char *binpath, ushort binoffset);
|
||||||
void emul_deinit();
|
void emul_deinit();
|
||||||
bool emul_step();
|
bool emul_step();
|
||||||
bool emul_steps(unsigned int steps);
|
bool emul_steps(unsigned int steps);
|
||||||
|
@ -76,7 +76,7 @@ static void iowr_sety(uint8_t val)
|
|||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
Machine *m = emul_init();
|
Machine *m = emul_init(NULL, 0);
|
||||||
if (m == NULL) {
|
if (m == NULL) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <termios.h>
|
#include <termios.h>
|
||||||
|
#define MAX_ROMSIZE 0x2000
|
||||||
#include "emul.h"
|
#include "emul.h"
|
||||||
#include "acia.h"
|
#include "acia.h"
|
||||||
#include "sio.h"
|
#include "sio.h"
|
||||||
@ -24,7 +25,6 @@
|
|||||||
#define SIO_ADATA_PORT 0x81
|
#define SIO_ADATA_PORT 0x81
|
||||||
#define SDC_CTL 0x05
|
#define SDC_CTL 0x05
|
||||||
#define SDC_SPI 0x04
|
#define SDC_SPI 0x04
|
||||||
#define MAX_ROMSIZE 0x2000
|
|
||||||
|
|
||||||
bool use_sio = false;
|
bool use_sio = false;
|
||||||
static ACIA acia;
|
static ACIA acia;
|
||||||
@ -123,7 +123,7 @@ static void _write(uint8_t val)
|
|||||||
|
|
||||||
static void usage()
|
static void usage()
|
||||||
{
|
{
|
||||||
fprintf(stderr, "Usage: ./classic [-s] [-c sdcard.img] /path/to/rom\n");
|
fprintf(stderr, "Usage: ./rc2014 [-s] [-c sdcard.img] /path/to/rom\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
@ -159,23 +159,9 @@ int main(int argc, char *argv[])
|
|||||||
usage();
|
usage();
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
fp = fopen(argv[optind], "r");
|
Machine *m = emul_init(argv[optind], 0);
|
||||||
if (fp == NULL) {
|
if (m == NULL) return 1;
|
||||||
fprintf(stderr, "Can't open %s\n", argv[1]);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
Machine *m = emul_init();
|
|
||||||
m->ramstart = RAMSTART;
|
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));
|
bool tty = isatty(fileno(stdin));
|
||||||
struct termios term, saved_term;
|
struct termios term, saved_term;
|
||||||
if (tty) {
|
if (tty) {
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
#define XK_MISCELLANY
|
#define XK_MISCELLANY
|
||||||
#include <X11/keysymdef.h>
|
#include <X11/keysymdef.h>
|
||||||
|
|
||||||
|
#define MAX_ROMSIZE 0x8000
|
||||||
#include "emul.h"
|
#include "emul.h"
|
||||||
#include "sms_vdp.h"
|
#include "sms_vdp.h"
|
||||||
#include "sms_ports.h"
|
#include "sms_ports.h"
|
||||||
@ -23,7 +24,6 @@
|
|||||||
#define PORTS_IO2_PORT 0xdd
|
#define PORTS_IO2_PORT 0xdd
|
||||||
#define SDC_CTL 0x05
|
#define SDC_CTL 0x05
|
||||||
#define SDC_SPI 0x04
|
#define SDC_SPI 0x04
|
||||||
#define MAX_ROMSIZE 0x8000
|
|
||||||
|
|
||||||
static xcb_connection_t *conn;
|
static xcb_connection_t *conn;
|
||||||
static xcb_screen_t *screen;
|
static xcb_screen_t *screen;
|
||||||
@ -334,18 +334,9 @@ int main(int argc, char *argv[])
|
|||||||
fprintf(stderr, "Can't open %s\n", argv[1]);
|
fprintf(stderr, "Can't open %s\n", argv[1]);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
m = emul_init();
|
m = emul_init(argv[optind], 0);
|
||||||
|
if (m == NULL) return 1;
|
||||||
m->ramstart = RAMSTART;
|
m->ramstart = RAMSTART;
|
||||||
int i = 0;
|
|
||||||
int c;
|
|
||||||
while ((c = fgetc(fp)) != EOF && i < MAX_ROMSIZE) {
|
|
||||||
m->mem[i++] = c & 0xff;
|
|
||||||
}
|
|
||||||
pclose(fp);
|
|
||||||
if (c != EOF) {
|
|
||||||
fprintf(stderr, "ROM image too large.\n");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
if (use_kbd) {
|
if (use_kbd) {
|
||||||
ports.portA_rd = iord_kbd;
|
ports.portA_rd = iord_kbd;
|
||||||
} else {
|
} else {
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
#define XK_MISCELLANY
|
#define XK_MISCELLANY
|
||||||
#include <X11/keysymdef.h>
|
#include <X11/keysymdef.h>
|
||||||
|
|
||||||
|
#define MAX_ROMSIZE 0x4000
|
||||||
#include "emul.h"
|
#include "emul.h"
|
||||||
#include "t6a04.h"
|
#include "t6a04.h"
|
||||||
#include "ti84_kbd.h"
|
#include "ti84_kbd.h"
|
||||||
@ -23,7 +24,6 @@
|
|||||||
#define INTERRUPT_PORT 0x03
|
#define INTERRUPT_PORT 0x03
|
||||||
#define LCD_CMD_PORT 0x10
|
#define LCD_CMD_PORT 0x10
|
||||||
#define LCD_DATA_PORT 0x11
|
#define LCD_DATA_PORT 0x11
|
||||||
#define MAX_ROMSIZE 0x4000
|
|
||||||
|
|
||||||
static xcb_connection_t *conn;
|
static xcb_connection_t *conn;
|
||||||
static xcb_screen_t *screen;
|
static xcb_screen_t *screen;
|
||||||
@ -288,23 +288,9 @@ int main(int argc, char *argv[])
|
|||||||
fprintf(stderr, "Usage: ./ti84 /path/to/rom\n");
|
fprintf(stderr, "Usage: ./ti84 /path/to/rom\n");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
FILE *fp = fopen(argv[1], "r");
|
m = emul_init(argv[1], 0);
|
||||||
if (fp == NULL) {
|
if (m == NULL) return 1;
|
||||||
fprintf(stderr, "Can't open %s\n", argv[1]);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
m = emul_init();
|
|
||||||
m->ramstart = RAMSTART;
|
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;
|
|
||||||
}
|
|
||||||
t6a04_init(&lcd);
|
t6a04_init(&lcd);
|
||||||
kbd_init(&kbd);
|
kbd_init(&kbd);
|
||||||
lcd_changed = false;
|
lcd_changed = false;
|
||||||
|
103
emul/z80/trs80.c
Normal file
103
emul/z80/trs80.c
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <curses.h>
|
||||||
|
#include <termios.h>
|
||||||
|
#include "emul.h"
|
||||||
|
|
||||||
|
#define WCOLS 80
|
||||||
|
#define WLINES 24
|
||||||
|
#define RAMSTART 0
|
||||||
|
#define BINSTART 0x3000
|
||||||
|
|
||||||
|
WINDOW *bw, *dw, *w;
|
||||||
|
|
||||||
|
void debug_panel()
|
||||||
|
{
|
||||||
|
char buf[30];
|
||||||
|
emul_debugstr(buf);
|
||||||
|
mvwaddnstr(dw, 0, 0, buf, 30);
|
||||||
|
wrefresh(dw);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void pchookfunc(Machine *m)
|
||||||
|
{
|
||||||
|
byte val;
|
||||||
|
switch (m->cpu.R1.br.A) {
|
||||||
|
case 0x01: // @KEY
|
||||||
|
debug_panel();
|
||||||
|
m->cpu.R1.br.A = wgetch(w);
|
||||||
|
break;
|
||||||
|
case 0x02: // @DSP
|
||||||
|
val = m->cpu.R1.br.C;
|
||||||
|
if (val == '\r') {
|
||||||
|
val = '\n';
|
||||||
|
}
|
||||||
|
if (val >= 0x20 || val == '\n') {
|
||||||
|
wechochar(w, val);
|
||||||
|
} else if (val == 0x08) {
|
||||||
|
int y, x; getyx(w, y, x);
|
||||||
|
wmove(w, y, x-1);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 0x03: // @GET
|
||||||
|
break;
|
||||||
|
case 0x04: // @PUT
|
||||||
|
break;
|
||||||
|
case 0x0f: // @VDCTL
|
||||||
|
wmove(w, m->cpu.R1.br.H, m->cpu.R1.br.L);
|
||||||
|
break;
|
||||||
|
case 0x16: // @EXIT
|
||||||
|
break;
|
||||||
|
case 0x1a: // @ERROR
|
||||||
|
break;
|
||||||
|
case 0x28: // @DCSTAT
|
||||||
|
break;
|
||||||
|
case 0x31: // @RDSEC
|
||||||
|
break;
|
||||||
|
case 0x35: // @WRSEC
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
fprintf(stderr, "Unhandled RST28: %x\n", m->cpu.R1.br.A);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void usage()
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Usage: ./trs80 /path/to/rom\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
if (argc < 2) {
|
||||||
|
usage();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
Machine *m = emul_init(argv[1], BINSTART);
|
||||||
|
if (m == NULL) return 1;
|
||||||
|
m->ramstart = RAMSTART;
|
||||||
|
m->pchookfunc = pchookfunc;
|
||||||
|
m->pchooks_cnt = 1;
|
||||||
|
m->pchooks[0] = 0x28; // RST 28
|
||||||
|
// Place a RET at 0x28 so that it properly returns after pchookfunc().
|
||||||
|
m->mem[0x28] = 0xc9; // RET
|
||||||
|
m->cpu.PC = BINSTART;
|
||||||
|
initscr(); cbreak(); noecho(); nl(); clear();
|
||||||
|
// border window
|
||||||
|
bw = newwin(WLINES+2, WCOLS+2, 0, 0);
|
||||||
|
wborder(bw, 0, 0, 0, 0, 0, 0, 0, 0);
|
||||||
|
wrefresh(bw);
|
||||||
|
// debug panel
|
||||||
|
dw = newwin(1, 30, LINES-1, COLS-30);
|
||||||
|
w = newwin(WLINES, WCOLS, 1, 1);
|
||||||
|
scrollok(w, 1);
|
||||||
|
while (emul_steps(1000)) {
|
||||||
|
debug_panel();
|
||||||
|
}
|
||||||
|
nocbreak(); echo(); delwin(w); delwin(bw); delwin(dw); endwin();
|
||||||
|
printf("\nDone!\n");
|
||||||
|
emul_printdebug();
|
||||||
|
printf("PC: %x\n", m->cpu.PC);
|
||||||
|
emul_deinit();
|
||||||
|
return 0;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user