Homebrew Emulation GameYob, a gameboy emulator for DS

Deleted member 319809

MAH BOI/GURL
Member
Joined
Dec 22, 2012
Messages
900
Trophies
0
XP
461
Country
Canada
For link cable emulation, what you could do is emulate both Game Boys on each DS.

That way, all of the information that is missing for the link cable session is the keys pressed on the other Game Boy, which is provided by the other DS. The link cable can then be emulated locally, as if the other Game Boy had sent what it needed to send given its global state and the keys pressed on it.

The link initialisation would be as follows:

1. Pause both GBs at the next vblank, collapse cached flags, etc.

2. Synchronise the ROM such that "GB B" on DS A gets a copy of the ROM on GB B on DS B. Similarly, synchronise the ROM such that "GB A" on DS B gets a copy of the ROM on GB A on DS A. This step may require memory allocations or the creation of a scratch file on both DSes, and may be helped by global hashes and block hashes to avoid sending an entire ROM. From this point on, each DS emulates both its own ROM (showing its progress on-screen) and the ROM of its link slave (hiding its progress on-screen).

3. Synchronise all other emulation variables such that "GB B" on DS A gets a copy of the state of GB B on DS B. Similarly, synchronise all other emulation variables such that "GB A" on DS B gets a copy of the state of DS A. This step could be implemented by writing a saved state and the SRAM in memory, then sending it to the other DS perhaps with compression. From this point on, each DS can see the progress of both its Game Boy and the other DS's Game Boy.

4. End link initialisation. Enter "link mode".

In link mode:

1. The controls pressed on GB A of DS A are sent to DS B at the beginning of each frame, so that DS B can be made aware of how GB A will advance for the frame, and vice-versa. Each DS waits for the other GB's keypresses, which can be encoded in one byte. Each DS ignores keypresses until its own Game Boy has emulated one frame, so as to keep the synchronisation (e.g. you can't press A and have it apply in the middle of a frame anymore, because the other Game Boy will never know).

2. Each DS also emulates a single frame of each Game Boy, given the controls pressed at the beginning of their respective frames. The control passes from one Game Boy to the other when a byte is sent on the cable, so that the other Game Boy can receive the byte that was just sent.

3. Show the screen and sound only for the Game Boy respective to each DS.

That way, the link cable emulation can simply be 1 byte per frame! Each Game Boy will advance correctly, given that it has the memory contents of the other at all times, so you can press A on one Game Boy then A on the other to trade a Pokémon (which the other Game Boy will know about, so it can send the right bytes on the pretend cable), or the arrow keys in Tetris to move a piece (which the other Game Boy can turn into the right data for the link cable).

I take no credit for this idea.
 

Deleted member 319809

MAH BOI/GURL
Member
Joined
Dec 22, 2012
Messages
900
Trophies
0
XP
461
Country
Canada
^ Didn't get half of that ...but sounds awesome :P
It's basically a way to avoid needing to synchronise link-cable data over WiFi when latencies are high. You shift all the work to the link initialisation phase and then it becomes pain-free to send data, because the link cable data is generated in the very same Nintendo DS.

Even more simply: It's a way to avoid the "my Pokémon is now named PPPDGEY and its level is 0 because the WiFi lagged and duplicated bytes" problem :)
 
  • Like
Reactions: Deleted-236924

2ndApex

Well-Known Member
Member
Joined
Jul 12, 2012
Messages
677
Trophies
0
XP
418
Country
United States
For link cable emulation, what you could do is emulate both Game Boys on each DS.

That way, all of the information that is missing for the link cable session is the keys pressed on the other Game Boy, which is provided by the other DS. The link cable can then be emulated locally, as if the other Game Boy had sent what it needed to send given its global state and the keys pressed on it.

The link initialisation would be as follows:

1. Pause both GBs at the next vblank, collapse cached flags, etc.

2. Synchronise the ROM such that "GB B" on DS A gets a copy of the ROM on GB B on DS B. Similarly, synchronise the ROM such that "GB A" on DS B gets a copy of the ROM on GB A on DS A. This step may require memory allocations or the creation of a scratch file on both DSes, and may be helped by global hashes and block hashes to avoid sending an entire ROM. From this point on, each DS emulates both its own ROM (showing its progress on-screen) and the ROM of its link slave (hiding its progress on-screen).

