#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) { int word; int offset = 1; 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); for (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); }