Преглед на файлове

Add input support; fix cycle counts; implement frame timing

v2
Nathaniel Walizer преди 9 месеца
родител
ревизия
9aee1f1d2e
променени са 6 файла, в които са добавени 136 реда и са изтрити 28 реда
  1. +53
    -21
      src/f6502.c
  2. +1
    -0
      src/input.h
  3. +79
    -5
      src/linux/port.c
  4. +2
    -0
      src/memory.h
  5. +1
    -1
      src/nes.c
  6. +0
    -1
      src/nes.h

+ 53
- 21
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


+ 1
- 0
src/input.h Целия файл

@@ -4,6 +4,7 @@

#define nes_controller_bus_mask (0b11111000)

#define nes_controller_num_buttons (8U)

typedef enum {
Button_A = 0,


+ 79
- 5
src/linux/port.c Целия файл

@@ -3,6 +3,7 @@

#include <time.h>
#include <sys/mman.h>
#include <unistd.h>

#include <SDL.h>

@@ -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;
}



+ 2
- 0
src/memory.h Целия файл

@@ -3,6 +3,7 @@

#include <stdint.h>

#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;


+ 1
- 1
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;


+ 0
- 1
src/nes.h Целия файл

@@ -13,7 +13,6 @@ typedef struct {
nes_PPU ppu;
// TODO: PPU
// TODO: APU
nes_Input input;
} nes;

void nes_init(nes*);


Loading…
Отказ
Запис