VOGONS

Common searches


First post, by Peter Swinkels

User metadata
Rank Member
Rank
Member

Hi,

I converted a Quick Basic program to work in GWBasic. At an emulated speed of 66 Mhz my program runs at an acceptable speed in Quick Basic, in GWBasic however it runs very slugglishly. It having been long a while ago I ever played with GWBasic can't figure out where exactly the problem might be. Yes, the interpreter is probably a bit slower, but that doesn't tell me what exactly is causing the massive slow down.

10 DEFINT A-Z: GOSUB 900: END
20 FULLROW = 0
30 FOR PITY = 0 TO 15
40 FULLROW = -1
50 FOR PITX = 0 TO 9
60 IF MID$(PIT$, ((10 * PITY) + PITX) + 1, 1) = "0" THEN FULLROW = 0: GOTO 80
70 NEXT PITX
80 IF FULLROW THEN REMOVEDROW = PITY: GOSUB 1100
90 NEXT PITY
100 RETURN
110 DROPRATE! = 1
120 SHAPE = INT(RND * 6)
130 SHAPEANGLE = INT(RND * 4)
140 NEWANGLE = SHAPEANGLE
150 GOSUB 510
160 SHAPEMAP$ = ROTATEDMAP$
170 GOSUB 450
180 SHAPEX = RANDOMSHAPEX
190 SHAPEY = -4
200 RETURN
210 COLOR 4: LOCATE 3, 2
220 IF GAMEOVER THEN PRINT "Game over.": LOCATE , 2: PRINT "Press Enter to play a new game."; ELSE PRINT ; "Score:" + STR$(SCORE);
230 RETURN
240 LINE (116, 32)-STEP(92, 145), 15, B
250 LINE (116, 32)-STEP(92, 0), 0
260 FOR PITY = 0 TO 15
270 FOR PITX = 0 TO 9
280 IF GAMEOVER THEN BLOCKCOLOR$ = "4" ELSE BLOCKCOLOR$ = MID$(PIT$, ((10 * PITY) + PITX) + 1, 1)
290 DRAWX = PITX: DRAWY = PITY: GOSUB 320
300 NEXT PITX: NEXT PITY
310 RETURN
320 DRAWX = DRAWX * 9: DRAWY = DRAWY * 9
330 LINE (DRAWX + 117, DRAWY + 32)-STEP(9, 9), VAL("&H" + BLOCKCOLOR$), BF
340 LINE (DRAWX + 118, DRAWY + 33)-STEP(6, 6), 0, B
350 RETURN
360 FOR BLOCKX = 0 TO 3
370 FOR BLOCKY = 0 TO 3
380 PITX = SHAPEX + BLOCKX: PITY = SHAPEY + BLOCKY
390 IF PITX >= 0 AND PITX < 10 AND PITY >= 0 AND PITY < 16 THEN GOSUB 420
400 NEXT BLOCKY: NEXT BLOCKX
410 RETURN
420 IF ERASESHAPE THEN BLOCKCOLOR$ = MID$(PIT$, ((10 * PITY) + PITX) + 1, 1) ELSE BLOCKCOLOR$ = MID$(SHAPEMAP$, ((4 * BLOCKY) + BLOCKX) + 1, 1): IF BLOCKCOLOR$ = "0" THEN BLOCKCOLOR$ = MID$(PIT$, ((10 * PITY) + PITX) + 1, 1)
430 DRAWX = PITX: DRAWY = PITY: GOSUB 320
440 RETURN
450 INTENDEDSHAPEX = INT((RND * 10) - 1): RANDOMSHAPEX = 0
460 FOR XMOVE = 0 TO INTENDEDSHAPEX
470 MOVEDMAP$ = SHAPEMAP$: XDIRECTION = 1: YDIRECTION = 0: GOSUB 620
480 IF CANMOVE THEN RANDOMSHAPEX = RANDOMSHAPEX + 1 ELSE RETURN
490 NEXT XMOVE
500 RETURN
510 GOSUB 720
520 IF NEWANGLE = 0 THEN ROTATEDMAP$ = MAP$: RETURN
530 ROTATEDMAP$ = STRING$(4 * 4, "0")
540 FOR BLOCKX = 0 TO 3
550 FOR BLOCKY = 0 TO 3
560 IF NEWANGLE = 1 THEN NEWBLOCKX = 3 - BLOCKY: NEWBLOCKY = BLOCKX
570 IF NEWANGLE = 2 THEN NEWBLOCKX = 3 - BLOCKX: NEWBLOCKY = 3 - BLOCKY
580 IF NEWANGLE = 3 THEN NEWBLOCKX = BLOCKY: NEWBLOCKY = 3 - BLOCKX
590 MID$(ROTATEDMAP$, ((4 * NEWBLOCKY) + NEWBLOCKX) + 1, 1) = MID$(MAP$, ((4 * BLOCKY) + BLOCKX) + 1, 1)
600 NEXT BLOCKY: NEXT BLOCKX
Show last 83 lines
610 RETURN
620 CANMOVE = -1
630 FOR BLOCKY = 0 TO 3
640 FOR BLOCKX = 0 TO 3
650 IF NOT MID$(MOVEDMAP$, ((4 * BLOCKY) + BLOCKX) + 1, 1) = "0" THEN GOSUB 680
660 NEXT BLOCKX: NEXT BLOCKY
670 RETURN
680 PITX = (SHAPEX + BLOCKX) + XDIRECTION: PITY = (SHAPEY + BLOCKY) + YDIRECTION
690 IF PITX >= 0 AND PITX < 10 AND PITY >= 0 AND PITY < 16 THEN IF NOT MID$(PIT$, (((10 * PITY) + PITX) + 1), 1) = "0" THEN CANMOVE = 0: RETURN
700 IF PITX < 0 OR PITX >= 10 OR PITY >= 16 THEN CANMOVE = 0: RETURN
710 RETURN
720 MAP$ = ""
730 IF SHAPE = 0 THEN MAP$ = "0000333300000000"
740 IF SHAPE = 1 THEN MAP$ = "0000111000100000"
750 IF SHAPE = 2 THEN MAP$ = "0000666060000000"
760 IF SHAPE = 3 THEN MAP$ = "00000EE00EE00000"
770 IF SHAPE = 4 THEN MAP$ = "0000022022000000"
780 IF SHAPE = 5 THEN MAP$ = "0000555005000000"
790 IF SHAPE = 7 THEN MAP$ = "0000440004400000"
800 RETURN
810 RANDOMIZE TIMER: KEY OFF: PLAY "ML L64": SCREEN 7: CLS : COLOR 9
820 LOCATE 1, 1: PRINT "GWBlocks v1.00": PRINT "By: Peter Swinkels, ***2021***";
830 GOSUB 110
840 GAMEOVER = 0
850 PIT$ = STRING$(10 * 16, "0")
860 SCORE = 0
870 GOSUB 240
880 GOSUB 210
890 RETURN
900 GOSUB 810
910 STARTTIME! = TIMER
920 WHILE NOT KEYV$ = CHR$(27)
930 KEYV$ = ""
940 WHILE KEYV$ = ""
950 IF (NOT GAMEOVER) AND (TIMER >= STARTTIME! + DROPRATE! OR STARTTIME! > TIMER) THEN GOSUB 1290: STARTTIME! = TIMER
960 KEYV$ = INKEY$
970 WEND
980 ERASESHAPE = -1: GOSUB 360
990 IF KEYV$ = CHR$(27) THEN SCREEN 0: END
1000 IF GAMEOVER AND KEYV$ = CHR$(13) THEN GOSUB 810
1010 IF (NOT GAMEOVER) AND (KEYV$ = "A" OR KEYV$ = "a") THEN GOSUB 1170
1020 IF (NOT GAMEOVER) AND (KEYV$ = CHR$(0) + "K") THEN MOVEDMAP$ = SHAPEMAP$: XDIRECTION = -1: YDIRECTION = 0: GOSUB 620
1030 IF (NOT GAMEOVER) AND (KEYV$ = CHR$(0) + "M") THEN MOVEDMAP$ = SHAPEMAP$: XDIRECTION = 1: YDIRECTION = 0: GOSUB 620
1040 IF (NOT GAMEOVER) AND (KEYV$ = CHR$(0) + "K" AND CANMOVE) THEN SHAPEX = SHAPEX - 1
1050 IF (NOT GAMEOVER) AND (KEYV$ = CHR$(0) + "M" AND CANMOVE) THEN SHAPEX = SHAPEX + 1
1060 IF (NOT GAMEOVER) AND (KEYV$ = " ") THEN DROPRATE! = 0
1070 ERASESHAPE = 0: GOSUB 360
1080 WEND
1090 RETURN
1100 FOR PITROWY = REMOVEDROW TO 0 STEP -1
1110 FOR PITX = 0 TO 10 - 1
1120 IF PITROWY = 0 THEN BLOCKCOLOR$ = "0" ELSE BLOCKCOLOR$ = MID$(PIT$, ((10 * (PITROWY - 1)) + PITX) + 1, 1)
1130 MID$(PIT$, ((10 * PITROWY) + PITX) + 1, 1) = BLOCKCOLOR$
1140 NEXT PITX: NEXT PITROWY
1150 SCORE = SCORE + 1
1160 RETURN
1170 IF SHAPEANGLE = 3 THEN NEWANGLE = 0 ELSE NEWANGLE = SHAPEANGLE + 1
1180 GOSUB 510
1190 MOVEDMAP$ = ROTATEDMAP$: XDIRECTION = 0: YDIRECTION = 0: GOSUB 620
1200 IF CANMOVE THEN SHAPEANGLE = NEWANGLE: SHAPEMAP$ = ROTATEDMAP$
1210 RETURN
1220 PLAY "N21"
1230 FOR BLOCKY = 0 TO 3
1240 FOR BLOCKX = 0 TO 3
1250 PITX = SHAPEX + BLOCKX: PITY = SHAPEY + BLOCKY
1260 IF (PITX >= 0 AND PITX < 10 AND PITY >= 0 AND PITY < 16) AND (NOT MID$(SHAPEMAP$, ((4 * BLOCKY) + BLOCKX) + 1, 1) = "0") THEN MID$(PIT$, ((10 * PITY) + PITX) + 1, 1) = MID$(SHAPEMAP$, ((4 * BLOCKY) + BLOCKX) + 1, 1)
1270 NEXT BLOCKX: NEXT BLOCKY
1280 RETURN
1290 MOVEDMAP$ = SHAPEMAP$: XDIRECTION = 0: YDIRECTION = 1: GOSUB 620
1300 IF CANMOVE THEN GOSUB 1320 ELSE GOSUB 1360
1310 RETURN
1320 ERASESHAPE = -1: GOSUB 360
1330 IF DROPRATE! > 0 THEN SOUND 37, .3
1340 SHAPEY = SHAPEY + 1: ERASESHAPE = 0: GOSUB 360
1350 RETURN
1360 GOSUB 1220
1370 GAMEOVER = (SHAPEY < 0)
1380 GOSUB 20
1390 GOSUB 240
1400 GOSUB 210
1410 IF NOT GAMEOVER THEN GOSUB 110: ERASESHAPE = 0: GOSUB 360
1420 RETURN

