Category Archives: 6502

The 6502 in "The Terminator"

In the first Terminator movie, the audience sees the world from the T-800′s view several times. It is well-known that in two instances, there is 6502 assembly code on the T-800′s HUD, and many sites have analyzed the contents: It’s Apple-II code taken from Nibble Magazine. Here are HD versions of the shots, thanks to Dominik Wagner:

This is the first assembly snippet:


This is the second assembly snippet:


There are some assembly equates:


On the left, these are the assembled opcodes of the second assembly listing, reaching from “LDY#10″ to “SEC”. On the right, there is output of a run of the checksum application Key Perfect on a file names “OVLY.OBJ”, which prints a 16 bit checksum for every 0×50 bytes:



cbmbasic 1.0 with Plugins

I moved cbmbasic development to SourceForge and released version 1.0, which has the following added features:

  • RDTIM/SETTIM support (George Talbot)
  • LOAD”$” on Win32 (Lorenzo)
  • RND() is now random (Wolfram Sang)
  • The C code now hooks into the cbmbasic plugin infrastructure. This lets developers add additional statements, functions etc. Right now, you can turn this on with “SYS 1″ (turn off with “SYS 0″), and use the new statements LOCATE y,x (set cursor position), SYSTEM string (run command line command) and the extended WAIT port,mask, which implements the Bill Gates easter egg.

The Ultimate Commodore 64 Talk: Video Recording

Link: 25C3: The Ultimate Commodore 64 Talk

Thanks to F!XMBR for providing the flash video.

Additionally, here are a few links to the video recording of the talk:

Bittorrent (OGG), Mirror 1 (OGG), Mirror 1 (WMV), Mirror 2 (OGG), Mirror 2 (WMV), Mirror 3 (OGG), Mirror 3 (WMV), Mirror 4 (OGG), Mirror 4 (WMV), Mirror 5 (OGG)

The OGG version seems to be in better quality. I’ll post new links as soon as the talk is available in a standards-compliant version (MPEG-4).

P.S.: If you enjoyed this, you might also like my “Reverse Engineering the MOS 6502 CPU” talk.

The Ultimate Commodore 64 Talk @25C3

Update: Video recording available.

I am going to present The Ultimate Commodore 64 Talk (Everything about the C64 in 64 Minutes) at the 25th Chaos Communication Congress 2008 (25C3) in Berlin on 29 Dec 2008. The following article, which will be printed in the Congress Proceedings, is supposed to give you an overview of what I am going to talk about. If you cannot attend 25C3, you will be able to watch a recoding afterwards, which I am going to link to. And yes, it is a lot of information, and it’ll be about 256 slides (in 64 minutes) – that is exactly the challenge. If you don’t believe I cannot present all this in a way that the audience can understand it: Watch it!


Retrocomputing is cool as never before. People play C64 games in emulators and listen to SID music, but few people know much about the C64 architecture and its limitations, and what programming was like back then. This paper attempts to give a comprehensive overview of the Commodore 64, including its internals and quirks, making the point that classic computer systems aren’t all that hard to understand – and that programmers today should be more aware of the art that programming once used to be.

Commodore History

The company that became Commodore Business Machines was founded in 1954 by Jack Tramiel. The company specialized on electronic calculators, and in 1976, Commodore bought the chip manufacturer MOS Technology and decided to have Chuck Peddle from MOS evolve their KIM-1 computer kit (a design that demos their new MOS 6502 8 bit CPU) into the Commodore PET series: computers with built-in monitors for the home, school and small business market that ended up competing with devices from Atari and Apple.

In 1981, Commodore introduced the VIC-20, a 5 KB stripped down monitorless computer-in-the-keyboard design based on the PET for the home computer market. This was followed by the (incompatible) higher-end Commodore 64 in 1982 that included more PET features, came with 64 KB of RAM (an immense amount compared to the rest of the market) and was very aggressively priced at US$595 beating the competition by a factor of two. This was made possible by designing and building most of the system in-house.

Although some features of the C64 were taken from the PET models of the time, it had to be connected to a TV set, which only made 40 columns of text possible (as opposed to then-current 80 columns on the PET). Also, the BASIC 4.0 codebase was stripped down to the old 2.0 feature set to make it fit into 8 KB.

In the beginning, the C64 did well in the competition. The superior but compatible C128 from 1985 did well, too, but was never more popular than the C64, which continued to be sold. The direct successor to the low-end VIC-20, the 1984 Plus/4 and its siblings, the C16 and the C116, failed, mostly because they were incompatible with the C64, which at that time already had a remarkable software library.

A few years after the introduction, the C64 was still offered as a low-end alternative to the Commodore Amiga, and while it became less popular in the USA, it gained more and more popularity in Europe. In the early 90s, when manufacturing costs of a C64 were as low as $25, it gained a second life in Eastern Europe. Production did not end until the liquidation of Commodore in 1994. According to the 1993 Annual Report, 17 million C64 had been produced in by then, as well as 4.5 million C128.

Look and Feel

A C64 only needs to be connected to power and a TV set (or monitor) to be fully functional. When turned on, it shows a blue-on-blue theme with a startup message and drops into a BASIC interpreter derived from Microsoft BASIC. In order to load and save BASIC programs or use third party software, the C64 requires mass storage – either a “datasette” cassette tape drive or a disk drive like the 5.25″ Commodore 1541.

Unless the user really wanted to interact with the BASIC interpreter, he would typically only use the BASIC instructions LOAD, LIST and RUN in order to access mass storage. LOAD”$”,8 followed by LIST shows the directory of the disk in the drive, and LOAD”filename”,8 followed by RUN would load and start a program. If a tape drive is connected, LOAD and RUN will launch the first program on tape – pressing SHIFT and the RUN/STOP key has the same effect.

By default, typing characters without SHIFT will result in upper case characters being shown on the screen. This can be changed by pressing the Commodore key and SHIFT at the same time, which switches to the upper/lower character set. This behavior is due to the fact that the first PET only had uppercase characters, and that BASIC required keywords unshifted – but all tutorials taught them as uppercase. This is also the reason why in Commodore’s version of ASCII, called PETSCII, the codes for uppercase and lowercase characters are reversed.

The text based user interface is not a line editor, but a screen editor, i.e. the cursor can be moved freely on the screen, and pressing RETURN on a line with existing text will present the text to the application as if it were just typed in.

While a physical screen line is only 40 characters, the screen editor logic can extend it to a logical 80 characters – whenever a character is entered on the 40th column, the rest of the screen is moved down by one line, ”opening” a line that extends the previous line to 80 characters.

The C64 screen editor supports selecting one out of 16 color for the foreground text by pressing the key combinations Ctrl+1 to Ctrl+8 and Commodore+1 to Commodore+8. Reverse text mode can be turned on and off with Ctrl+9 and Ctrl+0.

Ports and Connections

The C64 has a whole range of connection possibilities. On the side, it has two 9 pin Atari-style joystick connectors that can also be used for a mouse, light pens or paddles. On the back, there is the expansion port, which exports the complete processor bus, allowing not only game cartridges but also cartridges with I/O chips that map themselves into the CPU’s address space – or even cartridges that completely replace the CPU. For a conventional TV connection, there is an RCA connector that outputs an RF signal. For monitors, there is an extra DIN connector that carries separate chroma, luma and audio signals (although not 100% S-Video, it is compatible with a lot of S-Video equipment).

For connecting Commodore compatible printers and disk drives, there is a DIN connector for the IEC bus. There is also a dedicated connector for datasette drives. The ”User Port” consists of several GPIO pins that can be used for custom hardware projects, or as a RS-232 port (with TTL levels), for which support exists in the ROM.

Board

On the C64 motherboard, there is a dedicated IC each for the main tasks. There is the MOS 6510 CPU, eight 64 KBit RAM chips (later consolidated into 2), three ROM chips with KERNAL (I/O library), BASIC and the character set (KERNAL and BASIC were later consolidated), two 6526 CIA I/O controllers (one for keyboard and joystick, one for the IEC bus and the user port), the 6581 SID sound chip, and the 6567/6569 VIC video chip, as well as the RAM chip that holds the 512 bytes of Color RAM.

All non-RAM chips are custom chips designed manufactured by MOS, Commodore’s inhouse chip company.

Address Space

The 8 bit C64 design has a 16 bit address bus, allowing the CPU to address 64 KB of memory. Since the C64 has 64 KB of RAM, filling the complete address space, ROM and I/O chips are mapped into regions of the address space that are shared with RAM: The CPU can switch these regions between RAM and a second or third mapping. These regions are as follows:

  • $0000-$9FFF: RAM
  • $A000-$BFFF: RAM or BASIC ROM
  • $C000-$CFFF: RAM
  • $D000-$DFFF: RAM or memory mapped I/O chips or character ROM
  • $E000-$FFFF: RAM or KERNAL ROM

In contrast to CPUs like the Z80 and the 8086, and like most modern CPUs, I/O devices are memory mapped on the C64′s 6510 CPU. The mapping is as follows:

  • $D000-$D3FF: VIC video controller
  • $D400-$D7FF: SID sound controller
  • $D800-$DBFF: Color RAM
  • $DC00-$DCFF: CIA 1 I/O controller
  • $DD00-$DDFF: CIA 2 I/O controller
  • $DE00-$DFFF: for extensions on the expansion port

6502 CPU

