don't click here

Basic Questions & Answers thread

Discussion in 'Engineering & Reverse Engineering' started by Tweaker, May 29, 2008.

  1. Caverns 4

    Caverns 4

    Member
    346
    0
    16
    Sonic: Retold
    Oay, so I need some help from some one who knows a few things about working with Sonic 3k.

    I'm trying to port Sonic 3k's level layout format into Sonic 2 for many reasons, mainly just because it's better, and conserves both ROM and RAM space, and while I have made some progress, I'm having a problem.

    First off, I ported the actual level layout reading code into my hack, and made the necessary changes to have support for 3 acts:
    Code (Text):
    1. loadLevelLayout:
    2.         moveq   #0,d0
    3.         move.w  (Current_ZoneAndAct).w,d0
    4.         ror.b   #2,d0
    5.         lsr.w   #4,d0
    6.         andi.w  #$FC,d0
    7.         lea (off_level).l,a0
    8.         movea.l (a0,d0.w),a0
    9.         lea (Level_layout_header).w,a1
    10.         move.w  #$7FF,d2
    11.  
    12. loc_1C382:
    13.         move.w  (a0)+,(a1)+
    14.         dbf d2,loc_1C382
    15.         rts
    16. ; End of function loadLevelLayout
    I also changed the RAM equates a bit:
    Code (Text):
    1. Level_Layout:
    2. Level_layout_header: ds.w 4
    3. Level_layout_main:
    4.             ds.w $40
    5. Level_Layout_End:
    6.  
    7.   ds.b $F78 ;Unused
    I'm going to ask my first question here: are the $F78 bytes going to always be unused? From my understanding, all it will need in RAM is the 8-byte header and $40 words to function, so the other bytes would be free.

    After this, I basically just used SonLvL level converter to convert a few of the level layouts to use the new format, and changed the array at Off_Level to something more like this:

    Code (Text):
    1. Off_Level:
    2.     dc.l Level_EHZ1 ; $00 ;Emerald Hill Zone
    3.     dc.l Level_EHZ2
    Clearly however, I missed something; the end result is this:
    [​IMG]
    (Yes, I did convert this level properly).

    Does anyone know what I have forgotten?
     
  2. MainMemory

    MainMemory

    Kate the Wolf Tech Member
    4,796
    383
    63
    SonLVL
    Those "unused" bytes aren't actually unused, that's the buffer for the actual row data, the 64 words are the addresses of each row's data in RAM. You haven't moved Level_Layout to somewhere other than $FF8000 have you? And, um, you changed every function that reads or writes to Level_Layout to accommodate the new format, right?
     
  3. Caverns 4

    Caverns 4

    Member
    346
    0
    16
    Sonic: Retold
    That's what I have been having a problem with. I haven't been able to figure what's the important stuff to change, and what it matches up to in Sonic 3k. Many functions don't seem to be named, and since there's not much similar code I can find, I'm a little lost. Do you know of anything?

    One possible issue, I tried downloading the Sonic 3K hg disassembly from the disassemblies page earlier, and the link is broken. I'm not sure if mine is the most up-to-date or not.
    The folder is called skdisasm_hg-a42a5feacc39, is that up-to-date?
     
  4. MainMemory

    MainMemory

    Kate the Wolf Tech Member
    4,796
    383
    63
    SonLVL
    Probably, nobody's done much with it. Unfortunately I don't know much about the level layout code.
     
  5. RetroKoH

    RetroKoH

    Member
    1,728
    107
    43
    S1Fixed: A successor to ReadySonic
    I have a copy of the up to date one... if you still cannot get a copy of the disasm, I can host it on my Mediafire for you to get. Lemme know if you need this.
     
  6. Hitaxas

    Hitaxas

    Retro 80's themed Twitch streamer ( on hiatus) Member
    The repo is currently giving me a 404 error. I was going to download the latest version of Sonic 2 so that I could mess around with it and see if I could get acquainted with it.


    Edit: Ok, question. Not sure if I am going to word this correctly though... How are springs in Sonic 2, how does that object in particular handle subtypes/bits or object type IDs?

    I've been trying to reinvent the wheel for my own amusement but cannot figure this part out. I know how to set an object to use different art if the object is set to type 01,02 or 03, However I cannot figure out how to do more beyond that. I want to be able to change an objects color or art but keep the subtype the same while still using the coding.

    My aim is to have a spring coding that takes up less space while still providing the same end result to an extent, and not have it look neater and not like the mess the coding originally looks like to me.


    Until I can get this sorted I am resorting to taking up 2 spots in Obj_Index but calling for the object. In the object code (calling it ObjED) I check for ObjE8, which calls for ED in Obj_Index, and if it is E8, it changes the art/palette line of the object. Not the best way to handle it though. =/
     
  7. RetroKoH

    RetroKoH

    Member
    1,728
    107
    43
    S1Fixed: A successor to ReadySonic
    Provided I have permission from those in charge here to be hosting their disassemblies and the like elsewhere, I'll get on it immediately. I'd rather get the ok from an admin or someone first though... maybe this isn't even a thing, but better safe than sorry.
     
  8. MainMemory

    MainMemory

    Kate the Wolf Tech Member
    4,796
    383
    63
    SonLVL
    subtype(a0) holds the subtype for the current object, it then can perform various combinations of btst, andi, and lsr to check the state of a bit or a set of bits.
     
  9. Caverns 4

    Caverns 4

    Member
    346
    0
    16
    Sonic: Retold
    I would appreciate it if I could have the most up-to-date one. It might make my life easier in terms of cracking everything that needs doing.
     
  10. Alriightyman

    Alriightyman

    I am back... from the dead! Tech Member
    357
    11
    18
    Somewhere in hot, death Florida
    0101001101101111011011100110100101100011 00000010: 0101001100000011 01000101011001000110100101110100011010010110111101101110
    I have this working in my Sonic 2 hack. I would say it is better, it just allows larger levels. So if you are not planning to make really large levels, then I wouldn't see any reason to port it. When I ported this to Sonic 2, I also ported S3K's drawing system as well. I wouldn't recommend porting the drawing system unless you want to venture into unknown territory in which no help exists.

    As for the layout format, it is quite simple. I was originally building Sonic 2 layouts by hand to S3K format.
    The first 8 bytes is a header broken up like this:
    Code (Text):
    1.  
    2. 1st word:  number of columns for the foreground in 128x128 blocks.
    3. 2nd word: Number of columns for the background in 128x128 blocks.
    4. 3rd word: number of rows for the foreground in 128x128 blocks.
    5. 4th word: number of rows for the background in 128x128 blocks.
    6.  
    The next 0x40 words (0x80 bytes) is a table of addresses in RAM. Each word represents an address. The first word is the layout data for the foreground, usually starts at 8088. The second word is the layout data for the background. This pattern repeats.

    Now, the layout data is set up like this:
    The first word in the header is the size in bytes of the foreground data. The second word is the size in bytes of the background data.
    So, if your 8 byte header looked like this:
    0082 0004 000C 0002
    Then the 0x80 byte table would look like this:
    Code (Text):
    1.  
    2. 8088 86A0
    3. 810A 86A4
    4. 818C 0000
    5. 820E 0000
    6. 8290 0000
    7. 8312 0000
    8. 8394 0000
    9. 8416 0000
    10. 8498 0000
    11. 851A 0000
    12. 859C 0000
    13. 861E 0000
    14. 0000 0000
    15. 0000 0000
    16. 0000 0000
    17. 0000 0000
    18. 0000 0000
    19. 0000 0000
    20. 0000 0000
    21. 0000 0000
    22. 0000 0000
    23. 0000 0000
    24. 0000 0000
    25. 0000 0000
    26. 0000 0000
    27. 0000 0000
    28. 0000 0000
    29. 0000 0000
    30. 0000 0000
    31. 0000 0000
    32. 0000 0000
    33.  
    Notice that there are a bunch of zeros or empty space. These are unused for this level but MUST be present. At 8088, the data is setup just like Sonic 2, but is 82 byte long. At 86A0, it is 4 bytes long.

    This makes a lot more sense when you load up a Sonic 3 layout in SonEd2 or SonLVL, and open it in a hex editor at the same time.Just remember to subtract 8000 from each word in your table to find it in a hex editor. So for 8088, when opened in a hex editor, the data is at address 0088.

    Also, your RAM equates should look more like:
    Code (Text):
    1.  
    2. Level_Layout:
    3. Level_layout_header:    ds.w 4  
    4. Level_layout_main:      ds.w $40  
    5.                     ds.b $F78 ; layout data
    6. Level_Layout_End:
    7.  
     
  11. Caverns 4

    Caverns 4

    Member
    346
    0
    16
    Sonic: Retold
    Thank you for the response, I'm glad to see someone else was able to get this working with success, and look forward to hopefully cracking this myself as well.
    For now though, while I'm still interested in implementing this, if I'm not mistaken, it really won't free up any RAM at all, which was my main reason for wanting it.

    I have however taken a step back for the moment; my current hack has all sorts of kinks that go back a long way (When I didn't know much about ASM), so I'm starting fresh. Now that my assembly skills are much better, I can think of countless ways to improve and speed up the code I had before, so I'm starting anew for now.

    Either way, I did change my equates as you said, and I'll probably want to use the Sonic3k layout format later, so do except me to come back to this one once I've got a little more headway in the remake.

    With that said, I'm asking a question here, but it's mainly just curiosity rather than how-to.

    Right now, I'm editing the Chopper from EHZ. Orgiginally, if the subtype was greater than 0, it jumps higher ($700), using cmpi.b #1,subtype(a0). I'm assuming using btst #0 would just be "Is is an even or odd number", and that's fine for what I'm doing, but how much will it save the processor?
     
  12. MainMemory

    MainMemory

    Kate the Wolf Tech Member
    4,796
    383
    63
    SonLVL
    If it's a simple matter of whether the number is 0 or nonzero, you can also use tst.b and beq (branch if zero) or bne (branch if nonzero).
     
  13. Caverns 4

    Caverns 4

    Member
    346
    0
    16
    Sonic: Retold
    Can somebody explain, in laymans terms, exactly how a DMA Transfer is set up?

    I understand what it does, and I've worked with it before to load uncompressed art, but right now, I'm having a hard time with just a simple routine, just getting ArtA that is literally a direct pointer provided to go to the VRAM at ArtTileB. I feel dumb having to ask this, but what would be the proper way to set this up?
     
  14. MarkeyJester

    MarkeyJester

    Original, No substitute Resident Jester
    2,250
    510
    93
    Japan
    We'll start with a very simple routine:

    Code (Text):
    1.         move.w  #$9380,($C00004).l
    2.         move.w  #$9400,($C00004).l
    3.         move.w  #$9500,($C00004).l
    4.         move.w  #$9680,($C00004).l
    5.         move.w  #$977F,($C00004).l
    6.         move.w  #$4000,($C00004).l
    7.         move.w  #$0080,($C00004).l
    You'll notice the first 5 words of data being moved start with 93, 94, 95, 96 and 97, and each of these numbers represents a VDP register ID. The number directly after is the value being set into that VDP register, so when moving 9380, the number 80 is put into VDP register 93.

    Registers 93 and 94 are the size (or length) of the transfer, the size is a word, and is often a division of 2 (likely related to the auto-incrementing). Register 93 is given the lower byte of the size, and 94 is given the upper byte of the size. If your size is $0100 bytes to transfer, you would idealy divide it by 2 to get you $0080, and then take the 80 and put that into register 93, and take the 00 and put that into register 94.

    Registers 95, 96 and 97 are the source address (where the data is being transfered from), the source address is a 24-bit address (3 bytes big). Register 95 is the lowest byte of the address, 96 is the middle byte of the address, and 97 is the upper byte of the address. If your source location was in RAM FF0000, then you would idealy divide it by 2 to get 7F8000, and then the 00 is put into register 95, 80 is put into register 96, and 7F is put into register 97.

    The last two words is the destination, this is the standard VDP address/mode setting. We'll read both words as a long-word (40000080) and break them up into binary:

    M1 M0 DD DC - DB DA D9 D8 - D7 D6 D5 D4 - D3 D2 D1 D0 - N0 N0 N0 N0 - N0 N0 N0 N0 - Z0 M4 M3 M2 - N0 N0 DF DE

    DF - D0 is the VRAM address (it's a word address from 0000 - FFFF)
    M4 - M0 is the VRAM mode:

    Code (Text):
    1.         M4 M3 M2 M1 M0
    2. VRAM Write  0  0  0  0  1
    3. CRAM Write  0  0  0  1  1
    4. VSRAM Write 0  0  1  0  1
    5. VRAM Read   0  0  0  0  0
    6. CRAM Read   0  1  0  0  0
    7. VSRAM Read  0  0  1  0  0
    Z0 is the DMA flag (0 manual/1 DMA)
    N0 are the null bits (always keep 0)

    We'll pretend our destination VRAM address is A740, in binary that's DF - D0: 1010 0111 0100 0000, we want to write to VRAM, so M4 - M0 is VRAM Write mode 00001, and the destination is used for DMA, so Z0 is 1. We now end up with:

    0 1 1 0 - 0 1 1 1 - 0 1 0 0 - 0 0 0 0 - 0 0 0 0 - 0 0 0 0 - 1 0 0 0 - 0 0 1 0

    In hexadecimal that's 67400082, and so, that is moved into the VDP port:

    Code (Text):
    1.         move.w  #$6740,($C00004).l
    2.         move.w  #$0082,($C00004).l
    As soon as these values are put into the port, the VDP will read them, halt the 68k, and then perform the transfer. 0100 bytes will be transfered from 68k RAM FF0000 - FF00FF, to VRAM A740 - A83F.

    Now, you can move two words at once in the form of a long-word, for example:

    Code (Text):
    1.         move.l  #$94009340,($C00004).l
    2.         move.l  #$96FD9580,($C00004).l
    3.         move.l  #$977F6740,($C00004).l
    4.         move.w  #$0082,($C00004).l
    It is important though that the VDP registers are set first, and that the last word of the destination is ALWAYS written to the first port C00004 - C00007 (I.e. ensuring the last word of the destination is moved seperately), otherwise the first word of data will not transfer correctly.

    You should be able to transfer from most places in the 68k region, including the ROM area. But places such as the Z80 memory space, the I/O section, and multiple other places may be restricted. Another note is that your data must generally be within a 68k memory range of roughly $20000 bytes (I.e. from ROM 000000 - 01FFFF, or 020000 - 03FFFF, or 040000 - 05FFFF, etc). So if your data is in ROM at offset $081F000 and is say $1800 bytes big, then it'll spill over the $81FFFF mark, the last $800 bytes of data will not transfer correctly. In cases such as this, you will be advised to align the data up to the next $20000th byte in the ROM.
     
  15. MainMemory

    MainMemory

    Kate the Wolf Tech Member
    4,796
    383
    63
    SonLVL
    I think the dma68kToVDP macro does all that, but that doesn't tell you how it works so...
     
  16. Caverns 4

    Caverns 4

    Member
    346
    0
    16
    Sonic: Retold
    That was... Quite an explanation... How anyone ever figured all this out is beyond me...
    I think it's probably a safe bet that the original programmers had something much less complicated to work with, or some easier way of managing all of this, and the only reason we get something as difficult as this is because our disassemblies are literally decompiled machine code.

    I guess it's reasons like this that the hg disassemblies' macros exist, which leads me into the next post.

    It doesn't, but I asked a question, and MarkeyJester undeniably gave me an answer. However, upon learning the real depth of simply popping art into VRAM, I find that it would most likely be more efficient and save time to simply use that macro.

    Thanks to the both of you!
     
  17. vladikcomper

    vladikcomper

    Tech Member
    211
    170
    43
    Sonic Warped
    Trust me, this isn't the hardest thing in the world. Sometimes you come across something much worse, especially if you coded for Sega Genesis add-ons :)

    Actually, old-school programmers had to know how things like this worked. I don't mean knowing what each bit and VDP register does by heart here of course, but that level of knowledge, when you understand what you're doing and know what directing you should move in, but you just need to look through the docs at times to remind yourself which registers you should use and how. After all, it's just the matter of moving bits around and sending right combinations to the data ports. If you know the binary codes and you know the rules, you should do fine.

    Of course, the programmer may not want to do that amount of calculations every time he wants programmed DMA. Programmers of old days used macros just they way we do in modern disassemblies (although, not in such insane numbers like recent Sonic 1 HG does).

    Yuji Naka himself was a macros lover, from what I've seen. Here's an example of his original code from Sonic 2:

    Code (Text):
    1.  
    2. dcblw   macro   \1,\2,\3,\4,\5
    3.     dc.l    (\1)*$1000000+(\2)
    4.     dc.w    (\4)+(\5)*$100
    5.     dc.w    (\3)
    6.     endm
    7.  
    8. edittbl:
    9.     dc.w    edit1tbl-edittbl
    10.     dc.w    edit2tbl-edittbl
    11.     dc.w    edit3tbl-edittbl
    12.     dc.w    edit4tbl-edittbl
    13.     dc.w    edit5tbl-edittbl
    14.     dc.w    edit6tbl-edittbl
    15.     dc.w    edit7tbl-edittbl
    16. edit1tbl:
    17.     dc.w    14
    18.     dcblw   ring_act,ringpat,$26bc,0,$00        ;1:
    19.     dcblw   item_act,itempat,$0680,0,$00        ;2:
    20.     dcblw   kani_act,kanipat,$0400,0,$00        ;3:
    21.     dcblw   hachi_act,hachipat,$0444,0,$00      ;4:
    22.     dcblw   fish_act,fishpat,$0470,0,$00        ;5:
    23.     dcblw   toge_act,togepat,$04a0,0,$00        ;6:
    24.     dcblw   shima_act,shimapat2,$4000,0,$00     ;7:
    25.     dcblw   jyama_act,jyamapat,$66c0,0,$00      ;8:
    26.     dcblw   musi_act,musipat,$04e0,0,$00        ;9:
    27.     dcblw   sjump_act,sjumppat,$04a8,0,$00      ;10:
    28.     dcblw   kamere_act,kamerepat,$249b,0,$00    ;11:
    29.     dcblw   kageb_act,kagebpat,$434c,0,$00      ;12:
    30.     dcblw   save_act,savepat,$26bc,0,$01        ;13:
    31.     dcblw   colichg_act,colichgpat,$26bc,0,$00  ;14:
    He used macros to handle debug pointers list in the original source code. I cannot tell how he did DMAs though. But the fact is, Sonic 2 featured DPLC queue system, which was basically dynamically formed DMA requests. This required writing a 68k routine that converted source and destination offsets, transfer length into the sequence of VDP requests, which would be imposible to code without knowledge of how DMA is programmed in detail.
     
  18. Aerosol

    Aerosol

    Not here. Moderator
    11,200
    601
    93
    Not where I want to be.
    Sonic (?): Coming summer of 2055...?
    Hey uh, I apologize if this has actually been explained before but I can't find an explanation that satisfies my curiosity.

    How exactly does the Sonic 3 & Knuckles lock-on work? You know, from a technical standpoint.
     
  19. GerbilSoft

    GerbilSoft

    RickRotate'd. Administrator
    2,972
    85
    28
    USA
    rom-properties
    Sonic & Knuckles is located at $000000 and is 2 MB.
    Sonic 3 is located at $200000 and is also 2 MB.

    On startup, S&K checks $200100 to see if a valid Mega Drive header is present, and if it matches one of its known games:
    • If it matches Sonic 3, then it enables S3&K mode.
    • If it matches Sonic 2, it enables Sonic 2 mode, plus it switches some other register in the cartridge to enable an extra 256 KB ROM at $300000. (This register isn't emulated in most emulators; instead, the S2&K ROM is precombined with the extra ROM, which has the same effect.)
    • If it matches Sonic 1 or another otherwise valid MD ROM, it goes to "NO WAY!" (with full Blue Sphere mode enabled for Sonic 1).
    • If it just gets back garbage data (e.g. $FF), it starts S&K normally.
    Due to the way the expansion port is wired, cartridges larger than 2 MB (e.g. Sonic 3D) will result in S&K assuming no cartridge is detected. This is because it passes through the A21 line ($200000) as-is, which is ignored by cartridges smaller than 2 MB (Sonic 3 and earlier), but used by larger cartridges to access higher addresses. (SRAM is located at $200000 in Sonic 3 as well; I'm assuming the S3 cartridge has the standard SRAM register at $A130F1, so the /TIME line is simply passed through to the cartridge. On Gens, the /TIME line is ignored for ROMs smaller than 2 MB, which is necessary for some games that don't have the register.)
     
  20. Aerosol

    Aerosol

    Not here. Moderator
    11,200
    601
    93
    Not where I want to be.
    Sonic (?): Coming summer of 2055...?
    Sorry, I should've been clearer. That part I understand.

    What I really mean is how do the different modes work themselves? I understand Sonic 2 uses a patch ROM (that 256K ROM I presume). Is that more or less how it's done for S3&K too? The S&K program just launches different routines if it detects Sonic 3 in the expansion slot?