Full Control: Real-time RAM writing with 8F
Posted by: Cryo
Date: 2016-12-20 22:58:25
HOW IT WORKS:
When 8F is used, the screen and sound will function as normal, but the controls will lock up and you will now be in INPUT mode. During this mode, any button you press will add that button's value to the specified byte in memory, with two exceptions: If you press the same button twice in a row, it will move on to the next byte; and if you hold (or press) the START and SELECT buttons together at the same time, the program will jump to the specified offset and start executing your code.
This allows for a 1-to-1 mapping of buttons to values for optimal speed while still providing all necessary functionality.
NOTE: Due to space limitations, you must press A once before you enter anything on the joypad. When you press A to select the USE option on 8F, the game registers that as the initial value to be executed. Pressing A before you enter your own code skips this junk byte, and it's also why HL is initially set to $D3FE instead of $D400 in the code below.
ITEM LIST:
[tt]Item Quantity
========================
8F x1
[Any Item] xAny
X Accuracy x254
Escape Rope x84
Potion x213
HP Up x205
TM50 x63
Soda Pop x240
TM48 x254
Burn Heal x200
TM40 x178
Guard Spec. x183
Rare Candy x241
X Speed x185
X Defense x40
TM35 x79
X Attack x134
Ether x119
X Special x24
TM28 xAny[/tt]
ASM:
Start:
ld l,$FE ; Destination = $D3FE
dec e ; Zero out E
ld d,h ; DE = $D300
inc d ; DE = $D400 (Code Start)
push de ; Push onto the stack for later RET
Next_Byte:
inc hl ; Go to the next byte
Check_Input:
call $3FFA ; Joypad polling subroutine
dec a ; - Padding -
ldh a,($F8) ; Get the current held button
cp $0C ; START + SELECT = exit
ret z ; Jump to arbitrary code ($D400)
Check_Released:
ldh a,($B2) ; Get the most recently released button
scf ; - Padding -
or a ; Check whether a button was released
jr z,Check_Input ; If not, keep looping until one is
Check_Previous:
ld b,e ; - Padding -
cp c ; Check if the button was pressed twice
ld b,d ; - Padding -
jr z,Next_Byte ; Move to the next byte if so
Modify_Byte:
ld c,a ; Backup the previous button
ld b,c ; - Padding -
add a,(hl) ; Add button value to current value
ld d,b ; - Padding -
ld (hl),a ; Save new value in memory
ld b,h ; - Padding -
jr Check_Input ; Loop until manually RET
End:
EXAMPLE:
One of the things that I use this method for the most has to do with cartridge swapping capabilities. The setup below simply puts P14 to low and enters STOP mode, followed by a RET instruction to continue normal execution. This means that you can enter STOP mode, swap cartridges, then press any button on the D-Pad to return execution.
ASM:
ld a,$EF
ldh ($00),a
stop
ret
To execute the code above, simply use the 8F item, then press the buttons below in sequence. Each line represents a byte written to memory, with the first line skipping the junk byte and the last line jumping to the written program.
BUTTON INPUT:
[tt]A
LEFT RIGHT START SELECT B B
DOWN UP LEFT START SELECT B A A
DOWN UP LEFT LEFT
LEFT
RIGHT RIGHT
A A
DOWN UP START A A
START + SELECT[/tt]
NOTE: If you pop the cartridge out, put the same cartridge back in, then exit STOP mode, then the game will continue executing as normally. However, the values written to RAM remain there, so in order to re-run the stop routine above, the only buttons you'd have to press are [tt]START + SELECT[/tt].
Putting in the same combination twice without resetting will effectively double the existing program's bytes in RAM, since this is an additive approach. One pretty rad workaround is the polymorphic aspect of the setupyou can alter the items in your bag (to remove padding, etc.) and save the game with your new RAM writing bulldozer. ;D
PS: This method was successfully tested on Pokemon Red and Pokemon Blue hardware; I'm not sure about Pokemon Yellow yet, but it should work just fine.