Joypad:
The gameboy has an inbuilt joypad with 8 buttons. There are 4 directional buttons (up, down, left and right) and standard buttons (start,select, A and B). The joypad register can be found at address 0xFF00 and it is broken down like so:
Taken from pandocs
Bit 7 - Not used
Bit 6 - Not used
Bit 5 - P15 Select Button Keys (0=Select)
Bit 4 - P14 Select Direction Keys (0=Select)
Bit 3 - P13 Input Down or Start (0=Pressed) (Read Only)
Bit 2 - P12 Input Up or Select (0=Pressed) (Read Only)
Bit 1 - P11 Input Left or Button B (0=Pressed) (Read Only)
Bit 0 - P10 Input Right or Button A (0=Pressed) (Read Only)
The way I believe this works in the original gameboy hardware is the game writes to memory 0xFF00 with bit 4 or 5 set (never both). It then reads from memory 0xFF00 and instead of reading back what it just wrote what is returned is the state of the joypad based on whether bit 4 or bit 5 was set. For example if the game wanted to check which directional buttons was pressed it would set bit 4 to 1 and then it would do a read memory on 0xFF00. If the up key is pressed then when reading 0xFF00 bit 2 would be set to 0 to signal that the directional button up is pressed (0 means pressed, 1 unpressed). However if up was not pressed but the select button was then bit 2 would be left at 1 meaning nothing is pressed, even though the select button is pressed which maps on to bit 2. The reason why bit 2 would be set to 1 signalling it is not pressed even when it is is because bit 4 was set to 1 meaning the game is only interested in the state of the directional buttons.
The way I emulate this is I have a BYTE variabled called m_JoypadState where each bit represents the state of the joypad (8 buttons and 8 bits so it works fine). Whenever a button is pressed I set the correct bit in m_JoypadState to 0 and if it is not pressed it is set to 1. If a bit in m_JoypadState goes from 1 to 0 then it means this button has just been pressed so a joypad interupt is requested.
Whenever the game reads from 0xFF00 i trap it with ReadMemory and call a function which looks at memory address 0xFF00 to see if the game is interest in directional buttons or standard buttons and then I return a byte data which combines m_JoypadState with 0xFF00 so the game gets the correct state of the joypad. While I remember lets add the following to ReadMemory
else if (0xFF00 == address)
return GetJoypadState() ;
void Emulator::KeyPressed(int key)
{
bool previouslyUnset = false ;
// if setting from 1 to 0 we may have to request an interupt
if (TestBit(m_JoypadState, key)==false)
previouslyUnset = true ;
// remember if a keypressed its bit is 0 not 1
m_JoypadState = BitReset(m_JoypadState, key) ;
// button pressed
bool button = true ;
// is this a standard button or a directional button?
if (key > 3)
button = true ;
else // directional button pressed
button = false ;
BYTE keyReq = m_Rom[0xFF00] ;
bool requestInterupt = false ;
// only request interupt if the button just pressed is
// the style of button the game is interested in
if (button && !TestBit(keyReq,5))
requestInterupt = true ;
// same as above but for directional button
else if (!button && !TestBit(keyReq,4))
requestInterupt = true ;
// request interupt
if (requestInterupt && !previouslyUnset)
RequestInterupt(4) ;
}
void Emulator::KeyReleased(int key)
{
m_JoypadState = BitSet(m_JoypadState,key) ;
}
SDLK_a : key = 4
SDLK_s : key = 5
SDLK_RETURN : key = 7
SDLK_SPACE : key = 6
SDLK_RIGHT : key = 0
SDLK_LEFT : key = 1
SDLK_UP : key = 2
SDLK_DOWN : key = 3
BYTE Emulator::GetJoypadState() const
{
BYTE res = m_Rom[0xFF00] ;
// flip all the bits
res ^= 0xFF ;
// are we interested in the standard buttons?
if (!TestBit(res, 4))
{
BYTE topJoypad = m_JoypadState >> 4 ;
topJoypad |= 0xF0 ; // turn the top 4 bits on
res &= topJoypad ; // show what buttons are pressed
}
else if (!TestBit(res,5))//directional buttons
{
BYTE bottomJoypad = m_JoypadState & 0xF ;
bottomJoypad |= 0xF0 ;
res &= bottomJoypad ;
}
return res ;
}