The CPU inside the C64 is a 0.985 MHz (on PAL) MOS 6510, which is a close derivative of the well-known 8 bit little-endian MOS 6502. The 6502 was introduced in 1975 by MOS Technology, a company formed earlier the same year by former Motorola engineers, with engineering headed by Chuck Peddle. The philosophy of the 6502 was to have a reduced instruction set and a small register file, making it simpler and faster than CPUs like the Z80 at the same clock speed, as well as cheaper to manufacture.

Unlike most modern CPUs, the 6502 does not have a set of general purpose registers. Instead, it has a single accumulator A (for arithmetic and logic), two index registers X and Y (for incrementing, decrementing and indexing memory) and a stack pointer. All these registers are 8 bits. The processor status, consisting of the negative (N), overflow (V), break (B), decimal (D), interrupt (I), zero (Z) and carry (C) flags is exposed as register P. The program counter (PC) is 16 bits wide. The fact that the stack pointer is 8 bit means that the stack is confined to the area between $0100 and $01FF in the address space, i.e. the upper half of the effective stack pointer is hard-coded to $01. There is another special area in the address space: The first 256 bytes, at $0000 to $00FF are referred to as the zero page (ZP). Many instructions support special encodings for zero page addresses, which saves one byte in the instruction encoding as well as at least one cycle of execution time. This can be seen as an extension of the register file to another 256 (though external) registers.

All opcodes are one byte, and have 0, 1 or 2 byte operands. The 8×8 opcode matrix is somewhat logical (e.g. branch instructions are encoded as $10, $30, $50, …), but there is no easy rule to construct the opcode table. Nevertheless, the opcode table is a minimal encoding for optimal decoding in the 6502′s internal PLA ROM.

Instruction Set

The instruction set is very streamlined, and avoids redundancies. There are load instructions (LDA/LDX/LDY to load A, X and Y respecively), store instructions (STA/STX/STY), read-modify-write instructions (logic: ASL/LSR/ROL/ROR, count: INC/DEC), arithmetic (ADC/SBC; note that these always include the carry: CLC/ADC is a regular addition, and SEC/SBC is a regular subtraction, because of the one’s complement logic), compare (CMP/CPX/CPY; these are subtractions without storing the result), logic (AND/ORA/EOR, and BIT, which is AND without storing the result), as well as branch instructions, flag manipulation, register transfer and stack manipulation.

Addressing Modes

Each instruction supports one or more addressing modes. Common instructions like LDA (load accumulator) support more addressing modes than less common ones (BIT).

  • The immediate addressing mode is indicated with a # sign: LDA #$17 loads the immediate value of $17 into the accumulator.
  • Absolute addressing specifies a 16 bit address as an operand: LDA $0314 loads from the memory address $0314.
  • Zero page addressing is an optimized version of absolute addressing: LDA $02 will read from address $0002 in memory, but the instruction can be encoded more tightly, and execution is faster.
  • Absolute-X-indexed addressing reads from a specified address, to which the contents of the X register is added. LDA $0200,X reads from the address $020A, in case X is $0A. This allows reading from tables.
  • Absolute-Y-indexed is the same thing, but with the Y register.
  • Zero-Page-X-indexed is an optimized version of Absolute-X-indexed. LDA $F0,X reads from the Xth location in a table stored starting at $00F0 in memory. Note that all zero page addresses will wrap around, so $F0 + $10 = $00.
  • Zero-Page-Y-indexed is the same thing, but with the Y register.
  • Zero-Page-X-indexed-indirect adds X to a specified zero page address, reads a 16 bit pointer from the resulting address and finally accesses memory at that address. So LDA ($80,X) will read from an address specified by the array of pointers at $0080 and the index X into the array. This addressing mode is rarely used.
  • Zero-Page-indirect-Y-indexed treats two consecutive bytes in zero page as an address and adds Y to the address. LDA ($14),Y will read from $E020, if the address stored at $14/$15 is $E000 and Y is $20. This addressing mode is the most convenient way to work with pointers, as no register can hold 16 bits.

Register Transfer and Stack

There are several 1 byte instructions without operands that move data between registers. TAX, TXA, TAY and TYA move between A, X and Y. TSX and TXS copies between X and the stack pointer.

The stack pointer always points to the next address that is written to. This means that an empty stack has a stack pointer of $FF, and pushing a value first writes the value and then decrements the stack pointer. The 6502 can move the accumulator from and to the stack (PHA/PLA), as well as the processor status P (PHP/PLP).

Control Transfer

Next to the absolute JMP instruction, there is an indirect version that jumps over a vector (e.g. JMP ($FFFC)). JSR (jump to subroutine) only has an absolute version, and stores the address of the next instruction minus one on the stack. RTS (return from subroutine) takes the address from the stack, adds one, and moves it into the program counter. The ”minus one” logic was chosen because it could save one cycle in the implementation of JSR.

A hardware interrupt, unless disabled by a set interrupt (I) flag, pushes the address of the next instruction minus one (just like JSR), pushes the processor status afterwards, disables interrupts, and jumps over the vector at $FFFE/$FFFF. RTI return from interrupt) is the same as the combination of PLP and RTS. BRK causes a software interrupt and behaves the same as a hardware interrupt, except that it sets the B flag in the processor status on the stack to 1 (a hardware interrupt sets it to 0). Note that the B flag does not exist in the actual processor status register, the corresponding bit is only used in a status byte on the stack.

From a software perspective, NMIs behave the same as IRQs, but they cannot be masked, and they use the $FFFA/$FFFB vector. The reset vector is at $FFFC/$FFFD.

Flags and Branches

All load and logic instructions set N and Z accordingly, shift instructions also modify C, and arithmetic instructions touch N V, Z and C. The D (decimal), I (interrupt disable) and C flags can be set and cleared programmatically (CLD/SED, CLI/SEI, CLC/SEC), while the V flag can only be cleared (CLV). Conditional branches are possible based on the value of the negative (BPL/BMI), overflow (BVC/BVS), zero (BNE/BEQ) and carry (BCC/BCS) flags. Branches encode an 8 bit relative offset and can therefore reach code in the area of +127 and -128, counting from the byte after the branch instruction. Since a compare is the same as a subtraction, BCC is a branch on (unsigned) below, and BCS is a branch on above-or-equal.

NOP

NOP (no operation) does nothing. Its encoding is $EA.

Decimal Mode

If the D flag is set, all ADC and SBC operations will be BCD-adjusted afterwards, i.e. $09+$02 won’t be $0B, but $11, since 9+2=11. The BCD correction circuit has been patented in US patent 3,991,307.

Cycle Counting

It is quite straightforward to find out how many cycles an instruction takes. As a rule of thumb, an instruction takes as many cycles as the number of memory fetches it has to perform, but at least two.

Therefore, single-byte opcodes (one byte fetches; NOP/TAX/INX etc.) as well as instructions with immediate operands take two cycles. Zero page instructions take three memory accesses (opcode, address, data), so they are three cycles. Absolute instructions take four accesses (opcode, address low, address high, data), so they are four cycles.

Read-modify-write instructions (INC/DEC/shift/rotate) are an exception: They require 4 memory accesses for the zero page case and 5 otherwise, but they take 5 and 6 cycles, respectively.

Branches take 3 cycles if they are taken and two if they are not taken. And extra cycle has to be added if the branch crosses a page boundary. JMP is 3, push is 3, pull is 4, JSR and RTS are 6 each.

All other timings can be looked up in the 6502′s reference, but they are very easy to memorize.

Common Tricks

The BIT instruction exists in a two-byte (immediate operand) and three-byte (absolute operand) variant. Since BIT only changes the flags, it effectively skips one or two bytes in the instruction stream. This can be used to replace a two-byte branch or a three-byte JMP with a one-byte BIT if only one or two bytes have to be skipped.

The architecture allows safe self-modifying code, so a common optimization for copy loops is to use LDA $nn00,X and STA $mm00,X, looping X from $00 to $FF and then incrementing the bytes that encode nn and mm for the next page. Compared to a LDA (zp1),Y: STA (zp2),Y sequence, this gets the inner loop down from 16 cycles (5 LDA, 6 STA, 2 INY, 3 BNE) to 14 (4 LDA, 5 STA, 2 INY, 3 BNE).

A PHA/PLA combination is 7 cycles, while an STA/LDA combination in the zero page is 6 cycles, so unless there is no free zero page space, PHA/PLA should be avoided to quickly store a value. Using an absolute store to write the value into the operand of a future immediate load (i.e. self modification) is the same speed at the zero page solution, but does not waste zero page space.

An elegant way to store a flag is to have it in bit #7 of a zero page address. While a load/store combination has to be used to set the flag (or the slower, but register-preserving SEC/ROR combination), it can be cleared with a simple LSR (5 cycles) and tested with BIT (3 cycles), without affecting register contents.

Since the 6502 is so register starved, only 3 bytes can be passed to a subroutine in registers. Also, the stack is small, and accessing it is slow, so stack frames as seen on modern architectures are very uncommon. Many applications and libraries (e.g. GEOS) use a dedicated area in the zero page as virtual registers.

The 6502 has no instructions for multiplication, division or floating point arithmetic. Most 6502-based computers have a BASIC interpreter in ROM though, and they typically include a math and floating point library.

Bugs and Quirks

