Update to v097r05 release.

byuu says:

More V30MZ implemented, a lot more to go.

icarus now supports importing WS and WSC games. It expects them to have
the correct file extension, same for GB and GBC.

> Ugh, apparently HiDPI icarus doesn't let you press the check boxes.

I set the flag value in the plist to false for now. Forgot to do it for
higan, but hopefully I won't forget before release.
This commit is contained in:
Tim Allen 2016-01-30 17:40:35 +11:00
parent a8323d0d2b
commit 605a8aa3e9
37 changed files with 719 additions and 198 deletions

View File

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

View File

@ -4,6 +4,7 @@ namespace Emulator {
struct Interface {
struct Information {
string manufacturer;
string name;
uint width;
uint height;

View File

@ -8,12 +8,14 @@ Settings settings;
Interface::Interface() {
interface = this;
information.manufacturer = "Nintendo";
information.name = "Famicom";
information.width = 256;
information.height = 240;
information.overscan = true;
information.aspectRatio = 8.0 / 7.0;
information.resettable = true;
information.capability.states = true;
information.capability.cheats = true;

View File

@ -9,12 +9,14 @@ Interface::Interface() {
interface = this;
hook = nullptr;
information.manufacturer = "Nintendo";
information.name = "Game Boy";
information.width = 160;
information.height = 144;
information.overscan = false;
information.aspectRatio = 1.0;
information.resettable = false;
information.capability.states = true;
information.capability.cheats = true;

View File

@ -8,12 +8,14 @@ Settings settings;
Interface::Interface() {
interface = this;
information.manufacturer = "Nintendo";
information.name = "Game Boy Advance";
information.width = 240;
information.height = 160;
information.overscan = false;
information.aspectRatio = 1.0;
information.resettable = false;
information.capability.states = true;
information.capability.cheats = false;

View File

@ -0,0 +1,30 @@
//(0 = odd, 1 = even) number of bits set in value
auto V30MZ::parity(uint16 value) const -> bool {
value ^= value >> 8;
value ^= value >> 4;
value ^= value >> 2;
value ^= value >> 1;
return !(value & 1);
}
auto V30MZ::albAnd(uint8 x, uint8 y) -> uint8 {
uint8 result = x & y;
r.f.c = 0;
r.f.p = parity(result);
r.f.h = 0;
r.f.z = result == 0;
r.f.s = result & 0x80;
r.f.v = 0;
return result;
}
auto V30MZ::alwAnd(uint16 x, uint16 y) -> uint16 {
uint16 result = x & y;
r.f.c = 0;
r.f.p = parity(result);
r.f.h = 0;
r.f.z = result == 0;
r.f.s = result & 0x8000;
r.f.v = 0;
return result;
}

View File

@ -2,19 +2,67 @@ 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);
enum : uint { Byte, Word, Segment };
auto readByte = [&](uint offset) -> string {
uint8 byte = read((cs << 4) + (uint16)(ip + offset++));
return {"$", hex(byte, 2L)};
};
auto readWord = [&](uint offset) {
auto readWord = [&](uint offset) -> string {
uint16 word = read((cs << 4) + (uint16)(ip + offset++)) << 0;
word | read((cs << 4) + (uint16)(ip + offset++)) << 8;
return hex(word, 4L);
word |= read((cs << 4) + (uint16)(ip + offset++)) << 8;
return {"$", hex(word, 4L)};
};
auto readRelativeByte = [&](uint offset, uint displacement) -> string {
uint8 byte = read((cs << 4) + (uint16)(ip + offset++));
return {"$", hex(ip + displacement + (int8)byte, 4L)};
};
auto readRelativeWord = [&](uint offset, uint displacement) -> string {
uint16 word = read((cs << 4) + (uint16)(ip + offset++)) << 0;
word |= read((cs << 4) + (uint16)(ip + offset++)) << 8;
return {"$", hex(ip + displacement + (int16)word, 4L)};
};
auto readModRM = [&](uint offset, uint mode) -> string {
uint8 modRM = read((cs << 4) + (uint16)(ip + offset++));
static const string reg[3][8] = {
{"al", "cl", "dl", "bl", "ah", "ch", "dh", "bh"},
{"ax", "cx", "dx", "bx", "sp", "bp", "si", "di"},
{"es", "cs", "ss", "ds", "es", "cs", "ss", "ds"},
};
string d = reg[mode][(uint3)(modRM >> 3)];
if(modRM >= 0xc0) return {d, ",", reg[Word][modRM & 7]};
if((modRM & 0xc7) == 0x06) return {d, ",[", readWord(offset), "]"};
static const string mem[8] = {"bx+si", "bx+di", "bp+si", "bp+di", "si", "di", "bp", "bx"};
if((modRM & 0xc0) == 0x40) return {d, ",[", mem[modRM & 7], "+", readByte(offset), "]"};
if((modRM & 0xc0) == 0x80) return {d, ",[", mem[modRM & 7], "+", readWord(offset), "]"};
return {d, ",[", mem[modRM & 7], "]"};
};
uint8 opcode = read(ea);
switch(opcode) {
case 0x90: s = {"nop"}; break;
case 0x22: s = {"xor ", readModRM(1, Byte)}; break;
case 0x33: s = {"xor ", readModRM(1, Word)}; break;
case 0x70: s = {"jo ", readRelativeByte(1, 2)}; break;
case 0x71: s = {"jno ", readRelativeByte(1, 2)}; break;
case 0x72: s = {"jc ", readRelativeByte(1, 2)}; break;
case 0x73: s = {"jnc ", readRelativeByte(1, 2)}; break;
case 0x74: s = {"jz ", readRelativeByte(1, 2)}; break;
case 0x75: s = {"jnz ", readRelativeByte(1, 2)}; break;
case 0x76: s = {"jcz ", readRelativeByte(1, 2)}; break;
case 0x77: s = {"jncz ", readRelativeByte(1, 2)}; break;
case 0x78: s = {"js ", readRelativeByte(1, 2)}; break;
case 0x79: s = {"jns ", readRelativeByte(1, 2)}; break;
case 0x7a: s = {"jp ", readRelativeByte(1, 2)}; break;
case 0x7b: s = {"jnp ", readRelativeByte(1, 2)}; break;
case 0x7c: s = {"jl ", readRelativeByte(1, 2)}; break;
case 0x7d: s = {"jnl ", readRelativeByte(1, 2)}; break;
case 0x7e: s = {"jle ", readRelativeByte(1, 2)}; break;
case 0x7f: s = {"jnle ", readRelativeByte(1, 2)}; break;
case 0x8a: s = {"mov ", readModRM(1, Byte)}; break;
case 0x8b: s = {"mov ", readModRM(1, Word)}; break;
case 0x8e: s = {"mov ", readModRM(1, Segment)}; break;
case 0x90: s = {"nop "}; break;
case 0xa8: s = {"test al,", readByte(1)}; break;
case 0xa9: s = {"test ax,", readWord(1)}; 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;
@ -31,21 +79,23 @@ auto V30MZ::disassemble(uint16 cs, uint16 ip, bool registers) -> string {
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 0xc3: s = {"ret "}; 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 0xe8: s = {"call ", readRelativeWord(1, 3)}; 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;
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), "]"};
@ -64,8 +114,8 @@ auto V30MZ::disassemble(uint16 cs, uint16 ip, bool registers) -> string {
" di:", hex(r.di, 4L),
" bp:", hex(r.bp, 4L),
" sp:", hex(r.sp, 4L),
" ip:", hex(r.ip, 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), " ",

View File

@ -1,61 +1,127 @@
auto V30MZ::opJumpFar() {
auto ip = readWord();
auto cs = readWord();
r.ip = ip;
r.cs = cs;
auto V30MZ::opbXorRegisterModRM() {
auto md = readbIP();
regb(md) ^= readbModRM(md);
}
auto V30MZ::opInByte() {
auto port = readByte();
r.al = in(port);
auto V30MZ::opwXorRegisterModRM() {
auto md = readbIP();
regw(md) ^= readwModRM(md);
}
auto V30MZ::opInWord() {
auto port = readByte();
r.ax = in(port);
auto V30MZ::opJumpIf(bool condition) {
auto displacement = (int8)readbIP();
if(condition) r.ip += displacement;
}
auto V30MZ::opOutByte() {
auto port = readByte();
out(port, r.al);
auto V30MZ::opbMoveRegisterModRM() {
auto md = readbIP();
regb(md) = readbModRM(md);
}
auto V30MZ::opOutWord() {
auto port = readByte();
out(port, r.ax);
auto V30MZ::opwMoveRegisterModRM() {
auto md = readbIP();
regw(md) = readwModRM(md);
}
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::opMoveSegmentRegisterModRM() {
wait(1);
auto md = readbIP();
sreg(md) = readwModRM(md);
}
auto V30MZ::opNoOperation() {
}
auto V30MZ::opTestAL() {
albAnd(r.al, readbIP());
}
auto V30MZ::opTestAX() {
alwAnd(r.ax, readwIP());
}
auto V30MZ::opbMoveRegisterImmediate(uint8& rd) {
rd = readbIP();
}
auto V30MZ::opwMoveRegisterImmediate(uint16& rd) {
rd = readwIP();
}
auto V30MZ::opReturn() {
wait(5);
r.ip = readSP();
}
auto V30MZ::opbIn() {
wait(5);
auto port = readbIP();
r.al = in(port);
}
auto V30MZ::opwIn() {
wait(5);
auto port = readbIP();
r.al = in(port + 0);
r.ah = in(port + 1);
}
auto V30MZ::opbOut() {
wait(5);
auto port = readbIP();
out(port, r.al);
}
auto V30MZ::opwOut() {
wait(5);
auto port = readbIP();
out(port + 0, r.al);
out(port + 1, r.ah);
}
auto V30MZ::opCallNear() {
wait(4);
auto displacement = (int16)readwIP();
writeSP(r.ip);
r.ip += displacement;
}
auto V30MZ::opJumpFar() {
wait(6);
auto ip = readwIP();
auto cs = readwIP();
r.ip = ip;
r.cs = cs;
}
auto V30MZ::opbInDX() {
wait(5);
r.al = in(r.dx);
}
auto V30MZ::opwInDX() {
wait(5);
r.al = in(r.dx + 0);
r.ah = in(r.dx + 1);
}
auto V30MZ::opbOutDX() {
wait(5);
out(r.dx, r.al);
}
auto V30MZ::opwOutDX() {
wait(5);
out(r.dx + 0, r.al);
out(r.dx + 1, r.ah);
}
auto V30MZ::opClearFlag(bool& flag) {
wait(3);
flag = false;
}
auto V30MZ::opSetFlag(bool& flag) {
wait(3);
flag = true;
}

View File

@ -1,8 +1,75 @@
auto V30MZ::readByte() -> uint8 {
return read((r.cs << 4) + (r.ip++));
auto V30MZ::readbIP() -> 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;
auto V30MZ::readwIP() -> uint16 {
uint16 word = read((r.cs << 4) + r.ip++) << 0;
return word | read((r.cs << 4) + r.ip++) << 8;
}
//
auto V30MZ::readSP() -> uint16 {
uint16 word = read((r.ss << 4) + r.sp++) << 0;
return word | read((r.ss << 4) + r.sp++) << 8;
}
auto V30MZ::writeSP(uint16 data) -> void {
write((r.ss << 4) + --r.sp, data >> 8);
write((r.ss << 4) + --r.sp, data >> 0);
}
//
auto V30MZ::readb(uint20 ea) -> uint8 {
return read(ea++);
}
auto V30MZ::readw(uint20 ea) -> uint16 {
uint16 word = read(ea++) << 0;
return word | read(ea++) << 8;
}
//
auto V30MZ::readb(uint16 rs, uint16 ea) -> uint8 {
return read((rs << 4) + ea++);
}
auto V30MZ::readw(uint16 rs, uint16 ea) -> uint16 {
uint16 word = read((rs << 4) + ea++) << 0;
return word | read((rs << 4) + ea++) << 8;
}
//
//todo: return tuple<uint16 seg, uint16 ea>
auto V30MZ::readModRM(uint8 modRM) -> uint20 {
if((modRM & 0xc7) == 0x06) return (r.ds << 4) + (int16)readwIP();
int16 displacement = 0;
if((modRM & 0xc0) == 0x40) displacement = (int8)readbIP();
if((modRM & 0xc0) == 0x80) displacement = (int16)readwIP();
switch(modRM & 7) {
case 0: return (r.ds << 4) + r.bx + r.si + displacement;
case 1: return (r.ds << 4) + r.bx + r.di + displacement;
case 2: return (r.ss << 4) + r.bp + r.si + displacement;
case 3: return (r.ss << 4) + r.bp + r.di + displacement;
case 4: return (r.ds << 4) + r.si + displacement;
case 5: return (r.ds << 4) + r.di + displacement;
case 6: return (r.ss << 4) + r.bp + displacement;
case 7: return (r.ds << 4) + r.bx + displacement;
}
unreachable;
}
auto V30MZ::readbModRM(uint8 modRM) -> uint8 {
if(modRM >= 0xc0) return regb(modRM);
return readb(readModRM(modRM));
}
auto V30MZ::readwModRM(uint8 modRM) -> uint16 {
if(modRM >= 0xc0) return regw(modRM);
return readw(readModRM(modRM));
}

View File

@ -1,3 +1,18 @@
auto V30MZ::regb(uint8 modRM) -> uint8& {
static uint8* p[] = {&r.al, &r.cl, &r.dl, &r.bl, &r.ah, &r.ch, &r.dh, &r.bh};
return *p[(modRM >> 3) & 7];
}
auto V30MZ::regw(uint8 modRM) -> uint16& {
static uint16* p[] = {&r.ax, &r.cx, &r.dx, &r.bx, &r.sp, &r.bp, &r.si, &r.di};
return *p[(modRM >> 3) & 7];
}
auto V30MZ::sreg(uint8 modRM) -> uint16& {
static uint16* p[] = {&r.es, &r.cs, &r.ss, &r.ds};
return *p[(modRM >> 3) & 3];
}
V30MZ::Registers::Flags::operator uint16() const {
return m << 15 | 1 << 14 | 1 << 13 | 1 << 12
| v << 11 | d << 10 | i << 9 | b << 8

View File

@ -5,6 +5,7 @@ namespace Processor {
#include "registers.cpp"
#include "memory.cpp"
#include "algorithms.cpp"
#include "instructions.cpp"
#include "disassembler.cpp"
@ -21,36 +22,61 @@ auto V30MZ::exec() -> void {
auto V30MZ::execOpcode() -> void {
executed++;
uint8 opcode = readByte();
uint8 opcode = readbIP();
wait(1);
switch(opcode) {
case 0x32: return opbXorRegisterModRM();
case 0x33: return opwXorRegisterModRM();
case 0x70: return opJumpIf(r.f.v == 1);
case 0x71: return opJumpIf(r.f.v == 0);
case 0x72: return opJumpIf(r.f.c == 1);
case 0x73: return opJumpIf(r.f.c == 0);
case 0x74: return opJumpIf(r.f.z == 1);
case 0x75: return opJumpIf(r.f.z == 0);
case 0x76: return opJumpIf(r.f.z == 1 || r.f.c == 1);
case 0x77: return opJumpIf(r.f.z != 1 && r.f.c != 1);
case 0x78: return opJumpIf(r.f.s == 1);
case 0x79: return opJumpIf(r.f.s == 0);
case 0x7a: return opJumpIf(r.f.p == 1);
case 0x7b: return opJumpIf(r.f.p == 0);
case 0x7c: return opJumpIf(r.f.s != r.f.v && r.f.z == 0);
case 0x7d: return opJumpIf(r.f.s == r.f.v || r.f.z == 1);
case 0x7e: return opJumpIf(r.f.s != r.f.v || r.f.z == 1);
case 0x7f: return opJumpIf(r.f.s == r.f.v && r.f.z == 0);
case 0x8a: return opbMoveRegisterModRM();
case 0x8b: return opwMoveRegisterModRM();
case 0x8e: return opMoveSegmentRegisterModRM();
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 0xa8: return opTestAL();
case 0xa9: return opTestAX();
case 0xb0: return opbMoveRegisterImmediate(r.al);
case 0xb1: return opbMoveRegisterImmediate(r.cl);
case 0xb2: return opbMoveRegisterImmediate(r.dl);
case 0xb3: return opbMoveRegisterImmediate(r.bl);
case 0xb4: return opbMoveRegisterImmediate(r.ah);
case 0xb5: return opbMoveRegisterImmediate(r.ch);
case 0xb6: return opbMoveRegisterImmediate(r.dh);
case 0xb7: return opbMoveRegisterImmediate(r.bh);
case 0xb8: return opwMoveRegisterImmediate(r.ax);
case 0xb9: return opwMoveRegisterImmediate(r.cx);
case 0xba: return opwMoveRegisterImmediate(r.dx);
case 0xbb: return opwMoveRegisterImmediate(r.bx);
case 0xbc: return opwMoveRegisterImmediate(r.sp);
case 0xbd: return opwMoveRegisterImmediate(r.bp);
case 0xbe: return opwMoveRegisterImmediate(r.si);
case 0xbf: return opwMoveRegisterImmediate(r.di);
case 0xc3: return opReturn();
case 0xe4: return opbIn();
case 0xe5: return opwIn();
case 0xe6: return opbOut();
case 0xe7: return opwOut();
case 0xe8: return opCallNear();
case 0xea: return opJumpFar();
case 0xec: return opInDXByte();
case 0xed: return opInDXWord();
case 0xee: return opOutDXByte();
case 0xef: return opOutDXWord();
case 0xec: return opbInDX();
case 0xed: return opwInDX();
case 0xee: return opbOutDX();
case 0xef: return opwOutDX();
case 0xf8: return opClearFlag(r.f.c);
case 0xf9: return opSetFlag(r.f.c);
case 0xfa: return opClearFlag(r.f.i);

View File

@ -8,30 +8,64 @@ struct V30MZ {
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;
virtual auto in(uint16 port) -> uint8 = 0;
virtual auto out(uint16 port, uint8 data) -> void = 0;
auto exec() -> void;
auto execOpcode() -> void;
auto power() -> void;
//registers.cpp
auto regb(uint8) -> uint8&;
auto regw(uint8) -> uint16&;
auto sreg(uint8) -> uint16&;
//memory.cpp
auto readByte() -> uint8;
auto readWord() -> uint16;
auto readbIP() -> uint8;
auto readwIP() -> uint16;
auto readSP() -> uint16;
auto writeSP(uint16) -> void;
auto readb(uint20 ea) -> uint8;
auto readw(uint20 ea) -> uint16;
auto readb(uint16 rs, uint16 ea) -> uint8;
auto readw(uint16 rs, uint16 ea) -> uint16;
auto readModRM(uint8) -> uint20;
auto readbModRM(uint8) -> uint8;
auto readwModRM(uint8) -> uint16;
//algorithms.cpp
auto parity(uint16) const -> bool;
auto albAnd(uint8, uint8) -> uint8;
auto alwAnd(uint16, uint16) -> 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 opbXorRegisterModRM();
auto opwXorRegisterModRM();
auto opJumpIf(bool);
auto opbMoveRegisterModRM();
auto opwMoveRegisterModRM();
auto opMoveSegmentRegisterModRM();
auto opNoOperation();
auto opTestAL();
auto opTestAX();
auto opbMoveRegisterImmediate(uint8&);
auto opwMoveRegisterImmediate(uint16&);
auto opReturn();
auto opbIn();
auto opwIn();
auto opbOut();
auto opwOut();
auto opCallNear();
auto opJumpFar();
auto opbInDX();
auto opwInDX();
auto opbOutDX();
auto opwOutDX();
auto opClearFlag(bool&);
auto opSetFlag(bool&);
@ -44,10 +78,10 @@ struct V30MZ {
struct Registers {
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); }; };
union { uint16 ax; struct { uint8 order_lsb2(al, ah); }; };
union { uint16 bx; struct { uint8 order_lsb2(bl, bh); }; };
union { uint16 cx; struct { uint8 order_lsb2(cl, ch); }; };
union { uint16 dx; struct { uint8 order_lsb2(dl, dh); }; };
uint16 si;
uint16 di;
uint16 bp;
@ -56,6 +90,7 @@ struct V30MZ {
uint16 ds;
uint16 es;
uint16 ss;
struct Flags {
operator uint16() const;
auto operator=(uint16 data);

View File

@ -9,12 +9,14 @@ Interface::Interface() {
interface = this;
system.init();
information.manufacturer = "Nintendo";
information.name = "Super Famicom";
information.width = 256;
information.height = 240;
information.overscan = true;
information.aspectRatio = 8.0 / 7.0;
information.resettable = true;
information.capability.states = true;
information.capability.cheats = true;

View File

@ -55,6 +55,11 @@ auto Cartridge::unload() -> void {
}
auto Cartridge::power() -> void {
iomap[0x00c0] = this;
iomap[0x00c1] = this;
iomap[0x00c2] = this;
iomap[0x00c3] = this;
r.bank_rom0 = 0xff;
r.bank_rom1 = 0xff;
r.bank_rom2 = 0xff;

View File

@ -1,4 +1,4 @@
struct Cartridge {
struct Cartridge : IO {
auto loaded() const -> bool;
auto load() -> void;
@ -11,6 +11,9 @@ struct Cartridge {
auto ramRead(uint addr) -> uint8;
auto ramWrite(uint addr, uint8 data) -> void;
auto portRead(uint16 addr) -> uint8 override;
auto portWrite(uint16 addr, uint8 data) -> void override;
struct Registers {
uint8 bank_rom0;
uint8 bank_rom1;

View File

@ -24,3 +24,45 @@ auto Cartridge::ramWrite(uint addr, uint8 data) -> void {
if(!ram.data) return;
ram.data[addr & ram.mask] = data;
}
auto Cartridge::portRead(uint16 addr) -> uint8 {
if(addr == 0x00c0) {
return r.bank_rom2;
}
if(addr == 0x00c1) {
return r.bank_sram;
}
if(addr == 0x00c2) {
return r.bank_rom0;
}
if(addr == 0x00c3) {
return r.bank_rom1;
}
return 0x00;
}
auto Cartridge::portWrite(uint16 addr, uint8 data) -> void {
if(addr == 0x00c0) {
r.bank_rom2 = data;
return;
}
if(addr == 0x00c1) {
r.bank_sram = data;
return;
}
if(addr == 0x00c2) {
r.bank_rom0 = data;
return;
}
if(addr == 0x00c3) {
r.bank_rom1 = data;
return;
}
}

View File

@ -40,17 +40,19 @@ auto CPU::write(uint20 addr, uint8 data) -> void {
return bus.write(addr, data);
}
auto CPU::in(uint16 port) -> uint16 {
auto CPU::in(uint16 port) -> uint8 {
return iomap[port]->portRead(port);
}
auto CPU::out(uint16 port, uint16 data) -> void {
auto CPU::out(uint16 port, uint8 data) -> void {
return iomap[port]->portWrite(port, data);
}
auto CPU::power() -> void {
V30MZ::power();
create(CPU::Enter, 3072000);
iomap[0x00a0] = this;
}
}

View File

@ -7,8 +7,8 @@ struct CPU : Processor::V30MZ, Thread, IO {
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 in(uint16 port) -> uint8 override;
auto out(uint16 port, uint8 data) -> void override;
auto power() -> void;
@ -16,8 +16,8 @@ struct CPU : Processor::V30MZ, Thread, IO {
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;
auto portRead(uint16 addr) -> uint8 override;
auto portWrite(uint16 addr, uint8 data) -> void override;
};
extern CPU cpu;

View File

@ -8,9 +8,21 @@ auto CPU::ramWrite(uint16 addr, uint8 data) -> void {
iram[addr] = data;
}
auto CPU::portRead(uint16 addr) -> uint16 {
return 0x0000;
auto CPU::portRead(uint16 addr) -> uint8 {
//HW_FLAGS
if(addr == 0x00a0) {
return 1 << 7 //1 = built-in self-test passed
| 1 << 2 //0 = 8-bit bus width; 1 = 16-bit bus width
| !WS() << 1 //0 = WonderSwan; 1 = WonderSwan Color or SwanCrystal
| 1 << 0; //0 = BIOS mapped; 1 = cartridge mapped
}
return 0x00; //should never occur
}
auto CPU::portWrite(uint16 addr, uint16 data) -> void {
auto CPU::portWrite(uint16 addr, uint8 data) -> void {
//HW_FLAGS
if(addr == 0x00a0) {
//todo: d2 (bus width) bit is writable; but ... it will do very bad things
}
}

View File

@ -8,6 +8,7 @@ Settings settings;
Interface::Interface() {
interface = this;
information.manufacturer = "Bandai";
information.name = "WonderSwan";
information.width = 224; //note: technically 224x144; but screen can be rotated
information.height = 224; //by using a square size; this can be done in the core

View File

@ -11,13 +11,13 @@ auto IO::power() -> void {
for(auto& n : iomap) n = &unmapped;
}
auto IO::portRead(uint16 addr) -> uint16 {
auto IO::portRead(uint16 addr) -> uint8 {
print("[", hex(addr, 4L), "]: port unmapped\n");
return 0x0000;
return 0x00;
}
auto IO::portWrite(uint16 addr, uint16 data) -> void {
print("[", hex(addr, 4L), "] = ", hex(data, 4L), ": port unmapped\n");
auto IO::portWrite(uint16 addr, uint8 data) -> void {
print("[", hex(addr, 4L), "] = ", hex(data, 2L), ": port unmapped\n");
}
auto Bus::read(uint20 addr) -> uint8 {

View File

@ -1,8 +1,8 @@
struct IO {
static auto power() -> void;
virtual auto portRead(uint16 addr) -> uint16;
virtual auto portWrite(uint16 addr, uint16 data) -> void;
virtual auto portRead(uint16 addr) -> uint8;
virtual auto portWrite(uint16 addr, uint8 data) -> void;
};
struct Bus {

View File

@ -1,11 +1,13 @@
Icarus::Icarus() {
database.famicom = BML::unserialize(string::read(locate({configpath(), "icarus/"}, "Database/Famicom.bml")));
database.superFamicom = BML::unserialize(string::read(locate({configpath(), "icarus/"}, "Database/Super Famicom.bml")));
database.gameBoy = BML::unserialize(string::read(locate({configpath(), "icarus/"}, "Database/Game Boy.bml")));
database.gameBoyColor = BML::unserialize(string::read(locate({configpath(), "icarus/"}, "Database/Game Boy Color.bml")));
database.gameBoyAdvance = BML::unserialize(string::read(locate({configpath(), "icarus/"}, "Database/Game Boy Advance.bml")));
database.bsMemory = BML::unserialize(string::read(locate({configpath(), "icarus/"}, "Database/BS Memory.bml")));
database.sufamiTurbo = BML::unserialize(string::read(locate({configpath(), "icarus/"}, "Database/Sufami Turbo.bml")));
database.famicom = BML::unserialize(string::read(locate("Database/Famicom.bml")));
database.superFamicom = BML::unserialize(string::read(locate("Database/Super Famicom.bml")));
database.gameBoy = BML::unserialize(string::read(locate("Database/Game Boy.bml")));
database.gameBoyColor = BML::unserialize(string::read(locate("Database/Game Boy Color.bml")));
database.gameBoyAdvance = BML::unserialize(string::read(locate("Database/Game Boy Advance.bml")));
database.wonderSwan = BML::unserialize(string::read(locate("Database/WonderSwan.bml")));
database.wonderSwanColor = BML::unserialize(string::read(locate("Database/WonderSwan Color.bml")));
database.bsMemory = BML::unserialize(string::read(locate("Database/BS Memory.bml")));
database.sufamiTurbo = BML::unserialize(string::read(locate("Database/Sufami Turbo.bml")));
}
auto Icarus::error() const -> string {
@ -32,6 +34,8 @@ auto Icarus::manifest(string location) -> string {
if(type == ".gb") return gameBoyManifest(location);
if(type == ".gbc") return gameBoyColorManifest(location);
if(type == ".gba") return gameBoyAdvanceManifest(location);
if(type == ".ws") return wonderSwanManifest(location);
if(type == ".wsc") return wonderSwanColorManifest(location);
if(type == ".bs") return bsMemoryManifest(location);
if(type == ".st") return sufamiTurboManifest(location);
@ -65,6 +69,8 @@ auto Icarus::import(string location) -> string {
if(type == ".gb") return gameBoyImport(buffer, location);
if(type == ".gbc") return gameBoyColorImport(buffer, location);
if(type == ".gba") return gameBoyAdvanceImport(buffer, location);
if(type == ".ws") return wonderSwanImport(buffer, location);
if(type == ".wsc") return wonderSwanColorImport(buffer, location);
if(type == ".bs") return bsMemoryImport(buffer, location);
if(type == ".st") return sufamiTurboImport(buffer, location);

View File

@ -47,6 +47,16 @@ struct Icarus {
auto sufamiTurboManifest(vector<uint8>& buffer, string location) -> string;
auto sufamiTurboImport(vector<uint8>& buffer, string location) -> string;
//wonderswan.cpp
auto wonderSwanManifest(string location) -> string;
auto wonderSwanManifest(vector<uint8>& buffer, string location) -> string;
auto wonderSwanImport(vector<uint8>& buffer, string location) -> string;
//wonderswan-color.cpp
auto wonderSwanColorManifest(string location) -> string;
auto wonderSwanColorManifest(vector<uint8>& buffer, string location) -> string;
auto wonderSwanColorImport(vector<uint8>& buffer, string location) -> string;
private:
string errorMessage;
@ -56,6 +66,8 @@ private:
Markup::Node gameBoy;
Markup::Node gameBoyColor;
Markup::Node gameBoyAdvance;
Markup::Node wonderSwan;
Markup::Node wonderSwanColor;
Markup::Node bsMemory;
Markup::Node sufamiTurbo;
} database;

View File

@ -0,0 +1,51 @@
auto Icarus::wonderSwanColorManifest(string location) -> string {
vector<uint8> buffer;
concatenate(buffer, {location, "program.rom"});
return wonderSwanColorManifest(buffer, location);
}
auto Icarus::wonderSwanColorManifest(vector<uint8>& buffer, string location) -> string {
string manifest;
string digest = Hash::SHA256(buffer.data(), buffer.size()).digest();
if(settings["icarus/UseDatabase"].boolean() && !manifest) {
for(auto node : database.wonderSwanColor) {
if(node["sha256"].text() == digest) {
manifest.append(node.text(), "\n sha256: ", digest, "\n");
break;
}
}
}
if(settings["icarus/UseHeuristics"].boolean() && !manifest) {
WonderSwanColorCartridge 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");
}
}
return manifest;
}
auto Icarus::wonderSwanColorImport(vector<uint8>& buffer, string location) -> string {
auto name = prefixname(location);
auto source = pathname(location);
string target{settings["Library/Location"].text(), "WonderSwan Color/", name, ".wsc/"};
//if(directory::exists(target)) return failure("game already exists");
auto manifest = wonderSwanColorManifest(buffer, location);
if(!manifest) return failure("failed to parse ROM image");
if(!directory::create(target)) return failure("library path unwritable");
if(file::exists({source, name, ".sav"}) && !file::exists({target, "save.ram"})) {
file::copy({source, name, ".sav"}, {target, "save.ram"});
}
if(settings["icarus/CreateManifests"].boolean()) file::write({target, "manifest.bml"}, manifest);
file::write({target, "program.rom"}, buffer);
return success(target);
}

View File

@ -0,0 +1,51 @@
auto Icarus::wonderSwanManifest(string location) -> string {
vector<uint8> buffer;
concatenate(buffer, {location, "program.rom"});
return wonderSwanManifest(buffer, location);
}
auto Icarus::wonderSwanManifest(vector<uint8>& buffer, string location) -> string {
string manifest;
string digest = Hash::SHA256(buffer.data(), buffer.size()).digest();
if(settings["icarus/UseDatabase"].boolean() && !manifest) {
for(auto node : database.wonderSwan) {
if(node["sha256"].text() == digest) {
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");
}
}
return manifest;
}
auto Icarus::wonderSwanImport(vector<uint8>& buffer, string location) -> string {
auto name = prefixname(location);
auto source = pathname(location);
string target{settings["Library/Location"].text(), "WonderSwan/", name, ".ws/"};
//if(directory::exists(target)) return failure("game already exists");
auto manifest = wonderSwanManifest(buffer, location);
if(!manifest) return failure("failed to parse ROM image");
if(!directory::create(target)) return failure("library path unwritable");
if(file::exists({source, name, ".sav"}) && !file::exists({target, "save.ram"})) {
file::copy({source, name, ".sav"}, {target, "save.ram"});
}
if(settings["icarus/CreateManifests"].boolean()) file::write({target, "manifest.bml"}, manifest);
file::write({target, "program.rom"}, buffer);
return success(target);
}

View File

@ -0,0 +1,16 @@
struct WonderSwanColorCartridge {
WonderSwanColorCartridge(uint8* data, uint size);
string manifest;
//private:
struct Information {
} information;
};
WonderSwanColorCartridge::WonderSwanColorCartridge(uint8* data, uint size) {
if(size < 0x10000) return;
manifest.append("board\n");
manifest.append(" rom name=program.rom size=0x", hex(size), "\n");
}

View File

@ -0,0 +1,16 @@
struct WonderSwanCartridge {
WonderSwanCartridge(uint8* data, uint size);
string manifest;
//private:
struct Information {
} information;
};
WonderSwanCartridge::WonderSwanCartridge(uint8* data, uint size) {
if(size < 0x10000) return;
manifest.append("board\n");
manifest.append(" rom name=program.rom size=0x", hex(size), "\n");
}

View File

@ -4,23 +4,28 @@ using namespace nall;
#include <hiro/hiro.hpp>
using namespace hiro;
//if file already exists in the same path as the binary; use it (portable mode)
//if not, use default requested path (*nix/user mode)
auto locate(string pathname, string filename) -> string {
string location{programpath(), filename};
auto locate(string name) -> string {
string location = {programpath(), name};
if(file_system_object::exists(location)) return location;
return {pathname, filename};
location = {configpath(), "icarus/", name};
if(file_system_object::exists(location)) return location;
directory::create({localpath(), "icarus/"});
return {localpath(), "icarus/", name};
}
#include "settings.cpp"
Settings settings;
#include "heuristics/famicom.hpp"
#include "heuristics/super-famicom.hpp"
#include "heuristics/game-boy.hpp"
#include "heuristics/game-boy-advance.hpp"
#include "heuristics/bs-memory.hpp"
#include "heuristics/sufami-turbo.hpp"
#include "heuristics/famicom.cpp"
#include "heuristics/super-famicom.cpp"
#include "heuristics/game-boy.cpp"
#include "heuristics/game-boy-advance.cpp"
#include "heuristics/wonderswan.cpp"
#include "heuristics/wonderswan-color.cpp"
#include "heuristics/bs-memory.cpp"
#include "heuristics/sufami-turbo.cpp"
#include "core/core.hpp"
#include "core/core.cpp"
@ -29,6 +34,8 @@ Settings settings;
#include "core/game-boy.cpp"
#include "core/game-boy-color.cpp"
#include "core/game-boy-advance.cpp"
#include "core/wonderswan.cpp"
#include "core/wonderswan-color.cpp"
#include "core/bs-memory.cpp"
#include "core/sufami-turbo.cpp"
Icarus icarus;
@ -60,7 +67,7 @@ auto nall::main(lstring args) -> void {
if(string source = BrowserDialog()
.setTitle("Load ROM Image")
.setPath(settings["icarus/Path"].text())
.setFilters("ROM Files|*.fc:*.nes:*.sfc:*.smc:*.gb:*.gbc:*.gba:*.bs:*.st:*.zip")
.setFilters("ROM Files|*.fc:*.nes:*.sfc:*.smc:*.gb:*.gbc:*.gba:*.ws:*.wsc:*.bs:*.st:*.zip")
.openFile()) {
if(string target = icarus.import(source)) {
settings["icarus/Path"].setValue(pathname(source));

View File

@ -13,7 +13,7 @@
<string>icarus.icns</string>
-->
<key>NSHighResolutionCapable</key>
<true/>
<false/>
<key>NSSupportsAutomaticGraphicsSwitching</key>
<true/>
</dict>

View File

@ -4,7 +4,7 @@ struct Settings : Markup::Node {
};
Settings::Settings() {
Markup::Node::operator=(BML::unserialize(string::read(locate({configpath(), "icarus/"}, "settings.bml"))));
Markup::Node::operator=(BML::unserialize(string::read(locate("settings.bml"))));
auto set = [&](const string& name, const string& value) {
//create node and set to default value only if it does not already exist
@ -20,6 +20,5 @@ Settings::Settings() {
}
Settings::~Settings() {
directory::create({configpath(), "icarus/"});
file::write(locate({configpath(), "icarus/"}, "settings.bml"), BML::serialize(*this));
file::write(locate("settings.bml"), BML::serialize(*this));
}