From baa585fd21f631708de215929241d40f66b58498 Mon Sep 17 00:00:00 2001 From: Hannes Schulze Date: Sun, 19 Jan 2025 23:40:45 +0100 Subject: some ppu fixes --- src/nes/cpu.cc | 47 +++++++++++++++++++++++++++++---------- src/nes/cpu.hh | 3 ++- src/nes/mapper.cc | 3 +-- src/nes/ppu.cc | 66 ++++++++++++++++++++++++++++++++++++------------------- src/nes/ppu.hh | 14 ++++++------ src/scene.mm | 2 +- 6 files changed, 91 insertions(+), 44 deletions(-) diff --git a/src/nes/cpu.cc b/src/nes/cpu.cc index bb4830f..b488a08 100644 --- a/src/nes/cpu.cc +++ b/src/nes/cpu.cc @@ -3,6 +3,8 @@ #include "nes/controller.hh" #include "nes/mapper.hh" +#define ENABLE_PPU + namespace nes { cpu::cpu(ppu& ppu, mapper& mapper, controller& controller_1, controller& controller_2) @@ -57,14 +59,16 @@ namespace nes if (nmi_pending_) { +#ifdef ENABLE_PPU execute_interrupt(address{ 0xFFFA }); +#endif nmi_pending_ = false; } auto const opcode = advance_pc8(); switch (opcode) { - case 0x00: run_brk(); break; + case 0x00: run_brk(); break; case 0x01: run_ora(); break; case 0x02: run_stp(); break; case 0x03: run_slo(); break; @@ -102,7 +106,7 @@ namespace nes case 0x23: run_rla(); break; case 0x24: run_bit(); break; case 0x25: run_and(); break; - case 0x26: run_rol(); break; + case 0x26: run_rol(); break; case 0x27: run_rla(); break; case 0x28: run_plp(); break; case 0x29: run_and(); break; @@ -328,10 +332,12 @@ namespace nes // Instructions // ----------------------------------------------------------------------------------------------------------------- + template auto cpu::run_brk() -> void { - // TODO: Proper BRK handling - std::abort(); + // BRK: Break + fetch_operand(); + execute_interrupt(address{ 0xFFFE }); } template @@ -377,8 +383,7 @@ namespace nes auto cpu::run_php() -> void { // PHP: Push Processor Status - // Always set bits 4 and 5. - push_stack8(registers_.p | 0b00110000); + eval_php(); current_cycles_ += cycle_count::from_cpu(3); } @@ -453,7 +458,7 @@ namespace nes auto operand = fetch_operand(); auto const arg = operand.read(); auto const res = (registers_.a & arg) != 0; - registers_.z = !res; + registers_.z = res == 0; registers_.v = (arg & 0x40) != 0; registers_.n = (arg & 0x80) != 0; current_cycles_ += operand.get_cycles(); @@ -701,7 +706,7 @@ namespace nes auto cpu::run_xaa() -> void { // XAA: Depends on analog behavior. - run_brk(); + run_stp(); } template @@ -1046,16 +1051,15 @@ namespace nes } } - auto cpu::execute_interrupt(address vector) -> void + auto cpu::execute_interrupt(address const vector) -> void { push_stack16(registers_.pc); - run_php(); + eval_php(); registers_.pc = read16(vector); registers_.i = true; current_cycles_ += cycle_count::from_cpu(7); } - auto cpu::eval_ror(std::uint8_t const arg) -> std::uint8_t { auto const res = static_cast((registers_.c << 7) | (arg >> 1)); @@ -1132,6 +1136,11 @@ namespace nes (registers_.p & 0b00110000); } + auto cpu::eval_php() -> void + { + // Always set bits 4 and 5. + push_stack8(registers_.p | 0b00110000); + } // ----------------------------------------------------------------------------------------------------------------- // Operands @@ -1272,6 +1281,7 @@ namespace nes if (addr <= address{ 0x1FFF }) { return ram_[addr.get_absolute() % ram_size]; } if (addr <= address{ 0x3FFF }) { +#ifdef ENABLE_PPU switch (addr.get_absolute() % 8) { case 0: return ppu_.read_latch(); @@ -1284,9 +1294,16 @@ namespace nes case 7: return ppu_.read_ppudata(); default: return 0x0; } +#else + return 0x0; +#endif } if (addr <= address{ 0x4013 }) { return 0x0; } // TODO: APU registers +#ifdef ENABLE_PPU if (addr == address{ 0x4014 }) { return ppu_.read_latch(); } +#else + if (addr == address{ 0x4014 }) { return 0x0; } +#endif if (addr == address{ 0x4015 }) { return 0x0; } // TODO: APU registers if (addr == address{ 0x4016 }) { return controller_1_.read(); } if (addr == address{ 0x4017 }) { return controller_2_.read(); } @@ -1306,6 +1323,7 @@ namespace nes if (addr <= address{ 0x1FFF }) { ram_[addr.get_absolute() % ram_size] = value; return; } if (addr <= address{ 0x3FFF }) { +#ifdef ENABLE_PPU switch (addr.get_absolute() % 8) { case 0: ppu_.write_ppuctrl(value); return; @@ -1318,9 +1336,16 @@ namespace nes case 7: ppu_.write_ppudata(value); return; default: return; } +#else + return; +#endif } if (addr <= address{ 0x4013 }) { return; } // TODO: APU registers +#ifdef ENABLE_PPU if (addr == address{ 0x4014 }) { ppu_.write_oamdma(value); return; } +#else + if (addr == address{ 0x4014 }) { return; } +#endif if (addr == address{ 0x4015 }) { return; } // TODO: APU registers if (addr == address{ 0x4016 }) { controller_1_.write(value); return; } if (addr == address{ 0x4017 }) { controller_2_.write(value); return; } diff --git a/src/nes/cpu.hh b/src/nes/cpu.hh index 070a72d..0614f76 100644 --- a/src/nes/cpu.hh +++ b/src/nes/cpu.hh @@ -155,7 +155,7 @@ namespace nes #define DEFINE_OPERAND_INSTRUCTION(name) \ template auto run_##name() -> void; - DEFINE_SIMPLE_INSTRUCTION(brk) + DEFINE_OPERAND_INSTRUCTION(brk) DEFINE_OPERAND_INSTRUCTION(ora) DEFINE_SIMPLE_INSTRUCTION(stp) DEFINE_OPERAND_INSTRUCTION(slo) @@ -258,6 +258,7 @@ namespace nes auto eval_eor(std::uint8_t arg) -> void; auto eval_cmp(std::uint8_t a, std::uint8_t b) -> void; auto eval_plp() -> void; + auto eval_php() -> void; // Operands diff --git a/src/nes/mapper.cc b/src/nes/mapper.cc index 237ab47..aa9618e 100644 --- a/src/nes/mapper.cc +++ b/src/nes/mapper.cc @@ -109,8 +109,7 @@ namespace nes auto mapper::nrom_mirrored_vram_address(address const addr) const -> unsigned { - auto rel = addr.get_absolute(); - if (rel >= 0x3000) { rel -= 0x1000; } + auto const rel = addr.get_absolute() % 0x1000; switch (cartridge_.get_name_table_arrangement()) { diff --git a/src/nes/ppu.cc b/src/nes/ppu.cc index 003c3d6..29a5643 100644 --- a/src/nes/ppu.cc +++ b/src/nes/ppu.cc @@ -50,12 +50,10 @@ namespace nes scanline_cycle_ = 0; scanline_ += 1; - if (scanline_ == 261) + if (scanline_ == 262) { scanline_ = 0; even_frame_ = !even_frame_; - - display_.switch_buffers(); } } @@ -76,6 +74,7 @@ namespace nes if (render_line && fetch_cycle) { + tile_data_ <<= 4; switch (scanline_cycle_ % 8) { case 1: @@ -139,6 +138,7 @@ namespace nes // Vblank Logic if (scanline_ == 241 && scanline_cycle_ == 1) { + display_.switch_buffers(); status_.vblank = true; nmi_change(); } @@ -146,7 +146,7 @@ namespace nes { status_.vblank = false; nmi_change(); - status_.sprite_0_hit = false; + status_.sprite_zero_hit = false; status_.sprite_overflow = false; } } @@ -180,11 +180,12 @@ namespace nes break; } - if (x < 8 && !mask_.show_background_start) { background_color = color_index{ 0 }; } - if (x < 8 && !mask_.show_sprites_start) { foreground_color = color_index{ 0 }; } + auto has_background = background_color.color != 0; + auto has_foreground = foreground_color.color != 0; + + if (x < 8 && !mask_.show_background_start) { has_background = false; } + if (x < 8 && !mask_.show_sprites_start) { has_foreground = false; } - auto const has_background = background_color.color != 0; - auto const has_foreground = foreground_color.color != 0; auto color = color_index{ 0 }; if (!has_background && has_foreground) { @@ -198,7 +199,7 @@ namespace nes { if (foreground.is_sprite_zero && x < 255) { - status_.sprite_0_hit = true; + status_.sprite_zero_hit = true; } color = foreground.is_in_front ? foreground_color : background_color; @@ -236,7 +237,7 @@ namespace nes auto const table = control_.background_pattern_table; auto const tile = name_table_byte_; auto const addr = static_cast(0x1000 * table + 16 * tile + fine_y); - low_tile_byte_ = read8(address{ addr } + 8); + high_tile_byte_ = read8(address{ addr } + 8); } auto ppu::store_tile_data() -> void @@ -251,7 +252,6 @@ namespace nes data <<= 4; data |= attribute_table_byte_ | p1 | p2; } - tile_data_ <<= 32; tile_data_ |= data; } @@ -261,7 +261,9 @@ namespace nes if ((internal_.v & 0x1F) == 0x1F) { // Coarse X = 0 - internal_.v &= ~0x1F; + // TODO: Check + // internal_.v &= ~0x1F; + internal_.v &= 0xFFE0; // Switch horizontal nametable internal_.v ^= 0x400; } @@ -299,7 +301,9 @@ namespace nes y += 1; } // Put coarse Y back into v - internal_.v &= ~0x03E0; + // TODO: Check + // internal_.v &= ~0x03E0; + internal_.v &= 0xFC1F; internal_.v |= y << 5; } else @@ -335,7 +339,9 @@ namespace nes { sprites_[sprite_count_].pattern = fetch_sprite_pattern(i, row); sprites_[sprite_count_].position = x; - sprites_[sprite_count_].is_in_front = a & 0b00100000; + // TODO: Check + // sprites_[sprite_count_].is_in_front = a & 0b00100000; + sprites_[sprite_count_].is_in_front = (a >> 5) & 1; sprites_[sprite_count_].is_sprite_zero = i == 0; sprite_count_ += 1; } @@ -383,10 +389,21 @@ namespace nes for (auto j = unsigned{ 0 }; j < 8; ++j) { auto const flip_horizontal = (attributes & 0x40) == 0x40; - auto const p1 = flip_horizontal ? (low_tile_byte & 1) << 0 : (low_tile_byte & 0x80) >> 7; - auto const p2 = flip_horizontal ? (high_tile_byte & 1) << 1 : (high_tile_byte & 0x80) >> 6; - low_tile_byte <<= 1; - high_tile_byte <<= 1; + std::uint8_t p1, p2; + if (flip_horizontal) + { + p1 = (low_tile_byte & 1) << 0; + p2 = (high_tile_byte & 1) << 1; + low_tile_byte >>= 1; + high_tile_byte >>= 1; + } + else + { + p1 = (low_tile_byte & 0x80) >> 7; + p2 = (high_tile_byte & 0x80) >> 6; + low_tile_byte <<= 1; + high_tile_byte <<= 1; + } data <<= 4; data |= a | p1 | p2; } @@ -454,13 +471,17 @@ namespace nes internal_.w = false; nmi_change(); - write_latch(res); + //write_latch(res); return res; } auto ppu::read_oamdata() -> std::uint8_t { - auto const res = oam_[oamaddr_]; + auto res = oam_[oamaddr_]; + if ((oamaddr_ & 0x3) == 0x2) + { + res &= 0xE3; + } write_latch(res); return res; } @@ -470,7 +491,7 @@ namespace nes auto const addr = address{ internal_.v }; auto res = read8(addr); - if (addr <= address{ 0x3F00 }) + if (addr % 0x4000 <= address{ 0x3F00 }) { std::swap(res, ppudata_read_buffer_); } @@ -496,6 +517,7 @@ namespace nes if (current_cycles_ > boot_up_cycles) { control_.value = value; + internal_.t = (internal_.t & 0xF3FF) | ((value & 0x03) << 10); nmi_change(); } } @@ -539,7 +561,7 @@ namespace nes if (!internal_.w) { // First write. - internal_.t = (internal_.t & 0b0000000011111111) | ((value & 0b00111111) << 8); + internal_.t = (internal_.t & 0b1000000011111111) | ((value & 0b00111111) << 8); } else { diff --git a/src/nes/ppu.hh b/src/nes/ppu.hh index 60784f7..9bd6139 100644 --- a/src/nes/ppu.hh +++ b/src/nes/ppu.hh @@ -43,7 +43,7 @@ namespace nes union color_index { - struct + struct __attribute__((packed)) { unsigned color : 2; unsigned palette : 2; @@ -85,7 +85,7 @@ namespace nes std::uint8_t latch_{}; // The last read/written IO register value, returned when reading write-only registers. union { - struct + struct __attribute__((packed)) { bool : 1; bool : 1; @@ -93,14 +93,14 @@ namespace nes bool : 1; bool : 1; bool sprite_overflow : 1; - bool sprite_0_hit : 1; + bool sprite_zero_hit : 1; bool vblank : 1; }; std::uint8_t value{ 0b00000000 }; } status_{}; union { - struct + struct __attribute__((packed)) { std::uint8_t base_name_table : 2; // Base nametable address. vram_increment vram_increment : 1; // VRAM increment per CPU read/write of PPUDATA. @@ -114,7 +114,7 @@ namespace nes } control_{}; union { - struct + struct __attribute__((packed)) { bool grayscale : 1; // Enable/disable grayscale. bool show_background_start : 1; // Show background in leftmost 8 pixels of screen. @@ -137,8 +137,8 @@ namespace nes } internal_{}; std::uint8_t oamaddr_{}; // Address accessed by IO registers std::uint8_t ppudata_read_buffer_{}; // Delays PPUDATA reads by one. - unsigned scanline_{ 0 }; - unsigned scanline_cycle_{ 0 }; + unsigned scanline_{ 240 }; + unsigned scanline_cycle_{ 340 }; bool even_frame_{ true }; std::uint64_t tile_data_{ 0 }; // Data for the current tile (higher 32 bits) and the next tile (lower 32 bits). std::uint8_t name_table_byte_{ 0 }; diff --git a/src/scene.mm b/src/scene.mm index e4cb6b6..f858185 100644 --- a/src/scene.mm +++ b/src/scene.mm @@ -35,7 +35,7 @@ namespace auto actually_switch_buffers() -> void { - auto texture = [SKTexture textureWithData:buffer_front_ size:CGSizeMake(width, height)]; + auto texture = [SKTexture textureWithData:buffer_front_ size:CGSizeMake(width, height) flipped:YES]; [texture setFilteringMode:SKTextureFilteringNearest]; [node_ setTexture:texture]; } -- cgit v1.3.1