From 74736011728099ac926a344f5f6a78938e96a890 Mon Sep 17 00:00:00 2001 From: David Given Date: Thu, 7 Feb 2019 22:39:57 +0100 Subject: [PATCH 1/3] Add the cpm emulator from Cowgol, modified to work with an 8080 emulator rather than libz80ex; enable tests for cpm. --- plat/cpm/emu/bdos.s | 116 ++++ plat/cpm/emu/biosbdos.c | 451 +++++++++++++++ plat/cpm/emu/build.lua | 44 ++ plat/cpm/emu/emulator.c | 378 +++++++++++++ plat/cpm/emu/fileio.c | 440 +++++++++++++++ plat/cpm/emu/globals.h | 51 ++ plat/cpm/emu/intel_8080_emulator.c | 874 +++++++++++++++++++++++++++++ plat/cpm/emu/intel_8080_emulator.h | 25 + plat/cpm/emu/main.c | 83 +++ plat/cpm/tests/build.lua | 7 + util/cmisc/build.lua | 5 + 11 files changed, 2474 insertions(+) create mode 100644 plat/cpm/emu/bdos.s create mode 100644 plat/cpm/emu/biosbdos.c create mode 100644 plat/cpm/emu/build.lua create mode 100644 plat/cpm/emu/emulator.c create mode 100644 plat/cpm/emu/fileio.c create mode 100644 plat/cpm/emu/globals.h create mode 100644 plat/cpm/emu/intel_8080_emulator.c create mode 100644 plat/cpm/emu/intel_8080_emulator.h create mode 100644 plat/cpm/emu/main.c create mode 100644 plat/cpm/tests/build.lua diff --git a/plat/cpm/emu/bdos.s b/plat/cpm/emu/bdos.s new file mode 100644 index 000000000..a2a59487f --- /dev/null +++ b/plat/cpm/emu/bdos.s @@ -0,0 +1,116 @@ +# +.sect .text +.sect .rom +.sect .data +.sect .bss + +.sect .text + +bdos: ! BDOS entry point + out 0xff + ora a + ret + +COLDSTART: ! system startup entry point --- this needs to be four bytes after FBASE. + jmp boot ! 0: Cold start routine +bios: + jmp wboot ! 1: Warm boot - reload command processor + jmp const ! 2: Console status + jmp conin ! 3: Console input + jmp conout ! 4: Console output + jmp list ! 5: Printer output + jmp punch ! 6: Paper tape punch output + jmp reader ! 7: Paper tape reader input + jmp home ! 8: Move disc head to track 0 + jmp seldsk ! 9: Select disc drive + jmp settrk !10: Set track number + jmp setsec !11: Set sector number + jmp setdma !12: Set DMA address + jmp read !13: Read a sector + jmp write !14: Write a sector + +boot: + xra a + sta 3 ! iobyte + sta 4 ! drive + ! falls through +wboot: + mvi a, 0xc3 ! jmp + sta 0 + sta 5 + + lxi h, bios + shld 1 + + lxi h, bdos + shld 6 + + lda 4 ! get the current drive/user + mov c, a + out 1 + +const: + out 2 + ora a + ret + +conin: + out 3 + ora a + ret + +conout: + out 4 + ora a + ret + +list: + out 5 + ora a + ret + +punch: + out 6 + ora a + ret + +reader: + out 7 + ora a + ret + +home: + out 8 + ora a + ret + +seldsk: + out 9 + ora a + ret + +settrk: + out 10 + ora a + ret + +setsec: + out 11 + ora a + ret + +setdma: + out 12 + ora a + ret + +read: + out 13 + ora a + ret + +write: + out 14 + ora a + ret + diff --git a/plat/cpm/emu/biosbdos.c b/plat/cpm/emu/biosbdos.c new file mode 100644 index 000000000..0e0a4890e --- /dev/null +++ b/plat/cpm/emu/biosbdos.c @@ -0,0 +1,451 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "intel_8080_emulator.h" +#include "globals.h" + +#define FBASE 0xff00 +#define COLDSTART (FBASE + 4) /* see bdos.asm */ +#define CBASE (FBASE - (7*1024)) + +static uint16_t dma; +static int exitcode = 0; + +struct fcb +{ + cpm_filename_t filename; /* includes drive */ + uint8_t extent; + uint8_t s1; + uint8_t s2; + uint8_t recordcount; + uint8_t d[16]; + uint8_t currentrecord; + uint8_t r[3]; +}; + +static void bios_getchar(void); + +static uint16_t get_de(void) +{ + return i8080_read_reg16(DE); +} + +static uint8_t get_c(void) +{ + return i8080_read_reg8(C); +} + +static uint8_t get_d(void) +{ + return i8080_read_reg8(D); +} + +static uint8_t get_e(void) +{ + return i8080_read_reg8(E); +} + +static uint8_t get_a(void) +{ + return i8080_read_reg8(A); +} + +static void set_a(uint8_t a) +{ + i8080_write_reg8(A, a); +} + +static void set_result(uint16_t result) +{ + i8080_write_reg16(HL, result); + + i8080_write_reg8(A, result); + uint8_t f = i8080_read_reg8(FLAGS); + f &= ~(1<<6) & ~(1<<7); + if (!result) + f |= 1<<6; + if (result & 0x80) + f |= 1<<7; + i8080_write_reg8(FLAGS, f); + + i8080_write_reg8(B, result); +} + +void bios_coldboot(void) +{ + memcpy(&ram[FBASE], bdos_data, bdos_len); + i8080_write_reg16(PC, COLDSTART); +} + +static void bios_warmboot(void) +{ + dma = 0x0080; + + if (!user_command_line[0]) + fatal("running the CCP isn't supported"); + + static bool terminate_next_time = false; + if (terminate_next_time) + exit(exitcode); + terminate_next_time = true; + + i8080_write_reg16(PC, 0x0100); + + /* Push the magic exit code onto the stack. */ + i8080_write_reg16(SP, FBASE-4); + ram[FBASE-4] = (FBASE-2) & 0xFF; + ram[FBASE-3] = (FBASE-2) >> 8; + ram[FBASE-2] = 0xD3; // out (??), a + ram[FBASE-1] = 0xFE; // exit emulator + + int fd = open(user_command_line[0], O_RDONLY); + if (fd == -1) + fatal("couldn't open program: %s", strerror(errno)); + read(fd, &ram[0x0100], 0xFE00); + close(fd); + + int offset = 1; + for (int word = 1; user_command_line[word]; word++) + { + if (word > 1) + { + ram[0x0080 + offset] = ' '; + offset++; + } + + const char* pin = user_command_line[word]; + while (*pin) + { + if (offset > 125) + fatal("user command line too long"); + ram[0x0080 + offset] = toupper(*pin++); + offset++; + } + } + ram[0x0080] = offset; + ram[0x0080+offset] = 0; +} + +static void bios_const(void) +{ + struct pollfd pollfd = { 0, POLLIN, 0 }; + poll(&pollfd, 1, 0); + set_a((pollfd.revents & POLLIN) ? 0xff : 0); +} + +static void bios_getchar(void) +{ + char c = 0; + (void) read(0, &c, 1); + if (c == '\n') + c = '\r'; + set_a(c); +} + +static void bios_putchar(void) +{ + char c = get_c(); + (void) write(1, &c, 1); +} + +static void bios_entry(uint8_t bios_call) +{ + switch (bios_call) + { + case 0: bios_coldboot(); return; + case 1: bios_warmboot(); return; + case 2: bios_const(); return; // const + case 3: bios_getchar(); return; // conin + case 4: bios_putchar(); return; // conout + + case 0xFE: exit(0); // magic emulator exit + } + + showregs(); + fatal("unimplemented bios entry %d", bios_call); +} + +static void bdos_getchar(void) +{ + bios_getchar(); + set_result(get_a()); +} + +static void bdos_putchar(void) +{ + uint8_t c = get_e(); + (void) write(1, &c, 1); +} + +static void bdos_consoleio(void) +{ + uint8_t c = get_e(); + if (c == 0xff) + { + bios_const(); + if (get_a() == 0xff) + bios_getchar(); + } + else + bdos_putchar(); +} + +static void bdos_printstring(void) +{ + uint16_t de = get_de(); + for (;;) + { + uint8_t c = ram[de++]; + if (c == '$') + break; + (void) write(1, &c, 1); + } +} + +static void bdos_consolestatus(void) +{ + bios_const(); + set_result(get_a()); +} + +void bdos_readline(void) +{ + fflush(stdout); + + uint16_t de = i8080_read_reg16(DE); + uint8_t maxcount = ram[de+0]; + int count = read(0, &ram[de+2], maxcount); + if ((count > 0) && (ram[de+2+count-1] == '\n')) + count--; + ram[de+1] = count; + set_result(count); +} + +static struct fcb* fcb_at(uint16_t address) +{ + struct fcb* fcb = (struct fcb*) &ram[address]; + + /* Autoselect the current drive. */ + if (fcb->filename.drive == 0) + fcb->filename.drive = ram[4] + 1; + + return fcb; +} + +static struct fcb* find_fcb(void) +{ + return fcb_at(i8080_read_reg16(DE)); +} + +static int get_current_record(struct fcb* fcb) +{ + return (fcb->extent * 128) + fcb->currentrecord; +} + +static void set_current_record(struct fcb* fcb, int record, int total) +{ + int extents = total / 128; + fcb->extent = record / 128; + if (fcb->extent < extents) + fcb->recordcount = 128; + else + fcb->recordcount = total % 128; + fcb->currentrecord = record % 128; +} + +static void bdos_resetdisk(void) +{ + dma = 0x0080; + ram[4] = 0; /* select drive A */ + set_result(0xff); +} + +static void bdos_selectdisk(void) +{ + uint8_t e = get_e(); + ram[4] = e; +} + +static void bdos_getdisk(void) +{ + set_result(ram[4]); +} + +static void bdos_openfile(void) +{ + struct fcb* fcb = find_fcb(); + struct file* f = file_open(&fcb->filename); + if (f) + { + set_current_record(fcb, 0, file_getrecordcount(f)); + set_result(0); + } + else + set_result(0xff); +} + +static void bdos_makefile(void) +{ + struct fcb* fcb = find_fcb(); + struct file* f = file_create(&fcb->filename); + if (f) + { + set_current_record(fcb, 0, 0); + set_result(0); + } + else + set_result(0xff); +} + +static void bdos_closefile(void) +{ + struct fcb* fcb = find_fcb(); + struct file* f = file_open(&fcb->filename); + if (file_getrecordcount(f) < 128) + file_setrecordcount(f, fcb->recordcount); + int result = file_close(&fcb->filename); + set_result(result ? 0xff : 0); +} + +static void bdos_renamefile(void) +{ + struct fcb* srcfcb = fcb_at(i8080_read_reg16(DE)); + struct fcb* destfcb = fcb_at(i8080_read_reg16(DE)+16); + int result = file_rename(&srcfcb->filename, &destfcb->filename); + set_result(result ? 0xff : 0); +} + +static void bdos_findnext(void) +{ + struct fcb* fcb = (struct fcb*) &ram[dma]; + memset(fcb, 0, sizeof(struct fcb)); + int i = file_findnext(&fcb->filename); + set_result(i ? 0xff : 0); +} + +static void bdos_findfirst(void) +{ + struct fcb* fcb = find_fcb(); + int i = file_findfirst(&fcb->filename); + if (i == 0) + bdos_findnext(); + else + set_result(i ? 0xff : 0); +} + +static void bdos_deletefile(void) +{ + struct fcb* fcb = find_fcb(); + int i = file_delete(&fcb->filename); + set_result(i ? 0xff : 0); +} + +typedef int readwrite_cb(struct file* f, uint8_t* ptr, uint16_t record); + +static void bdos_readwritesequential(readwrite_cb* readwrite) +{ + struct fcb* fcb = find_fcb(); + + struct file* f = file_open(&fcb->filename); + int here = get_current_record(fcb); + int i = readwrite(f, &ram[dma], here); + set_current_record(fcb, here+1, file_getrecordcount(f)); + if (i == -1) + set_result(0xff); + else if (i == 0) + set_result(1); + else + set_result(0); +} + +static void bdos_readwriterandom(readwrite_cb* readwrite) +{ + struct fcb* fcb = find_fcb(); + + uint16_t record = fcb->r[0] + (fcb->r[1]<<8); + struct file* f = file_open(&fcb->filename); + int i = readwrite(f, &ram[dma], record); + set_current_record(fcb, record, file_getrecordcount(f)); + if (i == -1) + set_result(0xff); + else if (i == 0) + set_result(1); + else + set_result(0); +} + +static void bdos_filelength(void) +{ + struct fcb* fcb = find_fcb(); + struct file* f = file_open(&fcb->filename); + + int length = file_getrecordcount(f); + fcb->r[0] = length; + fcb->r[1] = length>>8; + fcb->r[2] = length>>16; +} + +static void bdos_getsetuser(void) +{ + if (get_e() == 0xff) + set_result(0); +} + +static void bdos_entry(uint8_t bdos_call) +{ + switch (bdos_call) + { + case 1: bdos_getchar(); return; + case 2: bdos_putchar(); return; + case 6: bdos_consoleio(); return; + case 9: bdos_printstring(); return; + case 10: bdos_readline(); return; + case 11: bdos_consolestatus(); return; + case 12: set_result(0x0022); return; // get CP/M version + case 13: bdos_resetdisk(); return; // reset disk system + case 14: bdos_selectdisk(); return; // select disk + case 15: bdos_openfile(); return; + case 16: bdos_closefile(); return; + case 17: bdos_findfirst(); return; + case 18: bdos_findnext(); return; + case 19: bdos_deletefile(); return; + case 20: bdos_readwritesequential(file_read); return; + case 21: bdos_readwritesequential(file_write); return; + case 22: bdos_makefile(); return; + case 23: bdos_renamefile(); return; + case 24: set_result(0xffff); return; // get login vector + case 25: bdos_getdisk(); return; // get current disk + case 26: dma = get_de(); return; // set DMA + case 27: set_result(0); return; // get allocation vector + case 29: set_result(0x0000); return; // get read-only vector + case 31: set_result(0); return; // get disk parameter block + case 32: bdos_getsetuser(); return; + case 33: bdos_readwriterandom(file_read); return; + case 34: bdos_readwriterandom(file_write); return; + case 35: bdos_filelength(); return; + case 40: bdos_readwriterandom(file_write); return; + case 45: return; // set hardware error action + case 108: exitcode = get_d(); return; // set exit code + } + + showregs(); + fatal("unimplemented bdos entry %d", bdos_call); +} + +void biosbdos_entry(int syscall) +{ + if (syscall == 0xff) + bdos_entry(i8080_read_reg16(BC)); + else + bios_entry(syscall); +} + diff --git a/plat/cpm/emu/build.lua b/plat/cpm/emu/build.lua new file mode 100644 index 000000000..ce831aa45 --- /dev/null +++ b/plat/cpm/emu/build.lua @@ -0,0 +1,44 @@ +ackfile { + name = "bdos_o", + srcs = { "./*.s" }, + vars = { plat = "cpm" }, +} + +normalrule { + name = "bdos_out", + ins = { + "util/led+led", + "+bdos_o", + }, + outleaves = { "bdos.out" }, + commands = { "%{ins[1]} -b0:0xff00 %{ins[2]} -o %{outs[1]}" } +} + +normalrule { + name = "bdos_img", + ins = { + "util/amisc+aslod", + "+bdos_out", + }, + outleaves = { "bdos.img" }, + commands = { "%{ins[1]} %{ins[2]} %{outs[1]}" } +} + +normalrule { + name = "bdos_c", + ins = { + "util/cmisc+objectify", + "+bdos_img", + }, + outleaves = { "bdos.c" }, + commands = { "%{ins[1]} bdos < %{ins[2]} > %{outs[1]}" } +} + +cprogram { + name = "emu", + srcs = { + "+bdos_c", + "./*.c" + }, +} + diff --git a/plat/cpm/emu/emulator.c b/plat/cpm/emu/emulator.c new file mode 100644 index 000000000..0bed46532 --- /dev/null +++ b/plat/cpm/emu/emulator.c @@ -0,0 +1,378 @@ +#define _POSIX_C_SOURCE 199309 +#include +#include +#include +#include +#include +#include "intel_8080_emulator.h" +#include "globals.h" + +uint8_t ram[0x10000]; + +struct watchpoint +{ + uint16_t address; + uint8_t value; + bool enabled; +}; + +static uint16_t breakpoints[16]; +static struct watchpoint watchpoints[16]; +static bool tracing = false; +static bool singlestepping = true; +static bool bdosbreak = false; + +uint8_t i8080_read(uint16_t addr) +{ + return ram[addr]; +} + +void i8080_write(uint16_t addr, uint8_t value) +{ + ram[addr] = value; +} + +uint8_t i8080_inport(uint8_t addr) +{ + return 0; +} + +void i8080_outport(uint8_t addr, uint8_t value) +{ + biosbdos_entry(addr & 0xff); + if (bdosbreak) + singlestepping = true; +} + +void showregs(void) +{ + uint16_t af = i8080_read_reg16(AF); + printf("%c%c.%c.%c%c%c sp=%04x af=%04x bc=%04x de=%04x hl=%04x\n", + (af & 0x80) ? 'S' : 's', + (af & 0x40) ? 'Z' : 'z', + (af & 0x10) ? 'H' : 'h', + (af & 0x04) ? 'P' : 'p', + (af & 0x02) ? 'N' : 'n', + (af & 0x01) ? 'C' : 'c', + i8080_read_reg16(SP), + af, + i8080_read_reg16(BC), + i8080_read_reg16(DE), + i8080_read_reg16(HL)); + + char buffer[80]; + int tstates; + uint16_t pc = i8080_read_reg16(PC); + //z80ex_dasm(buffer, sizeof(buffer), 0, &tstates, &tstates, dasm_read_cb, pc, NULL); + strcpy(buffer, ""); + printf("%04x : %s\n", pc, buffer); +} + +static void cmd_register(void) +{ + char* w1 = strtok(NULL, " "); + char* w2 = strtok(NULL, " "); + + if (w1 && w2) + { + Z80_REG_T reg = -1; + if (strcmp(w1, "sp") == 0) + reg = SP; + else if (strcmp(w1, "pc") == 0) + reg = PC; + else if (strcmp(w1, "af") == 0) + reg = AF; + else if (strcmp(w1, "bc") == 0) + reg = BC; + else if (strcmp(w1, "de") == 0) + reg = DE; + else if (strcmp(w1, "hl") == 0) + reg = HL; + else + { + printf("Bad register\n"); + return; + } + + i8080_write_reg16(reg, strtoul(w2, NULL, 16)); + } + + showregs(); +} + +static void cmd_break(void) +{ + char* w1 = strtok(NULL, " "); + if (w1) + { + uint16_t breakpc = strtoul(w1, NULL, 16); + for (int i=0; ienabled) + { + w->address = watchaddr; + w->enabled = true; + w->value = ram[watchaddr]; + return; + } + } + printf("Too many breakpoints\n"); + } + else + { + for (int i=0; ienabled) + printf("%04x (current value: %02x)\n", w->address, w->value); + } + } +} + +static void cmd_delete_breakpoint(void) +{ + char* w1 = strtok(NULL, " "); + if (w1) + { + uint16_t breakpc = strtoul(w1, NULL, 16); + for (int i=0; ienabled && (w->address == address)) + { + w->enabled = false; + return; + } + } + printf("No such watchpoint\n"); + } +} + +static void cmd_memory(void) +{ + char* w1 = strtok(NULL, " "); + char* w2 = strtok(NULL, " "); + + if (!w2) + w2 = "100"; + + if (w1 && w2) + { + uint16_t startaddr = strtoul(w1, NULL, 16); + uint16_t endaddr = startaddr + strtoul(w2, NULL, 16); + uint16_t startrounded = startaddr & ~0xf; + uint16_t endrounded = (endaddr + 0xf) & ~0xf; + + uint16_t p = startrounded; + + while (p < endrounded) + { + printf("%04x : ", p); + for (int i = 0; i < 16; i++) + { + uint16_t pp = p + i; + if ((pp >= startaddr) && (pp < endaddr)) + printf("%02x ", ram[pp]); + else + printf(" "); + } + printf(": "); + for (int i = 0; i < 16; i++) + { + uint16_t pp = p + i; + if ((pp >= startaddr) && (pp < endaddr)) + { + uint8_t c = ram[pp]; + if ((c < 32) || (c > 127)) + c = '.'; + putchar(c); + } + else + putchar(' '); + } + p += 16; + putchar('\n'); + } + } +} + +static void cmd_bdos(void) +{ + char* w1 = strtok(NULL, " "); + if (w1) + bdosbreak = !!strtoul(w1, NULL, 16); + else + printf("break on bdos entry: %s\n", bdosbreak ? "on" : "off"); +} + +static void cmd_tracing(void) +{ + char* w1 = strtok(NULL, " "); + if (w1) + tracing = !!strtoul(w1, NULL, 16); + else + printf("tracing: %s\n", tracing ? "on" : "off"); +} + +static void cmd_help(void) +{ + printf("Sleazy debugger\n" + " r show registers\n" + " r set register\n" + " b show breakpoints\n" + " b set breakpoint\n" + " db delete breakpoint\n" + " w set watchpoint\n" + " dw delete watchpoint\n" + " m show memory\n" + " s single step\n" + " g continue\n" + " bdos 0|1 enable break on bdos entry\n" + " trace 0|1 enable tracing\n" + ); +} + +static void debug(void) +{ + bool go = false; + showregs(); + while (!go) + { + char cmdline[80]; + printf("debug> "); + fflush(stdout); + if (!fgets(cmdline, sizeof(cmdline), stdin)) + exit(0); + + char* token = strtok(cmdline, " "); + if (token != NULL) + { + if (strcmp(token, "?") == 0) + cmd_help(); + else if (strcmp(token, "r") == 0) + cmd_register(); + else if (strcmp(token, "b") == 0) + cmd_break(); + else if (strcmp(token, "w") == 0) + cmd_watch(); + else if (strcmp(token, "db") == 0) + cmd_delete_breakpoint(); + else if (strcmp(token, "dw") == 0) + cmd_delete_watchpoint(); + else if (strcmp(token, "m") == 0) + cmd_memory(); + else if (strcmp(token, "s") == 0) + { + singlestepping = true; + go = true; + } + else if (strcmp(token, "g") == 0) + { + singlestepping = false; + go = true; + } + else if (strcmp(token, "bdos") == 0) + cmd_bdos(); + else if (strcmp(token, "tracing") == 0) + cmd_tracing(); + else + printf("Bad command\n"); + } + } +} + +static void sigusr1_cb(int number) +{ + singlestepping = true; +} + +void emulator_init(void) +{ + for (int i=0; ienabled && (ram[w->address] != w->value)) + { + printf("\nWatchpoint hit: %04x has changed from %02x to %02x\n", + w->address, w->value, ram[w->address]); + w->value = ram[w->address]; + singlestepping = true; + } + } + + if (singlestepping) + debug(); + else if (tracing) + showregs(); + + i8080_exec(1); + } +} + diff --git a/plat/cpm/emu/fileio.c b/plat/cpm/emu/fileio.c new file mode 100644 index 000000000..2b338cd66 --- /dev/null +++ b/plat/cpm/emu/fileio.c @@ -0,0 +1,440 @@ +#define _XOPEN_SOURCE 500 +#define _POSIX_C_SOURCE 200809 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "globals.h" + +#define logf(args...) while (0) +//#define logf(args...) printf(args) + +struct file +{ + struct file* prev; + struct file* next; + cpm_filename_t filename; + int fd; + int flags; +}; + +#define NUM_FILES 16 +static struct file files[NUM_FILES]; +static struct file* firstfile; + +#define NUM_DRIVES 16 +static int drives[NUM_DRIVES]; + +static cpm_filename_t currentpattern; +static int currentsearchdrivefd; +static DIR* currentdir; + +void files_init(void) +{ + for (int i=0; iprev = NULL; + else + f->prev = &files[i-1]; + + if (i == (NUM_FILES-1)) + f->next = NULL; + else + f->next = &files[i+1]; + + memset(&f->filename.bytes, ' ', 11); + f->filename.drive = 0; + f->fd = -1; + f->flags = 0; + } + + firstfile = &files[0]; +} + +void file_set_drive(int drive, const char* path) +{ + if ((drive < 0) || (drive >= NUM_DRIVES)) + fatal("bad drive letter"); + + if (drives[drive] != -1) + close(drives[drive]); + drives[drive] = open(path, O_RDONLY); + if (drives[drive] == -1) + fatal("could not open '%s': %s", path, strerror(errno)); + + struct stat st; + fstat(drives[drive], &st); + if (!S_ISDIR(st.st_mode)) + fatal("could not open '%s': not a directory", path); + logf("[drive %c now pointing at %s (fd %d)]\n", drive+'A', path, drives[drive]); +} + +static void bump(struct file* f) +{ + // logf("[bumping file %d to front]\n", f-files); + + if (f != firstfile) + { + /* Remove from list. */ + if (f->prev) + f->prev->next = f->next; + if (f->next) + f->next->prev = f->prev; + + /* Reinsert at head of list. */ + firstfile->prev = f; + f->prev = NULL; + f->next = firstfile; + firstfile = f; + } + + // logf("[first file is %d]\n", firstfile-files); + // for (int i=0; ifilename.drive, f->filename.bytes, f->fd, + // f->prev ? (f->prev - files) : -1, + // f->next ? (f->next - files) : -1); + // } +} + +static void cpm_filename_to_unix(cpm_filename_t* cpmfilename, char* unixfilename) +{ + char* pin = cpmfilename->bytes; + char* pout = unixfilename; + + for (int i=0; i<8; i++) + { + char c = *pin++; + if (c != ' ') + *pout++ = tolower(c); + } + *pout++ = '.'; + for (int i=0; i<3; i++) + { + char c = *pin++; + if (c != ' ') + *pout++ = tolower(c); + } + if (pout[-1] == '.') + pout--; + *pout = '\0'; +} + +static bool unix_filename_to_cpm(const char* unixfilename, cpm_filename_t* cpmfilename) +{ + const char* pin = unixfilename; + + memset(cpmfilename, ' ', sizeof(cpm_filename_t)); + char* pout = &cpmfilename->bytes[0]; + int count = 0; + int maxcount = 8; + for (;;) + { + char c = *pin++; + if ((c == '.') && (maxcount == 8)) + { + maxcount = 3; + count = 0; + pout = &cpmfilename->bytes[8]; + } + else if (c == '\0') + break; + else if (count == maxcount) + return false; + else if (isupper(c)) + return false; + else + { + *pout++ = toupper(c); + count++; + } + } + + return true; +} + +static bool match_filenames(cpm_filename_t* pattern, cpm_filename_t* filename) +{ + if (pattern->drive != filename->drive) + return false; + + for (int i=0; ibytes); i++) + { + char p = pattern->bytes[i]; + if (p == '?') + continue; + if (p != filename->bytes[i]) + return false; + } + return true; +} + +static int get_drive_fd(cpm_filename_t* filename) +{ + int drive = filename->drive - 1; + if ((drive < 0) || (drive >= NUM_DRIVES)) + { + logf("[reference to bad drive %c]\n", drive + 'A'); + return -1; + } + int drivefd = drives[drive]; + if (drivefd == -1) + { + logf("[reference to undefined drive %c]\n", drive + 'A'); + return -1; + } + logf("[selecting drive %c on fd %d]\n", drive + 'A', drivefd); + return drivefd; +} + +static void reopen(struct file* f, int flags) +{ + if ((f->fd == -1) || ((f->flags == O_RDONLY) && (flags == O_RDWR))) + { + char unixfilename[13]; + cpm_filename_to_unix(&f->filename, unixfilename); + + if (f->fd != -1) + { + logf("[reopening actual file '%s' on %d with different flags]\n", unixfilename, f->fd); + close(f->fd); + } + + int drivefd = get_drive_fd(&f->filename); + if (drivefd == -1) + return; + + f->flags = flags & O_ACCMODE; + errno = 0; + f->fd = openat(drivefd, unixfilename, flags, 0666); + logf("[opened actual file '%s' to fd %d: %s]\n", unixfilename, f->fd, strerror(errno)); + } + +} + +static struct file* find_file(cpm_filename_t* filename) +{ + struct file* f = firstfile; + for (;;) + { + if (memcmp(filename, &f->filename, sizeof(cpm_filename_t)) == 0) + break; + + if (f->next) + f = f->next; + else + { + logf("[allocating file %d for '%.11s']\n", f-files, filename->bytes); + bump(f); + if (f->fd != -1) + { + logf("[closing old file %d for '%.11s']\n", f-files, f->filename.bytes); + close(f->fd); + } + f->fd = -1; + f->filename = *filename; + f->flags = 0; + break; + } + } + return f; +} + +struct file* file_open(cpm_filename_t* filename) +{ + struct file* f = find_file(filename); + reopen(f, O_RDONLY); + if (f->fd == -1) + return NULL; + return f; +} + +struct file* file_create(cpm_filename_t* filename) +{ + struct file* f = find_file(filename); + logf("[creating file %d for '%.11s']\n", f-files, f->filename.bytes); + reopen(f, O_RDWR | O_CREAT); + if (f->fd == -1) + return NULL; + return f; +} + +int file_close(cpm_filename_t* filename) +{ + struct file* f = find_file(filename); + + logf("[explicitly closing file %d for '%.11s']\n", f-files, f->filename.bytes); + if (f->fd != -1) + { + logf("[closing file descriptor %d]\n", f->fd); + close(f->fd); + } + + memset(&f->filename.bytes, ' ', 11); + f->fd = -1; + f->flags = 0; + + return 0; +} + +int file_read(struct file* f, uint8_t* data, uint16_t record) +{ + reopen(f, O_RDONLY); + + logf("[read record %04x from file %d for '%.11s']\n", record, f-files, f->filename.bytes); + bump(f); + memset(data, '\0', 128); + return pread(f->fd, data, 128, record*128); +} + +int file_write(struct file* f, uint8_t* data, uint16_t record) +{ + reopen(f, O_RDWR); + + logf("[write record %04x from file %d for '%.11s']\n", record, f-files, f->filename.bytes); + bump(f); + return pwrite(f->fd, data, 128, record*128); +} + +int file_getrecordcount(struct file* f) +{ + reopen(f, O_RDONLY); + + struct stat st; + fstat(f->fd, &st); + return (st.st_size + 127) >> 7; +} + +void file_setrecordcount(struct file* f, int count) +{ + reopen(f, O_RDONLY); + + if (count != file_getrecordcount(f)) + { + logf("[truncating file %d to %d records]\n", f-files, count); + reopen(f, O_RDWR); + ftruncate(f->fd, count*128); + } +} + +int file_findfirst(cpm_filename_t* pattern) +{ + if (currentdir) + { + closedir(currentdir); + currentdir = NULL; + } + + currentpattern = *pattern; + logf("[reset search; current find pattern is '%.11s']\n", currentpattern.bytes); + currentsearchdrivefd = get_drive_fd(pattern); + if (currentsearchdrivefd == -1) + return 0; + + currentdir = fdopendir(dup(currentsearchdrivefd)); + if (currentdir) + { + rewinddir(currentdir); + return 0; + } + return -1; +} + +int file_findnext(cpm_filename_t* result) +{ + for (;;) + { + if (!currentdir) + return -1; + + struct dirent* de = readdir(currentdir); + if (!de) + { + closedir(currentdir); + currentdir = NULL; + logf("[finished search]\n"); + return -1; + } + + struct stat st; + if ((fstatat(currentsearchdrivefd, de->d_name, &st, 0) == 0) + && S_ISREG(st.st_mode) + && unix_filename_to_cpm(de->d_name, result)) + { + result->drive = currentpattern.drive; + logf("[compare '%.11s' with pattern '%.11s']\n", result->bytes, currentpattern.bytes); + if (match_filenames(¤tpattern, result)) + { + logf("[positive match]\n"); + return 0; + } + } + } +} + +int file_delete(cpm_filename_t* pattern) +{ + logf("[attempting to delete pattern '%.11s' on drive %c]\n", pattern->bytes, '@'+pattern->drive); + int drivefd = get_drive_fd(pattern); + DIR* dir = fdopendir(dup(drivefd)); + if (!dir) + return -1; + rewinddir(dir); + + int result = -1; + for (;;) + { + struct dirent* de = readdir(dir); + if (!de) + break; + + struct stat st; + cpm_filename_t candidate; + + if ((fstatat(drivefd, de->d_name, &st, 0) == 0) + && S_ISREG(st.st_mode) + && unix_filename_to_cpm(de->d_name, &candidate)) + { + candidate.drive = pattern->drive; + logf("[compare '%.11s' with pattern '%.11s']\n", candidate.bytes, pattern->bytes); + if (match_filenames(pattern, &candidate)) + { + logf("[positive match, deleting]\n"); + unlinkat(drivefd, de->d_name, 0); + result = 0; + } + } + } + + closedir(dir); + return result; +} + +int file_rename(cpm_filename_t* src, cpm_filename_t* dest) +{ + logf("[renaming %.11s to %.11s on drive %c]\n", + src->bytes, dest->bytes, '@'+src->drive); + + char srcunixfilename[13]; + cpm_filename_to_unix(src, srcunixfilename); + + char destunixfilename[13]; + cpm_filename_to_unix(dest, destunixfilename); + + int drivefd = get_drive_fd(src); + return renameat(drivefd, srcunixfilename, drivefd, destunixfilename); +} \ No newline at end of file diff --git a/plat/cpm/emu/globals.h b/plat/cpm/emu/globals.h new file mode 100644 index 000000000..b07a4d07d --- /dev/null +++ b/plat/cpm/emu/globals.h @@ -0,0 +1,51 @@ +#ifndef GLOBALS_H +#define GLOBALS_H + +#include +#include + +extern Z80EX_CONTEXT* z80; +extern uint8_t ram[0x10000]; + +extern void emulator_init(void); +extern void emulator_run(void); +extern void showregs(void); + +extern const uint8_t ccp_data[]; +extern const int ccp_len; + +extern const uint8_t bdos_data[]; +extern const int bdos_len; + +extern void bios_coldboot(void); + +extern void biosbdos_entry(int syscall); + +typedef struct +{ + uint8_t drive; + char bytes[11]; +} +cpm_filename_t; + +extern void files_init(void); +extern void file_set_drive(int drive, const char* path); +extern struct file* file_open(cpm_filename_t* filename); +extern struct file* file_create(cpm_filename_t* filename); +extern int file_close(cpm_filename_t* filename); +extern int file_read(struct file* file, uint8_t* data, uint16_t record); +extern int file_write(struct file* file, uint8_t* data, uint16_t record); +extern int file_getrecordcount(struct file* f); +extern void file_setrecordcount(struct file* f, int count); +extern int file_findfirst(cpm_filename_t* pattern); +extern int file_findnext(cpm_filename_t* result); +extern int file_delete(cpm_filename_t* pattern); +extern int file_rename(cpm_filename_t* src, cpm_filename_t* dest); + +extern void fatal(const char* message, ...); + +extern bool flag_enter_debugger; +extern char* const* user_command_line; + +#endif + diff --git a/plat/cpm/emu/intel_8080_emulator.c b/plat/cpm/emu/intel_8080_emulator.c new file mode 100644 index 000000000..af99ba49a --- /dev/null +++ b/plat/cpm/emu/intel_8080_emulator.c @@ -0,0 +1,874 @@ +/* + Intel 8080 emulator in C + Written by Mike Chambers, April 2018 + + Use this code for whatever you want. I don't care. It's officially public domain. + Credit would be appreciated. +*/ + +#include +#include +#include +#include "intel_8080_emulator.h" + +#define ALLOW_UNDEFINED + +#define reg16_PSW (((uint16_t)reg8[A] << 8) | (uint16_t)reg8[FLAGS]) +#define reg16_BC (((uint16_t)reg8[B] << 8) | (uint16_t)reg8[C]) +#define reg16_DE (((uint16_t)reg8[D] << 8) | (uint16_t)reg8[E]) +#define reg16_HL (((uint16_t)reg8[H] << 8) | (uint16_t)reg8[L]) + +uint8_t reg8[9], INTE = 0; +uint16_t reg_SP, reg_PC; + +#define set_S() reg8[FLAGS] |= 0x80 +#define set_Z() reg8[FLAGS] |= 0x40 +#define set_AC() reg8[FLAGS] |= 0x10 +#define set_P() reg8[FLAGS] |= 0x04 +#define set_C() reg8[FLAGS] |= 0x01 +#define clear_S() reg8[FLAGS] &= 0x7F +#define clear_Z() reg8[FLAGS] &= 0xBF +#define clear_AC() reg8[FLAGS] &= 0xEF +#define clear_P() reg8[FLAGS] &= 0xFB +#define clear_C() reg8[FLAGS] &= 0xFE +#define test_S() (reg8[FLAGS] & 0x80) +#define test_Z() (reg8[FLAGS] & 0x40) +#define test_AC() (reg8[FLAGS] & 0x10) +#define test_P() (reg8[FLAGS] & 0x04) +#define test_C() (reg8[FLAGS] & 0x01) + +static const uint8_t parity[0x100] = { + 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, + 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, + 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, + 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, + 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, + 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, + 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, + 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1 +}; + +uint16_t read_RP(uint8_t rp) { + switch (rp) { + case 0x00: + return reg16_BC; + case 0x01: + return reg16_DE; + case 0x02: + return reg16_HL; + case 0x03: + return reg_SP; + } + return 0; +} + +uint16_t read_RP_PUSHPOP(uint8_t rp) { + switch (rp) { + case 0x00: + return reg16_BC; + case 0x01: + return reg16_DE; + case 0x02: + return reg16_HL; + case 0x03: + return (reg16_PSW | 0x02) & 0xFFD7; + } + return 0; +} + +void write_RP(uint8_t rp, uint8_t lb, uint8_t hb) { + switch (rp) { + case 0x00: + reg8[C] = lb; + reg8[B] = hb; + break; + case 0x01: + reg8[E] = lb; + reg8[D] = hb; + break; + case 0x02: + reg8[L] = lb; + reg8[H] = hb; + break; + case 0x03: + reg_SP = (uint16_t)lb | ((uint16_t)hb << 8); + break; + } +} + +void write16_RP(uint8_t rp, uint16_t value) { + switch (rp) { + case 0x00: + reg8[C] = value & 0x00FF; + reg8[B] = value >> 8; + break; + case 0x01: + reg8[E] = value & 0x00FF; + reg8[D] = value >> 8; + break; + case 0x02: + reg8[L] = value & 0x00FF; + reg8[H] = value >> 8; + break; + case 0x03: + reg_SP = value; + break; + } +} + +void write16_RP_PUSHPOP(uint8_t rp, uint16_t value) { + switch (rp) { + case 0x00: + reg8[C] = value & 0x00FF; + reg8[B] = value >> 8; + break; + case 0x01: + reg8[E] = value & 0x00FF; + reg8[D] = value >> 8; + break; + case 0x02: + reg8[L] = value & 0x00FF; + reg8[H] = value >> 8; + break; + case 0x03: + reg8[FLAGS] = ((value & 0x00FF) | 0x02) & 0xD7; + reg8[A] = value >> 8; + break; + } +} + +void calc_SZP(uint8_t value) { + if (value == 0) set_Z(); else clear_Z(); + if (value & 0x80) set_S(); else clear_S(); + if (parity[value]) set_P(); else clear_P(); +} + +void calc_AC(uint8_t val1, uint8_t val2) { + if (((val1 & 0x0F) + (val2 & 0x0F)) > 0x0F) { + set_AC(); + } else { + clear_AC(); + } +} + +void calc_AC_carry(uint8_t val1, uint8_t val2) { + if (((val1 & 0x0F) + (val2 & 0x0F)) >= 0x0F) { + set_AC(); + } else { + clear_AC(); + } +} + +void calc_subAC(int8_t val1, uint8_t val2) { + if ((val2 & 0x0F) <= (val1 & 0x0F)) { + set_AC(); + } else { + clear_AC(); + } +} + +void calc_subAC_borrow(int8_t val1, uint8_t val2) { + if ((val2 & 0x0F) < (val1 & 0x0F)) { + set_AC(); + } else { + clear_AC(); + } +} + +uint8_t test_cond(uint8_t code) { + switch (code) { + case 0: //Z not set + if (!test_Z()) return 1; else return 0; + case 1: //Z set + if (test_Z()) return 1; else return 0; + case 2: //C not set + if (!test_C()) return 1; else return 0; + case 3: //C set + if (test_C()) return 1; else return 0; + case 4: //P not set + if (!test_P()) return 1; else return 0; + case 5: //P set + if (test_P()) return 1; else return 0; + case 6: //S not set + if (!test_S()) return 1; else return 0; + case 7: //S set + if (test_S()) return 1; else return 0; + } + return 0; +} + +void i8080_push(uint16_t value) { + i8080_write(--reg_SP, value >> 8); + i8080_write(--reg_SP, (uint8_t)value); +} + +uint16_t i8080_pop() { + uint16_t temp; + temp = i8080_read(reg_SP++); + temp |= (uint16_t)i8080_read(reg_SP++) << 8; + return temp; +} + +void i8080_interrupt(uint8_t n) { + if (!INTE) return; + i8080_push(reg_PC); + reg_PC = (uint16_t)n << 3; + INTE = 0; +} + +void i8080_jump(uint16_t addr) { + reg_PC = addr; +} + +void i8080_reset() { + reg_PC = reg_SP = 0x0000; + //reg8[FLAGS] = 0x02; +} + +void i8080_write_reg8(reg_t reg, uint8_t value) { + if (reg == M) { + i8080_write(reg16_HL, value); + } else { + reg8[reg] = value; + } +} + +uint8_t i8080_read_reg8(reg_t reg) { + if (reg == M) { + return i8080_read(reg16_HL); + } else { + return reg8[reg]; + } +} + +uint16_t i8080_read_reg16(reg_t reg) { + switch (reg) { + case AF: return reg16_PSW; + case BC: return reg16_BC; + case DE: return reg16_DE; + case HL: return reg16_HL; + case SP: return reg_SP; + case PC: return reg_PC; + } + return 0; +} + +void i8080_write_reg16(reg_t reg, uint16_t value) { + switch (reg) { + case AF: reg8[A] = value>>8; reg8[FLAGS] = value; break; + case BC: reg8[B] = value>>8; reg8[C] = value; break; + case DE: reg8[D] = value>>8; reg8[E] = value; break; + case HL: reg8[H] = value>>8; reg8[L] = value; break; + case SP: reg_SP = value; break; + case PC: reg_PC = value; break; + } +} + +int i8080_exec(int cycles) { + uint8_t opcode, temp8, reg, reg2; + uint16_t temp16; + uint32_t temp32; + + while (cycles > 0) { + opcode = i8080_read(reg_PC++); + + switch (opcode) { + case 0x3A: //LDA a - load A from memory + temp16 = (uint16_t)i8080_read(reg_PC) | ((uint16_t)i8080_read(reg_PC+1)<<8); + reg8[A] = i8080_read(temp16); + reg_PC += 2; + cycles -= 13; + break; + case 0x32: //STA a - store A to memory + temp16 = (uint16_t)i8080_read(reg_PC) | ((uint16_t)i8080_read(reg_PC+1)<<8); + i8080_write(temp16, reg8[A]); + reg_PC += 2; + cycles -= 13; + break; + case 0x2A: //LHLD a - load H:L from memory + temp16 = (uint16_t)i8080_read(reg_PC) | ((uint16_t)i8080_read(reg_PC+1)<<8); + reg8[L] = i8080_read(temp16++); + reg8[H] = i8080_read(temp16); + reg_PC += 2; + cycles -= 16; + break; + case 0x22: //SHLD a - store H:L to memory + temp16 = (uint16_t)i8080_read(reg_PC) | ((uint16_t)i8080_read(reg_PC+1)<<8); + i8080_write(temp16++, reg8[L]); + i8080_write(temp16, reg8[H]); + reg_PC += 2; + cycles -= 16; + break; + case 0xEB: //XCHG - exchange DE and HL content + temp8 = reg8[D]; + reg8[D] = reg8[H]; + reg8[H] = temp8; + temp8 = reg8[E]; + reg8[E] = reg8[L]; + reg8[L] = temp8; + cycles -= 5; + break; + case 0xC6: //ADI # - add immediate to A + temp8 = i8080_read(reg_PC++); + temp16 = (uint16_t)reg8[A] + (uint16_t)temp8; + if (temp16 & 0xFF00) set_C(); else clear_C(); + calc_AC(reg8[A], temp8); + calc_SZP((uint8_t)temp16); + reg8[A] = (uint8_t)temp16; + cycles -= 7; + break; + case 0xCE: //ACI # - add immediate to A with carry + temp8 = i8080_read(reg_PC++); + temp16 = (uint16_t)reg8[A] + (uint16_t)temp8 + (uint16_t)test_C(); + if (test_C()) calc_AC_carry(reg8[A], temp8); else calc_AC(reg8[A], temp8); + if (temp16 & 0xFF00) set_C(); else clear_C(); + calc_SZP((uint8_t)temp16); + reg8[A] = (uint8_t)temp16; + cycles -= 7; + break; + case 0xD6: //SUI # - subtract immediate from A + temp8 = i8080_read(reg_PC++); + temp16 = (uint16_t)reg8[A] - (uint16_t)temp8; + if (((temp16 & 0x00FF) >= reg8[A]) && temp8) set_C(); else clear_C(); + calc_subAC(reg8[A], temp8); + calc_SZP((uint8_t)temp16); + reg8[A] = (uint8_t)temp16; + cycles -= 7; + break; + case 0x27: //DAA - decimal adjust accumulator + temp16 = reg8[A]; + if (((temp16 & 0x0F) > 0x09) || test_AC()) { + if (((temp16 & 0x0F) + 0x06) & 0xF0) set_AC(); else clear_AC(); + temp16 += 0x06; + if (temp16 & 0xFF00) set_C(); //can also cause carry to be set during addition to the low nibble + } + if (((temp16 & 0xF0) > 0x90) || test_C()) { + temp16 += 0x60; + if (temp16 & 0xFF00) set_C(); //doesn't clear it if this clause is false + } + calc_SZP((uint8_t)temp16); + reg8[A] = (uint8_t)temp16; + cycles -= 4; + break; + case 0xE6: //ANI # - AND immediate with A + temp8 = i8080_read(reg_PC++); + if ((reg8[A] | temp8) & 0x08) set_AC(); else clear_AC(); + reg8[A] &= temp8; + clear_C(); + calc_SZP(reg8[A]); + cycles -= 7; + break; + case 0xF6: //ORI # - OR immediate with A + reg8[A] |= i8080_read(reg_PC++); + clear_AC(); + clear_C(); + calc_SZP(reg8[A]); + cycles -= 7; + break; + case 0xEE: //XRI # - XOR immediate with A + reg8[A] ^= i8080_read(reg_PC++); + clear_AC(); + clear_C(); + calc_SZP(reg8[A]); + cycles -= 7; + break; + case 0xDE: //SBI # - subtract immediate from A with borrow + temp8 = i8080_read(reg_PC++); + temp16 = (uint16_t)reg8[A] - (uint16_t)temp8 - (uint16_t)test_C(); + if (test_C()) calc_subAC_borrow(reg8[A], temp8); else calc_subAC(reg8[A], temp8); + if (((temp16 & 0x00FF) >= reg8[A]) && (temp8 | test_C())) set_C(); else clear_C(); + calc_SZP((uint8_t)temp16); + reg8[A] = (uint8_t)temp16; + cycles -= 7; + break; + case 0xFE: //CPI # - compare immediate with A + temp8 = i8080_read(reg_PC++); + temp16 = (uint16_t)reg8[A] - (uint16_t)temp8; + if (((temp16 & 0x00FF) >= reg8[A]) && temp8) set_C(); else clear_C(); + calc_subAC(reg8[A], temp8); + calc_SZP((uint8_t)temp16); + cycles -= 7; + break; + case 0x07: //RLC - rotate A left + if (reg8[A] & 0x80) set_C(); else clear_C(); + reg8[A] = (reg8[A] >> 7) | (reg8[A] << 1); + cycles -= 4; + break; + case 0x0F: //RRC - rotate A right + if (reg8[A] & 0x01) set_C(); else clear_C(); + reg8[A] = (reg8[A] << 7) | (reg8[A] >> 1); + cycles -= 4; + break; + case 0x17: //RAL - rotate A left through carry + temp8 = test_C(); + if (reg8[A] & 0x80) set_C(); else clear_C(); + reg8[A] = (reg8[A] << 1) | temp8; + cycles -= 4; + break; + case 0x1F: //RAR - rotate A right through carry + temp8 = test_C(); + if (reg8[A] & 0x01) set_C(); else clear_C(); + reg8[A] = (reg8[A] >> 1) | (temp8 << 7); + cycles -= 4; + break; + case 0x2F: //CMA - compliment A + reg8[A] = ~reg8[A]; + cycles -= 4; + break; + case 0x3F: //CMC - compliment carry flag + reg8[FLAGS] ^= 1; + cycles -= 4; + break; + case 0x37: //STC - set carry flag + set_C(); + cycles -= 4; + break; + case 0xC7: //RST n - restart (call n*8) + case 0xD7: + case 0xE7: + case 0xF7: + case 0xCF: + case 0xDF: + case 0xEF: + case 0xFF: + i8080_push(reg_PC); + reg_PC = (uint16_t)((opcode >> 3) & 7) << 3; + cycles -= 11; + break; + case 0xE9: //PCHL - jump to address in H:L + reg_PC = reg16_HL; + cycles -= 5; + break; + case 0xE3: //XTHL - swap H:L with top word on stack + temp16 = i8080_pop(); + i8080_push(reg16_HL); + write16_RP(2, temp16); + cycles -= 18; + break; + case 0xF9: //SPHL - set SP to content of HL + reg_SP = reg16_HL; + cycles -= 5; + break; + case 0xDB: //IN p - read input port into A + reg8[A] = i8080_inport(i8080_read(reg_PC++)); + cycles -= 10; + break; + case 0xD3: //OUT p - write A to output port + i8080_outport(i8080_read(reg_PC++), reg8[A]); + cycles -= 10; + break; + case 0xFB: //EI - enable interrupts + INTE = 1; + cycles -= 4; + break; + case 0xF3: //DI - disbale interrupts + INTE = 0; + cycles -= 4; + break; + case 0x76: //HLT - halt processor + reg_PC--; + cycles -= 7; + break; + case 0x00: //NOP - no operation +#ifdef ALLOW_UNDEFINED + case 0x10: + case 0x20: + case 0x30: + case 0x08: + case 0x18: + case 0x28: + case 0x38: +#endif + cycles -= 4; + break; + case 0x40: case 0x50: case 0x60: case 0x70: //MOV D,S - move register to register + case 0x41: case 0x51: case 0x61: case 0x71: + case 0x42: case 0x52: case 0x62: case 0x72: + case 0x43: case 0x53: case 0x63: case 0x73: + case 0x44: case 0x54: case 0x64: case 0x74: + case 0x45: case 0x55: case 0x65: case 0x75: + case 0x46: case 0x56: case 0x66: + case 0x47: case 0x57: case 0x67: case 0x77: + case 0x48: case 0x58: case 0x68: case 0x78: + case 0x49: case 0x59: case 0x69: case 0x79: + case 0x4A: case 0x5A: case 0x6A: case 0x7A: + case 0x4B: case 0x5B: case 0x6B: case 0x7B: + case 0x4C: case 0x5C: case 0x6C: case 0x7C: + case 0x4D: case 0x5D: case 0x6D: case 0x7D: + case 0x4E: case 0x5E: case 0x6E: case 0x7E: + case 0x4F: case 0x5F: case 0x6F: case 0x7F: + reg = (opcode >> 3) & 7; + reg2 = opcode & 7; + i8080_write_reg8(reg, i8080_read_reg8(reg2)); + if ((reg == M) || (reg2 == M)) { + cycles -= 7; + } else { + cycles -= 5; + } + break; + case 0x06: //MVI D,# - move immediate to register + case 0x16: + case 0x26: + case 0x36: + case 0x0E: + case 0x1E: + case 0x2E: + case 0x3E: + reg = (opcode >> 3) & 7; + i8080_write_reg8(reg, i8080_read(reg_PC++)); + if (reg == M) { + cycles -= 10; + } else { + cycles -= 7; + } + break; + case 0x01: //LXI RP,# - load register pair immediate + case 0x11: + case 0x21: + case 0x31: + reg = (opcode >> 4) & 3; + write_RP(reg, i8080_read(reg_PC), i8080_read(reg_PC + 1)); + reg_PC += 2; + cycles -= 10; + break; + case 0x0A: //LDAX BC - load A indirect through BC + reg8[A] = i8080_read(reg16_BC); + cycles -= 7; + break; + case 0x1A: //LDAX DE - load A indirect through DE + reg8[A] = i8080_read(reg16_DE); + cycles -= 7; + break; + case 0x02: //STAX BC - store A indirect through BC + i8080_write(reg16_BC, reg8[A]); + cycles -= 7; + break; + case 0x12: //STAX DE - store A indirect through DE + i8080_write(reg16_DE, reg8[A]); + cycles -= 7; + break; + case 0x04: //INR D - increment register + case 0x14: + case 0x24: + case 0x34: + case 0x0C: + case 0x1C: + case 0x2C: + case 0x3C: + reg = (opcode >> 3) & 7; + temp8 = i8080_read_reg8(reg); //reg8[reg]; + calc_AC(temp8, 1); + calc_SZP(temp8 + 1); + i8080_write_reg8(reg, temp8 + 1); //reg8[reg]++; + if (reg == M) { + cycles -= 10; + } else { + cycles -= 5; + } + break; + case 0x05: //DCR D - decrement register + case 0x15: + case 0x25: + case 0x35: + case 0x0D: + case 0x1D: + case 0x2D: + case 0x3D: + reg = (opcode >> 3) & 7; + temp8 = i8080_read_reg8(reg); //reg8[reg]; + calc_subAC(temp8, 1); + calc_SZP(temp8 - 1); + i8080_write_reg8(reg, temp8 - 1); //reg8[reg]--; + if (reg == M) { + cycles -= 10; + } else { + cycles -= 5; + } + break; + case 0x03: //INX RP - increment register pair + case 0x13: + case 0x23: + case 0x33: + reg = (opcode >> 4) & 3; + write16_RP(reg, read_RP(reg) + 1); + cycles -= 5; + break; + case 0x0B: //DCX RP - decrement register pair + case 0x1B: + case 0x2B: + case 0x3B: + reg = (opcode >> 4) & 3; + write16_RP(reg, read_RP(reg) - 1); + cycles -= 5; + break; + case 0x09: //DAD RP - add register pair to HL + case 0x19: + case 0x29: + case 0x39: + reg = (opcode >> 4) & 3; + temp32 = (uint32_t)reg16_HL + (uint32_t)read_RP(reg); + write16_RP(2, (uint16_t)temp32); + if (temp32 & 0xFFFF0000) set_C(); else clear_C(); + cycles -= 10; + break; + case 0x80: //ADD S - add register or memory to A + case 0x81: + case 0x82: + case 0x83: + case 0x84: + case 0x85: + case 0x86: + case 0x87: + reg = opcode & 7; + temp8 = i8080_read_reg8(reg); + temp16 = (uint16_t)reg8[A] + (uint16_t)temp8; + if (temp16 & 0xFF00) set_C(); else clear_C(); + calc_AC(reg8[A], temp8); + calc_SZP((uint8_t)temp16); + reg8[A] = (uint8_t)temp16; + if (reg == M) { + cycles -= 7; + } else { + cycles -= 4; + } + break; + case 0x88: //ADC S - add register or memory to A with carry + case 0x89: + case 0x8A: + case 0x8B: + case 0x8C: + case 0x8D: + case 0x8E: + case 0x8F: + reg = opcode & 7; + temp8 = i8080_read_reg8(reg); + temp16 = (uint16_t)reg8[A] + (uint16_t)temp8 + (uint16_t)test_C(); + if (test_C()) calc_AC_carry(reg8[A], temp8); else calc_AC(reg8[A], temp8); + if (temp16 & 0xFF00) set_C(); else clear_C(); + calc_SZP((uint8_t)temp16); + reg8[A] = (uint8_t)temp16; + if (reg == M) { + cycles -= 7; + } else { + cycles -= 4; + } + break; + case 0x90: //SUB S - subtract register or memory from A + case 0x91: + case 0x92: + case 0x93: + case 0x94: + case 0x95: + case 0x96: + case 0x97: + reg = opcode & 7; + temp8 = i8080_read_reg8(reg); + temp16 = (uint16_t)reg8[A] - (uint16_t)temp8; + if (((temp16 & 0x00FF) >= reg8[A]) && temp8) set_C(); else clear_C(); + calc_subAC(reg8[A], temp8); + calc_SZP((uint8_t)temp16); + reg8[A] = (uint8_t)temp16; + if (reg == M) { + cycles -= 7; + } else { + cycles -= 4; + } + break; + case 0x98: //SBB S - subtract register or memory from A with borrow + case 0x99: + case 0x9A: + case 0x9B: + case 0x9C: + case 0x9D: + case 0x9E: + case 0x9F: + reg = opcode & 7; + temp8 = i8080_read_reg8(reg); + temp16 = (uint16_t)reg8[A] - (uint16_t)temp8 - (uint16_t)test_C(); + if (test_C()) calc_subAC_borrow(reg8[A], temp8); else calc_subAC(reg8[A], temp8); + if (((temp16 & 0x00FF) >= reg8[A]) && (temp8 | test_C())) set_C(); else clear_C(); + calc_SZP((uint8_t)temp16); + reg8[A] = (uint8_t)temp16; + if (reg == M) { + cycles -= 7; + } else { + cycles -= 4; + } + break; + case 0xA0: //ANA S - AND register with A + case 0xA1: + case 0xA2: + case 0xA3: + case 0xA4: + case 0xA5: + case 0xA6: + case 0xA7: + reg = opcode & 7; + temp8 = i8080_read_reg8(reg); + if ((reg8[A] | temp8) & 0x08) set_AC(); else clear_AC(); + reg8[A] &= temp8; + clear_C(); + calc_SZP(reg8[A]); + if (reg == M) { + cycles -= 7; + } else { + cycles -= 4; + } + break; + case 0xB0: //ORA S - OR register with A + case 0xB1: + case 0xB2: + case 0xB3: + case 0xB4: + case 0xB5: + case 0xB6: + case 0xB7: + reg = opcode & 7; + reg8[A] |= i8080_read_reg8(reg); + clear_AC(); + clear_C(); + calc_SZP(reg8[A]); + if (reg == M) { + cycles -= 7; + } else { + cycles -= 4; + } + break; + case 0xA8: //XRA S - XOR register with A + case 0xA9: + case 0xAA: + case 0xAB: + case 0xAC: + case 0xAD: + case 0xAE: + case 0xAF: + reg = opcode & 7; + reg8[A] ^= i8080_read_reg8(reg); + clear_AC(); + clear_C(); + calc_SZP(reg8[A]); + if (reg == M) { + cycles -= 7; + } else { + cycles -= 4; + } + break; + case 0xB8: //CMP S - compare register with A + case 0xB9: + case 0xBA: + case 0xBB: + case 0xBC: + case 0xBD: + case 0xBE: + case 0xBF: + reg = opcode & 7; + temp8 = i8080_read_reg8(reg); + temp16 = (uint16_t)reg8[A] - (uint16_t)temp8; + if (((temp16 & 0x00FF) >= reg8[A]) && temp8) set_C(); else clear_C(); + calc_subAC(reg8[A], temp8); + calc_SZP((uint8_t)temp16); + if (reg == M) { + cycles -= 7; + } else { + cycles -= 4; + } + break; + case 0xC3: //JMP a - unconditional jump +#ifdef ALLOW_UNDEFINED + case 0xCB: +#endif + temp16 = (uint16_t)i8080_read(reg_PC) | (((uint16_t)i8080_read(reg_PC + 1)) << 8); + reg_PC = temp16; + cycles -= 10; + break; + case 0xC2: //Jccc - conditional jumps + case 0xCA: + case 0xD2: + case 0xDA: + case 0xE2: + case 0xEA: + case 0xF2: + case 0xFA: + temp16 = (uint16_t)i8080_read(reg_PC) | (((uint16_t)i8080_read(reg_PC + 1)) << 8); + if (test_cond((opcode >> 3) & 7)) reg_PC = temp16; else reg_PC += 2; + cycles -= 10; + break; + case 0xCD: //CALL a - unconditional call +#ifdef ALLOW_UNDEFINED + case 0xDD: + case 0xED: + case 0xFD: +#endif + temp16 = (uint16_t)i8080_read(reg_PC) | (((uint16_t)i8080_read(reg_PC + 1)) << 8); + i8080_push(reg_PC + 2); + reg_PC = temp16; + cycles -= 17; + break; + case 0xC4: //Cccc - conditional calls + case 0xCC: + case 0xD4: + case 0xDC: + case 0xE4: + case 0xEC: + case 0xF4: + case 0xFC: + temp16 = (uint16_t)i8080_read(reg_PC) | (((uint16_t)i8080_read(reg_PC + 1)) << 8); + if (test_cond((opcode >> 3) & 7)) { + i8080_push(reg_PC + 2); + reg_PC = temp16; + cycles -= 17; + } else { + reg_PC += 2; + cycles -= 11; + } + break; + case 0xC9: //RET - unconditional return +#ifdef ALLOW_UNDEFINED + case 0xD9: +#endif + reg_PC = i8080_pop(); + cycles -= 10; + break; + case 0xC0: //Rccc - conditional returns + case 0xC8: + case 0xD0: + case 0xD8: + case 0xE0: + case 0xE8: + case 0xF0: + case 0xF8: + if (test_cond((opcode >> 3) & 7)) { + reg_PC = i8080_pop(); + cycles -= 11; + } else { + cycles -= 5; + } + break; + case 0xC5: //PUSH RP - push register pair on the stack + case 0xD5: + case 0xE5: + case 0xF5: + reg = (opcode >> 4) & 3; + i8080_push(read_RP_PUSHPOP(reg)); + cycles -= 11; + break; + case 0xC1: //POP RP - pop register pair from the stack + case 0xD1: + case 0xE1: + case 0xF1: + reg = (opcode >> 4) & 3; + write16_RP_PUSHPOP(reg, i8080_pop()); + cycles -= 10; + break; + +#ifndef ALLOW_UNDEFINED + default: + printf("UNRECOGNIZED INSTRUCTION @ %04Xh: %02X\n", reg_PC - 1, opcode); + exit(0); +#endif + } + + } + + return cycles; +} diff --git a/plat/cpm/emu/intel_8080_emulator.h b/plat/cpm/emu/intel_8080_emulator.h new file mode 100644 index 000000000..cbfca20fe --- /dev/null +++ b/plat/cpm/emu/intel_8080_emulator.h @@ -0,0 +1,25 @@ +#ifndef INTEL_I8080_EMULATOR_H +#define INTEL_I8080_EMULATOR_H + +typedef enum +{ + B=0, C, D, E, H, L, M, A, FLAGS, + AF, BC, DE, HL, SP, PC +} +reg_t; + +extern uint8_t i8080_read(uint16_t addr); +extern void i8080_write(uint16_t addr, uint8_t value); +extern uint8_t i8080_inport(uint8_t port); +extern void i8080_outport(uint8_t port, uint8_t value); + +extern uint8_t i8080_read_reg8(reg_t reg); +extern void i8080_write_reg8(reg_t reg, uint8_t value); + +extern uint16_t i8080_read_reg16(reg_t reg); +extern void i8080_write_reg16(reg_t reg, uint16_t value); + +extern int i8080_exec(int cycles); + +#endif + diff --git a/plat/cpm/emu/main.c b/plat/cpm/emu/main.c new file mode 100644 index 000000000..98eca0bef --- /dev/null +++ b/plat/cpm/emu/main.c @@ -0,0 +1,83 @@ +#include +#include +#include +#include +#include +#include +#include "globals.h" + +bool flag_enter_debugger = false; +char* const* user_command_line = NULL; + +void fatal(const char* message, ...) +{ + va_list ap; + va_start(ap, message); + fprintf(stderr, "fatal: "); + vfprintf(stderr, message, ap); + fprintf(stderr, "\n"); + exit(1); +} + +static void syntax(void) +{ + printf("cpm [] [command] [args]:\n"); + printf(" -h this help\n"); + printf(" -d enter debugger on startup\n"); + printf(" -p DRIVE=PATH map a drive to a path (by default, A=.)\n"); + printf("If command is specified, a Unix file of that name will be loaded and\n"); + printf("injected directly into memory (it's not loaded through the CCP).\n"); + printf("Arguments may also be provided, but note that any FCBs aren't set up,\n"); + printf("so traditional Unix utilities probably won't work.\n"); + exit(1); +} + +static void parse_options(int argc, char* const* argv) +{ + for (;;) + { + switch (getopt(argc, argv, "hdp:")) + { + case -1: + goto end_of_flags; + + case 'd': + flag_enter_debugger = true; + break; + + case 'p': + { + if (!optarg[0] || (optarg[1] != '=')) + fatal("invalid syntax in drive assignment"); + + uint8_t drive = toupper(optarg[0]) - 'A'; + const char* path = &optarg[2]; + file_set_drive(drive, path); + break; + } + + default: + syntax(); + } + } + +end_of_flags: + user_command_line = &argv[optind]; +} + +int main(int argc, char* const* argv) +{ + files_init(); + parse_options(argc, argv); + + emulator_init(); + bios_coldboot(); + + for (;;) + { + emulator_run(); + } + + return 0; +} + diff --git a/plat/cpm/tests/build.lua b/plat/cpm/tests/build.lua new file mode 100644 index 000000000..db1d4b0f8 --- /dev/null +++ b/plat/cpm/tests/build.lua @@ -0,0 +1,7 @@ +include("tests/plat/build.lua") + +plat_testsuite { + name = "tests", + plat = "cpm", + method = "plat/cpm/emu+emu" +} diff --git a/util/cmisc/build.lua b/util/cmisc/build.lua index dec466279..f0278a503 100644 --- a/util/cmisc/build.lua +++ b/util/cmisc/build.lua @@ -29,3 +29,8 @@ cprogram { srcs = { "./ed.c" } } +cprogram { + name = "objectify", + srcs = { "./objectify.c" } +} + From 80bfbd17b7eb7db8bed703069e5ca6e41d90dad2 Mon Sep 17 00:00:00 2001 From: David Given Date: Thu, 7 Feb 2019 23:01:10 +0100 Subject: [PATCH 2/3] Add missing file. --- build.lua | 11 ++++++----- util/cmisc/objectify.c | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 5 deletions(-) create mode 100644 util/cmisc/objectify.c diff --git a/build.lua b/build.lua index b653c1c9f..156ff7cea 100644 --- a/build.lua +++ b/build.lua @@ -19,11 +19,12 @@ vars.plats = { "em22", } vars.plats_with_tests = { - "linux68k", - "linux386", - "linuxppc", - "linuxmips", - "pc86", + "cpm", +-- "linux386", +-- "linux68k", +-- "linuxmips", +-- "linuxppc", +-- "pc86", } local plat_packages = {} diff --git a/util/cmisc/objectify.c b/util/cmisc/objectify.c new file mode 100644 index 000000000..090956131 --- /dev/null +++ b/util/cmisc/objectify.c @@ -0,0 +1,33 @@ +#include +#include +#include + +int main(int argc, const char* argv[]) +{ + size_t count = 0; + + if (argc != 2) + { + fprintf(stderr, "syntax: objectify \n"); + exit(1); + } + + printf("#include \n"); + printf("#include \n"); + printf("const uint8_t %s_data[] = {", argv[1]); + for (;;) + { + int c = getchar(); + if (c == -1) + break; + if ((count & 15) == 0) + putchar('\n'); + printf("0x%02x, ", c); + count++; + } + printf("\n};\n"); + printf("const size_t %s_len = %d;\n", argv[1], count); + + return 0; +} + From 8876ddcad211fe45dc22b698bf9d1ee5c688dbce Mon Sep 17 00:00:00 2001 From: David Given Date: Thu, 7 Feb 2019 23:36:45 +0100 Subject: [PATCH 3/3] Build the CP/M tests (which got turned off accidentally). Add a disassembler to the emulator debugger. --- build.lua | 10 +- plat/cpm/emu/dis8080.c | 366 ++++++++++++++++++++++++++++++++++++++++ plat/cpm/emu/dis8080.h | 6 + plat/cpm/emu/emulator.c | 6 +- 4 files changed, 380 insertions(+), 8 deletions(-) create mode 100644 plat/cpm/emu/dis8080.c create mode 100644 plat/cpm/emu/dis8080.h diff --git a/build.lua b/build.lua index 156ff7cea..d7eb0829f 100644 --- a/build.lua +++ b/build.lua @@ -20,11 +20,11 @@ vars.plats = { } vars.plats_with_tests = { "cpm", --- "linux386", --- "linux68k", --- "linuxmips", --- "linuxppc", --- "pc86", + "linux68k", + "linux386", + "linuxppc", + "linuxmips", + "pc86", } local plat_packages = {} diff --git a/plat/cpm/emu/dis8080.c b/plat/cpm/emu/dis8080.c new file mode 100644 index 000000000..1adf2bc26 --- /dev/null +++ b/plat/cpm/emu/dis8080.c @@ -0,0 +1,366 @@ +#include +#include +#include +#include +#include "intel_8080_emulator.h" +#include "dis8080.h" + +enum +{ + NOTHING, + CONST8, + CONST16, +}; + +struct insn +{ + const char* name; + int operand; +}; + +static struct insn insns[0x100] = +{ + /* 00-07 */ + { "nop", NOTHING }, + { "lxi b, 0x%04x", CONST16 }, + { "stax b", NOTHING }, + { "inx b", NOTHING }, + { "inr b", NOTHING }, + { "dcr b", NOTHING }, + { "mvi b, 0x%02x", CONST8 }, + { "rlc", NOTHING }, + + /* 08-0f */ + { "undef", NOTHING }, + { "dad b", NOTHING }, + { "ldax b", NOTHING }, + { "dcx b", NOTHING }, + { "inr c", NOTHING }, + { "dcr c", NOTHING }, + { "mvi c, 0x%02x", CONST8 }, + { "rrc", NOTHING }, + + /* 10-17 */ + { "nop", NOTHING }, + { "lxi d, 0x%04x", CONST16 }, + { "stax d", NOTHING }, + { "inx d", NOTHING }, + { "inr d", NOTHING }, + { "dcr d", NOTHING }, + { "mvi d, 0x%02x", CONST8 }, + { "ral", NOTHING }, + + /* 18-1f */ + { "undef", NOTHING }, + { "dad d", NOTHING }, + { "ldax d", NOTHING }, + { "dcx d", NOTHING }, + { "inr e", NOTHING }, + { "dcr e", NOTHING }, + { "mvi e, 0x%02x", CONST8 }, + { "rar", NOTHING }, + + /* 20-27 */ + { "nop", NOTHING }, + { "lxi h, 0x%04x", CONST16 }, + { "shld 0x%04x", CONST16 }, + { "inx h", NOTHING }, + { "inr h", NOTHING }, + { "dcr h", NOTHING }, + { "mvi h, 0x%02x", CONST8 }, + { "daa", NOTHING }, + + /* 28-2f */ + { "undef", NOTHING }, + { "dad h", NOTHING }, + { "lhld 0x%04x", CONST16 }, + { "dcx h", NOTHING }, + { "inr l", NOTHING }, + { "dcr l", NOTHING }, + { "mvi l, 0x%02x", CONST8 }, + { "cma", NOTHING }, + + /* 30-37 */ + { "nop", NOTHING }, + { "lxi sp, 0x%04x", CONST16 }, + { "sta 0x%04x", CONST16 }, + { "inx sp", NOTHING }, + { "inr m", NOTHING }, + { "dcr m", NOTHING }, + { "mvi m, 0x%02x", CONST8 }, + { "stc", NOTHING }, + + /* 38-3f */ + { "undef", NOTHING }, + { "dad sp", NOTHING }, + { "lda 0x%04x", CONST16 }, + { "dcx sp", NOTHING }, + { "inr a", NOTHING }, + { "dcr a", NOTHING }, + { "mvi a, 0x%02x", CONST8 }, + { "cmc", NOTHING }, + + /* 40-47 */ + { "mov b, b", NOTHING }, + { "mov b, c", NOTHING }, + { "mov b, d", NOTHING }, + { "mov b, e", NOTHING }, + { "mov b, h", NOTHING }, + { "mov b, l", NOTHING }, + { "mov b, m", NOTHING }, + { "mov b, a", NOTHING }, + + /* 47-4f */ + { "mov c, b", NOTHING }, + { "mov c, c", NOTHING }, + { "mov c, d", NOTHING }, + { "mov c, e", NOTHING }, + { "mov c, h", NOTHING }, + { "mov c, l", NOTHING }, + { "mov c, m", NOTHING }, + { "mov c, a", NOTHING }, + + /* 50-57 */ + { "mov d, b", NOTHING }, + { "mov d, c", NOTHING }, + { "mov d, d", NOTHING }, + { "mov d, e", NOTHING }, + { "mov d, h", NOTHING }, + { "mov d, l", NOTHING }, + { "mov d, m", NOTHING }, + { "mov d, a", NOTHING }, + + /* 57-5f */ + { "mov e, b", NOTHING }, + { "mov e, c", NOTHING }, + { "mov e, d", NOTHING }, + { "mov e, e", NOTHING }, + { "mov e, h", NOTHING }, + { "mov e, l", NOTHING }, + { "mov e, m", NOTHING }, + { "mov e, a", NOTHING }, + + /* 60-67 */ + { "mov h, b", NOTHING }, + { "mov h, c", NOTHING }, + { "mov h, d", NOTHING }, + { "mov h, e", NOTHING }, + { "mov h, h", NOTHING }, + { "mov h, l", NOTHING }, + { "mov h, m", NOTHING }, + { "mov h, a", NOTHING }, + + /* 67-6f */ + { "mov l, b", NOTHING }, + { "mov l, c", NOTHING }, + { "mov l, d", NOTHING }, + { "mov l, e", NOTHING }, + { "mov l, h", NOTHING }, + { "mov l, l", NOTHING }, + { "mov l, m", NOTHING }, + { "mov l, a", NOTHING }, + + /* 70-77 */ + { "mov m, b", NOTHING }, + { "mov m, c", NOTHING }, + { "mov m, d", NOTHING }, + { "mov m, e", NOTHING }, + { "mov m, h", NOTHING }, + { "mov m, l", NOTHING }, + { "mov m, m", NOTHING }, + { "hlt", NOTHING }, + + /* 77-7f */ + { "mov a, b", NOTHING }, + { "mov a, c", NOTHING }, + { "mov a, d", NOTHING }, + { "mov a, e", NOTHING }, + { "mov a, h", NOTHING }, + { "mov a, l", NOTHING }, + { "mov a, m", NOTHING }, + { "mov a, a", NOTHING }, + + /* 80-87 */ + { "add b", NOTHING }, + { "add c", NOTHING }, + { "add d", NOTHING }, + { "add e", NOTHING }, + { "add h", NOTHING }, + { "add l", NOTHING }, + { "add m", NOTHING }, + { "add a", NOTHING }, + + /* 88-8f */ + { "adc b", NOTHING }, + { "adc c", NOTHING }, + { "adc d", NOTHING }, + { "adc e", NOTHING }, + { "adc h", NOTHING }, + { "adc l", NOTHING }, + { "adc m", NOTHING }, + { "adc a", NOTHING }, + + /* 90-97 */ + { "sub b", NOTHING }, + { "sub c", NOTHING }, + { "sub d", NOTHING }, + { "sub e", NOTHING }, + { "sub h", NOTHING }, + { "sub l", NOTHING }, + { "sub m", NOTHING }, + { "sub a", NOTHING }, + + /* 98-9f */ + { "sbb b", NOTHING }, + { "sbb c", NOTHING }, + { "sbb d", NOTHING }, + { "sbb e", NOTHING }, + { "sbb h", NOTHING }, + { "sbb l", NOTHING }, + { "sbb m", NOTHING }, + { "sbb a", NOTHING }, + + /* a0-a7 */ + { "ana b", NOTHING }, + { "ana c", NOTHING }, + { "ana d", NOTHING }, + { "ana e", NOTHING }, + { "ana h", NOTHING }, + { "ana l", NOTHING }, + { "ana m", NOTHING }, + { "ana a", NOTHING }, + + /* a8-af */ + { "xra b", NOTHING }, + { "xra c", NOTHING }, + { "xra d", NOTHING }, + { "xra e", NOTHING }, + { "xra h", NOTHING }, + { "xra l", NOTHING }, + { "xra m", NOTHING }, + { "xra a", NOTHING }, + + /* b0-b7 */ + { "ora b", NOTHING }, + { "ora c", NOTHING }, + { "ora d", NOTHING }, + { "ora e", NOTHING }, + { "ora h", NOTHING }, + { "ora l", NOTHING }, + { "ora m", NOTHING }, + { "ora a", NOTHING }, + + /* b8-bf */ + { "cmp b", NOTHING }, + { "cmp c", NOTHING }, + { "cmp d", NOTHING }, + { "cmp e", NOTHING }, + { "cmp h", NOTHING }, + { "cmp l", NOTHING }, + { "cmp m", NOTHING }, + { "cmp a", NOTHING }, + + /* c0-c7 */ + { "rnz", NOTHING }, + { "pop b", NOTHING }, + { "jnz 0x%04x", CONST16 }, + { "jmp 0x%04x", CONST16 }, + { "cnz 0x%04x", CONST16 }, + { "push b", NOTHING }, + { "adi 0x%02x", CONST8 }, + { "rst 0", NOTHING }, + + /* c8-cf */ + { "rz", NOTHING }, + { "ret", NOTHING }, + { "jz 0x%04x", CONST16 }, + { "*jmp 0x%04x", CONST16 }, + { "cz 0x%04x", CONST16 }, + { "call 0x%04x", CONST16 }, + { "aci 0x%02x", CONST8 }, + { "rst 1", NOTHING }, + + /* d0-d7 */ + { "rnc", NOTHING }, + { "pop d", NOTHING }, + { "jnc 0x%04x", CONST16 }, + { "out 0x%02x", CONST8 }, + { "cnc 0x%04x", CONST16 }, + { "push d", NOTHING }, + { "sui 0x%02x", CONST8 }, + { "rst 2", NOTHING }, + + /* d8-df */ + { "rc", NOTHING }, + { "*ret", NOTHING }, + { "jc 0x%04x", CONST16 }, + { "in 0x%02x", CONST8 }, + { "cc 0x%04x", CONST16 }, + { "*call 0x%04x", CONST16 }, + { "sbi 0x%02x", CONST8 }, + { "rst 3", NOTHING }, + + /* e0-e7 */ + { "rpo", NOTHING }, + { "pop h", NOTHING }, + { "jpo 0x%04x", CONST16 }, + { "xthl", NOTHING }, + { "cpo 0x%04x", CONST16 }, + { "push h", NOTHING }, + { "ani 0x%02x", CONST8 }, + { "rst 4", NOTHING }, + + /* e8-ef */ + { "rpe", NOTHING }, + { "pchl", NOTHING }, + { "jpe 0x%04x", CONST16 }, + { "xchg", NOTHING }, + { "cpe 0x%04x", CONST16 }, + { "*call 0x%04x", CONST16 }, + { "xri 0x%02x", CONST8 }, + { "rst 5", NOTHING }, + + /* f0-f7 */ + { "rp", NOTHING }, + { "pop psw", NOTHING }, + { "jp 0x%04x", CONST16 }, + { "di", NOTHING }, + { "cp 0x%04x", CONST16 }, + { "push psw", NOTHING }, + { "ori 0x%02x", CONST8 }, + { "rst 6", NOTHING }, + + /* f8-ff */ + { "rm", NOTHING }, + { "sphl", NOTHING }, + { "jm 0x%04x", CONST16 }, + { "ei", NOTHING }, + { "cm 0x%04x", CONST16 }, + { "*call 0x%04x", CONST16 }, + { "cpi 0x%02x", CONST8 }, + { "rst 7", NOTHING }, +}; + +uint16_t i8080_disassemble(char* buffer, size_t bufsiz, uint16_t pc) +{ + uint8_t opcode = i8080_read(pc++); + struct insn* insn = &insns[opcode]; + uint16_t value = 0; + switch (insn->operand) + { + case NOTHING: + break; + + case CONST8: + value = i8080_read(pc++); + break; + + case CONST16: + value = i8080_read(pc++); + value |= i8080_read(pc++) << 8; + break; + } + + snprintf(buffer, bufsiz, insn->name, value); + return pc; +} diff --git a/plat/cpm/emu/dis8080.h b/plat/cpm/emu/dis8080.h new file mode 100644 index 000000000..5379a6104 --- /dev/null +++ b/plat/cpm/emu/dis8080.h @@ -0,0 +1,6 @@ +#ifndef DIS8080_H +#define DIS8080_H + +extern uint16_t i8080_disassemble(char* buffer, size_t bufsiz, uint16_t pc); + +#endif diff --git a/plat/cpm/emu/emulator.c b/plat/cpm/emu/emulator.c index 0bed46532..5f8dd1c43 100644 --- a/plat/cpm/emu/emulator.c +++ b/plat/cpm/emu/emulator.c @@ -5,6 +5,7 @@ #include #include #include "intel_8080_emulator.h" +#include "dis8080.h" #include "globals.h" uint8_t ram[0x10000]; @@ -63,8 +64,7 @@ void showregs(void) char buffer[80]; int tstates; uint16_t pc = i8080_read_reg16(PC); - //z80ex_dasm(buffer, sizeof(buffer), 0, &tstates, &tstates, dasm_read_cb, pc, NULL); - strcpy(buffer, ""); + i8080_disassemble(buffer, sizeof(buffer), pc); printf("%04x : %s\n", pc, buffer); } @@ -289,7 +289,7 @@ static void debug(void) if (!fgets(cmdline, sizeof(cmdline), stdin)) exit(0); - char* token = strtok(cmdline, " "); + char* token = strtok(cmdline, " \n\r\t"); if (token != NULL) { if (strcmp(token, "?") == 0)