mirror of https://github.com/bsnes-emu/bsnes.git
Update to v097r04 release.
byuu says: Lots of improvements. We're now able to start executing some V30MZ instructions. 32 of 256 opcodes implemented so far. I hope this goes without saying, but there's absolutely no point in loading WS/WSC games right now. You won't see anything until I have the full CPU and partial PPU implemented. ROM bank 2 works properly now, the I/O map is 16-bit (address) x 16-bit (data) as it should be*, and I have a basic disassembler in place (adding to it as I emulate new opcodes.) (* I don't know what happens if you access an 8-bit port in 16-bit mode or vice versa, so for now I'm just treating the handlers as always being 16-bit, and discarding the upper 8-bits when not needed.)
This commit is contained in:
parent
d7998b23ef
commit
a8323d0d2b
|
@ -6,7 +6,7 @@ using namespace nall;
|
|||
|
||||
namespace Emulator {
|
||||
static const string Name = "higan";
|
||||
static const string Version = "097.03";
|
||||
static const string Version = "097.04";
|
||||
static const string Author = "byuu";
|
||||
static const string License = "GPLv3";
|
||||
static const string Website = "http://byuu.org/";
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
#include <cmath>
|
||||
|
||||
Video video;
|
||||
|
||||
Video::Video() {
|
||||
|
|
|
@ -0,0 +1,83 @@
|
|||
auto V30MZ::disassemble(uint16 cs, uint16 ip, bool registers) -> string {
|
||||
string s;
|
||||
uint20 ea = (cs << 4) + ip;
|
||||
|
||||
auto readByte = [&](uint offset) {
|
||||
uint8 byte = read((cs << 4) + (uint16)(ip + offset));
|
||||
return hex(byte, 2L);
|
||||
};
|
||||
auto readWord = [&](uint offset) {
|
||||
uint16 word = read((cs << 4) + (uint16)(ip + offset++)) << 0;
|
||||
word | read((cs << 4) + (uint16)(ip + offset++)) << 8;
|
||||
return hex(word, 4L);
|
||||
};
|
||||
|
||||
uint8 opcode = read(ea);
|
||||
switch(opcode) {
|
||||
case 0x90: s = {"nop"}; break;
|
||||
case 0xb0: s = {"mov al,", readByte(1)}; break;
|
||||
case 0xb1: s = {"mov cl,", readByte(1)}; break;
|
||||
case 0xb2: s = {"mov dl,", readByte(1)}; break;
|
||||
case 0xb3: s = {"mov bl,", readByte(1)}; break;
|
||||
case 0xb4: s = {"mov ah,", readByte(1)}; break;
|
||||
case 0xb5: s = {"mov ch,", readByte(1)}; break;
|
||||
case 0xb6: s = {"mov dh,", readByte(1)}; break;
|
||||
case 0xb7: s = {"mov bh,", readByte(1)}; break;
|
||||
case 0xb8: s = {"mov ax,", readWord(1)}; break;
|
||||
case 0xb9: s = {"mov cx,", readWord(1)}; break;
|
||||
case 0xba: s = {"mov dx,", readWord(1)}; break;
|
||||
case 0xbb: s = {"mov bx,", readWord(1)}; break;
|
||||
case 0xbc: s = {"mov sp,", readWord(1)}; break;
|
||||
case 0xbd: s = {"mov bp,", readWord(1)}; break;
|
||||
case 0xbe: s = {"mov si,", readWord(1)}; break;
|
||||
case 0xbf: s = {"mov di,", readWord(1)}; break;
|
||||
case 0xe4: s = {"in al,", readByte(1)}; break;
|
||||
case 0xe5: s = {"in ax,", readByte(1)}; break;
|
||||
case 0xe6: s = {"out ", readByte(1), ",al"}; break;
|
||||
case 0xe7: s = {"out ", readByte(1), ",ax"}; break;
|
||||
case 0xea: s = {"jmp ", readWord(3), ":", readWord(1)}; break;
|
||||
case 0xec: s = {"in al,dx"}; break;
|
||||
case 0xed: s = {"in ax,dx"}; break;
|
||||
case 0xee: s = {"out dx,al"}; break;
|
||||
case 0xef: s = {"out dx,ax"}; break;
|
||||
case 0xf8: s = {"clc"}; break;
|
||||
case 0xf9: s = {"stc"}; break;
|
||||
case 0xfa: s = {"cli"}; break;
|
||||
case 0xfb: s = {"sti"}; break;
|
||||
case 0xfc: s = {"cld"}; break;
|
||||
case 0xfd: s = {"std"}; break;
|
||||
|
||||
default:
|
||||
s = {"??? [", hex(opcode, 2L), "]"};
|
||||
}
|
||||
while(s.size() < 20) s.append(" ");
|
||||
|
||||
if(!registers) return {hex(ea, 5L), " ", s};
|
||||
|
||||
return {
|
||||
hex(ea, 5L), " ", s,
|
||||
" ax:", hex(r.ax, 4L),
|
||||
" bx:", hex(r.bx, 4L),
|
||||
" cx:", hex(r.cx, 4L),
|
||||
" dx:", hex(r.dx, 4L),
|
||||
" si:", hex(r.si, 4L),
|
||||
" di:", hex(r.di, 4L),
|
||||
" bp:", hex(r.bp, 4L),
|
||||
" sp:", hex(r.sp, 4L),
|
||||
" cs:", hex(r.cs, 4L),
|
||||
" ip:", hex(ip, 4L),
|
||||
" ds:", hex(r.ds, 4L),
|
||||
" es:", hex(r.es, 4L),
|
||||
" ss:", hex(r.ss, 4L), " ",
|
||||
r.f.m ? "M" : "m",
|
||||
r.f.v ? "V" : "v",
|
||||
r.f.d ? "D" : "d",
|
||||
r.f.i ? "I" : "i",
|
||||
r.f.b ? "B" : "b",
|
||||
r.f.s ? "S" : "s",
|
||||
r.f.z ? "Z" : "z",
|
||||
r.f.h ? "H" : "h",
|
||||
r.f.p ? "P" : "p",
|
||||
r.f.c ? "C" : "c"
|
||||
};
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
auto V30MZ::opJumpFar() {
|
||||
auto ip = readWord();
|
||||
auto cs = readWord();
|
||||
r.ip = ip;
|
||||
r.cs = cs;
|
||||
}
|
||||
|
||||
auto V30MZ::opInByte() {
|
||||
auto port = readByte();
|
||||
r.al = in(port);
|
||||
}
|
||||
|
||||
auto V30MZ::opInWord() {
|
||||
auto port = readByte();
|
||||
r.ax = in(port);
|
||||
}
|
||||
|
||||
auto V30MZ::opOutByte() {
|
||||
auto port = readByte();
|
||||
out(port, r.al);
|
||||
}
|
||||
|
||||
auto V30MZ::opOutWord() {
|
||||
auto port = readByte();
|
||||
out(port, r.ax);
|
||||
}
|
||||
|
||||
auto V30MZ::opInDXByte() {
|
||||
r.al = in(r.dx);
|
||||
}
|
||||
|
||||
auto V30MZ::opInDXWord() {
|
||||
r.ax = in(r.dx);
|
||||
}
|
||||
|
||||
auto V30MZ::opOutDXByte() {
|
||||
out(r.dx, r.al);
|
||||
}
|
||||
|
||||
auto V30MZ::opOutDXWord() {
|
||||
out(r.dx, r.ax);
|
||||
}
|
||||
|
||||
auto V30MZ::opMoveRegisterImmediateByte(uint8& breg) {
|
||||
breg = readByte();
|
||||
}
|
||||
|
||||
auto V30MZ::opMoveRegisterImmediateWord(uint16& wreg) {
|
||||
wreg = readWord();
|
||||
}
|
||||
|
||||
auto V30MZ::opNoOperation() {
|
||||
}
|
||||
|
||||
auto V30MZ::opClearFlag(bool& flag) {
|
||||
flag = false;
|
||||
}
|
||||
|
||||
auto V30MZ::opSetFlag(bool& flag) {
|
||||
flag = true;
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
auto V30MZ::readByte() -> uint8 {
|
||||
return read((r.cs << 4) + (r.ip++));
|
||||
}
|
||||
|
||||
auto V30MZ::readWord() -> uint16 {
|
||||
uint16 word = read((r.cs << 4) + (r.ip++)) << 0;
|
||||
return word | read((r.cs << 4) + (r.ip++)) << 8;
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
V30MZ::Registers::Flags::operator uint16() const {
|
||||
return m << 15 | 1 << 14 | 1 << 13 | 1 << 12
|
||||
| v << 11 | d << 10 | i << 9 | b << 8
|
||||
| s << 7 | z << 6 | h << 4 | p << 2
|
||||
| 1 << 1 | c << 0;
|
||||
}
|
||||
|
||||
auto V30MZ::Registers::Flags::operator=(uint16 data) {
|
||||
m = (uint1)(data >> 15);
|
||||
v = (uint1)(data >> 11);
|
||||
d = (uint1)(data >> 10);
|
||||
i = (uint1)(data >> 9);
|
||||
b = (uint1)(data >> 8);
|
||||
s = (uint1)(data >> 7);
|
||||
z = (uint1)(data >> 6);
|
||||
h = (uint1)(data >> 4);
|
||||
p = (uint1)(data >> 2);
|
||||
c = (uint1)(data >> 0);
|
||||
return *this;
|
||||
}
|
|
@ -3,23 +3,85 @@
|
|||
|
||||
namespace Processor {
|
||||
|
||||
#include "registers.cpp"
|
||||
#include "memory.cpp"
|
||||
#include "instructions.cpp"
|
||||
#include "disassembler.cpp"
|
||||
|
||||
auto V30MZ::exec() -> void {
|
||||
step(1);
|
||||
if(halt) return wait(1);
|
||||
|
||||
#if 1
|
||||
print(disassemble(r.cs, r.ip), "\n");
|
||||
#endif
|
||||
|
||||
execOpcode();
|
||||
}
|
||||
|
||||
auto V30MZ::execOpcode() -> void {
|
||||
executed++;
|
||||
|
||||
uint8 opcode = readByte();
|
||||
wait(1);
|
||||
|
||||
switch(opcode) {
|
||||
case 0x90: return opNoOperation();
|
||||
case 0xb0: return opMoveRegisterImmediateByte(r.al);
|
||||
case 0xb1: return opMoveRegisterImmediateByte(r.cl);
|
||||
case 0xb2: return opMoveRegisterImmediateByte(r.dl);
|
||||
case 0xb3: return opMoveRegisterImmediateByte(r.bl);
|
||||
case 0xb4: return opMoveRegisterImmediateByte(r.ah);
|
||||
case 0xb5: return opMoveRegisterImmediateByte(r.ch);
|
||||
case 0xb6: return opMoveRegisterImmediateByte(r.dh);
|
||||
case 0xb7: return opMoveRegisterImmediateByte(r.bh);
|
||||
case 0xb8: return opMoveRegisterImmediateWord(r.ax);
|
||||
case 0xb9: return opMoveRegisterImmediateWord(r.cx);
|
||||
case 0xba: return opMoveRegisterImmediateWord(r.dx);
|
||||
case 0xbb: return opMoveRegisterImmediateWord(r.bx);
|
||||
case 0xbc: return opMoveRegisterImmediateWord(r.sp);
|
||||
case 0xbd: return opMoveRegisterImmediateWord(r.bp);
|
||||
case 0xbe: return opMoveRegisterImmediateWord(r.si);
|
||||
case 0xbf: return opMoveRegisterImmediateWord(r.di);
|
||||
case 0xe4: return opInByte();
|
||||
case 0xe5: return opInWord();
|
||||
case 0xe6: return opOutByte();
|
||||
case 0xe7: return opOutWord();
|
||||
case 0xea: return opJumpFar();
|
||||
case 0xec: return opInDXByte();
|
||||
case 0xed: return opInDXWord();
|
||||
case 0xee: return opOutDXByte();
|
||||
case 0xef: return opOutDXWord();
|
||||
case 0xf8: return opClearFlag(r.f.c);
|
||||
case 0xf9: return opSetFlag(r.f.c);
|
||||
case 0xfa: return opClearFlag(r.f.i);
|
||||
case 0xfb: return opSetFlag(r.f.i);
|
||||
case 0xfc: return opClearFlag(r.f.d);
|
||||
case 0xfd: return opSetFlag(r.f.d);
|
||||
}
|
||||
|
||||
print("error: unknown opcode: ", hex(opcode, 2L), "\n");
|
||||
print("executed: ", --executed, "\n");
|
||||
halt = true;
|
||||
}
|
||||
|
||||
auto V30MZ::power() -> void {
|
||||
halt = false;
|
||||
executed = 0;
|
||||
|
||||
r.ip = 0x0000;
|
||||
r.ax = 0x0000;
|
||||
r.bx = 0x0000;
|
||||
r.cx = 0x0000;
|
||||
r.dx = 0x0000;
|
||||
r.bx = 0x0000;
|
||||
r.sp = 0x0000;
|
||||
r.bp = 0x0000;
|
||||
r.si = 0x0000;
|
||||
r.di = 0x0000;
|
||||
r.es = 0x0000;
|
||||
r.bp = 0x0000;
|
||||
r.sp = 0x0000;
|
||||
r.cs = 0xffff;
|
||||
r.ss = 0x0000;
|
||||
r.ds = 0x0000;
|
||||
r.es = 0x0000;
|
||||
r.ss = 0x0000;
|
||||
r.f = 0x8000;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -5,26 +5,72 @@
|
|||
namespace Processor {
|
||||
|
||||
struct V30MZ {
|
||||
virtual auto step(uint clocks) -> void = 0;
|
||||
virtual auto read(uint32 addr) -> uint8 = 0;
|
||||
virtual auto write(uint32 addr, uint8 data) -> void = 0;
|
||||
virtual auto wait(uint clocks = 1) -> void = 0;
|
||||
virtual auto read(uint20 addr) -> uint8 = 0;
|
||||
virtual auto write(uint20 addr, uint8 data) -> void = 0;
|
||||
virtual auto in(uint16 port) -> uint16 = 0;
|
||||
virtual auto out(uint16 port, uint16 data) -> void = 0;
|
||||
|
||||
auto exec() -> void;
|
||||
auto execOpcode() -> void;
|
||||
auto power() -> void;
|
||||
|
||||
//memory.cpp
|
||||
auto readByte() -> uint8;
|
||||
auto readWord() -> uint16;
|
||||
|
||||
//instructions.cpp
|
||||
auto opJumpFar();
|
||||
auto opInByte();
|
||||
auto opInWord();
|
||||
auto opOutByte();
|
||||
auto opOutWord();
|
||||
auto opInDXByte();
|
||||
auto opInDXWord();
|
||||
auto opOutDXByte();
|
||||
auto opOutDXWord();
|
||||
auto opMoveRegisterImmediateByte(uint8&);
|
||||
auto opMoveRegisterImmediateWord(uint16&);
|
||||
auto opNoOperation();
|
||||
auto opClearFlag(bool&);
|
||||
auto opSetFlag(bool&);
|
||||
|
||||
//disassembler.cpp
|
||||
auto disassemble(uint16 cs, uint16 ip, bool registers = true) -> string;
|
||||
|
||||
//state
|
||||
bool halt = false;
|
||||
uint executed = 0;
|
||||
|
||||
struct Registers {
|
||||
struct { uint16 ax; uint8 order_lsb2(al, ah); };
|
||||
struct { uint16 cx; uint8 order_lsb2(cl, ch); };
|
||||
struct { uint16 dx; uint8 order_lsb2(dl, dh); };
|
||||
struct { uint16 bx; uint8 order_lsb2(bl, bh); };
|
||||
uint16 sp;
|
||||
uint16 bp;
|
||||
uint16 ip;
|
||||
union { struct { uint16 ax; uint8 order_lsb2(al, ah); }; };
|
||||
union { struct { uint16 bx; uint8 order_lsb2(bl, bh); }; };
|
||||
union { struct { uint16 cx; uint8 order_lsb2(cl, ch); }; };
|
||||
union { struct { uint16 dx; uint8 order_lsb2(dl, dh); }; };
|
||||
uint16 si;
|
||||
uint16 di;
|
||||
uint16 es;
|
||||
uint16 bp;
|
||||
uint16 sp;
|
||||
uint16 cs;
|
||||
uint16 ss;
|
||||
uint16 ds;
|
||||
uint16 es;
|
||||
uint16 ss;
|
||||
struct Flags {
|
||||
operator uint16() const;
|
||||
auto operator=(uint16 data);
|
||||
|
||||
bool m; //mode
|
||||
bool v; //overflow
|
||||
bool d; //direction
|
||||
bool i; //interrupt
|
||||
bool b; //break
|
||||
bool s; //sign
|
||||
bool z; //zero
|
||||
bool h; //half-carry
|
||||
bool p; //parity
|
||||
bool c; //carry
|
||||
} f;
|
||||
} r;
|
||||
};
|
||||
|
||||
|
|
|
@ -107,5 +107,6 @@ else ifeq ($(platform),macosx)
|
|||
if [ -d /Applications/$(name).app ]; then rm -r /Applications/$(name).app; fi
|
||||
else ifneq ($(filter $(platform),linux bsd),)
|
||||
if [ -f $(prefix)/bin/$(name) ]; then rm $(prefix)/bin/$(name); fi
|
||||
if [ -f $(prefix)/share/applications/$(name).desktop ]; then rm $(prefix)/share/applications/$(name).desktop; fi
|
||||
if [ -f $(prefix)/share/icons/$(name).png ]; then rm $(prefix)/share/icons/$(name).png; fi
|
||||
endif
|
||||
|
|
|
@ -20,14 +20,16 @@ auto Cartridge::load() -> void {
|
|||
if(auto node = document["board/rom"]) {
|
||||
rom.name = node["name"].text();
|
||||
rom.size = node["size"].natural();
|
||||
if(rom.size) rom.data = new uint8[rom.size]();
|
||||
rom.mask = bit::round(rom.size) - 1;
|
||||
if(rom.size) rom.data = new uint8[rom.mask + 1]();
|
||||
if(rom.name) interface->loadRequest(ID::ROM, rom.name, true);
|
||||
}
|
||||
|
||||
if(auto node = document["board/ram"]) {
|
||||
ram.name = node["name"].text();
|
||||
ram.size = node["size"].natural();
|
||||
if(ram.size) ram.data = new uint8[ram.size]();
|
||||
ram.mask = bit::round(ram.size) - 1;
|
||||
if(ram.size) ram.data = new uint8[ram.mask + 1]();
|
||||
if(ram.name) interface->loadRequest(ID::RAM, ram.name, true);
|
||||
}
|
||||
|
||||
|
@ -42,19 +44,21 @@ auto Cartridge::unload() -> void {
|
|||
delete[] rom.data;
|
||||
rom.data = nullptr;
|
||||
rom.size = 0;
|
||||
rom.mask = 0;
|
||||
rom.name = "";
|
||||
|
||||
delete[] ram.data;
|
||||
ram.data = nullptr;
|
||||
ram.size = 0;
|
||||
ram.mask = 0;
|
||||
ram.name = "";
|
||||
}
|
||||
|
||||
auto Cartridge::power() -> void {
|
||||
r.bank_rom0 = 0x00;
|
||||
r.bank_rom1 = 0x00;
|
||||
r.bank_rom2 = 0x00;
|
||||
r.bank_sram = 0x00;
|
||||
r.bank_rom0 = 0xff;
|
||||
r.bank_rom1 = 0xff;
|
||||
r.bank_rom2 = 0xff;
|
||||
r.bank_sram = 0xff;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@ struct Cartridge {
|
|||
struct Memory {
|
||||
uint8* data = nullptr;
|
||||
uint size = 0;
|
||||
uint mask = 0;
|
||||
string name;
|
||||
} rom, ram;
|
||||
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
//20000-fffff
|
||||
auto Cartridge::romRead(uint addr) -> uint8 {
|
||||
switch(addr >> 16) {
|
||||
switch((uint4)(addr >> 16)) {
|
||||
case 2: addr = r.bank_rom0 << 16 | (uint16)addr; break; //20000-2ffff
|
||||
case 3: addr = r.bank_rom1 << 16 | (uint16)addr; break; //30000-3ffff
|
||||
default: addr = r.bank_rom2 << 16 | (uint16)addr; break; //40000-fffff
|
||||
default: addr = r.bank_rom2 << 20 | (uint20)addr; break; //40000-fffff
|
||||
}
|
||||
if(!rom.data || addr >= rom.size) return 0x00;
|
||||
return rom.data[addr];
|
||||
if(!rom.data) return 0x00;
|
||||
return rom.data[addr & rom.mask];
|
||||
}
|
||||
|
||||
auto Cartridge::romWrite(uint addr, uint8 data) -> void {
|
||||
|
@ -15,12 +15,12 @@ auto Cartridge::romWrite(uint addr, uint8 data) -> void {
|
|||
//10000-1ffff
|
||||
auto Cartridge::ramRead(uint addr) -> uint8 {
|
||||
addr = r.bank_sram << 16 | (uint16)addr;
|
||||
if(!ram.data || addr >= ram.size) return 0x00;
|
||||
return ram.data[addr];
|
||||
if(!ram.data) return 0x00;
|
||||
return ram.data[addr & ram.mask];
|
||||
}
|
||||
|
||||
auto Cartridge::ramWrite(uint addr, uint8 data) -> void {
|
||||
addr = r.bank_sram << 16 | (uint16)addr;
|
||||
if(!ram.data || addr >= ram.size) return;
|
||||
ram.data[addr] = data;
|
||||
if(!ram.data) return;
|
||||
ram.data[addr & ram.mask] = data;
|
||||
}
|
||||
|
|
|
@ -28,14 +28,26 @@ auto CPU::step(uint clocks) -> void {
|
|||
if(apu.clock < 0) co_switch(apu.thread);
|
||||
}
|
||||
|
||||
auto CPU::read(uint32 addr) -> uint8 {
|
||||
auto CPU::wait(uint clocks) -> void {
|
||||
step(clocks);
|
||||
}
|
||||
|
||||
auto CPU::read(uint20 addr) -> uint8 {
|
||||
return bus.read(addr);
|
||||
}
|
||||
|
||||
auto CPU::write(uint32 addr, uint8 data) -> void {
|
||||
auto CPU::write(uint20 addr, uint8 data) -> void {
|
||||
return bus.write(addr, data);
|
||||
}
|
||||
|
||||
auto CPU::in(uint16 port) -> uint16 {
|
||||
return iomap[port]->portRead(port);
|
||||
}
|
||||
|
||||
auto CPU::out(uint16 port, uint16 data) -> void {
|
||||
return iomap[port]->portWrite(port, data);
|
||||
}
|
||||
|
||||
auto CPU::power() -> void {
|
||||
V30MZ::power();
|
||||
create(CPU::Enter, 3072000);
|
||||
|
|
|
@ -1,14 +1,23 @@
|
|||
struct CPU : Processor::V30MZ, Thread {
|
||||
struct CPU : Processor::V30MZ, Thread, IO {
|
||||
static auto Enter() -> void;
|
||||
|
||||
auto main() -> void;
|
||||
auto step(uint clocks) -> void override;
|
||||
auto read(uint32 addr) -> uint8 override;
|
||||
auto write(uint32 addr, uint8 data) -> void override;
|
||||
auto step(uint clocks) -> void;
|
||||
|
||||
auto wait(uint clocks = 1) -> void override;
|
||||
auto read(uint20 addr) -> uint8 override;
|
||||
auto write(uint20 addr, uint8 data) -> void override;
|
||||
auto in(uint16 port) -> uint16 override;
|
||||
auto out(uint16 port, uint16 data) -> void override;
|
||||
|
||||
auto power() -> void;
|
||||
|
||||
auto ramRead(uint addr) -> uint8;
|
||||
auto ramWrite(uint addr, uint8 data) -> void;
|
||||
//memory.cpp
|
||||
auto ramRead(uint16 addr) -> uint8;
|
||||
auto ramWrite(uint16 addr, uint8 data) -> void;
|
||||
|
||||
auto portRead(uint16 addr) -> uint16 override;
|
||||
auto portWrite(uint16 addr, uint16 data) -> void override;
|
||||
};
|
||||
|
||||
extern CPU cpu;
|
||||
|
|
|
@ -1,9 +1,16 @@
|
|||
auto CPU::ramRead(uint addr) -> uint8 {
|
||||
uint mask = system.monochrome() ? 0x3fff : 0xffff;
|
||||
return iram[addr & mask];
|
||||
auto CPU::ramRead(uint16 addr) -> uint8 {
|
||||
if(WS() && addr >= 0x4000) return 0x90;
|
||||
return iram[addr];
|
||||
}
|
||||
|
||||
auto CPU::ramWrite(uint addr, uint8 data) -> void {
|
||||
uint mask = system.monochrome() ? 0x3fff : 0xffff;
|
||||
iram[addr & mask] = data;
|
||||
auto CPU::ramWrite(uint16 addr, uint8 data) -> void {
|
||||
if(WS() && addr >= 0x4000) return;
|
||||
iram[addr] = data;
|
||||
}
|
||||
|
||||
auto CPU::portRead(uint16 addr) -> uint16 {
|
||||
return 0x0000;
|
||||
}
|
||||
|
||||
auto CPU::portWrite(uint16 addr, uint16 data) -> void {
|
||||
}
|
||||
|
|
|
@ -21,24 +21,40 @@ Interface::Interface() {
|
|||
media.append({ID::WonderSwan, "WonderSwan", "ws", true});
|
||||
media.append({ID::WonderSwanColor, "WonderSwan Color", "wsc", true});
|
||||
|
||||
{ Device device{0, ID::Device, "Controller"};
|
||||
device.input.append({ 0, 0, "X1" });
|
||||
device.input.append({ 1, 0, "X2" });
|
||||
device.input.append({ 2, 0, "X3" });
|
||||
device.input.append({ 3, 0, "X4" });
|
||||
device.input.append({ 4, 0, "Y1" });
|
||||
device.input.append({ 5, 0, "Y2" });
|
||||
device.input.append({ 6, 0, "Y3" });
|
||||
device.input.append({ 7, 0, "Y4" });
|
||||
device.input.append({ 8, 0, "B" });
|
||||
device.input.append({ 9, 0, "A" });
|
||||
device.input.append({10, 0, "Sound"});
|
||||
device.input.append({11, 0, "Start"});
|
||||
device.order = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};
|
||||
{ Device device{0, ID::DeviceHorizontal, "Controller"};
|
||||
device.input.append({ 0, 0, "X1"});
|
||||
device.input.append({ 1, 0, "X2"});
|
||||
device.input.append({ 2, 0, "X3"});
|
||||
device.input.append({ 3, 0, "X4"});
|
||||
device.input.append({ 4, 0, "Y1"});
|
||||
device.input.append({ 5, 0, "Y2"});
|
||||
device.input.append({ 6, 0, "Y3"});
|
||||
device.input.append({ 7, 0, "Y4"});
|
||||
device.input.append({ 8, 0, "B"});
|
||||
device.input.append({ 9, 0, "A"});
|
||||
device.input.append({10, 0, "Start"});
|
||||
device.order = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
|
||||
this->device.append(device);
|
||||
}
|
||||
|
||||
port.append({0, "Device", {device[0]}});
|
||||
{ Device device{1, ID::DeviceVertical, "Controller"};
|
||||
device.input.append({ 0, 0, "X1"});
|
||||
device.input.append({ 1, 0, "X2"});
|
||||
device.input.append({ 2, 0, "X3"});
|
||||
device.input.append({ 3, 0, "X4"});
|
||||
device.input.append({ 4, 0, "Y1"});
|
||||
device.input.append({ 5, 0, "Y2"});
|
||||
device.input.append({ 6, 0, "Y3"});
|
||||
device.input.append({ 7, 0, "Y4"});
|
||||
device.input.append({ 8, 0, "B"});
|
||||
device.input.append({ 9, 0, "A"});
|
||||
device.input.append({10, 0, "Start"});
|
||||
device.order = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
|
||||
this->device.append(device);
|
||||
}
|
||||
|
||||
port.append({0, "Horizontal Orientation", {device[0]}});
|
||||
port.append({1, "Vertical Orientation", {device[1]}});
|
||||
}
|
||||
|
||||
auto Interface::manifest() -> string {
|
||||
|
|
|
@ -16,7 +16,8 @@ struct ID {
|
|||
};
|
||||
|
||||
enum : uint {
|
||||
Device = 1,
|
||||
DeviceHorizontal = 1,
|
||||
DeviceVertical = 2,
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
@ -3,12 +3,21 @@
|
|||
namespace WonderSwan {
|
||||
|
||||
uint8 iram[64 * 1024] = {0};
|
||||
IO* io[256];
|
||||
IO* iomap[64 * 1024] = {0};
|
||||
Bus bus;
|
||||
|
||||
auto IO::power() -> void {
|
||||
static IO unmapped;
|
||||
for(auto& n : io) n = &unmapped;
|
||||
for(auto& n : iomap) n = &unmapped;
|
||||
}
|
||||
|
||||
auto IO::portRead(uint16 addr) -> uint16 {
|
||||
print("[", hex(addr, 4L), "]: port unmapped\n");
|
||||
return 0x0000;
|
||||
}
|
||||
|
||||
auto IO::portWrite(uint16 addr, uint16 data) -> void {
|
||||
print("[", hex(addr, 4L), "] = ", hex(data, 4L), ": port unmapped\n");
|
||||
}
|
||||
|
||||
auto Bus::read(uint20 addr) -> uint8 {
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
struct IO {
|
||||
static auto power() -> void;
|
||||
|
||||
virtual auto in(uint8 addr) -> uint8 { return 0x00; }
|
||||
virtual auto out(uint8 addr, uint8 data) -> void {}
|
||||
virtual auto portRead(uint16 addr) -> uint16;
|
||||
virtual auto portWrite(uint16 addr, uint16 data) -> void;
|
||||
};
|
||||
|
||||
struct Bus {
|
||||
|
@ -11,5 +11,5 @@ struct Bus {
|
|||
};
|
||||
|
||||
extern uint8 iram[64 * 1024];
|
||||
extern IO* io[256];
|
||||
extern IO* iomap[64 * 1024];
|
||||
extern Bus bus;
|
||||
|
|
|
@ -39,6 +39,8 @@ auto PPU::power() -> void {
|
|||
|
||||
status.vclk = 0;
|
||||
status.hclk = 0;
|
||||
|
||||
video.power();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -9,16 +9,6 @@ Video::Video() {
|
|||
auto Video::power() -> void {
|
||||
memory::fill(output(), 224 * 224 * sizeof(uint32));
|
||||
|
||||
if(system.monochrome()) {
|
||||
for(auto color : range(16)) {
|
||||
paletteLiteral[color] = color;
|
||||
|
||||
uint L = image::normalize(15 - color, 4, 16);
|
||||
paletteStandard[color] = interface->videoColor(L, L, L);
|
||||
}
|
||||
}
|
||||
|
||||
if(system.color()) {
|
||||
for(auto color : range(1 << 12)) {
|
||||
paletteLiteral[color] = color;
|
||||
|
||||
|
@ -31,7 +21,6 @@ auto Video::power() -> void {
|
|||
B = image::normalize(B, 4, 16);
|
||||
paletteStandard[color] = interface->videoColor(R, G, B);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto Video::refresh() -> void {
|
||||
|
|
|
@ -8,14 +8,6 @@ auto System::revision() const -> Revision {
|
|||
return _revision;
|
||||
}
|
||||
|
||||
auto System::monochrome() const -> bool {
|
||||
return revision() == Revision::WonderSwan;
|
||||
}
|
||||
|
||||
auto System::color() const -> bool {
|
||||
return revision() != Revision::WonderSwan;
|
||||
}
|
||||
|
||||
auto System::init() -> void {
|
||||
}
|
||||
|
||||
|
@ -28,6 +20,8 @@ auto System::load(Revision revision) -> void {
|
|||
interface->loadRequest(ID::SystemManifest, "manifest.bml", true);
|
||||
auto document = BML::unserialize(information.manifest);
|
||||
|
||||
//note: IPLROM is currently undumped; otherwise we'd load it here ...
|
||||
|
||||
cartridge.load();
|
||||
}
|
||||
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
struct System {
|
||||
enum class Revision : uint { WonderSwan, WonderSwanColor, SwanCrystal };
|
||||
enum class Revision : uint {
|
||||
WonderSwan, //SW-001 (ASWAN)
|
||||
WonderSwanColor, //WSC-001 (SPHINX)
|
||||
SwanCrystal, //SCT-001 (SPHINX2)
|
||||
};
|
||||
|
||||
auto revision() const -> Revision;
|
||||
auto monochrome() const -> bool;
|
||||
auto color() const -> bool;
|
||||
|
||||
auto init() -> void;
|
||||
auto term() -> void;
|
||||
|
@ -16,7 +18,7 @@ struct System {
|
|||
} information;
|
||||
|
||||
privileged:
|
||||
Revision _revision;
|
||||
Revision _revision = Revision::WonderSwan;
|
||||
};
|
||||
|
||||
extern System system;
|
||||
|
|
|
@ -49,6 +49,10 @@ namespace WonderSwan {
|
|||
#include <ws/cpu/cpu.hpp>
|
||||
#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; }
|
||||
}
|
||||
|
||||
#include <ws/interface/interface.hpp>
|
||||
|
|
Loading…
Reference in New Issue