瀏覽代碼

Add proper timing code, etc.

master
Ben Kurtovic 9 年之前
父節點
當前提交
a202baa3a6
共有 7 個檔案被更改,包括 120 行新增19 行删除
  1. +38
    -5
      src/gamegear.c
  2. +4
    -4
      src/gamegear.h
  3. +7
    -5
      src/iomanager.c
  4. +31
    -0
      src/util.c
  5. +1
    -0
      src/util.h
  6. +32
    -3
      src/z80.c
  7. +7
    -2
      src/z80.h

+ 38
- 5
src/gamegear.c 查看文件

@@ -5,6 +5,10 @@

#include "gamegear.h"
#include "logging.h"
#include "util.h"

/* Clock speed in Hz was taken from the official Sega GG documentation */
#define CPU_CLOCK_SPEED 3579545

/*
Create and return a pointer to a new GameGear object.
@@ -18,7 +22,7 @@ GameGear* gamegear_create()
OUT_OF_MEMORY()

// mmu_init(&gg->mmu, ...);
z80_init(&gg->cpu, CPU_CLOCK_SPEED);
z80_init(&gg->cpu, &gg->mmu);
gg->powered = false;
return gg;
}
@@ -30,6 +34,7 @@ GameGear* gamegear_create()
*/
void gamegear_destroy(GameGear *gg)
{
// mmu_free(&gg->mmu);
free(gg);
}

@@ -64,6 +69,9 @@ void gamegear_power(GameGear *gg, bool state)
if (state) {
// mmu_power(&gg->mmu);
z80_power(&gg->cpu);
gg->last_tick = get_time_ns();
} else {
// TODO: free exception buffer
}
gg->powered = state;
}
@@ -75,12 +83,37 @@ void gamegear_power(GameGear *gg, bool state)
time since the last call to gamegear_simulate() or gamegear_power() if the
system was just powered on. If the system is powered off, this function
does nothing.

The return value indicates whether an exception flag has been set
somewhere. If true, emulation must be stopped. gamegear_get_exception() can
be used to fetch exception information. Power-cycling the GameGear with
gamegear_power(gg, false) followed by gamegear_power(gg, true) will reset
the exception flag and allow emulation to restart normally.
*/
void gamegear_simulate(GameGear *gg)
bool gamegear_simulate(GameGear *gg)
{
if (!gg->powered)
return;
return false;

uint64_t last = gg->last_tick, tick;
tick = gg->last_tick = get_time_ns();
return z80_do_cycles(&gg->cpu, (tick - last) * CPU_CLOCK_SPEED / 1e9);
}

/*
If an exception flag has been set in the GameGear, return the reason.

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.
*/
const char* gamegear_get_exception(GameGear *gg)
{
if (!gg->powered)
return NULL;

// TODO
// z80_do_cycles(&gg->cpu, ...);
// TODO: return ptr to exception buffer
return "Unknown exception";
}

+ 4
- 4
src/gamegear.h 查看文件

@@ -4,20 +4,19 @@
#pragma once

#include <stdbool.h>
#include <stdint.h>

#include "mmu.h"
#include "rom.h"
#include "z80.h"

/* Clock speed in Hz was taken from the official Sega GG documentation */
#define CPU_CLOCK_SPEED 3579545

/* Structs */

typedef struct {
MMU mmu;
Z80 cpu;
bool powered;
uint64_t last_tick;
} GameGear;

/* Functions */
@@ -26,4 +25,5 @@ GameGear* gamegear_create();
void gamegear_destroy(GameGear*);
void gamegear_load(GameGear*, ROM*);
void gamegear_power(GameGear*, bool);
void gamegear_simulate(GameGear*);
bool gamegear_simulate(GameGear*);
const char* gamegear_get_exception(GameGear*);

+ 7
- 5
src/iomanager.c 查看文件

@@ -16,13 +16,15 @@ void iomanager_emulate(GameGear *gg)
DEBUG("IOManager powering GameGear")
gamegear_power(gg, true);

#ifdef DEBUG_MODE
z80_dump_registers(&gg->cpu);
#endif

// TODO: use SDL events
while (1) {
gamegear_simulate(gg);
if (gamegear_simulate(gg)) {
DEBUG("IOManager caught exception: %s", gamegear_get_exception(gg))
#ifdef DEBUG_MODE
z80_dump_registers(&gg->cpu);
#endif
break;
}
usleep(1000 * 1000 / 60);
}



