Not complete yet, but has @KEY and @DSP, enough to get prompt.master
@@ -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) |
@@ -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"); | |||||
if (fp == NULL) { | |||||
fprintf(stderr, "Can't open %s\n", argv[1]); | |||||
return 1; | |||||
} | |||||
Machine *m = emul_init(); | |||||
Machine *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 (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"); | |||||
if (fp == NULL) { | |||||
fprintf(stderr, "Can't open %s\n", argv[1]); | |||||
return 1; | |||||
} | |||||
m = emul_init(); | |||||
m = emul_init(argv[1], 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 (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; | ||||
@@ -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; | |||||
} |