Vs. DualSystem
Moderator: Moderators
Vs. DualSystem
I'm trying to determine the plausibility of integrating Vs. DualSystem support with Netplay in my emulator. But I can't seem to get any of the DualSystem ROMs to function. From the wiki and other sources, I've collected the following information:
The DualSystem uses 2 CPUs, one configured as Master and the other Slave. That status can be determined by reading 4016.7 (0 = Slave, 1 = Master).
The CPUs share a common memory region (6000--67FF), though it can only be accessed by 1 CPU at a time. Access is coordinated via 4016.1 on both CPUs. Specifically, when Master 4016.1 drops from 1 to 0, that triggers an interrupt in Slave. Slave acknowledges that the interrupt handler completed by dropping its 4016.1 from 1 to 0. I think the cycle works something like:
1. Master and Slave both start out with 4016.1 set to 1.
2. Master reads and writes from shared memory.
3. Master 4016.1 drops from 1 to 0 indicating that it's done with shared memory.
4. That drop triggers an interrupt on Slave.
5. Slave's interrupt handler reads and writes from shared memory.
6. Slave 4016.1 drops from 1 to 0 indicating that it's done with shared memory.
7. Master sets 4016.1 to 1.
8. Slave responds to rise in Master's 4016.1 by setting its 4016.1 to 1.
9. Goto 1
I suspect, but I can't find documentation to back this up, that there is some external clock that causes interrupts in Master that drives that cycle. If that exists, can someone point me to some info on it.
Regarding the Vs. DualSystem ROMs. I'm looking at Balloon Fight (VS) [!].nes. PRG ROM and CHR ROM are twice the size as their counterparts in Balloon Fight (U) [!].nes. I didn't compare the bytes, but I assume the CHR ROMs are probably the same, but the code in PRG ROMs might be different, enabling 1 CPU to act as Master and the other Slave. But that could also be achieved through configuration; so, it might be exactly the same ROMs repeated twice in the file.
Anyway, I'd like to get VS Balloon Fight running in a single CPU (with some hacking of my emulator) if that's possible. Specifically, can I fool the Master into thinking that a Slave exists, when there is no actual Slave connected? In other words, what is the minimum things that I need to do to simulate a fake Slave?
Regarding Netplay, I need to know how much data is transferred in shared memory region and how often that region is accessed. E.g. can I transfer that data once-per-frame and keep the CPUs working? If I can simulate a fake Slave, then I can analyze how often shared memory is accessed and determine if Netplay is possible.
Any input is much appreciated. Thx.
The DualSystem uses 2 CPUs, one configured as Master and the other Slave. That status can be determined by reading 4016.7 (0 = Slave, 1 = Master).
The CPUs share a common memory region (6000--67FF), though it can only be accessed by 1 CPU at a time. Access is coordinated via 4016.1 on both CPUs. Specifically, when Master 4016.1 drops from 1 to 0, that triggers an interrupt in Slave. Slave acknowledges that the interrupt handler completed by dropping its 4016.1 from 1 to 0. I think the cycle works something like:
1. Master and Slave both start out with 4016.1 set to 1.
2. Master reads and writes from shared memory.
3. Master 4016.1 drops from 1 to 0 indicating that it's done with shared memory.
4. That drop triggers an interrupt on Slave.
5. Slave's interrupt handler reads and writes from shared memory.
6. Slave 4016.1 drops from 1 to 0 indicating that it's done with shared memory.
7. Master sets 4016.1 to 1.
8. Slave responds to rise in Master's 4016.1 by setting its 4016.1 to 1.
9. Goto 1
I suspect, but I can't find documentation to back this up, that there is some external clock that causes interrupts in Master that drives that cycle. If that exists, can someone point me to some info on it.
Regarding the Vs. DualSystem ROMs. I'm looking at Balloon Fight (VS) [!].nes. PRG ROM and CHR ROM are twice the size as their counterparts in Balloon Fight (U) [!].nes. I didn't compare the bytes, but I assume the CHR ROMs are probably the same, but the code in PRG ROMs might be different, enabling 1 CPU to act as Master and the other Slave. But that could also be achieved through configuration; so, it might be exactly the same ROMs repeated twice in the file.
Anyway, I'd like to get VS Balloon Fight running in a single CPU (with some hacking of my emulator) if that's possible. Specifically, can I fool the Master into thinking that a Slave exists, when there is no actual Slave connected? In other words, what is the minimum things that I need to do to simulate a fake Slave?
Regarding Netplay, I need to know how much data is transferred in shared memory region and how often that region is accessed. E.g. can I transfer that data once-per-frame and keep the CPUs working? If I can simulate a fake Slave, then I can analyze how often shared memory is accessed and determine if Netplay is possible.
Any input is much appreciated. Thx.
Re: Vs. DualSystem
Backwards... I know I helped Sour implement Vs. System recently, and he was having seriously problems with it backwards. The current text on the wiki was his suggested wording ... are you using an older snapshot of the wiki?zeroone wrote:That status can be determined by reading 4016.7 (0 = Slave, 1 = Master).
Nocash's EveryNES has it backwards.
2 KiB of RAM, but mirrored over the entire range from $6000-$7FFF.The CPUs share a common memory region (6000--67FF)
/IRQ is level triggered, not edge triggered. IRQ will re-start until the other CPU releases it.4016.1 drops from 1 to 0, that triggers an interrupt in Slave. Slave acknowledges that the interrupt handler completed by dropping its 4016.1 from 1 to 0.
The specifics of the protocol is game-specific, so unfortunately you're not going to get away with faking something. You simply must emulate the second CPU. You probably don't have to emulate the second PPU.Anyway, I'd like to get VS Balloon Fight running in a single CPU (with some hacking of my emulator) if that's possible. Specifically, can I fool the Master into thinking that a Slave exists, when there is no actual Slave connected? In other words, what is the minimum things that I need to do to simulate a fake Slave?
Re: Vs. DualSystem
Can we gather all the DualSystem information onto a dedicated Wiki page or a separate block within the existing VS page? It seems scattered. And I did use NoCash's info to fill in gaps, which apparently mixed me up.
If I knew a little more about what data is passed over shared memory, it sounds like I could fake the Slave. Of course, that's game specific.
@Sour please give me some hints!
Can you spell that out for me in terms of 4016.1 reads and writes? The wiki says that Master sets 4016.1 to 1 to gain shared memory access and when Master sets 4016.1 to 0, Slave gains shared memory access. When Master writes 0 to 4016.1, I assume that asserts the interrupt on Slave. And Slave writes 0 to 4016.1 to acknowledge that it's done with shared memory?lidnariq wrote:/IRQ is level triggered, not edge triggered. IRQ will re-start until the other CPU releases it.
If I knew a little more about what data is passed over shared memory, it sounds like I could fake the Slave. Of course, that's game specific.
@Sour please give me some hints!
Re: Vs. DualSystem
The VS schematic is available, if that helps. The shared RAM is in the upper-left corner.
https://archive.org/details/vsschematicmds-02-cpu
https://archive.org/details/vsschematicmds-02-cpu
Re: Vs. DualSystem
It's not exactly fresh in my mind, but Mesen actually runs both CPUs (and both PPUs) at once (running 1 full CPU instruction on one before switching to the other, which isn't perfectly accurate, but close enough).
Logic used on $4016 writes (pseudocode, see VsControlManager::UpdateSlaveMasterBit in Mesen if you want to see the actual code):
For netplay/savestates/movies, the state of the master & slave consoles are both saved together in the same data stream. For the "P1" and "P2" inputs of the slave console, I cheated and internally mapped them to the standard P3 and P4 logic, which allows netplay and movies to work without any Dualsystem-specific changes.
Hopefully this helps a bit? I can't really offer any advice on how to fake the slave CPU, though, since I just emulated everything and display both screens in the emulator.
Edit: RE: Netplay, Mesen emulates both CPUs on both machines when playing via netplay. It doesn't try to synchronize the shared RAM between both PCs - that is most likely not viable since the RAM is probably read/written to every frame (e.g to synchronize the position of players/monsters in Balloon fight, for example)
Logic used on $4016 writes (pseudocode, see VsControlManager::UpdateSlaveMasterBit in Mesen if you want to see the actual code):
Code: Select all
void UpdateSlaveMasterBit(uint8_t slaveMasterBit)
{
//slaveMasterBit is the value of bit 1 ($02) that was just written to $4016
CPU* otherCpu= _currentCpu->GetOtherCpu();
if(otherCpu != nullptr) {
if(_currentCpu->IsMasterCpu()) {
//Update memory access - this is only done when the master CPU writes to $4016
if(slaveMasterBit) {
//Set memory at $6000-$7FFF as readable/writable for master CPU
//Set memory at $6000-$7FFF as open bus for slave CPU
} else {
//Set memory at $6000-$7FFF as open bus for master CPU
//Set memory at $6000-$7FFF as readable/writable for slave CPU
}
}
if(slaveMasterBit) {
otherCpu->ClearIrqSource(IRQSource::External);
} else {
//When low, asserts /IRQ on the other CPU
otherCpu->SetIrqSource(IRQSource::External);
}
}
}
Hopefully this helps a bit? I can't really offer any advice on how to fake the slave CPU, though, since I just emulated everything and display both screens in the emulator.
Edit: RE: Netplay, Mesen emulates both CPUs on both machines when playing via netplay. It doesn't try to synchronize the shared RAM between both PCs - that is most likely not viable since the RAM is probably read/written to every frame (e.g to synchronize the position of players/monsters in Balloon fight, for example)
Last edited by Sour on Mon Dec 31, 2018 4:40 pm, edited 1 time in total.
Re: Vs. DualSystem
Does the Dualsystem PCB actually say "master" or "slave"?
-
- Posts: 1510
- Joined: Thu May 19, 2005 11:30 am
Re: Vs. DualSystem
There is also my conversion utility that will create canonical NES ROM files from MAME split ROMs.zeroone wrote:Regarding the Vs. DualSystem ROMs. I'm looking at Balloon Fight (VS) [!].nes
Fake certain responses in the shared ram at $6000. Doing so can only be done on a game-specific basis. Here is what I did before I added actual emulation of a second CPU to NintendulatorNRS:zeroone wrote:Anyway, I'd like to get VS Balloon Fight running in a single CPU (with some hacking of my emulator) if that's possible. Specifically, can I fool the Master into thinking that a Slave exists, when there is no actual Slave connected? In other words, what is the minimum things that I need to do to simulate a fake Slave?
Code: Select all
void MAPINT Write4 (int Bank, int Addr, int Val) {
if (Addr ==0x16) {
EMU->SetIRQ((Val &2)? 1: 0);
// Fake Vs. Dual System
if (~Val &2) switch (ROM->PRGROMCRC32) {
// Vs. Balloon fight
case 0x9213A19E: case 0xAD407F52: case 0x6AD67502:
switch((EMU->GetCPUReadHandler(6))(6, 0x000)) {
case 0x00: (EMU->GetCPUWriteHandler(6))(6, 0x000, 0x00); break;
case 0x55: (EMU->GetCPUWriteHandler(6))(6, 0x000, 0xAA); break;
case 0xAA: (EMU->GetCPUWriteHandler(6))(6, 0x000, 0x55); break;
}
switch((EMU->GetCPUReadHandler(6))(6, 0x220)) {
case 0x00: (EMU->GetCPUWriteHandler(6))(6, 0x220, 0x55); break;
case 0x55: (EMU->GetCPUWriteHandler(6))(6, 0x220, 0xAA); break;
}
switch((EMU->GetCPUReadHandler(6))(6, 0x0FF)) {
case 0x00: (EMU->GetCPUWriteHandler(6))(6, 0x0FF, 0x11); break;
case 0x11: (EMU->GetCPUWriteHandler(6))(6, 0x0FF, 0x88); break;
}
break;
// Vs. Wrecking Crew
case 0x008A9C16: case 0x30C42B1E: case 0x12B36F73:
switch((EMU->GetCPUReadHandler(6))(6, 0x051)) {
case 0x00: (EMU->GetCPUWriteHandler(6))(6, 0x051, 0x01); break;
}
switch((EMU->GetCPUReadHandler(6))(6, 0x053)) {
case 0x00: (EMU->GetCPUWriteHandler(6))(6, 0x053, 0x01); break;
}
break;
// Vs. Baseball
case 0xF5DEBF88: case 0xB5853830: case 0xC4DD2523: case 0x13A91937: case 0xF64D7252: case 0x968A6E9D:
case 0x44691677: case 0x327BD71D: case 0x038E1E1B: case 0x60C90D8A:
switch((EMU->GetCPUReadHandler(6))(6, 0x002)) {
case 0x63: (EMU->GetCPUWriteHandler(6))(6, 0x002, 0x64); break;
}
switch((EMU->GetCPUReadHandler(6))(6, 0x00A)) {
case 0x01: (EMU->GetCPUWriteHandler(6))(6, 0x00A, 0x00); break;
}
break;
// Vs. Tennis
case 0xB90497AA: case 0xBC202DB6: case 0x2AF7E14E: case 0x777EE984: case 0xD46B8C5F:
switch((EMU->GetCPUReadHandler(6))(6, 0x002)) {
case 0x00: (EMU->GetCPUWriteHandler(6))(6, 0x002, 0x80); break;
case 0x40: (EMU->GetCPUWriteHandler(6))(6, 0x002, 0xC0); break;
}
switch((EMU->GetCPUReadHandler(6))(6, 0x113)) {
case 0x00: (EMU->GetCPUWriteHandler(6))(6, 0x113, 0x01); break;
}
break;
// Vs. Mahjong
case 0x381E5E08:
switch((EMU->GetCPUReadHandler(6))(6, 0x002)) {
case 0x3C: (EMU->GetCPUWriteHandler(6))(6, 0x002, 0xA5); break;
}
switch((EMU->GetCPUReadHandler(6))(6, 0x011)) {
case 0x00: (EMU->GetCPUWriteHandler(6))(6, 0x011, 0x88); break;
}
break;
}
}
_Write4(Bank, Addr, Val);
}
Re: Vs. DualSystem
I've added a bit of a summary about how the Vs. System is like and how it's dislike a plain NES at the top of the page. I hope it clears up any confusion....zeroone wrote:Can we gather all the DualSystem information onto a dedicated Wiki page or a separate block within the existing VS page? It seems scattered. And I did use NoCash's info to fill in gaps, which apparently mixed me up.
There's no other information on the wiki about this. What would we add?
It means that as long as the last value CPU "A" wrote to $4016 had the 2s bit clear (".1"), and CPU "B" has not disabled interrupts ("sei" or due to being in its NMI or IRQ handler), CPU "B" will always enter its IRQ handler.zeroone wrote:Can you spell that out for me in terms of 4016.1 reads and writes?
This is just like any other IRQ in the NES, it's just that it's coming from a CPU instead of a timer.
Maybe! Maybe not! The hardware only imposes these exact behaviors:And Slave writes 0 to 4016.1 to acknowledge that it's done with shared memory?
* The primary CPU controls which CPU has access to RAM
* When the primary CPU asserts /IRQ on the secondary CPU, this also gives the secondary CPU access to RAM.
* The secondary CPU can assert /IRQ on the primary CPU.
Any higher-level interpretation will be fragile and game-specific.
—
Tepples: All the OEM documentation uses "M" and "S" ubiquitously.
Last edited by lidnariq on Mon Dec 31, 2018 7:23 pm, edited 1 time in total.
Re: Vs. DualSystem
How can each CPU determine whether /IRQ was asserted because of APU DMC or because of a request from the other CPU? Is looking for a signature in shared RAM the primary way?lidnariq wrote:The hardware only imposes these exact behaviors:
* The primary CPU controls which CPU has access to RAM
* When the primary CPU asserts /IRQ on the secondary CPU, this also gives the secondary CPU access to RAM.
* The secondary CPU can assert /IRQ on the primary CPU.
How are vertical blanks aligned between the two PPUs? Synchronized? Constant offset? Offset that drifts over the course of a game?
lidnariq wrote:All the OEM documentation uses "M" and "S" ubiquitously.
-
- Posts: 1510
- Joined: Thu May 19, 2005 11:30 am
Re: Vs. DualSystem
The IRQ handler usually checks $4016 D7.tepples wrote:How can each CPU determine whether /IRQ was asserted because of APU DMC or because of a request from the other CPU? Is looking for a signature in shared RAM the primary way?
Re: Vs. DualSystem
The pithy answer is "they don't use DMC or frame interrupts".tepples wrote:How can each CPU determine whether /IRQ was asserted because of APU DMC or because of a request from the other CPU?
That's to handle only having one set of ROMs that can run on both sides, not an indication that the IRQ is caused by the other CPU.NewRisingSun wrote:The IRQ handler usually checks $4016 D7.
Ok, these are things that should be on the page.How are vertical blanks aligned between the two PPUs? Synchronized? Constant offset? Offset that drifts over the course of a game?
To answer:
PPUs are perfectly synchronized, but entirely by accident. All CPUs and PPUs are fed by the same 21.477MHz crystal, and all CPUs and PPUs are released from reset at the same time. The RGB PPUs never have missing dots under any conditions, so cannot get out of sync. Since they are fed by the same clock, they cannot drift.
Re: Vs. DualSystem
I see what you're doing. Stop now.tepples wrote:Does the Dualsystem PCB actually say "master" or "slave"?
Re: Vs. DualSystem
Thanks everyone for the great responses. It's going to take me a bit to fully digest all this.
There are only 5 VS. DualSystem ROMs that I am aware of: Balloon Fight, Baseball, Mahjong, Tennis and Wrecking Crew. I have all of them as listed as mapper 0, except for Baseball, which is mapper 99. But I could not confirm those mapper numbers. Does anyone have a table of them?
There are only 5 VS. DualSystem ROMs that I am aware of: Balloon Fight, Baseball, Mahjong, Tennis and Wrecking Crew. I have all of them as listed as mapper 0, except for Baseball, which is mapper 99. But I could not confirm those mapper numbers. Does anyone have a table of them?
Re: Vs. DualSystem
Nocash additionally lists a version of Ice Climber.
No games for the Vs. System are really ever mapper 0; they're all actually mapper 99. If only 8 KiB of CHR is present—which I think specifically means only Vs. Mahjong—when $4016.2 is set there will be garbage graphics—it'll fetch open bus. All other Vs. DualSystem games seem to have 16 KiB CHR.
MAME enumerates 6 Vs. DualSystem games, plus five more variants (earlier versions or bad dumps):
"vstennis", 32 KiB PRG and 16 KiB CHR per side, different PRG and CHR for both sides
("vstennisa" and "vstennisb", same, with different PRG from above but identical CHR)
"wrecking" [crew], 32 KiB PRG and 16 KiB CHR per side, the same CHR on both sides
"balonfgt" [balloon fight], 32 and 16, same CHR on both sides
"vsmahjng" [mahjong], 24 and 8, same CHR on both sides
"vsbball" [baseball], 32 and 16, same CHR on both sides
("vsbballj", "vsbballja", and "vsbballjb", 32 and 16, all with the same CHR that differs from above)
"iceclmrd", 32 and 16, same CHR on both sides and almost-identical PRG.
No games for the Vs. System are really ever mapper 0; they're all actually mapper 99. If only 8 KiB of CHR is present—which I think specifically means only Vs. Mahjong—when $4016.2 is set there will be garbage graphics—it'll fetch open bus. All other Vs. DualSystem games seem to have 16 KiB CHR.
MAME enumerates 6 Vs. DualSystem games, plus five more variants (earlier versions or bad dumps):
"vstennis", 32 KiB PRG and 16 KiB CHR per side, different PRG and CHR for both sides
("vstennisa" and "vstennisb", same, with different PRG from above but identical CHR)
"wrecking" [crew], 32 KiB PRG and 16 KiB CHR per side, the same CHR on both sides
"balonfgt" [balloon fight], 32 and 16, same CHR on both sides
"vsmahjng" [mahjong], 24 and 8, same CHR on both sides
"vsbball" [baseball], 32 and 16, same CHR on both sides
("vsbballj", "vsbballja", and "vsbballjb", 32 and 16, all with the same CHR that differs from above)
"iceclmrd", 32 and 16, same CHR on both sides and almost-identical PRG.
Re: Vs. DualSystem
I am looking at the Dipswitch definitions file in Mesen. The number of switches and options suggests that the VS. DualSystem games have 16 DIPs instead of 8 DIPs. Perhaps this is possible since there are 4 controller ports instead of 2?
Is there a table in MAME or Nintendulator-NRS by any chance? I can't seem to locate it is in the sources.
I've used other sources for reference, but I have VS Mighty Bomb Jack and VS Slalom as mapper 0. In addition, many other VS games are not mapper 99. They appear to work
Is there a table in MAME or Nintendulator-NRS by any chance? I can't seem to locate it is in the sources.
lidnariq wrote:No games for the Vs. System are really ever mapper 0; they're all actually mapper 99. If only 8 KiB of CHR is present—which I think specifically means only Vs. Mahjong—when $4016.2 is set there will be garbage graphics—it'll fetch open bus. All other Vs. DualSystem games seem to have 16 KiB CHR.
I've used other sources for reference, but I have VS Mighty Bomb Jack and VS Slalom as mapper 0. In addition, many other VS games are not mapper 99. They appear to work