mirror of https://github.com/bsnes-emu/bsnes.git
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:
parent
a8323d0d2b
commit
605a8aa3e9
|
@ -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/";
|
||||
|
|
|
@ -4,6 +4,7 @@ namespace Emulator {
|
|||
|
||||
struct Interface {
|
||||
struct Information {
|
||||
string manufacturer;
|
||||
string name;
|
||||
uint width;
|
||||
uint height;
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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), " ",
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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");
|
||||
}
|
|
@ -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");
|
||||
}
|
|
@ -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));
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
<string>icarus.icns</string>
|
||||
-->
|
||||
<key>NSHighResolutionCapable</key>
|
||||
<true/>
|
||||
<false/>
|
||||
<key>NSSupportsAutomaticGraphicsSwitching</key>
|
||||
<true/>
|
||||
</dict>
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue