02.03.08

Reading Old GW-Basic Programs

Posted in personal, programming at 3:50 am by danvk

I found a disk image I’d made of an old hard drive of mine today (circa 1995) and had some fun browsing through my files. Back then, I was programming in a combination of QBASIC and GW-BASIC. It’s easy to read old QBASIC programs, since QB saved code as human-readable text.

Not so, GW-BASIC. To save space, it stored code in a compact, binary format. This seems like an unnecessary optimization now, but back in 1984 it made a lot of sense. GW-BASIC was an interactive environment, and it stored all your code in memory. Memory was a scarce resource at the time, so every byte counted. Hence the binary format.

I wanted to read my old GW-BASIC programs, so I dug around and found this discussion of the GW-BASIC binary file format. It’s incredibly detailed, which let me whip up a decoder in Python over two solid hours of hacking. Without further ado, here it is:

GW-Basic Program Decoder

For a sample decoding, see below the fold.

Here’s a 20-questions style program I wrote on August 15, 1995:

   10 CLS
   20 DIM G$(15),RP(15),RD(15)
   30 PRINT "In this game I will make up a three digit code. The code will be between 100 and 999. You have 15 guesses. After each guess, I will tell you how many digits are correct and how many are in the right position. If you enter an invalid guess, ";
   35 PRINT "or a duplicate answer, I will let you try again. "
   40 PRINT
   50 PRINT "Press any key when ready. . . "
   60 GOSUB 1000
   70 CLS
   80 GOSUB 2000
   90 IF CODE < 100 OR CODE >999 THEN GOTO 80
  100 CODE$=RIGHT$(STR$(CODE),3)
  110 D$=CODE$
  120 FLAG=0
  130 GOSUB 1500
  140 IF FLAG=1 THEN GOTO 80
  150 FOR I=1 TO 15
  160     CLS
  170     IF I=1 THEN GOTO 230
  180     FOR J=1 TO I-1
  190             PRINT "Guess #";J;": ";G$(J);". results: ";
  200             PRINT "digits: ";RD(J);"positions: ";RP(J)
  210     NEXT J
  220     REM
  230     PRINT
  240     PRINT "enter guess #";I;
  250     INPUT G
  260     IF G<100 OR G>999 THEN 240
  270     G$(I)=STR$(G)
  280     IF LEN(G$(I))=0 THEN GOTO 240
  290     G$(I)=RIGHT$(G$(I),3)
  300     D$=G$(I)
  310     FLAG = 0
  320     GOSUB 1500
  330     IF FLAG=1 THEN GOTO 240
  340     FLAG=0
  350     GOSUB 1400
  360     IF FLAG=1 THEN 240
  370     GOSUB 1100
  380     IF RP(I)<>3 THEN GOTO 410
  390     PRINT "Good job!! You guessed the code in";I;"try's."
  400     END
  410 NEXT I
  420 PRINT "Your guesses are up!"
  430 PRINT "The code was";CODE$
  440 END
 1000 R$=INPUT$(1)
 1010 RETURN
 1100 FOR K=1 TO 3
 1110    FOR L=1 TO 3
 1120            G$=MID$(G$(I),K,1)
 1130            C$=MID$(CODE$,L,1)
 1140            IF G$<>C$ THEN GOTO 1170
 1150            IF K<>L THEN RD(I)=RD(I)+1
 1160            IF K=L THEN RP(I)=RP(I)+1
 1170    NEXT L
 1180 NEXT K
 1190 RETURN
 1400 IF I=1 THEN RETURN
 1410 FOR K=1 TO I-1
 1420    IF G$(I)=G$(K) THEN FLAG=1 : RETURN
 1430 NEXT K
 1460 RETURN
 1500 REM
 1510 FOR K=1 TO 3
 1520    FOR L=K+1 TO 3
 1530            IF MID$(D$,K,1)=MID$(D$,L,1) THEN FLAG=1: RETURN
 1540    NEXT L
 1550 NEXT K
 1560 RETURN
 2000 OPEN "r", 1, "b:amunt.ran", 2: FIELD 1, 2 AS T$
 2010 IF LOF(1)=0 THEN CODE = INT(100+RND*999): T$=MKI$(0): GOTO 2050
 2020 GET 1,1
 2030 FOR I=1 TO CVI(T$): CODE=RND:NEXT
 2040 CODE = INT(100+RND*999)
 2050 LSET T$=MKI$(CVI(T$)+1): PUT 1,1
 2060 CLOSE #1: RETURN

Oh, for the days of unstructured programming and rampant use of the “GOTO” statement. The convention was to number your lines 10, 20, 30, … so that you could go back and add extra lines between your originals. Hence line 35 above. I must have been really ambitious jumping to line 1000!

2 Comments

  1. aqeel said,

    April 12, 2010 at 12:01 am

    please give this version

  2. CVI Program said,

    May 27, 2011 at 6:03 am

    Hello,
    I just wanted to take a minute to tell you that you have a great site!
    Keep up the good work…..