Adding a delay to loop in BASIC

bbc micro/electron/atom/risc os coding queries and routines
Post Reply
Adam James
Posts: 196
Joined: Tue May 26, 2020 2:32 pm
Contact:

Adding a delay to loop in BASIC

Post by Adam James » Thu Jul 23, 2020 8:14 pm

I want to have a game get gradually faster, so I tried something like:

Code: Select all

10 D%=100
20 REPEAT
30 FOR I%=0 TO D%:NEXT
(game logic)
90 UNTIL0
It works fine, however I'd really like it to be as fast as possible when D% gets down to zero, having just spent most of the morning optimising my main loop for speed!

But of course the FOR statement is taking processing time even when D% is zero, so my main loop is only getting up to about 97% of possible speed.

If I instead write

Code: Select all

30 IF D%>0 THEN FOR I%=0 TO D%:NEXT
It is a bit faster but I still begrudge it being there when D% is zero!

Is there a cunning way to do this sort of thing so that when maximum speed is required, a variable delay loop takes minimum time?

Soruk
Posts: 800
Joined: Mon Jul 09, 2018 11:31 am
Location: Basingstoke, Hampshire
Contact:

Re: Adding a delay to loop in BASIC

Post by Soruk » Thu Jul 23, 2020 8:32 pm

Adam James wrote:
Thu Jul 23, 2020 8:14 pm

Code: Select all

30 IF D%>0 THEN FOR I%=0 TO D%:NEXT
It is a bit faster but I still begrudge it being there when D% is zero!
This might be a tiny bit faster - and removed the spaces as that does help for speed...

Code: Select all

30IFD%THENFORI%=0TOD%:NEXT
Basically (no pun intended), I've removed the >0 check, so it's true if D% is non-zero. it just means that if D% goes negative the FOR loop will do one iteration, but the IF test should be faster.
Matrix Brandy BASIC VI (work in progress)

Adam James
Posts: 196
Joined: Tue May 26, 2020 2:32 pm
Contact:

Re: Adding a delay to loop in BASIC

Post by Adam James » Thu Jul 23, 2020 8:45 pm

Soruk wrote:
Thu Jul 23, 2020 8:32 pm
Adam James wrote:
Thu Jul 23, 2020 8:14 pm

Code: Select all

30 IF D%>0 THEN FOR I%=0 TO D%:NEXT
It is a bit faster but I still begrudge it being there when D% is zero!
This might be a tiny bit faster - and removed the spaces as that does help for speed...

Code: Select all

30IFD%THENFORI%=0TOD%:NEXT
Basically (no pun intended), I've removed the >0 check, so it's true if D% is non-zero. it just means that if D% goes negative the FOR loop will do one iteration, but the IF test should be faster.
That is faster, thanks.

I've gone from almost 98% full speed to almost 99% full speed:)

julie_m
Posts: 236
Joined: Wed Jul 24, 2019 9:53 pm
Location: Derby, UK
Contact:

Re: Adding a delay to loop in BASIC

Post by julie_m » Thu Jul 23, 2020 11:04 pm

Double up the REPEAT statement to create two nested loops, and have the delay loop between two UNTIL statements: UNTIL D% followed by FORI%=0TOD%:NEXT:UNTIL 0.

That will have the inner loop execute exactly once as long as D% is bigger than 0; but once D% reaches zero, the inner loop will begin repeating forever without ever dropping back to the outer loop.

Whether the inner REPEAT ..... UNTIL has a worse speed penalty than using IF ..... THEN around the FOR ..... NEXT loop, or even just letting BASIC jump straight to the statement after the NEXT associated with a redundant FOR, is a matter probably best settled by experimentation!

User avatar
jgharston
Posts: 4125
Joined: Thu Sep 24, 2009 12:22 pm
Location: Whitby/Sheffield
Contact:

Re: Adding a delay to loop in BASIC

Post by jgharston » Thu Jul 23, 2020 11:14 pm