I am using Quick Basic 4.5 as an editor and am trying to get to run the above smoothly in GWBasic 2.23. Does anyone have any idea here?

Last edited by Peter Swinkels on 2021-02-26, 15:45. Edited 1 time in total.

Reply 1 of 19, by konc

User metadata
Rank Oldbie
Rank
Oldbie

Have a look here
https://bhtooefr.org/blog/2017/12/19/benchmar … crosoft-basics/
It appears that Quick Basic 4.5 is taking advantage of the FPU and can be times faster than GW when there is one available. Could it be that?

Reply 2 of 19, by Peter Swinkels

User metadata
Rank Member
Rank
Member

I checked the link, while it is an interesting read it doesn't appear to be all that useful to my specific problem. Is it possible to disable the FPU in DOSBox or Quick Basic to check this? I am going to try restructuring the program flow to make it more efficient, while it is probably going to hurt readibility and maintainability I suspect eliminating a few GOSUB/RETURNS is going to solve a lot.

Last edited by Peter Swinkels on 2021-02-15, 12:34. Edited 1 time in total.

Reply 4 of 19, by Peter Swinkels

User metadata
Rank Member
Rank
Member

Okay, I made a few changes which should make the code faster, but it doesn't appear to help much:

10 DEFINT A-Z: GOSUB 950: END
20 FULLROW = 0
30 FOR PITY = 0 TO 15
40 FULLROW = -1
50 FOR PITX = 0 TO 9
60 IF MID$(PIT$, ((10 * PITY) + PITX) + 1, 1) = "0" THEN FULLROW = 0: GOTO 80
70 NEXT PITX
80 IF FULLROW THEN REMOVEDROW = PITY: GOSUB 1150
90 NEXT PITY
100 RETURN
110 DROPRATE! = 1
120 SHAPE = INT(RND * 6)
130 SHAPEANGLE = INT(RND * 4)
140 NEWANGLE = SHAPEANGLE
150 GOSUB 560
160 SHAPEMAP$ = ROTATEDMAP$
170 GOSUB 500
180 SHAPEX = RANDOMSHAPEX
190 SHAPEY = -4
200 RETURN
210 COLOR 4: LOCATE 3, 2
220 IF GAMEOVER THEN PRINT "Game over.": LOCATE , 2: PRINT "Press Enter to play a new game."; ELSE PRINT ; "Score:" + STR$(SCORE);
230 RETURN
240 LINE (116, 32)-STEP(92, 145), 15, B
250 LINE (116, 32)-STEP(92, 0), 0
260 FOR PITY = 0 TO 15
270 FOR PITX = 0 TO 9
280 IF GAMEOVER THEN BLOCKCOLOR$ = "4" ELSE BLOCKCOLOR$ = MID$(PIT$, ((10 * PITY) + PITX) + 1, 1)
290 LINE ((PITX * 9) + 117, (PITY * 9) + 32)-STEP(9, 9), VAL("&H" + BLOCKCOLOR$), BF
300 LINE ((PITX * 9) + 118, (PITY * 9) + 33)-STEP(6, 6), 0, B
310 NEXT PITX: NEXT PITY
320 RETURN
330 FOR BLOCKX = 0 TO 3
340 FOR BLOCKY = 0 TO 3
350 PITX = SHAPEX + BLOCKX: PITY = SHAPEY + BLOCKY
360 IF PITX >= 0 AND PITX < 10 AND PITY >= 0 AND PITY < 16 THEN GOSUB 390
370 NEXT BLOCKY: NEXT BLOCKX
380 RETURN
390 BLOCKCOLOR$ = MID$(SHAPEMAP$, ((4 * BLOCKY) + BLOCKX) + 1, 1)
400 IF BLOCKCOLOR$ = "0" THEN BLOCKCOLOR$ = MID$(PIT$, ((10 * PITY) + PITX) + 1, 1)
410 LINE ((PITX * 9) + 117, (PITY * 9) + 32)-STEP(9, 9), VAL("&H" + BLOCKCOLOR$), BF
420 LINE ((PITX * 9) + 118, (PITY * 9) + 33)-STEP(6, 6), 0, B
430 RETURN
440 FOR BLOCKX = 0 TO 3
450 FOR BLOCKY = 0 TO 3
460 PITX = SHAPEX + BLOCKX: PITY = SHAPEY + BLOCKY
470 IF PITX >= 0 AND PITX < 10 AND PITY >= 0 AND PITY < 16 THEN LINE ((PITX * 9) + 117, (PITY * 9) + 32)-STEP(9, 9), VAL("&H" + MID$(PIT$, ((10 * PITY) + PITX) + 1, 1)), BF
480 NEXT BLOCKY: NEXT BLOCKX
490 RETURN
500 INTENDEDSHAPEX = INT((RND * 10) - 1): RANDOMSHAPEX = 0
510 FOR XMOVE = 0 TO INTENDEDSHAPEX
520 MOVEDMAP$ = SHAPEMAP$: XDIRECTION = 1: YDIRECTION = 0: GOSUB 670
530 IF CANMOVE THEN RANDOMSHAPEX = RANDOMSHAPEX + 1 ELSE RETURN
540 NEXT XMOVE
550 RETURN
560 GOSUB 770
570 IF NEWANGLE = 0 THEN ROTATEDMAP$ = MAP$: RETURN
580 ROTATEDMAP$ = STRING$(4 * 4, "0")
590 FOR BLOCKX = 0 TO 3
600 FOR BLOCKY = 0 TO 3
Show last 88 lines
610 IF NEWANGLE = 1 THEN NEWBLOCKX = 3 - BLOCKY: NEWBLOCKY = BLOCKX
620 IF NEWANGLE = 2 THEN NEWBLOCKX = 3 - BLOCKX: NEWBLOCKY = 3 - BLOCKY
630 IF NEWANGLE = 3 THEN NEWBLOCKX = BLOCKY: NEWBLOCKY = 3 - BLOCKX
640 MID$(ROTATEDMAP$, ((4 * NEWBLOCKY) + NEWBLOCKX) + 1, 1) = MID$(MAP$, ((4 * BLOCKY) + BLOCKX) + 1, 1)
650 NEXT BLOCKY: NEXT BLOCKX
660 RETURN
670 CANMOVE = -1
680 FOR BLOCKY = 0 TO 3
690 FOR BLOCKX = 0 TO 3
700 IF NOT MID$(MOVEDMAP$, ((4 * BLOCKY) + BLOCKX) + 1, 1) = "0" THEN GOSUB 730
710 NEXT BLOCKX: NEXT BLOCKY
720 RETURN
730 PITX = (SHAPEX + BLOCKX) + XDIRECTION: PITY = (SHAPEY + BLOCKY) + YDIRECTION
740 IF PITX >= 0 AND PITX < 10 AND PITY >= 0 AND PITY < 16 THEN IF NOT MID$(PIT$, (((10 * PITY) + PITX) + 1), 1) = "0" THEN CANMOVE = 0: RETURN
750 IF PITX < 0 OR PITX >= 10 OR PITY >= 16 THEN CANMOVE = 0: RETURN
760 RETURN
770 MAP$ = ""
780 IF SHAPE = 0 THEN MAP$ = "0000333300000000"
790 IF SHAPE = 1 THEN MAP$ = "0000111000100000"
800 IF SHAPE = 2 THEN MAP$ = "0000666060000000"
810 IF SHAPE = 3 THEN MAP$ = "00000EE00EE00000"
820 IF SHAPE = 4 THEN MAP$ = "0000022022000000"
830 IF SHAPE = 5 THEN MAP$ = "0000555005000000"
840 IF SHAPE = 7 THEN MAP$ = "0000440004400000"
850 RETURN
860 RANDOMIZE TIMER: KEY OFF: PLAY "ML L64": SCREEN 7: CLS : COLOR 9
870 LOCATE 1, 1: PRINT "GWBlocks v1.00": PRINT "By: Peter Swinkels, ***2021***";
880 GOSUB 110
890 GAMEOVER = 0
900 PIT$ = STRING$(10 * 16, "0")
910 SCORE = 0
920 GOSUB 240
930 GOSUB 210
940 RETURN
950 GOSUB 860
960 STARTTIME! = TIMER
970 WHILE NOT KEYV$ = CHR$(27)
980 KEYV$ = ""
990 WHILE KEYV$ = ""
1000 IF (NOT GAMEOVER) AND (TIMER >= STARTTIME! + DROPRATE! OR STARTTIME! > TIMER) THEN GOSUB 1340: STARTTIME! = TIMER
1010 KEYV$ = INKEY$
1020 WEND
1030 GOSUB 440
1040 IF KEYV$ = CHR$(27) THEN SCREEN 0: WIDTH 80: KEY ON: END
1050 IF GAMEOVER AND KEYV$ = CHR$(13) THEN GOSUB 860
1060 IF (NOT GAMEOVER) AND (KEYV$ = "A" OR KEYV$ = "a") THEN GOSUB 1220
1070 IF (NOT GAMEOVER) AND (KEYV$ = CHR$(0) + "K") THEN MOVEDMAP$ = SHAPEMAP$: XDIRECTION = -1: YDIRECTION = 0: GOSUB 670
1080 IF (NOT GAMEOVER) AND (KEYV$ = CHR$(0) + "K" AND CANMOVE) THEN SHAPEX = SHAPEX - 1
1090 IF (NOT GAMEOVER) AND (KEYV$ = CHR$(0) + "M") THEN MOVEDMAP$ = SHAPEMAP$: XDIRECTION = 1: YDIRECTION = 0: GOSUB 670
1100 IF (NOT GAMEOVER) AND (KEYV$ = CHR$(0) + "M" AND CANMOVE) THEN SHAPEX = SHAPEX + 1
1110 IF (NOT GAMEOVER) AND (KEYV$ = " ") THEN DROPRATE! = 0
1120 GOSUB 330
1130 WEND
1140 RETURN
1150 FOR PITROWY = REMOVEDROW TO 0 STEP -1
1160 FOR PITX = 0 TO 10 - 1
1170 IF PITROWY = 0 THEN BLOCKCOLOR$ = "0" ELSE BLOCKCOLOR$ = MID$(PIT$, ((10 * (PITROWY - 1)) + PITX) + 1, 1)
1180 MID$(PIT$, ((10 * PITROWY) + PITX) + 1, 1) = BLOCKCOLOR$
1190 NEXT PITX: NEXT PITROWY
1200 SCORE = SCORE + 1
1210 RETURN
1220 IF SHAPEANGLE = 3 THEN NEWANGLE = 0 ELSE NEWANGLE = SHAPEANGLE + 1
1230 GOSUB 560
1240 MOVEDMAP$ = ROTATEDMAP$: XDIRECTION = 0: YDIRECTION = 0: GOSUB 670
1250 IF CANMOVE THEN SHAPEANGLE = NEWANGLE: SHAPEMAP$ = ROTATEDMAP$
1260 RETURN
1270 PLAY "N21"
1280 FOR BLOCKY = 0 TO 3
1290 FOR BLOCKX = 0 TO 3
1300 PITX = SHAPEX + BLOCKX: PITY = SHAPEY + BLOCKY
1310 IF (PITX >= 0 AND PITX < 10 AND PITY >= 0 AND PITY < 16) AND (NOT MID$(SHAPEMAP$, ((4 * BLOCKY) + BLOCKX) + 1, 1) = "0") THEN MID$(PIT$, ((10 * PITY) + PITX) + 1, 1) = MID$(SHAPEMAP$, ((4 * BLOCKY) + BLOCKX) + 1, 1)
1320 NEXT BLOCKX: NEXT BLOCKY
1330 RETURN
1340 MOVEDMAP$ = SHAPEMAP$: XDIRECTION = 0: YDIRECTION = 1: GOSUB 670
1350 IF CANMOVE THEN GOSUB 1370 ELSE GOSUB 1410
1360 RETURN
1370 GOSUB 440
1380 IF DROPRATE! = 1 THEN SOUND 37, .3
1390 SHAPEY = SHAPEY + 1: GOSUB 330
1400 RETURN
1410 GOSUB 1270
1420 GAMEOVER = (SHAPEY < 0)
1430 GOSUB 20
1440 GOSUB 240
1450 GOSUB 210
1460 IF NOT GAMEOVER THEN GOSUB 110: GOSUB 330
1470 RETURN

