Skip to content

Instantly share code, notes, and snippets.

@ISSOtm
Created May 13, 2025 00:23
Show Gist options
  • Save ISSOtm/2e3e8a7fa7517ac4d803438f441dcc40 to your computer and use it in GitHub Desktop.
Save ISSOtm/2e3e8a7fa7517ac4d803438f441dcc40 to your computer and use it in GitHub Desktop.
Attempting a reformat of hardware.inc
; *****************************************************************************
; Game Boy hardware constant definitions
; *****************************************************************************
; To the extent possible under law, the authors of this work have
; waived all copyright and related or neighboring rights to the work.
; See https://creativecommons.org/publicdomain/zero/1.0/ for details.
; SPDX-License-Identifier: CC0-1.0
; If this file was already included, don't do it again
if !def(HARDWARE_INC)
; Check for the minimum supported RGBDS version
if !def(__RGBDS_MAJOR__) || !def(__RGBDS_MINOR__) || !def(__RGBDS_PATCH__)
fail "This version of 'hardware.inc' requires RGBDS version 0.5.0 or later"
endc
if __RGBDS_MAJOR__ == 0 && __RGBDS_MINOR__ < 5
fail "This version of 'hardware.inc' requires RGBDS version 0.5.0 or later"
endc
; Define the include guard and the current hardware.inc version
; (do this after the RGBDS version check since the `def` syntax depends on it)
def HARDWARE_INC equ 1
def HARDWARE_INC_VERSION equs "5.0.0"
; Usage: check_hardware_inc_version <min_ver>
; Example: check_hardware_inc_version 5.0
MACRO check_hardware_inc_version
def hardware_inc_cur_ver\@ equs strrpl("{HARDWARE_INC_VERSION}", ".", ",")
def hardware_inc_min_ver\@ equs strrpl("\1", ".", ",")
def hardware_inc_define_check\@ equs """MACRO hardware_inc_internal_check\@
if (\\1) < (\\4) || ((\1) == (\\4) && ((\\2) < (\\5) || ((\\2) == (\\5) && (\\3) < (\\6))))
fail "Version \\1.\\2.\\3 of 'hardware.inc' is incompatible with minimum version \\4.\\5.\\6"
endc
\nENDM"""
hardware_inc_define_check\@
hardware_inc_internal_check\@ {hardware_inc_cur_ver\@}, {hardware_inc_min_ver\@}, 0, 0
purge hardware_inc_cur_ver\@, hardware_inc_min_ver\@, hardware_inc_define_check\@, hardware_inc_internal_check\@
ENDM
; -----------------------------------------------------------------------------
; JOYPAD ($FF00): Face buttons
;
; - Bit 5-4 = which set of four inputs to read [r/w]
; - Bit 3-0 = the four inputs being read (0 = pressed) [ro]
; -----------------------------------------------------------------------------
def rJoypad equ $ff00
def BIT_JOYPAD_CTRL_PAD equ 5 ; 0 = reading buttons [r/w]
def BIT_JOYPAD_BUTTONS equ 4 ; 0 = reading Control Pad [r/w]
def BIT_JOYPAD_START equ 3 ; 0 = Start is pressed (if reading buttons) [ro]
def BIT_JOYPAD_SELECT equ 2 ; 0 = Select is pressed (if reading buttons) [ro]
def BIT_JOYPAD_B equ 1 ; 0 = B is pressed (if reading buttons) [ro]
def BIT_JOYPAD_A equ 0 ; 0 = A is pressed (if reading buttons) [ro]
def BIT_JOYPAD_DOWN equ 3 ; 0 = Down is pressed (if reading Control Pad) [ro]
def BIT_JOYPAD_UP equ 2 ; 0 = Up is pressed (if reading Control Pad) [ro]
def BIT_JOYPAD_LEFT equ 1 ; 0 = Left is pressed (if reading Control Pad) [ro]
def BIT_JOYPAD_RIGHT equ 0 ; 0 = Right is pressed (if reading Control Pad) [ro]
def MASK_JOYPAD_GET equ %00_11_0000
def JOYPAD_GET_BUTTONS equ %00_01_0000 ; read A/B/Select/Start buttons
def JOYPAD_GET_CTRL_PAD equ %00_10_0000 ; read Control Pad directions
def JOYPAD_GET_NONE equ %00_11_0000 ; read nothing
def MASK_JOYPAD_INPUTS equ %0000_1111
def JOYPAD_START equ 1 << BIT_JOYPAD_START
def JOYPAD_SELECT equ 1 << BIT_JOYPAD_SELECT
def JOYPAD_B equ 1 << BIT_JOYPAD_B
def JOYPAD_A equ 1 << BIT_JOYPAD_A
def JOYPAD_DOWN equ 1 << BIT_JOYPAD_DOWN
def JOYPAD_UP equ 1 << BIT_JOYPAD_UP
def JOYPAD_LEFT equ 1 << BIT_JOYPAD_LEFT
def JOYPAD_RIGHT equ 1 << BIT_JOYPAD_RIGHT
; Combined input byte, with Control Pad in high nybble (conventional)
def BIT_INPUT_DOWN equ BIT_JOYPAD_DOWN + 4
def BIT_INPUT_UP equ BIT_JOYPAD_UP + 4
def BIT_INPUT_LEFT equ BIT_JOYPAD_LEFT + 4
def BIT_INPUT_RIGHT equ BIT_JOYPAD_RIGHT + 4
def BIT_INPUT_START equ BIT_JOYPAD_START
def BIT_INPUT_SELECT equ BIT_JOYPAD_SELECT
def BIT_INPUT_B equ BIT_JOYPAD_B
def BIT_INPUT_A equ BIT_JOYPAD_A
def INPUT_DOWN equ 1 << BIT_INPUT_DOWN
def INPUT_UP equ 1 << BIT_INPUT_UP
def INPUT_LEFT equ 1 << BIT_INPUT_LEFT
def INPUT_RIGHT equ 1 << BIT_INPUT_RIGHT
def INPUT_START equ 1 << BIT_INPUT_START
def INPUT_SELECT equ 1 << BIT_INPUT_SELECT
def INPUT_B equ 1 << BIT_INPUT_B
def INPUT_A equ 1 << BIT_INPUT_A
; Combined input byte, with Control Pad in low nybble (swapped)
def BIT_INPUT_SWAP_START equ BIT_JOYPAD_START + 4
def BIT_INPUT_SWAP_SELECT equ BIT_JOYPAD_SELECT + 4
def BIT_INPUT_SWAP_B equ BIT_JOYPAD_B + 4
def BIT_INPUT_SWAP_A equ BIT_JOYPAD_A + 4
def BIT_INPUT_SWAP_DOWN equ BIT_JOYPAD_DOWN
def BIT_INPUT_SWAP_UP equ BIT_JOYPAD_UP
def BIT_INPUT_SWAP_LEFT equ BIT_JOYPAD_LEFT
def BIT_INPUT_SWAP_RIGHT equ BIT_JOYPAD_RIGHT
def INPUT_SWAP_START equ 1 << BIT_INPUT_SWAP_START
def INPUT_SWAP_SELECT equ 1 << BIT_INPUT_SWAP_SELECT
def INPUT_SWAP_B equ 1 << BIT_INPUT_SWAP_B
def INPUT_SWAP_A equ 1 << BIT_INPUT_SWAP_A
def INPUT_SWAP_DOWN equ 1 << BIT_INPUT_SWAP_DOWN
def INPUT_SWAP_UP equ 1 << BIT_INPUT_SWAP_UP
def INPUT_SWAP_LEFT equ 1 << BIT_INPUT_SWAP_LEFT
def INPUT_SWAP_RIGHT equ 1 << BIT_INPUT_SWAP_RIGHT
; -----------------------------------------------------------------------------
; SERIAL_DATA ($FF01): Serial transfer data [r/w]
; -----------------------------------------------------------------------------
def rSerialData equ $ff01
; -----------------------------------------------------------------------------
; SERIAL CTL ($FF02): Serial transfer control
; -----------------------------------------------------------------------------
def rSerialCtl equ $ff02
def BIT_SERIAL_CTL_TRANSFER equ 7 ; Reading 1 = transfer in progress; writing 1 = start transfer [r/w]
def MASK_SERIAL_CTL_TRANSFER equ 1 << BIT_SERIAL_CTL_TRANSFER
def SERIAL_CTL_TRANSFER_OFF equ 0 << BIT_SERIAL_CTL_TRANSFER
def SERIAL_CTL_TRANSFER_ON equ 1 << BIT_SERIAL_CTL_TRANSFER
def BIT_SERIAL_CTL_CLOCK_SPD equ 1 ; (CGB only) 1 = use TODOx faster clock if master [r/w]
def MASK_SERIAL_CTL_CLOCK_SPD equ 1 << BIT_SERIAL_CTL_CLOCK_SPD
def SERIAL_CTL_LOW_SPD_CLK equ 0 << BIT_SERIAL_CTL_CLK_SPD
def SERIAL_CTL_HIGH_SPD_CLK equ 1 << BIT_SERIAL_CTL_CLK_SPD
def BIT_SERIAL_CTL_CLOCK_SEL equ 0 ; 0 = use external clock ("slave"), 1 = use internal clock ("master") [r/w]
def MASK_SERIAL_CTL_CLOCK_SEL equ 1 << BIT_SERIAL_CTL_CLOCK_SEL
def SERIAL_CTL_EXTERNAL_CLK equ 0 << BIT_SERIAL_CTL_CLK_SEL
def SERIAL_CTL_INTERNAL_CLK equ 1 << BIT_SERIAL_CTL_CLK_SEL
; -----------------------------------------------------------------------------
; $FF03 is unused
; -----------------------------------------------------------------------------
; -----------------------------------------------------------------------------
; CYCLE_CNT ($FF04): Divider register [r/w]
; -----------------------------------------------------------------------------
def rCycleCnt equ $ff04
; -----------------------------------------------------------------------------
; TIMER_CNT ($FF05): Timer counter [r/w]
; -----------------------------------------------------------------------------
def rTimerCnt equ $ff05
; -----------------------------------------------------------------------------
; TIMER_MOD ($FF06): Timer modulo [r/w]
; -----------------------------------------------------------------------------
def rTimerMod equ $ff06
; -----------------------------------------------------------------------------
; TIMER_CTL ($FF07): Timer control
; -----------------------------------------------------------------------------
def rTimerCtl equ $ff07
def BIT_TIMER_CTL_ENABLE equ 2 ; 1 = enable incrementing TIMER_CNT [r/w]
def MASK_TIMER_CTL_ENABLE equ 1 << BIT_TIMER_CTL_ENABLE
def TIMER_CTL_START equ 0 << BIT_TIMER_CTL_ENABLE
def TIMER_CTL_STOP equ 1 << BIT_TIMER_CTL_ENABLE
def MASK_TIMER_CTL_CLOCK_SELECT equ %000000_11 ; the frequency at which TIMER_CNT increments [r/w]
def TIMER_CTL_INC_256_M_CYCLES equ %000000_00 ; every 256 M-cycles = ~4 KHz on DMG
def TIMER_CTL_INC_4_M_CYCLES equ %000000_01 ; every 4 M-cycles = ~262 KHz on DMG
def TIMER_CTL_INC_16_M_CYCLES equ %000000_10 ; every 16 M-cycles = ~65 KHz on DMG
def TIMER_CTL_INC_64_M_CYCLES equ %000000_11 ; every 64 M-cycles = ~16 KHz on DMG
; -----------------------------------------------------------------------------
; $FF08-$FF0E are unused
; -----------------------------------------------------------------------------
; -----------------------------------------------------------------------------
; INTR_PENDING ($FF0F): Pending interrupts
; -----------------------------------------------------------------------------
def rIntrFlg equ $ff0f
def BIT_INTR_PENDING_JOYPAD equ 4 ; 1 = Joypad interrupt is pending [r/w]
def MASK_INTR_PENDING_JOYPAD equ 1 << BIT_INTR_PENDING_JOYPAD
def rINTR_PENDING_JOYPAD_OFF equ 0 << BIT_INTR_PENDING_JOYPAD
def rINTR_PENDING_JOYPAD_ON equ 1 << BIT_INTR_PENDING_JOYPAD
def BIT_INTR_PENDING_SERIAL equ 3 ; 1 = Serial interrupt is pending [r/w]
def MASK_INTR_PENDING_SERIAL equ 1 << BIT_INTR_PENDING_SERIAL
def rINTR_PENDING_SERIAL_OFF equ 0 << BIT_INTR_PENDING_SERIAL
def rINTR_PENDING_SERIAL_ON equ 1 << BIT_INTR_PENDING_SERIAL
def BIT_INTR_PENDING_TIMER equ 2 ; 1 = Timer interrupt is pending [r/w]
def MASK_INTR_PENDING_TIMER equ 1 << BIT_INTR_PENDING_TIMER
def rINTR_PENDING_TIMER_OFF equ 0 << BIT_INTR_PENDING_TIMER
def rINTR_PENDING_TIMER_ON equ 1 << BIT_INTR_PENDING_TIMER
def BIT_INTR_PENDING_LCD equ 1 ; 1 = LCD interrupt is pending [r/w]
def MASK_INTR_PENDING_LCD equ 1 << BIT_INTR_PENDING_LCD
def rINTR_PENDING_LCD_OFF equ 0 << BIT_INTR_PENDING_LCD
def rINTR_PENDING_LCD_ON equ 1 << BIT_INTR_PENDING_LCD
def BIT_INTR_PENDING_VBLANK equ 0 ; 1 = VBlank interrupt is pending [r/w]
def MASK_INTR_PENDING_VBLANK equ 1 << BIT_INTR_PENDING_VBLANK
def rINTR_PENDING_VBLANK_OFF equ 0 << BIT_INTR_PENDING_VBLANK
def rINTR_PENDING_VBLANK_ON equ 1 << BIT_INTR_PENDING_VBLANK
; -----------------------------------------------------------------------------
; AUD1_SWEEP ($FF10): Audio channel 1 sweep
; -----------------------------------------------------------------------------
def rAud1Sweep equ $ff10
def MASK_AUD1_SWEEP_PACE equ %0_111_0000 ; how long between sweep iterations [r/w]
; (in 128 Hz ticks, ~ 7.8 ms apart)
def BIT_AUD1_SWEEP_DIR equ 3 ; sweep direction [r/w]
def MASK_AUD1_SWEEP_DIR equ 1 << BIT_AUD1_SWEEP_DIR
def AUD1_SWEEP_UP equ 0 << BIT_AUD1_SWEEP_DIR
def AUD1_SWEEP_DOWN equ 1 << BIT_AUD1_SWEEP_DIR
def MASK_AUD1_SWEEP_STEP equ %00000_111 ; by how much the period inc-/decreases per iteration [r/w]
; -----------------------------------------------------------------------------
; AUD1_LEN ($FF11): Audio channel 1 length timer and duty cycle
; -----------------------------------------------------------------------------
def rAud1Len equ $ff11
def MASK_AUD1_LEN_DUTY equ %11_000000 ; ratio of time spent high vs. time spent low [r/w]
def AUD1_LEN_DUTY_12_5 equ %00_000000 ; 12.5%
def AUD1_LEN_DUTY_25 equ %01_000000 ; 25%
def AUD1_LEN_DUTY_50 equ %10_000000 ; 50%
def AUD1_LEN_DUTY_75 equ %11_000000 ; 75%
def MASK_AUD1_LEN_TIMER equ %00_111111 ; initial length timer [wo]
; -----------------------------------------------------------------------------
; AUD1_ENV/NR12 ($FF12): Audio channel 1 volume and envelope
; -----------------------------------------------------------------------------
def rAud1Env equ $ff12
def MASK_AUD1_ENV_VOLUME equ %1111_0000 ; initial volume [r/w]
def BIT_AUD1_ENV_DIR equ 3 ; direction of volume envelope [r/w]
def MASK_AUD1_ENV_DIR equ 1 << BIT_AUD1_ENV_DIR
def AUD1_ENV_DOWN equ 0 << BIT_AUD1_ENV_DIR
def AUD1_ENV_UP equ 1 << BIT_AUD1_ENV_DIR
def MASK_AUD1_ENV_PACE equ %00000_111 ; how long between envelope iterations [r/w]
; (in TODO Hz ticks, ~ TODO apart)
; -----------------------------------------------------------------------------
; TODO
endc ; def(HARDWARE_INC)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment