From 8b5823cc76e4d13d0df7d7a001cc4b9d730386a3 Mon Sep 17 00:00:00 2001 From: MrL314 <11429905+MrL314@users.noreply.github.com> Date: Wed, 24 Nov 2021 16:31:30 -0500 Subject: [PATCH 1/5] added dos auto-mapping sans manifest --- common/nall/snes/cartridge.hpp | 1813 ++++++++++++++++---------------- 1 file changed, 911 insertions(+), 902 deletions(-) diff --git a/common/nall/snes/cartridge.hpp b/common/nall/snes/cartridge.hpp index 255a94ec..9678f5ac 100644 --- a/common/nall/snes/cartridge.hpp +++ b/common/nall/snes/cartridge.hpp @@ -1,902 +1,911 @@ -#ifndef NALL_SNES_CARTRIDGE_HPP -#define NALL_SNES_CARTRIDGE_HPP - -namespace nall { - -class SNESCartridge { -public: - string xmlMemoryMap; - inline SNESCartridge(const uint8_t *data, unsigned size); - -//private: - inline void read_header(const uint8_t *data, unsigned size); - inline unsigned find_header(const uint8_t *data, unsigned size); - inline unsigned score_header(const uint8_t *data, unsigned size, unsigned addr); - inline unsigned gameboy_ram_size(const uint8_t *data, unsigned size); - inline bool gameboy_has_rtc(const uint8_t *data, unsigned size); - inline unsigned sufamiturbo_ram_size(const uint8_t *data, unsigned size); - - enum HeaderField { - CartName = 0x00, - Mapper = 0x15, - RomType = 0x16, - RomSize = 0x17, - RamSize = 0x18, - CartRegion = 0x19, - Company = 0x1a, - Version = 0x1b, - Complement = 0x1c, //inverse checksum - Checksum = 0x1e, - ResetVector = 0x3c, - }; - - enum Type { - TypeNormal, - TypeBsxSlotted, - TypeBsxBios, - TypeBsx, - TypeSufamiTurboBios, - TypeSufamiTurbo, - TypeSuperGameBoy1Bios, - TypeSuperGameBoy2Bios, - TypeGameBoy, - TypeUnknown, - }; - - enum Region { - NTSC, - PAL, - }; - - enum MemoryMapper { - SGBROM, - LoROM, - HiROM, - ExHiROM, - SuperFXROM, - SA1ROM, - SDD1ROM, - SPC7110ROM, - BSCLoROM, - BSCHiROM, - BSXROM, - STROM, - Cx4ROM, - }; - - enum DSP1MemoryMapper { - DSP1Unmapped, - DSP1LoROM1MB, - DSP1LoROM2MB, - DSP1HiROM, - }; - - enum BSXPackType { - FlashROM, - MaskROM, - }; - - unsigned rom_size; - unsigned ram_size; - - Type type; - Region region; - MemoryMapper mapper; - DSP1MemoryMapper dsp1_mapper; - BSXPackType bsxpack_type; - - bool has_bsx_slot; - bool has_spc7110rtc; - bool has_srtc; - bool has_dsp1; - bool has_dsp2; - bool has_dsp3; - bool has_dsp4; - bool has_obc1; - bool has_st010; - bool has_st011; - bool has_st018; -}; - -SNESCartridge::SNESCartridge(const uint8_t *data, unsigned size) { - read_header(data, size); - - string xml = "\n"; - - if(type == TypeBsx) { - xml << "\n"; - xmlMemoryMap = xml; - return; - } - - if(type == TypeSufamiTurbo) { - xml << ""; - if(sufamiturbo_ram_size(data, size) > 0) { - xml << " \n"; - } - xml << "\n"; - xmlMemoryMap = xml; - return; - } - - if(type == TypeGameBoy) { - xml << "\n"; - if(gameboy_ram_size(data, size) > 0) { - xml << " \n"; - } - xml << "\n"; - xmlMemoryMap = xml; - return; - } - - xml << "\n"; - - if(mapper == SGBROM) { - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - const unsigned revision = (type == TypeSuperGameBoy2Bios) ? 2 : 1; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - - } else if(mapper == LoROM) { - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - - if(ram_size > 0) { - xml << " \n"; - const unsigned range = (rom_size > 0x200000) || (ram_size > 32 * 1024) ? 0x7fff : 0xffff; - xml << " \n"; - xml << " \n"; - xml << " \n"; - } - - } else if(mapper == HiROM) { - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - - if(ram_size > 0) { - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - } - - } else if(mapper == ExHiROM) { - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - - if(ram_size > 0) { - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - } - - } else if(mapper == SuperFXROM) { - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - - if(ram_size > 0) { - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - } - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - - } else if(mapper == SA1ROM) { - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - - if(ram_size > 0) { - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - } - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - - if(has_bsx_slot) { - xml << " "; // Super MMC controls BS-X slot mapping - } - - } else if(mapper == SDD1ROM) { - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - - if(ram_size > 0) { - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - } - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - - } else if(mapper == SPC7110ROM) { - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - if (size >= 0x700000) { - // Tengai Makyou Zero english translation - xml << " \n"; - } - xml << " \n"; - - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - - if(ram_size > 0) { - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - } - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - - if(has_spc7110rtc) { - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - } - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - - } else if(mapper == BSCLoROM) { - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - - if(ram_size > 0) { - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - } - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - - } else if(mapper == BSCHiROM) { - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - - if(ram_size > 0) { - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - } - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - - } else if(mapper == BSXROM) { - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - - } else if(mapper == STROM) { - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - - } else if(mapper == Cx4ROM) { - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - } - - if(has_srtc) { - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - } - - if(has_dsp1) { - xml << " \n"; - if(dsp1_mapper == DSP1LoROM1MB) { - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - } else if(dsp1_mapper == DSP1LoROM2MB) { - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - } else if(dsp1_mapper == DSP1HiROM) { - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - } - xml << " \n"; - } - - if(has_dsp2) { - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - } - - if(has_dsp3) { - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - } - - if(has_dsp4) { - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - } - - if(has_obc1) { - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - } - - if(has_st010) { - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - } - - if(has_st011) { - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - } - - if(has_st018) { - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - } - - xml << "\n"; - xmlMemoryMap = xml; -} - -void SNESCartridge::read_header(const uint8_t *data, unsigned size) { - type = TypeUnknown; - mapper = LoROM; - dsp1_mapper = DSP1Unmapped; - bsxpack_type = FlashROM; - region = NTSC; - rom_size = size; - ram_size = 0; - - has_bsx_slot = false; - has_spc7110rtc = false; - has_srtc = false; - has_dsp1 = false; - has_dsp2 = false; - has_dsp3 = false; - has_dsp4 = false; - has_obc1 = false; - has_st010 = false; - has_st011 = false; - has_st018 = false; - - //===================== - //detect Game Boy carts - //===================== - - if(size >= 0x0140) { - if(data[0x0104] == 0xce && data[0x0105] == 0xed && data[0x0106] == 0x66 && data[0x0107] == 0x66 - && data[0x0108] == 0xcc && data[0x0109] == 0x0d && data[0x010a] == 0x00 && data[0x010b] == 0x0b) { - type = TypeGameBoy; - return; - } - } - - if(size < 32768) { - type = TypeUnknown; - return; - } - - const unsigned index = find_header(data, size); - const uint8_t mapperid = data[index + Mapper]; - const uint8_t rom_type = data[index + RomType]; - const uint8_t rom_size = data[index + RomSize]; - const uint8_t company = data[index + Company]; - const uint8_t regionid = data[index + CartRegion] & 0x7f; - - ram_size = 1024 << (data[index + RamSize] & 7); - if(ram_size == 1024) ram_size = 0; //no RAM present - - //0, 1, 11, 13, 15, 16 = NTSC; others = PAL - switch (regionid) { - case 0: case 1: case 11: case 13: case 15: case 16: - region = NTSC; - break; - default: - region = PAL; - break; - } - - //======================= - //detect BS-X flash carts - //======================= - - if(data[index + 0x13] == 0x00 || data[index + 0x13] == 0xff) { - if(data[index + 0x14] == 0x00) { - const uint8_t n15 = data[index + 0x15]; - if(n15 == 0x00 || n15 == 0x80 || n15 == 0x84 || n15 == 0x9c || n15 == 0xbc || n15 == 0xfc) { - if(data[index + 0x1a] == 0x33 || data[index + 0x1a] == 0xff) { - type = TypeBsx; - //Check if FlashROM or MaskROM - uint8_t i = 0; - for (i = 0; i < 20; i++) - { - uint8_t checkbyte; - switch(i) - { - case 0x00: checkbyte = 0x4D; break; - case 0x02: checkbyte = 0x50; break; - case 0x06: checkbyte = 0x70; break; - default: checkbyte = 0x00; - } - - if (i != 0x06) - { - if (data[index - 0xC0 + i] != checkbyte) - { - break; - } - } - else - { - //Only check 0xF0 for i = 6, only Memory Pack type matters - if ((data[index - 0xC0 + i] & 0xF0) != checkbyte) - { - break; - } - } - } - - if (i == 20) - { - //if i reaches 20, that means all the checks are successful - bsxpack_type = MaskROM; - } - else - { - bsxpack_type = FlashROM; - } - region = NTSC; //BS-X only released in Japan - return; - } - } - } - } - - //========================= - //detect Sufami Turbo carts - //========================= - - if(!memcmp(data, "BANDAI SFC-ADX", 14)) { - if(!memcmp(data + 16, "SFC-ADX BACKUP", 14)) { - type = TypeSufamiTurboBios; - } else { - type = TypeSufamiTurbo; - } - mapper = STROM; - region = NTSC; //Sufami Turbo only released in Japan - return; //RAM size handled outside this routine - } - - //========================== - //detect Super Game Boy BIOS - //========================== - - if(!memcmp(data + index, "Super GAMEBOY2", 14)) { - type = TypeSuperGameBoy2Bios; - mapper = SGBROM; - return; - } - - if(!memcmp(data + index, "Super GAMEBOY", 13)) { - type = TypeSuperGameBoy1Bios; - mapper = SGBROM; - return; - } - - //===================== - //detect standard carts - //===================== - - //detect presence of BS-X flash cartridge connector (reads extended header information) - if(data[index - 14] == 'Z') { - if(data[index - 11] == 'J') { - uint8_t n13 = data[index - 13]; - if((n13 >= 'A' && n13 <= 'Z') || (n13 >= '0' && n13 <= '9')) { - if(company == 0x33 || (data[index - 10] == 0x00 && data[index - 4] == 0x00)) { - has_bsx_slot = true; - } - } - } - } - - if(has_bsx_slot) { - if(!memcmp(data + index, "Satellaview BS-X ", 21)) { - //BS-X base cart - type = TypeBsxBios; - mapper = BSXROM; - region = NTSC; //BS-X only released in Japan - return; //RAM size handled internally by load_cart_bsx() -> BSXCart class - } else { - type = TypeBsxSlotted; - mapper = (index == 0x7fc0 ? BSCLoROM : BSCHiROM); - region = NTSC; //BS-X slotted cartridges only released in Japan - } - } else { - //standard cart - type = TypeNormal; - - if(index == 0x7fc0) { - mapper = LoROM; - } else if(index == 0xffc0) { - mapper = HiROM; - } else { //index == 0x40ffc0 - mapper = ExHiROM; - } - } - - if(mapperid == 0x20 && (rom_type == 0x13 || rom_type == 0x14 || rom_type == 0x15 || rom_type == 0x1a)) { - mapper = SuperFXROM; - ram_size = 1024 << (data[index - 3] & 7); - if(ram_size == 1024) ram_size = 0; - } - - if(mapperid == 0x23 && (rom_type == 0x32 || rom_type == 0x34 || rom_type == 0x35 || rom_type == 0x36)) { - mapper = SA1ROM; - } - - if(mapperid == 0x32 && (rom_type == 0x43 || rom_type == 0x45)) { - mapper = SDD1ROM; - } - - if(mapperid == 0x3a && (rom_type == 0xf5 || rom_type == 0xf9)) { - mapper = SPC7110ROM; - has_spc7110rtc = (rom_type == 0xf9); - } - - if(mapperid == 0x35 && rom_type == 0x55) { - has_srtc = true; - } - - if(mapperid == 0x20 && rom_type == 0xf3) { - mapper = Cx4ROM; - } - - if((mapperid == 0x20 || mapperid == 0x21) && rom_type == 0x03) { - has_dsp1 = true; - } - - if(mapperid == 0x30 && rom_type == 0x05 && company != 0xb2) { - has_dsp1 = true; - } - - if(mapperid == 0x31 && (rom_type == 0x03 || rom_type == 0x05)) { - has_dsp1 = true; - } - - if(has_dsp1) { - if((mapperid & 0x2f) == 0x20 && size <= 0x100000) { - dsp1_mapper = DSP1LoROM1MB; - } else if((mapperid & 0x2f) == 0x20) { - dsp1_mapper = DSP1LoROM2MB; - } else if((mapperid & 0x2f) == 0x21) { - dsp1_mapper = DSP1HiROM; - } - } - - if(mapperid == 0x20 && rom_type == 0x05) { - has_dsp2 = true; - } - - if(mapperid == 0x30 && rom_type == 0x05 && company == 0xb2) { - has_dsp3 = true; - } - - if(mapperid == 0x30 && rom_type == 0x03) { - has_dsp4 = true; - } - - if(mapperid == 0x30 && rom_type == 0x25) { - has_obc1 = true; - } - - if(mapperid == 0x30 && rom_type == 0xf6 && rom_size >= 10) { - has_st010 = true; - } - - if(mapperid == 0x30 && rom_type == 0xf6 && rom_size < 10) { - has_st011 = true; - } - - if(mapperid == 0x30 && rom_type == 0xf5) { - has_st018 = true; - } -} - -unsigned SNESCartridge::find_header(const uint8_t *data, unsigned size) { - unsigned score_lo = score_header(data, size, 0x007fc0); - unsigned score_hi = score_header(data, size, 0x00ffc0); - unsigned score_ex = score_header(data, size, 0x40ffc0); - if(score_ex) score_ex += 4; //favor ExHiROM on images > 32mbits - - if(score_lo >= score_hi && score_lo >= score_ex) { - return 0x007fc0; - } else if(score_hi >= score_ex) { - return 0x00ffc0; - } else { - return 0x40ffc0; - } -} - -unsigned SNESCartridge::score_header(const uint8_t *data, unsigned size, unsigned addr) { - if(size < addr + 64) return 0; //image too small to contain header at this location? - int score = 0; - - uint16_t resetvector = data[addr + ResetVector] | (data[addr + ResetVector + 1] << 8); - uint16_t checksum = data[addr + Checksum ] | (data[addr + Checksum + 1] << 8); - uint16_t complement = data[addr + Complement ] | (data[addr + Complement + 1] << 8); - - uint8_t resetop = data[(addr & ~0x7fff) | (resetvector & 0x7fff)]; //first opcode executed upon reset - uint8_t mapper = data[addr + Mapper] & ~0x10; //mask off irrelevent FastROM-capable bit - - //$00:[0000-7fff] contains uninitialized RAM and MMIO. - //reset vector must point to ROM at $00:[8000-ffff] to be considered valid. - if(resetvector < 0x8000) return 0; - - //some images duplicate the header in multiple locations, and others have completely - //invalid header information that cannot be relied upon. - //below code will analyze the first opcode executed at the specified reset vector to - //determine the probability that this is the correct header. - - //most likely opcodes - if(resetop == 0x78 //sei - || resetop == 0x18 //clc (clc; xce) - || resetop == 0x38 //sec (sec; xce) - || resetop == 0x9c //stz $nnnn (stz $4200) - || resetop == 0x4c //jmp $nnnn - || resetop == 0x5c //jml $nnnnnn - ) score += 8; - - //plausible opcodes - if(resetop == 0xc2 //rep #$nn - || resetop == 0xe2 //sep #$nn - || resetop == 0xad //lda $nnnn - || resetop == 0xae //ldx $nnnn - || resetop == 0xac //ldy $nnnn - || resetop == 0xaf //lda $nnnnnn - || resetop == 0xa9 //lda #$nn - || resetop == 0xa2 //ldx #$nn - || resetop == 0xa0 //ldy #$nn - || resetop == 0x20 //jsr $nnnn - || resetop == 0x22 //jsl $nnnnnn - ) score += 4; - - //implausible opcodes - if(resetop == 0x40 //rti - || resetop == 0x60 //rts - || resetop == 0x6b //rtl - || resetop == 0xcd //cmp $nnnn - || resetop == 0xec //cpx $nnnn - || resetop == 0xcc //cpy $nnnn - ) score -= 4; - - //least likely opcodes - if(resetop == 0x00 //brk #$nn - || resetop == 0x02 //cop #$nn - || resetop == 0xdb //stp - || resetop == 0x42 //wdm - || resetop == 0xff //sbc $nnnnnn,x - ) score -= 8; - - //at times, both the header and reset vector's first opcode will match ... - //fallback and rely on info validity in these cases to determine more likely header. - - //a valid checksum is the biggest indicator of a valid header. - if((checksum + complement) == 0xffff && (checksum != 0) && (complement != 0)) score += 4; - - if(addr == 0x007fc0 && mapper == 0x20) score += 2; //0x20 is usually LoROM - if(addr == 0x00ffc0 && mapper == 0x21) score += 2; //0x21 is usually HiROM - if(addr == 0x007fc0 && mapper == 0x22) score += 2; //0x22 is usually SDD1 - if(addr == 0x40ffc0 && mapper == 0x25) score += 2; //0x25 is usually ExHiROM - - if(data[addr + Company] == 0x33) score += 2; //0x33 indicates extended header - if(data[addr + RomType] < 0x08) score++; - if(data[addr + RomSize] < 0x10) score++; - if(data[addr + RamSize] < 0x08) score++; - if(data[addr + CartRegion] < 14) score++; - - if(score < 0) score = 0; - return score; -} - -unsigned SNESCartridge::gameboy_ram_size(const uint8_t *data, unsigned size) { - if(size < 512) return 0; - if(data[0x0147] == 0x06) return 512; //MBC2 has 512 nibbles of internal RAM - switch(data[0x0149]) { - case 0x00: return 0 * 1024; - case 0x01: return 2 * 1024; - case 0x02: return 8 * 1024; - case 0x03: return 32 * 1024; - case 0x04: return 128 * 1024; - case 0x05: return 128 * 1024; - default: return 128 * 1024; - } -} - -bool SNESCartridge::gameboy_has_rtc(const uint8_t *data, unsigned size) { - if(size < 512) return false; - if(data[0x0147] == 0x0f || data[0x0147] == 0x10) return true; - return false; -} - -unsigned SNESCartridge::sufamiturbo_ram_size(const uint8_t *data, unsigned size) { - if(size < 0x38) return 0; - return data[0x37] * 2048; -} - -} - -#endif +#ifndef NALL_SNES_CARTRIDGE_HPP +#define NALL_SNES_CARTRIDGE_HPP + +namespace nall { + +class SNESCartridge { +public: + string xmlMemoryMap; + inline SNESCartridge(const uint8_t *data, unsigned size); + +//private: + inline void read_header(const uint8_t *data, unsigned size); + inline unsigned find_header(const uint8_t *data, unsigned size); + inline unsigned score_header(const uint8_t *data, unsigned size, unsigned addr); + inline unsigned gameboy_ram_size(const uint8_t *data, unsigned size); + inline bool gameboy_has_rtc(const uint8_t *data, unsigned size); + inline unsigned sufamiturbo_ram_size(const uint8_t *data, unsigned size); + + enum HeaderField { + CartName = 0x00, + Mapper = 0x15, + RomType = 0x16, + RomSize = 0x17, + RamSize = 0x18, + CartRegion = 0x19, + Company = 0x1a, + Version = 0x1b, + Complement = 0x1c, //inverse checksum + Checksum = 0x1e, + ResetVector = 0x3c, + }; + + enum Type { + TypeNormal, + TypeBsxSlotted, + TypeBsxBios, + TypeBsx, + TypeSufamiTurboBios, + TypeSufamiTurbo, + TypeSuperGameBoy1Bios, + TypeSuperGameBoy2Bios, + TypeGameBoy, + TypeUnknown, + }; + + enum Region { + NTSC, + PAL, + }; + + enum MemoryMapper { + SGBROM, + LoROM, + HiROM, + ExHiROM, + SuperFXROM, + SA1ROM, + SDD1ROM, + SPC7110ROM, + BSCLoROM, + BSCHiROM, + BSXROM, + STROM, + Cx4ROM, + }; + + enum DSP1MemoryMapper { + DSP1Unmapped, + DSP1LoROM1MB, + DSP1LoROM2MB, + DSP1HiROM, + }; + + enum BSXPackType { + FlashROM, + MaskROM, + }; + + unsigned rom_size; + unsigned ram_size; + + Type type; + Region region; + MemoryMapper mapper; + DSP1MemoryMapper dsp1_mapper; + BSXPackType bsxpack_type; + + bool has_bsx_slot; + bool has_spc7110rtc; + bool has_srtc; + bool has_dsp1; + bool has_dsp2; + bool has_dsp3; + bool has_dsp4; + bool has_obc1; + bool has_st010; + bool has_st011; + bool has_st018; +}; + +SNESCartridge::SNESCartridge(const uint8_t *data, unsigned size) { + read_header(data, size); + + string xml = "\n"; + + if(type == TypeBsx) { + xml << "\n"; + xmlMemoryMap = xml; + return; + } + + if(type == TypeSufamiTurbo) { + xml << ""; + if(sufamiturbo_ram_size(data, size) > 0) { + xml << " \n"; + } + xml << "\n"; + xmlMemoryMap = xml; + return; + } + + if(type == TypeGameBoy) { + xml << "\n"; + if(gameboy_ram_size(data, size) > 0) { + xml << " \n"; + } + xml << "\n"; + xmlMemoryMap = xml; + return; + } + + xml << "\n"; + + if(mapper == SGBROM) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + const unsigned revision = (type == TypeSuperGameBoy2Bios) ? 2 : 1; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + + } else if(mapper == LoROM) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + + if(ram_size > 0) { + xml << " \n"; + const unsigned range = (rom_size > 0x200000) || (ram_size > 32 * 1024) ? 0x7fff : 0xffff; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } + + } else if(mapper == HiROM) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + + if(ram_size > 0) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } + + } else if(mapper == ExHiROM) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + + if(ram_size > 0) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } + + } else if(mapper == SuperFXROM) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + + if(ram_size > 0) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + + } else if(mapper == SA1ROM) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + + if(ram_size > 0) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + + if(has_bsx_slot) { + xml << " "; // Super MMC controls BS-X slot mapping + } + + } else if(mapper == SDD1ROM) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + + if(ram_size > 0) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + + } else if(mapper == SPC7110ROM) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + if (size >= 0x700000) { + // Tengai Makyou Zero english translation + xml << " \n"; + } + xml << " \n"; + + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + + if(ram_size > 0) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + + if(has_spc7110rtc) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + + } else if(mapper == BSCLoROM) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + + if(ram_size > 0) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + + } else if(mapper == BSCHiROM) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + + if(ram_size > 0) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + + } else if(mapper == BSXROM) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + + } else if(mapper == STROM) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + + } else if(mapper == Cx4ROM) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } + + if(has_srtc) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } + + if(has_dsp1) { + xml << " \n"; + if(dsp1_mapper == DSP1LoROM1MB) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } else if(dsp1_mapper == DSP1LoROM2MB) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } else if(dsp1_mapper == DSP1HiROM) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } + xml << " \n"; + } + + if(has_dsp2) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } + + if(has_dsp3) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } + + if(has_dsp4) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } + + if(has_obc1) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } + + if(has_st010) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } + + if(has_st011) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } + + if(has_st018) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } + + // always set dos default mapping if none applied + has_dos = true; + if (has_dos) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } + + xml << "\n"; + xmlMemoryMap = xml; +} + +void SNESCartridge::read_header(const uint8_t *data, unsigned size) { + type = TypeUnknown; + mapper = LoROM; + dsp1_mapper = DSP1Unmapped; + bsxpack_type = FlashROM; + region = NTSC; + rom_size = size; + ram_size = 0; + + has_bsx_slot = false; + has_spc7110rtc = false; + has_srtc = false; + has_dsp1 = false; + has_dsp2 = false; + has_dsp3 = false; + has_dsp4 = false; + has_obc1 = false; + has_st010 = false; + has_st011 = false; + has_st018 = false; + + //===================== + //detect Game Boy carts + //===================== + + if(size >= 0x0140) { + if(data[0x0104] == 0xce && data[0x0105] == 0xed && data[0x0106] == 0x66 && data[0x0107] == 0x66 + && data[0x0108] == 0xcc && data[0x0109] == 0x0d && data[0x010a] == 0x00 && data[0x010b] == 0x0b) { + type = TypeGameBoy; + return; + } + } + + if(size < 32768) { + type = TypeUnknown; + return; + } + + const unsigned index = find_header(data, size); + const uint8_t mapperid = data[index + Mapper]; + const uint8_t rom_type = data[index + RomType]; + const uint8_t rom_size = data[index + RomSize]; + const uint8_t company = data[index + Company]; + const uint8_t regionid = data[index + CartRegion] & 0x7f; + + ram_size = 1024 << (data[index + RamSize] & 7); + if(ram_size == 1024) ram_size = 0; //no RAM present + + //0, 1, 11, 13, 15, 16 = NTSC; others = PAL + switch (regionid) { + case 0: case 1: case 11: case 13: case 15: case 16: + region = NTSC; + break; + default: + region = PAL; + break; + } + + //======================= + //detect BS-X flash carts + //======================= + + if(data[index + 0x13] == 0x00 || data[index + 0x13] == 0xff) { + if(data[index + 0x14] == 0x00) { + const uint8_t n15 = data[index + 0x15]; + if(n15 == 0x00 || n15 == 0x80 || n15 == 0x84 || n15 == 0x9c || n15 == 0xbc || n15 == 0xfc) { + if(data[index + 0x1a] == 0x33 || data[index + 0x1a] == 0xff) { + type = TypeBsx; + //Check if FlashROM or MaskROM + uint8_t i = 0; + for (i = 0; i < 20; i++) + { + uint8_t checkbyte; + switch(i) + { + case 0x00: checkbyte = 0x4D; break; + case 0x02: checkbyte = 0x50; break; + case 0x06: checkbyte = 0x70; break; + default: checkbyte = 0x00; + } + + if (i != 0x06) + { + if (data[index - 0xC0 + i] != checkbyte) + { + break; + } + } + else + { + //Only check 0xF0 for i = 6, only Memory Pack type matters + if ((data[index - 0xC0 + i] & 0xF0) != checkbyte) + { + break; + } + } + } + + if (i == 20) + { + //if i reaches 20, that means all the checks are successful + bsxpack_type = MaskROM; + } + else + { + bsxpack_type = FlashROM; + } + region = NTSC; //BS-X only released in Japan + return; + } + } + } + } + + //========================= + //detect Sufami Turbo carts + //========================= + + if(!memcmp(data, "BANDAI SFC-ADX", 14)) { + if(!memcmp(data + 16, "SFC-ADX BACKUP", 14)) { + type = TypeSufamiTurboBios; + } else { + type = TypeSufamiTurbo; + } + mapper = STROM; + region = NTSC; //Sufami Turbo only released in Japan + return; //RAM size handled outside this routine + } + + //========================== + //detect Super Game Boy BIOS + //========================== + + if(!memcmp(data + index, "Super GAMEBOY2", 14)) { + type = TypeSuperGameBoy2Bios; + mapper = SGBROM; + return; + } + + if(!memcmp(data + index, "Super GAMEBOY", 13)) { + type = TypeSuperGameBoy1Bios; + mapper = SGBROM; + return; + } + + //===================== + //detect standard carts + //===================== + + //detect presence of BS-X flash cartridge connector (reads extended header information) + if(data[index - 14] == 'Z') { + if(data[index - 11] == 'J') { + uint8_t n13 = data[index - 13]; + if((n13 >= 'A' && n13 <= 'Z') || (n13 >= '0' && n13 <= '9')) { + if(company == 0x33 || (data[index - 10] == 0x00 && data[index - 4] == 0x00)) { + has_bsx_slot = true; + } + } + } + } + + if(has_bsx_slot) { + if(!memcmp(data + index, "Satellaview BS-X ", 21)) { + //BS-X base cart + type = TypeBsxBios; + mapper = BSXROM; + region = NTSC; //BS-X only released in Japan + return; //RAM size handled internally by load_cart_bsx() -> BSXCart class + } else { + type = TypeBsxSlotted; + mapper = (index == 0x7fc0 ? BSCLoROM : BSCHiROM); + region = NTSC; //BS-X slotted cartridges only released in Japan + } + } else { + //standard cart + type = TypeNormal; + + if(index == 0x7fc0) { + mapper = LoROM; + } else if(index == 0xffc0) { + mapper = HiROM; + } else { //index == 0x40ffc0 + mapper = ExHiROM; + } + } + + if(mapperid == 0x20 && (rom_type == 0x13 || rom_type == 0x14 || rom_type == 0x15 || rom_type == 0x1a)) { + mapper = SuperFXROM; + ram_size = 1024 << (data[index - 3] & 7); + if(ram_size == 1024) ram_size = 0; + } + + if(mapperid == 0x23 && (rom_type == 0x32 || rom_type == 0x34 || rom_type == 0x35 || rom_type == 0x36)) { + mapper = SA1ROM; + } + + if(mapperid == 0x32 && (rom_type == 0x43 || rom_type == 0x45)) { + mapper = SDD1ROM; + } + + if(mapperid == 0x3a && (rom_type == 0xf5 || rom_type == 0xf9)) { + mapper = SPC7110ROM; + has_spc7110rtc = (rom_type == 0xf9); + } + + if(mapperid == 0x35 && rom_type == 0x55) { + has_srtc = true; + } + + if(mapperid == 0x20 && rom_type == 0xf3) { + mapper = Cx4ROM; + } + + if((mapperid == 0x20 || mapperid == 0x21) && rom_type == 0x03) { + has_dsp1 = true; + } + + if(mapperid == 0x30 && rom_type == 0x05 && company != 0xb2) { + has_dsp1 = true; + } + + if(mapperid == 0x31 && (rom_type == 0x03 || rom_type == 0x05)) { + has_dsp1 = true; + } + + if(has_dsp1) { + if((mapperid & 0x2f) == 0x20 && size <= 0x100000) { + dsp1_mapper = DSP1LoROM1MB; + } else if((mapperid & 0x2f) == 0x20) { + dsp1_mapper = DSP1LoROM2MB; + } else if((mapperid & 0x2f) == 0x21) { + dsp1_mapper = DSP1HiROM; + } + } + + if(mapperid == 0x20 && rom_type == 0x05) { + has_dsp2 = true; + } + + if(mapperid == 0x30 && rom_type == 0x05 && company == 0xb2) { + has_dsp3 = true; + } + + if(mapperid == 0x30 && rom_type == 0x03) { + has_dsp4 = true; + } + + if(mapperid == 0x30 && rom_type == 0x25) { + has_obc1 = true; + } + + if(mapperid == 0x30 && rom_type == 0xf6 && rom_size >= 10) { + has_st010 = true; + } + + if(mapperid == 0x30 && rom_type == 0xf6 && rom_size < 10) { + has_st011 = true; + } + + if(mapperid == 0x30 && rom_type == 0xf5) { + has_st018 = true; + } +} + +unsigned SNESCartridge::find_header(const uint8_t *data, unsigned size) { + unsigned score_lo = score_header(data, size, 0x007fc0); + unsigned score_hi = score_header(data, size, 0x00ffc0); + unsigned score_ex = score_header(data, size, 0x40ffc0); + if(score_ex) score_ex += 4; //favor ExHiROM on images > 32mbits + + if(score_lo >= score_hi && score_lo >= score_ex) { + return 0x007fc0; + } else if(score_hi >= score_ex) { + return 0x00ffc0; + } else { + return 0x40ffc0; + } +} + +unsigned SNESCartridge::score_header(const uint8_t *data, unsigned size, unsigned addr) { + if(size < addr + 64) return 0; //image too small to contain header at this location? + int score = 0; + + uint16_t resetvector = data[addr + ResetVector] | (data[addr + ResetVector + 1] << 8); + uint16_t checksum = data[addr + Checksum ] | (data[addr + Checksum + 1] << 8); + uint16_t complement = data[addr + Complement ] | (data[addr + Complement + 1] << 8); + + uint8_t resetop = data[(addr & ~0x7fff) | (resetvector & 0x7fff)]; //first opcode executed upon reset + uint8_t mapper = data[addr + Mapper] & ~0x10; //mask off irrelevent FastROM-capable bit + + //$00:[0000-7fff] contains uninitialized RAM and MMIO. + //reset vector must point to ROM at $00:[8000-ffff] to be considered valid. + if(resetvector < 0x8000) return 0; + + //some images duplicate the header in multiple locations, and others have completely + //invalid header information that cannot be relied upon. + //below code will analyze the first opcode executed at the specified reset vector to + //determine the probability that this is the correct header. + + //most likely opcodes + if(resetop == 0x78 //sei + || resetop == 0x18 //clc (clc; xce) + || resetop == 0x38 //sec (sec; xce) + || resetop == 0x9c //stz $nnnn (stz $4200) + || resetop == 0x4c //jmp $nnnn + || resetop == 0x5c //jml $nnnnnn + ) score += 8; + + //plausible opcodes + if(resetop == 0xc2 //rep #$nn + || resetop == 0xe2 //sep #$nn + || resetop == 0xad //lda $nnnn + || resetop == 0xae //ldx $nnnn + || resetop == 0xac //ldy $nnnn + || resetop == 0xaf //lda $nnnnnn + || resetop == 0xa9 //lda #$nn + || resetop == 0xa2 //ldx #$nn + || resetop == 0xa0 //ldy #$nn + || resetop == 0x20 //jsr $nnnn + || resetop == 0x22 //jsl $nnnnnn + ) score += 4; + + //implausible opcodes + if(resetop == 0x40 //rti + || resetop == 0x60 //rts + || resetop == 0x6b //rtl + || resetop == 0xcd //cmp $nnnn + || resetop == 0xec //cpx $nnnn + || resetop == 0xcc //cpy $nnnn + ) score -= 4; + + //least likely opcodes + if(resetop == 0x00 //brk #$nn + || resetop == 0x02 //cop #$nn + || resetop == 0xdb //stp + || resetop == 0x42 //wdm + || resetop == 0xff //sbc $nnnnnn,x + ) score -= 8; + + //at times, both the header and reset vector's first opcode will match ... + //fallback and rely on info validity in these cases to determine more likely header. + + //a valid checksum is the biggest indicator of a valid header. + if((checksum + complement) == 0xffff && (checksum != 0) && (complement != 0)) score += 4; + + if(addr == 0x007fc0 && mapper == 0x20) score += 2; //0x20 is usually LoROM + if(addr == 0x00ffc0 && mapper == 0x21) score += 2; //0x21 is usually HiROM + if(addr == 0x007fc0 && mapper == 0x22) score += 2; //0x22 is usually SDD1 + if(addr == 0x40ffc0 && mapper == 0x25) score += 2; //0x25 is usually ExHiROM + + if(data[addr + Company] == 0x33) score += 2; //0x33 indicates extended header + if(data[addr + RomType] < 0x08) score++; + if(data[addr + RomSize] < 0x10) score++; + if(data[addr + RamSize] < 0x08) score++; + if(data[addr + CartRegion] < 14) score++; + + if(score < 0) score = 0; + return score; +} + +unsigned SNESCartridge::gameboy_ram_size(const uint8_t *data, unsigned size) { + if(size < 512) return 0; + if(data[0x0147] == 0x06) return 512; //MBC2 has 512 nibbles of internal RAM + switch(data[0x0149]) { + case 0x00: return 0 * 1024; + case 0x01: return 2 * 1024; + case 0x02: return 8 * 1024; + case 0x03: return 32 * 1024; + case 0x04: return 128 * 1024; + case 0x05: return 128 * 1024; + default: return 128 * 1024; + } +} + +bool SNESCartridge::gameboy_has_rtc(const uint8_t *data, unsigned size) { + if(size < 512) return false; + if(data[0x0147] == 0x0f || data[0x0147] == 0x10) return true; + return false; +} + +unsigned SNESCartridge::sufamiturbo_ram_size(const uint8_t *data, unsigned size) { + if(size < 0x38) return 0; + return data[0x37] * 2048; +} + +} + +#endif From 8702dfb8978d40152787f9c19d6e588cf83530e0 Mon Sep 17 00:00:00 2001 From: MrL314 <11429905+MrL314@users.noreply.github.com> Date: Wed, 24 Nov 2021 16:32:47 -0500 Subject: [PATCH 2/5] added dos auto-mapping sans manifest --- bsnes/snes/cartridge/cartridge.cpp | 337 ++++---- bsnes/snes/cartridge/xml.cpp | 1261 ++++++++++++++-------------- 2 files changed, 794 insertions(+), 804 deletions(-) diff --git a/bsnes/snes/cartridge/cartridge.cpp b/bsnes/snes/cartridge/cartridge.cpp index 1b5e40de..9c4c6ce1 100644 --- a/bsnes/snes/cartridge/cartridge.cpp +++ b/bsnes/snes/cartridge/cartridge.cpp @@ -1,169 +1,168 @@ -#include - -#include -#include - -#define CARTRIDGE_CPP -namespace SNES { - -#include "xml.cpp" -#include "serialization.cpp" - -namespace memory { - MappedRAM cartrom, cartram, cartrtc; - MappedRAM bsxpack, bsxpram; - MappedRAM stArom, stAram; - MappedRAM stBrom, stBram; - MappedRAM gbrom, gbram, gbrtc; -}; - -Cartridge cartridge; - -int Cartridge::rom_offset(unsigned addr) const { - Bus::Page &page = bus.page[addr >> 8]; - if (page.access == &memory::cartrom || - page.access == &memory::cx4rom || - page.access == &memory::gsurom || - page.access == &memory::fxrom || - page.access == &memory::vsprom) { - return page.offset + addr; - } - - return -1; -} - -void Cartridge::load(Mode cartridge_mode, const lstring &xml_list) { - mode = cartridge_mode; - region = Region::NTSC; - ram_size = 0; - spc7110_data_rom_offset = 0x100000; - st_A_ram_size = 0; - st_B_ram_size = 0; - bsxpack_type = BSXPackType::Unknown; - supergameboy_version = SuperGameBoyVersion::Version1; - supergameboy_ram_size = 0; - supergameboy_rtc_size = 0; - - has_bsx_slot = false; - has_superfx = false; - has_sa1 = false; - has_necdsp = false; - has_srtc = false; - has_sdd1 = false; - has_spc7110 = false; - has_spc7110rtc = false; - has_cx4 = false; - has_obc1 = false; - has_st0018 = false; - has_msu1 = false; - has_serial = false; - has_dos = false; - dos_mapped = false; - - parse_xml(xml_list); -//print(xml_list[0], "\n\n"); - - // autodetect MSU1 if it wasn't specified in a manifest - if(!has_msu1 && file::exists(string(basename(), ".msu"))) { - has_msu1 = true; - - Mapping m(msu1); - m.addrlo = 0x2000; - m.addrhi = 0x2007; - mapping.append(m); - } - - if(ram_size > 0) { - memory::cartram.map(allocate(ram_size, 0xff), ram_size); - } - - if(has_srtc || has_spc7110rtc) { - memory::cartrtc.map(allocate(20, 0xff), 20); - } - - if(mode == Mode::Bsx) { - memory::bsxpram.map(allocate(512 * 1024, 0xff), 512 * 1024); - } - - if(mode == Mode::SufamiTurbo) { - if(st_A_ram_size) memory::stAram.map(allocate(st_A_ram_size, 0xff), st_A_ram_size); - if(st_B_ram_size) memory::stBram.map(allocate(st_B_ram_size, 0xff), st_B_ram_size); - } - - if(mode == Mode::SuperGameBoy) { - if(memory::gbrom.data()) { - if(supergameboy_ram_size) memory::gbram.map(allocate(supergameboy_ram_size, 0xff), supergameboy_ram_size); - if(supergameboy_rtc_size) memory::gbrtc.map(allocate(supergameboy_rtc_size, 0x00), supergameboy_rtc_size); - } - } - - memory::cartrom.write_protect(true); - memory::cartram.write_protect(false); - memory::cartrtc.write_protect(false); - memory::bsxpack.write_protect(true); - memory::bsxpram.write_protect(false); - memory::stArom.write_protect(true); - memory::stAram.write_protect(false); - memory::stBrom.write_protect(true); - memory::stBram.write_protect(false); - memory::gbrom.write_protect(true); - memory::gbram.write_protect(false); - memory::gbrtc.write_protect(false); - - unsigned checksum = ~0; foreach(n, memory::cartrom) checksum = crc32_adjust(checksum, n); - if(memory::bsxpack.size() != 0) foreach(n, memory::bsxpack) checksum = crc32_adjust(checksum, n); - if(memory::stArom.size() != 0) foreach(n, memory::stArom ) checksum = crc32_adjust(checksum, n); - if(memory::stBrom.size() != 0) foreach(n, memory::stBrom ) checksum = crc32_adjust(checksum, n); - if(memory::gbrom.size() != 0) foreach(n, memory::gbrom ) checksum = crc32_adjust(checksum, n); - crc32 = ~checksum; - - sha256_ctx sha; - uint8_t shahash[32]; - sha256_init(&sha); - sha256_chunk(&sha, memory::cartrom.data(), memory::cartrom.size()); - sha256_final(&sha); - sha256_hash(&sha, shahash); - - string hash; - foreach(n, shahash) hash << hex<2>(n); - sha256 = hash; - - bus.load_cart(); - system.serialize_init(); - loaded = true; -} - -void Cartridge::unload() { - memory::cartrom.reset(); - memory::cartram.reset(); - memory::cartrtc.reset(); - memory::bsxpack.reset(); - memory::bsxpram.reset(); - memory::stArom.reset(); - memory::stAram.reset(); - memory::stBrom.reset(); - memory::stBram.reset(); - memory::gbrom.reset(); - memory::gbram.reset(); - memory::gbrtc.reset(); - - if(loaded == false) return; - bus.unload_cart(); - loaded = false; -} - -Memory& Cartridge::bsxpack_access() { - if(memory::bsxpack.size() == 0) return memory::memory_unmapped; - return (bsxpack_type == BSXPackType::FlashROM) ? (Memory&)bsxflash : (Memory&)memory::bsxpack; -} - -Cartridge::Cartridge() { - loaded = false; - unload(); -} - -Cartridge::~Cartridge() { - unload(); -} - -} +#include + +#include +#include + +#define CARTRIDGE_CPP +namespace SNES { + +#include "xml.cpp" +#include "serialization.cpp" + +namespace memory { + MappedRAM cartrom, cartram, cartrtc; + MappedRAM bsxpack, bsxpram; + MappedRAM stArom, stAram; + MappedRAM stBrom, stBram; + MappedRAM gbrom, gbram, gbrtc; +}; + +Cartridge cartridge; + +int Cartridge::rom_offset(unsigned addr) const { + Bus::Page &page = bus.page[addr >> 8]; + if (page.access == &memory::cartrom || + page.access == &memory::cx4rom || + page.access == &memory::gsurom || + page.access == &memory::fxrom || + page.access == &memory::vsprom) { + return page.offset + addr; + } + + return -1; +} + +void Cartridge::load(Mode cartridge_mode, const lstring &xml_list) { + mode = cartridge_mode; + region = Region::NTSC; + ram_size = 0; + spc7110_data_rom_offset = 0x100000; + st_A_ram_size = 0; + st_B_ram_size = 0; + bsxpack_type = BSXPackType::Unknown; + supergameboy_version = SuperGameBoyVersion::Version1; + supergameboy_ram_size = 0; + supergameboy_rtc_size = 0; + + has_bsx_slot = false; + has_superfx = false; + has_sa1 = false; + has_necdsp = false; + has_srtc = false; + has_sdd1 = false; + has_spc7110 = false; + has_spc7110rtc = false; + has_cx4 = false; + has_obc1 = false; + has_st0018 = false; + has_msu1 = false; + has_serial = false; + has_dos = false; + + parse_xml(xml_list); +//print(xml_list[0], "\n\n"); + + // autodetect MSU1 if it wasn't specified in a manifest + if(!has_msu1 && file::exists(string(basename(), ".msu"))) { + has_msu1 = true; + + Mapping m(msu1); + m.addrlo = 0x2000; + m.addrhi = 0x2007; + mapping.append(m); + } + + if(ram_size > 0) { + memory::cartram.map(allocate(ram_size, 0xff), ram_size); + } + + if(has_srtc || has_spc7110rtc) { + memory::cartrtc.map(allocate(20, 0xff), 20); + } + + if(mode == Mode::Bsx) { + memory::bsxpram.map(allocate(512 * 1024, 0xff), 512 * 1024); + } + + if(mode == Mode::SufamiTurbo) { + if(st_A_ram_size) memory::stAram.map(allocate(st_A_ram_size, 0xff), st_A_ram_size); + if(st_B_ram_size) memory::stBram.map(allocate(st_B_ram_size, 0xff), st_B_ram_size); + } + + if(mode == Mode::SuperGameBoy) { + if(memory::gbrom.data()) { + if(supergameboy_ram_size) memory::gbram.map(allocate(supergameboy_ram_size, 0xff), supergameboy_ram_size); + if(supergameboy_rtc_size) memory::gbrtc.map(allocate(supergameboy_rtc_size, 0x00), supergameboy_rtc_size); + } + } + + memory::cartrom.write_protect(true); + memory::cartram.write_protect(false); + memory::cartrtc.write_protect(false); + memory::bsxpack.write_protect(true); + memory::bsxpram.write_protect(false); + memory::stArom.write_protect(true); + memory::stAram.write_protect(false); + memory::stBrom.write_protect(true); + memory::stBram.write_protect(false); + memory::gbrom.write_protect(true); + memory::gbram.write_protect(false); + memory::gbrtc.write_protect(false); + + unsigned checksum = ~0; foreach(n, memory::cartrom) checksum = crc32_adjust(checksum, n); + if(memory::bsxpack.size() != 0) foreach(n, memory::bsxpack) checksum = crc32_adjust(checksum, n); + if(memory::stArom.size() != 0) foreach(n, memory::stArom ) checksum = crc32_adjust(checksum, n); + if(memory::stBrom.size() != 0) foreach(n, memory::stBrom ) checksum = crc32_adjust(checksum, n); + if(memory::gbrom.size() != 0) foreach(n, memory::gbrom ) checksum = crc32_adjust(checksum, n); + crc32 = ~checksum; + + sha256_ctx sha; + uint8_t shahash[32]; + sha256_init(&sha); + sha256_chunk(&sha, memory::cartrom.data(), memory::cartrom.size()); + sha256_final(&sha); + sha256_hash(&sha, shahash); + + string hash; + foreach(n, shahash) hash << hex<2>(n); + sha256 = hash; + + bus.load_cart(); + system.serialize_init(); + loaded = true; +} + +void Cartridge::unload() { + memory::cartrom.reset(); + memory::cartram.reset(); + memory::cartrtc.reset(); + memory::bsxpack.reset(); + memory::bsxpram.reset(); + memory::stArom.reset(); + memory::stAram.reset(); + memory::stBrom.reset(); + memory::stBram.reset(); + memory::gbrom.reset(); + memory::gbram.reset(); + memory::gbrtc.reset(); + + if(loaded == false) return; + bus.unload_cart(); + loaded = false; +} + +Memory& Cartridge::bsxpack_access() { + if(memory::bsxpack.size() == 0) return memory::memory_unmapped; + return (bsxpack_type == BSXPackType::FlashROM) ? (Memory&)bsxflash : (Memory&)memory::bsxpack; +} + +Cartridge::Cartridge() { + loaded = false; + unload(); +} + +Cartridge::~Cartridge() { + unload(); +} + +} diff --git a/bsnes/snes/cartridge/xml.cpp b/bsnes/snes/cartridge/xml.cpp index f1ec026f..f3355e7a 100644 --- a/bsnes/snes/cartridge/xml.cpp +++ b/bsnes/snes/cartridge/xml.cpp @@ -1,635 +1,626 @@ -#ifdef CARTRIDGE_CPP - -void Cartridge::parse_xml(const lstring &list) { - mapping.reset(); - - //parse any slots *before* parsing the base cartridge - if(mode == Mode::BsxSlotted) { - parse_xml_bsx(list[1]); - } else if(mode == Mode::Bsx) { - parse_xml_bsx(list[1]); - } else if(mode == Mode::SufamiTurbo) { - parse_xml_sufami_turbo(list[1], 0); - parse_xml_sufami_turbo(list[2], 1); - } else if(mode == Mode::SuperGameBoy) { - parse_xml_gameboy(list[1]); - } - - parse_xml_cartridge(list[0]); -} - -void Cartridge::parse_xml_cartridge(const char *data) { - xml_element document = xml_parse(data); - if(document.element.size() == 0) return; - - - foreach(head, document.element) { - if(head.name == "cartridge") { - foreach(attr, head.attribute) { - if(attr.name == "region") { - if(attr.content == "NTSC") region = Region::NTSC; - if(attr.content == "PAL") region = Region::PAL; - } - } - - foreach(node, head.element) { - if(node.name == "rom") xml_parse_rom(node); - if(node.name == "ram") xml_parse_ram(node); - if(node.name == "superfx") xml_parse_superfx(node); - if(node.name == "sa1") xml_parse_sa1(node); - if(node.name == "necdsp") xml_parse_necdsp(node); - if(node.name == "bsx") xml_parse_bsx(node); - if(node.name == "sufamiturbo") xml_parse_sufamiturbo(node); - if(node.name == "supergameboy") xml_parse_supergameboy(node); - if(node.name == "srtc") xml_parse_srtc(node); - if(node.name == "sdd1") xml_parse_sdd1(node); - if(node.name == "spc7110") xml_parse_spc7110(node); - if(node.name == "cx4") xml_parse_cx4(node); - if(node.name == "obc1") xml_parse_obc1(node); - if(node.name == "setarisc") xml_parse_setarisc(node); - if(node.name == "msu1") xml_parse_msu1(node); - if(node.name == "serial") xml_parse_serial(node); - if(node.name == "dos") xml_parse_dos(node); - } - } - } - - // set dos mapping here if no mapping given. Fix later? - if (!dos_mapped) { - has_dos = true; - dos_mapped = true; - bus.map(Bus::MapMode::Direct, 0x00, 0x3f, 0x5f00, 0x5fff, dos); - bus.map(Bus::MapMode::Direct, 0x80, 0xbf, 0x5f00, 0x5fff, dos); - } -} - -void Cartridge::parse_xml_bsx(const char *data) { - xml_element document = xml_parse(data); - if(document.element.size() == 0) return; - - bsxpack_type = BSXPackType::FlashROM; - - foreach(head, document.element) { - if(head.name == "cartridge") { - foreach(attr, head.attribute) { - if(attr.name == "type") { - if(attr.content == "FlashROM") bsxpack_type = BSXPackType::FlashROM; - if(attr.content == "MaskROM") bsxpack_type = BSXPackType::MaskROM; - } - } - } - } -} - -void Cartridge::parse_xml_sufami_turbo(const char *data, bool slot) { - xml_element document = xml_parse(data); - if(document.element.size() == 0) return; - - foreach(head, document.element) { - if(head.name == "cartridge") { - foreach(leaf, head.element) { - if(leaf.name == "ram") { - foreach(attr, leaf.attribute) { - if(attr.name == "size") { - (slot == 0 ? st_A_ram_size : st_B_ram_size) = hex(attr.content); - } - } - } - } - } - } -} - -void Cartridge::parse_xml_gameboy(const char *data) { - xml_element document = xml_parse(data); - if(document.element.size() == 0) return; - - foreach(head, document.element) { - if(head.name == "cartridge") { - foreach(attr, head.attribute) { - if(attr.name == "rtc") { - supergameboy_rtc_size = (attr.content == "true") ? 4 : 0; - } - } - - foreach(leaf, head.element) { - if(leaf.name == "ram") { - foreach(attr, leaf.attribute) { - if(attr.name == "size") { - supergameboy_ram_size = hex(attr.content); - } - } - } - } - } - } -} - -void Cartridge::xml_parse_memory(xml_element &root, Memory &memory) { - foreach(leaf, root.element) { - if(leaf.name == "map") { - Mapping m(memory); - foreach(attr, leaf.attribute) { - if(attr.name == "address") xml_parse_address(m, attr.content); - if(attr.name == "mode") xml_parse_mode(m, attr.content); - if(attr.name == "offset") m.offset = hex(attr.content); - if(attr.name == "size") m.size = hex(attr.content); - } - mapping.append(m); - } - } -} - -void Cartridge::xml_parse_rom(xml_element &root) { - xml_parse_memory(root, memory::cartrom); -} - -void Cartridge::xml_parse_ram(xml_element &root) { - foreach(attr, root.attribute) { - if(attr.name == "size") ram_size = hex(attr.content); - } - if(ram_size > 0) { - xml_parse_memory(root, memory::cartram); - } -} - -void Cartridge::xml_parse_superfx(xml_element &root) { - has_superfx = true; - - foreach(node, root.element) { - if(node.name == "rom") { - xml_parse_memory(node, memory::fxrom); - } else if(node.name == "ram") { - foreach(attr, node.attribute) { - if(attr.name == "size") ram_size = hex(attr.content); - } - if(ram_size > 0) { - xml_parse_memory(node, memory::fxram); - } - } else if(node.name == "mmio") { - foreach(leaf, node.element) { - if(leaf.name == "map") { - Mapping m(superfx); - foreach(attr, leaf.attribute) { - if(attr.name == "address") xml_parse_address(m, attr.content); - } - mapping.append(m); - } - } - } - } -} - -void Cartridge::xml_parse_sa1(xml_element &root) { - has_sa1 = true; - - foreach(node, root.element) { - if(node.name == "rom") { - xml_parse_memory(node, memory::vsprom); - } else if(node.name == "iram") { - xml_parse_memory(node, memory::cpuiram); - } else if(node.name == "bwram") { - foreach(attr, node.attribute) { - if(attr.name == "size") ram_size = hex(attr.content); - } - if (ram_size) { - xml_parse_memory(node, memory::cc1bwram); - } - } else if(node.name == "mmio") { - foreach(leaf, node.element) { - if(leaf.name == "map") { - Mapping m(sa1); - foreach(attr, leaf.attribute) { - if(attr.name == "address") xml_parse_address(m, attr.content); - } - mapping.append(m); - } - } - } - } -} - -void Cartridge::xml_parse_necdsp(xml_element &root) { - has_necdsp = true; - necdsp.revision = NECDSP::Revision::uPD7725; - necdsp.frequency = 8000000; - - for(unsigned n = 0; n < 16384; n++) necdsp.programROM[n] = 0x000000; - for(unsigned n = 0; n < 2048; n++) necdsp.dataROM[n] = 0x0000; - - string program, programhash; - string sha256; - - foreach(attr, root.attribute) { - if(attr.name == "revision") { - if(attr.content == "upd7725" ) necdsp.revision = NECDSP::Revision::uPD7725; - if(attr.content == "upd96050") necdsp.revision = NECDSP::Revision::uPD96050; - } else if(attr.name == "frequency") { - necdsp.frequency = decimal(attr.content); - } else if(attr.name == "program") { - program << filepath(dir(basename()), config().path.firmware); - program << attr.content; - } else if(attr.name == "sha256") { - sha256 = attr.content; - } - } - - unsigned promsize = (necdsp.revision == NECDSP::Revision::uPD7725 ? 2048 : 16384); - unsigned dromsize = (necdsp.revision == NECDSP::Revision::uPD7725 ? 1024 : 2048); - unsigned filesize = promsize * 3 + dromsize * 2; - - file fp; - if(fp.open(program, file::mode::read)) { - if(fp.size() == filesize) { - for(unsigned n = 0; n < promsize; n++) necdsp.programROM[n] = fp.readm(3); - for(unsigned n = 0; n < dromsize; n++) necdsp.dataROM[n] = fp.readm(2); - - fp.seek(0); - uint8_t data[filesize]; - fp.read(data, filesize); - - sha256_ctx sha; - uint8 shahash[32]; - sha256_init(&sha); - sha256_chunk(&sha, data, filesize); - sha256_final(&sha); - sha256_hash(&sha, shahash); - foreach(n, shahash) programhash.append(hex<2>(n)); - } - fp.close(); - } - - foreach(node, root.element) { - if(node.name == "dr") { - foreach(attr, node.attribute) { - if(attr.name == "mask") necdsp.drmask = hex(attr.content); - if(attr.name == "test") necdsp.drtest = hex(attr.content); - } - } - - if(node.name == "sr") { - foreach(attr, node.attribute) { - if(attr.name == "mask") necdsp.srmask = hex(attr.content); - if(attr.name == "test") necdsp.srtest = hex(attr.content); - } - } - - if(node.name == "dp") { - foreach(attr, node.attribute) { - if(attr.name == "mask") necdsp.dpmask = hex(attr.content); - if(attr.name == "test") necdsp.dptest = hex(attr.content); - } - } - - if(node.name == "map") { - Mapping m(necdsp); - foreach(attr, node.attribute) { - if(attr.name == "address") xml_parse_address(m, attr.content); - } - mapping.append(m); - } - } - - if(programhash == "") { - system.interface->message({ "Warning: NEC DSP program ", program, " is missing." }); - } else if(sha256 != "" && sha256 != programhash) { - system.interface->message({ - "Warning: NEC DSP program ", program, " SHA256 is incorrect.\n\n" - "Expected:\n", sha256, "\n\n" - "Actual:\n", programhash - }); - } -} - -void Cartridge::xml_parse_bsx(xml_element &root) { - has_bsx_slot = true; - if(mode != Mode::BsxSlotted && mode != Mode::Bsx) return; - - foreach(node, root.element) { - if(node.name == "slot") { - xml_parse_memory(node, bsxpack_access()); - } else if(node.name == "mcc") { - foreach(leaf, node.element) { - if(leaf.name == "map") { - Mapping m(bsxcart); - foreach(attr, leaf.attribute) { - if(attr.name == "address") xml_parse_address(m, attr.content); - } - mapping.append(m); - } - } - } - } -} - -void Cartridge::xml_parse_sufamiturbo(xml_element &root) { - if(mode != Mode::SufamiTurbo) return; - - foreach(node, root.element) { - if(node.name == "slot") { - bool slotid = 0; - foreach(attr, node.attribute) { - if(attr.name == "id") { - if(attr.content == "A") slotid = 0; - if(attr.content == "B") slotid = 1; - } - } - - Memory &rom = (slotid == 0) ? memory::stArom : memory::stBrom; - if(rom.size() == 0) continue; - Memory &ram = (slotid == 0) ? memory::stAram : memory::stBram; - unsigned ram_size = (slotid == 0) ? st_A_ram_size : st_B_ram_size; - - foreach(slot, node.element) { - if(slot.name == "rom") { - xml_parse_memory(slot, rom); - } else if(slot.name == "ram" && ram_size > 0) { - xml_parse_memory(slot, ram); - } - } - } - } -} - -void Cartridge::xml_parse_supergameboy(xml_element &root) { - if(mode != Mode::SuperGameBoy) return; - - foreach(attr, root.attribute) { - if(attr.name == "revision") { - if(attr.content == "1") supergameboy_version = SuperGameBoyVersion::Version1; - if(attr.content == "2") supergameboy_version = SuperGameBoyVersion::Version2; - } - } - - foreach(node, root.element) { - if(node.name == "map") { - Mapping m((Memory&)supergameboy); - foreach(attr, node.attribute) { - if(attr.name == "address") xml_parse_address(m, attr.content); - } - mapping.append(m); - } - } -} - -void Cartridge::xml_parse_srtc(xml_element &root) { - has_srtc = true; - - foreach(node, root.element) { - if(node.name == "map") { - Mapping m(srtc); - foreach(attr, node.attribute) { - if(attr.name == "address") xml_parse_address(m, attr.content); - } - mapping.append(m); - } - } -} - -void Cartridge::xml_parse_sdd1(xml_element &root) { - has_sdd1 = true; - - foreach(node, root.element) { - if(node.name == "mcu") { - foreach(leaf, node.element) { - if(leaf.name == "map") { - Mapping m((Memory&)sdd1); - foreach(attr, leaf.attribute) { - if(attr.name == "address") xml_parse_address(m, attr.content); - } - mapping.append(m); - } - } - } else if(node.name == "mmio") { - foreach(leaf, node.element) { - if(leaf.name == "map") { - Mapping m((MMIO&)sdd1); - foreach(attr, leaf.attribute) { - if(attr.name == "address") xml_parse_address(m, attr.content); - } - mapping.append(m); - } - } - } - } -} - -void Cartridge::xml_parse_spc7110(xml_element &root) { - has_spc7110 = true; - - foreach(node, root.element) { - if(node.name == "dcu") { - foreach(leaf, node.element) { - if(leaf.name == "map") { - Mapping m(spc7110dcu); - foreach(attr, leaf.attribute) { - if(attr.name == "address") xml_parse_address(m, attr.content); - } - mapping.append(m); - } - } - } else if(node.name == "mcu") { - foreach(leaf, node.element) { - if(leaf.name == "map") { - Mapping m(spc7110mcu); - foreach(attr, leaf.attribute) { - if(attr.name == "address") xml_parse_address(m, attr.content); - if(attr.name == "offset") spc7110_data_rom_offset = hex(attr.content); - } - mapping.append(m); - } - } - } else if(node.name == "mmio") { - foreach(leaf, node.element) { - if(leaf.name == "map") { - Mapping m(spc7110); - foreach(attr, leaf.attribute) { - if(attr.name == "address") xml_parse_address(m, attr.content); - } - mapping.append(m); - } - } - } else if(node.name == "ram") { - foreach(attr, node.attribute) { - if(attr.name == "size") ram_size = hex(attr.content); - } - if(ram_size > 0) { - xml_parse_memory(node, spc7110ram); - } - } else if(node.name == "rtc") { - has_spc7110rtc = true; - - foreach(leaf, node.element) { - if(leaf.name == "map") { - Mapping m(spc7110); - foreach(attr, leaf.attribute) { - if(attr.name == "address") xml_parse_address(m, attr.content); - } - mapping.append(m); - } - } - } - } -} - -void Cartridge::xml_parse_cx4(xml_element &root) { - has_cx4 = true; - cx4.frequency = 20000000; - - // TODO: allow custom data ROM, maybe - - foreach(attr, root.attribute) { - if(attr.name == "frequency") { - cx4.frequency = decimal(attr.content); - } - } - - foreach(node, root.element) { - if(node.name == "rom") { - xml_parse_memory(node, memory::cx4rom); - } else if(node.name == "ram") { - xml_parse_memory(node, memory::cx4ram); - } else if(node.name == "map") { - Mapping m(cx4); - foreach(attr, node.attribute) { - if(attr.name == "address") xml_parse_address(m, attr.content); - } - mapping.append(m); - } - } -} - -void Cartridge::xml_parse_obc1(xml_element &root) { - has_obc1 = true; - - foreach(node, root.element) { - if(node.name == "map") { - Mapping m(obc1); - foreach(attr, node.attribute) { - if(attr.name == "address") xml_parse_address(m, attr.content); - } - m.min_ram_size = 0x2000; - mapping.append(m); - } - } -} - -void Cartridge::xml_parse_setarisc(xml_element &root) { - unsigned program = 0; - - foreach(attr, root.attribute) { - if(attr.name == "program") { - if(attr.content == "ST-0018") { - program = 1; - has_st0018 = true; - } - } - } - - MMIO *map[2] = { 0, &st0018 }; - - foreach(node, root.element) { - if(node.name == "map" && map[program]) { - Mapping m(*map[program]); - foreach(attr, node.attribute) { - if(attr.name == "address") xml_parse_address(m, attr.content); - } - mapping.append(m); - } - } -} - -void Cartridge::xml_parse_msu1(xml_element &root) { - has_msu1 = true; - - foreach(node, root.element) { - if(node.name == "mmio") { - foreach(leaf, node.element) { - if(leaf.name == "map") { - Mapping m(msu1); - foreach(attr, leaf.attribute) { - if(attr.name == "address") xml_parse_address(m, attr.content); - } - mapping.append(m); - } - } - } - } -} - -void Cartridge::xml_parse_serial(xml_element &root) { - has_serial = true; -} - - -void Cartridge::xml_parse_dos(xml_element& root) { - has_dos = true; - - foreach(node, root.element) { - if (node.name == "map") { - Mapping m(dos); - foreach(attr, node.attribute) { - if (attr.name == "address") { - xml_parse_address(m, attr.content); - dos_mapped = true; - } - } - mapping.append(m); - } - } -} - - -void Cartridge::xml_parse_address(Mapping &m, const string &data) { - lstring part; - part.split(":", data); - if(part.size() != 2) return; - - lstring subpart; - subpart.split("-", part[0]); - if(subpart.size() == 1) { - m.banklo = hex(subpart[0]); - m.bankhi = m.banklo; - } else if(subpart.size() == 2) { - m.banklo = hex(subpart[0]); - m.bankhi = hex(subpart[1]); - } - - subpart.split("-", part[1]); - if(subpart.size() == 1) { - m.addrlo = hex(subpart[0]); - m.addrhi = m.addrlo; - } else if(subpart.size() == 2) { - m.addrlo = hex(subpart[0]); - m.addrhi = hex(subpart[1]); - } -} - -void Cartridge::xml_parse_mode(Mapping &m, const string& data) { - if(data == "direct") m.mode = Bus::MapMode::Direct; - else if(data == "linear") m.mode = Bus::MapMode::Linear; - else if(data == "shadow") m.mode = Bus::MapMode::Shadow; -} - -Cartridge::Mapping::Mapping() { - memory = 0; - mmio = 0; - mode = Bus::MapMode::Direct; - banklo = bankhi = addrlo = addrhi = offset = size = min_ram_size = 0; -} - -Cartridge::Mapping::Mapping(Memory &memory_) { - memory = &memory_; - mmio = 0; - mode = Bus::MapMode::Direct; - banklo = bankhi = addrlo = addrhi = offset = size = min_ram_size = 0; -} - -Cartridge::Mapping::Mapping(MMIO &mmio_) { - memory = 0; - mmio = &mmio_; - mode = Bus::MapMode::Direct; - banklo = bankhi = addrlo = addrhi = offset = size = min_ram_size = 0; -} - -#endif +#ifdef CARTRIDGE_CPP + +void Cartridge::parse_xml(const lstring &list) { + mapping.reset(); + + //parse any slots *before* parsing the base cartridge + if(mode == Mode::BsxSlotted) { + parse_xml_bsx(list[1]); + } else if(mode == Mode::Bsx) { + parse_xml_bsx(list[1]); + } else if(mode == Mode::SufamiTurbo) { + parse_xml_sufami_turbo(list[1], 0); + parse_xml_sufami_turbo(list[2], 1); + } else if(mode == Mode::SuperGameBoy) { + parse_xml_gameboy(list[1]); + } + + parse_xml_cartridge(list[0]); +} + +void Cartridge::parse_xml_cartridge(const char *data) { + xml_element document = xml_parse(data); + if(document.element.size() == 0) return; + + + foreach(head, document.element) { + if(head.name == "cartridge") { + foreach(attr, head.attribute) { + if(attr.name == "region") { + if(attr.content == "NTSC") region = Region::NTSC; + if(attr.content == "PAL") region = Region::PAL; + } + } + + foreach(node, head.element) { + if(node.name == "rom") xml_parse_rom(node); + if(node.name == "ram") xml_parse_ram(node); + if(node.name == "superfx") xml_parse_superfx(node); + if(node.name == "sa1") xml_parse_sa1(node); + if(node.name == "necdsp") xml_parse_necdsp(node); + if(node.name == "bsx") xml_parse_bsx(node); + if(node.name == "sufamiturbo") xml_parse_sufamiturbo(node); + if(node.name == "supergameboy") xml_parse_supergameboy(node); + if(node.name == "srtc") xml_parse_srtc(node); + if(node.name == "sdd1") xml_parse_sdd1(node); + if(node.name == "spc7110") xml_parse_spc7110(node); + if(node.name == "cx4") xml_parse_cx4(node); + if(node.name == "obc1") xml_parse_obc1(node); + if(node.name == "setarisc") xml_parse_setarisc(node); + if(node.name == "msu1") xml_parse_msu1(node); + if(node.name == "serial") xml_parse_serial(node); + if(node.name == "dos") xml_parse_dos(node); + } + } + } +} + +void Cartridge::parse_xml_bsx(const char *data) { + xml_element document = xml_parse(data); + if(document.element.size() == 0) return; + + bsxpack_type = BSXPackType::FlashROM; + + foreach(head, document.element) { + if(head.name == "cartridge") { + foreach(attr, head.attribute) { + if(attr.name == "type") { + if(attr.content == "FlashROM") bsxpack_type = BSXPackType::FlashROM; + if(attr.content == "MaskROM") bsxpack_type = BSXPackType::MaskROM; + } + } + } + } +} + +void Cartridge::parse_xml_sufami_turbo(const char *data, bool slot) { + xml_element document = xml_parse(data); + if(document.element.size() == 0) return; + + foreach(head, document.element) { + if(head.name == "cartridge") { + foreach(leaf, head.element) { + if(leaf.name == "ram") { + foreach(attr, leaf.attribute) { + if(attr.name == "size") { + (slot == 0 ? st_A_ram_size : st_B_ram_size) = hex(attr.content); + } + } + } + } + } + } +} + +void Cartridge::parse_xml_gameboy(const char *data) { + xml_element document = xml_parse(data); + if(document.element.size() == 0) return; + + foreach(head, document.element) { + if(head.name == "cartridge") { + foreach(attr, head.attribute) { + if(attr.name == "rtc") { + supergameboy_rtc_size = (attr.content == "true") ? 4 : 0; + } + } + + foreach(leaf, head.element) { + if(leaf.name == "ram") { + foreach(attr, leaf.attribute) { + if(attr.name == "size") { + supergameboy_ram_size = hex(attr.content); + } + } + } + } + } + } +} + +void Cartridge::xml_parse_memory(xml_element &root, Memory &memory) { + foreach(leaf, root.element) { + if(leaf.name == "map") { + Mapping m(memory); + foreach(attr, leaf.attribute) { + if(attr.name == "address") xml_parse_address(m, attr.content); + if(attr.name == "mode") xml_parse_mode(m, attr.content); + if(attr.name == "offset") m.offset = hex(attr.content); + if(attr.name == "size") m.size = hex(attr.content); + } + mapping.append(m); + } + } +} + +void Cartridge::xml_parse_rom(xml_element &root) { + xml_parse_memory(root, memory::cartrom); +} + +void Cartridge::xml_parse_ram(xml_element &root) { + foreach(attr, root.attribute) { + if(attr.name == "size") ram_size = hex(attr.content); + } + if(ram_size > 0) { + xml_parse_memory(root, memory::cartram); + } +} + +void Cartridge::xml_parse_superfx(xml_element &root) { + has_superfx = true; + + foreach(node, root.element) { + if(node.name == "rom") { + xml_parse_memory(node, memory::fxrom); + } else if(node.name == "ram") { + foreach(attr, node.attribute) { + if(attr.name == "size") ram_size = hex(attr.content); + } + if(ram_size > 0) { + xml_parse_memory(node, memory::fxram); + } + } else if(node.name == "mmio") { + foreach(leaf, node.element) { + if(leaf.name == "map") { + Mapping m(superfx); + foreach(attr, leaf.attribute) { + if(attr.name == "address") xml_parse_address(m, attr.content); + } + mapping.append(m); + } + } + } + } +} + +void Cartridge::xml_parse_sa1(xml_element &root) { + has_sa1 = true; + + foreach(node, root.element) { + if(node.name == "rom") { + xml_parse_memory(node, memory::vsprom); + } else if(node.name == "iram") { + xml_parse_memory(node, memory::cpuiram); + } else if(node.name == "bwram") { + foreach(attr, node.attribute) { + if(attr.name == "size") ram_size = hex(attr.content); + } + if (ram_size) { + xml_parse_memory(node, memory::cc1bwram); + } + } else if(node.name == "mmio") { + foreach(leaf, node.element) { + if(leaf.name == "map") { + Mapping m(sa1); + foreach(attr, leaf.attribute) { + if(attr.name == "address") xml_parse_address(m, attr.content); + } + mapping.append(m); + } + } + } + } +} + +void Cartridge::xml_parse_necdsp(xml_element &root) { + has_necdsp = true; + necdsp.revision = NECDSP::Revision::uPD7725; + necdsp.frequency = 8000000; + + for(unsigned n = 0; n < 16384; n++) necdsp.programROM[n] = 0x000000; + for(unsigned n = 0; n < 2048; n++) necdsp.dataROM[n] = 0x0000; + + string program, programhash; + string sha256; + + foreach(attr, root.attribute) { + if(attr.name == "revision") { + if(attr.content == "upd7725" ) necdsp.revision = NECDSP::Revision::uPD7725; + if(attr.content == "upd96050") necdsp.revision = NECDSP::Revision::uPD96050; + } else if(attr.name == "frequency") { + necdsp.frequency = decimal(attr.content); + } else if(attr.name == "program") { + program << filepath(dir(basename()), config().path.firmware); + program << attr.content; + } else if(attr.name == "sha256") { + sha256 = attr.content; + } + } + + unsigned promsize = (necdsp.revision == NECDSP::Revision::uPD7725 ? 2048 : 16384); + unsigned dromsize = (necdsp.revision == NECDSP::Revision::uPD7725 ? 1024 : 2048); + unsigned filesize = promsize * 3 + dromsize * 2; + + file fp; + if(fp.open(program, file::mode::read)) { + if(fp.size() == filesize) { + for(unsigned n = 0; n < promsize; n++) necdsp.programROM[n] = fp.readm(3); + for(unsigned n = 0; n < dromsize; n++) necdsp.dataROM[n] = fp.readm(2); + + fp.seek(0); + uint8_t data[filesize]; + fp.read(data, filesize); + + sha256_ctx sha; + uint8 shahash[32]; + sha256_init(&sha); + sha256_chunk(&sha, data, filesize); + sha256_final(&sha); + sha256_hash(&sha, shahash); + foreach(n, shahash) programhash.append(hex<2>(n)); + } + fp.close(); + } + + foreach(node, root.element) { + if(node.name == "dr") { + foreach(attr, node.attribute) { + if(attr.name == "mask") necdsp.drmask = hex(attr.content); + if(attr.name == "test") necdsp.drtest = hex(attr.content); + } + } + + if(node.name == "sr") { + foreach(attr, node.attribute) { + if(attr.name == "mask") necdsp.srmask = hex(attr.content); + if(attr.name == "test") necdsp.srtest = hex(attr.content); + } + } + + if(node.name == "dp") { + foreach(attr, node.attribute) { + if(attr.name == "mask") necdsp.dpmask = hex(attr.content); + if(attr.name == "test") necdsp.dptest = hex(attr.content); + } + } + + if(node.name == "map") { + Mapping m(necdsp); + foreach(attr, node.attribute) { + if(attr.name == "address") xml_parse_address(m, attr.content); + } + mapping.append(m); + } + } + + if(programhash == "") { + system.interface->message({ "Warning: NEC DSP program ", program, " is missing." }); + } else if(sha256 != "" && sha256 != programhash) { + system.interface->message({ + "Warning: NEC DSP program ", program, " SHA256 is incorrect.\n\n" + "Expected:\n", sha256, "\n\n" + "Actual:\n", programhash + }); + } +} + +void Cartridge::xml_parse_bsx(xml_element &root) { + has_bsx_slot = true; + if(mode != Mode::BsxSlotted && mode != Mode::Bsx) return; + + foreach(node, root.element) { + if(node.name == "slot") { + xml_parse_memory(node, bsxpack_access()); + } else if(node.name == "mcc") { + foreach(leaf, node.element) { + if(leaf.name == "map") { + Mapping m(bsxcart); + foreach(attr, leaf.attribute) { + if(attr.name == "address") xml_parse_address(m, attr.content); + } + mapping.append(m); + } + } + } + } +} + +void Cartridge::xml_parse_sufamiturbo(xml_element &root) { + if(mode != Mode::SufamiTurbo) return; + + foreach(node, root.element) { + if(node.name == "slot") { + bool slotid = 0; + foreach(attr, node.attribute) { + if(attr.name == "id") { + if(attr.content == "A") slotid = 0; + if(attr.content == "B") slotid = 1; + } + } + + Memory &rom = (slotid == 0) ? memory::stArom : memory::stBrom; + if(rom.size() == 0) continue; + Memory &ram = (slotid == 0) ? memory::stAram : memory::stBram; + unsigned ram_size = (slotid == 0) ? st_A_ram_size : st_B_ram_size; + + foreach(slot, node.element) { + if(slot.name == "rom") { + xml_parse_memory(slot, rom); + } else if(slot.name == "ram" && ram_size > 0) { + xml_parse_memory(slot, ram); + } + } + } + } +} + +void Cartridge::xml_parse_supergameboy(xml_element &root) { + if(mode != Mode::SuperGameBoy) return; + + foreach(attr, root.attribute) { + if(attr.name == "revision") { + if(attr.content == "1") supergameboy_version = SuperGameBoyVersion::Version1; + if(attr.content == "2") supergameboy_version = SuperGameBoyVersion::Version2; + } + } + + foreach(node, root.element) { + if(node.name == "map") { + Mapping m((Memory&)supergameboy); + foreach(attr, node.attribute) { + if(attr.name == "address") xml_parse_address(m, attr.content); + } + mapping.append(m); + } + } +} + +void Cartridge::xml_parse_srtc(xml_element &root) { + has_srtc = true; + + foreach(node, root.element) { + if(node.name == "map") { + Mapping m(srtc); + foreach(attr, node.attribute) { + if(attr.name == "address") xml_parse_address(m, attr.content); + } + mapping.append(m); + } + } +} + +void Cartridge::xml_parse_sdd1(xml_element &root) { + has_sdd1 = true; + + foreach(node, root.element) { + if(node.name == "mcu") { + foreach(leaf, node.element) { + if(leaf.name == "map") { + Mapping m((Memory&)sdd1); + foreach(attr, leaf.attribute) { + if(attr.name == "address") xml_parse_address(m, attr.content); + } + mapping.append(m); + } + } + } else if(node.name == "mmio") { + foreach(leaf, node.element) { + if(leaf.name == "map") { + Mapping m((MMIO&)sdd1); + foreach(attr, leaf.attribute) { + if(attr.name == "address") xml_parse_address(m, attr.content); + } + mapping.append(m); + } + } + } + } +} + +void Cartridge::xml_parse_spc7110(xml_element &root) { + has_spc7110 = true; + + foreach(node, root.element) { + if(node.name == "dcu") { + foreach(leaf, node.element) { + if(leaf.name == "map") { + Mapping m(spc7110dcu); + foreach(attr, leaf.attribute) { + if(attr.name == "address") xml_parse_address(m, attr.content); + } + mapping.append(m); + } + } + } else if(node.name == "mcu") { + foreach(leaf, node.element) { + if(leaf.name == "map") { + Mapping m(spc7110mcu); + foreach(attr, leaf.attribute) { + if(attr.name == "address") xml_parse_address(m, attr.content); + if(attr.name == "offset") spc7110_data_rom_offset = hex(attr.content); + } + mapping.append(m); + } + } + } else if(node.name == "mmio") { + foreach(leaf, node.element) { + if(leaf.name == "map") { + Mapping m(spc7110); + foreach(attr, leaf.attribute) { + if(attr.name == "address") xml_parse_address(m, attr.content); + } + mapping.append(m); + } + } + } else if(node.name == "ram") { + foreach(attr, node.attribute) { + if(attr.name == "size") ram_size = hex(attr.content); + } + if(ram_size > 0) { + xml_parse_memory(node, spc7110ram); + } + } else if(node.name == "rtc") { + has_spc7110rtc = true; + + foreach(leaf, node.element) { + if(leaf.name == "map") { + Mapping m(spc7110); + foreach(attr, leaf.attribute) { + if(attr.name == "address") xml_parse_address(m, attr.content); + } + mapping.append(m); + } + } + } + } +} + +void Cartridge::xml_parse_cx4(xml_element &root) { + has_cx4 = true; + cx4.frequency = 20000000; + + // TODO: allow custom data ROM, maybe + + foreach(attr, root.attribute) { + if(attr.name == "frequency") { + cx4.frequency = decimal(attr.content); + } + } + + foreach(node, root.element) { + if(node.name == "rom") { + xml_parse_memory(node, memory::cx4rom); + } else if(node.name == "ram") { + xml_parse_memory(node, memory::cx4ram); + } else if(node.name == "map") { + Mapping m(cx4); + foreach(attr, node.attribute) { + if(attr.name == "address") xml_parse_address(m, attr.content); + } + mapping.append(m); + } + } +} + +void Cartridge::xml_parse_obc1(xml_element &root) { + has_obc1 = true; + + foreach(node, root.element) { + if(node.name == "map") { + Mapping m(obc1); + foreach(attr, node.attribute) { + if(attr.name == "address") xml_parse_address(m, attr.content); + } + m.min_ram_size = 0x2000; + mapping.append(m); + } + } +} + +void Cartridge::xml_parse_setarisc(xml_element &root) { + unsigned program = 0; + + foreach(attr, root.attribute) { + if(attr.name == "program") { + if(attr.content == "ST-0018") { + program = 1; + has_st0018 = true; + } + } + } + + MMIO *map[2] = { 0, &st0018 }; + + foreach(node, root.element) { + if(node.name == "map" && map[program]) { + Mapping m(*map[program]); + foreach(attr, node.attribute) { + if(attr.name == "address") xml_parse_address(m, attr.content); + } + mapping.append(m); + } + } +} + +void Cartridge::xml_parse_msu1(xml_element &root) { + has_msu1 = true; + + foreach(node, root.element) { + if(node.name == "mmio") { + foreach(leaf, node.element) { + if(leaf.name == "map") { + Mapping m(msu1); + foreach(attr, leaf.attribute) { + if(attr.name == "address") xml_parse_address(m, attr.content); + } + mapping.append(m); + } + } + } + } +} + +void Cartridge::xml_parse_serial(xml_element &root) { + has_serial = true; +} + + +void Cartridge::xml_parse_dos(xml_element& root) { + has_dos = true; + + foreach(node, root.element) { + if (node.name == "map") { + Mapping m(dos); + foreach(attr, node.attribute) { + if (attr.name == "address") { + xml_parse_address(m, attr.content); + } + } + mapping.append(m); + } + } +} + + +void Cartridge::xml_parse_address(Mapping &m, const string &data) { + lstring part; + part.split(":", data); + if(part.size() != 2) return; + + lstring subpart; + subpart.split("-", part[0]); + if(subpart.size() == 1) { + m.banklo = hex(subpart[0]); + m.bankhi = m.banklo; + } else if(subpart.size() == 2) { + m.banklo = hex(subpart[0]); + m.bankhi = hex(subpart[1]); + } + + subpart.split("-", part[1]); + if(subpart.size() == 1) { + m.addrlo = hex(subpart[0]); + m.addrhi = m.addrlo; + } else if(subpart.size() == 2) { + m.addrlo = hex(subpart[0]); + m.addrhi = hex(subpart[1]); + } +} + +void Cartridge::xml_parse_mode(Mapping &m, const string& data) { + if(data == "direct") m.mode = Bus::MapMode::Direct; + else if(data == "linear") m.mode = Bus::MapMode::Linear; + else if(data == "shadow") m.mode = Bus::MapMode::Shadow; +} + +Cartridge::Mapping::Mapping() { + memory = 0; + mmio = 0; + mode = Bus::MapMode::Direct; + banklo = bankhi = addrlo = addrhi = offset = size = min_ram_size = 0; +} + +Cartridge::Mapping::Mapping(Memory &memory_) { + memory = &memory_; + mmio = 0; + mode = Bus::MapMode::Direct; + banklo = bankhi = addrlo = addrhi = offset = size = min_ram_size = 0; +} + +Cartridge::Mapping::Mapping(MMIO &mmio_) { + memory = 0; + mmio = &mmio_; + mode = Bus::MapMode::Direct; + banklo = bankhi = addrlo = addrhi = offset = size = min_ram_size = 0; +} + +#endif From 1151b50820170113960ab6e3bce2dbc427e2e68c Mon Sep 17 00:00:00 2001 From: MrL314 <11429905+MrL314@users.noreply.github.com> Date: Wed, 24 Nov 2021 16:46:41 -0500 Subject: [PATCH 3/5] dos auto-mapping + line-end fix --- common/nall/snes/cartridge.hpp | 1822 ++++++++++++++++---------------- 1 file changed, 911 insertions(+), 911 deletions(-) diff --git a/common/nall/snes/cartridge.hpp b/common/nall/snes/cartridge.hpp index 9678f5ac..f5df9fac 100644 --- a/common/nall/snes/cartridge.hpp +++ b/common/nall/snes/cartridge.hpp @@ -1,911 +1,911 @@ -#ifndef NALL_SNES_CARTRIDGE_HPP -#define NALL_SNES_CARTRIDGE_HPP - -namespace nall { - -class SNESCartridge { -public: - string xmlMemoryMap; - inline SNESCartridge(const uint8_t *data, unsigned size); - -//private: - inline void read_header(const uint8_t *data, unsigned size); - inline unsigned find_header(const uint8_t *data, unsigned size); - inline unsigned score_header(const uint8_t *data, unsigned size, unsigned addr); - inline unsigned gameboy_ram_size(const uint8_t *data, unsigned size); - inline bool gameboy_has_rtc(const uint8_t *data, unsigned size); - inline unsigned sufamiturbo_ram_size(const uint8_t *data, unsigned size); - - enum HeaderField { - CartName = 0x00, - Mapper = 0x15, - RomType = 0x16, - RomSize = 0x17, - RamSize = 0x18, - CartRegion = 0x19, - Company = 0x1a, - Version = 0x1b, - Complement = 0x1c, //inverse checksum - Checksum = 0x1e, - ResetVector = 0x3c, - }; - - enum Type { - TypeNormal, - TypeBsxSlotted, - TypeBsxBios, - TypeBsx, - TypeSufamiTurboBios, - TypeSufamiTurbo, - TypeSuperGameBoy1Bios, - TypeSuperGameBoy2Bios, - TypeGameBoy, - TypeUnknown, - }; - - enum Region { - NTSC, - PAL, - }; - - enum MemoryMapper { - SGBROM, - LoROM, - HiROM, - ExHiROM, - SuperFXROM, - SA1ROM, - SDD1ROM, - SPC7110ROM, - BSCLoROM, - BSCHiROM, - BSXROM, - STROM, - Cx4ROM, - }; - - enum DSP1MemoryMapper { - DSP1Unmapped, - DSP1LoROM1MB, - DSP1LoROM2MB, - DSP1HiROM, - }; - - enum BSXPackType { - FlashROM, - MaskROM, - }; - - unsigned rom_size; - unsigned ram_size; - - Type type; - Region region; - MemoryMapper mapper; - DSP1MemoryMapper dsp1_mapper; - BSXPackType bsxpack_type; - - bool has_bsx_slot; - bool has_spc7110rtc; - bool has_srtc; - bool has_dsp1; - bool has_dsp2; - bool has_dsp3; - bool has_dsp4; - bool has_obc1; - bool has_st010; - bool has_st011; - bool has_st018; -}; - -SNESCartridge::SNESCartridge(const uint8_t *data, unsigned size) { - read_header(data, size); - - string xml = "\n"; - - if(type == TypeBsx) { - xml << "\n"; - xmlMemoryMap = xml; - return; - } - - if(type == TypeSufamiTurbo) { - xml << ""; - if(sufamiturbo_ram_size(data, size) > 0) { - xml << " \n"; - } - xml << "\n"; - xmlMemoryMap = xml; - return; - } - - if(type == TypeGameBoy) { - xml << "\n"; - if(gameboy_ram_size(data, size) > 0) { - xml << " \n"; - } - xml << "\n"; - xmlMemoryMap = xml; - return; - } - - xml << "\n"; - - if(mapper == SGBROM) { - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - const unsigned revision = (type == TypeSuperGameBoy2Bios) ? 2 : 1; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - - } else if(mapper == LoROM) { - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - - if(ram_size > 0) { - xml << " \n"; - const unsigned range = (rom_size > 0x200000) || (ram_size > 32 * 1024) ? 0x7fff : 0xffff; - xml << " \n"; - xml << " \n"; - xml << " \n"; - } - - } else if(mapper == HiROM) { - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - - if(ram_size > 0) { - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - } - - } else if(mapper == ExHiROM) { - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - - if(ram_size > 0) { - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - } - - } else if(mapper == SuperFXROM) { - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - - if(ram_size > 0) { - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - } - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - - } else if(mapper == SA1ROM) { - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - - if(ram_size > 0) { - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - } - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - - if(has_bsx_slot) { - xml << " "; // Super MMC controls BS-X slot mapping - } - - } else if(mapper == SDD1ROM) { - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - - if(ram_size > 0) { - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - } - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - - } else if(mapper == SPC7110ROM) { - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - if (size >= 0x700000) { - // Tengai Makyou Zero english translation - xml << " \n"; - } - xml << " \n"; - - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - - if(ram_size > 0) { - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - } - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - - if(has_spc7110rtc) { - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - } - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - - } else if(mapper == BSCLoROM) { - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - - if(ram_size > 0) { - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - } - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - - } else if(mapper == BSCHiROM) { - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - - if(ram_size > 0) { - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - } - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - - } else if(mapper == BSXROM) { - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - - } else if(mapper == STROM) { - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - - } else if(mapper == Cx4ROM) { - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - } - - if(has_srtc) { - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - } - - if(has_dsp1) { - xml << " \n"; - if(dsp1_mapper == DSP1LoROM1MB) { - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - } else if(dsp1_mapper == DSP1LoROM2MB) { - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - } else if(dsp1_mapper == DSP1HiROM) { - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - } - xml << " \n"; - } - - if(has_dsp2) { - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - } - - if(has_dsp3) { - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - } - - if(has_dsp4) { - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - } - - if(has_obc1) { - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - } - - if(has_st010) { - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - } - - if(has_st011) { - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - } - - if(has_st018) { - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - } - - // always set dos default mapping if none applied - has_dos = true; - if (has_dos) { - xml << " \n"; - xml << " \n"; - xml << " \n"; - xml << " \n"; - } - - xml << "\n"; - xmlMemoryMap = xml; -} - -void SNESCartridge::read_header(const uint8_t *data, unsigned size) { - type = TypeUnknown; - mapper = LoROM; - dsp1_mapper = DSP1Unmapped; - bsxpack_type = FlashROM; - region = NTSC; - rom_size = size; - ram_size = 0; - - has_bsx_slot = false; - has_spc7110rtc = false; - has_srtc = false; - has_dsp1 = false; - has_dsp2 = false; - has_dsp3 = false; - has_dsp4 = false; - has_obc1 = false; - has_st010 = false; - has_st011 = false; - has_st018 = false; - - //===================== - //detect Game Boy carts - //===================== - - if(size >= 0x0140) { - if(data[0x0104] == 0xce && data[0x0105] == 0xed && data[0x0106] == 0x66 && data[0x0107] == 0x66 - && data[0x0108] == 0xcc && data[0x0109] == 0x0d && data[0x010a] == 0x00 && data[0x010b] == 0x0b) { - type = TypeGameBoy; - return; - } - } - - if(size < 32768) { - type = TypeUnknown; - return; - } - - const unsigned index = find_header(data, size); - const uint8_t mapperid = data[index + Mapper]; - const uint8_t rom_type = data[index + RomType]; - const uint8_t rom_size = data[index + RomSize]; - const uint8_t company = data[index + Company]; - const uint8_t regionid = data[index + CartRegion] & 0x7f; - - ram_size = 1024 << (data[index + RamSize] & 7); - if(ram_size == 1024) ram_size = 0; //no RAM present - - //0, 1, 11, 13, 15, 16 = NTSC; others = PAL - switch (regionid) { - case 0: case 1: case 11: case 13: case 15: case 16: - region = NTSC; - break; - default: - region = PAL; - break; - } - - //======================= - //detect BS-X flash carts - //======================= - - if(data[index + 0x13] == 0x00 || data[index + 0x13] == 0xff) { - if(data[index + 0x14] == 0x00) { - const uint8_t n15 = data[index + 0x15]; - if(n15 == 0x00 || n15 == 0x80 || n15 == 0x84 || n15 == 0x9c || n15 == 0xbc || n15 == 0xfc) { - if(data[index + 0x1a] == 0x33 || data[index + 0x1a] == 0xff) { - type = TypeBsx; - //Check if FlashROM or MaskROM - uint8_t i = 0; - for (i = 0; i < 20; i++) - { - uint8_t checkbyte; - switch(i) - { - case 0x00: checkbyte = 0x4D; break; - case 0x02: checkbyte = 0x50; break; - case 0x06: checkbyte = 0x70; break; - default: checkbyte = 0x00; - } - - if (i != 0x06) - { - if (data[index - 0xC0 + i] != checkbyte) - { - break; - } - } - else - { - //Only check 0xF0 for i = 6, only Memory Pack type matters - if ((data[index - 0xC0 + i] & 0xF0) != checkbyte) - { - break; - } - } - } - - if (i == 20) - { - //if i reaches 20, that means all the checks are successful - bsxpack_type = MaskROM; - } - else - { - bsxpack_type = FlashROM; - } - region = NTSC; //BS-X only released in Japan - return; - } - } - } - } - - //========================= - //detect Sufami Turbo carts - //========================= - - if(!memcmp(data, "BANDAI SFC-ADX", 14)) { - if(!memcmp(data + 16, "SFC-ADX BACKUP", 14)) { - type = TypeSufamiTurboBios; - } else { - type = TypeSufamiTurbo; - } - mapper = STROM; - region = NTSC; //Sufami Turbo only released in Japan - return; //RAM size handled outside this routine - } - - //========================== - //detect Super Game Boy BIOS - //========================== - - if(!memcmp(data + index, "Super GAMEBOY2", 14)) { - type = TypeSuperGameBoy2Bios; - mapper = SGBROM; - return; - } - - if(!memcmp(data + index, "Super GAMEBOY", 13)) { - type = TypeSuperGameBoy1Bios; - mapper = SGBROM; - return; - } - - //===================== - //detect standard carts - //===================== - - //detect presence of BS-X flash cartridge connector (reads extended header information) - if(data[index - 14] == 'Z') { - if(data[index - 11] == 'J') { - uint8_t n13 = data[index - 13]; - if((n13 >= 'A' && n13 <= 'Z') || (n13 >= '0' && n13 <= '9')) { - if(company == 0x33 || (data[index - 10] == 0x00 && data[index - 4] == 0x00)) { - has_bsx_slot = true; - } - } - } - } - - if(has_bsx_slot) { - if(!memcmp(data + index, "Satellaview BS-X ", 21)) { - //BS-X base cart - type = TypeBsxBios; - mapper = BSXROM; - region = NTSC; //BS-X only released in Japan - return; //RAM size handled internally by load_cart_bsx() -> BSXCart class - } else { - type = TypeBsxSlotted; - mapper = (index == 0x7fc0 ? BSCLoROM : BSCHiROM); - region = NTSC; //BS-X slotted cartridges only released in Japan - } - } else { - //standard cart - type = TypeNormal; - - if(index == 0x7fc0) { - mapper = LoROM; - } else if(index == 0xffc0) { - mapper = HiROM; - } else { //index == 0x40ffc0 - mapper = ExHiROM; - } - } - - if(mapperid == 0x20 && (rom_type == 0x13 || rom_type == 0x14 || rom_type == 0x15 || rom_type == 0x1a)) { - mapper = SuperFXROM; - ram_size = 1024 << (data[index - 3] & 7); - if(ram_size == 1024) ram_size = 0; - } - - if(mapperid == 0x23 && (rom_type == 0x32 || rom_type == 0x34 || rom_type == 0x35 || rom_type == 0x36)) { - mapper = SA1ROM; - } - - if(mapperid == 0x32 && (rom_type == 0x43 || rom_type == 0x45)) { - mapper = SDD1ROM; - } - - if(mapperid == 0x3a && (rom_type == 0xf5 || rom_type == 0xf9)) { - mapper = SPC7110ROM; - has_spc7110rtc = (rom_type == 0xf9); - } - - if(mapperid == 0x35 && rom_type == 0x55) { - has_srtc = true; - } - - if(mapperid == 0x20 && rom_type == 0xf3) { - mapper = Cx4ROM; - } - - if((mapperid == 0x20 || mapperid == 0x21) && rom_type == 0x03) { - has_dsp1 = true; - } - - if(mapperid == 0x30 && rom_type == 0x05 && company != 0xb2) { - has_dsp1 = true; - } - - if(mapperid == 0x31 && (rom_type == 0x03 || rom_type == 0x05)) { - has_dsp1 = true; - } - - if(has_dsp1) { - if((mapperid & 0x2f) == 0x20 && size <= 0x100000) { - dsp1_mapper = DSP1LoROM1MB; - } else if((mapperid & 0x2f) == 0x20) { - dsp1_mapper = DSP1LoROM2MB; - } else if((mapperid & 0x2f) == 0x21) { - dsp1_mapper = DSP1HiROM; - } - } - - if(mapperid == 0x20 && rom_type == 0x05) { - has_dsp2 = true; - } - - if(mapperid == 0x30 && rom_type == 0x05 && company == 0xb2) { - has_dsp3 = true; - } - - if(mapperid == 0x30 && rom_type == 0x03) { - has_dsp4 = true; - } - - if(mapperid == 0x30 && rom_type == 0x25) { - has_obc1 = true; - } - - if(mapperid == 0x30 && rom_type == 0xf6 && rom_size >= 10) { - has_st010 = true; - } - - if(mapperid == 0x30 && rom_type == 0xf6 && rom_size < 10) { - has_st011 = true; - } - - if(mapperid == 0x30 && rom_type == 0xf5) { - has_st018 = true; - } -} - -unsigned SNESCartridge::find_header(const uint8_t *data, unsigned size) { - unsigned score_lo = score_header(data, size, 0x007fc0); - unsigned score_hi = score_header(data, size, 0x00ffc0); - unsigned score_ex = score_header(data, size, 0x40ffc0); - if(score_ex) score_ex += 4; //favor ExHiROM on images > 32mbits - - if(score_lo >= score_hi && score_lo >= score_ex) { - return 0x007fc0; - } else if(score_hi >= score_ex) { - return 0x00ffc0; - } else { - return 0x40ffc0; - } -} - -unsigned SNESCartridge::score_header(const uint8_t *data, unsigned size, unsigned addr) { - if(size < addr + 64) return 0; //image too small to contain header at this location? - int score = 0; - - uint16_t resetvector = data[addr + ResetVector] | (data[addr + ResetVector + 1] << 8); - uint16_t checksum = data[addr + Checksum ] | (data[addr + Checksum + 1] << 8); - uint16_t complement = data[addr + Complement ] | (data[addr + Complement + 1] << 8); - - uint8_t resetop = data[(addr & ~0x7fff) | (resetvector & 0x7fff)]; //first opcode executed upon reset - uint8_t mapper = data[addr + Mapper] & ~0x10; //mask off irrelevent FastROM-capable bit - - //$00:[0000-7fff] contains uninitialized RAM and MMIO. - //reset vector must point to ROM at $00:[8000-ffff] to be considered valid. - if(resetvector < 0x8000) return 0; - - //some images duplicate the header in multiple locations, and others have completely - //invalid header information that cannot be relied upon. - //below code will analyze the first opcode executed at the specified reset vector to - //determine the probability that this is the correct header. - - //most likely opcodes - if(resetop == 0x78 //sei - || resetop == 0x18 //clc (clc; xce) - || resetop == 0x38 //sec (sec; xce) - || resetop == 0x9c //stz $nnnn (stz $4200) - || resetop == 0x4c //jmp $nnnn - || resetop == 0x5c //jml $nnnnnn - ) score += 8; - - //plausible opcodes - if(resetop == 0xc2 //rep #$nn - || resetop == 0xe2 //sep #$nn - || resetop == 0xad //lda $nnnn - || resetop == 0xae //ldx $nnnn - || resetop == 0xac //ldy $nnnn - || resetop == 0xaf //lda $nnnnnn - || resetop == 0xa9 //lda #$nn - || resetop == 0xa2 //ldx #$nn - || resetop == 0xa0 //ldy #$nn - || resetop == 0x20 //jsr $nnnn - || resetop == 0x22 //jsl $nnnnnn - ) score += 4; - - //implausible opcodes - if(resetop == 0x40 //rti - || resetop == 0x60 //rts - || resetop == 0x6b //rtl - || resetop == 0xcd //cmp $nnnn - || resetop == 0xec //cpx $nnnn - || resetop == 0xcc //cpy $nnnn - ) score -= 4; - - //least likely opcodes - if(resetop == 0x00 //brk #$nn - || resetop == 0x02 //cop #$nn - || resetop == 0xdb //stp - || resetop == 0x42 //wdm - || resetop == 0xff //sbc $nnnnnn,x - ) score -= 8; - - //at times, both the header and reset vector's first opcode will match ... - //fallback and rely on info validity in these cases to determine more likely header. - - //a valid checksum is the biggest indicator of a valid header. - if((checksum + complement) == 0xffff && (checksum != 0) && (complement != 0)) score += 4; - - if(addr == 0x007fc0 && mapper == 0x20) score += 2; //0x20 is usually LoROM - if(addr == 0x00ffc0 && mapper == 0x21) score += 2; //0x21 is usually HiROM - if(addr == 0x007fc0 && mapper == 0x22) score += 2; //0x22 is usually SDD1 - if(addr == 0x40ffc0 && mapper == 0x25) score += 2; //0x25 is usually ExHiROM - - if(data[addr + Company] == 0x33) score += 2; //0x33 indicates extended header - if(data[addr + RomType] < 0x08) score++; - if(data[addr + RomSize] < 0x10) score++; - if(data[addr + RamSize] < 0x08) score++; - if(data[addr + CartRegion] < 14) score++; - - if(score < 0) score = 0; - return score; -} - -unsigned SNESCartridge::gameboy_ram_size(const uint8_t *data, unsigned size) { - if(size < 512) return 0; - if(data[0x0147] == 0x06) return 512; //MBC2 has 512 nibbles of internal RAM - switch(data[0x0149]) { - case 0x00: return 0 * 1024; - case 0x01: return 2 * 1024; - case 0x02: return 8 * 1024; - case 0x03: return 32 * 1024; - case 0x04: return 128 * 1024; - case 0x05: return 128 * 1024; - default: return 128 * 1024; - } -} - -bool SNESCartridge::gameboy_has_rtc(const uint8_t *data, unsigned size) { - if(size < 512) return false; - if(data[0x0147] == 0x0f || data[0x0147] == 0x10) return true; - return false; -} - -unsigned SNESCartridge::sufamiturbo_ram_size(const uint8_t *data, unsigned size) { - if(size < 0x38) return 0; - return data[0x37] * 2048; -} - -} - -#endif +#ifndef NALL_SNES_CARTRIDGE_HPP +#define NALL_SNES_CARTRIDGE_HPP + +namespace nall { + +class SNESCartridge { +public: + string xmlMemoryMap; + inline SNESCartridge(const uint8_t *data, unsigned size); + +//private: + inline void read_header(const uint8_t *data, unsigned size); + inline unsigned find_header(const uint8_t *data, unsigned size); + inline unsigned score_header(const uint8_t *data, unsigned size, unsigned addr); + inline unsigned gameboy_ram_size(const uint8_t *data, unsigned size); + inline bool gameboy_has_rtc(const uint8_t *data, unsigned size); + inline unsigned sufamiturbo_ram_size(const uint8_t *data, unsigned size); + + enum HeaderField { + CartName = 0x00, + Mapper = 0x15, + RomType = 0x16, + RomSize = 0x17, + RamSize = 0x18, + CartRegion = 0x19, + Company = 0x1a, + Version = 0x1b, + Complement = 0x1c, //inverse checksum + Checksum = 0x1e, + ResetVector = 0x3c, + }; + + enum Type { + TypeNormal, + TypeBsxSlotted, + TypeBsxBios, + TypeBsx, + TypeSufamiTurboBios, + TypeSufamiTurbo, + TypeSuperGameBoy1Bios, + TypeSuperGameBoy2Bios, + TypeGameBoy, + TypeUnknown, + }; + + enum Region { + NTSC, + PAL, + }; + + enum MemoryMapper { + SGBROM, + LoROM, + HiROM, + ExHiROM, + SuperFXROM, + SA1ROM, + SDD1ROM, + SPC7110ROM, + BSCLoROM, + BSCHiROM, + BSXROM, + STROM, + Cx4ROM, + }; + + enum DSP1MemoryMapper { + DSP1Unmapped, + DSP1LoROM1MB, + DSP1LoROM2MB, + DSP1HiROM, + }; + + enum BSXPackType { + FlashROM, + MaskROM, + }; + + unsigned rom_size; + unsigned ram_size; + + Type type; + Region region; + MemoryMapper mapper; + DSP1MemoryMapper dsp1_mapper; + BSXPackType bsxpack_type; + + bool has_bsx_slot; + bool has_spc7110rtc; + bool has_srtc; + bool has_dsp1; + bool has_dsp2; + bool has_dsp3; + bool has_dsp4; + bool has_obc1; + bool has_st010; + bool has_st011; + bool has_st018; +}; + +SNESCartridge::SNESCartridge(const uint8_t *data, unsigned size) { + read_header(data, size); + + string xml = "\n"; + + if(type == TypeBsx) { + xml << "\n"; + xmlMemoryMap = xml; + return; + } + + if(type == TypeSufamiTurbo) { + xml << ""; + if(sufamiturbo_ram_size(data, size) > 0) { + xml << " \n"; + } + xml << "\n"; + xmlMemoryMap = xml; + return; + } + + if(type == TypeGameBoy) { + xml << "\n"; + if(gameboy_ram_size(data, size) > 0) { + xml << " \n"; + } + xml << "\n"; + xmlMemoryMap = xml; + return; + } + + xml << "\n"; + + if(mapper == SGBROM) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + const unsigned revision = (type == TypeSuperGameBoy2Bios) ? 2 : 1; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + + } else if(mapper == LoROM) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + + if(ram_size > 0) { + xml << " \n"; + const unsigned range = (rom_size > 0x200000) || (ram_size > 32 * 1024) ? 0x7fff : 0xffff; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } + + } else if(mapper == HiROM) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + + if(ram_size > 0) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } + + } else if(mapper == ExHiROM) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + + if(ram_size > 0) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } + + } else if(mapper == SuperFXROM) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + + if(ram_size > 0) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + + } else if(mapper == SA1ROM) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + + if(ram_size > 0) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + + if(has_bsx_slot) { + xml << " "; // Super MMC controls BS-X slot mapping + } + + } else if(mapper == SDD1ROM) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + + if(ram_size > 0) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + + } else if(mapper == SPC7110ROM) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + if (size >= 0x700000) { + // Tengai Makyou Zero english translation + xml << " \n"; + } + xml << " \n"; + + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + + if(ram_size > 0) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + + if(has_spc7110rtc) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + + } else if(mapper == BSCLoROM) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + + if(ram_size > 0) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + + } else if(mapper == BSCHiROM) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + + if(ram_size > 0) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + + } else if(mapper == BSXROM) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + + } else if(mapper == STROM) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + + } else if(mapper == Cx4ROM) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } + + if(has_srtc) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } + + if(has_dsp1) { + xml << " \n"; + if(dsp1_mapper == DSP1LoROM1MB) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } else if(dsp1_mapper == DSP1LoROM2MB) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } else if(dsp1_mapper == DSP1HiROM) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } + xml << " \n"; + } + + if(has_dsp2) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } + + if(has_dsp3) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } + + if(has_dsp4) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } + + if(has_obc1) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } + + if(has_st010) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } + + if(has_st011) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } + + if(has_st018) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } + + // always set dos default mapping if none applied + has_dos = true; + if (has_dos) { + xml << " \n"; + xml << " \n"; + xml << " \n"; + xml << " \n"; + } + + xml << "\n"; + xmlMemoryMap = xml; +} + +void SNESCartridge::read_header(const uint8_t *data, unsigned size) { + type = TypeUnknown; + mapper = LoROM; + dsp1_mapper = DSP1Unmapped; + bsxpack_type = FlashROM; + region = NTSC; + rom_size = size; + ram_size = 0; + + has_bsx_slot = false; + has_spc7110rtc = false; + has_srtc = false; + has_dsp1 = false; + has_dsp2 = false; + has_dsp3 = false; + has_dsp4 = false; + has_obc1 = false; + has_st010 = false; + has_st011 = false; + has_st018 = false; + + //===================== + //detect Game Boy carts + //===================== + + if(size >= 0x0140) { + if(data[0x0104] == 0xce && data[0x0105] == 0xed && data[0x0106] == 0x66 && data[0x0107] == 0x66 + && data[0x0108] == 0xcc && data[0x0109] == 0x0d && data[0x010a] == 0x00 && data[0x010b] == 0x0b) { + type = TypeGameBoy; + return; + } + } + + if(size < 32768) { + type = TypeUnknown; + return; + } + + const unsigned index = find_header(data, size); + const uint8_t mapperid = data[index + Mapper]; + const uint8_t rom_type = data[index + RomType]; + const uint8_t rom_size = data[index + RomSize]; + const uint8_t company = data[index + Company]; + const uint8_t regionid = data[index + CartRegion] & 0x7f; + + ram_size = 1024 << (data[index + RamSize] & 7); + if(ram_size == 1024) ram_size = 0; //no RAM present + + //0, 1, 11, 13, 15, 16 = NTSC; others = PAL + switch (regionid) { + case 0: case 1: case 11: case 13: case 15: case 16: + region = NTSC; + break; + default: + region = PAL; + break; + } + + //======================= + //detect BS-X flash carts + //======================= + + if(data[index + 0x13] == 0x00 || data[index + 0x13] == 0xff) { + if(data[index + 0x14] == 0x00) { + const uint8_t n15 = data[index + 0x15]; + if(n15 == 0x00 || n15 == 0x80 || n15 == 0x84 || n15 == 0x9c || n15 == 0xbc || n15 == 0xfc) { + if(data[index + 0x1a] == 0x33 || data[index + 0x1a] == 0xff) { + type = TypeBsx; + //Check if FlashROM or MaskROM + uint8_t i = 0; + for (i = 0; i < 20; i++) + { + uint8_t checkbyte; + switch(i) + { + case 0x00: checkbyte = 0x4D; break; + case 0x02: checkbyte = 0x50; break; + case 0x06: checkbyte = 0x70; break; + default: checkbyte = 0x00; + } + + if (i != 0x06) + { + if (data[index - 0xC0 + i] != checkbyte) + { + break; + } + } + else + { + //Only check 0xF0 for i = 6, only Memory Pack type matters + if ((data[index - 0xC0 + i] & 0xF0) != checkbyte) + { + break; + } + } + } + + if (i == 20) + { + //if i reaches 20, that means all the checks are successful + bsxpack_type = MaskROM; + } + else + { + bsxpack_type = FlashROM; + } + region = NTSC; //BS-X only released in Japan + return; + } + } + } + } + + //========================= + //detect Sufami Turbo carts + //========================= + + if(!memcmp(data, "BANDAI SFC-ADX", 14)) { + if(!memcmp(data + 16, "SFC-ADX BACKUP", 14)) { + type = TypeSufamiTurboBios; + } else { + type = TypeSufamiTurbo; + } + mapper = STROM; + region = NTSC; //Sufami Turbo only released in Japan + return; //RAM size handled outside this routine + } + + //========================== + //detect Super Game Boy BIOS + //========================== + + if(!memcmp(data + index, "Super GAMEBOY2", 14)) { + type = TypeSuperGameBoy2Bios; + mapper = SGBROM; + return; + } + + if(!memcmp(data + index, "Super GAMEBOY", 13)) { + type = TypeSuperGameBoy1Bios; + mapper = SGBROM; + return; + } + + //===================== + //detect standard carts + //===================== + + //detect presence of BS-X flash cartridge connector (reads extended header information) + if(data[index - 14] == 'Z') { + if(data[index - 11] == 'J') { + uint8_t n13 = data[index - 13]; + if((n13 >= 'A' && n13 <= 'Z') || (n13 >= '0' && n13 <= '9')) { + if(company == 0x33 || (data[index - 10] == 0x00 && data[index - 4] == 0x00)) { + has_bsx_slot = true; + } + } + } + } + + if(has_bsx_slot) { + if(!memcmp(data + index, "Satellaview BS-X ", 21)) { + //BS-X base cart + type = TypeBsxBios; + mapper = BSXROM; + region = NTSC; //BS-X only released in Japan + return; //RAM size handled internally by load_cart_bsx() -> BSXCart class + } else { + type = TypeBsxSlotted; + mapper = (index == 0x7fc0 ? BSCLoROM : BSCHiROM); + region = NTSC; //BS-X slotted cartridges only released in Japan + } + } else { + //standard cart + type = TypeNormal; + + if(index == 0x7fc0) { + mapper = LoROM; + } else if(index == 0xffc0) { + mapper = HiROM; + } else { //index == 0x40ffc0 + mapper = ExHiROM; + } + } + + if(mapperid == 0x20 && (rom_type == 0x13 || rom_type == 0x14 || rom_type == 0x15 || rom_type == 0x1a)) { + mapper = SuperFXROM; + ram_size = 1024 << (data[index - 3] & 7); + if(ram_size == 1024) ram_size = 0; + } + + if(mapperid == 0x23 && (rom_type == 0x32 || rom_type == 0x34 || rom_type == 0x35 || rom_type == 0x36)) { + mapper = SA1ROM; + } + + if(mapperid == 0x32 && (rom_type == 0x43 || rom_type == 0x45)) { + mapper = SDD1ROM; + } + + if(mapperid == 0x3a && (rom_type == 0xf5 || rom_type == 0xf9)) { + mapper = SPC7110ROM; + has_spc7110rtc = (rom_type == 0xf9); + } + + if(mapperid == 0x35 && rom_type == 0x55) { + has_srtc = true; + } + + if(mapperid == 0x20 && rom_type == 0xf3) { + mapper = Cx4ROM; + } + + if((mapperid == 0x20 || mapperid == 0x21) && rom_type == 0x03) { + has_dsp1 = true; + } + + if(mapperid == 0x30 && rom_type == 0x05 && company != 0xb2) { + has_dsp1 = true; + } + + if(mapperid == 0x31 && (rom_type == 0x03 || rom_type == 0x05)) { + has_dsp1 = true; + } + + if(has_dsp1) { + if((mapperid & 0x2f) == 0x20 && size <= 0x100000) { + dsp1_mapper = DSP1LoROM1MB; + } else if((mapperid & 0x2f) == 0x20) { + dsp1_mapper = DSP1LoROM2MB; + } else if((mapperid & 0x2f) == 0x21) { + dsp1_mapper = DSP1HiROM; + } + } + + if(mapperid == 0x20 && rom_type == 0x05) { + has_dsp2 = true; + } + + if(mapperid == 0x30 && rom_type == 0x05 && company == 0xb2) { + has_dsp3 = true; + } + + if(mapperid == 0x30 && rom_type == 0x03) { + has_dsp4 = true; + } + + if(mapperid == 0x30 && rom_type == 0x25) { + has_obc1 = true; + } + + if(mapperid == 0x30 && rom_type == 0xf6 && rom_size >= 10) { + has_st010 = true; + } + + if(mapperid == 0x30 && rom_type == 0xf6 && rom_size < 10) { + has_st011 = true; + } + + if(mapperid == 0x30 && rom_type == 0xf5) { + has_st018 = true; + } +} + +unsigned SNESCartridge::find_header(const uint8_t *data, unsigned size) { + unsigned score_lo = score_header(data, size, 0x007fc0); + unsigned score_hi = score_header(data, size, 0x00ffc0); + unsigned score_ex = score_header(data, size, 0x40ffc0); + if(score_ex) score_ex += 4; //favor ExHiROM on images > 32mbits + + if(score_lo >= score_hi && score_lo >= score_ex) { + return 0x007fc0; + } else if(score_hi >= score_ex) { + return 0x00ffc0; + } else { + return 0x40ffc0; + } +} + +unsigned SNESCartridge::score_header(const uint8_t *data, unsigned size, unsigned addr) { + if(size < addr + 64) return 0; //image too small to contain header at this location? + int score = 0; + + uint16_t resetvector = data[addr + ResetVector] | (data[addr + ResetVector + 1] << 8); + uint16_t checksum = data[addr + Checksum ] | (data[addr + Checksum + 1] << 8); + uint16_t complement = data[addr + Complement ] | (data[addr + Complement + 1] << 8); + + uint8_t resetop = data[(addr & ~0x7fff) | (resetvector & 0x7fff)]; //first opcode executed upon reset + uint8_t mapper = data[addr + Mapper] & ~0x10; //mask off irrelevent FastROM-capable bit + + //$00:[0000-7fff] contains uninitialized RAM and MMIO. + //reset vector must point to ROM at $00:[8000-ffff] to be considered valid. + if(resetvector < 0x8000) return 0; + + //some images duplicate the header in multiple locations, and others have completely + //invalid header information that cannot be relied upon. + //below code will analyze the first opcode executed at the specified reset vector to + //determine the probability that this is the correct header. + + //most likely opcodes + if(resetop == 0x78 //sei + || resetop == 0x18 //clc (clc; xce) + || resetop == 0x38 //sec (sec; xce) + || resetop == 0x9c //stz $nnnn (stz $4200) + || resetop == 0x4c //jmp $nnnn + || resetop == 0x5c //jml $nnnnnn + ) score += 8; + + //plausible opcodes + if(resetop == 0xc2 //rep #$nn + || resetop == 0xe2 //sep #$nn + || resetop == 0xad //lda $nnnn + || resetop == 0xae //ldx $nnnn + || resetop == 0xac //ldy $nnnn + || resetop == 0xaf //lda $nnnnnn + || resetop == 0xa9 //lda #$nn + || resetop == 0xa2 //ldx #$nn + || resetop == 0xa0 //ldy #$nn + || resetop == 0x20 //jsr $nnnn + || resetop == 0x22 //jsl $nnnnnn + ) score += 4; + + //implausible opcodes + if(resetop == 0x40 //rti + || resetop == 0x60 //rts + || resetop == 0x6b //rtl + || resetop == 0xcd //cmp $nnnn + || resetop == 0xec //cpx $nnnn + || resetop == 0xcc //cpy $nnnn + ) score -= 4; + + //least likely opcodes + if(resetop == 0x00 //brk #$nn + || resetop == 0x02 //cop #$nn + || resetop == 0xdb //stp + || resetop == 0x42 //wdm + || resetop == 0xff //sbc $nnnnnn,x + ) score -= 8; + + //at times, both the header and reset vector's first opcode will match ... + //fallback and rely on info validity in these cases to determine more likely header. + + //a valid checksum is the biggest indicator of a valid header. + if((checksum + complement) == 0xffff && (checksum != 0) && (complement != 0)) score += 4; + + if(addr == 0x007fc0 && mapper == 0x20) score += 2; //0x20 is usually LoROM + if(addr == 0x00ffc0 && mapper == 0x21) score += 2; //0x21 is usually HiROM + if(addr == 0x007fc0 && mapper == 0x22) score += 2; //0x22 is usually SDD1 + if(addr == 0x40ffc0 && mapper == 0x25) score += 2; //0x25 is usually ExHiROM + + if(data[addr + Company] == 0x33) score += 2; //0x33 indicates extended header + if(data[addr + RomType] < 0x08) score++; + if(data[addr + RomSize] < 0x10) score++; + if(data[addr + RamSize] < 0x08) score++; + if(data[addr + CartRegion] < 14) score++; + + if(score < 0) score = 0; + return score; +} + +unsigned SNESCartridge::gameboy_ram_size(const uint8_t *data, unsigned size) { + if(size < 512) return 0; + if(data[0x0147] == 0x06) return 512; //MBC2 has 512 nibbles of internal RAM + switch(data[0x0149]) { + case 0x00: return 0 * 1024; + case 0x01: return 2 * 1024; + case 0x02: return 8 * 1024; + case 0x03: return 32 * 1024; + case 0x04: return 128 * 1024; + case 0x05: return 128 * 1024; + default: return 128 * 1024; + } +} + +bool SNESCartridge::gameboy_has_rtc(const uint8_t *data, unsigned size) { + if(size < 512) return false; + if(data[0x0147] == 0x0f || data[0x0147] == 0x10) return true; + return false; +} + +unsigned SNESCartridge::sufamiturbo_ram_size(const uint8_t *data, unsigned size) { + if(size < 0x38) return 0; + return data[0x37] * 2048; +} + +} + +#endif From ea9b0eab2f1acb80a4bcbbee7551cfc6ef360d27 Mon Sep 17 00:00:00 2001 From: MrL314 <11429905+MrL314@users.noreply.github.com> Date: Wed, 24 Nov 2021 16:48:25 -0500 Subject: [PATCH 4/5] dos auto-map + line-end fix --- bsnes/snes/cartridge/cartridge.cpp | 336 ++++---- bsnes/snes/cartridge/xml.cpp | 1252 ++++++++++++++-------------- 2 files changed, 794 insertions(+), 794 deletions(-) diff --git a/bsnes/snes/cartridge/cartridge.cpp b/bsnes/snes/cartridge/cartridge.cpp index 9c4c6ce1..c207e1df 100644 --- a/bsnes/snes/cartridge/cartridge.cpp +++ b/bsnes/snes/cartridge/cartridge.cpp @@ -1,168 +1,168 @@ -#include - -#include -#include - -#define CARTRIDGE_CPP -namespace SNES { - -#include "xml.cpp" -#include "serialization.cpp" - -namespace memory { - MappedRAM cartrom, cartram, cartrtc; - MappedRAM bsxpack, bsxpram; - MappedRAM stArom, stAram; - MappedRAM stBrom, stBram; - MappedRAM gbrom, gbram, gbrtc; -}; - -Cartridge cartridge; - -int Cartridge::rom_offset(unsigned addr) const { - Bus::Page &page = bus.page[addr >> 8]; - if (page.access == &memory::cartrom || - page.access == &memory::cx4rom || - page.access == &memory::gsurom || - page.access == &memory::fxrom || - page.access == &memory::vsprom) { - return page.offset + addr; - } - - return -1; -} - -void Cartridge::load(Mode cartridge_mode, const lstring &xml_list) { - mode = cartridge_mode; - region = Region::NTSC; - ram_size = 0; - spc7110_data_rom_offset = 0x100000; - st_A_ram_size = 0; - st_B_ram_size = 0; - bsxpack_type = BSXPackType::Unknown; - supergameboy_version = SuperGameBoyVersion::Version1; - supergameboy_ram_size = 0; - supergameboy_rtc_size = 0; - - has_bsx_slot = false; - has_superfx = false; - has_sa1 = false; - has_necdsp = false; - has_srtc = false; - has_sdd1 = false; - has_spc7110 = false; - has_spc7110rtc = false; - has_cx4 = false; - has_obc1 = false; - has_st0018 = false; - has_msu1 = false; - has_serial = false; - has_dos = false; - - parse_xml(xml_list); -//print(xml_list[0], "\n\n"); - - // autodetect MSU1 if it wasn't specified in a manifest - if(!has_msu1 && file::exists(string(basename(), ".msu"))) { - has_msu1 = true; - - Mapping m(msu1); - m.addrlo = 0x2000; - m.addrhi = 0x2007; - mapping.append(m); - } - - if(ram_size > 0) { - memory::cartram.map(allocate(ram_size, 0xff), ram_size); - } - - if(has_srtc || has_spc7110rtc) { - memory::cartrtc.map(allocate(20, 0xff), 20); - } - - if(mode == Mode::Bsx) { - memory::bsxpram.map(allocate(512 * 1024, 0xff), 512 * 1024); - } - - if(mode == Mode::SufamiTurbo) { - if(st_A_ram_size) memory::stAram.map(allocate(st_A_ram_size, 0xff), st_A_ram_size); - if(st_B_ram_size) memory::stBram.map(allocate(st_B_ram_size, 0xff), st_B_ram_size); - } - - if(mode == Mode::SuperGameBoy) { - if(memory::gbrom.data()) { - if(supergameboy_ram_size) memory::gbram.map(allocate(supergameboy_ram_size, 0xff), supergameboy_ram_size); - if(supergameboy_rtc_size) memory::gbrtc.map(allocate(supergameboy_rtc_size, 0x00), supergameboy_rtc_size); - } - } - - memory::cartrom.write_protect(true); - memory::cartram.write_protect(false); - memory::cartrtc.write_protect(false); - memory::bsxpack.write_protect(true); - memory::bsxpram.write_protect(false); - memory::stArom.write_protect(true); - memory::stAram.write_protect(false); - memory::stBrom.write_protect(true); - memory::stBram.write_protect(false); - memory::gbrom.write_protect(true); - memory::gbram.write_protect(false); - memory::gbrtc.write_protect(false); - - unsigned checksum = ~0; foreach(n, memory::cartrom) checksum = crc32_adjust(checksum, n); - if(memory::bsxpack.size() != 0) foreach(n, memory::bsxpack) checksum = crc32_adjust(checksum, n); - if(memory::stArom.size() != 0) foreach(n, memory::stArom ) checksum = crc32_adjust(checksum, n); - if(memory::stBrom.size() != 0) foreach(n, memory::stBrom ) checksum = crc32_adjust(checksum, n); - if(memory::gbrom.size() != 0) foreach(n, memory::gbrom ) checksum = crc32_adjust(checksum, n); - crc32 = ~checksum; - - sha256_ctx sha; - uint8_t shahash[32]; - sha256_init(&sha); - sha256_chunk(&sha, memory::cartrom.data(), memory::cartrom.size()); - sha256_final(&sha); - sha256_hash(&sha, shahash); - - string hash; - foreach(n, shahash) hash << hex<2>(n); - sha256 = hash; - - bus.load_cart(); - system.serialize_init(); - loaded = true; -} - -void Cartridge::unload() { - memory::cartrom.reset(); - memory::cartram.reset(); - memory::cartrtc.reset(); - memory::bsxpack.reset(); - memory::bsxpram.reset(); - memory::stArom.reset(); - memory::stAram.reset(); - memory::stBrom.reset(); - memory::stBram.reset(); - memory::gbrom.reset(); - memory::gbram.reset(); - memory::gbrtc.reset(); - - if(loaded == false) return; - bus.unload_cart(); - loaded = false; -} - -Memory& Cartridge::bsxpack_access() { - if(memory::bsxpack.size() == 0) return memory::memory_unmapped; - return (bsxpack_type == BSXPackType::FlashROM) ? (Memory&)bsxflash : (Memory&)memory::bsxpack; -} - -Cartridge::Cartridge() { - loaded = false; - unload(); -} - -Cartridge::~Cartridge() { - unload(); -} - -} +#include + +#include +#include + +#define CARTRIDGE_CPP +namespace SNES { + +#include "xml.cpp" +#include "serialization.cpp" + +namespace memory { + MappedRAM cartrom, cartram, cartrtc; + MappedRAM bsxpack, bsxpram; + MappedRAM stArom, stAram; + MappedRAM stBrom, stBram; + MappedRAM gbrom, gbram, gbrtc; +}; + +Cartridge cartridge; + +int Cartridge::rom_offset(unsigned addr) const { + Bus::Page &page = bus.page[addr >> 8]; + if (page.access == &memory::cartrom || + page.access == &memory::cx4rom || + page.access == &memory::gsurom || + page.access == &memory::fxrom || + page.access == &memory::vsprom) { + return page.offset + addr; + } + + return -1; +} + +void Cartridge::load(Mode cartridge_mode, const lstring &xml_list) { + mode = cartridge_mode; + region = Region::NTSC; + ram_size = 0; + spc7110_data_rom_offset = 0x100000; + st_A_ram_size = 0; + st_B_ram_size = 0; + bsxpack_type = BSXPackType::Unknown; + supergameboy_version = SuperGameBoyVersion::Version1; + supergameboy_ram_size = 0; + supergameboy_rtc_size = 0; + + has_bsx_slot = false; + has_superfx = false; + has_sa1 = false; + has_necdsp = false; + has_srtc = false; + has_sdd1 = false; + has_spc7110 = false; + has_spc7110rtc = false; + has_cx4 = false; + has_obc1 = false; + has_st0018 = false; + has_msu1 = false; + has_serial = false; + has_dos = false; + + parse_xml(xml_list); +//print(xml_list[0], "\n\n"); + + // autodetect MSU1 if it wasn't specified in a manifest + if(!has_msu1 && file::exists(string(basename(), ".msu"))) { + has_msu1 = true; + + Mapping m(msu1); + m.addrlo = 0x2000; + m.addrhi = 0x2007; + mapping.append(m); + } + + if(ram_size > 0) { + memory::cartram.map(allocate(ram_size, 0xff), ram_size); + } + + if(has_srtc || has_spc7110rtc) { + memory::cartrtc.map(allocate(20, 0xff), 20); + } + + if(mode == Mode::Bsx) { + memory::bsxpram.map(allocate(512 * 1024, 0xff), 512 * 1024); + } + + if(mode == Mode::SufamiTurbo) { + if(st_A_ram_size) memory::stAram.map(allocate(st_A_ram_size, 0xff), st_A_ram_size); + if(st_B_ram_size) memory::stBram.map(allocate(st_B_ram_size, 0xff), st_B_ram_size); + } + + if(mode == Mode::SuperGameBoy) { + if(memory::gbrom.data()) { + if(supergameboy_ram_size) memory::gbram.map(allocate(supergameboy_ram_size, 0xff), supergameboy_ram_size); + if(supergameboy_rtc_size) memory::gbrtc.map(allocate(supergameboy_rtc_size, 0x00), supergameboy_rtc_size); + } + } + + memory::cartrom.write_protect(true); + memory::cartram.write_protect(false); + memory::cartrtc.write_protect(false); + memory::bsxpack.write_protect(true); + memory::bsxpram.write_protect(false); + memory::stArom.write_protect(true); + memory::stAram.write_protect(false); + memory::stBrom.write_protect(true); + memory::stBram.write_protect(false); + memory::gbrom.write_protect(true); + memory::gbram.write_protect(false); + memory::gbrtc.write_protect(false); + + unsigned checksum = ~0; foreach(n, memory::cartrom) checksum = crc32_adjust(checksum, n); + if(memory::bsxpack.size() != 0) foreach(n, memory::bsxpack) checksum = crc32_adjust(checksum, n); + if(memory::stArom.size() != 0) foreach(n, memory::stArom ) checksum = crc32_adjust(checksum, n); + if(memory::stBrom.size() != 0) foreach(n, memory::stBrom ) checksum = crc32_adjust(checksum, n); + if(memory::gbrom.size() != 0) foreach(n, memory::gbrom ) checksum = crc32_adjust(checksum, n); + crc32 = ~checksum; + + sha256_ctx sha; + uint8_t shahash[32]; + sha256_init(&sha); + sha256_chunk(&sha, memory::cartrom.data(), memory::cartrom.size()); + sha256_final(&sha); + sha256_hash(&sha, shahash); + + string hash; + foreach(n, shahash) hash << hex<2>(n); + sha256 = hash; + + bus.load_cart(); + system.serialize_init(); + loaded = true; +} + +void Cartridge::unload() { + memory::cartrom.reset(); + memory::cartram.reset(); + memory::cartrtc.reset(); + memory::bsxpack.reset(); + memory::bsxpram.reset(); + memory::stArom.reset(); + memory::stAram.reset(); + memory::stBrom.reset(); + memory::stBram.reset(); + memory::gbrom.reset(); + memory::gbram.reset(); + memory::gbrtc.reset(); + + if(loaded == false) return; + bus.unload_cart(); + loaded = false; +} + +Memory& Cartridge::bsxpack_access() { + if(memory::bsxpack.size() == 0) return memory::memory_unmapped; + return (bsxpack_type == BSXPackType::FlashROM) ? (Memory&)bsxflash : (Memory&)memory::bsxpack; +} + +Cartridge::Cartridge() { + loaded = false; + unload(); +} + +Cartridge::~Cartridge() { + unload(); +} + +} diff --git a/bsnes/snes/cartridge/xml.cpp b/bsnes/snes/cartridge/xml.cpp index f3355e7a..4ed8a74d 100644 --- a/bsnes/snes/cartridge/xml.cpp +++ b/bsnes/snes/cartridge/xml.cpp @@ -1,626 +1,626 @@ -#ifdef CARTRIDGE_CPP - -void Cartridge::parse_xml(const lstring &list) { - mapping.reset(); - - //parse any slots *before* parsing the base cartridge - if(mode == Mode::BsxSlotted) { - parse_xml_bsx(list[1]); - } else if(mode == Mode::Bsx) { - parse_xml_bsx(list[1]); - } else if(mode == Mode::SufamiTurbo) { - parse_xml_sufami_turbo(list[1], 0); - parse_xml_sufami_turbo(list[2], 1); - } else if(mode == Mode::SuperGameBoy) { - parse_xml_gameboy(list[1]); - } - - parse_xml_cartridge(list[0]); -} - -void Cartridge::parse_xml_cartridge(const char *data) { - xml_element document = xml_parse(data); - if(document.element.size() == 0) return; - - - foreach(head, document.element) { - if(head.name == "cartridge") { - foreach(attr, head.attribute) { - if(attr.name == "region") { - if(attr.content == "NTSC") region = Region::NTSC; - if(attr.content == "PAL") region = Region::PAL; - } - } - - foreach(node, head.element) { - if(node.name == "rom") xml_parse_rom(node); - if(node.name == "ram") xml_parse_ram(node); - if(node.name == "superfx") xml_parse_superfx(node); - if(node.name == "sa1") xml_parse_sa1(node); - if(node.name == "necdsp") xml_parse_necdsp(node); - if(node.name == "bsx") xml_parse_bsx(node); - if(node.name == "sufamiturbo") xml_parse_sufamiturbo(node); - if(node.name == "supergameboy") xml_parse_supergameboy(node); - if(node.name == "srtc") xml_parse_srtc(node); - if(node.name == "sdd1") xml_parse_sdd1(node); - if(node.name == "spc7110") xml_parse_spc7110(node); - if(node.name == "cx4") xml_parse_cx4(node); - if(node.name == "obc1") xml_parse_obc1(node); - if(node.name == "setarisc") xml_parse_setarisc(node); - if(node.name == "msu1") xml_parse_msu1(node); - if(node.name == "serial") xml_parse_serial(node); - if(node.name == "dos") xml_parse_dos(node); - } - } - } -} - -void Cartridge::parse_xml_bsx(const char *data) { - xml_element document = xml_parse(data); - if(document.element.size() == 0) return; - - bsxpack_type = BSXPackType::FlashROM; - - foreach(head, document.element) { - if(head.name == "cartridge") { - foreach(attr, head.attribute) { - if(attr.name == "type") { - if(attr.content == "FlashROM") bsxpack_type = BSXPackType::FlashROM; - if(attr.content == "MaskROM") bsxpack_type = BSXPackType::MaskROM; - } - } - } - } -} - -void Cartridge::parse_xml_sufami_turbo(const char *data, bool slot) { - xml_element document = xml_parse(data); - if(document.element.size() == 0) return; - - foreach(head, document.element) { - if(head.name == "cartridge") { - foreach(leaf, head.element) { - if(leaf.name == "ram") { - foreach(attr, leaf.attribute) { - if(attr.name == "size") { - (slot == 0 ? st_A_ram_size : st_B_ram_size) = hex(attr.content); - } - } - } - } - } - } -} - -void Cartridge::parse_xml_gameboy(const char *data) { - xml_element document = xml_parse(data); - if(document.element.size() == 0) return; - - foreach(head, document.element) { - if(head.name == "cartridge") { - foreach(attr, head.attribute) { - if(attr.name == "rtc") { - supergameboy_rtc_size = (attr.content == "true") ? 4 : 0; - } - } - - foreach(leaf, head.element) { - if(leaf.name == "ram") { - foreach(attr, leaf.attribute) { - if(attr.name == "size") { - supergameboy_ram_size = hex(attr.content); - } - } - } - } - } - } -} - -void Cartridge::xml_parse_memory(xml_element &root, Memory &memory) { - foreach(leaf, root.element) { - if(leaf.name == "map") { - Mapping m(memory); - foreach(attr, leaf.attribute) { - if(attr.name == "address") xml_parse_address(m, attr.content); - if(attr.name == "mode") xml_parse_mode(m, attr.content); - if(attr.name == "offset") m.offset = hex(attr.content); - if(attr.name == "size") m.size = hex(attr.content); - } - mapping.append(m); - } - } -} - -void Cartridge::xml_parse_rom(xml_element &root) { - xml_parse_memory(root, memory::cartrom); -} - -void Cartridge::xml_parse_ram(xml_element &root) { - foreach(attr, root.attribute) { - if(attr.name == "size") ram_size = hex(attr.content); - } - if(ram_size > 0) { - xml_parse_memory(root, memory::cartram); - } -} - -void Cartridge::xml_parse_superfx(xml_element &root) { - has_superfx = true; - - foreach(node, root.element) { - if(node.name == "rom") { - xml_parse_memory(node, memory::fxrom); - } else if(node.name == "ram") { - foreach(attr, node.attribute) { - if(attr.name == "size") ram_size = hex(attr.content); - } - if(ram_size > 0) { - xml_parse_memory(node, memory::fxram); - } - } else if(node.name == "mmio") { - foreach(leaf, node.element) { - if(leaf.name == "map") { - Mapping m(superfx); - foreach(attr, leaf.attribute) { - if(attr.name == "address") xml_parse_address(m, attr.content); - } - mapping.append(m); - } - } - } - } -} - -void Cartridge::xml_parse_sa1(xml_element &root) { - has_sa1 = true; - - foreach(node, root.element) { - if(node.name == "rom") { - xml_parse_memory(node, memory::vsprom); - } else if(node.name == "iram") { - xml_parse_memory(node, memory::cpuiram); - } else if(node.name == "bwram") { - foreach(attr, node.attribute) { - if(attr.name == "size") ram_size = hex(attr.content); - } - if (ram_size) { - xml_parse_memory(node, memory::cc1bwram); - } - } else if(node.name == "mmio") { - foreach(leaf, node.element) { - if(leaf.name == "map") { - Mapping m(sa1); - foreach(attr, leaf.attribute) { - if(attr.name == "address") xml_parse_address(m, attr.content); - } - mapping.append(m); - } - } - } - } -} - -void Cartridge::xml_parse_necdsp(xml_element &root) { - has_necdsp = true; - necdsp.revision = NECDSP::Revision::uPD7725; - necdsp.frequency = 8000000; - - for(unsigned n = 0; n < 16384; n++) necdsp.programROM[n] = 0x000000; - for(unsigned n = 0; n < 2048; n++) necdsp.dataROM[n] = 0x0000; - - string program, programhash; - string sha256; - - foreach(attr, root.attribute) { - if(attr.name == "revision") { - if(attr.content == "upd7725" ) necdsp.revision = NECDSP::Revision::uPD7725; - if(attr.content == "upd96050") necdsp.revision = NECDSP::Revision::uPD96050; - } else if(attr.name == "frequency") { - necdsp.frequency = decimal(attr.content); - } else if(attr.name == "program") { - program << filepath(dir(basename()), config().path.firmware); - program << attr.content; - } else if(attr.name == "sha256") { - sha256 = attr.content; - } - } - - unsigned promsize = (necdsp.revision == NECDSP::Revision::uPD7725 ? 2048 : 16384); - unsigned dromsize = (necdsp.revision == NECDSP::Revision::uPD7725 ? 1024 : 2048); - unsigned filesize = promsize * 3 + dromsize * 2; - - file fp; - if(fp.open(program, file::mode::read)) { - if(fp.size() == filesize) { - for(unsigned n = 0; n < promsize; n++) necdsp.programROM[n] = fp.readm(3); - for(unsigned n = 0; n < dromsize; n++) necdsp.dataROM[n] = fp.readm(2); - - fp.seek(0); - uint8_t data[filesize]; - fp.read(data, filesize); - - sha256_ctx sha; - uint8 shahash[32]; - sha256_init(&sha); - sha256_chunk(&sha, data, filesize); - sha256_final(&sha); - sha256_hash(&sha, shahash); - foreach(n, shahash) programhash.append(hex<2>(n)); - } - fp.close(); - } - - foreach(node, root.element) { - if(node.name == "dr") { - foreach(attr, node.attribute) { - if(attr.name == "mask") necdsp.drmask = hex(attr.content); - if(attr.name == "test") necdsp.drtest = hex(attr.content); - } - } - - if(node.name == "sr") { - foreach(attr, node.attribute) { - if(attr.name == "mask") necdsp.srmask = hex(attr.content); - if(attr.name == "test") necdsp.srtest = hex(attr.content); - } - } - - if(node.name == "dp") { - foreach(attr, node.attribute) { - if(attr.name == "mask") necdsp.dpmask = hex(attr.content); - if(attr.name == "test") necdsp.dptest = hex(attr.content); - } - } - - if(node.name == "map") { - Mapping m(necdsp); - foreach(attr, node.attribute) { - if(attr.name == "address") xml_parse_address(m, attr.content); - } - mapping.append(m); - } - } - - if(programhash == "") { - system.interface->message({ "Warning: NEC DSP program ", program, " is missing." }); - } else if(sha256 != "" && sha256 != programhash) { - system.interface->message({ - "Warning: NEC DSP program ", program, " SHA256 is incorrect.\n\n" - "Expected:\n", sha256, "\n\n" - "Actual:\n", programhash - }); - } -} - -void Cartridge::xml_parse_bsx(xml_element &root) { - has_bsx_slot = true; - if(mode != Mode::BsxSlotted && mode != Mode::Bsx) return; - - foreach(node, root.element) { - if(node.name == "slot") { - xml_parse_memory(node, bsxpack_access()); - } else if(node.name == "mcc") { - foreach(leaf, node.element) { - if(leaf.name == "map") { - Mapping m(bsxcart); - foreach(attr, leaf.attribute) { - if(attr.name == "address") xml_parse_address(m, attr.content); - } - mapping.append(m); - } - } - } - } -} - -void Cartridge::xml_parse_sufamiturbo(xml_element &root) { - if(mode != Mode::SufamiTurbo) return; - - foreach(node, root.element) { - if(node.name == "slot") { - bool slotid = 0; - foreach(attr, node.attribute) { - if(attr.name == "id") { - if(attr.content == "A") slotid = 0; - if(attr.content == "B") slotid = 1; - } - } - - Memory &rom = (slotid == 0) ? memory::stArom : memory::stBrom; - if(rom.size() == 0) continue; - Memory &ram = (slotid == 0) ? memory::stAram : memory::stBram; - unsigned ram_size = (slotid == 0) ? st_A_ram_size : st_B_ram_size; - - foreach(slot, node.element) { - if(slot.name == "rom") { - xml_parse_memory(slot, rom); - } else if(slot.name == "ram" && ram_size > 0) { - xml_parse_memory(slot, ram); - } - } - } - } -} - -void Cartridge::xml_parse_supergameboy(xml_element &root) { - if(mode != Mode::SuperGameBoy) return; - - foreach(attr, root.attribute) { - if(attr.name == "revision") { - if(attr.content == "1") supergameboy_version = SuperGameBoyVersion::Version1; - if(attr.content == "2") supergameboy_version = SuperGameBoyVersion::Version2; - } - } - - foreach(node, root.element) { - if(node.name == "map") { - Mapping m((Memory&)supergameboy); - foreach(attr, node.attribute) { - if(attr.name == "address") xml_parse_address(m, attr.content); - } - mapping.append(m); - } - } -} - -void Cartridge::xml_parse_srtc(xml_element &root) { - has_srtc = true; - - foreach(node, root.element) { - if(node.name == "map") { - Mapping m(srtc); - foreach(attr, node.attribute) { - if(attr.name == "address") xml_parse_address(m, attr.content); - } - mapping.append(m); - } - } -} - -void Cartridge::xml_parse_sdd1(xml_element &root) { - has_sdd1 = true; - - foreach(node, root.element) { - if(node.name == "mcu") { - foreach(leaf, node.element) { - if(leaf.name == "map") { - Mapping m((Memory&)sdd1); - foreach(attr, leaf.attribute) { - if(attr.name == "address") xml_parse_address(m, attr.content); - } - mapping.append(m); - } - } - } else if(node.name == "mmio") { - foreach(leaf, node.element) { - if(leaf.name == "map") { - Mapping m((MMIO&)sdd1); - foreach(attr, leaf.attribute) { - if(attr.name == "address") xml_parse_address(m, attr.content); - } - mapping.append(m); - } - } - } - } -} - -void Cartridge::xml_parse_spc7110(xml_element &root) { - has_spc7110 = true; - - foreach(node, root.element) { - if(node.name == "dcu") { - foreach(leaf, node.element) { - if(leaf.name == "map") { - Mapping m(spc7110dcu); - foreach(attr, leaf.attribute) { - if(attr.name == "address") xml_parse_address(m, attr.content); - } - mapping.append(m); - } - } - } else if(node.name == "mcu") { - foreach(leaf, node.element) { - if(leaf.name == "map") { - Mapping m(spc7110mcu); - foreach(attr, leaf.attribute) { - if(attr.name == "address") xml_parse_address(m, attr.content); - if(attr.name == "offset") spc7110_data_rom_offset = hex(attr.content); - } - mapping.append(m); - } - } - } else if(node.name == "mmio") { - foreach(leaf, node.element) { - if(leaf.name == "map") { - Mapping m(spc7110); - foreach(attr, leaf.attribute) { - if(attr.name == "address") xml_parse_address(m, attr.content); - } - mapping.append(m); - } - } - } else if(node.name == "ram") { - foreach(attr, node.attribute) { - if(attr.name == "size") ram_size = hex(attr.content); - } - if(ram_size > 0) { - xml_parse_memory(node, spc7110ram); - } - } else if(node.name == "rtc") { - has_spc7110rtc = true; - - foreach(leaf, node.element) { - if(leaf.name == "map") { - Mapping m(spc7110); - foreach(attr, leaf.attribute) { - if(attr.name == "address") xml_parse_address(m, attr.content); - } - mapping.append(m); - } - } - } - } -} - -void Cartridge::xml_parse_cx4(xml_element &root) { - has_cx4 = true; - cx4.frequency = 20000000; - - // TODO: allow custom data ROM, maybe - - foreach(attr, root.attribute) { - if(attr.name == "frequency") { - cx4.frequency = decimal(attr.content); - } - } - - foreach(node, root.element) { - if(node.name == "rom") { - xml_parse_memory(node, memory::cx4rom); - } else if(node.name == "ram") { - xml_parse_memory(node, memory::cx4ram); - } else if(node.name == "map") { - Mapping m(cx4); - foreach(attr, node.attribute) { - if(attr.name == "address") xml_parse_address(m, attr.content); - } - mapping.append(m); - } - } -} - -void Cartridge::xml_parse_obc1(xml_element &root) { - has_obc1 = true; - - foreach(node, root.element) { - if(node.name == "map") { - Mapping m(obc1); - foreach(attr, node.attribute) { - if(attr.name == "address") xml_parse_address(m, attr.content); - } - m.min_ram_size = 0x2000; - mapping.append(m); - } - } -} - -void Cartridge::xml_parse_setarisc(xml_element &root) { - unsigned program = 0; - - foreach(attr, root.attribute) { - if(attr.name == "program") { - if(attr.content == "ST-0018") { - program = 1; - has_st0018 = true; - } - } - } - - MMIO *map[2] = { 0, &st0018 }; - - foreach(node, root.element) { - if(node.name == "map" && map[program]) { - Mapping m(*map[program]); - foreach(attr, node.attribute) { - if(attr.name == "address") xml_parse_address(m, attr.content); - } - mapping.append(m); - } - } -} - -void Cartridge::xml_parse_msu1(xml_element &root) { - has_msu1 = true; - - foreach(node, root.element) { - if(node.name == "mmio") { - foreach(leaf, node.element) { - if(leaf.name == "map") { - Mapping m(msu1); - foreach(attr, leaf.attribute) { - if(attr.name == "address") xml_parse_address(m, attr.content); - } - mapping.append(m); - } - } - } - } -} - -void Cartridge::xml_parse_serial(xml_element &root) { - has_serial = true; -} - - -void Cartridge::xml_parse_dos(xml_element& root) { - has_dos = true; - - foreach(node, root.element) { - if (node.name == "map") { - Mapping m(dos); - foreach(attr, node.attribute) { - if (attr.name == "address") { - xml_parse_address(m, attr.content); - } - } - mapping.append(m); - } - } -} - - -void Cartridge::xml_parse_address(Mapping &m, const string &data) { - lstring part; - part.split(":", data); - if(part.size() != 2) return; - - lstring subpart; - subpart.split("-", part[0]); - if(subpart.size() == 1) { - m.banklo = hex(subpart[0]); - m.bankhi = m.banklo; - } else if(subpart.size() == 2) { - m.banklo = hex(subpart[0]); - m.bankhi = hex(subpart[1]); - } - - subpart.split("-", part[1]); - if(subpart.size() == 1) { - m.addrlo = hex(subpart[0]); - m.addrhi = m.addrlo; - } else if(subpart.size() == 2) { - m.addrlo = hex(subpart[0]); - m.addrhi = hex(subpart[1]); - } -} - -void Cartridge::xml_parse_mode(Mapping &m, const string& data) { - if(data == "direct") m.mode = Bus::MapMode::Direct; - else if(data == "linear") m.mode = Bus::MapMode::Linear; - else if(data == "shadow") m.mode = Bus::MapMode::Shadow; -} - -Cartridge::Mapping::Mapping() { - memory = 0; - mmio = 0; - mode = Bus::MapMode::Direct; - banklo = bankhi = addrlo = addrhi = offset = size = min_ram_size = 0; -} - -Cartridge::Mapping::Mapping(Memory &memory_) { - memory = &memory_; - mmio = 0; - mode = Bus::MapMode::Direct; - banklo = bankhi = addrlo = addrhi = offset = size = min_ram_size = 0; -} - -Cartridge::Mapping::Mapping(MMIO &mmio_) { - memory = 0; - mmio = &mmio_; - mode = Bus::MapMode::Direct; - banklo = bankhi = addrlo = addrhi = offset = size = min_ram_size = 0; -} - -#endif +#ifdef CARTRIDGE_CPP + +void Cartridge::parse_xml(const lstring &list) { + mapping.reset(); + + //parse any slots *before* parsing the base cartridge + if(mode == Mode::BsxSlotted) { + parse_xml_bsx(list[1]); + } else if(mode == Mode::Bsx) { + parse_xml_bsx(list[1]); + } else if(mode == Mode::SufamiTurbo) { + parse_xml_sufami_turbo(list[1], 0); + parse_xml_sufami_turbo(list[2], 1); + } else if(mode == Mode::SuperGameBoy) { + parse_xml_gameboy(list[1]); + } + + parse_xml_cartridge(list[0]); +} + +void Cartridge::parse_xml_cartridge(const char *data) { + xml_element document = xml_parse(data); + if(document.element.size() == 0) return; + + + foreach(head, document.element) { + if(head.name == "cartridge") { + foreach(attr, head.attribute) { + if(attr.name == "region") { + if(attr.content == "NTSC") region = Region::NTSC; + if(attr.content == "PAL") region = Region::PAL; + } + } + + foreach(node, head.element) { + if(node.name == "rom") xml_parse_rom(node); + if(node.name == "ram") xml_parse_ram(node); + if(node.name == "superfx") xml_parse_superfx(node); + if(node.name == "sa1") xml_parse_sa1(node); + if(node.name == "necdsp") xml_parse_necdsp(node); + if(node.name == "bsx") xml_parse_bsx(node); + if(node.name == "sufamiturbo") xml_parse_sufamiturbo(node); + if(node.name == "supergameboy") xml_parse_supergameboy(node); + if(node.name == "srtc") xml_parse_srtc(node); + if(node.name == "sdd1") xml_parse_sdd1(node); + if(node.name == "spc7110") xml_parse_spc7110(node); + if(node.name == "cx4") xml_parse_cx4(node); + if(node.name == "obc1") xml_parse_obc1(node); + if(node.name == "setarisc") xml_parse_setarisc(node); + if(node.name == "msu1") xml_parse_msu1(node); + if(node.name == "serial") xml_parse_serial(node); + if(node.name == "dos") xml_parse_dos(node); + } + } + } +} + +void Cartridge::parse_xml_bsx(const char *data) { + xml_element document = xml_parse(data); + if(document.element.size() == 0) return; + + bsxpack_type = BSXPackType::FlashROM; + + foreach(head, document.element) { + if(head.name == "cartridge") { + foreach(attr, head.attribute) { + if(attr.name == "type") { + if(attr.content == "FlashROM") bsxpack_type = BSXPackType::FlashROM; + if(attr.content == "MaskROM") bsxpack_type = BSXPackType::MaskROM; + } + } + } + } +} + +void Cartridge::parse_xml_sufami_turbo(const char *data, bool slot) { + xml_element document = xml_parse(data); + if(document.element.size() == 0) return; + + foreach(head, document.element) { + if(head.name == "cartridge") { + foreach(leaf, head.element) { + if(leaf.name == "ram") { + foreach(attr, leaf.attribute) { + if(attr.name == "size") { + (slot == 0 ? st_A_ram_size : st_B_ram_size) = hex(attr.content); + } + } + } + } + } + } +} + +void Cartridge::parse_xml_gameboy(const char *data) { + xml_element document = xml_parse(data); + if(document.element.size() == 0) return; + + foreach(head, document.element) { + if(head.name == "cartridge") { + foreach(attr, head.attribute) { + if(attr.name == "rtc") { + supergameboy_rtc_size = (attr.content == "true") ? 4 : 0; + } + } + + foreach(leaf, head.element) { + if(leaf.name == "ram") { + foreach(attr, leaf.attribute) { + if(attr.name == "size") { + supergameboy_ram_size = hex(attr.content); + } + } + } + } + } + } +} + +void Cartridge::xml_parse_memory(xml_element &root, Memory &memory) { + foreach(leaf, root.element) { + if(leaf.name == "map") { + Mapping m(memory); + foreach(attr, leaf.attribute) { + if(attr.name == "address") xml_parse_address(m, attr.content); + if(attr.name == "mode") xml_parse_mode(m, attr.content); + if(attr.name == "offset") m.offset = hex(attr.content); + if(attr.name == "size") m.size = hex(attr.content); + } + mapping.append(m); + } + } +} + +void Cartridge::xml_parse_rom(xml_element &root) { + xml_parse_memory(root, memory::cartrom); +} + +void Cartridge::xml_parse_ram(xml_element &root) { + foreach(attr, root.attribute) { + if(attr.name == "size") ram_size = hex(attr.content); + } + if(ram_size > 0) { + xml_parse_memory(root, memory::cartram); + } +} + +void Cartridge::xml_parse_superfx(xml_element &root) { + has_superfx = true; + + foreach(node, root.element) { + if(node.name == "rom") { + xml_parse_memory(node, memory::fxrom); + } else if(node.name == "ram") { + foreach(attr, node.attribute) { + if(attr.name == "size") ram_size = hex(attr.content); + } + if(ram_size > 0) { + xml_parse_memory(node, memory::fxram); + } + } else if(node.name == "mmio") { + foreach(leaf, node.element) { + if(leaf.name == "map") { + Mapping m(superfx); + foreach(attr, leaf.attribute) { + if(attr.name == "address") xml_parse_address(m, attr.content); + } + mapping.append(m); + } + } + } + } +} + +void Cartridge::xml_parse_sa1(xml_element &root) { + has_sa1 = true; + + foreach(node, root.element) { + if(node.name == "rom") { + xml_parse_memory(node, memory::vsprom); + } else if(node.name == "iram") { + xml_parse_memory(node, memory::cpuiram); + } else if(node.name == "bwram") { + foreach(attr, node.attribute) { + if(attr.name == "size") ram_size = hex(attr.content); + } + if (ram_size) { + xml_parse_memory(node, memory::cc1bwram); + } + } else if(node.name == "mmio") { + foreach(leaf, node.element) { + if(leaf.name == "map") { + Mapping m(sa1); + foreach(attr, leaf.attribute) { + if(attr.name == "address") xml_parse_address(m, attr.content); + } + mapping.append(m); + } + } + } + } +} + +void Cartridge::xml_parse_necdsp(xml_element &root) { + has_necdsp = true; + necdsp.revision = NECDSP::Revision::uPD7725; + necdsp.frequency = 8000000; + + for(unsigned n = 0; n < 16384; n++) necdsp.programROM[n] = 0x000000; + for(unsigned n = 0; n < 2048; n++) necdsp.dataROM[n] = 0x0000; + + string program, programhash; + string sha256; + + foreach(attr, root.attribute) { + if(attr.name == "revision") { + if(attr.content == "upd7725" ) necdsp.revision = NECDSP::Revision::uPD7725; + if(attr.content == "upd96050") necdsp.revision = NECDSP::Revision::uPD96050; + } else if(attr.name == "frequency") { + necdsp.frequency = decimal(attr.content); + } else if(attr.name == "program") { + program << filepath(dir(basename()), config().path.firmware); + program << attr.content; + } else if(attr.name == "sha256") { + sha256 = attr.content; + } + } + + unsigned promsize = (necdsp.revision == NECDSP::Revision::uPD7725 ? 2048 : 16384); + unsigned dromsize = (necdsp.revision == NECDSP::Revision::uPD7725 ? 1024 : 2048); + unsigned filesize = promsize * 3 + dromsize * 2; + + file fp; + if(fp.open(program, file::mode::read)) { + if(fp.size() == filesize) { + for(unsigned n = 0; n < promsize; n++) necdsp.programROM[n] = fp.readm(3); + for(unsigned n = 0; n < dromsize; n++) necdsp.dataROM[n] = fp.readm(2); + + fp.seek(0); + uint8_t data[filesize]; + fp.read(data, filesize); + + sha256_ctx sha; + uint8 shahash[32]; + sha256_init(&sha); + sha256_chunk(&sha, data, filesize); + sha256_final(&sha); + sha256_hash(&sha, shahash); + foreach(n, shahash) programhash.append(hex<2>(n)); + } + fp.close(); + } + + foreach(node, root.element) { + if(node.name == "dr") { + foreach(attr, node.attribute) { + if(attr.name == "mask") necdsp.drmask = hex(attr.content); + if(attr.name == "test") necdsp.drtest = hex(attr.content); + } + } + + if(node.name == "sr") { + foreach(attr, node.attribute) { + if(attr.name == "mask") necdsp.srmask = hex(attr.content); + if(attr.name == "test") necdsp.srtest = hex(attr.content); + } + } + + if(node.name == "dp") { + foreach(attr, node.attribute) { + if(attr.name == "mask") necdsp.dpmask = hex(attr.content); + if(attr.name == "test") necdsp.dptest = hex(attr.content); + } + } + + if(node.name == "map") { + Mapping m(necdsp); + foreach(attr, node.attribute) { + if(attr.name == "address") xml_parse_address(m, attr.content); + } + mapping.append(m); + } + } + + if(programhash == "") { + system.interface->message({ "Warning: NEC DSP program ", program, " is missing." }); + } else if(sha256 != "" && sha256 != programhash) { + system.interface->message({ + "Warning: NEC DSP program ", program, " SHA256 is incorrect.\n\n" + "Expected:\n", sha256, "\n\n" + "Actual:\n", programhash + }); + } +} + +void Cartridge::xml_parse_bsx(xml_element &root) { + has_bsx_slot = true; + if(mode != Mode::BsxSlotted && mode != Mode::Bsx) return; + + foreach(node, root.element) { + if(node.name == "slot") { + xml_parse_memory(node, bsxpack_access()); + } else if(node.name == "mcc") { + foreach(leaf, node.element) { + if(leaf.name == "map") { + Mapping m(bsxcart); + foreach(attr, leaf.attribute) { + if(attr.name == "address") xml_parse_address(m, attr.content); + } + mapping.append(m); + } + } + } + } +} + +void Cartridge::xml_parse_sufamiturbo(xml_element &root) { + if(mode != Mode::SufamiTurbo) return; + + foreach(node, root.element) { + if(node.name == "slot") { + bool slotid = 0; + foreach(attr, node.attribute) { + if(attr.name == "id") { + if(attr.content == "A") slotid = 0; + if(attr.content == "B") slotid = 1; + } + } + + Memory &rom = (slotid == 0) ? memory::stArom : memory::stBrom; + if(rom.size() == 0) continue; + Memory &ram = (slotid == 0) ? memory::stAram : memory::stBram; + unsigned ram_size = (slotid == 0) ? st_A_ram_size : st_B_ram_size; + + foreach(slot, node.element) { + if(slot.name == "rom") { + xml_parse_memory(slot, rom); + } else if(slot.name == "ram" && ram_size > 0) { + xml_parse_memory(slot, ram); + } + } + } + } +} + +void Cartridge::xml_parse_supergameboy(xml_element &root) { + if(mode != Mode::SuperGameBoy) return; + + foreach(attr, root.attribute) { + if(attr.name == "revision") { + if(attr.content == "1") supergameboy_version = SuperGameBoyVersion::Version1; + if(attr.content == "2") supergameboy_version = SuperGameBoyVersion::Version2; + } + } + + foreach(node, root.element) { + if(node.name == "map") { + Mapping m((Memory&)supergameboy); + foreach(attr, node.attribute) { + if(attr.name == "address") xml_parse_address(m, attr.content); + } + mapping.append(m); + } + } +} + +void Cartridge::xml_parse_srtc(xml_element &root) { + has_srtc = true; + + foreach(node, root.element) { + if(node.name == "map") { + Mapping m(srtc); + foreach(attr, node.attribute) { + if(attr.name == "address") xml_parse_address(m, attr.content); + } + mapping.append(m); + } + } +} + +void Cartridge::xml_parse_sdd1(xml_element &root) { + has_sdd1 = true; + + foreach(node, root.element) { + if(node.name == "mcu") { + foreach(leaf, node.element) { + if(leaf.name == "map") { + Mapping m((Memory&)sdd1); + foreach(attr, leaf.attribute) { + if(attr.name == "address") xml_parse_address(m, attr.content); + } + mapping.append(m); + } + } + } else if(node.name == "mmio") { + foreach(leaf, node.element) { + if(leaf.name == "map") { + Mapping m((MMIO&)sdd1); + foreach(attr, leaf.attribute) { + if(attr.name == "address") xml_parse_address(m, attr.content); + } + mapping.append(m); + } + } + } + } +} + +void Cartridge::xml_parse_spc7110(xml_element &root) { + has_spc7110 = true; + + foreach(node, root.element) { + if(node.name == "dcu") { + foreach(leaf, node.element) { + if(leaf.name == "map") { + Mapping m(spc7110dcu); + foreach(attr, leaf.attribute) { + if(attr.name == "address") xml_parse_address(m, attr.content); + } + mapping.append(m); + } + } + } else if(node.name == "mcu") { + foreach(leaf, node.element) { + if(leaf.name == "map") { + Mapping m(spc7110mcu); + foreach(attr, leaf.attribute) { + if(attr.name == "address") xml_parse_address(m, attr.content); + if(attr.name == "offset") spc7110_data_rom_offset = hex(attr.content); + } + mapping.append(m); + } + } + } else if(node.name == "mmio") { + foreach(leaf, node.element) { + if(leaf.name == "map") { + Mapping m(spc7110); + foreach(attr, leaf.attribute) { + if(attr.name == "address") xml_parse_address(m, attr.content); + } + mapping.append(m); + } + } + } else if(node.name == "ram") { + foreach(attr, node.attribute) { + if(attr.name == "size") ram_size = hex(attr.content); + } + if(ram_size > 0) { + xml_parse_memory(node, spc7110ram); + } + } else if(node.name == "rtc") { + has_spc7110rtc = true; + + foreach(leaf, node.element) { + if(leaf.name == "map") { + Mapping m(spc7110); + foreach(attr, leaf.attribute) { + if(attr.name == "address") xml_parse_address(m, attr.content); + } + mapping.append(m); + } + } + } + } +} + +void Cartridge::xml_parse_cx4(xml_element &root) { + has_cx4 = true; + cx4.frequency = 20000000; + + // TODO: allow custom data ROM, maybe + + foreach(attr, root.attribute) { + if(attr.name == "frequency") { + cx4.frequency = decimal(attr.content); + } + } + + foreach(node, root.element) { + if(node.name == "rom") { + xml_parse_memory(node, memory::cx4rom); + } else if(node.name == "ram") { + xml_parse_memory(node, memory::cx4ram); + } else if(node.name == "map") { + Mapping m(cx4); + foreach(attr, node.attribute) { + if(attr.name == "address") xml_parse_address(m, attr.content); + } + mapping.append(m); + } + } +} + +void Cartridge::xml_parse_obc1(xml_element &root) { + has_obc1 = true; + + foreach(node, root.element) { + if(node.name == "map") { + Mapping m(obc1); + foreach(attr, node.attribute) { + if(attr.name == "address") xml_parse_address(m, attr.content); + } + m.min_ram_size = 0x2000; + mapping.append(m); + } + } +} + +void Cartridge::xml_parse_setarisc(xml_element &root) { + unsigned program = 0; + + foreach(attr, root.attribute) { + if(attr.name == "program") { + if(attr.content == "ST-0018") { + program = 1; + has_st0018 = true; + } + } + } + + MMIO *map[2] = { 0, &st0018 }; + + foreach(node, root.element) { + if(node.name == "map" && map[program]) { + Mapping m(*map[program]); + foreach(attr, node.attribute) { + if(attr.name == "address") xml_parse_address(m, attr.content); + } + mapping.append(m); + } + } +} + +void Cartridge::xml_parse_msu1(xml_element &root) { + has_msu1 = true; + + foreach(node, root.element) { + if(node.name == "mmio") { + foreach(leaf, node.element) { + if(leaf.name == "map") { + Mapping m(msu1); + foreach(attr, leaf.attribute) { + if(attr.name == "address") xml_parse_address(m, attr.content); + } + mapping.append(m); + } + } + } + } +} + +void Cartridge::xml_parse_serial(xml_element &root) { + has_serial = true; +} + + +void Cartridge::xml_parse_dos(xml_element& root) { + has_dos = true; + + foreach(node, root.element) { + if (node.name == "map") { + Mapping m(dos); + foreach(attr, node.attribute) { + if (attr.name == "address") { + xml_parse_address(m, attr.content); + } + } + mapping.append(m); + } + } +} + + +void Cartridge::xml_parse_address(Mapping &m, const string &data) { + lstring part; + part.split(":", data); + if(part.size() != 2) return; + + lstring subpart; + subpart.split("-", part[0]); + if(subpart.size() == 1) { + m.banklo = hex(subpart[0]); + m.bankhi = m.banklo; + } else if(subpart.size() == 2) { + m.banklo = hex(subpart[0]); + m.bankhi = hex(subpart[1]); + } + + subpart.split("-", part[1]); + if(subpart.size() == 1) { + m.addrlo = hex(subpart[0]); + m.addrhi = m.addrlo; + } else if(subpart.size() == 2) { + m.addrlo = hex(subpart[0]); + m.addrhi = hex(subpart[1]); + } +} + +void Cartridge::xml_parse_mode(Mapping &m, const string& data) { + if(data == "direct") m.mode = Bus::MapMode::Direct; + else if(data == "linear") m.mode = Bus::MapMode::Linear; + else if(data == "shadow") m.mode = Bus::MapMode::Shadow; +} + +Cartridge::Mapping::Mapping() { + memory = 0; + mmio = 0; + mode = Bus::MapMode::Direct; + banklo = bankhi = addrlo = addrhi = offset = size = min_ram_size = 0; +} + +Cartridge::Mapping::Mapping(Memory &memory_) { + memory = &memory_; + mmio = 0; + mode = Bus::MapMode::Direct; + banklo = bankhi = addrlo = addrhi = offset = size = min_ram_size = 0; +} + +Cartridge::Mapping::Mapping(MMIO &mmio_) { + memory = 0; + mmio = &mmio_; + mode = Bus::MapMode::Direct; + banklo = bankhi = addrlo = addrhi = offset = size = min_ram_size = 0; +} + +#endif From 48f45a9cdeacdc8fbf1e13baaf2f49b7293ed7bd Mon Sep 17 00:00:00 2001 From: MrL314 <11429905+MrL314@users.noreply.github.com> Date: Wed, 24 Nov 2021 17:30:22 -0500 Subject: [PATCH 5/5] dos auto-map --- common/nall/snes/cartridge.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/common/nall/snes/cartridge.hpp b/common/nall/snes/cartridge.hpp index f5df9fac..a1b48c8e 100644 --- a/common/nall/snes/cartridge.hpp +++ b/common/nall/snes/cartridge.hpp @@ -96,6 +96,7 @@ class SNESCartridge { bool has_st010; bool has_st011; bool has_st018; + bool has_dos; }; SNESCartridge::SNESCartridge(const uint8_t *data, unsigned size) { @@ -541,6 +542,7 @@ void SNESCartridge::read_header(const uint8_t *data, unsigned size) { has_st010 = false; has_st011 = false; has_st018 = false; + has_dos = false; //===================== //detect Game Boy carts