From a8169981222941ac1548b8c1975e13c4d4373a4a Mon Sep 17 00:00:00 2001 From: Tim Allen Date: Sun, 26 Jun 2016 18:54:12 +1000 Subject: [PATCH] Update to v099r10 release. byuu says: Changelog: - higan/profile/ => higan/systems/ [temporary; unless we can't think of a better base folder name] - god-damn-better-have fixed the input polling bug - re-added command-line and drag-and-drop loading - command-line loading can now load multiple folders at once (SGB+GB game; Sufami Turbo+Slot A+Slot B; etc) - if you load just the base cart, it'll present you with a dialog to optionally load slotted cart(s) - MSU1 now goes through nall/vfs instead of directly accessing the filesystem - Famicom Cartridge, PPU cores updated to newer programming style - there's countless opportunity for BitField and .bits() in the PPU ... but I'm worried about breaking things If anyone has a working MSU1 game and can test the changes out, that'd be appreciated. I still don't have a test ROM on my dev box. I wouldn't worry too much about extensively testing the Famicom PPU changes just yet ... I'm still struggling with what to name the structs inside the classes between all of my emulators, and the BitField/.bits() changes will be much more important to test at a later date. The only use case left for Emulator::Interface::path(uint id) is for 21fx emulation. This peripheral loads a DLL/SO via LoadLibrary/dlopen, which do not have any official ways to open a file in RAM. I'm very hesitant to use the portable trick of writing the memory to a temporary file, loading it, and deleting the temporary file once done ... it's a real waste of disk activity. I might make something like vfs::file::isVirtual->bool,path()->string to get around this. But even once I do, the underlying LoadLibrary/dlopen call is still going to be direct disk access. --- higan/emulator/emulator.hpp | 2 +- higan/emulator/interface.hpp | 1 - higan/fc/cartridge/board/bandai-fcg.cpp | 4 +- higan/fc/cartridge/board/konami-vrc1.cpp | 4 +- higan/fc/cartridge/board/konami-vrc2.cpp | 4 +- higan/fc/cartridge/board/konami-vrc3.cpp | 4 +- higan/fc/cartridge/board/konami-vrc4.cpp | 4 +- higan/fc/cartridge/board/konami-vrc6.cpp | 4 +- higan/fc/cartridge/board/konami-vrc7.cpp | 4 +- higan/fc/cartridge/board/nes-axrom.cpp | 4 +- higan/fc/cartridge/board/nes-bnrom.cpp | 4 +- higan/fc/cartridge/board/nes-cnrom.cpp | 4 +- higan/fc/cartridge/board/nes-fxrom.cpp | 4 +- higan/fc/cartridge/board/nes-gxrom.cpp | 4 +- higan/fc/cartridge/board/nes-hkrom.cpp | 4 +- higan/fc/cartridge/board/nes-nrom.cpp | 4 +- higan/fc/cartridge/board/nes-pxrom.cpp | 4 +- higan/fc/cartridge/board/nes-sxrom.cpp | 4 +- higan/fc/cartridge/board/nes-txrom.cpp | 4 +- higan/fc/cartridge/board/nes-uxrom.cpp | 4 +- higan/fc/cartridge/board/sunsoft-5b.cpp | 4 +- higan/fc/cartridge/cartridge.cpp | 8 +- higan/fc/cartridge/cartridge.hpp | 8 +- higan/fc/cartridge/chip/mmc5.cpp | 8 +- higan/fc/memory/memory.cpp | 4 +- higan/fc/ppu/memory.cpp | 121 +++++ higan/fc/ppu/ppu.cpp | 456 +----------------- higan/fc/ppu/ppu.hpp | 116 +++-- higan/fc/ppu/render.cpp | 250 ++++++++++ higan/fc/ppu/serialization.cpp | 101 ++-- higan/sfc/coprocessor/msu1/msu1.cpp | 79 ++- higan/sfc/coprocessor/msu1/msu1.hpp | 22 +- higan/sfc/coprocessor/msu1/serialization.cpp | 8 +- .../Famicom.sys/manifest.bml | 0 .../Game Boy Advance.sys/manifest.bml | 0 .../Game Boy Color.sys/boot.rom | Bin .../Game Boy Color.sys/manifest.bml | 0 .../Game Boy.sys/boot.rom | Bin .../Game Boy.sys/manifest.bml | 0 .../Super Famicom.sys/ipl.rom | Bin .../Super Famicom.sys/manifest.bml | 0 .../WonderSwan Color.sys/manifest.bml | 0 .../WonderSwan.sys/manifest.bml | 0 higan/target-tomoko/GNUmakefile | 4 +- higan/target-tomoko/input/input.cpp | 37 +- higan/target-tomoko/input/input.hpp | 14 +- .../presentation/presentation.cpp | 6 + higan/target-tomoko/program/interface.cpp | 41 +- higan/target-tomoko/program/medium.cpp | 18 +- higan/target-tomoko/program/program.cpp | 3 + higan/target-tomoko/program/program.hpp | 4 +- higan/target-tomoko/settings/input.cpp | 20 +- 52 files changed, 705 insertions(+), 702 deletions(-) create mode 100644 higan/fc/ppu/memory.cpp create mode 100644 higan/fc/ppu/render.cpp rename higan/{profile => systems}/Famicom.sys/manifest.bml (100%) rename higan/{profile => systems}/Game Boy Advance.sys/manifest.bml (100%) rename higan/{profile => systems}/Game Boy Color.sys/boot.rom (100%) rename higan/{profile => systems}/Game Boy Color.sys/manifest.bml (100%) rename higan/{profile => systems}/Game Boy.sys/boot.rom (100%) rename higan/{profile => systems}/Game Boy.sys/manifest.bml (100%) rename higan/{profile => systems}/Super Famicom.sys/ipl.rom (100%) rename higan/{profile => systems}/Super Famicom.sys/manifest.bml (100%) rename higan/{profile => systems}/WonderSwan Color.sys/manifest.bml (100%) rename higan/{profile => systems}/WonderSwan.sys/manifest.bml (100%) diff --git a/higan/emulator/emulator.hpp b/higan/emulator/emulator.hpp index 6e564990..238c407f 100644 --- a/higan/emulator/emulator.hpp +++ b/higan/emulator/emulator.hpp @@ -11,7 +11,7 @@ using namespace nall; namespace Emulator { static const string Name = "higan"; - static const string Version = "099.09"; + static const string Version = "099.10"; static const string Author = "byuu"; static const string License = "GPLv3"; static const string Website = "http://byuu.org/"; diff --git a/higan/emulator/interface.hpp b/higan/emulator/interface.hpp index 45f03bc8..4d00bc3b 100644 --- a/higan/emulator/interface.hpp +++ b/higan/emulator/interface.hpp @@ -30,7 +30,6 @@ struct Interface { struct Input { uint type; //0 = digital, 1 = analog (relative), 2 = rumble string name; - uintptr userData; }; vector inputs; }; diff --git a/higan/fc/cartridge/board/bandai-fcg.cpp b/higan/fc/cartridge/board/bandai-fcg.cpp index d784064e..2a3ea45a 100644 --- a/higan/fc/cartridge/board/bandai-fcg.cpp +++ b/higan/fc/cartridge/board/bandai-fcg.cpp @@ -65,13 +65,13 @@ struct BandaiFCG : Board { } auto chr_read(uint addr) -> uint8 { - if(addr & 0x2000) return ppu.ciram_read(ciram_addr(addr)); + if(addr & 0x2000) return ppu.readCIRAM(ciram_addr(addr)); addr = (chr_bank[addr >> 10] << 10) | (addr & 0x03ff); return Board::chr_read(addr); } auto chr_write(uint addr, uint8 data) -> void { - if(addr & 0x2000) return ppu.ciram_write(ciram_addr(addr), data); + if(addr & 0x2000) return ppu.writeCIRAM(ciram_addr(addr), data); addr = (chr_bank[addr >> 10] << 10) | (addr & 0x03ff); return Board::chr_write(addr, data); } diff --git a/higan/fc/cartridge/board/konami-vrc1.cpp b/higan/fc/cartridge/board/konami-vrc1.cpp index ff10d8cc..481fd101 100644 --- a/higan/fc/cartridge/board/konami-vrc1.cpp +++ b/higan/fc/cartridge/board/konami-vrc1.cpp @@ -12,12 +12,12 @@ struct KonamiVRC1 : Board { } auto chr_read(uint addr) -> uint8 { - if(addr & 0x2000) return ppu.ciram_read(vrc1.ciram_addr(addr)); + if(addr & 0x2000) return ppu.readCIRAM(vrc1.ciram_addr(addr)); return Board::chr_read(vrc1.chr_addr(addr)); } auto chr_write(uint addr, uint8 data) -> void { - if(addr & 0x2000) return ppu.ciram_write(vrc1.ciram_addr(addr), data); + if(addr & 0x2000) return ppu.writeCIRAM(vrc1.ciram_addr(addr), data); return Board::chr_write(vrc1.chr_addr(addr), data); } diff --git a/higan/fc/cartridge/board/konami-vrc2.cpp b/higan/fc/cartridge/board/konami-vrc2.cpp index d033d2b5..4424601b 100644 --- a/higan/fc/cartridge/board/konami-vrc2.cpp +++ b/higan/fc/cartridge/board/konami-vrc2.cpp @@ -22,12 +22,12 @@ struct KonamiVRC2 : Board { } auto chr_read(uint addr) -> uint8 { - if(addr & 0x2000) return ppu.ciram_read(vrc2.ciram_addr(addr)); + if(addr & 0x2000) return ppu.readCIRAM(vrc2.ciram_addr(addr)); return Board::chr_read(vrc2.chr_addr(addr)); } auto chr_write(uint addr, uint8 data) -> void { - if(addr & 0x2000) return ppu.ciram_write(vrc2.ciram_addr(addr), data); + if(addr & 0x2000) return ppu.writeCIRAM(vrc2.ciram_addr(addr), data); return Board::chr_write(vrc2.chr_addr(addr), data); } diff --git a/higan/fc/cartridge/board/konami-vrc3.cpp b/higan/fc/cartridge/board/konami-vrc3.cpp index 5a6cd5da..5a7be3c5 100644 --- a/higan/fc/cartridge/board/konami-vrc3.cpp +++ b/higan/fc/cartridge/board/konami-vrc3.cpp @@ -21,7 +21,7 @@ struct KonamiVRC3 : Board { auto chr_read(uint addr) -> uint8 { if(addr & 0x2000) { if(settings.mirror == 0) addr = ((addr & 0x0800) >> 1) | (addr & 0x03ff); - return ppu.ciram_read(addr & 0x07ff); + return ppu.readCIRAM(addr & 0x07ff); } return chrram.read(addr); } @@ -29,7 +29,7 @@ struct KonamiVRC3 : Board { auto chr_write(uint addr, uint8 data) -> void { if(addr & 0x2000) { if(settings.mirror == 0) addr = ((addr & 0x0800) >> 1) | (addr & 0x03ff); - return ppu.ciram_write(addr & 0x07ff, data); + return ppu.writeCIRAM(addr & 0x07ff, data); } return chrram.write(addr, data); } diff --git a/higan/fc/cartridge/board/konami-vrc4.cpp b/higan/fc/cartridge/board/konami-vrc4.cpp index 13041ee6..b8a6ac5b 100644 --- a/higan/fc/cartridge/board/konami-vrc4.cpp +++ b/higan/fc/cartridge/board/konami-vrc4.cpp @@ -26,12 +26,12 @@ struct KonamiVRC4 : Board { } auto chr_read(uint addr) -> uint8 { - if(addr & 0x2000) return ppu.ciram_read(vrc4.ciram_addr(addr)); + if(addr & 0x2000) return ppu.readCIRAM(vrc4.ciram_addr(addr)); return Board::chr_read(vrc4.chr_addr(addr)); } auto chr_write(uint addr, uint8 data) -> void { - if(addr & 0x2000) return ppu.ciram_write(vrc4.ciram_addr(addr), data); + if(addr & 0x2000) return ppu.writeCIRAM(vrc4.ciram_addr(addr), data); return Board::chr_write(vrc4.chr_addr(addr), data); } diff --git a/higan/fc/cartridge/board/konami-vrc6.cpp b/higan/fc/cartridge/board/konami-vrc6.cpp index 3f34f2ae..0b4972f4 100644 --- a/higan/fc/cartridge/board/konami-vrc6.cpp +++ b/higan/fc/cartridge/board/konami-vrc6.cpp @@ -18,12 +18,12 @@ struct KonamiVRC6 : Board { } auto chr_read(uint addr) -> uint8 { - if(addr & 0x2000) return ppu.ciram_read(vrc6.ciram_addr(addr)); + if(addr & 0x2000) return ppu.readCIRAM(vrc6.ciram_addr(addr)); return Board::chr_read(vrc6.chr_addr(addr)); } auto chr_write(uint addr, uint8 data) -> void { - if(addr & 0x2000) return ppu.ciram_write(vrc6.ciram_addr(addr), data); + if(addr & 0x2000) return ppu.writeCIRAM(vrc6.ciram_addr(addr), data); return Board::chr_write(vrc6.chr_addr(addr), data); } diff --git a/higan/fc/cartridge/board/konami-vrc7.cpp b/higan/fc/cartridge/board/konami-vrc7.cpp index a5008717..ad29458c 100644 --- a/higan/fc/cartridge/board/konami-vrc7.cpp +++ b/higan/fc/cartridge/board/konami-vrc7.cpp @@ -19,12 +19,12 @@ struct KonamiVRC7 : Board { } auto chr_read(uint addr) -> uint8 { - if(addr & 0x2000) return ppu.ciram_read(vrc7.ciram_addr(addr)); + if(addr & 0x2000) return ppu.readCIRAM(vrc7.ciram_addr(addr)); return chrram.read(vrc7.chr_addr(addr)); } auto chr_write(uint addr, uint8 data) -> void { - if(addr & 0x2000) return ppu.ciram_write(vrc7.ciram_addr(addr), data); + if(addr & 0x2000) return ppu.writeCIRAM(vrc7.ciram_addr(addr), data); return chrram.write(vrc7.chr_addr(addr), data); } diff --git a/higan/fc/cartridge/board/nes-axrom.cpp b/higan/fc/cartridge/board/nes-axrom.cpp index dc79e005..7c7733e0 100644 --- a/higan/fc/cartridge/board/nes-axrom.cpp +++ b/higan/fc/cartridge/board/nes-axrom.cpp @@ -20,12 +20,12 @@ struct NES_AxROM : Board { } auto chr_read(uint addr) -> uint8 { - if(addr & 0x2000) return ppu.ciram_read((mirror_select << 10) | (addr & 0x03ff)); + if(addr & 0x2000) return ppu.readCIRAM((mirror_select << 10) | (addr & 0x03ff)); return Board::chr_read(addr); } auto chr_write(uint addr, uint8 data) -> void { - if(addr & 0x2000) return ppu.ciram_write((mirror_select << 10) | (addr & 0x03ff), data); + if(addr & 0x2000) return ppu.writeCIRAM((mirror_select << 10) | (addr & 0x03ff), data); return Board::chr_write(addr, data); } diff --git a/higan/fc/cartridge/board/nes-bnrom.cpp b/higan/fc/cartridge/board/nes-bnrom.cpp index c39c60c5..d23a64ed 100644 --- a/higan/fc/cartridge/board/nes-bnrom.cpp +++ b/higan/fc/cartridge/board/nes-bnrom.cpp @@ -17,7 +17,7 @@ struct NES_BNROM : Board { auto chr_read(uint addr) -> uint8 { if(addr & 0x2000) { if(settings.mirror == 0) addr = ((addr & 0x0800) >> 1) | (addr & 0x03ff); - return ppu.ciram_read(addr); + return ppu.readCIRAM(addr); } return Board::chr_read(addr); } @@ -25,7 +25,7 @@ struct NES_BNROM : Board { auto chr_write(uint addr, uint8 data) -> void { if(addr & 0x2000) { if(settings.mirror == 0) addr = ((addr & 0x0800) >> 1) | (addr & 0x03ff); - return ppu.ciram_write(addr, data); + return ppu.writeCIRAM(addr, data); } return Board::chr_write(addr, data); } diff --git a/higan/fc/cartridge/board/nes-cnrom.cpp b/higan/fc/cartridge/board/nes-cnrom.cpp index d0028f5d..a2c9ff87 100644 --- a/higan/fc/cartridge/board/nes-cnrom.cpp +++ b/higan/fc/cartridge/board/nes-cnrom.cpp @@ -17,7 +17,7 @@ struct NES_CNROM : Board { auto chr_read(uint addr) -> uint8 { if(addr & 0x2000) { if(settings.mirror == 0) addr = ((addr & 0x0800) >> 1) | (addr & 0x03ff); - return ppu.ciram_read(addr & 0x07ff); + return ppu.readCIRAM(addr & 0x07ff); } addr = (chr_bank * 0x2000) + (addr & 0x1fff); return Board::chr_read(addr); @@ -26,7 +26,7 @@ struct NES_CNROM : Board { auto chr_write(uint addr, uint8 data) -> void { if(addr & 0x2000) { if(settings.mirror == 0) addr = ((addr & 0x0800) >> 1) | (addr & 0x03ff); - return ppu.ciram_write(addr & 0x07ff, data); + return ppu.writeCIRAM(addr & 0x07ff, data); } addr = (chr_bank * 0x2000) + (addr & 0x1fff); Board::chr_write(addr, data); diff --git a/higan/fc/cartridge/board/nes-fxrom.cpp b/higan/fc/cartridge/board/nes-fxrom.cpp index 170a0ab1..0da3c33c 100644 --- a/higan/fc/cartridge/board/nes-fxrom.cpp +++ b/higan/fc/cartridge/board/nes-fxrom.cpp @@ -34,7 +34,7 @@ struct NES_FxROM : Board { } auto chr_read(uint addr) -> uint8 { - if(addr & 0x2000) return ppu.ciram_read(ciram_addr(addr)); + if(addr & 0x2000) return ppu.readCIRAM(ciram_addr(addr)); bool region = addr & 0x1000; uint bank = chr_bank[region][latch[region]]; if((addr & 0x0ff8) == 0x0fd8) latch[region] = 0; @@ -43,7 +43,7 @@ struct NES_FxROM : Board { } auto chr_write(uint addr, uint8 data) -> void { - if(addr & 0x2000) return ppu.ciram_write(ciram_addr(addr), data); + if(addr & 0x2000) return ppu.writeCIRAM(ciram_addr(addr), data); bool region = addr & 0x1000; uint bank = chr_bank[region][latch[region]]; if((addr & 0x0ff8) == 0x0fd8) latch[region] = 0; diff --git a/higan/fc/cartridge/board/nes-gxrom.cpp b/higan/fc/cartridge/board/nes-gxrom.cpp index d5d58aac..7549ae41 100644 --- a/higan/fc/cartridge/board/nes-gxrom.cpp +++ b/higan/fc/cartridge/board/nes-gxrom.cpp @@ -21,7 +21,7 @@ struct NES_GxROM : Board { auto chr_read(uint addr) -> uint8 { if(addr & 0x2000) { if(settings.mirror == 0) addr = ((addr & 0x0800) >> 1) | (addr & 0x03ff); - return ppu.ciram_read(addr & 0x07ff); + return ppu.readCIRAM(addr & 0x07ff); } addr = (chr_bank * 0x2000) + (addr & 0x1fff); return Board::chr_read(addr); @@ -30,7 +30,7 @@ struct NES_GxROM : Board { auto chr_write(uint addr, uint8 data) -> void { if(addr & 0x2000) { if(settings.mirror == 0) addr = ((addr & 0x0800) >> 1) | (addr & 0x03ff); - return ppu.ciram_write(addr & 0x07ff, data); + return ppu.writeCIRAM(addr & 0x07ff, data); } addr = (chr_bank * 0x2000) + (addr & 0x1fff); Board::chr_write(addr, data); diff --git a/higan/fc/cartridge/board/nes-hkrom.cpp b/higan/fc/cartridge/board/nes-hkrom.cpp index 599dd24b..065f4156 100644 --- a/higan/fc/cartridge/board/nes-hkrom.cpp +++ b/higan/fc/cartridge/board/nes-hkrom.cpp @@ -19,13 +19,13 @@ struct NES_HKROM : Board { auto chr_read(uint addr) -> uint8 { mmc6.irq_test(addr); - if(addr & 0x2000) return ppu.ciram_read(mmc6.ciram_addr(addr)); + if(addr & 0x2000) return ppu.readCIRAM(mmc6.ciram_addr(addr)); return Board::chr_read(mmc6.chr_addr(addr)); } auto chr_write(uint addr, uint8 data) -> void { mmc6.irq_test(addr); - if(addr & 0x2000) return ppu.ciram_write(mmc6.ciram_addr(addr), data); + if(addr & 0x2000) return ppu.writeCIRAM(mmc6.ciram_addr(addr), data); return Board::chr_write(mmc6.chr_addr(addr), data); } diff --git a/higan/fc/cartridge/board/nes-nrom.cpp b/higan/fc/cartridge/board/nes-nrom.cpp index 2969a5b9..89662f05 100644 --- a/higan/fc/cartridge/board/nes-nrom.cpp +++ b/higan/fc/cartridge/board/nes-nrom.cpp @@ -17,7 +17,7 @@ struct NES_NROM : Board { auto chr_read(uint addr) -> uint8 { if(addr & 0x2000) { if(settings.mirror == 0) addr = ((addr & 0x0800) >> 1) | (addr & 0x03ff); - return ppu.ciram_read(addr & 0x07ff); + return ppu.readCIRAM(addr & 0x07ff); } if(chrram.size) return chrram.read(addr); return chrrom.read(addr); @@ -26,7 +26,7 @@ struct NES_NROM : Board { auto chr_write(uint addr, uint8 data) -> void { if(addr & 0x2000) { if(settings.mirror == 0) addr = ((addr & 0x0800) >> 1) | (addr & 0x03ff); - return ppu.ciram_write(addr & 0x07ff, data); + return ppu.writeCIRAM(addr & 0x07ff, data); } if(chrram.size) return chrram.write(addr, data); } diff --git a/higan/fc/cartridge/board/nes-pxrom.cpp b/higan/fc/cartridge/board/nes-pxrom.cpp index 473455ef..a2753ece 100644 --- a/higan/fc/cartridge/board/nes-pxrom.cpp +++ b/higan/fc/cartridge/board/nes-pxrom.cpp @@ -40,7 +40,7 @@ struct NES_PxROM : Board { } auto chr_read(uint addr) -> uint8 { - if(addr & 0x2000) return ppu.ciram_read(ciram_addr(addr)); + if(addr & 0x2000) return ppu.readCIRAM(ciram_addr(addr)); bool region = addr & 0x1000; uint bank = chr_bank[region][latch[region]]; if((addr & 0x0ff8) == 0x0fd8) latch[region] = 0; @@ -49,7 +49,7 @@ struct NES_PxROM : Board { } auto chr_write(uint addr, uint8 data) -> void { - if(addr & 0x2000) return ppu.ciram_write(ciram_addr(addr), data); + if(addr & 0x2000) return ppu.writeCIRAM(ciram_addr(addr), data); bool region = addr & 0x1000; uint bank = chr_bank[region][latch[region]]; if((addr & 0x0ff8) == 0x0fd8) latch[region] = 0; diff --git a/higan/fc/cartridge/board/nes-sxrom.cpp b/higan/fc/cartridge/board/nes-sxrom.cpp index 0ef9bacc..70162acb 100644 --- a/higan/fc/cartridge/board/nes-sxrom.cpp +++ b/higan/fc/cartridge/board/nes-sxrom.cpp @@ -48,12 +48,12 @@ struct NES_SxROM : Board { } auto chr_read(uint addr) -> uint8 { - if(addr & 0x2000) return ppu.ciram_read(mmc1.ciram_addr(addr)); + if(addr & 0x2000) return ppu.readCIRAM(mmc1.ciram_addr(addr)); return Board::chr_read(mmc1.chr_addr(addr)); } auto chr_write(uint addr, uint8 data) -> void { - if(addr & 0x2000) return ppu.ciram_write(mmc1.ciram_addr(addr), data); + if(addr & 0x2000) return ppu.writeCIRAM(mmc1.ciram_addr(addr), data); return Board::chr_write(mmc1.chr_addr(addr), data); } diff --git a/higan/fc/cartridge/board/nes-txrom.cpp b/higan/fc/cartridge/board/nes-txrom.cpp index 9f4b119e..b2c6c1fd 100644 --- a/higan/fc/cartridge/board/nes-txrom.cpp +++ b/higan/fc/cartridge/board/nes-txrom.cpp @@ -20,13 +20,13 @@ struct NES_TxROM : Board { auto chr_read(uint addr) -> uint8 { mmc3.irq_test(addr); - if(addr & 0x2000) return ppu.ciram_read(mmc3.ciram_addr(addr)); + if(addr & 0x2000) return ppu.readCIRAM(mmc3.ciram_addr(addr)); return Board::chr_read(mmc3.chr_addr(addr)); } auto chr_write(uint addr, uint8 data) -> void { mmc3.irq_test(addr); - if(addr & 0x2000) return ppu.ciram_write(mmc3.ciram_addr(addr), data); + if(addr & 0x2000) return ppu.writeCIRAM(mmc3.ciram_addr(addr), data); return Board::chr_write(mmc3.chr_addr(addr), data); } diff --git a/higan/fc/cartridge/board/nes-uxrom.cpp b/higan/fc/cartridge/board/nes-uxrom.cpp index 305672f9..61fbb06c 100644 --- a/higan/fc/cartridge/board/nes-uxrom.cpp +++ b/higan/fc/cartridge/board/nes-uxrom.cpp @@ -19,7 +19,7 @@ struct NES_UxROM : Board { auto chr_read(uint addr) -> uint8 { if(addr & 0x2000) { if(settings.mirror == 0) addr = ((addr & 0x0800) >> 1) | (addr & 0x03ff); - return ppu.ciram_read(addr); + return ppu.readCIRAM(addr); } return Board::chr_read(addr); } @@ -27,7 +27,7 @@ struct NES_UxROM : Board { auto chr_write(uint addr, uint8 data) -> void { if(addr & 0x2000) { if(settings.mirror == 0) addr = ((addr & 0x0800) >> 1) | (addr & 0x03ff); - return ppu.ciram_write(addr, data); + return ppu.writeCIRAM(addr, data); } return Board::chr_write(addr, data); } diff --git a/higan/fc/cartridge/board/sunsoft-5b.cpp b/higan/fc/cartridge/board/sunsoft-5b.cpp index 7eab4782..68d2d1df 100644 --- a/higan/fc/cartridge/board/sunsoft-5b.cpp +++ b/higan/fc/cartridge/board/sunsoft-5b.cpp @@ -154,12 +154,12 @@ struct Sunsoft5B : Board { } auto chr_read(uint addr) -> uint8 { - if(addr & 0x2000) return ppu.ciram_read(ciram_addr(addr)); + if(addr & 0x2000) return ppu.readCIRAM(ciram_addr(addr)); return Board::chr_read(chr_addr(addr)); } auto chr_write(uint addr, uint8 data) -> void { - if(addr & 0x2000) return ppu.ciram_write(ciram_addr(addr), data); + if(addr & 0x2000) return ppu.writeCIRAM(ciram_addr(addr), data); return Board::chr_write(chr_addr(addr), data); } diff --git a/higan/fc/cartridge/cartridge.cpp b/higan/fc/cartridge/cartridge.cpp index a18de35a..f911a6a5 100644 --- a/higan/fc/cartridge/cartridge.cpp +++ b/higan/fc/cartridge/cartridge.cpp @@ -53,19 +53,19 @@ auto Cartridge::reset() -> void { board->reset(); } -auto Cartridge::prg_read(uint addr) -> uint8 { +auto Cartridge::readPRG(uint addr) -> uint8 { return board->prg_read(addr); } -auto Cartridge::prg_write(uint addr, uint8 data) -> void { +auto Cartridge::writePRG(uint addr, uint8 data) -> void { return board->prg_write(addr, data); } -auto Cartridge::chr_read(uint addr) -> uint8 { +auto Cartridge::readCHR(uint addr) -> uint8 { return board->chr_read(addr); } -auto Cartridge::chr_write(uint addr, uint8 data) -> void { +auto Cartridge::writeCHR(uint addr, uint8 data) -> void { return board->chr_write(addr, data); } diff --git a/higan/fc/cartridge/cartridge.hpp b/higan/fc/cartridge/cartridge.hpp index 12d78f19..a3676ccd 100644 --- a/higan/fc/cartridge/cartridge.hpp +++ b/higan/fc/cartridge/cartridge.hpp @@ -29,11 +29,11 @@ struct Cartridge : Thread { //privileged: Board* board = nullptr; - auto prg_read(uint addr) -> uint8; - auto prg_write(uint addr, uint8 data) -> void; + auto readPRG(uint addr) -> uint8; + auto writePRG(uint addr, uint8 data) -> void; - auto chr_read(uint addr) -> uint8; - auto chr_write(uint addr, uint8 data) -> void; + auto readCHR(uint addr) -> uint8; + auto writeCHR(uint addr, uint8 data) -> void; //scanline() is for debugging purposes only: //boards must detect scanline edges on their own diff --git a/higan/fc/cartridge/chip/mmc5.cpp b/higan/fc/cartridge/chip/mmc5.cpp index b1909450..c73fee03 100644 --- a/higan/fc/cartridge/chip/mmc5.cpp +++ b/higan/fc/cartridge/chip/mmc5.cpp @@ -269,8 +269,8 @@ struct MMC5 : Chip { if(vs_fetch && (hcounter & 2) != 0) return exram[vs_vpos / 32 * 8 + vs_hpos / 32 + 0x03c0]; switch(nametable_mode[(addr >> 10) & 3]) { - case 0: return ppu.ciram_read(0x0000 | (addr & 0x03ff)); - case 1: return ppu.ciram_read(0x0400 | (addr & 0x03ff)); + case 0: return ppu.readCIRAM(0x0000 | (addr & 0x03ff)); + case 1: return ppu.readCIRAM(0x0400 | (addr & 0x03ff)); case 2: return exram_mode < 2 ? exram[addr & 0x03ff] : (uint8)0x00; case 3: return (hcounter & 2) == 0 ? fillmode_tile : fillmode_color; } @@ -327,8 +327,8 @@ struct MMC5 : Chip { auto chr_write(uint addr, uint8 data) -> void { if(addr & 0x2000) { switch(nametable_mode[(addr >> 10) & 3]) { - case 0: return ppu.ciram_write(0x0000 | (addr & 0x03ff), data); - case 1: return ppu.ciram_write(0x0400 | (addr & 0x03ff), data); + case 0: return ppu.writeCIRAM(0x0000 | (addr & 0x03ff), data); + case 1: return ppu.writeCIRAM(0x0400 | (addr & 0x03ff), data); case 2: exram[addr & 0x03ff] = data; break; } } diff --git a/higan/fc/memory/memory.cpp b/higan/fc/memory/memory.cpp index 564edaad..b5d8e014 100644 --- a/higan/fc/memory/memory.cpp +++ b/higan/fc/memory/memory.cpp @@ -12,7 +12,7 @@ Bus bus; //$4018-ffff = Cartridge auto Bus::read(uint16 addr) -> uint8 { - uint8 data = cartridge.prg_read(addr); + uint8 data = cartridge.readPRG(addr); if(addr <= 0x1fff) data = cpu.readRAM(addr); else if(addr <= 0x3fff) data = ppu.readIO(addr); else if(addr <= 0x4017) data = cpu.readIO(addr); @@ -25,7 +25,7 @@ auto Bus::read(uint16 addr) -> uint8 { } auto Bus::write(uint16 addr, uint8 data) -> void { - cartridge.prg_write(addr, data); + cartridge.writePRG(addr, data); if(addr <= 0x1fff) return cpu.writeRAM(addr, data); if(addr <= 0x3fff) return ppu.writeIO(addr, data); if(addr <= 0x4017) return cpu.writeIO(addr, data); diff --git a/higan/fc/ppu/memory.cpp b/higan/fc/ppu/memory.cpp new file mode 100644 index 00000000..9f1d8a1a --- /dev/null +++ b/higan/fc/ppu/memory.cpp @@ -0,0 +1,121 @@ +auto PPU::readCIRAM(uint11 addr) -> uint8 { + return ciram[addr]; +} + +auto PPU::writeCIRAM(uint11 addr, uint8 data) -> void { + ciram[addr] = data; +} + +auto PPU::readCGRAM(uint5 addr) -> uint8 { + if((addr & 0x13) == 0x10) addr &= ~0x10; + uint8 data = cgram[addr]; + if(r.grayscale) data &= 0x30; + return data; +} + +auto PPU::writeCGRAM(uint5 addr, uint8 data) -> void { + if((addr & 0x13) == 0x10) addr &= ~0x10; + cgram[addr] = data; +} + +auto PPU::readIO(uint16 addr) -> uint8 { + uint8 result = 0x00; + + switch(addr & 7) { + case 2: //PPUSTATUS + result |= r.nmiFlag << 7; + result |= r.spriteZeroHit << 6; + result |= r.spriteOverflow << 5; + result |= r.mdr & 0x1f; + r.addressLatch = 0; + r.nmiHold = 0; + cpu.nmiLine(r.nmiFlag = 0); + break; + case 4: //OAMDATA + result = oam[r.oamAddress]; + break; + case 7: //PPUDATA + if(enable() && (r.ly <= 240 || r.ly == 261)) return 0x00; + + addr = r.vaddr & 0x3fff; + if(addr <= 0x1fff) { + result = r.busData; + r.busData = cartridge.readCHR(addr); + } else if(addr <= 0x3eff) { + result = r.busData; + r.busData = cartridge.readCHR(addr); + } else if(addr <= 0x3fff) { + result = readCGRAM(addr); + r.busData = cartridge.readCHR(addr); + } + r.vaddr += r.vramIncrement; + break; + } + + return result; +} + +auto PPU::writeIO(uint16 addr, uint8 data) -> void { + r.mdr = data; + + switch(addr & 7) { + case 0: //PPUCTRL + r.nmiEnable = data & 0x80; + r.masterSelect = data & 0x40; + r.spriteHeight = data & 0x20 ? 16 : 8; + r.bgAddress = (data & 0x10) ? 0x1000 : 0x0000; + r.spriteAddress = (data & 0x08) ? 0x1000 : 0x0000; + r.vramIncrement = (data & 0x04) ? 32 : 1; + r.taddr = (r.taddr & 0x73ff) | ((data & 0x03) << 10); + cpu.nmiLine(r.nmiEnable && r.nmiHold && r.nmiFlag); + return; + case 1: //PPUMASK + r.emphasis = data >> 5; + r.spriteEnable = data & 0x10; + r.bgEnable = data & 0x08; + r.spriteEdgeEnable = data & 0x04; + r.bgEdgeEnable = data & 0x02; + r.grayscale = data & 0x01; + return; + case 2: //PPUSTATUS + return; + case 3: //OAMADDR + r.oamAddress = data; + return; + case 4: //OAMDATA + if(r.oamAddress.bits(0,1) == 2) data.bits(2,4) = 0; //clear non-existent bits (always read back as 0) + oam[r.oamAddress++] = data; + return; + case 5: //PPUSCROLL + if(r.addressLatch == 0) { + r.xaddr = data & 0x07; + r.taddr = (r.taddr & 0x7fe0) | (data >> 3); + } else { + r.taddr = (r.taddr & 0x0c1f) | ((data & 0x07) << 12) | ((data >> 3) << 5); + } + r.addressLatch ^= 1; + return; + case 6: //PPUADDR + if(r.addressLatch == 0) { + r.taddr = (r.taddr & 0x00ff) | ((data & 0x3f) << 8); + } else { + r.taddr = (r.taddr & 0x7f00) | data; + r.vaddr = r.taddr; + } + r.addressLatch ^= 1; + return; + case 7: //PPUDATA + if(enable() && (r.ly <= 240 || r.ly == 261)) return; + + addr = r.vaddr & 0x3fff; + if(addr <= 0x1fff) { + cartridge.writeCHR(addr, data); + } else if(addr <= 0x3eff) { + cartridge.writeCHR(addr, data); + } else if(addr <= 0x3fff) { + writeCGRAM(addr, data); + } + r.vaddr += r.vramIncrement; + return; + } +} diff --git a/higan/fc/ppu/ppu.cpp b/higan/fc/ppu/ppu.cpp index 0e683dda..7f5e8507 100644 --- a/higan/fc/ppu/ppu.cpp +++ b/higan/fc/ppu/ppu.cpp @@ -3,7 +3,8 @@ namespace Famicom { PPU ppu; - +#include "memory.cpp" +#include "render.cpp" #include "serialization.cpp" auto PPU::Enter() -> void { @@ -11,37 +12,37 @@ auto PPU::Enter() -> void { } auto PPU::main() -> void { - raster_scanline(); + renderScanline(); } auto PPU::tick() -> void { - if(status.ly == 240 && status.lx == 340) status.nmi_hold = 1; - if(status.ly == 241 && status.lx == 0) status.nmi_flag = status.nmi_hold; - if(status.ly == 241 && status.lx == 2) cpu.nmiLine(status.nmi_enable && status.nmi_flag); + if(r.ly == 240 && r.lx == 340) r.nmiHold = 1; + if(r.ly == 241 && r.lx == 0) r.nmiFlag = r.nmiHold; + if(r.ly == 241 && r.lx == 2) cpu.nmiLine(r.nmiEnable && r.nmiFlag); - if(status.ly == 260 && status.lx == 340) status.sprite_zero_hit = 0, status.sprite_overflow = 0; + if(r.ly == 260 && r.lx == 340) r.spriteZeroHit = 0, r.spriteOverflow = 0; - if(status.ly == 260 && status.lx == 340) status.nmi_hold = 0; - if(status.ly == 261 && status.lx == 0) status.nmi_flag = status.nmi_hold; - if(status.ly == 261 && status.lx == 2) cpu.nmiLine(status.nmi_enable && status.nmi_flag); + if(r.ly == 260 && r.lx == 340) r.nmiHold = 0; + if(r.ly == 261 && r.lx == 0) r.nmiFlag = r.nmiHold; + if(r.ly == 261 && r.lx == 2) cpu.nmiLine(r.nmiEnable && r.nmiFlag); clock += 4; if(clock >= 0 && !scheduler.synchronizing()) co_switch(cpu.thread); - status.lx++; + r.lx++; } auto PPU::scanline() -> void { - status.lx = 0; - if(++status.ly == 262) { - status.ly = 0; + r.lx = 0; + if(++r.ly == 262) { + r.ly = 0; frame(); } - cartridge.scanline(status.ly); + cartridge.scanline(r.ly); } auto PPU::frame() -> void { - status.field ^= 1; + r.field ^= 1; scheduler.exit(Scheduler::Event::Frame); } @@ -55,432 +56,15 @@ auto PPU::power() -> void { auto PPU::reset() -> void { create(PPU::Enter, 21'477'272); - status.mdr = 0x00; - status.field = 0; - status.ly = 0; - status.bus_data = 0x00; - status.address_latch = 0; + memory::fill(&r, sizeof(Registers)); + memory::fill(&l, sizeof(Latches)); + r.vramIncrement = 1; - status.vaddr = 0x0000; - status.taddr = 0x0000; - status.xaddr = 0x00; - - status.nmi_hold = 0; - status.nmi_flag = 0; - - //$2000 - status.nmi_enable = false; - status.master_select = 0; - status.sprite_size = 0; - status.bg_addr = 0x0000; - status.sprite_addr = 0x0000; - status.vram_increment = 1; - - //$2001 - status.emphasis = 0; - status.sprite_enable = false; - status.bg_enable = false; - status.sprite_edge_enable = false; - status.bg_edge_enable = false; - status.grayscale = false; - - //$2002 - status.sprite_zero_hit = false; - status.sprite_overflow = false; - - //$2003 - status.oam_addr = 0x00; - - for(auto& n : buffer) n = 0; for(auto& n : ciram ) n = 0; for(auto& n : cgram ) n = 0; for(auto& n : oam ) n = 0; -} -auto PPU::readIO(uint16 addr) -> uint8 { - uint8 result = 0x00; - - switch(addr & 7) { - case 2: //PPUSTATUS - result |= status.nmi_flag << 7; - result |= status.sprite_zero_hit << 6; - result |= status.sprite_overflow << 5; - result |= status.mdr & 0x1f; - status.address_latch = 0; - status.nmi_hold = 0; - cpu.nmiLine(status.nmi_flag = 0); - break; - case 4: //OAMDATA - result = oam[status.oam_addr]; - break; - case 7: //PPUDATA - if(raster_enable() && (status.ly <= 240 || status.ly == 261)) return 0x00; - - addr = status.vaddr & 0x3fff; - if(addr <= 0x1fff) { - result = status.bus_data; - status.bus_data = cartridge.chr_read(addr); - } else if(addr <= 0x3eff) { - result = status.bus_data; - status.bus_data = cartridge.chr_read(addr); - } else if(addr <= 0x3fff) { - result = cgram_read(addr); - status.bus_data = cartridge.chr_read(addr); - } - status.vaddr += status.vram_increment; - break; - } - - return result; -} - -auto PPU::writeIO(uint16 addr, uint8 data) -> void { - status.mdr = data; - - switch(addr & 7) { - case 0: //PPUCTRL - status.nmi_enable = data & 0x80; - status.master_select = data & 0x40; - status.sprite_size = data & 0x20; - status.bg_addr = (data & 0x10) ? 0x1000 : 0x0000; - status.sprite_addr = (data & 0x08) ? 0x1000 : 0x0000; - status.vram_increment = (data & 0x04) ? 32 : 1; - status.taddr = (status.taddr & 0x73ff) | ((data & 0x03) << 10); - cpu.nmiLine(status.nmi_enable && status.nmi_hold && status.nmi_flag); - return; - case 1: //PPUMASK - status.emphasis = data >> 5; - status.sprite_enable = data & 0x10; - status.bg_enable = data & 0x08; - status.sprite_edge_enable = data & 0x04; - status.bg_edge_enable = data & 0x02; - status.grayscale = data & 0x01; - return; - case 2: //PPUSTATUS - return; - case 3: //OAMADDR - status.oam_addr = data; - return; - case 4: //OAMDATA - if(status.oam_addr.bits(0,1) == 2) data.bits(2,4) = 0; //clear non-existent bits (always read back as 0) - oam[status.oam_addr++] = data; - return; - case 5: //PPUSCROLL - if(status.address_latch == 0) { - status.xaddr = data & 0x07; - status.taddr = (status.taddr & 0x7fe0) | (data >> 3); - } else { - status.taddr = (status.taddr & 0x0c1f) | ((data & 0x07) << 12) | ((data >> 3) << 5); - } - status.address_latch ^= 1; - return; - case 6: //PPUADDR - if(status.address_latch == 0) { - status.taddr = (status.taddr & 0x00ff) | ((data & 0x3f) << 8); - } else { - status.taddr = (status.taddr & 0x7f00) | data; - status.vaddr = status.taddr; - } - status.address_latch ^= 1; - return; - case 7: //PPUDATA - if(raster_enable() && (status.ly <= 240 || status.ly == 261)) return; - - addr = status.vaddr & 0x3fff; - if(addr <= 0x1fff) { - cartridge.chr_write(addr, data); - } else if(addr <= 0x3eff) { - cartridge.chr_write(addr, data); - } else if(addr <= 0x3fff) { - cgram_write(addr, data); - } - status.vaddr += status.vram_increment; - return; - } -} - -auto PPU::ciram_read(uint16 addr) -> uint8 { - return ciram[addr & 0x07ff]; -} - -auto PPU::ciram_write(uint16 addr, uint8 data) -> void { - ciram[addr & 0x07ff] = data; -} - -auto PPU::cgram_read(uint16 addr) -> uint8 { - if((addr & 0x13) == 0x10) addr &= ~0x10; - uint8 data = cgram[addr & 0x1f]; - if(status.grayscale) data &= 0x30; - return data; -} - -auto PPU::cgram_write(uint16 addr, uint8 data) -> void { - if((addr & 0x13) == 0x10) addr &= ~0x10; - cgram[addr & 0x1f] = data; -} - -// - -//vaddr = 0yyy VHYY YYYX XXXX -//yyy = fine Yscroll (y:d0-d2) -//V = V nametable (y:d8) -//H = H nametable (x:d8) -//YYYYY = Y nametable (y:d3-d7) -//XXXXX = X nametable (x:d3-d7) - -auto PPU::raster_enable() const -> bool { - return (status.bg_enable || status.sprite_enable); -} - -auto PPU::nametable_addr() const -> uint { - return 0x2000 + (status.vaddr & 0x0c00); -} - -auto PPU::scrollx() const -> uint { - return ((status.vaddr & 0x1f) << 3) | status.xaddr; -} - -auto PPU::scrolly() const -> uint { - return (((status.vaddr >> 5) & 0x1f) << 3) | ((status.vaddr >> 12) & 7); -} - -auto PPU::sprite_height() const -> uint { - return status.sprite_size == 0 ? 8 : 16; -} - -// - -auto PPU::chr_load(uint16 addr) -> uint8 { - if(raster_enable() == false) return 0x00; - return cartridge.chr_read(addr); -} - -// - -auto PPU::scrollx_increment() -> void { - if(raster_enable() == false) return; - status.vaddr = (status.vaddr & 0x7fe0) | ((status.vaddr + 0x0001) & 0x001f); - if((status.vaddr & 0x001f) == 0x0000) { - status.vaddr ^= 0x0400; - } -} - -auto PPU::scrolly_increment() -> void { - if(raster_enable() == false) return; - status.vaddr = (status.vaddr & 0x0fff) | ((status.vaddr + 0x1000) & 0x7000); - if((status.vaddr & 0x7000) == 0x0000) { - status.vaddr = (status.vaddr & 0x7c1f) | ((status.vaddr + 0x0020) & 0x03e0); - if((status.vaddr & 0x03e0) == 0x03c0) { //0x03c0 == 30 << 5; 30 * 8 = 240 - status.vaddr &= 0x7c1f; - status.vaddr ^= 0x0800; - } - } -} - -// - -auto PPU::raster_pixel() -> void { - uint32* output = buffer + status.ly * 256; - - uint mask = 0x8000 >> (status.xaddr + (status.lx & 7)); - uint palette = 0, object_palette = 0; - bool object_priority = 0; - palette |= (raster.tiledatalo & mask) ? 1 : 0; - palette |= (raster.tiledatahi & mask) ? 2 : 0; - if(palette) { - uint attr = raster.attribute; - if(mask >= 256) attr >>= 2; - palette |= (attr & 3) << 2; - } - - if(status.bg_enable == false) palette = 0; - if(status.bg_edge_enable == false && status.lx < 8) palette = 0; - - if(status.sprite_enable == true) - for(int sprite = 7; sprite >= 0; sprite--) { - if(status.sprite_edge_enable == false && status.lx < 8) continue; - if(raster.oam[sprite].id == 64) continue; - - uint spritex = status.lx - raster.oam[sprite].x; - if(spritex >= 8) continue; - - if(raster.oam[sprite].attr & 0x40) spritex ^= 7; - uint mask = 0x80 >> spritex; - uint sprite_palette = 0; - sprite_palette |= (raster.oam[sprite].tiledatalo & mask) ? 1 : 0; - sprite_palette |= (raster.oam[sprite].tiledatahi & mask) ? 2 : 0; - if(sprite_palette == 0) continue; - - if(raster.oam[sprite].id == 0 && palette && status.lx != 255) status.sprite_zero_hit = 1; - sprite_palette |= (raster.oam[sprite].attr & 3) << 2; - - object_priority = raster.oam[sprite].attr & 0x20; - object_palette = 16 + sprite_palette; - } - - if(object_palette) { - if(palette == 0 || object_priority == 0) palette = object_palette; - } - - if(raster_enable() == false) palette = 0; - output[status.lx] = (status.emphasis << 6) | cgram_read(palette); -} - -auto PPU::raster_sprite() -> void { - if(raster_enable() == false) return; - - uint n = raster.oam_iterator++; - int ly = (status.ly == 261 ? -1 : status.ly); - uint y = ly - oam[(n * 4) + 0]; - - if(y >= sprite_height()) return; - if(raster.oam_counter == 8) { - status.sprite_overflow = 1; - return; - } - - raster.soam[raster.oam_counter].id = n; - raster.soam[raster.oam_counter].y = oam[(n * 4) + 0]; - raster.soam[raster.oam_counter].tile = oam[(n * 4) + 1]; - raster.soam[raster.oam_counter].attr = oam[(n * 4) + 2]; - raster.soam[raster.oam_counter].x = oam[(n * 4) + 3]; - raster.oam_counter++; -} - -auto PPU::raster_scanline() -> void { - if((status.ly >= 240 && status.ly <= 260)) { - for(auto x : range(341)) tick(); - return scanline(); - } - - raster.oam_iterator = 0; - raster.oam_counter = 0; - - for(auto n : range(8)) { - raster.soam[n].id = 64; - raster.soam[n].y = 0xff; - raster.soam[n].tile = 0xff; - raster.soam[n].attr = 0xff; - raster.soam[n].x = 0xff; - raster.soam[n].tiledatalo = 0; - raster.soam[n].tiledatahi = 0; - } - - for(uint tile : range(32)) { // 0-255 - uint nametable = chr_load(0x2000 | (status.vaddr & 0x0fff)); - uint tileaddr = status.bg_addr + (nametable << 4) + (scrolly() & 7); - raster_pixel(); - tick(); - - raster_pixel(); - tick(); - - uint attribute = chr_load(0x23c0 | (status.vaddr & 0x0fc0) | ((scrolly() >> 5) << 3) | (scrollx() >> 5)); - if(scrolly() & 16) attribute >>= 4; - if(scrollx() & 16) attribute >>= 2; - raster_pixel(); - tick(); - - scrollx_increment(); - if(tile == 31) scrolly_increment(); - raster_pixel(); - raster_sprite(); - tick(); - - uint tiledatalo = chr_load(tileaddr + 0); - raster_pixel(); - tick(); - - raster_pixel(); - tick(); - - uint tiledatahi = chr_load(tileaddr + 8); - raster_pixel(); - tick(); - - raster_pixel(); - raster_sprite(); - tick(); - - raster.nametable = (raster.nametable << 8) | nametable; - raster.attribute = (raster.attribute << 2) | (attribute & 3); - raster.tiledatalo = (raster.tiledatalo << 8) | tiledatalo; - raster.tiledatahi = (raster.tiledatahi << 8) | tiledatahi; - } - - for(auto n : range(8)) raster.oam[n] = raster.soam[n]; - - for(uint sprite : range(8)) { //256-319 - uint nametable = chr_load(0x2000 | (status.vaddr & 0x0fff)); - tick(); - - if(raster_enable() && sprite == 0) status.vaddr = (status.vaddr & 0x7be0) | (status.taddr & 0x041f); //257 - tick(); - - uint attribute = chr_load(0x23c0 | (status.vaddr & 0x0fc0) | ((scrolly() >> 5) << 3) | (scrollx() >> 5)); - uint tileaddr = (sprite_height() == 8) - ? status.sprite_addr + raster.oam[sprite].tile * 16 - : ((raster.oam[sprite].tile & ~1) * 16) + ((raster.oam[sprite].tile & 1) * 0x1000); - tick(); - tick(); - - uint spritey = (status.ly - raster.oam[sprite].y) & (sprite_height() - 1); - if(raster.oam[sprite].attr & 0x80) spritey ^= (sprite_height() - 1); - tileaddr += spritey + (spritey & 8); - - raster.oam[sprite].tiledatalo = chr_load(tileaddr + 0); - tick(); - tick(); - - raster.oam[sprite].tiledatahi = chr_load(tileaddr + 8); - tick(); - tick(); - - if(raster_enable() && sprite == 6 && status.ly == 261) status.vaddr = status.taddr; //304 - } - - for(uint tile : range(2)) { //320-335 - uint nametable = chr_load(0x2000 | (status.vaddr & 0x0fff)); - uint tileaddr = status.bg_addr + (nametable << 4) + (scrolly() & 7); - tick(); - tick(); - - uint attribute = chr_load(0x23c0 | (status.vaddr & 0x0fc0) | ((scrolly() >> 5) << 3) | (scrollx() >> 5)); - if(scrolly() & 16) attribute >>= 4; - if(scrollx() & 16) attribute >>= 2; - tick(); - - scrollx_increment(); - tick(); - - uint tiledatalo = chr_load(tileaddr + 0); - tick(); - tick(); - - uint tiledatahi = chr_load(tileaddr + 8); - tick(); - tick(); - - raster.nametable = (raster.nametable << 8) | nametable; - raster.attribute = (raster.attribute << 2) | (attribute & 3); - raster.tiledatalo = (raster.tiledatalo << 8) | tiledatalo; - raster.tiledatahi = (raster.tiledatahi << 8) | tiledatahi; - } - - //336-339 - chr_load(0x2000 | (status.vaddr & 0x0fff)); - tick(); - bool skip = (raster_enable() && status.field == 1 && status.ly == 261); - tick(); - - chr_load(0x2000 | (status.vaddr & 0x0fff)); - tick(); - tick(); - - //340 - if(skip == false) tick(); - - return scanline(); + for(auto& n : buffer) n = 0; } } diff --git a/higan/fc/ppu/ppu.hpp b/higan/fc/ppu/ppu.hpp index 4aa6df4c..06c4f756 100644 --- a/higan/fc/ppu/ppu.hpp +++ b/higan/fc/ppu/ppu.hpp @@ -10,99 +10,109 @@ struct PPU : Thread { auto power() -> void; auto reset() -> void; + //memory.cpp + auto readCIRAM(uint11 addr) -> uint8; + auto writeCIRAM(uint11 addr, uint8 data) -> void; + + auto readCGRAM(uint5 addr) -> uint8; + auto writeCGRAM(uint5 addr, uint8 data) -> void; + auto readIO(uint16 addr) -> uint8; auto writeIO(uint16 addr, uint8 data) -> void; - auto ciram_read(uint16 addr) -> uint8; - auto ciram_write(uint16 addr, uint8 data) -> void; + //render.cpp + auto enable() const -> bool; + auto nametableAddress() const -> uint; + auto scrollX() const -> uint; + auto scrollY() const -> uint; - auto cgram_read(uint16 addr) -> uint8; - auto cgram_write(uint16 addr, uint8 data) -> void; + auto loadCHR(uint16 addr) -> uint8; - auto raster_enable() const -> bool; - auto nametable_addr() const -> uint; - auto scrollx() const -> uint; - auto scrolly() const -> uint; - auto sprite_height() const -> uint; + auto scrollX_increment() -> void; + auto scrollY_increment() -> void; - auto chr_load(uint16 addr) -> uint8; - - auto scrollx_increment() -> void; - auto scrolly_increment() -> void; - - auto raster_pixel() -> void; - auto raster_sprite() -> void; - auto raster_scanline() -> void; + auto renderPixel() -> void; + auto renderSprite() -> void; + auto renderScanline() -> void; + //serialization.cpp auto serialize(serializer&) -> void; - struct Status { + struct Registers { + //internal uint8 mdr; bool field; uint lx; uint ly; - uint8 bus_data; + uint8 busData; - bool address_latch; + bool addressLatch; uint15 vaddr; uint15 taddr; uint8 xaddr; - bool nmi_hold; - bool nmi_flag; + bool nmiHold; + bool nmiFlag; //$2000 - bool nmi_enable; - bool master_select; - bool sprite_size; - uint bg_addr; - uint sprite_addr; - uint vram_increment; + bool nmiEnable; + bool masterSelect; + uint spriteHeight; + uint bgAddress; + uint spriteAddress; + uint vramIncrement; //$2001 uint3 emphasis; - bool sprite_enable; - bool bg_enable; - bool sprite_edge_enable; - bool bg_edge_enable; + bool spriteEnable; + bool bgEnable; + bool spriteEdgeEnable; + bool bgEdgeEnable; bool grayscale; //$2002 - bool sprite_zero_hit; - bool sprite_overflow; + bool spriteZeroHit; + bool spriteOverflow; //$2003 - uint8 oam_addr; - } status; + uint8 oamAddress; + } r; - struct Raster { + struct OAM { + //serialization.cpp + auto serialize(serializer&) -> void; + + uint8 id = 64; + uint8 y = 0xff; + uint8 tile = 0xff; + uint8 attr = 0xff; + uint8 x = 0xff; + + uint8 tiledataLo = 0; + uint8 tiledataHi = 0; + }; + + struct Latches { uint16 nametable; uint16 attribute; - uint16 tiledatalo; - uint16 tiledatahi; + uint16 tiledataLo; + uint16 tiledataHi; - uint oam_iterator; - uint oam_counter; + uint oamIterator; + uint oamCounter; - struct OAM { - uint8 id; - uint8 y; - uint8 tile; - uint8 attr; - uint8 x; + OAM oam[8]; //primary + OAM soam[8]; //secondary + } l; - uint8 tiledatalo; - uint8 tiledatahi; - } oam[8], soam[8]; - } raster; - - uint32 buffer[256 * 262]; uint8 ciram[2048]; uint8 cgram[32]; uint8 oam[256]; + + uint32 buffer[256 * 262]; }; extern PPU ppu; diff --git a/higan/fc/ppu/render.cpp b/higan/fc/ppu/render.cpp new file mode 100644 index 00000000..3458b760 --- /dev/null +++ b/higan/fc/ppu/render.cpp @@ -0,0 +1,250 @@ +//vaddr = 0yyy VHYY YYYX XXXX +//yyy = fine Yscroll (y:d0-d2) +//V = V nametable (y:d8) +//H = H nametable (x:d8) +//YYYYY = Y nametable (y:d3-d7) +//XXXXX = X nametable (x:d3-d7) + +auto PPU::enable() const -> bool { + return r.bgEnable || r.spriteEnable; +} + +auto PPU::nametableAddress() const -> uint { + return 0x2000 + (r.vaddr & 0x0c00); +} + +auto PPU::scrollX() const -> uint { + return ((r.vaddr & 0x1f) << 3) | r.xaddr; +} + +auto PPU::scrollY() const -> uint { + return (((r.vaddr >> 5) & 0x1f) << 3) | ((r.vaddr >> 12) & 7); +} + +// + +auto PPU::loadCHR(uint16 addr) -> uint8 { + if(!enable()) return 0x00; + return cartridge.readCHR(addr); +} + +// + +auto PPU::scrollX_increment() -> void { + if(!enable()) return; + r.vaddr = (r.vaddr & 0x7fe0) | ((r.vaddr + 0x0001) & 0x001f); + if((r.vaddr & 0x001f) == 0x0000) { + r.vaddr ^= 0x0400; + } +} + +auto PPU::scrollY_increment() -> void { + if(!enable()) return; + r.vaddr = (r.vaddr & 0x0fff) | ((r.vaddr + 0x1000) & 0x7000); + if((r.vaddr & 0x7000) == 0x0000) { + r.vaddr = (r.vaddr & 0x7c1f) | ((r.vaddr + 0x0020) & 0x03e0); + if((r.vaddr & 0x03e0) == 0x03c0) { //0x03c0 == 30 << 5; 30 * 8 = 240 + r.vaddr &= 0x7c1f; + r.vaddr ^= 0x0800; + } + } +} + +// + +auto PPU::renderPixel() -> void { + uint32* output = buffer + r.ly * 256; + + uint mask = 0x8000 >> (r.xaddr + (r.lx & 7)); + uint palette = 0; + uint objectPalette = 0; + bool objectPriority = 0; + palette |= (l.tiledataLo & mask) ? 1 : 0; + palette |= (l.tiledataHi & mask) ? 2 : 0; + if(palette) { + uint attr = l.attribute; + if(mask >= 256) attr >>= 2; + palette |= (attr & 3) << 2; + } + + if(!r.bgEnable) palette = 0; + if(!r.bgEdgeEnable && r.lx < 8) palette = 0; + + if(r.spriteEnable) + for(int sprite = 7; sprite >= 0; sprite--) { + if(!r.spriteEdgeEnable && r.lx < 8) continue; + if(l.oam[sprite].id == 64) continue; + + uint spriteX = r.lx - l.oam[sprite].x; + if(spriteX >= 8) continue; + + if(l.oam[sprite].attr & 0x40) spriteX ^= 7; + uint mask = 0x80 >> spriteX; + uint spritePalette = 0; + spritePalette |= (l.oam[sprite].tiledataLo & mask) ? 1 : 0; + spritePalette |= (l.oam[sprite].tiledataHi & mask) ? 2 : 0; + if(spritePalette == 0) continue; + + if(l.oam[sprite].id == 0 && palette && r.lx != 255) r.spriteZeroHit = 1; + spritePalette |= (l.oam[sprite].attr & 3) << 2; + + objectPriority = l.oam[sprite].attr & 0x20; + objectPalette = 16 + spritePalette; + } + + if(objectPalette) { + if(palette == 0 || objectPriority == 0) palette = objectPalette; + } + + if(!enable()) palette = 0; + output[r.lx] = r.emphasis << 6 | readCGRAM(palette); +} + +auto PPU::renderSprite() -> void { + if(!enable()) return; + + uint n = l.oamIterator++; + int ly = (r.ly == 261 ? -1 : r.ly); + uint y = ly - oam[(n * 4) + 0]; + + if(y >= r.spriteHeight) return; + if(l.oamCounter == 8) { + r.spriteOverflow = 1; + return; + } + + auto& o = l.soam[l.oamCounter++]; + o.id = n; + o.y = oam[(n * 4) + 0]; + o.tile = oam[(n * 4) + 1]; + o.attr = oam[(n * 4) + 2]; + o.x = oam[(n * 4) + 3]; +} + +auto PPU::renderScanline() -> void { + if(r.ly >= 240 && r.ly <= 260) { + for(auto x : range(341)) tick(); + return scanline(); + } + + l.oamIterator = 0; + l.oamCounter = 0; + + for(auto n : range(8)) l.soam[n] = {}; + + for(uint tile : range(32)) { // 0-255 + uint nametable = loadCHR(0x2000 | (r.vaddr & 0x0fff)); + uint tileaddr = r.bgAddress + (nametable << 4) + (scrollY() & 7); + renderPixel(); + tick(); + + renderPixel(); + tick(); + + uint attribute = loadCHR(0x23c0 | (r.vaddr & 0x0fc0) | ((scrollY() >> 5) << 3) | (scrollX() >> 5)); + if(scrollY() & 16) attribute >>= 4; + if(scrollX() & 16) attribute >>= 2; + renderPixel(); + tick(); + + scrollX_increment(); + if(tile == 31) scrollY_increment(); + renderPixel(); + renderSprite(); + tick(); + + uint tiledataLo = loadCHR(tileaddr + 0); + renderPixel(); + tick(); + + renderPixel(); + tick(); + + uint tiledataHi = loadCHR(tileaddr + 8); + renderPixel(); + tick(); + + renderPixel(); + renderSprite(); + tick(); + + l.nametable = l.nametable << 8 | nametable; + l.attribute = l.attribute << 2 | (attribute & 3); + l.tiledataLo = l.tiledataLo << 8 | tiledataLo; + l.tiledataHi = l.tiledataHi << 8 | tiledataHi; + } + + for(auto n : range(8)) l.oam[n] = l.soam[n]; + + for(uint sprite : range(8)) { //256-319 + uint nametable = loadCHR(0x2000 | (r.vaddr & 0x0fff)); + tick(); + + if(enable() && sprite == 0) r.vaddr = (r.vaddr & 0x7be0) | (r.taddr & 0x041f); //257 + tick(); + + uint attribute = loadCHR(0x23c0 | (r.vaddr & 0x0fc0) | ((scrollY() >> 5) << 3) | (scrollX() >> 5)); + uint tileaddr = (r.spriteHeight == 8) + ? r.spriteAddress + l.oam[sprite].tile * 16 + : ((l.oam[sprite].tile & ~1) * 16) + ((l.oam[sprite].tile & 1) * 0x1000); + tick(); + tick(); + + uint spriteY = (r.ly - l.oam[sprite].y) & (r.spriteHeight - 1); + if(l.oam[sprite].attr & 0x80) spriteY ^= (r.spriteHeight - 1); + tileaddr += spriteY + (spriteY & 8); + + l.oam[sprite].tiledataLo = loadCHR(tileaddr + 0); + tick(); + tick(); + + l.oam[sprite].tiledataHi = loadCHR(tileaddr + 8); + tick(); + tick(); + + if(enable() && sprite == 6 && r.ly == 261) r.vaddr = r.taddr; //304 + } + + for(uint tile : range(2)) { //320-335 + uint nametable = loadCHR(0x2000 | (r.vaddr & 0x0fff)); + uint tileaddr = r.bgAddress + (nametable << 4) + (scrollY() & 7); + tick(); + tick(); + + uint attribute = loadCHR(0x23c0 | (r.vaddr & 0x0fc0) | ((scrollY() >> 5) << 3) | (scrollX() >> 5)); + if(scrollY() & 16) attribute >>= 4; + if(scrollX() & 16) attribute >>= 2; + tick(); + + scrollX_increment(); + tick(); + + uint tiledataLo = loadCHR(tileaddr + 0); + tick(); + tick(); + + uint tiledataHi = loadCHR(tileaddr + 8); + tick(); + tick(); + + l.nametable = l.nametable << 8 | nametable; + l.attribute = l.attribute << 2 | (attribute & 3); + l.tiledataLo = l.tiledataLo << 8 | tiledataLo; + l.tiledataHi = l.tiledataHi << 8 | tiledataHi; + } + + //336-339 + loadCHR(0x2000 | (r.vaddr & 0x0fff)); + tick(); + bool skip = enable() && r.field == 1 && r.ly == 261; + tick(); + + loadCHR(0x2000 | (r.vaddr & 0x0fff)); + tick(); + tick(); + + //340 + if(!skip) tick(); + + return scanline(); +} diff --git a/higan/fc/ppu/serialization.cpp b/higan/fc/ppu/serialization.cpp index 63123d8f..7d7e38a9 100644 --- a/higan/fc/ppu/serialization.cpp +++ b/higan/fc/ppu/serialization.cpp @@ -1,74 +1,67 @@ auto PPU::serialize(serializer& s) -> void { Thread::serialize(s); - s.integer(status.mdr); + s.integer(r.mdr); - s.integer(status.field); - s.integer(status.lx); - s.integer(status.ly); + s.integer(r.field); + s.integer(r.lx); + s.integer(r.ly); - s.integer(status.bus_data); + s.integer(r.busData); - s.integer(status.address_latch); + s.integer(r.addressLatch); - s.integer(status.vaddr); - s.integer(status.taddr); - s.integer(status.xaddr); + s.integer(r.vaddr); + s.integer(r.taddr); + s.integer(r.xaddr); - s.integer(status.nmi_hold); - s.integer(status.nmi_flag); + s.integer(r.nmiHold); + s.integer(r.nmiFlag); - s.integer(status.nmi_enable); - s.integer(status.master_select); - s.integer(status.sprite_size); - s.integer(status.bg_addr); - s.integer(status.sprite_addr); - s.integer(status.vram_increment); + s.integer(r.nmiEnable); + s.integer(r.masterSelect); + s.integer(r.spriteHeight); + s.integer(r.bgAddress); + s.integer(r.spriteAddress); + s.integer(r.vramIncrement); - s.integer(status.emphasis); - s.integer(status.sprite_enable); - s.integer(status.bg_enable); - s.integer(status.sprite_edge_enable); - s.integer(status.bg_edge_enable); - s.integer(status.grayscale); + s.integer(r.emphasis); + s.integer(r.spriteEnable); + s.integer(r.bgEnable); + s.integer(r.spriteEdgeEnable); + s.integer(r.bgEdgeEnable); + s.integer(r.grayscale); - s.integer(status.sprite_zero_hit); - s.integer(status.sprite_overflow); + s.integer(r.spriteZeroHit); + s.integer(r.spriteOverflow); - s.integer(status.oam_addr); + s.integer(r.oamAddress); - s.integer(raster.nametable); - s.integer(raster.attribute); - s.integer(raster.tiledatalo); - s.integer(raster.tiledatahi); + s.integer(l.nametable); + s.integer(l.attribute); + s.integer(l.tiledataLo); + s.integer(l.tiledataHi); - s.integer(raster.oam_iterator); - s.integer(raster.oam_counter); + s.integer(l.oamIterator); + s.integer(l.oamCounter); - for(auto n : range(8)) { - s.integer(raster.oam[n].id); - s.integer(raster.oam[n].y); - s.integer(raster.oam[n].tile); - s.integer(raster.oam[n].attr); - s.integer(raster.oam[n].x); + for(auto& o : l.oam) o.serialize(s); + for(auto& o : l.soam) o.serialize(s); - s.integer(raster.oam[n].tiledatalo); - s.integer(raster.oam[n].tiledatahi); - } - - for(auto n : range(8)) { - s.integer(raster.soam[n].id); - s.integer(raster.soam[n].y); - s.integer(raster.soam[n].tile); - s.integer(raster.soam[n].attr); - s.integer(raster.soam[n].x); - - s.integer(raster.soam[n].tiledatalo); - s.integer(raster.soam[n].tiledatahi); - } - - s.array(buffer); s.array(ciram); s.array(cgram); s.array(oam); + + s.array(buffer); +} + +auto PPU::OAM::serialize(serializer& s) -> void { + s.integer(id); + s.integer(y); + s.integer(tile); + s.integer(attr); + s.integer(x); + + s.integer(tiledataLo); + s.integer(tiledataHi); } diff --git a/higan/sfc/coprocessor/msu1/msu1.cpp b/higan/sfc/coprocessor/msu1/msu1.cpp index 7c5b58d6..78de1445 100644 --- a/higan/sfc/coprocessor/msu1/msu1.cpp +++ b/higan/sfc/coprocessor/msu1/msu1.cpp @@ -11,34 +11,30 @@ auto MSU1::Enter() -> void { } auto MSU1::main() -> void { - int16 left = 0, right = 0; + double left = 0.0; + double right = 0.0; if(mmio.audioPlay) { - if(audioFile.open()) { - if(audioFile.end()) { + if(audioFile) { + if(audioFile->end()) { if(!mmio.audioRepeat) { mmio.audioPlay = false; - audioFile.seek(mmio.audioPlayOffset = 8); + audioFile->seek(mmio.audioPlayOffset = 8); } else { - audioFile.seek(mmio.audioPlayOffset = mmio.audioLoopOffset); + audioFile->seek(mmio.audioPlayOffset = mmio.audioLoopOffset); } } else { mmio.audioPlayOffset += 4; - left = audioFile.readl(2); - right = audioFile.readl(2); + left = (double)audioFile->readl(2) / 32768.0 * (double)mmio.audioVolume / 255.0; + right = (double)audioFile->readl(2) / 32768.0 * (double)mmio.audioVolume / 255.0; + if(dsp.mute()) left = 0, right = 0; } } else { mmio.audioPlay = false; } } - int lchannel = (double)left * (double)mmio.audioVolume / 255.0; - int rchannel = (double)right * (double)mmio.audioVolume / 255.0; - left = sclamp<16>(lchannel); - right = sclamp<16>(rchannel); - if(dsp.mute()) left = 0, right = 0; - - stream->sample(left / 32768.0, right / 32768.0); + stream->sample(left, right); step(1); synchronizeCPU(); } @@ -50,8 +46,8 @@ auto MSU1::load() -> void { } auto MSU1::unload() -> void { - if(dataFile.open()) dataFile.close(); - if(audioFile.open()) audioFile.close(); + dataFile.reset(); + audioFile.reset(); } auto MSU1::power() -> void { @@ -73,28 +69,28 @@ auto MSU1::reset() -> void { mmio.audioResumeTrack = ~0; //no resume mmio.audioResumeOffset = 0; - mmio.dataBusy = false; - mmio.audioBusy = false; - mmio.audioRepeat = false; - mmio.audioPlay = false; mmio.audioError = false; + mmio.audioPlay = false; + mmio.audioRepeat = false; + mmio.audioBusy = false; + mmio.dataBusy = false; dataOpen(); audioOpen(); } auto MSU1::dataOpen() -> void { - if(dataFile.open()) dataFile.close(); + dataFile.reset(); auto document = BML::unserialize(cartridge.information.manifest.cartridge); string name = document["board/msu1/rom/name"].text(); if(!name) name = "msu1.rom"; - if(dataFile.open({interface->path(ID::SuperFamicom), name}, file::mode::read)) { - dataFile.seek(mmio.dataReadOffset); + if(dataFile = interface->open(ID::SuperFamicom, name, File::Read)) { + dataFile->seek(mmio.dataReadOffset); } } auto MSU1::audioOpen() -> void { - if(audioFile.open()) audioFile.close(); + audioFile.reset(); auto document = BML::unserialize(cartridge.information.manifest.cartridge); string name = {"track-", mmio.audioTrack, ".pcm"}; for(auto track : document.find("board/msu1/track")) { @@ -102,18 +98,18 @@ auto MSU1::audioOpen() -> void { name = track["name"].text(); break; } - if(audioFile.open({interface->path(ID::SuperFamicom), name}, file::mode::read)) { - if(audioFile.size() >= 8) { - uint32 header = audioFile.readm(4); + if(audioFile = interface->open(ID::SuperFamicom, name, File::Read)) { + if(audioFile->size() >= 8) { + uint32 header = audioFile->readm(4); if(header == 0x4d535531) { //"MSU1" - mmio.audioLoopOffset = 8 + audioFile.readl(4) * 4; - if(mmio.audioLoopOffset > audioFile.size()) mmio.audioLoopOffset = 8; + mmio.audioLoopOffset = 8 + audioFile->readl(4) * 4; + if(mmio.audioLoopOffset > audioFile->size()) mmio.audioLoopOffset = 8; mmio.audioError = false; - audioFile.seek(mmio.audioPlayOffset); + audioFile->seek(mmio.audioPlayOffset); return; } } - audioFile.close(); + audioFile.reset(); } mmio.audioError = true; } @@ -125,18 +121,19 @@ auto MSU1::read(uint24 addr, uint8) -> uint8 { switch(addr) { case 0x2000: return ( - mmio.dataBusy << 7 - | mmio.audioBusy << 6 - | mmio.audioRepeat << 5 - | mmio.audioPlay << 4 + Revision << 0 | mmio.audioError << 3 - | Revision << 0 + | mmio.audioPlay << 4 + | mmio.audioRepeat << 5 + | mmio.audioBusy << 6 + | mmio.dataBusy << 7 ); case 0x2001: if(mmio.dataBusy) return 0x00; - if(dataFile.end()) return 0x00; + if(!dataFile) return 0x00; + if(dataFile->end()) return 0x00; mmio.dataReadOffset++; - return dataFile.read(); + return dataFile->read(); case 0x2002: return 'S'; case 0x2003: return '-'; case 0x2004: return 'M'; @@ -156,7 +153,7 @@ auto MSU1::write(uint24 addr, uint8 data) -> void { case 0x2002: mmio.dataSeekOffset.byte(2) = data; break; case 0x2003: mmio.dataSeekOffset.byte(3) = data; mmio.dataReadOffset = mmio.dataSeekOffset; - dataOpen(); + if(dataFile) dataFile->seek(mmio.dataReadOffset); break; case 0x2004: mmio.audioTrack.byte(0) = data; break; case 0x2005: mmio.audioTrack.byte(1) = data; @@ -174,9 +171,9 @@ auto MSU1::write(uint24 addr, uint8 data) -> void { case 0x2007: if(mmio.audioBusy) break; if(mmio.audioError) break; - bool audioResume = data.bit(2); - mmio.audioRepeat = data.bit(1); mmio.audioPlay = data.bit(0); + mmio.audioRepeat = data.bit(1); + bool audioResume = data.bit(2); if(!mmio.audioPlay && audioResume) { mmio.audioResumeTrack = mmio.audioTrack; mmio.audioResumeOffset = mmio.audioPlayOffset; diff --git a/higan/sfc/coprocessor/msu1/msu1.hpp b/higan/sfc/coprocessor/msu1/msu1.hpp index 899a0309..0d5e61ba 100644 --- a/higan/sfc/coprocessor/msu1/msu1.hpp +++ b/higan/sfc/coprocessor/msu1/msu1.hpp @@ -18,16 +18,16 @@ struct MSU1 : Cothread { auto serialize(serializer&) -> void; private: - file dataFile; - file audioFile; + vfs::shared::file dataFile; + vfs::shared::file audioFile; enum Flag : uint { - DataBusy = 0x80, - AudioBusy = 0x40, - AudioRepeating = 0x20, - AudioPlaying = 0x10, + Revision = 0x02, //max: 0x07 AudioError = 0x08, - Revision = 0x02, + AudioPlaying = 0x10, + AudioRepeating = 0x20, + AudioBusy = 0x40, + DataBusy = 0x80, }; struct MMIO { @@ -43,11 +43,11 @@ private: uint32 audioResumeTrack; uint32 audioResumeOffset; - bool dataBusy; - bool audioBusy; - bool audioRepeat; - bool audioPlay; bool audioError; + bool audioPlay; + bool audioRepeat; + bool audioBusy; + bool dataBusy; } mmio; }; diff --git a/higan/sfc/coprocessor/msu1/serialization.cpp b/higan/sfc/coprocessor/msu1/serialization.cpp index 767a8d46..1c4e852b 100644 --- a/higan/sfc/coprocessor/msu1/serialization.cpp +++ b/higan/sfc/coprocessor/msu1/serialization.cpp @@ -13,11 +13,11 @@ auto MSU1::serialize(serializer& s) -> void { s.integer(mmio.audioResumeTrack); s.integer(mmio.audioResumeOffset); - s.integer(mmio.dataBusy); - s.integer(mmio.audioBusy); - s.integer(mmio.audioRepeat); - s.integer(mmio.audioPlay); s.integer(mmio.audioError); + s.integer(mmio.audioPlay); + s.integer(mmio.audioRepeat); + s.integer(mmio.audioBusy); + s.integer(mmio.dataBusy); dataOpen(); audioOpen(); diff --git a/higan/profile/Famicom.sys/manifest.bml b/higan/systems/Famicom.sys/manifest.bml similarity index 100% rename from higan/profile/Famicom.sys/manifest.bml rename to higan/systems/Famicom.sys/manifest.bml diff --git a/higan/profile/Game Boy Advance.sys/manifest.bml b/higan/systems/Game Boy Advance.sys/manifest.bml similarity index 100% rename from higan/profile/Game Boy Advance.sys/manifest.bml rename to higan/systems/Game Boy Advance.sys/manifest.bml diff --git a/higan/profile/Game Boy Color.sys/boot.rom b/higan/systems/Game Boy Color.sys/boot.rom similarity index 100% rename from higan/profile/Game Boy Color.sys/boot.rom rename to higan/systems/Game Boy Color.sys/boot.rom diff --git a/higan/profile/Game Boy Color.sys/manifest.bml b/higan/systems/Game Boy Color.sys/manifest.bml similarity index 100% rename from higan/profile/Game Boy Color.sys/manifest.bml rename to higan/systems/Game Boy Color.sys/manifest.bml diff --git a/higan/profile/Game Boy.sys/boot.rom b/higan/systems/Game Boy.sys/boot.rom similarity index 100% rename from higan/profile/Game Boy.sys/boot.rom rename to higan/systems/Game Boy.sys/boot.rom diff --git a/higan/profile/Game Boy.sys/manifest.bml b/higan/systems/Game Boy.sys/manifest.bml similarity index 100% rename from higan/profile/Game Boy.sys/manifest.bml rename to higan/systems/Game Boy.sys/manifest.bml diff --git a/higan/profile/Super Famicom.sys/ipl.rom b/higan/systems/Super Famicom.sys/ipl.rom similarity index 100% rename from higan/profile/Super Famicom.sys/ipl.rom rename to higan/systems/Super Famicom.sys/ipl.rom diff --git a/higan/profile/Super Famicom.sys/manifest.bml b/higan/systems/Super Famicom.sys/manifest.bml similarity index 100% rename from higan/profile/Super Famicom.sys/manifest.bml rename to higan/systems/Super Famicom.sys/manifest.bml diff --git a/higan/profile/WonderSwan Color.sys/manifest.bml b/higan/systems/WonderSwan Color.sys/manifest.bml similarity index 100% rename from higan/profile/WonderSwan Color.sys/manifest.bml rename to higan/systems/WonderSwan Color.sys/manifest.bml diff --git a/higan/profile/WonderSwan.sys/manifest.bml b/higan/systems/WonderSwan.sys/manifest.bml similarity index 100% rename from higan/profile/WonderSwan.sys/manifest.bml rename to higan/systems/WonderSwan.sys/manifest.bml diff --git a/higan/target-tomoko/GNUmakefile b/higan/target-tomoko/GNUmakefile index ae24d83d..fbc238c7 100644 --- a/higan/target-tomoko/GNUmakefile +++ b/higan/target-tomoko/GNUmakefile @@ -83,7 +83,7 @@ else ifeq ($(platform),macosx) mkdir -p ~/Emulation/System/ cp -R out/$(name).app /Applications/$(name).app cp data/cheats.bml ~/Library/Application\ Support/$(name)/ - cp -R profile/* ~/Library/Application\ Support/$(name)/ + cp -R systems/* ~/Library/Application\ Support/$(name)/ else ifneq ($(filter $(platform),linux bsd),) mkdir -p $(prefix)/bin/ mkdir -p $(prefix)/share/icons/ @@ -92,7 +92,7 @@ else ifneq ($(filter $(platform),linux bsd),) cp data/$(name).desktop $(prefix)/share/applications/$(name).desktop cp data/$(name).png $(prefix)/share/icons/$(name).png cp data/cheats.bml $(prefix)/share/$(name)/cheats.bml - cp -R profile/* $(prefix)/share/$(name)/ + cp -R systems/* $(prefix)/share/$(name)/ endif uninstall: diff --git a/higan/target-tomoko/input/input.cpp b/higan/target-tomoko/input/input.cpp index 3db68bb9..3a0d7412 100644 --- a/higan/target-tomoko/input/input.cpp +++ b/higan/target-tomoko/input/input.cpp @@ -146,8 +146,8 @@ InputManager::InputManager() { inputManager = this; for(auto& emulator : program->emulators) { - emulators.append(InputEmulator()); - auto& inputEmulator = emulators.right(); + auto& inputEmulator = emulators(emulators.size()); + inputEmulator.interface = emulator; inputEmulator.name = emulator->information.name; for(auto& port : emulator->ports) { @@ -157,16 +157,13 @@ InputManager::InputManager() { auto& inputDevice = inputPort.devices(device.id); inputDevice.name = device.name; for(auto& input : device.inputs) { - auto inputMapping = new InputMapping; - inputDevice.mappings.append(inputMapping); + auto& inputMapping = inputDevice.mappings(inputDevice.mappings.size()); + inputMapping.name = input.name; + inputMapping.type = input.type; - inputMapping->name = input.name; - inputMapping->link = &input; - input.userData = (uintptr)inputMapping; - - inputMapping->path = string{inputEmulator.name, "/", inputPort.name, "/", inputDevice.name, "/", inputMapping->name}.replace(" ", ""); - inputMapping->assignment = settings(inputMapping->path).text(); - inputMapping->bind(); + inputMapping.path = string{inputEmulator.name, "/", inputPort.name, "/", inputDevice.name, "/", inputMapping.name}.replace(" ", ""); + inputMapping.assignment = settings(inputMapping.path).text(); + inputMapping.bind(); } } } @@ -175,12 +172,24 @@ InputManager::InputManager() { appendHotkeys(); } +//Emulator::Interface::inputPoll() needs to call into InputManager::InputEmulator +//this function is calling during Program::loadMedium() to link the two together +auto InputManager::bind(Emulator::Interface* interface) -> void { + this->emulator = nullptr; + for(auto& emulator : emulators) { + if(emulator.interface == interface) { + this->emulator = &emulator; + } + } + assert(this->emulator != nullptr); +} + auto InputManager::bind() -> void { for(auto& emulator : emulators) { for(auto& port : emulator.ports) { for(auto& device : port.devices) { for(auto& mapping : device.mappings) { - mapping->bind(); + mapping.bind(); } } } @@ -194,13 +203,13 @@ auto InputManager::bind() -> void { auto InputManager::poll() -> void { auto devices = input->poll(); bool changed = devices.size() != this->devices.size(); - if(changed == false) { + if(!changed) { for(auto n : range(devices)) { changed = devices[n] != this->devices[n]; if(changed) break; } } - if(changed == true) { + if(changed) { this->devices = devices; bind(); } diff --git a/higan/target-tomoko/input/input.hpp b/higan/target-tomoko/input/input.hpp index f3551d95..e65f3e7c 100644 --- a/higan/target-tomoko/input/input.hpp +++ b/higan/target-tomoko/input/input.hpp @@ -5,17 +5,17 @@ struct InputMapping { auto rumble(bool enable) -> void; auto unbind() -> void; - auto isDigital() const -> bool { return !link || link->type == 0; } - auto isAnalog() const -> bool { return link && link->type == 1; } - auto isRumble() const -> bool { return link && link->type == 2; } + auto isDigital() const -> bool { return type == 0; } + auto isAnalog() const -> bool { return type == 1; } + auto isRumble() const -> bool { return type == 2; } auto assignmentName() -> string; auto deviceName() -> string; string path; //configuration file key path string name; //input name (human readable) + uint type = 0; string assignment = "None"; - Emulator::Interface::Device::Input* link = nullptr; shared_pointer device; uint group = 0; uint input = 0; @@ -31,7 +31,7 @@ struct InputHotkey : InputMapping { struct InputDevice { string name; - vector mappings; //pointers used so that addresses do not change when arrays are resized + vector mappings; }; struct InputPort { @@ -40,12 +40,14 @@ struct InputPort { }; struct InputEmulator { + Emulator::Interface* interface = nullptr; string name; vector ports; }; struct InputManager { InputManager(); + auto bind(Emulator::Interface*) -> void; auto bind() -> void; auto poll() -> void; auto onChange(shared_pointer device, uint group, uint input, int16_t oldValue, int16_t newValue) -> void; @@ -60,6 +62,8 @@ struct InputManager { vector> devices; vector emulators; vector hotkeys; + + InputEmulator* emulator = nullptr; //points to InputEmulator that represents the currently active emulator }; extern unique_pointer inputManager; diff --git a/higan/target-tomoko/presentation/presentation.cpp b/higan/target-tomoko/presentation/presentation.cpp index 34be99b4..e921631e 100644 --- a/higan/target-tomoko/presentation/presentation.cpp +++ b/higan/target-tomoko/presentation/presentation.cpp @@ -131,6 +131,12 @@ Presentation::Presentation() { statusBar.setFont(Font().setBold()); statusBar.setVisible(settings["UserInterface/ShowStatusBar"].boolean()); + viewport.setDroppable().onDrop([&](auto locations) { + if(!directory::exists(locations(0))) return; + program->mediumQueue.append(locations(0)); + program->loadMedium(); + }); + onClose([&] { program->quit(); }); setTitle({"higan v", Emulator::Version}); diff --git a/higan/target-tomoko/program/interface.cpp b/higan/target-tomoko/program/interface.cpp index 65a01d80..d2aa21ea 100644 --- a/higan/target-tomoko/program/interface.cpp +++ b/higan/target-tomoko/program/interface.cpp @@ -3,28 +3,39 @@ auto Program::path(uint id) -> string { } auto Program::open(uint id, string name, vfs::file::mode mode, bool required) -> vfs::shared::file { - if(auto result = vfs::fs::file::open({path(id), name}, mode)) return result; - if(name == "manifest.bml") { - if(auto manifest = execute("icarus", "--manifest", path(id))) { - return vfs::memory::file::open(manifest.output.data(), manifest.output.size()); + if(name == "manifest.bml" && !path(id).endsWith(".sys/")) { + if(!file::exists({path(id), name}) || settings["Library/IgnoreManifests"].boolean()) { + if(auto manifest = execute("icarus", "--manifest", path(id))) { + return vfs::memory::file::open(manifest.output.data(), manifest.output.size()); + } } } + + if(auto result = vfs::fs::file::open({path(id), name}, mode)) return result; + if(required) { MessageDialog() .setTitle({"Error"}) .setText({"Error: missing required file:\n\n", path(id), name}) .error(); } + return {}; } auto Program::load(uint id, string name, string type) -> maybe { - string location = BrowserDialog() - .setTitle({"Load ", name}) - .setPath({settings["Library/Location"].text(), name}) - .setFilters({string{name, "|*.", type}, "All|*.*"}) - .openFolder(); - if(!directory::exists(location)) return nothing; + string location; + if(mediumQueue) { + location = mediumQueue.takeLeft().transform("\\", "/"); + if(!location.endsWith("/")) location.append("/"); + } else { + location = BrowserDialog() + .setTitle({"Load ", name}) + .setPath({settings["Library/Location"].text(), name}) + .setFilters({string{name, "|*.", type}, "All|*.*"}) + .openFolder(); + } + if(!directory::exists(location)) return mediumQueue.reset(), nothing; uint pathID = mediumPaths.size(); mediumPaths.append(location); @@ -81,18 +92,16 @@ auto Program::audioSample(const double* samples, uint channels) -> void { auto Program::inputPoll(uint port, uint device, uint input) -> int16 { if(presentation->focused() || settings["Input/FocusLoss/AllowInput"].boolean()) { - auto userData = emulator->ports[port].devices[device].inputs[input].userData; - auto mapping = (InputMapping*)userData; - if(mapping) return mapping->poll(); + auto& mapping = inputManager->emulator->ports[port].devices[device].mappings[input]; + return mapping.poll(); } return 0; } auto Program::inputRumble(uint port, uint device, uint input, bool enable) -> void { if(presentation->focused() || settings["Input/FocusLoss/AllowInput"].boolean() || !enable) { - auto userData = emulator->ports[port].devices[device].inputs[input].userData; - auto mapping = (InputMapping*)userData; - if(mapping) return mapping->rumble(enable); + auto& mapping = inputManager->emulator->ports[port].devices[device].mappings[input]; + return mapping.rumble(enable); } } diff --git a/higan/target-tomoko/program/medium.cpp b/higan/target-tomoko/program/medium.cpp index 61ba41b4..3d8960af 100644 --- a/higan/target-tomoko/program/medium.cpp +++ b/higan/target-tomoko/program/medium.cpp @@ -1,10 +1,26 @@ +auto Program::loadMedium() -> void { + if(!mediumQueue) return; + + string location = mediumQueue.left(); + string type = suffixname(location).trimLeft(".", 1L); + + for(auto& emulator : emulators) { + for(auto& medium : emulator->media) { + if(medium.type != type) continue; + return loadMedium(*emulator, medium); + } + } + + mediumQueue.reset(); +} + auto Program::loadMedium(Emulator::Interface& interface, const Emulator::Interface::Medium& medium) -> void { unloadMedium(); mediumPaths.append(locate({medium.name, ".sys/"})); Emulator::audio.reset(2, audio->get(Audio::Frequency).get(44100)); - emulator = &interface; + inputManager->bind(emulator = &interface); connectDevices(); if(!emulator->load(medium.id)) { emulator = nullptr; diff --git a/higan/target-tomoko/program/program.cpp b/higan/target-tomoko/program/program.cpp index 98e579bc..c473f7a6 100644 --- a/higan/target-tomoko/program/program.cpp +++ b/higan/target-tomoko/program/program.cpp @@ -56,8 +56,11 @@ Program::Program(lstring args) { for(auto& argument : args) { if(argument == "--fullscreen") { presentation->toggleFullScreen(); + } else if(directory::exists(argument)) { + mediumQueue.append(argument); } } + loadMedium(); } auto Program::main() -> void { diff --git a/higan/target-tomoko/program/program.hpp b/higan/target-tomoko/program/program.hpp index 70b3448e..2800bd4b 100644 --- a/higan/target-tomoko/program/program.hpp +++ b/higan/target-tomoko/program/program.hpp @@ -16,6 +16,7 @@ struct Program : Emulator::Interface::Bind { auto notify(string text) -> void override; //medium.cpp + auto loadMedium() -> void; auto loadMedium(Emulator::Interface& interface, const Emulator::Interface::Medium& medium) -> void; auto unloadMedium() -> void; @@ -39,7 +40,8 @@ struct Program : Emulator::Interface::Bind { vector emulators; - vector mediumPaths; + vector mediumQueue; //for command-line and drag-and-drop loading + vector mediumPaths; //for keeping track of loaded folder locations string statusText; string statusMessage; diff --git a/higan/target-tomoko/settings/input.cpp b/higan/target-tomoko/settings/input.cpp index 56d960a9..038de2c1 100644 --- a/higan/target-tomoko/settings/input.cpp +++ b/higan/target-tomoko/settings/input.cpp @@ -24,13 +24,13 @@ InputSettings::InputSettings(TabFrame* parent) : TabFrameItem(parent) { assignMouse3.setVisible(false).onActivate([&] { assignMouseInput(2); }); resetButton.setText("Reset").onActivate([&] { if(MessageDialog("Are you sure you want to erase all mappings for this device?").setParent(*settingsManager).question() == "Yes") { - for(auto& mapping : activeDevice().mappings) mapping->unbind(); + for(auto& mapping : activeDevice().mappings) mapping.unbind(); refreshMappings(); } }); eraseButton.setText("Erase").onActivate([&] { if(auto mapping = mappingList.selected()) { - activeDevice().mappings[mapping.offset()]->unbind(); + activeDevice().mappings[mapping.offset()].unbind(); refreshMappings(); } }); @@ -45,13 +45,13 @@ auto InputSettings::updateControls() -> void { assignMouse3.setVisible(false); if(auto mapping = mappingList.selected()) { - auto input = activeDevice().mappings[mapping.offset()]; + auto& input = activeDevice().mappings[mapping.offset()]; - if(input->isDigital()) { + if(input.isDigital()) { assignMouse1.setVisible().setText("Mouse Left"); assignMouse2.setVisible().setText("Mouse Middle"); assignMouse3.setVisible().setText("Mouse Right"); - } else if(input->isAnalog()) { + } else if(input.isAnalog()) { assignMouse1.setVisible().setText("Mouse X-axis"); assignMouse2.setVisible().setText("Mouse Y-axis"); } @@ -99,7 +99,7 @@ auto InputSettings::reloadMappings() -> void { ); for(auto& mapping : activeDevice().mappings) { mappingList.append(TableViewItem() - .append(TableViewCell().setText(mapping->name)) + .append(TableViewCell().setText(mapping.name)) .append(TableViewCell()) .append(TableViewCell()) ); @@ -110,8 +110,8 @@ auto InputSettings::reloadMappings() -> void { auto InputSettings::refreshMappings() -> void { uint position = 0; for(auto& mapping : activeDevice().mappings) { - mappingList.item(position).cell(1).setText(mapping->assignmentName()); - mappingList.item(position).cell(2).setText(mapping->deviceName()); + mappingList.item(position).cell(1).setText(mapping.assignmentName()); + mappingList.item(position).cell(2).setText(mapping.deviceName()); position++; } mappingList.resizeColumns(); @@ -121,7 +121,7 @@ auto InputSettings::assignMapping() -> void { inputManager->poll(); //clear any pending events first if(auto mapping = mappingList.selected()) { - activeMapping = activeDevice().mappings[mapping->offset()]; + activeMapping = &activeDevice().mappings[mapping.offset()]; settingsManager->layout.setEnabled(false); settingsManager->statusBar.setText({"Press a key or button to map [", activeMapping->name, "] ..."}); } @@ -130,7 +130,7 @@ auto InputSettings::assignMapping() -> void { auto InputSettings::assignMouseInput(uint id) -> void { if(auto mouse = inputManager->findMouse()) { if(auto mapping = mappingList.selected()) { - activeMapping = activeDevice().mappings[mapping->offset()]; + activeMapping = &activeDevice().mappings[mapping.offset()]; if(activeMapping->isDigital()) { return inputEvent(mouse, HID::Mouse::GroupID::Button, id, 0, 1, true);