News:

Don't forget to visit the main site! There's lots of helpful docs, patches, and more!

Main Menu

[SM] Tweaking the Maridia Power Bomb Tunnel

Started by Munchy, March 07, 2019, 12:11:20 AM

Previous topic - Next topic

Munchy

Hiya,

I'm struggling to find the right spot to hijack this PLM.

I've got some cool stuff going on with exploding rooms and they basically work just fine.  I only need to make a couple of small tweaks: I'd like to be able to choose the tile that the PLM uses rather than having the PLM assign a tile for me in the top-left corner.  I'd like to be able to change custom Room states.  The latter shouldn't be difficult, but the fomer, I can't seem to find the sweet spot...

I've found the PLM command offset:


PLM Command from $84:D70C = CC D6
thus

$84D6CC BE 87 1C    LDX $1C87,y[$7E:1CCF] ; RAM @ $7E1CCF = DC 4F 54
$84D6CF A9 44 80    LDA #$8044
$84D6D2 20 B4 82    JSR $82B4  [$84:82B4]
$84D6D5 60          RTS


This stub only seems to be calling a series of - I would guess - data into X for the tunnel and then running the map update (presumably to reveal that more of the map when you blow the tunnel up!)

a few questions then please :)

1) am I right in thinking this RAM data is used by the "create and read room defined PLMs" routine at $84:846A to dictate what should be represented by the PLM on screen?  or is it used by $84:83D7 (the main sub-routine that creates PLMs)?
2) and/or... is there a sub-routine specific to the power bomb tunnel PLM through which the tile is chosen from out of the tileset?

cheerzzzz!

Smiley

PLM headers have two pointers. The first is a pointer to initilization code, which is what's in that snippet. The second is a pointer to an instruction list (ie. more pointers), which also handles drawing the PLM.
The tube's instruction list pointer is $D4D4, so you need to look there. The format for the instruction list is as follows:
If a value is >=8000, then it's a pointer to code. There may or may not be arguments after the pointer.
If a value is <8000, then a tilemap defined by the following pointer is drawn for that many frames before the next instruction.

$84:D4D4             dx 882D,000B,D521, ; Go to $D521 if the event 000Bh is set
                        8A24,D4E8,      ; Link instruction = $D4E8
                        86C1,BD26,      ; Pre-instruction = go to link register if shot with a power bomb
                        0001,98D1,
                        86B4            ; Sleep

So what's happening here is some instructions, and then a tilemap defined at $98D1 is drawn for one frame.
$84:98D1             dw 0001, C540
                        0000

And the tilemap is one tile, tiletype C (shot block), horizontally flipped, tile $140.

Munchy

#2
Thank you once again @SMILEuser96 :)

If you don't mind my asking, how did you figure out these long lists of dw in the disassemblies? I'm absolutely fine with opcodes and pointer math, but I'm always confused when I hit these sections of the code (The tutorials we have on this site - and other ASM docs I've read - don't really explain them).

[EDIT]
nevermind, I get what you mean now about >=8000 <8000. 

is this always the case across all ROM banks?

Smiley

Actually I just copy-pasted from PJ's logs (extremely useful if you're doing anything with ASM). Anyway, understanding instruction lists just requires looking at the code the pointers point to and understanding what it does; the current PLM instruction pointer is held in the Y register, so if the code reads and increases (or otherwise overwrites) it, that means the instruction has arguments. For example, if you look at the code at $84:882D, you will see 4 INYs. Meaning, instruction $882D has 4 bytes of arguments:
dx 882D,000B,D521, ; Go to $D521 if the event 000Bh is set
Note that there won't be any INYs in instructions without any arguments.

This instruction list format is common and is used by at least enemies, enemy projectiles and HDMA in addition to PLMs.

Munchy

Wow, haven't seen PJ's maps before, my disassemblies are pretty antique by comparison!

oooh, ok I think I get it.

so, looking at


$84:D4D4             dx 882D,000B,D521, ; Go to $D521 if the event 000Bh is set
                        8A24,D4E8,      ; Link instruction = $D4E8
                        86C1,BD26,      ; Pre-instruction = go to link register if shot with a power bomb
                        0001,98D1,
                        86B4            ; Sleep


since BD26 doesn't seem to be incrementing an index, I'd surmise that the action happens in $86B4

see my comments below.