The original 6502 implementation has a series of bugs and other anomalies that have never been fixed in MOS chips (not counting the 65CE02, which was only used in Amiga peripherals).

The indirect version of JMP loads the program counter from the wrong address, if the vector’s address lies on a page boundary: JMP ($23FF) will read the address from $23FF and $2300 instead of $23FF and $2400.

When in decimal mode, the negative flag reflects the original binary result, not the effective decimal result.

If a software interrupt (BRK) and a hardware interrupt occur at the same time, the BRK is dropped.

STA with the absolute-indexed addressing mode takes first reads from the absolute address without the index register added, and then reads again from the correct address. LDX #$07 STA $DC0D,X will first read from $DC0D, discard the result, and then write to $DC14. On the C64, this read form $DC0D would ACK all pending CIA 1 interrupts, while it is only supposed to write to $DC14.

Read-modify-write instructions with absolute addresses first read the value, but one cycle before they store the result, they store the original value again. On a C64, this can be seen when incrementing the screen border color at a defined area of the screen, as every write to the register will cause a tiny gray dot on the screen (on later HMOS versions of the VIC). When this is used with certain I/O ports, this can have other side effects. The latter two quirks have been used heavily for obfuscating copy protection software.

Instruction decoding in the 6502 is done by a PLA that compares the current cycle number within the instruction and the current opcode against a ROM of 130 mask lines, of which any number can fire independently. The outputs of these lines are then fed into components like the ALU, bus control, register control and program counter logic. The instruction set only consists of 151 defined opcodes, and since handling the remaining 105 opcodes as NOPs or traps would have required extra lines in the PLA, they will match against some lines that were meant for instructions with similar opcodes. Some of these ”illegal opcodes” lead to useful results and are used in some software (SAX = store A & X), but most of the instructions make little sense (LAS = loads from memory, ANDs it with S and stores it into A, X and S), and some even lock up the CPU, disabling IRQs and NMIs (CRA/KIL).

The MOS 6510

Except for the pin layout, the MOS 6510 that is used in the C64 differs from the generic MOS 6502 in two ways: It can make the bus tri-state when not used, so the VIC can use it, and it has a 6 bit I/O port built in, which can be controlled using zero page locations 0 and 1. In register 0, each bit from 0-5 set it to output if 1, and to input if 0. Bits 0-5 in register 1 are the actual I/O pins. On the C64, bits 0-2 are outputs and control bank switching, they turn the ROMs and the I/O area on and off. Bits 3-5 go to the tape connector and control the motor and the data sent to the head, and detect whether a key on the tape deck is pressed.

BASIC

Microsoft had a strong position in the market for (mostly ROM) BASIC interpreters in 8080-based home computers when the MOS 6502 was released in 1975, so they rewrote their interpreter in 6502 assembly. Microsoft BASIC was pure 6502 code with a minimal character I/O interface to the machine’s ”monitor”, i.e. I/O library.

Commodore decided to license the interpreter for the 1977 PET and extended it slightly to interface with their disk and tape libraries. Commodore BASIC was very buggy, so they went back to Microsoft for an update, which, with the Commodore changes re-applied, shipped in newer PETs as BASIC V2. For version 4, Commodore added several instructions for more conveniently dealing with disk.

Being a low-end machine, Commodore took the bugfixed BASIC V4 codebase and removed all features after V2, making it independent of the machine’s graphics and sound features again, and fitting it back into 8 KB, and shipped this version on the VIC-20. The Commodore 64 got the almost exact same version, but it runs at a different memory address ($A000-$BFFF).

Microsoft BASIC is a line-based editor, that is, lines can be shown with the LIST command, and they can be modified by re-typing them. This integrates nicely with the KERNAL screen editor: The cursor can be moved up to LISTed lines, the lines can be modified, and when RETURN is pressed, the whole line is fed into BASIC again.

A nice feature of this and later versions of Commodore BASIC is the fact that all important parts, like the tokenizer, the detokenizer and the interpreter loop jump over a jump table in RAM before they do their work, allowing the user to extend BASIC arbitrarily. The most well-known BASIC extension is Simons’ BASIC, a cartridge that maps 8 KB of extra ROM at $8000-$9FFF.

KERNAL

The C64 has an 8 KB I/O library at $E000-$FFFF which is utilized by BASIC, but is intended to be used by other applications as well. All Commodore 8 bit systems have a standardized library call interface in the form of jump tables at the very top of memory that call into machine-specific functions for I/O.

KERNAL is started form the RESET vector, initializes the machine, sets up an interrupt service routine that handles the keyboard, animates the cursor and does the real time clock. The C64 has a hardware clock in each of the CIA chips, but KERNAL has not been updated to use this feature since the VIC-20.

KERNAL provides an abstract character I/O interface to a number of devices. All devices support open, read, write and close. The open call takes three parameters: The logical file number (there is a maximum of 16 channels), the device number and the secondary address. There are 32 device numbers statically assigned to the devices. The 8 bit secondary address can signal something to the device, like speed or an operation mode. Some devices (tape and IEC) support an optional filename.

Device 0 is the keyboard. While KERNAL exports raw key presses, the keyboard can also be accessed through character I/O, which will go through the screen editor and replay all characters on the screen that are in the line of the cursor, regardless of whether the user typed them or they had been there before.

Device 1 is the tape drive. KERNAL reads and writes blocks of data at a time and buffers them for character I/O.

Device 2 is RS-232. KERNAL contains a very sophisticated (but rarely used) software RS-232 implementation that supports up to 2400 baud.

Device 3 is the screen. KERNAL interprets special codes, manages the cursor position and handles scrolling.

Devices 4 and greater will be directed to the IEC bus. By convention, devices 4 to 7 are printers and plotters, and devices 8 to 30 are floppy drives or hard disks.

KERNAL allows interacting with the IEC bus manually by sending TALK and LISTEN requests to the bus.

GEOS

While KERNAL is a minimal character-based operating system in ROM, there is also a disk-based operating system with a graphical user interface for the C64. GEOS was released by Berkeley Softworks in 1986 and Commodore bundled it with the C64 for some time. The GUI, which can be controlled by a joystick or a mouse, runs in 320×200 graphics mode and resembles early versions of MacOS. GEOS is a 16 KB library that includes an optimized disk interface (faster, support for timstamps, icons and multi-fork ”VLIR” files), library code for drawing to the screen, high level UI primitives for menus, buttons and dialog boxes (with callbacks) and a simple memory swapping facility. Furthermore GEOS allows input and printer driver plugins, as well as proportional fonts in different sizes. Internally, GEOS has a jump table to its library routines that consists of about 150 entries.

GEOS came with applications like GeoPaint and GeoWrite; Berkeley Softworks themselves offered solutions like GeoPublish and GeoCalc, and more software was available from third parties.

GEOS’ only requirement is a 1541 disk drive, but a 3.5″ 1581 drive, a RAM extension or one of the later hard drives helped speed it up a lot.

6526 CIA

The C64 has two identical 6526 CIAs (Complex Interface Adapter) that are mostly used for I/O. One CIA features 16 general purpose I/O pins (8 bit port A and 8 bit port B) that can be used either as an input or an output, two programmable timers and a real-time-clock.

The timers have 16 bit counters and count down by one either on each clock cycle, or on an external event, or on a timer A underflow (in the case of timer B). This allows concatenating the timers to one 32 bit timer. On an underflow, the CIA can be programmed to cause an IRQ or to send data through a serial shift register. The CIA also supports receiving data through a shift register.

The real-time-clock has a resolution of 1/10 of seconds and supports generating interrupts at a certain time.

CIA 1 is hooked up to the keyboard and the joystick ports. Since the keyboard consists of 64 keys (plus SHIFT LOCK, which is parallel to the left SHIFT key, and RESTORE, which is directly connected to the CPU’s NMI line), these can be laid out in a 8×8 matrix of lines, key presses connecting the intersections. One side of the matrix is connected to port A (output), and the perpendicular side is connected to port B (input). The keyboard driver can now write the values of $01, $02, $04 etc. in port A and test the input of port B to see which keys are pressed. The two joysticks are connected in parallel to port A and port B, so they can cause spurious keyboard events.

CIA 2 is hooked up to the IEC bus, and I/O lines control the VIC bank. The rest is exposed on the user port, and can be used for RS-232.

KERNAL uses CIA 1 for the 60 Hz system timer, but, apart from the ports, doesn’t use any of the extra features of either CIA.

6581 SID

The SID (Sound Interface Device) is a whole topic of its own.

6567/6569 VIC

The video chip inside the C64 is called the MOS 6567/6569 VIC-II (Video Interface Controller) – the video chip in the VIC-20 had been the original VIC, and was the reason for the marketing name of the VIC-20.

The VIC supports a 40×25 text mode, a 320×200 bitmap mode, 16 colors and 8 sprites – all of these features have lots of sub-modes and options. The amount of memory the VIC can address is 16 KB, and while by default, it accesses the first 16 KB of the C64 RAM, it can be configured to use any of the four banks.

The 16 colors of the VIC are divided into two sets of eight. The first eight are the more important colors, as some modes only support the first eight. The colors are, in the original order: black, white, red, cyan, purple, green, blue, yellow, orange, brown, pink, dark gray, gray, light green, light blue and light gray.

Character Mode

The C64 has two built-in character sets that the VIC can access. They can be shown on the screen by writing the numbers 0 to 255 into screen RAM (at $0400 by default). The default font has uppercase characters and lots of line-drawing symbols in the lower 128 characters, and the second half consists of the same characters, but inverse. The alternative font has upper- and lowercase characters and omits some of the symbols.

The foreground color of the characters can be changed by writing the color numbers into the Color RAM, which is located at $D800-$DBFF. There is one byte per character, but only the lower 4 bits are actually preserved by Color RAM.

Each character is 8×8 pixels, and stored as eight bytes in the character ROM (or RAM, if a user-defined character set is enabled), one line being one byte. A 1-bit will take the color from the Color RAM ($D800-$DBFF), and a 0 bit will take it from the global background color register ($D021). The pixel matrix is determined by looking up the character index in the screen RAM (at $0400-$07FF by default) and consequently looking up the pattern the current character set (the VIC sees the default font at $1000, although for the 6502 it is invisible there).

In Extended Color Mode (ECM), it is possible to chose between one of four background colors (registers $D021 to $D024) with the upper two bits of the character index, but then only 6 bits will be used to look up the character pattern, decreasing the number of possible characters to 64. The built-in (uppercase) character set is well-suited for this: While it is similar to the ASCII encoding, it has the uppercase characters mapped to codes $01 to $1A, so the most important characters are within the first $40.

Multi-Color Character Mode allows up to four colors per character and is intended for tile-based games, like platformers. If bit 3 of the value in Color RAM is 0, then the character gets displayed just like in non-multicolor mode, but colors are restricted to the first eight. If bit 3 is 1, then pairs of horizontally adjacent bits are combined in their meaning: 00 represents the screen background ($D021), 01 is the second background register ($D022), 10 is the third background register ($D023), and 11 is the color specified in bits 0-2 of the Color RAM. Pixels in these characters are twice as wide, so the resolution of a character is 4×8.

Bitmap Mode

In hi-res graphics mode, the VIC supports a resolution of 320×200, which uses the same pixel frequency as 40×25 character mode (40*8=320, 25*8=200). The bitmap can reside at $0000 or $2000, and the VIC reads one byte for 8 pixels. But hi-res mode does not only support monochrome graphics: The foreground and background colors of each 8×8 tile are taken from the high and low nibble of screen RAM, which would otherwise be unused. Color RAM is not used in this mode.

The encoding of the bitmap is identical to the encoding of a character set, making it a non-linear framebuffer: The first eight bytes of the bitmap represent the pixels in the tile at character position (0,0), the second eight bytes represent the tile at character position (1,0), which is pixel position (8,0), and so on. This layout makes pixel addressing in software slower.

In Multi-Color Bitmap Mode, the horizontal resolution is halved to 160×200, and pixels are twice the width. Every set of two bits in the encodes one of four colors per tile: 00 takes it from the global background register ($D021), 01 and 10 take it from the upper and lower nibble of screen RAM, respectively, and 11 takes it from Color RAM.

Scrolling

The VIC supports hardware X and Y scrolling by 0 to 7 pixels. Since the 40th column is half visible and another column left of the first column is half-visible when the horizontal shift register is set to e.g. 4, a 41th column would be needed. Instead, it is possible to switch the screen to 38 column mode, i.e. the whole screen is a little narrower, and more border is shown on the left and on the right. The screen can also be switched from 25 to 24 lines the same way.

Sprites

The VIC has eight hardware sprites (also called MOBs, movable objects). Each sprite is 24×21 pixels, which is encoded in 63 bytes. Set bits will be drawn in the sprite’s individual foreground color, and cleared bits will be transparent. The index to the sprite’s bitmap data in memory is an 8 bit value that is read from the last 8 bytes of screen RAM – since the screen is only 1000 characters, the last 24 characters of the $0400=1024 bytes area would otherwise be unused.

There is also a multicolor mode for sprites, which makes pixels twice as wide and decreases the horizontal resolution to 12 pixels. In this mode, 00 is still transparent, and 10 encodes the sprite’s individual color. The codes 01 and 11 take the color out of the sprite multicolor registers ($D025 and $D026), which are shared among all sprites.

Sprites can be positioned at arbitrary pixel positions on the screen, and overlap. In this case, sprites with lower numbers have priority over sprites with higher numbers. Each sprite can either be shown in front or behind background pixels. Sprites can be X- and Y-expanded by a factor of two, and collision of two sprites or of a sprite and background pixels is detected by hardware: Whenever two non-transparent sprite pixels are drawn at the same position on the screen, they have collided. Whenever a non-background pixel is drawn by the character generator at the same position where a non-background pixel of a sprite is draw, the sprite has collided with the background. An exception from this rule is the 01 code in the character data, which also counts as background. This way, a background picture can be constructed that does not cause collisions in certain areas. In practice, most newer games do not use the hardware functionality, but instead test for overlapping sprite bounding boxes in software.

Memory Layout

The VIC can address 16 KB at a time. All VIC data structures can be stored anywhere in these 16 KB, but they have to be aligned to their size.

  • The screen RAM is $0400 bytes in size and can be at $0000, $0400, …
  • The character set is $0800 bytes, and can be at $0000, $0800, …
  • The bitmap is $2000 bytes and can be at $0000 or $2000.
  • Sprites are $40 bytes, and can be at $0000, $0040, …

Two GPIO pins of the second 6526 CIA are connected to bits 6 and 7 of RAM when the VIC accesses it. By changing the lower 2 bits of $DD02, the VIC can be switched between banks $0000-$3FFF (11), $4000-$7FFF (10), $8000-$BFFF (01) and $C000-$FFFF (00).

If the VIC is set to banks $0000 or $8000, then the two built-in character sets shadow RAM in the area of $1000-$1FFF. This means that the built-in character set can be used on those banks without occupying RAM, but it also means that the area from $1000-$1FFF cannot be used for bitmap, screen RAM or sprite data either.

For timing reasons, color information is not taken from main RAM, but from a dedicated Color RAM. These $0400 half-bytes are accessible to the C64 at $D800-$DBFF and can not be bank switched.

Timing

For advanced VIC programming, it is necessary to not just set up a certain mode and have the VIC display it, but to reprogram the VIC while it is drawing the picture. For this, it is necessary, to understand its timing.

While the pixels within the screen area are 320 by 200, the VIC actively draws pixels in the border color outside of this area, which (on PAL) is 403×284 pixels. Analog TV standards specify an H blank area at the end of every line, and V blank area at the end of every screen. So counting this timing as pixels, this gives an absolute resolution of 504×312 pixels. The interesting and very useful connection about the pixel clock and the system clocks is that an 8 pixel character is drawn every system clock cycle, i.e. about 1 million times a second. The 504 horizontal pixels therefore mean that a line is drawn on the screen every 63 cycles. With this information, it is possible to do cycle-exact timing of assembly code to switch a VIC register at an 8 pixel granularity.

Further timing details (badlines, sprite timing), as well as the application of this information to do tricks like FLD, FLI and AGSP would go beyond the scope of this article, but are talked about in the presentation at the 25th Chaos Communication Congress.

Memory Configuration

In a running system with BASIC and KERNAL, the BASIC and KERNAL ROMs are turned on and visible at $A000-$BFFF and $E000-$FFFF respectively, and at $D000-$DFFF, the I/O area is visible. Using the 3 lowest bits in the processor port at address 1, this configuration can be altered. The ROMs can be turned off, revealing RAM instead, and the I/O area can be configured to show either RAM or the character set ROM. Note that writing to ROM will always direct the data to the underlying RAM.

In practice, many programs run in the default configuration and use both KERNAL library routines, as well as functions in BASIC, to keep their own code as small as possible. More recent programs and almost all games turn off all of ROM, to get direct control of the interrupt vector without having to go though the KERNAL handler first. The I/O area is typically configured to show the I/O registers and Color RAM, and only rarely switched to a different configuration to temporarily access the RAM underneath. A few applications read the character ROM at program start and modify the copy.

The C64 supports another ROM bank at $8000-$9FFF, which can only be serviced by an external cartridge connected to the expansion port. Also, the $A000-$BFFF bank can be overridden by an external ROM. If KERNAL detects the magic string ”CBM80” at $8004 on startup, it will jump to the code of the cartridge right away.

Expansion port cartidges can also put the C64 into “ULTIMAX” mode, in which the it can play game cartridges for the failed C64-based stripped down “ULTIMAX” system (“Commodore MAX”, “VC-10″): All internal ROM is disabled, $8000-$9FFF and $E000-$FFFF show external ROM, RAM is only visible at $0000-$0FFF, and the VIC has a different view on the address space. This mode is used by freezer cartridges which can use it to replace the $E000-$FFFF bank and thus override the hardware vectors.

Tape Interface

The tape interface consists of a single line each for data input and output, motor control and key sense. The raw data is read from and written to the data lines, and all encoding and decoding of the data stream is done in software. 3 of the required lines are connected to the processor port at zero page location 1, and one (data input) is connected to CIA 2.

IEC Bus

The IEC bus is a serial version of the IEEE-488 bus used on the PET. Devices on the IEC bus are daisy-chained, and are all connected to the same three lines: ATN (attention), clock and data. IEC has a single bus master, which is the computer. It is the only device to ever raise ATN, while every device can output to clock and data, depending on the state of the bus.

