don't click here

Basic Questions & Answers thread

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

  1. Oh...that makes sense. Now I feel dumb for not realizing it was a problem with the shield. Thank you Kilo.
     
    Last edited: May 10, 2024
  2. RetroKoH

    RetroKoH

    Member
    1,728
    107
    43
    S1Fixed: A successor to ReadySonic
    Hi guys! I've got two questions but I'm gonna wait to ask the second one until after the first one is resolved, as it's a little messy to explain.

    So, tl;dr, how do I accurately generate a random number from 1-xxx in Sonic 1 that ISN'T a multiple of 2? I know how to call RandomNumber, and I know to use v_framecount if I wanna base "random" purely on timing, but IDK how to manipulate the end result for other ranges. Details below:

    I implemented the random monitor into Sonic 1 as an optional toggle, and I want it to randomly give out one of 6 items (indexed 0-5). I couldn't ANDI #7, so I took a long convoluted route to get there that is likely more complicated (and less balanced) than it needs to be:

    Code (Text):
    1. ; give 'random' item (Fix randomizer to include S3K Monitors)
    2.         jsr        (RandomNumber).w    ; call for random number
    3.         andi.w    #$11,d0                ; random items in two pools (upper nybble used later)
    4.         move.w    (v_framecount).w,d1    ; use the timer to determine which item
    5.         andi.w    #1,d1                ; get 0 or 1 (then decide whether to increment)
    6.         addq.w    #1,d1                ; add 1 to prevent getting the static monitor
    7.         btst    #4,d0                ; should we increment d1 a second time?
    8.         beq.s    .noInc                ; if not, branch
    9.         addq.w    #1,d1                ; increment again
    10.  
    11. ;  d1 = (1, 2, or 3)
    12.     .noInc:
    13.         btst    #0,d0                        ; which item group?
    14.         bne.s    .group0
    15.         addq.w    #3,d1
    16.     .group0:
    17.         move.b    d1,obAnim(a0)        ; get subtype
    So it generates a random number into d0, but only retains bytes 0 and 4. bit 0 denotes which pool to pull from (Pool 1 or Pool 2), while bit 4 denotes whether or not we will increment the next value by 1 or not.
    It then loads the current frame count into d1, only retaining the least significant bit. This value determines which of the three items in the chosen pool we will receive, but note that we only have two resulting values for d1. To get a third, we use bit 4 from d0 to determine whether or not to increment d1. This allows d1 to be either 0, 1 or 2. d1 is then loaded into the icon's animation OST.

    My question is, is there a better way to do this, where all items have closer to equal weight in randomness?
     
    Last edited: Aug 23, 2024
  3. Devon

    Devon

    La mer va embrassé moi et délivré moi lakay. Tech Member
    1,428
    1,744
    93
    your mom
    An easy method is to mask out the high word of the random number (AND with 0xFFFF) and use modulus to fit it within a range. Let's say you want to generate a number within the range [1, 4] (inclusive). After getting the random number and applying the mask (this is important, because division is a 32-bit by 16-bit function, and the result must fit in 16-bits, because the top word is overwritten with the remainder of the division), you divide it by the max number you want - the min number you want + 1. In this case, you'd be dividing by (4 - 1) + 1... which is 4. Then, you perform a swap to access the remainder, with will be in range [0, 3], so then you add the min number. to get the range you want.
    Code (ASM):
    1.         jsr     RandomNumber                    ; Get random number
    2.         ; Add further randomness modification here before continuing
    3.         andi.l  #$FFFF,d0                       ; Make sure division will always work
    4.         divu.w  #(MAX_NUM-MIN_NUM)+1,d0         ; Divide by adjusted maximum number
    5.         swap    d0                              ; Get remainder of division
    6.         addi.w  #MIN_NUM,d0                     ; Add minimum number

    A hacked up method that doesn't involve division is to AND mask the random number with the smallest possible mask you can, and then check if the result fits in the range, and if not, do something else to get a number that does (get another random value, modify the value, use a default value, etc.).
    Code (ASM):
    1. .GetRandom
    2.         jsr     RandomNumber                    ; Get random number
    3.         ; Add further randomness modification here before continuing
    4.         andi.w  #MASK_VALUE,d0                  ; Mask it
    5.         cmpi.w  #MAX_NUM-MIN_NUM,d0             ; Is it in range?
    6.         bhi.s   .GetRandom                      ; If not, branch
    7.         addi.w  #MIN_NUM,d0                     ; Add minimum number

    or
    Code (ASM):
    1. .GetRandom
    2.         jsr     RandomNumber                    ; Get random number
    3.         ; Add further randomness modification here before continuing
    4.         andi.w  #MASK_VALUE,d0                  ; Mask it
    5.         cmpi.w  #MAX_NUM-MIN_NUM,d0             ; Is it in range?
    6.         bls.s   .GotRandom                      ; If so, branch
    7.         ; Modify number here
    8.  
    9. .GotRandom:
    10.         addi.w  #MIN_NUM,d0                     ; Add minimum number
     
  4. RetroKoH

    RetroKoH

    Member
    1,728
    107
    43
    S1Fixed: A successor to ReadySonic
    Ok. Onto the second question... I don't know how to fully explain this without including a ROM, so here is the latest build from S1Fixed: https://github.com/RetroKoH/S1Fixed/blob/main/S1Fixed.bin

    So, long story short, some of my art doesn't load into VRAM properly in Labyrinth Zone. The most apparent example is the Ring Count on the HUD. It seems that the code is being run, but the proper art doesn't appear, at least not initially. Oddly, this only happens when the Centiseconds mod is enabled. Meanwhile, the ? monitor icon NEVER loads when PLCs are loaded if the Randomizer mod is active.

    Art loading bugs like these only occur in Labyrinth Zone (and by extension, SBZ3). Does anyone have any insight as to why this might be happening? Source code is available on the Github. Everything is public. TIA
     
  5. so, I've got no idea on how to use `PlaneMapToVRAM` (or `copyTilemap` in Sonic 1).
    apparently the tilemap shows up correctly in Blastem's plane map debugger, but nothing actually appears on screen. what am I doing wrong here?
    my code: (sidenote: I don't know how to make a code-block appear, so I've changed the font to a monospaced one to approximate it.)

    move.b #bgm_Stop,d0
    bsr.w PlaySound_Special ; stop music
    bsr.w PaletteFadeOut

    clearRAM v_objspace
    clearRAM v_misc_variables
    clearRAM v_levelvariables
    clearRAM v_timingandscreenvariables

    disable_ints
    move.w (v_vdp_buffer1).w,d0
    andi.b #$BF,d0
    move.w d0,(vdp_control_port).l

    lea (vdp_control_port).l,a6
    move.w #$8B03,(a6) ; line scroll mode
    move.w #$8200+(vram_fg>>10),(a6) ; set foreground nametable address
    move.w #$8400+(vram_bg>>13),(a6) ; set background nametable address

    bsr.w ClearScreen
    lea (ArtNem_DemoEndingScreen).l, a0
    locVRAM 0 ; ArtTile_Credits_Font*tile_size
    bsr.w NemDec
    lea (v_256x256&$FFFFFF).l,a1
    lea (Eni_DemoEndingScreen).l,a0 ; load title screen mappings
    move.w #0,d0
    bsr.w EniDec

    disable_ints

    copyTilemap v_256x256&$FFFFFF,vram_bg,34,22

    moveq #palid_Title, d0
    bsr.w PalLoad_Fade
    bsr.w PaletteFadeIn

    -
    nop ; temporary infinite loop
    bra.s -

    additionally, I also made a screenshot of this, attached here.
    upload_2024-9-2_10-47-42.png
     
  6. Kilo

    Kilo

    Deathly afraid of the YM2612 Tech Member
    1,065
    1,050
    93
    Canada
    Sonic 1 Source Code Recreation + Source Code Wiki Page
    Code (Text):
    1. move.w (v_vdp_buffer1).w,d0
    2. andi.b #$BF,d0
    3. move.w d0,(vdp_control_port).l
    This is masking out the bit to enable the VDP to display anything to the screen.
    v_vdp_buffer1 stores a copy of VDP register mode 1. It's a bitfield laid out like this
    |0|E|V|D|H|1|0|0
    E = Enable display
    V = Enable V-Blank
    D = Enable DMA
    H = Screen height (Only usable in PAL)
    $BF in binary is 10111111, and the AND instruction means that only the bits that are 1 will be left alone, while the bits that are 0 will be turned to 0, and that clearly is being applied to the enable display flag.

    So to fix this, after
    Code (Text):
    1. copyTilemap v_256x256&$FFFFFF,vram_bg,34,22
    Place
    Code (Text):
    1.         move.w    #$8100|%01110100,(vdp_control_port).l
    I'd also probably recommend moving that 2nd disable_ints to after the branch to PaletteFadeIn, not sure if that effects anything, but base Sonic 1 usually does that after a palette fade call rather than after decompressing mapping data.
     
  7. hah, I knew it was something like this! thank you~
    also for your suggestion for disable_ints, that was a failed safety measure in case that was what I was missing.
     
  8. penBorefield

    penBorefield

    Living in my best life Member
    241
    46
    28
    Basement
    Patching things up
    Quick question. What's the difference between two disassembly types (AS vs. ASM68K)?
     
  9. Kilo

    Kilo

    Deathly afraid of the YM2612 Tech Member
    1,065
    1,050
    93
    Canada
    Sonic 1 Source Code Recreation + Source Code Wiki Page
    Those aren't disassembly types, those are assemblers that build the code.
    ASM68k assembles only 68k code. It's fast and reliable, but old and closed source.
    AS assembles for a multitude of CPUs, which includes the 68k and Z80. However it's slow, taking up to a minute to assemble games from the early 90's, seemingly unstable, often crapping itself for seemingly no reason and then reassembling just fine, but open source.
    We've been trying to look for a solution to the problem of ASM68k only being limited to the 68k, and AS being garbage for like 5 years now. Some suggest ASMX, some VASM. Everything has pros and cons. If you don't intend to mess around with the sound driver though, you'd probably be fine with ASM68k.

    Oh also ASM68k's local label prefix by default is @ while AS' is . and I don't care what you say @ is a far better prefix, it's clear and unique.
     
    Last edited: Sep 2, 2024
  10. MainMemory

    MainMemory

    Kate the Wolf Tech Member
    4,796
    383
    63
    SonLVL
    Technically AS' local label prefix is $$, the . is a separate feature called composed temporary symbols, that allow you to refer to them from outside their scope by including the name of the previous full label before the dot.
     
    • Informative Informative x 2
    • Like Like x 1
    • Agree Agree x 1
    • List
  11. penBorefield

    penBorefield

    Living in my best life Member
    241
    46
    28
    Basement
    Patching things up
    I got a fatal error from BlastEm. It says "Unable to create SDL window. Window is too large". I set the video width to 320 then reverted to 640. Now it's stuck. What should I do?
     
  12. Kilo

    Kilo

    Deathly afraid of the YM2612 Tech Member
    1,065
    1,050
    93
    Canada
    Sonic 1 Source Code Recreation + Source Code Wiki Page
    So when Blastem's width is set to 320p the width field does not render properly, and you've accidentally set your width to 320640, happens all the time, major pain in the ass. To fix this go to C:/Users/name/AppData/Local/blastem, open blastem.cfg and at the very end will be the width setting, just put it to 640, save, and you're good to go.
     
  13. XPointZPoint

    XPointZPoint

    That's no good! Member
    Recently, I've been working on a hack that utilizes the Mega CD if attached. Here's how I plan for it to work: If Mega CD is attached, in the main menu, the game will open up the disc tray, allowing for audio playback through the CD in different levels. Think of it similar to how Sonic: Winter Adventures handles it, except it's on a Menu screen instead of a title screen. I've been looking around through numerous places to look for something that can send a request via the SubCPU or anything similar to open the tray (if possible) and then when the disc is in, run the contents off of the disc. Any places I should look? It would be greatly appreciated.
     
  14. OrionNavattan

    OrionNavattan

    Tech Member
    175
    177
    43
    Oregon
    Not going to get into the exact nitty-gritty of how it would be implemented, but you will need to have some means of being able to send commands to the sub CPU from the main CPU (I will recommend looking at Sonic CD's command handler system to get an idea of how to do this, as poor of an implementation as that might be).

    Nevertheless, what you are looking to do is 100% possible. On the SubCPU side, you'd want to use the DRVOPEN BIOS call, which opens the disc tray if motorized (Model 1 + Pioneer LaserActive) or waits for the user to open the door. Once the user closes the tray or signals that they have done so, use DRVINIT to read and load the disc's TOC. I'm unsure if your plan is to simply play through the disc, or assign specific disc tracks to specific levels, but if it's the latter, I would verify that the disc has enough tracks by running the CDBSTAT BIOS call and checking what number is written to the BIOS status table at $5E91:

    Code (ASM):
    1.         move.w    #CDBSTAT,d0
    2.         jsr    (_CDBIOS).w  
    3.    
    4.         cmpi.b    #countof_tracks,($5E91).w    ; does disc have enough tracks for CD playback?
    5.         bcs.s    .nodisc                ; branch if not
     
    Last edited: Oct 2, 2024
    • Like Like x 2
    • Informative Informative x 2
    • List
  15. kyasarintsu

    kyasarintsu

    Member
    381
    149
    43
    Why is it so hard to go in a straight line sometimes in Sonic Adventure? Whether I'm just letting Sonic roll down a slope or I'm actually holding forwards on the analog stick, it feels like there are certain areas (most notably in Windy Valley's floating pathways) where Sonic just drifts into walls.
     
  16. XPointZPoint

    XPointZPoint

    That's no good! Member
    I think it has something to do with the camera and the way it was programmed. Depending on the direction your camera is facing in-game, Sonic will steer that way very slightly. This is very noticible on auto-parts if you're not careful enough.
     
  17. Chimes

    Chimes

    The One SSG-EG Maniac Member
    935
    644
    93
    The Dreamcast stick has a whole host of weird DC shit.
     
  18. XPointZPoint

    XPointZPoint

    That's no good! Member
    Would there be a variable I could set that can teleport Sonic? (a.k.a set Sonic's position via ASM). I'm currently working on a primitive Sonic 1 level creator hack based off of the debug mode and inspired by Hedgehog Abuse Simulator and once you reach the end of your level, I want to add a monitor that teleports you to the start of the stage again.

    Edit: Added context.
     
  19. can't you just do that? you know... overwrite his x_ and y_pos?
    or is that not feasible?
    I remember doing sth like this, but it was in Sonic 2 engine.
     
    • Agree Agree x 2
    • Like Like x 1
    • List
  20. RetroKoH

    RetroKoH

    Member
    1,728
    107
    43
    S1Fixed: A successor to ReadySonic
    You can just reset his X/Y positions directly and run a routine to set the camera positions and screen boundaries accordingly. The last one is important because if the lower boundary is above where you send Sonic to, he will die instantly and it can make a mess of things.

    There might be a little more nuance to it than that (you may wanna reset Sonic similarly to how he resets upon exiting Debug Mode).