diff --git a/src/f6502.c b/src/f6502.c index b590dee..3b05c0b 100644 --- a/src/f6502.c +++ b/src/f6502.c @@ -34,6 +34,17 @@ static inline uint16_t f6502_read16_zp(nes_Memory* mem, ((uint16_t)f6502_read_zp(mem, addr + 1) << 8)); } +static inline uint8_t read_gamepad_bit(nes_Gamepad* gamepad) { + uint8_t state = 1; + if (gamepad->shift < 0) { + state = (gamepad->buttons >> Button_A) & 1; + } else if (gamepad->shift < nes_controller_num_buttons) { + state = (gamepad->buttons >> gamepad->shift) & 1; + gamepad->shift++; + } + return state; +} + static inline uint8_t f6502_read(nes_Memory* mem, uint16_t addr) { #ifdef F6502_FLAT @@ -78,7 +89,17 @@ static inline uint8_t f6502_read(nes_Memory* mem, break; case 0x4000: - // TODO: APU Reg + switch (addr & 0x1FU) { + case 0x16: + case 0x17: + { nes_Gamepad* gamepad = &mem->input.gamepads[addr & 1]; + return ((addr >> 8) & nes_controller_bus_mask) | + read_gamepad_bit(gamepad); + } break; + + default: + // TODO: APU Reg + } break; case 0x6000: @@ -98,9 +119,9 @@ static inline uint16_t f6502_read16(nes_Memory* mem, ((uint16_t)f6502_read(mem, addr + 1) << 8)); } -static inline bool f6502_write(nes_Memory* mem, - uint16_t addr, uint8_t val) { - bool ret = 0; +static inline int f6502_write(nes_Memory* mem, + uint16_t addr, uint8_t val) { + int ret = 0; #ifdef F6502_TRACE printf("W $%04X <- %02X\n", addr, val); #endif @@ -124,7 +145,10 @@ static inline bool f6502_write(nes_Memory* mem, f6502_set_NMI(core, nmi); f6502_set_IRQ(core, irq); - ret = 1; // Interrupt [may have been] triggered + // Interrupt [may have been] triggered + // We're not strictly counting cycles, + // so just overload the counter + ret = 1000; } #endif mem->ram[addr] = val; @@ -229,7 +253,7 @@ static inline bool f6502_write(nes_Memory* mem, case 0x4000: // TODO: APU switch (addr & 0x1FU) { - case 0x14: + case 0x14: // OAM DMA { uint8_t* src = NULL; // OAM DMA switch (val >> 5) { @@ -253,9 +277,18 @@ static inline bool f6502_write(nes_Memory* mem, if (NULL != src) { memcpy(mem->ppu.oam, src, NES_PPU_OAM_SIZE); } - // TODO: Spend 513 cycles - } - break; + ret = 513; + } break; + + case 0x16: // Reset Gamepad + if (val & 1) { + mem->input.gamepads[0].shift = -1; + mem->input.gamepads[1].shift = -1; + } else { + mem->input.gamepads[0].shift = 0; + mem->input.gamepads[1].shift = 0; + } + break; } break; @@ -412,7 +445,7 @@ static inline int f6502_check_interrupts(f6502_Core* core) { register uint8_t val = f6502_read(&core->memory, addr); \ SET(P, table_asl[val]); \ val <<= 1; \ - int_trig = f6502_write(&core->memory, addr, val); \ + clocks_elapsed += f6502_write(&core->memory, addr, val); \ }) #define ASL_A() \ CLR(P, f6502_Status_N | f6502_Status_Z | f6502_Status_C); \ @@ -455,7 +488,7 @@ static inline int f6502_check_interrupts(f6502_Core* core) { register uint8_t val = f6502_read(&core->memory, addr); \ --val; \ TEST(val); \ - int_trig = f6502_write(&core->memory, addr, val); \ + clocks_elapsed += f6502_write(&core->memory, addr, val); \ }) #define EOR(v) A ^= (v); TEST(A) #define INC(a) ({ \ @@ -463,7 +496,7 @@ static inline int f6502_check_interrupts(f6502_Core* core) { register uint8_t val = f6502_read(&core->memory, addr); \ ++val; \ TEST(val); \ - int_trig = f6502_write(&core->memory, addr, val); \ + clocks_elapsed += f6502_write(&core->memory, addr, val); \ }) #define JMP(a) PC = (a) #define LD(reg, v) reg = (v); TEST(reg) @@ -475,7 +508,7 @@ static inline int f6502_check_interrupts(f6502_Core* core) { val >>= 1; \ SET(P, val & f6502_Status_N); \ if (!val) SET(P, f6502_Status_Z); \ - int_trig = f6502_write(&core->memory, addr, val); \ + clocks_elapsed += f6502_write(&core->memory, addr, val); \ }) #define LSR_A() \ CLR(P, f6502_Status_N | f6502_Status_Z | f6502_Status_C); \ @@ -493,7 +526,7 @@ static inline int f6502_check_interrupts(f6502_Core* core) { P |= new_c; \ if (!val) SET(P, f6502_Status_Z); \ SET(P, val & f6502_Status_N); \ - int_trig = f6502_write(&core->memory, addr, val); \ + clocks_elapsed += f6502_write(&core->memory, addr, val); \ }) #define ROL_A() { \ register uint8_t new_c = (A & 0x80) ? f6502_Status_C : 0; \ @@ -513,7 +546,7 @@ static inline int f6502_check_interrupts(f6502_Core* core) { P |= new_c; \ if (!val) SET(P, f6502_Status_Z); \ SET(P, (val & f6502_Status_N)); \ - int_trig = f6502_write(&core->memory, addr, val); \ + clocks_elapsed += f6502_write(&core->memory, addr, val); \ }) #define ROR_A() { \ register uint8_t new_c = (A & f6502_Status_C); \ @@ -536,7 +569,7 @@ static inline int f6502_check_interrupts(f6502_Core* core) { } \ A = res; \ } -#define ST(reg, a) int_trig = f6502_write(&core->memory, (a), reg) +#define ST(reg, a) clocks_elapsed += f6502_write(&core->memory, (a), reg) static int f6502_do_step(f6502_Core* core, int clocks) { @@ -549,9 +582,7 @@ static int f6502_do_step(f6502_Core* core, int clocks) { register uint8_t Y = core->registers.Y; register uint8_t P = core->registers.P; - register bool int_trig = 0; - - while (clocks_elapsed < clocks && !int_trig) { + while (clocks_elapsed < clocks) { uint8_t opcode = f6502_read(&core->memory, PC++); #ifdef F6502_TRACE printf("S:%02x A:%02x X:%02x Y:%02x P:%02x\n", @@ -804,7 +835,8 @@ static int f6502_do_step(f6502_Core* core, int clocks) { SET(P, f6502_Status_1 | f6502_Status_B); POP16(core, S, PC); CLK(6); - int_trig = 1; + // Interrupt might be triggered; break out + clocks = 0; break; case 0x41: // EOR (zp, X) @@ -1573,7 +1605,7 @@ static int f6502_do_step(f6502_Core* core, int clocks) { } #ifdef F6502_TRACE - if (int_trig) printf("Possible interrupt trigger\n"); +// if (int_trig) printf("Possible interrupt trigger\n"); #endif #ifdef F6502_HCF diff --git a/src/input.h b/src/input.h index 3d223a7..520b851 100644 --- a/src/input.h +++ b/src/input.h @@ -4,6 +4,7 @@ #define nes_controller_bus_mask (0b11111000) +#define nes_controller_num_buttons (8U) typedef enum { Button_A = 0, diff --git a/src/linux/port.c b/src/linux/port.c index 0379d02..cf758c1 100644 --- a/src/linux/port.c +++ b/src/linux/port.c @@ -3,6 +3,7 @@ #include #include +#include #include @@ -30,6 +31,9 @@ int nese_unmap_file(void* addr, int size) { typedef struct { nes* sys; + + int64_t t_target; + SDL_Window* window; SDL_Renderer* renderer; SDL_Texture* texture; @@ -37,18 +41,56 @@ typedef struct { SDL_Surface* screen; } platform_data; + +static const int sdl_alt_start_key = SDLK_RETURN; + +static const int sdl_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, const int* codes) { + int index = nes_controller_num_buttons - 1; + for ( ; index >= 0 && keycode != codes[index]; --index); + return index; +} + // TODO: Return an action enum? static int process_events(nes* sys) { int status = 0; + nes_Input* input = &sys->core.memory.input; SDL_Event event = {0}; while (0 == status && 0 != SDL_PollEvent(&event)) { if (SDL_QUIT == event.type) { status = -1; - } - // TODO: Update gamepad - // TODO: Menu actions, &c. + } else if ( ( SDL_KEYDOWN == event.type || + SDL_KEYUP == event.type) && + 0 == event.key.repeat + ) { + int index = ( sdl_alt_start_key == + event.key.keysym.sym) ? + Button_Start : button_index( + event.key.keysym.sym, + sdl_keycodes); + if (index >= 0) { + uint8_t mask = (1 << index); + if (SDL_KEYDOWN == event.type) { + input->gamepads[0].buttons |= mask; + } else { + input->gamepads[0].buttons &= ~mask; + } + } + // TODO: Menu or other hotkeys + } + // TODO: Controller inputs } return status; @@ -103,6 +145,26 @@ int nese_line_ready(void* plat_data, uint8_t* buffer, int line) { return 0; } + +#define NS_PER_S (1000U * 1000U * 1000U) +#define FRAME_TIME_NS (16639267L) + +uint64_t time_now(void) { + struct timespec ts_now = {0}; + clock_gettime(CLOCK_REALTIME, &ts_now); + return ((int64_t)ts_now.tv_sec * NS_PER_S) + ts_now.tv_nsec; +} + +int64_t time_sleep_until(int64_t t_target) { + int64_t t_now = time_now(); + int64_t t_diff = (t_target - t_now) / 1000; + if (t_diff > 0) { + usleep(t_diff); + } + return t_diff * 1000; +} + + int nese_frame_ready(void* plat_data) { int status = 0; platform_data* plat = (platform_data*)plat_data; @@ -158,13 +220,21 @@ int nese_frame_ready(void* plat_data) { // TODO: Perform menu actions - // TODO: Time sync + if (0 == status) { + plat->t_target += FRAME_TIME_NS; + int64_t slept_ns = time_sleep_until(plat->t_target); + if (slept_ns <= -FRAME_TIME_NS) { + // We're way out of sync. + plat->t_target = time_now(); + printf("Out of sync: %d\n", (int)slept_ns); + } + } return status; } int nese_update_input(void* plat_data, nes_Input* input) { - // TODO: Populate the gamepad states + // Gamepad states are already updated in nese_frame_ready() return 0; } @@ -236,6 +306,10 @@ static int plat_init(platform_data* plat) { } } + if (0 == status) { + plat->t_target = time_now(); + } + return status; } diff --git a/src/memory.h b/src/memory.h index c1bc0af..a417ea2 100644 --- a/src/memory.h +++ b/src/memory.h @@ -3,6 +3,7 @@ #include +#include "input.h" #include "mapper.h" #include "ppu.h" @@ -31,6 +32,7 @@ struct nes_Memory { int n_rom_banks; nes_PPU_Memory ppu; nes_Mapper mapper; + nes_Input input; #endif }; typedef struct nes_Memory nes_Memory; diff --git a/src/nes.c b/src/nes.c index ac38659..b7cd574 100644 --- a/src/nes.c +++ b/src/nes.c @@ -38,7 +38,7 @@ static int nes_vsync(nes* sys, void* plat) { nes_Memory* mem = &sys->core.memory; if (0 == status && NULL != mem->mapper.vsync) { mem->mapper.vsync(&mem->mapper); - status = nese_update_input(plat, &sys->input); + status = nese_update_input(plat, &mem->input); } return status; diff --git a/src/nes.h b/src/nes.h index 3de9957..4891640 100644 --- a/src/nes.h +++ b/src/nes.h @@ -13,7 +13,6 @@ typedef struct { nes_PPU ppu; // TODO: PPU // TODO: APU - nes_Input input; } nes; void nes_init(nes*);