From d3413db04a8b32807ae872cfa937e280baa792f0 Mon Sep 17 00:00:00 2001 From: Tim Allen Date: Sat, 19 Mar 2016 18:35:25 +1100 Subject: [PATCH] Update to v097r27 release. byuu says: Absolutely major improvements to the WS/C emulation today. Changelog: (all WS/C related) - fixed channel 3 sweep pitch adjustment - fixed channel 3 sweep value sign extension - removed errant channel 5 speed setting (not what's really going on) - fixed sign extension on channel 5 samples - improved DAC mixing of all five audio channels - fixed r26 regression with PPU timing loop - fixed sprite windowing behavior (sprite attribute flag is window mode; not window enable) - added per-scanline register latching to the PPU - IRQs should terminate HLT even when the IRQ enable register bits are clear - fixed PALMONO reads - added blur emulation - added color emulation (based on GBA, so it heavily desaturates colors; not entirely correct, but it helps a lot) - no longer decimating audio to 24KHz; running at full 3.072MHz through the windowed sinc filter [1] - cleaned up PPU portRead / portWrite functions significantly - emulated a weird quirk as mentioned by trap15 regarding timer frequency writes enabling said timers [2] - emulated LCD_CTRL sleep bit; screen can now be disabled (always draws black in this case for now) - improved OAM caching; but it's still disabled because it causes huge amounts of sprite glitches (unsure why) - fixed rendering of sprites that wrap around the screen edges back to the top/left of the display - emulated keypad interrupts - icarus: detect orientation bit in game header - higan: use orientation setting in manifest to set default screen rotation [1] the 24KHz -> 3.072MHz sound change is huge. Sound is substantially improved over the previous WIPs. It does come at a pretty major speed penalty, though. This is the highest frequency of any system in higan running through an incredibly (amazing, yet) demanding sinc resampler. Frame rate dropped from around 240fps to 150fps with the sinc filter on. If you choose a different audio filter, you'll get most of that speed back, but audio will sound worse again. [2] we aren't sure if this is correct hardware behavior or not. It seems to very slightly help Magical Drop, but not much. The blur emulation is brutal. It's absolutely required for Riviera's translucency simulation of selected menu items, but it causes serious headaches due to the WS's ~75hz refresh rate running on ~60hz monitors without vsync. It's probably best to leave it off and just deal with the awful flickering on Riviera's menu options. Overall, WS/C emulation is starting to get quite usable indeed. Couple of major bugs that I'd really like to get fixed before releasing it, though. But they're getting harder and harder to fix ... Major Bugs: - Final Fantasy battle background music is absent. Sound effects still work. Very weird. - Final Fantasy IV scrolling during airship flight opening sequence is horribly broken. Scrolls one screen at a time. - Magical Drop flickers like crazy in-game. Basically unplayable like this. - Star Hearts character names don't appear in the smaller dialog box that pops up. Minor Bugs: - Occasional flickering during Riviera opening scenes. - One-frame flicker of Leda's sprite at the start of the first stage. --- .gitignore | 4 +- higan/emulator/emulator.hpp | 2 +- higan/ws/apu/apu.cpp | 11 +- higan/ws/apu/apu.hpp | 10 +- higan/ws/apu/channel3.cpp | 2 +- higan/ws/apu/channel5.cpp | 13 +- higan/ws/cartridge/cartridge.cpp | 1 + higan/ws/cartridge/cartridge.hpp | 1 + higan/ws/cpu/interrupt.cpp | 6 +- higan/ws/interface/interface.cpp | 8 +- higan/ws/interface/interface.hpp | 2 + higan/ws/ppu/io.cpp | 254 +++++++++++++------------------ higan/ws/ppu/ppu.cpp | 93 +++++++---- higan/ws/ppu/ppu.hpp | 68 ++++++--- higan/ws/ppu/render-color.cpp | 39 +++-- higan/ws/ppu/render-mono.cpp | 39 +++-- higan/ws/ppu/render-sprite.cpp | 22 ++- higan/ws/ppu/video.cpp | 43 ++++-- higan/ws/ppu/video.hpp | 1 + higan/ws/system/system.cpp | 9 +- icarus/core/wonderswan-color.cpp | 14 +- icarus/core/wonderswan.cpp | 14 +- icarus/heuristics/wonderswan.cpp | 14 +- 23 files changed, 361 insertions(+), 309 deletions(-) diff --git a/.gitignore b/.gitignore index 31714c94..3ca1d794 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,2 @@ -higan/profile/WonderSwan.sys/internal.rom -higan/profile/WonderSwan Color.sys/internal.rom +higan/profile/WonderSwan.sys/internal.ram +higan/profile/WonderSwan Color.sys/internal.ram diff --git a/higan/emulator/emulator.hpp b/higan/emulator/emulator.hpp index 1718d867..96fe9c21 100644 --- a/higan/emulator/emulator.hpp +++ b/higan/emulator/emulator.hpp @@ -6,7 +6,7 @@ using namespace nall; namespace Emulator { static const string Name = "higan"; - static const string Version = "097.26"; + static const string Version = "097.27"; static const string Author = "byuu"; static const string License = "GPLv3"; static const string Website = "http://byuu.org/"; diff --git a/higan/ws/apu/apu.cpp b/higan/ws/apu/apu.cpp index 6e135616..98169266 100644 --- a/higan/ws/apu/apu.cpp +++ b/higan/ws/apu/apu.cpp @@ -22,9 +22,8 @@ auto APU::main() -> void { channel3.run(); channel4.run(); channel5.run(); - if(s.clock.bits(0,12) == 0) channel3.sweep(); - if(s.clock.bits(0, 6) == 0) dacRun(); - s.clock++; + dacRun(); + if(++s.sweepClock == 0) channel3.sweep(); step(1); } @@ -40,7 +39,7 @@ auto APU::dacRun() -> void { if(channel2.r.enable) left += channel2.o.left; if(channel3.r.enable) left += channel3.o.left; if(channel4.r.enable) left += channel4.o.left; - if(channel5.r.enable) left += (int11)channel5.o.left >> 3; + if(channel5.r.enable) left += channel5.o.left; left = sclamp<16>(left << 5); int right = 0; @@ -48,7 +47,7 @@ auto APU::dacRun() -> void { if(channel2.r.enable) right += channel2.o.right; if(channel3.r.enable) right += channel3.o.right; if(channel4.r.enable) right += channel4.o.right; - if(channel5.r.enable) right += (int11)channel5.o.right >> 3; + if(channel5.r.enable) right += channel5.o.right; right = sclamp<16>(right << 5); if(!r.headphoneEnable) { @@ -73,7 +72,7 @@ auto APU::power() -> void { bus.map(this, 0x006a, 0x006b); bus.map(this, 0x0080, 0x0095); - s.clock = 0; + s.sweepClock = 0; r.waveBase = 0; r.speakerEnable = 0; r.speakerShift = 0; diff --git a/higan/ws/apu/apu.hpp b/higan/ws/apu/apu.hpp index db124068..d9bdc752 100644 --- a/higan/ws/apu/apu.hpp +++ b/higan/ws/apu/apu.hpp @@ -11,7 +11,7 @@ struct APU : Thread, IO { auto portWrite(uint16 addr, uint8 data) -> void; struct State { - uint13 clock; + uint13 sweepClock; } s; struct DMA { @@ -146,7 +146,7 @@ struct APU : Thread, IO { uint4 volumeRight; //$008c SND_SWEEP_VALUE - uint8 sweepValue; + int8 sweepValue; //$008d SND_SWEEP_TIME uint5 sweepTime; @@ -197,13 +197,13 @@ struct APU : Thread, IO { auto run() -> void; struct Output { - uint11 left; - uint11 right; + int11 left; + int11 right; } o; struct State { uint clock; - uint8 data; + int8 data; } s; struct Registers { diff --git a/higan/ws/apu/channel3.cpp b/higan/ws/apu/channel3.cpp index 2ee6211a..cb89bc63 100644 --- a/higan/ws/apu/channel3.cpp +++ b/higan/ws/apu/channel3.cpp @@ -1,7 +1,7 @@ auto APU::Channel3::sweep() -> void { if(r.sweep && --s.sweepCounter < 0) { s.sweepCounter = r.sweepTime; - r.pitch += r.sweepTime; + r.pitch += r.sweepValue; } } diff --git a/higan/ws/apu/channel5.cpp b/higan/ws/apu/channel5.cpp index 50cf2009..77a5c180 100644 --- a/higan/ws/apu/channel5.cpp +++ b/higan/ws/apu/channel5.cpp @@ -1,17 +1,12 @@ auto APU::Channel5::run() -> void { - if(r.speed <= 5 && s.clock++ < 1536) return; //2000hz - if(r.speed == 6 && s.clock++ < 2048) return; //1500hz - if(r.speed == 7 && s.clock++ < 3072) return; //1000hz - s.clock = 0; - - uint11 output = s.data; + int11 output = (int8)s.data; switch(r.scale) { case 0: output <<= 3 - r.volume; break; case 1: output <<= 3 - r.volume; output |= -0x100 << (3 - r.volume); break; case 2: output <<= 3 - r.volume; break; - case 3: output <<= r.volume; break; + case 3: output <<= 3; break; } - o.left = r.leftEnable ? output : (uint11)0; - o.right = r.rightEnable ? output : (uint11)0; + o.left = r.leftEnable ? output : (int11)0; + o.right = r.rightEnable ? output : (int11)0; } diff --git a/higan/ws/cartridge/cartridge.cpp b/higan/ws/cartridge/cartridge.cpp index 6be7d5dd..3cfb56c8 100644 --- a/higan/ws/cartridge/cartridge.cpp +++ b/higan/ws/cartridge/cartridge.cpp @@ -40,6 +40,7 @@ auto Cartridge::load() -> void { } information.title = document["information/title"].text(); + information.orientation = document["information/orientation"].text() == "vertical"; information.sha256 = Hash::SHA256(rom.data, rom.size).digest(); } diff --git a/higan/ws/cartridge/cartridge.hpp b/higan/ws/cartridge/cartridge.hpp index 3a194ca5..c25b9b0c 100644 --- a/higan/ws/cartridge/cartridge.hpp +++ b/higan/ws/cartridge/cartridge.hpp @@ -31,6 +31,7 @@ struct Cartridge : IO { struct Information { string manifest; string title; + bool orientation; //0 = horizontal; 1 = vertical string sha256; } information; }; diff --git a/higan/ws/cpu/interrupt.cpp b/higan/ws/cpu/interrupt.cpp index 858fd274..f888452d 100644 --- a/higan/ws/cpu/interrupt.cpp +++ b/higan/ws/cpu/interrupt.cpp @@ -1,10 +1,12 @@ auto CPU::poll() -> void { - if(!V30MZ::r.f.i || !state.poll) return; + if(!state.poll) return; for(int n = 7; n >= 0; n--) { if(!r.interruptEnable.bit(n)) continue; if(!r.interruptStatus.bit(n)) continue; - return interrupt(r.interruptBase + n); + state.halt = false; + if(V30MZ::r.f.i) interrupt(r.interruptBase + n); + return; } } diff --git a/higan/ws/interface/interface.cpp b/higan/ws/interface/interface.cpp index 0c742be2..1e7cfc30 100644 --- a/higan/ws/interface/interface.cpp +++ b/higan/ws/interface/interface.cpp @@ -73,7 +73,7 @@ auto Interface::videoFrequency() -> double { } auto Interface::audioFrequency() -> double { - return 3072000.0 / 128.0; //24Khz + return 3072000.0; } auto Interface::loaded() -> bool { @@ -177,14 +177,20 @@ auto Interface::unserialize(serializer& s) -> bool { } auto Interface::cap(const string& name) -> bool { + if(name == "Blur Emulation") return true; + if(name == "Color Emulation") return true; return false; } auto Interface::get(const string& name) -> any { + if(name == "Blur Emulation") return settings.blurEmulation; + if(name == "Color Emulation") return settings.colorEmulation; return {}; } auto Interface::set(const string& name, const any& value) -> bool { + if(name == "Blur Emulation" && value.is()) return settings.blurEmulation = value.get(), true; + if(name == "Color Emulation" && value.is()) return settings.colorEmulation = value.get(), true; return false; } diff --git a/higan/ws/interface/interface.hpp b/higan/ws/interface/interface.hpp index 31e043ad..61edea33 100644 --- a/higan/ws/interface/interface.hpp +++ b/higan/ws/interface/interface.hpp @@ -56,6 +56,8 @@ private: }; struct Settings { + bool blurEmulation = true; + bool colorEmulation = true; }; extern Interface* interface; diff --git a/higan/ws/ppu/io.cpp b/higan/ws/ppu/io.cpp index 96f12825..a11e2f24 100644 --- a/higan/ws/ppu/io.cpp +++ b/higan/ws/ppu/io.cpp @@ -1,39 +1,29 @@ auto PPU::portRead(uint16 addr) -> uint8 { //DISP_CTRL - if(addr == 0x0000) { - return ( - r.screenTwoWindowEnable << 5 - | r.screenTwoWindowInvert << 4 - | r.spriteWindowEnable << 3 - | r.spriteEnable << 2 - | r.screenTwoEnable << 1 - | r.screenOneEnable << 0 - ); - } + if(addr == 0x0000) return ( + r.screenOneEnable << 0 + | r.screenTwoEnable << 1 + | r.spriteEnable << 2 + | r.spriteWindowEnable << 3 + | r.screenTwoWindowInvert << 4 + | r.screenTwoWindowEnable << 5 + ); //BACK_COLOR - if(addr == 0x0001) { - if(!system.depth()) { - return r.backColor.bits(0,2); - } else { - return r.backColor.bits(0,7); - } - } + if(addr == 0x0001) return ( + r.backColor.bits(0, !system.depth() ? 2 : 7) + ); //LINE_CUR - if(addr == 0x0002) return status.vclk; + if(addr == 0x0002) return s.vclk; //LINE_CMP if(addr == 0x0003) return r.lineCompare; //SPR_BASE - if(addr == 0x0004) { - if(!system.depth()) { - return r.spriteBase.bits(0,4); - } else { - return r.spriteBase.bits(0,5); - } - } + if(addr == 0x0004) return ( + r.spriteBase.bits(0, 4 + system.depth()) + ); //SPR_FIRST if(addr == 0x0005) return r.spriteFirst; @@ -42,13 +32,10 @@ auto PPU::portRead(uint16 addr) -> uint8 { if(addr == 0x0006) return r.spriteCount; //MAP_BASE - if(addr == 0x0007) { - if(!system.depth()) { - return r.screenTwoMapBase.bits(0,2) << 4 | r.screenOneMapBase.bits(0,2) << 0; - } else { - return r.screenTwoMapBase.bits(0,3) << 4 | r.screenOneMapBase.bits(0,3) << 0; - } - } + if(addr == 0x0007) return ( + r.screenOneMapBase.bits(0, 2 + system.depth()) << 0 + | r.screenTwoMapBase.bits(0, 2 + system.depth()) << 4 + ); //SCR2_WIN_X0 if(addr == 0x0008) return r.screenTwoWindowX0; @@ -87,19 +74,21 @@ auto PPU::portRead(uint16 addr) -> uint8 { if(addr == 0x0013) return r.scrollTwoY; //LCD_CTRL - if(addr == 0x0014) return r.control; + if(addr == 0x0014) return ( + r.lcdEnable << 0 + | r.lcdContrast << 1 + | r.lcdUnknown << 2 + ); //LCD_ICON - if(addr == 0x0015) { - return ( - r.iconAux3 << 5 - | r.iconAux2 << 4 - | r.iconAux1 << 3 - | r.iconHorizontal << 2 - | r.iconVertical << 1 - | r.iconSleep << 0 - ); - } + if(addr == 0x0015) return ( + r.iconSleep << 0 + | r.iconVertical << 1 + | r.iconHorizontal << 2 + | r.iconAux1 << 3 + | r.iconAux2 << 4 + | r.iconAux3 << 5 + ); //LCD_VTOTAL if(addr == 0x0016) return r.vtotal; @@ -108,20 +97,16 @@ auto PPU::portRead(uint16 addr) -> uint8 { if(addr == 0x0017) return r.vblank; //PALMONO_POOL - if(addr >= 0x001c && addr <= 0x001f) { - return ( - r.pool[addr.bits(1,0) * 2 + 1] << 4 - | r.pool[addr.bits(1,0) * 2 + 0] << 0 - ); - } + if(addr >= 0x001c && addr <= 0x001f) return ( + r.pool[addr.bits(0,1) * 2 + 0] << 0 + | r.pool[addr.bits(0,1) * 2 + 1] << 4 + ); //PALMONO - if(addr >= 0x0020 && addr <= 0x003f) { - return ( - r.palette[addr.bits(3,1)].color[addr.bit(0) * 2 + 1] << 4 - | r.palette[addr.bits(3,1)].color[addr.bit(0) * 2 + 0] << 0 - ); - } + if(addr >= 0x0020 && addr <= 0x003f) return ( + r.palette[addr.bits(1,4)].color[addr.bit(0) * 2 + 0] << 0 + | r.palette[addr.bits(1,4)].color[addr.bit(0) * 2 + 1] << 4 + ); //TMR_CTRL if(addr == 0x00a2) return ( @@ -153,165 +138,113 @@ auto PPU::portRead(uint16 addr) -> uint8 { auto PPU::portWrite(uint16 addr, uint8 data) -> void { //DISP_CTRL if(addr == 0x0000) { - r.screenTwoWindowEnable = data.bit(5); - r.screenTwoWindowInvert = data.bit(4); - r.spriteWindowEnable = data.bit(3); - r.spriteEnable = data.bit(2); - r.screenTwoEnable = data.bit(1); r.screenOneEnable = data.bit(0); - return; + r.screenTwoEnable = data.bit(1); + r.spriteEnable = data.bit(2); + r.spriteWindowEnable = data.bit(3); + r.screenTwoWindowInvert = data.bit(4); + r.screenTwoWindowEnable = data.bit(5); } //BACK_COLOR - if(addr == 0x0001) { - r.backColor = data; - return; - } + if(addr == 0x0001) r.backColor = data; //LINE_CMP - if(addr == 0x0003) { - r.lineCompare = data; - return; - } + if(addr == 0x0003) r.lineCompare = data; //SPR_BASE - if(addr == 0x0004) { - r.spriteBase = data.bits(0,5); - return; - } + if(addr == 0x0004) r.spriteBase = data.bits(0,5); //SPR_FIRST - if(addr == 0x0005) { - r.spriteFirst = data.bits(6,0); - return; - } + if(addr == 0x0005) r.spriteFirst = data.bits(6,0); //SPR_COUNT - if(addr == 0x0006) { - r.spriteCount = data; //0 - 128 - return; - } + if(addr == 0x0006) r.spriteCount = data; //MAP_BASE if(addr == 0x0007) { r.screenOneMapBase = data.bits(0,3); r.screenTwoMapBase = data.bits(4,7); - return; } //SCR2_WIN_X0 - if(addr == 0x0008) { - r.screenTwoWindowX0 = data; - return; - } + if(addr == 0x0008) r.screenTwoWindowX0 = data; //SCR2_WIN_Y0 - if(addr == 0x0009) { - r.screenTwoWindowY0 = data; - return; - } + if(addr == 0x0009) r.screenTwoWindowY0 = data; //SCR2_WIN_X1 - if(addr == 0x000a) { - r.screenTwoWindowX1 = data; - return; - } + if(addr == 0x000a) r.screenTwoWindowX1 = data; //SCR2_WIN_Y1 - if(addr == 0x000b) { - r.screenTwoWindowY1 = data; - return; - } + if(addr == 0x000b) r.screenTwoWindowY1 = data; //SPR_WIN_X0 - if(addr == 0x000c) { - r.spriteWindowX0 = data; - return; - } + if(addr == 0x000c) r.spriteWindowX0 = data; //SPR_WIN_Y0 - if(addr == 0x000d) { - r.spriteWindowY0 = data; - return; - } + if(addr == 0x000d) r.spriteWindowY0 = data; //SPR_WIN_X1 - if(addr == 0x000e) { - r.spriteWindowX1 = data; - return; - } + if(addr == 0x000e) r.spriteWindowX1 = data; //SPR_WIN_Y1 - if(addr == 0x000f) { - r.spriteWindowY1 = data; - return; - } + if(addr == 0x000f) r.spriteWindowY1 = data; //SCR1_X - if(addr == 0x0010) { - r.scrollOneX = data; - return; - } + if(addr == 0x0010) r.scrollOneX = data; //SCR1_Y - if(addr == 0x0011) { - r.scrollOneY = data; - return; - } + if(addr == 0x0011) r.scrollOneY = data; //SCR2_X - if(addr == 0x0012) { - r.scrollTwoX = data; - return; - } + if(addr == 0x0012) r.scrollTwoX = data; //SCR2_Y - if(addr == 0x0013) { - r.scrollTwoY = data; - return; - } + if(addr == 0x0013) r.scrollTwoY = data; //LCD_CTRL if(addr == 0x0014) { - r.control = data; - return; + r.lcdEnable = data.bit (0); + r.lcdContrast = data.bit (1); + r.lcdUnknown = data.bits(2,7); + + if(system.model() == Model::WonderSwanColor) { + r.lcdUnknown &= 0b111100; + } + + if(system.model() == Model::SwanCrystal) { + r.lcdContrast = 0; + r.lcdUnknown = 0; + } } //LCD_ICON if(addr == 0x0015) { - r.iconAux3 = data.bit(5); - r.iconAux2 = data.bit(4); - r.iconAux1 = data.bit(3); - r.iconHorizontal = data.bit(2); - r.iconVertical = data.bit(1); r.iconSleep = data.bit(0); - return; + r.iconVertical = data.bit(1); + r.iconHorizontal = data.bit(2); + r.iconAux1 = data.bit(3); + r.iconAux2 = data.bit(4); + r.iconAux3 = data.bit(5); } //LCD_VTOTAL - if(addr == 0x0016) { - r.vtotal = data; - return; - } + if(addr == 0x0016) r.vtotal = data; //LCD_VBLANK - if(addr == 0x0017) { - r.vblank = data; - return; - } + if(addr == 0x0017) r.vblank = data; //PALMONO_POOL if(addr >= 0x001c && addr <= 0x001f) { - r.pool[addr.bits(1,0) * 2 + 1] = data.bits(7,4); - r.pool[addr.bits(1,0) * 2 + 0] = data.bits(3,0); - return; + r.pool[addr.bits(0,1) * 2 + 0] = data.bits(0,3); + r.pool[addr.bits(0,1) * 2 + 1] = data.bits(4,7); } //PALMONO if(addr >= 0x0020 && addr <= 0x003f) { - r.palette[addr.bits(4,1)].color[addr.bit(0) * 2 + 1] = data.bits(6,4); - r.palette[addr.bits(4,1)].color[addr.bit(0) * 2 + 0] = data.bits(2,0); - return; + r.palette[addr.bits(1,4)].color[addr.bit(0) * 2 + 0] = data.bits(0,2); + r.palette[addr.bits(1,4)].color[addr.bit(0) * 2 + 1] = data.bits(4,6); } //TMR_CTRL @@ -320,6 +253,9 @@ auto PPU::portWrite(uint16 addr, uint8 data) -> void { r.htimerRepeat = data.bit(1); r.vtimerEnable = data.bit(2); r.vtimerRepeat = data.bit(3); + + if(r.htimerEnable) r.htimerCounter = 0; + if(r.vtimerEnable) r.vtimerCounter = 0; } //HTMR_FREQ @@ -329,4 +265,18 @@ auto PPU::portWrite(uint16 addr, uint8 data) -> void { //VTMR_FREQ if(addr == 0x00a6) r.vtimerFrequency.byte(0) = data; if(addr == 0x00a7) r.vtimerFrequency.byte(1) = data; + + //todo: is this correct? + if(addr == 0x00a5) { + r.htimerEnable = true; + r.htimerRepeat = true; + r.htimerCounter = 0; + } + + //todo: is this correct? + if(addr == 0x00a7) { + r.vtimerEnable = true; + r.vtimerRepeat = true; + r.vtimerCounter = 0; + } } diff --git a/higan/ws/ppu/ppu.cpp b/higan/ws/ppu/ppu.cpp index c5490bd3..3a01bb5d 100644 --- a/higan/ws/ppu/ppu.cpp +++ b/higan/ws/ppu/ppu.cpp @@ -14,11 +14,14 @@ auto PPU::Enter() -> void { } auto PPU::main() -> void { - if(status.vclk < 144) { + if(s.vclk < 144) { + latchRegisters(); renderSpriteFetch(); renderSpriteDecode(); - for(auto x : range(256)) { - if(!system.color()) { + for(auto x : range(224)) { + if(!r.lcdEnable) { + pixel = {Pixel::Source::Back, 0x000}; + } else if(!system.color()) { renderMonoBack(); renderMonoScreenOne(); renderMonoScreenTwo(); @@ -29,7 +32,7 @@ auto PPU::main() -> void { renderColorScreenTwo(); renderColorSprite(); } - output[status.vclk * 224 + status.hclk] = pixel.color; + output[s.vclk * 224 + s.hclk] = pixel.color; step(1); } step(32); @@ -50,12 +53,12 @@ auto PPU::main() -> void { } auto PPU::scanline() -> void { - status.hclk = 0; - status.vclk++; - if(status.vclk == r.lineCompare) { + s.hclk = 0; + s.vclk++; + if(s.vclk == r.lineCompare) { cpu.raise(CPU::Interrupt::LineCompare); } - if(status.vclk == 144) { + if(s.vclk == 144) { cpu.raise(CPU::Interrupt::Vblank); if(r.vtimerEnable && r.vtimerCounter < r.vtimerFrequency) { if(++r.vtimerCounter == r.vtimerFrequency) { @@ -68,22 +71,53 @@ auto PPU::scanline() -> void { } } } - if(status.vclk == 159) frame(); + if(s.vclk == 159) frame(); } auto PPU::frame() -> void { - status.vclk = 0; + s.field = !s.field; + s.vclk = 0; video.refresh(); scheduler.exit(Scheduler::Event::Frame); } auto PPU::step(uint clocks) -> void { - status.hclk += clocks; + s.hclk += clocks; clock += clocks; if(clock >= 0 && !scheduler.synchronizing()) co_switch(cpu.thread); } +auto PPU::latchRegisters() -> void { + l.backColor = r.backColor; + + l.screenOneEnable = r.screenOneEnable; + l.screenOneMapBase = r.screenOneMapBase; + l.scrollOneX = r.scrollOneX; + l.scrollOneY = r.scrollOneY; + + l.screenTwoEnable = r.screenTwoEnable; + l.screenTwoMapBase = r.screenTwoMapBase; + l.scrollTwoX = r.scrollTwoX; + l.scrollTwoY = r.scrollTwoY; + l.screenTwoWindowEnable = r.screenTwoWindowEnable; + l.screenTwoWindowInvert = r.screenTwoWindowInvert; + l.screenTwoWindowX0 = r.screenTwoWindowX0; + l.screenTwoWindowY0 = r.screenTwoWindowY0; + l.screenTwoWindowX1 = r.screenTwoWindowX1; + l.screenTwoWindowY1 = r.screenTwoWindowY1; + + l.spriteEnable = r.spriteEnable; + l.spriteBase = r.spriteBase; + l.spriteFirst = r.spriteFirst; + l.spriteCount = r.spriteCount; + l.spriteWindowEnable = r.spriteWindowEnable; + l.spriteWindowX0 = r.spriteWindowX0; + l.spriteWindowY0 = r.spriteWindowY0; + l.spriteWindowX1 = r.spriteWindowX1; + l.spriteWindowY1 = r.spriteWindowY1; +} + auto PPU::power() -> void { create(PPU::Enter, 3'072'000); @@ -93,24 +127,25 @@ auto PPU::power() -> void { bus.map(this, 0x00a4, 0x00ab); for(auto& n : output) n = 0; - for(auto& n : oam) n = 0; + for(auto& n : oam[0]) n = 0; + for(auto& n : oam[1]) n = 0; - status.vclk = 0; - status.hclk = 0; + s.vclk = 0; + s.hclk = 0; - r.screenTwoWindowEnable = 0; - r.screenTwoWindowInvert = 0; - r.spriteWindowEnable = 0; - r.spriteEnable = 0; - r.screenTwoEnable = 0; r.screenOneEnable = 0; + r.screenTwoEnable = 0; + r.spriteEnable = 0; + r.spriteWindowEnable = 0; + r.screenTwoWindowInvert = 0; + r.screenTwoWindowEnable = 0; r.backColor = 0; r.lineCompare = 0xff; r.spriteBase = 0; r.spriteFirst = 0; r.spriteCount = 0; - r.screenTwoMapBase = 0; r.screenOneMapBase = 0; + r.screenTwoMapBase = 0; r.screenTwoWindowX0 = 0; r.screenTwoWindowY0 = 0; r.screenTwoWindowX1 = 0; @@ -123,15 +158,19 @@ auto PPU::power() -> void { r.scrollOneY = 0; r.scrollTwoX = 0; r.scrollTwoY = 0; - r.control = 0; - r.iconAux3 = 0; - r.iconAux2 = 0; - r.iconAux1 = 0; - r.iconHorizontal = 0; - r.iconVertical = 0; + r.lcdEnable = 1; + r.lcdContrast = 0; + r.lcdUnknown = 0; r.iconSleep = 0; + r.iconVertical = 0; + r.iconHorizontal = 0; + r.iconAux1 = 0; + r.iconAux2 = 0; + r.iconAux3 = 0; r.vtotal = 158; r.vblank = 155; + for(auto& color : r.pool) color = 0; + for(auto& p : r.palette) for(auto& color : p.color) color = 0; r.htimerEnable = 0; r.htimerRepeat = 0; r.vtimerEnable = 0; @@ -140,8 +179,6 @@ auto PPU::power() -> void { r.vtimerFrequency = 0; r.htimerCounter = 0; r.vtimerCounter = 0; - for(auto& color : r.pool) color = 0; - for(auto& p : r.palette) for(auto& color : p.color) color = 0; video.power(); } diff --git a/higan/ws/ppu/ppu.hpp b/higan/ws/ppu/ppu.hpp index 58baab0d..89a8816c 100644 --- a/higan/ws/ppu/ppu.hpp +++ b/higan/ws/ppu/ppu.hpp @@ -6,6 +6,7 @@ struct PPU : Thread, IO { auto scanline() -> void; auto frame() -> void; auto step(uint clocks) -> void; + auto latchRegisters() -> void; auto power() -> void; //io.cpp @@ -34,12 +35,7 @@ struct PPU : Thread, IO { //state uint12 output[224 * 144]; - uint32 oam[128]; - - struct Status { - uint vclk; - uint hclk; - } status; + uint32 oam[2][128]; struct Sprite { uint8 x; @@ -59,14 +55,50 @@ struct PPU : Thread, IO { uint12 color; } pixel; - struct Registers { - //$0000 DISP_CTRL + struct State { + bool field; + uint vclk; + uint hclk; + } s; + + struct Latches { + uint8 backColor; + + uint1 screenOneEnable; + uint4 screenOneMapBase; + uint8 scrollOneX; + uint8 scrollOneY; + + uint1 screenTwoEnable; + uint4 screenTwoMapBase; + uint8 scrollTwoX; + uint8 scrollTwoY; uint1 screenTwoWindowEnable; uint1 screenTwoWindowInvert; - uint1 spriteWindowEnable; + uint8 screenTwoWindowX0; + uint8 screenTwoWindowY0; + uint8 screenTwoWindowX1; + uint8 screenTwoWindowY1; + uint1 spriteEnable; - uint1 screenTwoEnable; + uint6 spriteBase; + uint7 spriteFirst; + uint8 spriteCount; + uint1 spriteWindowEnable; + uint8 spriteWindowX0; + uint8 spriteWindowY0; + uint8 spriteWindowX1; + uint8 spriteWindowY1; + } l; + + struct Registers { + //$0000 DISP_CTRL uint1 screenOneEnable; + uint1 screenTwoEnable; + uint1 spriteEnable; + uint1 spriteWindowEnable; + uint1 screenTwoWindowInvert; + uint1 screenTwoWindowEnable; //$0001 BACK_COLOR uint8 backColor; @@ -84,8 +116,8 @@ struct PPU : Thread, IO { uint8 spriteCount; //0 - 128 //$0007 MAP_BASE - uint4 screenTwoMapBase; uint4 screenOneMapBase; + uint4 screenTwoMapBase; //$0008 SCR2_WIN_X0 uint8 screenTwoWindowX0; @@ -124,15 +156,17 @@ struct PPU : Thread, IO { uint8 scrollTwoY; //$0014 LCD_CTRL - uint8 control; + uint1 lcdEnable; + uint1 lcdContrast; //WSC only + uint6 lcdUnknown; //$0015 LCD_ICON - uint1 iconAux3; - uint1 iconAux2; - uint1 iconAux1; - uint1 iconHorizontal; - uint1 iconVertical; uint1 iconSleep; + uint1 iconVertical; + uint1 iconHorizontal; + uint1 iconAux1; + uint1 iconAux2; + uint1 iconAux3; //$0016 LCD_VTOTAL uint8 vtotal; diff --git a/higan/ws/ppu/render-color.cpp b/higan/ws/ppu/render-color.cpp index fae8b517..01a244a7 100644 --- a/higan/ws/ppu/render-color.cpp +++ b/higan/ws/ppu/render-color.cpp @@ -22,17 +22,17 @@ auto PPU::renderColorPalette(uint4 palette, uint4 index) -> uint12 { } auto PPU::renderColorBack() -> void { - uint12 color = iram.read(0xfe00 + (r.backColor << 1), Word); + uint12 color = iram.read(0xfe00 + (l.backColor << 1), Word); pixel = {Pixel::Source::Back, color}; } auto PPU::renderColorScreenOne() -> void { - if(!r.screenOneEnable) return; + if(!l.screenOneEnable) return; - uint8 scrollY = status.vclk + r.scrollOneY; - uint8 scrollX = status.hclk + r.scrollOneX; + uint8 scrollY = s.vclk + l.scrollOneY; + uint8 scrollX = s.hclk + l.scrollOneX; - uint16 tilemapOffset = r.screenOneMapBase << 11; + uint16 tilemapOffset = l.screenOneMapBase << 11; tilemapOffset += (scrollY >> 3) << 6; tilemapOffset += (scrollX >> 3) << 1; @@ -47,17 +47,17 @@ auto PPU::renderColorScreenOne() -> void { } auto PPU::renderColorScreenTwo() -> void { - if(!r.screenTwoEnable) return; + if(!l.screenTwoEnable) return; - bool windowInside = status.vclk >= r.screenTwoWindowY0 && status.vclk <= r.screenTwoWindowY1 - && status.hclk >= r.screenTwoWindowX0 && status.hclk <= r.screenTwoWindowX1; - windowInside ^= r.screenTwoWindowInvert; - if(r.screenTwoWindowEnable && !windowInside) return; + bool windowInside = s.vclk >= l.screenTwoWindowY0 && s.vclk <= l.screenTwoWindowY1 + && s.hclk >= l.screenTwoWindowX0 && s.hclk <= l.screenTwoWindowX1; + windowInside ^= l.screenTwoWindowInvert; + if(l.screenTwoWindowEnable && !windowInside) return; - uint8 scrollY = status.vclk + r.scrollTwoY; - uint8 scrollX = status.hclk + r.scrollTwoX; + uint8 scrollY = s.vclk + l.scrollTwoY; + uint8 scrollX = s.hclk + l.scrollTwoX; - uint16 tilemapOffset = r.screenTwoMapBase << 11; + uint16 tilemapOffset = l.screenTwoMapBase << 11; tilemapOffset += (scrollY >> 3) << 6; tilemapOffset += (scrollX >> 3) << 1; @@ -72,17 +72,16 @@ auto PPU::renderColorScreenTwo() -> void { } auto PPU::renderColorSprite() -> void { - if(!r.spriteEnable) return; + if(!l.spriteEnable) return; - bool windowInside = status.hclk >= r.spriteWindowX0 && status.hclk <= r.spriteWindowX1; + bool windowInside = s.hclk >= l.spriteWindowX0 && s.hclk <= l.spriteWindowX1; for(auto& sprite : sprites) { - if(r.spriteWindowEnable && sprite.window && !windowInside) continue; - if(status.hclk < sprite.x) continue; - if(status.hclk > sprite.x + 7) continue; + if(l.spriteWindowEnable && sprite.window == windowInside) continue; + if((uint8)(s.hclk - sprite.x) > 7) continue; uint16 tileOffset = 0x4000 + (sprite.tile << 5); - uint3 tileY = (uint8)(status.vclk - sprite.y) ^ (sprite.vflip ? 7 : 0); - uint3 tileX = (uint8)(status.hclk - sprite.x) ^ (sprite.hflip ? 7 : 0); + uint3 tileY = (uint8)(s.vclk - sprite.y) ^ (sprite.vflip ? 7 : 0); + uint3 tileX = (uint8)(s.hclk - sprite.x) ^ (sprite.hflip ? 7 : 0); uint4 tileColor = renderColorFetch(tileOffset, tileY, tileX); if(tileColor == 0) continue; if(!sprite.priority && pixel.source == Pixel::Source::ScreenTwo) continue; diff --git a/higan/ws/ppu/render-mono.cpp b/higan/ws/ppu/render-mono.cpp index 497319a1..303a5da8 100644 --- a/higan/ws/ppu/render-mono.cpp +++ b/higan/ws/ppu/render-mono.cpp @@ -22,17 +22,17 @@ auto PPU::renderMonoPalette(uint4 palette, uint2 index) -> uint12 { } auto PPU::renderMonoBack() -> void { - uint4 poolColor = 15 - r.pool[r.backColor.bits(0,2)]; + uint4 poolColor = 15 - r.pool[l.backColor.bits(0,2)]; pixel = {Pixel::Source::Back, poolColor << 0 | poolColor << 4 | poolColor << 8}; } auto PPU::renderMonoScreenOne() -> void { - if(!r.screenOneEnable) return; + if(!l.screenOneEnable) return; - uint8 scrollY = status.vclk + r.scrollOneY; - uint8 scrollX = status.hclk + r.scrollOneX; + uint8 scrollY = s.vclk + l.scrollOneY; + uint8 scrollX = s.hclk + l.scrollOneX; - uint14 tilemapOffset = r.screenOneMapBase.bits(0,2) << 11; + uint14 tilemapOffset = l.screenOneMapBase.bits(0,2) << 11; tilemapOffset += (scrollY >> 3) << 6; tilemapOffset += (scrollX >> 3) << 1; @@ -47,17 +47,17 @@ auto PPU::renderMonoScreenOne() -> void { } auto PPU::renderMonoScreenTwo() -> void { - if(!r.screenTwoEnable) return; + if(!l.screenTwoEnable) return; - bool windowInside = status.vclk >= r.screenTwoWindowY0 && status.vclk <= r.screenTwoWindowY1 - && status.hclk >= r.screenTwoWindowX0 && status.hclk <= r.screenTwoWindowX1; - windowInside ^= r.screenTwoWindowInvert; - if(r.screenTwoWindowEnable && !windowInside) return; + bool windowInside = s.vclk >= l.screenTwoWindowY0 && s.vclk <= l.screenTwoWindowY1 + && s.hclk >= l.screenTwoWindowX0 && s.hclk <= l.screenTwoWindowX1; + windowInside ^= l.screenTwoWindowInvert; + if(l.screenTwoWindowEnable && !windowInside) return; - uint8 scrollY = status.vclk + r.scrollTwoY; - uint8 scrollX = status.hclk + r.scrollTwoX; + uint8 scrollY = s.vclk + l.scrollTwoY; + uint8 scrollX = s.hclk + l.scrollTwoX; - uint14 tilemapOffset = r.screenTwoMapBase.bits(0,2) << 11; + uint14 tilemapOffset = l.screenTwoMapBase.bits(0,2) << 11; tilemapOffset += (scrollY >> 3) << 6; tilemapOffset += (scrollX >> 3) << 1; @@ -72,17 +72,16 @@ auto PPU::renderMonoScreenTwo() -> void { } auto PPU::renderMonoSprite() -> void { - if(!r.spriteEnable) return; + if(!l.spriteEnable) return; - bool windowInside = status.hclk >= r.spriteWindowX0 && status.hclk <= r.spriteWindowX1; + bool windowInside = s.hclk >= l.spriteWindowX0 && s.hclk <= l.spriteWindowX1; for(auto& sprite : sprites) { - if(r.spriteWindowEnable && sprite.window && !windowInside) continue; - if(status.hclk < sprite.x) continue; - if(status.hclk > sprite.x + 7) continue; + if(l.spriteWindowEnable && sprite.window == windowInside) continue; + if((uint8)(s.hclk - sprite.x) > 7) continue; uint14 tileOffset = 0x2000 + (sprite.tile << 4); - uint3 tileY = (uint8)(status.vclk - sprite.y) ^ (sprite.vflip ? 7 : 0); - uint3 tileX = (uint8)(status.hclk - sprite.x) ^ (sprite.hflip ? 7 : 0); + uint3 tileY = (uint8)(s.vclk - sprite.y) ^ (sprite.vflip ? 7 : 0); + uint3 tileX = (uint8)(s.hclk - sprite.x) ^ (sprite.hflip ? 7 : 0); uint2 tileColor = renderMonoFetch(tileOffset, tileY, tileX); if(sprite.palette.bit(2) && tileColor == 0) continue; if(!sprite.priority && pixel.source == Pixel::Source::ScreenTwo) continue; diff --git a/higan/ws/ppu/render-sprite.cpp b/higan/ws/ppu/render-sprite.cpp index f255bbcc..43b7eac5 100644 --- a/higan/ws/ppu/render-sprite.cpp +++ b/higan/ws/ppu/render-sprite.cpp @@ -1,34 +1,32 @@ auto PPU::renderSpriteFetch() -> void { - uint16 spriteBase = r.spriteBase.bits(0, 4 + system.depth()) << 9; + uint16 spriteBase = l.spriteBase.bits(0, 4 + system.depth()) << 9; for(auto spriteIndex : range(128)) { - oam[spriteIndex] = iram.read(spriteBase + (spriteIndex << 2), Long); + oam[s.field][spriteIndex] = iram.read(spriteBase + (spriteIndex << 2), Long); } } auto PPU::renderSpriteDecode() -> void { sprites.reset(); sprites.reserve(32); - if(!r.spriteEnable) return; + if(!l.spriteEnable) return; uint offset = 0; - bool windowInside = status.vclk >= r.spriteWindowY0 && status.vclk <= r.spriteWindowY1; - uint16 spriteBase = r.spriteBase.bits(0, 4 + system.depth()) << 9; - uint7 spriteIndex = r.spriteFirst; - uint8 spriteCount = min(128, (uint)r.spriteCount); + bool windowInside = s.vclk >= l.spriteWindowY0 && s.vclk <= l.spriteWindowY1; + uint7 spriteIndex = l.spriteFirst; + uint8 spriteCount = min(128, (uint)l.spriteCount); while(spriteCount--) { - uint32 attributes = oam[spriteIndex++]; + uint32 attributes = oam[s.field][spriteIndex++]; Sprite sprite; sprite.x = attributes.bits(24,31); - if(sprite.x > 224) continue; + if(sprite.x > 224 && sprite.x < 249) continue; sprite.y = attributes.bits(16,23); - if(status.vclk < sprite.y) continue; - if(status.vclk > sprite.y + 7) continue; + if((uint8)(s.vclk - sprite.y) > 7) continue; sprite.vflip = attributes.bit(15); sprite.hflip = attributes.bit(14); sprite.priority = attributes.bit(13); sprite.window = attributes.bit(12); - if(r.spriteWindowEnable && sprite.window && !windowInside) continue; + if(l.spriteWindowEnable && sprite.window == windowInside) continue; sprite.palette = 8 + attributes.bits(9,11); sprite.tile = attributes.bits(0,8); diff --git a/higan/ws/ppu/video.cpp b/higan/ws/ppu/video.cpp index 68e9b6d7..f014d21b 100644 --- a/higan/ws/ppu/video.cpp +++ b/higan/ws/ppu/video.cpp @@ -4,6 +4,7 @@ Video::Video() { output = new uint32[224 * 224]; paletteLiteral = new uint32[1 << 12]; paletteStandard = new uint32[1 << 12]; + paletteEmulation = new uint32[1 << 12]; } auto Video::power() -> void { @@ -12,18 +13,30 @@ auto Video::power() -> void { for(uint12 color : range(1 << 12)) { paletteLiteral[color] = color; - uint B = color.bits(0, 3); - uint G = color.bits(4, 7); - uint R = color.bits(8,11); + uint b = color.bits(0, 3); + uint g = color.bits(4, 7); + uint r = color.bits(8,11); - R = image::normalize(R, 4, 16); - G = image::normalize(G, 4, 16); - B = image::normalize(B, 4, 16); + uint R = image::normalize(r, 4, 16); + uint G = image::normalize(g, 4, 16); + uint B = image::normalize(b, 4, 16); paletteStandard[color] = interface->videoColor(R, G, B); + + //todo: this uses the Game Boy Advance color emulation algorithm + //need to determine proper color emulation for WonderSwan systems + R = (r * 26 + g * 4 + b * 2); + G = ( g * 24 + b * 8); + B = (r * 6 + g * 4 + b * 22); + R = image::normalize(min(480, R), 9, 16); + G = image::normalize(min(480, G), 9, 16); + B = image::normalize(min(480, B), 9, 16); + paletteEmulation[color] = interface->videoColor(R, G, B); } } auto Video::refresh() -> void { + auto& palette = settings.colorEmulation ? paletteEmulation : paletteStandard; + if(system.orientation() == 0) { for(uint y = 0; y < 224; y++) { auto target = output() + y * 224; @@ -33,8 +46,13 @@ auto Video::refresh() -> void { } auto source = ppu.output + (y - 40) * 224; for(uint x = 0; x < 224; x++) { - auto color = paletteStandard[*source++]; - *target++ = color; + auto color = palette[*source++]; + if(settings.blurEmulation) { + auto a = color, b = *target; + *target++ = (a + b - ((a ^ b) & 0x01010101)) >> 1; + } else { + *target++ = color; + } } } } @@ -47,8 +65,13 @@ auto Video::refresh() -> void { target += 40; for(uint x = 0; x < 144; x++) { auto source = ppu.output + x * 224 + (223 - y); - auto color = paletteStandard[*source]; - *target++ = color; + auto color = palette[*source]; + if(settings.blurEmulation) { + auto a = color, b = *target; + *target++ = (a + b - ((a ^ b) & 0x01010101)) >> 1; + } else { + *target++ = color; + } } } } diff --git a/higan/ws/ppu/video.hpp b/higan/ws/ppu/video.hpp index adb39ccb..f0a07c19 100644 --- a/higan/ws/ppu/video.hpp +++ b/higan/ws/ppu/video.hpp @@ -8,6 +8,7 @@ private: unique_pointer output; unique_pointer paletteLiteral; unique_pointer paletteStandard; + unique_pointer paletteEmulation; }; extern Video video; diff --git a/higan/ws/system/system.cpp b/higan/ws/system/system.cpp index dabfe54d..e8248ace 100644 --- a/higan/ws/system/system.cpp +++ b/higan/ws/system/system.cpp @@ -21,7 +21,6 @@ auto System::term() -> void { auto System::load(Model model) -> void { _model = model; - _orientation = 0; interface->loadRequest(ID::SystemManifest, "manifest.bml", true); auto document = BML::unserialize(information.manifest); @@ -37,6 +36,7 @@ auto System::load(Model model) -> void { cartridge.load(); _loaded = true; + _orientation = cartridge.information.orientation; } auto System::unload() -> void { @@ -85,6 +85,13 @@ auto System::run() -> void { keypad.start = interface->inputPoll(_orientation, 0, 10); keypad.rotate = interface->inputPoll(_orientation, 0, 11); + if(keypad.y1 || keypad.y2 || keypad.y3 || keypad.y4 + || keypad.x1 || keypad.x2 || keypad.x3 || keypad.x4 + || keypad.b || keypad.a || keypad.start + ) { + cpu.raise(CPU::Interrupt::Input); + } + if(!rotate && keypad.rotate) { _orientation = !_orientation; } diff --git a/icarus/core/wonderswan-color.cpp b/icarus/core/wonderswan-color.cpp index 1745121f..63a7076f 100644 --- a/icarus/core/wonderswan-color.cpp +++ b/icarus/core/wonderswan-color.cpp @@ -6,26 +6,20 @@ auto Icarus::wonderSwanColorManifest(string location) -> string { auto Icarus::wonderSwanColorManifest(vector& buffer, string location) -> string { string manifest; - string digest = Hash::SHA256(buffer.data(), buffer.size()).digest(); if(settings["icarus/UseDatabase"].boolean() && !manifest) { + string digest = Hash::SHA256(buffer.data(), buffer.size()).digest(); for(auto node : database.wonderSwanColor) { if(node["sha256"].text() == digest) { - manifest.append(node.text(), "\n sha256: ", digest, "\n"); + manifest.append(node.text(), "\n sha256: ", digest, "\n"); break; } } } if(settings["icarus/UseHeuristics"].boolean() && !manifest) { - WonderSwanCartridge cartridge{buffer.data(), buffer.size()}; - if(manifest = cartridge.manifest) { - manifest.append("\n"); - manifest.append("information\n"); - manifest.append(" title: ", prefixname(location), "\n"); - manifest.append(" sha256: ", digest, "\n"); - manifest.append(" note: ", "heuristically generated by icarus\n"); - } + WonderSwanCartridge cartridge{location, buffer.data(), buffer.size()}; + manifest = cartridge.manifest; } return manifest; diff --git a/icarus/core/wonderswan.cpp b/icarus/core/wonderswan.cpp index 0e228876..34ffe133 100644 --- a/icarus/core/wonderswan.cpp +++ b/icarus/core/wonderswan.cpp @@ -6,26 +6,20 @@ auto Icarus::wonderSwanManifest(string location) -> string { auto Icarus::wonderSwanManifest(vector& buffer, string location) -> string { string manifest; - string digest = Hash::SHA256(buffer.data(), buffer.size()).digest(); if(settings["icarus/UseDatabase"].boolean() && !manifest) { + string digest = Hash::SHA256(buffer.data(), buffer.size()).digest(); for(auto node : database.wonderSwan) { if(node["sha256"].text() == digest) { - manifest.append(node.text(), "\n sha256: ", digest, "\n"); + manifest.append(node.text(), "\n sha256: ", digest, "\n"); break; } } } if(settings["icarus/UseHeuristics"].boolean() && !manifest) { - WonderSwanCartridge cartridge{buffer.data(), buffer.size()}; - if(manifest = cartridge.manifest) { - manifest.append("\n"); - manifest.append("information\n"); - manifest.append(" title: ", prefixname(location), "\n"); - manifest.append(" sha256: ", digest, "\n"); - manifest.append(" note: ", "heuristically generated by icarus\n"); - } + WonderSwanCartridge cartridge{location, buffer.data(), buffer.size()}; + manifest = cartridge.manifest; } return manifest; diff --git a/icarus/heuristics/wonderswan.cpp b/icarus/heuristics/wonderswan.cpp index be1f5d50..017e3dad 100644 --- a/icarus/heuristics/wonderswan.cpp +++ b/icarus/heuristics/wonderswan.cpp @@ -1,5 +1,5 @@ struct WonderSwanCartridge { - WonderSwanCartridge(uint8_t* data, uint size); + WonderSwanCartridge(string location, uint8_t* data, uint size); string manifest; @@ -9,10 +9,11 @@ struct WonderSwanCartridge { string ramType; uint ramSize; + bool orientation; //0 = horizontal; 1 = vertical } information; }; -WonderSwanCartridge::WonderSwanCartridge(uint8_t* data, uint size) { +WonderSwanCartridge::WonderSwanCartridge(string location, uint8_t* data, uint size) { if(size < 0x10000) return; auto metadata = data + size - 16; @@ -31,8 +32,17 @@ WonderSwanCartridge::WonderSwanCartridge(uint8_t* data, uint size) { case 0x50: information.ramType = "eeprom"; information.ramSize = 1024; break; } + information.orientation = metadata[12] & 1; + manifest.append("board\n"); manifest.append(" rom name=program.rom size=0x", hex(size), "\n"); if(information.ramType && information.ramSize) manifest.append(" ram name=save.ram type=", information.ramType, " size=0x", hex(information.ramSize), "\n"); + manifest.append("\n"); + manifest.append("information\n"); + manifest.append(" title: ", prefixname(location), "\n"); + manifest.append(" orientation: ", !information.orientation ? "horizontal" : "vertical", "\n"); + manifest.append(" sha256: ", Hash::SHA256(data, size).digest(), "\n"); + manifest.append("\n"); + manifest.append("note: heuristically generated by icarus\n"); }