This is what appears to be the code for the game's RNG at RAM addresses $000D through $000F:
$CF5E:A5 0D LDA $000D
$CF60:29 48 AND #$48
$CF62:69 38 ADC #$38
$CF64:0A ASL
$CF65:0A ASL
$CF66:26 0F ROL $000F
$CF68:26 0E ROL $000E
$CF6A:E6 0D INC $000D
$CF6C:A5 0D LDA $000D
$CF6E:65 04 ADC $0004
$CF70:65 05 ADC $0005
$CF72:65 10 ADC $0010
$CF74:65 11 ADC $0011
$CF76:65 12 ADC $0012
$CF78:65 14 ADC $0014
$CF7A:69 0D ADC #$0D
$CF7C:85 0D STA $000D
Using FCEUX 2.2.3, I set write breakpoints on $000D through $000F with the condition P<#CF66 || P>#CF7C and played the latest published movie. The only other time those addresses were written was when they were loaded with 0 upon console reset, so I'm pretty confident that we have all the variables necessary to manipulate the RNG.
$0004 stores the currently pressed buttons as bits for as long as they are pressed, while $0005 does the same thing but only for the first two frames when a button is pressed. The bit order--most to least significant--is A, B, Select, Start, Up, Down, Left, Right.
$0010 through $0012 form a three-byte counter that increases by one each frame, with $0010 the least significant byte and $0012 the most significant byte. The counter seems to restart from zero each time you enter a door.
$0014 is another counter that loops upward from 0 to 7 and then resets. It increases by one each frame but pauses when the screen is black, such as during transitions between areas after entering a door.
When the camera X-coordinate gets within 0x40 of the previously mentioned (0x0502, 0x00FE) spawn coordinate, $04B9 starts counting downward from 0. It doesn't seem like you can get outside the acceptable range of camera Y-coordinates in the small space by the first wizard guild, thankfully. $04B9 pauses if camera X > 0x0542, however, and restarts from 0 every time camera X <= 0x0542.
$04B9 decreases by one every two frames. When it reaches 0 during the countdown, an enemy cloud may spawn. Each cloud has another timer that starts at 0 and counts up by one every two frames. So far, I've seen these timers stored in $04BB and $04BC. Other addresses may be possible if you can get three or more clouds active at once. This leads to:
$8372:FE A8 04 INC $04A8,X ; This is the cloud's timer
$8375:BD A8 04 LDA $04A8,X
$8378:C9 80 CMP #$80
$837A:D0 3C BNE $83B8 ; Value must be 0x80
$837C:A5 0D LDA $000D ; One of the RNG values
$837E:4A LSR
$837F:90 37 BCC $83B8 ; Branch if bit 0 was not set
$8381:A5 85 LDA $0085 ; Current costume type
$8383:C9 03 CMP #$03 ; Check if wizard
$8385:F0 38 BEQ $83BF ; Branch if wizard, attack otherwise
So the cloud only attacks when:
- Its timer is 0x80,
- The least significant bit of $000D is set, and
- Current costume is not wizard.
This means that it will take about 768 frames (2 * 256 for $04B9 + 2 * 128 for $04BB or $04BC) to spawn a cloud and get it into an attacking state. The good thing is that the way those counters work gives us time to manipulate the RNG into favorable states. It takes at least 3179 frames to get the knight costume, 433 frames to get the statue and 2746 to complete the trial (estimated using frame numbers for the first completely black screens when entering/leaving doors into the appropriate rooms), so we can save around 2400 frames by skipping it. We still have to get the crown jewel from the princess in the Palace, however, so we can't skip that area altogether.
The location at which the cloud spawns is determined by some combination of RNG address $000F (primarily), RNG address $000E (in some cases), the camera X- and Y-coordinates, and Kuros's X- and Y-coordinates. The entire code is long, so I'll just post the results of my analysis. If you want to verify it, set an execution breakpoint at $D7BE. The subroutine starts there and goes through the RTS at $D857. Some helpful info:
- $02EE,Y and $0308,Y are the low and high bytes of the cloud's X-coordinate, respectively.
- $033C,Y and $0356,Y are the low and high bytes of the cloud's Y-coordinate, respectively.
- $00B4 and $00B5 seem to always be 0x07 in the part of the UnderWorld where we're doing the glitch.
Bits 1 and 2 of $000F are tested to determine different X and Y spawn locations.
Bit 1 set, Bit 2 set:
Cloud X = Camera X - 0x20
Cloud Y = ($000E AND 0x7F) - 0x40 + Kuros Y
Bit 1 set, Bit 2 clear:
Cloud X = Camera X + 0x120
Cloud Y = ($000E AND 0x7F) - 0x40 + Kuros Y
Bit 1 clear, Bit 2 set:
Cloud X = ($000F AND 0x3F) - 0x20 + Kuros X
Cloud Y = Camera Y - 0x30
Bit 1 clear, Bit 2 clear:
Cloud X = ($000F AND 0x3F) - 0x20 + Kuros X
Cloud Y = Camera Y + 0xD0
It is important to note that the code actually discards any overflow after calculating the low byte of Cloud Y when bit 1 is set; the high byte is set directly to the high byte of Kuros's Y-coordinate.
The Bit 1 clear, Bit 2 set case is useless for our purposes. When leaving the wizard guild, the camera coordinates are set to (0x052A, 0x0000). I have not found a way to get the Y-coordinate greater than 0x30 without scrolling the X-coordinate beyond 0x0542, which will reset the $04B9 counter. So the cloud gets spawned at some Y-coordinate with high byte 0xFF and immediately gets despawned. I think that one of the bit 2 clear cases should be our target. We can spawn the cloud to the right of and below Kuros, which will make it attack in a diagonal direction that makes Kuros clip up and left through the wall.