Browse Source

Add APU Skeleton

v2
Nathaniel Walizer 9 months ago
parent
commit
6568131ec4
10 changed files with 283 additions and 18 deletions
  1. +1
    -1
      Makefile
  2. +91
    -0
      src/apu.c
  3. +105
    -0
      src/apu.h
  4. +49
    -5
      src/f6502.c
  5. +27
    -6
      src/linux/port.c
  6. +2
    -0
      src/memory.h
  7. +4
    -3
      src/nes.c
  8. +2
    -2
      src/nes.h
  9. +1
    -1
      src/nese.c
  10. +1
    -0
      src/port.h

+ 1
- 1
Makefile View File

@@ -29,7 +29,7 @@ MAPDIR = $(SRCDIR)/map

NESE_SRC_SRCS = f6502.c f6502_opcodes.c
NESE_SRC_SRCS += nese.c nes.c cart.c mapper.c
NESE_SRC_SRCS += ppu.c
NESE_SRC_SRCS += ppu.c apu.c
NESE_SRC_SRCS += $(OS)/port.c
NESE_MAP_SRCS = $(notdir $(wildcard $(MAPDIR)/*.c))



+ 91
- 0
src/apu.c View File

@@ -0,0 +1,91 @@
#include "memory.h"


void nes_apu_init(nes_APU* apu, nes_APU_Memory* mem, int freq) {
// TODO
}

void nes_apu_hsync(nes_APU* apu, nes_Memory* core_mem) {
nes_APU_Memory* mem = &core_mem->apu;

for (int i = 0; i < core_mem->apu.n_events; ++i) {
int reg = core_mem->apu.events[i].reg;
int val = core_mem->apu.events[i].val;

switch (reg) {
// TODO: 0x00 - 0x13
// TODO: 0x15
case 0x17: // Frame Counter
apu->frame_reg = val & ( apu_Frame_Mode |
apu_Frame_Inhibit);
apu->frame = 0;
apu->frame_delay = 0;

if (val & apu_Frame_Mode) {
// TODO
/*
nes_apu_clock_quarter_frame(apu);
nes_apu_clock_half_frame(apu);
*/
}

if (val & apu_Frame_Inhibit) {
mem->status &= ~apu_Status_Frame_Int;
}

break;

}
}

core_mem->apu.n_events = 0;

// Frame advance

apu->frame_delay += nes_apu_hsync_ticks;

if (apu->frame_delay >= nes_apu_quarter_frame_ticks) {
apu->frame_delay -= nes_apu_quarter_frame_ticks;

int end = 0;
int quarter_frame = 1;
int half_frame = 0;

if (1 == apu->frame) {
half_frame = 1;
}

if (apu->frame_reg & apu_Frame_Mode) {
if (3 == apu->frame) {
quarter_frame = 0;
} else if (4 <= apu->frame) {
half_frame = 1;
end = 1;
}
} else {
if (3 <= apu->frame) {
half_frame = 1;
end = 1;
}
}

if (half_frame) {
// TODO
//nes_apu_clock_half_frame(apu);
}

if (quarter_frame) {
// TODO
//nes_apu_clock_quarter_frame(apu);
}

if (end) {
if (0 == apu->frame_reg) {
mem->status |= apu_Status_Frame_Int;
}
apu->frame = 0;
} else {
apu->frame++;
}
}
}

+ 105
- 0
src/apu.h View File

@@ -0,0 +1,105 @@
#ifndef NESE_APU_H_
#define NESE_APU_H_

#include <stdint.h>


#define nes_apu_hsync_ticks (4U)
#define nes_apu_quarter_frame_ticks (262U)


typedef struct {
uint8_t reg;
uint8_t val;
} nes_APU_Event;

#define APU_MAX_EVENTS (64U)

typedef enum __attribute__ ((__packed__)) {
apu_Status_Square_0 = 0b00000001,
apu_Status_Square_1 = 0b00000010,
apu_Status_Triangle = 0b00000100,
apu_Status_Noise = 0b00001000,
apu_Status_DMC = 0b00010000,
apu_Status_Frame_Int = 0b01000000,
apu_Status_DMC_Int = 0b10000000,
} nes_apu_Status;

typedef struct {
nes_APU_Event events[APU_MAX_EVENTS];
uint8_t n_events;
nes_apu_Status status;
} nes_APU_Memory;


