#include /* Z_NULL */ #include #include #include #include #define CYCLES_PER_FRAME 69888 #define CYCLES_AT_INT 24 #define CYCLES_PER_INT 32 #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; Device* devices; zusize device_count; } 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 //printf("TERMINAL_READ"); //return 'H'; return 0x00; } if (decoded <= 0x3F) { // Port 1 (0x20 to 0x3F): sound card (sn76489) printf("\nsound_cmd[IN]: Not supported!\n"); return 0x00; } if (decoded <= 0x5F) { // Port 2 (0x40 to 0x5F) printf("IO_ERROR_IN: No device at port 2\n"); return 0x00; } if (decoded <= 0x7F) { // Port 3 (0x60 to 0x7F) printf("IO_ERROR_IN: No device at port 3\n"); return 0x00; } if (decoded <= 0x9F) { // Port 4 (0x80 to 0x9F) printf("IO_ERROR_IN: No device at port 4\n"); return 0x00; } if (decoded <= 0x5F) { // Port 5 (0xA0 to 0xBF) printf("IO_ERROR_IN: No device at port 5\n"); return 0x00; } if (decoded <= 0x5F) { // Port 6 (0xC0 to 0xDF) printf("IO_ERROR_IN: No device at port 6\n"); return 0x00; } if (decoded <= 0x5F) { // Port 7 (0xE0 to 0xFF) printf("IO_ERROR_IN: No device at port 7\n"); } else { printf("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 printf("%c", value); } else if (decoded <= 0x3F) { // Port 1 (0x20 to 0x3F): sound card (sn76489) printf("\nsound_cmd[%#04x]\n", value); } else if (decoded <= 0x5F) { // Port 2 (0x40 to 0x5F) printf("IO_ERROR_OUT: No device at port 2\n"); } else if (decoded <= 0x7F) { // Port 3 (0x60 to 0x7F) printf("IO_ERROR_OUT: No device at port 3\n"); } else if (decoded <= 0x9F) { // Port 4 (0x80 to 0x9F) printf("IO_ERROR_OUT: No device at port 4\n"); } else if (decoded <= 0x5F) { // Port 5 (0xA0 to 0xBF) printf("IO_ERROR_OUT: No device at port 5\n"); } else if (decoded <= 0x5F) { // Port 6 (0xC0 to 0xDF) printf("IO_ERROR_OUT: No device at port 6\n"); } else if (decoded <= 0x5F) { // Port 7 (0xE0 to 0xFF) printf("IO_ERROR_OUT: No device at port 7\n"); } else { printf("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; /* Create and initialize devices... */ } 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); } // void machine_run_frame(Machine *self) // { // /* CPU cycles before the INT signal */ // self->cycles += z80_execute(&self->cpu, CYCLES_AT_INT - self->cycles); // /* CPU cycles during the INT signal */ // z80_int(&self->cpu, Z_TRUE); // self->cycles += z80_run(&self->cpu, (CYCLES_AT_INT + CYCLES_PER_INT) - self->cycles); // z80_int(&self->cpu, Z_FALSE); // /* CPU cycles after the INT signal */ // self->cycles += z80_execute(&self->cpu, CYCLES_PER_FRAME - self->cycles); // self->cycles -= CYCLES_PER_FRAME; // } // TEST IMPLEM DEVICE zuint8 device_terminal_read(void *context) { return 'H'; //return 0; } void device_terminal_write(void *context, zuint8 value) { printf("%c", value); } zuint8 device_sound_read(void *context) { return 0; } void device_sound_write(void *context, zuint8 value) { printf("sound[%c]\n", value); } // TEST IMPLEM DEVICE 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 = 100000, /*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); // Declare system devices Device patoTerminal = { .read = *device_terminal_read, .write = *device_terminal_write, }; Device patoSound = { .read = *device_sound_read, .write = *device_sound_write, }; Device patoDevices[2] = { patoTerminal, // Port 0 (0x00 to 0x1F) patoSound // Port 1 (0x20 to 0x3F) }; pat80.devices = patoDevices; pat80.device_count = 2; machine_reset(&pat80); machine_run(&pat80); }