| @@ -7,8 +7,8 @@ | |||||
| #include "src/assembler.h" | #include "src/assembler.h" | ||||
| #include "src/config.h" | #include "src/config.h" | ||||
| #include "src/disassembler.h" | #include "src/disassembler.h" | ||||
| #include "src/emulator.h" | |||||
| #include "src/gamegear.h" | #include "src/gamegear.h" | ||||
| #include "src/iomanager.h" | |||||
| #include "src/logging.h" | #include "src/logging.h" | ||||
| #include "src/rom.h" | #include "src/rom.h" | ||||
| @@ -46,7 +46,7 @@ int main(int argc, char *argv[]) | |||||
| printf("crater: emulating: %s\n", rom->name); | printf("crater: emulating: %s\n", rom->name); | ||||
| gamegear_load(gg, rom); | gamegear_load(gg, rom); | ||||
| iomanager_emulate(gg); | |||||
| emulate(gg); | |||||
| gamegear_destroy(gg); | gamegear_destroy(gg); | ||||
| rom_close(rom); | rom_close(rom); | ||||
| @@ -5,7 +5,7 @@ | |||||
| #include <stdbool.h> | #include <stdbool.h> | ||||
| #include <unistd.h> | #include <unistd.h> | ||||
| #include "iomanager.h" | |||||
| #include "emulator.h" | |||||
| #include "logging.h" | #include "logging.h" | ||||
| static volatile bool caught_signal; | static volatile bool caught_signal; | ||||
| @@ -24,12 +24,12 @@ static void handle_sigint(int sig) | |||||
| Block until emulation is finished. | Block until emulation is finished. | ||||
| */ | */ | ||||
| void iomanager_emulate(GameGear *gg) | |||||
| void emulate(GameGear *gg) | |||||
| { | { | ||||
| caught_signal = false; | caught_signal = false; | ||||
| signal(SIGINT, handle_sigint); | signal(SIGINT, handle_sigint); | ||||
| DEBUG("IOManager powering GameGear") | |||||
| DEBUG("Interface powering GameGear") | |||||
| gamegear_power(gg, true); | gamegear_power(gg, true); | ||||
| // TODO: use SDL events | // TODO: use SDL events | ||||
| @@ -49,7 +49,7 @@ void iomanager_emulate(GameGear *gg) | |||||
| z80_dump_registers(&gg->cpu); | z80_dump_registers(&gg->cpu); | ||||
| } | } | ||||
| DEBUG("IOManager unpowering GameGear") | |||||
| DEBUG("Interface unpowering GameGear") | |||||
| gamegear_power(gg, false); | gamegear_power(gg, false); | ||||
| signal(SIGINT, SIG_DFL); | signal(SIGINT, SIG_DFL); | ||||
| @@ -7,4 +7,4 @@ | |||||
| /* Functions */ | /* Functions */ | ||||
| void iomanager_emulate(GameGear*); | |||||
| void emulate(GameGear*); | |||||
| @@ -17,7 +17,10 @@ GameGear* gamegear_create() | |||||
| { | { | ||||
| GameGear *gg = cr_malloc(sizeof(GameGear)); | GameGear *gg = cr_malloc(sizeof(GameGear)); | ||||
| mmu_init(&gg->mmu); | 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->powered = false; | ||||
| gg->exc_buffer[0] = '\0'; | gg->exc_buffer[0] = '\0'; | ||||
| return gg; | return gg; | ||||
| @@ -31,6 +34,7 @@ GameGear* gamegear_create() | |||||
| void gamegear_destroy(GameGear *gg) | void gamegear_destroy(GameGear *gg) | ||||
| { | { | ||||
| mmu_free(&gg->mmu); | mmu_free(&gg->mmu); | ||||
| vdp_free(&gg->vdp); | |||||
| free(gg); | free(gg); | ||||
| } | } | ||||
| @@ -65,6 +69,8 @@ void gamegear_power(GameGear *gg, bool state) | |||||
| if (state) { | if (state) { | ||||
| mmu_power(&gg->mmu); | mmu_power(&gg->mmu); | ||||
| vdp_power(&gg->vdp); | |||||
| io_power(&gg->io); | |||||
| z80_power(&gg->cpu); | z80_power(&gg->cpu); | ||||
| gg->last_tick = get_time_ns(); | gg->last_tick = get_time_ns(); | ||||
| } else { | } else { | ||||
| @@ -121,8 +127,8 @@ const char* gamegear_get_exception(GameGear *gg) | |||||
| case Z80_EXC_UNIMPLEMENTED_OPCODE: | case Z80_EXC_UNIMPLEMENTED_OPCODE: | ||||
| SET_EXC("unimplemented opcode: 0x%02X", gg->cpu.exc_data) | SET_EXC("unimplemented opcode: 0x%02X", gg->cpu.exc_data) | ||||
| break; | 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; | break; | ||||
| default: | default: | ||||
| SET_EXC("unknown exception") | SET_EXC("unknown exception") | ||||
| @@ -6,6 +6,7 @@ | |||||
| #include <stdbool.h> | #include <stdbool.h> | ||||
| #include <stdint.h> | #include <stdint.h> | ||||
| #include "io.h" | |||||
| #include "mmu.h" | #include "mmu.h" | ||||
| #include "rom.h" | #include "rom.h" | ||||
| #include "z80.h" | #include "z80.h" | ||||
| @@ -15,8 +16,10 @@ | |||||
| /* Structs */ | /* Structs */ | ||||
| typedef struct { | typedef struct { | ||||
| MMU mmu; | |||||
| Z80 cpu; | Z80 cpu; | ||||
| MMU mmu; | |||||
| VDP vdp; | |||||
| IO io; | |||||
| bool powered; | bool powered; | ||||
| uint64_t last_tick; | uint64_t last_tick; | ||||
| char exc_buffer[GG_EXC_BUFF_SIZE]; | 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 | Register values are invalid until z80_power() is called. No other Z80 | ||||
| functions should be called before it. | 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->mmu = mmu; | ||||
| z80->io = io; | |||||
| z80->except = true; | z80->except = true; | ||||
| z80->exc_code = Z80_EXC_NOT_POWERED; | z80->exc_code = Z80_EXC_NOT_POWERED; | ||||
| z80->exc_data = 0; | 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) | 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 <stdbool.h> | ||||
| #include <stdint.h> | #include <stdint.h> | ||||
| #include "io.h" | |||||
| #include "mmu.h" | #include "mmu.h" | ||||
| #define Z80_EXC_NOT_POWERED 0 | #define Z80_EXC_NOT_POWERED 0 | ||||
| #define Z80_EXC_UNIMPLEMENTED_OPCODE 1 | #define Z80_EXC_UNIMPLEMENTED_OPCODE 1 | ||||
| #define Z80_EXC_UNIMPLEMENTED_PORT 2 | |||||
| #define Z80_EXC_IO_ERROR 2 | |||||
| /* Structs */ | /* Structs */ | ||||
| @@ -32,6 +33,7 @@ typedef struct { | |||||
| typedef struct { | typedef struct { | ||||
| Z80RegFile regfile; | Z80RegFile regfile; | ||||
| MMU *mmu; | MMU *mmu; | ||||
| IO *io; | |||||
| bool except; | bool except; | ||||
| uint8_t exc_code, exc_data; | uint8_t exc_code, exc_data; | ||||
| double pending_cycles; | double pending_cycles; | ||||
| @@ -40,7 +42,7 @@ typedef struct { | |||||
| /* Functions */ | /* Functions */ | ||||
| void z80_init(Z80*, MMU*); | |||||
| void z80_init(Z80*, MMU*, IO*); | |||||
| void z80_power(Z80*); | void z80_power(Z80*); | ||||
| bool z80_do_cycles(Z80*, double); | bool z80_do_cycles(Z80*, double); | ||||
| void z80_dump_registers(const Z80*); | void z80_dump_registers(const Z80*); | ||||