If the computer raises ATN, every device on the bus listens for the command which includes the 5 bit device number (0-30) and compares it with its own. The protocol on who sends and who receives is determined by the computer sending TALK/UNTALK and LISTEN/UNLISTEN commands.

While KERNAL exports the interface at this level, it also allows high-level open, close, read and write operations on the IEC bus, as well as load and save operations. The BASIC LOAD and SAVE commands are directly hooked up to this interface.

The IEC bus was designed for the serial shift register in the VIA (Versatile Interface Adapter) of the VIC-20 and its disk drive, but it turned out that the VIA had a bug that made the shift register unusable, so the IEC protocol had to be implemented in software. While the C64 has CIAs, in which the bug has been fixed, the 1541 still used VIAs. It wasn’t until the C128 (in its native mode only) that the computer could talk to the floppy drive (Commodore 1570/1571/1581) in its intended speed.

1541 Disk Drive

The Commodore 1541 Disk Drive is the most common disk drive used with the C64. It uses 5.25″ SS/DD (single side, double density) disks, but disks can be flipped, and the other side can be used as well, if the disks are double sided. The 1541 does not use the index hole, and uses software markers (SYNC) instead to be able to tell the start of a sector. Due to reliability problems of early drives, the 1541 only uses 35 out of the 40 possible tracks on a 5.25″ disk. The tracks have a variable number of 256 byte sectors, ranging from 21 on the outside to 17 on the inside. The data is written in 4 different speeds. This makes an overall 683 sectors, or 174,848 bytes.

The file system is stored on track 18. Track 18, sector 0 contains the disk name and the BAM (block availability map), which stores one bit per sector (1 = free). Track 18, sector 1 is the first sector containing directory entries: There are eight 32 byte entries per sector, with a maximum filename length of 16 characters. The first two bytes of a directory sector point to the next directory entry sector.

The files on disk are also stored as a linked list. The first two bytes of every sector are either the track and sector number of the next block, or the first byte is 0 and the second byte is the number of valid bytes in this sector.

The 1541 is a stripped down version of the PET drive series, which had a parallel connection, and contained two 6502 CPUs: One for doing the filesystem and communicating with the computer, and one for reading data from disk and writing data to it, as well as encoding and decoding the data. The 1541 only has a single 6502 CPU running at 1 MHz, which (using timer IRQs) regularly switches itself between the two modes. The two virtual CPUs still communicate with each other using a messaging interface in the zero page. The 1541 has 2 KB of RAM at $0000-$07FF.

The 1541 has two VIA I/O controllers at $1800 (for the IEC bus) and at $1C00 (for the drive). The firmware is located at $C000-$FFFF.

Since loading an application or a game takes minutes on an unmodified C64, several ”floppy speeders” appeared (either as software on disk or built into applications, as ROM extension cartridges, or as internal replacement ROMs), that consisted of implementations of more optimized protocols for the IEC bus for both the C64 and the 1541. The 1541 code was uploaded using the old bus protocol. Such a new protocol would for example not do a handshake on every bit using the clock line, but shift a complete byte through in 4 steps, two bits at a time, using the clock and data line at the same time. This would of course only work if both CPUs were not interrupted. VIC timing on the C64 side could already affect this, so many floppy speeders turned off the screen while loading.

Other Peripherals

The 1541 is the necessary companion to a C64. It can be replaced by a 1570 (1541 with fast bus routines for the C128) or a 1571 (double-sided 1570), since they include a 1541 compatibility mode. The 3.5″ Commodore 1581, which supports 880 KB per disk, can hardly be a replacement for a 1541, because most applications contain their own floppy speeders that make lots of assumptions on the exact on-disk format. For GEOS, it can be very useful though.

Creative Micro Devices sold and continues to sell a line of hard drives that have an IEC connection but contain a 3.5″ SCSI drive inside. Although they have a 6502 CPU built in and allow code execution on their CPU, they are not compatible enough to replace a 1541 either.

Several memory extension cartridges exist for the expansion port of the C64: The Commodore REU (contains a DMA chip that transfers data between itself and main RAM), as well as the third party GeoRAM (maps a block to the $DE00-$DFFF area) and RAMLink (battery backed, designed as RAM disk).

Freezer cartridges like the Action Replay and the Final Cartridge were not only popular because they could dump all of memory to disk and thus copy certain copy-protected games, but also because they featured floppy speeders that disabled the original routines directly at startup time, without any effort from the user.

In the mid 90s, CPU speeders for the expansion port became popular. The 8 MHz Flash 8 is rare today, but many enthusiasts have a SuperCPU, which replaces the onboard CPU with a 20 MHz 65C816, which has a native 16 bit mode that can address up to 16 MB or RAM. There are a few applications and games that require a SuperCPU. The speedup of GEOS with a SuperCPU is significant.

In the 2000s, the enthusiast scene created all kinds of peripherals, like ethernet interfaces, IDE interfaces and SD card readers.

And there are not only peripherals: In 2004, the Commodore 64 DTV, a reimplemented C64 appeared in the form of a Joystick. The device is fairly compatible and can be extended to connect to a keyboard and a 1541.

64'er 04/1984 (PDF)

I converted the first issue of the German Commodore 64 magazine 64′er into a searchable PDF:


64′er 04/1984
(44MB)

Here is a screenshot of the PDF in Mac OS X Preview:

Here are two sample pages, and details in the original size:



Yes, there is another project to digitize them, but it has very different goals. I was told that the publisher doesn’t have the paper archive any more. I do.

The pages have been reconstructed from 600 dpi scans by adjusting the curves of each of the four channels in the CMYK color space, OCRed using ABBYY FineReader, then the resulting PDF has been descreened in Adobe Acrobat by scaling it down to 150 dpi and compressed by converting the PDF images into JPEG.

Commodore BASIC as a Scripting Language for UNIX and Windows – now Open Source

Update: The project has moved to Sourceforge.

Attention Slashdot crowd, here is a little background:

This application is a recompiled version of the original Commodore 64 binary – it is not a reimplementation, so while it runs at pretty much the maximum possible speed, it is still 100% compatible. The huge C file in the archive has been produced by feeding the original 6502 code into my static recompiler and optimizing it with LLVM. The original operating system interface (character I/O, LOAD, SAVE etc.) has been reimplemented in native C, so Commodore BASIC interfaces nicely with OS X/Windows/Unix – you can use pipe I/O, and you can pass the filename of a BASIC program on the command line.

Yes, you could also just run a standard C64 emulator, but it wouldn’t be nearly this speed, and everything would run inside a sandbox; and there would be no way to interface this to your OS.

A while back, I released Apple I BASIC and Commodore BASIC as a scripting language for Mac OS X 10.5 on Intel. It did not work on any other OS or on a different CPU type.

Today, we are releasing Commodore BASIC as a Scripting Language – it works on Linux, Windows, Mac OS X 10.4/10.5 (Intel and PowerPC), and you even get the source, so you can adapt it to other operating systems and CPUs.

Download it here: cbmbasic.zip

The archive comes with binaries for Mac OS X and Windows. The source compiles on Linux, Windows and Mac OS X. All code is BSD-licensed. Main work by Michael Steil, speed optimizations, Linux and Windows fixes by James Abbatiello.

The core of the BASIC interpreter is in the file cbmbasic.c, which is platform, endianness and bitness independent. For all I/O, it calls out into runtime.c, do it should be able to adapt this project for any OS by just changing runtime.c.

All function calls that the core interpreter can’t handle end up in kernal_dispatch() in runtime.c, where a switch statement dispatches these to C functions. For Commodore BASIC, runtime.c has to support several Commodore KERNAL library routines. Some of them are very important (CHRIN, CHROUT) and some are only used for certain BASIC statements (LOAD, SAVE, OPEN, SETTIM). runtime.c does not implement all functions yet.

Feel free to port cbmbasic to your system and architecture of choice, and extend runtime.c to support more functions. If you like, send your changes back to us (mist64<at>mac<dot>com, abbeyj<at>gmail<dot>com), so we can update the main project and give your changes to everyone. We’re also interested how fast you can get it with different compilers and settings.

See my old article for more info as well as some insights on how it is done.

See also:

Transactor November 1987: Volume 8, Issue 3 (PDF)

The other day, I found this at WeirdStuff:


Transactor November 1987: Volume 8, Issue 3
(27MB)

Click on the image above for the PDF. It is the original scan, but it is fully searchable in your favorite PDF reader, you can extract text, and the entries in the table of contents are hyperlinks.

I know Craig Bruce has scans online, but I find searchable PDFs a lot more useful than single images on a website. Search engines are even going to index this issue!

Some background: The pages have been downsampled to 196 dpi (which was the native resolution of the press) to get rid of MoirĂŠ patterns. The color pages have been converted into CMYK, and every channel’s curve has been adjusted to get rid of unwanted parts of the histogram. All images are JPEG compressed greyscale – while this is not optimal for printing (although you can scale the images up and then convert them to B/W with quite good results), it looks better than monochrome/G4 in most viewers, and works well with pages that combine text and graphics. I’m open to input on how to improve the quality or get the size down more.

Create your own Version of Microsoft BASIC for 6502

If you disassemble a single binary, you can never tell why something was done in a certain way. If you have eight different versions, you can tell a lot. This episode of “Computer Archeology” is about reverse engineering eight different versions of Microsoft BASIC 6502 (Commodore, AppleSoft etc.), reconstructing the family tree, and understanding when bugs were fixed and when new bugs, features and easter eggs were introduced.

