Humble beginnings

Integer Basic

When the Apple II became available in 1977, it came with no Disk Operating System. Indeed, it did not use Floppy discs as mass storage media, but audio tapes (then called “cassettes”), which are not well suited to host a file system. Thus, after being powered on, the Apple II would launch its Integer BASIC. Then, the user could load / save data from / to the attached cassette.

DOS 3.X

But cassettes were too cumbersome for the most advanced usages, so in 1978 Apple introduced the Disk II, its first floppy disk drive. Managing files became a necessity, thus Apple also introduced DOS 3.1: Apple’s very first Disk Operating System.

DOS was primarily designed to provide BASIC programs an access to the floppies. It does not launch an independent command line interpreter, but can intercept the specific DOS commands entered in the interactive BASIC interpreter.

BASIC interpreter DOS 3 can intercept its commands from the interactive BASIC interpreter

Three versions of DOS 3 (3.1 to 3.3) were shipped. Each new version added welcomed features, but in the end, all are still rather primitive. As DOS was designed to interact with BASIC, its functions are difficult to use from programs written in assembly. It requires calling undocumented functions and laying the data out as if it were coming from an Applesoft program. Worse, DOS is tied to the Disk II floppy drive: hard disks, ram disk and even 3.5” floppy disks cannot be used from DOS 3.3 without major patching.

At the end of 1983, computing landscape had moved and DOS 3.3 replacement was eagerly awaited.

Enter ProDOS

The Professional Disk Operating system brought numerous enhancements to the table. It handles files way faster than DOS 3.3, is interruptible, its file system is device independent and supports sub-directories. It also sports an API, named the MLI (Machine Language Interface), callable from assembly. No wonder that DOS 3.3 was soon considered obsolete!

Boot sequence

Just after powering up, the Apple II executes its Autostart ROM1. It scans down all the expansion slots, from 7 to 1, in order to examine the first few bytes of the ROM from the cards plugged-in. If a specified byte sequence, identifying a mass storage controller, is present, control is given to that card. Then it can load the operating system.

  1. The program in the ROM reads the loader program from blocks 0 and 1 of the disk, places it into memory starting at location 0x800, and then executes it.
  2. This loader program looks for the file with the name PRODOS and type 0xFF in the volume directory of the startup disk, loads it into memory starting at location 0x2000 (also the location of the HIRES Graphics first page), and executes it.
  3. ProDOS ascertains the computer’s memory size and moves itself to its final location.
  4. ProDOS then searches the volume directory of the boot disk for the first file with the name XXX.SYSTEM and type 0xFF, loads it into memory starting at 0x2000, and executes it.

(1) As the Autostart ROM was introduced by the Apple II+ in 1978, the very first Apple models’ boot sequence is slightly different. After powering up, the user has to manually indicate from which extension slot to boot.

XXX.SYSTEM programs usually serve as interface between the user and ProDOS. ProDOS is indeed laid out as following:

ProDOS Architecture ProDOS architecture

BASIC.SYSTEM

One of the SYSTEM programs provided out of the box by ProDOS is BASIC.SYSTEM. It allows the user to interact with Applesoft as he would have done with DOS 3.3. It also provides an API to call Applesoft routines from machine language programs. That’s quite powerful, but comes at a price: BASIC.SYSTEM consumes 10.5kB, which is huge for the machine.

What about my rogue-like game?

As Escape! (working title) grew, I hit a wall. A well-behaved program shall start at 0x800. Then from 0x2000 to 0x6000, memory is reserved to HIRES graphics. That only leaves 6144 bytes, to be used by the code and the data, between 0x800 an 0x2000.

My current toolchain is cc65. Its linker produces a single binary file. As the location of the various segments can be manually specified, I naively assumed that, if I placed some of them after 0X6000, something (ProDOS? BASIC.SYSTEM?) would place those segments to their final location when BRUNing the program (ie. BRUN = Binary RUN = launch a machine language program).

But ProDOS/BASIC.SYSTEM is rather bare by modern standards: the binary program is one single contiguous file and is loaded contiguously into memory, from address 0x800. Indeed, ProDOS has no way to know how to separate the data loaded into segments and where to load them.

That’s why, the first thing Escape! does, after being loaded, is copying some of its segments at the right location, after the HGR zone. The very same location that is described in the cc65’s linker configuration file. Otherwise, the first thing drawn in the HGR area would badly crash the program 😉

; APPLESOFT required
; HGR1 & HGR2 segment reserved
FEATURES {
    STARTADDRESS: default = $0803;
}
SYMBOLS {
    __EXEHDR__:    type = import;
}
MEMORY {
    ZP:     file = "",               start = \$0000,         size = \$00FF;
    HEADER: file = %O,               start = %S - 4,        size = $0004;
    MAIN:   file = %O, define = yes, start = %S,            size = $2000 - %S;
    HGR:    file = "", define = no,  start = \$2000,         size = \$4000;
    DATA:   file = %O, define = yes, start = \$6000,         size = \$2000;
    BSSMEM: file = "", define = no, start = __DATA_LAST__,         size = $9600 - __DATA_LAST__;
}
SEGMENTS {
    ZEROPAGE: load = ZP,     type = zp,  optional = yes;
    EXEHDR:   load = HEADER, type = ro;
    CODE:     load = MAIN,   type = rw;
    RODATA:   load = DATA,   type = ro,  optional = yes, align = $100;
    DATA:     load = DATA,   type = rw,  optional = yes, align = $100;
    BSS:      load = BSSMEM, type = bss, optional = yes, define = yes, align = $100;
}

Linker configuration file, indicating the layout in memory

For the time beings, I need to use BASIC.SYSTEM, mainly for debugging reasons. So, I accept to lose a lot of memory. If at some point I need to claim it, I will write my own SYSTEM program. Its sole purpose will be to load the game’s bytes at the right memory location then give it control.