MAMEWorld >> EmuChat
View all threads Index   Threaded Mode Threaded  

Pages: 1

Rotwang
Life is too short to be little...
Reged: 03/21/17
Posts: 109
Send PM


Digging into the secrets of SFII: Rainbow Edition
#389344 - 01/01/21 08:41 PM


I have always been curious about what exactly is going on beneath the hood of some of these SFII:CE bootlegs. Seems to me that the best place to start would be the king of bootlegs, SFII: Rainbow Edition. The romset sf2rb seems to be based on a World region build of revision 920322. No known official World release of 920322 exists, but the Japanese romset sf2ceja shares this revision date, and apart from the new code inserted by the hackers and the region switch itself, the code and data all seems to be in the same locations. I did a file compare in a hex editor to see where the hackers made changes.

The first differences appear at $C84 which is in a part of the program used to skip frames. This effect can be seen when pressing a button during the region warning text screen to make it draw faster, and while holding down buttons during victory poses to speed up the arrival of the next round. This is where the hackers have injected their own code for a rudimentary turbo mode. To be clear, this is different from the walking speed increases that each character received.

I have only heard scattered anecdotes that Rainbow Edition even HAS a turbo mode, but never a definitive explanation of how it works. Here is that definitive explanation. What the hackers have done is hijack the game's existing frame skip code and add a counter-based system that will run the frame skip code in controlled bursts.


Code:

rb_turbomode:
0E5478 clr.w D4
0E547A move.w ($9c0,A5), D0 ; Matches Cleared counter
0E547E move.w D0, D3 ; Preserve Matches Cleared counter to d3
0E5480 move.b ($64c,A5), D1 ; 1P human (0) or CPU (1)
0E5484 or.b ($94c,A5), D1 ; 2P human (0) or CPU (1)
0E5488 beq rb_turbo_noturbo ; $e54dc ; branch out of turbo if this is a PVP game
0E548C move.b ($87,A5), D4 ; load DIP switch bank B
0E5490 lsr.b #4, D4 ; Shift switches 5 through 8 right.
; Bit mask according to switch numbers:
; XXXX 8765
; These switches are documented as
; unused in SFII:CE manual.
0E5492 andi.w #$f, D4
0E5496 btst #$3, D4 ; Test highest bit of DIP bank B (switch 8)
0E549A beq rb_turbo_fixed ; $e554c ; Branch to Fixed Turbo mode if switch 8 is off
0E549E bclr #$3, D4 ; Switch 8 is on, and we are in Variable Turbo mode
; Clear switch 8 from our dip read
0E54A2 move.w (-$6f2e,A5), D1 ; Load preserved round clear counter
0E54A6 cmpi.b #$7, D4 ; A comparison with no action taken after.
0E54AA move.w #$0, D1
0E54AE sub.b D1, D0 ; A weird way of checking if matches cleared counter is zero.
0E54B0 bls rb_turbo_noturbo ; Branch out of turbo if this is the first match
0E54B4 mulu.w #$c, D4 ; Multiply dip B 5,6,7 values to create turbo table base offset
0E54B8 add.w D4, D0 ; Add to rounds cleared counter to create final offset
0E54BA move.b ($30,PC,D0.w), D1 ; Load turbo counter max value
0E54BE move.b (-$6f30,A5), D0 ; Load turbo counter current value
0E54C2 cmp.b D1, D0 ; Compare current turbo value to max
0E54C4 bhi rb_turbo_clearcounter ;$e54d2 ; Branch if turbo counter max exceeded

rb_turbo_incrementcounter
0E54C8 addq.b #1, (-$6f30,A5) ; Increment turbo counter
0E54CC movem.l (A7)+, D0-D4/A1 ; Pull register values from stack and return
0E54D0 rts

rb_turbo_clearcounter
0E54D2 clr.b (-$6f30,A5) ; reset turbo counter (returns with Z flag set, frame skip will run)
0E54D6 movem.l (A7)+, D0-D4/A1
0E54DA rts

rb_turbo_noturbo
0E54DC move.w D3, (-$6f2e,A5) ; Store matches cleared counter
0E54E0 move.b #$1, (-$6f30,A5) ; Store 1 at turbo counter
0E54E6 movem.l (A7)+, D0-D4/A1 ; Pull register values from stack and return
0E54EA rts

rb_turbo_fixed:
0E554C tst.b D4 ; Test D4 to see if any of switches 5,6,7 are set
0E554E beq $e54dc ; Branch out of turbo if no switches set.
0E5550 move.b ($10,PC,D4.w), D1 ; This turbo mode doesn't use the matches cleared counter.
; Dip B 5,6,7 control the turbo counter max values.
0E5554 move.b (-$6f30,A5), D0
0E5558 cmp.b D1, D0
0E555A bhi rb_turbo_clearcounter ;$e54d2 ; Clear turbo counter
0E555E bra rb_turbo_incrementcounter ;$e54c8 ; Increment turbo counter



