diff --git a/.gitignore b/.gitignore index a6d31fb..0471227 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,6 @@ /obj/ /dep/ /assets/ + +src/*.2bpp +src/*.tilemap \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..062ee96 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,18 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + + { + "type": "emulicious-debugger", + "request": "launch", + "name": "Launch in Emulicious", + "program": "./bin/binx.gb", + "host": "172.19.64.1", + "port": 58870, + "stopOnEntry": true + } + ] +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index acda1e6..9e2f4d4 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -2,6 +2,7 @@ "rgbdsz80.includePath": [ "src/", "src/constants", - "src/macros" + "src/macros", + "include" ] } diff --git a/LICENSE-gb-boilerplate b/LICENSE-gb-boilerplate deleted file mode 100644 index 28732c9..0000000 --- a/LICENSE-gb-boilerplate +++ /dev/null @@ -1,19 +0,0 @@ -zlib License - -Copyright (c) 2020-2022 Eldred Habert - -This software is provided 'as-is', without any express or implied -warranty. In no event will the authors be held liable for any damages -arising from the use of this software. - -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute it -freely, subject to the following restrictions: - -1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. -2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. -3. This notice may not be removed or altered from any source distribution. diff --git a/include/hardware.inc b/include/hardware.inc index 1ba2c22..206f0e9 100644 --- a/include/hardware.inc +++ b/include/hardware.inc @@ -22,7 +22,7 @@ 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 "4.10.0" +def HARDWARE_INC_VERSION equs "5.3.0" ; Usage: rev_Check_hardware_inc ; Examples: @@ -30,16 +30,16 @@ def HARDWARE_INC_VERSION equs "4.10.0" ; rev_Check_hardware_inc 1.2 (equivalent to 1.2.0) ; rev_Check_hardware_inc 1 (equivalent to 1.0.0) MACRO rev_Check_hardware_inc - def hw_inc_cur_ver\@ equs strrpl("{HARDWARE_INC_VERSION}", ".", ",") - def hw_inc_min_ver\@ equs strrpl("\1", ".", ",") - def hw_inc_def_check\@ equs """MACRO hw_inc_check\@ - if \\1 != \\4 || (\\2 < \\5 || (\\2 == \\5 && \\3 < \\6)) - fail "Version \\1.\\2.\\3 of 'hardware.inc' is incompatible with requested version \\4.\\5.\\6" + if _NARG == 1 ; Actual invocation by the user + def hw_inc_cur_ver\@ equs strrpl("{HARDWARE_INC_VERSION}", ".", ",") + def hw_inc_min_ver\@ equs strrpl("\1", ".", ",") + rev_Check_hardware_inc {hw_inc_cur_ver\@}, {hw_inc_min_ver\@}, 0, 0 + purge hw_inc_cur_ver\@, hw_inc_min_ver\@ + else ; Recursive invocation + if \1 != \4 || (\2 < \5 || (\2 == \5 && \3 < \6)) + fail "Version \1.\2.\3 of 'hardware.inc' is incompatible with requested version \4.\5.\6" endc - \nENDM""" - hw_inc_def_check\@ - hw_inc_check\@ {hw_inc_cur_ver\@}, {hw_inc_min_ver\@}, 0, 0 - purge hw_inc_cur_ver\@, hw_inc_min_ver\@, hw_inc_def_check\@, hw_inc_check\@ + endc ENDM @@ -51,66 +51,78 @@ ENDM ; Joypad face buttons def rJOYP equ $FF00 -def JOYPB_GET_BTN equ 5 ; 0 = reading buttons [r/w] -def JOYPB_GET_DPAD equ 4 ; 0 = reading Control Pad [r/w] - def JOYPF_GET equ %00_11_0000 ; select which inputs to read from the lower nybble - def JOYP_GET_BTN equ %00_01_0000 ; reading A/B/Select/Start buttons - def JOYP_GET_DPAD equ %00_10_0000 ; reading Control Pad directions - def JOYP_GET_NONE equ %00_11_0000 ; reading nothing +def B_JOYP_GET_BUTTONS equ 5 ; 0 = reading buttons [r/w] +def B_JOYP_GET_CTRL_PAD equ 4 ; 0 = reading Control Pad [r/w] + def JOYP_GET equ %00_11_0000 ; select which inputs to read from the lower nybble + def JOYP_GET_BUTTONS equ %00_01_0000 ; reading A/B/Select/Start buttons + def JOYP_GET_CTRL_PAD equ %00_10_0000 ; reading Control Pad directions + def JOYP_GET_NONE equ %00_11_0000 ; reading nothing -def JOYPB_START equ 3 ; 0 = Start is pressed (if reading buttons) [ro] -def JOYPB_SELECT equ 2 ; 0 = Select is pressed (if reading buttons) [ro] -def JOYPB_B equ 1 ; 0 = B is pressed (if reading buttons) [ro] -def JOYPB_A equ 0 ; 0 = A is pressed (if reading buttons) [ro] -def JOYPB_DOWN equ 3 ; 0 = Down is pressed (if reading Control Pad) [ro] -def JOYPB_UP equ 2 ; 0 = Up is pressed (if reading Control Pad) [ro] -def JOYPB_LEFT equ 1 ; 0 = Left is pressed (if reading Control Pad) [ro] -def JOYPB_RIGHT equ 0 ; 0 = Right is pressed (if reading Control Pad) [ro] - def JOYPF_INPUTS equ %0000_1111 - def JOYPF_START equ 1 << JOYPB_START - def JOYPF_SELECT equ 1 << JOYPB_SELECT - def JOYPF_B equ 1 << JOYPB_B - def JOYPF_A equ 1 << JOYPB_A - def JOYPF_DOWN equ 1 << JOYPB_DOWN - def JOYPF_UP equ 1 << JOYPB_UP - def JOYPF_LEFT equ 1 << JOYPB_LEFT - def JOYPF_RIGHT equ 1 << JOYPB_RIGHT +def B_JOYP_START equ 3 ; 0 = Start is pressed (if reading buttons) [ro] +def B_JOYP_SELECT equ 2 ; 0 = Select is pressed (if reading buttons) [ro] +def B_JOYP_B equ 1 ; 0 = B is pressed (if reading buttons) [ro] +def B_JOYP_A equ 0 ; 0 = A is pressed (if reading buttons) [ro] +def B_JOYP_DOWN equ 3 ; 0 = Down is pressed (if reading Control Pad) [ro] +def B_JOYP_UP equ 2 ; 0 = Up is pressed (if reading Control Pad) [ro] +def B_JOYP_LEFT equ 1 ; 0 = Left is pressed (if reading Control Pad) [ro] +def B_JOYP_RIGHT equ 0 ; 0 = Right is pressed (if reading Control Pad) [ro] + def JOYP_INPUTS equ %0000_1111 ; bits equal to 0 indicate pressed (when reading inputs) + def JOYP_START equ 1 << B_JOYP_START + def JOYP_SELECT equ 1 << B_JOYP_SELECT + def JOYP_B equ 1 << B_JOYP_B + def JOYP_A equ 1 << B_JOYP_A + def JOYP_DOWN equ 1 << B_JOYP_DOWN + def JOYP_UP equ 1 << B_JOYP_UP + def JOYP_LEFT equ 1 << B_JOYP_LEFT + def JOYP_RIGHT equ 1 << B_JOYP_RIGHT + +; SGB command packet transfer uses for JOYP bits +def B_JOYP_SGB_ONE equ 5 ; 0 = sending 1 bit +def B_JOYP_SGB_ZERO equ 4 ; 0 = sending 0 bit + def JOYP_SGB_START equ %00_00_0000 ; start SGB packet transfer + def JOYP_SGB_ONE equ %00_01_0000 ; send 1 bit + def JOYP_SGB_ZERO equ %00_10_0000 ; send 0 bit + def JOYP_SGB_FINISH equ %00_11_0000 ; finish SGB packet transfer ; Combined input byte, with Control Pad in high nybble (conventional order) -def PADB_DOWN equ 7 -def PADB_UP equ 6 -def PADB_LEFT equ 5 -def PADB_RIGHT equ 4 -def PADB_START equ 3 -def PADB_SELECT equ 2 -def PADB_B equ 1 -def PADB_A equ 0 - def PADF_DOWN equ 1 << PADB_DOWN - def PADF_UP equ 1 << PADB_UP - def PADF_LEFT equ 1 << PADB_LEFT - def PADF_RIGHT equ 1 << PADB_RIGHT - def PADF_START equ 1 << PADB_START - def PADF_SELECT equ 1 << PADB_SELECT - def PADF_B equ 1 << PADB_B - def PADF_A equ 1 << PADB_A +def B_PAD_DOWN equ 7 +def B_PAD_UP equ 6 +def B_PAD_LEFT equ 5 +def B_PAD_RIGHT equ 4 +def B_PAD_START equ 3 +def B_PAD_SELECT equ 2 +def B_PAD_B equ 1 +def B_PAD_A equ 0 + def PAD_CTRL_PAD equ %1111_0000 + def PAD_BUTTONS equ %0000_1111 + def PAD_DOWN equ 1 << B_PAD_DOWN + def PAD_UP equ 1 << B_PAD_UP + def PAD_LEFT equ 1 << B_PAD_LEFT + def PAD_RIGHT equ 1 << B_PAD_RIGHT + def PAD_START equ 1 << B_PAD_START + def PAD_SELECT equ 1 << B_PAD_SELECT + def PAD_B equ 1 << B_PAD_B + def PAD_A equ 1 << B_PAD_A ; Combined input byte, with Control Pad in low nybble (swapped order) -def PADB_SWAP_START equ 7 -def PADB_SWAP_SELECT equ 6 -def PADB_SWAP_B equ 5 -def PADB_SWAP_A equ 4 -def PADB_SWAP_DOWN equ 3 -def PADB_SWAP_UP equ 2 -def PADB_SWAP_LEFT equ 1 -def PADB_SWAP_RIGHT equ 0 - def PADF_SWAP_START equ 1 << PADB_SWAP_START - def PADF_SWAP_SELECT equ 1 << PADB_SWAP_SELECT - def PADF_SWAP_B equ 1 << PADB_SWAP_B - def PADF_SWAP_A equ 1 << PADB_SWAP_A - def PADF_SWAP_DOWN equ 1 << PADB_SWAP_DOWN - def PADF_SWAP_UP equ 1 << PADB_SWAP_UP - def PADF_SWAP_LEFT equ 1 << PADB_SWAP_LEFT - def PADF_SWAP_RIGHT equ 1 << PADB_SWAP_RIGHT +def B_PAD_SWAP_START equ 7 +def B_PAD_SWAP_SELECT equ 6 +def B_PAD_SWAP_B equ 5 +def B_PAD_SWAP_A equ 4 +def B_PAD_SWAP_DOWN equ 3 +def B_PAD_SWAP_UP equ 2 +def B_PAD_SWAP_LEFT equ 1 +def B_PAD_SWAP_RIGHT equ 0 + def PAD_SWAP_CTRL_PAD equ %0000_1111 + def PAD_SWAP_BUTTONS equ %1111_0000 + def PAD_SWAP_START equ 1 << B_PAD_SWAP_START + def PAD_SWAP_SELECT equ 1 << B_PAD_SWAP_SELECT + def PAD_SWAP_B equ 1 << B_PAD_SWAP_B + def PAD_SWAP_A equ 1 << B_PAD_SWAP_A + def PAD_SWAP_DOWN equ 1 << B_PAD_SWAP_DOWN + def PAD_SWAP_UP equ 1 << B_PAD_SWAP_UP + def PAD_SWAP_LEFT equ 1 << B_PAD_SWAP_LEFT + def PAD_SWAP_RIGHT equ 1 << B_PAD_SWAP_RIGHT ; -- SB ($FF01) --------------------------------------------------------------- ; Serial transfer data [r/w] @@ -120,16 +132,16 @@ def rSB equ $FF01 ; Serial transfer control def rSC equ $FF02 -def SCB_START equ 7 ; reading 1 = transfer in progress, writing 1 = start transfer [r/w] -def SCB_SPEED equ 1 ; (CGB only) 1 = use faster internal clock [r/w] -def SCB_SOURCE equ 0 ; 0 = use external clock ("slave"), 1 = use internal clock ("master") [r/w] - def SCF_START equ 1 << SCB_START - def SCF_SPEED equ 1 << SCB_SPEED - def SC_SLOW equ 0 << SCB_SPEED - def SC_FAST equ 1 << SCB_SPEED - def SCF_SOURCE equ 1 << SCB_SOURCE - def SC_EXTERNAL equ 0 << SCB_SOURCE - def SC_INTERNAL equ 1 << SCB_SOURCE +def B_SC_START equ 7 ; reading 1 = transfer in progress, writing 1 = start transfer [r/w] +def B_SC_SPEED equ 1 ; (CGB only) 1 = use faster internal clock [r/w] +def B_SC_SOURCE equ 0 ; 0 = use external clock ("slave"), 1 = use internal clock ("master") [r/w] + def SC_START equ 1 << B_SC_START + def SC_SPEED equ 1 << B_SC_SPEED + def SC_SLOW equ 0 << B_SC_SPEED + def SC_FAST equ 1 << B_SC_SPEED + def SC_SOURCE equ 1 << B_SC_SOURCE + def SC_EXTERNAL equ 0 << B_SC_SOURCE + def SC_INTERNAL equ 1 << B_SC_SOURCE ; -- $FF03 is unused ---------------------------------------------------------- @@ -149,15 +161,15 @@ def rTMA equ $FF06 ; Timer control def rTAC equ $FF07 -def TACB_START equ 2 ; enable incrementing TIMA [r/w] - def TACF_STOP equ 0 << TACB_START - def TACF_START equ 1 << TACB_START +def B_TAC_START equ 2 ; enable incrementing TIMA [r/w] + def TAC_STOP equ 0 << B_TAC_START + def TAC_START equ 1 << B_TAC_START -def TACF_CLOCK equ %000000_11 ; the frequency at which TIMER_CNT increments [r/w] - def TACF_4KHZ equ %000000_00 ; every 256 M-cycles = ~4 KHz on DMG - def TACF_262KHZ equ %000000_01 ; every 4 M-cycles = ~262 KHz on DMG - def TACF_65KHZ equ %000000_10 ; every 16 M-cycles = ~65 KHz on DMG - def TACF_16KHZ equ %000000_11 ; every 64 M-cycles = ~16 KHz on DMG +def TAC_CLOCK equ %000000_11 ; the frequency at which TIMA increments [r/w] + def TAC_4KHZ equ %000000_00 ; every 256 M-cycles = ~4 KHz on DMG + def TAC_262KHZ equ %000000_01 ; every 4 M-cycles = ~262 KHz on DMG + def TAC_65KHZ equ %000000_10 ; every 16 M-cycles = ~65 KHz on DMG + def TAC_16KHZ equ %000000_11 ; every 64 M-cycles = ~16 KHz on DMG ; -- $FF08-$FF0E are unused --------------------------------------------------- @@ -165,28 +177,28 @@ def TACF_CLOCK equ %000000_11 ; the frequency at which TIMER_CNT increments [r/w ; Pending interrupts def rIF equ $FF0F -def IFB_JOYPAD equ 4 ; 1 = joypad interrupt is pending [r/w] -def IFB_SERIAL equ 3 ; 1 = serial interrupt is pending [r/w] -def IFB_TIMER equ 2 ; 1 = timer interrupt is pending [r/w] -def IFB_STAT equ 1 ; 1 = STAT interrupt is pending [r/w] -def IFB_VBLANK equ 0 ; 1 = VBlank interrupt is pending [r/w] - def IFF_JOYPAD equ 1 << IFB_JOYPAD - def IFF_SERIAL equ 1 << IFB_SERIAL - def IFF_TIMER equ 1 << IFB_TIMER - def IFF_STAT equ 1 << IFB_STAT - def IFF_VBLANK equ 1 << IFB_VBLANK +def B_IF_JOYPAD equ 4 ; 1 = joypad interrupt is pending [r/w] +def B_IF_SERIAL equ 3 ; 1 = serial interrupt is pending [r/w] +def B_IF_TIMER equ 2 ; 1 = timer interrupt is pending [r/w] +def B_IF_STAT equ 1 ; 1 = STAT interrupt is pending [r/w] +def B_IF_VBLANK equ 0 ; 1 = VBlank interrupt is pending [r/w] + def IF_JOYPAD equ 1 << B_IF_JOYPAD + def IF_SERIAL equ 1 << B_IF_SERIAL + def IF_TIMER equ 1 << B_IF_TIMER + def IF_STAT equ 1 << B_IF_STAT + def IF_VBLANK equ 1 << B_IF_VBLANK ; -- AUD1SWEEP / NR10 ($FF10) ------------------------------------------------- ; Audio channel 1 sweep def rAUD1SWEEP equ $FF10 -def AUD1SWEEPF_TIME equ %0_111_0000 ; how long between sweep iterations - ; (in 128 Hz ticks, ~7.8 ms apart) [r/w] +def AUD1SWEEP_TIME equ %0_111_0000 ; how long between sweep iterations + ; (in 128 Hz ticks, ~7.8 ms apart) [r/w] -def AUD1SWEEPB_DIR equ 3 ; sweep direction [r/w] - def AUD1SWEEPF_DIR equ 1 << AUD1SWEEPB_DIR - def AUD1SWEEP_UP equ 0 << AUD1SWEEPB_DIR - def AUD1SWEEP_DOWN equ 1 << AUD1SWEEPB_DIR +def B_AUD1SWEEP_DIR equ 3 ; sweep direction [r/w] + def AUD1SWEEP_DIR equ 1 << B_AUD1SWEEP_DIR + def AUD1SWEEP_UP equ 0 << B_AUD1SWEEP_DIR + def AUD1SWEEP_DOWN equ 1 << B_AUD1SWEEP_DIR def AUD1SWEEP_SHIFT equ %00000_111 ; how much the period increases/decreases per iteration [r/w] @@ -194,49 +206,43 @@ def AUD1SWEEP_SHIFT equ %00000_111 ; how much the period increases/decreases per ; Audio channel 1 length timer and duty cycle def rAUD1LEN equ $FF11 -; These values are also applicable to AUD2LEN -def AUDLENF_DUTY equ %11_000000 ; ratio of time spent high vs. time spent low [r/w] - def AUDLEN_DUTY_12_5 equ %00_000000 ; 12.5% - def AUDLEN_DUTY_25 equ %01_000000 ; 25% - def AUDLEN_DUTY_50 equ %10_000000 ; 50% - def AUDLEN_DUTY_75 equ %11_000000 ; 75% +def AUD1LEN_DUTY equ %11_000000 ; ratio of time spent high vs. time spent low [r/w] + def AUD1LEN_DUTY_12_5 equ %00_000000 ; 12.5% + def AUD1LEN_DUTY_25 equ %01_000000 ; 25% + def AUD1LEN_DUTY_50 equ %10_000000 ; 50% + def AUD1LEN_DUTY_75 equ %11_000000 ; 75% -; This value is also applicable to AUD2LEN and AUD4LEN -def AUDLENF_TIMER equ %00_111111 ; initial length timer (0-63) [wo] +def AUD1LEN_TIMER equ %00_111111 ; initial length timer (0-63) [wo] ; -- AUD1ENV / NR12 ($FF12) --------------------------------------------------- ; Audio channel 1 volume and envelope def rAUD1ENV equ $FF12 -; Values are also applicable to AUD2ENV and AUD4ENV +def AUD1ENV_INIT_VOLUME equ %1111_0000 ; initial volume [r/w] -def AUDENVF_INIT_VOL equ %1111_0000 ; initial volume [r/w] +def B_AUD1ENV_DIR equ 3 ; direction of volume envelope [r/w] + def AUD1ENV_DIR equ 1 << B_AUD1ENV_DIR + def AUD1ENV_DOWN equ 0 << B_AUD1ENV_DIR + def AUD1ENV_UP equ 1 << B_AUD1ENV_DIR -def AUDENVB_DIR equ 3 ; direction of volume envelope [r/w] - def AUDENVF_DIR equ 1 << AUDENVB_DIR - def AUDENV_DOWN equ 0 << AUDENVB_DIR - def AUDENV_UP equ 1 << AUDENVB_DIR - -def AUDENVF_PACE equ %00000_111 ; how long between envelope iterations +def AUD1ENV_PACE equ %00000_111 ; how long between envelope iterations ; (in 64 Hz ticks, ~15.6 ms apart) [r/w] ; -- AUD1LOW / NR13 ($FF13) --------------------------------------------------- -; Audio channel 1 period (low 8 bits) [r/w] +; Audio channel 1 period (low 8 bits) [wo] def rAUD1LOW equ $FF13 ; -- AUD1HIGH / NR14 ($FF14) -------------------------------------------------- ; Audio channel 1 period (high 3 bits) and control def rAUD1HIGH equ $FF14 -; Values are also applicable to AUD2HIGH and AUD3HIGH +def B_AUD1HIGH_RESTART equ 7 ; 1 = restart the channel [wo] +def B_AUD1HIGH_LEN_ENABLE equ 6 ; 1 = reset the channel after the length timer expires [r/w] + def AUD1HIGH_RESTART equ 1 << B_AUD1HIGH_RESTART + def AUD1HIGH_LENGTH_OFF equ 0 << B_AUD1HIGH_LEN_ENABLE + def AUD1HIGH_LENGTH_ON equ 1 << B_AUD1HIGH_LEN_ENABLE -def AUDHIGHB_RESTART equ 7 ; 1 = restart the channel [wo] -def AUDHIGHB_LEN_ENABLE equ 6 ; 1 = reset the channel after the length timer expires [r/w] - def AUDHIGH_RESTART equ 1 << AUDHIGHB_RESTART - def AUDHIGH_LENGTH_OFF equ 0 << AUDHIGHB_LEN_ENABLE - def AUDHIGH_LENGTH_ON equ 1 << AUDHIGHB_LEN_ENABLE - -def AUDHIGHF_PERIOD_HIGH equ %00000_111 ; upper 3 bits of the channel's period [r/w] +def AUD1HIGH_PERIOD_HIGH equ %00000_111 ; upper 3 bits of the channel's period [wo] ; -- $FF15 is unused ---------------------------------------------------------- @@ -244,31 +250,51 @@ def AUDHIGHF_PERIOD_HIGH equ %00000_111 ; upper 3 bits of the channel's period [ ; Audio channel 2 length timer and duty cycle def rAUD2LEN equ $FF16 -; Values are reused from AUD1LEN +def AUD2LEN_DUTY equ %11_000000 ; ratio of time spent high vs. time spent low [r/w] + def AUD2LEN_DUTY_12_5 equ %00_000000 ; 12.5% + def AUD2LEN_DUTY_25 equ %01_000000 ; 25% + def AUD2LEN_DUTY_50 equ %10_000000 ; 50% + def AUD2LEN_DUTY_75 equ %11_000000 ; 75% + +def AUD2LEN_TIMER equ %00_111111 ; initial length timer (0-63) [wo] ; -- AUD2ENV / NR22 ($FF17) --------------------------------------------------- ; Audio channel 2 volume and envelope def rAUD2ENV equ $FF17 -; Values are reused from AUD1ENV +def AUD2ENV_INIT_VOLUME equ %1111_0000 ; initial volume [r/w] + +def B_AUD2ENV_DIR equ 3 ; direction of volume envelope [r/w] + def AUD2ENV_DIR equ 1 << B_AUD2ENV_DIR + def AUD2ENV_DOWN equ 0 << B_AUD2ENV_DIR + def AUD2ENV_UP equ 1 << B_AUD2ENV_DIR + +def AUD2ENV_PACE equ %00000_111 ; how long between envelope iterations + ; (in 64 Hz ticks, ~15.6 ms apart) [r/w] ; -- AUD2LOW / NR23 ($FF18) --------------------------------------------------- -; Audio channel 2 period (low 8 bits) [r/w] +; Audio channel 2 period (low 8 bits) [wo] def rAUD2LOW equ $FF18 ; -- AUD2HIGH / NR24 ($FF19) -------------------------------------------------- ; Audio channel 2 period (high 3 bits) and control def rAUD2HIGH equ $FF19 -; Values are reused from AUD1HIGH +def B_AUD2HIGH_RESTART equ 7 ; 1 = restart the channel [wo] +def B_AUD2HIGH_LEN_ENABLE equ 6 ; 1 = reset the channel after the length timer expires [r/w] + def AUD2HIGH_RESTART equ 1 << B_AUD2HIGH_RESTART + def AUD2HIGH_LENGTH_OFF equ 0 << B_AUD2HIGH_LEN_ENABLE + def AUD2HIGH_LENGTH_ON equ 1 << B_AUD2HIGH_LEN_ENABLE + +def AUD2HIGH_PERIOD_HIGH equ %00000_111 ; upper 3 bits of the channel's period [wo] ; -- AUD3ENA / NR30 ($FF1A) --------------------------------------------------- ; Audio channel 3 enable def rAUD3ENA equ $FF1A -def AUD3ENAB_ENABLE equ 7 ; 1 = channel is active [r/w] - def AUD3ENA_OFF equ 0 << AUD3ENAB_ENABLE - def AUD3ENA_ON equ 1 << AUD3ENAB_ENABLE +def B_AUD3ENA_ENABLE equ 7 ; 1 = channel is active [r/w] + def AUD3ENA_OFF equ 0 << B_AUD3ENA_ENABLE + def AUD3ENA_ON equ 1 << B_AUD3ENA_ENABLE ; -- AUD3LEN / NR31 ($FF1B) --------------------------------------------------- ; Audio channel 3 length timer [wo] @@ -278,119 +304,131 @@ def rAUD3LEN equ $FF1B ; Audio channel 3 volume def rAUD3LEVEL equ $FF1C -def AUD3LEVELF_VOLUME equ %0_11_00000 ; volume level [r/w] +def AUD3LEVEL_VOLUME equ %0_11_00000 ; volume level [r/w] def AUD3LEVEL_MUTE equ %0_00_00000 ; 0% (muted) def AUD3LEVEL_100 equ %0_01_00000 ; 100% def AUD3LEVEL_50 equ %0_10_00000 ; 50% def AUD3LEVEL_25 equ %0_11_00000 ; 25% ; -- AUD3LOW / NR33 ($FF1D) --------------------------------------------------- -; Audio channel 3 period (low 8 bits) [r/w] +; Audio channel 3 period (low 8 bits) [wo] def rAUD3LOW equ $FF1D ; -- AUD3HIGH / NR34 ($FF1E) -------------------------------------------------- ; Audio channel 3 period (high 3 bits) and control def rAUD3HIGH equ $FF1E -; Values are reused from AUD1HIGH +def B_AUD3HIGH_RESTART equ 7 ; 1 = restart the channel [wo] +def B_AUD3HIGH_LEN_ENABLE equ 6 ; 1 = reset the channel after the length timer expires [r/w] + def AUD3HIGH_RESTART equ 1 << B_AUD3HIGH_RESTART + def AUD3HIGH_LENGTH_OFF equ 0 << B_AUD3HIGH_LEN_ENABLE + def AUD3HIGH_LENGTH_ON equ 1 << B_AUD3HIGH_LEN_ENABLE + +def AUD3HIGH_PERIOD_HIGH equ %00000_111 ; upper 3 bits of the channel's period [wo] ; -- $FF1F is unused ---------------------------------------------------------- ; -- AUD4LEN / NR41 ($FF20) --------------------------------------------------- -; Audio channel 4 length timer [wo] +; Audio channel 4 length timer def rAUD4LEN equ $FF20 -; AUDLENF_TIMER value is reused from AUD1LEN +def AUD4LEN_TIMER equ %00_111111 ; initial length timer (0-63) [wo] ; -- AUD4ENV / NR42 ($FF21) --------------------------------------------------- ; Audio channel 4 volume and envelope def rAUD4ENV equ $FF21 -; Values are reused from AUD1ENV +def AUD4ENV_INIT_VOLUME equ %1111_0000 ; initial volume [r/w] + +def B_AUD4ENV_DIR equ 3 ; direction of volume envelope [r/w] + def AUD4ENV_DIR equ 1 << B_AUD4ENV_DIR + def AUD4ENV_DOWN equ 0 << B_AUD4ENV_DIR + def AUD4ENV_UP equ 1 << B_AUD4ENV_DIR + +def AUD4ENV_PACE equ %00000_111 ; how long between envelope iterations + ; (in 64 Hz ticks, ~15.6 ms apart) [r/w] ; -- AUD4POLY / NR43 ($FF22) -------------------------------------------------- ; Audio channel 4 period and randomness def rAUD4POLY equ $FF22 -def AUD4POLYF_SHIFT equ %1111_0000 ; coarse control of the channel's period [r/w] +def AUD4POLY_SHIFT equ %1111_0000 ; coarse control of the channel's period [r/w] -def AUD4POLYB_WIDTH equ 3 ; controls the noise generator (LFSR)'s step width [r/w] - def AUD4POLY_15STEP equ 0 << AUD4POLYB_WIDTH - def AUD4POLY_7STEP equ 1 << AUD4POLYB_WIDTH +def B_AUD4POLY_WIDTH equ 3 ; controls the noise generator (LFSR)'s step width [r/w] + def AUD4POLY_15STEP equ 0 << B_AUD4POLY_WIDTH + def AUD4POLY_7STEP equ 1 << B_AUD4POLY_WIDTH -def AUD4POLYF_DIV equ %00000_111 ; fine control of the channel's period [r/w] +def AUD4POLY_DIV equ %00000_111 ; fine control of the channel's period [r/w] ; -- AUD4GO / NR44 ($FF23) ---------------------------------------------------- ; Audio channel 4 control def rAUD4GO equ $FF23 -def AUD4GOB_RESTART equ 7 ; 1 = restart the channel [wo] -def AUD4GOB_LEN_ENABLE equ 6 ; 1 = reset the channel after the length timer expires [r/w] - def AUD4GO_RESTART equ 1 << AUD4GOB_RESTART - def AUD4GO_LENGTH_OFF equ 0 << AUD4GOB_LEN_ENABLE - def AUD4GO_LENGTH_ON equ 1 << AUD4GOB_LEN_ENABLE +def B_AUD4GO_RESTART equ 7 ; 1 = restart the channel [wo] +def B_AUD4GO_LEN_ENABLE equ 6 ; 1 = reset the channel after the length timer expires [r/w] + def AUD4GO_RESTART equ 1 << B_AUD4GO_RESTART + def AUD4GO_LENGTH_OFF equ 0 << B_AUD4GO_LEN_ENABLE + def AUD4GO_LENGTH_ON equ 1 << B_AUD4GO_LEN_ENABLE ; -- AUDVOL / NR50 ($FF24) ---------------------------------------------------- ; Audio master volume and VIN mixer def rAUDVOL equ $FF24 -def AUDVOLB_VIN_LEFT equ 7 ; 1 = output VIN to left ear (SO2, speaker 2) [r/w] - def AUDVOL_VIN_LEFT equ 1 << AUDVOLB_VIN_LEFT +def B_AUDVOL_VIN_LEFT equ 7 ; 1 = output VIN to left ear (SO2, speaker 2) [r/w] + def AUDVOL_VIN_LEFT equ 1 << B_AUDVOL_VIN_LEFT -def AUDVOLF_LEFT equ %0_111_0000 ; 0 = barely audible, 7 = full volume [r/w] +def AUDVOL_LEFT equ %0_111_0000 ; 0 = barely audible, 7 = full volume [r/w] -def AUDVOLB_VIN_RIGHT equ 3 ; 1 = output VIN to right ear (SO1, speaker 1) [r/w] - def AUDVOL_VIN_RIGHT equ 1 << AUDVOLB_VIN_RIGHT +def B_AUDVOL_VIN_RIGHT equ 3 ; 1 = output VIN to right ear (SO1, speaker 1) [r/w] + def AUDVOL_VIN_RIGHT equ 1 << B_AUDVOL_VIN_RIGHT -def AUDVOLF_RIGHT equ %00000_111 ; 0 = barely audible, 7 = full volume [r/w] +def AUDVOL_RIGHT equ %00000_111 ; 0 = barely audible, 7 = full volume [r/w] ; -- AUDTERM / NR51 ($FF25) --------------------------------------------------- ; Audio channel mixer def rAUDTERM equ $FF25 -def AUDTERMB_4_LEFT equ 7 ; 1 = output channel 4 to left ear [r/w] -def AUDTERMB_3_LEFT equ 6 ; 1 = output channel 3 to left ear [r/w] -def AUDTERMB_2_LEFT equ 5 ; 1 = output channel 2 to left ear [r/w] -def AUDTERMB_1_LEFT equ 4 ; 1 = output channel 1 to left ear [r/w] -def AUDTERMB_4_RIGHT equ 3 ; 1 = output channel 4 to right ear [r/w] -def AUDTERMB_3_RIGHT equ 2 ; 1 = output channel 3 to right ear [r/w] -def AUDTERMB_2_RIGHT equ 1 ; 1 = output channel 2 to right ear [r/w] -def AUDTERMB_1_RIGHT equ 0 ; 1 = output channel 1 to right ear [r/w] - def AUDTERM_4_LEFT equ 1 << AUDTERMB_4_LEFT - def AUDTERM_3_LEFT equ 1 << AUDTERMB_3_LEFT - def AUDTERM_2_LEFT equ 1 << AUDTERMB_2_LEFT - def AUDTERM_1_LEFT equ 1 << AUDTERMB_1_LEFT - def AUDTERM_4_RIGHT equ 1 << AUDTERMB_4_RIGHT - def AUDTERM_3_RIGHT equ 1 << AUDTERMB_3_RIGHT - def AUDTERM_2_RIGHT equ 1 << AUDTERMB_2_RIGHT - def AUDTERM_1_RIGHT equ 1 << AUDTERMB_1_RIGHT +def B_AUDTERM_4_LEFT equ 7 ; 1 = output channel 4 to left ear [r/w] +def B_AUDTERM_3_LEFT equ 6 ; 1 = output channel 3 to left ear [r/w] +def B_AUDTERM_2_LEFT equ 5 ; 1 = output channel 2 to left ear [r/w] +def B_AUDTERM_1_LEFT equ 4 ; 1 = output channel 1 to left ear [r/w] +def B_AUDTERM_4_RIGHT equ 3 ; 1 = output channel 4 to right ear [r/w] +def B_AUDTERM_3_RIGHT equ 2 ; 1 = output channel 3 to right ear [r/w] +def B_AUDTERM_2_RIGHT equ 1 ; 1 = output channel 2 to right ear [r/w] +def B_AUDTERM_1_RIGHT equ 0 ; 1 = output channel 1 to right ear [r/w] + def AUDTERM_4_LEFT equ 1 << B_AUDTERM_4_LEFT + def AUDTERM_3_LEFT equ 1 << B_AUDTERM_3_LEFT + def AUDTERM_2_LEFT equ 1 << B_AUDTERM_2_LEFT + def AUDTERM_1_LEFT equ 1 << B_AUDTERM_1_LEFT + def AUDTERM_4_RIGHT equ 1 << B_AUDTERM_4_RIGHT + def AUDTERM_3_RIGHT equ 1 << B_AUDTERM_3_RIGHT + def AUDTERM_2_RIGHT equ 1 << B_AUDTERM_2_RIGHT + def AUDTERM_1_RIGHT equ 1 << B_AUDTERM_1_RIGHT ; -- AUDENA / NR52 ($FF26) ---------------------------------------------------- ; Audio master enable def rAUDENA equ $FF26 -def AUDENAB_ENABLE equ 7 ; 0 = disable the APU (resets all audio registers to 0!) [r/w] -def AUDENAB_ENABLE_CH4 equ 3 ; 1 = channel 4 is running [ro] -def AUDENAB_ENABLE_CH3 equ 2 ; 1 = channel 3 is running [ro] -def AUDENAB_ENABLE_CH2 equ 1 ; 1 = channel 2 is running [ro] -def AUDENAB_ENABLE_CH1 equ 0 ; 1 = channel 1 is running [ro] - def AUDENA_OFF equ 0 << AUDENAB_ENABLE - def AUDENA_ON equ 1 << AUDENAB_ENABLE - def AUDENAF_CH4_OFF equ 0 << AUDENAB_ENABLE_CH4 - def AUDENAF_CH4_ON equ 1 << AUDENAB_ENABLE_CH4 - def AUDENAF_CH3_OFF equ 0 << AUDENAB_ENABLE_CH3 - def AUDENAF_CH3_ON equ 1 << AUDENAB_ENABLE_CH3 - def AUDENAF_CH2_OFF equ 0 << AUDENAB_ENABLE_CH2 - def AUDENAF_CH2_ON equ 1 << AUDENAB_ENABLE_CH2 - def AUDENAF_CH1_OFF equ 0 << AUDENAB_ENABLE_CH1 - def AUDENAF_CH1_ON equ 1 << AUDENAB_ENABLE_CH1 +def B_AUDENA_ENABLE equ 7 ; 0 = disable the APU (resets all audio registers to 0!) [r/w] +def B_AUDENA_ENABLE_CH4 equ 3 ; 1 = channel 4 is running [ro] +def B_AUDENA_ENABLE_CH3 equ 2 ; 1 = channel 3 is running [ro] +def B_AUDENA_ENABLE_CH2 equ 1 ; 1 = channel 2 is running [ro] +def B_AUDENA_ENABLE_CH1 equ 0 ; 1 = channel 1 is running [ro] + def AUDENA_OFF equ 0 << B_AUDENA_ENABLE + def AUDENA_ON equ 1 << B_AUDENA_ENABLE + def AUDENA_CH4_OFF equ 0 << B_AUDENA_ENABLE_CH4 + def AUDENA_CH4_ON equ 1 << B_AUDENA_ENABLE_CH4 + def AUDENA_CH3_OFF equ 0 << B_AUDENA_ENABLE_CH3 + def AUDENA_CH3_ON equ 1 << B_AUDENA_ENABLE_CH3 + def AUDENA_CH2_OFF equ 0 << B_AUDENA_ENABLE_CH2 + def AUDENA_CH2_ON equ 1 << B_AUDENA_ENABLE_CH2 + def AUDENA_CH1_OFF equ 0 << B_AUDENA_ENABLE_CH1 + def AUDENA_CH1_ON equ 1 << B_AUDENA_ENABLE_CH1 ; -- $FF27-$FF2F are unused --------------------------------------------------- ; -- AUD3WAVE ($FF30-$FF3F) --------------------------------------------------- ; Audio channel 3 wave pattern RAM [r/w] -def _AUD3WAVERAM equ $FF30 ; $FF30-$FF3F - def rAUD3WAVE_0 equ $FF30 def rAUD3WAVE_1 equ $FF31 def rAUD3WAVE_2 equ $FF32 @@ -408,63 +446,69 @@ def rAUD3WAVE_D equ $FF3D def rAUD3WAVE_E equ $FF3E def rAUD3WAVE_F equ $FF3F -def AUD3WAVE_SIZE equ 16 - ; -- LCDC ($FF40) ------------------------------------------------------------- ; PPU graphics control def rLCDC equ $FF40 -def LCDCB_ON equ 7 ; whether the PPU (and LCD) are turned on [r/w] -def LCDCB_WIN9C00 equ 6 ; which tilemap the Window reads from [r/w] -def LCDCB_WINON equ 5 ; whether the Window is enabled [r/w] -def LCDCB_BLKS equ 4 ; which "tile blocks" the BG and Window use [r/w] -def LCDCB_BG9C00 equ 3 ; which tilemap the BG reads from [r/w] -def LCDCB_OBJ16 equ 2 ; how many pixels tall each OBJ is [r/w] -def LCDCB_OBJON equ 1 ; whether OBJs are enabled [r/w] -def LCDCB_BGON equ 0 ; (DMG only) whether the BG is enabled [r/w] -def LCDCB_PRION equ 0 ; (CGB only) whether OBJ priority bits are enabled [r/w] - def LCDCF_OFF equ 0 << LCDCB_ON - def LCDCF_ON equ 1 << LCDCB_ON - def LCDCF_WIN9800 equ 0 << LCDCB_WIN9C00 - def LCDCF_WIN9C00 equ 1 << LCDCB_WIN9C00 - def LCDCF_WINOFF equ 0 << LCDCB_WINON - def LCDCF_WINON equ 1 << LCDCB_WINON - def LCDCF_BLKS equ 1 << LCDCB_BLKS - def LCDCF_BLK21 equ 0 << LCDCB_BLKS - def LCDCF_BLK01 equ 1 << LCDCB_BLKS - def LCDCF_BG9800 equ 0 << LCDCB_BG9C00 - def LCDCF_BG9C00 equ 1 << LCDCB_BG9C00 - def LCDCF_OBJ8 equ 0 << LCDCB_OBJ16 - def LCDCF_OBJ16 equ 1 << LCDCB_OBJ16 - def LCDCF_OBJOFF equ 0 << LCDCB_OBJON - def LCDCF_OBJON equ 1 << LCDCB_OBJON - def LCDCF_BGOFF equ 0 << LCDCB_BGON - def LCDCF_BGON equ 1 << LCDCB_BGON - def LCDCF_PRIOFF equ 0 << LCDCB_PRION - def LCDCF_PRION equ 1 << LCDCB_PRION +def B_LCDC_ENABLE equ 7 ; whether the PPU (and LCD) are turned on [r/w] +def B_LCDC_WIN_MAP equ 6 ; which tilemap the Window reads from [r/w] +def B_LCDC_WINDOW equ 5 ; whether the Window is enabled [r/w] +def B_LCDC_BLOCKS equ 4 ; which "tile blocks" the BG and Window use [r/w] +def B_LCDC_BG_MAP equ 3 ; which tilemap the BG reads from [r/w] +def B_LCDC_OBJ_SIZE equ 2 ; how many pixels tall each OBJ is [r/w] +def B_LCDC_OBJS equ 1 ; whether OBJs are enabled [r/w] +def B_LCDC_BG equ 0 ; (DMG only) whether the BG is enabled [r/w] +def B_LCDC_PRIO equ 0 ; (CGB only) whether OBJ priority bits are enabled [r/w] + def LCDC_ENABLE equ 1 << B_LCDC_ENABLE + def LCDC_OFF equ 0 << B_LCDC_ENABLE + def LCDC_ON equ 1 << B_LCDC_ENABLE + def LCDC_WIN_MAP equ 1 << B_LCDC_WIN_MAP + def LCDC_WIN_9800 equ 0 << B_LCDC_WIN_MAP + def LCDC_WIN_9C00 equ 1 << B_LCDC_WIN_MAP + def LCDC_WINDOW equ 1 << B_LCDC_WINDOW + def LCDC_WIN_OFF equ 0 << B_LCDC_WINDOW + def LCDC_WIN_ON equ 1 << B_LCDC_WINDOW + def LCDC_BLOCKS equ 1 << B_LCDC_BLOCKS + def LCDC_BLOCK21 equ 0 << B_LCDC_BLOCKS + def LCDC_BLOCK01 equ 1 << B_LCDC_BLOCKS + def LCDC_BG_MAP equ 1 << B_LCDC_BG_MAP + def LCDC_BG_9800 equ 0 << B_LCDC_BG_MAP + def LCDC_BG_9C00 equ 1 << B_LCDC_BG_MAP + def LCDC_OBJ_SIZE equ 1 << B_LCDC_OBJ_SIZE + def LCDC_OBJ_8 equ 0 << B_LCDC_OBJ_SIZE + def LCDC_OBJ_16 equ 1 << B_LCDC_OBJ_SIZE + def LCDC_OBJS equ 1 << B_LCDC_OBJS + def LCDC_OBJ_OFF equ 0 << B_LCDC_OBJS + def LCDC_OBJ_ON equ 1 << B_LCDC_OBJS + def LCDC_BG equ 1 << B_LCDC_BG + def LCDC_BG_OFF equ 0 << B_LCDC_BG + def LCDC_BG_ON equ 1 << B_LCDC_BG + def LCDC_PRIO equ 1 << B_LCDC_PRIO + def LCDC_PRIO_OFF equ 0 << B_LCDC_PRIO + def LCDC_PRIO_ON equ 1 << B_LCDC_PRIO ; -- STAT ($FF41) ------------------------------------------------------------- ; Graphics status and interrupt control def rSTAT equ $FF41 -def STATB_LYC equ 6 ; 1 = LY match triggers the STAT interrupt [r/w] -def STATB_MODE10 equ 5 ; 1 = OAM Scan triggers the PPU interrupt [r/w] -def STATB_MODE01 equ 4 ; 1 = VBlank triggers the PPU interrupt [r/w] -def STATB_MODE00 equ 3 ; 1 = HBlank triggers the PPU interrupt [r/w] -def STATB_LYCF equ 2 ; 1 = LY is currently equal to LYC [ro] -def STATB_BUSY equ 1 ; 1 = the PPU is currently accessing VRAM [ro] - def STATF_LYC equ 1 << STATB_LYC - def STATF_MODE10 equ 1 << STATB_MODE10 - def STATF_MODE01 equ 1 << STATB_MODE01 - def STATF_MODE00 equ 1 << STATB_MODE00 - def STATF_LYCF equ 1 << STATB_LYCF - def STATF_BUSY equ 1 << STATB_BUSY +def B_STAT_LYC equ 6 ; 1 = LY match triggers the STAT interrupt [r/w] +def B_STAT_MODE_2 equ 5 ; 1 = OAM Scan triggers the PPU interrupt [r/w] +def B_STAT_MODE_1 equ 4 ; 1 = VBlank triggers the PPU interrupt [r/w] +def B_STAT_MODE_0 equ 3 ; 1 = HBlank triggers the PPU interrupt [r/w] +def B_STAT_LYCF equ 2 ; 1 = LY is currently equal to LYC [ro] +def B_STAT_BUSY equ 1 ; 1 = the PPU is currently accessing VRAM [ro] + def STAT_LYC equ 1 << B_STAT_LYC + def STAT_MODE_2 equ 1 << B_STAT_MODE_2 + def STAT_MODE_1 equ 1 << B_STAT_MODE_1 + def STAT_MODE_0 equ 1 << B_STAT_MODE_0 + def STAT_LYCF equ 1 << B_STAT_LYCF + def STAT_BUSY equ 1 << B_STAT_BUSY -def STATF_MODE equ %000000_11 ; PPU's current status [ro] - def STATF_HBL equ %000000_00 ; waiting after a line's rendering (HBlank) - def STATF_VBL equ %000000_01 ; waiting between frames (VBlank) - def STATF_OAM equ %000000_10 ; checking which OBJs will be rendered on this line (OAM scan) - def STATF_LCD equ %000000_11 ; pushing pixels to the LCD +def STAT_MODE equ %000000_11 ; PPU's current status [ro] + def STAT_HBLANK equ %000000_00 ; waiting after a line's rendering (HBlank) + def STAT_VBLANK equ %000000_01 ; waiting between frames (VBlank) + def STAT_OAM equ %000000_10 ; checking which OBJs will be rendered on this line (OAM scan) + def STAT_LCD equ %000000_11 ; pushing pixels to the LCD ; -- SCY ($FF42) -------------------------------------------------------------- ; Background Y scroll offset (in pixels) [r/w] @@ -510,31 +554,32 @@ def rWY equ $FF4A ; X coordinate of the Window's top-left pixel, plus 7 (7-166) [r/w] def rWX equ $FF4B -def WX_OFS equ 7 ; subtract this to get the actual Window Y coordinate +def WX_OFS equ 7 ; subtract this to get the actual Window X coordinate -; -- KEY0 ($FF4C) ------------------------------------------------------------- +; -- SYS / KEY0 ($FF4C) ------------------------------------------------------- ; (CGB boot ROM only) CPU mode select -def rKEY0 equ $FF4C +def rSYS equ $FF4C -; KEY0 is known as the "CPU mode register" in Fig. 11 of this patent: +; This is known as the "CPU mode register" in Fig. 11 of this patent: ; https://patents.google.com/patent/US6322447B1/en?oq=US6322447bi ; "OBJ priority mode designating register" in the same patent ; Credit to @mattcurrie for this finding! -def KEY0F_MODE equ %0000_11_00 ; current system mode [r/w] - def KEY0F_CGB equ %0000_00_00 ; CGB mode - def KEY0F_DMG equ %0000_01_00 ; DMG compatibility mode - def KEY0F_PGB1 equ %0000_10_00 ; LCD is driven externally, CPU is stopped - def KEY0F_PGB2 equ %0000_11_00 ; LCD is driven externally, CPU is running +def SYS_MODE equ %0000_11_00 ; current system mode [r/w] + def SYS_CGB equ %0000_00_00 ; CGB mode + def SYS_DMG equ %0000_01_00 ; DMG compatibility mode + def SYS_PGB1 equ %0000_10_00 ; LCD is driven externally, CPU is stopped + def SYS_PGB2 equ %0000_11_00 ; LCD is driven externally, CPU is running ; -- SPD / KEY1 ($FF4D) ------------------------------------------------------- ; (CGB only) Double-speed mode control def rSPD equ $FF4D -def SPDB_DBLSPEED equ 7 ; current clock speed [ro] -def SPDB_PREPARE equ 0 ; 1 = next `stop` instruction will switch clock speeds [r/w] - def SPDF_DBLSPEED equ 1 << SPDB_DBLSPEED - def SPDF_PREPARE equ 1 << SPDB_PREPARE +def B_SPD_DOUBLE equ 7 ; current clock speed [ro] +def B_SPD_PREPARE equ 0 ; 1 = next `stop` instruction will switch clock speeds [r/w] + def SPD_SINGLE equ 0 << B_SPD_DOUBLE + def SPD_DOUBLE equ 1 << B_SPD_DOUBLE + def SPD_PREPARE equ 1 << B_SPD_PREPARE ; -- $FF4E is unused ---------------------------------------------------------- @@ -548,15 +593,15 @@ def VBK_BANK equ %0000000_1 ; mapped VRAM bank [r/w] ; (boot ROM only) Boot ROM mapping control def rBANK equ $FF50 -def BANKB_ON equ 0 ; whether the boot ROM is mapped [wo] - def BANKF_ON equ 0 << BANKB_ON - def BANKF_OFF equ 1 << BANKB_ON +def B_BANK_ON equ 0 ; whether the boot ROM is mapped [wo] + def BANK_ON equ 0 << B_BANK_ON + def BANK_OFF equ 1 << B_BANK_ON ; -- VDMA_SRC_HIGH / HDMA1 ($FF51) -------------------------------------------- ; (CGB only) VRAM DMA source address (high 8 bits) [wo] def rVDMA_SRC_HIGH equ $FF51 -; -- VDMA_SRC_LO / HDMA2 ($FF52) ---------------------------------------------- +; -- VDMA_SRC_LOW / HDMA2 ($FF52) --------------------------------------------- ; (CGB only) VRAM DMA source address (low 8 bits) [wo] def rVDMA_SRC_LOW equ $FF52 @@ -564,7 +609,7 @@ def rVDMA_SRC_LOW equ $FF52 ; (CGB only) VRAM DMA destination address (high 8 bits) [wo] def rVDMA_DEST_HIGH equ $FF53 -; -- VDMA_DEST_LOW / HDMA3 ($FF54) -------------------------------------------- +; -- VDMA_DEST_LOW / HDMA4 ($FF54) -------------------------------------------- ; (CGB only) VRAM DMA destination address (low 8 bits) [wo] def rVDMA_DEST_LOW equ $FF54 @@ -572,32 +617,32 @@ def rVDMA_DEST_LOW equ $FF54 ; (CGB only) VRAM DMA length, mode, and start def rVDMA_LEN equ $FF55 -def VDMA_LENB_MODE equ 7 ; on write: VRAM DMA mode [wo] - def VDMA_LENF_MODE equ 1 << VDMA_LENB_MODE - def VDMA_LENF_MODE_GP equ 0 << VDMA_LENB_MODE ; GDMA (general-purpose) - def VDMA_LENF_MODE_HBL equ 1 << VDMA_LENB_MODE ; HDMA (HBlank) +def B_VDMA_LEN_MODE equ 7 ; on write: VRAM DMA mode [wo] + def VDMA_LEN_MODE equ 1 << B_VDMA_LEN_MODE + def VDMA_LEN_MODE_GENERAL equ 0 << B_VDMA_LEN_MODE ; GDMA (general-purpose) + def VDMA_LEN_MODE_HBLANK equ 1 << B_VDMA_LEN_MODE ; HDMA (HBlank) -def VDMA_LENB_BUSY equ 7 ; on read: is a VRAM DMA active? - def VDMA_LENF_BUSY equ 1 << VDMA_LENB_BUSY - def VDMA_LENF_NO equ 0 << VDMA_LENB_BUSY - def VDMA_LENF_YES equ 1 << VDMA_LENB_BUSY +def B_VDMA_LEN_BUSY equ 7 ; on read: is a VRAM DMA active? + def VDMA_LEN_BUSY equ 1 << B_VDMA_LEN_BUSY + def VDMA_LEN_NO equ 0 << B_VDMA_LEN_BUSY + def VDMA_LEN_YES equ 1 << B_VDMA_LEN_BUSY -def VDMA_LENB_SIZE equ %0_1111111 ; how many 16-byte blocks (minus 1) to transfer [r/w] +def VDMA_LEN_SIZE equ %0_1111111 ; how many 16-byte blocks (minus 1) to transfer [r/w] ; -- RP ($FF56) --------------------------------------------------------------- ; (CGB only) Infrared communications port def rRP equ $FF56 -def RPF_READ equ %11_000000 ; whether the IR read is enabled [r/w] - def RPF_DISREAD equ %00_000000 - def RPF_ENREAD equ %11_000000 +def RP_READ equ %11_000000 ; whether the IR read is enabled [r/w] + def RP_DISABLE equ %00_000000 + def RP_ENABLE equ %11_000000 -def RPB_DATAIN equ 1 ; 0 = IR light is being received [ro] -def RPB_LED_ON equ 0 ; 1 = IR light is being sent [r/w] - def RPF_DATAIN equ 1 << RPB_DATAIN - def RPF_LED_ON equ 1 << RPB_LED_ON - def RPF_WRITE_LO equ 0 << RPB_LED_ON - def RPF_WRITE_HI equ 1 << RPB_LED_ON +def B_RP_DATA_IN equ 1 ; 0 = IR light is being received [ro] +def B_RP_LED_ON equ 0 ; 1 = IR light is being sent [r/w] + def RP_DATA_IN equ 1 << B_RP_DATA_IN + def RP_LED_ON equ 1 << B_RP_LED_ON + def RP_WRITE_LOW equ 0 << B_RP_LED_ON + def RP_WRITE_HIGH equ 1 << B_RP_LED_ON ; -- $FF57-$FF67 are unused --------------------------------------------------- @@ -605,10 +650,10 @@ def RPB_LED_ON equ 0 ; 1 = IR light is being sent [r/w] ; (CGB only) Background palette I/O index def rBGPI equ $FF68 -def BGPIB_AUTOINC equ 7 ; whether the index field is incremented after each write to BCPD [r/w] - def BGPIF_AUTOINC equ 1 << BGPIB_AUTOINC +def B_BGPI_AUTOINC equ 7 ; whether the index field is incremented after each write to BCPD [r/w] + def BGPI_AUTOINC equ 1 << B_BGPI_AUTOINC -def BGPIF_INDEX equ %00_111111 ; the index within Palette RAM accessed via BCPD [r/w] +def BGPI_INDEX equ %00_111111 ; the index within Palette RAM accessed via BCPD [r/w] ; -- BGPD / BCPD ($FF69) ------------------------------------------------------ ; (CGB only) Background palette I/O access [r/w] @@ -618,10 +663,10 @@ def rBGPD equ $FF69 ; (CGB only) OBJ palette I/O index def rOBPI equ $FF6A -def OBPIB_AUTOINC equ 7 ; whether the index field is incremented after each write to OBPD [r/w] - def OBPIF_AUTOINC equ 1 << OBPIB_AUTOINC +def B_OBPI_AUTOINC equ 7 ; whether the index field is incremented after each write to OBPD [r/w] + def OBPI_AUTOINC equ 1 << B_OBPI_AUTOINC -def OBPIF_INDEX equ %00_111111 ; the index within Palette RAM accessed via OBPD [r/w] +def OBPI_INDEX equ %00_111111 ; the index within Palette RAM accessed via OBPD [r/w] ; -- OBPD / OCPD ($FF6B) ------------------------------------------------------ ; (CGB only) OBJ palette I/O access [r/w] @@ -631,34 +676,81 @@ def rOBPD equ $FF6B ; (CGB boot ROM only) OBJ draw priority mode def rOPRI equ $FF6C -def OPRIB_PRI equ 0 ; which drawing priority is used for OBJs [r/w] - def OPRIF_PRI equ 1 << OPRIB_PRI - def OPRI_OAM equ 0 << OPRIB_PRI ; CGB mode default: earliest OBJ in OAM wins - def OPRI_COORD equ 1 << OPRIB_PRI ; DMG mode default: leftmost OBJ wins +def B_OPRI_PRIORITY equ 0 ; which drawing priority is used for OBJs [r/w] + def OPRI_PRIORITY equ 1 << B_OPRI_PRIORITY + def OPRI_OAM equ 0 << B_OPRI_PRIORITY ; CGB mode default: earliest OBJ in OAM wins + def OPRI_COORD equ 1 << B_OPRI_PRIORITY ; DMG mode default: leftmost OBJ wins ; -- $FF6D-$FF6F are unused --------------------------------------------------- -; -- WBK / SVBK / SMBK ($FF70) ------------------------------------------------ +; -- WBK / SVBK ($FF70) ------------------------------------------------------- ; (CGB only) WRAM bank number def rWBK equ $FF70 -def WBKF_BANK equ %00000_111 ; mapped WRAM bank (0-7) [r/w] +def WBK_BANK equ %00000_111 ; mapped WRAM bank (0-7) [r/w] -; -- $FF71-$FF75 are unused --------------------------------------------------- +; -- PSW ($FF71) -------------------------------------------------------------- +; (CGB boot ROM's DMG mode only) Palette Selection Window and NMI control. [r/w] +; Bits 1-6 are always 1. +; In CGB mode, reads return $FF and writes are ignored. +def rPSW equ $FF71 + +def B_PSW_WINDOW equ 7 ; whether the Palette Selection Window is enabled [r/w] +def B_PSW_NMI equ 0 ; whether the NMI is enabled [r/w] + def PSW_WINDOW equ 1 << B_PSW_WINDOW + def PSW_WIN_OFF equ 0 << B_PSW_WINDOW + def PSW_WIN_ON equ 1 << B_PSW_WINDOW + def PSW_NMI equ 1 << B_PSW_NMI + def PSW_NMI_DISABLE equ 0 << B_PSW_NMI + def PSW_NMI_ENABLE equ 1 << B_PSW_NMI + +; -- PSWX ($FF72) ------------------------------------------------------------- +; (CGB boot ROM only) X coordinate of the Palette Selection Window's top-left pixel, plus 7 (7-166) [r/w] +; Readable and writable in both CGB and DMG mode. +def rPSWX equ $FF72 + +; -- PSWY ($FF73) ------------------------------------------------------------- +; (CGB boot ROM only) Y coordinate of the Palette Selection Window's top-left pixel (0-143) [r/w] +; Readable and writable in both CGB and DMG mode. +def rPSWY equ $FF73 + +; -- PSM ($FF74) -------------------------------------------------------------- +; (CGB boot ROM only) Set the Palette Selection Window button mask (triggers NMI when pressed) [r/w] +; Readable and writable in both CGB and DMG mode. +def rPSM equ $FF74 + +def B_PSM_START equ 7 +def B_PSM_SELECT equ 6 +def B_PSM_B equ 5 +def B_PSM_A equ 4 +def B_PSM_DOWN equ 3 +def B_PSM_UP equ 2 +def B_PSM_LEFT equ 1 +def B_PSM_RIGHT equ 0 + def PSM_START equ 1 << B_PSM_START + def PSM_SELECT equ 1 << B_PSM_SELECT + def PSM_B equ 1 << B_PSM_B + def PSM_A equ 1 << B_PSM_A + def PSM_DOWN equ 1 << B_PSM_DOWN + def PSM_UP equ 1 << B_PSM_UP + def PSM_LEFT equ 1 << B_PSM_LEFT + def PSM_RIGHT equ 1 << B_PSM_RIGHT + +; -- $FF75 is unused ---------------------------------------------------------- ; -- PCM12 ($FF76) ------------------------------------------------------------ ; Audio channels 1 and 2 output def rPCM12 equ $FF76 -def PCM12F_CH2 equ %1111_0000 ; audio channel 2 output [ro] -def PCM12F_CH1 equ %0000_1111 ; audio channel 1 output [ro] +def PCM12_CH2 equ %1111_0000 ; audio channel 2 output [ro] +def PCM12_CH1 equ %0000_1111 ; audio channel 1 output [ro] ; -- PCM34 ($FF77) ------------------------------------------------------------ ; Audio channels 3 and 4 output def rPCM34 equ $FF77 -def PCM34F_CH4 equ %1111_0000 ; audio channel 4 output [ro] -def PCM34F_CH3 equ %0000_1111 ; audio channel 3 output [ro] +def PCM34_CH4 equ %1111_0000 ; audio channel 4 output [ro] +def PCM34_CH3 equ %0000_1111 ; audio channel 3 output [ro] ; -- $FF78-$FF7F are unused --------------------------------------------------- @@ -666,16 +758,16 @@ def PCM34F_CH3 equ %0000_1111 ; audio channel 3 output [ro] ; Interrupt enable def rIE equ $FFFF -def IEB_JOYPAD equ 4 ; 1 = joypad interrupt is enabled [r/w] -def IEB_SERIAL equ 3 ; 1 = serial interrupt is enabled [r/w] -def IEB_TIMER equ 2 ; 1 = timer interrupt is enabled [r/w] -def IEB_STAT equ 1 ; 1 = STAT interrupt is enabled [r/w] -def IEB_VBLANK equ 0 ; 1 = VBlank interrupt is enabled [r/w] - def IEF_JOYPAD equ 1 << IEB_JOYPAD - def IEF_SERIAL equ 1 << IEB_SERIAL - def IEF_TIMER equ 1 << IEB_TIMER - def IEF_STAT equ 1 << IEB_STAT - def IEF_VBLANK equ 1 << IEB_VBLANK +def B_IE_JOYPAD equ 4 ; 1 = joypad interrupt is enabled [r/w] +def B_IE_SERIAL equ 3 ; 1 = serial interrupt is enabled [r/w] +def B_IE_TIMER equ 2 ; 1 = timer interrupt is enabled [r/w] +def B_IE_STAT equ 1 ; 1 = STAT interrupt is enabled [r/w] +def B_IE_VBLANK equ 0 ; 1 = VBlank interrupt is enabled [r/w] + def IE_JOYPAD equ 1 << B_IE_JOYPAD + def IE_SERIAL equ 1 << B_IE_SERIAL + def IE_TIMER equ 1 << B_IE_TIMER + def IE_STAT equ 1 << B_IE_STAT + def IE_VBLANK equ 1 << B_IE_VBLANK ;****************************************************************************** @@ -686,86 +778,243 @@ def IEB_VBLANK equ 0 ; 1 = VBlank interrupt is enabled [r/w] ; however, one address for each of these ranges is considered the "canonical" one, and ; these addresses are what's provided here. + +; ** Common to most MBCs ****************************************************** + ; -- RAMG ($0000-$1FFF) ------------------------------------------------------- ; Whether SRAM can be accessed [wo] def rRAMG equ $0000 -; Common values -def CART_SRAM_DISABLE equ $00 -def CART_SRAM_ENABLE equ $0A ; some MBCs accept any value whose low nybble is $A +; Common values (not for HuC1 or HuC-3) +def RAMG_SRAM_DISABLE equ $00 +def RAMG_SRAM_ENABLE equ $0A ; some MBCs accept any value whose low nybble is $A -; -- ROMB0 ($2000-$3FFF) ------------------------------------------------------ -; ROM bank number (low 8 bits when applicable) [wo] -def rROMB0 equ $2000 +; (HuC-3 only) switch SRAM to map cartridge RAM, RTC, or IR +def RAMG_CART_RAM_RO equ $00 ; select cartridge RAM [ro] +def RAMG_CART_RAM equ $0A ; select cartridge RAM [r/w] +def RAMG_RTC_IN equ $0B ; select RTC command/argument [wo] + def RAMG_RTC_IN_CMD equ %0_111_0000 ; command + def RAMG_RTC_IN_ARG equ %0_000_1111 ; argument +def RAMG_RTC_OUT equ $0C ; select RTC command/response [ro] + def RAMG_RTC_OUT_CMD equ %0_111_0000 ; command + def RAMG_RTC_OUT_RESULT equ %0_000_1111 ; result +def RAMG_RTC_SEMAPHORE equ $0D ; select RTC semaphore [r/w] +def RAMG_IR equ $0E ; (HuC1 and HuC-3 only) select IR [r/w] -; -- ROMB1 ($3000-$3FFF) ------------------------------------------------------ -; (MBC5 only) ROM bank number high bit (bit 8) [wo] -def rROMB1 equ $3000 +; -- ROMB ($2000-$3FFF) ------------------------------------------------------- +; ROM bank number (not for MBC5 or MBC6) [wo] +def rROMB equ $2000 ; -- RAMB ($4000-$5FFF) ------------------------------------------------------- - ; SRAM bank number [wo] +; SRAM bank number (not for MBC2, MBC6, or MBC7) [wo] def rRAMB equ $4000 -; (MBC3-only) Special RAM bank numbers that actually map values into RTCREG -def RTC_S equ $08 ; seconds counter (0-59) -def RTC_M equ $09 ; minutes counter (0-59) -def RTC_H equ $0A ; hours counter (0-23) -def RTC_DL equ $0B ; days counter, low byte (0-255) -def RTC_DH equ $0C ; days counter, high bit and other flags - def RTC_DHB_CARRY equ 7 ; 1 = days counter overflowed [wo] - def RTC_DHB_HALT equ 6 ; 0 = run timer, 1 = stop timer [wo] - def RTC_DHB_HIGH equ 0 ; days counter, high bit (bit 8) [wo] - def RTC_DHF_CARRY equ 1 << RTC_DHB_CARRY - def RTC_DHF_HALT equ 1 << RTC_DHB_HALT - def RTC_DHF_HIGH equ 1 << RTC_DHB_HIGH +; (MBC3 only) Special RAM bank numbers that actually map values into RTCREG +def RAMB_RTC_S equ $08 ; seconds counter (0-59) +def RAMB_RTC_M equ $09 ; minutes counter (0-59) +def RAMB_RTC_H equ $0A ; hours counter (0-23) +def RAMB_RTC_DL equ $0B ; days counter, low byte (0-255) +def RAMB_RTC_DH equ $0C ; days counter, high bit and other flags + def B_RAMB_RTC_DH_CARRY equ 7 ; 1 = days counter overflowed [wo] + def B_RAMB_RTC_DH_HALT equ 6 ; 0 = run timer, 1 = stop timer [wo] + def B_RAMB_RTC_DH_HIGH equ 0 ; days counter, high bit (bit 8) [wo] + def RAMB_RTC_DH_CARRY equ 1 << B_RAMB_RTC_DH_CARRY + def RAMB_RTC_DH_HALT equ 1 << B_RAMB_RTC_DH_HALT + def RAMB_RTC_DH_HIGH equ 1 << B_RAMB_RTC_DH_HIGH -def CARTB_RUMBLE_ON equ 3 ; (MBC5 and MBC7 only) enable the rumble motor (if any) - def CARTF_RUMBLE_ON equ 1 << CARTB_RUMBLE_ON - def CART_RUMBLE_OFF equ 0 << CARTB_RUMBLE_ON - def CART_RUMBLE_ON equ 1 << CARTB_RUMBLE_ON +def B_RAMB_RUMBLE equ 3 ; (MBC5 and MBC7 only) enable the rumble motor (if any) + def RAMB_RUMBLE equ 1 << B_RAMB_RUMBLE + def RAMB_RUMBLE_OFF equ 0 << B_RAMB_RUMBLE + def RAMB_RUMBLE_ON equ 1 << B_RAMB_RUMBLE + + +; ** MBC1 and MMM01 only ****************************************************** + +; -- BMODE ($6000-$7FFF) ------------------------------------------------------ +; Banking mode select [wo] +def rBMODE equ $6000 + +def BMODE_SIMPLE equ $00 ; locks ROMB and RAMB to bank 0 +def BMODE_ADVANCED equ $01 ; allows bank-switching with RAMB + + +; ** MBC2 only **************************************************************** + +; -- ROM2B ($0000-$3FFF with bit 8 set) --------------------------------------- +; ROM bank number [wo] +def rROM2B equ $2100 + + +; ** MBC3 only **************************************************************** ; -- RTCLATCH ($6000-$7FFF) --------------------------------------------------- -; (MBC3 only) RTC latch clock data [wo] +; RTC latch clock data [wo] def rRTCLATCH equ $6000 ; Write $00 then $01 to latch the current time into RTCREG def RTCLATCH_START equ $00 def RTCLATCH_FINISH equ $01 -; -- RTCREG ($A000-$BFFF) --------------------------------------------------- -; (MBC3 only) RTC register [r/w] +; -- RTCREG ($A000-$BFFF) ----------------------------------------------------- +; RTC register [r/w] def rRTCREG equ $A000 +; ** MBC5 only **************************************************************** + +; -- ROMB0 ($2000-$2FFF) ------------------------------------------------------ +; ROM bank number low byte (bits 0-7) [wo] +def rROMB0 equ $2000 + +; -- ROMB1 ($3000-$3FFF) ------------------------------------------------------ +; ROM bank number high bit (bit 8) [wo] +def rROMB1 equ $3000 + + +; ** MBC6 only **************************************************************** + +; -- RAMBA ($0400-$07FF) ------------------------------------------------------ +; RAM bank A number [wo] +def rRAMBA equ $0400 + +; -- RAMBB ($0800-$0BFF) ------------------------------------------------------ +; RAM bank B number [wo] +def rRAMBB equ $0800 + +; -- FLASH ($0C00-$0FFF) ------------------------------------------------------ +; Whether the flash chip can be accessed [wo] +def rFLASH equ $0C00 + +; -- FMODE ($1000) ------------------------------------------------------------ +; Write mode select for the flash chip +def rFMODE equ $1000 + +; -- ROMBA ($2000-$27FF) ------------------------------------------------------ +; ROM/Flash bank A number [wo] +def rROMBA equ $2000 + +; -- FLASHA ($2800-$2FFF) ----------------------------------------------------- +; ROM/Flash bank A select [wo] +def rFLASHA equ $2800 + +; -- ROMBB ($3000-$37FF) ------------------------------------------------------ +; ROM/Flash bank B number [wo] +def rROMBB equ $3000 + +; -- FLASHB ($3800-$3FFF) ----------------------------------------------------- +; ROM/Flash bank B select [wo] +def rFLASHB equ $3800 + + +; ** MBC7 only **************************************************************** + +; -- RAMREG ($4000-$5FFF) ----------------------------------------------------- +; Enable RAM register access [wo] +def rRAMREG equ $4000 + +def RAMREG_ENABLE equ $40 + +; -- ACCLATCH0 ($Ax0x) -------------------------------------------------------- +; Latch accelerometer start [wo] +def rACCLATCH0 equ $A000 + +; Write $55 to ACCLATCH0 to erase the latched data +def ACCLATCH0_START equ $55 + +; -- ACCLATCH1 ($Ax1x) -------------------------------------------------------- +; Latch accelerometer finish [wo] +def rACCLATCH1 equ $A010 + +; Write $AA to ACCLATCH1 to latch the accelerometer and update ACCEL* +def ACCLATCH1_FINISH equ $AA + +; -- ACCELX0 ($Ax2x) ---------------------------------------------------------- +; Accelerometer X value low byte [ro] +def rACCELX0 equ $A020 + +; -- ACCELX1 ($Ax3x) ---------------------------------------------------------- +; Accelerometer X value high byte [ro] +def rACCELX1 equ $A030 + +; -- ACCELY0 ($Ax4x) ---------------------------------------------------------- +; Accelerometer Y value low byte [ro] +def rACCELY0 equ $A040 + +; -- ACCELY1 ($Ax5x) ---------------------------------------------------------- +; Accelerometer Y value high byte [ro] +def rACCELY1 equ $A050 + +; -- EEPROM ($Ax8x) ----------------------------------------------------------- +; EEPROM access [r/w] +def rEEPROM equ $A080 + + +; ** HuC1 only **************************************************************** + +; -- IRREG ($A000-$BFFF) ------------------------------------------------------ +; IR register [r/w] +def rIRREG equ $A000 + +; whether the IR transmitter sees light +def IR_LED_OFF equ $C0 +def IR_LED_ON equ $C1 + + ;****************************************************************************** ; Screen-related constants ;****************************************************************************** -def SCRN_X equ 160 ; width of screen in pixels -def SCRN_Y equ 144 ; height of screen in pixels -def SCRN_X_B equ 20 ; width of screen in bytes -def SCRN_Y_B equ 18 ; height of screen in bytes +def SCREEN_WIDTH_PX equ 160 ; width of screen in pixels +def SCREEN_HEIGHT_PX equ 144 ; height of screen in pixels +def SCREEN_WIDTH equ 20 ; width of screen in bytes +def SCREEN_HEIGHT equ 18 ; height of screen in bytes +def SCREEN_AREA equ SCREEN_WIDTH * SCREEN_HEIGHT ; size of screen in bytes -def SCRN_VX equ 256 ; width of tilemap in pixels -def SCRN_VY equ 256 ; height of tilemap in pixels -def SCRN_VX_B equ 32 ; width of tilemap in bytes -def SCRN_VY_B equ 32 ; height of tilemap in bytes +def TILEMAP_WIDTH_PX equ 256 ; width of tilemap in pixels +def TILEMAP_HEIGHT_PX equ 256 ; height of tilemap in pixels +def TILEMAP_WIDTH equ 32 ; width of tilemap in bytes +def TILEMAP_HEIGHT equ 32 ; height of tilemap in bytes +def TILEMAP_AREA equ TILEMAP_WIDTH * TILEMAP_HEIGHT ; size of tilemap in bytes -def TILE_X equ 8 ; width of tile in pixels -def TILE_Y equ 8 ; height of tile in pixels -def TILE_B equ 16 ; size of tile in bytes (2 bits/pixel) +def TILE_WIDTH equ 8 ; width of tile in pixels +def TILE_HEIGHT equ 8 ; height of tile in pixels +def TILE_SIZE equ 16 ; size of tile in bytes (2 bits/pixel) -def COLOR_B equ 2 ; size of color in bytes (little-endian BGR555) - def COLORF_GREEN_LOW equ %111_00000 ; for the low byte - def COLORF_RED equ %000_11111 ; for the low byte - def COLORF_BLUE equ %0_11111_00 ; for the high byte - def COLORF_GREEN_HIGH equ %000000_11 ; for the high byte +def COLOR_SIZE equ 2 ; size of color in bytes (little-endian BGR555) def PAL_COLORS equ 4 ; colors per palette -def PAL_B equ COLOR_B * PAL_COLORS ; size of palette in bytes +def PAL_SIZE equ COLOR_SIZE * PAL_COLORS ; size of palette in bytes + +def COLOR_CH_WIDTH equ 5 ; bits per RGB color channel +def COLOR_CH_MAX equ (1 << COLOR_CH_WIDTH) - 1 + def B_COLOR_RED equ COLOR_CH_WIDTH * 0 ; bits 4-0 + def B_COLOR_GREEN equ COLOR_CH_WIDTH * 1 ; bits 9-5 + def B_COLOR_BLUE equ COLOR_CH_WIDTH * 2 ; bits 14-10 + def COLOR_RED equ %000_11111 ; for the low byte + def COLOR_GREEN_LOW equ %111_00000 ; for the low byte + def COLOR_GREEN_HIGH equ %0_00000_11 ; for the high byte + def COLOR_BLUE equ %0_11111_00 ; for the high byte + +; (DMG only) grayscale shade indexes for BGP, OBP0, and OBP1 +def SHADE_WHITE equ %00 +def SHADE_LIGHT equ %01 +def SHADE_DARK equ %10 +def SHADE_BLACK equ %11 ; Tilemaps the BG or Window can read from (controlled by LCDC) -def _SCRN0 equ $9800 ; $9800-$9BFF -def _SCRN1 equ $9C00 ; $9C00-$9FFF +def TILEMAP0 equ $9800 ; $9800-$9BFF +def TILEMAP1 equ $9C00 ; $9C00-$9FFF + +; (CGB only) BG tile attribute fields +def B_BG_PRIO equ 7 ; whether the BG tile colors 1-3 are drawn above OBJs +def B_BG_YFLIP equ 6 ; whether the whole BG tile is flipped vertically +def B_BG_XFLIP equ 5 ; whether the whole BG tile is flipped horizontally +def B_BG_BANK1 equ 3 ; which VRAM bank the BG tile is taken from +def BG_PALETTE equ %00000_111 ; which palette the BG tile uses + def BG_PRIO equ 1 << B_BG_PRIO + def BG_YFLIP equ 1 << B_BG_YFLIP + def BG_XFLIP equ 1 << B_BG_XFLIP + def BG_BANK0 equ 0 << B_BG_BANK1 + def BG_BANK1 equ 1 << B_BG_BANK1 ;****************************************************************************** @@ -780,23 +1029,37 @@ def OAMA_X rb ; 1 def OAM_X_OFS equ 8 ; subtract 8 from what's written to OAM to get the real X position def OAMA_TILEID rb ; 2 def OAMA_FLAGS rb ; 3 - def OAMB_PRI equ 7 ; whether the OBJ is drawn above BG colors 1-3 - def OAMB_YFLIP equ 6 ; whether the whole OBJ is flipped vertically - def OAMB_XFLIP equ 5 ; whether the whole OBJ is flipped horizontally - def OAMB_PAL1 equ 4 ; (DMG only) which of the two palettes the OBJ uses - def OAMB_BANK1 equ 3 ; (CGB only) which VRAM bank the OBJ takes its tile(s) from - def OAMF_PALMASK equ %00000_111 ; (CGB only) which palette the OBJ uses - def OAMF_PRI equ 1 << OAMB_PRI - def OAMF_YFLIP equ 1 << OAMB_YFLIP - def OAMF_XFLIP equ 1 << OAMB_XFLIP - def OAMF_PAL0 equ 0 << OAMB_PAL1 - def OAMF_PAL1 equ 1 << OAMB_PAL1 - def OAMF_BANK0 equ 0 << OAMB_BANK1 - def OAMF_BANK1 equ 1 << OAMB_BANK1 -def OBJ_B rb 0 ; size of OBJ in bytes = 4 + def B_OAM_PRIO equ 7 ; whether the OBJ is drawn below BG colors 1-3 + def B_OAM_YFLIP equ 6 ; whether the whole OBJ is flipped vertically + def B_OAM_XFLIP equ 5 ; whether the whole OBJ is flipped horizontally + def B_OAM_PAL1 equ 4 ; (DMG only) which of the two palettes the OBJ uses + def B_OAM_BANK1 equ 3 ; (CGB only) which VRAM bank the OBJ takes its tile(s) from + def OAM_PALETTE equ %00000_111 ; (CGB only) which palette the OBJ uses + def OAM_PRIO equ 1 << B_OAM_PRIO + def OAM_YFLIP equ 1 << B_OAM_YFLIP + def OAM_XFLIP equ 1 << B_OAM_XFLIP + def OAM_PAL0 equ 0 << B_OAM_PAL1 + def OAM_PAL1 equ 1 << B_OAM_PAL1 + def OAM_BANK0 equ 0 << B_OAM_BANK1 + def OAM_BANK1 equ 1 << B_OAM_BANK1 +def OBJ_SIZE rb 0 ; size of OBJ in bytes = 4 def OAM_COUNT equ 40 ; how many OBJs there are room for in OAM -def OAM_B equ OBJ_B * OAM_COUNT +def OAM_SIZE equ OBJ_SIZE * OAM_COUNT + + +;****************************************************************************** +; Audio channel RAM addresses +;****************************************************************************** + +def AUD1RAM equ $FF10 ; $FF10-$FF14 +def AUD2RAM equ $FF15 ; $FF15-$FF19 +def AUD3RAM equ $FF1A ; $FF1A-$FF1E +def AUD4RAM equ $FF1F ; $FF1F-$FF23 +def AUDRAM_SIZE equ 5 ; size of each audio channel RAM in bytes + +def _AUD3WAVERAM equ $FF30 ; $FF30-$FF3F +def AUD3WAVE_SIZE equ 16 ; size of wave pattern RAM in bytes ;****************************************************************************** @@ -822,9 +1085,25 @@ def BOOTUP_A_MGB equ $FF def BOOTUP_A_SGB2 equ BOOTUP_A_MGB ; Register B = CPU qualifier (if A is BOOTUP_A_CGB) -def BOOTUPB_B_AGB equ 0 - def BOOTUP_B_CGB equ 0 << BOOTUPB_B_AGB - def BOOTUP_B_AGB equ 1 << BOOTUPB_B_AGB +def B_BOOTUP_B_AGB equ 0 + def BOOTUP_B_CGB equ 0 << B_BOOTUP_B_AGB + def BOOTUP_B_AGB equ 1 << B_BOOTUP_B_AGB + +; Register C = CPU qualifier +def BOOTUP_C_DMG equ $13 +def BOOTUP_C_SGB equ $14 +def BOOTUP_C_CGB equ $00 ; CGB or AGB + +; Register D = color qualifier +def BOOTUP_D_MONO equ $00 ; DMG, MGB, SGB, or CGB or AGB in DMG mode +def BOOTUP_D_COLOR equ $FF ; CGB or AGB + +; Register E = CPU qualifier (distinguishes DMG variants) +def BOOTUP_E_DMG0 equ $C1 +def BOOTUP_E_DMG equ $C8 +def BOOTUP_E_SGB equ $00 +def BOOTUP_E_CGB_DMGMODE equ $08 ; CGB or AGB in DMG mode +def BOOTUP_E_CGB equ $56 ; CGB or AGB ;****************************************************************************** @@ -835,15 +1114,6 @@ def BOOTUPB_B_AGB equ 0 ; less directly meaningful or human-readable. def rP1 equ rJOYP - def P1F_GET_BTN equ JOYP_GET_BTN - def P1F_GET_DPAD equ JOYP_GET_DPAD - def P1F_GET_NONE equ JOYP_GET_NONE - def P1F_5 equ JOYP_GET_DPAD - def P1F_4 equ JOYP_GET_BTN - def P1F_3 equ JOYPF_DOWN - def P1F_2 equ JOYPF_UP - def P1F_1 equ JOYPF_LEFT - def P1F_0 equ JOYPF_RIGHT def rNR10 equ rAUD1SWEEP def rNR11 equ rAUD1LEN @@ -867,156 +1137,21 @@ def rNR50 equ rAUDVOL def rNR51 equ rAUDTERM def rNR52 equ rAUDENA +def rKEY0 equ rSYS def rKEY1 equ rSPD - def KEY1F_DBLSPEED equ SPDF_DBLSPEED - def KEY1F_PREPARE equ SPDF_PREPARE def rHDMA1 equ rVDMA_SRC_HIGH def rHDMA2 equ rVDMA_SRC_LOW def rHDMA3 equ rVDMA_DEST_HIGH def rHDMA4 equ rVDMA_DEST_LOW def rHDMA5 equ rVDMA_LEN - def HDMA5B_MODE equ VDMA_LENB_MODE - def HDMA5F_MODE_GP equ VDMA_LENF_MODE_GP - def HDMA5F_MODE_HBL equ VDMA_LENF_MODE_HBL - def HDMA5F_BUSY equ VDMA_LENF_BUSY def rBCPS equ rBGPI - def BCPSB_AUTOINC equ BGPIB_AUTOINC - def BCPSF_AUTOINC equ BGPIF_AUTOINC def rBCPD equ rBGPD def rOCPS equ rOBPI - def OCPSB_AUTOINC equ OBPIB_AUTOINC - def OCPSF_AUTOINC equ OBPIF_AUTOINC def rOCPD equ rOBPD def rSVBK equ rWBK -def rSMBK equ rWBK - - -;****************************************************************************** -; (deprecated) Memory regions -;****************************************************************************** - -; These values are deprecated; please use RGBASM and RGBLINK features instead. -; Note that the value of `STARTOF()` is determined at link time. - -def _ROM equ $0000 ; $0000-$3FFF / $0000-$7FFF (prefer `STARTOF(ROM0)`) -def _ROMBANK equ $4000 ; $4000-$7FFF (prefer `STARTOF(ROMX)`) -def _VRAM equ $8000 ; $8000-$9FFF (prefer `STARTOF(VRAM)`) -def _SRAM equ $A000 ; $A000-$BFFF (prefer `STARTOF(SRAM)`) -def _RAM equ $C000 ; $C000-$CFFF / $C000-$DFFF (prefer `STARTOF(WRAM0)`) -def _RAMBANK equ $D000 ; $D000-$DFFF (prefer `STARTOF(WRAMX)`) -def _OAMRAM equ $FE00 ; $FE00-$FE9F (prefer `STARTOF(OAM)`) -def _IO equ $FF00 ; $FF00-$FF7F, $FFFF (prefer `ldh [c]` to `ld [_IO+c]`) -def _HRAM equ $FF80 ; $FF80-$FFFE (prefer `STARTOF(HRAM)`) - -def _VRAM8000 equ _VRAM -def _VRAM8800 equ _VRAM + $800 -def _VRAM9000 equ _VRAM + $1000 - - -;****************************************************************************** -; (deprecated) Cartridge header -;****************************************************************************** - -; These values are deprecated; please use RGBFIX instead. -; Zero-filled space can be reserved for fixable header values like this: -; -; SECTION "Cartridge header", ROM0[$0100] -; nop :: jp $0150 ; Entry point ($0100-$0104) -; ds $150 - @, $00 ; Header ($0104-$014FF) filled with $00s for RGBFIX to populate - -; -- Nintendo logo ($0104-$0133) ---------------------------------------------- -; Prefer `rgbfix -f/--fix-spec l` for the official logo, or `rgbfix -L ` for a custom one -MACRO NINTENDO_LOGO - db $CE,$ED,$66,$66,$CC,$0D,$00,$0B,$03,$73,$00,$83,$00,$0C,$00,$0D - db $00,$08,$11,$1F,$88,$89,$00,$0E,$DC,$CC,$6E,$E6,$DD,$DD,$D9,$99 - db $BB,$BB,$67,$63,$6E,$0E,$EC,$CC,$DD,$DC,$99,$9F,$BB,$B9,$33,$3E -ENDM - -; -- CGB compatibility code ($0143) ------------------------------------------- -def CART_COMPATIBLE_DMG equ $00 ; default value if header is zero-filled -def CART_COMPATIBLE_DMG_GBC equ $80 ; prefer `rgbfix -c/--color-compatible` -def CART_COMPATIBLE_GBC equ $C0 ; prefer `rgbfix -C/--color-only` - -; -- SGB flag ($0146) --------------------------------------------------------- -def CART_INDICATOR_GB equ $00 ; default value if header is zero-filled -def CART_INDICATOR_SGB equ $03 ; prefer `rgblink -s/--sgb-compatible` - -; -- Cartridge type ($0147) --------------------------------------------------- -; Prefer `rgblink -m/--mbc_type ` -def CART_ROM equ $00 -def CART_ROM_MBC1 equ $01 -def CART_ROM_MBC1_RAM equ $02 -def CART_ROM_MBC1_RAM_BAT equ $03 -def CART_ROM_MBC2 equ $05 -def CART_ROM_MBC2_BAT equ $06 -def CART_ROM_RAM equ $08 -def CART_ROM_RAM_BAT equ $09 -def CART_ROM_MMM01 equ $0B -def CART_ROM_MMM01_RAM equ $0C -def CART_ROM_MMM01_RAM_BAT equ $0D -def CART_ROM_MBC3_BAT_RTC equ $0F -def CART_ROM_MBC3_RAM_BAT_RTC equ $10 -def CART_ROM_MBC3 equ $11 -def CART_ROM_MBC3_RAM equ $12 -def CART_ROM_MBC3_RAM_BAT equ $13 -def CART_ROM_MBC5 equ $19 -def CART_ROM_MBC5_RAM equ $1A -def CART_ROM_MBC5_RAM_BAT equ $1B -def CART_ROM_MBC5_RUMBLE equ $1C -def CART_ROM_MBC5_RAM_RUMBLE equ $1D -def CART_ROM_MBC5_RAM_BAT_RUMBLE equ $1E -def CART_ROM_MBC7_RAM_BAT_GYRO equ $22 -def CART_ROM_POCKET_CAMERA equ $FC -def CART_ROM_BANDAI_TAMA5 equ $FD -def CART_ROM_HUDSON_HUC3 equ $FE -def CART_ROM_HUDSON_HUC1 equ $FF - -; -- ROM size ($0148) --------------------------------------------------------- -; Prefer `rgbfix -p/--pad_value `, which pads to the smallest valid size -def CART_ROM_32KB equ $00 ; 2 banks -def CART_ROM_64KB equ $01 ; 4 banks -def CART_ROM_128KB equ $02 ; 8 banks -def CART_ROM_256KB equ $03 ; 16 banks -def CART_ROM_512KB equ $04 ; 32 banks -def CART_ROM_1024KB equ $05 ; 64 banks -def CART_ROM_2048KB equ $06 ; 128 banks -def CART_ROM_4096KB equ $07 ; 256 banks -def CART_ROM_8192KB equ $08 ; 512 banks -def CART_ROM_1152KB equ $52 ; 72 banks -def CART_ROM_1280KB equ $53 ; 80 banks -def CART_ROM_1536KB equ $54 ; 96 banks - -; -- SRAM size ($0149) -------------------------------------------------------- -; Prefer `rgbfix -r/--ram_size ` -def CART_SRAM_NONE equ 0 ; none -def CART_SRAM_2KB equ 1 ; 1 incomplete bank (homebrew only) -def CART_SRAM_8KB equ 2 ; 1 bank -def CART_SRAM_32KB equ 3 ; 4 banks -def CART_SRAM_128KB equ 4 ; 16 banks - -; -- Destination code ($014A) ------------------------------------------------- -def CART_DEST_JAPANESE equ $00 ; default value if header is zero-filled -def CART_DEST_NON_JAPANESE equ $01 ; prefer `rgbfix -j/--non-japanese` - - -;****************************************************************************** -; Deprecated constants -;****************************************************************************** - -; These values are deprecated; please avoid using them. - -def LCDCB_BG8000 equ LCDCB_BLKS - def LCDCF_BG8800 equ LCDCF_BLK21 - def LCDCF_BG8000 equ LCDCF_BLK01 - -def IEB_HILO equ IEB_JOYPAD - def IEF_HILO equ IEF_JOYPAD -def IEF_LCDC equ IEF_STAT - -def sizeof_OAM_ATTRS equ OBJ_B endc ; HARDWARE_INC diff --git a/project.mk b/project.mk index 19e2ebc..c755e94 100644 --- a/project.mk +++ b/project.mk @@ -3,7 +3,7 @@ # Value that the ROM will be filled with. -PADVALUE := 0xFF +PADVALUE := 0x00 ## Header constants (passed to RGBFIX). @@ -11,10 +11,10 @@ PADVALUE := 0xFF VERSION := 0 # 4-ASCII letter game ID. -GAMEID := BOIL +GAMEID := BINX # Game title, up to 11 ASCII chars. -TITLE := BOILERPLATE +TITLE := BINX # New licensee, 2 ASCII chars. # Homebrew games FTW!. @@ -36,7 +36,7 @@ MBC := 0x00 SRAMSIZE := 0x00 # ROM name. -ROMNAME := boilerplate +ROMNAME := binx ROMEXT := gb diff --git a/scripts/update_hardware_inc.sh b/scripts/update_hardware_inc.sh old mode 100644 new mode 100755 diff --git a/src/data.asm b/src/data.asm new file mode 100644 index 0000000..adcac25 --- /dev/null +++ b/src/data.asm @@ -0,0 +1 @@ +if !DEF(DATA_ASM) \ No newline at end of file diff --git a/src/game.asm b/src/game.asm new file mode 100644 index 0000000..2195910 --- /dev/null +++ b/src/game.asm @@ -0,0 +1,23 @@ +if !def(GAME_INC) +DEF GAME_INC EQU 1 + +INCLUDE "hardware.inc" + rev_Check_hardware_inc 5.3.0 + +INCLUDE "input.asm" +INCLUDE "util.asm" +INCLUDE "ram.asm" + +SECTION FRAGMENT "Game", ROM0 +loop: ; Main Loop + call WaitVBlank + + call RUN_DMA + + call HandleDirectionInput + + jr loop +done: + jr done + +endc ; game_inc \ No newline at end of file diff --git a/src/header.asm b/src/header.asm index 030b4b6..13cd69b 100644 --- a/src/header.asm +++ b/src/header.asm @@ -1,24 +1,68 @@ - INCLUDE "hardware.inc" - rev_Check_hardware_inc 4.0 + rev_Check_hardware_inc 5.3.0 + +INCLUDE "util.asm" +INCLUDE "ram.asm" +INCLUDE "data.asm" +INCLUDE "game.asm" +INCLUDE "input.asm" +INCLUDE "screen.asm" SECTION "Header", ROM0[$100] - - ; This is your ROM's entry point - ; You have 4 bytes of code to do... something di jp EntryPoint - ; Make sure to allocate some space for the header, so no important - ; code gets put there and later overwritten by RGBFIX. - ; RGBFIX is designed to operate over a zero-filled header, so make - ; sure to put zeros regardless of the padding value. (This feature - ; was introduced in RGBDS 0.4.0, but the -MG etc flags were also - ; introduced in that version.) ds $150 - @, 0 SECTION "Entry point", ROM0 EntryPoint: - ; Here is where the fun begins, happy coding :) - jr @ + ; turn off audio + ld a, $00 + ld [rNR52], a + + call WaitVBlank + + ; turn the LCD off, but wait for VBlank first + ld a, $00 + ld [rLCDC], a + + ; Copy the DMA proc to HRAM + ld de, RUN_DMA + ld hl, DMA_Stub + ld bc, DMA_Stub.End - DMA_Stub + call CopyData + + ; Clear shadow OAM + call ResetShadowOAM + + ; Load tile data + ld de, $9000 + ld hl, TestLevelTileData + ld bc, TestLevelTileData.End - TestLevelTileData + call CopyData + + ; Load level tilemap + ld de, $9800 + ld hl, TestLevelTileMap + ld bc, TestLevelTileMap.End - TestLevelTileMap + call CopyData + + ; Set the scroll registers + ld a, $00 + ld [rSCY], a + ld [rSCX], a + + ; Initialize scroll speed counter + ld a, $00 + ld [INPUT_COUNTER_ADDR], a + + ; Set the pallets + ld a, %11100100 + ld [rBGP], a + + ; Turn on the display + ld a, %11000001 + ld [rLCDC], a + + jp loop \ No newline at end of file diff --git a/src/input.asm b/src/input.asm new file mode 100644 index 0000000..3ae052f --- /dev/null +++ b/src/input.asm @@ -0,0 +1,121 @@ +if !def(INPUT_INC) +DEF INPUT_INC EQU 1 + +INCLUDE "hardware.inc" + rev_Check_hardware_inc 5.3.0 + +INCLUDE "ram.asm" + +SECTION FRAGMENT "Input", ROM0 + +DEF DPAD_BUTTON_MASK EQU %11101111 +DEF RIGHT_DPAD_MASK EQU %00000001 +DEF LEFT_DPAD_MASK EQU %00000010 +DEF UP_DPAD_MASK EQU %00000100 +DEF DOWN_DPAD_MASK EQU %00001000 + +DEF COUNTER_TICKRATE EQU 10 + +; register b will contain the direction button data +ReadDirectionButtons: + ld a, DPAD_BUTTON_MASK + ld [rJOYP], a + + ld a, [rJOYP] + ld a, [rJOYP] + ld a, [rJOYP] + ld a, [rJOYP] + ld b, a + + ret + +HandleDirectionInput: + ; load counter and check + ld a, [INPUT_COUNTER_ADDR] + cp a, COUNTER_TICKRATE + jr c, .ret ; return if we haven't hit the tickrate + + call ReadDirectionButtons ; b = input data + ld a, b + + ld a, b + and a, RIGHT_DPAD_MASK + call z, PressRight + + ld a, b + and a, LEFT_DPAD_MASK + call z, PressLeft + + ld a, b + and a, UP_DPAD_MASK + call z, PressUp + + ld a, b + and a, DOWN_DPAD_MASK + call z, PressDown + + ; reset the counter + ld a, $00 + ld [INPUT_COUNTER_ADDR], a +.ret + ; increment the counter on every call + ld a, [INPUT_COUNTER_ADDR] + inc a + ld [INPUT_COUNTER_ADDR], a + ret + +PressLeft: + push af + + ld a, [rSCX] + or a, a + jr z, .ret + + dec a + ld [rSCX], a +.ret: + pop af + ret + +PressRight: + push af + + ld a, [rSCX] + add a, 159 + jr c, .ret ; c is set if we go above 255, which means past this tileset + + ld a, [rSCX] + inc a + ld [rSCX], a +.ret: + pop af + ret + +PressUp: + push af + + ld a, [rSCY] + or a, a + jr z, .ret + + dec a + ld [rSCY], a +.ret + pop af + ret + +PressDown: + push af + + ld a, [rSCY] + add a, 143 + jr c, .ret ; c is set if we go above 255, which means past this tileset + + inc a + ld [rSCY], a +.ret: + pop af + ret + + +endc ; input_inc \ No newline at end of file diff --git a/src/ram.asm b/src/ram.asm new file mode 100644 index 0000000..842187e --- /dev/null +++ b/src/ram.asm @@ -0,0 +1,10 @@ +if !def(RAM_INC) +DEF RAM_INC EQU 1 + +DEF WRAM_START EQU $C000 +DEF SHADOW_OAM_ADDR EQU WRAM_START +DEF RUN_DMA EQU $FF80 + +DEF INPUT_COUNTER_ADDR EQU $C100 + +endc ; ram_inc \ No newline at end of file diff --git a/src/screen.asm b/src/screen.asm new file mode 100644 index 0000000..ceea4d9 --- /dev/null +++ b/src/screen.asm @@ -0,0 +1,10 @@ +if !def(SCREEN_INC) +DEF SCREEN_INC EQU 1 + +INCLUDE "hardware.inc" + rev_Check_hardware_inc 5.3.0 + +SECTION FRAGMENT "Input", ROM0 + + +endc ; screen_inc diff --git a/src/util.asm b/src/util.asm new file mode 100644 index 0000000..16677d0 --- /dev/null +++ b/src/util.asm @@ -0,0 +1,58 @@ +if !def(UTIL_INC) +DEF UTIL_INC EQU 1 + +INCLUDE "hardware.inc" + rev_Check_hardware_inc 5.3.0 + +SECTION FRAGMENT "Utilities", ROM0 + +WaitVBlank: + ldh a, [rLY] + cp 144 ; + jr c, WaitVBlank + ret + +; Params +; de: dest +; hl: source +; bc: size +CopyData: + ld a, [hli] + ld [de], a + + inc de + dec bc + + ld a, b + or c + jr nz, CopyData + ret + +; Needs to be copied to HRAM +DMA_Stub: + ld a, HIGH($C100) + ldh [$FF46], a ; start DMA transfer (starts right after instruction) + ld a, 40 ; delay for a total of 4×40 = 160 M-cycles +.wait + dec a ; 1 M-cycle + jr nz, .wait ; 3 M-cycles + ret +.End + +ResetShadowOAM: + ld bc, $F9 + ld de, $C100 +.copy + ld a, 0 + ld [de], a + + inc de + dec bc + + ld a, b + or c + jr nz, .copy + ret + + +endc ; util_inc \ No newline at end of file