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
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.
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.
Well that's what this is for d= Ideally you'd need something similar for the bank switch instructions, specifically these: Code (Text): ld a,(SegaPCM>>00Fh)&001h ; set bit 15 (+8000 address) ld (06000h),a ; '' ld b,008h ; set remaining number of bits to write 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.
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): move.b #$B,$1A(a0) ; use broken monitor frame To Code (Text): move.b #9,$1A(a0) ; use broken monitor frame Next at Obj2E_Main: remove Code (Text): 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!
Funnily enough, the two Sonic icons are slightly different around the ear area. Here's the frames, for reference (colors may not be accurate): Spoiler: Monitor Spoiler: HUD Spoiler: Comparison GIF Which one is the "real" or "correct" one, I'm not certain.
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.
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): cmpi.b #60,(a1) With this Code (Text): moveq #0,d0 moveq #0,d1 move.b (a1),d1 move.b #60,d0 ; Set amount of frames needed to tick over a second. btst #6,($FFFFFFF8).w ; Is the console PAL? beq.s @Cont ; If not continue. move.b #50,d0 ; Otherwise, use 50 frames. @Cont: cmp.b d0,d1 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): HUD_CsTimesNTSC: dc.b 0, 1, 3, 5, 6, 8, 10, 11, 13, 15, 16 dc.b 18, 20, 21, 23, 25, 26, 28, 30, 31, 33 dc.b 35, 36, 38, 40, 41, 43, 45, 46, 48, 50 dc.b 51, 53, 55, 56, 58, 60, 61, 63, 65, 66 dc.b 68, 70, 71, 73, 75, 76, 78, 80, 81, 83 dc.b 85, 86, 88, 90, 91, 93, 95, 96, 98, 0 even HUD_CsTimesPAL: dc.b 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20 dc.b 22, 24, 26, 28, 30, 32, 34, 36, 38, 40 dc.b 42, 44, 46, 48, 50, 52, 54, 56, 58, 60 dc.b 62, 64, 66, 68, 70, 72, 74, 76, 78, 80 dc.b 82, 84, 86, 88, 90, 92, 94, 96, 98, 0 even Then replace your existing centiseconds code, which looks like this Code (Text): moveq #0,d1 move.b ($FFFFFE25).w,d1 ; Load frames mulu.w #100,d1 ; Convert to centiseconds divu.w #60,d1 swap d1 move.w #0,d1 swap d1 cmpi.l #$93B3B,($FFFFFE22).w ; Are we at the max time? bne.s @NotMax ; If not, branch move.w #99,d1 ; If so, set centiseconds to 99 @NotMax: bra.w Hud_Secs With our new code that uses the LUT Code (Text): moveq #0,d1 move.b ($FFFFFE25).w,d1 ; Load frames btst #6,($FFFFFFF8).w ; Is the console PAL? beq.s @NTSC ; If not load NTSC values move.b HUD_CsTimesPAL(pc,d1.w),d1 bra.s @Cont2 @NTSC: move.b HUD_CsTimesNTSC(pc,d1.w),d1 @Cont2: cmpi.l #$93B3B,($FFFFFE22).w ; Are we at the max time? bne.s @NotMax ; If not, branch move.w #99,d1 ; If so, set centiseconds to 99 @NotMax: bra.w Hud_Secs
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): lea HUD_CsTimesNTSC(pc),a0 ; Use the NTSC table by default btst #6,($FFFFFFF8).w ; Is this actually a PAL console? beq.s @GetCentisecond ; If not, branch lea HUD_CsTimesPAL(pc),a0 ; If so, use the PAL table @GetCentisecond: 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): move.l #(9<<16)|(59<<8)|(59),d0 ; Check for 9:59:59 by default btst #6,($FFFFFFF8).w ; Is this actually a PAL console? beq.s @CheckMaxTime ; If not, branch move.b #49,d0 ; If so, check the PAL frame counter value @CheckMaxTime: cmp.l ($FFFFFE22).w,d0 ; Are we at the max time? That should be implemented for the time over check, and the centisecond calculation.
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): lea ($FFFFFE22).w,a1 cmpi.l #$93B3B,(a1)+ ; is the time 9.59? beq.s TimeOver ; if yes, branch with Code (Text): lea ($FFFFFE22).w,a1 ; Load the current time. move.l #$93B3B,d5 ; Set the max time to 9:59:59 move.l (a1)+,d1 btst #6,($FFFFFFF8).w ; Is the console PAL? beq.s @Check959 ; If not continue. move.b #$31,d5 ; Otherwise, set it to 9:59:49. @Check959: cmp.l d5,d1 ; Does the current time match our max time? 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): cmp.l ($FFFFFE22).w,d5 ; Have we time over'd? bne.s @NotMax ; If not, continue. move.w #99,d1 ; Max out digits.
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.
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: 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): 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): tst.b $3F(a0) ; Is the align to water surface flag set? beq.s @Cont ; If not, continue. move.w ($FFFFF646).w,$C(a0) ; Otherwise, align to the water's surface! @Cont: And ya done. 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.
Properly properly removing the roll-jump lock After seeing like 3 attempts including one of my own, I think I've got the definitive version to removing the rolling-jump lock. First let's look at exactly what the jump code is doing, which can be found at what's commonly called Sonic_Jump. Code (Text): move.b #38/2,obj_yhitbox(a0) ; Set Sonic's hitbox to standing. move.b #18/2,obj_xhitbox(a0) btst #STATUS_INBALL,obj_status(a0) ; Is Sonic already in a ball? bne.s @RollJump ; If so set the roll jump lock. Now this is an issue, Sonic's hitbox being set to his standing height was a remnant of the prototype's victory function. However, even there doing a rolling jump would result in Sonic using his standing hitbox, here's the prototype's logic: Code (Text): move.b #38/2,obj_yhitbox(a0) ; Set Sonic's hitbox to standing. move.b #18/2,obj_xhitbox(a0) tst.b (player_victory).w ; Should we use the victory animation? bne.s @Victory ; If so, go off to play the victory animation. btst #STATUS_INBALL,obj_status(a0) ; Is Sonic already in a ball? bne.s @RollJump ; If so set the roll jump lock. Then after this, it just sets Sonic to be in his ball state. And importantly lower him to the ground so he doesn't jump from his origin (Somehow Sonic Mania failed this, as well as many fangames). Code (Text): move.b #28/2,obj_yhitbox(a0) ; Set Sonic's hitbox to ball. move.b #14/2,obj_xhitbox(a0) move.b #ANIID_ROLL,obj_anim(a0) ; Set animation to rolling. bset #STATUS_INBALL,obj_status(a0) ; Mark Sonic as in a ball. addq.w #5,obj_ypos(a0) ; Lower Sonic so he jumps from the floor rather than his origin. @Return: rts What I believe the intention was, was to simply just skip this code if you were rolling because you'd already have the ball hitbox, animation, and you'd already be aligned with the floor so no need to waste cycles on that. But due to the victory animation and it's improper removal it all got mucked up, so not only does your controls get locked, but you also end up jumping from Sonic's center mid air because now he's using his standing hitbox. So the way to properly fix the roll-jump lock and it's hitbox is to replace the lower part of Sonic_Jump (Just after we play the sound) with this: Code (Text): btst #STATUS_INBALL,obj_status(a0) ; Is Sonic already in a ball? bne.s @Return ; If so, we're rolling and we don't have to do any of the below. move.b #28/2,obj_yhitbox(a0) ; Set Sonic's hitbox to ball. move.b #14/2,obj_xhitbox(a0) move.b #ANIID_ROLL,obj_anim(a0) ; Set animation to rolling. bset #STATUS_INBALL,obj_status(a0) ; Mark Sonic as in a ball. addq.w #5,obj_ypos(a0) ; Lower Sonic so he jumps from the floor rather than his origin. @Return: rts That's it. No more roll-jump lock, no more standing hitbox. Then just get rid of anything involving #STATUS_ROLLJUMP,obj_status(a0), or #4,$22(a0) or whatever your disassembly of choice refers to it as, and you also free up a status bit for whatever purpose you may need. It looks much cleaner, doesn't leave any dead code or unnecessary checks. An alternative fix, if you want to keep the jump lock but fix Sonic's hitbox is to either delete the first 2 instructions that set Sonic's hitbox. Or if you have the victory animation set up then move them into the function that actually plays the animation rather than in the main jump routine.
Mind you, you still would need to remove the air speed cap to prevent pressing left or right from destroying your momentum when jumping after rolling at a fast speed, as shown in these GIFs: I am pretty sure the lock the introduced as a hack to prevent this from happening, so when removing it, you'll wanna make sure to properly fix this as well.
I still think the air speed cap stuff is a far more glaring and obvious thing that honestly speaks for itself. It just makes more sense to me compared to a bunch of theories from an ancient thread, which might I add, the people who made those posts were definitely not really aware of the air speed cap thing that the lock was preventing from happening.