diff --git a/src/f6502.c b/src/f6502.c index f504d0b..2fd489d 100644 --- a/src/f6502.c +++ b/src/f6502.c @@ -15,7 +15,7 @@ (type *)((char *)__mptr - offsetof(type,member));}) #endif -#define DEBUG "Core" +//#define DEBUG "Core" #include "log.h" @@ -158,10 +158,12 @@ static inline bool f6502_write(nes_Memory* mem, case ppu_reg_scroll: if (mem->ppu.latch) { + LOGI("PPU: SCROLL_Y %02X", val); mem->ppu.t &= 0b0000110000011111U; mem->ppu.t |= (uint16_t)(val & 0b00000111U) << 12; mem->ppu.t |= (uint16_t)(val & 0b11111000U) << 2; } else { + LOGI("PPU: SCROLL_X %02X", val); mem->ppu.t &= ~(0b11111U); mem->ppu.t |= (val & 0b11111000U) >> 3; mem->ppu.x = (val & 0b111U); @@ -192,7 +194,18 @@ static inline bool f6502_write(nes_Memory* mem, LOGI("PPU W %04X < %02X", addr, val); mem->ppu.bank[addr >> 10][addr & 0x3FFU] = val; if (addr >= NES_PPU_PAL_START) { - addr = 0x300U | (addr & 0x1FU); + // Copy to render reference + addr &= 0x1FU; + mem->ppu.palette[addr] = val; + if (0 == (addr & 0xF)) { + uint8_t* pal = mem->ppu.palette; + pal[0] = pal[4] = pal[8] = pal[12] = + pal[16] = pal[20] = pal[24] = + pal[28] = val; + } + + // Memory-mapped mirroring + addr |= 0x300U; for (int mirror = 0; mirror < 0x100; mirror += 0x20) { mem->ppu.pal_bank[addr + mirror] = val; } @@ -442,7 +455,7 @@ static inline int f6502_check_interrupts(f6502_Core* core) { #define ROL(a) ({ \ register uint16_t addr = a; \ register uint8_t val = f6502_read(&core->memory, addr); \ - register uint8_t new_c = (A & 0x80) ? f6502_Status_C : 0; \ + register uint8_t new_c = (val & 0x80) ? f6502_Status_C : 0; \ val = (val << 1) | (P & f6502_Status_C); \ CLR(P, f6502_Status_C | f6502_Status_Z | f6502_Status_N); \ P |= new_c; \ @@ -517,12 +530,14 @@ static int f6502_do_step(f6502_Core* core, int clocks) { printf(" %02x", (int)f6502_read(&core->memory, i + f6502_Base_Stack)); putc('\n', stdout); - printf("INT %02x\n", core->interrupts); +// printf("INT %02x\n", core->interrupts); + uint16_t operand = f6502_read16(&core->memory, PC); printf("$%04x: ", PC - 1); - f6502_dump_instr(core, PC - 1); + f6502_dump_instr(core, opcode, (uint8_t*)&operand); printf(" -> "); - f6502_fprintf_instr(stdout, opcode, core->memory.ram + PC); + + f6502_fprintf_instr(stdout, opcode, (uint8_t*)&operand); putc('\n', stdout); #endif switch (opcode) { diff --git a/src/f6502_opcodes.c b/src/f6502_opcodes.c index b360910..20375e6 100644 --- a/src/f6502_opcodes.c +++ b/src/f6502_opcodes.c @@ -56,7 +56,7 @@ static int adr_zp_y(const uint8_t* operand, char* str, int max) { static inline int adr_ind_reg(char reg, const uint8_t* op, char* str, int max) { - return snprintf(str, max, "($%04X,%c)", *(uint16_t*)op, reg); + return snprintf(str, max, "($%02X,%c)", *(uint8_t*)op, reg); } static int adr_ind_x(const uint8_t* operand, char* str, int max) { @@ -409,14 +409,12 @@ static int f6502_instr_size(uint8_t opcode) { } } -void f6502_dump_instr(f6502_Core* core, uint16_t addr) { - uint8_t opcode = core->memory.ram[addr]; +void f6502_dump_instr(f6502_Core* core, uint8_t opcode, uint8_t* operand) { int size = f6502_instr_size(opcode); printf("$%02x", opcode); if (size == 2) { - printf(" $%02x", core->memory.ram[addr + 1]); + printf(" $%02x", *(uint8_t*)operand); } else if (size == 3) { - printf(" $%04x", core->memory.ram[addr + 1] | - ((uint16_t)core->memory.ram[addr + 2] << 8)); + printf(" $%04x", *(uint16_t*)operand); } } diff --git a/src/f6502_opcodes.h b/src/f6502_opcodes.h index a11dec6..bc550f3 100644 --- a/src/f6502_opcodes.h +++ b/src/f6502_opcodes.h @@ -7,7 +7,7 @@ int f6502_fprintf_instr(FILE* file, uint8_t opcode, uint8_t* operand); -void f6502_dump_instr(f6502_Core* core, uint16_t addr); +void f6502_dump_instr(f6502_Core* core, uint8_t opcode, uint8_t* operand); #endif // F6502_OPCODES_H_ diff --git a/src/linux/port.c b/src/linux/port.c index 6485d6c..d6ff8ca 100644 --- a/src/linux/port.c +++ b/src/linux/port.c @@ -34,6 +34,7 @@ typedef struct { SDL_Renderer* renderer; SDL_Texture* texture; SDL_Surface* target; + SDL_Surface* scanline; } platform_data; // TODO: Return an action enum? @@ -76,8 +77,17 @@ static SDL_Color nes_palette[64] = { }; int nese_line_ready(void* plat_data, uint8_t* buffer, int line) { - // TODO: Render line to target? - (void)nes_palette; + platform_data* plat = (platform_data*)plat_data; + + SDL_Rect rect = { + .x = 0, + .y = line, + .w = nes_ppu_render_w, + .h = 1, + }; + + SDL_BlitSurface(plat->scanline, NULL, plat->target, &rect); + return 0; } @@ -102,13 +112,22 @@ int nese_frame_ready(void* plat_data) { t_last = t_now; */ - // TODO: Render frame - // TODO: Time sync + SDL_UnlockTexture(plat->texture); + SDL_RenderCopy(plat->renderer, plat->texture, + NULL, NULL); + SDL_LockTextureToSurface(plat->texture, NULL, + &plat->target); + + // TODO: Overlay + + SDL_RenderPresent(plat->renderer); status = process_events(plat->sys); // TODO: Perform menu actions + // TODO: Time sync + return status; } @@ -166,6 +185,23 @@ static int plat_init(platform_data* plat) { } } + if (0 == status) { + plat->scanline = SDL_CreateRGBSurfaceWithFormatFrom( + plat->sys->ppu.line_data, nes_ppu_render_w, 1, + 8, nes_ppu_render_w, + SDL_PIXELFORMAT_INDEX8 + ); + if (NULL == plat->scanline) { + LOGE("SDL: Failed to create scanline"); + status = -1; + } else { + SDL_SetPaletteColors( + plat->scanline->format->palette, + nes_palette, 0U, 64U + ); + } + } + return status; } diff --git a/src/nes.c b/src/nes.c index 70114b6..048116d 100644 --- a/src/nes.c +++ b/src/nes.c @@ -3,7 +3,7 @@ #include "nes.h" #include "port.h" -#define DEBUG "NES" +//#define DEBUG "NES" #include "log.h" @@ -28,7 +28,7 @@ static int nes_vsync(nes* sys, void* plat) { sys->core.memory.ppu.status |= ppu_Status_VBlank; if (sys->core.memory.ppu.ctrl & ppu_Control_VBlank) { - LOGI("VBlank NMI"); + LOGD("VBlank NMI"); f6502_set_NMI(&sys->core, 1); } diff --git a/src/ppu.c b/src/ppu.c index fec41ff..97c9a9c 100644 --- a/src/ppu.c +++ b/src/ppu.c @@ -2,7 +2,7 @@ #include "ppu.h" -#define DEBUG "PPU" +//#define DEBUG "PPU" #include "log.h" @@ -19,7 +19,7 @@ void nes_ppu_set_mirroring(nes_PPU_Memory* mem, nes_Nametable_Mirroring mirror) { for (int i = 0; i < 4; ++i) { mem->bank[nes_PPU_Nametable_Bank_Index + i] = - vram_page(mem, (int)mirror_schemes[mirror][0]); + vram_page(mem, (int)mirror_schemes[mirror][i]); } } @@ -63,8 +63,7 @@ void nes_ppu_render_line(nes_PPU* ppu, nes_PPU_Memory* mem) { if (!(mem->mask & ppu_Mask_Back)) { memset(ptr, 0, nes_ppu_render_w); } else { -/* - int bank = nes_PPU_Nametable_Bank_Index + + int back_bank = nes_PPU_Nametable_Bank_Index + ((mem->addr >> 10) & 3); int y_coarse = (mem->addr >> 5) & 0x1F; @@ -73,14 +72,84 @@ void nes_ppu_render_line(nes_PPU* ppu, nes_PPU_Memory* mem) { const int bank_off = !!(mem->ctrl & ppu_Control_Back_Bank) << 2; - const uint8_t* nametable = mem->bank[bank] + (y_coarse * 32) + x_coarse; - const uint8_t* attrs = mem->bank[bank] + 0x3C0U + ((y_coarse >> 2) << 3); + const uint8_t* nametable = mem->bank[back_bank] + (y_coarse * 32) + x_coarse; + const uint8_t* attrs = mem->bank[back_bank] + 0x3C0U + ((y_coarse >> 2) << 3); ptr += 8 - mem->x; -*/ + // TODO: Omit if left column is masked? + { + const uint8_t* pal = mem->palette + + (((attrs[x_coarse >> 2] >> + ( (x_coarse & 2) + + ((y_coarse & 2) << 1))) & 3) << 2); + const int ch = *nametable; + const int bank = (ch >> 6) + bank_off; + const int addr_off = ((ch & 0x3F) << 4) + y_fine; + 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); + switch (mem->x) { + case 0: ptr[-8] = pal[(pat1 >> 6) & 3]; + case 1: ptr[-7] = pal[(pat0 >> 6) & 3]; + case 2: ptr[-6] = pal[(pat1 >> 4) & 3]; + case 3: ptr[-5] = pal[(pat0 >> 4) & 3]; + case 4: ptr[-4] = pal[(pat1 >> 2) & 3]; + case 5: ptr[-3] = pal[(pat0 >> 2) & 3]; + case 6: ptr[-2] = pal[(pat1 >> 0) & 3]; + case 7: ptr[-1] = pal[(pat0 >> 0) & 3]; + } + } + + // TODO: Mapper ppu_bus + + ++x_coarse; + ++nametable; + + void __attribute__((always_inline)) inline render_bg(void) { + const uint8_t* pal = mem->palette + + (((attrs[x_coarse >> 2] >> + ( (x_coarse & 2) + + ((y_coarse & 2) << 1))) & 3) << 2); + const int ch = *nametable; + const int bank = (ch >> 6) + bank_off; + const int addr_off = ((ch & 0x3F) << 4) + y_fine; + 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) << 1) | ((pl1 & 0x55) << 2); + //const int pat1 = ((pl0 & 0xAA) << 0) | ((pl1 & 0xAA) << 1); + const int pat0 = (pl0 & 0x55) | ((pl1 << 1) & 0xAA); + const int pat1 = ((pl0 >> 1) & 0x55) | (pl1 & 0xAA); + + ptr[0] = pal[(pat1 >> 6) & 3]; + ptr[1] = pal[(pat0 >> 6) & 3]; + ptr[2] = pal[(pat1 >> 4) & 3]; + ptr[3] = pal[(pat0 >> 4) & 3]; + ptr[4] = pal[(pat1 >> 2) & 3]; + ptr[5] = pal[(pat0 >> 2) & 3]; + ptr[6] = pal[(pat1 >> 0) & 3]; + ptr[7] = pal[(pat0 >> 0) & 3]; + ptr += 8; + } - // TODO: Draw Background + for (; x_coarse < 32; ++x_coarse) { + render_bg(); + // TODO: Mapper ppu_bus + ++nametable; + } + + back_bank ^= 0b01; + nametable = mem->bank[back_bank] + (y_coarse * 32); + attrs = mem->bank[back_bank] + 0x3C0U + ((y_coarse >> 2) << 3); + + for (x_coarse = 0; x_coarse < (mem->addr & 0x1FU); ++x_coarse) { + render_bg(); + // TODO: Mapper ppu_bus + ++nametable; + } } // TODO: Draw Sprites diff --git a/src/ppu.h b/src/ppu.h index 9bcd554..ab03d54 100644 --- a/src/ppu.h +++ b/src/ppu.h @@ -98,8 +98,9 @@ typedef struct { int n_chr_banks; uint8_t* chr_ram; // TODO: Could this be a flag? uint8_t* bank[16]; + uint8_t palette[32]; // Rendering palette with transparency masked uint8_t vram[NES_PPU_VRAM_SIZE]; - uint8_t pal_bank[NES_CHR_ROM_PAGE_SIZE]; // Mirrored in banks 12-15, also pal + uint8_t pal_bank[NES_CHR_ROM_PAGE_SIZE]; // Raw palette data mirrored in banks 12-15, also pal uint8_t oam[NES_PPU_OAM_SIZE]; } nes_PPU_Memory;