/** * PAT80 Emulator * * Emulates a PAT80. */ #include /* Z_NULL */ #include #include #include #include #define ROM_SIZE 0x8000 /* 32 KiB */ #define MEMORY_SIZE 0xFFFF /* 64 KiB */ typedef struct { void* context; zuint8 (* read)(void *context); void (* write)(void *context, zuint8 value); } Device; typedef struct { zusize cycles; zuint8 memory[65536]; Z80 cpu; } Machine; static zuint8 machine_cpu_read(Machine *self, zuint16 address) { return address < MEMORY_SIZE ? self->memory[address] : 0xFF; } static void machine_cpu_write(Machine *self, zuint16 address, zuint8 value) { if (address >= ROM_SIZE && address < MEMORY_SIZE) self->memory[address] = value; } static zuint8 machine_cpu_in(Machine *self, zuint16 port) { // Pat80 has 8 devices, decoded based on the 3 most significant IO addr bits. // Note the Z80 has 16 bit address bus, but only the first 8 are used as IO addr, // so the 3 most significant IO addr bits in this case are A7, A6, A5. The bits // A4-A0 may be used by the single device, at its own discretion. zuint16 bitmask = 7; // 0000000000000111 int decoded = port & bitmask; if (decoded <= 0x1F) { // Port 0 (0x00 to 0x1F): terminal // Read char from stin return getch(); } if (decoded <= 0x3F) { // Port 1 (0x20 to 0x3F): sound card (sn76489) printw("\nsound_cmd[IN]: Not supported!\n"); return 0x00; } if (decoded <= 0x5F) { // Port 2 (0x40 to 0x5F) printw("IO_ERROR_IN: No device at port 2\n"); return 0x00; } if (decoded <= 0x7F) { // Port 3 (0x60 to 0x7F) printw("IO_ERROR_IN: No device at port 3\n"); return 0x00; } if (decoded <= 0x9F) { // Port 4 (0x80 to 0x9F) printw("IO_ERROR_IN: No device at port 4\n"); return 0x00; } if (decoded <= 0x5F) { // Port 5 (0xA0 to 0xBF) printw("IO_ERROR_IN: No device at port 5\n"); return 0x00; } if (decoded <= 0x5F) { // Port 6 (0xC0 to 0xDF) printw("IO_ERROR_IN: No device at port 6\n"); return 0x00; } if (decoded <= 0x5F) { // Port 7 (0xE0 to 0xFF) printw("IO_ERROR_IN: No device at port 7\n"); } else { printw("IO_ERROR_IN: Invalid port address: %#04x\n", port); } } static void machine_cpu_out(Machine *self, zuint16 port, zuint8 value) { // Pat80 has 8 devices, decoded based on the 3 most significant IO addr bits. // Note the Z80 has 16 bit address bus, but only the first 8 are used as IO addr, // so the 3 most significant IO addr bits in this case are A7, A6, A5. The bits // A4-A0 may be used by the single device, at its own discretion. zuint16 bitmask = 0xE0; // 0000000011100000 int decoded = port & bitmask; if (decoded <= 0x1F) { // Port 0 (0x00 to 0x1F): terminal attron(A_BOLD); printw("%c", value); attroff(A_BOLD); } else if (decoded <= 0x3F) { // Port 1 (0x20 to 0x3F): sound card (sn76489) printw("\nsound_cmd[%#04x]\n", value); } else if (decoded <= 0x5F) { // Port 2 (0x40 to 0x5F) printw("IO_ERROR_OUT: No device at port 2\n"); } else if (decoded <= 0x7F) { // Port 3 (0x60 to 0x7F) printw("IO_ERROR_OUT: No device at port 3\n"); } else if (decoded <= 0x9F) { // Port 4 (0x80 to 0x9F) printw("IO_ERROR_OUT: No device at port 4\n"); } else if (decoded <= 0x5F) { // Port 5 (0xA0 to 0xBF) printw("IO_ERROR_OUT: No device at port 5\n"); } else if (decoded <= 0x5F) { // Port 6 (0xC0 to 0xDF) printw("IO_ERROR_OUT: No device at port 6\n"); } else if (decoded <= 0x5F) { // Port 7 (0xE0 to 0xFF) printw("IO_ERROR_OUT: No device at port 7\n"); } else { printw("IO_ERROR_OUT: Invalid port address: %#04x\n", port); } } void machine_initialize(Machine *self) { self->cpu.context = self; self->cpu.fetch_opcode = self->cpu.fetch = self->cpu.nop = self->cpu.read = (Z80Read )machine_cpu_read; self->cpu.write = (Z80Write)machine_cpu_write; self->cpu.in = (Z80Read )machine_cpu_in; self->cpu.out = (Z80Write)machine_cpu_out; self->cpu.halt = Z_NULL; self->cpu.nmia = Z_NULL; self->cpu.inta = Z_NULL; self->cpu.int_fetch = Z_NULL; self->cpu.ld_i_a = Z_NULL; self->cpu.ld_r_a = Z_NULL; self->cpu.reti = Z_NULL; self->cpu.retn = Z_NULL; self->cpu.hook = Z_NULL; self->cpu.illegal = Z_NULL; self->cpu.options = Z80_MODEL_ZILOG_NMOS; } void machine_power(Machine *self, zbool state) { if (state) { self->cycles = 0; memset(self->memory, 0, MEMORY_SIZE); } z80_power(&self->cpu, state); } void machine_reset(Machine *self) { z80_instant_reset(&self->cpu); } void machine_run(Machine *self) { z80_run(&self->cpu, Z80_MAXIMUM_CYCLES); } int main(int argc, char *argv[]) { // Parse arguments if (argc < 2) { printf("Usage: %s [romFile]\n", argv[0]); exit(0); } char* romFilePath = argv[1]; // Setup virtual Pat80 computer Z80 pat80Cpu = {}; Machine pat80 = { /*zusize*/ .cycles = 0, /*Z80*/ .cpu = pat80Cpu }; machine_initialize(&pat80); machine_power(&pat80, Z_TRUE); // Load ROM into memory FILE *romFile; romFile = fopen(romFilePath,"rb"); if (romFile == NULL) { printf("Unable to open rom file at %s", romFilePath); exit(1); } fread(&pat80.memory,ROM_SIZE,1,romFile); // load rom from file into memory, up > fclose(romFile); // Init ncurses initscr(); // Start curses mode raw(); // Line buffering disabled (get character without waiting for ENTER key) keypad(stdscr, TRUE); // We get F1, F2 etc.. noecho(); // Don't echo() while we do getch scrollok(stdscr, TRUE); // Allow scrolling when reached end of window // Start emulated computer machine_reset(&pat80); machine_run(&pat80); // Stop ncurses endwin(); /* End curses mode */ return 0; }