Oh...that makes sense. Now I feel dumb for not realizing it was a problem with the shield. Thank you Kilo.
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: Spoiler: Details and Code 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): ; give 'random' item (Fix randomizer to include S3K Monitors) jsr (RandomNumber).w ; call for random number andi.w #$11,d0 ; random items in two pools (upper nybble used later) move.w (v_framecount).w,d1 ; use the timer to determine which item andi.w #1,d1 ; get 0 or 1 (then decide whether to increment) addq.w #1,d1 ; add 1 to prevent getting the static monitor btst #4,d0 ; should we increment d1 a second time? beq.s .noInc ; if not, branch addq.w #1,d1 ; increment again ; d1 = (1, 2, or 3) .noInc: btst #0,d0 ; which item group? bne.s .group0 addq.w #3,d1 .group0: 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?
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): jsr RandomNumber ; Get random number ; Add further randomness modification here before continuing andi.l #$FFFF,d0 ; Make sure division will always work divu.w #(MAX_NUM-MIN_NUM)+1,d0 ; Divide by adjusted maximum number swap d0 ; Get remainder of division 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): .GetRandom jsr RandomNumber ; Get random number ; Add further randomness modification here before continuing andi.w #MASK_VALUE,d0 ; Mask it cmpi.w #MAX_NUM-MIN_NUM,d0 ; Is it in range? bhi.s .GetRandom ; If not, branch addi.w #MIN_NUM,d0 ; Add minimum number or Code (ASM): .GetRandom jsr RandomNumber ; Get random number ; Add further randomness modification here before continuing andi.w #MASK_VALUE,d0 ; Mask it cmpi.w #MAX_NUM-MIN_NUM,d0 ; Is it in range? bls.s .GotRandom ; If so, branch ; Modify number here .GotRandom: addi.w #MIN_NUM,d0 ; Add minimum number
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
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.
Code (Text): move.w (v_vdp_buffer1).w,d0 andi.b #$BF,d0 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): copyTilemap v_256x256&$FFFFFF,vram_bg,34,22 Place Code (Text): 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.
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.
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.
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.
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?
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.