| @@ -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); | |||
| @@ -5,7 +5,7 @@ | |||
| #include <stdbool.h> | |||
| #include <unistd.h> | |||
| #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); | |||
| @@ -7,4 +7,4 @@ | |||
| /* Functions */ | |||
| void iomanager_emulate(GameGear*); | |||
| void emulate(GameGear*); | |||
| @@ -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") | |||
| @@ -6,6 +6,7 @@ | |||
| #include <stdbool.h> | |||
| #include <stdint.h> | |||
| #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]; | |||
| @@ -0,0 +1,79 @@ | |||
| /* Copyright (C) 2014-2016 Ben Kurtovic <ben.kurtovic@gmail.com> | |||
| 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; | |||
| } | |||
| @@ -0,0 +1,24 @@ | |||
| /* Copyright (C) 2014-2016 Ben Kurtovic <ben.kurtovic@gmail.com> | |||
| Released under the terms of the MIT License. See LICENSE for details. */ | |||
| #pragma once | |||
| #include <stdbool.h> | |||
| #include <stdint.h> | |||
| #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); | |||
| @@ -0,0 +1,76 @@ | |||
| /* Copyright (C) 2014-2016 Ben Kurtovic <ben.kurtovic@gmail.com> | |||
| 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 | |||
| } | |||
| @@ -0,0 +1,26 @@ | |||
| /* Copyright (C) 2014-2016 Ben Kurtovic <ben.kurtovic@gmail.com> | |||
| Released under the terms of the MIT License. See LICENSE for details. */ | |||
| #pragma once | |||
| #include <stdbool.h> | |||
| #include <stdint.h> | |||
| /* 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); | |||
| @@ -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); | |||
| } | |||
| /* | |||
| @@ -6,11 +6,12 @@ | |||
| #include <stdbool.h> | |||
| #include <stdint.h> | |||
| #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*); | |||