$8486B4 88          DEY ; ---v
$8486B5 88          DEY ; ---> decrement Y reg down 2 spaces in current word list (from above
$8486B6 98          TYA ; ---> transfer Y to A
$8486B7 9D 27 1D    STA $1D27,x[$7E:1D75] ; store A to RAM at 7E:1D75.
$8486BA 68          PLA ; etc. etc.
$8486BB 60          RTS
$8486BC 9E 37 1C    STZ $1C37,x[$7E:1C85]
$8486BF 68          PLA
$8486C0 60          RTS

sooooo, therefore, If I hijack $8486B7, I can then choose to replace A with a value of my choosing and assign it to that RAM address accordingly... right?!

Smiley

You could hijack it, but you probably don't want to. $86B4 is the sleep/wait instruction, meaning the PLM will not proceed past it. Notice that decrementing Y twice means the next instruction will be the same as the current one; $86B4 is essentially an infinite loop. It's used by many PLMs.

Quotesince BD26 doesn't seem to be incrementing an index, I'd surmise that the action happens in $86B4
$BD26 has nothing to do with $86B4. You need to look at the whole. First I need to explain what "link instruction" and "pre-instruction" are:
Link instruction is a pointer to some part of the instruction list. It's stored to RAM, $7EDEBC,x specifically (where x is the current PLM index), and can be called later.
Pre-instruction is code that runs every frame before the main instructions. Simple stuff.

First the PLM checks if the tube has already been broken. If so, it jumps elsewhere (to $D521, which enables water physics and deletes the PLM.)
Next, the link instruction is set to be $D4E8. So, later the PLM can jump to that part of the instruction list.
Then the pre-instruction is set to $BD26, which checks for power bomb explosions, and jumps to the link instruction if hit by one. In short, hit detection.
Then the PLM draws something for one frame.
And then the PLM waits. Forever. Until it's hit by a power bomb, which is when it'll jump to $D4E8.

$84:D4E8             dx 8A24,D4F2,      ; Link instruction = $D4F2
                        86C1,D4BF,      ; Pre-instruction = wake PLM if A/X/B/Y/left/right pressed
                        86B4            ; Sleep

Should be fairly obvious what that does.

Munchy

#6
[EDIT] SEE NEXT POST FOR FINISHED VERSION!

Thanks for all your help!  I eventually nailed the tile stuff, though I have to tweak the tile draw during the explosion (which I shouldn't have a problem with now thanks to all the snippets above, pretty sure I was hitting that routine with the debugger at one point!)

For posterity, here is my lazy implementation of tile drawing.  Depending on if I decide to have even more exploding tunnels in a single room, I'll maybe write something more sophisticated that uses X indexes and whatnot, but this'll do for now!

From what I can surmise, during room load, a pre-allocated chunk of RAM is injected with the PLM tile data.  It's at that point, I found a sweet spot to inject custom logic.

SOOOO, for those that want to make multiple exploding tunnels inside a single room and want a different BTS tile for each tunnel, here's basically how to do it!


lorom
org $848601 ; 20604.  The point at which the PLM tile data pointer is supplied to RAM
JSR $F000

; My compiler wasn't letting me define these bytes, inject them directly at the supplied addresses
; 40 41/0C C1 = the tile numbers.  E.g. C140 = tile $140 in SMILE
; $84EFE0
; 01 00 0C C1 00 00
; $84EFF0
; 01 00 40 C1 00 00
org $84F000
LDA $0002,y
CMP #$98D1 ; if the reference goes to the tile data ...
BEQ +
RTS
+ ; ... then supply the repointed
LDA $7FF00F ; have we loaded this before?
CMP #$0001 ; if yes ... goto next + and render tile data at EFF0
BEQ +
LDA #$0001 ; if no ... EFE0
STA $7FF00F
LDA #$EFE0 ; get tile data pointer $84:EFE0 for first 6 bytes mentioned at the top of this stub
RTS
+
LDA #$EFF0 ; get tile data pointer $84:EFF0 for second 6 bytes mentioned at the top of this stub
RTS

Munchy

Here is a complete version that allows for 2 exploding tunnels with different PLM tiles in a room. It now also decorates the PLM properly while the tunnel is in the process of exploding.  Bear in mind, this is only the cosmetic aspect of the tunnel, you'll still have to wire room event stuff etc.

hope someone finds it useful

lorom
org $848601 ; PC $20601.  The point at which the pre-power bomb PLM tile data pointer is supplied to RAM
JSR $F000

; -- NB. DATA YOU WILL NEED --
; My compiler wasn't letting me define these bytes,
; so you'll have to inject them directly at the supplied addresses using HexD
; I choose tiles $116 and $10C from my tileset for tunnel 1 and 2.
; They're read in PLM index order for that room

; --- DATA DATA DATA ---
; $84EFE0 ; 1st 6 bytes are tile state 1 (before samus drops power bomb), 2nd 6 are tile state 2 (after, while the tunnel is cracking)
; 01 00 16 C1 00 00 01 00 16 81 00 00
; $84EFF0 ; as above
; 01 00 0C C1 00 00 01 00 0C 81 00 00

org $84F000 ; PC $027000 - PRE-POWER-BOMB EVENT
LDA $0002,y
CMP #$98D1 ; if the reference goes to the pre power bomb tile data ...
BEQ +
CMP #$98D7 ; if the reference goes to the post power bomb tile data ...
BEQ postPowerBomb
RTS
+ ; ... then supply the repointed
LDA $7FF00F ; have we loaded this before?
CMP #$0001 ; if yes ... goto next + and render tile data at EFF0
BEQ +
LDA #$0001 ; if no ... EFE0
STA $7FF00F
LDA #$EFE0 ; get tile data at $84:EFE0
RTS
+
LDA #$0000 ; reset boolean, to make sure that the right tile is drawn when power bomb detonates the tunnel
STA $7FF00F
LDA #$EFF0 ; get tile data at $84:EFF0
RTS

postPowerBomb: ; PC $027030 - POST-POWER-BOMB EVENT
LDA $7EDE6C,x
CMP #$C116 ; is C116 stored at this RAM address?
BEQ firstTunnel
CMP #$C10C ; is C10C stored at this RAM address?
BEQ secondTunnel
RTS

firstTunnel:
LDA #$EFE6
STA $7EDE6C,x
RTS

secondTunnel:
LDA #$EFF6
STA $7EDE6C,x
RTS