- Fix palette reference - Fix live tracing memory addressing - Fix ROL bug - Fix indirect X/Y addressing decode - Fix nametable mirroringv2
| @@ -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) { | |||
| @@ -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); | |||
| } | |||
| } | |||
| @@ -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_ | |||
| @@ -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; | |||
| } | |||
| @@ -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); | |||
| } | |||
| @@ -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 | |||
| @@ -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; | |||