3. Synchronise all other emulation variables such that "GB B" on DS A gets a copy of the state of GB B on DS B. Similarly, synchronise all other emulation variables such that "GB A" on DS B gets a copy of the state of DS A. This step could be implemented by writing a saved state and the SRAM in memory, then sending it to the other DS perhaps with compression. From this point on, each DS can see the progress of both its Game Boy and the other DS's Game Boy.

4. End link initialisation. Enter "link mode".

In link mode:

1. The controls pressed on GB A of DS A are sent to DS B at the beginning of each frame, so that DS B can be made aware of how GB A will advance for the frame, and vice-versa. Each DS waits for the other GB's keypresses, which can be encoded in one byte. Each DS ignores keypresses until its own Game Boy has emulated one frame, so as to keep the synchronisation (e.g. you can't press A and have it apply in the middle of a frame anymore, because the other Game Boy will never know).

2. Each DS also emulates a single frame of each Game Boy, given the controls pressed at the beginning of their respective frames. The control passes from one Game Boy to the other when a byte is sent on the cable, so that the other Game Boy can receive the byte that was just sent.

3. Show the screen and sound only for the Game Boy respective to each DS.

That way, the link cable emulation can simply be 1 byte per frame! Each Game Boy will advance correctly, given that it has the memory contents of the other at all times, so you can press A on one Game Boy then A on the other to trade a Pokémon (which the other Game Boy will know about, so it can send the right bytes on the pretend cable), or the arrow keys in Tetris to move a piece (which the other Game Boy can turn into the right data for the link cable).

I take no credit for this idea.

Does this only work on games that can go at 120 FPS or does it need more power because of link cable emulation or vice versa because you're emulating the same rom twice?
 

Deleted member 319809

MAH BOI/GURL
Member
Joined
Dec 22, 2012
Messages
900
Trophies
0
XP
461
Country
Canada
Does this only work on games that can go at 120 FPS or does it need more power because of link cable emulation or vice versa because you're emulating the same rom twice?
If the ROM used in GB A and the ROM used in GB B are the same, then the combination can only go at 60 FPS if the ROM can fast-forward to 120 FPS (120/2 = 60 FPS). Otherwise, you get slowdown (80/2 = 40 FPS).

If the ROM used in GB B is a "receiver cartridge" like the Game Boy Printer ROM (if that ROM is supported), then GB A could be slower than 120 FPS and still get the correct speed.
 

Walker D

I have a hat
Member
Joined
Nov 15, 2009
Messages
1,334
Trophies
0
Location
My home
XP
748
Country
Brazil
So, by what I understood, you link your game (gb A) to another one previously loaded in your own ds (gb B).

But if the old link cable streaming method was losing bytes in the way, wouldn't it happen with the save state transferring here also?
Maybe the data transfer in this situation allows more passes than in the link cable scenario ..is that the case, or I still didn't get it? :P
 
Joined
Dec 17, 2009
Messages
1,340
Trophies
2
XP
2,867
Country
Poland
Hey, still testing daily. Today I had a look at Mickey's Speedway USA (U), which had problems with garbage images appearing during transitions on LameBoy, but GameYob emulates that perfectly! However, the racing itself (in all its proto-3D glory) just crashes the emulator before it even renders a single frame.

Anyway, I have a suggestion, but dunno how other users feel about it. I personally would love if the ROM selection screen had an indicator other than that asterisk on the left corner, which is not all that helpful. A color change in the font or a colored background behind the name of the game (like in AKAIO or just about any OS ever) would go a long way in making it clearer what ROM you are selecting.

Thanks for the continued effort!
 

CoolAs

Well-Known Member
Newcomer
Joined
Oct 21, 2011
Messages
75
Trophies
0
XP
109
Country
1. The controls pressed on GB A of DS A are sent to DS B at the beginning of each frame, so that DS B can be made aware of how GB A will advance for the frame, and vice-versa.
And how do you have it so that the frames are synchronized on each DS? (swiWaitForVBlank(); won't happen at the same time on each of the DS')
Or is this a calculated frame, not a graphical one?
It sounds to much like how Nintendo did Mario Kart DS and NSMB.
How do you do cheats in multiplayer this way?
 

