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