Adam James wrote:
Thu Jul 23, 2020 8:14 pm
I want to have a game get gradually faster, so I tried something like:

Code: Select all

10 D%=100
20 REPEAT
30 FOR I%=0 TO D%:NEXT
(game logic)
90 UNTIL0
That will take different amounts of time on different machines, in different weather, during different phases of the moon, when a disk is still rotating, when a network packet flies past, when a cosmic partical hits the serial ULA, and during any amount of other events. What you need is:

T%=TIME+delay:REPEAT UNTIL TIME>T%
It works fine, however I'd really like it to be as fast as possible when D% gets down to zero, having just spent most of the morning optimising my main loop for speed!
IF delay THEN T%=TIME+delay:REPEAT UNTIL TIME>T%
compacted to:
IFD%:T%=TIME+D%:REP.UN.TIME>T%

Code: Select all

$ bbcbasic
PDP11 BBC BASIC IV Version 0.32
(C) Copyright J.G.Harston 1989,2005-2020
>_

Adam James
Posts: 196
Joined: Tue May 26, 2020 2:32 pm
Contact:

Re: Adding a delay to loop in BASIC

Post by Adam James » Fri Jul 24, 2020 12:40 pm

julie_m wrote:
Thu Jul 23, 2020 11:04 pm
Double up the REPEAT statement to create two nested loops, and have the delay loop between two UNTIL statements: UNTIL D% followed by FORI%=0TOD%:NEXT:UNTIL 0.

That will have the inner loop execute exactly once as long as D% is bigger than 0; but once D% reaches zero, the inner loop will begin repeating forever without ever dropping back to the outer loop.

Whether the inner REPEAT ..... UNTIL has a worse speed penalty than using IF ..... THEN around the FOR ..... NEXT loop, or even just letting BASIC jump straight to the statement after the NEXT associated with a redundant FOR, is a matter probably best settled by experimentation!
The seems cunning, but should that be UNTIL NOT D%? If not then I don't think I'm understanding this. Either way I tried it (with a NOT) and it was a bit slower than the IF approach earlier.
jgharston wrote:
Thu Jul 23, 2020 11:14 pm
That will take different amounts of time on different machines, in different weather, during different phases of the moon, when a disk is still rotating, when a network packet flies past, when a cosmic partical hits the serial ULA, and during any amount of other events. ...
Gosh. I suspect you and me both need to brace ourselves for the thread I'm about to start on the fact I've just let a GOTO slip into this program:)
jgharston wrote:
Thu Jul 23, 2020 11:14 pm
...What you need is:

IF delay THEN T%=TIME+delay:REPEAT UNTIL TIME>T%
compacted to:
IFD%:T%=TIME+D%:REP.UN.TIME>T%
Top tip, thank you. If I'm going to let 'good practice' slip into this program, I might as well do it on this line:)

User avatar
Richard Russell
Posts: 1668
Joined: Sun Feb 27, 2011 10:35 am
Location: Downham Market, Norfolk
Contact:

Re: Adding a delay to loop in BASIC

Post by Richard Russell » Fri Jul 24, 2020 1:28 pm

jgharston wrote:
Thu Jul 23, 2020 11:14 pm
compacted to:
IFD%:T%=TIME+D%:REP.UN.TIME>T%
If you're really trying to save every byte, the colon after the first D% is superfluous (U. is also an acceptable abbreviation for UNTIL, but that doesn't make any difference to the resulting tokenised program):

Code: Select all

      IFD%T%=TI.+D%:REP.U.TI.>T%

julie_m
Posts: 236
Joined: Wed Jul 24, 2019 9:53 pm
Location: Derby, UK
Contact:

Re: Adding a delay to loop in BASIC

Post by julie_m » Fri Jul 24, 2020 2:26 pm

Adam James wrote:
Fri Jul 24, 2020 12:40 pm
The seems cunning, but should that be UNTIL NOT D%? If not then I don't think I'm understanding this. Either way I tried it (with a NOT) and it was a bit slower than the IF approach earlier.
No; if D% is non-zero, you want the inner loop to execute just once and then fall through to the FOR loop before the outer UNTIL. If D% is zero, then you want the inner loop to repeat forever, without getting as far as the FOR loop.