Deleted member 319809

MAH BOI/GURL
Member
Joined
Dec 22, 2012
Messages
900
Trophies
0
XP
461
Country
Canada
And how do you have it so that the frames are synchronized on each DS? (swiWaitForVBlank(); won't happen at the same time on each of the DS')
Or is this a calculated frame, not a graphical one?
It sounds to much like how Nintendo did Mario Kart DS and NSMB.
How do you do cheats in multiplayer this way?
Graphical frames. It would wait for vblank, then send keypresses and wait for the other DS's keypresses. So that way, it's more like: (pseudocode)
Code:
swiWaitForVBlank();
nifiSendPacketByte(gbKeys);
unsigned char otherGbKeys = nifiWaitForPacket();
runEmul(gbKeys);
runEmulSlaveGb(otherGbKeys);
 

Drenn

Well-Known Member
OP
Member
Joined
Feb 22, 2013
Messages
574
Trophies
0
XP
684
Country
Canada
For link cable emulation, what you could do is emulate both Game Boys on each DS.

That way, all of the information that is missing for the link cable session is the keys pressed on the other Game Boy, which is provided by the other DS. The link cable can then be emulated locally, as if the other Game Boy had sent what it needed to send given its global state and the keys pressed on it.

The link initialisation would be as follows:

1. Pause both GBs at the next vblank, collapse cached flags, etc.

2. Synchronise the ROM such that "GB B" on DS A gets a copy of the ROM on GB B on DS B. Similarly, synchronise the ROM such that "GB A" on DS B gets a copy of the ROM on GB A on DS A. This step may require memory allocations or the creation of a scratch file on both DSes, and may be helped by global hashes and block hashes to avoid sending an entire ROM. From this point on, each DS emulates both its own ROM (showing its progress on-screen) and the ROM of its link slave (hiding its progress on-screen).

3. Synchronise all other emulation variables such that "GB B" on DS A gets a copy of the state of GB B on DS B. Similarly, synchronise all other emulation variables such that "GB A" on DS B gets a copy of the state of DS A. This step could be implemented by writing a saved state and the SRAM in memory, then sending it to the other DS perhaps with compression. From this point on, each DS can see the progress of both its Game Boy and the other DS's Game Boy.

4. End link initialisation. Enter "link mode".

In link mode:

1. The controls pressed on GB A of DS A are sent to DS B at the beginning of each frame, so that DS B can be made aware of how GB A will advance for the frame, and vice-versa. Each DS waits for the other GB's keypresses, which can be encoded in one byte. Each DS ignores keypresses until its own Game Boy has emulated one frame, so as to keep the synchronisation (e.g. you can't press A and have it apply in the middle of a frame anymore, because the other Game Boy will never know).

2. Each DS also emulates a single frame of each Game Boy, given the controls pressed at the beginning of their respective frames. The control passes from one Game Boy to the other when a byte is sent on the cable, so that the other Game Boy can receive the byte that was just sent.

3. Show the screen and sound only for the Game Boy respective to each DS.

That way, the link cable emulation can simply be 1 byte per frame! Each Game Boy will advance correctly, given that it has the memory contents of the other at all times, so you can press A on one Game Boy then A on the other to trade a Pokémon (which the other Game Boy will know about, so it can send the right bytes on the pretend cable), or the arrow keys in Tetris to move a piece (which the other Game Boy can turn into the right data for the link cable).

I take no credit for this idea.
Cool idea! The trickiest part of all this would be going back over all my code to allow for more than 1 emulated gameboy, as you probably know this will take some reworking. It'd probably be worth it though!
 

Drenn

