Classic Line-Numbered BASIC
You will meet line-numbered code in old books, magazines, emulators, and legacy systems.
You will meet line-numbered code in old books, magazines, emulators, and legacy systems. QB64-PE and FreeBASIC (-lang qb) can still run most of it.
5.1 Line numbers and the immediate mode
In GW-BASIC, Commodore, and Applesoft BASIC, a line typed with a number is stored in the program; a line without a number executes immediately (direct mode).
Common commands in classic interpreters:
Run the program from the lowest line number LIST / LIST 100-200 Show the program (or a range) Erase the program in memory SAVE "NAME" / LOAD "NAME" Save / load a program Automatic line numbering (GW-BASIC)
Renumber lines and fix GOTO/GOSUB targets (GW-BASIC) DELETE 100-200 Delete a range of lines Continue after STOP or break TRON / TROFF
Trace on/off — print each line number as it executes Programmers numbered lines in steps of 10 (10, 20, 30...) so they could insert lines later (15, 16...). Typing a line with an existing number replaces it.
PRINT 2 + 2 : REM direct mode: prints 4 at once
10 PRINT "HELLO" : REM stored as line 10
20 GOTO 10 : REM infinite loop (press Ctrl+Break / RUN-STOP to stop)5.2 GOTO, IF...THEN line, and loops
LET was required in early BASICs and is optional in later ones. IF ... THEN 30 jumps to line 30.
10 REM COUNT FROM 1 TO 5 USING GOTO
20 LET I = 1
30 PRINT I
40 LET I = I + 1
50 IF I <= 5 THEN 30
60 END5.3 GOSUB and RETURN — the original subroutines
GOSUB 200 jumps to line 200 and remembers where it came from; RETURN goes back to the statement after the GOSUB. There are no parameters and no local variables — all variables are global.
10 REM GUESS THE NUMBER (CLASSIC STYLE)
20 RANDOMIZE TIMER
30 N = INT(RND * 100) + 1: T = 0
40 INPUT "YOUR GUESS"; G
50 T = T + 1
60 GOSUB 200
70 IF G <> N THEN 40
80 PRINT "CORRECT IN"; T; "TRIES"
90 END
200 REM ---- SUBROUTINE: GIVE A HINT ----
210 IF G < N THEN PRINT "TOO LOW"
220 IF G > N THEN PRINT "TOO HIGH"
230 RETURN5.4 ON...GOTO and ON...GOSUB
If C is 2, control goes to the second line number in the list.
10 INPUT "CHOOSE 1-3"; C
20 ON C GOSUB 100, 200, 300
30 GOTO 10
100 PRINT "ONE": RETURN
200 PRINT "TWO": RETURN
300 PRINT "THREE": RETURN5.5 Other classic features
Two-letter variable names: many 8-bit BASICs (Commodore, Applesoft) only looked at the first two characters, so SCORE and SCALE were the same variable SC.
Keyword crunching: FORI=1TO10:PRINTI:NEXT — spaces were optional and often omitted to save memory.
DEF FN: one-line functions — 10 DEF FNSQ(X) = X * X then PRINT FNSQ(5). QB64 still supports DEF FN.
PEEK/POKE/SYS/CALL/USR to access memory and machine code (Chapter 29).
5.6 Why structured BASIC replaced line numbers
In 1968 Edsger Dijkstra's letter "Go To Statement Considered Harmful" argued that unrestricted jumps make programs hard to understand. Line-numbered BASIC programs often became spaghetti code:
Hard to read: control jumps anywhere; you cannot see a loop's structure.
Hard to change: inserting code means renumbering and fixing jump targets.
No local variables: every subroutine can accidentally overwrite another's variables.
No parameters: subroutines communicate through global variables.
No meaningful names: GOSUB 2000 says nothing; CALL DrawBoard says everything.
Structured BASIC (BBC BASIC, True BASIC, QuickBASIC) added named procedures, block statements, local variables, and made line numbers optional. Compare:
FreeBASIC: line numbers and GOSUB require -lang qb (or fblite). In the default -lang fb, use Sub/Function. VB.NET: no line numbers, no GOSUB; GoTo with labels still exists but is rarely used.
' Structured QB64 version of the guessing game
RANDOMIZE TIMER
secret = INT(RND * 100) + 1
tries = 0
DO
INPUT "Your guess"; guess
tries = tries + 1
GiveHint guess, secret
LOOP UNTIL guess = secret
PRINT "Correct in"; tries; "tries"
END
SUB GiveHint (g, s)
IF g < s THEN
PRINT "Too low"
ELSEIF g > s THEN
PRINT "Too high"
END IF
END SUB