53

How did the 8-bit computers (Spectrum, Commodore, Atari, Amstrad etc) typically "bootstrap" from bare electronics into a platform with a working assembly language and OS?

What I mean: An assembler is not an application exactly trivial to write. There's a text editor, there's a parser/lexer that converts the mnemonics and parameters to machine code, there are I/O procedures to save the sources and the binary, at the very least. It's a fairly largish piece of code that you have no programming language to write in, not even an assembler! And there's even no OS to use procedures for I/O, it still needs to be written. And there were no simulators/emulators for existing platforms to use, since this was a brand new platform.

How was this problem handled? How did programmers approach bootstrapping the micros from 'bare metal' to a 'marketable product with a working assembler'?

24
  • 28
    They used cross-development, of course.
    – Leo B.
    Commented Jul 18, 2017 at 16:09
  • 8
    Getting enough of an assembler written isn't actually too hard, I wrote one for the Apple ][ (using the monitor) as I couldn't afford anything else. i seem to recall there was the source code (in BASIC) for one in a Rodney Zaks book. You might find this article at zdnet zdnet.com/article/the-zx-spectrum-birthday-memories interesting, written by an ex-colleague who used to work at Sinclair when they used a VAX.
    – PeterI
    Commented Jul 18, 2017 at 17:19
  • 9
    Early assemblers were much simpler than today's assemblers. A modern assembler for Windows is pretty much a high-level language (with loops, functions, register abstraction, strings, DLLs, structures, multi-threading, dynamic memory allocation...) compared to many of the early "high-level" languages like BASIC or Pascal. Remember, you only have something like 4 kiB of RAM - handwriting four thousand values at most isn't a big deal. I've coded in machine code before (on my own CPU), and it doesn't take long to get to a decent assembler (and later, C-compiler) from scratch.
    – Luaan
    Commented Jul 19, 2017 at 12:05
  • 7
    Text editor WTF? None of the current major free-software x86 assemblers (NASM, YASM, and GNU as) are text editors. Even MASM comes with an IDE, it's is just an assembler that runs as a separate executable . They all just read text and assemble bytes into a binary output file. You edit text with a separate text-editing program. Commented Jul 20, 2017 at 3:27
  • 5
    The first code I ever wrote in 1978 was done by hand, hand-assembling the opcodes (some of which I still recall to this day) from my hand-written assembly listing. These byte values were entered into RAM via toggle switches on a panel I designed and built. Those bytes were the boot loader that let me then use the QWERTY keyboard to start poking bytes directly in to RAM which became the software that I wanted to run. Eventually I bought an EEPROM programmer that would let me dump whatever was in RAM in to a ROM, which made getting to the next stage, a simple assembler, a lot easier.
    – Justin
    Commented Jul 29, 2017 at 6:39

12 Answers 12

53