Or, consider bypassing the delay loop with IF NOT D% GOTO <the number of the line immediately following the REPEAT>.

User avatar
1024MAK
Posts: 10294
Joined: Mon Apr 18, 2011 5:46 pm
Location: Looking forward to summer in Somerset, UK...
Contact:

Re: Adding a delay to loop in BASIC

Post by 1024MAK » Fri Jul 24, 2020 3:49 pm

D% = zero = false
D% = any other value = true

Try

Code: Select all

10 FOR A%=0 TO 24
20 PRINT “A%=”;A%;“ ”; 
30 IF A% PRINT “TRUE” ELSE PRINT “FALSE”
40 NEXT

Mark

Adam James
Posts: 196
Joined: Tue May 26, 2020 2:32 pm
Contact:

Re: Adding a delay to loop in BASIC

Post by Adam James » Sun Jul 26, 2020 11:36 pm

Thanks people. I'm finding that the one-liner is as good as any in terms of simplicity and time taken. However...
jgharston wrote:
Thu Jul 23, 2020 11:14 pm
That will take different amounts of time on different machines, in different weather, during different phases of the moon, when a disk is still rotating, when a network packet flies past, when a cosmic partical hits the serial ULA, and during any amount of other events. What you need is:

T%=TIME+delay:REPEAT UNTIL TIME>T%
I'm finding that doesn't give me the kind of resolution I was hoping for.

When delay is 1/100th of a second, it can get roughly a play speed that feels right, but 2/100th of a second then makes it feel far too slow.

On my BBC B under normal conditions a simple for...next loop with an integer counter can get to about 33 in 1/100th of a second.

Is there a good-practice way from within BASIC which gets better resolution?

User avatar
sweh
Posts: 2263
Joined: Sat Mar 10, 2012 12:05 pm
Location: New York, New York
Contact:

Re: Adding a delay to loop in BASIC

Post by sweh » Mon Jul 27, 2020 1:27 am

Since each machine runs at different speeds (and co-processors can make this even worse) maybe you should start your program with a simple counter loop and see how many times you loop

Maybe something like

Code: Select all

T%=TIME+200:REPEAT:LET A=A+1:UNTIL TIME>T%
Now you have some rough value. Maybe it takes 950 iterations to do 2 seconds. You can now scale a simple

Code: Select all

LET foobar=wanted*A/2:LET X=0:REPEAT:LET X=X+1:UNTIL X>foobar
You get consisent speed across platforms, but you'll never get to 100% speed with code like this, but you might be able to do some of the earlier optimisations to short-cut in the case where "wanted" is zero.
Rgds
Stephen

User avatar
Richard Russell
Posts: 1668
Joined: Sun Feb 27, 2011 10:35 am
Location: Downham Market, Norfolk
Contact:

Re: Adding a delay to loop in BASIC

Post by Richard Russell » Mon Jul 27, 2020 9:22 am

Adam James wrote:
Sun Jul 26, 2020 11:36 pm
Is there a good-practice way from within BASIC which gets better resolution?
For resolutions less than 1/100 second it will be platform-dependent. As far as my BASICs are concerned these will give you a delay in milliseconds (but bear in mind that the OS only guarantees a minimum delay of the specified amount):

BBC BASIC for Windows:

Code: Select all

      SYS "Sleep", n%
BBC BASIC for SDL 2.0:

Code: Select all

      SYS "SDL_Delay", n%

Adam James
Posts: 196
Joined: Tue May 26, 2020 2:32 pm
Contact:

Re: Adding a delay to loop in BASIC

Post by Adam James » Mon Jul 27, 2020 10:36 am

Thanks people.
sweh wrote:
Mon Jul 27, 2020 1:27 am
Since each machine runs at different speeds (and co-processors can make this even worse) maybe you should start your program with a simple counter loop and see how many times you loop

