Game Boy emulator input issue [solved]

Discussion of programming and development for the original Game Boy and Game Boy Color.
Post Reply
Near
Founder of higan project
Posts: 1553
Joined: Mon Mar 27, 2006 5:23 pm

Game Boy emulator input issue [solved]

Post by Near »

Anyone knowledgeable with writing Game Boy emulators here?

Have a strange problem in Metroid II: Return of Samus.

Image

If you hold left and then switch to holding right, or right and then left, Samus gets stuck in the turning state between the two directions, and ends up sliding around like in the above picture with no animation.

This is *not* because of allowing left+right at the same time, I explicitly filtered that out and made certain that wasn't the case.

I think it might have something to do with slow decay/transition states of the real hardware buttons, but I'm not sure how to emulate that to prevent this behavior.

Any insight would be greatly appreciated.
Last edited by Near on Thu Dec 05, 2013 8:30 pm, edited 1 time in total.
tepples
Posts: 22705
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Re: Game Boy emulator input issue

Post by tepples »

Are you making sure to emulate the lack of an inverter on Game Boy, GBC, and GBA controllers (1=released, 0=pressed)?
Near
Founder of higan project
Posts: 1553
Joined: Mon Mar 27, 2006 5:23 pm

Re: Game Boy emulator input issue

Post by Near »

Yeah you wouldn't be moving at all without that. Input seems fine other than this one issue.

Code: Select all

void CPU::mmio_joyp_poll() {
  unsigned button = 0, dpad = 0;

  //inputPoll() returns 1 when pressed, 0 when released
  button |= interface->inputPoll(0, 0, (unsigned)Input::Start) << 3;
  button |= interface->inputPoll(0, 0, (unsigned)Input::Select) << 2;
  button |= interface->inputPoll(0, 0, (unsigned)Input::B) << 1;
  button |= interface->inputPoll(0, 0, (unsigned)Input::A) << 0;

  //hacky just to be 100% sure it wasn't allowing left+right at the same time
  if(interface->inputPoll(0, 0, (unsigned)Input::Up)) dpad |= 4;
  else if(interface->inputPoll(0, 0, (unsigned)Input::Down)) dpad |= 8;
  if(interface->inputPoll(0, 0, (unsigned)Input::Left)) dpad |= 2;
  else if(interface->inputPoll(0, 0, (unsigned)Input::Right)) dpad |= 1;

  status.joyp = 0x0f;
  //it's supposed to act wonky like this when p15 and p14 are both clear ... but I verified Metroid II isn't doing this
  if(status.p15 == 0) status.joyp &= button ^ 0x0f;
  if(status.p14 == 0) status.joyp &= dpad ^ 0x0f;  //inversion is here
  if(status.joyp != 0x0f) interrupt_raise(Interrupt::Joypad);
}
nitro2k01
Posts: 252
Joined: Sat Aug 28, 2010 9:01 am

Re: Game Boy emulator input issue

Post by nitro2k01 »

byuu wrote:I think it might have something to do with slow decay/transition states of the real hardware buttons, but I'm not sure how to emulate that to prevent this behavior.
The only transition states that exist are between when you select a line and when you read out the button state. On a DMG, when you write a 0 to activate one of the lines, this signal is propagated through the ribbon cable up to the display board and back to the relevant input pin on the CPU. Because of capacitance and series resistance, it may take a couple of microseconds for the state to propagate. This is why you see games read out $FF00 multiple times. This is nothing more than a delay, and only the last read does anything. As you might have noticed, the button group usually has a longer delay in the readout routine than the D-pad group, likely because the start/select group has a longer propagation delay than the other buttons, especially if it's worn/dirty. (I've tested this.)

That said, the propagation delay never needs to be emulated for gameplay of licensed ROMs. The only time when it would matter is if a program is actively checking for it, as in reading the button input early and making sure the input hasn't propagated. Even then, GBC completely lacks this delay, as it has 8 input pins and this switching is handled in logic inside the chip, which means there's no propagation delay. However, I convinced beware to implement it into BGB, to discourage people from writing ROMs that don't work on real hardware. (As a primary use of BGB is as a development tool.)

One theory I had was that the problem might happen if only left was pressed in one frame, and then only right was pressed the next frame, a situation unlikely to occur on a real console as it would likely take a human more than one frame to pivot the D-pad from one side to the other. But this would be trivially reproducible in BGB by single frame stepping, and that's not it.

I tried to reproduce this in Higan 093 (the latest publicly available version as of writing this) and this game has another problem, which is the implementation of the priority sprite bit, which makes Samus almost invisible for the first screen. I don't know if you've fixed this in the beta you're working on or if this happens to me because of different settings.

