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"); }