@@ -207,9 +207,6 @@ const char* gamegear_get_exception(GameGear *gg) | |||||
case Z80_EXC_UNIMPLEMENTED_OPCODE: | case Z80_EXC_UNIMPLEMENTED_OPCODE: | ||||
SET_EXC("unimplemented opcode: 0x%02X", gg->cpu.exc_data) | SET_EXC("unimplemented opcode: 0x%02X", gg->cpu.exc_data) | ||||
break; | break; | ||||
case Z80_EXC_IO_ERROR: | |||||
SET_EXC("I/O error on port 0x%02X", gg->cpu.exc_data) | |||||
break; | |||||
default: | default: | ||||
SET_EXC("unknown exception") | SET_EXC("unknown exception") | ||||
break; | break; | ||||
@@ -2,6 +2,7 @@ | |||||
Released under the terms of the MIT License. See LICENSE for details. */ | Released under the terms of the MIT License. See LICENSE for details. */ | ||||
#include "io.h" | #include "io.h" | ||||
#include "logging.h" | |||||
/* | /* | ||||
Initialize an IO object. | Initialize an IO object. | ||||
@@ -17,7 +18,12 @@ void io_init(IO *io, VDP *vdp, PSG *psg) | |||||
*/ | */ | ||||
void io_power(IO *io) | 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. | Read and return a byte from the given port. | ||||
*/ | */ | ||||
uint8_t io_port_read(IO *io, uint8_t 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; | return 0xFF; | ||||
} else if (port <= 0x7F && !(port % 2)) { | |||||
else if (port <= 0x7F && !(port % 2)) | |||||
return io->vdp->v_counter; | return io->vdp->v_counter; | ||||
} else if (port <= 0x7F) { | |||||
else if (port <= 0x7F) | |||||
return io->vdp->h_counter; | return io->vdp->h_counter; | ||||
} else if (port <= 0xBF && !(port % 2)) { | |||||
else if (port <= 0xBF && !(port % 2)) | |||||
return vdp_read_data(io->vdp); | return vdp_read_data(io->vdp); | ||||
} else if (port <= 0xBF) { | |||||
else if (port <= 0xBF) | |||||
return vdp_read_control(io->vdp); | 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; | 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) | 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); | psg_write(io->psg, value); | ||||
} else if (port <= 0xBF && !(port % 2)) { | |||||
else if (port <= 0xBF && !(port % 2)) | |||||
vdp_write_data(io->vdp, value); | vdp_write_data(io->vdp, value); | ||||
} else if (port <= 0xBF) { | |||||
else if (port <= 0xBF) | |||||
vdp_write_control(io->vdp, value); | vdp_write_control(io->vdp, value); | ||||
} else { | |||||
return; | |||||
} | |||||
return; | |||||
except: | |||||
io->except = true; | |||||
io->exc_port = port; | |||||
} | } |
@@ -14,8 +14,7 @@ | |||||
typedef struct { | typedef struct { | ||||
VDP *vdp; | VDP *vdp; | ||||
PSG *psg; | PSG *psg; | ||||
bool except; | |||||
uint8_t exc_port; | |||||
uint8_t ports[6]; | |||||
} IO; | } IO; | ||||
/* Functions */ | /* Functions */ | ||||
@@ -41,3 +41,13 @@ void psg_write(PSG *psg, uint8_t byte) | |||||
(void) psg; | (void) psg; | ||||
TRACE("PSG ignoring write: 0x%02X", byte) | 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_free(PSG*); | ||||
void psg_power(PSG*); | void psg_power(PSG*); | ||||
void psg_write(PSG*, uint8_t); | 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("- $08: 0x%02X (HS)", regs[0x08]) | ||||
DEBUG("- $09: 0x%02X (VS)", regs[0x09]) | DEBUG("- $09: 0x%02X (VS)", regs[0x09]) | ||||
DEBUG("- $0A: 0x%02X (LC)", regs[0x0A]) | 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. | 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) | static inline uint8_t* extract_reg(Z80 *z80, uint8_t opcode) | ||||
@@ -11,7 +11,6 @@ | |||||
#define Z80_EXC_NOT_POWERED 0 | #define Z80_EXC_NOT_POWERED 0 | ||||
#define Z80_EXC_UNIMPLEMENTED_OPCODE 1 | #define Z80_EXC_UNIMPLEMENTED_OPCODE 1 | ||||
#define Z80_EXC_IO_ERROR 2 | |||||
/* Structs */ | /* Structs */ | ||||
@@ -1969,7 +1969,7 @@ static uint8_t z80_inst_in_a_n(Z80 *z80, uint8_t opcode) | |||||
{ | { | ||||
(void) opcode; | (void) opcode; | ||||
uint8_t port = mmu_read_byte(z80->mmu, ++z80->regs.pc); | 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++; | z80->regs.pc++; | ||||
return 11; | 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) | 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) | if (opcode != 0x70) | ||||
*extract_reg(z80, opcode) = data; | *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) | static uint8_t z80_inst_ini(Z80 *z80, uint8_t opcode) | ||||
{ | { | ||||
(void) 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); | set_flags_blockio(z80); | ||||
z80->regs.hl++; | 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) | static uint8_t z80_inst_ind(Z80 *z80, uint8_t opcode) | ||||
{ | { | ||||
(void) 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); | set_flags_blockio(z80); | ||||
z80->regs.hl--; | z80->regs.hl--; | ||||
@@ -2055,7 +2057,7 @@ static uint8_t z80_inst_out_n_a(Z80 *z80, uint8_t opcode) | |||||
{ | { | ||||
(void) opcode; | (void) opcode; | ||||
uint8_t port = mmu_read_byte(z80->mmu, ++z80->regs.pc); | 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++; | z80->regs.pc++; | ||||
return 11; | 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) | static uint8_t z80_inst_out_c_r(Z80 *z80, uint8_t opcode) | ||||
{ | { | ||||
uint8_t value = opcode != 0x71 ? *extract_reg(z80, opcode) : 0; | 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++; | z80->regs.pc++; | ||||
return 12; | 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) | static uint8_t z80_inst_outi(Z80 *z80, uint8_t opcode) | ||||
{ | { | ||||
(void) 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); | set_flags_blockio(z80); | ||||
z80->regs.hl++; | 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) | static uint8_t z80_inst_outd(Z80 *z80, uint8_t opcode) | ||||
{ | { | ||||
(void) 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); | set_flags_blockio(z80); | ||||
z80->regs.hl--; | z80->regs.hl--; | ||||