57

I am reading the book The Elements of Computing Systems: Building a Modern Computer from First Principles, which contains projects encompassing the build of a computer from boolean gates all the way to high level applications (in that order). The current project I'm working on is writing an assembler using a high level language of my choice, to translate from Hack assembly code to Hack machine code (Hack is the name of the hardware platform built in the previous chapters). Although the hardware has all been built in a simulator, I have tried to pretend that I am really constructing each level using only the tools available to me at that point in the real process.

That said, it got me thinking. Using a high level language to write my assembler is certainly convenient, but for the very first assembler ever written (i.e. in history), wouldn't it need to be written in machine code, since that's all that existed at the time?

And a correlated question... how about today? If a brand new CPU architecture comes out, with a brand new instruction set, and a brand new assembly syntax, how would the assembler be constructed? I'm assuming you could still use an existing high level language to generate binaries for the assembler program, since if you know the syntax of both the assembly and machine languages for your new platform, then the task of writing the assembler is really just a text analysis task and is not inherently related to that platform (i.e. needing to be written in that platform's machine language)... which is the very reason I am able to "cheat" while writing my Hack assembler in 2012, and use some preexisting high level language to help me out.

6
  • 21
    You can always write a cross-compiler and use that to produce a code for new hardware nowadays.
    – Kerrek SB
    Commented Jan 8, 2012 at 20:26
  • 2
    @YannisRizos No problem, happens to the best of us :) Commented Jan 8, 2012 at 20:42
  • 9
    It is possible that first assembler might have been written in assembly on a piece of paper. Conversion to machine code might then have been performed still on paper and burned to some sort of ROM with switches, one word at a time.
    – mouviciel
    Commented Jan 9, 2012 at 8:29
  • 2
    My first computer was a ZX81, with 1KB RAM, so my (admitedly short) machine code programs were in fact hand-translated.
    – user281377
    Commented Jan 12, 2012 at 14:45
  • 4
    Likely, the first assemblers were people. Commented Jun 4, 2016 at 21:36

11 Answers 11

49

for the very first assembler ever written (i.e. in history), wouldn't it need to be written in machine code

Not necessarily. Of course the very first version v0.00 of the assembler must have been written in machine code, but it would not be sufficiently powerful to be called an assembler. It would not support even half the features of a "real" assembler, but it would be sufficient to write the next version of itself. Then you could re-write v0.00 in the subset of the assembly language, call it v0.01, use it to build the next feature set of your assembler v0.02, then use v0.02 to build v0.03, and so on, until you get to v1.00. As the result, only the first version will be in machine code; the first released version will be in the assembly language.

I have bootstrapped development of a template language compiler using this trick. My initial version was using printf statements, but the first version that I put to use in my company was using the very template processor that it was processing. The bootstrapping phase lasted less than four hours: as soon as my processor could produce barely useful output, I re-wrote it in its own language, compiled, and threw away the non-templated version.

6
  • 8
    Have you still got all the phases? I would love to see them, and compare them against eachother. Just to get a feel for the process you went through. Commented Jan 8, 2012 at 21:17
  • 4
    @MarjanVenema No, I don't have them any longer - I built it in 1998, and kept using it until 2005, when I discovered StringTemplate. I was overwriting the previous phase with the next as I was working towards the initial usable version. My development cycle consisted of coding new stuff, running the code generator to build itself in a separate directory, running diff against the current code generator to see that the generated portion of code did not change in an unexpected way, replacing the code in place, and running it once more to finish the cycle. Commented Jan 9, 2012 at 1:27
  • 4
    I would think you need to keep some sort of bootstrapping chain. Machine Code => Limited ASM => Full ASM => Some Language. Otherwise, if you loose your binary any step along the way you'll be screwed. (Alternately you could have a C cross-compiled version, since realistically not all C compiler binaries will disappear at once.) Commented Jan 9, 2012 at 14:07
  • 2
    @MarjanVenema: "Bootstrapping a simple compiler from nothing" sounds pretty similar -- see stackoverflow.com/questions/13537/bootstrapping-a-language for some discussion.
    – David Cary
    Commented Aug 16, 2012 at 10:19
  • 5
    The only "features" that an assembler needs to be a "real" assembler is to assemble.
    – mrr
    Commented Apr 30, 2014 at 5:47
