summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorHannes Schulze <haschu0103@outlook.de>2025-01-19 23:40:45 +0100
committerHannes Schulze <haschu0103@outlook.de>2025-01-19 23:40:45 +0100
commitbaa585fd21f631708de215929241d40f66b58498 (patch)
treec50643222b86829915a597c851b828faf07c4c24 /src
parent502dc171f45e5acf00c34521328b414d5285f598 (diff)
some ppu fixes
Diffstat (limited to 'src')
-rw-r--r--src/nes/cpu.cc47
-rw-r--r--src/nes/cpu.hh3
-rw-r--r--src/nes/mapper.cc3
-rw-r--r--src/nes/ppu.cc66
-rw-r--r--src/nes/ppu.hh14
-rw-r--r--src/scene.mm2
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<addressing_mode::immediate>(); break;
case 0x01: run_ora<addressing_mode::indexed_indirect>(); break;
case 0x02: run_stp(); break;
case 0x03: run_slo<addressing_mode::indexed_indirect>(); break;
@@ -102,7 +106,7 @@ namespace nes
case 0x23: run_rla<addressing_mode::indexed_indirect>(); break;
case 0x24: run_bit<addressing_mode::zero_page>(); break;
case 0x25: run_and<addressing_mode::zero_page>(); break;
- case 0x26: run_rol<addressing_mode::accumulator>(); break;
+ case 0x26: run_rol<addressing_mode::zero_page>(); break;
case 0x27: run_rla<addressing_mode::zero_page>(); break;
case 0x28: run_plp(); break;
case 0x29: run_and<addressing_mode::immediate>(); break;
@@ -328,10 +332,12 @@ namespace nes
// Instructions
// -----------------------------------------------------------------------------------------------------------------
+ template<cpu::addressing_mode Mode>
auto cpu::run_brk() -> void
{
- // TODO: Proper BRK handling
- std::abort();
+ // BRK: Break
+ fetch_operand<Mode>();
+ execute_interrupt(address{ 0xFFFE });
}
template<cpu::addressing_mode Mode>
@@ -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<Mode>();
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<cpu::addressing_mode Mode>
@@ -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<std::uint8_t>((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<addressing_mode Mode> 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<std::uint16_t>(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];
}