@@ -24,6 +24,7 @@ GameGear* gamegear_create() | |||
// mmu_init(&gg->mmu, ...); | |||
z80_init(&gg->cpu, &gg->mmu); | |||
gg->powered = false; | |||
gg->exc_buffer[0] = '\0'; | |||
return gg; | |||
} | |||
@@ -57,7 +58,7 @@ void gamegear_load(GameGear *gg, ROM *rom) | |||
Powering on the GameGear executes boot code (e.g. clearing memory and | |||
setting initial register values) and starts the clock. Powering it off | |||
stops the clock. | |||
stops the clock and clears any exception data. | |||
Setting the power state to its current value has no effect. | |||
*/ | |||
@@ -71,7 +72,7 @@ void gamegear_power(GameGear *gg, bool state) | |||
z80_power(&gg->cpu); | |||
gg->last_tick = get_time_ns(); | |||
} else { | |||
// TODO: free exception buffer | |||
gg->exc_buffer[0] = '\0'; | |||
} | |||
gg->powered = state; | |||
} | |||
@@ -105,15 +106,33 @@ bool gamegear_simulate(GameGear *gg) | |||
This function returns a const pointer to a buffer holding a human-readable | |||
exception string (although it may be cryptic to an end-user). The buffer is | |||
owned by the GameGear object and should not be freed - it lasts until the | |||
GameGear's power state is changed. If no exception flag is set, this | |||
function returns NULL. | |||
owned by the GameGear object and should not be freed - its contents last | |||
until the GameGear's power state is changed. If no exception flag is set, | |||
this function returns NULL. | |||
*/ | |||
const char* gamegear_get_exception(GameGear *gg) | |||
{ | |||
#define SET_EXC(...) snprintf(gg->exc_buffer, GG_EXC_BUFF_SIZE, __VA_ARGS__); | |||
if (!gg->powered) | |||
return NULL; | |||
// TODO: return ptr to exception buffer | |||
return "Unknown exception"; | |||
if (!gg->exc_buffer[0]) { | |||
if (gg->cpu.except) { | |||
switch (gg->cpu.exc_code) { | |||
case Z80_EXC_NOT_POWERED: | |||
SET_EXC("CPU not powered") | |||
break; | |||
case Z80_EXC_UNIMPLEMENTED_OPCODE: | |||
SET_EXC("Unimplemented opcode: 0x%02X", gg->cpu.exc_data) | |||
break; | |||
default: | |||
SET_EXC("Unknown exception") | |||
break; | |||
} | |||
} else { | |||
return NULL; | |||
} | |||
} | |||
return gg->exc_buffer; | |||
#undef SET_EXC | |||
} |
@@ -10,6 +10,8 @@ | |||
#include "rom.h" | |||
#include "z80.h" | |||
#define GG_EXC_BUFF_SIZE 128 | |||
/* Structs */ | |||
typedef struct { | |||
@@ -17,6 +19,7 @@ typedef struct { | |||
Z80 cpu; | |||
bool powered; | |||
uint64_t last_tick; | |||
char exc_buffer[GG_EXC_BUFF_SIZE]; | |||
} GameGear; | |||
/* Functions */ | |||
@@ -22,6 +22,8 @@ void z80_init(Z80 *z80, MMU *mmu) | |||
{ | |||
z80->mmu = mmu; | |||
z80->except = true; | |||
z80->exc_code = Z80_EXC_NOT_POWERED; | |||
z80->exc_data = 0; | |||
} | |||
/* | |||
@@ -86,18 +88,7 @@ static inline uint8_t get_interrupt_mode(Z80 *z80) | |||
return 2; | |||
} | |||
/* ------------------------------------------------------------------------- */ | |||
static uint8_t z80_inst_nop(Z80 *z80) { | |||
z80->regfile.pc++; | |||
return 4; | |||
} | |||
static uint8_t (*instruction_lookup_table[256])(Z80*) = { | |||
z80_inst_nop | |||
}; | |||
/* ------------------------------------------------------------------------- */ | |||
#include "z80_instructions.inc" | |||
/* | |||
Emulate the given number of cycles of the Z80, or until an exception. | |||
@@ -108,18 +99,15 @@ static uint8_t (*instruction_lookup_table[256])(Z80*) = { | |||
*/ | |||
bool z80_do_cycles(Z80 *z80, double cycles) | |||
{ | |||
if (z80->except) | |||
return true; | |||
cycles -= z80->pending_cycles; | |||
while (cycles > 0) { | |||
while (cycles > 0 && !z80->except) { | |||
// uint8_t opcode = mmu_read_byte(&z80->mmu, z80->regfile.pc); | |||
uint8_t opcode = 0x00; | |||
cycles -= (*instruction_lookup_table[opcode])(z80) - 2; | |||
cycles -= (*instruction_lookup_table[opcode])(z80, opcode) - 2; | |||
} | |||
z80->pending_cycles = -cycles; | |||
return false; | |||
return z80->except; | |||
} | |||
#ifdef DEBUG_MODE | |||
@@ -8,6 +8,9 @@ | |||
#include "mmu.h" | |||
#define Z80_EXC_NOT_POWERED 0 | |||
#define Z80_EXC_UNIMPLEMENTED_OPCODE 1 | |||
/* Structs */ | |||
typedef struct { | |||
@@ -23,6 +26,7 @@ typedef struct { | |||
Z80RegFile regfile; | |||
MMU *mmu; | |||
bool except; | |||
uint8_t exc_code, exc_data; | |||
double pending_cycles; | |||
} Z80; | |||
@@ -0,0 +1,34 @@ | |||
/* Copyright (C) 2014-2015 Ben Kurtovic <ben.kurtovic@gmail.com> | |||
Released under the terms of the MIT License. See LICENSE for details. */ | |||
/* | |||
This file contains code to implement the Z80 instruction set. Since there | |||
are a lot of functions, it is kept separate from the main z80.c file. It is | |||
included in the middle of z80.c and should not be compiled separately. | |||
*/ | |||
/* | |||
0x00: NOP | |||
*/ | |||
static uint8_t z80_inst_nop(Z80 *z80, uint8_t opcode) | |||
{ | |||
(void) opcode; | |||
z80->regfile.pc++; | |||
return 4; | |||
} | |||
/* | |||
Unimplemented opcode handler. | |||
*/ | |||
static uint8_t z80_inst_ERR(Z80 *z80, uint8_t opcode) | |||
{ | |||
z80->except = true; | |||
z80->exc_code = Z80_EXC_UNIMPLEMENTED_OPCODE; | |||
z80->exc_data = opcode; | |||
return 2; | |||
} | |||
static uint8_t (*instruction_lookup_table[256])(Z80*, uint8_t) = { | |||
z80_inst_nop, | |||
z80_inst_ERR | |||
}; |