Browse Source

Add full exception handling logic.

master
Ben Kurtovic 9 years ago
parent
commit
b21e0ed35a
5 changed files with 73 additions and 25 deletions
  1. +26
    -7
      src/gamegear.c
  2. +3
    -0
      src/gamegear.h
  3. +6
    -18
      src/z80.c
  4. +4
    -0
      src/z80.h
  5. +34
    -0
      src/z80_instructions.inc

+ 26
- 7
src/gamegear.c View File

@@ -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
}

+ 3
- 0
src/gamegear.h View File

@@ -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 */


+ 6
- 18
src/z80.c View File

@@ -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


+ 4
- 0
src/z80.h View File

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



+ 34
- 0
src/z80_instructions.inc View File

@@ -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
};

Loading…
Cancel
Save