This article also presents a set of assembly source files that can be made to compile into a byte exact copy of seven different versions of Microsoft BASIC, and lets you even create your own version.

Microsoft BASIC for MOS 6502

First written in 1976, Microsoft BASIC for the 8 bit MOS 6502 has been available for virtually every 6502-based computer including the Commodore series (PET, C64), the Apple II series, Atari 8 bit machines, and many more.

These are the first eight versions of Microsoft BASIC:

Name Release VER ROM FP ROR Buffer Extensions Version
Commodore BASIC 1 1977 Y 9 Y ZP CBM 1.0
OSI BASIC 1977 1.0rev3.2 Y 6 Y - 1.0a
AppleSoft I 1977 1.1 N 9 Y 0200 Apple 1.1
KIM BASIC 1977 1.1 N 9 N ZP - 1.1a
AppleSoft II 1978 Y 9 Y 0200 Apple 2
Commodore BASIC 2 1979 Y 9 Y 0200 CBM 2a
KBD BASIC 1982 Y 6 Y 0700 KBD 2b
MicroTAN 1980 Y 9 N ZP - 2c

Name: Name of the computer system or BASIC interpreter

Release: Release date of this version – not necessarily the date when the source code was forked from Microsoft’s

VER: Version string inside the interpreter itself

ROM: Whether the software shipped in ROM, or was a program supposed to be loaded into RAM

FP: Whether the 6 digit or 9 digit floating point library was included. 9 digit als means that long error messages were included instead of two character codes, and the GET statement was supported.

ROR: Whether the ROR assembly instruction was used or whether the code worked around it

Buffer: Location of the direct mode input buffer; either zero page or above

Extensions: What BASIC extensions were added by the OEM, of any.

Version: My private version number used in this article and in my combined source

The Microsoft BASIC 6502 Combined Source Code

Download the assembly source code here: msbasic.zip

In order to assemble if, you will need the CC65 compiler/assembler/linker package.

The source can be assembled into byte-exact versions of the following seven BASICs:

  • Commodore BASIC 1
  • OSI BASIC
  • AppleSoft I
  • KIM-1 BASIC
  • Commodore BASIC 2 (PET)
  • Intellivision Keyboard Component BASIC
  • MicroTAN BASIC

You can build the source by running the shell script make.sh. This will create the seven files cbmbasic1.bin, osi.bin, applesoft.bin, kb9.bin, cbmbasic2.bin, kbd.bin and microtan.bin in the “tmp” directory, which are identical to the original ROMs.

You are welcome to help clean up the source more, to make it more readable and to break features out into CONFIG_* defines, so that the source base can be made more customizable.

Make sure to read on to the end of the article, as it explains more about the source and what you can do with it.

Microsoft BASIC 1

Ric Weiland, Bill Gates and Monte Davidoff at Microsoft wrote MOS 6502 BASIC in the summer of 1976 by converting the Intel 8080 version. While the former could fit well into 8 KB, so that a computer manufacturer could add some machine-specific I/O code and ship a single 8 KB ROM, code density was less on the 6502, and they could not fit it significantly below 8 KB – it was around 7900 bytes – so that computers with BASIC in ROM would require more than a single 8 KB ROM chip.

Spilling over 8 KB anyway, they decided to also offer an improved version with extra features in a little under 9 KB: This version had a 40 bit floating point library (“9 digits”) instead of the 32 bit one (“6 digits”), and the two-character error codes were replaced with actual error messages:

6 digit BASIC 9 digit BASIC
?NF ERROR
OK
?NEXT WITHOUT FOR ERROR
OK

9 digit BASIC also added support for the GET statement to read single keystrokes from the keyboard.

On startup, Microsoft BASIC 6502 asks for the size of memory:

MEMORY SIZE?

If the user just presses return, BASIC detects the size of memory itself. If, on the other hand, the user enters “A”, it prints:

WRITTEN BY RICHARD W. WEILAND.

Versions since 1.1 print:

WRITTEN BY WEILAND & GATES

Then it asks:

TERMINAL WIDTH?

Microsoft’s codebase could also be assembled either for use in ROM or in RAM: The RAM version additionally asks:

WANT SIN-COS-TAN-ATN?

These four statements are located at the very end of the interpreter image (actually, the init code is at the very end, but that gets overwritten anyway), so that up to 250 more bytes are available for the BASIC program if the start of BASIC RAM was set to the beginning of the SIN/COS/TAN/ATN code (“N”), or to overwrite ATN only (“A”) – in this case, the user would gain about 100 bytes extra.

All these questions were very similar to the ones presented on an Intel 8080 BASIC system – after all, BASIC 6502 was a direct port.

The start message looks something like this:

MOS TECH 6502 BASIC V1.0
COPYRIGHT 1977 BY MICROSOFT CO.
n BYTES FREE
OK

Microsoft’s codebase was very generic and didn’t make any assumptions on the machine it was running on. A single binary image could run on any 6502 system, if the start of RAM was set correctly, the calls to “MONRDKEY”, “MONCOUT”, “LOAD” and “SAVE” were filled with pointers to the machine-specific I/O code, and the “ISCNTC” function was filled with code to test for Ctrl+C.

Microsoft maintained this source tree internally and, at different points in time, handed their current version of the source to OEMs, which adapted and/or extended it for their machines. While most OEM versions were heavily modified in its user interaction (startup screen, line editing…), most of the code was very similar; some functions were even never changed for any version of BASIC. No OEM ever came back to Microsoft for updates, except for Apple and Commodore, which both synced once each, up to the bugfixed version 2.

Commodore BASIC 1 (1.0)

The BASIC that shipped with the first Commodore PET in 1977 is the oldest known version of Microsoft BASIC for 6502. It does not say “Microsoft” anywhere, and memory size detection and screen width were hardcoded, so on startup, it just prints *** COMMODORE BASIC ***, followed by the number of bytes available for BASIC.

Commodore added the “OPEN”, “CLOSE”, “PRINT#”, “INPUT#” and “CMD” statements for file I/O and added VERIFY to compare a program in memory to a file on a storage device. They also added “SYS” to call into assembly code – Microsoft’s code had only provided the “USR” function with a similar purpose. It seems Commodore didn’t like the “OK” prompt, so they renamed it to “READY.”.

All machine-specifics were properly abstracted by calls into the KERNAL jump table, the upper 7 KB of the 16 KB ROM – except for one call out into the screen editor part of the PET ROM:

        iny
        lda     (INDEX),y
.ifdef CONFIG_CBM1_PATCHES
        jsr     LE7F3	; patch
.else
        ldy     #$00
        asl     a
        adc     #$05
.endif
        adc     INDEX
        sta     INDEX
        bcc     L33A7
        inc     INDEX+1

This code fixes the garbage collector by doing the missing ldy/asl/adc in the patch code.

Speaking of patches: Commodore BASIC 1 has been binary patched a lot: There are six patch functions appended to the very end of the interpreter image that work around miscellaneous fixes. This is what one of these calls into a patch function looks like:

  .ifdef CONFIG_CBM1_PATCHES
        jmp     PATCH1
  .else
        clc
        jmp     CONTROL_C_TYPED
  .endif

Here is the patch function – someone indeed forget to clear the carry flag:

PATCH1:
        clc
        jmp     CONTROL_C_TYPED

Some of these patches are in generic code, and some in Microsoft-specific code. Later fixes in generic code are not necessarily identical to these patches. So this indicates that Commodore wrote the fixes. But it is unknown why these additions were done in the binary as opposed to the source: Commodore had the source and made lots of additions to it. Maybe it was just more convenient to patch the binary for debugging at some point.

Ohio Scientific (1.0a)

Ohio Scientific sold a wide series of 6502-based machines for several years, but they all shipped with the same version of 6 digit BASIC bought from Microsoft in 1977.

6 digit vs. 9 digit was probably a compile time option, because the differences are pretty straightforward, as can be seen in this example:

; ----------------------------------------------------------------------------
; ADD MANTISSAS OF FAC AND ARG INTO FAC
; ----------------------------------------------------------------------------
FADD4:
        adc     ARGEXTENSION
        sta     FACEXTENSION
.ifndef CONFIG_SMALL
        lda     FAC+4
        adc     ARG+4
        sta     FAC+4
.endif
        lda     FAC+3
        adc     ARG+3
        sta     FAC+3
        lda     FAC+2
        adc     ARG+2
        sta     FAC+2
        lda     FAC+1
        adc     ARG+1
        sta     FAC+1
        jmp     NORMALIZE_FAC5

Ohio Scientific only made minimal adaptions for their computers, and added no extensions. It asks for memory size and terminal width, and then prints OSI 6502 BASIC VERSION 1.0 REV 3.2".

One quirk on the Ohio Scientific is the inclusion of the WANT SIN-COS-TAN-ATN string, although BASIC ran in ROM. The code to print this string and adjust memory layout accordingly is not included. OSI BASIC is 7906 bytes in size. Without the extra string, they could have saved 21 bytes.

The string Garbage Collector was horribly broken in OSI BASIC, effectively destroying all string data – in Commodore BASIC 1, it had been binary patched for fix the problem.

AppleSoft I (1.1)

