mirror of https://github.com/bsnes-emu/bsnes.git
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:
parent
c273297577
commit
9c25f128f9
|
@ -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/";
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 "};
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue