don't click here

Some changes/fixes for Sonic 1

Discussion in 'Engineering & Reverse Engineering' started by RetroKoH, Sep 4, 2012.

  1. MarkeyJester

    MarkeyJester

    Original, No substitute Resident Jester
    2,202
    432
    63
    Japan
    In fairness, the correct fix to the original problem is to ensure the SEGA PCM sample does not cross an $8000 boundary.

    As long as you align the SEGA PCM such that it doesn't cross, and change the bankswitch instructions (which if my memory doesn't fail me is right at the beginning during init of the Z80 driver in general), then you don't have to use the 68k method.

    Not that it matters much either way, we're talking minute benefits of both, so your solution is still more than welcome, just putting it out there while we're on topic ;)
     
  2. Kilo

    Kilo

    That inbetween sprite from S&K's title screen Member
    309
    309
    63
    Canada
    Might as well go with the 68k solution rather than being concerned with boundaries (They're already a pain with DMA boundaries) and it's not like much happens on the Sega screen in the first place to worry about the chant's CPU usage. But you're definitely right it is worth pointing out that as an option.
     
  3. Brainulator

    Brainulator

    Regular garden-variety member Member
    For Mega Games 10 at least, Sonic 1 was slightly modified to add an instruction that corrects the bank number immediately after decompressing the Z80 driver. This isn't the cleanest way, but if you can't be bothered to recompress the code, it might work. Bear in mind you'd also have to make sure the location within the bank is correct, too.
     
    • Like Like x 1
    • Informative Informative x 1
    • List
  4. MarkeyJester

    MarkeyJester

    Original, No substitute Resident Jester
    2,202
    432
    63
    Japan
    Well that's what this is for d=
    Ideally you'd need something similar for the bank switch instructions, specifically these:
    Code (Text):
    1.        ld   a,(SegaPCM>>00Fh)&001h       ; set bit 15 (+8000 address)
    2.        ld   (06000h),a               ; ''
    3.        ld   b,008h                   ; set remaining number of bits to write
    4.        ld   a,(SegaPCM>>010h)&03Fh       ; prepare 68k "SEGA" upper byte address
    But then, you'll have to hope the two bytes of the address are not part of an offset/length sequence in the kosinski compressed data (i.e. hope they're not a copy of a previous necessary byte, or to be copied to another necessary byte). The location didn't, by dumb luck on 2005 Hivebrain's part. It would be a dirty fix, but a fix none the less and would preserve the ROM 1:1, if of course; you didn't want a disassembly of the Z80 and to have to reassemble/recompress at build time.

    None of this is an issue for us now. We've got multiple methods, the 68k one Kilo is talking about (with their cleanup) and I suspect the newer GIT probably has the Z80 portion reassembled/recompressed correctly, possibly with Clownacy's research on accurate SEGA compressions. So this is mearly a thought experiment for a solution we're not desperate for.
     
  5. Kilo

    Kilo

    That inbetween sprite from S&K's title screen Member
    309
    309
    63
    Canada
    Saving a Couple Tiles With Monitors
    Monitors are pretty unoptimized in Sonic 1. They use 3 frames of noise for the static frames, which in my opinion, is excessive. And duplicate Sonic's life icon, when it's already loaded in VRAM! Let's reduce this and save some tiles.
    Target disasm: Hivebrain 2005
    Advantages: Saves 12 tiles in VRAM, which is enough for 3 more power ups (Most likely you'd want that for the elemental shields), saves a small bit of ROM space since we're reducing tile, mapping, and animation data. 1-up monitors will now use the HUD's life icon allowing it to dynamically change if you add extra characters without any extra work.
    Disadvantages: Static frames are less unique since it's reduced to 1 frame.

    First, download the Optimized Monitor Assets zip provided and merge it with the root of your disassembly.
    Then open sonic1.asm and head to Obj26:.
    Change
    Code (Text):
    1.         move.b    #$B,$1A(a0)    ; use broken monitor frame
    To
    Code (Text):
    1.         move.b    #9,$1A(a0)    ; use broken monitor frame
    Next at Obj2E_Main: remove
    Code (Text):
    1.         addq.b    #2,d0
    Included as well is a zip with an updated Monitor.xml for SonLVL, just replace the one in the Common folder within your project folder with this.

    And it's as simple as that! Enjoy your free 12 tiles!
    upload_2024-4-3_6-43-43.png
     

    Attached Files:

  6. Brainulator

    Brainulator

    Regular garden-variety member Member
    Funnily enough, the two Sonic icons are slightly different around the ear area.

    Here's the frames, for reference (colors may not be accurate):
    upload_2024-4-3_18-8-36.png
    upload_2024-4-3_18-8-51.png
    Sonic 1 suu.gif

    Which one is the "real" or "correct" one, I'm not certain.
     
    • Informative Informative x 3
    • Agree Agree x 2
    • Like Like x 1
    • List
  7. nineko

    nineko

    I am the Holy Cat Tech Member
    6,308
    486
    63
    italy
    I never noticed that :O

    (in fact, even I made them share art in my hack back in the day)
     
  8. Kilo

    Kilo

    That inbetween sprite from S&K's title screen Member
    309
    309
    63
    Canada
    I personally believe the life icon from the HUD to be the intended one, as the developers would've been looking at it more often than the monitors making them more likely to feel the need to update it without considering the fact that the monitor needed to be changed too, especially if it's only a 2 pixel difference.
     
  9. Brainulator

    Brainulator

    Regular garden-variety member Member
    I had that feeling, too, if only because the HUD's art is slightly more advanced in its shading.
     
  10. Kilo

    Kilo

    That inbetween sprite from S&K's title screen Member
    309
    309
    63
    Canada
    Nineko brought up a great point just now about the centiseconds timer in Sonic CD only being calculated properly at 60 FPS. Which made me realize, the timer as a whole only works at 60 FPS, it's a pretty easy fix so let's do it!
    Under Hud_ChkTime, just after the branch to TimeOver, replace this line
    Code (Text):
    1.         cmpi.b    #60,(a1)
    With this
    Code (Text):
    1.         moveq   #0,d0
    2.         moveq   #0,d1
    3.         move.b  (a1),d1
    4.         move.b  #60,d0              ; Set amount of frames needed to tick over a second.
    5.         btst    #6,($FFFFFFF8).w    ; Is the console PAL?
    6.         beq.s   @Cont               ; If not continue.
    7.         move.b  #50,d0              ; Otherwise, use 50 frames.
    8.  
    9. @Cont:
    10.         cmp.b   d0,d1
    11.  
    If you have the centiseconds counter from Sonic CD too you'll want to update this too. Nineko also suggested using a LUT instead of using the slow mulu and divu instructions.
    After TimeOver, place the table.
    Code (Text):
    1. HUD_CsTimesNTSC:
    2.         dc.b    0, 1, 3, 5, 6, 8, 10, 11, 13, 15, 16
    3.         dc.b    18, 20, 21, 23, 25, 26, 28, 30, 31, 33
    4.         dc.b    35, 36, 38, 40, 41, 43, 45, 46, 48, 50
    5.         dc.b    51, 53, 55, 56, 58, 60, 61, 63, 65, 66
    6.         dc.b    68, 70, 71, 73, 75, 76, 78, 80, 81, 83
    7.         dc.b    85, 86, 88, 90, 91, 93, 95, 96, 98, 0
    8.         even
    9.  
    10. HUD_CsTimesPAL:
    11.         dc.b    0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20
    12.         dc.b    22, 24, 26, 28, 30, 32, 34, 36, 38, 40
    13.         dc.b    42, 44, 46, 48, 50, 52, 54, 56, 58, 60
    14.         dc.b    62, 64, 66, 68, 70, 72, 74, 76, 78, 80
    15.         dc.b    82, 84, 86, 88, 90, 92, 94, 96, 98, 0
    16.         even
    Then replace your existing centiseconds code, which looks like this
    Code (Text):
    1.         moveq    #0,d1
    2.         move.b    ($FFFFFE25).w,d1 ; Load frames
    3.         mulu.w    #100,d1            ; Convert to centiseconds
    4.         divu.w    #60,d1
    5.         swap    d1
    6.         move.w    #0,d1
    7.         swap    d1
    8.         cmpi.l    #$93B3B,($FFFFFE22).w    ; Are we at the max time?
    9.         bne.s    @NotMax        ; If not, branch
    10.         move.w    #99,d1                ; If so, set centiseconds to 99
    11.  
    12. @NotMax:
    13.         bra.w    Hud_Secs
    With our new code that uses the LUT
    Code (Text):
    1.         moveq    #0,d1
    2.         move.b    ($FFFFFE25).w,d1 ; Load frames
    3.         btst    #6,($FFFFFFF8).w    ; Is the console PAL?
    4.         beq.s    @NTSC                ; If not load NTSC values
    5.         move.b    HUD_CsTimesPAL(pc,d1.w),d1
    6.         bra.s    @Cont2
    7.  
    8. @NTSC:
    9.         move.b    HUD_CsTimesNTSC(pc,d1.w),d1
    10.  
    11. @Cont2:
    12.         cmpi.l    #$93B3B,($FFFFFE22).w    ; Are we at the max time?
    13.         bne.s    @NotMax        ; If not, branch
    14.         move.w    #99,d1                ; If so, set centiseconds to 99
    15.  
    16. @NotMax:
    17.         bra.w    Hud_Secs
     
    Last edited: Apr 10, 2024
  11. Devon

    Devon

    I'm a loser, baby, so why don't you kill me? Tech Member
    1,248
    1,419
    93
    your mom
    You should also update the check that checks if the timer is at the maximum value. Otherwise time overs won't actually happen in PAL mode, since it'll just perpetually wait until the frame counter reaches 59, which will never happen.

    EDIT: Also, if you want a cleaner way of picking a LUT, try starting off with a default pointer in an address register, check the condition, and then swap out the pointer for when the condition is true (if there's a register clash with a0 here, you can just pick another register to use instead).

    Code (ASM):
    1.         lea     HUD_CsTimesNTSC(pc),a0          ; Use the NTSC table by default
    2.         btst    #6,($FFFFFFF8).w                ; Is this actually a PAL console?
    3.         beq.s   @GetCentisecond                 ; If not, branch
    4.         lea     HUD_CsTimesPAL(pc),a0           ; If so, use the PAL table
    5.  
    6. @GetCentisecond:
    7.         move.b  (a0,d1.w),d1                    ; Get centisecond value

    Also, for deciding between checking 9:59:49 (for PAL) or 9:59:59 (for NTSC) when checking for the maximum timer value, you can do something like this:

    Code (ASM):
    1.         move.l  #(9<<16)|(59<<8)|(59),d0        ; Check for 9:59:59 by default
    2.         btst    #6,($FFFFFFF8).w                ; Is this actually a PAL console?
    3.         beq.s   @CheckMaxTime                   ; If not, branch
    4.         move.b  #49,d0                          ; If so, check the PAL frame counter value
    5.  
    6. @CheckMaxTime:
    7.         cmp.l   ($FFFFFE22).w,d0                ; Are we at the max time?

    That should be implemented for the time over check, and the centisecond calculation.
     
    Last edited: Apr 10, 2024
  12. Kilo

    Kilo

    That inbetween sprite from S&K's title screen Member
    309
    309
    63
    Canada
    There are 2 checks for a time over, so I'd actually put the time over value in a register less likely to be used by the drawing subroutines that will happen in between the first and second check, something like d5, and that should make things a little quicker too.
    So at Hud_ChkTime you'd replace
    Code (Text):
    1.         lea    ($FFFFFE22).w,a1
    2.         cmpi.l    #$93B3B,(a1)+    ; is the time 9.59?
    3.         beq.s    TimeOver    ; if yes, branch
    with
    Code (Text):
    1.         lea    ($FFFFFE22).w,a1        ; Load the current time.
    2.         move.l    #$93B3B,d5            ; Set the max time to 9:59:59
    3.         move.l    (a1)+,d1
    4.         btst    #6,($FFFFFFF8).w    ; Is the console PAL?
    5.         beq.s    @Check959            ; If not continue.
    6.         move.b    #$31,d5                ; Otherwise, set it to 9:59:49.
    7.  
    8. @Check959:
    9.         cmp.l    d5,d1                ; Does the current time match our max time?
    10.         beq.w    TimeOver            ; If so, then we've time overed.
    Then when it's the centiseconds turn at checking for a timeover you can just use this
    Code (Text):
    1.         cmp.l    ($FFFFFE22).w,d5    ; Have we time over'd?
    2.         bne.s    @NotMax    ; If not, continue.
    3.         move.w    #99,d1    ; Max out digits.
     
  13. Devon

    Devon

    I'm a loser, baby, so why don't you kill me? Tech Member
    1,248
    1,419
    93
    your mom
    Yeah I was thinking about something like that, but I just wanted to throw some example code in to get my point across. Regardless, nice.
     
  14. Kilo

    Kilo

    That inbetween sprite from S&K's title screen Member
    309
    309
    63
    Canada
    Uncle Kilo's back again to fix another oversight.
    Anyone who's played Sonic 1 for a long time knows about this secret platform in LZ1 to skip most of the level:
    upload_2024-4-16_0-58-33.png
    Problem: It's supposed to give the illusion of being a raft that's floating on the water's surface. However, it doesn't actually sync it's Y position to the water. That's lazy! Let's fix it.

    At Obj52_Type07 insert this line after the label
    Code (Text):
    1.         move.b    #1,$3F(a0)        ; Use $3F as an "align to water surface" flag.
    Then at both Obj52_Type02 and Obj52_Type05 insert this after the label.
    Code (Text):
    1.         tst.b    $3F(a0)                    ; Is the align to water surface flag set?
    2.         beq.s    @Cont                    ; If not, continue.
    3.         move.w    ($FFFFF646).w,$C(a0)    ; Otherwise, align to the water's surface!
    4.  
    5. @Cont:
    And ya done.
    ezgif-6-d8a727ac50.gif

    I should note that if you use object $52 with a type of 7 outside of Labyrinth it can cause issues. But I'd imagine if you're using the guide, then you're not making crazy modifications to the layouts that specifically needs this object type.
     
    Last edited: Apr 16, 2024
  15. nineko

    nineko

    I am the Holy Cat Tech Member
    6,308
    486
    63
    italy
    That's such a small detail, and yet, it does look much nicer with the wobble, great work!
     
    • Like Like x 2
    • Agree Agree x 1
    • List
  16. penBorefield

    penBorefield

    Living in my best life Member
    120
    19
    18
    Basement
    Patching things up
    Isn't that "fix" also included in Sonic Delta hack or something?