27

Were the first assemblers written in machine code?

According to Wikipedia, the first ever assembler / assembly language was implemented for the IBM 701 by Nathaniel Rochester. (The dates are a little uncertain from the Wikipedia article. It states that Rochester joined IBM in 1948, but another Wikipedia page states that the 701 was publicly announced in 1952. And this IBM page states that "[a]ctual design started on February 1, 1951 and was completed a year later".)

However "Assemblers and Loaders" by David Salomon states (on page 7) that EDSAC also had an assembler:

"One of the first stored program computers was the EDSAC (Electronic Delay Storage Automatic Calculator) developed at Cambridge University in 1949 by Maurice Wilkes and W. Renwick. From its very first days the EDSAC had an assembler, called Initial Orders. It was implemented in a read-only memory formed from a set of rotary telephone selectors, and it accepted symbolic instructions. Each instruction consisted of a one letter mnemonic, a decimal address, and a third field that was a letter. The third field caused one of 12 constants preset by the programmer to be added to the address at assembly time." (References omitted ... see the original.)

Assuming we accept that Initial Orders takes precedence, we have clear evidence that the first assembler was implemented in machine code.

This pattern (writing the initial assemblers in machine code) would have been the norm well into the 1950s. However, according to Wikipedia, "[a]ssemblers were the first language tools to bootstrap themselves". See also this section which explains how a primordial assembler written machine code was used to bootstrap a more advanced assembler that was coded in assembly language.

And a correlated question... how about today? If a brand new CPU architecture comes out, with a brand new instruction set, and a brand new assembly syntax, how would the assembler be constructed?

These days assemblers (and compilers) are written in higher level languages, and an assembler for a new machine architecture is typically developed on an existing system with a different (older) architecture and cross-compiled for the new platform.

Writing and debugging non-trivial programs in machine code is an exceedingly laborious process. Someone developing an assembler in machine code would most likely bootstrap to an assembler written in assembler as soon as possible.

This Wikipedia page on bootstrapping compilers and assemblers is worth a read ... if this is all baffling to you.

0
15

I presume that the first assemblers were written in machine code, because as you say, nothing else was available back then.

Today, however, when a brand new CPU architecture comes out, we use what is known as a Cross-Compiler, which is a compiler that produces machine code not for the architecture on which it is running, but for a different architecture.

(As a matter of fact, as I am sure you will find out later on in the book you are reading, there is absolutely nothing which makes a compiler inherently more suitable for producing machine code for the architecture on which it is running than on any other architecture. It is just a matter of which architecture you, as the creator of the compiler, are going to target.)

So, today it is even possible (at least in theory) to create a brand new architecture and have high-level language compilers natively running on it (compiled on other architectures using cross-compilers) before you even have an assembler for that architecture.

15

At first "assembly" was written on paper and then manually "compiled" onto punch cards.

My grand father was working with a ZRA1 (sorry, page only exists in German, but the Google translation is ok to the point where you can actually pick up the most important facts :D).
The modus operandi was to write down your code on paper in a sort of assembly language and the secretary would actually do the transcription to punch cards, then pass them to the operator and the result would be handed back the morning after.

All this was essentially before programmers had the luxury of inputting data through a keyboard and view it on a screen.

1
  • 7
    When I was studying at the university, they still had the paper blocks used to write machine code. You write the program on the right, there are columns on the left to translate the instructions to hexadecimal. And a column for the current address. The first assemblers were actually human.
    – Florian F
    Commented Nov 9, 2014 at 16:02
11

It's hard to be certain about the very first assembler (hard to even define what that was). Years ago, when I wrote a few assemblers for machines that lacked assemblers, I still wrote the code in assembly language. Then, after I had a section of code reasonably complete I translated it into machine code by hand. Those were still two entirely separate phases though -- when I was writing the code, I wasn't working or thinking at a machine-code level at all.

