Switch on a Commodore 64 and the first thing it tells you is the name of its programming language: **** COMMODORE 64 BASIC V2 ****, with 38911 BASIC BYTES FREE underneath. The machine had a three-voice sound chip and eight hardware sprites, yet the BASIC waiting at the cursor had no command for either. It had been written in the 1970s for computers that had neither.
That BASIC came from Microsoft. Tim Onosko’s 1983 guide to the 64 explained to new owners that Commodore BASIC was “a version of the language originally written for the company by Microsoft”, first written for the MITS Altair. Sheldon Leemon’s Mapping the Commodore 64 put the family tree precisely: the 64’s interpreter was “almost identical to the Microsoft BASIC interpreter found on the VIC-20”, and both were “slightly modified versions of PET BASIC 2.0”. The keyword tables in the three machines’ ROMs bear him out: the PET’s BASIC 2, the VIC-20’s and the Commodore 64’s list the same 76 tokens in the same order. The PET’s later BASIC 4.0 added fifteen disk commands, from DLOAD to DIRECTORY, that neither home computer got.
A $25,000 BASIC
Microsoft’s 6502 BASIC was finished in 1976 by Bill Gates and Ric Weiland. Commodore needed a BASIC for its new PET, and Jack Tramiel, Commodore’s founder, would not pay per machine. In his own account, given in 2007, Gates asked for $3 a unit: “I told him that the highest price I’m willing to pay is $25,000, and about six weeks later he came and took the $25,000.” Microsoft’s own history of the code gives the same figure, a flat fee of $25,000, and dates the licence to 1977.
It was a bargain that kept paying. The same interpreter, with Commodore’s changes, went into the VIC-20 and then the 64. Kit Spencer, who ran Commodore’s marketing in Britain, described it as “an operating system that we put on 20 million computers and it didn’t cost us anything in reality”. The deal also explains the missing commands. PET BASIC was built for a machine with a monochrome character screen and no sound chip. Commodore kept it for the VIC-20 and the 64 and did not add words for the new hardware.
Microsoft published the source of this BASIC on 3 September 2025: version 1.1 of BASIC M6502 8K, released on GitHub under the MIT licence. Microsoft’s announcement says this revision includes garbage-collector fixes that Commodore identified and that Commodore’s John Feagans and Gates made together in 1978. The source’s own change log names no one, but it dates its garbage-collection fixes to 1 July 1978 and records that one of them, for a bug where garbage collection “NEVER(!) COLLECTS TEMPS”, “WAS COMMODORE BUG #2”.
What the interpreter does with a line
When you type a numbered line and press RETURN, the C64 does not store the words you typed. It converts every keyword it recognises into a single byte, a token, from $80 (END) to $CB (GO). That is why ? works as a short form of PRINT, and why the Programmer’s Reference Guide points out that abbreviations “do NOT save any memory”: the stored program is the same either way. LIST turns the tokens back into words, always in full.
The screen editor works on a logical line of 80 characters, two rows of the 40-column screen. Abbreviations let you pack more than 80 characters of statements into one line, but the guide warns that such a line cannot be edited after it has been listed, because the listing is longer than the editor will read back.
The tokeniser is also the source of the best-known trap in the language. It looks for keywords everywhere outside quotes and REM, including inside variable names. SCORE contains OR; TOTAL begins with TO. Type this:
10 SC=5:PRINT SC
20 SCORE=10Line 10 prints 5. Line 20 stops with ?SYNTAX ERROR IN 20, because BASIC has stored it as SC, the token for OR, and E=10. The Programmer’s Reference Guide states the rule: variable names “may NOT contain keywords in the middle of variable names”. It also states the second rule that surprises people. Names can be any length, but “only the first two characters are considered significant”, so SC, SCALE and SCRAP are all the same variable.
The vocabulary
The BASIC ROM holds 76 tokens. Sixty-eight are words and eight are the symbols + - * / ↑ > = <. The guide’s own count is “a vocabulary of 65 keywords”. ST, TI and TI$ are reserved variables rather than tokens, and π has a token of its own.
| Group | Keywords |
|---|---|
| Program flow | END FOR TO STEP NEXT GOTO GO GOSUB RETURN IF THEN ON STOP WAIT |
| Data and variables | DATA READ RESTORE DIM LET DEF FN CLR |
| Screen and keyboard | PRINT INPUT GET TAB( SPC( POS CMD |
| Files and devices | LOAD SAVE VERIFY OPEN CLOSE PRINT# INPUT# (and GET#, which is GET followed by #) |
| Direct commands | RUN LIST CONT NEW REM |
| Memory and machine code | PEEK POKE SYS USR FRE |
| Numbers | SGN INT ABS SQR RND LOG EXP COS SIN TAN ATN |
| Strings | LEN STR$ VAL ASC CHR$ LEFT$ RIGHT$ MID$ |
| Logic | AND OR NOT |
There is no ELSE, no WHILE or REPEAT, no procedures with parameters, no error trapping and no command that names a colour, a sprite, a sound or a disk directory. Leemon counted error trapping among the features that were by then “standard on most home computers”.
ELSE is worth a closer look, because a line using it does not fail where you expect:
10 IF 1 THEN PRINT "YES" ELSE PRINT "NO"This prints YES 0 and only then stops with ?SYNTAX ERROR. BASIC reads ELSE as a variable called EL, prints its value, zero, and fails when it reaches the second PRINT.
Behaviour worth knowing
These were checked by running them on the Commodore 64’s own ROMs.
Operator precedence. Relational operators (=, <, >) are worked out before AND and OR. The joystick test people most often write, IF J AND 1=0, therefore means J AND (1=0), which is always zero. The brackets are needed:
10 J=PEEK(56320)
20 IF (J AND 1)=0 THEN PRINT "UP"
30 IF (J AND 16)=0 THEN PRINT "FIRE"
40 GOTO 10Location 56320 is the data port that reads the joystick in control port 2. Its bits go to 0, not 1, when a direction or the button is pressed.
The same slip is in Commodore’s own sprite notes. The Programmer’s Reference Guide suggests IF PEEK(V+30) AND X = X THEN to test whether one sprite has collided. Because X = X is always true (-1), the test fires whenever any sprite has collided. IF (PEEK(V+30) AND X) = X THEN does what the guide intended.
INPUT. Pressing RETURN without typing anything leaves the variable as it was and carries on. Typing letters into a numeric variable gives ?REDO FROM START and asks again. GET does not wait at all: it returns an empty string if no key is down, which is why games use it instead of INPUT.
STOP. The program halts with BREAK IN 160 (the line number, without the word “line”), and CONT carries on from there with every variable intact.
Stack depth. FOR loops and GOSUB calls share the 6502’s 256-byte stack. A FOR takes 18 bytes and a GOSUB five, so on a C64 about nine or ten FOR loops can be nested, or about 23 GOSUBs, before ?OUT OF MEMORY appears with plenty of memory free. A subroutine that jumps out with GOTO instead of RETURN leaves its entry on the stack, and enough of them cause the same error.
NEXT without a variable is legal and slightly faster: FOR I=1 TO 2000:NEXT took 125 jiffies (sixtieths of a second) against 162 for NEXT I.
RND. The sign of the argument matters, not its size. A positive argument gives the next number in the sequence. A negative one reseeds from the number itself, so RND(-1) followed by RND(1) gives the same series every time, which is handy for debugging. RND(0) takes its value from the hardware timers and makes that value the new seed, so it also changes what RND(1) gives next; Leemon warned that this form “doesn’t really work right on the 64 either” and suggested RND(-TI) to seed from the jiffy clock. See Random numbers.
STR$ and VAL. STR$(123) is " 123", with a leading space where the sign would go. VAL("12ABC") is 12 and VAL("ABC") is 0. INT rounds down, so INT(-3.7) is -4.
Commas in PRINT move to the next 10-column zone, four to a line.
Memory and speed
A C64 program starts at 2049 ($0801). Simple variables follow the program, arrays follow those, and string text is stored downward from the top of BASIC memory, just below 40960 ($A000). When the free space between them runs out, BASIC collects its garbage strings, and only if that fails does it report ?OUT OF MEMORY.
Every simple variable takes seven bytes: two for the name and five for the value. Leemon drew the lessons in Mapping the Commodore 64:
- Integer variables such as
A%save nothing: “nonarray integer variables take up no less space than floating point variables”, and because most commands convert them to floating point first, they are usually slower. Adding 1 toA%a thousand times took 302 jiffies; doing the same toAtook 252. - Integer arrays do save space:
DIM C%(99)took 207 bytes andDIM D(99)took 507. - BASIC finds a variable by searching the table from the start, in the order the variables were created, so frequently used variables should be created first.
The biggest cost is interpretation itself. Filling the 1,000-character screen with POKE 1024+I,1 in a FOR loop took 563 jiffies, over nine seconds. Twenty-five PRINT statements writing 39 characters each took 42 jiffies, well under a second, because PRINT does its looping in machine code. POKE is the right tool for changing a single cell or a hardware register, not for bulk drawing. PRINT vs POKE goes into the trade-off.
The POKE culture
Without commands for the hardware, everything interesting on a C64 was done by writing numbers into its chips. The border colour is POKE 53280,0; the VIC-II graphics chip occupies 53248 to 53294, the SID sound chip starts at 54272, and the screen itself is at 1024 to 2023, with its colours at 55296 to 56295. Onosko summed it up in 1983: “Programming even the most rudimentary graphics and sounds on the 64 is an endless job of PEEKing and POKEing.”
That shaped how a generation learned the machine. Commodore’s own Programmer’s Reference Guide told readers that because sprites were handled by hardware “it is even possible to write a good quality game in BASIC!”, and showed them how with example programs built from POKE statements. Whole books, Mapping the Commodore 64 among them, existed to explain what each address did.
When BASIC ran out, programmers used SYS to call machine code at an address, often at 49152 ($C000), a 4K block BASIC does not use. USR(X) also calls machine code, through the address stored at 785 and 786, and returns whatever is left in BASIC’s floating-point accumulator. A few ROM routines are useful straight from BASIC:
| Command | Effect |
|---|---|
SYS 58692 |
Clear the screen ($E544) |
SYS 58648 |
Reset the screen editor and clear the screen ($E518); this also puts the text colour back to light blue |
SYS 64738 |
Reset the whole machine ($FCE2, the address in the 6510’s reset vector) |
Others went the other way and replaced the BASIC. Commodore itself sold Simons’ BASIC, whose “114 new commands”, Onosko wrote, “almost makes the 64 into an entirely new computer”.
Sources
- Commodore 64 Programmer’s Reference Guide (Commodore Business Machines, 1982): the 65 keywords (p. 2); variable names, two significant characters and embedded keywords (p. 7); hierarchy of operations (p. 15); abbreviations, tokens and the 80-character line (“How to Crunch BASIC Programs”, p. 24);
STOPandBREAK IN(chapter 2); sprites and “a good quality game in BASIC” (chapter 3);IF PEEK(V+30) AND X = X(chapter 3, sprite notes). - Sheldon Leemon, Mapping the Commodore 64 (COMPUTE! Books, 1984): introduction to the BASIC ROM at 40960 ($A000), lineage from PET BASIC 2.0 and missing features; locations 45-46 (VARTAB), seven-byte variables, integer variables and variable search order; locations 139-143 (RNDX),
RNDandRND(-TI); location 57495 ($E097),RND(0)and the new seed; locations 785-786 (USRADD). - Tim Onosko, Commodore 64: Getting the Most From It (Robert J. Brady, 1983): Microsoft and the Altair, BASIC V2 (p. 51); Simons’ BASIC and PEEKing and POKEing (p. 190).
- Tim Danton, The Computers That Made Britain (Raspberry Pi Press, 2021), “Commodore PET 2001” chapter: Jack Tramiel’s 2007 account of the $25,000 fee; Kit Spencer on the deal.
- Agam Shah, “Commodore 64 celebrates its 25th anniversary”, InfoWorld, 12 December 2007: Tramiel’s account at the Computer History Museum (web, secondary). https://www.infoworld.com/article/2332757/commodore-64-celebrates-its-25th-anniversary.html
- Scott Hanselman and Stacey Haffner, “Microsoft Releases Historic 6502 BASIC”, Microsoft Open Source Blog, 3 September 2025: Gates and Weiland, the 1977 flat fee, Feagans’s fixes, the MIT-licensed release (web). https://opensource.microsoft.com/blog/2025/09/03/microsoft-open-source-historic-6502-basic/ and https://github.com/microsoft/BASIC-M6502
- Microsoft,
m6502.asmin BASIC-M6502 (GitHub, published 3 September 2025): the change log’s entries dated 1 July 1978, including the garbage-collection fix marked “COMMODORE BUG #2”. https://github.com/microsoft/BASIC-M6502/blob/main/m6502.asm - Commodore 64 BASIC ROM (901226-01) and KERNAL ROM (901227-03); PET BASIC 2 and 4 and VIC-20 BASIC ROMs: keyword tables, token values and the ROM routines listed above, including the KERNAL ROM’s RND routine at $E097, whose
RND(0)path stores the timer value in the seed at $8B. Timings, stack depths and memory figures were measured by running the programs on these ROMs.
See also
- Microsoft
- Commodore 64
- Commodore VIC-20
- Simons’ BASIC
- PETSCII chart: character codes for
PRINTandCHR$ - Screen memory
- Variables and memory
- VIC-II
- Collision detection