68K ASM Proofread Help (<20 lines of code)

Discussion in 'Engineering & Reverse Engineering' started by eskayelle, Dec 8, 2019.

  1. eskayelle

    eskayelle

    NBA Jam 2K20 TE / TMNT of Rage Developer Member
    20
    0
    1
    Hey there. So... Streets of Rage 2 has this functionality where the default life points (health value) are assigned, and each time a player dies, they receive that same number of life points for their next life. I'd like to break the code up a bit, such that the first life is assigned fewer life points, and then when a player dies, their next lives are full lives (0x68 life points).

    I've written some VERY rudimentary ASM and am looking to see if someone wouldn't mind reading through the code and letting me know what fatal flaws I've written. There are, for certain, syntax errors, and I'm using certain 65c816 mnemonics, as I don't know the 68K equivalents yet. My beq v. bne is probably also going in the wrong direction.

    I also plan to inject the code straight into the ROM via the equivalent hex bytes and test the functioning once the code makes sense.

    Any help you can provide would be much appreciated! (I tend to credit folks in game and/or on the distribution site and within the readme.)

    Code (Text):
    1. ;=========================================
    2. ;Streets of Rage 2
    3. ;Initial Health Code Modifications v7
    4. ;=========================================
    5.  
    6. ;-----------------------------------------
    7. ;Purpose: Hijack the code before the 1st
    8. ;stage starts and send it to another area
    9. ;in ROM to add functionality to two more
    10. ;RAM bytes.
    11. ;-----------------------------------------
    12. org $00005CBE   ;offset to hijack
    13. ; lea ($00005D74),A6 ;4D F9 00 00 5D 74 ;original (hijacked) $5CBE code
    14.  jsr $00024E00  ;4E B9 00 02 4E 00 ;jump to new code location
    15.  
    16. ;-----------------------------------------
    17. ;Purpose: Set two unused RAM bytes to zero
    18. ;before a game (stage 1) starts.
    19. ;These bytes will be changed so the health
    20. ;value increases to 0x68 whenever a player
    21. ;dies, but the very first life should have
    22. ;a health value much less than the default
    23. ;0x68.
    24. ;-----------------------------------------
    25. org $00024E00   ;offset to hijack
    26.  move.b #$00,$F0BE ;11 FC 00 00 F0 BE ;create a player 1 parameter (1 byte)
    27.  move.b #$00,$F0BF ;11 FC 00 00 F0 BF ;create a player 2 parameter (1 byte)
    28.  lea ($00005D74),A6 ;4D F9 00 00 5D 74 ;original (hijacked) $5CBE code
    29.  rts   ;4E 75   ;return to hijacked location and continue code
    30.  
    31. ;-----------------------------------------
    32. ;Purpose: The byte with the initial health
    33. ;value is at offset $7EC8.
    34. ;Need to modify this code to split the
    35. ;commands such that the initial value is
    36. ;reduced, but all lives after receive a
    37. ;larger health value (the old 0x68).
    38. ;-----------------------------------------
    39. org $00007EC8   ;offset to hijack
    40. ; move.w #$0068,$0080(A2) ;35 7C 00 68 00 80 ;this is the original code at $7EC8
    41.  jsr $00024E50  ;4E B9 00 02 4E 50 ;jump to new code location
    42.  
    43. org $00024E50   ;new code to split out 0x68 health per player after death
    44.  addq.b,#1,$F0BE  ;52 38 F0 BE  ;increment new player 1 byte
    45.  addq.b,#1,$F0BF  ;52 38 F0 BF  ;increment new player 2 byte
    46.  cmpi.b #$01,$F0BE ;0C 38 00 01 F0 BE ;P1 RAM byte should be #$01 the first stage, first life
    47.  beq.s splithealth ;67 10  ;if equals #$01, give #$01 in health; else, give health of #$68
    48.  cmpi.b #$01,$F0BF ;0C 38 00 01 F0 BF ;P2 RAM byte should be #$01 the first stage, first life
    49.  beq.s splithealth ;67 08  ;if equals #$01, give #$01 in health; else, give health of #$68
    50.  move.w #$0068,$0080(A2) ;35 7C 00 68 00 80 ;this is the original code at $7EC8
    51.  rts   ;4E 75   ;looking to jump back to $7ECE -- clr.w $00A8(A2)
    52.  
    53. splithealth:
    54. ; move.b #$68,$EF80 ;11 FC 00 68 EF 80 ;$EF80 appears to be where the default health value comes from; modifying original $7EC8 code may be better
    55. ; move.w #$0068,$0080(A2) ;35 7C 00 68 00 80 ;this is the original code at $7EC8; seems to do the same thing my code above would have
    56.  move.w #$0001,$0080(A2) ;35 7C 00 01 00 80 ;change #$68 to a lower number, so when $F0BE or $F0BF = #$01, that player starts game with less life
    57.  rts   ;4E 75   ;looking to jump back to $7ECE -- clr.w $00A8(A2)
    58. ;
    59. ;------------------------------------------
    60. ;As written:
    61. ;When the ROM starts, P1 and P2 have RAM
    62. ;values of zero.
    63. ;When the first stage of the first game
    64. ;starts, the RAM values will increment to
    65. ;#$01.  The comparison will occur.
    66. ;The code will branch, and default health
    67. ;for both players will be 1 unit.
    68. ;Plus, both RAM values will increment.
    69. ;P1 and P2 will have RAM values of 2.
    70. ;So, whenever either dies, when this code
    71. ;runs again, it will not branch, and life
    72. ;points assigned will be #$68.
    73. ;
    74. ;When a game is reset (hard or soft),
    75. ;the RAM bytes should be assigned values
    76. ;of zero.
    77. ;
    78. ;Test:
    79. ;Seems to work as intended for P1,
    80. ;but P2 will start with full life.
    81. ;This will be the puzzle...
    82. ;
    83. ;Will the stronger player take P1?
    84. ;Will P2 let P1 take the 1up or make P1
    85. ;take the suicide run for the chicken?
    86. ;------------------------------------------
     
    Last edited: Dec 10, 2019
  2. MainMemory

    MainMemory

    Have no fear...Amy Rose is here! Tech Member
    4,449
    79
    28
    SonLVL
    1. What's a jsl?
    2. That beq is not valid syntax, you need to specify an address or a label, as well as a size on the instruction itself (.s or .w).
    3. cmpi takes two parameters, one immediate value and a register/memory.
     
  3. Wafer

    Wafer

    Find me on Twitter instead Member
    255
    75
    28
    Addendum to MainMemory's point 1: based on a quick Google search I'm guessing that jsl is meant to jump to a subroutine, so you'll want to use a jsr there (or maybe a bsr.x where x is s or w, check the 68k docs for the difference between the two opcodes. You're safe with jsr, but bsr can be faster sometimes, IIRC).
     
    Last edited: Dec 8, 2019
  4. Ralakimus

    Ralakimus

    pretty much a dead account Tech Member
    557
    162
    43
    In the 65c816 CPU, JSL means "Jump to Subroutine Long", which takes a 24 bit address, so that it can jump to pretty much anywhere in the address space (JSR only does it within a bank). Though, you also have to use "RTL" to return properly. They mentioned "65c816" in the original post, so I think that's what they're referring to.

    The 68000 doesn't naturally do bankswitching, so in this case, "JSR" would be the equivalent to "JSL". Likewise, "JML"' equivalent would be JMP.
     
    Last edited: Dec 8, 2019
  5. Wafer

    Wafer

    Find me on Twitter instead Member
    255
    75
    28
    @Ralakimus That was my gut feeling too (not being familiar with 65c816), with the matching org directives backing it up, but I figured I'd be thorough. I use bsr and bra probably more often than is necessary in my own code (and spend more time fixing out-of-range issues as a result).
     
  6. eskayelle

    eskayelle

    NBA Jam 2K20 TE / TMNT of Rage Developer Member
    20
    0
    1
    Thanks, guys! I was indeed using a 65c816 mnemonic, since I'm more used to that code from my 3 summers of NBA Jam TE hacking. I looked into a couple tutorials as well as the responses here and went with jsr. I also added the parameter to the cmpi. Edits made to the code in the original post.

    For the beq, I assumed you could just tell the 68k to go to the next byte (or go [x] bytes, like an effective address). Is that not the case? I haven't figured out what you guys are using for an assembler, based on a number of Google searches, so my alternative was going to be convert the code by hand to hex and drop that into the rom.

    I'm admittedly not sure of the org equivalent for 68k assembly, so that's another placeholder and 65c816 mnemonic (e.g., for assembling SNES code via xkas).
     
  7. Wafer

    Wafer

    Find me on Twitter instead Member
    255
    75
    28
    I imagine most people are using (or prefer) asm68k, which is totally pirated and hacked but ¯\_(ツ)_/¯ Same goes for snasm68k, I believe. The alternative is AS, which is more legally sound (and I believe why most of the Sonic disasms use it) but comes with its own... eccentricities.

    (I keep thinking about writing a Python preprocessor for AS to make it behave more like asm68k but I haven't found the energy for it yet)

    What MainMemory was getting at with the beq syntax is that you need to specify the width of the effective address. So .s if your effective address fits in a byte, or .w otherwise. If you haven't already, do a search for 68k instruction set. There's a PDF there that's a good reference (would link but my phone is being bleh). You could also look for MarkeyJester's 68k reference site (http://mrjester.hapisan.com/04_MC68/), or RHS's intro to 68k assembly right here on the wiki: http://info.sonicretro.org/SCHG_How-to:Work_with_Motorola_68000_assembly
     
    Last edited: Dec 8, 2019
  8. MainMemory

    MainMemory

    Have no fear...Amy Rose is here! Tech Member
    4,449
    79
    28
    SonLVL
    ORG is fine, 68000 assemblers also use that directive. For the beq, it needs to look something like this:
    Code (Text):
    1.    beq.s mylabel
    2.    ;code here
    3. mylabel:
    4.    ;more code
     
  9. eskayelle

    eskayelle

    NBA Jam 2K20 TE / TMNT of Rage Developer Member
    20
    0
    1
    Thanks for all the help! I did indeed start with MarkeyJester's tutorial to get a feel for 68K mnemonics, and it's been pretty helpful. I'm also peeking through the RHS intro docs on the site as well.

    I've made some changes to the code in the OP based on all the guidance above; I also killed those lea commands I had written because they don't seem to work same as a 658c16 LDA. I replaced them with more direct cmpi.b commands and shortened the code.
     
    Last edited: Dec 10, 2019
  10. eskayelle

    eskayelle

    NBA Jam 2K20 TE / TMNT of Rage Developer Member
    20
    0
    1
    This move.w #$0068,$0080(A2) command at $7EC8 has me curious. Both players 1 and 2 use the same subroutine to generate/re-generate the player, with $FFFFEF80 the life counter for P1 and $FFFFF080 the life counter for P2. The code will reduce the life value for P1's first life, but it ignores it for P2 and gives them the default 0x68 value. Not sure why or what I've written that didn't impact P2 as intended.
     
    Last edited: Dec 10, 2019