ON INTERVAL GOSUB for game loop ticks?

By mohi13

Supporter (2)

Аватар пользователя mohi13

04-09-2019, 00:35

I haven't touched MSX BASIC in almost 25 years, and I've recently got the itch to see if my years of real world programming experience can finally produce the game my younger self always struggled to produce. So I was reviewing the commands to refresh my memory (and learn things I was too inexperienced to understand) and stumbled on ON INTERVAL GOSUB.

Would using this to execute ticks in a game loop be a good idea? So that at certain periods it would execute the subroutine there to move sprites and run game logic before returning to the main body to intercept player input or the like.

Thanks in advance!

Для того, чтобы оставить комментарий, необходимо регистрация или !login

By thegeps

Champion (272)

Аватар пользователя thegeps

04-09-2019, 08:13

Yes, it's a good idea. I did It in a recently breakout clone for a basic contest (used on interval to execute main sprite display and Joy check. Please, show Is your progressess and have a loro of fine with your MSX Smile

By NYYRIKKI

Enlighted (5382)

Аватар пользователя NYYRIKKI

04-09-2019, 10:14

Yes, it can be done, but is it a good idea? I wouldn't say it is...

In modern programming environments like Visual BASIC it is common to do things event based, but in MSX-BASIC I think this makes things just more hard to debug & control. Overall I would try to minimize all usage of subroutines to avoid unnecessary slow downs & "spaghetti code" and prefer up to down programming style mainly because MSX-BASIC lacks local variables, labels, parameters and return values, but it still has very well working full screen editor and RENUM-command. How ever don't get me wrong. Subroutines are very good idea when they are really needed, just ask your self first "Am I going to need this routine more than once?"

Practically I would implement the game loop something like this:

10 ' Init the game here... For example:
20 DEFINT A-Z
30 X=128:Y=96
40 FORI=2TO8:XD(I)=SGN(5-I):YD(8-I)=XD(I):NEXT:SWAPYD(0),YD(8)
50 SCREEN2,1:SPRITE$(0)="0HH0"

100 ' Reset time counter
110 TIME=0

120 ' Main loop: Do joystick checks, game logic, 
130 ' exception handling etc. here... For example:
140 S=STICK(J)
150 X=X+XD(S):Y=Y+YD(S)
160 IF X<0 THEN X=0
170 IF Y<0 THEN Y=0
180 IF X>255 THEN X=255
190 IF Y>191 THEN Y=191

200 ' Sync to time
210 IF TIME<5 THEN 210 ELSE TIME=0
220 ' Update the screen
230 PUTSPRITE0,(X,Y),8
240 ' Repeat the main loop
250 GOTO 120

By thegeps

Champion (272)

Аватар пользователя thegeps

04-09-2019, 10:46

Well, I did on interval way 'cause in the main loop I update ball Sprite position and I do all collisione checks with bricks, that are redefined chars in screen1, so I had to convert ball position to VPEEK position, check for collisione and so on. O used on interval so player's controls weren't delayed

By theNestruo

Expert (111)

Аватар пользователя theNestruo

04-09-2019, 13:09

I would recommend ON INTERVAL GOSUB for events that doesn't happen on every frame and that can be quickly dispatched. E.g.: decrease a timer (every second), switch enemies directions (every half second), etc... but I would do player input/ interactions/ put sprite/ etc. in the main loop, as they should be processed every frame

I suggest this structure (line numbres are merely orientative to separate the code "sections"):

10 GOTO2000: ' Jump to initialization

20 ' main loop starts here
30 ' inputs, etc.
40 ' interactions, etc.
50 ' put sprites, vpokes, etc.
60 GOTO20

500 ' Subroutines of the main loop here
510 ' (called with GOSUB, ending with RETURN)
520 ' Located after the main loop in order of decreasing frequency
530 ' (i.e.: every frame subroutines first, most occasional subroutines last)
540 ' These include the ON INTERVAL subroutine, in proper order according frequency
550 ' ...

1000 ' Non-critical performance screens subroutines here
1010 ' Title screen, "GAME OVER" screen, etc.
1020 ' ...

2000 DEFINTA-Z:SCREEN2,2,0: ' Initialization here
2010 ' ...
2020 GOTO20: ' Enter the main loop

2100 DATA ...
2110 ' ... (initialization data)

The idea is to optimize the GOTO and GOSUB jumps performance: they start seeking the target line from the current one (if the jump is ahead) or from the beginning of the program (if the jump is backwards). The most frequent jump is the main loop (looped every frame, and jumping backwards), so it should be as close as possible to the beginning of the program, and the shortest way to achieve this is the first line being a simple "GOTO initialization".

By mohi13

Supporter (2)

Аватар пользователя mohi13

04-09-2019, 16:25

That's an interesting trick to put the initialization code in the bottom like that. You've all certainly gave me a lot to think about. I guess I'll just have to get my hands dirty and experiment.

Thanks everyone!

By theNestruo

Expert (111)

Аватар пользователя theNestruo

04-09-2019, 16:39

There are many other tricks to speed up MSX-BASIC performance: removing REMs, removing whitespace, IF..GOTO instead of IF..THEN, calculations that can be evaluated as written (I*7+13 is faster than 13+7*I)...
I discovered many of these tricks during the development of Pérez the Mouse, and described most of them in the source code analysis (...but it's in Spanish only; maybe Google Translate could help)

By NYYRIKKI

Enlighted (5382)

Аватар пользователя NYYRIKKI

04-09-2019, 17:17

theNestruo wrote:

The idea is to optimize the GOTO and GOSUB jumps performance: they start seeking the target line from the current one (if the jump is ahead) or from the beginning of the program (if the jump is backwards).

Well... This is not quite accurate... This is true when the GOTO or GOSUB is executed first time, but when the same command is executed again, BASIC will remember where it needs to jump and so the seek process is not needed anymore.

theNestruo wrote:

There are many other tricks to speed up MSX-BASIC performance: removing REMs, removing whitespace.

Yes, each not needed byte is extra weight, but certain amount of documentation may be good to include as long as you keep it simple. If you want to exclude whole executable line and save it for possible future use, please use ELSE instead of REM or " ' ". This will both save memory and solve possible problems with RENUM-command in case this remark line includes line numbers.

Generally for speed related problems I warmly recommend loading MSX-BASIC kun compiler. (See X-BASIC, Nestor BASIC) It will make your program multiple times faster, but it will also make your possible work memory problems a lot worse.

By theNestruo

Expert (111)

Аватар пользователя theNestruo

04-09-2019, 19:02

NYYRIKKI wrote:
theNestruo wrote:

The idea is to optimize the GOTO and GOSUB jumps performance: they start seeking the target line from the current one (if the jump is ahead) or from the beginning of the program (if the jump is backwards).

Well... This is not quite accurate... This is true when the GOTO or GOSUB is executed first time, but when the same command is executed again, BASIC will remember where it needs to jump and so the seek process is not needed anymore.

Thanks for the clarification! Smile

By zPasi

Champion (471)

Аватар пользователя zPasi

04-09-2019, 19:48

theNestruo wrote:

The idea is to optimize the GOTO and GOSUB jumps performance: they start seeking the target line from the current one (if the jump is ahead) or from the beginning of the program (if the jump is backwards).

That is indeed a common design on BASIC interpreters. But llke Nyyrikki stated, MSX-BASIC is smarter than that. It works by replacing the line number parameters of GOTOs and GOSUBS with absolute addresses of target lines. They are changed back before saving the program to disk or cassette.

I don't know if any other BASIC interpreter uses that trick. I think at least SpectraVideo pre-MSX models?