Gates and Allen used remote terminal access to a minicomputer (Harvard's DEC PDP-10) to cross-assemble, and simulate, their implementation of BASIC for the Altair 8800. Commodore Basic (for the 6502) is reportedly derived from Altair Basic, and also cross-assembled using Macro-10 on a DEC 10.

Woz (and many other early Apple programmers) could code 6502 machine code in absolute hex, which could then be burned into EPROMs to boot the machine into a monitor, from which more hex machine code (or a single line of mini-assembler mnemonics) could be entered. Many programmers in those days memorized raw hex or octal machine opcodes instead of (or in addition to) assembly language mnemonics.

It's a reasonable guess that some other microcomputer system developers may have used cross-assemblers on the first CP/M systems and/or Intel MDS development systems to write early development tools for later 8-bit systems. And those systems were, in turn, used to cross-develop for subsequent systems.

It appears that the very first textual mnemonic assembly language programs, for the EDSAC and other early vacuum tube computers, were manually translated to hex, octal or binary machine code on paper, by hand, and entered into the computer by front panel switches, punched cards, or paper tape. Thus, the "computers" used for the very first cross-assemblers may well have been a roomful of women with pencils. (See the 2017 movie, Hidden Figures, or the 2008 Book When Computers were Human .)

6
  • 17
    In the early days memorising binary instructions was useful because a lot of computers allowed you to alter RAM contents using physical switches on a control panel. Indeed, entering a bootloader by directly toggling RAM values was standard boot procedure on some early computers.
    – slebetman
    Commented Jul 18, 2017 at 22:45
  • 3
    ` Many programmers in those days memorized ` and still do! Either my brain isn't EPROM, or there wasn't enough ultraviolet light yet. to delete this them in it
    – Tommylee2k
    Commented Jul 19, 2017 at 13:17
  • 4
    "Woz ... could code in absolute hex 6502" - and Seymour Cray designed the instruction sets for the CDC7600, and the early Cray supercomputers, so one could do exactly the same - even when writing vectorised code. Writing a simple assembler for those machines (the most powerful of their time) directly in machine code was almost a trivial exercise.
    – alephzero
    Commented Jul 19, 2017 at 13:25
  • 4
    Some early programmers took advantage of knowing the machine encoding of instructions by also using them as data. The Story of Mel mentions this, and is a fascinating look at what a master craftsman could do with machine-code on a 1960's drum-memory machine. (Also an interesting human ethics story, definitely worth a read.) Commented Jul 20, 2017 at 4:17
  • @slebetman I remember doing that in one of my earlier mainframe operator jobs. We used a manually entered bootloader to load further instructions from punched card, and the punched cards contained a bootstrap that allowed us to load a full OS from a drum hard disk. Good times...
    – Rob Moir
    Commented Jul 20, 2017 at 9:53
45

As someone who did it.... We wrote an assembler for an 8080, as there was nothing affordable from Intel. We wrote it in ALGOL 60, if I recall, and ran it on a mainframe.

the first thing we ran through it was .. itself, re-coded in assembler. Oh, and a boot-loader, though I think maybe we had already hand-assembled a minimal version of that into binary.

After that there was no stopping us :-)

1
  • 6
    Upvoting, because this is the process used not just for assemblers but for pretty much every compiler in existence too.
    – T.E.D.
    Commented Jul 21, 2017 at 13:28
25

The same answer as everybody else, just with more detail:

What I mean: An assembler is not an application exactly trivial to write.

Oh, but it is. A "first" assembler on a platform simply reads some bytes, transforms them in a more or less 1:1 relationship to other bytes, that's it.

The target architecture was very simple. There was no shared objects / DLLs or anything like that. At least the assembler I used on a Atari 800XL either had no binding/linking stage, or I didn't know about it/did not see a need for it. There certainly were no standard libraries or anything like that. You would skip all convenience features as well.

There's a text editor,

They are pretty easy to write as well. For starters, you don't absolutely need a full fledged modern editor. If in doubt, you can get away with something line-number based like the early BASICs. And even if you wanted to handcraft an actual editor, that would not be that hard either.

there's a parser/lexer

For recognizing the assembler commands, you can use a simple lookup table or direct string comparison. You wouldn't need very complex "formula" parsing either. Don't forget that the CPUs of that time were very simple as compared to today. The 6502 had 3(!) registers (A, X, Y) and only a handful of flags. The datasheet lists only roughly 60 (!) different instructions, 11 addressing modes.

All commands, flags, addressing modes, op codes, timings and instruction-adressing combinations fit on two (!) single-sided pages of paper. The physical CPU had only 40(!) pins.

there are I/O procedures to save the sources and the binary,

You'd need those anyway, and, again, those were kind of trivial back then. You could easily start off with everything on tapes, and then it's just about streaming some bytes in/out.

I recall disassembling/reverse engineering the firmware of one of the Atari 800 XL floppy drives (as well as the OS) back then. It was doable, not too bad.

a fairly largish piece of code

Really not. It was common back then to publish whole games in print magazines (and books), and teenage boys would easily type in thousands of lines of code over the course of a rainy weekend. When your machine has only a few KB of usable RAM anyway, the size of your code will have a natural limit.

And there's even no OS to use procedures for I/O, it still needs to be written.