First thing sf2rb's turbo code does is check if both players are human controlled. If so, it will return out of turbo mode and not speed up the game. The hackers wanted their turbo feature to only work during CPU battles.

Sf2rb has utilized four dip switches that were unused in the original SFII:CE. Dip bank B, switches 5, 6, 7, and 8 now control sf2rb's turbo mode. In fact, sf2rb has added two different turbo modes for operators to choose between. Turbo mode will not run at all if Dip B SW 5,6,7,8 are all off. SW 8 controls which of the two turbo modes will run. Since nobody else has given these a name, I will call these modes Fixed Turbo and Variable Turbo.

Fixed Turbo is enabled by setting DIP B SW 8 to OFF and any of DIP B SW 5,6,7 (let's call these the turbo intensity switches) to ON. The turbo intensity switches is used as an offset to a table of values that control how fast the game runs. The full list of turbo values for Fixed Turbo mode are:


Code:

80 50 40 30 28 18 10 08 04 02



However, since Fixed Turbo mode can only run if any of the turbo intensity switches are set, the first value #$80 goes entirely unused. Likewise, the highest value we can get from that table with the 3 switches is #$08, and the last 2 values can never be read.

Variable Turbo makes the game go faster based on a combination of which Turbo Intensity switches are set, and how many matches the player has cleared. The player has to clear at least one match (flag crossed out on world map) for Variable Turbo to run. The higher the turbo intensity switches, and the more matches have been cleared, the faster the game will run. After a certain point it becomes virtually unplayable.

Now, how does this actually affect the game itself?

RB's turbo code controls a counter that the hackers set aside in an unused section of RAM. Whenever either of sf2rb's turbo modes runs, it will check this counter against the turbo value, and if the counter has exceeded the turbo value, the counter will be reset. Otherwise, the counter is incremented. What's happening is when this new subroutine returns back to SFII:CE's existing turbo code, it uses the zero flag to determine whether it will skip a frame or not using the original game's turbo mode where this hijack was inserted into. If the turbo counter was reset, then it returns with the zero flag set, and the frame gets skipped. The lower the turbo value, the more often the code will return zero and a frame will get skipped. Sort of like a controlled trickle of the game's existing turbo function.

So, to summarize the rules of Street Fighter II: Rainbow Edition's turbo modes:

- Only works during a fight against a CPU opponent.
- DIP bank B switch 8 selects between Fixed Turbo (OFF) and Variable Turbo (ON)
- DIP bank B switches 5, 6 and 7 control the intensity of the turbo modes.
- Variable Turbo only works after you have cleared at least one match off the world map.
- Variable Turbo becomes faster the more matches you clear.
- Fixed Turbo's speed is controlled solely by the turbo intensity switches and works even if you haven't cleared any matches yet.


Here is a dip switch setting table for Rainbow Edition's turbo modes:


Code:


DIP SWITCH BANK B
5 6 7 8
============================
OFF OFF OFF OFF | Turbo Disabled
ON OFF OFF OFF | Fixed Turbo 1
OFF ON OFF OFF | Fixed Turbo 2
ON ON OFF OFF | Fixed Turbo 3
OFF OFF ON OFF | Fixed Turbo 4
ON OFF ON OFF | Fixed Turbo 5
OFF ON ON OFF | Fixed Turbo 6
ON ON ON OFF | Fixed Turbo 7
OFF OFF OFF ON | Variable Turbo 1
ON OFF OFF ON | Variable Turbo 2
OFF ON OFF ON | Variable Turbo 3
ON ON OFF ON | Variable Turbo 4
OFF OFF ON ON | Variable Turbo 5
ON OFF ON ON | Variable Turbo 6
OFF ON ON ON | Variable Turbo 7
ON ON ON ON | Variable Turbo 8



I can show my work for all this, but I'm not sure what the rules are concerning posting disassembled code here.

EDIT: Included disassembly of Rainbow Edition's turbo code with comments

Edited by Rotwang (01/02/21 09:41 PM)



Vas Crabb
BOFH
Reged: 12/13/05
Posts: 4462
Loc: Melbourne, Australia
Send PM


Re: Digging into the secrets of SFII: Rainbow Edition new [Re: Rotwang]
#389353 - 01/02/21 05:33 AM


> I can show my work for all this, but I'm not sure what the rules are concerning
> posting disassembled code here.

Using fragments of disassembly to support the explanation is fine. That’s fair use.



Rotwang
Life is too short to be little...
Reged: 03/21/17
Posts: 109
Send PM


Re: Digging into the secrets of SFII: Rainbow Edition new [Re: Vas Crabb]
#389367 - 01/02/21 10:06 PM


Sounds good, I've added the disassembly to the original post.

Also, I have a question about Rainbow Edition in general. It was always my understanding that these hacks were sold as EPROM upgrade kits for operators with the original board, but something I saw while disassembling this turbo code suggests that there was some kind of hardware protection added by the hackers. The hijacked turbo code leads here before running Rainbow Edition turbo code proper:


Code:


0E544A movem.l D0-D4/A1, -(A7) ; Push registers to stack for later retrieval
0E544E clr.w D2
0E5450 clr.w D0
0E5452 move.b $201201.l, D0 ; Reads 0x40
0E5458 move.b $281201.l, D2 ; Reads 0x02
0E545E sub.b D0, D2
0E5460 subi.b #$30, D2 ; Result: 0x0E
0E5464 jmp ($4,PC,D2.w) ; Jump to $E5478 (Rainbow Edition's turbo code)



The $200000 region isn't normally mapped to anything in CPS1 games. I see this is accounted for in Mame's CPS1 driver, as these addresses are forced to contain the correct values that create the correct jump offset to the turbo code. But what did this actually look like on the board? Was Rainbow Edition exclusively found on bootleg hardware as opposed to an upgrade kit for legit boards?



Haze
Reged: 09/23/03
Posts: 5245
Send PM


Re: Digging into the secrets of SFII: Rainbow Edition new [Re: Rotwang]
#389368 - 01/02/21 10:24 PM


> Sounds good, I've added the disassembly to the original post.
>
> Also, I have a question about Rainbow Edition in general. It was always my
> understanding that these hacks were sold as EPROM upgrade kits for operators with the
> original board, but something I saw while disassembling this turbo code suggests that
> there was some kind of hardware protection added by the hackers. The hijacked turbo
> code leads here before running Rainbow Edition turbo code proper:
>
>
> 0E544A movem.l D0-D4/A1, -(A7) ; Push registers to stack for later retrieval
> 0E544E clr.w D2
> 0E5450 clr.w D0
> 0E5452 move.b $201201.l, D0 ; Reads 0x40
> 0E5458 move.b $281201.l, D2 ; Reads 0x02
> 0E545E sub.b D0, D2
> 0E5460 subi.b #$30, D2 ; Result: 0x0E
> 0E5464 jmp ($4,PC,D2.w) ; Jump to $E5478 (Rainbow Edition's turbo code)
>
> The $200000 region isn't normally mapped to anything in CPS1 games. I see this is
> accounted for in Mame's CPS1 driver, as these addresses are forced to contain the
> correct values that create the correct jump offset to the turbo code. But what did
> this actually look like on the board? Was Rainbow Edition exclusively found on
> bootleg hardware as opposed to an upgrade kit for legit boards?

I believe some of the boards have an extra PAL kludged in there, which probably provides the values. I can't find any examples, but I seem to recall it was something like a sub-board in place of the 68k with the 68k and a PAL on it. (or something similar with wire mods and a sub-board)

Basically the people selling kits didn't want anybody bootlegging their kits, so they had to do something more than make them a ROM swap.



Rotwang
Life is too short to be little...
Reged: 03/21/17
Posts: 109
Send PM


Re: Digging into the secrets of SFII: Rainbow Edition new [Re: Haze]
#389374 - 01/03/21 07:37 AM


> I believe some of the boards have an extra PAL kludged in there, which probably
> provides the values. I can't find any examples, but I seem to recall it was something
> like a sub-board in place of the 68k with the 68k and a PAL on it. (or something
> similar with wire mods and a sub-board)
>
> Basically the people selling kits didn't want anybody bootlegging their kits, so they
> had to do something more than make them a ROM swap.

Very interesting. Would love to see some photos of a board.

Disabling the protection would be a pretty straightforward change that I might submit to HBMAME soon.



RobbbertModerator
Sir
Reged: 08/21/04
Posts: 3200
Loc: A long way from you
Send PM


Re: Digging into the secrets of SFII: Rainbow Edition new [Re: Rotwang]
#389395 - 01/05/21 01:07 PM


>



Vas Crabb
BOFH
Reged: 12/13/05
Posts: 4462
Loc: Melbourne, Australia
Send PM


Re: Digging into the secrets of SFII: Rainbow Edition new [Re: Rotwang]
#389538 - 01/11/21 06:41 PM


Documented in MAME:
https://github.com/mamedev/mame/commit/c51c7e743c0b62015c2d45bfe3a2bb695451bc3d



Rotwang
Life is too short to be little...
Reged: 03/21/17
Posts: 109
Send PM


Re: Digging into the secrets of SFII: Rainbow Edition (Fireballs) new [Re: Vas Crabb]
#389548 - 01/12/21 08:24 AM


> Documented in MAME:
> https://github.com/mamedev/mame/commit/c51c7e743c0b62015c2d45bfe3a2bb695451bc3d

Thank you for adding this. I'm glad this research, however esoteric it may be, contributed in some way to Mame. While you're labeling dip switches, however...

New entry in what I hope will be an ongoing series:

This is the instruction in the original code that was overwritten with a jump to sf2rb's turbo mode.


Code:


000C86 btst #$0, ($88, A5) ; Test dip switch C1



Normally when a hack overwrites an instruction like this in order to hijack the program, the original behavior is replicated somewhere in the destination. However this instruction does not appear anywhere in sf2rb's new turbo code. So what does it do? It tests dip switch 1 on bank C. This switch is unused according to sf2ce's original manual. Here, however, it comes after a test of a byte in RAM at $FF82DE. I'm not entirely sure what this address does, but setting it to a non zero value forces the game into test mode. When you access test mode via the service button, this byte is not set. However, if somehow this byte gets set and undocumented switch C1 is set, you will enter test mode with sf2ce's built in turbo mode on full blast. Obviously a leftover debug feature, but I'm unsure what purpose it fulfilled.

This trivia segues nicely into the next difference between sf2rb and the original game. The hackers didn't just omit that instruction in the turbo code because it was an obsolete debug feature, but because they utilized dip switch C1 to toggle between two different fireball modes. When switch C1 is off, any fireballs you throw will home in on your opponent and track them, even if they try to jump out of the way. When switch C1 is on, fireballs will move in a consistent zigzag pattern along the ground. So how does it work?

The fireball handler in sf2ce calls subroutine $2302 to apply the fireball's movement on each frame. At this point in the code, address register A0 contains a pointer to an entry on a table of signed values to be added to the fireball's x and y positions, driving it across the screen. Fireballs are not the only entities to use this directional movement subroutine, however. Other entities that need to travel a specific distance in a specific direction, such as the airplane on the world map, also use this subroutine and table.

So when our intrepid hackers decided to change the way fireballs move in sf2rb, what did they do? They did what any sensible person would do and created a whole bunch of nested comparisons to figure out which kind of fireball was moving around on the screen.


Code:


rb_fireballs:
0E5216 btst #$0, ($88,A5) ; Test Dip C1
0E521C bne rb_fireballs_swc1_set ; Branch to zigzag fireball code if set
0E5220 movem.l D1-D3/A2, -(A7)
0E5224 move.w (-$6ed0,A5), D3 ; $FF1130. This is a counter that
; resets every time a new fireball
; is made. This controls the delayed
; speed increases of certain fireballs.
0E5228 move.w #$100, D2 ; fireball horizontal speed (xpos mod)
0E522C cmpa.l #$10f038, A0 ; A0 contains pointer to original game's
; fireball speed tables. This code is
; trying to figure out what kind of
; fireball this is by comparing A0 to
; known fireball types. They did not
; include a check for Sagat's fast tiger
; shot ($10F07A)
; it has no special behavior applied,
; and is now slower than the medium
; tiger shot.
;
; $10F038 = Slow hadoken/sonic boom/tiger shot/yoga fire, Right facing
0E5232 beq rb_fireballs_homing_movecalc
0E5236 cmpa.l #$10f048, A0 ; $10F048 = Medium hadoken/sonic boom/tiger, Right facing
0E523C bne rb_fireballs_homing_movecalc_tryfast
0E5240 cmpi.w #$10, D3 ; Keep slow speed for #$10 frames
0E5244 bls rb_fireballs_homing_movecalc
0E5248 move.w #$800, D2 ; Then go 8x faster
0E524C bra rb_fireballs_homing_movecalc

rb_fireballs_homing_movecalc_tryfast:
0E5250 cmpa.l #$10f05c, A0 ; $10F05C = Medium tiger shot, Fast hadoken/yoga fire/sonic boom, Right facing
0E5256 bne rb_fireballs_homing_movecalc_leftfacing ; Test all left facing fireballs if none of the right facing ones
0E525A cmpi.w #$4, D3 ; Keep slow speed for 4 frames
0E525E bls rb_fireballs_homing_movecalc
0E5262 move.w #$1000, D2 ; Then go 16x faster
0E5266 bra rb_fireballs_homing_movecalc

rb_fireballs_homing_movecalc_leftfacing
0E526A move.w #$ff00, D2
0E526E cmpa.l #$10f03c, A0 ; $10F03C = Slow hadoken/tiger shot/yoga fire/sonic boom, Left facing
0E5274 beq rb_fireballs_homing_movecalc
0E5278 cmpa.l #$10f04c, A0 ; $10F04C = Medium hadoken/yoga fire/sonic boom, left facing
0E527E bne rb_fireballs_homing_movecalc_leftfacing_tryfast
0E5282 cmpi.w #$10, D3 ; Keep slow speed for #$10 frames
0E5286 bls rb_fireballs_homing_movecalc
0E528A move.w #$f800, D2 ; Then go 8x faster
0E528E bra rb_fireballs_homing_movecalc

rb_fireballs_homing_movecalc_leftfacing_tryfast:
0E5292 cmpa.l #$10f060, A0 ; $10F060 = Medium tiger shot, Fast hadoken/yoga fire/sonic boom, left facing
0E5298 bne rb_fireballs_homing_normalmove
0E529C cmpi.w #$4, D3 ; Keep slow speed for 4 frames
0E52A0 bls rb_fireballs_homing_movecalc
0E52A4 move.w #$f000, D2 ; Then go 16x faster
0E52A8 bra rb_fireballs_homing_movecalc

rb_fireballs_homing_normalmove:
0E52AC move.w (A0)+, D0 ; Same fireball movement code
; as unmodified game. This is
; a copy of the code that was
; overwritten by the hijack.
;
; Any projectiles or other
; entities not caught by the
; above comparisons will wind
; up here.
0E52AE ext.l D0
0E52B0 asl.l #8, D0
0E52B2 add.l D0, ($6,A6) ; Write Xpos
0E52B6 move.w (A0), D0
0E52B8 ext.l D0
0E52BA asl.l #8, D0
0E52BC add.l D0, ($a,A6) ; Write Ypos
0E52C0 movem.l (A7)+, D1-D3/A2
0E52C4 rts

rb_fireballs_homing_movecalc:
0E52C6 move.w ($a,A2), D1 ; Opponent's Y-pos
0E52CA addi.w #$30, D1
0E52CE move.w ($a,A6), D0 ; Fireball's Y-pos
0E52D2 cmp.w D1, D0
0E52D4 blt rb_fireballs_homing_moveup ; Projectile's Ypos < Opponent's
0E52D8 bhi rb_fireballs_homing_movedown ; Projectile's Ypos > Opponent's
0E52DC bra rb_fireballs_homing_applymove ; Projectile's Ypos == Opponent's

rb_fireballs_homing_moveup:
0E52E0 addq.w #2, D0
0E52E2 bra rb_fireballs_zigzag_apply ; Use the zigzag apply movement code.
; This code does not increment the
; delayed speed counter, so if a fireball
; is thrown at an enemy who is higher in
; the air, the fireball will stay slow
; until their heights are equal or
; greater.

rb_fireballs_homing_movedown:
0E52E6 subq.w #2, D0

rb_fireballs_homing_applymove:
0E52E8 move.w D0, ($a,A6) ; Write projectile ypos
0E52EC move.w D2, D0
0E52EE ext.l D0
0E52F0 asl.l #8, D0
0E52F2 add.l D0, ($6,A6) ; Write projectile xpos
0E52F6 addq.w #1, (-$6ed0,A5) ; increment delayed speed counter
0E52FA movem.l (A7)+, D1-D3/A2
0E52FE rts

rb_fireballs_swc1_set:
0E5300 movem.l D1-D3/A2, -(A7) ; Fireballs move in a zigzag pattern.
; Tests same pointers as with homing fireballs.
; Fast tiger shot is also missed in this code.
0E5304 move.w #$100, D2
0E5308 cmpa.l #$10f038, A0 ; $10F038 = Slow hadoken/sonic boom/tiger shot/yoga fire, Right facing
0E530E beq rb_fireballs_zigzag_findslot
0E5312 cmpa.l #$10f048, A0 ; $10F048 = Medium hadoken/sonic boom/tiger, Right facing
0E5318 bne rb_fireballs_zigzag_findslot_tryfast
0E531C move.w #$600, D2 ; Not as much of a speed increase as homing fireball
0E5320 bra rb_fireballs_zigzag_findslot

rb_fireballs_zigzag_findslot_tryfast:
0E5324 cmpa.l #$10f05c, A0 ; $10F05C = Medium tiger shot, Fast hadoken/yoga fire/sonic boom, Right facing
0E532A bne rb_fireballs_zigzag_findslot_leftfacing
0E532E move.w #$a00, D2 ; Again, not as much of a speed increase
0E5332 bra rb_fireballs_zigzag_findslot

rb_fireballs_zigzag_findslot_leftfacing:
0E5336 move.w #$ff00, D2
0E533A cmpa.l #$10f03c, A0 ; $10F03C = Slow hadoken/tiger shot/yoga fire/sonic boom, Left facing
0E5340 beq rb_fireballs_zigzag_findslot
0E5344 cmpa.l #$10f04c, A0 ; $10F04C = Medium hadoken/yoga fire/sonic boom, left facing
0E534A bne rb_fireballs_zigzag_findslot_leftfacing_tryfast
0E534E move.w #$fa00, D2
0E5352 bra rb_fireballs_zigzag_findslot

rb_fireballs_zigzag_findslot_leftfacing_tryfast:
0E5356 cmpa.l #$10f060, A0 ; $10F060 = Medium tiger shot, Fast hadoken/yoga fire/sonic boom, left facing
0E535C bne rb_fireballs_zigzag_normalmove
0E5360 move.w #$f600, D2
0E5364 bra rb_fireballs_zigzag_findslot

rb_fireballs_zigzag_normalmove:
0E5368 move.w (A0)+, D0 ; Duplicate of rb_fireballs_homing_normalmove
0E536A ext.l D0
0E536C asl.l #8, D0
0E536E add.l D0, ($6,A6) ; Write Xpos
0E5372 move.w (A0), D0
0E5374 ext.l D0
0E5376 asl.l #8, D0
0E5378 add.l D0, ($a,A6) ; Write Ypos
0E537C movem.l (A7)+, D1-D3/A2
0E5380 rts

rb_fireballs_zigzag_findslot_findslot:
0E5382 move.w #$98b6, D1 ; Base address of last fireball RAM slot
0E5386 move.w #$0, D3

rb_fireballs_zigzag_findslot_findslot_loop:
0E538A cmp.w A6, D1 ; Cycles through the projectile RAM slots
; to match one with the current entity.
0E538C beq rb_fireballs_zigzag_slotfound
0E5390 subi.w #$c0, D1 ; Subtract length of a fireball RAM slot
; to test on next pass of loop
0E5394 addq.w #2, D3 ; All this loop does is find the numerical
; position of this fireball RAM slot.
0E5396 cmpi.w #$12, D3
0E539A blt rb_fireballs_zigzag_findslot_findslot_loop

rb_fireballs_zigzag_slotfound:
0E539C move.w ($a,A6), D0 ; Fireball's ypos
0E53A0 lea $ff1a10.l, A2 ; table of fireball zigzag direction switches.
; 1 = up, 2 = down
; Word length entries for some reason.
0E53A6 adda.w D3, A2 ; Offset for this projectile slot
0E53A8 move.w (A2), D3
0E53AA tst.w D3
0E53AC bne rb_fireballs_zigzag_directioncalc
0E53B0 move.w #$1, D3 ; if empty, init with 1 (move up)
0E53B4 move.b ($1c,A5), D1 ; load frame counter
0E53B8 andi.w #$1, D1 ; mask for alternating frames
0E53BC add.w D1, D3 ; On an odd frame, fireball begins by moving down
0E53BE move.w D3, (A2) ; Store to direction table

rb_fireballs_zigzag_directioncalc:
0E53C0 cmpi.w #$1, D3 ; Comparison determines if fireball goes up or down
0E53C4 bne rb_fireballs_zigzag_movedown
0E53C8 addq.w #4, D0 ; Add 4 to ypos
0E53CA cmpi.w #$90, D0 ; Switch to downward movement if above #$90
0E53CE bls rb_fireballs_zigzag_apply
0E53D2 move.w #$2, (A2) ; Switch to downward direction
0E53D6 bra rb_fireballs_zigzag_apply

rb_fireballs_zigzag_movedown:
0E53DA subq.w #4, D0 ; Subtract 4 from ypos
0E53DC cmpi.w #$30, D0 ; Switch to upward movement if below #$30
0E53E0 bgt rb_fireballs_zigzag_apply
0E53E4 move.w #$1, (A2) ; Switch to upward direction
0E53E8 bra rb_fireballs_zigzag_apply

rb_fireballs_zigzag_apply:
0E53EC move.w D0, ($a,A6) ; Apply ypos
0E53F0 move.w D2, D0
0E53F2 ext.l D0
0E53F4 asl.l #8, D0
0E53F6 add.l D0, ($6,A6) ; Apply xpos
0E53FA movem.l (A7)+, D1-D3/A2
0E53FE rts



Hopefully the comments speak for themselves. I will summarize the main takeaways.

- Fast tiger shots are NOT affected by either of the new fireball code due to an oversight by the hackers. They will simply fly straight across the screen at the same speed as always, and will not track the opponent or zigzag around. They now travel slower than a medium tiger shot.
- Medium and fast homing fireballs have a brief delay at their start before picking up speed while slow fireballs stay slow for their entire duration.
- If a homing fireball is thrown at an enemy who is higher in the air than the fireball, it will not pick up speed until it is at least at level height with the enemy.
- Zigzag fireballs will begin angled up on even frames and angled down on odd frames.
- Zigzag fireballs will not stay high up in the air if you throw one while jumping. They always seek out the same distances from the ground every time.


Code:


DIP SWITCH BANK C 1
==========================
PROJECTILE TYPE |
----------------|
Homing | OFF
Zigzag | ON



Edited by Rotwang (01/14/21 08:39 PM)



Vas Crabb
BOFH
Reged: 12/13/05
Posts: 4462
Loc: Melbourne, Australia
Send PM


Re: Digging into the secrets of SFII: Rainbow Edition (Fireballs) new [Re: Rotwang]
#389550 - 01/12/21 10:32 AM


Confirmed and documented. Thanks.



fortuna_chan
Por que yo hablo español muy bien.
Reged: 06/01/08
Posts: 238
Send PM


Re: Digging into the secrets of SFII: Rainbow Edition new [Re: Rotwang]
#389562 - 01/13/21 09:25 PM


Exists this in sets like M9, Koryu, YYC, L735, Dstreet6, Mstreet-6?

"Playing" with the unknown dips various change various things in game too, like fireballs and speed (like high speed)



Rotwang
Life is too short to be little...
Reged: 03/21/17
Posts: 109
Send PM


Re: Digging into the secrets of SFII: Rainbow Edition new [Re: fortuna_chan]
#389565 - 01/14/21 05:07 AM


> Exists this in sets like M9, Koryu, YYC, L735, Dstreet6, Mstreet-6?
>
> "Playing" with the unknown dips various change various things in game too, like
> fireballs and speed (like high speed)

I haven't looked into any other bootlegs yet. Not all of them are based on the same version but some of them at least have similar features. Don't know what the code looks like (yet).



CiroConsentino
Frontend freak!
Reged: 09/21/03
Posts: 6211
Loc: Alien from Terra Prime... and Brazil
Send PM


Re: Digging into the secrets of SFII: Rainbow Edition (Fireballs) new [Re: Vas Crabb]
#389567 - 01/14/21 01:02 PM


This is so cool. Can't wait to try it out.



Emu Loader
Ciro Alfredo Consentino
home: http://emuloader.mameworld.info
e-mail: [email protected]



fortuna_chan
Por que yo hablo español muy bien.
Reged: 06/01/08
Posts: 238
Send PM


Re: Digging into the secrets of SFII: Rainbow Edition new [Re: Rotwang]
#389577 - 01/14/21 11:58 PM


> > Exists this in sets like M9, Koryu, YYC, L735, Dstreet6, Mstreet-6?
> >
> > "Playing" with the unknown dips various change various things in game too, like
> > fireballs and speed (like high speed)
>
> I haven't looked into any other bootlegs yet. Not all of them are based on the same
> version but some of them at least have similar features. Don't know what the code
> looks like (yet).


Thanks for your response, i'm wait for good news pass the weeks, take your time



Rotwang
Life is too short to be little...
Reged: 03/21/17
Posts: 109
Send PM


Re: Secrets of SFII' Rainbow (CPU opponents, in-air moves, title colors) new [Re: Rotwang]
#389745 - 01/28/21 08:48 PM


I'm back after taking some time off from looking at this hack. I'll be covering several subjects in one post this time.

The next code hijack in this ROM happens at $26E6 which is some code that runs prior to the start of any new round. Here is what SF2RB redirects it to:


Code:


0E5000 move.w #$40, (-$6700,A5) ; $FF1900
0E5006 tst.b ($94c,A5) ; Test 2P active status
0E500A bne $e5028 ; Branch if 2P active
0E500E tst.b ($64c,A5) ; Test 1P active status
0E5012 beq $e5036 ; Return if 1P inactive (both inactive)

0E5016 move.w #$6, (-$6f10,A5) ; write to $FF10F0
0E501C move.w #$6, (-$6c10,A5) ; write to $FF13F0
0E5022 move.b (-$65ff,A5), ($94f,A5) ; ($FF1A01) copied to 2P character ID

0E5028 tst.b ($64c,A5) ; Test 1P active status
0E502C bne $e5036 ; Return if both 1P and 2P active (feature only works VS CPU)
0E5030 move.b (-$65ff,A5), ($64f,A5) ; ($FF1A01) copied to 1P character ID

0E5036 move.b ($1c,A5), D0 ; original hijacked coide
0E503A andi.w #$3, D0
0E503E rts



Before I explain what this does, here is the next hijack which is found at $2BC6, which runs once before any new match begins.


Code:


0E5040 move.w (A1,D0.w), ($9be,A5) ; Original hijacked code (this instruction only)
; Copies the next CPU opponent's character ID to 2P RAM.
0E5046 move.w ($9be,A5), (-$6600,A5) ; $FF1A00 - Preserves CPU opponent's
; character ID so it can be reset
; between rounds. This assumes
; 2P is CPU controlled.
0E504C move.b ($64f,A5), (-$6603,A5) ; 1P character ID, $FF19FD
0E5052 rts



Here's what's happening. When 1P selects a character, the program populates a table in RAM at $FFDD5E with word length player character IDs. This is the order in which the player will fight the rest of the characters.




Code:


0 = Ryu
1 = E.Honda
2 = Blanka
3 = Guile
4 = Ken
5 = Chun Li
6 = Zangief
7 = Dhalsim
8 = Dictator
9 = Sagat
A = Boxer
B = Claw



When the round ends and your CPU opponent has switched to some other character, that first code hijack forces the CPU's character back to what it originally was. If both players are human controlled they keep their changed character at the next round's start. Whats weird here though is that it does something extra if player 1 is the active player. It writes #6 to two addresses, and doesnt do that if 2P is the human player fighting a CPU. Not sure yet what those addresses do, but hopefully we will find out in another post.

What sub $E0040 does is just copies the CPU opponent's character ID to be retrieved by sub $E0000 before the round starts proper. It assumes 2P is CPU controlled though, possibly because a Human VS CPU game can only be started from 1P's side.

================================

The next change is a fun one. It's not even a proper code hijack either, all the hackers did was change one instruction.


Code:


0032D6 tst.b ($28e,A6)
0032DA beq $3344
0032DE tst.b ($181,A6) ; Test if player is in-air
0032E2 bne $32e6 ; Used to branch to $3344
0032E6 tst.b ($2e3,A5)
0032EA bne $3344
0032EE tst.b ($ac2,A5)
0032F2 bne $3344
0032F6 tst.b ($ace,A5)
0032FA bne $3344
0032FE cmpi.b #$c, ($3,A6)
003304 beq $3344
etc, etc...



What this change does is, you guessed it, allow the player to perform a special move in the air. The conditional branch after the byte test has been changed to simply branch to the instruction that comes right after it. I wonder why the hackers did it this way as opposed to NOP-ing out the whole comparison, but it wouldn't be the first thing I've seen in this hack that made me scratch my head.

================================

The code changes I'll cover in this section are also not hijacks. They are further tweaks to the Player VS CPU experience to account for the CPU being able to switch characters in the middle of a fight. The function these changes appear in are what "clears" a match from the world map. At the conclusion of a fight, this code checks the losing player's character ID and then clears that match from the world map. All the hackers have changed is where it reads that character ID from. Instead of retrieving it from the losing player's RAM region, it reads it from the preserved CPU character ID that was set aside in sub $E5040. So, if you were fighting CPU Guile and he turned into Zangief before you won the match, Guile's stage will be cleared on the map instead of Zangief's.

I'm not going to reverse engineer the entire function as it's long and only a few instructions have been changed. Instead I will only highlight which instructions have been modified. Basically, any instance of reading from ($291,A1) (NOTE: at this point in the code, A1 contains a pointer to the losing player's base RAM region and $291 is the offset to the player's character ID) has been changed to (-$65ff,A5).


Code:

$91DE  move.b (-$65ff,A5), D0

$91F0 cmpi.b #$8, (-$65ff,A5) ; tests if defeated opponent is Dictator

$9212 cmpi.b #$8, (-$65ff,A5) ; tests if defeated opponent is Dictator (again)

$9228 move.b (-$65ff,A5), D0

$924A move.b (-$65ff,A5), D1



A side effect of this change is that this also applies to player VS player fights. If 2P has challenged 1P to a PVP game and defeats 1P, it won't clear 1P's character's stage from the map, it will clear whoever 1P's CPU opponent originally was. In the unmodified original game, the defeated opponent's stage would be cleared on the world map whoever it was.

================================

I'll end on a bit of a fluff change. The next change in ROM is only one byte long. $19E07 has been changed from #$1F to #$04. This is what changes the title screen graphic's palette assignment. OBJ palette #$04 is actually Guile's sprite palette. His American flag tattoo, yellow hair and green clothes are what give the title screen the funky colors of this hack's unofficial namesake. (EDIT: this same change appears again soon after at $19E6D, probably the first is for the smaller billboard title graphic and the second is the larger one)





================================

That's all for now. Hope you've been enjoying these.

Edited by Rotwang (01/28/21 10:50 PM)


Pages: 1

MAMEWorld >> EmuChat
View all threads Index   Threaded Mode Threaded  

Extra information Permissions
Moderator:  Robbbert, Tafoid 
0 registered and 349 anonymous users are browsing this forum.
You cannot start new topics
You cannot reply to topics
HTML is enabled
UBBCode is enabled
Thread views: 1198