typedef enum __attribute__ ((__packed__)) {
apu_Frame_Inhibit = 0b01000000,
apu_Frame_Mode = 0b10000000,
} nes_apu_Frame;


typedef enum {
apu_Channel_Square_0 = 0,
apu_Channel_Square_1 = 1,
apu_Channel_Triangle = 2,
apu_Channel_Noise = 3,
apu_Channel_DMC = 4,
} nes_apu_Channel_Index;

// Applies to Square and Noise
typedef enum {
apu_Envelope_Volume = 0b00001111,
apu_Envelope_Constant = 0b00010000,
apu_Envelope_Halt = 0b00100000,
apu_Envelope_Duty = 0b11000000,
} nes_apu_Envelope_Reg_0_Mask;

typedef enum {
apu_Square_Shift = 0b00000111,
apu_Square_Negate = 0b00001000,
apu_Square_Period = 0b01110000,
apu_Square_Enable = 0b10000000,
} nes_apu_Square_Reg_1_Mask;

typedef enum {
apu_Triangle_Count = 0b01111111,
apu_Triangle_Halt = 0b10000000,
} nes_apu_Triangle_Reg_0_Mask;

typedef enum {
apu_Noise_Period = 0b00001111,
apu_Noise_Mode = 0b10000000,
} nes_apu_Noise_Reg_2_Mask;

typedef enum {
apu_Noise_Length = 0b11111000,
} nes_apu_Noise_Reg_3_Mask;

typedef enum {
apu_DMC_Period = 0b00001111,
apu_DMC_Loop = 0b01000000,
apu_DMC_IRQ_Enable = 0b10000000,
} nes_apu_DMC_Reg_0_Mask;

typedef enum {
apu_Channel_Reload = 0b00000001,
apu_Channel_Start = 0b00000010,
} nes_apu_Channel_Flag;

typedef struct {
nes_apu_Frame frame_reg;
int frame;
int frame_delay;

uint8_t reg[20];

// TODO: Channel data
} nes_APU;

struct nes_Memory;

void nes_apu_init(nes_APU* apu, nes_APU_Memory* mem, int freq);
void nes_apu_hsync(nes_APU*, struct nes_Memory*);


#endif // NESE_APU_H_

+ 49
- 5
src/f6502.c View File