Again, I/O was very simple back then. A handful of different hardware models (tape drive and 1 or 2 different floppy drives), no choice of file system - heck, no file system at all for the tape, and extremely limited FS for floppies as well. No concurrency, no virtual memory, no swapping, no nothing. Definitely possible to handcode in assembler.

And there were no simulators/emulators for existing platforms to use, since this was a brand new platform.

No simulators/emulators (though I don't know for sure), but cross compilation is, again, pretty simple. That is, if you had (as a larger company) access to existing computers, you would probably write your code there.

How was this problem handled? How did programmers approach bootstrapping the micros from 'bare metal' to a 'marketable product with a working assembler'?

I don't know how they actually did that, but I really would not be much surprised to learn that all the original code was hand-written in cross-compiled assembler or straight hex code.

Remember that rather large things like the Ultima games (starting with II, I think) were hand-written in Assembler. It was doable.

10
  • 6
    Important note: you only need to hand-build the machine code for the very simplest assembler. Then you can iterate on the assembler, making it progressively better. The first version doesn't need any I/O at all, for example. Even high-level languages (for the time) like Pascal or LISP had compilers written in... themselves. Start simple, iterate, a year later you have a full-blown system.
    – Luaan
    Commented Jul 19, 2017 at 12:00
  • 1
    The difference between writing in assembler and in machine code is pretty big. I once created a patch for a program; the assembler code (some 12 lines) took me maybe 15 minutes to plan, design, write, check, double-check. Then I spent another hour manually converting that to machine code by hand. Look up command in this addressing mode. Look up address of this special register. Change bits to match that register within command byte. Align this, check that. Looking up the numbers in books took ages!
    – SF.
    Commented Jul 19, 2017 at 16:52
  • 1
    Yes, of course, @SF. Somehow back then, if I recall correctly, one seemed to crunch through all of this somewhat stoically, if needed. Sure you have to look in tables, but you can keep the whole of what a CPU like the 6502 "is" in your brain, or on some pieces of paper right in front of you... My usage of "easy" and "simple" is in comparison to later machines. One would not, in the faintest dream, think about hand-crafting code for a modern CPU...
    – AnoE
    Commented Jul 19, 2017 at 17:02
  • 3
    I was one of those teenage kids typing in programs from magazines, first on a TI-99/4A and later on an Atari 130XE. I seem to remember writing a disassembler at some point, and discovering that the bits representing the addressing mode were 'coincidentally' always in the same position. What luck! Commented Jul 19, 2017 at 20:11
  • 2
    @SF.: If you've only hand-assembled 12 lines of code, it might reasonably take 5 minutes a line, but with practice it's possible to get a lot faster. Certain opcodes will get used a lot, and will thus become second nature. Hand-assembled code may tend to be a bit simplistic (using a sequence of memorized instructions in favor of a shorter sequence of instructions one would have to look up) but it may still be able to get the job done.
    – supercat
    Commented Nov 1, 2017 at 16:21
23

The 1974 Altair 8800 kick-started the industry but at the time offered no keyboard, no screen, just a bunch of switches and lights connected directly to the bus and a counter to help you input or output sequential values. So you'd work out the binary representation of your program by hand and input it byte by byte, bit by bit.

The world's introduction to the 6502 wasn't so much more advanced: 1976's KIM-1 has a hexadecimal display and input pad but you're still hand-assembling and inputting.

So by the time you really get to the boom, in 1980 or so, the market has had half a decade of building terminals, tape interfaces and the rest, often in an ad hoc fashion, from which to put together the second-wave machines. You're not bootstrapping from nothing, you're standing upon experience with the CPUs and with how you can add a bunch of things to them. Through CP/M you already have a huge number of development tools for the 8080 and Z80. Throw in a minicomputer and you've probably got the grunt to simulate. If you're after about 1980 and considering filling your machine with PALs or ULAs then the manufacturer of those will probably lease you the minicomputer and supply the simulation software.

So my answer is: they weren't building from nothing by then, and definitely not in isolation.

1
  • 1
    Indeed. If you have one working CP/M 8080 machine in the lab, getting CP/M onto your target 8080 machine is a relatively small project of writing a BIOS and some I/O drivers. You can do that using the native toolchain on the existing machine - technically it's cross development, but only barely so. 6502 systems didn't have that same commonality of operating system, so you needed to develop more, but you could still make use of an existing toolchain to target a different machine with the same processor if you bring along knowledge of (or freshly create) all the BIOS hooks and hardware. Commented Jul 22, 2017 at 12:50
17

It is worth remembering that by the time the home hobby microcomputers appeared, Computer Scientists had more that 25 years experience in building assemblers and designing bootstrap loaders.

As a summer job in the mid-1960s, I worked as an operator in an IBM datacenter with a IBM 1401 - 16K of RAM, no disk, 5 tape drives a cardreader/punch and a printer. Programs were on decks of 80 column cards. You loaded the card deck in the hopper and pressed "Start" This read the first card into the start of memory and executed the instructions encoded on the card. I believe that it read 2 more cards to get the bootstrap fully loaded and then started to read the rest of the deck to get the actual application program read and started.

At university we had a PDP-8 that used papertape as a program storage media. You had to key in the bootstrap using toggle switches. I don't recall how many instructions had to be keyed in but it was very few before the PDP-8 had enough code to start to read the tape and finish loading the program.

In the late 1960's as an undergraduate, I wrote an emulator that ran on the PDP-10 and emulated the IBM-360.

By the time the micro-computers appeared, the concept of using cross-assemblers to produce executable code was pretty well understood and emulators allowed code to be tested even before an actual hardware chip was available. There was usually a long delay between the time a new chip functional design was finalised and the actual chip was available.

Without the modern tools for chip design (CAD, design emulators, etc.) and cheap workstations for engineers to use, it was not that easy to make the masks for the chips and to test actual chips prior to going into production. It was completely impossible to wait for a "real" chip to start programming all of the software.

These problems had all been worked out long before the microchip.

6
  • I specifically targetted the home computers with my question, as they often didn't have big businesses with government contracts behind them, that would allow hiring an army of computer scientists and spending years punching cards, byte by byte. The competition was fierce and the markets tight; you had to get your product out of the door fast and on a budget - and who'd want to buy a home computer you could only program in machine language, on a mainframe? Producing a usable native assembler was an essential step of release, on a budget and under a deadline.
    – SF.
    Commented Jul 19, 2017 at 23:07
  • The PDP-8 paper tape boot loader was 15 words of 12 bits each. Early PDP-8s with magnetic core memory would retain memory contents when powered off, so it was only necessary to key in the boot loader if a program crash accidentally overwrote it. Commented Jul 20, 2017 at 1:33
  • Similar on the HP 2100. The disk boot loader was an entirely manageable 24 or so sixteen-bit words. The machine had core memory so usually it was always there, but it was no great trouble to re-enter it via the front panel buttons if one had to. (It must have been 24 or fewer because it started at octal 77750!) As with many modern boot loaders its job was just to read one sector from the disk and transfer control to it, and that sector of course contained more "bootstrap" code. Commented Jul 23, 2017 at 7:51
  • 2
    There was an article in volume 1, issue 12 Byte magazine describing, in a whimsical style, how one hobbyist built an assembler from scratch. There was no code provided but all of the essential mechanisms, including a hash table for symbol lookup, were described. Article title was "Jack and the Machine Talk". Commented Jul 23, 2017 at 7:54
  • @A.I.Breveleri: almost. 16 instructions plus 1 data word which you didn't have to load -- but you did have to re-load the start address to run it. Commented Sep 3, 2017 at 0:41
9

Early assemblers were either cross-assembled or hand-translated. Writing a Z80 assembler in Forth takes just a handful of screens (if you are using mnemonics based on TDL's extension of the Intel 8080 mnemonics rather than the original Zilog Z80 mnemonics). Its main job, once you are talking about a practised coder, is resolution of jump targets.

To put this in perspective: the Nascom Z80 editor/assembler occupied a whopping 4kB of code (the data structure for labels took 2 bytes for each label, namely only its value: for every use of a label, the source was searched from the front for its definition, with the label table being filled in order of definitions, so assembly times grew as O(n^3) with source size). There was a disassembler taking up 3kB, and a very thorough debugger (using NMI and a few hardware bits in lieu of a "trace" flag/feature) taking up another 1kB and using the disassembler internals.

As a result, translating such code into binary (once you had it on paper) was less arduous than it would seem at first glance and once you had the relevant bugs under control, you could host natively.

2
  • 1
    What purpose would be served by storing the values of labels without names? If one is going to search the entire source code from scratch every time a label is encountered, why not discard all information about the label as soon as one has found out its value?
    – supercat
    Commented Apr 27, 2018 at 19:35
  • The Z80 instruction set has variable instruction sizes, so the only way to determine the "value" of a label is to basically reassemble the source. I suspect simply scanning the source to get the label's index was faster than reassembling to get its value.
    – K. A. Buhr
    Commented Feb 4, 2022 at 21:01
8

I used to regularly program my organization's mainframe in hand-coded machine language. It was a 60s vintage machine (a GE400 system), but it had an online patcher so I could modify the operating system on the fly by sticking a deck of cards into the reader and typing a one-character command on the console.

The patches were originally written in assembler, then hand-translated into machine language (in this case in octal). Punch the numbers into a card with a symbolic offset for the starting address, and the patcher basically did a "go to this address, load this instruction". It wasn't far removed from loading up a PDP-8 by using the front panel toggle switches.

1
  • Welcome to Retrocomputing Stack Exchange. Please read the tour. Thanks for the answer - it's good to see personal experience.
    – wizzwizz4
    Commented Jul 19, 2017 at 6:50
7

The first assemblers for home computers were almost certainly written on non-home computers, whose first assemblers were written on their predecessors and so on back to the very first assembler which was undoubtedly implemented in good old fashioned machine code.

3
  • 4
    "good old fashioned machine code" I see that you're a "real programmer". :-) Welcome to Retrocomputing.
    – wizzwizz4
    Commented Jul 19, 2017 at 6:49
  • do not underestimate ingenuity and the power to complicate things. ;) Commented Jul 19, 2017 at 8:28
  • Inaccurate - a significant minority of the young early developers used writing a toolkit (assembler, debugger, line editor, linker , loader), as a way to introduce themselves to an architecture. When I was at Amiga - the majority of the outside early developers had written a toolkit especially the critical people who were to create the game engines, video processors, and the speed kings ( increased application performance by at least a multiple of 10).
    – LOIS 16192
    Commented Oct 24, 2017 at 6:02
4

The availability of cross-platform tools such as the Ocode bootstrap method for BCPL, and the Pcode bootstrap for Pascal, were also useful for creating and testing programs written in high-level languages, and then porting them to new systems.

BCPL in particular was designed with machine-architecture portability in mind - which no doubt gave rise to the ethos of portability in C.

4
  • If I remember correctly, ocode was translated to acode. You then wrote an simple acode interpreter to pull in the ocode interpreter and eventually pull in the compiler.
    – cup
    Commented Jul 19, 2017 at 12:05
  • Cintcode ring a bell ?!
    – MikeW
    Commented Jul 19, 2017 at 13:50
  • Don't remember cintcode - possibly an advancement since acode. It certainly has a lot more instructions. acode only had 6..
    – cup
    Commented Jul 19, 2017 at 21:25
  • I still have a book describing the P-code instructions and virtual machine.
    – JDługosz
    Commented Jul 20, 2017 at 9:05
3

Well Here is how I did it, and occasionally still do. In my day it was the system hardware guys who created the 1st code. We needed it to test and fix the hardware.

At this point (1978) the big advantage was the terminal (24 lines by 80 characters), and the UART (serial port, COM port). although on one occasion, I built a unit just using a 12 digit 7 segment display and a scientific calculator keyboard (40 keys).

The 1st step is to write a monitor.

It needs operations to

  1. Accept a character, print a character
  2. Accept and print a lines
  3. Display and change memory
  4. Set a break point, step and run programs

and at the second step

Load and save programs. The earliest cheap mechanism was audio cassette tapes (30/1800 bytes per second/minute ).

This was not a great deal of code. maybe a few hundred lines of code. You wrote it and coded it by hand, Gridded notepads were the key.

The big break through for me was something called a rom emulator.

(This is a modern kit version to attach to a PC http://www.sparetimegizmos.com /Hardware/EPROM_Emulator.htm. The 1st one I bought for about $100 (I was getting paid at this point!) had a simple display, and a 20 key keyboard, so you could edit the memory and.. program eproms).

I could connect it to the rom socket and and it enabled me to edit memory interactively. All I needed then was some sort of display.

Once the monitor was working, Assembler was next.

First a simple line by line assembler. recognized Mnemonics, and register names, and simple labels.

Next was a 2 pass assembler which provided for relocatable code. This needed the load function as you needed to load the program twice, once to allocate memory, and generate code, and the 2nd to update addresses.

I wrote my 1st multi-tasking operating system in 4k ROM + 2K ram this way.

The CPM and the PC extended the monitor into a bios, add in Floppy drives and you need file system, and as the saying goes bob's your uncle you have a system.

And that was it til I discovered Forth and life became really interesting.

Forth is not a compiler, it really is a linker, which can be easily extended and normally includes an assembler, and often makes you ask why you need high level languages. You do because optimizing compilers are real time and resource savers, but they were still at least 5 years away or $100K plus in the early 80s.

Once you "get" Forth you wonder what is all the fuss about OSes etc., is, then you get older and realize that they would be very useful when they finally create a good one.

On the other hand Do not write in assembler, write in "C" at a minimum.

2

There's nothing special about an assembler. It takes one type of instruction and does a 1:1 mapping to another type (the hex values for machine code). The simplest ones were quite simple - in the Red Book for the Apple][+ ( pdf ), on pages 91-93 in rather large type (complete with comments) is the entirety of the assembly code - less a page (256 bytes) and a half in size, on the order of 150 instructions.

Such could easily be written by hand and blown to a prom with little difficulty. From this, larger and more complex applications can be built. But the simplest and first assemblers on the personal computers of the day? Trivially hand coded and loaded.

5
  • For the typical assembler, it's frequently not a 1:1 mapping. For example, according to my x86 assembly-language reference, the simple add instruction maps to one of 14 different opcodes depending on data size and data source. The mov instruction adds the complexity of addressing modes, for 22 different opcodes.
    – Mark
    Commented Jul 22, 2017 at 1:28
  • @Mark instruction type to another type - in many machine languages addressing mode of an instruction is a specific subset of bits, so for example, the mov mnemonic may map to a specific value on bits 3-7 and its addressing mode to bits 0-2 forming the machine language command.
    – SF.
    Commented Jul 22, 2017 at 3:29
  • Welcome to Retrocomputing Stack Exchange. Please read the tour. Thanks for the answer; it's amazing to think how much even assembly language has grown since the early days.
    – wizzwizz4
    Commented Jul 22, 2017 at 9:30
  • That's just an example of how the x86 ISA is the weirdo, @Mark. For the typical assembler, it is a 1:1 mapping of mnemonic and operands. Commented Jul 23, 2017 at 17:33
  • As simple as an assembler may seem, I have a copy of the PDP-10 Assembly Language Manual written in 1967 which has over 170 pages.
    – Ron
    Commented Jul 24, 2017 at 0:31
2

You might be considering them in terms of modern CPUs. The older machines (8 bit 1970s and earlier like the 6502) had a very simple opcode set. It was consequently much easier to write an assembler/disassembler than you might think, and would have been easier even if coded in machine code too.

Disclosure - I wrote one, although not in machine code.

1
  • This. The OP seems to be thinking in terms of modern assemblers (and maybe compilers/linkers). There was none of that. The opcode set was incredibly simple, there were only a few registers, and the job of an assembler was pretty much to translate mnemonics via a lookup table, and resolve jumps (during a 2nd pass) based on whether they needed to jump < or >= 256 bytes.
    – Stilez
    Commented Nov 5, 2019 at 11:29

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .