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