@@ -90,15 +90,21 @@ static inline uint8_t f6502_read(nes_Memory* mem,

case 0x4000:
switch (addr & 0x1FU) {
case 0x15:
return mem->apu.status;

case 0x16:
case 0x17:
{ nes_Gamepad* gamepad = &mem->input.gamepads[addr & 1];
return ((addr >> 8) & nes_controller_bus_mask) |
read_gamepad_bit(gamepad);
} break;
read_gamepad_bit(
&mem->input.gamepads[addr & 1]
);

default:
// TODO: APU Reg
if (mem->mapper.read_apu) {
return mem->mapper.read_apu(&mem->mapper, mem,
addr & 0x1FU);
}
}
break;

@@ -246,8 +252,40 @@ static inline int f6502_write(nes_Memory* mem,
break;

case 0x4000:
// TODO: APU
switch (addr & 0x1FU) {
case 0x00:
case 0x01:
case 0x02:
case 0x03:
case 0x04:
case 0x05:
case 0x06:
case 0x07:
case 0x08:
case 0x09:
case 0x0A:
case 0x0B:
case 0x0C:
case 0x0D:
case 0x0E:
case 0x0F:
case 0x10:
case 0x11:
case 0x12:
case 0x13:
case 0x15:
case 0x17:
if (mem->apu.n_events >= APU_MAX_EVENTS) {
LOGE("APU event buffer full");
} else {
mem->apu.events[mem->apu.n_events++] =
(nes_APU_Event){
.reg = addr & 0x1FU,
.val = val,
};
}
break;

case 0x14: // OAM DMA
{ uint8_t* src = NULL;
// OAM DMA
@@ -285,6 +323,12 @@ static inline int f6502_write(nes_Memory* mem,
mem->input.gamepads[1].shift = 0;
}
break;

default:
if (mem->mapper.write_apu) {
ret = mem->mapper.write_apu(&mem->mapper, mem,
addr, val);
}
}
break;



+ 27
- 6
src/linux/port.c View File

@@ -15,7 +15,8 @@
#include "log.h"


/* OS-specific file operations
/*
* OS-specific file operations
* Memory mapping specifically needs to be ported for each OS
*/

@@ -42,7 +43,8 @@ static int nese_file_size(FILE* file) {
}


/* Platform-specific allocation
/*
* Platform-specific allocation
* GPU and CPU refer to emulator memory spaces
* Note: GPU (and possibly CPU) regions may need
* to be placed into internal ram for performance
@@ -61,7 +63,8 @@ void* nese_alloc(int size) {
}


/* SDL-specific init and entry point
/*
* SDL-specific init and entry point
* This should be maximally reusable across platforms
*/

@@ -84,6 +87,14 @@ typedef struct {
} platform_data;


/* Input */

int nese_update_input(void* plat_data, nes_Input* input) {
// Gamepad states are already updated in nese_frame_ready()
return 0;
}


static const int sdl_alt_start_key = SDLK_RETURN;

static const int sdl_keycodes[nes_controller_num_buttons] = {
@@ -138,6 +149,9 @@ static int process_events(nes* sys) {
return status;
}


/* Time / Video */

static SDL_Color nes_palette[64] = {
{0x80,0x80,0x80}, {0x00,0x00,0xBB}, {0x37,0x00,0xBF}, {0x84,0x00,0xA6},
{0xBB,0x00,0x6A}, {0xB7,0x00,0x1E}, {0xB3,0x00,0x00}, {0x91,0x26,0x00},
@@ -278,11 +292,18 @@ int nese_frame_ready(void* plat_data) {
return status;
}

int nese_update_input(void* plat_data, nes_Input* input) {
// Gamepad states are already updated in nese_frame_ready()
return 0;

/* Audio */

int nese_get_audio_frequency(void*) {
return 44100;
}

// TODO: Audio functions


/* Platform Data */

static int plat_init(platform_data* plat) {
int status = SDL_Init(
SDL_INIT_EVENTS |


+ 2
- 0
src/memory.h View File

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

#include <stdint.h>

#include "apu.h"
#include "input.h"
#include "mapper.h"
#include "ppu.h"
@@ -33,6 +34,7 @@ struct nes_Memory {
nes_PPU_Memory ppu;
nes_Mapper mapper;
nes_Input input;
nes_APU_Memory apu;
#endif
};
typedef struct nes_Memory nes_Memory;


+ 4
- 3
src/nes.c View File

@@ -7,10 +7,11 @@
#include "log.h"


void nes_init(nes* sys) {
void nes_init(nes* sys, void* plat) {
f6502_init(&sys->core);
nes_ppu_init(&sys->ppu, &sys->core.memory.ppu);
// TODO: Init APU
nes_apu_init(&sys->apu, &sys->core.memory.apu,
nese_get_audio_frequency(plat));
}

void nes_reset(nes* sys) {
@@ -47,7 +48,7 @@ static int nes_vsync(nes* sys, void* plat) {
static int nes_hsync(nes* sys, void* plat) {
int status = 0;

// TODO: APU sync
nes_apu_hsync(&sys->apu, &sys->core.memory);

if (sys->ppu.scanline < nes_ppu_postrender_line) {
if (sys->ppu.scanline < nes_ppu_visible_line) {


+ 2
- 2
src/nes.h View File

@@ -11,10 +11,10 @@ typedef struct {
const ines_Header* cart_header;
f6502_Core core;
nes_PPU ppu;
// TODO: APU
nes_APU apu;
} nes;

void nes_init(nes*);
void nes_init(nes*, void*);
void nes_reset(nes*);
void nes_done(nes*);
int nes_loop(nes*, void*);


+ 1
- 1
src/nese.c View File

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


int nese_start(nes* sys, void* plat) {
nes_init(sys);
nes_init(sys, plat);

nes_reset(sys);



+ 1
- 0
src/port.h View File

@@ -10,6 +10,7 @@ int nese_frame_start(void*, uint8_t background);
int nese_line_ready(void*, uint8_t* buffer, int line);
int nese_frame_ready(void*);
int nese_update_input(void*, nes_Input*);
int nese_get_audio_frequency(void*);

void* nese_alloc_gpu(int);
void* nese_alloc_cpu(int);


Loading…
Cancel
Save