Apple shipped the first Apple II systems with Integer BASIC in ROM, Microsoft BASIC was only available as an option loaded from disk or tape. AppleSoft BASIC, as it was named, had only minor adaptions and extensions. On startup, it printed:

APPLE BASIC V1.1
COPYRIGHT 1977 BY MICROSOFT CO.

In order to make AppleSoft feel more like Integer BASIC, it showed a ‘]’ character instead of “OK” and said “ERR” instead of ERROR.

The memory size easter egg was modified in this version, it printed COPYRIGHT 1977 BY MICROSOFT CO instead of Weiland’s and Gates’ names. Since the Apple II character output code ignored the uppermost bit, this text could be hidden in ROM by setting the MSBs of every character:

.;287F C3 CF D0 D9 D2 C9 C7 C8 "COPYRIGH"
.;2887 D4 A0 B1 B9 B7 B7 A0 C2 "T 1977 B"
.;288F D9 A0 CD C9 C3 D2 CF D3 "Y MICROS"
.;2897 CF C6 D4 A0 C3 CF 0D 00 "OFT CO."

This version introduced another easter egg present in all later versions: BASIC 1.1 was the first version to include the “MICROSOFT!” easter egg text, as described in a previous article. The encoded (XOR 0×87) text was hidden in some floating point constants and never addressed.

AppleSoft I is the oldest known BASIC 1.1. Compared to 1.0, version 1.1 included minor bugfixes in GET/INPUT/READ, TAB() and LIST, as well as the fix in the Garbage Collector present in the Ohio Scientific machines and binary patched in Commodore BASIC 1.

BASIC 1.0 also had a bug where lines in direct mode that started with a colon were ignored:

        jsr     CHRGET
.ifdef CONFIG_11
        tax
.endif
        beq     L2351

CHRGET is supposed to set the zero flag on the end of an instruction, which can be end of line (0 character) or a colon. The original code wanted to check for an empty line and got the first character, and went on reading another line of it was empty – but a colon as the first character had the same effect. 1.1 fixed this by setting the flags on the value again.

Version 1.1 also contained various tiny speed optimizations: BEQs and BNEs were changed so that a cycle could be saved on the more likely case.

Here is another optimization in LEFT$/RIGHT$/MID$:

.ifndef CONFIG_11
        sta     JMPADRS+1
        pla
        sta     JMPADRS+2
.else
        tay
        pla
        sta     Z52
.endif
[...]
.ifdef CONFIG_11
        lda     Z52
        pha
        tya
        pha
.endif
        ldy     #$00
        txa
.ifndef CONFIG_11
        inc     JMPADRS+1
        jmp     (JMPADRS+1)
.else
        rts
.endif

The original code isn’t only suboptimal, it’s even dangerous, because it only increments the low byte of the address it wants to jump to and assumes it doesn’t roll over to $00.

For some reason, the random number seed was changed slightly:

    .ifdef CONFIG_11
        .byte   $80,$4F,$C7,$52,$58
    .else
        .byte   $80,$4F,$C7,$52,$59
    .endif

But this doesn’t make a difference, due to a bug present in all 9 digit versions of BASIC: The value is copied into the zero page together with the CHRGET routine:

.ifdef CONFIG_SMALL
        ldx     #GENERIC_CHRGET_END-GENERIC_CHRGET
.else
        ldx     #GENERIC_CHRGET_END-GENERIC_CHRGET-1
.endif
L4098:
        lda     GENERIC_CHRGET-1,x
        sta     CHRGET-1,x
        dex
        bne     L4098

On 9 digit BASIC, one extra byte had to be copied, but the start index was not changed, so the last digit was omitted. This bug exists in every known version of Microsoft BASIC.

Another bug was introduced on the Apple II: All previous versions of BASIC had the input buffer for instructions in direct mode in the zero page. On the Apple II, it was at $0200 in RAM, which broke some code that made assumptions on the address:

NEWSTT:
        jsr     ISCNTC   ; check for Ctrl + C
        lda     TXTPTR
        ldy     TXTPTR+1 ; high-byte of instruction pointer
        beq     L2683    ; 0 -> direct mode
        sta     OLDTEXT
        sty     OLDTEXT+1

Subsequent versions of BASIC compared the high-address of the text pointer:

        cpy     #>INPUTBUFFER

KIM-1 (1.1a)

The KIM-1 is a computer kit based around the MOS 6502, which was sold by the makers of the 6502 to show off the capabilities of this CPU. A 6 digit and a 9 digit version of Microsoft BASIC was available on tape, but the 6 digit version seems to be very rare. BASIC for the KIM-1 is the most authentic version of Microsoft BASIC, because it has only been minimally modified, it contains all questions about memory size, screen width, and the trigonometric functions, as well as the memory width easter egg. The encoded “MICROSOFT!” string can be found at the same spot as on the Apple II.

Although this is based on BASIC 1.1, just like AppleSoft I, there are a few fixes in array handling and the PRINT statement.

But they also introduced another bug: In input handling, again concerning the location of the input buffer, there is the following code:

        ldx     #<(INPUTBUFFER-1)
        ldy     #>(INPUTBUFFER-1)
        bne     L2AF8	; always

This code has been in place since 1.0 and assumes that INPUTBUFFER is above $0100. On the CBM1, which had the input buffer in the zero page, this had been hotfixed by Commodore by swapping the ldx and the ldy. On the OSI, this code didn’t exist, as it is only included in versions that have the GET statement, i.e. 9 digit versions. AppleSoft I was not affected either, because it had the input buffer at $0200. And versions after the KIM fixed this by replacing the BNE with a BEQ in case the input buffer is in the zero page. It is obviously hard to maintain a single codebase with many compile time options that still does optimizations like these.

Since the first KIM-1 systems shipped in late 1975, their CPUs had the 6502 ROR bug, so KIM-1 BASIC had to work around this: Every ROR instruction is replaced by a corresponding sequence using LSR instead.

AppleSoft II (2.0)

AppleSoft II is the oldest version of Microsoft BASIC 2. It was available on tape or disk, and also in ROM in later Apple II models. It is the first BASIC from an OEM that had extended BASIC which was re-sync’ed with Microsoft’s codebase. In other words: Apple licensed an improved and bugfixed version of BASIC, and merged their old changes into it.

BASIC 2 contains mostly bugfixes (all input buffer location bugs have finally been eliminated), small optimizations (reuse two adjacent zeros inside the floating point constant of 1/2 as the 16 bit constant of zero instead of laying it down separately), better error handling for DEF FN, and support for “GO TO” with a space in between as a synonym for GOTO. Also, the memory test pattern has been changed from $92/$24 to the more standard $55/$AA.

In AppleSoft II, Apple also eliminated the “memory size” and “terminal width” questions.

Commodore BASIC 2 (2.0a)

Just like Apple, Commodore went back to Microsoft for an updated version of BASIC, and integrated its changes into the new version. The version they got was slightly newer than Apple’s, but the major difference was that Microsoft added the “WAIT 6502″ easter egg. For this, they changed the encoding of the string “MICROSOFT!” that was hidden in every BASIC since 1.1 from XORed ASCII into PETSCII with the upper two bits randomly set – this way, the text would be just as obfuscated, but it the decoder would be shorter on PET systems. So Commodore BASIC 2 is the only version of Microsoft BASIC that ever accesses this hidden text.

Every version since 2.0a had the PETSCII version of the “MICROSOFT!” text in it – and so did every version of BASIC for 6809.

Intellivision Keyboard Component BASIC (2.0b)

The Mattel Intellivision is a game console released in 1980 that contained a very nonstandard 16 bit “CP1610″ CPU. After a series of delays, the “Keyboard Component”, an extension with its own 6502 CPU and Microsoft BASIC, was released in 1982, but canceled very soon. They are very rare today.

The BASIC in the Keyboard Component is the most custom of all known versions. It is based on a 6 digit version of BASIC 2 and younger than Commodore BASIC 2: It contains two bugfixes: One piece of code that pulled its caller’s address from the stack and normalized it by adding one, had forgotten to respect the carry, so this could fail if the caller sits just on a page boundary. The other fix changed the number of steps needed for normalizing a floating point number.

Intellivision BASIC replaced LOAD and SAVE by PLOD, PSAV, VLOD, VSAV and SLOD, PRT, GETC and VER were added, and PEEK, POKE and WAIT were removed. But the customizations were even more extensive: Instead of keeping the interface to library code, a lot of code was replaced inline, and the whole init code was rewritten. While most of the generic code, for example memory handling was unchanged across Commodore, Ohio, AppleSoft and KIM, making it easier to later integrate Microsoft’s fixes, some of even this code was altered on the Keyboard Component.

What is interesting about the strings in Intellivision BASIC is that they use both upper- and lower case. The start message is this:

INTELLIVISION BASIC
Copyright Microsoft, Mattel  1980

But upper-/lowercase support doesn’t stop here: The complete code has been extended to be case insensitive, but case preserving. The CHRGET code, a super-optimized function living in the zeropage has been patched with a call to this function:

LF430:
        cmp     #'a'
        bcc     LF43A
        cmp     #'z'+1
        bcs     LF43A
        sbc     #$1F
LF43A:
        rts

This very unoptimized piece of code adds at least 17 cycles to every CHRGET, and will slow down execution measurably.

Microtan BASIC (2.0c)