I should add that in a few cases, I went a step further: I wrote most of the code in an assembly language I found simpler to use, then wrote a tiny kernel (more or less what we'd now call a virtual machine) to interpret that on the target processor. That was deathly slow (especially on a 1 MHz, 8-bit processor), but that didn't matter much, since it normally only ran once (or at most, a few times).

11

You don't need an assembler to hand assemble assembly language code into machine code. Just as you don't need an editor to write assembly language code.

A historical perspective

The first assemblers were probably written in assembly language and then hand assembled into machine code. Even if the processor had no official 'assembly language' then programmers probably did most of the job of programming using some kind of pseudo code before translating that code into machine instructions.

Even in the earliest days of computing, programmers wrote programs in a kind of symbolic notation and translated them into machine code before feeding it into their computer. In Augusta Ada King's case, she would have needed to translate them into punched cards for Babbage's Analytical Engine, but alas it was never built.

Personal experience

The first computer I owned was a Sinclair ZX81 (Timex 1000 in the US). The back of the manual had all of the information you needed to translate Z80 assembly language into machine code (even inclusing all of the weird index mode opcodes the Z80 had).

I would write out a program (on paper) in assembly language and dry-run through the code. When I was happy that my program was bug-free, I would look up each instruction in the back of the manual, translate it into machine code and write the machine code down on the paper too. Finally I would type all of the machine code instructions into my ZX81 before saving it to tape and trying to run it.

If it didn't work, I would double check my hand assembly and if any translation was wrong I would patch the bytes loaded from tape before re-saving it and trying again to run the program.

From experience, I can tell you that it is much easier to debug your code if it is written in assembly language than in machine code - hence the popularity of disassemblers. Even if you don't have an assembler, hand assembling is less error prone than trying to write machine code directly, though I guess a Real Programmer like Mel might disagree. *8')

6

There is no difference then or now. You want to invent a new programming language, you choose one of the languages available to you today to make the first compiler. over some period of time, if it is a goal of the project, you create a compiler in that language and it can then self-host.

If all you had was pencil and paper and some switches or punch cards as your user interface to the first or next new instruction set, you used one or all of the items available to you. You could very well have written an assembly language, on paper, and then used an assembler, you, to convert it into machine code, maybe in octal, then at some point that went into the interface to the machine.

When a brand new instruction set is invented today, no different, depending on the company/individuals, practices, etc. it is quite likely that the hardware engineer probably programming in verilog or vhdl, writes the first few test programs by hand in machine code (probably in hex or binary). depending on the software teams progress they may very quickly or not for a very long time coming switch to assembly language and then a compiler.

The first computing machines were not general purpose machines that you could use to create assemblers and compilers from. You programmed them by moving some wires between the output of the prior alu to the input of the next. Eventually you had a general purpose processor such that you could write an assembler in assembly, hand assemble it, feed it in as machine code, then use that to parse ebcdic, ascii, etc. and then self-host. store the binary to some media that you could later read/load not having to keep flipping switches to hand feed machine code.

Think of punch cards and paper tape. Instead of flipping switches you could most definitely make a completely mechanical machine, a labor saving device, that created the media the computer would read. Instead of having to enter the machine code bits with switches like an altair you could instead feed paper tape or punch cards (using something mechanical, not processor driven, that fed the memory or processor, OR using a small machine code written boot loader). This was not a bad idea because you could make something, driven by the computer that could also mechanically produce the paper tapes or punch cards, and then feed those back in. Two sources of punch cards, the non-computer based mechanical labor saving device, and the computer driven machine. both produce the "binaries" for the computer.

2
  • 2
    +1 for the "assembler, you" comment. It's easy to get attached to one definition of a word (i.e. assembler = software) but your comment really puts the obvious back into perspective... that the "assembly process" is just a system/routine, one which easily could be undertaken by a human assembler.
    – The111
    Commented Jan 13, 2012 at 9:53
  • 2
    also folks keep getting stuck on this idea that early computers had instruction sets. The early computers were women with good math skills with pencil and paper, and that is what they were called, computers. then those women (or one in particular) programmed the eniac by connecting wires not using an instruction set. Programming with an instruction set was well well down the road. Yes, very easy to get caught up in the use of a word or term like assembler or computer or early.
    – old_timer
    Commented Jan 13, 2012 at 15:55
