I have been learning m68k assembly recently with some help from the nice guides in the wiki. I'm starting with Sonic 1, generally implementing the bug fixes and learning as I go. I thought of a relatively simple feature I'd like to add, the ability to restart a level by pressing a button when paused, like in CD. I managed to track down the specific code that handles the button inputs in the pause loop and moved instructions around so that the A button can be pressed even with debug mode off. Having done that I started to dig deep into the disassembly looking for how the game restarts a level when you die. After a while of trying out different labels like 'KillSonic', 'Sonic_Death' and other similar-sounding instructions I realized it wasn't going to be as easy as "calling a function". So TL;DR: where and how does Sonic 1 restart levels? How can I replicate it in my own label? (by 'label' I mean where you begin 'functions', like "Pause_Reset:", with the colon at the end, but no '@' sign to the left) Here's the code I have right now, please excuse the mess, I like to comment out things before I'm certain they're working: Spoiler Code (Text): Pause_Loop: move.b #$10,(v_vbla_routine).w bsr.w WaitForVBla ; now the A button will reset even outside of debug mode btst #bitA,(v_jpadpress1).w ; is button A pressed? bne.s Pause_Reset tst.b (f_slomocheat).w ; is slow-motion cheat on? beq.s Pause_ChkStart ; if not, branch ;btst #bitA,(v_jpadpress1).w ; is button A pressed? beq.s Pause_ChkBC ; if not, branch ;move.b #id_Title,(v_gamemode).w ; set game mode to 4 (title screen) ;nop ;bra.s Pause_EndMusic ; =========================================================================== ; TODO: make reset just reload the level Pause_Reset: move.b #id_Title,(v_gamemode).w ; set game mode to 4 (title screen) nop bra.s Pause_EndMusic As you can see all I have done for now was separate the game restart functionality, so I could work on it more clearly. Also I'm new to these forums, so please tell me if I did anything wrong.
Actually, you were very close. Calling "KillSonic" would have done the trick. The thing is, the subroutine "KillSonic" expects Sonic's object RAM address to be inside address register "a0". So simply do the following: Code (Text): lea ($FFFFD000).w,a0 jsr KillSonic This will trigger Sonic's routines to perform the series of death based functions. But of course, the game is still paused, so you'll have force the game to unpause and return out of the pause loop: Code (Text): move.b #$80,($FFFFF003).w move.w #0,($FFFFF63A).w rts
Okay, I'll try that, thanks a lot. By the way, I love your hack "Next Level", Sonic's rotating sprite and more animation frames are amazing.
Great, now Sonic has a self-destruct button :D This is the final code I ended up with: Spoiler Code (Text): Pause_Reset: lea (v_player).w,a0 ; 'KillSonic' expects Sonic's object RAM address in a0 jsr KillSonic move.w #0,(f_pause).w ; unpause nop bra.s Pause_EndMusic I've copied part of the original game-reset functionality to fix the music (it wouldn't play again after killing sonic). Without 'nop' the emulator (Kega) crashed upon loading the rom for some reason, but I didn't test twice before adding it, seems to be harmless though. Now what I'm gonna do is see if I can manually replicate only parts of the kill function, so that it doesn't look as if it's actually killing Sonic, just restarting the level. Also I'm going to make it so restarting doesn't take away a life, because I believe that's unfair, but to keep the game balanced I'll have to see some way to keep track of the last score when the level started or when Sonic really died, that way I can reset to said score and prevent players from restarting over and over to accumulate more points.
Bumping this extremely old thread, but I've got a way to actually make the level reset without outright killing Sonic (Because imo that's a bit of a slap in the face) It's pretty simple, just make the Pause_Reset function look like this: Code (Text): Pause_Reset cmp.b #1,($FFFFFE12).w ; Check if you only have 1 life beq.s Reset_Return ; If so branch (This way you don't get 0 lives and then underflow) addq.b #1,($FFFFFE1C).w ; update lives counter subq.b #1,($FFFFFE12).w ; subtract 1 from number of lives move.b #0,($FFFFFE30).w ; clear lamppost counter move.b #1,($FFFFFE02).w ; Set the level reset flag move.b #$80,($FFFFF003).w ; Unpause music move.w #0,($FFFFF63A).w Reset_Return rts ; Return And this might have something to do with the original tutorial, but once you hit 1 life (AKA you can't reset) it will go into slow-motion mode. Not a massive issue, but it should be noted. Also if you're in debug, then it seems to force the slow-motion cheat and you can't get out of it?
Following in the footsteps of Iso Kilo here's my version, it doesn't have the slow mo issue. In Pause_Loop: change beq.s Pause_ChkStart to beq.s Pause_Check_Reset Code (Text): Pause_Loop: move.b #$10,(v_vbla_routine).w bsr.w WaitForVBla tst.b (f_slomocheat).w ; is slow-motion cheat on? beq.s Pause_Check_Reset ; if not, branch Then add these below the Pause_Loop: Subroutine Code (Text): Pause_Check_Reset: cmp.b #1,(v_lives).w ; Check if you only have 1 life beq.s Pause_ChkStart ; If so branch (This way you don't get 0 lives and then underflow) btst #bitA,(v_jpadpress1).w ; is button A pressed? bne.s Pause_Reset ; if so, branch btst #bitB,(v_jpadpress1).w ; is button B pressed? bne.s Pause_Reset ; if so, branch btst #bitC,(v_jpadpress1).w ; is button C pressed? bne.s Pause_Reset ; if so, branch bra.s Pause_ChkStart ; Check Start Buttom Pause_Reset: lea (v_objspace).w,a0 jsr KillSonic ; Kill Sonic bra.s Pause_EndMusic ; Unpause