Basic Questions and Answers Thread

Discussion in 'Discussion & Q&A' started by Malevolence, Jul 7, 2009.

  1. MainMemory

    MainMemory Well-Known Member Member

    Joined:
    Mar 29, 2011
    Messages:
    922
    If everything appears fine in SonLVL, then there's probably not a problem with the files, but with something in the game. It could be related to how GHZ has two art files, you might want to set up the title screen to use its own art and have GHZ's tiles in one file.
     
    Last edited by a moderator: Mar 12, 2017
  2. Arsen

    Arsen Active Member In Limbo

    Joined:
    May 16, 2014
    Messages:
    39
    Hello everyone.

    I started to learn Assembly by reading Sonic Retro's basic guide. I really like that guide. But I'm really want to learn more. Question is how do you guys making loops and arrays, because I need them in the future. Also I want other guide that teaches assembly completely.

    Thank you so much for help.
     
  3. AURORA☆FIELDS

    AURORA☆FIELDS so uh yes Exiled

    Joined:
    Oct 7, 2011
    Messages:
    759
    This is not really a complete guide, but it covers a lot of the basics.

    I do not know what you mean with loops per se, it is too vague term really. Arrays however, are whats interesting. Unlike in modern languages, you do not define arrays in any meaningful way to the processor or assembler. Instead, arrays are commonly just data that comes right after each other. Here, let me give you an example. Lets pretend that $FFFF8002 contains the array offset you wish to use. Here is an example of an array access that would return pointers to routines, for example to be used to call some code depending on what value is inside $FFFF8002.

    Code:
    GrabRoutine:
        moveq    #0,d0            ; ensure d0 is clear! This is important, because we get a reference to the offset as byte, but array access uses a word!
        move.b    $FFFF8002.w,d0        ; get array index to d0 (range of 0-255, use word if more entries are necessary)
        add.w    d0,d0            ; doube offset (using word, so we can take full effect of the index byte)
        add.w    d0,d0            ; quadruple offset (since in this imaginary array, each entry is 4 bytes long, we therefore must align it properly. However, if you are clever, you can avoid this, at the cost of having 4x less possible entries!)
    
        lea    ArrayOfRoutines(pc),a1    ; grab the array we want to access to a1
        move.l    (a1,d0.w),a0        ; now, grab the desired entry from the array at index denoted by d0. a0 will now contain the address of the routine in 68000 memory (as opposed to its pointer inside the array, if you wish to get that, use lea instead of move.l!) (d0 contains the total offset, rather than index at this point!)
        rts                ; now a0 contains one of the routines we selected
    
    ArrayOfRoutines:        ; <- this is where the array starts in 68000 memory
        dc.l Routine_00        ; pointer routine 00 (this is returned when $FFFF8002 = 0)
        dc.l Routine_01        ; pointer routine 01 (this is returned when $FFFF8002 = 1)
        dc.l Routine_02        ; pointer routine 02 (this is returned when $FFFF8002 = 2)
        ; we could continue this up to Routine_FF, which would get returned when $FFFF8002 = $FF (AKA 255)
    
    Routine_00:            : final address of routine 00
        rts            ; insert stuff here?
    
    Routine_01:            : final address of routine 01
        nop            ; insert stuff here?
    
    Routine_02:            : final address of routine 02
        illegal            ; insert stuff here?
    I will also give another example of a routine that would use the value inside $FFFF8002, to store another value inside $FFFF8004, this time it would read a byte, and store a word.

    Code:
    GrabValue:
        moveq    #0,d0            ; ensure d0 is clear! This is important, because we get a reference to the offset as byte, but array access uses a word!
        move.b    $FFFF8002.w,d0        ; get array index to d0 (range of 0-255, use word if more entries are necessary)
        add.w    d0,d0            ; doube offset (using word, so we can take full effect of the index byte) (since in this imaginary array, each entry is 2 bytes long, we therefore must align it properly. However, if you are clever, you can avoid this, at the cost of having 2x less possible entries!)
    
        lea    ArrayOfNums(pc),a1    ; grab the array we want to access to a1
        move.w    (a1,d0.w),$FFFF8004.w    ; now, grab the desired entry from the array at index denoted by d0. $FFFF8004 will now contain the value (d0 contains the total offset, rather than index at this point!)
        rts                ; now $FFFF8004 contains the value we wanted
    
    ArrayOfNums:            ; <- this is where the array starts in 68000 memory
        dc.w $100        ; example value (this is returned when $FFFF8002 = 0)
        dc.w $300        ; example value (this is returned when $FFFF8002 = 1)
        dc.w $200        ; example value (this is returned when $FFFF8002 = 2)
        dc.w 0            ; example value (this is returned when $FFFF8002 = 3)
        dc.w $180        ; example value (this is returned when $FFFF8002 = 4)
        dc.w $120        ; example value (this is returned when $FFFF8002 = 5)
    Now, I just want to give a large emphasis on the fact that this simplified! You could rewrite these routines to be much more optimal, but I opted to make them simpler and less optimal to allow me to give you more in-depth understanding of what is actually going on behind the scenes. And of course, these are just 2 examples of how arrays may be accessed, you will most likely write custom code for practically any array access, as many times you are doing something different with the array anyway. The way you write arrays depends on the use-case in the end.
     
  4. Arsen

    Arsen Active Member In Limbo

    Joined:
    May 16, 2014
    Messages:
    39
    Very helpful information, thank you so much for all your support. Sorry that I being unclear. Loops more like "Repeaters" that repeats N times. For example JavaScript's "For" loop or "while" loop.
     
  5. AURORA☆FIELDS

    AURORA☆FIELDS so uh yes Exiled

    Joined:
    Oct 7, 2011
    Messages:
    759
    You can do for loops with a simple dbf loop. For example this;
    Code:
        moveq    #4-1,d0        ; loop 4 times (since dbf tests dn before decrementing, it will not branch only when d0 is -1!)
    
    For:
        nop            ; insert code here
        dbf    d0,For        ; loop for n times
        ; loop is over
    And while loops are more complex, they depend on what you actually want to compare, but the basic idea is to use a case, where if it is satistified (or not!), jump backwards. Else keep on going
    Code:
    While:
        nop            ; some code here
        tst.b    $FFFF8002.w    ; check this flag
        bpl.s    While        ; if the flag is still positive, keep looping
        ; while is over
    Now, you can actually combine both for and while (ever used a while, which terminates conditionally, or when a counter reaches 0?). You can do this with various instructions, much like branches (bcc), or set (scc). Commonly denoted as dbcc. A good example of this, is the function that finds a few object in S3K, where they used dbeq, because object slots are limited, but may also be empty or full.
     
    ProjectFM and Arsen like this.
  6. MotoBadnik

    MotoBadnik Newcomer Trialist

    Joined:
    Mar 11, 2017
    Messages:
    6
    Location:
    Sunset Hill
    I looked again to the rom, and the points now overlap the Invencibility stars. How can I fix it?
     
  7. EMK-20218

    EMK-20218 The Fuss Maker Exiled

    Joined:
    Aug 8, 2008
    Messages:
    1,067
    Location:
    Jardim Capelinha, São Paulo
    Free up VRAM by removing the points or make the points to be loaded in a different area of the VRAM. There's also a guide somewhere here in the forum which teaches how to optimize the invincibility and the shield.
     
  8. MotoBadnik

    MotoBadnik Newcomer Trialist

    Joined:
    Mar 11, 2017
    Messages:
    6
    Location:
    Sunset Hill
    These are the effects after following the guide
     
    EMK-20218 likes this.
  9. Clownacy

    Clownacy Retired Staff lolololo Member

    Joined:
    Aug 15, 2014
    Messages:
    1,016
    Remember when I said that guide was broken? Well, one bug in the guide prevents the stars from loading all of its art.
     
    Last edited: Mar 14, 2017
  10. AdelTheQadi

    AdelTheQadi Leader of the anti body pillow movement Member

    Joined:
    Jan 19, 2013
    Messages:
    31
    Location:
    Heck off
    Perfect now; thanks a lot.
     
  11. Ashuro

    Ashuro Anti-Cosmic Metal Of Death Member

    Joined:
    Sep 27, 2014
    Messages:
    550
    Location:
    France
    Hello friends, i want to make some cut-scenes in my hack, but i don't know how to put a "timer", to make Sonic waits a few seconds before moving or doing an action. I searched in the routine for the ending sequence, but it doesn't work for me.
    I know it is by calculating frames, like the countdown of Super-Sonic.
    Can anyone enlighten me? It would make me move forward considerably for many things. Thank you very much.
     
  12. ProjectFM

    ProjectFM Optimistic and self-dependent Member

    Joined:
    Oct 4, 2014
    Messages:
    912
    Location:
    Orono, Maine
    It's pretty simple and a lot of objects do it (the label usually includes "_wait"). You just set a ram address to a number at the end of a piece of code, make the code subtract 1 from that ram address every time it goes through it, and branch to what you want the object to do next when the ram address reaches 0.
     
    Ashuro likes this.
  13. Ashuro

    Ashuro Anti-Cosmic Metal Of Death Member

    Joined:
    Sep 27, 2014
    Messages:
    550
    Location:
    France
    Thank you bro'.
     
  14. MarkeyJester

    MarkeyJester ♡ ! Member

    Joined:
    Jun 27, 2009
    Messages:
    2,867
    Looks like it's time for me to ask a question for a change =)

    This code repeats 6 times over:
    Code:
    Code:
    		ldi
    		add	a,b
    		adc	hl,sp
    		ldi
    		add	a,b
    		adc	hl,sp
    		ldi
    		add	a,b
    		adc	hl,sp
    		ldi
    		add	a,b
    		adc	hl,sp
    		exx
    		ex	af,af'
    		ld	a,(de)
    		add	a,(hl)
    		add	a,080h
    		ld	(bc),a
    		inc	e
    		inc	l
    		ex	af,af'
    		exx
    I have optimised this to be as fast as possible, this is the quickest solution I have so far, I'm not sure if it's possible to speed it up any further, I've been at this for about a week now. While I have ideas, they involve editing the data in such a way that would cause inconveniences too far to be acceptable.

    Any ideas anyone?
     
    Last edited: Mar 18, 2017
  15. Arsen

    Arsen Active Member In Limbo

    Joined:
    May 16, 2014
    Messages:
    39
    I'm going to look at the problem as Web Developer. Because we also using loops. 64K and JS/Java/PHP have the same function (Also because it's been a while but nobody answered this question, so I decided to post my opinions about this situation and please don't mind that I'm Trialist. ). Anyways as Web Developer, a short answer is NO and I will tell you why. For us "For" and "While" are the fastest loops and you can't boost more, unfortunately. Proof that those loops are faster is making an unlimited loop on Browser. So what is going to happen if I run that? That's right! It crashes because the browser can't handle that speed. The only way to speed up is removing unnecessary line code (Which you did already because you tried to make fast as possible and you cleaned unnecessary code). Loop is getting slow is because emulator can't handle that. He needs some time to finish that action. It's like give a human giant rock and say "run" (Sorry if it sounds rude).

    I got the problem with speeding up as well. I trying to deal up with a HUGE database (MySQL) and it took ages to read. So what I did to make even faster? Well instead of getting almost all information from the database, I took information's that I need to (like in this case I just need username and messages). Also what I did, is cut every database with pages (like in forums or Google).

    So what's my solution? I recommend to add loading screen instead of freezing entire game. It going to look good and not going to ruin an entire game.

    Thanks for reading. Maybe I helped you a little bit. It's my opinions anyway.
     
    Last edited by a moderator: Mar 21, 2017
    MarkeyJester likes this.
  16. AURORA☆FIELDS

    AURORA☆FIELDS so uh yes Exiled

    Joined:
    Oct 7, 2011
    Messages:
    759
    Like this?

    [​IMG]
     
    Devon and TheStoneBanana like this.
  17. Arsen

    Arsen Active Member In Limbo

    Joined:
    May 16, 2014
    Messages:
    39
    It depends on what it's actually running. Like if it's level, you can just add Splash Screen. If its object that needs to load longer, you can just add loading circle bottom left (or another place), so we don't need to move to Splash Screen again.

    EDIT: So it will make players sense, that something is loading, without ruining gameplay (not pausing).
     
    Last edited by a moderator: Mar 20, 2017
  18. Clownacy

    Clownacy Retired Staff lolololo Member

    Joined:
    Aug 15, 2014
    Messages:
    1,016
    If the fact that the code is Z80 assembly is anything to go by, then that's part of a DAC driver. It streams (and mixes) PCM data in real-time, meaning having a loading screen would totally defeat the point.

    Also, don't end your posts with your username. I've brought this up before.
     
    Arsen likes this.
  19. MarkeyJester

    MarkeyJester ♡ ! Member

    Joined:
    Jun 27, 2009
    Messages:
    2,867
    I should have shared the code with a great deal of context, it might have saved you time writing out that wonderful post (it really is great advice to anyone programming in general), for this, I am truly sorry.

    The code you see is assembly code for the Zilog Z80 sub-CPU, this CPU is usually used for audio control for the Mega Drive, while the Motorola 68k main-CPU is used for the game logic. So if the Z80 was caught in a massive loop, this would not hinder the game as the 68k is still running the game logic, so as you can tell, this is not the problem. As you've probably guessed from Natsumi's/Clownacy's response, this code is dealing with PCM sample playback. I am going to use and describe the SEGA Mega CD's PCM playback hardware, and then explain the Mega Drive's PCM playback hardware, so you can better understand why the PCM sample data cannot be loaded before the game starts.

    The SEGA Mega CD has an audio chip specifically designed to playback PCM samples, it has several channels, and a dedicated RAM space to play back the audio from, that is to say that each channel can collect and playback the samples from that sample RAM space so long as you've loaded it before hand, and set the channel's pointer, pitch, volume and flags (I know all this, because I've written a sound driver for it once many years back). Now, the Mega Drive does NOT have a specific and dedicated chip to playback PCM samples, instead, it has a synthesiser chip called the YM2612 for playing back FM operator based sounds.

    It does however have a port for what is known as the DAC (Digital to Audio Converter, or as the manual calls it, Digitized Audio Controller), this holds an 8-bit value in which it can convert to the electrical signal and flush out, this is a constant thing however, the YM2612 will constantly flush out the audio of this port, if 80 is inside, it will constantly flush out the unsigned electrical audio signal of 80, over and over again. This means, the YM2612 does NOT control the playback of the PCM data at all, only the flush of a single byte. No pointers, no volume control, no pitch control, nothing, just a flush.

    So, your job is to constantly change the value in the YM2612's DAC port, so the electrical signal changes, causing the position of the speaker to change and produce a wave. So you MUST do this at a frequent rate, constantly, and balanced. All of the sample data is controlled by the Z80 in this case, this includes the pointers, pitch, volume, and the writing to the DAC port (or as Clownacy rightly said, "streamed"). The samples cannot be loaded before hand and played back, because there's nowhere to load them to, and the Z80 chip IS the controller, not the YM2612.

    The code in question is doing the following:
    Code:
    Code:
    		ldi			; load byte (hl) to (de) (from 68k window to Z80 buffer)
    		add	a,b		; add pitch/pointer advance fraction/dividend
    		adc	hl,sp		; add pitch/pointer advance quotient (move hl forwards), and add the carry of the fraction/dividend
    		ldi			; '' (same as above)
    		add	a,b		; ''
    		adc	hl,sp		; ''
    		ldi			; ''
    		add	a,b		; ''
    		adc	hl,sp		; ''
    		ldi			; ''
    		add	a,b		; ''
    		adc	hl,sp		; ''
    		exx			; swap registers (bc, de and hl get stored away and replaced with another set)
    		ex	af,af'		; swap af (store away, exx doesn't do af)
    		ld	a,(de)		; load byte from (de) (from sample 1 buffer)
    		add	a,(hl)		; add byte from (hl) (from sample 2 buffer, mix together)
    		add	a,080h		; convert to unsigned (the YM2612 DAC port converts from unsigned 8-bit)
    		ld	(bc),a		; save the byte to (bc) (the YM2612 data port 4001)
    		inc	e		; move e forwards (to move de pointer forwards)
    		inc	l		; move l forwards (to move hl pointer forwards)
    		ex	af,af'		; swap af (restore, exx doesn't do af)
    		exx			; swap registers (bc, de and hl get restored)
    There is technically no actual loop, it is a huge line of instructions repeating, this is to help reduce time on a loop instruction, and to help free up another register which can be used to further speed up the routine in question.

    I really do appreciate your help nevertheless, at least you're trying, that's more than I can say for most. So thank you anyways. I do apologise once again, it was my fault for not providing contaxt.
     
    Pacca, DanielHall, Arsen and 2 others like this.
  20. Arsen

    Arsen Active Member In Limbo

    Joined:
    May 16, 2014
    Messages:
    39
    So that's how it is. That's a shame tho, that you can't load before the game starts. But on modern programs for game developing you can actually do that. I will give you an example Sonic generations loading screen. You probability know why.

    Anyways that become Prospect for 1 week I got more questions about sonic hacking.

    The first thing I want to know is how collisions work. For example, I do really want my GHZ boss' ball fall to the ground, unlike in original it just disappearing, or collision between bullets and ground.
    Secondly, is working with animations. A question is how can you move an object for example left to right or up to down with timeout (for example HUD movement before and after act) and does addresses are same for x y position on every object?.

    Thanks for help!