Maybe something like

Code: Select all

T%=TIME+200:REPEAT:LET A=A+1:UNTIL TIME>T%
Now you have some rough value. Maybe it takes 950 iterations to do 2 seconds. You can now scale...
This is my favourite answer so far, thanks. Very pragmatic and no more prone to problems than other approaches from what I can tell.

User avatar
Richard Russell
Posts: 1668
Joined: Sun Feb 27, 2011 10:35 am
Location: Downham Market, Norfolk
Contact:

Re: Adding a delay to loop in BASIC

Post by Richard Russell » Mon Jul 27, 2020 12:19 pm

Adam James wrote:
Thu Jul 23, 2020 8:14 pm
I want to have a game get gradually faster...
Going back to the original question, the optimum way of achieving that doesn't involve a variable delay.

The fundamental 'timebase' in a any kind of video game is the display refresh rate (typically 50 or 60 frames per second depending on platform). To achieve smooth animation you should update the positions of moving objects at that rate - anything slower and the movement will not be smooth and anything faster will either be wasted effort - the resulting frame(s) will never be displayed - or you may get 'tearing' part way down the screen, depending on how the display refresh happens.

So in an ideal world you should have a fixed update rate, synchronised with the display refresh rate, and then the speed of the game depends on how far objects move in that fixed period (velocity = ds/dt). To arrange that the game gets gradually faster what you need to do is to gradually increase the distance moved in a given time (ds) not reduce the time taken to move a given distance (dt).

Of course if you're working with a slow machine like a BBC Micro you may well not have the luxury of being able to animate everything at the display refresh rate, or even at (say) half that rate - especially in BASIC - and non-smooth movement may therefore be unavoidable. But it's still worth considering the approach of keeping the update rate more-or-less constant and changing the distance moved by objects in that time, rather than having a variable delay.

On a modern platform updating the entire screen at 60 frames per second may be entirely practical, even in BASIC. This is one of David Williams's prizewinning games which is 100% BBC BASIC code (no assembler):


AJW
Posts: 907
Joined: Sun Feb 15, 2004 2:01 pm
Contact:

Re: Adding a delay to loop in BASIC

Post by AJW » Mon Aug 03, 2020 3:52 pm

Richard Russell wrote:
Mon Jul 27, 2020 12:19 pm
Adam James wrote:
Thu Jul 23, 2020 8:14 pm
I want to have a game get gradually faster...
Going back to the original question, the optimum way of achieving that doesn't involve a variable delay.

The fundamental 'timebase' in a any kind of video game is the display refresh rate (typically 50 or 60 frames per second depending on platform). To achieve smooth animation you should update the positions of moving objects at that rate - anything slower and the movement will not be smooth and anything faster will either be wasted effort - the resulting frame(s) will never be displayed - or you may get 'tearing' part way down the screen, depending on how the display refresh happens.

So in an ideal world you should have a fixed update rate, synchronised with the display refresh rate,
How? Using the system VIA?

User avatar
Richard Russell
Posts: 1668
Joined: Sun Feb 27, 2011 10:35 am
Location: Downham Market, Norfolk
Contact:

Re: Adding a delay to loop in BASIC

Post by Richard Russell » Mon Aug 03, 2020 4:58 pm

AJW wrote:
Mon Aug 03, 2020 3:52 pm
How? Using the system VIA?
My comment was a general one, not specifically related to the BBC Micro. I don't know precisely how one synchronises with display refresh on that machine (on an Archimedes etc. one can use WAIT, in BBC BASIC for SDL 2.0 one can use *REFRESH) but it must be possible because there are programs like Elite that rely on it.

Coming from a TV background (the same applies in computer graphics and film) I just wanted to emphasise that in any animation the update rate should be kept constant and variable velocity achieved by changing the distance moved in that time, not by moving a fixed distance and changing the update rate by using a variable delay.

Post Reply

Return to “programming”