Update to v104r07 release.

byuu says:

Changelog:

  - md/vdp: added VIP bit to status register; fixes Cliffhanger
  - processor/m68k/disassembler: added modes 7 and 8 to LEA address
    disassembly
  - processor/m68k/disassembler: enhanced ILLEGAL to display LINEA/LINEF
    $xxx variants
  - processor/m68k: ILLEGAL/LINEA/LINEF do not modify the stack
    register; fixes Caeser no Yabou II
  - icarus/sfc: request sgb1.boot.rom and sgb2.boot.rom separately; as
    they are different
  - icarus/sfc: removed support for external firmware when loading ROM
    images

The hack to run Mega Drive Ballz 3D isn't in place, as I don't know if
it's correct, and the graphics were corrupted anyway.

The SGB boot ROM change is going to require updating the icarus database
as well. I will add that in when I start dumping more cartridges here
soon.

Finally ... I explained this already, but I'll do so here as well: I
removed icarus' support for loading SNES coprocessor firmware games with
external firmware files (eg dsp1.program.rom + dsp1.data.rom in the same
path as supermariokart.sfc, for example.)

I realize most are going to see this as an antagonizing/stubborn move
given the recent No-Intro discussion, and I won't deny that said thread
is why this came to the forefront of my mind. But on my word, I honestly
believe this was an ineffective solution for many reasons not related to
our disagreements:

 1. No-Intro distributes SNES coprocessor firmware as a merged file, eg
    "DSP1 (World).zip/DSP1 (World).bin" -- icarus can't possibly know
    about every ROM distribution set's naming conventions for firmware.
    (Right now, it appears GoodSNES and NSRT are mostly dead; but there
    may be more DATs in the future -- including my own.)
 2. Even if the user obtains the firmware and tries to rename it, it
    won't work: icarus parses manifests generated by the heuristics
    module and sees two ROM files: dsp1.program.rom and dsp1.data.rom.
    icarus cannot identify a file named dsp1.rom as containing both
    of these sub-files. Users are going to have to know how to split
    files, which there is no way to do on stock Windows. Merging files,
    however, can be done via `copy /b supermariokart.sfc+dsp1.rom
    supermariokartdsp.sfc`; - and dsp1.rom can be named whatever now.
    I am not saying this will be easy for the average user, but it's
    easier than splitting files.
 3. Separate firmware breaks icarus' database lookup. If you have
    pilotwings.sfc but without firmware, icarus will not find a match
    for it in the database lookup phase. It will then fall back on
    heuristics. The heuristics will pick DSP1B for compatibility with
    Ballz 3D which requires it. And so it will try to pull in the
    wrong firmware, and the game's intro will not work correctly.
    Furthermore, the database information will be unavailable, resulting
    in inaccurate mirroring.

So for these reasons, I have removed said support. You must now load
SNES coprocessor games into higan in one of two ways: 1) game paks with
split files; or 2) SFC images with merged firmware.

If and when No-Intro deploys a method I can actually use, I give you all
my word I will give it a fair shot and if it's reasonable, I'll support
it in icarus.
This commit is contained in:
Tim Allen 2017-08-28 22:46:14 +10:00
parent c273297577
commit 9c25f128f9
13 changed files with 111 additions and 78 deletions

View File

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

View File

