Results 1 to 14 of 14

Thread: [RESOLVED] GWBasic code optimization question->

  1. #1

    Thread Starter
    Frenzied Member
    Join Date
    Feb 2003
    Posts
    1,807

    Resolved [RESOLVED] GWBasic code optimization question->

    Okay, feeling like playing with really old BASIC dialects I converted a program I wrote in Quick Basic 4.5 to GWBasic 2.23. In DOSBox it runs at an acceptable speed in Quick Basic, in GWBasic however there is a slow down which makes the game hard to play properly. I am using an emulated speed of 66 Mhz which is already unrealistically fast for someone who would be using GWBasic. My question is: can the code posted below be modified to run more smoothly in GWBasic at 66 Mhz at even slower CPU speeds?

    Code:
    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
    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

  2. #2
    PowerPoster yereverluvinuncleber's Avatar
    Join Date
    Feb 2014
    Location
    Norfolk UK (inbred)
    Posts
    2,235

    Re: GWBasic code optimization question->

    Quick question, have you ever run the original program on a true x86 at 66mhz or similar? I am just verifying that your expectations that your GWBASIC program will perform well on that sort of system are really founded well in reality? Are you using your own emulator and have you tested it on others to see how it performs there?

    I would just check my assumptions first.

  3. #3

    Thread Starter
    Frenzied Member
    Join Date
    Feb 2003
    Posts
    1,807

    Re: GWBasic code optimization question->

    1. No, I haven't.
    2. Yes, it is possible that my expectation are unrealistic for GWBasic on an actual 80486 DX 66 MHz. If they are however, then I have trouble understanding how much requiring any degree of speed could be done on what must have been the 4.7 MHz - 25 MHz 8086/8088/80286/80386 systems originally used for GWBasic.
    3. I am not using my own emulator. Just DOSBox and nothing else.

    However, if anyone sees a flaw in my code that causes an unnecessary slowdown, please let me know. Okay?

  4. #4
    PowerPoster yereverluvinuncleber's Avatar
    Join Date
    Feb 2014
    Location
    Norfolk UK (inbred)
    Posts
    2,235

    Re: GWBasic code optimization question->

    To me, your code looks very reasonable. I appreciate why you believe it should run well on a machine of the original PC spec. and I would agree with you. That is why I think it might be a flaw with DOSbox.

  5. #5

    Thread Starter
    Frenzied Member
    Join Date
    Feb 2003
    Posts
    1,807

    Re: GWBasic code optimization question->

    I put a request on the DOSBox forums asking for people who own 80's/90's hardware to test my code. I am curious what the result will be.

  6. #6
    Sinecure devotee
    Join Date
    Aug 2013
    Location
    Southern Tier NY
    Posts
    6,582

    Re: GWBasic code optimization question->

    You're saying Quick Basic 4.5, which produces a Compiled executable.
    GWBASIC is Interpreted so it is going to be much slower. I would think minimally it would be at 10x slower on most things and could be quite a bit slower in others.
    Also, the interpreted BASIC would use the BIOS to handle a lot of the screen I/O so that is often a big slow down.

    The original Quick BASIC compilers also used the BIOS routines for screen I/O originally so were relatively slow at doing screen updates, especially for a compiled language. Since there wasn't competition in the BASIC arena at the time, they just used the same BIOS calls their previous interpreted BASICs used.

    Since the IBM PCs and compatibles had a fairly standard set of possible video cards, Turbo Basic from Borland made the effort to include code to detect the video card and compile code to write directly to the video card, bypassing the BIOS routines. That is the primary reason for the "Turbo" in the name. Where you could watch the text being written to the screen line by line with the Microsoft Quick Basic Compiler, the same program in Turbo Basic the whole screen of text would appear to just show up instantly.

    That forced Microsoft to quickly do the same in their later releases of Quick Basic, as they didn't want Borland making inroads into their BASIC monopoly of the time.

    Anyway, Quick BASIC 4.5 would thus have two different capabilities which would make those programs much faster than the interpreted versions of BASIC could be, one being compiled, and two bypassing the BIOS.
    I don't know if there were later interpreted versions of BASIC that bypassed the BIOS, but expecting a graphical game in GWBASIC to have any parity on speed compared to the same game being a done in a compiled version of BASIC is not realistic.
    "Anyone can do any amount of work, provided it isn't the work he is supposed to be doing at that moment" Robert Benchley, 1930

  7. #7

    Thread Starter
    Frenzied Member
    Join Date
    Feb 2003
    Posts
    1,807

    Re: GWBasic code optimization question->

    Passel,

    Thank you for your answer. I haven't tried compiling my program with Quick Basic although I suspect it would run even faster as an *.exe file. I am going to try compiling the code with the Microsoft Basic compiler and see if that produces an executable running at an acceptable speed.

    This however means GWBasic's graphics statements are suitable only for simple graphics that change with a minimal interval of a few seconds only on early computers. I had forgotten just how primitive it all was way back then. I guess I got spoiled with QBasic 1.0 running on a 80386 at a whopping 25 Mhz when I started programming. I have worked with older Basic's on much slower machines though.

    yours,
    Peter Swinkels

  8. #8
    PowerPoster yereverluvinuncleber's Avatar
    Join Date
    Feb 2014
    Location
    Norfolk UK (inbred)
    Posts
    2,235

    Re: GWBasic code optimization question->

    I am sure Passel may be on to something, always good to check your assumptions first. To confirm, your QB program is also interpreted?

    I remember that there were a couple of attempts at a GWbasic compiler, so you could try one of those.

  9. #9

    Thread Starter
    Frenzied Member
    Join Date
    Feb 2003
    Posts
    1,807

    Re: GWBasic code optimization question->

    1. Yes, Passel is probably right.
    2. Indeed, unwarrented assumptions can make you overlook something obvious.
    3. Apparently one official GWBasic compiler has been released by Microsoft during the 80's. I am going to download and try it once I have the time and access to my pc again.

  10. #10

    Thread Starter
    Frenzied Member
    Join Date
    Feb 2003
    Posts
    1,807

    Re: GWBasic code optimization question->

    Okay, I can find Bascom 2.20 from 1986... which does not even support low resolution EGA... - Does anyone know whether there was a more recent version that did support EGA? Yes, I could compile it was the Quick Basic 4.5 compiler but that is not the point of what I am trying to do.

  11. #11
    Sinecure devotee
    Join Date
    Aug 2013
    Location
    Southern Tier NY
    Posts
    6,582

    Re: GWBasic code optimization question->

    I don't understand. You said you wrote it in Quick Basic 4.5, but you didn't create an executable to run the game?
    So, you always ran it from the IDE?

    Even if that is the case, it was still converted to pcode which is much more efficient than the older interpreters.
    Even QBasic, which replaced GWBasic and was included with the DOS 5.0 and above, and included in the Windows OS up to, but not including Windows 2000, wasn't a strict interpreter. It couldn't produce stand alone executables, but since it was based on Quick Basic 4.5 it still produced and ran pcode in an IDE environment.

    The older Microsoft Basic interpreters were implemented using essentially a linked list. Each line started with a two byte line number followed by a statement length value and then the statement itself. The length value could be added to the current address to get to the start of the next statement (i.e. to the line number of the next statement).

    When you did a GOTO or GOSUB to a line number, if the line number was greater than the current line number, the current offset would be added to the current address to get the address of the next statement. The line number would be compared to the one you wanted, and if it didn't match, the process would repeat, i.e. it would read the statement length, add it to the current address and then check to see if this statement number was the one you were "going to".

    Bottom line, when you did a GOTO a line number, it didn't directly jump to the line number, it had no way of knowing where that line was in memory directly. It had to follow the "links" from statement to statement until if found the line you designated.

    The other case, where you were doing a GOTO to a line that was less than your current statement number, was potentially much worse. There was no way to follow "links" backwards to a previous line, so the interpreter had to start with the first line of the program, which was at a fixed starting address, and follow the "links" statement by statement until if found the line.

    That is why a common optimization was to have an immediate GOTO or GOSUB at the beginning of the program to jump to a line number near the end of the code, and then do all your initialization there, since this should only need to be done once.
    Then the code you would run most should be near the beginning and the routines that were called most frequently should be the first ones in the file, so they could be found relatively quickly.

    That is one of the reasons that pcode was developed because it was a vast improvement on the Interpreter because the code was processed like a compiler would and GOTOs and GOSUBs and other branches in the code could be examined and jumps written into the pcode to branch to specific address directly saving a lot of time in just the basic process of branching around within the code. Also the memory space available by the time pcode was implemented allowed the management of variables and the other mechanics of the computer language implementation made it more efficient than the old interpreters, which were usually limited to less than 64K of memory total.

    The p-code is still technically a form of interpreted code, but it is a much more efficient form of interpretation giving a huge boost in speed over the old interpreters, placing it firmly in the middle ground between old interpreted code and compiled code.

    I would expect a relatively complicated program that was written in GWBASIC would run much quicker in QBasic (the replacement BASIC "interpreter" provided with DOS 5.0 and later), because of the switch to using a p-code generator and interpreter at that time (in addition to the switching from BIOS screen I/O to direct screen I/O at the same time).
    "Anyone can do any amount of work, provided it isn't the work he is supposed to be doing at that moment" Robert Benchley, 1930

  12. #12

    Thread Starter
    Frenzied Member
    Join Date
    Feb 2003
    Posts
    1,807

    Re: GWBasic code optimization question->

    Yes, I always run the game from the Qb45 IDE.

    Aha, I see so the way the Qb45 IDE interprets code is more efficient, that explains a lot.

    So I need to move frequently used procedures to the bottom of my code because forward jumps are more efficient?

    If and when I get around to it I will look into this. Right now I think the game runs in an acceptable fashion except for the "quick dropping of bricks" (activated by pressing the space bar.)

  13. #13

    Thread Starter
    Frenzied Member
    Join Date
    Feb 2003
    Posts
    1,807

    Re: GWBasic code optimization question->

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

    https://github.com/PeterSwinkels/GWB...n/GWBLOCKS.BAS

    There appears to be a slight increase in speed.

  14. #14

    Thread Starter
    Frenzied Member
    Join Date
    Feb 2003
    Posts
    1,807

    Re: GWBasic code optimization question->

    Okay, while I didn't really manage to fix the speed issue I did fix a few other bugs. Final version (for now): https://github.com/PeterSwinkels/GWBlocks.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width