Class: Amaterasu::GameBoy::Joypad

Inherits:
Object
  • Object
show all
Defined in:
lib/amaterasu/game_boy/joypad.rb

Overview

Models the joypad inputs and logic.

Constant Summary collapse

BIT_MASK_UNUSED_BITS =
0b11000000
BIT_MASK_BUTTONS_SELECT_BIT =
0b00100000
BIT_MASK_DPAD_SELECT_BIT =
0b00010000
FACE_BUTTONS =

Maps all face buttons to its relevant bit.

{
  a: 0,
  b: 1,
  select: 2,
  start: 3
}.freeze
DPAD_BUTTONS =

Maps all D-pad buttons to its relevant bit.

{
  right: 0,
  left: 1,
  up: 2,
  down: 3
}.freeze

Instance Method Summary collapse

Constructor Details

#initialize(interrupts, skip_boot_rom: true) ⇒ Joypad

Creates a joypad object.

  • Holds interrupts instance to request a :joypad interrupt.

  • @p1 register only holds the relevant selection bits (Bits 5 and 4).

  • Dpad and button state are tracked as separate nibbles.



33
34
35
36
37
38
39
# File 'lib/amaterasu/game_boy/joypad.rb', line 33

def initialize(interrupts, skip_boot_rom: true)
  @interrupts = interrupts
  @p1 = skip_boot_rom ? 0xCF : 0x00

  @dpad = 0xF
  @buttons = 0xF
end

Instance Method Details

#p1Object

Returns the 8-bit value stored in the P1 register.

  • Bits 7 and 6 are unused, they always read 1.

  • The actual @p1 register only holds the 2 selection bits (Bit 5 and 4).

  • Calculate the buttons state based on the pressed buttons and use it as the lower nibble.



46
47
48
# File 'lib/amaterasu/game_boy/joypad.rb', line 46

def p1
  BIT_MASK_UNUSED_BITS | @p1 | buttons_state
end

#p1=(value) ⇒ Object

Sets a 8-bit value into the P1 register.

  • The lower nibble is read-only.

  • Bit 5 is used to select the face buttons.

  • Bit 4 is used to select the d-pad.



55
56
57
58
59
60
61
62
# File 'lib/amaterasu/game_boy/joypad.rb', line 55

def p1=(value)
  old_state = buttons_state
  @p1 = value & 0b00110000
  new_state = buttons_state

  falling_edges = old_state & ~new_state # checks for bits that went 1 -> 0
  request_interrupt if falling_edges.anybits?(0x0F)
end

#press_dpad(button) ⇒ Object

Parameters:

  • button (Symbol)

    Dpad button pressed (:up, :down, :left, :right).



65
66
67
68
69
70
71
72
73
# File 'lib/amaterasu/game_boy/joypad.rb', line 65

def press_dpad(button)
  relevant_bit = DPAD_BUTTONS[button]
  return if (@dpad >> relevant_bit).nobits?(1) # already pressed

  clear_mask = ~(1 << relevant_bit)
  @dpad &= clear_mask

  request_interrupt if dpad_selected?
end

#press_face(button) ⇒ Object

Parameters:

  • button (Symbol)

    Face button pressed (:a, :b, :start, :select).



85
86
87
88
89
90
91
92
93
# File 'lib/amaterasu/game_boy/joypad.rb', line 85

def press_face(button)
  relevant_bit = FACE_BUTTONS[button]
  return if (@buttons >> relevant_bit).nobits?(1) # already pressed

  clear_mask = ~(1 << relevant_bit)
  @buttons &= clear_mask

  request_interrupt if buttons_selected?
end

#release_dpad(button) ⇒ Object

Parameters:

  • button (Symbol)

    Dpad button released (:up, :down, :left, :right).



76
77
78
79
80
81
82
# File 'lib/amaterasu/game_boy/joypad.rb', line 76

def release_dpad(button)
  relevant_bit = DPAD_BUTTONS[button]
  return if (@dpad >> relevant_bit).anybits?(1) # already released

  set_mask = (1 << relevant_bit)
  @dpad |= set_mask
end

#release_face(button) ⇒ Object

Parameters:

  • button (Symbol)

    Face button released (:a, :b, :start, :select).



96
97
98
99
100
101
102
# File 'lib/amaterasu/game_boy/joypad.rb', line 96

def release_face(button)
  relevant_bit = FACE_BUTTONS[button]
  return if (@buttons >> relevant_bit).anybits?(1) # already released

  set_mask = (1 << relevant_bit)
  @buttons |= set_mask
end