diff --git a/ares/n64/dd/controller.cpp b/ares/n64/dd/controller.cpp index aaa6384f4d..25558b5c35 100644 --- a/ares/n64/dd/controller.cpp +++ b/ares/n64/dd/controller.cpp @@ -187,7 +187,11 @@ auto DD::command(n16 command) -> void { auto DD::mechaResponse() -> void { if(state.seek) { state.seek = 0; - motorActive(); + if (io.status.diskPresent) { + motorActive(); + } else { + motorStop(); + } } io.status.busyState = 0; raise(IRQ::MECHA); diff --git a/ares/n64/dd/dd.cpp b/ares/n64/dd/dd.cpp index 3971c7045e..5c0152ec0e 100644 --- a/ares/n64/dd/dd.cpp +++ b/ares/n64/dd/dd.cpp @@ -31,6 +31,8 @@ auto DD::load(Node::Object parent) -> void { iplrom.load(fp); } + rtcLoad(); + debugger.load(parent->append("Nintendo 64DD")); } @@ -79,9 +81,9 @@ auto DD::connect() -> void { if(auto fp = pak->read("program.disk")) { disk.allocate(fp->size()); disk.load(fp); + io.status.diskChanged = 1; + io.status.diskPresent = 1; } - - rtcLoad(); } auto DD::disconnect() -> void { @@ -89,7 +91,35 @@ auto DD::disconnect() -> void { save(); pak.reset(); + disk.reset(); information = {}; + + if(iplrom) { + string id; + id.append((char)iplrom.read(0x3b)); + id.append((char)iplrom.read(0x3c)); + id.append((char)iplrom.read(0x3d)); + id.append((char)iplrom.read(0x3e)); + if(id.match("NDDJ")) dd.information.cic = "CIC-NUS-8303"; + if(id.match("NDDE")) dd.information.cic = "CIC-NUS-DDUS"; + if(id.match("NDXJ")) dd.information.cic = "CIC-NUS-8401"; + } + + io.status.diskChanged = 0; + io.status.diskPresent = 0; + + //Deal with cases when the disk is removed while in use + if(io.status.busyState) { + //MECHA + io.status.mechaError = 1; + } + + if(io.bm.start) { + //BM + io.bm.start = 0; + io.bm.error = 1; + } + motorStop(); } auto DD::save() -> void { @@ -112,6 +142,7 @@ auto DD::power(bool reset) -> void { state = {}; io.status.resetState = 1; + if(disk) io.status.diskPresent = 1; io.id = 3; if(dd.information.cic.match("CIC-NUS-8401")) io.id = 4; diff --git a/ares/n64/dd/dd.hpp b/ares/n64/dd/dd.hpp index d6b9f2a7e8..e5d0618061 100644 --- a/ares/n64/dd/dd.hpp +++ b/ares/n64/dd/dd.hpp @@ -125,6 +125,7 @@ struct DD : Memory::PI
{ n1 writeProtect; n1 mechaError; n1 diskChanged; + n1 diskPresent; } status; n16 currentTrack; diff --git a/ares/n64/dd/io.cpp b/ares/n64/dd/io.cpp index a85269edd9..9716ee25ef 100644 --- a/ares/n64/dd/io.cpp +++ b/ares/n64/dd/io.cpp @@ -20,7 +20,7 @@ auto DD::readHalf(u32 address) -> u16 { data.bit(4) = io.status.spindleMotorStopped; data.bit(6) = io.status.resetState; data.bit(7) = io.status.busyState; - data.bit(8) = (bool)disk; //disk present + data.bit(8) = io.status.diskPresent; data.bit(9) = irq.mecha.line; data.bit(10) = irq.bm.line; data.bit(11) = io.bm.error; @@ -57,7 +57,7 @@ auto DD::readHalf(u32 address) -> u16 { data.bit(0,7) = io.error.sector; data.bit(8) = io.error.selfStop; data.bit(9) = io.error.clockUnlock; - data.bit(10) = ~(bool)disk; //no disk + data.bit(10) = ~io.status.diskPresent; //no disk data.bit(11) = io.error.offTrack; data.bit(12) = io.error.overrun; data.bit(13) = io.error.spindle; diff --git a/desktop-ui/emulator/nintendo-64.cpp b/desktop-ui/emulator/nintendo-64.cpp index 4f19676f20..3b94aa7711 100644 --- a/desktop-ui/emulator/nintendo-64.cpp +++ b/desktop-ui/emulator/nintendo-64.cpp @@ -1,6 +1,7 @@ struct Nintendo64 : Emulator { Nintendo64(); auto load() -> bool override; + auto load(Menu) -> void override; auto save() -> bool override; auto pak(ares::Node::Object) -> shared_pointer override; @@ -8,6 +9,7 @@ struct Nintendo64 : Emulator { shared_pointer disk; shared_pointer gb; u32 regionID = 0; + Timer diskInsertTimer; }; Nintendo64::Nintendo64() { @@ -162,6 +164,30 @@ auto Nintendo64::load() -> bool { return true; } +auto Nintendo64::load(Menu menu) -> void { + if(disk) { + MenuItem changeDisk{&menu}; + changeDisk.setIcon(Icon::Device::Optical); + changeDisk.setText("Change Disk").onActivate([&] { + save(); + auto drive = root->find("Nintendo 64DD/Disk Drive"); + drive->disconnect(); + + if(!disk->load(Emulator::load(disk, configuration.game))) { + return; + } + + //give the emulator core a few seconds to notice an empty drive state before reconnecting + diskInsertTimer.onActivate([&] { + diskInsertTimer.setEnabled(false); + auto drive = root->find("Nintendo 64DD/Disk Drive"); + drive->allocate(); + drive->connect(); + }).setInterval(3000).setEnabled(); + }); + } +} + auto Nintendo64::save() -> bool { root->save(); system->save(system->location); diff --git a/desktop-ui/emulator/nintendo-64dd.cpp b/desktop-ui/emulator/nintendo-64dd.cpp index 476acd05f8..f668353066 100644 --- a/desktop-ui/emulator/nintendo-64dd.cpp +++ b/desktop-ui/emulator/nintendo-64dd.cpp @@ -1,11 +1,13 @@ struct Nintendo64DD : Emulator { Nintendo64DD(); auto load() -> bool override; + auto load(Menu) -> void override; auto save() -> bool override; auto pak(ares::Node::Object) -> shared_pointer override; shared_pointer gamepad; u32 regionID = 0; + Timer diskInsertTimer; }; Nintendo64DD::Nintendo64DD() { @@ -109,6 +111,28 @@ auto Nintendo64DD::load() -> bool { return true; } +auto Nintendo64DD::load(Menu menu) -> void { + MenuItem changeDisk{&menu}; + changeDisk.setIcon(Icon::Device::Optical); + changeDisk.setText("Change Disk").onActivate([&] { + save(); + auto drive = root->find("Nintendo 64DD/Disk Drive"); + drive->disconnect(); + + if(!game->load(Emulator::load(game, configuration.game))) { + return; + } + + //give the emulator core a few seconds to notice an empty drive state before reconnecting + diskInsertTimer.onActivate([&] { + diskInsertTimer.setEnabled(false); + auto drive = root->find("Nintendo 64DD/Disk Drive"); + drive->allocate(); + drive->connect(); + }).setInterval(3000).setEnabled(); + }); +} + auto Nintendo64DD::save() -> bool { root->save(); system->save(system->location); diff --git a/mia/medium/nintendo-64dd.cpp b/mia/medium/nintendo-64dd.cpp index f49117441a..1906a6db4d 100644 --- a/mia/medium/nintendo-64dd.cpp +++ b/mia/medium/nintendo-64dd.cpp @@ -516,5 +516,42 @@ auto Nintendo64DD::transform(array_view input, vector errorTable) -> vec } } + //add new timestamp to differenciate every disk file for swapping + { + //get current time + chrono::timeinfo timeinfo = chrono::local::timeinfo(); + + u8 rng = random(); + u8 second = BCD::encode(min(59, timeinfo.second)); + u8 minute = BCD::encode(timeinfo.minute); + u8 hour = BCD::encode(timeinfo.hour); + u8 day = BCD::encode(timeinfo.day); + u8 month = BCD::encode(1 + timeinfo.month); + u8 yearlo = BCD::encode(timeinfo.year % 100); + u8 yearhi = BCD::encode(timeinfo.year / 100); + + u32 diskIdBlocks[2] = {14, 15}; + for(u32 n : range(2)) { + u32 offsetCalc = diskIdBlocks[n] * blockSizeTable[0]; + //change disk id to all sectors + for(u32 m : range(85)) { + //change manufacture line info (0x08 to 0x0F) (is normally a BCD number but officially "PTN64" was also used there) + output[offsetCalc + (m * (blockSizeTable[0] / 85)) + 0x0B] = rng; + output[offsetCalc + (m * (blockSizeTable[0] / 85)) + 0x0C] = 0x41; //'A' + output[offsetCalc + (m * (blockSizeTable[0] / 85)) + 0x0D] = 0x52; //'R' + output[offsetCalc + (m * (blockSizeTable[0] / 85)) + 0x0E] = 0x45; //'E' + output[offsetCalc + (m * (blockSizeTable[0] / 85)) + 0x0F] = 0x53; //'S' + //change disk manufacture time (0x10 to 0x17) to now + output[offsetCalc + (m * (blockSizeTable[0] / 85)) + 0x11] = yearhi; + output[offsetCalc + (m * (blockSizeTable[0] / 85)) + 0x12] = yearlo; + output[offsetCalc + (m * (blockSizeTable[0] / 85)) + 0x13] = month; + output[offsetCalc + (m * (blockSizeTable[0] / 85)) + 0x14] = day; + output[offsetCalc + (m * (blockSizeTable[0] / 85)) + 0x15] = hour; + output[offsetCalc + (m * (blockSizeTable[0] / 85)) + 0x16] = minute; + output[offsetCalc + (m * (blockSizeTable[0] / 85)) + 0x17] = second; + } + } + } + return output; }