don't click here

Basic Questions & Answers thread

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

  1. MarkeyJester

    MarkeyJester

    Original, No substitute Resident Jester
    2,203
    432
    63
    Japan
    https://krikzz.com/pub/support/mega-everdrive/x3x5x7/dev/extended_ssf-v2.txt doesn't specify if the bits should be 1 or 0, I assumed default 0 is no and 1 is yes, so it's possible your bne to wait is the opposite, and you need beq.

    I couldn't find the example code you referenced, but I did find a ROM and disassembled that instead (you'll have to avert your eyes at the unoptimisation, it looks like it was written in C):

    upload_2024-4-7_10-14-29.png

    This is looping if the bit is still clear, not set, so I think my assumption is right, 0 is default no, 1 is default yes.

    Furthermore, the data port is a word big, of which the lower byte is the data read/write:

    Code (Text):
    1. 0xA130E2 [........ DDDDDDDD] read/write
    You're writing to the upper byte only, which is likely another reason why data isn't getting through.

    As a small side note (probably doesn't matter but I'll say it anyway); I personally would put a delay after the config set and before the status read, admitedly, I don't know how fast it is and it might be okay, but precautions aren't bad to have, especially if it's only used for debugging.
     
  2. Kilo

    Kilo

    That inbetween sprite from S&K's title screen Tech Member
    363
    357
    63
    Canada
    It was written in C which is why I was getting confused on how to translate it. The reason I used a bne instead of a beq is because of this code here:
    upload_2024-4-7_5-12-44.png
    And I was sending 8-bit data only because the C function to send data only has an 8-bit input:
    upload_2024-4-7_5-14-33.png
    So hopefully that explains how I ended up doing what I did.

    Still no luck though, I even moved the function over onto the title screen just to be sure I wasn't being too fast with setting the register access and config
    upload_2024-4-7_5-32-30.png
    (Well having $A13000 in a0 would be a good start I suppose)

    But even fixing that, the game either hangs or just doesn't write the A.
     
    Last edited: Apr 7, 2024
  3. MarkeyJester

    MarkeyJester

    Original, No substitute Resident Jester
    2,203
    432
    63
    Japan
    Here are some key differences from yours and theirs:
    • They read the data byte back after writing.
    • They set W on the control port, not just P.
    • They setup the ROM bank addresses from F1 to FF, to point to 01 to 07.
    • Their ROM checks for reading data if you don't press "A" to send data.
    I assume most of what's here is not relevant to you, for example, the bank addresses should be setup already, and you don't need the ROM to be re-writable, you don't need the data returned back, and you're not looking for received data. But I felt to point them out just in-case there's a weird strict quirk involving the above preventing you from writing data.

    One final thing, their header is "SEGA SSF". This page again specifies:

    Now again, this could be just to do with the ROM mapper specifically and nothing of the USB, but if they're tied together, perhaps the USB portion is tied and only activated if the rest is available.

    The crashing you're experiencing I'm "assuming" is because the 68k is locked in a loop waiting for USB write to be ready, and it's not getting set to ready.

    I won't claim any of this is your solution, but I'm low on ideas and there's limited evidence to help here.
     
    Last edited: Apr 7, 2024
  4. Kilo

    Kilo

    That inbetween sprite from S&K's title screen Tech Member
    363
    357
    63
    Canada
    After much browsing, sure enough it is required to use the SSF mapper's header to communicate with the PC, quote from here.
    However it does not seem that setting up the mapper itself is required, just the header, which is bizarre.
    And sure enough, by simply changing the console to SEGA SSF
    upload_2024-4-7_9-45-4.png

    Now that I've figured this out, once I get some more spare time I'll make a thread on properly setting up and using the USB port for hacks. (This is going to be insanely useful, not just for real time debugging or demo recording, but potential live level editing or online multiplayer on hardware)
     
    Last edited: Apr 7, 2024
  5. Kilo

    Kilo

    That inbetween sprite from S&K's title screen Tech Member
    363
    357
    63
    Canada
    So, I'm at a total loss on this.
    I'm trying to add the centiseconds timer from Sonic CD into Sonic 1. I used the exact code from the disassembly, but it just refuses to work.
    upload_2024-4-9_17-36-20.png
    Just as a test I decided to null out everything except the line to set it to 99 when a time over is hit, and that's fine.
    upload_2024-4-9_17-37-5.png
    I then noticed there's an instruction to clear the upper word of the centiseconds timer as it's being calculated, I figured maybe that was getting in the way, even though that was just a part of CD's original code. But that didn't do anything either.
    upload_2024-4-9_17-39-33.png
    Some other things I tried included just making the centiseconds counter display the full seconds, that worked fine.
    Made the counter just use the frame count without doing the math, that did not work
    Any help is seriously appreciated.
     
    Last edited: Apr 9, 2024
  6. Devon

    Devon

    A̸ ̴S̴ ̵C̵ ̷E̶ ̸N̸ ̴D̶ ̵E̶ ̸D̶ Tech Member
    1,261
    1,432
    93
    your mom
    For the record, Hud_Secs takes d1 as a longword. The division puts the remainder into the upper word of d1, which is why Sonic CD clears it. As for your problem, I don't really see any glaring issues, besides the possibility of an incorrect RAM address, or maybe the value is somehow getting overwritten, or isn't actually being written to at all? Might help to post more of the HUD drawing function for additional context.
     
    Last edited: Apr 9, 2024
  7. Kilo

    Kilo

    That inbetween sprite from S&K's title screen Tech Member
    363
    357
    63
    Canada
    I double checked via Regen that the frame counter is in fact incrementing properly, and I'm using the right RAM address. My current theory is that it's trying to update the counter too quickly? That doesn't make sense though.

    Edit:
    Well I went and tried this out
    Code (Text):
    1.         move.b    ($FFFFFE25).w,d1 ; Load frames
    2.         tst.l    d1
    3.         beq.s    @Cont
    4.         illegal
    5. @Cont:
    And the game didn't crash. So yeah the frame counter is being set to 0, which confuses me even more, because code above it to tick seconds and minutes works fine when that couldn't be the case if the frame count was 0.

    upload_2024-4-9_18-10-12.png
    So I may be stupid.
     

    Attached Files:

    Last edited: Apr 10, 2024
  8. Devon

    Devon

    A̸ ̴S̴ ̵C̵ ̷E̶ ̸N̸ ̴D̶ ̵E̶ ̸D̶ Tech Member
    1,261
    1,432
    93
    your mom
    Okay, I figured out why.

    Code (ASM):
    1.         addq.b   #1,-(a1)
    2.         cmpi.b   #60,(a1)
    3.         bcs.s    Hud_End

    This bit completely exits the HUD drawing function if the frame counter is not 0. In Sonic 1, this is to prevent the HUD from being drawn too often than necessary, since the frame counter isn't visible to the player. In Sonic CD, since the frame counter is drawn (as centiseconds), they changed the branch to the equivalent of "loc_1C734" so that the HUD is redrawn every frame.
     
  9. nineko

    nineko

    I am the Holy Cat Tech Member
    6,312
    489
    63
    italy
    I know that this is besides the point, but, do we really want to use mulu and divu in a code which by definition is supposed to run 60 times per second? Wouldn't a LUT be much more practical at a negligible cost in terms of ROM space? Also, would that hardcoded 60 play nice in PAL mode? All the more reason to use two LUTs, I seem to remember someone did something like that in the past (though I can't look for it at this time).
     
  10. Devon

    Devon

    A̸ ̴S̴ ̵C̵ ̷E̶ ̸N̸ ̴D̶ ̵E̶ ̸D̶ Tech Member
    1,261
    1,432
    93
    your mom
    Well, even in PAL, it counts up to 60 frames before adding 1 second, so you'd have to account for that, too.
     
  11. Kilo

    Kilo

    That inbetween sprite from S&K's title screen Tech Member
    363
    357
    63
    Canada
    So I've been scratching my head at this for a couple days.
    What I'm doing is trying to calculate what Metal Sonic's velocity should be given his current position and a target position to interpolate his movement. The issue is that when this velocity should be negative it only ever results in a very large positive number. Any clues as to what I may be doing wrong? I tried ChatGPT and best it could give me was 'Try subx' which did absolutely nothing.
    Video example of the issue, showing Metal Sonic's velocity at D050 and D052:


    The code that does the work:
    Code (Text):
    1.         move.w    obvar_xpos(a0),d2
    2.         sub.w    d2,d0                            ; Get distance between X position and Y target.
    3.         divs.w    #6,d0                            ; Divide by data update delay.
    4.         move.w    d0,obvar_xvel(a0)                ; And we have our X velocity.
    5.         move.w    obvar_ypos(a0),d2
    6.         sub.w    d2,d1                            ; Get distance between Y position and Y target.
    7.         divs.w    #6,d1                            ; Divide by data update delay.
    8.         move.w    d1,obvar_yvel(a0)                ; And we have our Y velocity.
    I should note I tried switching around subtracting the target from the position, which just made Metal fly all over the stage.
     
    Last edited: Apr 22, 2024
  12. OrionNavattan

    OrionNavattan

    Tech Member
    167
    165
    43
    Oregon
    From this sentence alone, I'm getting the impression that the velocities you're calculating (or something related to them) are being mistreated as unsigned values elsewhere in your code (possibly an unsigned branch condition being used instead of a signed one?).
     
  13. Devon

    Devon

    A̸ ̴S̴ ̵C̵ ̷E̶ ̸N̸ ̴D̶ ̵E̶ ̸D̶ Tech Member
    1,261
    1,432
    93
    your mom
    The 68000's division instructions involve dividing a 16-bit number from a 32-bit number. After performing the subtraction and before dividing, use ext.l to sign extend the dividend into a longword.

    Code (ASM):
    1.         move.w    obvar_xpos(a0),d2
    2.         sub.w     d2,d0                            ; Get distance between X position and Y target.
    3.         ext.l     d0                               ; <-- Sign-extend into longword so that division behaves properly
    4.         divs.w    #6,d0                            ; Divide by data update delay.
    5.         move.w    d0,obvar_xvel(a0)                ; And we have our X velocity.
    6.         move.w    obvar_ypos(a0),d2
    7.         sub.w     d2,d1                            ; Get distance between Y position and Y target.
    8.         ext.l     d1                               ; <-- Sign-extend into longword so that division behaves properly
    9.         divs.w    #6,d1                            ; Divide by data update delay.
    10.         move.w    d1,obvar_yvel(a0)                ; And we have our Y velocity.
     
  14. Kilo

    Kilo

    That inbetween sprite from S&K's title screen Tech Member
    363
    357
    63
    Canada
    And so it works, thanks a ton! Definitely something to keep in mind in the future. :thumbsup:
     
  15. DeltaWooloo

    DeltaWooloo

    Be a boss-man and come to my big and tall man shop Member
    So, I'm trying to get a random ring monitor working. So far, I have this subroutine that loads a table which is supposed to load the different values of rings you can get like so:
    Code (Text):
    1. DetermineRingValue:
    2.         moveq    #0,d0
    3.         move.b    (v_randomrings).w,d0
    4.         cmp.b    #9,d0 ; for some reason #(MonitorValues_LUT_End-MonitorValues_LUT)-1 will not work
    5.         bne.s    .calcvalue
    6.         clr.b    d0
    7.         move.b    d0,(v_randomrings).w
    8.  
    9.     .calcvalue:
    10.         lsl.w    #1,d0    ; doubles the value of d0
    11.         move.w    MonitorValues_LUT(pc,d0.w),d0
    12.         move.w    d0,(v_randomringsamount).w ; stores the value in v_randomringsamount
    13.         rts
    14.  
    15. MonitorValues_LUT:
    16.         dc.w    5, 10, 15, 20, 25, 30, 35, 40, 45, 50   ; Values in MonitorValues_LUT array
    17. MonitorValues_LUT_End:  
    18.         even
    Right now, I'm having difficulty incorporating RandomNumber, which is supposed to assist me in randomizing the table.

    In the meantime, I've done this:
    Code (Text):
    1. Pow_ChkRandomRings:
    2.         cmpi.b    #5,d0        ; does monitor contain invincibility?
    3.         bne.s    Pow_ChkRings
    4.         addq.b    #1,(v_randomrings).w
    5.         move.w    (v_randomringsamount).w,d0
    6.         bsr.w    DetermineRingValue
    7.         add.w    d0,(v_rings).w                ; add whatever amount of rings you got from the randomizer
    8.         ori.b    #1,(f_ringcount).w            ; update the ring counter
    9.         move.w    #sfx_RandomMonitor2,d0
    10.         jmp    (PlaySound_Special).w
    I set it up to increment v_randomrings by 1 and initialize v_randomringsamount as a variable to load the LUT. However, currently, when playing the game, it follows a predetermined sequence: 10 rings, then 15, then 20, in order from the table. This isn't randomizing it, and I prefer not to alter the list to include a mix of numbers, as that would make it too easy to identify a pattern. Does anyone have a straightforward solution to randomize the table?
     
  16. Devon

    Devon

    A̸ ̴S̴ ̵C̵ ̷E̶ ̸N̸ ̴D̶ ̵E̶ ̸D̶ Tech Member
    1,261
    1,432
    93
    your mom
    2 things
    1. Regarding the comment with "(MonitorValues_LUT_End-MonitorValues_LUT)-1", that returns that total number of BYTES the table takes up, minus 1. Change the -1 to a /2. For one thing, when subtracting 1, you are basically excluding the last table entry, it would reset to 0 if the index points to that last entry. The division by 2 divides the number of bytes per table entry (2), which will get you the number of table entries. Even without that, you are checking if the index is 9, which IS a valid table index, as it points to the 50, but you make the index reset back to 0 if it's 9, so the 50 goes unused. You wanna check if the index goes out of bounds, or change the branch to effectively make it a check if the index is greater than 9.
    2. If you want to get a random table entry instead, you can use RandomNumber, which will return a random 32-bit number in register d0. You can limit the result to fit the table in one of two ways.
      1. A quick and dirty method would be to do an AND mask with the smallest possible bit mask. In this case it would be 0xF. Then, you would check if the value is an out of range index (0xA-0xF), and if so, either loop back to the RandomNumber call to get a new value, or just overwrite it with some other default value or whatever you want to do.
      2. This would be more proper and straightforward, but it involves division. Basically, get a random number (and it would also be best to mask it into a 16-bit value via ANDing with 0xFFFF, so that the division is guaranteed to work, since it performs a 32-bit / 16-bit operation, and if the result is too large, it gets discarded), divide it by the number of table entries (0xA), and then perform a swap to get the remainder, which will be a value between 0 and 9.
     
    Last edited: May 6, 2024 at 1:14 AM
    • Like Like x 3
    • Agree Agree x 1
    • List
  17. penBorefield

    penBorefield

    Living in my best life Member
    125
    20
    18
    Basement
    Patching things up
    Cross-post from SSRG:
    Is there a solution to remove the lives system in Sonic 1/2/3&Knux?
     
  18. Kilo

    Kilo

    That inbetween sprite from S&K's title screen Tech Member
    363
    357
    63
    Canada
    Find the RAM address/variable name for lives for the given game/disassembly. Look at every instance that it's used and figure out what it does, and then delete it accordingly. Would be a good learning experience on how to navigate your disasm and learn how all of the game's systems and logic link together. :thumbsup: