#include #include #include typedef enum { Unplugged, Standard, Mempak, Rumblepak, Transferpak, Mouse, } ControllerType; typedef enum { UP = 1 << 0, DOWN = 1 << 1, LEFT = 1 << 2, RIGHT = 1 << 3, B = 1 << 4, A = 1 << 5, C_UP = 1 << 6, C_DOWN = 1 << 7, C_LEFT = 1 << 8, C_RIGHT = 1 << 9, L = 1 << 10, R = 1 << 11, Z = 1 << 12, START = 1 << 13, } Buttons_t; struct BizPlatform : ares::Platform { auto attach(ares::Node::Object) -> void override; auto pak(ares::Node::Object) -> ares::VFS::Pak override; auto audio(ares::Node::Audio::Stream) -> void override; auto input(ares::Node::Input::Input) -> void override; auto time() -> n64 override; ares::VFS::Pak bizpak = nullptr; u16* soundbuf = alloc_invisible(1024 * 2); u32 nsamps = 0; bool hack = false; void (*inputcb)() = nullptr; bool lagged = true; u64 biztime = 0; }; auto BizPlatform::attach(ares::Node::Object node) -> void { if (auto stream = node->cast()) { stream->setResamplerFrequency(44100); } } auto BizPlatform::pak(ares::Node::Object) -> ares::VFS::Pak { return bizpak; } auto BizPlatform::audio(ares::Node::Audio::Stream stream) -> void { while (stream->pending()) { f64 buf[2]; stream->read(buf); soundbuf[nsamps * 2 + 0] = (s16)std::clamp(buf[0] * 32768, -32768.0, 32767.0); soundbuf[nsamps * 2 + 1] = (s16)std::clamp(buf[1] * 32768, -32768.0, 32767.0); if (nsamps < 1023) nsamps++; } } auto BizPlatform::input(ares::Node::Input::Input node) -> void { if (auto input = node->cast()) { if (input->name() == "Start" || input->name() == "Left Click") { lagged = false; if (inputcb) inputcb(); } } } auto BizPlatform::time() -> n64 { return biztime; } static ares::Node::System root = nullptr; static BizPlatform* platform = nullptr; static array_view* pifData = nullptr; static array_view* iplData = nullptr; static array_view* romData = nullptr; static array_view* diskData = nullptr; static array_view* diskErrorData = nullptr; static array_view* saveData = nullptr; static array_view* rtcData = nullptr; static array_view* gbRomData[4] = { nullptr, nullptr, nullptr, nullptr, }; typedef enum { NONE, EEPROM512, EEPROM2KB, SRAM32KB, SRAM96KB, SRAM128KB, FLASH128KB, } SaveType; static inline SaveType DetectSaveType(u8* rom) { string id; id.append((char)rom[0x3B]); id.append((char)rom[0x3C]); id.append((char)rom[0x3D]); char region_code = rom[0x3E]; u8 revision = rom[0x3F]; SaveType ret = NONE; if (id == "NTW") ret = EEPROM512; if (id == "NHF") ret = EEPROM512; if (id == "NOS") ret = EEPROM512; if (id == "NTC") ret = EEPROM512; if (id == "NER") ret = EEPROM512; if (id == "NAG") ret = EEPROM512; if (id == "NAB") ret = EEPROM512; if (id == "NS3") ret = EEPROM512; if (id == "NTN") ret = EEPROM512; if (id == "NBN") ret = EEPROM512; if (id == "NBK") ret = EEPROM512; if (id == "NFH") ret = EEPROM512; if (id == "NMU") ret = EEPROM512; if (id == "NBC") ret = EEPROM512; if (id == "NBH") ret = EEPROM512; if (id == "NHA") ret = EEPROM512; if (id == "NBM") ret = EEPROM512; if (id == "NBV") ret = EEPROM512; if (id == "NBD") ret = EEPROM512; if (id == "NCT") ret = EEPROM512; if (id == "NCH") ret = EEPROM512; if (id == "NCG") ret = EEPROM512; if (id == "NP2") ret = EEPROM512; if (id == "NXO") ret = EEPROM512; if (id == "NCU") ret = EEPROM512; if (id == "NCX") ret = EEPROM512; if (id == "NDY") ret = EEPROM512; if (id == "NDQ") ret = EEPROM512; if (id == "NDR") ret = EEPROM512; if (id == "NN6") ret = EEPROM512; if (id == "NDU") ret = EEPROM512; if (id == "NJM") ret = EEPROM512; if (id == "NFW") ret = EEPROM512; if (id == "NF2") ret = EEPROM512; if (id == "NKA") ret = EEPROM512; if (id == "NFG") ret = EEPROM512; if (id == "NGL") ret = EEPROM512; if (id == "NGV") ret = EEPROM512; if (id == "NGE") ret = EEPROM512; if (id == "NHP") ret = EEPROM512; if (id == "NPG") ret = EEPROM512; if (id == "NIJ") ret = EEPROM512; if (id == "NIC") ret = EEPROM512; if (id == "NFY") ret = EEPROM512; if (id == "NKI") ret = EEPROM512; if (id == "NLL") ret = EEPROM512; if (id == "NLR") ret = EEPROM512; if (id == "NKT") ret = EEPROM512; if (id == "CLB") ret = EEPROM512; if (id == "NLB") ret = EEPROM512; if (id == "NMW") ret = EEPROM512; if (id == "NML") ret = EEPROM512; if (id == "NTM") ret = EEPROM512; if (id == "NMI") ret = EEPROM512; if (id == "NMG") ret = EEPROM512; if (id == "NMO") ret = EEPROM512; if (id == "NMS") ret = EEPROM512; if (id == "NMR") ret = EEPROM512; if (id == "NCR") ret = EEPROM512; if (id == "NEA") ret = EEPROM512; if (id == "NPW") ret = EEPROM512; if (id == "NPM") ret = EEPROM512; if (id == "NPY") ret = EEPROM512; if (id == "NPT") ret = EEPROM512; if (id == "NRA") ret = EEPROM512; if (id == "NWQ") ret = EEPROM512; if (id == "NSU") ret = EEPROM512; if (id == "NSN") ret = EEPROM512; if (id == "NK2") ret = EEPROM512; if (id == "NSV") ret = EEPROM512; if (id == "NFX") ret = EEPROM512; if (id == "NFP") ret = EEPROM512; if (id == "NS6") ret = EEPROM512; if (id == "NNA") ret = EEPROM512; if (id == "NRS") ret = EEPROM512; if (id == "NSW") ret = EEPROM512; if (id == "NSC") ret = EEPROM512; if (id == "NSA") ret = EEPROM512; if (id == "NB6") ret = EEPROM512; if (id == "NSM") ret = EEPROM512; if (id == "NSS") ret = EEPROM512; if (id == "NTX") ret = EEPROM512; if (id == "NT6") ret = EEPROM512; if (id == "NTP") ret = EEPROM512; if (id == "NTJ") ret = EEPROM512; if (id == "NRC") ret = EEPROM512; if (id == "NTR") ret = EEPROM512; if (id == "NTB") ret = EEPROM512; if (id == "NGU") ret = EEPROM512; if (id == "NIR") ret = EEPROM512; if (id == "NVL") ret = EEPROM512; if (id == "NVY") ret = EEPROM512; if (id == "NWR") ret = EEPROM512; if (id == "NWC") ret = EEPROM512; if (id == "NAD") ret = EEPROM512; if (id == "NWU") ret = EEPROM512; if (id == "NYK") ret = EEPROM512; if (id == "NMZ") ret = EEPROM512; if (id == "NDK" && region_code == 'J') ret = EEPROM512; if (id == "NWT" && region_code == 'J') ret = EEPROM512; if (id == "NB7") ret = EEPROM2KB; if (id == "NGT") ret = EEPROM2KB; if (id == "NFU") ret = EEPROM2KB; if (id == "NCW") ret = EEPROM2KB; if (id == "NCZ") ret = EEPROM2KB; if (id == "ND6") ret = EEPROM2KB; if (id == "NDO") ret = EEPROM2KB; if (id == "ND2") ret = EEPROM2KB; if (id == "N3D") ret = EEPROM2KB; if (id == "NMX") ret = EEPROM2KB; if (id == "NGC") ret = EEPROM2KB; if (id == "NIM") ret = EEPROM2KB; if (id == "NK4") ret = EEPROM2KB; if (id == "NNB") ret = EEPROM2KB; if (id == "NMV") ret = EEPROM2KB; if (id == "NM8") ret = EEPROM2KB; if (id == "NEV") ret = EEPROM2KB; if (id == "NPP") ret = EEPROM2KB; if (id == "NUB") ret = EEPROM2KB; if (id == "NPD") ret = EEPROM2KB; if (id == "NRZ") ret = EEPROM2KB; if (id == "NR7") ret = EEPROM2KB; if (id == "NEP") ret = EEPROM2KB; if (id == "NYS") ret = EEPROM2KB; if (id == "ND3" && region_code == 'J') ret = EEPROM2KB; if (id == "ND4" && region_code == 'J') ret = EEPROM2KB; if (id == "NTE") ret = SRAM32KB; if (id == "NVB") ret = SRAM32KB; if (id == "CFZ") ret = SRAM32KB; if (id == "NFZ") ret = SRAM32KB; if (id == "NSI") ret = SRAM32KB; if (id == "NG6") ret = SRAM32KB; if (id == "N3H") ret = SRAM32KB; if (id == "NGP") ret = SRAM32KB; if (id == "NYW") ret = SRAM32KB; if (id == "NHY") ret = SRAM32KB; if (id == "NIB") ret = SRAM32KB; if (id == "NPS") ret = SRAM32KB; if (id == "NPA") ret = SRAM32KB; if (id == "NP4") ret = SRAM32KB; if (id == "NJ5") ret = SRAM32KB; if (id == "NP6") ret = SRAM32KB; if (id == "NPE") ret = SRAM32KB; if (id == "NJG") ret = SRAM32KB; if (id == "CZL") ret = SRAM32KB; if (id == "NZL") ret = SRAM32KB; if (id == "NKG") ret = SRAM32KB; if (id == "NMF") ret = SRAM32KB; if (id == "NRI") ret = SRAM32KB; if (id == "NUT") ret = SRAM32KB; if (id == "NUM") ret = SRAM32KB; if (id == "NOB") ret = SRAM32KB; if (id == "CPS") ret = SRAM32KB; if (id == "NB5") ret = SRAM32KB; if (id == "NRE") ret = SRAM32KB; if (id == "NAL") ret = SRAM32KB; if (id == "NT3") ret = SRAM32KB; if (id == "NS4") ret = SRAM32KB; if (id == "NA2") ret = SRAM32KB; if (id == "NVP") ret = SRAM32KB; if (id == "NWL") ret = SRAM32KB; if (id == "NW2") ret = SRAM32KB; if (id == "NWX") ret = SRAM32KB; if (id == "NK4" && region_code == 'J' && revision < 2) ret = SRAM32KB; if (id == "CDZ") ret = SRAM96KB; if (id == "NCC") ret = FLASH128KB; if (id == "NDA") ret = FLASH128KB; if (id == "NAF") ret = FLASH128KB; if (id == "NJF") ret = FLASH128KB; if (id == "NKJ") ret = FLASH128KB; if (id == "NZS") ret = FLASH128KB; if (id == "NM6") ret = FLASH128KB; if (id == "NCK") ret = FLASH128KB; if (id == "NMQ") ret = FLASH128KB; if (id == "NPN") ret = FLASH128KB; if (id == "NPF") ret = FLASH128KB; if (id == "NPO") ret = FLASH128KB; if (id == "CP2") ret = FLASH128KB; if (id == "NP3") ret = FLASH128KB; if (id == "NRH") ret = FLASH128KB; if (id == "NSQ") ret = FLASH128KB; if (id == "NT9") ret = FLASH128KB; if (id == "NW4") ret = FLASH128KB; if (id == "NDP") ret = FLASH128KB; if (id[1] == 'E' && id[2] == 'D') { n8 config = revision; if (config.bit(4,7) == 1) ret = EEPROM512; else if (config.bit(4,7) == 2) ret = EEPROM2KB; else if (config.bit(4,7) == 3) ret = SRAM32KB; else if (config.bit(4,7) == 4) ret = SRAM96KB; else if (config.bit(4,7) == 5) ret = FLASH128KB; else if (config.bit(4,7) == 6) ret = SRAM128KB; } return ret; } static inline bool DetectRtc(u8* rom) { string id; id.append((char)rom[0x3B]); id.append((char)rom[0x3C]); id.append((char)rom[0x3D]); u8 revision = rom[0x3f]; if (id == "NAF") return true; if (id[1] == 'E' && id[2] == 'D') { n8 config = revision; return config.bit(0) == 1; } return false; } namespace ares::Nintendo64 { extern bool BobDeinterlace; extern bool FastVI; } typedef struct { u8* GbRomData; u64 GbRomLen; } GbRom; typedef struct { u8* PifData; u64 PifLen; u8* IplData; u64 IplLen; u8* RomData; u64 RomLen; u8* DiskData; u64 DiskLen; u8* DiskErrorData; u64 DiskErrorLen; GbRom GbRoms[4]; } LoadData; static bool LoadRom(LoadData* loadData, bool isPal) { u8* data; u32 len; string name; name = "program.rom"; len = loadData->RomLen; data = new u8[len]; memcpy(data, loadData->RomData, len); romData = new array_view(data, len); platform->bizpak->append(name, *romData); string cic = isPal ? "CIC-NUS-7101" : "CIC-NUS-6102"; u32 crc32 = Hash::CRC32({&data[0x40], 0x9C0}).value(); if (crc32 == 0x1DEB51A9) cic = "CIC-NUS-6101"; if (crc32 == 0xEC8B1325) cic = "CIC-NUS-7102"; if (crc32 == 0xC08E5BD6) cic = isPal ? "CIC-NUS-7101" : "CIC-NUS-6102"; if (crc32 == 0x03B8376A) cic = isPal ? "CIC-NUS-7103" : "CIC-NUS-6103"; if (crc32 == 0xCF7F41DC) cic = isPal ? "CIC-NUS-7105" : "CIC-NUS-6105"; if (crc32 == 0xD1059C6A) cic = isPal ? "CIC-NUS-7106" : "CIC-NUS-6106"; if (crc32 == 0x0C965795) cic = "CIC-NUS-8303"; if (crc32 == 0x10C68B18) cic = "CIC-NUS-8401"; if (crc32 == 0x8FEBA21E) cic = "CIC-NUS-DDUS"; platform->bizpak->setAttribute("cic", cic); SaveType save = DetectSaveType(data); if (save != NONE) { switch (save) { case EEPROM512: len = 512; name = "save.eeprom"; break; case EEPROM2KB: len = 2 * 1024; name = "save.eeprom"; break; case SRAM32KB: len = 32 * 1024; name = "save.ram"; break; case SRAM96KB: len = 96 * 1024; name = "save.ram"; break; case SRAM128KB: len = 128 * 1024; name = "save.ram"; break; case FLASH128KB: len = 128 * 1024; name = "save.flash"; break; default: return false; } data = new u8[len]; memset(data, 0xFF, len); saveData = new array_view(data, len); platform->bizpak->append(name, *saveData); } if (DetectRtc(data)) { len = 32, name = "save.rtc"; data = new u8[len]; memset(data, 0xFF, len); rtcData = new array_view(data, len); platform->bizpak->append(name, *rtcData); } if (auto port = root->find("Cartridge Slot")) { port->allocate(); port->connect(); } else { return false; } return true; } static bool LoadDisk(LoadData* loadData) { u8* data; u32 len; string name; name = "program.disk"; len = loadData->DiskLen; data = new u8[len]; memcpy(data, loadData->DiskData, len); diskData = new array_view(data, len); platform->bizpak->append(name, *diskData); name = "program.disk.error"; len = loadData->DiskErrorLen; data = new u8[len]; memcpy(data, loadData->DiskErrorData, len); diskErrorData = new array_view(data, len); platform->bizpak->append(name, *diskErrorData); if (auto port = root->find("Nintendo 64DD/Disk Drive")) { port->allocate(); port->connect(); } else { return false; } return true; } namespace angrylion { extern u32 * OutFrameBuffer; extern u32 OutHeight; } ECL_EXPORT bool Init(LoadData* loadData, ControllerType* controllers, bool isPal, u64 initTime) { platform = new BizPlatform; platform->bizpak = new vfs::directory; ares::platform = platform; platform->biztime = initTime; angrylion::OutFrameBuffer = NULL; angrylion::OutHeight = isPal ? 576 : 480; u8* data; u32 len; string name; name = isPal ? "pif.pal.rom" : "pif.ntsc.rom"; len = loadData->PifLen; data = new u8[len]; memcpy(data, loadData->PifData, len); pifData = new array_view(data, len); platform->bizpak->append(name, *pifData); // needs to be loaded before ares::Nintendo64::load if (loadData->IplData) { name = "64dd.ipl.rom"; len = loadData->IplLen; data = new u8[len]; memcpy(data, loadData->IplData, len); iplData = new array_view(data, len); platform->bizpak->append(name, *iplData); } string region = isPal ? "PAL" : "NTSC"; platform->bizpak->setAttribute("region", region); name = {"[Nintendo] Nintendo 64 (", region, ")"}; if (loadData->DiskData) name = "[Nintendo] Nintendo 64DD (NTSC-J)"; // todo: handle this better (name doesn't really matter at this point) if (!ares::Nintendo64::load(root, name)) { return false; } if (loadData->RomData) { if (!LoadRom(loadData, isPal)) { return false; } } if (loadData->DiskData) { if (!LoadDisk(loadData)) { return false; } } for (int i = 0; i < 4; i++) { if (loadData->GbRoms[i].GbRomData) { len = loadData->GbRoms[i].GbRomLen; data = new u8[len]; memcpy(data, loadData->GbRoms[i].GbRomData, len); gbRomData[i] = new array_view(data, len); } } for (int i = 0, j = 0; i < 4; i++) { if (auto port = root->find({"Controller Port ", 1 + i})) { if (controllers[i] == Unplugged) continue; if (controllers[i] == Mouse) { port->allocate("Mouse"); port->connect(); continue; } if (controllers[i] == Transferpak) { if (gbRomData[j]) { platform->bizpak->remove("gbrom.pak"); platform->bizpak->append("gbrom.pak", *gbRomData[j++]); } } auto peripheral = port->allocate("Gamepad"); port->connect(); switch (controllers[i]) { case Mempak: name = "Controller Pak"; break; case Rumblepak: name = "Rumble Pak"; break; case Transferpak: name = "Transfer Pak"; break; default: continue; } if (auto port = peripheral->find("Pak")) { port->allocate(name); port->connect(); } else { return false; } } else { return false; } } root->power(false); root->run(); // HACK, first frame dirties a ton of memory, so we emulate it then seal (this should be investigated, not sure why 60MBish of memory would be dirtied in a single frame?) return true; } // todo: might need to account for mbc5 rumble? // largely pointless tho ECL_EXPORT bool GetRumbleStatus(u32 num) { ares::Nintendo64::Gamepad* c = nullptr; switch (num) { case 0: c = dynamic_cast(ares::Nintendo64::controllerPort1.device.data()); break; case 1: c = dynamic_cast(ares::Nintendo64::controllerPort2.device.data()); break; case 2: c = dynamic_cast(ares::Nintendo64::controllerPort3.device.data()); break; case 3: c = dynamic_cast(ares::Nintendo64::controllerPort4.device.data()); break; } return c ? c->motor->enable() : false; } #define ADD_MEMORY_DOMAIN(mem, name, flags) do { \ m[i].Data = ares::Nintendo64::mem.data; \ m[i].Name = name; \ m[i].Size = ares::Nintendo64::mem.size; \ m[i].Flags = flags | MEMORYAREA_FLAGS_YUGEENDIAN | MEMORYAREA_FLAGS_SWAPPED | MEMORYAREA_FLAGS_WORDSIZE4 | MEMORYAREA_FLAGS_WRITABLE; \ i++; \ } while (0) #define ADD_MEMPAK_DOMAIN(NUM) do { \ if (auto c = dynamic_cast(ares::Nintendo64::controllerPort##NUM.device.data())) \ { \ m[i].Data = c->ram.data; \ m[i].Name = "MEMPAK " #NUM; \ m[i].Size = c->ram.size; \ m[i].Flags = MEMORYAREA_FLAGS_ONEFILLED | MEMORYAREA_FLAGS_SAVERAMMABLE | MEMORYAREA_FLAGS_YUGEENDIAN | MEMORYAREA_FLAGS_SWAPPED | MEMORYAREA_FLAGS_WORDSIZE4 | MEMORYAREA_FLAGS_WRITABLE; \ i++; \ } \ } while (0) #define ADD_GB_DOMAINS(NUM) do { \ if (auto c = dynamic_cast(ares::Nintendo64::controllerPort##NUM.device.data())) \ { \ m[i].Data = c->transferPak.rom.data; \ m[i].Name = "GB ROM " #NUM; \ m[i].Size = c->transferPak.rom.size; \ m[i].Flags = MEMORYAREA_FLAGS_YUGEENDIAN | MEMORYAREA_FLAGS_SWAPPED | MEMORYAREA_FLAGS_WORDSIZE4 | MEMORYAREA_FLAGS_WRITABLE; \ i++; \ \ m[i].Data = c->transferPak.ram.data; \ m[i].Name = "GB SRAM " #NUM; \ m[i].Size = c->transferPak.ram.size; \ m[i].Flags = MEMORYAREA_FLAGS_ONEFILLED | MEMORYAREA_FLAGS_SAVERAMMABLE | MEMORYAREA_FLAGS_YUGEENDIAN | MEMORYAREA_FLAGS_SWAPPED | MEMORYAREA_FLAGS_WORDSIZE4 | MEMORYAREA_FLAGS_WRITABLE; \ i++; \ } \ } while (0) static inline u8 GetByteFromWord(u32 word, u32 addr) { switch (addr & 3) { case 0: return (word >> 24) & 0xFF; case 1: return (word >> 16) & 0xFF; case 2: return (word >> 8) & 0xFF; case 3: return (word >> 0) & 0xFF; default: __builtin_unreachable(); } } static u8 PeekFunc(u64 address) { address &= 0x1fff'ffff; const u32 addr = address; if (addr > 0x0403'ffff && addr <= 0x0407'ffff) // RSP { address = (address & 0x3ffff) >> 2; if (address == 7) // SP_SEMAPHORE { return GetByteFromWord(ares::Nintendo64::rsp.status.semaphore & 1, addr); } } else if (addr > 0x0407'ffff && addr <= 0x040f'ffff) // RSP Status { address = (address & 0x7ffff) >> 2; if (address == 0) // SP_PC_REG { return GetByteFromWord(ares::Nintendo64::rsp.ipu.pc & 0xFFF, addr); } } else if (addr > 0x046f'ffff && addr <= 0x047f'ffff) // RI { address = (address & 0xfffff) >> 2; if (address == 3) // RI_SELECT { return GetByteFromWord(ares::Nintendo64::ri.io.select, addr); } } u32 unused = 0; return ares::Nintendo64::bus.read(addr, unused); } static void SysBusAccess(u8* buffer, u64 address, u64 count, bool write) { if (write) { u32 unused = 0; while (count--) ares::Nintendo64::bus.write(address++, *buffer++, unused); } else { while (count--) *buffer++ = PeekFunc(address++); } } ECL_EXPORT void GetMemoryAreas(MemoryArea *m) { int i = 0; ADD_MEMORY_DOMAIN(rdram.ram, "RDRAM", MEMORYAREA_FLAGS_PRIMARY); ADD_MEMORY_DOMAIN(cartridge.rom, "ROM", 0); ADD_MEMORY_DOMAIN(pif.rom, "PIF ROM", 0); ADD_MEMORY_DOMAIN(pif.ram, "PIF RAM", 0); ADD_MEMORY_DOMAIN(rsp.dmem, "RSP DMEM", 0); ADD_MEMORY_DOMAIN(rsp.imem, "RSP IMEM", 0); ADD_MEMORY_DOMAIN(cartridge.ram, "SRAM", MEMORYAREA_FLAGS_ONEFILLED | MEMORYAREA_FLAGS_SAVERAMMABLE); ADD_MEMORY_DOMAIN(cartridge.eeprom, "EEPROM", MEMORYAREA_FLAGS_ONEFILLED | MEMORYAREA_FLAGS_SAVERAMMABLE); ADD_MEMORY_DOMAIN(cartridge.flash, "FLASH", MEMORYAREA_FLAGS_ONEFILLED | MEMORYAREA_FLAGS_SAVERAMMABLE); ADD_MEMPAK_DOMAIN(1); ADD_MEMPAK_DOMAIN(2); ADD_MEMPAK_DOMAIN(3); ADD_MEMPAK_DOMAIN(4); ADD_GB_DOMAINS(1); ADD_GB_DOMAINS(2); ADD_GB_DOMAINS(3); ADD_GB_DOMAINS(4); m[i].Data = (void*)SysBusAccess; m[i].Name = "System Bus"; m[i].Size = 1ull << 32; m[i].Flags = MEMORYAREA_FLAGS_YUGEENDIAN | MEMORYAREA_FLAGS_SWAPPED | MEMORYAREA_FLAGS_WORDSIZE4 | MEMORYAREA_FLAGS_WRITABLE | MEMORYAREA_FLAGS_FUNCTIONHOOK; } struct MyFrameInfo : public FrameInfo { u64 Time; Buttons_t P1Buttons; Buttons_t P2Buttons; Buttons_t P3Buttons; Buttons_t P4Buttons; s16 P1XAxis; s16 P1YAxis; s16 P2XAxis; s16 P2YAxis; s16 P3XAxis; s16 P3YAxis; s16 P4XAxis; s16 P4YAxis; bool Reset; bool Power; bool BobDeinterlace; bool FastVI; bool SkipDraw; }; #define UPDATE_CONTROLLER(NUM) do { \ if (auto c = dynamic_cast(ares::Nintendo64::controllerPort##NUM.device.data())) \ { \ c->x->setValue(f->P##NUM##XAxis); \ c->y->setValue(f->P##NUM##YAxis); \ c->up->setValue(f->P##NUM##Buttons & UP); \ c->down->setValue(f->P##NUM##Buttons & DOWN); \ c->left->setValue(f->P##NUM##Buttons & LEFT); \ c->right->setValue(f->P##NUM##Buttons & RIGHT); \ c->b->setValue(f->P##NUM##Buttons & B); \ c->a->setValue(f->P##NUM##Buttons & A); \ c->cameraUp->setValue(f->P##NUM##Buttons & C_UP); \ c->cameraDown->setValue(f->P##NUM##Buttons & C_DOWN); \ c->cameraLeft->setValue(f->P##NUM##Buttons & C_LEFT); \ c->cameraRight->setValue(f->P##NUM##Buttons & C_RIGHT); \ c->l->setValue(f->P##NUM##Buttons & L); \ c->r->setValue(f->P##NUM##Buttons & R); \ c->z->setValue(f->P##NUM##Buttons & Z); \ c->start->setValue(f->P##NUM##Buttons & START); \ } \ else if (auto m = dynamic_cast(ares::Nintendo64::controllerPort##NUM.device.data())) \ { \ m->x->setValue(f->P##NUM##XAxis); \ m->y->setValue(f->P##NUM##YAxis); \ m->rclick->setValue(f->P##NUM##Buttons & B); \ m->lclick->setValue(f->P##NUM##Buttons & A); \ } \ } while (0) ECL_EXPORT void FrameAdvance(MyFrameInfo* f) { ares::Nintendo64::BobDeinterlace = f->BobDeinterlace; ares::Nintendo64::FastVI = f->FastVI; angrylion::OutFrameBuffer = f->SkipDraw ? NULL : f->VideoBuffer; platform->biztime = f->Time; if (f->Power) { root->power(false); } else if (f->Reset) { root->power(true); } UPDATE_CONTROLLER(1); UPDATE_CONTROLLER(2); UPDATE_CONTROLLER(3); UPDATE_CONTROLLER(4); platform->lagged = true; platform->nsamps = 0; root->run(); f->Width = 640; f->Height = angrylion::OutHeight; f->Samples = platform->nsamps; memcpy(f->SoundBuffer, platform->soundbuf, f->Samples * 4); f->Lagged = platform->lagged; } ECL_EXPORT void SetInputCallback(void (*callback)()) { platform->inputcb = callback; } ECL_EXPORT void PostLoadState() { // fixme: make it so we can actually use this approach (there's various invalidation problems with the recompiler atm) #if false ares::Nintendo64::cpu.recompiler.allocator.release(bump_allocator::zero_fill); ares::Nintendo64::cpu.recompiler.reset(); ares::Nintendo64::rsp.recompiler.allocator.release(bump_allocator::zero_fill); ares::Nintendo64::rsp.recompiler.reset(); #endif } ECL_EXPORT void GetDisassembly(u32 address, u32 instruction, char* buf) { auto s = ares::Nintendo64::cpu.disassembler.disassemble(address, instruction).strip(); strcpy(buf, s.data()); } ECL_EXPORT void GetRegisters(u64* buf) { for (int i = 0; i < 32; i++) { buf[i] = ares::Nintendo64::cpu.ipu.r[i].u64; } buf[32] = ares::Nintendo64::cpu.ipu.lo.u64; buf[33] = ares::Nintendo64::cpu.ipu.hi.u64; buf[34] = ares::Nintendo64::cpu.ipu.pc; }