| @@ -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. | |||
| */ | |||
| 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. | |||
| 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]; | |||
| } 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 */ | |||
| GameGear* gamegear_create(); | |||
| void gamegear_destroy(GameGear*); | |||
| void gamegear_load(GameGear*, const ROM*); | |||
| 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_display(GameGear*, uint32_t*); | |||
| @@ -24,6 +24,9 @@ void io_power(IO *io) | |||
| io->ports[0x03] = 0x00; | |||
| io->ports[0x04] = 0xFF; | |||
| 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. | |||
| */ | |||
| 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); | |||
| return (io->ports[port] & 0x7F) | (io->start << 7); | |||
| case 0x01: | |||
| case 0x02: | |||
| case 0x03: | |||
| @@ -91,9 +109,9 @@ uint8_t io_port_read(IO *io, uint8_t port) | |||
| else if (port <= 0xBF) | |||
| return vdp_read_control(io->vdp); | |||
| 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) | |||
| return 0xFF; // TODO: Return the I/O port B/misc. register | |||
| return 0xFF; // TODO | |||
| else | |||
| return 0xFF; | |||
| } | |||
| @@ -15,6 +15,8 @@ typedef struct { | |||
| VDP *vdp; | |||
| PSG *psg; | |||
| uint8_t ports[6]; | |||
| uint8_t buttons; | |||
| bool start; | |||
| } IO; | |||
| /* Functions */ | |||
| @@ -22,5 +24,7 @@ typedef struct { | |||
| void io_init(IO*, VDP*, PSG*); | |||
| void io_power(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); | |||
| 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 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; | |||
| uint16_t tile = get_background_tile(vdp, vcell, hcell); | |||
| uint16_t pattern = tile & 0x01FF; | |||
| @@ -217,13 +217,16 @@ static void draw_background(VDP *vdp) | |||
| bool hflip = tile & 0x0200; | |||
| 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; | |||
| for (pixel = 0; pixel < 8; pixel++) { | |||
| 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); | |||
| color = get_color(vdp, index, palette); | |||
| draw_pixel(vdp, dst_row, dst_col, color); | |||
| @@ -280,13 +283,16 @@ static void draw_sprites(VDP *vdp) | |||
| int16_t dst_col; | |||
| 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); | |||
| if (index == 0) | |||
| continue; | |||
| 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); | |||
| } | |||
| } | |||
| } | |||