Reply 5 of 19, by gerry

User metadata
Rank Member
Rank
Member

rather than your code being the issue, specifically, it probably is the difference in the way gwbasic and quickbasic interpret the code at runtime as konc was saying

I suspect, without knowing of course, that gw is more 'literal' in its interpretation and quick basic does a few optimisations or has better approach to subroutines

that might not be the case, it may be some other subtlety

Reply 6 of 19, by Peter Swinkels

User metadata
Rank Member
Rank
Member

Someone else pointed this out to at VBForums where I asked the same question. I tried compiling the code with BasCom 2.20 but that compiler is too old. 🙁

Reply 7 of 19, by konc

User metadata
Rank Oldbie
Rank
Oldbie

Apart from the gw-basic specifics or the code logic (to which I haven't really dived into) there are also classic minor improvements you can do to your code. Again, after you've made sure that the main logic or drawing process can't be greatly improved, these on their own won't make the code magically fly but I'm mentioning them as general observations.
You are doing a lot of repetitive calculations inside loops, that can be pre-calculated. Let me give an example or two, let's see gosub 770. Instead of going through all those IFs everytime you can have something like the following, pre-populated once outside the loop of course (excuse any syntax or index errors, it's been a long time since I last messed with basic but you'll get the idea)

DIM MAPARR$(8)
MAPARR$(0) = "xxx"
...
MAPARR$(7) = "yyy"

and then on line 560 do only a

MAP$ = MAPARR$(SHAPE)

No jumps, no IFs, no 10 lines of code to go through. Things like that, if you have other "complex" calculations done everytime inside a loop you can have them ready. Another example would be the result of (PITX * 9) + 117. For the 3rd time I want to make clear that these won't have a huge impact if most of the time is lost elsewhere, they are just simple good practices/tricks to squeeze a lit bit more performance out.

Reply 9 of 19, by Peter Swinkels

User metadata
Rank Member
Rank
Member

Okay, I changed the code so the most frequently occurring jumps are now forward jumps and got rid of a few repititive calculations.

https://github.com/PeterSwinkels/GWBlocks/blo … in/GWBLOCKS.BAS

There appears to be a slight increase in speed.

Reply 10 of 19, by Jo22

User metadata
Rank l33t++
Rank
l33t++

Did you try saving a copy of the basic program in tokenized format rather than using ASCII?
I don't know if this makes a difference but it's worth a try. 😀
Both GW-BASIC and QB can use their own format, but also ASCII.
For example, to make GW-BASIC saving in ASCII, the syntax is SAVE "PROGRAM.BAS", A

"Time, it seems, doesn't flow. For some it's fast, for some it's slow.
In what to one race is no time at all, another race can rise and fall..." - The Minstrel

//My video channel//

Reply 12 of 19, by Jo22

User metadata
Rank l33t++
Rank
l33t++
Peter Swinkels wrote on 2021-02-19, 21:13:

Tokenized or ASCII doesn't appear to matter.

Hm,okay. Sorry to hear. 🙁
Well, I heard that OEM versions of DOS (around prior MS-DOS 4/5) included custom utilities and modified MS-DOS programs.
Perhaps one of these is more optimized or compiled with 80186 instructions?
I don't know.

I used GW-BASIC/BASICA when I was little, so it's quite long ago.
Later, I mainly used QB, VB and also PowerBASIC.
PowerBASIC has a lot of optimization options, by the way.
It can compile for 80286s, for example..

"Time, it seems, doesn't flow. For some it's fast, for some it's slow.
In what to one race is no time at all, another race can rise and fall..." - The Minstrel

//My video channel//

Reply 13 of 19, by Peter Swinkels

User metadata
Rank Member
Rank
Member

Yes, I could use a more modern version of BASIC but that's not the point. The idea was to make a working game with the limited resources that would have been available when GWBasic 3.23 was just released.

Reply 14 of 19, by ripsaw8080

User metadata
Rank DOSBox Author
Rank
DOSBox Author

If you run the game in GW-BASIC in DOSBox and the speed becomes acceptable when cycles are sufficiently increased then it's obvious that the interpreter is just inefficient compared to the compiler.

I notice a good bit of sub-string stuff, e.g. MID$, which is painfully slow in the interpreter, so I suggest trying a different approach for those parts -- maybe rework the object drawing to use the DRAW statement. A couple of things I notice that aren't performance related: 1) consider adding a DIM MAPS$(6) at the top of the program; you can get away without it, but compilers may complain. 2) SHAPE = INT(RND * 6) will never select the last of your pieces; should be INT(RND * 7).

Reply 15 of 19, by Peter Swinkels

User metadata
Rank Member
Rank
Member

1. Yes, that is true. More cycles does fix the problem. But again the point of this exercise was getting the job done with what would have been available while GWBasic would still have been in general use. I think 66 MHz is already too fast in that regard. An actual machine running GWBasic probably would have ran at 25 MHz at the most.
2. Yes, perhaps I should use arrays instead of strings. It's worth a try.
3. I suspect the DRAW statement would be far slower than the LINE statement because it relies on a macro contained in a string.
4. Apparently DIM statements aren't required for any array of ten elements or less. I have had cases where I got away treating two variables of the same name as both a numeric value and an array in the same program. This happened by accident but is allowed. If I remember correctly this practice is even allowed by QBasic/Quick Basic. Compilers for those languages should allow it. I agree however that would be good practice to declare arrays. However there is a good chance I would just clutter the code with pointless DIM statements. Those old BASIC interpreters hardly appear to enfore what you declare in them anyway. I should test that to be sure though.
5. Thank you for pointing out that faulty RND statement. I thought I had already fixed that. Using CINT instead of INT should also work btw.

Reply 16 of 19, by pan069

User metadata
Rank Member
Rank
Member

If I'm not mistaken, GWBASIC literally interprets each line of code (i.e. "the text") as it executes it and does this over and over again. This is super slow. On the other hand, QuickBasic (and QBasic) compile the source code to an intermediate opcode (not x86 or JIT per se), it's this intermediate opcode that is executed by the runtime, which is much, much, faster.

Also, if I remember correctly, GWBASIC only uses floating point numbers for all numeric values even though it doesn't support an FPU whereas Q(uick)Basic supports integers etc. I could be wrong about this though...

Reply 17 of 19, by Peter Swinkels

User metadata
Rank Member
Rank
Member

Yes, the way GWBasic executes code is slow.

You’re mistaken, while variables are single precision floating points by default you can declare a variable as an integer. You can use the percentage or ampersant type declaration character to declare a variable as an integer. You can also declare variables with a name starting with specific letters to be integers using the DEFINT and DEFLNG statements.

Reply 18 of 19, by Peter Swinkels

User metadata
Rank Member
Rank
Member

@ripsaw8080:

I tried converting all the string based stuff to numeric arrays, but it doesn't appear to help much.

10 DEFINT A-Z
20 DIM MOVEDMAP(15)
30 DIM PIT(9, 15)
40 DIM ROTATEDMAP(15)
50 DIM SHAPEMAP(15)
60 DIM SHAPES(6, 15)
70 GOSUB 850
80 END
90 FULLROW = 0
100 FOR PITY = 0 TO 15
110 FULLROW = -1
120 FOR PITX = 0 TO 9
130 IF PIT(PITX, PITY) = 0 THEN FULLROW = 0: GOTO 150
140 NEXT PITX
150 IF FULLROW THEN REMOVEDROW = PITY: GOSUB 1050
160 NEXT PITY
170 RETURN
180 DROPRATE! = 1
190 SHAPE = CINT(RND * 6)
200 SHAPEANGLE = INT(RND * 4)
210 NEWANGLE = SHAPEANGLE
220 GOSUB 500
230 FOR BLOCK = 0 TO 15: SHAPEMAP(BLOCK) = ROTATEDMAP(BLOCK): NEXT BLOCK
240 GOSUB 420
250 SHAPEX = RANDOMSHAPEX
260 SHAPEY = -4
270 RETURN
280 COLOR 4: LOCATE 3, 2
290 IF GAMEOVER THEN PRINT "Game over.": LOCATE , 2: PRINT "Press Enter to play a new game."; ELSE PRINT "Score:"; SCORE;
300 RETURN
310 LINE (116, 32)-STEP(92, 145), 15, B
320 LINE (116, 32)-STEP(92, 0), 0
330 FOR PITY = 0 TO 15
340 Y = PITY * 9
350 FOR PITX = 0 TO 9
360 IF GAMEOVER THEN BLOCKCOLOR = 4 ELSE BLOCKCOLOR = PIT(PITX, PITY)
370 X = PITX * 9
380 LINE (X + 117, Y + 32)-STEP(9, 9), BLOCKCOLOR, BF
390 LINE (X + 118, Y + 33)-STEP(6, 6), 0, B
400 NEXT PITX: NEXT PITY
410 RETURN
420 INTENDEDSHAPEX = INT((RND * 10) - 1): RANDOMSHAPEX = 0
430 FOR XMOVE = 0 TO INTENDEDSHAPEX
440 FOR BLOCK = 0 TO 15: MOVEDMAP(BLOCK) = SHAPEMAP(BLOCK): NEXT BLOCK
450 XDIRECTION = 1: YDIRECTION = 0
460 GOSUB 600
470 IF CANMOVE THEN RANDOMSHAPEX = RANDOMSHAPEX + 1 ELSE RETURN
480 NEXT XMOVE
490 RETURN
500 IF NEWANGLE = 0 THEN FOR BLOCK = 0 TO 15: ROTATEDMAP(BLOCK) = SHAPES(SHAPE, BLOCK): NEXT BLOCK: RETURN
510 FOR BLOCK = 0 TO 15: ROTATEDMAP(BLOCK) = 0: NEXT BLOCK
520 FOR BLOCKX = 0 TO 3
530 FOR BLOCKY = 0 TO 3
540 IF NEWANGLE = 1 THEN NEWBLOCKX = 3 - BLOCKY: NEWBLOCKY = BLOCKX
550 IF NEWANGLE = 2 THEN NEWBLOCKX = 3 - BLOCKX: NEWBLOCKY = 3 - BLOCKY
560 IF NEWANGLE = 3 THEN NEWBLOCKX = BLOCKY: NEWBLOCKY = 3 - BLOCKX
570 ROTATEDMAP((4 * NEWBLOCKY) + NEWBLOCKX) = SHAPES(SHAPE, (4 * BLOCKY) + BLOCKX)
580 NEXT BLOCKY: NEXT BLOCKX
590 RETURN
600 CANMOVE = -1
Show last 114 lines
610 FOR BLOCKY = 0 TO 3
620 Y = 4 * BLOCKY
630 FOR BLOCKX = 0 TO 3
640 IF NOT MOVEDMAP(Y + BLOCKX) = 0 THEN GOSUB 670
650 NEXT BLOCKX: NEXT BLOCKY
660 RETURN
670 PITX = (SHAPEX + BLOCKX) + XDIRECTION: PITY = (SHAPEY + BLOCKY) + YDIRECTION
680 IF PITX >= 0 AND PITX < 10 AND PITY >= 0 AND PITY < 16 THEN IF NOT PIT(PITX, PITY) = 0 THEN CANMOVE = 0: RETURN
690 IF PITX < 0 OR PITX >= 10 OR PITY >= 16 THEN CANMOVE = 0: RETURN
700 RETURN
710 RANDOMIZE TIMER: KEY OFF: PLAY "ML L64": SCREEN 7: CLS : COLOR 9
720 GOSUB 1600
730 LOCATE 1, 1: PRINT "GWBlocks v1.03": PRINT "By: Peter Swinkels, ***2021***";
740 GOSUB 180
750 GAMEOVER = 0
760 FOR PITY = 0 TO 15
770 FOR PITX = 0 TO 9
780 PIT(PITX, PITY) = 0
790 NEXT PITX
800 NEXT PITY
810 SCORE = 0
820 GOSUB 310
830 GOSUB 280
840 RETURN
850 GOSUB 710
860 STARTTIME! = TIMER
870 WHILE NOT KEYV$ = CHR$(27)
880 KEYV$ = ""
890 WHILE KEYV$ = ""
900 IF (NOT GAMEOVER) AND (TIMER >= STARTTIME! + DROPRATE! OR STARTTIME! > TIMER) THEN GOSUB 1270: STARTTIME! = TIMER
910 KEYV$ = INKEY$
920 WEND
930 GOSUB 1540
940 IF KEYV$ = CHR$(27) THEN SCREEN 0: WIDTH 80: KEY ON: RETURN
950 IF GAMEOVER AND KEYV$ = CHR$(13) THEN GOSUB 710
960 IF (NOT GAMEOVER) AND (KEYV$ = "A" OR KEYV$ = "a") THEN GOSUB 1120
970 IF (NOT GAMEOVER) AND (KEYV$ = CHR$(0) + "K") THEN FOR BLOCK = 0 TO 15: MOVEDMAP(BLOCK) = SHAPEMAP(BLOCK): NEXT BLOCK: XDIRECTION = -1: YDIRECTION = 0: GOSUB 600
980 IF (NOT GAMEOVER) AND (KEYV$ = CHR$(0) + "K" AND CANMOVE) THEN SHAPEX = SHAPEX - 1
990 IF (NOT GAMEOVER) AND (KEYV$ = CHR$(0) + "M") THEN FOR BLOCK = 0 TO 15: MOVEDMAP(BLOCK) = SHAPEMAP(BLOCK): NEXT BLOCK: XDIRECTION = 1: YDIRECTION = 0: GOSUB 600
1000 IF (NOT GAMEOVER) AND (KEYV$ = CHR$(0) + "M" AND CANMOVE) THEN SHAPEX = SHAPEX + 1
1010 IF (NOT GAMEOVER) AND (KEYV$ = " ") THEN DROPRATE! = 0
1020 GOSUB 1430
1030 WEND
1040 RETURN
1050 FOR PITROW = REMOVEDROW TO 0 STEP -1
1060 FOR PITX = 0 TO 10 - 1
1070 IF PITROW = 0 THEN BLOCKCOLOR = 0 ELSE BLOCKCOLOR = PIT(PITX, PITROW - 1)
1080 PIT(PITX, PITROW) = BLOCKCOLOR
1090 NEXT PITX: NEXT PITROW
1100 SCORE = SCORE + 1
1110 RETURN
1120 IF SHAPEANGLE = 3 THEN NEWANGLE = 0 ELSE NEWANGLE = SHAPEANGLE + 1
1130 GOSUB 500
1140 FOR BLOCK = 0 TO 15: MOVEDMAP(BLOCK) = ROTATEDMAP(BLOCK): NEXT BLOCK
1150 XDIRECTION = 0: YDIRECTION = 0
1160 GOSUB 600
1170 IF CANMOVE THEN SHAPEANGLE = NEWANGLE: FOR BLOCK = 0 TO 15: SHAPEMAP(BLOCK) = ROTATEDMAP(BLOCK): NEXT BLOCK
1180 RETURN
1190 PLAY "N21"
1200 FOR BLOCKY = 0 TO 3
1210 Y = 4 * BLOCKY
1220 FOR BLOCKX = 0 TO 3
1230 PITX = SHAPEX + BLOCKX: PITY = SHAPEY + BLOCKY
1240 IF (PITX >= 0 AND PITX < 10 AND PITY >= 0 AND PITY < 16) AND (NOT SHAPEMAP(Y + BLOCKX) = 0) THEN PIT(PITX, PITY) = SHAPEMAP((4 * BLOCKY) + BLOCKX)
1250 NEXT BLOCKX: NEXT BLOCKY
1260 RETURN
1270 FOR BLOCK = 0 TO 15: MOVEDMAP(BLOCK) = SHAPEMAP(BLOCK): NEXT BLOCK
1280 XDIRECTION = 0: YDIRECTION = 1
1290 GOSUB 600
1300 IF CANMOVE THEN GOSUB 1320 ELSE GOSUB 1360
1310 RETURN
1320 GOSUB 1540
1330 IF DROPRATE! = 1 THEN SOUND 37, .3
1340 SHAPEY = SHAPEY + 1: GOSUB 1430
1350 RETURN
1360 GOSUB 1190
1370 GAMEOVER = (SHAPEY < 0)
1380 GOSUB 90
1390 GOSUB 310
1400 GOSUB 280
1410 IF NOT GAMEOVER THEN GOSUB 180: GOSUB 1430
1420 RETURN
1430 FOR BLOCKX = 0 TO 3
1440 FOR BLOCKY = 0 TO 3
1450 PITX = SHAPEX + BLOCKX: PITY = SHAPEY + BLOCKY
1460 IF PITX >= 0 AND PITX < 10 AND PITY >= 0 AND PITY < 16 THEN GOSUB 1490
1470 NEXT BLOCKY: NEXT BLOCKX
1480 RETURN
1490 BLOCKCOLOR = SHAPEMAP((4 * BLOCKY) + BLOCKX)
1500 IF BLOCKCOLOR = 0 THEN BLOCKCOLOR = PIT(PITX, PITY)
1510 LINE ((PITX * 9) + 117, (PITY * 9) + 32)-STEP(9, 9), BLOCKCOLOR, BF
1520 LINE ((PITX * 9) + 118, (PITY * 9) + 33)-STEP(6, 6), 0, B
1530 RETURN
1540 FOR BLOCKX = 0 TO 3
1550 FOR BLOCKY = 0 TO 3
1560 PITX = SHAPEX + BLOCKX: PITY = SHAPEY + BLOCKY
1570 IF PITX >= 0 AND PITX < 10 AND PITY >= 0 AND PITY < 16 THEN LINE ((PITX * 9) + 117, (PITY * 9) + 32)-STEP(9, 9), PIT(PITX, PITY), BF
1580 NEXT BLOCKY: NEXT BLOCKX
1590 RETURN
1600 RESTORE 1670
1610 FOR SHAPE = 0 TO 6
1620 FOR BLOCK = 0 TO 15
1630 READ SHAPES(SHAPE, BLOCK)
1640 NEXT BLOCK
1650 NEXT SHAPE
1660 RETURN
1670 DATA 0,0,0,0,3,3,3,3,0,0,0,0,0,0,0,0
1680 DATA 0,0,0,0,1,1,1,0,0,0,1,0,0,0,0,0
1690 DATA 0,0,0,0,6,6,6,0,6,0,0,0,0,0,0,0
1700 DATA 0,0,0,0,0,14,14,0,0,14,14,0,0,0,0,0
1710 DATA 0,0,0,0,0,2,2,0,2,2,0,0,0,0,0,0
1720 DATA 0,0,0,0,5,5,5,0,0,5,0,0,0,0,0,0
1730 DATA 0,0,0,0,4,4,0,0,0,4,4,0,0,0,0,0

I keep forgetting why I choose to use strings instead of arrays, initializing and copying arrays is a lot more hassle given the very limited support GWBasic offers for these. Can you believe there isn't even a function to check the bounds?

Reply 19 of 19, by Peter Swinkels

User metadata
Rank Member
Rank
Member

Okay, while I didn't really find an answer to my question I am going to close this project for now. The final version (for now) can be found at https://github.com/PeterSwinkels/GWBlocks .