The version of BASIC that shipped on the Tangerine MICROTAN 65 is, like the Ohio and KIM versions, again a very authentic version with few changes. The updated BASIC 2 contained a single bug fix, which is the floating point constant of -32768 which hadn’t been updated from 6 to 9 digits correctly and was missing a byte. The startup message looks like this:

MICROTAN BASIC
(C) 1980 MICROSOFT

Microtan BASIC contains the complete “memory size” and terminal width procedures and the “Weiland/Gates” easter egg.

Although the Microtan was introduced in 1980, its version of BASIC was, like the KIM version, assembled with code that worked around the ROR bug in 6502 chips until mid-1976. The I/O library on the other hand made use of ROR, suggesting that this compile time option was set in error.

Bugs never fixed

As you can see, the first versions had many bugs that were quickly fixed, but fixed became less and less – simply because there were only very few bugs left. But still there are some bugs that never got fixed. The short copy of the random number seed for example, exists on all versions.

Similarly, the two extra constants used for generating random numbers (CONRND1, CONRND2) are 4 bytes in all versions, which is one byte short for 9 digit BASIC. But this is another bug that doesn’t really matter, since the numbers will still be random enough.

The buggy check on large line numbers has also never been fixed. Typing 35072121 into any version of Microsoft BASIC will have the interpreter jump to a pseudo random memory address. The buggy code resides in “LINGET”.

Something similar happens in the case of PRINT 5+"A"+-5: The interpreter will build up the formula on the CPU stack, but miss the string/float type mismatch because of the “+-”, and messes up its stack when removing items. This bug is in “FRMEVL”.

But the fact that Microsoft never fixed these bugs in their codebase doesn’t mean none of the OEMs fixed them. While the LINGET and FRMEVL seem to have been unnoticed everywhere, at least the CONRND1/CONRND2 bug has been fixed by Commodore, at least as early as for the VIC-20 in 1980.

How to build your own BASIC

Now that you have the source that can build seven different OEM versions of Microsoft BASIC, and that you know about the differences between those, you might be interested in building your own version of BASIC 6502 for some 6502-based machine or customizing BASIC to build a bugfixed or extended version for some platform.

First duplicate one of the cfg files, and add it to make.sh. cbmbasic2 is a good start, as you can quite easily test the resulting images in the VICE emulator – CC65 can even provide symbol information for the VICE debugger. Add a case in defines.s to define one of CBM1, CBM2, APPLE etc., because you need one flavour of platform specific code, and include your own defines_*.s. For Commodore BASIC, you also need to define CONFIG_CBM_ALL.

If you are targeting a new type of computer, make sure to adjust the zero page locations in your defines_*.s file (ZP_STARTn) so that they don’t clash with your I/O library. Also make sure that, in case you are compiling for RAM, the init code does not try to detect the memory size and overwrite itself.

The CONFIG_n defines specify what Microsoft-version the OEM version is based on. If CONFIG_2B is defined, for example, CONFIG_2A, CONFIG_2, CONFIG_11A, CONFIG_11 and CONFIG_10A will be defined as well, and all bugfixes up to version 2B will be enabled. The following symbols can be defined in addition:

CONFIG_CBM1_PATCHES

jump out into CBM1′s binary patches instead of doing the right thing inline
CONFIG_CBM_ALL

add all Commodore-specific additions except file I/O
CONFIG_DATAFLG

?
CONFIG_EASTER_EGG

include the CBM2 “WAIT 6502″ easter egg
CONFIG_FILE

support Commodore PRINT#, INPUT#, GET#, CMD
CONFIG_IO_MSB

all I/O has bit #7 set
CONFIG_MONCOUT_DESTROYS_Y

Y needs to be preserved when calling MONCOUT
CONFIG_NO_CR

terminal doesn’t need explicit CRs on line ends
CONFIG_NO_LINE_EDITING

disable support for Microsoft-style “@”, “_”, BEL etc.
CONFIG_NO_POKE

don’t support PEEK, POKE and WAIT
CONFIG_NO_READ_Y_IS_ZERO_HACK

don’t do a very volatile trick that saves one byte
CONFIG_NULL

support for the NULL statement (send sync 0s for serial terminals)
CONFIG_PEEK_SAVE_LINNUM

preserve LINNUM on a PEEK
CONFIG_PRINTNULLS

whether PRINTNULLS does anything
CONFIG_PRINT_CR

print CR when line end reached
CONFIG_RAM

optimizations for RAM version of BASIC, only use on 1.x
CONFIG_ROR_WORKAROUND

use workaround for buggy 6502s from 1975/1976; not safe for CONFIG_SMALL!
CONFIG_SAFE_NAMENOTFOUND

check both bytes of the caller’s address in NAMENOTFOUND
CONFIG_SCRTCH_ORDER

where in the init code to call SCRTCH
CONFIG_SMALL

use 6 digit FP instead of 9 digit, use 2 character error messages, don’t have GET

Changing symbol definitions can alter an existing base configuration, but it is not guaranteed to assemble or work correctly.

I am very interested in your creations. Please add a comment to this article if you have made something new out of this source base!

Using the Floating Point Library Standalone

The complete project has been split into many components, each in their own assembly source file. The core floating point library is in float.s, extra trigonometric functions are in trig.s. It should not be too hard to use this broken-out part (in a 6 digit or 9 digit version) standalone in your own creations. The 9 digit version is a little over 2 KB in size, the 6 digit version is a little smaller.

Adding More Versions

If you want to add another version of BASIC into the source base, you can do it like this: Use “da65″ from the CC65 package to dissemble your version of BASIC and all existing .bin files (with the correct base addresses), and run a “diff” command on the new disassembly and each of the disassemblies of the existing versions. The diff that contained the fewest changes (just look at the file size) is probably a good candidate to base your new version on. Or look at the release date or the family tree to find a version which is similar.

Now create a new version in the source base, as described earlier. Make sure the new version assembles; then compare the disassembly of your version with the disassembly of the original binary in a diff program, like the excellent Mac OS X FileMerge, to find the differences. In most cases, you will only have to adjust a few defines (CONFIG_* and zero page locations) in your defines_*.s file to get matching output. Otherwise, add ifdefs to the respective source files. Run regress.sh to verify that you didn’t break the other versions.

Repeat the last step until the assembly process outputs the same file. Send your changes to me. :-)

Note that the idea of all versions of BASIC in the current source code is that they are all direct forks from Microsoft’s codebase. I chose not to include versions like Commodore BASIC 4, Commodore BASIC 2 for the VIC-20/C64 etc., and I wouldn’t add very late AppleSoft versions, because these are only extended versions of earlier forks and contain no extra code from the original Microsoft source base. Versions that would be very interesting to integrate would be AppleSoft II and Atari Microsoft BASIC, preferably the very first revisions of these.

Credits

  • Function names and all uppercase comments taken from Bob Sander-Cederlof’s excellent AppleSoft II disassembly
  • AppleSoft lite by Tom Greene helped a lot, too.
  • Thanks to Joe Zbicak for his help with Intellivision Keyboard BASIC
  • This work is dedicated to the memory of my dear hacking pal Michael “acidity” Kollmann.

"ROR" in Microsoft BASIC for 6502

If you disassemble any version of Microsoft BASIC for 6502, you’ll find this code in a function that normalizes the (simulated) floating point accumulator:

NORMALIZE_FAC6:
	inc	FAC		; MANTISSA CARRIED, SO SHIFT RIGHT
	beq	OVERFLOW	; OVERFLOW IF EXPONENT TOO BIG
	ror	FAC+1
	ror	FAC+2
	ror	FAC+3
	ror	FAC+4
	ror	FACEXTENSION
	rts

Well, not any BASIC. All versions of

  • Commodore BASIC (all versions, since 1977)
  • AppleSoft BASIC (all versions, since 1977)
  • Microsoft BASIC for the OHIO Scientific (all versions, since 1977)
  • Microsoft BASIC for the rare Mattel Intellivision Keyboard Component (1980)

use this code, but if you look at the disassembly of

  • Microsoft BASIC for the MOS KIM-1 (1977)
  • Microsoft BASIC for the Tangerine Microtan 65 (1979)

you will see this code instead:

NORMALIZE_FAC6:
        inc     FAC
        beq     OVERFLOW
        lda     #$00
        bcc     @1
        lda     #$80
@1:
        lsr     FAC+1
        ora     FAC+1
        sta     FAC+1
        lda     #$00
        bcc     @2
        lda     #$80
@2:
        lsr     FAC+2
        ora     FAC+2
        sta     FAC+2
        lda     #$00
        bcc     @3
        lda     #$80
@3
        lsr     FAC+3
        ora     FAC+3
        sta     FAC+3
        lda     #$00
        bcc     @4
        lda     #$80
@4:
        lsr     FAC+4
        ora     FAC+4
        sta     FAC+4
        lda     #$00
        bcc     @5
        lda     #$80
@5:
        lsr     FACEXTENSION
        ora     FACEXTENSION
        sta     FACEXTENSION
        rts

(Actually, the OHIO Scientific and Intellivision versions work on a 3 byte (“6 digit”) instead of a 4 byte (“9 digit”) mantissa, so the “FAC+4″ part is missing.)

Similar replacement has happened in other parts of the floating point library. It seems to be a compile-time option of the assembly source code.

Todays puzzle is to find out why there are two versions of this code, and why the different computer vendors chose to use one version or another.

See comments for solution.