+ 31
- 0
src/util.c 查看文件

@@ -3,6 +3,19 @@

#include "util.h"

#if defined __APPLE__
#include <mach/mach_time.h>
#elif defined __linux__
#include <time.h>
#ifndef CLOCK_MONOTONIC
#error "Unsupported operating system or compiler."
#endif
#else
#error "Unsupported operating system or compiler."
#endif

#define NS_PER_SEC 1000000000

/*
Convert a BCD-encoded hexadecimal number to decimal.
*/
@@ -10,3 +23,21 @@ uint8_t bcd_decode(uint8_t num)
{
return ((num >> 4) * 10) + (num & 0x0F);
}

/*
Return monotonic time, in nanoseconds.
*/
uint64_t get_time_ns()
{
#if defined __APPLE__
static mach_timebase_info_data_t tb_info;
if (tb_info.denom == 0)
mach_timebase_info(&tb_info);
// Apple's docs pretty much say "screw overflow" here...
return mach_absolute_time() * tb_info.numer / tb_info.denom;
#elif defined __linux__
struct timespec spec;
clock_gettime(CLOCK_MONOTONIC, &spec);
return spec.tv_sec * NS_PER_SEC + spec.tv_nsec;
#endif
}

+ 1
- 0
src/util.h 查看文件

@@ -8,3 +8,4 @@
/* Functions */

uint8_t bcd_decode(uint8_t);
uint64_t get_time_ns();

+ 32
- 3
src/z80.c 查看文件

@@ -14,14 +14,21 @@

/*
Initialize a Z80 object.

Register values are invalid until z80_power() is called. No other Z80
functions should be called before it.
*/
void z80_init(Z80 *z80, uint64_t clock_speed)
void z80_init(Z80 *z80, MMU *mmu)
{
z80->clock_speed = clock_speed;
z80->mmu = mmu;
z80->except = true;
}

/*
Power on the Z80, setting registers to their default values.

This also clears the exception flag, which is necessary before the Z80 can
begin emulation.
*/
void z80_power(Z80 *z80)
{
@@ -46,6 +53,9 @@ void z80_power(Z80 *z80)

regfile->im_a = regfile->im_b = 0;
regfile->iff1 = regfile->iff2 = 0;

z80->except = false;
z80->pending_cycles = 0;
}

/*
@@ -76,6 +86,24 @@ static inline uint8_t get_interrupt_mode(Z80 *z80)
return 2;
}

/*
Emulate the given number of cycles of the Z80, or until an exception.

The return value indicates whether the exception flag is set. If it is,
then emulation must be stopped because further calls to z80_do_cycles()
will have no effect. The exception flag can be reset with z80_power().
*/
bool z80_do_cycles(Z80 *z80, double cycles)
{
if (z80->except)
return true;

z80->pending_cycles += cycles;
// TODO: fetch instruction...

return false;
}

#ifdef DEBUG_MODE
/*
DEBUG FUNCTION: Print out all register values to stdout.
@@ -119,7 +147,8 @@ void z80_dump_registers(Z80 *z80)
DEBUG("- I: 0x%2X", regfile->i)
DEBUG("- R: 0x%2X", regfile->r)

DEBUG("- IM: 0b%u%u (mode: %u)", regfile->im_a, regfile->im_b, get_interrupt_mode(z80))
DEBUG("- IM: 0b%u%u (mode: %u)", regfile->im_a, regfile->im_b,
get_interrupt_mode(z80))
DEBUG("- IFF1: %u", regfile->iff1)
DEBUG("- IFF2: %u", regfile->iff2)
}


+ 7
- 2
src/z80.h 查看文件

@@ -6,6 +6,8 @@
#include <stdbool.h>
#include <stdint.h>

#include "mmu.h"

/* Structs */

typedef struct {
@@ -19,13 +21,16 @@ typedef struct {

typedef struct {
Z80RegFile regfile;
uint64_t clock_speed;
MMU *mmu;
bool except;
double pending_cycles;
} Z80;

/* Functions */

void z80_init(Z80*, uint64_t);
void z80_init(Z80*, MMU*);
void z80_power(Z80*);
bool z80_do_cycles(Z80*, double);

#ifdef DEBUG_MODE
void z80_dump_registers(Z80*);


Loading…
取消
儲存