- a -> A - s -> B - q -> Select - w -> Start - Arrow keysmaster
| @@ -13,7 +13,8 @@ BINDIR = bin | |||
| TARGET_1 = nese | |||
| 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 | |||
| @@ -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); | |||
| 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) { | |||
| 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); | |||
| 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); | |||
| for (int i = 0; i < nes_ppu_oam_size; ++i) { | |||
| 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; | |||
| // 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) { | |||
| nes_apu_write(&sys->apu, addr, val); | |||
| @@ -3,6 +3,7 @@ | |||
| #include "apu.h" | |||
| #include "ppu.h" | |||
| #include "input.h" | |||
| #include "e6502/e6502.h" | |||
| @@ -29,8 +30,13 @@ | |||
| #define nes_ppu_map_size (0x8U) | |||
| #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) | |||
| typedef struct { | |||
| // TODO: Dynamic size support | |||
| uint8_t prg[nes_mem_rom_size]; | |||
| @@ -47,6 +53,7 @@ typedef struct { | |||
| e6502_Core cpu; | |||
| nes_ppu ppu; | |||
| nes_apu apu; | |||
| nes_input input; | |||
| uint8_t ram[nes_mem_ram_size]; | |||
| nes_cart cart; | |||
| } nes; | |||
| @@ -4,6 +4,7 @@ | |||
| #include "ines.h" | |||
| #include "render.h" | |||
| #include "input.h" | |||
| 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; | |||
| } | |||
| extern nes_Renderer sdl_renderer; | |||
| extern nes_Input_Reader sdl_input; | |||
| static nes sys = {0}; | |||
| int main(int argc, char* argv[]) { | |||
| @@ -68,6 +73,11 @@ int main(int argc, char* argv[]) { | |||
| status = nes_render_init(rend); | |||
| } | |||
| nes_Input_Reader* input = &sdl_input; | |||
| if (status == 0) { | |||
| status = nes_input_init(input); | |||
| } | |||
| if (status == 0) { | |||
| nes_init(&sys); | |||
| nes_reset(&sys); | |||
| @@ -114,7 +124,8 @@ int main(int argc, char* argv[]) { | |||
| 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) { | |||
| 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 "ppu.h" | |||
| /* | |||
| typedef struct { | |||
| uint8_t r; | |||
| uint8_t g; | |||
| uint8_t b; | |||
| } __attribute__ (( packed )) sPal; | |||
| */ | |||
| static SDL_Color nes_palette[64] = { | |||
| {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 int filter(void*, SDL_Event* event) { | |||
| return (event->type == SDL_QUIT); | |||
| } | |||
| static int sdl_render_init(nes_Renderer* rend) { | |||
| sdl_render_data* data = &the_render_data; | |||
| int status = SDL_Init(SDL_INIT_VIDEO); | |||
| @@ -136,9 +125,6 @@ static int sdl_render_init(nes_Renderer* rend) { | |||
| nes_palette, 0U, 64U); | |||
| SDL_SetColorKey(data->sprite8, SDL_TRUE, 0xFFU); | |||
| // TODO: Move this to a separate component | |||
| SDL_SetEventFilter(filter, NULL); | |||
| 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 x = ppu->scroll_x / 8; | |||
| /* | |||
| // Kludge | |||
| if (ppu->scanline < 33) page = 0; | |||
| */ | |||
| // Left | |||
| render_background_area( | |||
| ppu, page, buffer, pitch, | |||
| @@ -480,10 +461,13 @@ static int sdl_render(nes_Renderer* rend, nes_ppu* ppu) { | |||
| data->background->pixels, | |||
| data->background->pitch); | |||
| // TODO: Y scroll support | |||
| int x_fine = ppu->scroll_x % 8; | |||
| // 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, | |||
| data->background->pixels, | |||
| data->background->pitch); | |||
| @@ -527,12 +511,6 @@ static int sdl_render(nes_Renderer* rend, nes_ppu* ppu) { | |||
| 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; | |||
| } | |||