diff --git a/src/gamegear.c b/src/gamegear.c index 5fc760e..6426c12 100644 --- a/src/gamegear.c +++ b/src/gamegear.c @@ -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 } diff --git a/src/gamegear.h b/src/gamegear.h index 759714f..4577aa2 100644 --- a/src/gamegear.h +++ b/src/gamegear.h @@ -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 */ diff --git a/src/z80.c b/src/z80.c index f572548..3b6e5ad 100644 --- a/src/z80.c +++ b/src/z80.c @@ -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 diff --git a/src/z80.h b/src/z80.h index 2b17b0d..cd35501 100644 --- a/src/z80.h +++ b/src/z80.h @@ -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; diff --git a/src/z80_instructions.inc b/src/z80_instructions.inc new file mode 100644 index 0000000..8523f59 --- /dev/null +++ b/src/z80_instructions.inc @@ -0,0 +1,34 @@ +/* Copyright (C) 2014-2015 Ben Kurtovic + 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 +};