Okay, so here's what's going on: The platform waits for the button to be pressed. When it is, the platform (subtype 7/Obj52_Type07) then changes its subtype to 4/Obj52_Type02. As per the new subtype, it waits for Sonic to stand on it. When it happens, it increments its subtype to 5/Obj52_Type05. Obj52_Type05 calls ObjHitWallRight, which OVERWRITES a1 as it gets the distance from the wall towards the right (it calls FindWall). As a result, when it returns to Obj52_StandOn, it'll enter MvSonicOnPtfm2 with a corrupted a1 value, and will modify the chunk data that referenced in ObjHitWallRight, instead of Sonic's object variables. The same problem happens when the subtype gets set to 3/Obj52_Type03. To fix this, when calling Obj52_Move (in both Obj52_Platform and Obj52_StandOn), make sure to restore a1 afterwards, so that PlatformObject and MvSonicOnPtfm2.
I managed to implement the Super Flickies from S3K to S1 successfully as you can see in the image below: (haven't got around to giving the birds its palette cycling yet) In S3K, the flickies tend to target any nearby enemies and destroy them in your path, however, in this case: they don't seem to target enemies. I managed to look into this section of the code to see what was the culprit and this seems to be the issue: Code (Text): Obj_SuperTailsBirds_GetDestination: tst.b superTailsBirds_target_found(a0) bne.s .fly_towards_enemy tst.b superTailsBirds_search_delay(a0) beq.s .look_for_target subq.b #1,superTailsBirds_search_delay(a0) bra.s .fly_around_tails ; --------------------------------------------------------------------------- .look_for_target: bsr.w Obj_SuperTailsBirds_FindTarget tst.w d1 bne.s .fly_towards_enemy .fly_around_tails: move.b superTailsBirds_angle(a0),d0 jsr (CalcSine).l asr.w #3,d0 asr.w #4,d1 move.w (v_player+x_pos).w,d2 move.w (v_player+y_pos).w,d3 subi.w #$20,d3 add.w d0,d2 add.w d1,d3 rts ; --------------------------------------------------------------------------- When I tried to replace the check in .look_for_target so it can detect almost every badnik that shares a similar collision type, they tend to fly out of bounds and/or in random occurrences on-screen and never fly back to Tails. Does anyone know a preferable check I can use so the flickies can target the correct objects? If you want me to send the full object code, then let me know and I can DM you it. (also I ported S3K's collision response list if that helps in any way)
It would definitely help to see your implementation of "Obj_SuperTailsBirds_FindTarget" and ".fly_towards_enemy" to understand what's going on.
Here you go: Code (Text): Obj_SuperTailsBirds_FindTarget: moveq #0,d1 lea (Collision_response_list).w,a4 move.w (a4)+,d6 beq.s loc_1A41A.return moveq #0,d0 addq.b #2,(_unkF66C).w cmp.b (_unkF66C).w,d6 bhi.s loc_1A41A move.b #0,(_unkF66C).w loc_1A41A: move.b (_unkF66C).w,d0 sub.w d0,d6 lea (a4,d0.w),a4 .loop: movea.w (a4)+,a1 move.b obColType(a1),d0 beq.s .ignore_object bsr.s .check_if_object_valid .ignore_object: subq.w #2,d6 bne.s loc_1A41A .return: rts ; End of function Obj_SuperTailsBirds_FindTarget Just as a quick note, uncommenting the lines that use _unkF66C doesn't change much. Code (Text): .fly_towards_enemy: movea.w superTailsBirds_target_address(a0),a1 move.w x_pos(a1),d2 move.w y_pos(a1),d3 tst.b render_flags(a1) bpl.s .enemy_off_screen move.w x_pos(a0),d0 sub.w d2,d0 addi.w #$C,d0 cmpi.w #$18,d0 bhs.s .enemy_out_of_range move.w y_pos(a0),d1 sub.w d3,d1 addi.w #$C,d1 cmpi.w #$18,d1 bhs.s .enemy_out_of_range bsr.s .hit_enemy .enemy_off_screen: move.b #0,superTailsBirds_target_lock(a1) move.b #0,superTailsBirds_target_found(a0) move.b #60*2,superTailsBirds_search_delay(a0) .enemy_out_of_range: rts ; End of function Obj_SuperTailsBirds_GetDestination
Aight, my hack currently has hit a snag. This is with the implementation of uncompressed chunks and its co-operation with water. My model 2 Genesis and the recent public Blastem release (0.6.2) freeze when loading in on Labyrinth Act 1/2 (basically when water's on screen). I was told that another hack also had the same problem, and was clued in how they fixed it: apparently they redid the HBlank (PalToCRAM) code. How can I optimize it to work with uncompressed chunks?
That's the weird part. I was trying to figure out what could've caused the freeze when I came across it, and I was worried that the implementation had something to do with it. It came into play with the integration of uncompressed chunks, the lack of the surface object didn't change anything (was ported to a different project without those chunks, it worked). When stripping away the code within PalToCRAM, it doesn't freeze in that spot on the aforementioned devices.
Anyone have any clue what these errors are? It's the Xenowhirl S2 disassembly by the way (been trying to get the Clone Driver working).
It’s a missing conditional assembly terminator. For every ‘if’ directive, there has to be a corresponding ‘endif’. In this case, there’s one missing somewhere (and unfortunately it can be difficult to find the missing one).
I think I fixed the endif, though the first error still won't go away and I have no idea what's wrong with the line it pinpoints.
Can you show us the line? It seems like you used a character that the assembler doesn't support in a symbol name, only standard latin letters (A-Z), numbers, and underscores are valid in names.
If '0xa0' is the problem symbol, then this would be the result of AS not allowing digits as the first character in symbol names. As MainMemory said, sharing the problematic line would help.
I was gonna copy the routine onto here, but when I pasted it, the anomoly shown up on here, seems to not be visible on the editor. Managed to fix it. It does link to another problem however, my disassembler is out of sync with my ASM it seems. For example the second error lists it on line 94909 but my asm only goes to 94891. Is there anything that would make it skip forward a few lines like that, since it make looking for errors kinda hard.
I'm gonna take a wild stabbing guess... ...have you included a binary file using "include" instead of "binclude", and as a result it's trying to process the binary file as if it were an assembly file of text? ...another posibility might be the text file format the source is saved as, I donno if AS supports anything other than ANSI?
Okay, I'm trying to get this guide working on the Github disassembly, but I'm getting pelt with nonsensical errors in the handler. https://info.sonicretro.org/SCHG_How-to:Add_a_new_zone_in_Sonic_1 This is just one of these errors, oddly complaining about player mappings/DPLC's when all I'm doing is just applying the "add a new zone" guide and following its steps. I'm legitimately frustrated here since as I've mentioned, I've done what the guide says but this BS keeps happening.
I think it's AniArt_Load, and I think you haven't put a pointer to "AniArt_none" on the end of the list. The register contents are telling me it's just done the level drawing from V-blank. Given most data registers are blank, there was no drawing. d0 contains 533C. The word which exists of the first instruction for "AniArt_GHZ", is 5338, so if "AniArt_none"s pointer is not there, that's the word it'll read. On a normal copy of Sonic 1, the "AniArt_Load" routine is at approximately 1BFXX + 5CC8 = 212XX approx. Your crash location is very close. I suspect random garbage code caused d0 to increment by 4 before eventually hitting a word beginning with F, and triggering F emulation.
Want to ensure I understand some things correctly regarding The Nemesis Compression Subroutine Nem_Build_Code_Table.Assuming we are In normal mode ( not xor). The first few questions relate to the first descriptive field read (pallets index , repeat, length In bits etc) upon entering sub routine Nem_Build_Code_Table isn't the first byte read at the top instruction: move.b (a0)+,d0 ; read first byte .. Isn't this the same thing as the byte that can be read 3 ways ( sign set , sign clear, FF ). But it is just that it is the initial time read so it is like the starts the end . And wont this first read byte read the first time always have sign bit set? the next byte read just at NemBCT_Chkend in this first loop around for the first loop is the repeat count and length of code. the comment for the code that checks if sign bit being set that signifies a new pallete index... Isn't this comment intended for proceeding loops adter the first one that go from using the same palette index to a new one? the next byte is the code itslf is the code (is both cases of whether or not we branch to NemBCT_ShortCode) Then it loops back to NemBCT_Looop. And we gon on to the next pass: Am I understanding this right: The first byte here would be the second byte in the first loop through upon the call to Nem_Build_Code_Table, here it is the first byte read. ( More questions on this obscuritiy below at bottom ) the first byte read is the first byte if the msb clear and we dont branch , is not the new pallete index, but rather the copy count and the code length in bits. Otherwise, if the MSB is clear of this byte ,this byte is the new pallete index and we branch to Nem_ChkEnd and if its' not FF then we pass down again to NEMBCT_LOOP where we read a byte at the top of this subroutine and this byte read is now the second byte ( where it just was the first ). ( again more question below on this ) then next byte is no different - it's the code itself the byte after is the three different possible meaning byte (MSB set , MSB clear, FF ) So weird flow seen in this subroutine occurs .... where the the meaning of an instruction and the purpose of the part of the descriptive byte read changes ... like the cmp.b #$80,d0 isn't functional if new palette index since this is the second byte and where a pass of NEMBCT_LOOP gets the first byte then if the sign is set for new pallete index goes back to Nem_ChkEnd and so when it goes and passes down through to NEMBCT_Loop again and reads another byte of what would be the first byte, it isn't the first byte.... or how the first byte read from NEMBCT_LOOP if sign bit is clear is the copy count and length of code instead of the pallete index - the purposed of an instruction and the part of the compression byte read is changing .... What do you call this kind of codeing phenemeon in the world of code design? What are ways to figure out the purpose of code and what it does that while rev engineering it without comments ... for example when a certain instruction can be useless in certain cases but yet they aren't branched over , making it seem they always serve a purpose in all cased... It would make one reverse engineering it tio assume it always have a purpose. Or a rev engineer may assume the bytes read's purpose is always the same, when in fact it changes if new pallete in example. How can one quickly sort out these kinds of thing's while breaking apart code? did they use this confusing code style like this for the reason of efficiency? I assume it's to make the bytes after them (code itself etc ) to always have the same purpose regardless of if same pallete index or new pallete index...? What is the best way to comment code that where some code loops back and thus instructions get different parts of the byte-stream in different cases for example in our subroutine here? Can you have layers of comments based on flow - seems too confusing? Is there literature of any kind on programming code design theories / principles in assembly that cover such a phenomenon where all this craziness happens? And the best way to reverse engineer it and comment it? Sonic dissasemblies comments them in a way where it is assumed you know which of the several flows that lead to the insturction that the instruction is commenting about. Lastly, looking through Nem_BCT_ShortCode I see d5 is the number of iterations of possible codes that are longer codes with the prefix of this code and since it uses prefix-free codes (no valid code is a prefix of a longer code) , it needs to have multiple entries for all the longer coded with it's prefix in the table? I don’t understand this table. I dont The first few questions relate to the first descriptive field read ( pallets index , repeat, length In bits etc) upon entering sub routine Nem_Build_Code_Table understand this table can someone explain it to me and how it relates to describing a compressed form of the patterns? Also Why when the code is 0 or if the code is 1 or if the code is 2 , d1 ( the number bits needed to shift left for the codes highest bit to be in bit 7 ) is equal to 5 and I see other weird things like if it's 0x7 binary then d1 is 4 and not 5 since ? it seems it'd be 5 shifts for 7 to go to being the biggest number but maybe it's code is 7 but i's length is not the bits needed for 7 ( 3 ) but rather 4 ???? Thnx
nevermind, the branch to the milliseconds code was in the wrong place, it should've been at the start of HUD_ChkLives, not at the end (move this to the trash if you want to mods) Spoiler: original post I added a milliseconds / centiseconds counter to the timer in stages, but it doesn't appear. As a matter of fact, if I perform a spin dash, the VRAM overwrites the missing extra numbers that are supposed to be drawn. Any way I can move the VRAM around to prevent this from happening? I'll provide the HUD_Update subroutine as well as mappings for the HUD (this is Hivebrain btw) Spoiler: HUD_Update Code (Text): ; --------------------------------------------------------------------------- ; Subroutine to update the HUD ; --------------------------------------------------------------------------- ; ||||||||||||||| S U B R O U T I N E ||||||||||||||||||||||||||||||||||||||| HudUpdate: tst.w ($FFFFFFFA).w ; is debug mode on? bne.w HudDebug ; if yes, branch tst.b ($FFFFFE1F).w ; does the score need updating? beq.s Hud_ChkRings ; if not, branch clr.b ($FFFFFE1F).w move.l #$5C800003,d0 ; set VRAM address move.l ($FFFFFE26).w,d1 ; load score bsr.w Hud_Score Hud_ChkRings: tst.b ($FFFFFE1D).w ; does the ring counter need updating? beq.s Hud_ChkTime ; if not, branch bpl.s loc_1C6E4 bsr.w Hud_LoadZero loc_1C6E4: clr.b ($FFFFFE1D).w move.l #$5F400003,d0 ; set VRAM address moveq #0,d1 move.w ($FFFFFE20).w,d1 ; load number of rings bsr.w Hud_Rings Hud_ChkTime: tst.b ($FFFFFE1E).w ; does the time need updating? beq.s Hud_ChkLives ; if not, branch tst.w ($FFFFF63A).w ; is the game paused? bne.s Hud_ChkLives ; if yes, branch lea ($FFFFFE22).w,a1 cmpi.l #$93B3B,(a1)+ ; is the time 9.59? beq.s TimeOver ; if yes, branch addq.b #1,-(a1) cmpi.b #60,(a1) bcs.s Hud_ChkLives move.b #0,(a1) addq.b #1,-(a1) cmpi.b #60,(a1) bcs.s loc_1C734 move.b #0,(a1) addq.b #1,-(a1) cmpi.b #9,(a1) bcs.s loc_1C734 move.b #9,(a1) loc_1C734: move.l #$5E400003,d0 moveq #0,d1 move.b ($FFFFFE23).w,d1 ; load minutes bsr.w Hud_Mins move.l #$5EC00003,d0 moveq #0,d1 move.b ($FFFFFE24).w,d1 ; load seconds bsr.w Hud_Secs Hud_ChkLives: tst.b ($FFFFFE1C).w ; does the lives counter need updating? beq.s Hud_ChkBonus ; if not, branch clr.b ($FFFFFE1C).w bsr.w Hud_Lives bsr.s CentiSecond Hud_ChkBonus: tst.b ($FFFFF7D6).w ; do time/ring bonus counters need updating? beq.s Hud_End ; if not, branch clr.b ($FFFFF7D6).w move.l #$6E000002,($C00004).l moveq #0,d1 move.w ($FFFFF7D2).w,d1 ; load time bonus bsr.w Hud_TimeRingBonus moveq #0,d1 move.w ($FFFFF7D4).w,d1 ; load ring bonus bsr.w Hud_TimeRingBonus Hud_End: rts TimeOver: ; XREF: Hud_ChkTime clr.b ($FFFFFE1E).w lea ($FFFFD000).w,a0 movea.l a0,a2 bsr.w KillSonic move.b #1,($FFFFFE1A).w rts CentiSecond: move.l #$75400003,d0 moveq #0,d1 move.b ($FFFFFE25).w,d1 mulu.w #$1AA,d1 lsr.l #8,d1 jsr Hud_Secs rts Spoiler: Mappings File ("obj21.asm") Code (Text): ; --------------------------------------------------------------------------- ; Sprite mappings - SCORE, TIME, RINGS ; --------------------------------------------------------------------------- dc.w byte_1C5BC-Map_obj21, byte_1C5F0-Map_obj21 dc.w byte_1C624-Map_obj21, byte_1C658-Map_obj21 ; Y-Pos, layout, VDP (2bytes - plane, pallet line (2bits), flip, mirror, first tile (11bits) - ), X-Pos byte_1C5BC: dc.b $C dc.b $80, $D, $80, 0, 0 ; SCOR dc.b $80, $D, $80, $18, $20 ; E + score left numbers dc.b $80, $D, $80, $20, $40 ; score right numbers dc.b $90, $D, $80, $10, 0 ; TIME dc.b $90, $D, $80, $28, $28 ; minutes : seconds dc.b $90, 1, $80, $2A, $48 ; : dc.b $90, 5, $80, $E0, $50 ; centiseconds dc.b $A0, $D, $80, 8, 0 ; RING dc.b $A0, 1, $80, 0, $20 ; S dc.b $A0, 9, $80, $30, $30 ; number of rings dc.b $40, 5, $81, $A, 0 ; lives icon dc.b $40, $D, $81, $E, $10 ; lives counter dc.b 0 byte_1C5F0: dc.b $C dc.b $80, $D, $80, 0, 0 ; SCOR dc.b $80, $D, $80, $18, $20 ; E + score left numbers dc.b $80, $D, $80, $20, $40 ; score right numbers dc.b $90, $D, $80, $10, 0 ; TIME dc.b $90, $D, $80, $28, $28 ; minutes : seconds dc.b $90, 1, $80, $2A, $48 ; : dc.b $90, 5, $80, $E0, $50 ; centiseconds dc.b $A0, $D, $A0, 8, 0 ; RING dc.b $A0, 1, $A0, 0, $20 ; S dc.b $A0, 9, $80, $30, $30 ; number of rings dc.b $40, 5, $81, $A, 0 ; lives icon dc.b $40, $D, $81, $E, $10 ; lives counter dc.b 0 byte_1C624: dc.b $C dc.b $80, $D, $80, 0, 0 ; SCOR dc.b $80, $D, $80, $18, $20 ; E + score left numbers dc.b $80, $D, $80, $20, $40 ; score right numbers dc.b $90, $D, $A0, $10, 0 ; TIME dc.b $90, $D, $80, $28, $28 ; minutes : seconds dc.b $90, 1, $80, $2A, $48 ; : dc.b $90, 5, $80, $E0, $50 ; centiseconds dc.b $A0, $D, $80, 8, 0 ; RING dc.b $A0, 1, $80, 0, $20 ; S dc.b $A0, 9, $80, $30, $30 ; number of rings dc.b $40, 5, $81, $A, 0 ; lives icon dc.b $40, $D, $81, $E, $10 ; lives counter dc.b 0 byte_1C658: dc.b $C dc.b $80, $D, $80, 0, 0 ; SCOR dc.b $80, $D, $80, $18, $20 ; E + score left numbers dc.b $80, $D, $80, $20, $40 ; score right numbers dc.b $90, $D, $A0, $10, 0 ; TIME dc.b $90, $D, $80, $28, $28 ; minutes : seconds dc.b $90, 1, $80, $2A, $48 ; : dc.b $90, 5, $80, $E0, $50 ; centiseconds dc.b $A0, $D, $A0, 8, 0 ; RING dc.b $A0, 1, $A0, 0, $20 ; S dc.b $A0, 9, $80, $30, $30 ; number of rings dc.b $40, 5, $81, $A, 0 ; lives icon dc.b $40, $D, $81, $E, $10 ; lives counter even
A dumb question. How did the snasm68 / asm68k assemblers get edited to fix stuff as people have in the SR community? Did they have to reverse engineer it and modify it in assembly or is their a C source?
They were reverse engineered. There is no source code leak or even a full decompilation that I know of.