924 lines
25 KiB
C++
924 lines
25 KiB
C++
#include <n64/n64.hpp>
|
|
|
|
#include <emulibc.h>
|
|
#include <waterboxcore.h>
|
|
|
|
struct CallinFenvGuard {
|
|
nall::float_env saved_fenv;
|
|
nall::float_env& fenv;
|
|
|
|
CallinFenvGuard(float_env& fenv_) : fenv(fenv_)
|
|
{
|
|
if (fenv.getRound() != saved_fenv.getRound())
|
|
{
|
|
fenv.setRound(fenv.getRound());
|
|
}
|
|
}
|
|
|
|
~CallinFenvGuard()
|
|
{
|
|
if (fenv.getRound() != saved_fenv.getRound())
|
|
{
|
|
saved_fenv.setRound(saved_fenv.getRound());
|
|
}
|
|
}
|
|
};
|
|
|
|
struct CallbackFenvGuard {
|
|
nall::float_env& saved_fenv;
|
|
|
|
CallbackFenvGuard(float_env& saved_fenv_) : saved_fenv(saved_fenv_)
|
|
{
|
|
nall::float_env cur_fenv;
|
|
if (cur_fenv.getRound() != nall::float_env::toNearest)
|
|
{
|
|
cur_fenv.setRound(nall::float_env::toNearest);
|
|
}
|
|
}
|
|
|
|
~CallbackFenvGuard()
|
|
{
|
|
nall::float_env cur_fenv;
|
|
if (cur_fenv.getRound() != saved_fenv.getRound())
|
|
{
|
|
saved_fenv.setRound(saved_fenv.getRound());
|
|
}
|
|
}
|
|
};
|
|
|
|
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<u16>(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<ares::Node::Audio::Stream>())
|
|
{
|
|
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<ares::Node::Input::Button>())
|
|
{
|
|
if (input->name() == "Start" || input->name() == "Left Click")
|
|
{
|
|
lagged = false;
|
|
if (inputcb)
|
|
{
|
|
CallbackFenvGuard guard(ares::Nintendo64::cpu.fenv);
|
|
inputcb();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
auto BizPlatform::time() -> n64
|
|
{
|
|
return biztime;
|
|
}
|
|
|
|
static ares::Node::System root = nullptr;
|
|
static BizPlatform* platform = nullptr;
|
|
static array_view<u8>* pifData = nullptr;
|
|
static array_view<u8>* iplData = nullptr;
|
|
static array_view<u8>* romData = nullptr;
|
|
static array_view<u8>* diskData = nullptr;
|
|
static array_view<u8>* diskErrorData = nullptr;
|
|
static array_view<u8>* saveData = nullptr;
|
|
static array_view<u8>* rtcData = nullptr;
|
|
static array_view<u8>* 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 == "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 == "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 == "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 == "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 == "NSM") ret = EEPROM512;
|
|
if (id == "NWR") 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 == "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 == "NK4") 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 == "NB5") ret = SRAM32KB;
|
|
if (id == "CFZ") ret = SRAM32KB;
|
|
if (id == "NFZ") ret = SRAM32KB;
|
|
if (id == "NSI") ret = SRAM32KB;
|
|
if (id == "NG6") 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 == "NPM") 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 == "N3H" && region_code == 'J') 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<u8>(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<u8>(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<u8>(data, len);
|
|
platform->bizpak->append(name, *rtcData);
|
|
}
|
|
|
|
if (auto port = root->find<ares::Node::Port>("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<u8>(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<u8>(data, len);
|
|
platform->bizpak->append(name, *diskErrorData);
|
|
|
|
if (auto port = root->find<ares::Node::Port>("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)
|
|
{
|
|
CallinFenvGuard guard(ares::Nintendo64::cpu.fenv);
|
|
|
|
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<u8>(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<u8>(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<u8>(data, len);
|
|
}
|
|
}
|
|
|
|
for (int i = 0, j = 0; i < 4; i++)
|
|
{
|
|
if (auto port = root->find<ares::Node::Port>({"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<ares::Node::Port>("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::Gamepad*>(ares::Nintendo64::controllerPort1.device.data()); break;
|
|
case 1: c = dynamic_cast<ares::Nintendo64::Gamepad*>(ares::Nintendo64::controllerPort2.device.data()); break;
|
|
case 2: c = dynamic_cast<ares::Nintendo64::Gamepad*>(ares::Nintendo64::controllerPort3.device.data()); break;
|
|
case 3: c = dynamic_cast<ares::Nintendo64::Gamepad*>(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::Gamepad*>(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::Gamepad*>(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);
|
|
}
|
|
}
|
|
|
|
ares::Nintendo64::Thread unused;
|
|
return ares::Nintendo64::bus.read<ares::Nintendo64::Byte>(addr, unused, nullptr);
|
|
}
|
|
|
|
static void SysBusAccess(u8* buffer, u64 address, u64 count, bool write)
|
|
{
|
|
if (write)
|
|
{
|
|
ares::Nintendo64::Thread unused;
|
|
while (count--)
|
|
ares::Nintendo64::bus.write<ares::Nintendo64::Byte>(address++, *buffer++, unused, nullptr);
|
|
}
|
|
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::Gamepad*>(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::Mouse*>(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)
|
|
{
|
|
CallinFenvGuard guard(ares::Nintendo64::cpu.fenv);
|
|
|
|
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;
|
|
}
|