Basic Questions & Answers thread

Discussion in 'Engineering & Reverse Engineering' started by Tweaker, May 29, 2008.

  1. E-122-Psi

    E-122-Psi

    Member
    2,470
    612
    93
    It does. Thanx, Markey, you've provided a fix for at least three of my projects. :D
     
  2. Caverns 4

    Caverns 4

    Member
    346
    0
    16
    Sonic: Retold
    Okay, so, I have been trying to make a change to my hack that may be a bit of an arbitrary idea, but something I wanted to figure out, mainly for the sake of learning. So far, my efforts have been... Less than successful.

    To put it simply, I'm trying to make the level layouts in my Sonic 2 hack uncompressed for now, and I've been having some issues figuring everything out.

    Here is the best result I've gotten so far:
    [​IMG]

    The original code looked like this:
    Code (Text):
    1. loadLevelLayout:
    2.     moveq   #0,d0
    3.     move.w  (Current_ZoneAndAct).w,d0
    4.     ror.b   #1,d0
    5.     lsr.w   #6,d0
    6.     lea (Off_Level).l,a0
    7.     move.w  (a0,d0.w),d0
    8.     lea (a0,d0.l),a0
    9.     lea (Level_Layout).w,a1
    10.     bra.w   JmpTo_KosDec
    As far as I can figure out, all I need to do is get rid of the jump to Kosdec, move a0 to a1, and add an RTS But that didn't do it. Any idea what I'm doing wrong?
     
  3. MarkeyJester

    MarkeyJester

    Original, No substitute Resident Jester
    2,214
    459
    63
    Japan
    It's not that you're doing anyting "wrong", it's just that you're missing something major out, so, let's observe, we'll change your above original code to the way that you specified, but with a slight difference:

    Code (ASM):
    1. loadLevelLayout:
    2.     moveq   #0,d0
    3.     move.w  (Current_ZoneAndAct).w,d0
    4.     ror.b   #1,d0
    5.     lsr.w   #6,d0
    6.     lea (Off_Level).l,a0
    7.     move.w  (a0,d0.w),d0
    8.     lea (a0,d0.l),a0
    9.     move.l  a0,(Level_Layout).w         ; save the "address" of the FG layout
    10.     lea $80(a0),a0              ; advance the layout address to the BG area
    11.     move.l  a0,(Level_Layout+$04).w         ; save the "address" of the BG layout
    12.     rts                     ; return
    So now, the address/location of the FG layout is stored from "Level_Layout" to "Level_Layout+$03" as a long-word address, and the address/location of the BG layout is stored from "Level_Layout+$04" to "Level_Layout+$07" as a long-word address.

    The loading/setting up aspect of the layout is covered, but what isn't covered is the reading of the layout, as far as the draw code and collision codes are concerned, the entire layout has been decompressed to RAM, it doesn't know yet that you've changed the way you've loaded the layout.

    In several areas of the source code, you will have instances such as "lea (Level_Layout).w,a4", which is used to load the RAM address as the location of the layout, you'll want to change them to something along the lines of "move.l (Level_Layout).w,a4", the same would go for the BG, I know for a fact that the old disassemblies have sections such as "lea ($FFFF8080).w,a4" (I don't think they had an equate for that RAM address), for that example, you would change to "move.l (Level_Layout+$04).w,a4" to load the ROM address of the BG layout.

    A side warning though, would be that some areas might do things differently, for example:

    Code (ASM):
    1.     lea (Level_Layout).w,a0         ; load FG layout
    2.     bsr ReadFG                  ; read FG layout
    3.     lea $80(a0),a0              ; load BG layout
    This is not in Sonic 2, I just made it up as an example, just in case something like this does exist somewhere, if it does, then you would want to change it to:

    Code (ASM):
    1.     move.l  (Level_Layout).w,a0         ; load FG layout
    2.     bsr ReadFG                  ; read FG layout
    3.     move.l  (Level_Layout+$04).w,a0         ; load BG layout
     
  4. flamewing

    flamewing

    Emerald Hunter Tech Member
    1,161
    65
    28
    France
    Sonic Classic Heroes; Sonic 2 Special Stage Editor; Sonic 3&K Heroes (on hold)
    The only thing have to add to add to MarkeyJester's post is that some levels perform level layout edits. Off the top of my head, Sonic 2 does this in WFZ (and I can't remember anywhere else). You will have to either add a workaround for it or remove the outside->inside swap that happens when you bust that thing near the boss.
     
  5. RetroKoH

    RetroKoH

    Member
    1,662
    22
    18
    Project Sonic 8x16
    LZ3 does something very similar, as I noticed when porting Sonic 128 to the HG disasm... which uses the coding methods that Markey is talking about. Here is the code for how Sonic 128 handles this layout change. I think you can apply something similar to the instances in Sonic 2. This is from Sonic 128's DynamicLevelEvent.asm file:

    Code (Text):
    1.  
    2.  
    3. DLE_LZ3:
    4.         tst.b   (f_switch+$F).w ; has switch $F been pressed?
    5.         beq.s   loc_6F28    ; if not, branch
    6.         move.l  (v_lvllayout).w,d0          ; MJ: load layout being read currently
    7.         cmp.l   #Level_LZ3_WALL,d0          ; MJ: is it already set to wall version?
    8.         beq.s   loc_6F28                ; MJ: if so, branch to skip
    9.         move.l  #Level_LZ3_WALL,(v_lvllayout).w     ; MJ: Set wall version of act 3's layout to be read
    10.         move.w  #sfx_Rumbling,d0
    11.         bsr.w   PlaySound_Special ; play rumbling sound
    12.  

    Basically you would have to have another layout file, nearly identical aside from the change that occurs... this is due to it being read from the ROM, and no longer from RAM. So just bear in mind that you'll be making your ROM a little bit larger.
     
  6. MainMemory

    MainMemory

    Kate the Wolf Tech Member
    4,745
    338
    63
    SonLVL
    I personally used an unrolled loop of movem instructions to copy the uncompressed layouts to RAM as quickly as possible, which still allows for dynamic layout changes, such as in CNZ and WFZ's boss arenas.
     
  7. Caverns 4

    Caverns 4

    Member
    346
    0
    16
    Sonic: Retold
    I have read your post entirely, and while it's an interesting idea, unless I'm missing something from what you said, It didn't seem to work.

    As you instructed, I did change all of the needed lea's to move.l.

    An "unrolled" loop? I have a little bit of an understanding with loops, but I'm not familiar with that terminology.
    I would like to hear a more about your method, it sounds closer to what I'm hoping to achieve.
     
  8. flamewing

    flamewing

    Emerald Hunter Tech Member
    1,161
    65
    28
    France
    Sonic Classic Heroes; Sonic 2 Special Stage Editor; Sonic 3&K Heroes (on hold)
    "Loop unrolling" is a technique that trades off space (code size) for speed. It consists on reducing the loop counter by a constant factor and replicating the code in the loop by the same factor, thus reducing the loop overhead. Let me give an example: consider this code:
    Code (Text):
    1.     move.w  #$E0-1,d1
    2. loop:   move.l  d0,(a1)+
    3.     dbra    d1,loop
    An unrolled version of it might look like this:
    Code (Text):
    1.     move.w  #($E0/8)-1,d1
    2. loop:   move.l  d0,(a1)+
    3.     move.l  d0,(a1)+
    4.     move.l  d0,(a1)+
    5.     move.l  d0,(a1)+
    6.     move.l  d0,(a1)+
    7.     move.l  d0,(a1)+
    8.     move.l  d0,(a1)+
    9.     move.l  d0,(a1)+
    10.     dbra    d1,loop
    with eight iterations unrolled. The number of "move.l" instructions executed is still the same, but there are 8 times less "dbra" instructions; this translates into less time to execute this loop. For this particular loop, unrolling 32 iterations results in gains of about 2000 cycles. Unrolling is made easier by use of macros; for instance, the above code could be written as
    Code (Text):
    1.     move.w  #($E0/8)-1,d1
    2. loop:   rept 8
    3.     move.l  d0,(a1)+
    4.     endm
    5.     dbra    d1,loop
    which allows easier tweaking of the number of repetitions unrolled. You could even take it to the extreme and unroll all iterations (doing away with the "move.w" and "dbra" instructions entirely), but it is usually not worth it to unroll that much (too much additional code).

    Edit: this is an extract from the LZ background scrolling code; there are a few instructions omitted for clarity. I unrolled it by a factor of 32 in my hack, which is why I remember the ballpark value of the number of cycles saved.
     
  9. Caverns 4

    Caverns 4

    Member
    346
    0
    16
    Sonic: Retold
    Here I thought more code just took longer to read. Shows how little I know.

    I like this idea, so I'm trying to do that with my code now. I do understand what I'm doing mostly now, except for some reason I'm getting incorrect 128x128 chunks again.

    In fact, it seems like that's been the problem the whole time; when I try getting the level layout uncompressed, using whatever means, I think it's just not using the offset the same way it does when decompressed or something.

    I tried using both the Kosinski and Nemesis decompression routines (KosDec and NemDecToRAM), and they get the proper level layout information.

    With that said, here's what I've done for the moment, perhaps it will shed some light on what still needs to be done.
    Code (Text):
    1. loadLevelLayout:
    2.     moveq   #0,d0
    3.     move.w  (Current_ZoneAndAct).w,d0
    4.     ror.b   #1,d0
    5.     lsr.w   #6,d0
    6.     lea (Off_Level).l,a0
    7.     move.w  (a0,d0.w),d0
    8.     lea (a0,d0.l),a0
    9.     lea (Level_Layout).w,a4
    10.     div.w #$20,d1
    11. LevelLayoutLoop:
    12.     move.l  (a0)+,a4 ;1
    13.     move.l  (a0)+,a4 ;2
    14.     move.l  (a0)+,a4 ;3
    15.     move.l  (a0)+,a4 ;4
    16.     move.l  (a0)+,a4 ;5
    17.     move.l  (a0)+,a4 ;6
    18.     move.l  (a0)+,a4 ;7
    19.     move.l  (a0)+,a4 ;8
    20.     dbra    d1,LevelLayoutLoop
    21.     rts
     
  10. MainMemory

    MainMemory

    Kate the Wolf Tech Member
    4,745
    338
    63
    SonLVL
    Normal loops have a register containing the number of iterations minus one and a dbf instruction that decrements the register and branches back to the beginning of the loop until the register's value is -1. For an unrolled loop, you can use rept count and endm, and AS will assemble the instructions between them count times.
    My code is like this:
    Code (ASM):
    1. loadLevelLayout:
    2.     moveq   #0,d0
    3.     move.w  (Current_ZoneAndAct).w,d0
    4.     ror.b   #1,d0
    5.     lsr.w   #6,d0
    6.     lsl.l   d0
    7.     lea (Off_Level).l,a0
    8.     move.l  (a0,d0.w),d0
    9.     lea (a0,d0.l),a0
    10.     lea (Level_Layout).w,a1
    11.     ;bra.w  JmpTo_KosDec
    12.     rept    78
    13.     movem.l (a0)+,d0-d7/a2-a6
    14.     movem.l d0-d7/a2-a6,(a1)
    15.     lea 13*4(a1),a1
    16.     endm
    17.     movem.l (a0),d0-d7/a2-a3
    18.     movem.l d0-d7/a2-a3,(a1)
    19.     rts
    Each loop uses 13 4-byte registers to copy 52 bytes per loop, since the loop is repeated 78 times, that's a total of 4056 bytes. Since level layouts in Sonic 2 are all 4KB (4096 bytes), you need an extra movem using 10 registers for the last 40 bytes.
     
  11. Caverns 4

    Caverns 4

    Member
    346
    0
    16
    Sonic: Retold
    That did it!
    Okay, now it seems like the only thing not working is the 4-act zone expansion.

    I tried changing
    Code (Text):
    1.         ror.b   #1,d0
    2.         lsr.w   #6,d0
    to

    Code (Text):
    1.         ror.b   #2,d0
    2.         lsr.w   #5,d0
    like we did before, but it didn't help. What else needs to be changed?
     
  12. MainMemory

    MainMemory

    Kate the Wolf Tech Member
    4,745
    338
    63
    SonLVL
    Oh, right. My level table uses longword offsets because word offsets didn't have enough range to fit all the uncompressed level data. If you're using word-sized offsets, you can just replace everything up to the commented out KosDec branch with the original code.
     
  13. Caverns 4

    Caverns 4

    Member
    346
    0
    16
    Sonic: Retold
    Unfortunately, my table is also a long.

    Using the unmodified vode you gave me, every act 1 and 2 seems to work (Though I haven't checked with every act and zone, obviously), but act 3 is using act 1's level layout. I can't say about act 4 since I don't really have any zones that use the fourth act currently.
     
  14. MainMemory

    MainMemory

    Kate the Wolf Tech Member
    4,745
    338
    63
    SonLVL
    As far as I can tell that should work... you could also change the lsr to 4 and remove the lsl to shave off an instruction.
     
  15. Caverns 4

    Caverns 4

    Member
    346
    0
    16
    Sonic: Retold
    I tried doing that, I'm still getting weird results... No idea why.

    Here's the code I have right now:

    Code (Text):
    1. loadLevelLayout:
    2.         moveq   #0,d0
    3.         move.w  (Current_ZoneAndAct).w,d0
    4.         ror.b   #2,d0
    5.         lsr.w   #4,d0
    6.         ;lsl.l   d0
    7.         lea     (Off_Level).l,a0
    8.         move.l  (a0,d0.w),d0
    9.         lea     (a0,d0.l),a0
    10.         lea     (Level_Layout).w,a1
    11.         ;bra.w  JmpTo_KosDec
    12.         rept    78
    13.         movem.l (a0)+,d0-d7/a2-a6
    14.         movem.l d0-d7/a2-a6,(a1)
    15.         lea     13*4(a1),a1
    16.         endm
    17.         movem.l (a0),d0-d7/a2-a3
    18.         movem.l d0-d7/a2-a3,(a1)
    19.         rts
    What have I done wrong?

    EDIT: I figured it out! It was the offset table, not your code.

    I changed it to longword offsets, but I didn't change the input for the zoneOrderedOffsetTable macro. I didn't think about it at first, but that seemed to quite fix it.

    Code (Text):
    1. Off_Level: zoneOrderedOffsetTable 2,4
    I should have changed that to

    Code (Text):
    1. Off_Level: zoneOrderedOffsetTable 4,4
    It's working now! Thanks so much for your help!
     
  16. Caverns 4

    Caverns 4

    Member
    346
    0
    16
    Sonic: Retold
    I.. Have been scratching my head a lot over this, and I finally decided I need some help. I feel like part of me knows what's wrong, but I can't quite put my finger on it.

    I imported the Sonic 1 Title Cards (Or, the Sonic 2NA Title cards without the RTS's before DisplaySprite branches) into Sonic 2, and while I got everthing working okay in terms of functionality, I have two problems, but I'm going to tackle the one that, in my eyes, is more important ATM:

    Whenever I start a zone, this happens:
    [​IMG]

    If I were to take a guess, it would be that the game is preparing Plane A to be deformed by the title card, but since the title card isn't there in the for it wants, it just stays like that. When you start moving, all the titles that were offscreen start displaying properly.

    I looked at (and spent hours trying to tweak) the routine DrawInitialBG:, which I also have renamed LoadTilesFromStart, mainly for consistency, and while I think that's part of the problem, "fixing" it to the best of my understanding to be consistent with the Sonic 2 NA version hasn't really resolved the issue.

    Below is my code for said Routine as I have it now, hopefully I just overlooked something simple:

    Code (Text):
    1. LoadTilesFromStart:
    2.     lea (VDP_control_port).l,a5
    3.     lea (VDP_data_port).l,a6
    4.     tst.w   (Two_player_mode).w
    5.     beq.s   loc_711E
    6.     lea (Camera_X_pos_P2).w,a3
    7.     lea (Level_Layout).w,a4
    8.     move.w  #$6000,d2
    9.     bsr.s   LoadTilesFromStart_2p
    10.  
    11. loc_711E:
    12.     lea (Camera_BG_X_pos).w,a3
    13.     lea (Level_Layout).w,a4 ; foreground
    14.     move.w  #$4000,d2
    15.     bsr.s   LoadTilesFromStart2
    16.     lea (Camera_BG_X_pos).w,a3
    17.     lea (Level_Layout+$80).w,a4 ; background
    18.     move.w  #$6000,d2               ; This selects a VRAM write and moves to PNT B
    19.  
    20.     cmpi.b  #casino_night_zone,(Current_Zone).w
    21.     beq.w   +
    22.     tst.w   (Two_player_mode).w
    23.     beq.w   LoadTilesFromStart2
    24.     cmpi.b  #mystic_cave_zone,(Current_Zone).w
    25.     beq.w   loc_E396
    26.    
    27. LoadTilesFromStart2:
    28.     moveq   #-$10,d4
    29. +   moveq   #$F,d6
    30.  
    31. -   movem.l d4-d6,-(sp)
    32.     moveq   #0,d5
    33.     move.w  d4,d1
    34.     bsr.w   CalcBlockVRAMPos
    35.     move.w  d1,d4
    36.     moveq   #0,d5
    37.     moveq   #$1F,d6
    38.     move    #$2700,sr
    39.     bsr.w   DrawBlockRow
    40.     move    #$2300,sr
    41.     movem.l (sp)+,d4-d6
    42.     addi.w  #$10,d4
    43.     dbf d6,-
    44.     rts

    As usual, I'd really appreciate any help.
     
  17. vladikcomper

    vladikcomper

    Tech Member
    205
    134
    43
    Sonic Warped
    Yes. Basically, title card share the same Plane A with the foreground. When the card starts moving out, the 16x16 blocks of FG are rendered instead of solid blue blocks of the title card, line by line. This happens fast enough to create a smooth motion and simulate that the blue area is scrolling up, showing us the level, whereas no scrolling is used.

    As for the code, try this:

    Code (ASM):
    1. ; ===========================================================================
    2. LoadTilesFromStart:
    3.     lea VDP_control_port,a5
    4.     lea VDP_data_port,a6      
    5.  
    6.     lea Camera_X_pos,a3     ; foreground camera
    7.     lea Level_Layout,a4     ; foreground layout
    8.     moveq   #$4000,d2       ; set VRAM location to Plane A
    9.     bsr.s   $$DrawPlane
    10.  
    11.     lea Camera_BG_X_pos,a3  ; background camera
    12.     lea Level_Layout+$80,a4 ; background layout
    13.     move.w  #$6000,d2       ; set VRAM location to Plane B
    14.  
    15. $$DrawPlane:
    16.     moveq   #0,d4           ; sets D4 to $00 or -$10 depending on the following conditions...
    17.     cmpi.b  #casino_night_zone,Current_Zone
    18.     beq.w   ++
    19.     tst.w   Two_player_mode
    20.     beq.w   +
    21.     cmpi.b  #mystic_cave_zone,Current_Zone
    22.     beq.w   loc_E396
    23. +   moveq   #-$10,d4
    24. +
    25.     moveq   #$F,d6          ; repeat for $10 horizontal lines of 16x16 blocks
    26. -   movem.l d4-d6,-(sp)
    27.     moveq   #0,d5
    28.     move.w  d4,d1
    29.     bsr.w   CalcBlockVRAMPos    ; calculate horizontal line start offset in VRAM
    30.     move.w  d1,d4
    31.     moveq   #0,d5
    32.     moveq   #$1F,d6         ; draw $20 16x16 blocks on a line
    33.     move    #$2700,sr       ; disable interrupts, as we're going to access VRAM
    34.     bsr.w   DrawBlockRow        ; ...
    35.     move    #$2300,sr       ; re-enable interrupts
    36.     movem.l (sp)+,d4-d6
    37.     addi.w  #$10,d4
    38.     dbf d6,-
    39.  
    40.     rts
    This basically copies Sonic 1 routine plus Sonic 2-specific tweaks. I'm not sure if the code that sets D4 should work for both Planes, or Plane B exclusively -- I've no idea about all the Sonic 2 quirks, as this is not the game I'm experienced in. If you encounter issues in CNZ/MCZ/2P mode, try placing $$DrawPlane directly above moveq #-$10,d4 line.
     
  18. Caverns 4

    Caverns 4

    Member
    346
    0
    16
    Sonic: Retold
    Your code did help, though I didn't end up using it. It did however, show me what I did wrong, so thank you for that. As it turns out, I overlooked this line here:

    Code (Text):
    1.     lea (Camera_BG_X_pos).w,a3
    2.     lea (Level_Layout).w,a4 ; foreground
    > Camera_BG_X_pos

    I derped. Should've been (Camera_X_pos).w

    Also, something I see you did, just for future advice, is you lea referenced a RAM address, like this: lea Level_Layout,a4. From my past experience, Sonic 2 disassemblies (at least, the hg one) don't like referencing memory addresses that way, and it would trow errors at me if I tried to use it. Sorry if I sound naggy or something, just thought I'd let you know. Sonic 1 and 2 are quite a different bag of worms in terms of assembly standards.

    Now, there's a single other issue, dealing with the fade in transition from the title card, which looks like this:

    Code (Text):
    1. Level_DelayLoop:
    2.     move.b  #8,(Vint_routine).w
    3.     bsr.w   WaitForVint
    4.     dbf d1,Level_DelayLoop
    5.    
    6.     move.w  #$202F,(Palette_fade_range).w
    7.     bsr.w   Pal_FadeTo2
    This area of code I completely changed pretty much from Sonic 2; I edited it to be consistent with the Sonic 2 NA prototype.

    If I comment out the the move.w and bsr.w, then the level loads the right palette, but it just kind of... Snaps into existence, and is quite jarring.

    However, if I use the fade-in routine, this happens:
    [​IMG]

    Does anyone know what's wrong? For reference, this is my (barely changed) Pal_FadeTo: routine:

    Code (Text):
    1. Pal_FadeTo:
    2.     move.w  #$3F,(Palette_fade_range).w
    3.    
    4. Pal_FadeTo2:
    5.     moveq   #0,d0
    6.     lea (Normal_palette).w,a0
    7.     move.b  (Palette_fade_start).w,d0
    8.     adda.w  d0,a0
    9.     moveq   #0,d1
    10.     move.b  (Palette_fade_length).w,d0
    11. ; loc_23DE:
    12. Pal_ToBlack:
    13.     move.w  d1,(a0)+
    14.     dbf d0,Pal_ToBlack  ; fill palette with $000 (black)
    15.  
    16.     move.w  #$15,d4
    17. -   move.b  #$12,(Vint_routine).w
    18.     bsr.w   WaitForVint
    19.     bsr.s   Pal_FadeIn
    20.     bsr.w   RunPLC_RAM
    21.     dbf d4,-
    22.  
    23.     rts
    24. ; End of function Pal_FadeTo
     
  19. flamewing

    flamewing

    Emerald Hunter Tech Member
    1,161
    65
    28
    France
    Sonic Classic Heroes; Sonic 2 Special Stage Editor; Sonic 3&K Heroes (on hold)
    It is because Sonic 1 and Sonic 2 use different assemblers. AS ( the assembler used in S2 disasm) has a syntax closer to Motorolla's than asm68k (the assembler of the S1 disasm) does, and it requires absolute addresses to be enclosed in parenthesis and with a size modifier (".w" for absolute short or ".l" for absolute long) appended:
    Code (Text):
    1.     lea    (XXX).w,aN    ; absolute short
    2.     lea    (XXX).l,aN    ; absolute long

    (the reason for the strikethroughs in in vladikcompiler's post and my own after that)
     
  20. RetroKoH

    RetroKoH

    Member
    1,662
    22
    18
    Project Sonic 8x16
    May I ask, what is the difference between using one or the other? and how does it cause such errors? I wonder if something like this is giving me the headaches I have in my hack.