Browse Source

Add controller support with keyboard via SDL

- a -> A
 - s -> B
 - q -> Select
 - w -> Start
 - Arrow keys
master
Nathaniel Walizer 1 year ago
parent
commit
57fcc77c27
8 changed files with 205 additions and 29 deletions
  1. +2
    -1
      Makefile
  2. +30
    -0
      src/input.c
  3. +70
    -0
      src/input.h
  4. +8
    -1
      src/nes.c
  5. +7
    -0
      src/nes.h
  6. +12
    -1
      src/nese.c
  7. +72
    -0
      src/sdl_input.c
  8. +4
    -26
      src/sdl_render.c

+ 2
- 1
Makefile View File

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



+ 30
- 0
src/input.c View File

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

+ 70
- 0
src/input.h View File

@@ -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_

+ 8
- 1
src/nes.c View File

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



+ 7
- 0
src/nes.h View File

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


+ 12
- 1
src/nese.c View File

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


+ 72
- 0
src/sdl_input.c View File

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

+ 4
- 26
src/sdl_render.c View File

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



Loading…
Cancel
Save