5

There are one or two instances in Brook's computer zoo where he said something like "the mnemonics are our invention, the designer simply used numeric opcode or the character whose code was the opcode", so there where machines for which there wasn't even an assembly language.

Entering programs end debugging at the front panel (for those who haven't done it, it was a way to set up the memory, you set some switches to the address, some others to the value and pressed a button, or another button to read the value) was common far later. Some old timers brag they would still be able to enter the boot code for machines they used extensively.

The difficulty of writing directly machine code and reading programs from memory dump is quite dependent on the machine language, some of them are relatively easy (the hardest part is tracking the addresses), x86 is one of the worse.

1
  • 1
    The pdp-11 didn't even have the luxury of knobs. You could alter storage by entering the binary address on 8 toggle switches, and, the value on 16 toggle switches then press the button. I actually saw someone fix a looping program this way! Commented Jan 13, 2012 at 6:20
4

An anecdote:

When I learned assembly language, on the Apple ][, There was a program included in the ROM called the micro-assembler. It did immediate translation of assembly instruction to bytes, as you entered them. This means there were no labels -- if you want to jump or load, you had to calculate the offsets yourself. It was much easier than looking up the instruction layouts and entering hex values though.

No doubt, real assemblers were first written using the micro-assembler, or some other not quite complete environment.

3

I built a computer in 1975. It was very advanced over its contemporary the Altair, because it had a 'monitor rom' which let me enter programs by typing in machine code in hex and viewing this code on a video monitor, where as with the Altair each machine instruction had to be entered a bit at time using a row of switches.

So yes, in the early days of computers and then again in the early days of personal computers people did write applications in machine code.

1

The first "assemblers" were humans. Assembly language where invented as a symbolic notation useful for programmers, but initially the programmer were expected to manually translate the symbolic code into numeric machine code which could be entered into the machine. (If you care for history you can read this fascinating paper from 1947 about how to program a computer. It uses an assembler-like symbolic notation which is manually translated into numeric machine codes. This is several years before the first primitive software assembler.)

Back then, programs were carefully developed with pen and paper. The programmer might start with a flowchart or some symbolic notation but would eventually end up with numeric machine code. Only then would the code be handed to an assistant who would enter the code into the machine. In the earliest years by directly connecting wires or toggling switches on the machine, later by creating punch-cards on some mechanical typewriter-like device. The batch of punch cards would then be put into a queue, and at some point, an operator would enter the punch cards into the machine. When the program had completed running, the printout would be delivered back to the programmer.

Manually translating into numeric machine code is not that difficult actually. The earliest machines had a limited instruction set so you quickly learn the numeric code by heart. The most tedious part is managing addresses: E.g. for a jump instruction you need to calculate the address to jump to. If you need to modify the program, you need to re-calculate all the addresses.

There were some intermediate steps between raw binary and assembler. The earliest computers were programmed by the direct configuration of toggles or wires. When the "stored program" was invented (the most significant breakthrough in computing history), programs were typically typed on punch cards and then loaded into the machine memory. A punch card is a binary format, but the "keypunch"-device used for punching the cards could mechanically translate numeric or alphanumeric keys into bit configuration. So already at this level, programmers did not need to work in raw binary but could work with the more convenient octal or hexadecimal codes.

The first assembler program was very simple - it took one-letter instruction codes followed by a numeric parameter. The big breakthrough was when support for jump labels was added. From here, each iteration made it easier to write more complex programs and hence improve the assembler - the next breakthrough was macros where you defined a keyword that expanded to multiple assembler instructions.

While assembler is still used for low-level programming, almost nobody programs in raw numeric machine code. When writing an assembler or compiler for a new instruction set, it is typically done on a different computer. If you need an assembler for a brand new processor, you could write it in a higher-level language like C, and then write a C-compiler backend generating this assembly language. Then you feed the assembler to the C-compiler, and voila, you have the assembler program as a binary that can be transferred to the new computer and executed.

Not the answer you're looking for? Browse other questions tagged or ask your own question.