@ -118,13 +118,23 @@ auto VDP::writeDataPort(uint16 data) -> void {
auto VDP::readControlPort() -> uint16 {
io.commandPending = false;
uint16 result = 0b0011'0100'0000'0000;
result |= Region::PAL() << 0;
result |= io.command.bit(5) << 1; //DMA active
result |= (state.hcounter >= 1280) << 2; //horizontal blank
result |= (state.vcounter >= screenHeight()) << 3; //vertical blank
result |= io.interlaceMode.bit(0) ? state.field << 4 : 0;
result |= 1 << 9; //FIFO empty
uint16 result;
result.bit( 0) = Region::PAL();
result.bit( 1) = io.command.bit(5); //DMA active
result.bit( 2) = state.hcounter >= 1280; //horizontal blank
result.bit( 3) = state.vcounter >= screenHeight(); //vertical blank
result.bit( 4) = io.interlaceMode.bit(0) && state.field;
result.bit( 5) = 0; //SCOL
result.bit( 6) = 0; //SOVR
result.bit( 7) = io.vblankIRQ;
result.bit( 8) = 0; //FIFO full
result.bit( 9) = 1; //FIFO empty
result.bit(10) = 1; //constants (bits 10-15)
result.bit(11) = 0;
result.bit(12) = 1;
result.bit(13) = 1;
result.bit(14) = 0;
result.bit(15) = 0;
return result;
}

View File

@ -11,6 +11,7 @@ auto VDP::serialize(serializer& s) -> void {
vsram.serialize(s);
cram.serialize(s);
s.integer(io.vblankIRQ);
s.integer(io.command);
s.integer(io.address);
s.integer(io.commandPending);

View File

@ -23,11 +23,13 @@ auto VDP::main() -> void {
if(state.vcounter == 0) {
latch.horizontalInterruptCounter = io.horizontalInterruptCounter;
io.vblankIRQ = false;
cpu.lower(CPU::Interrupt::VerticalBlank);
}
if(state.vcounter == screenHeight()) {
if(io.verticalBlankInterruptEnable) {
io.vblankIRQ = true;
cpu.raise(CPU::Interrupt::VerticalBlank);
}
//todo: should only stay high for ~2573/2 clocks

View File

@ -197,6 +197,9 @@ private:
} cram;
struct IO {
//status
uint1 vblankIRQ; //true after VIRQ triggers; cleared at start of next frame
//command
uint6 command;
uint16 address;

View File

@ -43,6 +43,8 @@ template<uint Size> auto M68K::_immediate() -> string {
}
template<uint Size> auto M68K::_address(EffectiveAddress& ea) -> string {
if(ea.mode == 7) return {"$", hex((int16)_readPC<Word>(), 6L)};
if(ea.mode == 8) return {"$", hex(readPC<Long>(), 6L)};
if(ea.mode == 9) return {"$", hex(_pc + (int16)_readPC(), 6L)};
return "???";
}
@ -291,7 +293,9 @@ template<uint Size> auto M68K::disassembleEXT(DataRegister with) -> string {
return {"ext", _suffix<Size>(), " ", _dataRegister(with)};
}
auto M68K::disassembleILLEGAL() -> string {
auto M68K::disassembleILLEGAL(uint16 code) -> string {
if(code.bits(12,15) == 0xa) return {"linea $", hex(code.bits(0,11), 3L)};
if(code.bits(12,15) == 0xf) return {"linef $", hex(code.bits(0,11), 3L)};
return {"illegal "};
}

View File

@ -538,7 +538,7 @@ M68K::M68K() {
//ILLEGAL
{ auto opcode = pattern("0100 1010 1111 1100");
bind(opcode, ILLEGAL);
bind(opcode, ILLEGAL, opcode);
}
//JMP
@ -1257,7 +1257,7 @@ M68K::M68K() {
//ILLEGAL
for(uint16 opcode : range(65536)) {
if(instructionTable[opcode]) continue;
bind(opcode, ILLEGAL);
bind(opcode, ILLEGAL, opcode);
}
#undef bind

View File

@ -521,10 +521,10 @@ template<> auto M68K::instructionEXT<Long>(DataRegister with) -> void {
r.n = sign<Long>(result) < 0;
}
auto M68K::instructionILLEGAL() -> void {
auto M68K::instructionILLEGAL(uint16 code) -> void {
r.pc -= 2;
if(opcode >> 12 == 0xa) return exception(Exception::Illegal, Vector::IllegalLineA);
if(opcode >> 12 == 0xf) return exception(Exception::Illegal, Vector::IllegalLineF);
if(code.bits(12,15) == 0xa) return exception(Exception::Illegal, Vector::IllegalLineA);
if(code.bits(12,15) == 0xf) return exception(Exception::Illegal, Vector::IllegalLineF);
return exception(Exception::Illegal, Vector::Illegal);
}

View File

@ -45,10 +45,12 @@ auto M68K::exception(uint exception, uint vector, uint priority) -> void {
auto pc = r.pc;
auto sr = readSR();
if(!r.s) swap(r.a[7], r.sp);
r.i = priority;
r.s = 1;
r.t = 0;
if(exception != Exception::Illegal) {
if(!r.s) swap(r.a[7], r.sp);
r.i = priority;
r.s = 1;
r.t = 0;
}
push<Long>(pc);
push<Word>(sr);

View File

@ -171,7 +171,7 @@ struct M68K {
auto instructionEXG(AddressRegister x, AddressRegister y) -> void;
auto instructionEXG(DataRegister x, AddressRegister y) -> void;
template<uint Size> auto instructionEXT(DataRegister with) -> void;
auto instructionILLEGAL() -> void;
auto instructionILLEGAL(uint16 code) -> void;
auto instructionJMP(EffectiveAddress target) -> void;
auto instructionJSR(EffectiveAddress target) -> void;
auto instructionLEA(AddressRegister ar, EffectiveAddress ea) -> void;
@ -326,7 +326,7 @@ private:
auto disassembleEXG(AddressRegister x, AddressRegister y) -> string;
auto disassembleEXG(DataRegister x, AddressRegister y) -> string;
template<uint Size> auto disassembleEXT(DataRegister with) -> string;
auto disassembleILLEGAL() -> string;
auto disassembleILLEGAL(uint16 code) -> string;
auto disassembleJMP(EffectiveAddress target) -> string;
auto disassembleJSR(EffectiveAddress target) -> string;
auto disassembleLEA(AddressRegister ar, EffectiveAddress ea) -> string;

View File

@ -18,7 +18,7 @@ struct Icarus {
//super-famicom.cpp
auto superFamicomManifest(string location) -> string;
auto superFamicomManifest(vector<uint8_t>& buffer, string location, bool* firmwareAppended = nullptr) -> string;
auto superFamicomManifest(vector<uint8_t>& buffer, string location, string* firmwareMissing = nullptr) -> string;
auto superFamicomManifestScan(vector<Markup::Node>& roms, Markup::Node node) -> void;
auto superFamicomImport(vector<uint8_t>& buffer, string location) -> string;

View File

@ -10,7 +10,7 @@ auto Icarus::superFamicomManifest(string location) -> string {
return superFamicomManifest(buffer, location);
}
auto Icarus::superFamicomManifest(vector<uint8_t>& buffer, string location, bool* firmwareAppended) -> string {
auto Icarus::superFamicomManifest(vector<uint8_t>& buffer, string location, string* firmwareMissing) -> string {
string markup;
string digest = Hash::SHA256(buffer.data(), buffer.size()).digest();
@ -27,7 +27,7 @@ auto Icarus::superFamicomManifest(vector<uint8_t>& buffer, string location, bool
bool hasMSU1 = file::exists({location, "msu1.rom"});
SuperFamicomCartridge cartridge{buffer.data(), buffer.size(), hasMSU1};
if(markup = cartridge.markup) {
if(firmwareAppended) *firmwareAppended = cartridge.firmware_appended;
if(firmwareMissing) *firmwareMissing = cartridge.firmware_missing;
markup.append("\n");
markup.append("information\n");
markup.append(" region: ", cartridge.region == SuperFamicomCartridge::Region::NTSC ? "NTSC" : "PAL", "\n");
@ -51,19 +51,10 @@ auto Icarus::superFamicomImport(vector<uint8_t>& buffer, string location) -> str
string target{settings["Library/Location"].text(), "Super Famicom/", name, ".sfc/"};
//if(directory::exists(target)) return failure("game already exists");
bool firmwareAppended = true;
auto markup = superFamicomManifest(buffer, location, &firmwareAppended);
string firmwareMissing;
auto markup = superFamicomManifest(buffer, location, &firmwareMissing);
if(!markup) return failure("failed to parse ROM image");
auto document = BML::unserialize(markup);
vector<Markup::Node> roms;
superFamicomManifestScan(roms, document["board"]);
for(auto rom : roms) {
auto name = rom["name"].text();
auto size = rom["size"].natural();
if(name == "program.rom" || name == "data.rom" || firmwareAppended) continue;
if(file::size({source, name}) != size) return failure({"firmware (", name, ") missing or invalid"});
}
if(firmwareMissing) return failure({"ROM image is missing ", firmwareMissing, " firmware data"});
if(!directory::create(target)) return failure("library path unwritable");
if(file::exists({source, name, ".srm"}) && !file::exists({target, "save.ram"})) {
@ -72,17 +63,15 @@ auto Icarus::superFamicomImport(vector<uint8_t>& buffer, string location) -> str
if(settings["icarus/CreateManifests"].boolean()) file::write({target, "manifest.bml"}, markup);
uint offset = (buffer.size() & 0x7fff) == 512 ? 512 : 0; //skip header if present
auto document = BML::unserialize(markup);
vector<Markup::Node> roms;
superFamicomManifestScan(roms, document["board"]);
for(auto rom : roms) {
auto name = rom["name"].text();
auto size = rom["size"].natural();
if(name == "program.rom" || name == "data.rom" || firmwareAppended) {
if(size > buffer.size() - offset) return failure("ROM image is missing data");
file::write({target, name}, buffer.data() + offset, size);
offset += size;
} else {
auto firmware = file::read({source, name});
file::write({target, name}, firmware);
}
if(size > buffer.size() - offset) return failure("ROM image is missing data");
file::write({target, name}, buffer.data() + offset, size);
offset += size;
}
return success(target);
}

View File

@ -57,12 +57,12 @@ struct SuperFamicomCartridge {
HiROM,
};
bool loaded = false; //is a base cartridge inserted?
uint crc32 = 0; //crc32 of all cartridges (base+slot(s))
bool loaded = false; //is a base cartridge inserted?
uint crc32 = 0; //crc32 of all cartridges (base+slot(s))
uint rom_size = 0;
uint ram_size = 0;
bool firmware_required = false; //true if firmware is required for emulation
bool firmware_appended = false; //true if firmware is present at end of data
uint firmware_size = 0;
string firmware_missing;
Type type = Type::Unknown;
Region region = Region::NTSC;
@ -90,6 +90,7 @@ SuperFamicomCartridge::SuperFamicomCartridge(const uint8_t* data, uint size, boo
//skip copier header
if((size & 0x7fff) == 512) data += 512, size -= 512;
//ignore images too small to be valid
if(size < 0x8000) return;
readHeader(data, size);
@ -102,80 +103,90 @@ SuperFamicomCartridge::SuperFamicomCartridge(const uint8_t* data, uint size, boo
const char* range = (rom_size > 0x200000) || (ram_size > 32 * 1024) ? "0000-7fff" : "0000-ffff";
markup.append("board region=", region == Region::NTSC ? "ntsc" : "pal", "\n");
//detect appended firmware
//detect firmware
if(has_dsp1) {
firmware_required = true;
if((size & 0x7fff) == 0x2000) {
firmware_appended = true;
rom_size -= 0x2000;
firmware_size = 0x2000;
} else {
firmware_missing = "DSP1";
}
}
if(has_dsp2) {
firmware_required = true;
if((size & 0x7fff) == 0x2000) {
firmware_appended = true;
rom_size -= 0x2000;
firmware_size = 0x2000;
} else {
firmware_missing = "DSP2";
}
}
if(has_dsp3) {
firmware_required = true;
if((size & 0x7fff) == 0x2000) {
firmware_appended = true;
rom_size -= 0x2000;
firmware_size = 0x2000;
} else {
firmware_missing = "DSP3";
}
}
if(has_dsp4) {
firmware_required = true;
if((size & 0x7fff) == 0x2000) {
firmware_appended = true;
rom_size -= 0x2000;
firmware_size = 0x2000;
} else {
firmware_missing = "DSP4";
}
}
if(has_st010) {
firmware_required = true;
if((size & 0xffff) == 0xd000) {
firmware_appended = true;
rom_size -= 0xd000;
firmware_size = 0xd000;
} else {
firmware_missing = "ST010";
}
}
if(has_st011) {
firmware_required = true;
if((size & 0xffff) == 0xd000) {
firmware_appended = true;
rom_size -= 0xd000;
firmware_size = 0xd000;
} else {
firmware_missing = "ST011";
}
}
if(has_st018) {
firmware_required = true;
if((size & 0x3ffff) == 0x28000) {
firmware_appended = true;
rom_size -= 0x28000;
firmware_size = 0x28000;
} else {
firmware_missing = "ST018";
}
}
if(has_cx4) {
firmware_required = true;
if((rom_size & 0x7fff) == 0xc00) {
firmware_appended = true;
rom_size -= 0xc00;
firmware_size = 0xc00;
} else {
firmware_missing = "CX4";
}
}
if(type == Type::SuperGameBoy1BIOS || type == Type::SuperGameBoy2BIOS) {
firmware_required = true;
if(type == Type::SuperGameBoy1BIOS) {
if((rom_size & 0x7fff) == 0x100) {
firmware_appended = true;
rom_size -= 0x100;
firmware_size = 0x100;
} else {
firmware_missing = "SGB1";
}
}
if(type == Type::SuperGameBoy2BIOS) {
if((rom_size & 0x7fff) == 0x100) {
firmware_size = 0x100;
} else {
firmware_missing = "SGB2";
}
}
rom_size -= firmware_size;
//end firmware detection
if(type == Type::SatellaviewBIOS) {
@ -211,14 +222,25 @@ SuperFamicomCartridge::SuperFamicomCartridge(const uint8_t* data, uint size, boo
);
}
else if(type == Type::SuperGameBoy1BIOS || type == Type::SuperGameBoy2BIOS) {
else if(type == Type::SuperGameBoy1BIOS) {
markup.append(
" rom name=program.rom size=0x", hex(rom_size), "\n"
" map address=00-7d,80-ff:8000-ffff mask=0x8000\n"
" map address=40-7d,c0-ff:0000-7fff mask=0x8000\n"
" icd2 revision=1\n"
" map address=00-3f,80-bf:6000-67ff,7000-7fff\n"
" rom name=sgb.boot.rom size=0x100\n"
" rom name=sgb1.boot.rom size=0x100\n"
);
}
else if(type == Type::SuperGameBoy2BIOS) {
markup.append(
" rom name=program.rom size=0x", hex(rom_size), "\n"
" map address=00-7d,80-ff:8000-ffff mask=0x8000\n"
" map address=40-7d,c0-ff:0000-7fff mask=0x8000\n"
" icd2 revision=2\n"
" map address=00-3f,80-bf:6000-67ff,7000-7fff\n"
" rom name=sgb2.boot.rom size=0x100\n"
);
}