Well-Known Member
OP
Member
Joined
Feb 22, 2013
Messages
574
Trophies
0
XP
684
Country
Canada
Maybe support for the 3in1 and official ram expansion will aid in the memory required to do this?
More ram would definitely help. But I'd prefer to get it running without the extra ram, since many don't own those ram expansions (myself included). But that might not work out for cross-game linking. Speaking of which, I've been looking for an ez-3in1 but I couldn't find any that would fit in a ds phat. I might end up buying a lite 3in1 and trying to modify it to fit in a phat. Apparently people have done that.
 

Kouen Hasuki

Coffee Addict
Member
Joined
Jan 9, 2013
Messages
1,387
Trophies
1
Age
39
Location
Behind you
XP
671
Country
Norway
More ram would definitely help. But I'd prefer to get it running without the extra ram, since many don't own those ram expansions (myself included). But that might not work out for cross-game linking. Speaking of which, I've been looking for an ez-3in1 but I couldn't find any that would fit in a ds phat. I might end up buying a lite 3in1 and trying to modify it to fit in a phat. Apparently people have done that.

Yup it would be very easy to do just get a old GBA Sports game open it and take out the game's board and put in the 3 in 1 fix it down to make sure it does not move and your done!

though you can get the official ram expansion for fat ds's on eBay
 

Clydefrosch

Well-Known Member
Member
Joined
Jan 2, 2009
Messages
5,952
Trophies
2
XP
4,409
Country
Germany
I'm very happy that you at least want to try getting all this done without needing extra ram.
while i actually have one for the ds phat browser, i'd lack a second one to allow multiplayer
 

Deleted member 319809

MAH BOI/GURL
Member
Joined
Dec 22, 2012
Messages
900
Trophies
0
XP
461
Country
Canada
Cool idea! The trickiest part of all this would be going back over all my code to allow for more than 1 emulated gameboy, as you probably know this will take some reworking. It'd probably be worth it though!
Yeah, it's all global variables at the moment, but you already have a wealth of functions to do work for you, like saving and loading a state. You could dump those into a class:
Code:
class GameBoy {
private:
  u8 vram[...];
  u8 rom[MAX_LOADED_ROM_BANKS][...];
  u8* romAddress[MAX_ROM_BANKS];
  . . .
public:
  GameBoy(const GameBoy& a) /* copy constructor */;
  GameBoy(const GameBoy& a, const u8* const newRom);
  GameBoy(const GameBoy& a, const std::string& newRomFile);
  int loadState(const u8* const data);
  int saveState(u8* const data) const;
  int runUntil(const int interrupt);
  void linkTo(const GameBoy& b);
  void unlink();
  . . .
}
And then:
Code:
GameBoy b = GameBoy(a); // if the ROM is the same
GameBoy b = GameBoy(a, newRom); // from a memory allocation <= 512 KiB if the ROMs aren't the same
GameBoy b = GameBoy(a, scratchFile); // from a file if the ROM is too large
 
if (linkMode) {
  int aCause, bCause;
  do {
    aCause = a.runUntil(SERIAL | VBLANK); // pass control to GB B
    bCause = b.runUntil(SERIAL | VBLANK); // pass control to GB A
  } while (!( aCause == VBLANK || bCause == VBLANK )); // until either has stopped due to VBLANK
  if (bCause == SERIAL) // then run the other GB until its VBLANK
    bCause = b.runUntil(VBLANK);
  else if (aCause == SERIAL)
    aCause = a.runUntil(VBLANK);
} else {
  a.runUntil(VBLANK);
}
a.render();
 

windwakr

Well-Known Member
Member
Joined
Sep 13, 2009
Messages
500
Trophies
1
Website
Visit site
XP
1,696
Country
United States
For anyone using a 3DS, this build fixes the clock being stuck and not updating(both in games like Pokemon G/S/C and the time display on GameYob):
http://filesmelt.com/dl/gameyob_time.nds

The issue seems to be in the way that libnds updates the clock. It's not necessarily a bug in libnds, but it looks like the functionality they use doesn't exist or doesn't function properly on the 3DS's DS mode.

Modified code is here:
https://github.com/windwakr/GameYob/tree/newtime
 
  • Like
Reactions: Rydian
General chit-chat
Help Users
  • No one is chatting at the moment.
    Skelletonike @ Skelletonike: No idea what that is tbh, is that like the iso or something?