Page 1 of 1

A better method for a "one shot" in Assembly?

Posted: Fri Jul 31, 2020 8:08 pm
by NeverCameBack
Sometimes I want a piece of code to run just once. What I do right now is:

Screen1music:
LDA S1musicstart
CMP #$01
BNE Screen1musicDN
.
.
.
(Other code here)
.
.
.
LDA #$00
STA S1musicstart
Screen1musicDN:

So I have to create a variable called "S1musicstart". Then set it to 1 at some point before it's called. When it comes time to hit the "Screen1music:" sub-routine, it runs once then sets S1musicstart to 0. Since nothing else sets it back to one, it'll never run the contents of this sub-routine again.

Anyone know if there is a better method to do this? I don't have much of a problem with it, but I'm designating a variable each time (8 bits) and I'm only using it like a bool. In PLC programming this would be called a "one-shot", where the "one shot" instruction only "shoots" once, running the code after it for one cycle only, when it detects a negative to positive edge transition.

Re: A better method for a "one shot" in Assembly?

Posted: Fri Jul 31, 2020 8:23 pm
by tokumaru
Most of the time, things that are supposed to happen only once are naturally "hidden" under the tests for the conditions that lead to them. If there's no condition inside your main loop that naturally leads to the need to start the music for screen 1, maybe you should be making this call *before* the main loop, in the initialization phase, which by definition runs only once.

Re: A better method for a "one shot" in Assembly?

Posted: Fri Jul 31, 2020 8:54 pm
by rox_midge
You could use a trampoline - a three-byte segment of RAM that has the machine code for "JMP $xxxx". On boot, you initialize the trampoline with your target address, which is just a normal subroutine that you use JSR to return from. When you want to not call the function anymore, you change the first byte of your trampoline from $4C (JMP) to $60 (RTS).

Re: A better method for a "one shot" in Assembly?

Posted: Sat Aug 01, 2020 11:08 am
by tepples
One common pattern is to track the ID of the playing song, and you start music only if the selected song does not match the playing song. During the game's init code run at reset, you set the playing song's ID to an invalid number so that it plays the first time it is requested.

Re: A better method for a "one shot" in Assembly?

Posted: Sat Aug 01, 2020 11:29 am
by tokumaru
tepples wrote:
Sat Aug 01, 2020 11:08 am
One common pattern is to track the ID of the playing song, and you start music only if the selected song does not match the playing song. During the game's init code run at reset, you set the playing song's ID to an invalid number so that it plays the first time it is requested.
I use the same pattern for a lot of things... Fading the screen, for example: I have a global variable indicating the current brightness of the screen, and another indicating the target brightness. Every frame I compare the two, and if they don't match, I move the brightness 1 step toward the target and schedule palette updates when necessary. Then, every time I need to fade in or out, all I have to do is change this one variable (the target brightness) - no need to call subroutines or coordinate complex state changes manually.

Re: A better method for a "one shot" in Assembly?

Posted: Sat Aug 01, 2020 11:24 pm
by Oziphantom
As with many things in Asm it comes down to the exact needs.

you either
find a spot that will already only run once and add the jsr there.

If you have a lot of "I need to run somethings every now and then" you can make a stack of functions
then you check the stack each frame, and call each function on it, until you read -1 items on the stack. This way you can just pop a think onto the stack for it to be run once next frame
Or you can split the hardware stack and push "return address" on, then move the stack and just let the RTS chains do the work. You make the highest item on this stack a "stack finished restore and return".

if you find your self doing I want to call this in N frames a lot, I make what I call "4 byte timers"
counter
resetValue
Ptr_Lo
Ptr_Hi

and then each frame I scan a pool of counters to see if their counter has reached 0 and if so I call the PTR. I then also typically make the reset value 7 bits and use the 8th as a "one shot/reload". If reload the resetValue is copied to counter and it lives, else I clear the ptr and mark it as free.

But the "set flag" and "test flag" is also a common technique.