I have always wanted to do serious custom zones in sonic 2, but something that stopped me was custom music. I could never get the S2 Clone Driver to work. So I try out the new tutorial and it turns out it isn't compatible with the 2007 disassembly version, the one I am used to. So I once again download the latest S2 disassembly, and got reminded why I disliked it so much: Pluses instead of the generic location labels. Can somebody please tell me how ALL this works? I assume the more pluses in a redirect command the more downwards you go. If you want to go up, do you use a minus (-)? Why was it changed to this?
A + or a - in label position defines a "nameless temporary label", as the AS User Manual calls them. You can reference them by using up to 3 + or - signs. One + means the next + label, two means the second next + label; one - means the previous - label, etc. Also, you can use / to define a label that can be accessed in both directions. I remember changing many labels to these many years ago, as I thought I couldn't give meaningful names to those labels (or I was just lazy), and I admit I went a bit crazy with it. Also, I now realize it's dangerous when you want to add code between a nameless temporary label and its references and you use nameless temporary labels, because suddenly some references point elsewhere without warning (unless you happen to encounter an out-of-range error).
The "+"'s are as FraGag said, essentially a local label. using a branch to '+' just means "branch to the next plus", branching to '++' means "branch to the plus after next", and finally, '+++' means jump ahead three pluses. One or two pluses can be more useful than a couple of extra named labels since it can be hard to create unique names for so many needed labels, but I'll admit that +++'s are bit excessive. As you asked, a '+' means going down, and a '-' means going up, and the same logic with a '-' applies as with a plus. Also, as demonstrated with SingleObjLoad, if you want a location to be a '+' and a '-' simultaneously, you can use '/' as a bi label. Hope that clears things up.
To elaborate more on labels in AS and their usage patterns in disasemblies: you have 4 kinds of labels: normal labels: global labels that can be referenced from anywhere in the program; they must be globally unique. An awful lot of times, they end up with generic and useless names such as label_91FA. nameless temporaries: the plusses, minuses and slashes. They are mostly used in the S2 disassembly; at times, they are more readable than generic labels, but their overuse can made pieces of code less readable. They can also break if you add or remove code and are not careful; and the only warning you could potentlally get in this case is an out-of-range branch. They only work well with normal labels and other nameless temporaries, in and across macros. named temporaries: labels starting with a $ exist only until they cross another type of label. They are most used in the S&K disassembly, and is the closest in idea (but not in workings) to ASM68K's labels starting with an @. In practice, they don't work well for referencing backwards (despite AS's manual using these as examples), and they don't work well in or across macros. They work only with normal labels and other named temporaries... sometimes. A good temporary label name can enhance readability in more cases than the nameless temporaries, but since named temporaries are so buggy, they aren't as useful as they could. composed "temporaries": labels starting with a period. The nearest non-dotted label above is prepended to all those labels, meaning they work like ASM68K's @ labels; but unlike those, they can be accessed from anywhere in the program (by using the "full" name). When used consistently, they can be used in or across macros without problems; they work best when mixed with normal labels or other composed temporaries. They are most used in... my generic error debugger screen, and nowhere else publicly available. A well selected composed "temporary" label can enhance readability in more cases than nameless temporaries, and composed "temporaries" work a lot better (currently) than named temporaries. I, for one, would like to see composed "temporaries" used more often and the others less often; this would be a huge undertaking, to be sure, but I can see only benefits here. Especially if their few bugs are fixed, and FraGag's assembler (whenever it is released) has a bug-free implementation of them (or an equivalent).
My port of the Sonic 1 disassembly to AS uses composed labels in place of all previously local labels, because of the buggy nature of AS' temporary labels.
That is good to know, yes; I haven't had time yet to look over that branch, but it has gained some points (in my estimation) sight-unseen because of that
Random question that doesn't warrant its own topic, has there been any hacking/disassemblies done for some of the lesser-liked Game Gear games, like Labyrinth? Doubt I'll do anything with the information, I'm just curious.
While looking through 68K SMPS, I keep seeing this: Code (ASM): addq.w #4,sp ; Tamper with return value to not return to caller rts I'm still unsure of certain areas of the Program Counter and this here is one of them. What does forcing this do? If the rts pops a longword from the Stack to the PC, but the Stack's been offset to read the longword before the usual one, how does this never result in the garbage data being written to the PC? My only guess is that this expects for there to be two consecutive 'return addresses' in the Stack, so... Code (ASM): jsr (Branch1).l nop nop Branch1: jsr (Branch2).l nop nop Branch2: addq.w #4,sp rts ...would result in the rts setting the PC, not to the address after jsr (Branch2).l, but the one after jsr (Branch1).l, due to the skipping of the second return address. Looking at an instance under S1's @locdblret (loc_721B6), the Stack-skipping would cause the jsr to Sound_ChkValue to not be returned to, but the jsr to UpdateMusic (sub_71B4C). Making it so that if @locdblret is ever reached, the driver will stop processing until the next V-Int. Is that the function, and if so, is there any practical use to this besides being a way to temporarily change a jsr (second of two) into a jmp?
The practical use comes from what's outside the call instructions, in other words, you need to look at it from a further distance, I.e. "why" did they skip a return address? "why" did they want to return to the second from previous call? It's that answer that can explain the purpose. The best example I can show you, is by using the spindash, I say this because you'll likely be familiar with it. Code (Text): Obj01_MdNormal: bsr.w Sonic_CheckSpindash bsr.w Sonic_Jump bsr.w Sonic_SlopeResist bsr.w Sonic_Move bsr.w Sonic_Roll bsr.w Sonic_LevelBound jsr ObjectMove bsr.w AnglePos bsr.w Sonic_SlopeRepel For the record, ObjectMove takes the speed values, and adds them to the positions correctly. The spindash subroutine actively puts news speeds into Sonic/Tails as it is being charged, the problem with doing that though is the subroutine "ObjectMove" will take the speed and start moving Sonic, before the player has had the chance to fully charge, or even release the spindash. The jump subroutine will read that buttons A, B or C are being pressed and will activate the jumping mechanism. The rolling subroutine will detect down is being held and that there is speed, resulting in a moving standard roll, the other routines may also cause problems when the spindash is attempting to be charged. So you don't want these routines ran, unless the spindash is not charging. Within the spindash routine, you'll find: Code (Text): addq.l #4,sp ...in the two places where the charge is functional, this will ensure that once the spindash subroutine has done what it needs to do to charge, when it returns, all of the subroutines afterwards never get the chance to run, and never get the chance to interrupt the spindash in any way. Instead, the subroutine will return to the previous call, which was here: Code (Text): move.b status(a0),d0 andi.w #6,d0 ; %0000 %0110 move.w Obj01_Modes(pc,d0.w),d1 jsr Obj01_Modes(pc,d1.w) ; run Sonic's movement control code + cmpi.w #-$100,(Camera_Min_Y_pos).w ; is vertical wrapping enabled? bne.s + ; if not, branch For the sound driver of course, one purpose you'll find may sit here: Code (Text): loc_71CE0: jsr sub_71D9E(pc) jsr sub_71DC6(pc) bra.w loc_71E24 The last routine there "loc_71E24" sends the FM frequency to the YM2612, however, should a situation occur where the frequency doesn't need updating, any one of those two subroutine above it, can advance the stack to remove the return address, to prevent the update of the FM frequency (For example, if the frequency hasn't changed since the last time it was sent). Other reasons may include data you push into the stack to store away, but have no use to reload again (for example, the subroutine comes to a situation, where you don't need that data you put into the stack after all), regardless of whether or not you need that data again, you still need the stack position to be where it was at before you pushed data into it: Code (Text): move.w #$0123,-(sp) pea ($0020).w,-(sp) ...some code here... ...turns out in this situation, the data in the stack was not needed. addi.w #$000C,sp rts
Thanks, that makes sense. I've been trying to expand my version of Vladikcomper's Optimising Z80 Stops guide in my Clone Driver's topic. He says that the stops are redundant and that's why they're removed. Inside V_ (and H_)Int in Sonic 2, the Z80 is stopped for DMA transfers, the relay of information to S2's Z80 Sound Driver, and Joypad Input-related code. Now, ignoring DMA transfers, the Z80 has to be stopped so that the 68K can interact with addresses that are inside the Z80's memory space ($A00000-$A0FFFF). I can understand this with things such as the YM2612's registers, which are around $A04000, and S2's driver input, which is around $A01B80, but JoypadInit and ReadJoypads have the Z80 stopped before being branched to, even though the addresses they seemingly have the Z80 stopped for, HW_Port_1_Data, HW_Port_2_Data, HW_Port_1_Control, HW_Port_2_Control, and HW_Expansion_Control, they're all $A10003+, beyond they address space that the Z80 restricts access to! I understand that there are some stops that are correct and required, I've left them be. But Vlad's guide only went through S1's V_Blank, which contains branches to, and Z80 stops for, ReadJoypads, so he indirectly removed the stops for them also, though he never mentioned them, only talking about DMA transfers, so they may have been unintentional. In S1, all branches to ReadJoypads are inside the same blocks of code between Z80stops and starts as DMA transfers, while in S2, there's one instance where the Z80 is stopped and started only for a branch to ReadJoypads, this is under VintSub14. This is my current understanding of the situation, so... are all of these stops really that redundant and erroneous? Or did I get the Z80's memory space wrong? I can't test on hardware, and though Regen claims that there's no problem in removing those stops, I doubt its accuracy, so I can't test this myself.
That would be correct, when accessing the I/O section of the hardware, you do not need to halt the Z80. These Z80 halts when accessing the I/O space, or attempting DMA transfers where requested by SEGA, and documented in their manual. Naturally, the programmers had to abide by those rules, they were paid to after all, even though it is completely unnecessary. The only time you really need to halt the Z80 is when accessing the Z80's memory space A00000 to A0FFFF (Z80 0000 - FFFF).
Hi, I need help with something. For testing purposes, I'm trying to make a cheat code that gives Sonic all Chaos Emeralds by pressing C at the title screen in Sonic 1. The whole pressing the button and playing the Chaos Emerald sound works, but every time I go to the ending sequence, it shows the normal ending, probably because I'm not telling it to set the flag correctly. Help would be appreciated.
v_emeralds needs to equal 6 for the good ending to trigger. There's a check for this just above the End_LoadData label, this one affects what layout is loaded (more/no flowers), and there's another under ESon_Main, which determines Sonic's and others' behaviour during the ending (releasing the Chaos Emeralds). Going by this, your cheat should just be move.b #6,(v_emeralds).w. Do note that PlayLevel actually clears v_emeralds when it's processed. That code is called when you press start on the title screen, and by the Level Select when you choose a level. A solution would be to have your cheat not only set v_emeralds to 6, but to also set a separate flag. This flag is checked just before PlayLevel tries to clear v_emeralds, and if that flag is set (the cheat's enabled), that line of code is skipped over, so it doesn't clear v_emeralds.
Guys can you tell me please at which speed Sonic and other characters can run/roll on water in hydrocity act 2? Maybe I'm just blind but physics guide doesn't seem to cover that.
You could probably figure it out yourself with a little math and proper usage of the frame advance feature.
I'd say 7 pixels minimum movement, I say this because upon running a spindash across the water and letting Sonic slow down, it isn't until the speed drops to below 0x07.00 that the splash sprites disappear and he starts falling into the water. Please note, I used readings from RAM to find this info rather than the disassembly, so it may be inaccurate.
Speaking of running on water, where is that located in the asm file? I want to remove it as I'm looking to mess around with S3K a little.
Check under Obj_HCZWaterSplash. Specifically, loc_384B2. Everything, or at least most of everything pertaining to water running should be there.
What I did find was this line: move.b #1,($FFFFB19E).w, commenting that out actually removes running on water. It appears it's also handled by Obj_HCZWaterSplash, but the above ram address isn't called for anywhere in the code at all, so how does it also handle running on water? Any insight into this would be interesting. Edit: sub_3857E seems to be the part that checks for a player's x_vel, putting an rts at the beginning of this code prevents running on water. Very close to figuring this out.