Browse Source

Some refactoring wrt I/O; start work on VDP.

master
Ben Kurtovic 8 years ago
parent
commit
a09242ac29
11 changed files with 249 additions and 58 deletions
  1. +2
    -2
      crater.c
  2. +4
    -4
      src/emulator.c
  3. +1
    -1
      src/emulator.h
  4. +9
    -3
      src/gamegear.c
  5. +4
    -1
      src/gamegear.h
  6. +79
    -0
      src/io.c
  7. +24
    -0
      src/io.h
  8. +76
    -0
      src/vdp.c
  9. +26
    -0
      src/vdp.h
  10. +20
    -45
      src/z80.c
  11. +4
    -2
      src/z80.h

+ 2
- 2
crater.c View File

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


src/iomanager.c → src/emulator.c View File

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

src/iomanager.h → src/emulator.h View File

@@ -7,4 +7,4 @@

/* Functions */

void iomanager_emulate(GameGear*);
void emulate(GameGear*);

+ 9
- 3
src/gamegear.c View File

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


+ 4
- 1
src/gamegear.h View File

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


+ 79
- 0
src/io.c View File

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

+ 24
- 0
src/io.h View File

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

+ 76
- 0
src/vdp.c View File

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

+ 26
- 0
src/vdp.h View File

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

+ 20
- 45
src/z80.c View File

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

/*


+ 4
- 2
src/z80.h View File

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

Loading…
Cancel
Save