- a -> A - s -> B - q -> Select - w -> Start - Arrow keysmaster
| @@ -13,7 +13,8 @@ BINDIR = bin | |||||
| TARGET_1 = nese | TARGET_1 = nese | ||||
| LDLIBS_1 = -lSDL2 | LDLIBS_1 = -lSDL2 | ||||
| SRC_SRCS_1 = nese.c ines.c nes.c ppu.c sdl_render.c | |||||
| SRC_SRCS_1 = nese.c ines.c nes.c ppu.c input.c | |||||
| SRC_SRCS_1 += sdl_render.c sdl_input.c | |||||
| EXT_SRCS_1 = e6502/e6502.c | EXT_SRCS_1 = e6502/e6502.c | ||||
| @@ -0,0 +1,30 @@ | |||||
| #include "nes.h" | |||||
| static uint8_t read_controller_bit(nes_controller* controller) { | |||||
| uint8_t state = 1; | |||||
| if (controller->shift < 0) { | |||||
| state = (controller->buttons >> Button_A) & 1; | |||||
| } else if (controller->shift < nes_controller_num_buttons) { | |||||
| state = (controller->buttons >> controller->shift) & 1; | |||||
| controller->shift++; | |||||
| } | |||||
| return state; | |||||
| } | |||||
| uint8_t nes_input_read(nes_input* input, uint16_t addr) { | |||||
| nes_controller* controller = | |||||
| &input->controllers[(nes_input_2_reg == addr)]; | |||||
| return ((addr >> 8) & nes_controller_bus_mask) | | |||||
| read_controller_bit(controller); | |||||
| } | |||||
| void nes_input_write(nes_input* input, uint16_t addr, uint8_t val) { | |||||
| if (val & 1) { | |||||
| input->controllers[0].shift = -1; | |||||
| input->controllers[1].shift = -1; | |||||
| } else { | |||||
| input->controllers[0].shift = 0; | |||||
| input->controllers[1].shift = 0; | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,70 @@ | |||||
| #ifndef NES_INPUT_H_ | |||||
| #define NES_INPUT_H_ | |||||
| #include <stdint.h> | |||||
| #include <stdio.h> | |||||
| // Emulator Implementation | |||||
| #define nes_controller_num_buttons (8U) | |||||
| typedef enum { | |||||
| Button_A = 0, | |||||
| Button_B, | |||||
| Button_Select, | |||||
| Button_Start, | |||||
| Button_Up, | |||||
| Button_Down, | |||||
| Button_Left, | |||||
| Button_Right, | |||||
| } nes_Input_Button; | |||||
| typedef enum { | |||||
| Controller_Primary = 0b001, | |||||
| Controller_Expansion = 0b010, | |||||
| Controller_Microphone = 0b100, | |||||
| } nes_Controller_State; | |||||
| typedef struct { | |||||
| uint32_t buttons; | |||||
| int shift; | |||||
| } nes_controller; | |||||
| typedef struct { | |||||
| nes_controller controllers[2]; | |||||
| } nes_input; | |||||
| #define nes_controller_bus_mask (0b11111000) | |||||
| uint8_t nes_input_read(nes_input* input, uint16_t addr); | |||||
| void nes_input_write(nes_input* input, uint16_t addr, uint8_t val); | |||||
| // System Glue | |||||
| typedef struct nes_Input_Reader_t { | |||||
| int (*init)(struct nes_Input_Reader_t*); | |||||
| void (*done)(struct nes_Input_Reader_t*); | |||||
| int (*update)(struct nes_Input_Reader_t*, nes_input*); | |||||
| void* data; | |||||
| } nes_Input_Reader; | |||||
| static inline int nes_input_init(nes_Input_Reader* reader) { | |||||
| return reader->init(reader); | |||||
| } | |||||
| static inline void nes_input_done(nes_Input_Reader* reader) { | |||||
| reader->done(reader); | |||||
| } | |||||
| static inline int nes_input_update(nes_Input_Reader* reader, | |||||
| nes_input* input) { | |||||
| return reader->update(reader, input); | |||||
| } | |||||
| #endif // NES_INPUT_H_ | |||||
| @@ -14,6 +14,10 @@ uint8_t nes_mem_read(nes* sys, uint16_t addr) { | |||||
| addr = (addr - nes_mem_ppu_start) & (nes_ppu_map_size - 1); | addr = (addr - nes_mem_ppu_start) & (nes_ppu_map_size - 1); | ||||
| val = nes_ppu_read(&sys->ppu, nes_mem_ppu_start + addr); | val = nes_ppu_read(&sys->ppu, nes_mem_ppu_start + addr); | ||||
| } else if ( nes_input_1_reg == addr || | |||||
| nes_input_2_reg == addr) { | |||||
| val = nes_input_read(&sys->input, addr); | |||||
| } else if (addr < nes_mem_exp_start) { | } else if (addr < nes_mem_exp_start) { | ||||
| val = nes_apu_read(&sys->apu, addr); | val = nes_apu_read(&sys->apu, addr); | ||||
| @@ -39,7 +43,7 @@ void nes_mem_write(nes* sys, uint16_t addr, uint8_t val) { | |||||
| addr = (addr - nes_mem_ppu_start) & (nes_ppu_map_size - 1); | addr = (addr - nes_mem_ppu_start) & (nes_ppu_map_size - 1); | ||||
| nes_ppu_write(&sys->ppu, nes_mem_ppu_start + addr, val); | nes_ppu_write(&sys->ppu, nes_mem_ppu_start + addr, val); | ||||
| } else if (addr == nes_ppu_dma_reg) { | |||||
| } else if (nes_ppu_dma_reg == addr) { | |||||
| // printf("PPU: OAM DMA $%02x00 > $%02x\n", val, sys->ppu.oam_addr); | // printf("PPU: OAM DMA $%02x00 > $%02x\n", val, sys->ppu.oam_addr); | ||||
| for (int i = 0; i < nes_ppu_oam_size; ++i) { | for (int i = 0; i < nes_ppu_oam_size; ++i) { | ||||
| sys->ppu.oam[(uint8_t)(i + sys->ppu.oam_addr)] = | sys->ppu.oam[(uint8_t)(i + sys->ppu.oam_addr)] = | ||||
| @@ -48,6 +52,9 @@ void nes_mem_write(nes* sys, uint16_t addr, uint8_t val) { | |||||
| sys->cpu.cycle += 513U; | sys->cpu.cycle += 513U; | ||||
| // Other subsystem cycles are driven by CPU cycles | // Other subsystem cycles are driven by CPU cycles | ||||
| } else if (addr == nes_input_set_reg) { | |||||
| nes_input_write(&sys->input, addr, val); | |||||
| } else if (addr < nes_mem_exp_start) { | } else if (addr < nes_mem_exp_start) { | ||||
| nes_apu_write(&sys->apu, addr, val); | nes_apu_write(&sys->apu, addr, val); | ||||
| @@ -3,6 +3,7 @@ | |||||
| #include "apu.h" | #include "apu.h" | ||||
| #include "ppu.h" | #include "ppu.h" | ||||
| #include "input.h" | |||||
| #include "e6502/e6502.h" | #include "e6502/e6502.h" | ||||
| @@ -29,8 +30,13 @@ | |||||
| #define nes_ppu_map_size (0x8U) | #define nes_ppu_map_size (0x8U) | ||||
| #define nes_ppu_dma_reg (0x4014U) | #define nes_ppu_dma_reg (0x4014U) | ||||
| #define nes_input_1_reg (0x4016U) | |||||
| #define nes_input_2_reg (0x4017U) | |||||
| #define nes_input_set_reg (0x4016U) | |||||
| #define nes_apu_map_size (0x20U) | #define nes_apu_map_size (0x20U) | ||||
| typedef struct { | typedef struct { | ||||
| // TODO: Dynamic size support | // TODO: Dynamic size support | ||||
| uint8_t prg[nes_mem_rom_size]; | uint8_t prg[nes_mem_rom_size]; | ||||
| @@ -47,6 +53,7 @@ typedef struct { | |||||
| e6502_Core cpu; | e6502_Core cpu; | ||||
| nes_ppu ppu; | nes_ppu ppu; | ||||
| nes_apu apu; | nes_apu apu; | ||||
| nes_input input; | |||||
| uint8_t ram[nes_mem_ram_size]; | uint8_t ram[nes_mem_ram_size]; | ||||
| nes_cart cart; | nes_cart cart; | ||||
| } nes; | } nes; | ||||
| @@ -4,6 +4,7 @@ | |||||
| #include "ines.h" | #include "ines.h" | ||||
| #include "render.h" | #include "render.h" | ||||
| #include "input.h" | |||||
| void e6502_print_registers(const e6502_Registers* regs, | void e6502_print_registers(const e6502_Registers* regs, | ||||
| @@ -51,8 +52,12 @@ static void t_add_ns(struct timespec* s, | |||||
| s->tv_nsec = nsec % NS_PER_S; | s->tv_nsec = nsec % NS_PER_S; | ||||
| } | } | ||||
| extern nes_Renderer sdl_renderer; | extern nes_Renderer sdl_renderer; | ||||
| extern nes_Input_Reader sdl_input; | |||||
| static nes sys = {0}; | static nes sys = {0}; | ||||
| int main(int argc, char* argv[]) { | int main(int argc, char* argv[]) { | ||||
| @@ -68,6 +73,11 @@ int main(int argc, char* argv[]) { | |||||
| status = nes_render_init(rend); | status = nes_render_init(rend); | ||||
| } | } | ||||
| nes_Input_Reader* input = &sdl_input; | |||||
| if (status == 0) { | |||||
| status = nes_input_init(input); | |||||
| } | |||||
| if (status == 0) { | if (status == 0) { | ||||
| nes_init(&sys); | nes_init(&sys); | ||||
| nes_reset(&sys); | nes_reset(&sys); | ||||
| @@ -114,7 +124,8 @@ int main(int argc, char* argv[]) { | |||||
| cycle_last_frame = total_cycles; | cycle_last_frame = total_cycles; | ||||
| status = 0; | |||||
| // Update button states every rendered frame | |||||
| status = nes_input_update(input, &sys.input); | |||||
| } | } | ||||
| } else if (result == ppu_Result_Halt) { | } else if (result == ppu_Result_Halt) { | ||||
| status = -1; | status = -1; | ||||
| @@ -0,0 +1,72 @@ | |||||
| #include <SDL2/SDL.h> | |||||
| #include "input.h" | |||||
| static int filter(void*, SDL_Event* event) { | |||||
| return ( SDL_QUIT == event->type || | |||||
| SDL_KEYDOWN == event->type || | |||||
| SDL_KEYUP == event->type | |||||
| ); | |||||
| } | |||||
| static int sdl_input_init(nes_Input_Reader*) { | |||||
| int status = SDL_Init(SDL_INIT_EVENTS); | |||||
| if (status == 0) { | |||||
| SDL_SetEventFilter(filter, NULL); | |||||
| } | |||||
| return status; | |||||
| } | |||||
| static void sdl_input_done(nes_Input_Reader*) {} | |||||
| static const int keycodes[nes_controller_num_buttons] = { | |||||
| SDLK_a, | |||||
| SDLK_s, | |||||
| SDLK_q, | |||||
| SDLK_w, | |||||
| SDLK_UP, | |||||
| SDLK_DOWN, | |||||
| SDLK_LEFT, | |||||
| SDLK_RIGHT, | |||||
| }; | |||||
| static int button_index(int keycode) { | |||||
| int index = nes_controller_num_buttons - 1; | |||||
| for ( ; index > 0 && keycode != keycodes[index]; --index); | |||||
| return index; | |||||
| } | |||||
| static int sdl_input_update(nes_Input_Reader*, nes_input* input) { | |||||
| int status = 0; | |||||
| SDL_Event event = {0}; | |||||
| while (0 != SDL_PollEvent(&event)) { | |||||
| if (SDL_QUIT == event.type) { | |||||
| status = -1; | |||||
| break; | |||||
| } else if ( SDL_KEYDOWN == event.type || | |||||
| SDL_KEYUP == event.type) { | |||||
| int index = button_index(event.key.keysym.sym); | |||||
| uint8_t mask = (1 << index); | |||||
| if (SDL_KEYDOWN == event.type) { | |||||
| input->controllers[0].buttons |= mask; | |||||
| } else { | |||||
| input->controllers[0].buttons &= ~mask; | |||||
| } | |||||
| } | |||||
| } | |||||
| return status; | |||||
| } | |||||
| nes_Input_Reader sdl_input = { | |||||
| .init = sdl_input_init, | |||||
| .done = sdl_input_done, | |||||
| .update = sdl_input_update, | |||||
| }; | |||||
| @@ -3,13 +3,6 @@ | |||||
| #include "render.h" | #include "render.h" | ||||
| #include "ppu.h" | #include "ppu.h" | ||||
| /* | |||||
| typedef struct { | |||||
| uint8_t r; | |||||
| uint8_t g; | |||||
| uint8_t b; | |||||
| } __attribute__ (( packed )) sPal; | |||||
| */ | |||||
| static SDL_Color nes_palette[64] = { | static SDL_Color nes_palette[64] = { | ||||
| {0x80,0x80,0x80}, {0x00,0x00,0xBB}, {0x37,0x00,0xBF}, {0x84,0x00,0xA6}, | {0x80,0x80,0x80}, {0x00,0x00,0xBB}, {0x37,0x00,0xBF}, {0x84,0x00,0xA6}, | ||||
| @@ -45,10 +38,6 @@ typedef struct { | |||||
| static sdl_render_data the_render_data = {0}; | static sdl_render_data the_render_data = {0}; | ||||
| static int filter(void*, SDL_Event* event) { | |||||
| return (event->type == SDL_QUIT); | |||||
| } | |||||
| static int sdl_render_init(nes_Renderer* rend) { | static int sdl_render_init(nes_Renderer* rend) { | ||||
| sdl_render_data* data = &the_render_data; | sdl_render_data* data = &the_render_data; | ||||
| int status = SDL_Init(SDL_INIT_VIDEO); | int status = SDL_Init(SDL_INIT_VIDEO); | ||||
| @@ -136,9 +125,6 @@ static int sdl_render_init(nes_Renderer* rend) { | |||||
| nes_palette, 0U, 64U); | nes_palette, 0U, 64U); | ||||
| SDL_SetColorKey(data->sprite8, SDL_TRUE, 0xFFU); | SDL_SetColorKey(data->sprite8, SDL_TRUE, 0xFFU); | ||||
| // TODO: Move this to a separate component | |||||
| SDL_SetEventFilter(filter, NULL); | |||||
| rend->data = &the_render_data; | rend->data = &the_render_data; | ||||
| } | } | ||||
| @@ -222,11 +208,6 @@ static void render_background_line(const nes_ppu* ppu, int line, | |||||
| int page = (ppu->control & ppu_Control_Nametable_Mask); | int page = (ppu->control & ppu_Control_Nametable_Mask); | ||||
| int x = ppu->scroll_x / 8; | int x = ppu->scroll_x / 8; | ||||
| /* | |||||
| // Kludge | |||||
| if (ppu->scanline < 33) page = 0; | |||||
| */ | |||||
| // Left | // Left | ||||
| render_background_area( | render_background_area( | ||||
| ppu, page, buffer, pitch, | ppu, page, buffer, pitch, | ||||
| @@ -480,10 +461,13 @@ static int sdl_render(nes_Renderer* rend, nes_ppu* ppu) { | |||||
| data->background->pixels, | data->background->pixels, | ||||
| data->background->pitch); | data->background->pitch); | ||||
| // TODO: Y scroll support | |||||
| int x_fine = ppu->scroll_x % 8; | int x_fine = ppu->scroll_x % 8; | ||||
| // Check for Sprite 0 Hit | // Check for Sprite 0 Hit | ||||
| if (ppu->mask & ppu_Mask_Sprite) { | |||||
| if ( 0 >= ppu->hit_line && | |||||
| (ppu->mask & ppu_Mask_Sprite)) { | |||||
| update_sprite_hit(ppu, | update_sprite_hit(ppu, | ||||
| data->background->pixels, | data->background->pixels, | ||||
| data->background->pitch); | data->background->pitch); | ||||
| @@ -527,12 +511,6 @@ static int sdl_render(nes_Renderer* rend, nes_ppu* ppu) { | |||||
| status = 1; | status = 1; | ||||
| } | } | ||||
| // TODO: Handle this in the input loop, or anywhere else | |||||
| SDL_Event event = {0}; | |||||
| if (1 == SDL_PollEvent(&event) && event.type == SDL_QUIT) { | |||||
| status = -1; | |||||
| } | |||||
| return status; | return status; | ||||
| } | } | ||||