I cannot reproduce the reported problem in Higan 093, however. If you stand against a wall facing right, and press right+left, Samus' animation gets stuck in the turning state, which is consistent with BGB, and probably hardware if you could press left+right. But not when walking freely.

Maybe you should consider the possibility that this bug is due to something other than the joypad input, for example that the sprites are not getting updated properly in the rendering pipeline, or in the ROM, for whatever reason, and that the turning animation that is normally visible for two frames sticks. Try firing a shot and see if that animates normally while Samus is in this state.
Near
Founder of higan project
Posts: 1553
Joined: Mon Mar 27, 2006 5:23 pm

Re: Game Boy emulator input issue

Post by Near »

Thank you for the help!

Yes, Samus being invisible was a huge bug, too. I was relying on BG-over-OAM mode allowing OAM only when the pixel color was 0. But it's supposed to be only when the palette color is 0. Really wish we had better GB docs than pandocs :(

And very, very, very weird. You're right, this bug doesn't happen on Windows. It only happens on Linux (even with v093 official, so no WIP modifications are at fault.)

Per your question, I can't shoot while walking straight, but I can shoot after jumping.

Image

Now I am really terrified. What kind of insane bug would cause an emulation input bug, but only on Linux?
Again, I am 100% certain that JOYP is never returning left+right set at the same time.

Side question: I noticed in gambatte that it was re-reading the keyboard/gamepad inputs every time you read $ff00 (JOYP), rather than latching them on write like the SNES. Is that correct behavior? (I trust gambatte 100x more than anything I've done, so I'll go with gambatte's method unless you know that's wrong.)
nitro2k01
Posts: 252
Joined: Sat Aug 28, 2010 9:01 am

Re: Game Boy emulator input issue

Post by nitro2k01 »

When you say you can't shoot, is this true only graphically speaking, ie. do you hear the shooting sound even if you can't see the the shot? My hypothesis was that this was a purely graphical bug where the sprites are not being updated while (perhaps) the CPU continues to be emulated normally.

As for your question about reading input. $FF00 is literally a 2 out, 4 in digital IO port, so what you're reading back is immediate. (Subject only to the microsecond delays explained above.)

The SNES controller readout is not immediate like that, as data has to be shifted in from a shift register in the controller. This is done by sending a number of clock pulses to read out one bit at a time. And then there's the automatic mode to read out the controller on VBlank, but you probably know that better than I do considering you've written a SNES emulator and I haven't touched SNES. I only know this process from the hardware side. (I've interfaced a SNES controller from the Gameboy's link port for example.)
Near
Founder of higan project
Posts: 1553
Joined: Mon Mar 27, 2006 5:23 pm

Re: Game Boy emulator input issue

Post by Near »

Man, it gets even weirder.

This happens on both of my development machine OSes, but not on my laptop.

Both the dev machine and laptop are Windows 7, running the stock v093 official release 64-bit balanced binary.
Game Boy binary hash is identical on both machines, so it's not a corrupted game image.

Looks like my only option is going to be to create an input playback and run trace logs against both machines to see where the code is branching differently. That'll be a treat.

Craziest bug ever.
Last edited by Near on Thu Dec 05, 2013 8:31 pm, edited 1 time in total.
User avatar
blargg
Posts: 3715
Joined: Mon Sep 27, 2004 8:33 am
Location: Central Texas, USA
Contact:

Re: Game Boy emulator input issue

Post by blargg »

Do the config files for the emulator differ across the machines?
Near
Founder of higan project
Posts: 1553
Joined: Mon Mar 27, 2006 5:23 pm

Re: Game Boy emulator input issue [solved]

Post by Near »

Just figured it out.

I didn't realize that I had a "multi-jump" cheat code active still on my dev machine. Obviously I know cheat codes screw games up, just didn't realize it was on because it's not an obvious one unless you try it. Dev machine shares the same cheat file for both OSes.

Apparently this code has the side-effect of making Samus moonwalk.

Well I feel stupid now. Sorry for the trouble :(
nitro2k01
Posts: 252
Joined: Sat Aug 28, 2010 9:01 am

Re: Game Boy emulator input issue [solved]

Post by nitro2k01 »

Just an addendum of the input latency question, since you asked. As far as the GB CPU is concerned, it's instant. On a SGB it is, of course, dependent on the SNES CPU. Data will only arrive at the SGB CPU when the SNES CPU propagates it to the cartridge, through the controller chip, however that works exactly from the SNES side.
Post Reply