@@ -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*); |