소스 검색

Add OAM DMA; More sprite rendering preparation

v2
부모
커밋
0239f9773f
6개의 변경된 파일213개의 추가작업 그리고 21개의 파일을 삭제
  1. +35
    -3
      src/f6502.c
  2. +24
    -8
      src/linux/port.c
  3. +18
    -5
      src/nes.c
  4. +1
    -0
      src/port.h
  5. +131
    -3
      src/ppu.c
  6. +4
    -2
      src/ppu.h

+ 35
- 3
src/f6502.c 파일 보기

@@ -1,4 +1,5 @@
#include <stdbool.h>
#include <string.h>

#include "f6502.h"
#include "f6502_consts.h"
@@ -196,12 +197,14 @@ static inline bool f6502_write(nes_Memory* mem,
if (addr >= NES_PPU_PAL_START) {
// Copy to render reference
addr &= 0x1FU;
mem->ppu.palette[addr] = val;
uint8_t* pal = mem->ppu.palette;
if (0 == (addr & 0xF)) {
uint8_t* pal = mem->ppu.palette;
// TODO: Just initialize this
pal[0] = pal[4] = pal[8] = pal[12] =
pal[16] = pal[20] = pal[24] =
pal[28] = val;
pal[28] = 0xFFU;
} else {
pal[addr] = val;
}

// Memory-mapped mirroring
@@ -225,6 +228,35 @@ static inline bool f6502_write(nes_Memory* mem,

case 0x4000:
// TODO: APU
switch (addr & 0x1FU) {
case 0x14:
{ uint8_t* src = NULL;
// OAM DMA
switch (val >> 5) {
case 0: // 0x0000 - 0x1FFF RAM
src = &mem->ram[((int)val << 8) & 0x7FFU];
break;

case 3: // 0x6000 - 0x7FFF SRAM
if (mem->sram_bank) {
src = &mem->sram_bank[((int)val << 8) & 0x1FFFU];
}
break;

case 4: // 0x8000 - 0x9FFF ROM
case 5: // 0xA000 - 0xBFFF ROM
case 6: // 0xC000 - 0xDFFF ROM
case 7: // 0xE000 - 0xFFFF ROM
src = &mem->rom_bank[(val >> 5) & 3][((int)val << 8) & 0x1FFFU];
break;
}
if (NULL != src) {
memcpy(mem->ppu.oam, src, NES_PPU_OAM_SIZE);
}
// TODO: Spend 513 cycles
}
break;
}
break;

case 0x6000:


+ 24
- 8
src/linux/port.c 파일 보기

@@ -34,7 +34,7 @@ typedef struct {
SDL_Renderer* renderer;
SDL_Texture* texture;
SDL_Surface* target;
SDL_Surface* scanline;
SDL_Surface* screen;
} platform_data;

// TODO: Return an action enum?
@@ -76,7 +76,18 @@ static SDL_Color nes_palette[64] = {
{0xB3,0xEE,0xFF}, {0xDD,0xDD,0xDD}, {0x11,0x11,0x11}, {0x11,0x11,0x11}
};

int nese_frame_start(void* plat_data, uint8_t background) {
platform_data* plat = (platform_data*)plat_data;

SDL_Color ext = nes_palette[background];
SDL_FillRect(plat->target, NULL, ((int)ext.r << 16) |
((int)ext.g << 8) | ext.b);

return 0;
}

int nese_line_ready(void* plat_data, uint8_t* buffer, int line) {
/*
platform_data* plat = (platform_data*)plat_data;

SDL_Rect rect = {
@@ -87,6 +98,7 @@ int nese_line_ready(void* plat_data, uint8_t* buffer, int line) {
};

SDL_BlitSurface(plat->scanline, NULL, plat->target, &rect);
*/

return 0;
}
@@ -112,6 +124,8 @@ int nese_frame_ready(void* plat_data) {
t_last = t_now;
*/

SDL_BlitSurface(plat->screen, NULL, plat->target, NULL);

SDL_UnlockTexture(plat->texture);
SDL_RenderCopy(plat->renderer, plat->texture,
NULL, NULL);
@@ -160,8 +174,8 @@ static int plat_init(platform_data* plat) {
if (0 == status) {
plat->renderer = SDL_CreateRenderer(
plat->window, -1,
SDL_RENDERER_ACCELERATED |
SDL_RENDERER_PRESENTVSYNC
SDL_RENDERER_ACCELERATED/* |
SDL_RENDERER_PRESENTVSYNC*/
);
if (NULL == plat->renderer) {
LOGE("SDL: Failed to create renderer");
@@ -186,19 +200,21 @@ static int plat_init(platform_data* plat) {
}

if (0 == status) {
plat->scanline = SDL_CreateRGBSurfaceWithFormatFrom(
plat->sys->ppu.line_data, nes_ppu_render_w, 1,
plat->screen = SDL_CreateRGBSurfaceWithFormatFrom(
plat->sys->ppu.screen_data,
nes_ppu_render_w, nes_ppu_render_h,
8, nes_ppu_render_w,
SDL_PIXELFORMAT_INDEX8
);
if (NULL == plat->scanline) {
LOGE("SDL: Failed to create scanline");
if (NULL == plat->screen) {
LOGE("SDL: Failed to create screen");
status = -1;
} else {
SDL_SetPaletteColors(
plat->scanline->format->palette,
plat->screen->format->palette,
nes_palette, 0U, 64U
);
SDL_SetColorKey(plat->screen, SDL_TRUE, 0xFFU);
}
}



+ 18
- 5
src/nes.c 파일 보기

@@ -60,7 +60,11 @@ static int nes_hsync(nes* sys, void* plat) {
nes_ppu_render_line(&sys->ppu,
&sys->core.memory.ppu);
nese_line_ready(
plat, sys->ppu.line_data,
plat,
sys->ppu.screen_data +
( nes_ppu_render_w *
( sys->ppu.scanline -
nes_ppu_visible_line)),
sys->ppu.scanline - nes_ppu_visible_line
);
}
@@ -73,11 +77,20 @@ static int nes_hsync(nes* sys, void* plat) {

switch (sys->ppu.scanline) {
case nes_ppu_prerender_line:
{ nes_PPU_Memory* mem = &sys->core.memory.ppu;

f6502_set_NMI(&sys->core, 0);
sys->core.memory.ppu.status &= ~( ppu_Status_VBlank |
ppu_Status_Hit);
nes_ppu_find_hit_line(&sys->ppu, &sys->core.memory.ppu);
break;

mem->status &= ~(ppu_Status_VBlank | ppu_Status_Hit);
nes_ppu_find_hit_line(&sys->ppu, mem);

// Emulate the happy part of the backdrop override quirk
int pal_idx = ((mem->addr & 0x3F00U) == 0x3F00U) ?
(mem->addr & 0x1FU) : 0;
// Don't use the rendering palette (masked transparency)
status = nese_frame_start(plat, mem->pal_bank[0x300 + pal_idx]);

} break;

case nes_ppu_postrender_line:
status = nese_frame_ready(plat);


+ 1
- 0
src/port.h 파일 보기

@@ -9,6 +9,7 @@
void* nese_map_file(FILE* file, int size);
int nese_unmap_file(void* addr, int size);

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


+ 131
- 3
src/ppu.c 파일 보기

@@ -58,15 +58,21 @@ void nes_ppu_find_hit_line(nes_PPU* ppu, nes_PPU_Memory* mem) {


void nes_ppu_render_line(nes_PPU* ppu, nes_PPU_Memory* mem) {
uint8_t* ptr = ppu->line_data;
uint8_t* scanline_ptr = ppu->screen_data +
( nes_ppu_render_w *
(ppu->scanline - nes_ppu_visible_line));
uint8_t* ptr = scanline_ptr;

if (!(mem->mask & ppu_Mask_Back)) {
memset(ptr, 0xFFU, nes_ppu_render_w);
/*
// Emulate the happy part of the backdrop override quirk
int pal_idx = ((mem->addr & 0x3F00U) == 0x3F00U) ?
(mem->addr & 0x1FU) : 0;
// Don't use the rendering palette (masked transparency)
memset(ptr, mem->pal_bank[0x300 + pal_idx],
nes_ppu_render_w);
*/
} else {
int back_bank = nes_PPU_Nametable_Bank_Index +
((mem->addr >> 10) & 3);
@@ -187,11 +193,133 @@ void nes_ppu_render_line(nes_PPU* ppu, nes_PPU_Memory* mem) {
// TODO: Mapper ppu_bus

if (!(mem->mask & ppu_Mask_Left_Back)) {
memset(ppu->line_data, mem->palette[0], 8);
memset(scanline_ptr, 0xFFU, 8);
}

// TODO: Draw Sprites
// TODO: Mapper VROM switch
/*
if (mem->mask & ppu_Mask_Sprite) {
// This time, we do need overflow protection
// uint8_t sprite_data[nes_ppu_render_w + 8] = {0};

const int sprite_height = (mem->ctrl & ppu_Control_Sprite_Size) ?
16 : 8;
const int scanline = ppu->scanline - nes_ppu_visible_line;

int bank = !!(mem->ctrl & ppu_Control_Sprite_Bank) << 2;

int n_sprites = 0;
const oam_sprite* sprites = (oam_sprite*)mem->oam;
const oam_sprite* sprite = sprites +
NES_PPU_SPRITE_COUNT - 1;
for ( ; sprite >= sprites && n_sprites < 8; --sprite) {
int y = sprite->y + 1;
if (y > scanline || y + sprite_height <= scanline) {
continue;
}

int y_off = scanline - y;
if (sprite->attr & oam_Attr_Flip_Y) y_off = sprite_height - y_off - 1;

int ch = sprite->index;
if (mem->ctrl & ppu_Control_Sprite_Size) {
bank = (ch & 1) << 2;
ch &= 0xFEU;
}

bank += (ch >> 6);
const int addr_off = ((ch & 0x3fU) << 4) + ((y_off & 8) << 1) + (y_off & 7);
const uint8_t* data = mem->bank[bank] + addr_off;
const uint8_t pl0 = data[0];
const uint8_t pl1 = data[8];
const int pat0 = (pl0 & 0x55) | ((pl1 << 1) & 0xAA);
const int pat1 = ((pl0 >> 1) & 0x55) | (pl1 & 0xAA);

const uint8_t* pal = &mem->palette[0x10 + ((sprite->attr & oam_Attr_Pal_Mask) << 2)];
uint8_t* dst = scanline_ptr + sprite->x;

const int over_x = ((int)sprite->x + 8) - nes_ppu_render_w;

// TODO: X Flip
switch (over_x) {
default:
{ int pal_idx = (pat1 << 0) & 3;
if ( ( !(sprite->attr & oam_Attr_Background) ||
dst[7] == 0xFFU)
&& pal_idx) {
dst[7] = pal[pal_idx];
}
}
case 1:
{ int pal_idx = (pat0 << 0) & 3;
if ( ( !(sprite->attr & oam_Attr_Background) ||
dst[6] == 0xFFU)
&& pal_idx) {
dst[6] = pal[pal_idx];
}
}
case 2:
{ int pal_idx = (pat1 << 2) & 3;
if ( ( !(sprite->attr & oam_Attr_Background) ||
dst[5] == 0xFFU)
&& pal_idx) {
dst[5] = pal[pal_idx];
}
}
case 3:
{ int pal_idx = (pat0 << 2) & 3;
if ( ( !(sprite->attr & oam_Attr_Background) ||
dst[4] == 0xFFU)
&& pal_idx) {
dst[4] = pal[pal_idx];
}
}
case 4:
{ int pal_idx = (pat1 << 4) & 3;
if ( ( !(sprite->attr & oam_Attr_Background) ||
dst[3] == 0xFFU)
&& pal_idx) {
dst[3] = pal[pal_idx];
}
}
case 5:
{ int pal_idx = (pat0 << 4) & 3;
if ( ( !(sprite->attr & oam_Attr_Background) ||
dst[2] == 0xFFU)
&& pal_idx) {
dst[2] = pal[pal_idx];
}
}
case 6:
{ int pal_idx = (pat1 << 6) & 3;
if ( ( !(sprite->attr & oam_Attr_Background) ||
dst[1] == 0xFFU)
&& pal_idx) {
dst[1] = pal[pal_idx];
}
}
case 7:
{ int pal_idx = (pat0 << 6) & 3;
if ( ( !(sprite->attr & oam_Attr_Background) ||
dst[0] == 0xFFU)
&& pal_idx) {
dst[0] = pal[pal_idx];
}
}
}

++n_sprites;
}

if (n_sprites >= 8) {
mem->status |= ppu_Status_Overflow;
} else {
mem->status &= ~ppu_Status_Overflow;
}
}
*/

// Increment internal registers
if (mem->mask & (ppu_Mask_Sprite | ppu_Mask_Back)) {
uint16_t mask = 0b10000011111;
mem->addr = (mem->addr & ~mask) | (mem->t & mask);


+ 4
- 2
src/ppu.h 파일 보기

@@ -74,10 +74,12 @@ typedef enum {
#define NES_CHR_ROM_PAGE_SIZE (0x0400U)
#define NES_VRAM_PAGE_SIZE (0x0400U)

#define NES_PPU_SPRITE_COUNT (64U)

#define NES_PPU_CHR_SIZE (0x2000U)
#define NES_PPU_VRAM_SIZE (0x1000U)
#define NES_PPU_RAM_SIZE (0x4000U)
#define NES_PPU_OAM_SIZE (64U * 4U)
#define NES_PPU_OAM_SIZE (NES_PPU_SPRITE_COUNT * 4U)
#define NES_PPU_PAL_START (0x3F00U)

typedef struct {
@@ -131,7 +133,7 @@ void nes_ppu_set_mirroring(nes_PPU_Memory* mem,
typedef struct {
int scanline;
int hit_line;
uint8_t line_data[nes_ppu_render_w];
uint8_t screen_data[nes_ppu_render_w * nes_ppu_render_h];
} nes_PPU;

void nes_ppu_find_hit_line(nes_PPU*, nes_PPU_Memory*);


불러오는 중...
취소
저장