@@ -207,9 +207,6 @@ 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_IO_ERROR: | |||
SET_EXC("I/O error on port 0x%02X", gg->cpu.exc_data) | |||
break; | |||
default: | |||
SET_EXC("unknown exception") | |||
break; | |||
@@ -2,6 +2,7 @@ | |||
Released under the terms of the MIT License. See LICENSE for details. */ | |||
#include "io.h" | |||
#include "logging.h" | |||
/* | |||
Initialize an IO object. | |||
@@ -17,7 +18,12 @@ void io_init(IO *io, VDP *vdp, PSG *psg) | |||
*/ | |||
void io_power(IO *io) | |||
{ | |||
io->except = false; | |||
io->ports[0x00] = 0xC0; // Overseas mode, NTSC | |||
io->ports[0x01] = 0x7F; | |||
io->ports[0x02] = 0xFF; | |||
io->ports[0x03] = 0x00; | |||
io->ports[0x04] = 0xFF; | |||
io->ports[0x05] = 0x00; | |||
} | |||
/* | |||
@@ -29,37 +35,67 @@ bool io_check_irq(IO *io) | |||
} | |||
/* | |||
Read from one of the system ports, which are numbered from 0x00 to 0x06. | |||
*/ | |||
static uint8_t read_system_port(IO *io, uint8_t port) | |||
{ | |||
switch (port) { | |||
case 0x00: | |||
// TODO: MSB is state of START button | |||
return (io->ports[port] & 0x7F) | (0 << 7); | |||
case 0x01: | |||
case 0x02: | |||
case 0x03: | |||
case 0x04: | |||
case 0x05: | |||
return io->ports[port]; | |||
} | |||
return 0xFF; | |||
} | |||
/* | |||
Write to one of the system ports, which are numbered from 0x00 to 0x06. | |||
*/ | |||
static void write_system_port(IO *io, uint8_t port, uint8_t value) | |||
{ | |||
switch (port) { | |||
case 0x01: | |||
case 0x02: | |||
case 0x03: | |||
io->ports[port] = value; | |||
break; | |||
case 0x05: | |||
io->ports[port] = value & 0xF8; | |||
break; | |||
case 0x06: | |||
psg_stereo(io->psg, value); | |||
break; | |||
} | |||
} | |||
/* | |||
Read and return a byte from the given port. | |||
*/ | |||
uint8_t io_port_read(IO *io, uint8_t port) | |||
{ | |||
if (port <= 0x06) { | |||
if (port == 0x00) | |||
return 0xC0; | |||
// TODO: GG specific registers; initial state: C0 7F FF 00 FF 00 FF | |||
} else if (port <= 0x3F) { | |||
if (port <= 0x06) | |||
return read_system_port(io, port); | |||
else if (port <= 0x3F) | |||
return 0xFF; | |||
} else if (port <= 0x7F && !(port % 2)) { | |||
else if (port <= 0x7F && !(port % 2)) | |||
return io->vdp->v_counter; | |||
} else if (port <= 0x7F) { | |||
else if (port <= 0x7F) | |||
return io->vdp->h_counter; | |||
} else if (port <= 0xBF && !(port % 2)) { | |||
else if (port <= 0xBF && !(port % 2)) | |||
return vdp_read_data(io->vdp); | |||
} else if (port <= 0xBF) { | |||
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 | |||
return 0xFF; | |||
} else if (port == 0xC1 || port == 0xDD) { | |||
// TODO: Return the I/O port B/misc. register | |||
else if (port == 0xCD || port == 0xDC) | |||
return 0xFF; // TODO: Return the I/O port A/B register | |||
else if (port == 0xC1 || port == 0xDD) | |||
return 0xFF; // TODO: Return the I/O port B/misc. register | |||
else | |||
return 0xFF; | |||
} else { | |||
return 0xFF; | |||
} | |||
io->except = true; | |||
io->exc_port = port; | |||
return 0; | |||
} | |||
/* | |||
@@ -67,28 +103,16 @@ uint8_t io_port_read(IO *io, uint8_t 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) { | |||
if (port <= 0x06) | |||
write_system_port(io, port, value); | |||
else if (port <= 0x3F && !(port % 2)) | |||
return; // TODO: Write to memory control register | |||
else if (port <= 0x3F) | |||
return; // TODO: Write to I/O control register | |||
else if (port <= 0x7F) | |||
psg_write(io->psg, value); | |||
} else if (port <= 0xBF && !(port % 2)) { | |||
else if (port <= 0xBF && !(port % 2)) | |||
vdp_write_data(io->vdp, value); | |||
} else if (port <= 0xBF) { | |||
else if (port <= 0xBF) | |||
vdp_write_control(io->vdp, value); | |||
} else { | |||
return; | |||
} | |||
return; | |||
except: | |||
io->except = true; | |||
io->exc_port = port; | |||
} |
@@ -14,8 +14,7 @@ | |||
typedef struct { | |||
VDP *vdp; | |||
PSG *psg; | |||
bool except; | |||
uint8_t exc_port; | |||
uint8_t ports[6]; | |||
} IO; | |||
/* Functions */ | |||
@@ -41,3 +41,13 @@ void psg_write(PSG *psg, uint8_t byte) | |||
(void) psg; | |||
TRACE("PSG ignoring write: 0x%02X", byte) | |||
} | |||
/* | |||
Send a byte to the PSG's stereo control. | |||
*/ | |||
void psg_stereo(PSG *psg, uint8_t byte) | |||
{ | |||
// TODO | |||
(void) psg; | |||
TRACE("PSG ignoring stereo: 0x%02X", byte) | |||
} |
@@ -19,3 +19,4 @@ void psg_init(PSG*); | |||
void psg_free(PSG*); | |||
void psg_power(PSG*); | |||
void psg_write(PSG*, uint8_t); | |||
void psg_stereo(PSG*, uint8_t); |
@@ -433,43 +433,4 @@ void vdp_dump_registers(const VDP *vdp) | |||
DEBUG("- $08: 0x%02X (HS)", regs[0x08]) | |||
DEBUG("- $09: 0x%02X (VS)", regs[0x09]) | |||
DEBUG("- $0A: 0x%02X (LC)", regs[0x0A]) | |||
// TODO: remove me! | |||
DEBUG("Dumping CRAM:") | |||
for (uint8_t i = 0; i < 32; i += 8) { | |||
uint16_t w[8]; | |||
for (uint8_t j = 0; j < 8; j++) | |||
w[j] = get_color(vdp, (i + j) % 16, i & 16); | |||
DEBUG("- %04X %04X %04X %04X %04X %04X %04X %04X", | |||
w[0], w[1], w[2], w[3], w[4], w[5], w[6], w[7]) | |||
} | |||
return; | |||
DEBUG("Dumping PNT:") | |||
for (uint16_t i = 0; i < 18; i++) { | |||
uint16_t w[20]; | |||
for (uint8_t j = 0; j < 20; j++) | |||
w[j] = get_background_tile(vdp, i + 3, j + 6); | |||
DEBUG("- %03X %03X %03X %03X %03X %03X %03X %03X %03X %03X" | |||
" %03X %03X %03X %03X %03X %03X %03X %03X %03X %03X", | |||
w[0x00], w[0x01], w[0x02], w[0x03], w[0x04], w[0x05], w[0x06], | |||
w[0x07], w[0x08], w[0x09], w[0x0A], w[0x0B], w[0x0C], w[0x0D], | |||
w[0x0E], w[0x0F], w[0x10], w[0x11], w[0x12], w[0x13]) | |||
} | |||
DEBUG("Dumping PGT:") | |||
for (uint16_t i = 0; i < /* 512 */ 32; i++) { | |||
uint32_t w[8]; | |||
for (uint8_t j = 0; j < 8; j++) | |||
w[j] = vdp->vram[32 * i + 4 * j] + | |||
(vdp->vram[32 * i + 4 * j + 1] << 8) + | |||
(vdp->vram[32 * i + 4 * j + 2] << 16) + | |||
(vdp->vram[32 * i + 4 * j + 3] << 24); | |||
DEBUG("- 0x%04X: %08X %08X %08X %08X %08X %08X %08X %08X", i, | |||
w[0], w[1], w[2], w[3], w[4], w[5], w[6], w[7]) | |||
} | |||
} |
@@ -130,37 +130,6 @@ static inline uint16_t stack_pop(Z80 *z80) | |||
} | |||
/* | |||
Check for errors after an I/O operation. | |||
*/ | |||
static void handle_io_errors(Z80 *z80) | |||
{ | |||
if (z80->io->except) { | |||
z80->except = true; | |||
z80->exc_code = Z80_EXC_IO_ERROR; | |||
z80->exc_data = z80->io->exc_port; | |||
} | |||
} | |||
/* | |||
Read and return a byte from the given port, and check for errors. | |||
*/ | |||
static inline 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, and check for errors. | |||
*/ | |||
static inline void write_port(Z80 *z80, uint8_t port, uint8_t value) | |||
{ | |||
io_port_write(z80->io, port, value); | |||
handle_io_errors(z80); | |||
} | |||
/* | |||
Extract an 8-bit register from the given opcode and return a pointer to it. | |||
*/ | |||
static inline uint8_t* extract_reg(Z80 *z80, uint8_t opcode) | |||
@@ -11,7 +11,6 @@ | |||
#define Z80_EXC_NOT_POWERED 0 | |||
#define Z80_EXC_UNIMPLEMENTED_OPCODE 1 | |||
#define Z80_EXC_IO_ERROR 2 | |||
/* Structs */ | |||
@@ -1969,7 +1969,7 @@ static uint8_t z80_inst_in_a_n(Z80 *z80, uint8_t opcode) | |||
{ | |||
(void) opcode; | |||
uint8_t port = mmu_read_byte(z80->mmu, ++z80->regs.pc); | |||
z80->regs.a = read_port(z80, port); | |||
z80->regs.a = io_port_read(z80->io, port); | |||
z80->regs.pc++; | |||
return 11; | |||
} | |||
@@ -1980,7 +1980,7 @@ static uint8_t z80_inst_in_a_n(Z80 *z80, uint8_t opcode) | |||
*/ | |||
static uint8_t z80_inst_in_r_c(Z80 *z80, uint8_t opcode) | |||
{ | |||
uint8_t data = read_port(z80, z80->regs.c); | |||
uint8_t data = io_port_read(z80->io, z80->regs.c); | |||
if (opcode != 0x70) | |||
*extract_reg(z80, opcode) = data; | |||
@@ -1996,7 +1996,8 @@ static uint8_t z80_inst_in_r_c(Z80 *z80, uint8_t opcode) | |||
static uint8_t z80_inst_ini(Z80 *z80, uint8_t opcode) | |||
{ | |||
(void) opcode; | |||
mmu_write_byte(z80->mmu, z80->regs.hl, read_port(z80, z80->regs.c)); | |||
uint8_t data = io_port_read(z80->io, z80->regs.c); | |||
mmu_write_byte(z80->mmu, z80->regs.hl, data); | |||
set_flags_blockio(z80); | |||
z80->regs.hl++; | |||
@@ -2025,7 +2026,8 @@ static uint8_t z80_inst_inir(Z80 *z80, uint8_t opcode) | |||
static uint8_t z80_inst_ind(Z80 *z80, uint8_t opcode) | |||
{ | |||
(void) opcode; | |||
mmu_write_byte(z80->mmu, z80->regs.hl, read_port(z80, z80->regs.c)); | |||
uint8_t data = io_port_read(z80->io, z80->regs.c); | |||
mmu_write_byte(z80->mmu, z80->regs.hl, data); | |||
set_flags_blockio(z80); | |||
z80->regs.hl--; | |||
@@ -2055,7 +2057,7 @@ static uint8_t z80_inst_out_n_a(Z80 *z80, uint8_t opcode) | |||
{ | |||
(void) opcode; | |||
uint8_t port = mmu_read_byte(z80->mmu, ++z80->regs.pc); | |||
write_port(z80, port, z80->regs.a); | |||
io_port_write(z80->io, port, z80->regs.a); | |||
z80->regs.pc++; | |||
return 11; | |||
} | |||
@@ -2068,7 +2070,7 @@ static uint8_t z80_inst_out_n_a(Z80 *z80, uint8_t opcode) | |||
static uint8_t z80_inst_out_c_r(Z80 *z80, uint8_t opcode) | |||
{ | |||
uint8_t value = opcode != 0x71 ? *extract_reg(z80, opcode) : 0; | |||
write_port(z80, z80->regs.c, value); | |||
io_port_write(z80->io, z80->regs.c, value); | |||
z80->regs.pc++; | |||
return 12; | |||
} | |||
@@ -2080,7 +2082,8 @@ static uint8_t z80_inst_out_c_r(Z80 *z80, uint8_t opcode) | |||
static uint8_t z80_inst_outi(Z80 *z80, uint8_t opcode) | |||
{ | |||
(void) opcode; | |||
write_port(z80, z80->regs.c, mmu_read_byte(z80->mmu, z80->regs.hl)); | |||
uint8_t data = mmu_read_byte(z80->mmu, z80->regs.hl); | |||
io_port_write(z80->io, z80->regs.c, data); | |||
set_flags_blockio(z80); | |||
z80->regs.hl++; | |||
@@ -2109,7 +2112,8 @@ static uint8_t z80_inst_otir(Z80 *z80, uint8_t opcode) | |||
static uint8_t z80_inst_outd(Z80 *z80, uint8_t opcode) | |||
{ | |||
(void) opcode; | |||
write_port(z80, z80->regs.c, mmu_read_byte(z80->mmu, z80->regs.hl)); | |||
uint8_t data = mmu_read_byte(z80->mmu, z80->regs.hl); | |||
io_port_write(z80->io, z80->regs.c, data); | |||
set_flags_blockio(z80); | |||
z80->regs.hl--; | |||