From a09242ac290d939ccf27885b730e30b5663a7adc Mon Sep 17 00:00:00 2001 From: Ben Kurtovic Date: Sun, 24 Apr 2016 19:46:50 -0500 Subject: [PATCH] Some refactoring wrt I/O; start work on VDP. --- crater.c | 4 +-- src/{iomanager.c => emulator.c} | 8 ++--- src/{iomanager.h => emulator.h} | 2 +- src/gamegear.c | 12 +++++-- src/gamegear.h | 5 ++- src/io.c | 79 +++++++++++++++++++++++++++++++++++++++++ src/io.h | 24 +++++++++++++ src/vdp.c | 76 +++++++++++++++++++++++++++++++++++++++ src/vdp.h | 26 ++++++++++++++ src/z80.c | 65 +++++++++++---------------------- src/z80.h | 6 ++-- 11 files changed, 249 insertions(+), 58 deletions(-) rename src/{iomanager.c => emulator.c} (88%) rename src/{iomanager.h => emulator.h} (84%) create mode 100644 src/io.c create mode 100644 src/io.h create mode 100644 src/vdp.c create mode 100644 src/vdp.h diff --git a/crater.c b/crater.c index 9b9527e..2cd7195 100644 --- a/crater.c +++ b/crater.c @@ -7,8 +7,8 @@ #include "src/assembler.h" #include "src/config.h" #include "src/disassembler.h" +#include "src/emulator.h" #include "src/gamegear.h" -#include "src/iomanager.h" #include "src/logging.h" #include "src/rom.h" @@ -46,7 +46,7 @@ int main(int argc, char *argv[]) printf("crater: emulating: %s\n", rom->name); gamegear_load(gg, rom); - iomanager_emulate(gg); + emulate(gg); gamegear_destroy(gg); rom_close(rom); diff --git a/src/iomanager.c b/src/emulator.c similarity index 88% rename from src/iomanager.c rename to src/emulator.c index 43d9eab..e57493a 100644 --- a/src/iomanager.c +++ b/src/emulator.c @@ -5,7 +5,7 @@ #include #include -#include "iomanager.h" +#include "emulator.h" #include "logging.h" static volatile bool caught_signal; @@ -24,12 +24,12 @@ static void handle_sigint(int sig) Block until emulation is finished. */ -void iomanager_emulate(GameGear *gg) +void emulate(GameGear *gg) { caught_signal = false; signal(SIGINT, handle_sigint); - DEBUG("IOManager powering GameGear") + DEBUG("Interface powering GameGear") gamegear_power(gg, true); // TODO: use SDL events @@ -49,7 +49,7 @@ void iomanager_emulate(GameGear *gg) z80_dump_registers(&gg->cpu); } - DEBUG("IOManager unpowering GameGear") + DEBUG("Interface unpowering GameGear") gamegear_power(gg, false); signal(SIGINT, SIG_DFL); diff --git a/src/iomanager.h b/src/emulator.h similarity index 84% rename from src/iomanager.h rename to src/emulator.h index cd71556..844f0b9 100644 --- a/src/iomanager.h +++ b/src/emulator.h @@ -7,4 +7,4 @@ /* Functions */ -void iomanager_emulate(GameGear*); +void emulate(GameGear*); diff --git a/src/gamegear.c b/src/gamegear.c index d6c71d2..5027e81 100644 --- a/src/gamegear.c +++ b/src/gamegear.c @@ -17,7 +17,10 @@ GameGear* gamegear_create() { GameGear *gg = cr_malloc(sizeof(GameGear)); mmu_init(&gg->mmu); - z80_init(&gg->cpu, &gg->mmu); + vdp_init(&gg->vdp); + io_init(&gg->io, &gg->vdp); + z80_init(&gg->cpu, &gg->mmu, &gg->io); + gg->powered = false; gg->exc_buffer[0] = '\0'; return gg; @@ -31,6 +34,7 @@ GameGear* gamegear_create() void gamegear_destroy(GameGear *gg) { mmu_free(&gg->mmu); + vdp_free(&gg->vdp); free(gg); } @@ -65,6 +69,8 @@ void gamegear_power(GameGear *gg, bool state) if (state) { mmu_power(&gg->mmu); + vdp_power(&gg->vdp); + io_power(&gg->io); z80_power(&gg->cpu); gg->last_tick = get_time_ns(); } else { @@ -121,8 +127,8 @@ const char* gamegear_get_exception(GameGear *gg) case Z80_EXC_UNIMPLEMENTED_OPCODE: SET_EXC("unimplemented opcode: 0x%02X", gg->cpu.exc_data) break; - case Z80_EXC_UNIMPLEMENTED_PORT: - SET_EXC("unimplemented port: 0x%02X", gg->cpu.exc_data) + case Z80_EXC_IO_ERROR: + SET_EXC("I/O error on port 0x%02X", gg->cpu.exc_data) break; default: SET_EXC("unknown exception") diff --git a/src/gamegear.h b/src/gamegear.h index c9c9bd8..707335a 100644 --- a/src/gamegear.h +++ b/src/gamegear.h @@ -6,6 +6,7 @@ #include #include +#include "io.h" #include "mmu.h" #include "rom.h" #include "z80.h" @@ -15,8 +16,10 @@ /* Structs */ typedef struct { - MMU mmu; Z80 cpu; + MMU mmu; + VDP vdp; + IO io; bool powered; uint64_t last_tick; char exc_buffer[GG_EXC_BUFF_SIZE]; diff --git a/src/io.c b/src/io.c new file mode 100644 index 0000000..885bc6d --- /dev/null +++ b/src/io.c @@ -0,0 +1,79 @@ +/* Copyright (C) 2014-2016 Ben Kurtovic + Released under the terms of the MIT License. See LICENSE for details. */ + +#include "io.h" + +/* + Initialize an IO object. +*/ +void io_init(IO *io, VDP *vdp) +{ + io->vdp = vdp; +} + +void io_power(IO *io) +{ + io->except = false; +} + +/* + Read and return a byte from the given port. +*/ +uint8_t io_port_read(IO *io, uint8_t port) +{ + if (port <= 0x06) { + // TODO: GG specific registers; initial state: C0 7F FF 00 FF 00 FF + } else if (port <= 0x3F) { + return 0xFF; + } else if (port <= 0x7F && !(port % 2)) { + // TODO: Return the V counter + } else if (port <= 0x7F) { + // TODO: Return the H counter + } else if (port <= 0xBF && !(port % 2)) { + return vdp_read_data(io->vdp); + } else if (port <= 0xBF) { + return vdp_read_control(io->vdp); + } else if (port == 0xCD || port == 0xDC) { + // TODO: Return the I/O port A/B register + } else if (port == 0xC1 || port == 0xDD) { + // TODO: Return the I/O port B/misc. register + } else { + return 0xFF; + } + + io->except = true; + io->exc_port = port; + return 0; +} + +/* + Write a byte to the given port. +*/ +void io_port_write(IO *io, uint8_t port, uint8_t value) +{ + if (port <= 0x06) { + // TODO: GG specific registers; initial state: C0 7F FF 00 FF 00 FF + goto except; + } else if (port <= 0x3F && !(port % 2)) { + // TODO: Write to memory control register + goto except; + } else if (port <= 0x3F) { + // TODO: Write to I/O control register + goto except; + } else if (port <= 0x7F) { + // TODO: Write to the SN76489 PSG + goto except; + } else if (port <= 0xBF && !(port % 2)) { + vdp_write_data(io->vdp, value); + } else if (port <= 0xBF) { + vdp_write_control(io->vdp, value); + } else { + return; + } + + return; + + except: + io->except = true; + io->exc_port = port; +} diff --git a/src/io.h b/src/io.h new file mode 100644 index 0000000..350a5f7 --- /dev/null +++ b/src/io.h @@ -0,0 +1,24 @@ +/* Copyright (C) 2014-2016 Ben Kurtovic + Released under the terms of the MIT License. See LICENSE for details. */ + +#pragma once + +#include +#include + +#include "vdp.h" + +/* Structs */ + +typedef struct { + VDP *vdp; + bool except; + uint8_t exc_port; +} IO; + +/* Functions */ + +void io_init(IO*, VDP*); +void io_power(IO*); +uint8_t io_port_read(IO*, uint8_t); +void io_port_write(IO*, uint8_t, uint8_t); diff --git a/src/vdp.c b/src/vdp.c new file mode 100644 index 0000000..335a108 --- /dev/null +++ b/src/vdp.c @@ -0,0 +1,76 @@ +/* Copyright (C) 2014-2016 Ben Kurtovic + Released under the terms of the MIT License. See LICENSE for details. */ + +#include "vdp.h" + +/* + Initialize the Video Display Processor (VDP). +*/ +void vdp_init(VDP *vdp) +{ + // TODO +} + +/* + Free memory previously allocated by the VDP. +*/ +void vdp_free(VDP *vdp) +{ + // TODO +} + +/* + Power on the VDP, setting up initial state. +*/ +void vdp_power(VDP *vdp) +{ + vdp->control_code = 0; + vdp->control_addr = 0; + vdp->control_flag = false; +} + +/* + Read a byte from the VDP's control port. +*/ +uint8_t vdp_read_control(VDP *vdp) +{ + vdp->control_flag = false; + + // TODO + return 0; +} + +/* + Read a byte from the VDP's data port. +*/ +uint8_t vdp_read_data(VDP *vdp) +{ + vdp->control_flag = false; + + // TODO + return 0; +} + +/* + Write a byte into the VDP's control port. +*/ +void vdp_write_control(VDP *vdp, uint8_t byte) +{ + if (!vdp->control_flag) { // First byte + vdp->control_addr = (vdp->control_addr & 0x3F00) + byte; + } else { // Second byte + vdp->control_addr = ((byte & 0x3F) << 8) + (vdp->control_addr & 0xFF); + vdp->control_code = byte >> 6; + } + vdp->control_flag = !vdp->control_flag; +} + +/* + Write a byte into the VDP's data port. +*/ +void vdp_write_data(VDP *vdp, uint8_t byte) +{ + vdp->control_flag = false; + + // TODO +} diff --git a/src/vdp.h b/src/vdp.h new file mode 100644 index 0000000..7422f99 --- /dev/null +++ b/src/vdp.h @@ -0,0 +1,26 @@ +/* Copyright (C) 2014-2016 Ben Kurtovic + Released under the terms of the MIT License. See LICENSE for details. */ + +#pragma once + +#include +#include + +/* Structs */ + +typedef struct { + uint8_t control_code; + uint16_t control_addr; + bool control_flag; +} VDP; + +/* Functions */ + +void vdp_init(VDP*); +void vdp_free(VDP*); +void vdp_power(VDP*); + +uint8_t vdp_read_control(VDP*); +uint8_t vdp_read_data(VDP*); +void vdp_write_control(VDP*, uint8_t); +void vdp_write_data(VDP*, uint8_t); diff --git a/src/z80.c b/src/z80.c index 893a4ee..e108dac 100644 --- a/src/z80.c +++ b/src/z80.c @@ -42,9 +42,10 @@ Register values are invalid until z80_power() is called. No other Z80 functions should be called before it. */ -void z80_init(Z80 *z80, MMU *mmu) +void z80_init(Z80 *z80, MMU *mmu, IO *io) { z80->mmu = mmu; + z80->io = io; z80->except = true; z80->exc_code = Z80_EXC_NOT_POWERED; z80->exc_data = 0; @@ -180,60 +181,34 @@ static inline uint16_t stack_pop(Z80 *z80) } /* - Read and return a byte from the given port. + Check for errors after an I/O operation. */ -static uint8_t read_port(Z80 *z80, uint8_t port) +static void handle_io_errors(Z80 *z80) { - if (port < 0x06) { - // TODO: GG specific registers; initial state: C0 7F FF 00 FF 00 FF - } else if (port < 0x3F) { - return 0xFF; - } else if (port < 0x7F && !(port % 2)) { - // TODO: Return the V counter - } else if (port < 0x7F) { - // TODO: Return the H counter - } else if (port < 0xBF && !(port % 2)) { - // TODO: Return the VDP data port contents - } else if (port < 0xBF) { - // TODO: Return the VDP status flags - } else if (port == 0xCD || port == 0xDC) { - // TODO: Return the I/O port A/B register - } else if (port == 0xC1 || port == 0xDD) { - // TODO: Return the I/O port B/misc. register - } else { - return 0xFF; + if (z80->io->except) { + z80->except = true; + z80->exc_code = Z80_EXC_IO_ERROR; + z80->exc_data = z80->io->exc_port; } +} - z80->except = true; - z80->exc_code = Z80_EXC_UNIMPLEMENTED_PORT; - z80->exc_data = port; - return 0; +/* + Read and return a byte from the given port, and check for errors. +*/ +static uint8_t read_port(Z80 *z80, uint8_t port) +{ + uint8_t value = io_port_read(z80->io, port); + handle_io_errors(z80); + return value; } /* - Write a byte to the given port. + Write a byte to the given port, and check for errors. */ static void write_port(Z80 *z80, uint8_t port, uint8_t value) { - if (port < 0x06) { - // TODO: GG specific registers; initial state: C0 7F FF 00 FF 00 FF - } else if (port < 0x3F && !(port % 2)) { - // TODO: Write to memory control register - } else if (port < 0x3F) { - // TODO: Write to I/O control register - } else if (port < 0x7F) { - // TODO: Write to the SN76489 PSG - } else if (port < 0xBF && !(port % 2)) { - // TODO: Write to the VDP data port - } else if (port < 0xBF) { - // TODO: Write to the VDP control port - } else { - return; - } - - z80->except = true; - z80->exc_code = Z80_EXC_UNIMPLEMENTED_PORT; - z80->exc_data = port; + io_port_write(z80->io, port, value); + handle_io_errors(z80); } /* diff --git a/src/z80.h b/src/z80.h index 4d6bdd5..f3c50e7 100644 --- a/src/z80.h +++ b/src/z80.h @@ -6,11 +6,12 @@ #include #include +#include "io.h" #include "mmu.h" #define Z80_EXC_NOT_POWERED 0 #define Z80_EXC_UNIMPLEMENTED_OPCODE 1 -#define Z80_EXC_UNIMPLEMENTED_PORT 2 +#define Z80_EXC_IO_ERROR 2 /* Structs */ @@ -32,6 +33,7 @@ typedef struct { typedef struct { Z80RegFile regfile; MMU *mmu; + IO *io; bool except; uint8_t exc_code, exc_data; double pending_cycles; @@ -40,7 +42,7 @@ typedef struct { /* Functions */ -void z80_init(Z80*, MMU*); +void z80_init(Z80*, MMU*, IO*); void z80_power(Z80*); bool z80_do_cycles(Z80*, double); void z80_dump_registers(const Z80*);