- 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));}) | (type *)((char *)__mptr - offsetof(type,member));}) | ||||
| #endif | #endif | ||||
| #define DEBUG "Core" | |||||
| //#define DEBUG "Core" | |||||
| #include "log.h" | #include "log.h" | ||||
| @@ -158,10 +158,12 @@ static inline bool f6502_write(nes_Memory* mem, | |||||
| case ppu_reg_scroll: | case ppu_reg_scroll: | ||||
| if (mem->ppu.latch) { | if (mem->ppu.latch) { | ||||
| LOGI("PPU: SCROLL_Y %02X", val); | |||||
| mem->ppu.t &= 0b0000110000011111U; | mem->ppu.t &= 0b0000110000011111U; | ||||
| mem->ppu.t |= (uint16_t)(val & 0b00000111U) << 12; | mem->ppu.t |= (uint16_t)(val & 0b00000111U) << 12; | ||||
| mem->ppu.t |= (uint16_t)(val & 0b11111000U) << 2; | mem->ppu.t |= (uint16_t)(val & 0b11111000U) << 2; | ||||
| } else { | } else { | ||||
| LOGI("PPU: SCROLL_X %02X", val); | |||||
| mem->ppu.t &= ~(0b11111U); | mem->ppu.t &= ~(0b11111U); | ||||
| mem->ppu.t |= (val & 0b11111000U) >> 3; | mem->ppu.t |= (val & 0b11111000U) >> 3; | ||||
| mem->ppu.x = (val & 0b111U); | mem->ppu.x = (val & 0b111U); | ||||
| @@ -192,7 +194,18 @@ static inline bool f6502_write(nes_Memory* mem, | |||||
| LOGI("PPU W %04X < %02X", addr, val); | LOGI("PPU W %04X < %02X", addr, val); | ||||
| mem->ppu.bank[addr >> 10][addr & 0x3FFU] = val; | mem->ppu.bank[addr >> 10][addr & 0x3FFU] = val; | ||||
| if (addr >= NES_PPU_PAL_START) { | 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) { | for (int mirror = 0; mirror < 0x100; mirror += 0x20) { | ||||
| mem->ppu.pal_bank[addr + mirror] = val; | mem->ppu.pal_bank[addr + mirror] = val; | ||||
| } | } | ||||
| @@ -442,7 +455,7 @@ static inline int f6502_check_interrupts(f6502_Core* core) { | |||||
| #define ROL(a) ({ \ | #define ROL(a) ({ \ | ||||
| register uint16_t addr = a; \ | register uint16_t addr = a; \ | ||||
| register uint8_t val = f6502_read(&core->memory, addr); \ | 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); \ | val = (val << 1) | (P & f6502_Status_C); \ | ||||
| CLR(P, f6502_Status_C | f6502_Status_Z | f6502_Status_N); \ | CLR(P, f6502_Status_C | f6502_Status_Z | f6502_Status_N); \ | ||||
| P |= new_c; \ | 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)); | printf(" %02x", (int)f6502_read(&core->memory, i + f6502_Base_Stack)); | ||||
| putc('\n', stdout); | 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); | printf("$%04x: ", PC - 1); | ||||
| f6502_dump_instr(core, PC - 1); | |||||
| f6502_dump_instr(core, opcode, (uint8_t*)&operand); | |||||
| printf(" -> "); | printf(" -> "); | ||||
| f6502_fprintf_instr(stdout, opcode, core->memory.ram + PC); | |||||
| f6502_fprintf_instr(stdout, opcode, (uint8_t*)&operand); | |||||
| putc('\n', stdout); | putc('\n', stdout); | ||||
| #endif | #endif | ||||
| switch (opcode) { | 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, | static inline int adr_ind_reg(char reg, const uint8_t* op, | ||||
| char* str, int max) { | 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) { | 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); | int size = f6502_instr_size(opcode); | ||||
| printf("$%02x", opcode); | printf("$%02x", opcode); | ||||
| if (size == 2) { | if (size == 2) { | ||||
| printf(" $%02x", core->memory.ram[addr + 1]); | |||||
| printf(" $%02x", *(uint8_t*)operand); | |||||
| } else if (size == 3) { | } 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, | int f6502_fprintf_instr(FILE* file, | ||||
| uint8_t opcode, uint8_t* operand); | 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_ | #endif // F6502_OPCODES_H_ | ||||
| @@ -34,6 +34,7 @@ typedef struct { | |||||
| SDL_Renderer* renderer; | SDL_Renderer* renderer; | ||||
| SDL_Texture* texture; | SDL_Texture* texture; | ||||
| SDL_Surface* target; | SDL_Surface* target; | ||||
| SDL_Surface* scanline; | |||||
| } platform_data; | } platform_data; | ||||
| // TODO: Return an action enum? | // 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) { | 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; | return 0; | ||||
| } | } | ||||
| @@ -102,13 +112,22 @@ int nese_frame_ready(void* plat_data) { | |||||
| t_last = t_now; | 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); | status = process_events(plat->sys); | ||||
| // TODO: Perform menu actions | // TODO: Perform menu actions | ||||
| // TODO: Time sync | |||||
| return status; | 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; | return status; | ||||
| } | } | ||||
| @@ -3,7 +3,7 @@ | |||||
| #include "nes.h" | #include "nes.h" | ||||
| #include "port.h" | #include "port.h" | ||||
| #define DEBUG "NES" | |||||
| //#define DEBUG "NES" | |||||
| #include "log.h" | #include "log.h" | ||||
| @@ -28,7 +28,7 @@ static int nes_vsync(nes* sys, void* plat) { | |||||
| sys->core.memory.ppu.status |= ppu_Status_VBlank; | sys->core.memory.ppu.status |= ppu_Status_VBlank; | ||||
| if (sys->core.memory.ppu.ctrl & ppu_Control_VBlank) { | if (sys->core.memory.ppu.ctrl & ppu_Control_VBlank) { | ||||
| LOGI("VBlank NMI"); | |||||
| LOGD("VBlank NMI"); | |||||
| f6502_set_NMI(&sys->core, 1); | f6502_set_NMI(&sys->core, 1); | ||||
| } | } | ||||
| @@ -2,7 +2,7 @@ | |||||
| #include "ppu.h" | #include "ppu.h" | ||||
| #define DEBUG "PPU" | |||||
| //#define DEBUG "PPU" | |||||
| #include "log.h" | #include "log.h" | ||||
| @@ -19,7 +19,7 @@ void nes_ppu_set_mirroring(nes_PPU_Memory* mem, | |||||
| nes_Nametable_Mirroring mirror) { | nes_Nametable_Mirroring mirror) { | ||||
| for (int i = 0; i < 4; ++i) { | for (int i = 0; i < 4; ++i) { | ||||
| mem->bank[nes_PPU_Nametable_Bank_Index + 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)) { | if (!(mem->mask & ppu_Mask_Back)) { | ||||
| memset(ptr, 0, nes_ppu_render_w); | memset(ptr, 0, nes_ppu_render_w); | ||||
| } else { | } else { | ||||
| /* | |||||
| int bank = nes_PPU_Nametable_Bank_Index + | |||||
| int back_bank = nes_PPU_Nametable_Bank_Index + | |||||
| ((mem->addr >> 10) & 3); | ((mem->addr >> 10) & 3); | ||||
| int y_coarse = (mem->addr >> 5) & 0x1F; | 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 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; | 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 | // TODO: Draw Sprites | ||||
| @@ -98,8 +98,9 @@ typedef struct { | |||||
| int n_chr_banks; | int n_chr_banks; | ||||
| uint8_t* chr_ram; // TODO: Could this be a flag? | uint8_t* chr_ram; // TODO: Could this be a flag? | ||||
| uint8_t* bank[16]; | uint8_t* bank[16]; | ||||
| uint8_t palette[32]; // Rendering palette with transparency masked | |||||
| uint8_t vram[NES_PPU_VRAM_SIZE]; | 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]; | uint8_t oam[NES_PPU_OAM_SIZE]; | ||||
| } nes_PPU_Memory; | } nes_PPU_Memory; | ||||