Update to v097r22 release.

byuu says:

Changelog:
- WS: fixed lods, scas instructions
- WS: implemented missing GRP4 instructions
- WS: fixed transparency for screen one
- WSC: added color-mode PPU rendering
- WS+WSC: added packed pixel mode support
- WS+WSC: added dummy sound register reads/writes
- SFC: added threading to SuperDisc (it's hanging for right now; need to
  clear IRQ on $21e2 writes)

SuperDisc Timer and Sound Check were failing before due to not turning
off IRQs on $21e4 clear, so I'm happy that's fixed now.

Riviera starts now, and displays the first intro screen before crashing.
Huge, huge amounts of corrupted graphics, though. This game's really
making me work for it :(

No color games seem fully playable yet, but a lot of monochrome and
color games are now at least showing more intro screen graphics before
dying.

This build defaults to horizontal orientation, but I left the inputs
bound to vertical orientation. Whoops. I still need to implement
a screen flip key binding.
This commit is contained in:
Tim Allen 2016-03-08 22:34:00 +11:00
parent b0d2f5033e
commit 3d3ac8c1db
28 changed files with 766 additions and 177 deletions

View File

@ -6,7 +6,7 @@ using namespace nall;
namespace Emulator {
static const string Name = "higan";
static const string Version = "097.21";
static const string Version = "097.22";
static const string Author = "byuu";
static const string License = "GPLv3";
static const string Website = "http://byuu.org/";

View File

@ -53,7 +53,7 @@ auto V30MZ::opGroup3MemImm(Size size) {
auto mem = getMem(size);
switch(modrm.reg) {
case 0: alAnd(size, mem, fetch(size)); break;
case 1: break;
case 1: debug("[V30MZ] GRP3.1"); break;
case 2: wait(2); setMem(size, alNot(size, mem)); break;
case 3: wait(2); setMem(size, alNeg(size, mem)); break;
case 4: wait(2); setAcc(size * 2, alMul(size, getAcc(size), mem)); break;
@ -67,15 +67,47 @@ auto V30MZ::opGroup3MemImm(Size size) {
//ff grp4 memw
auto V30MZ::opGroup4MemImm(Size size) {
modRM();
auto mem = getMem(size);
switch(modrm.reg) {
case 0: wait(2); setMem(size, alInc(size, mem)); break;
case 1: wait(2); setMem(size, alDec(size, mem)); break;
case 2: break;
case 3: break;
case 4: break;
case 5: break;
case 6: break;
case 7: break;
case 0:
wait(2);
setMem(size, alInc(size, getMem(size)));
break;
case 1:
wait(2);
setMem(size, alDec(size, getMem(size)));
break;
case 2:
if(size == Byte) { debug("[V30MZ] GRP4.2"); break; }
wait(5);
push(r.ip);
r.ip = getMem(Word);
break;
case 3:
if(size == Byte) { debug("[V30MZ] GRP4.3"); break; }
wait(11);
push(r.cs);
push(r.ip);
r.ip = getMem(Word, 0);
r.cs = getMem(Word, 2);
break;
case 4:
if(size == Byte) { debug("[V30MZ] GRP4.4"); break; }
wait(4);
r.ip = getMem(Word);
break;
case 5:
if(size == Byte) { debug("[V30MZ] GRP4.5"); break; }
wait(9);
r.ip = getMem(Word, 0);
r.cs = getMem(Word, 2);
break;
case 6:
if(size == Byte) { debug("[V30MZ] GRP4.6"); break; }
wait(1);
push(getMem(Word));
break;
case 7:
debug("[V30MZ] GRP4.7");
break;
}
}

View File

@ -65,6 +65,7 @@ auto V30MZ::opStoreString(Size size) {
auto V30MZ::opLoadString(Size size) {
wait(2);
setAcc(size, read(size, segment(r.ds), r.si));
r.si += r.f.d ? -size : size;
if(prefix.repeat && --r.cx) {
state.prefix = true;
@ -78,6 +79,7 @@ auto V30MZ::opScanString(Size size) {
wait(3);
auto x = getAcc(size);
auto y = read(size, r.es, r.di);
r.di += r.f.d ? -size : size;
alSub(size, x, y);
if(prefix.repeat && prefix.repeat() == r.f.z && --r.cx) {

View File

@ -17,6 +17,10 @@ namespace Processor {
#include "instructions-string.cpp"
#include "disassembler.cpp"
auto V30MZ::debug(string text) -> void {
print(text, "\n");
}
auto V30MZ::exec() -> void {
state.poll = true;
if(state.halt) return wait(1);

View File

@ -14,6 +14,7 @@ struct V30MZ {
virtual auto in(uint16 port) -> uint8 = 0;
virtual auto out(uint16 port, uint8 data) -> void = 0;
auto debug(string text) -> void;
auto exec() -> void;
auto instruction() -> void;
auto interrupt(uint8 vector) -> void;

View File

@ -4,6 +4,23 @@ namespace SuperFamicom {
SuperDisc superdisc;
auto SuperDisc::Enter() -> void {
while(true) scheduler.synchronize(), superdisc.main();
}
auto SuperDisc::main() -> void {
if(r21e4 & 0x04) {
cpu.regs.irq = 1;
r21e1 = 0x81;
} else {
cpu.regs.irq = 0;
r21e1 = 0x00;
}
step(1);
synchronizeCPU();
}
auto SuperDisc::init() -> void {
}
@ -16,18 +33,77 @@ auto SuperDisc::unload() -> void {
}
auto SuperDisc::power() -> void {
create(&SuperDisc::Enter, 75);
}
auto SuperDisc::reset() -> void {
r21e0 = 0x00;
r21e1 = 0x00;
r21e2 = 0x00;
r21e3 = 0x00;
r21e4 = 0x00;
r21e5 = 0x00;
}
auto SuperDisc::read(uint24 addr, uint8 data) -> uint8 {
addr = 0x21e0 | (addr & 7);
if(addr == 0x21e0) {
data = r21e0;
}
if(addr == 0x21e1) {
data = r21e1;
}
if(addr == 0x21e2) {
data = r21e2;
}
if(addr == 0x21e3) {
if(r21e2 == 0x01) data = 0x10;
else data = 0x00;
r21e2++;
}
if(addr == 0x21e4) {
data = r21e4;
}
if(addr == 0x21e5) {
data = r21e5;
}
return data;
}
auto SuperDisc::write(uint24 addr, uint8 data) -> void {
addr = 0x21e0 | (addr & 7);
if(addr == 0x21e0) {
r21e0 = data;
}
if(addr == 0x21e1) {
r21e1 = data;
}
if(addr == 0x21e2) {
r21e2 = data;
}
if(addr == 0x21e3) {
r21e2++;
r21e3 = data;
}
if(addr == 0x21e4) {
r21e4 = data;
}
if(addr == 0x21e5) {
r21e5 = data;
}
}
}

View File

@ -1,4 +1,7 @@
struct SuperDisc : Memory {
struct SuperDisc : Coprocessor, Memory {
static auto Enter() -> void;
auto main() -> void;
auto init() -> void;
auto load() -> void;
auto unload() -> void;
@ -9,6 +12,12 @@ struct SuperDisc : Memory {
auto write(uint24 addr, uint8 data) -> void;
private:
uint8 r21e0;
uint8 r21e1;
uint8 r21e2;
uint8 r21e3;
uint8 r21e4;
uint8 r21e5;
};
extern SuperDisc superdisc;

View File

@ -69,8 +69,8 @@ namespace SuperFamicom {
#include <sfc/controller/controller.hpp>
#include <sfc/system/system.hpp>
#include <sfc/scheduler/scheduler.hpp>
#include <sfc/expansion/expansion.hpp>
#include <sfc/coprocessor/coprocessor.hpp>
#include <sfc/expansion/expansion.hpp>
#include <sfc/slot/slot.hpp>
#include <sfc/cartridge/cartridge.hpp>
#include <sfc/cheat/cheat.hpp>

View File

@ -209,6 +209,7 @@ auto System::reset() -> void {
if(cartridge.hasSharpRTC()) cpu.coprocessors.append(&sharprtc);
if(cartridge.hasSPC7110()) cpu.coprocessors.append(&spc7110);
if(cartridge.hasMSU1()) cpu.coprocessors.append(&msu1);
if(expansionPort() == Device::ID::SuperDisc) cpu.coprocessors.append(&superdisc);
scheduler.reset();
device.connect(0, (Device::ID)settings.controllerPort1);

View File

@ -3,6 +3,7 @@
namespace WonderSwan {
APU apu;
#include "io.cpp"
auto APU::Enter() -> void {
while(true) scheduler.synchronize(), apu.main();
@ -20,6 +21,41 @@ auto APU::step(uint clocks) -> void {
auto APU::power() -> void {
create(APU::Enter, 3'072'000);
for(uint n = 0x0080; n <= 0x0094; n++) iomap[n] = this;
channel1.r.pitch = 0;
channel1.r.volumeLeft = 0;
channel1.r.volumeRight = 0;
channel1.r.enable = 0;
channel2.r.pitch = 0;
channel2.r.volumeLeft = 0;
channel2.r.volumeRight = 0;
channel2.r.enable = 0;
channel2.r.voice = 0;
channel3.r.pitch = 0;
channel3.r.volumeLeft = 0;
channel3.r.volumeRight = 0;
channel3.r.sweepValue = 0;
channel3.r.sweepTime = 0;
channel3.r.enable = 0;
channel3.r.sweep = 0;
channel4.r.pitch = 0;
channel4.r.volumeLeft = 0;
channel4.r.volumeRight = 0;
channel4.r.noiseMode = 0;
channel4.r.enable = 0;
channel4.r.noise = 0;
r.waveBase = 0;
r.speakerEnable = 0;
r.speakerShift = 0;
r.headphoneEnable = 0;
r.voiceEnableLeft = 0;
r.voiceEnableRight = 0;
}
}

View File

@ -1,8 +1,94 @@
struct APU : Thread {
struct APU : Thread, IO {
static auto Enter() -> void;
auto main() -> void;
auto step(uint clocks) -> void;
auto power() -> void;
//io.cpp
auto portRead(uint16 addr) -> uint8;
auto portWrite(uint16 addr, uint8 data) -> void;
struct Channel1 {
struct Registers {
//$0080-0081 SND_CH1_PITCH
uint11 pitch;
//$0088 SND_CH1_VOL
uint4 volumeLeft;
uint4 volumeRight;
//$0090 SND_CTRL
uint1 enable;
} r;
} channel1;
struct Channel2 {
struct Registers {
//$0082-0083 SND_CH2_PITCH
uint11 pitch;
//$0089 SND_CH2_VOL
uint4 volumeLeft;
uint4 volumeRight;
//$0090 SND_CTRL
uint1 enable;
uint1 voice;
} r;
} channel2;
struct Channel3 {
struct Registers {
//$0084-0085 SND_CH3_PITCH
uint11 pitch;
//$008a SND_CH3_VOL
uint4 volumeLeft;
uint4 volumeRight;
//$008c SND_SWEEP_VALUE
uint8 sweepValue;
//$008d SND_SWEEP_TIME
uint5 sweepTime;
//$0090 SND_CTRL
uint1 enable;
uint1 sweep;
} r;
} channel3;
struct Channel4 {
struct Registers {
//$0086-0087 SND_CH4_PITCH
uint11 pitch;
//$008b SND_CH4_VOL
uint4 volumeLeft;
uint4 volumeRight;
//$008e SND_NOISE
uint5 noiseMode;
//$0090 SND_CTRL
uint1 enable;
uint1 noise;
} r;
} channel4;
struct Registers {
//$008f SND_WAVE_BASE
uint8 waveBase;
//$0091 SND_OUTPUT
uint1 speakerEnable;
uint2 speakerShift;
uint1 headphoneEnable;
//$0092 SND_VOICE_CTRL
uint2 voiceEnableLeft;
uint2 voiceEnableRight;
} r;
};
extern APU apu;

181
higan/ws/apu/io.cpp Normal file
View File

@ -0,0 +1,181 @@
auto APU::portRead(uint16 addr) -> uint8 {
//SND_CH1_PITCH
if(addr == 0x0080) return channel1.r.pitch.bits(0, 7);
if(addr == 0x0081) return channel1.r.pitch.bits(8,11);
//SND_CH2_PITCH
if(addr == 0x0082) return channel2.r.pitch.bits(0, 7);
if(addr == 0x0083) return channel2.r.pitch.bits(8,11);
//SND_CH3_PITCH
if(addr == 0x0084) return channel3.r.pitch.bits(0, 7);
if(addr == 0x0085) return channel3.r.pitch.bits(8,11);
//SND_CH4_PITCH
if(addr == 0x0086) return channel4.r.pitch.bits(0, 7);
if(addr == 0x0087) return channel4.r.pitch.bits(8,11);
//SND_CH1_VOL
if(addr == 0x0088) return (
channel1.r.volumeRight << 0
| channel1.r.volumeLeft << 4
);
//SND_CH2_VOL
if(addr == 0x0089) return (
channel2.r.volumeRight << 0
| channel2.r.volumeLeft << 4
);
//SND_CH3_VOL
if(addr == 0x008a) return (
channel3.r.volumeRight << 0
| channel3.r.volumeLeft << 4
);
//SND_CH4_VOL
if(addr == 0x008b) return (
channel4.r.volumeRight << 0
| channel4.r.volumeLeft << 4
);
//SND_SWEEP_VALUE
if(addr == 0x008c) return channel3.r.sweepValue;
//SND_SWEEP_TIME
if(addr == 0x008d) return channel3.r.sweepTime;
//SND_NOISE
if(addr == 0x008e) return channel4.r.noiseMode;
//SND_WAVE_BASE
if(addr == 0x008f) return r.waveBase;
//SND_CTRL
if(addr == 0x0090) return (
channel1.r.enable << 0
| channel2.r.enable << 1
| channel3.r.enable << 2
| channel4.r.enable << 3
| channel2.r.voice << 5
| channel3.r.sweep << 6
| channel4.r.noise << 7
);
//SND_OUTPUT
if(addr == 0x0091) return (
r.speakerEnable << 0
| r.speakerShift << 1
| r.headphoneEnable << 3
| 1 << 7 //headphone connected
);
//SND_RANDOM
if(addr == 0x0092) return rand() & 0xff;
if(addr == 0x0093) return rand() & 0x7f;
//SND_VOICE_CTRL
if(addr == 0x0094) return (
r.voiceEnableRight << 0
| r.voiceEnableLeft << 2
);
return 0x00;
}
auto APU::portWrite(uint16 addr, uint8 data) -> void {
//SND_CH1_PITCH
if(addr == 0x0080) { channel1.r.pitch.bits(0, 7) = data.bits(0,7); return; }
if(addr == 0x0081) { channel1.r.pitch.bits(8,11) = data.bits(0,3); return; }
//SND_CH2_PITCH
if(addr == 0x0082) { channel2.r.pitch.bits(0, 7) = data.bits(0,7); return; }
if(addr == 0x0083) { channel2.r.pitch.bits(8,11) = data.bits(0,3); return; }
//SND_CH3_PITCH
if(addr == 0x0084) { channel3.r.pitch.bits(0, 7) = data.bits(0,7); return; }
if(addr == 0x0085) { channel3.r.pitch.bits(8,11) = data.bits(0,3); return; }
//SND_CH4_PITCH
if(addr == 0x0086) { channel4.r.pitch.bits(0, 7) = data.bits(0,7); return; }
if(addr == 0x0087) { channel4.r.pitch.bits(8,11) = data.bits(0,3); return; }
//SND_CH1_VOL
if(addr == 0x0088) {
channel1.r.volumeRight = data.bits(0,3);
channel1.r.volumeLeft = data.bits(4,7);
return;
}
//SND_CH2_VOL
if(addr == 0x0089) {
channel2.r.volumeRight = data.bits(0,3);
channel2.r.volumeLeft = data.bits(4,7);
return;
}
//SND_CH3_VOL
if(addr == 0x008a) {
channel3.r.volumeRight = data.bits(0,3);
channel3.r.volumeLeft = data.bits(4,7);
return;
}
//SND_CH4_VOL
if(addr == 0x008b) {
channel4.r.volumeRight = data.bits(0,3);
channel4.r.volumeLeft = data.bits(4,7);
return;
}
//SND_SWEEP_VALUE
if(addr == 0x008c) {
channel3.r.sweepValue = data;
return;
}
//SND_SWEEP_TIME
if(addr == 0x008d) {
channel3.r.sweepTime = data.bits(0,4);
return;
}
//SND_NOISE
if(addr == 0x008e) {
channel4.r.noiseMode = data.bits(0,4);
return;
}
//SND_WAVE_BASE
if(addr == 0x008f) {
r.waveBase = data;
return;
}
//SND_CTRL
if(addr == 0x0090) {
channel1.r.enable = data.bit(0);
channel2.r.enable = data.bit(1);
channel3.r.enable = data.bit(2);
channel4.r.enable = data.bit(3);
channel2.r.voice = data.bit(5);
channel3.r.sweep = data.bit(6);
channel4.r.noise = data.bit(7);
return;
}
//SND_OUTPUT
if(addr == 0x0091) {
r.speakerEnable = data.bit (0);
r.speakerShift = data.bits(1,2);
r.headphoneEnable = data.bit (3);
return;
}
//SND_VOICE_CTRL
if(addr == 0x0094) {
r.voiceEnableRight = data.bits(0,1);
r.voiceEnableLeft = data.bits(2,3);
return;
}
}

View File

@ -3,7 +3,6 @@
namespace WonderSwan {
CPU cpu;
#include "memory.cpp"
#include "io.cpp"
#include "interrupt.cpp"
#include "dma.cpp"
@ -61,14 +60,17 @@ auto CPU::power() -> void {
iomap[0x0062] = this;
}
r.dmaSource = 0x00000;
r.dmaTarget = 0x0000;
r.dmaLength = 0x0000;
r.dmaEnable = false;
r.dmaSource = 0;
r.dmaTarget = 0;
r.dmaLength = 0;
r.dmaEnable = 0;
r.dmaMode = 0;
r.interruptBase = 0x00;
r.interruptEnable = 0x00;
r.interruptStatus = 0x00;
r.interruptBase = 0;
r.interruptEnable = 0;
r.interruptStatus = 0;
r.ypadEnable = 0;
r.xpadEnable = 0;
r.buttonEnable = 0;
}
}

View File

@ -22,10 +22,6 @@ struct CPU : Processor::V30MZ, Thread, IO {
auto power() -> void;
//memory.cpp
auto ramRead(uint16 addr) -> uint8;
auto ramWrite(uint16 addr, uint8 data) -> void;
//io.cpp
auto keypadRead() -> uint4;
auto portRead(uint16 addr) -> uint8 override;

View File

@ -1,9 +0,0 @@
auto CPU::ramRead(uint16 addr) -> uint8 {
if(WS() && addr >= 0x4000) return 0x90;
return iram[addr];
}
auto CPU::ramWrite(uint16 addr, uint8 data) -> void {
if(WS() && addr >= 0x4000) return;
iram[addr] = data;
}

View File

@ -92,11 +92,11 @@ auto Interface::group(uint id) -> uint {
case ID::ROM:
case ID::RAM:
case ID::EEPROM:
switch(system.revision()) {
case System::Revision::WonderSwan:
switch(system.model()) {
case System::Model::WonderSwan:
return ID::WonderSwan;
case System::Revision::WonderSwanColor:
case System::Revision::SwanCrystal:
case System::Model::WonderSwanColor:
case System::Model::SwanCrystal:
return ID::WonderSwanColor;
}
}
@ -104,8 +104,8 @@ auto Interface::group(uint id) -> uint {
}
auto Interface::load(uint id) -> void {
if(id == ID::WonderSwan) system.load(System::Revision::WonderSwan);
if(id == ID::WonderSwanColor) system.load(System::Revision::WonderSwanColor);
if(id == ID::WonderSwan) system.load(System::Model::WonderSwan);
if(id == ID::WonderSwanColor) system.load(System::Model::WonderSwanColor);
}
auto Interface::save() -> void {

View File

@ -2,8 +2,8 @@
namespace WonderSwan {
uint8 iram[64 * 1024];
IO* iomap[64 * 1024] = {nullptr};
InternalRAM iram;
Bus bus;
auto IO::power() -> void {
@ -20,15 +20,32 @@ auto IO::portWrite(uint16 addr, uint8 data) -> void {
//print("[", hex(addr, 4L), "] = ", hex(data, 2L), ": port unmapped\n");
}
auto InternalRAM::power() -> void {
for(auto& byte : memory) byte = 0x00;
}
auto InternalRAM::read(uint16 addr, uint size) -> uint32 {
if(size == Long) return read(addr + 0, Word) << 0 | read(addr + 2, Word) << 16;
if(size == Word) return read(addr + 0, Byte) << 0 | read(addr + 1, Byte) << 8;
if(addr >= 0x4000 && !system.r.depth) return 0x90;
return memory[addr];
}
auto InternalRAM::write(uint16 addr, uint8 data) -> void {
if(addr >= 0x4000 && !system.r.depth) return;
memory[addr] = data;
}
auto Bus::read(uint20 addr) -> uint8 {
if(addr.bits(16,19) == 0) return cpu.ramRead(addr);
if(addr.bits(16,19) == 0) return iram.read(addr);
if(addr.bits(16,19) == 1) return cartridge.ramRead(addr);
if(addr.bits(16,19) >= 2) return cartridge.romRead(addr);
unreachable;
}
auto Bus::write(uint20 addr, uint8 data) -> void {
if(addr.bits(16,19) == 0) return cpu.ramWrite(addr, data);
if(addr.bits(16,19) == 0) return iram.write(addr, data);
if(addr.bits(16,19) == 1) return cartridge.ramWrite(addr, data);
if(addr.bits(16,19) >= 2) return cartridge.romWrite(addr, data);
}

View File

@ -5,11 +5,21 @@ struct IO {
virtual auto portWrite(uint16 addr, uint8 data) -> void;
};
struct InternalRAM {
auto power() -> void;
auto read(uint16 addr, uint size = Byte) -> uint32;
auto write(uint16 addr, uint8 data) -> void;
private:
uint8 memory[65536];
};
struct Bus {
auto read(uint20 addr) -> uint8;
auto write(uint20 addr, uint8 data) -> void;
};
extern uint8 iram[64 * 1024];
extern IO* iomap[64 * 1024];
extern InternalRAM iram;
extern Bus bus;

View File

@ -12,7 +12,7 @@ auto PPU::portRead(uint16 addr) -> uint8 {
}
//BACK_COLOR
if(addr == 0x0001) return r.backColorPalette << 4 | r.backColorIndex << 0;
if(addr == 0x0001) return r.backColor;
//LINE_CUR
if(addr == 0x0002) return status.vclk;
@ -105,16 +105,6 @@ auto PPU::portRead(uint16 addr) -> uint8 {
);
}
//DISP_MODE
if(addr == 0x0060) {
return (
r.bpp << 7
| r.color << 6
| r.format << 5
| r.u0060 << 0
);
}
return 0x00;
}
@ -133,11 +123,9 @@ auto PPU::portWrite(uint16 addr, uint8 data) -> void {
//BACK_COLOR
if(addr == 0x0001) {
if(WS()) {
r.backColorPalette = 0;
r.backColorIndex = data.bits(2,0);
r.backColor = data.bits(0,2);
} else {
r.backColorPalette = data.bits(7,4);
r.backColorIndex = data.bits(3,0);
r.backColor = data.bits(0,7);
}
return;
}
@ -296,13 +284,4 @@ auto PPU::portWrite(uint16 addr, uint8 data) -> void {
r.palette[addr.bits(4,1)].color[addr.bit(0) * 2 + 0] = data.bits(2,0);
return;
}
//DISP_MODE
if(addr == 0x0060) {
r.bpp = data.bit(7);
r.color = data.bit(6);
r.format = data.bit(5);
r.u0060 = data & 0b1011;
return;
}
}

View File

@ -4,7 +4,8 @@ namespace WonderSwan {
PPU ppu;
#include "io.cpp"
#include "render.cpp"
#include "render-mono.cpp"
#include "render-color.cpp"
#include "video.cpp"
auto PPU::Enter() -> void {
@ -14,10 +15,17 @@ auto PPU::Enter() -> void {
auto PPU::main() -> void {
if(status.vclk < 144) {
for(uint x = 0; x < 224; x++) {
renderBack();
renderScreenOne();
renderScreenTwo();
renderSprite();
if(!system.color()) {
renderMonoBack();
renderMonoScreenOne();
renderMonoScreenTwo();
renderMonoSprite();
} else {
renderColorBack();
renderColorScreenOne();
renderColorScreenTwo();
renderColorSprite();
}
output[status.vclk * 224 + status.hclk] = pixel.color;
step(1);
}
@ -60,14 +68,48 @@ auto PPU::power() -> void {
for(uint n = 0x0000; n <= 0x0017; n++) iomap[n] = this;
for(uint n = 0x001c; n <= 0x003f; n++) iomap[n] = this;
iomap[0x0060] = this;
for(auto& n : output) n = 0;
status.vclk = 0;
status.hclk = 0;
r.screenTwoWindowEnable = 0;
r.screenTwoWindowInvert = 0;
r.spriteWindowEnable = 0;
r.spriteEnable = 0;
r.screenTwoEnable = 0;
r.screenOneEnable = 0;
r.backColor = 0;
r.lineCompare = 0xff;
r.spriteBase = 0;
r.spriteFirst = 0;
r.spriteCount = 0;
r.screenTwoMapBase = 0;
r.screenOneMapBase = 0;
r.screenTwoWindowX0 = 0;
r.screenTwoWindowY0 = 0;
r.screenTwoWindowX1 = 0;
r.screenTwoWindowY1 = 0;
r.spriteWindowX0 = 0;
r.spriteWindowY0 = 0;
r.spriteWindowX1 = 0;
r.spriteWindowY1 = 0;
r.scrollOneX = 0;
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.iconSleep = 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;
video.power();
}

View File

@ -12,11 +12,19 @@ struct PPU : Thread, IO {
auto portRead(uint16 addr) -> uint8 override;
auto portWrite(uint16 addr, uint8 data) -> void override;
//render.cpp
auto renderBack() -> void;
auto renderScreenOne() -> void;
auto renderScreenTwo() -> void;
auto renderSprite() -> void;
//render-mono.cpp
auto renderMonoFetch(uint14 offset, uint3 y, uint3 x) -> uint2;
auto renderMonoBack() -> void;
auto renderMonoScreenOne() -> void;
auto renderMonoScreenTwo() -> void;
auto renderMonoSprite() -> void;
//render-color.cpp
auto renderColorFetch(uint16 offset, uint3 y, uint3 x) -> uint4;
auto renderColorBack() -> void;
auto renderColorScreenOne() -> void;
auto renderColorScreenTwo() -> void;
auto renderColorSprite() -> void;
//state
uint12 output[224 * 144];
@ -34,16 +42,15 @@ struct PPU : Thread, IO {
struct Registers {
//$0000 DISP_CTRL
bool screenTwoWindowEnable;
bool screenTwoWindowInvert;
bool spriteWindowEnable;
bool spriteEnable;
bool screenTwoEnable;
bool screenOneEnable;
uint1 screenTwoWindowEnable;
uint1 screenTwoWindowInvert;
uint1 spriteWindowEnable;
uint1 spriteEnable;
uint1 screenTwoEnable;
uint1 screenOneEnable;
//$0001 BACK_COLOR
uint4 backColorIndex;
uint4 backColorPalette;
uint8 backColor;
//$0003 LINE_CMP
uint8 lineCompare;
@ -101,12 +108,12 @@ struct PPU : Thread, IO {
uint8 control;
//$0015 LCD_ICON
bool iconAux3;
bool iconAux2;
bool iconAux1;
bool iconHorizontal;
bool iconVertical;
bool iconSleep;
uint1 iconAux3;
uint1 iconAux2;
uint1 iconAux1;
uint1 iconHorizontal;
uint1 iconVertical;
uint1 iconSleep;
//$0016 LCD_VTOTAL
uint8 vtotal;
@ -121,12 +128,6 @@ struct PPU : Thread, IO {
struct Palette {
uint3 color[4];
} palette[16];
//$0060 DISP_MODE
bool bpp;
bool color;
bool format;
uint5 u0060; //unknown purpose
} r;
};

View File

@ -0,0 +1,104 @@
auto PPU::renderColorFetch(uint16 offset, uint3 y, uint3 x) -> uint4 {
uint4 color;
if(system.planar()) {
uint32 data = iram.read(offset + (y << 2), Long);
color |= data.bit( 7 - x) << 0;
color |= data.bit(15 - x) << 1;
color |= data.bit(23 - x) << 2;
color |= data.bit(31 - x) << 3;
}
if(system.packed()) {
uint8 data = iram.read(offset + (y << 2) + (x >> 1));
color = data >> (4 - (x.bit(0) << 2));
}
return color;
}
auto PPU::renderColorBack() -> void {
uint12 color = iram.read(0xfe00 + (r.backColor << 1), Word);
pixel = {Pixel::Source::Back, color};
}
auto PPU::renderColorScreenOne() -> void {
if(!r.screenOneEnable) return;
uint8 scrollY = status.vclk + r.scrollOneY;
uint8 scrollX = status.hclk + r.scrollOneX;
uint16 tilemapOffset = r.screenOneMapBase << 11;
tilemapOffset += (scrollY >> 3) << 6;
tilemapOffset += (scrollX >> 3) << 1;
uint16 tile = iram.read(tilemapOffset, Word);
uint16 tileOffset = 0x4000 + (tile.bit(13) << 14) + (tile.bits(0,8) << 5);
uint3 tileY = (scrollY & 7) ^ (tile.bit(15) ? 7 : 0);
uint3 tileX = (scrollX & 7) ^ (tile.bit(14) ? 7 : 0);
uint4 tileColor = renderColorFetch(tileOffset, tileY, tileX);
if(tileColor == 0) return;
uint12 color = iram.read(0xfe00 + (tile.bits(9, 12) << 5) + (tileColor << 1), Word);
pixel = {Pixel::Source::ScreenOne, color};
}
auto PPU::renderColorScreenTwo() -> void {
if(!r.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;
uint8 scrollY = status.vclk + r.scrollTwoY;
uint8 scrollX = status.hclk + r.scrollTwoX;
uint16 tilemapOffset = r.screenTwoMapBase << 11;
tilemapOffset += (scrollY >> 3) << 6;
tilemapOffset += (scrollX >> 3) << 1;
uint16 tile = iram.read(tilemapOffset, Word);
uint16 tileOffset = 0x4000 + (tile.bit(13) << 14) + (tile.bits(0,8) << 5);
uint3 tileY = (scrollY & 7) ^ (tile.bit(15) ? 7 : 0);
uint3 tileX = (scrollX & 7) ^ (tile.bit(14) ? 7 : 0);
uint4 tileColor = renderColorFetch(tileOffset, tileY, tileX);
if(tileColor == 0) return;
uint12 color = iram.read(0xfe00 + (tile.bits(9,12) << 5) + (tileColor << 1), Word);
pixel = {Pixel::Source::ScreenTwo, color};
}
auto PPU::renderColorSprite() -> void {
if(!r.spriteEnable) return;
bool windowInside = status.vclk >= r.spriteWindowY0 && status.vclk <= r.spriteWindowY1
&& status.hclk >= r.spriteWindowX0 && status.hclk <= r.spriteWindowX1;
uint16 spriteBase = r.spriteBase << 9;
uint7 spriteIndex = r.spriteFirst;
uint8 spriteCount = min(128, (uint)r.spriteCount);
while(spriteCount--) {
uint32 sprite = iram.read(spriteBase + (spriteIndex++ << 2), Long);
if(r.spriteWindowEnable && sprite.bit(12) && !windowInside) continue;
uint8 spriteY = sprite.bits(16,23);
uint8 spriteX = sprite.bits(24,31);
if(status.vclk < spriteY) continue;
if(status.vclk > (uint8)(spriteY + 7)) continue;
if(status.hclk < spriteX) continue;
if(status.hclk > (uint8)(spriteX + 7)) continue;
uint16 tileOffset = 0x4000 + (sprite.bits(0,8) << 5);
uint3 tileY = (uint8)(status.vclk - spriteY) ^ (sprite.bit(15) ? 7 : 0);
uint3 tileX = (uint8)(status.hclk - spriteX) ^ (sprite.bit(14) ? 7 : 0);
uint4 tileColor = renderColorFetch(tileOffset, tileY, tileX);
if(tileColor == 0) continue;
if(!sprite.bit(13) && pixel.source == Pixel::Source::ScreenTwo) continue;
uint12 color = iram.read(0xff00 + (sprite.bits(9,11) << 5) + (tileColor << 1), Word);
pixel = {Pixel::Source::Sprite, color};
return;
}
}

View File

@ -1,47 +1,52 @@
auto PPU::renderBack() -> void {
uint4 poolColor = 15 - r.pool[r.backColorIndex];
auto PPU::renderMonoFetch(uint14 offset, uint3 y, uint3 x) -> uint2 {
uint2 color;
if(system.planar()) {
uint16 data = iram.read(offset + (y << 1), Word);
color |= data.bit( 7 - x) << 0;
color |= data.bit(15 - x) << 1;
}
if(system.packed()) {
uint8 data = iram.read(offset + (y << 1) + (x >> 2));
color = data >> (6 - (x.bits(0,1) << 1));
}
return color;
}
auto PPU::renderMonoBack() -> void {
uint4 poolColor = 15 - r.pool[r.backColor.bits(0,2)];
pixel = {Pixel::Source::Back, poolColor << 0 | poolColor << 4 | poolColor << 8};
}
auto PPU::renderScreenOne() -> void {
auto PPU::renderMonoScreenOne() -> void {
if(!r.screenOneEnable) return;
uint8 scrollX = status.hclk + r.scrollOneX;
uint8 scrollY = status.vclk + r.scrollOneY;
uint8 scrollX = status.hclk + r.scrollOneX;
uint14 tilemapOffset = r.screenOneMapBase << 11;
tilemapOffset += (scrollY >> 3) << 6;
tilemapOffset += (scrollX >> 3) << 1;
uint16 tile;
tile.byte(0) = iram[tilemapOffset++];
tile.byte(1) = iram[tilemapOffset++];
uint3 tileX = (scrollX & 7) ^ (tile.bit(14) ? 7 : 0);
uint16 tile = iram.read(tilemapOffset, Word);
uint14 tileOffset = 0x2000 + (tile.bits(0,8) << 4);
uint3 tileY = (scrollY & 7) ^ (tile.bit(15) ? 7 : 0);
uint14 tileOffset = 0x2000 + (tile.bits(0,8) << 4) + (tileY << 1);
uint8 d0 = iram[tileOffset++];
uint8 d1 = iram[tileOffset++];
uint8 tileMask = 0x80 >> tileX;
uint2 tileColor = (d0 & tileMask ? 1 : 0) | (d1 & tileMask ? 2 : 0);
uint3 tileX = (scrollX & 7) ^ (tile.bit(14) ? 7 : 0);
uint2 tileColor = renderMonoFetch(tileOffset, tileY, tileX);
if(tile.bit(11) && tileColor == 0) return;
uint3 paletteColor = r.palette[tile.bits(9,12)].color[tileColor];
uint4 poolColor = 15 - r.pool[paletteColor];
pixel = {Pixel::Source::ScreenOne, poolColor << 0 | poolColor << 4 | poolColor << 8};
}
auto PPU::renderScreenTwo() -> void {
auto PPU::renderMonoScreenTwo() -> void {
if(!r.screenTwoEnable) return;
bool windowInside = (
status.hclk >= r.screenTwoWindowX0
&& status.hclk <= r.screenTwoWindowX1
&& status.vclk >= r.screenTwoWindowY0
&& status.vclk <= r.screenTwoWindowY1
);
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;
@ -52,74 +57,48 @@ auto PPU::renderScreenTwo() -> void {
tilemapOffset += (scrollY >> 3) << 6;
tilemapOffset += (scrollX >> 3) << 1;
uint16 tile;
tile.byte(0) = iram[tilemapOffset++];
tile.byte(1) = iram[tilemapOffset++];
uint3 tileX = (scrollX & 7) ^ (tile.bit(14) ? 7 : 0);
uint16 tile = iram.read(tilemapOffset, Word);
uint14 tileOffset = 0x2000 + (tile.bits(0,8) << 4);
uint3 tileY = (scrollY & 7) ^ (tile.bit(15) ? 7 : 0);
uint14 tileOffset = 0x2000 + (tile.bits(0,8) << 4) + (tileY << 1);
uint8 d0 = iram[tileOffset++];
uint8 d1 = iram[tileOffset++];
uint8 tileMask = 0x80 >> tileX;
uint2 tileColor = (d0 & tileMask ? 1 : 0) | (d1 & tileMask ? 2 : 0);
uint3 tileX = (scrollX & 7) ^ (tile.bit(14) ? 7 : 0);
uint2 tileColor = renderMonoFetch(tileOffset, tileY, tileX);
if(tile.bit(11) && tileColor == 0) return;
uint3 paletteColor = r.palette[tile.bits(9,12)].color[tileColor];
uint4 poolColor = 15 - r.pool[paletteColor];
pixel = {Pixel::Source::ScreenTwo, poolColor << 0 | poolColor << 4 | poolColor << 8};
}
auto PPU::renderSprite() -> void {
auto PPU::renderMonoSprite() -> void {
if(!r.spriteEnable) return;
bool windowInside = (
status.hclk >= r.spriteWindowX0
&& status.hclk <= r.spriteWindowX1
&& status.vclk >= r.spriteWindowY0
&& status.vclk <= r.spriteWindowY1
);
bool windowInside = status.vclk >= r.spriteWindowY0 && status.vclk <= r.spriteWindowY1
&& status.hclk >= r.spriteWindowX0 && status.hclk <= r.spriteWindowX1;
uint14 spriteBase = r.spriteBase << 9;
uint7 spriteIndex = r.spriteFirst;
uint8 spriteCount = min(128, (uint)r.spriteCount);
while(spriteCount--) {
uint32 sprite;
sprite.byte(0) = iram[spriteBase + (spriteIndex << 2) + 0];
sprite.byte(1) = iram[spriteBase + (spriteIndex << 2) + 1];
sprite.byte(2) = iram[spriteBase + (spriteIndex << 2) + 2];
sprite.byte(3) = iram[spriteBase + (spriteIndex << 2) + 3];
spriteIndex++;
uint32 sprite = iram.read(spriteBase + (spriteIndex++ << 2), Long);
if(r.spriteWindowEnable && sprite.bit(12) && !windowInside) continue;
uint8 spriteY = sprite.bits(16,23);
uint8 spriteX = sprite.bits(24,31);
if(status.hclk < spriteX) continue;
if(status.hclk > (uint8)(spriteX + 7)) continue;
if(status.vclk < spriteY) continue;
if(status.vclk > (uint8)(spriteY + 7)) continue;
if(status.hclk < spriteX) continue;
if(status.hclk > (uint8)(spriteX + 7)) continue;
uint3 tileX = (uint8)(status.hclk - spriteX) ^ (sprite.bit(14) ? 7 : 0);
uint14 tileOffset = 0x2000 + (sprite.bits(0,8) << 4);
uint3 tileY = (uint8)(status.vclk - spriteY) ^ (sprite.bit(15) ? 7 : 0);
uint14 tileOffset = 0x2000 + (sprite.bits(0,8) << 4) + (tileY << 1);
uint8 d0 = iram[tileOffset++];
uint8 d1 = iram[tileOffset++];
uint8 tileMask = 0x80 >> tileX;
uint2 tileColor = (d0 & tileMask ? 1 : 0) | (d1 & tileMask ? 2 : 0);
uint3 tileX = (uint8)(status.hclk - spriteX) ^ (sprite.bit(14) ? 7 : 0);
uint2 tileColor = renderMonoFetch(tileOffset, tileY, tileX);
if(sprite.bit(11) && tileColor == 0) continue;
if(!sprite.bit(13) && pixel.source == Pixel::Source::ScreenTwo) continue;
uint3 paletteColor = r.palette[8 + sprite.bits(9,11)].color[tileColor];
uint4 poolColor = 15 - r.pool[paletteColor];
pixel = {Pixel::Source::Sprite, poolColor << 0 | poolColor << 4 | poolColor << 8};
return;
}

View File

@ -28,8 +28,8 @@ auto Video::refresh() -> void {
auto source = ppu.output + y * 224;
for(uint x = 0; x < 224; x++) {
auto color = paletteStandard[*source++];
//*(output() + y * 224 + x) = color;
*(output() + (223 - x) * 224 + 40 + y) = color;
*(output() + (y + 40) * 224 + x) = color;
//*(output() + (223 - x) * 224 + 40 + y) = color;
}
}

View File

@ -1,4 +1,12 @@
auto System::portRead(uint16 addr) -> uint8 {
//DISP_MODE
if(addr == 0x0060) return (
r.unknown << 0
| r.format << 5
| r.color << 6
| r.depth << 7
);
//IEEP_DATA
if(addr == 0x00ba) return eeprom.read(EEPROM::DataLo);
if(addr == 0x00bb) return eeprom.read(EEPROM::DataHi);
@ -12,6 +20,15 @@ auto System::portRead(uint16 addr) -> uint8 {
}
auto System::portWrite(uint16 addr, uint8 data) -> void {
//DISP_MODE
if(addr == 0x0060) {
r.unknown = data.bits(0,4) & 0b01011;
r.format = data.bit (5);
r.color = data.bit (6);
r.depth = data.bit (7);
return;
}
//IEEP_DATA
if(addr == 0x00ba) return eeprom.write(EEPROM::DataLo, data);
if(addr == 0x00bb) return eeprom.write(EEPROM::DataHi, data);

View File

@ -6,7 +6,10 @@ System system;
#include "io.cpp"
auto System::loaded() const -> bool { return _loaded; }
auto System::revision() const -> Revision { return _revision; }
auto System::model() const -> Model { return _model; }
auto System::color() const -> bool { return r.color; }
auto System::planar() const -> bool { return r.format == 0; }
auto System::packed() const -> bool { return r.format == 1; }
auto System::init() -> void {
}
@ -14,8 +17,8 @@ auto System::init() -> void {
auto System::term() -> void {
}
auto System::load(Revision revision) -> void {
_revision = revision;
auto System::load(Model model) -> void {
_model = model;
interface->loadRequest(ID::SystemManifest, "manifest.bml", true);
auto document = BML::unserialize(information.manifest);
@ -45,6 +48,7 @@ auto System::unload() -> void {
auto System::power() -> void {
IO::power();
iram.power();
eeprom.power();
cpu.power();
ppu.power();
@ -52,7 +56,13 @@ auto System::power() -> void {
cartridge.power();
scheduler.power();
iomap[0x0060] = this;
for(uint n = 0x00ba; n <= 0x00be; n++) iomap[n] = this;
r.depth = 0;
r.color = 0;
r.format = 0;
r.unknown = 0;
}
auto System::run() -> void {

View File

@ -5,18 +5,21 @@ enum class Keypad : uint {
};
struct System : IO {
enum class Revision : uint {
enum class Model : uint {
WonderSwan, //SW-001 (ASWAN)
WonderSwanColor, //WSC-001 (SPHINX)
SwanCrystal, //SCT-001 (SPHINX2)
};
auto loaded() const -> bool;
auto revision() const -> Revision;
auto model() const -> Model;
auto color() const -> bool;
auto planar() const -> bool;
auto packed() const -> bool;
auto init() -> void;
auto term() -> void;
auto load(Revision) -> void;
auto load(Model) -> void;
auto unload() -> void;
auto power() -> void;
auto run() -> void;
@ -30,9 +33,17 @@ struct System : IO {
EEPROM eeprom;
struct Registers {
//$0060 DISP_MODE
uint1 depth;
uint1 color;
uint1 format;
uint5 unknown;
} r;
privileged:
bool _loaded = false;
Revision _revision = Revision::WonderSwan;
Model _model = Model::WonderSwan;
};
extern System system;

View File

@ -42,6 +42,8 @@ namespace WonderSwan {
int64 clock = 0;
};
enum : uint { Byte = 1, Word = 2, Long = 4 };
#include <ws/memory/memory.hpp>
#include <ws/eeprom/eeprom.hpp>
#include <ws/system/system.hpp>
@ -51,9 +53,9 @@ namespace WonderSwan {
#include <ws/ppu/ppu.hpp>
#include <ws/apu/apu.hpp>
inline auto WS() { return system.revision() == System::Revision::WonderSwan; }
inline auto WSC() { return system.revision() == System::Revision::WonderSwanColor; }
inline auto SC() { return system.revision() == System::Revision::SwanCrystal; }
inline auto WS() { return system.model() == System::Model::WonderSwan; }
inline auto WSC() { return system.model() == System::Model::WonderSwanColor; }
inline auto SC() { return system.model() == System::Model::SwanCrystal; }
}
#include <ws/interface/interface.hpp>