@@ -86,17 +86,54 @@ static void draw_frame() | |||||
} | } | ||||
/* | /* | ||||
Handle a keyboard press; translate it into a Game Gear button press. | |||||
*/ | |||||
static void handle_keypress(GameGear *gg, SDL_Keycode key, bool state) | |||||
{ | |||||
GGButton button; | |||||
switch (key) { | |||||
case SDLK_UP: | |||||
case SDLK_w: | |||||
button = BUTTON_UP; break; | |||||
case SDLK_DOWN: | |||||
case SDLK_s: | |||||
button = BUTTON_DOWN; break; | |||||
case SDLK_LEFT: | |||||
case SDLK_a: | |||||
button = BUTTON_LEFT; break; | |||||
case SDLK_RIGHT: | |||||
case SDLK_d: | |||||
button = BUTTON_RIGHT; break; | |||||
case SDLK_j: | |||||
button = BUTTON_TRIGGER_1; break; | |||||
case SDLK_k: | |||||
button = BUTTON_TRIGGER_2; break; | |||||
case SDLK_RETURN: | |||||
button = BUTTON_START; break; | |||||
default: | |||||
return; | |||||
} | |||||
gamegear_input(gg, button, state); | |||||
} | |||||
/* | |||||
Handle SDL events, mainly quit events and button presses. | Handle SDL events, mainly quit events and button presses. | ||||
*/ | */ | ||||
static void handle_events(GameGear *gg) | static void handle_events(GameGear *gg) | ||||
{ | { | ||||
SDL_Event e; | |||||
while (SDL_PollEvent(&e)) { | |||||
if (e.type == SDL_QUIT) { | |||||
gamegear_power_off(gg); | |||||
return; | |||||
SDL_Event event; | |||||
while (SDL_PollEvent(&event)) { | |||||
switch (event.type) { | |||||
case SDL_QUIT: | |||||
gamegear_power_off(gg); | |||||
return; | |||||
case SDL_KEYDOWN: | |||||
handle_keypress(gg, event.key.keysym.sym, true); | |||||
break; | |||||
case SDL_KEYUP: | |||||
handle_keypress(gg, event.key.keysym.sym, false); | |||||
break; | |||||
} | } | ||||
// TODO: buttons | |||||
} | } | ||||
} | } | ||||
@@ -68,6 +68,20 @@ void gamegear_load(GameGear *gg, const ROM *rom) | |||||
} | } | ||||
/* | /* | ||||
Update the GameGear's button/joystick state. | |||||
'state' should be true when the button is pressed, and false when it is | |||||
released. | |||||
*/ | |||||
void gamegear_input(GameGear *gg, GGButton button, bool state) | |||||
{ | |||||
if (button == BUTTON_START) | |||||
io_set_start(&gg->io, state); | |||||
else | |||||
io_set_button(&gg->io, button, state); | |||||
} | |||||
/* | |||||
Power on the GameGear. | Power on the GameGear. | ||||
This clears the exception buffer and executes boot code (e.g. clearing | This clears the exception buffer and executes boot code (e.g. clearing | ||||
@@ -34,13 +34,24 @@ typedef struct GameGear { | |||||
char exc_buffer[GG_EXC_BUFF_SIZE]; | char exc_buffer[GG_EXC_BUFF_SIZE]; | ||||
} GameGear; | } GameGear; | ||||
typedef enum { | |||||
BUTTON_UP = 0, | |||||
BUTTON_DOWN = 1, | |||||
BUTTON_LEFT = 2, | |||||
BUTTON_RIGHT = 3, | |||||
BUTTON_TRIGGER_1 = 4, | |||||
BUTTON_TRIGGER_2 = 5, | |||||
BUTTON_START | |||||
} GGButton; | |||||
/* Functions */ | /* Functions */ | ||||
GameGear* gamegear_create(); | GameGear* gamegear_create(); | ||||
void gamegear_destroy(GameGear*); | void gamegear_destroy(GameGear*); | ||||
void gamegear_load(GameGear*, const ROM*); | void gamegear_load(GameGear*, const ROM*); | ||||
void gamegear_simulate(GameGear*); | void gamegear_simulate(GameGear*); | ||||
void gamegear_power_off(GameGear*); // TODO: generic "gamegear_input()" with a power-off option | |||||
void gamegear_input(GameGear*, GGButton, bool); | |||||
void gamegear_power_off(GameGear*); | |||||
void gamegear_attach_callback(GameGear*, GGFrameCallback); | void gamegear_attach_callback(GameGear*, GGFrameCallback); | ||||
void gamegear_attach_display(GameGear*, uint32_t*); | void gamegear_attach_display(GameGear*, uint32_t*); | ||||
@@ -24,6 +24,9 @@ void io_power(IO *io) | |||||
io->ports[0x03] = 0x00; | io->ports[0x03] = 0x00; | ||||
io->ports[0x04] = 0xFF; | io->ports[0x04] = 0xFF; | ||||
io->ports[0x05] = 0x00; | io->ports[0x05] = 0x00; | ||||
io->buttons = 0xFF; | |||||
io->start = true; | |||||
} | } | ||||
/* | /* | ||||
@@ -35,14 +38,29 @@ bool io_check_irq(IO *io) | |||||
} | } | ||||
/* | /* | ||||
Set the state of the given joystick button. | |||||
*/ | |||||
void io_set_button(IO *io, uint8_t button, bool state) | |||||
{ | |||||
io->buttons = (io->buttons & ~(1 << button)) | ((!state) << button); | |||||
} | |||||
/* | |||||
Set the state of the start button. | |||||
*/ | |||||
void io_set_start(IO *io, bool state) | |||||
{ | |||||
io->start = !state; | |||||
} | |||||
/* | |||||
Read from one of the system ports, which are numbered from 0x00 to 0x06. | 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) | static uint8_t read_system_port(IO *io, uint8_t port) | ||||
{ | { | ||||
switch (port) { | switch (port) { | ||||
case 0x00: | case 0x00: | ||||
// TODO: MSB is state of START button | |||||
return (io->ports[port] & 0x7F) | (0 << 7); | |||||
return (io->ports[port] & 0x7F) | (io->start << 7); | |||||
case 0x01: | case 0x01: | ||||
case 0x02: | case 0x02: | ||||
case 0x03: | case 0x03: | ||||
@@ -91,9 +109,9 @@ uint8_t io_port_read(IO *io, uint8_t port) | |||||
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) | else if (port == 0xCD || port == 0xDC) | ||||
return 0xFF; // TODO: Return the I/O port A/B register | |||||
return io->buttons; | |||||
else if (port == 0xC1 || port == 0xDD) | else if (port == 0xC1 || port == 0xDD) | ||||
return 0xFF; // TODO: Return the I/O port B/misc. register | |||||
return 0xFF; // TODO | |||||
else | else | ||||
return 0xFF; | return 0xFF; | ||||
} | } | ||||
@@ -15,6 +15,8 @@ typedef struct { | |||||
VDP *vdp; | VDP *vdp; | ||||
PSG *psg; | PSG *psg; | ||||
uint8_t ports[6]; | uint8_t ports[6]; | ||||
uint8_t buttons; | |||||
bool start; | |||||
} IO; | } IO; | ||||
/* Functions */ | /* Functions */ | ||||
@@ -22,5 +24,7 @@ typedef struct { | |||||
void io_init(IO*, VDP*, PSG*); | void io_init(IO*, VDP*, PSG*); | ||||
void io_power(IO*); | void io_power(IO*); | ||||
bool io_check_irq(IO*); | bool io_check_irq(IO*); | ||||
void io_set_button(IO*, uint8_t, bool); | |||||
void io_set_start(IO*, bool); | |||||
uint8_t io_port_read(IO*, uint8_t); | uint8_t io_port_read(IO*, uint8_t); | ||||
void io_port_write(IO*, uint8_t, uint8_t); | void io_port_write(IO*, uint8_t, uint8_t); |
@@ -207,7 +207,7 @@ static void draw_background(VDP *vdp) | |||||
uint8_t start_col = get_bg_hscroll(vdp) >> 3; | uint8_t start_col = get_bg_hscroll(vdp) >> 3; | ||||
uint8_t fine_scroll = get_bg_hscroll(vdp) % 8; | uint8_t fine_scroll = get_bg_hscroll(vdp) % 8; | ||||
for (col = 6; col < 20 + 6; col++) { | |||||
for (col = 5; col < 20 + 6; col++) { | |||||
hcell = (32 - start_col + col) % 32; | hcell = (32 - start_col + col) % 32; | ||||
uint16_t tile = get_background_tile(vdp, vcell, hcell); | uint16_t tile = get_background_tile(vdp, vcell, hcell); | ||||
uint16_t pattern = tile & 0x01FF; | uint16_t pattern = tile & 0x01FF; | ||||
@@ -217,13 +217,16 @@ static void draw_background(VDP *vdp) | |||||
bool hflip = tile & 0x0200; | bool hflip = tile & 0x0200; | ||||
uint8_t vshift = vflip ? (7 - src_row % 8) : (src_row % 8), hshift; | uint8_t vshift = vflip ? (7 - src_row % 8) : (src_row % 8), hshift; | ||||
uint8_t pixel, dst_col, index; | |||||
uint8_t pixel, index; | |||||
int16_t dst_col; | |||||
uint16_t color; | uint16_t color; | ||||
for (pixel = 0; pixel < 8; pixel++) { | for (pixel = 0; pixel < 8; pixel++) { | ||||
dst_col = ((col - 6) << 3) + pixel + fine_scroll; | dst_col = ((col - 6) << 3) + pixel + fine_scroll; | ||||
hshift = hflip ? (7 - pixel) : pixel; | |||||
if (dst_col < 0 || dst_col >= 160) | |||||
continue; | |||||
hshift = hflip ? (7 - pixel) : pixel; | |||||
index = read_pattern(vdp, pattern, vshift, hshift); | index = read_pattern(vdp, pattern, vshift, hshift); | ||||
color = get_color(vdp, index, palette); | color = get_color(vdp, index, palette); | ||||
draw_pixel(vdp, dst_row, dst_col, color); | draw_pixel(vdp, dst_row, dst_col, color); | ||||
@@ -280,13 +283,16 @@ static void draw_sprites(VDP *vdp) | |||||
int16_t dst_col; | int16_t dst_col; | ||||
for (pixel = 0; pixel < 8; pixel++) { | for (pixel = 0; pixel < 8; pixel++) { | ||||
dst_col = x + pixel - (6 << 3); | |||||
if (dst_col < 0 || dst_col >= 160) | |||||
continue; | |||||
index = read_pattern(vdp, pattern, vshift, pixel); | index = read_pattern(vdp, pattern, vshift, pixel); | ||||
if (index == 0) | if (index == 0) | ||||
continue; | continue; | ||||
color = get_color(vdp, index, 1); | color = get_color(vdp, index, 1); | ||||
dst_col = x + pixel - (6 << 3); | |||||
if (dst_col >= 0 && dst_col < 160) | |||||
draw_pixel(vdp, dst_row, dst_col, color); | |||||
draw_pixel(vdp, dst_row, dst_col, color); | |||||
} | } | ||||
} | } | ||||
} | } | ||||