diff --git a/Makefile b/Makefile index ef09cb0..99d05b0 100644 --- a/Makefile +++ b/Makefile @@ -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 diff --git a/src/input.c b/src/input.c new file mode 100644 index 0000000..0839d6f --- /dev/null +++ b/src/input.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; + } +} diff --git a/src/input.h b/src/input.h new file mode 100644 index 0000000..cb488b1 --- /dev/null +++ b/src/input.h @@ -0,0 +1,70 @@ +#ifndef NES_INPUT_H_ +#define NES_INPUT_H_ + +#include +#include + + +// 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_ diff --git a/src/nes.c b/src/nes.c index 7a305b1..9ca833d 100644 --- a/src/nes.c +++ b/src/nes.c @@ -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); diff --git a/src/nes.h b/src/nes.h index b6cef28..b4d175f 100644 --- a/src/nes.h +++ b/src/nes.h @@ -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; diff --git a/src/nese.c b/src/nese.c index 44da303..2c96124 100644 --- a/src/nese.c +++ b/src/nese.c @@ -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; diff --git a/src/sdl_input.c b/src/sdl_input.c new file mode 100644 index 0000000..ac04a9a --- /dev/null +++ b/src/sdl_input.c @@ -0,0 +1,72 @@ +#include + +#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, +}; diff --git a/src/sdl_render.c b/src/sdl_render.c index a198b3b..f580803 100644 --- a/src/sdl_render.c +++ b/src/sdl_render.c @@ -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; }