From ca3eac8393239f62e6774b89aae516a612702523 Mon Sep 17 00:00:00 2001 From: Lauchmelder Date: Sat, 5 Mar 2022 17:14:07 +0100 Subject: [PATCH] fixed ppu postfetching --- .gitignore | 4 +- roms/all_instrs.nes | Bin 0 -> 262160 bytes roms/cpu_dummy_reads.nes | Bin 0 -> 40976 bytes src/Bus.cpp | 3 + src/CMakeLists.txt | 2 +- src/CPU.cpp | 80 +++++++++++++++--- src/CPU.hpp | 19 ++++- src/Cartridge.cpp | 4 + src/Mapper.hpp | 9 +- src/PPU.cpp | 24 ++++-- src/debugger/PatternTableViewer.cpp | 2 + src/mappers/Mapper000.cpp | 1 - src/mappers/Mapper000.hpp | 4 - src/mappers/Mapper001.cpp | 126 ++++++++++++++++++++++++++++ src/mappers/Mapper001.hpp | 27 ++++++ src/mappers/Mapper003.cpp | 48 +++++++++++ src/mappers/Mapper003.hpp | 21 +++++ 17 files changed, 343 insertions(+), 31 deletions(-) create mode 100644 roms/all_instrs.nes create mode 100644 roms/cpu_dummy_reads.nes create mode 100644 src/mappers/Mapper001.cpp create mode 100644 src/mappers/Mapper001.hpp create mode 100644 src/mappers/Mapper003.cpp create mode 100644 src/mappers/Mapper003.hpp diff --git a/.gitignore b/.gitignore index db5d2a1..6824e28 100644 --- a/.gitignore +++ b/.gitignore @@ -5,4 +5,6 @@ out/ *.json *.nes -!roms/nestest.nes \ No newline at end of file +!roms/nestest.nes +!roms/cpu_dummy_reads.nes +!roms/all_instrs.nes \ No newline at end of file diff --git a/roms/all_instrs.nes b/roms/all_instrs.nes new file mode 100644 index 0000000000000000000000000000000000000000..d5a1b83c9fdd4bf34418ce20aee5bc36f8767fc0 GIT binary patch literal 262160 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ଽH6d3h@xDTzHd(FeQ5OI{mRGzYg8OE* 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 Pz`?1nlkxc8B=_fkZn(uE literal 0 HcmV?d00001 diff --git a/roms/cpu_dummy_reads.nes b/roms/cpu_dummy_reads.nes new file mode 100644 index 0000000000000000000000000000000000000000..2682568dbdb66f1a1240b634dd6b91e601178377 GIT binary patch literal 40976 zcmeI1Uuav`8Nffuu`CNADR%AL*jjqX!bpPI?z%q}OvGwg%yp)fLbtt)t=FC{fDDiUGC&5%02v?yWPl8i0Wv@a$N(8217v^< zkO4A42FL&zAOmE843GgbKnBPF86X2>fDDiUGC&5%02v?yWPl8i0Wv@a$N(8217v^< zkO4A42FL&zAOmE843GgbKnBPF86X2>fDHWS3~V!~o8M&KsyWA$s;-${_KOX3#yDZt z4JFMy>u>B1s~yRi4P=B%Q^xMFk7%vksI1M}3oyHr7I;#xZBG?TYmgAav zj3>UepE&P59K8H#8JCY_TwXtrv6E+fb3fzCX%mc)$&B!w(9sp+*t#4OyO0aRh2mIM zjvZLPJ7t)slhK;#<2QY0|E4nS%IViaM-$7meC$ACF$L4Q-C~wzcJajT?k6sQ>2+51 zYiBo_>E|JT`m~Ur?&VK@Z)5-beArd$y1jepHZ(dfHI6NLE1;L4;=_aR$4ZB0{uxh3 zTA6BB{_%-!vyBHvjQv&Lc8c!T*pI6wWAmHov)H(ve7@?}ZiuTtEpYvJTUgk4tG6&h zW6DNayM%@1tLby$QeeqD<(pEI^P8nWqf-oHU2`(xngTqtuHDd9py6XTs_fnBolxab z`t@`@)bQx_uwI&23KrB4C$>rxxBtaHu3}2w4s4YIqTQ_~Z&bDGI5(}^eR{pG(TNA) zT204;(Py@eh;iKLF?#hged^~IpSTz)_(ytEJ*nfDmlPvX>>d5c=q>h)zGd_jkB|P$ zI9`m5vb&Z0M I%`%79lwo?w$Z`e$fIPt%05@u%oskRx)DyZH>)Njr)tIi$}!Vp z^q0I#y+x%FX1}Q#;o=D+gaKH`ImLbqGy0R=8+@l7cS5oG2voXWjeLw7)p^V}Ht@<{ zMuok%5j)=qEIsyP?QEd3gL+JuYUjNP)06K@*$IEWB<)q5aFO#=7h2qY=E-G`_vWB|QuW|be5J;|y@_-1ilN1U(iR@Z@RVO?%BC@}IB2rMlgr?}8`&*< zX7S|BK{-V%5|@ z^ddIQ^+BO)rD4FL=hfGs@)*yMa3U^jsm6M0$!BXN&+U%n{mn{5GoRa+h&`HSerLaf zRjO(1`Tl{P4{B-l&IaD3u?$^XmEB~x4zMR)EJDu;;7C21C`SJfZ8Y_vzC;2~n>p^J za!#rK{MhX5&Ar6Vd}(Cl8AyKp88$XP zJCubNz9nnC@9dAx4b6)_r{?qxWzV^Nyheo#kO4A42FL&zAOmE843GgbKnBPF86X2> zfDDiUGC&5%02v?y|6c}_`cF1buN{nxO0VH#FD4tz! zf#gC^iTmsvNJitCatrq)*|eHIJ}w#8t)`7_$(D`xS5=VA{=Sx7T+D%F&bAkP!7Sd- zA?}m!_2{4O_90bNg*%qy+ZH=_iE@Kt++oi4MO9ESzr%Ry>gB8Y1(1woY`bOKAc>3T zKah;4EXzv8G2Y=$*|zQS4oJ2vt7%y+I84iJTW~?Q?%Amed&JpzI~ye9T)%?(U7qrx zPV^^fm)9C=WsG;Ji?V=^mX`&vAGTVnJfCF!QR^@Fuw5*+S z8K18t_Lr@4mX8YtF}hsNfh6y5TFy9UR8_Owk0b!b=kS2Lbn=$n{AGNX@omQVJvyJF zY4?*fZCT>MW4uEij73c11bEO|U0prEwFkIBY)i(uT5L<={&w)e;|X>s)`uS{$+ZXk zT=4l0vsN~^gKd{;4sRR9^|}PS+x-lVA7GZR3)_OYI{y6xN$kz?_3hF1gYm*s6W{D6 zjynwD*HKYIASsRmo)^h!{F*AtG)U@y{}t{9KY=cJoeCKs17v^fDDiUGVs4`;NP0O_BsFn literal 0 HcmV?d00001 diff --git a/src/Bus.cpp b/src/Bus.cpp index 9bfcb86..e10ef0b 100644 --- a/src/Bus.cpp +++ b/src/Bus.cpp @@ -204,6 +204,9 @@ void Bus::WritePPU(Word addr, Byte val) if(cartridge.MapCIRAM(addr)) cartridge.WriteVRAM(addr, val); + if (val != 0x00) + volatile int jfkd = 3; + VRAM[addr & 0xFFF] = val; } else if (0x3F00 <= addr && addr < 0x4000) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 2d14af9..fe34aa3 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -13,7 +13,7 @@ add_executable(nesemu "debugger/PPUWatcher.cpp" "debugger/Disassembler.cpp" "debugger/MemoryViewer.cpp" - "debugger/NametableViewer.cpp" "ControllerPort.cpp" "controllers/StandardController.cpp" "gfx/Input.cpp" "debugger/ControllerPortViewer.cpp" "gfx/Screen.cpp" "debugger/Palettes.cpp" "APU.cpp" "debugger/PatternTableViewer.cpp") + "debugger/NametableViewer.cpp" "ControllerPort.cpp" "controllers/StandardController.cpp" "gfx/Input.cpp" "debugger/ControllerPortViewer.cpp" "gfx/Screen.cpp" "debugger/Palettes.cpp" "APU.cpp" "debugger/PatternTableViewer.cpp" "mappers/Mapper003.cpp" "mappers/Mapper001.cpp") target_include_directories(nesemu PRIVATE mappers diff --git a/src/CPU.cpp b/src/CPU.cpp index f4cfc1e..4bd551d 100644 --- a/src/CPU.cpp +++ b/src/CPU.cpp @@ -87,6 +87,7 @@ void CPU::CreateInstructionTable() InstructionTable[0x08] = NEW_INSTRUCTION(PHP, IMP, 1, 3); InstructionTable[0x09] = NEW_INSTRUCTION(ORA, IMM, 2, 2); InstructionTable[0x0A] = NEW_INSTRUCTION(ASL, ACC, 1, 2); + InstructionTable[0x0B] = NEW_ILLGL_INSTR(ANC, IMM, 2, 2); InstructionTable[0x0D] = NEW_INSTRUCTION(ORA, ABS, 3, 4); InstructionTable[0x0C] = NEW_ILLGL_INSTR(NOP, ABS, 3, 4); InstructionTable[0x0E] = NEW_INSTRUCTION(ASL, ABS, 3, 6); @@ -119,6 +120,7 @@ void CPU::CreateInstructionTable() InstructionTable[0x28] = NEW_INSTRUCTION(PLP, IMP, 1, 4); InstructionTable[0x29] = NEW_INSTRUCTION(AND, IMM, 2, 2); InstructionTable[0x2A] = NEW_INSTRUCTION(ROL, ACC, 1, 2); + InstructionTable[0x2B] = NEW_ILLGL_INSTR(ANC, IMM, 2, 2); InstructionTable[0x2C] = NEW_INSTRUCTION(BIT, ABS, 3, 4); InstructionTable[0x2D] = NEW_INSTRUCTION(AND, ABS, 3, 4); InstructionTable[0x2E] = NEW_INSTRUCTION(ROL, ABS, 3, 6); @@ -163,6 +165,7 @@ void CPU::CreateInstructionTable() InstructionTable[0x55] = NEW_INSTRUCTION(EOR, ZPX, 2, 4); InstructionTable[0x56] = NEW_INSTRUCTION(LSR, ZPX, 2, 6); InstructionTable[0x57] = NEW_ILLGL_INSTR(SRE, ZPX, 2, 6); + InstructionTable[0x58] = NEW_INSTRUCTION(CLI, IMP, 1, 2); InstructionTable[0x59] = NEW_INSTRUCTION(EOR, ABY, 3, 4); InstructionTable[0x5A] = NEW_ILLGL_INSTR(NOP, IMP, 1, 2); InstructionTable[0x5B] = NEW_ILLGL_INSTR(SRE, ABY, 3, 7); @@ -181,6 +184,7 @@ void CPU::CreateInstructionTable() InstructionTable[0x68] = NEW_INSTRUCTION(PLA, IMP, 1, 4); InstructionTable[0x69] = NEW_INSTRUCTION(ADC, IMM, 2, 2); InstructionTable[0x6A] = NEW_INSTRUCTION(ROR, ACC, 1, 2); + InstructionTable[0x6B] = NEW_ILLGL_INSTR(ARR, IMM, 2, 2); InstructionTable[0x6C] = NEW_INSTRUCTION(JMP, IND, 3, 5); InstructionTable[0x6D] = NEW_INSTRUCTION(ADC, ABS, 3, 4); InstructionTable[0x6E] = NEW_INSTRUCTION(ROR, ABS, 3, 6); @@ -228,7 +232,9 @@ void CPU::CreateInstructionTable() InstructionTable[0x98] = NEW_INSTRUCTION(TYA, IMP, 1, 2); InstructionTable[0x99] = NEW_INSTRUCTION(STA, ABY, 3, 5); InstructionTable[0x9A] = NEW_INSTRUCTION(TXS, IMP, 1, 2); + InstructionTable[0x9C] = NEW_ILLGL_INSTR(SHY, ABX, 3, 5); InstructionTable[0x9D] = NEW_INSTRUCTION(STA, ABX, 3, 5); + InstructionTable[0x9E] = NEW_ILLGL_INSTR(SHX, ABY, 3, 5); InstructionTable[0xA0] = NEW_INSTRUCTION(LDY, IMM, 2, 2); InstructionTable[0xA1] = NEW_INSTRUCTION(LDA, IDX, 2, 6); @@ -273,6 +279,7 @@ void CPU::CreateInstructionTable() InstructionTable[0xC8] = NEW_INSTRUCTION(INY, IMP, 1, 2); InstructionTable[0xC9] = NEW_INSTRUCTION(CMP, IMM, 2, 2); InstructionTable[0xCA] = NEW_INSTRUCTION(DEX, IMP, 1, 2); + InstructionTable[0xCB] = NEW_ILLGL_INSTR(SBX, IMM, 2, 2); InstructionTable[0xCC] = NEW_INSTRUCTION(CPY, ABS, 3, 4); InstructionTable[0xCD] = NEW_INSTRUCTION(CMP, ABS, 3, 4); InstructionTable[0xCE] = NEW_INSTRUCTION(DEC, ABS, 3, 6); @@ -330,7 +337,7 @@ void CPU::CreateInstructionTable() void CPU::Powerup() { - status.Raw = 0x34; + status.Raw = 0x24; acc = 0; idx = 0; idy = 0; @@ -362,7 +369,7 @@ void CPU::IRQ() Push(pc.Bytes.hi); Push(pc.Bytes.lo); - Push(status.Raw | (0x20 << 4)); + Push(status.Raw | (0x1 << 5)); status.Flag.InterruptDisable = 1; pc.Bytes.lo = Read(0xFFFE); @@ -373,7 +380,7 @@ void CPU::NMI() { Push(pc.Bytes.hi); Push(pc.Bytes.lo); - Push(status.Raw | (0x20 << 4)); + Push(status.Raw | (0x1 << 5)); status.Flag.InterruptDisable = 1; pc.Bytes.lo = Read(0xFFFA); @@ -515,6 +522,16 @@ void CPU::ALR() additionalCycles = 0; } +void CPU::ANC() +{ + FetchValue(); + acc &= fetchedVal; + + CHECK_NEGATIVE(acc); + CHECK_ZERO(acc); + status.Flag.Carry = ((acc & 0x80) == 0x80); +} + void CPU::AND() { FetchValue(); @@ -537,6 +554,20 @@ void CPU::ANE() additionalCycles = 0; } +void CPU::ARR() +{ + FetchValue(); + acc &= fetchedVal; + + acc >>= 1; + acc |= (status.Flag.Carry << 7); + + CHECK_NEGATIVE(acc); + CHECK_ZERO(acc); + status.Flag.Overflow = ((acc >> 6) & 0x1) ^ ((acc >> 5) & 0x1); + status.Flag.Carry = ((acc >> 6) & 0x1); +} + void CPU::ASL() { FetchValue(); @@ -650,7 +681,7 @@ void CPU::BRK() pc.Raw++; Push(pc.Bytes.hi); Push(pc.Bytes.lo); - Push(status.Raw | (0x30 << 4)); + Push(status.Raw | (0x3 << 4)); status.Flag.InterruptDisable = 1; pc.Bytes.lo = Read(0xFFFE); @@ -894,12 +925,11 @@ void CPU::LSR() void CPU::LXA() { FetchValue(); - Byte result = (acc & 0xFF) & fetchedVal; - acc = result; - idx = result; + acc = fetchedVal; + idx = acc; - CHECK_NEGATIVE(result); - CHECK_ZERO(result); + CHECK_NEGATIVE(acc); + CHECK_ZERO(acc); additionalCycles = 0; } @@ -925,7 +955,7 @@ void CPU::PHA() void CPU::PHP() { - Push(status.Raw | (0x30 << 4)); + Push(status.Raw | (0x3 << 4)); } void CPU::PLA() @@ -1042,6 +1072,18 @@ void CPU::SAX() Write(absoluteAddress.Raw, acc & idx); } +void CPU::SBX() +{ + FetchValue(); + Word result = (acc & idx) - fetchedVal; + + CHECK_NEGATIVE(result); + CHECK_ZERO(result); + status.Flag.Carry = ((acc & idx) >= fetchedVal); + + idx = result; +} + void CPU::SBC() { FetchValue(); @@ -1070,6 +1112,24 @@ void CPU::SEI() status.Flag.InterruptDisable = 1; } +void CPU::SHX() +{ + Byte result = idx & (rawAddress.Bytes.hi + 1); + absoluteAddress.Bytes.hi = result; + Write(absoluteAddress.Raw, result); + + additionalCycles = 0; +} + +void CPU::SHY() +{ + Byte result = idy & (rawAddress.Bytes.hi + 1); + absoluteAddress.Bytes.hi = result; + Write(absoluteAddress.Raw, result); + + additionalCycles = 0; +} + void CPU::SLO() { FetchValue(); diff --git a/src/CPU.hpp b/src/CPU.hpp index e7b71f9..a4128ef 100644 --- a/src/CPU.hpp +++ b/src/CPU.hpp @@ -116,12 +116,20 @@ private: /** * @brief Push a byte to the stack */ - inline void Push(Byte val) { Write(0x0100 | (sp--), val); } + inline void Push(Byte val) + { + Write(0x0100 | sp, val); + sp--; + } /** * @brief Pop a byte from the stack. */ - inline Byte Pop() { return Read(0x0100 | (++sp)); } + inline Byte Pop() + { + sp++; + return Read(0x0100 | sp); + } /** @@ -166,8 +174,10 @@ private: // Stuff regarding instructions // They simply perform the operations needed void ADC(); void ALR(); + void ANC(); void AND(); void ANE(); + void ARR(); void ASL(); void BCC(); void BCS(); @@ -216,10 +226,13 @@ private: // Stuff regarding instructions void RTI(); void RTS(); void SAX(); + void SBX(); void SBC(); void SEC(); void SED(); void SEI(); + void SHX(); + void SHY(); void SLO(); void SRE(); void STA(); @@ -237,7 +250,7 @@ private: // CPU internals Byte acc; Byte idx, idy; Address pc; - Word sp; + Byte sp; StatusFlag status; Instruction* currentInstruction = nullptr; diff --git a/src/Cartridge.cpp b/src/Cartridge.cpp index d52883c..a4e39c7 100644 --- a/src/Cartridge.cpp +++ b/src/Cartridge.cpp @@ -6,6 +6,8 @@ #include "Mapper.hpp" #include "mappers/Mapper000.hpp" +#include "mappers/Mapper001.hpp" +#include "mappers/Mapper003.hpp" Cartridge::Cartridge(Bus* bus) : bus(bus), mapper(nullptr) @@ -35,6 +37,8 @@ void Cartridge::Load(std::string path) switch (mapperNumber) { case 0: mapper = new Mapper000(header, file); break; + case 1: mapper = new Mapper001(header, file); break; + case 3: mapper = new Mapper003(header, file); break; default: throw std::runtime_error("Unsupported mapper ID " + std::to_string(mapperNumber)); diff --git a/src/Mapper.hpp b/src/Mapper.hpp index 28e3418..5c95552 100644 --- a/src/Mapper.hpp +++ b/src/Mapper.hpp @@ -1,6 +1,7 @@ #pragma once #include +#include "../Log.hpp" #include "Types.hpp" class Mapper @@ -20,7 +21,7 @@ public: * enforce nametable mirroring, or even completely remap the address * to internal VRAM */ - bool MapCIRAM(Word& addr) + virtual bool MapCIRAM(Word& addr) { if (header.Flag6.IgnoreMirroringBit) return true; @@ -42,10 +43,14 @@ public: virtual void WriteVRAM(Word addr, Byte val) {} protected: - Mapper(const Header& header) : header(header) {} + Mapper(const Header& header) : header(header), prgBanks(header.PrgROM), chrBanks(header.ChrROM) + { + } protected: std::vector PRG_ROM; std::vector CHR_ROM; + Byte prgBanks = 0; + Byte chrBanks = 0; Header header; }; diff --git a/src/PPU.cpp b/src/PPU.cpp index 0569256..629a417 100644 --- a/src/PPU.cpp +++ b/src/PPU.cpp @@ -157,7 +157,7 @@ void PPU::Tick() current.NametableSel |= temporary.NametableSel & 0x1; } - if (scanlineType == ScanlineType::PreRender && x >= 280 && x <= 304) + if (scanlineType == ScanlineType::PreRender && ppumask.Flag.ShowBackground && x >= 280 && x <= 304) { current.FineY = temporary.FineY; current.CoarseY = temporary.CoarseY; @@ -180,10 +180,9 @@ void PPU::Tick() palette = 0x00; uint8_t colorVal = Read(0x3F00 | (palette << 2) | color); - if (colorVal != 0x0f) - volatile int dfjk = 3; - - screen->SetPixel(x, y, colorTable[colorVal]); + + if(ppumask.Flag.ShowBackground) + screen->SetPixel(x, y, colorTable[colorVal]); } if (cycleType == CycleType::Fetching || cycleType == CycleType::PreFetching) @@ -197,21 +196,23 @@ void PPU::Tick() Byte PPU::ReadRegister(Byte id) { + Byte data = 0; + // Reading from a register fills the latch with the contents of the register // Write-only regs don't fill the latch // But in any case, the latch contents are returned switch (id) { case 0: - latch = ppuctrl.Raw; + data = ppuctrl.Raw; break; case 1: - latch = ppumask.Raw; + data = ppumask.Raw; break; case 2: - latch = ppustatus.Raw; + data = ppustatus.Raw; ppustatus.Flag.VBlankStarted = 0; addressLatch = 0; break; @@ -223,7 +224,12 @@ Byte PPU::ReadRegister(Byte id) break; case 7: + data = latch; latch = bus->ReadPPU(ppuaddr.Raw); + + if (ppuaddr.Raw >= 0x3F00) + data = latch; + ppuaddr.Raw += (ppuctrl.Flag.VRAMAddrIncrement ? 32 : 1); break; @@ -232,7 +238,7 @@ Byte PPU::ReadRegister(Byte id) break; } - return latch; + return data; } void PPU::WriteRegister(Byte id, Byte val) diff --git a/src/debugger/PatternTableViewer.cpp b/src/debugger/PatternTableViewer.cpp index 79f9db0..bbf4ec4 100644 --- a/src/debugger/PatternTableViewer.cpp +++ b/src/debugger/PatternTableViewer.cpp @@ -63,6 +63,8 @@ void PatternTableViewer::DecodePatternTable(int index, std::vector& buffe { // uint8_t stride = 128; Word baseAddr = 0x1000 * index; + if (baseAddr >= mapper->CHR_ROM.size()) + return; for (int y = 0; y < 16; y++) { diff --git a/src/mappers/Mapper000.cpp b/src/mappers/Mapper000.cpp index 38df639..772f507 100644 --- a/src/mappers/Mapper000.cpp +++ b/src/mappers/Mapper000.cpp @@ -8,7 +8,6 @@ Mapper000::Mapper000(const Header& header, std::ifstream& ifs) : Mapper(header) { LOG_CORE_INFO("Allocating PRG ROM"); - prgBanks = header.PrgROM; PRG_ROM = std::vector(0x4000 * prgBanks); ifs.read((char*)PRG_ROM.data(), 0x4000 * prgBanks); diff --git a/src/mappers/Mapper000.hpp b/src/mappers/Mapper000.hpp index 459e488..132c6ff 100644 --- a/src/mappers/Mapper000.hpp +++ b/src/mappers/Mapper000.hpp @@ -1,6 +1,5 @@ #pragma once -#include #include #include "../Mapper.hpp" @@ -16,7 +15,4 @@ public: virtual Byte ReadPPU(Word addr) override; virtual void WriteCPU(Word addr, Byte val) override; virtual void WritePPU(Word addr, Byte val) override; - -private: - Byte prgBanks = 0; }; diff --git a/src/mappers/Mapper001.cpp b/src/mappers/Mapper001.cpp new file mode 100644 index 0000000..d729857 --- /dev/null +++ b/src/mappers/Mapper001.cpp @@ -0,0 +1,126 @@ +#include "Mapper001.hpp" + +Mapper001::Mapper001(const Header& header, std::ifstream& ifs) : + Mapper(header) +{ + LOG_CORE_INFO("Allocating PRG ROM"); + PRG_ROM = std::vector(0x4000 * prgBanks); + ifs.read((char*)PRG_ROM.data(), 0x4000 * prgBanks); + + LOG_CORE_INFO("Allocating CHR ROM"); + CHR_ROM = std::vector(0x2000 * chrBanks); + ifs.read((char*)CHR_ROM.data(), 0x2000 * chrBanks); +} + +Byte Mapper001::ReadCPU(Word addr) +{ + if (0x8000 <= addr && addr <= 0xFFFF) + { + Byte selectedBank = prgBank; + Byte prgControl = (control >> 2) & 0x3; + + switch (prgControl) + { + case 0: + selectedBank &= ~0x1; + break; + + case 1: + if (addr < 0xC000) + selectedBank = 1; + break; + + case 2: + if (addr >= 0xC000) + selectedBank = prgBanks; + break; + } + + return PRG_ROM[addr & (0x4000 * selectedBank - 1)]; + } + + return 0x00; +} + +Byte Mapper001::ReadPPU(Word addr) +{ + if (0x0000 <= addr && addr <= 0x1FFF) + { + Byte selectedBank = 0x00; + Byte chrControl = (control >> 4) & 0x1; + + if (chrControl) + { + if (addr < 0x1000) + selectedBank = chrBank0; + else + selectedBank = chrBank1; + } + else + { + selectedBank = chrBank0 & ~0x1; + } + + return CHR_ROM[addr & (0x1000 * selectedBank - 1)]; + } + + return 0x00; +} + +void Mapper001::WriteCPU(Word addr, Byte val) +{ + if (addr <= 0x8000 && addr <= 0xFFFF) + { + if ((val & 0x80) == 0x80) + { + shiftRegister = 0x00; + control |= 0x0C; + return; + } + + latch++; + shiftRegister >>= 1; + shiftRegister |= (val & 0x1) << 4; + + if (latch == 5) + { + Byte registerSelect = (addr & 0x6000) >> 13; + switch (registerSelect) + { + case 0: control = shiftRegister; break; + case 1: chrBank0 = shiftRegister; break; + case 2: chrBank1 = shiftRegister; break; + case 3: prgBank = shiftRegister; break; + } + + shiftRegister = 0x00; + latch = 0; + } + } +} + +void Mapper001::WritePPU(Word addr, Byte val) +{ + +} + +bool Mapper001::MapCIRAM(Word& addr) +{ + if ((control & 0x3) < 2) + { + LOG_CORE_WARN("Mapper MMC1 doesn't yet support One-Screen mirroring"); + return true; + } + + if ((control & 0x3) == 0x3) + { + // Shift Bit 11 into Bit 10 + addr &= ~(1 << 10); + addr |= ((addr & (1 << 11)) >> 1); + } + + // Unset bit 11 + addr &= ~(1 << 11); + + return false; +} diff --git a/src/mappers/Mapper001.hpp b/src/mappers/Mapper001.hpp new file mode 100644 index 0000000..9a13b7e --- /dev/null +++ b/src/mappers/Mapper001.hpp @@ -0,0 +1,27 @@ +#pragma once + +#include +#include "../Mapper.hpp" + +class Mapper001 : + public Mapper +{ +public: + Mapper001(const Header& header, std::ifstream& ifs); + + virtual Byte ReadCPU(Word addr) override; + virtual Byte ReadPPU(Word addr) override; + virtual void WriteCPU(Word addr, Byte val) override; + virtual void WritePPU(Word addr, Byte val) override; + + virtual bool MapCIRAM(Word& addr) override; + +private: + Byte latch = 0; + + Byte shiftRegister = 0x00; + Byte control = 0x00; + Byte chrBank0 = 0x00; + Byte chrBank1 = 0x00; + Byte prgBank = 0x00; +}; diff --git a/src/mappers/Mapper003.cpp b/src/mappers/Mapper003.cpp new file mode 100644 index 0000000..9dd01c0 --- /dev/null +++ b/src/mappers/Mapper003.cpp @@ -0,0 +1,48 @@ +#include "Mapper003.hpp" + +#include +#include "../Log.hpp" + +Mapper003::Mapper003(const Header& header, std::ifstream& ifs) : + Mapper(header) +{ + LOG_CORE_INFO("Allocating PRG ROM"); + PRG_ROM = std::vector(0x4000 * prgBanks); + ifs.read((char*)PRG_ROM.data(), 0x4000 * prgBanks); + + LOG_CORE_INFO("Allocating CHR ROM"); + CHR_ROM = std::vector(0x2000 * chrBanks); + ifs.read((char*)CHR_ROM.data(), 0x2000 * chrBanks); +} + +Byte Mapper003::ReadCPU(Word addr) +{ + if (0x8000 <= addr && addr <= 0xFFFF) + { + return PRG_ROM[addr & (0x4000 * prgBanks - 1)]; + } + + return 0x00; +} + +Byte Mapper003::ReadPPU(Word addr) +{ + if (0x0000 <= addr && addr <= 0x1FFF) + { + return CHR_ROM[0x2000 * selectedChrBank + addr]; + } + + return 0x00; +} + +void Mapper003::WriteCPU(Word addr, Byte val) +{ + if (0x8000 <= addr && addr <= 0xFFFF) + { + selectedChrBank = val & 0x3; + } +} + +void Mapper003::WritePPU(Word addr, Byte val) +{ +} diff --git a/src/mappers/Mapper003.hpp b/src/mappers/Mapper003.hpp new file mode 100644 index 0000000..648cc2b --- /dev/null +++ b/src/mappers/Mapper003.hpp @@ -0,0 +1,21 @@ +#pragma once + +#include +#include "../Mapper.hpp" + +struct Header; + +class Mapper003 : + public Mapper +{ +public: + Mapper003(const Header& header, std::ifstream& ifs); + + virtual Byte ReadCPU(Word addr) override; + virtual Byte ReadPPU(Word addr) override; + virtual void WriteCPU(Word addr, Byte val) override; + virtual void WritePPU(Word addr, Byte val) override; + +private: + Byte selectedChrBank = 0; +};