PiDP-8/I SoftwareA Field Guide to PDP-8 Assemblers
Not logged in

Introduction

There are many commonly-used PDP-8 assemblers, several of which we ship with the PiDP-8/I software distribution; there are more available elsewhere. How does a programmer discriminate among them, other than to carefully read each one's manual, then spend several months writing code in it to develop a feel for what it is like? They cannot all be equal, else they'd be interchangeable and you'd just pick one based on what was available. One cannot be clearly better for all things, else everyone would just use that.

This article is a preliminary survey of these assemblers with an eye toward listing the key distinctions among them. It is modeled on a birder's field guide: having read through this document, a reader should be able to recognize which assembler(s) are likely to be able to process a given piece of assembly code by recognizing uses of key features of the assemblers we describe here.

Secondarily, we hope to provide enough information that a programmer beginning a PDP-8 assembly language project be able to make a sensible assembler selection without getting bogged down in minutiae. That ambition likely exceeds this document's current scope, but we strive anyway.

A distant third goal is historical: we wish to document the evolution of the major PDP-8 assemblers in enough detail that the reader can anchor each covered assembler in its historical context. Although this history is of inherent interest, we do this primarily to make it easier for the reader to keep these assemblers and their interrelationships straight. An evolutionary narrative makes this easier than would discussing each assembler as if it were created in isolation.

When multiple versions of a given assembler are available, our commentary applies to the version currently in the PiDP-8/I software distribution, if present, or the latest available version of the assembler if it is hosted externally. We tend to prefer earlier manuals, however, interested as we are here in history.

Criteria

This survey does not include every PDP-8 assembler, on purpose. This is not a comprehensive survey, it is not a genealogy project, and it is not an advocacy platform. In addition to meeting the goals laid out in the previous section, an assembler must meet all of these criteria to be covered:

That list is not intended to be normative or complete, just informative: if an assembler fails to meet all of those criteria, that is likely why it is not listed here.

As a rule, we discuss only the latest available version of any given assembler. Our MACREL coverage is for MACREL v2, for example, not MACREL v1, even though the latter is more readily found online. We've made an exception for PAL-D, covering both the 4K and 8K versions because they were contemporaneous, serving different segments of the PDP-8 market.

Disclaimer

The author is not an expert in this topic, though he has occasionally consulted with experts. The claims in this document are checked against manuals, published example code, and sometimes against running implementations. However, do not expect it to be complete or comprehensive.

Contributing

If you have important information to add and you do not have an account on this wiki, post it to either the PiDP-8/I or SIMH mailing lists, or send me private email.

Keep the criteria above in mind when contributing.

The Assemblers

PAL-III

Introduced August 1965
Manual PDF, 790 kB
Delivery paper tape

We may presume from the name that PAL-III has at least two predecessors. I was able to find a reference to DEC product number 5-1-S, the Program Assembly Language for the PDP-5 in the PDP-5 program catalog from December 1964; we may reasonably call this assembler PAL-I. That catalog also says that the replacement for PDP-5 PAL is an early version of PAL-III for the PDP-8. This makes sense, because the PDP-8 directly replaced the PDP-5 in DEC's product line, being upward compatible with it.

I have yet to come across a reference to anything called "PAL-II." Perhaps it went the way of Angular 3, Winamp 4, Netscape 5, PHP 6, MySQL 7, QuickTime 8, and Windows 9.

The introduction date above is the publication date of the earliest PAL-III manual I have found, but clearly PAL's ancestry dates back to at least December 1964. An early PDP-5 brochure I found from March 1964 speaks of a "Symbolic Assembler" rather than PAL; is that just marketing-speak for "PAL," or does PAL first date to somewhere in that range?

The PAL-III assembler shipped on punched paper tape as part of the grandiosely-named "PDP-8 Assembly System." This amounted to an assembler, an editor, a debugger, and a loader, each a separate paper tape. The programmer would first toggle the RIM loader into the PDP-8's front panel, then use that to load the BIN loader from a fifth paper tape, separate from the PDP-8 Assembly System, then run each of these tapes through the paper tape reader repeatedly — reusing the still-in-core BIN loader each time — in order to iterate his way toward a working program.

The PAL-III assembler tape required at least two passes through the tape reader in order to produce a computer-readable BIN format output tape, plus an optional third pass if you also wanted a human-readable listing on the teletype.

If you look at pictures of PDP-8 computers, you can often see a tray with narrow slots in it, each meant to hold one of these key paper tapes, as they were needed near at hand when using a paper-tape based PDP-8. There is a good picture of a blue one on the cover of the OS/8 Handbook, 1974 ed.

PAL-III can be considered the baseline assembler for a PDP-8, since for roughly its first decade, PDP-8s sold for interactive use came with a Teletype Model 33 ASR with built-in paper tape reader and punch, which meant the ability to read paper tapes was a baseline feature for interactively-used PDP-8s. (Contrast headless embedded PDP-8s, a very common use for the PDP-8 prior to the introduction of the microprocessor.)

More importantly for our purposes here, most of the other assemblers surveyed here share PAL-III's basic syntax. This doubtless follows on from the prior point, since it meant PAL-III had the first-mover advantage.

A brief summary of the feature set of PAL-III:

We give this list not to try to replace the PAL-III manual, but instead to allow searching this document for language features. You may assume that assemblers listed below also support these listed features, especially within the PAL genealogical line; we call out the exceptions below.

MACRO-8

Introduced October 1965
Manual PDF, 1.7 MB
Delivery paper tape

MACRO-8 was another paper tape assembler for the PDP-8. Its eponymous advantage over PAL-III is that it added macros, a very useful feature in assembly language programming as it reduces a lot of the tedium of writing such programs.

MACRO-8 is based on the PAL-III language, but it added much more than just macros:

The DEFINE feature is clearly this assembler's marquee feature, but it is the rest of the list that should open your eyes: all of these features except for DUBL and FLTG were later adopted as-is into the PAL family.

MACRO-8 dropped the FIXMRI pseudo-op relative to PAL-III. One manual I examined showed an example that could only be done using FIXMRI under PAL-III, by which I infer that MACRO-8 simply doesn't need the hint from the user: memory reference instructions can be detected as part of regular symbol table manipulations.

Historical vignette: Where the earlier PDP-8 assembler manuals refer to the new ASCII character constant feature, they actually either refer to "USA SCII" or to "ANSCII", those being the original names of ASCII: the United States of America Standard Code for Information Interchange; or the American National Standard Code for Information Interchange. You may also see "US-ASCII" in some places, being another way of writing "USA SCII".

PAL-D

Introduced April 1968
Manual PDF, 1.9 MB
Delivery paper tape

PAL-D is a descendant of PAL-III, originally appearing as part of the PDP-8 Disk Monitor System, one of OS/8's predecessors. It would later reappear under TSS/8.

Wikipedia claims that the first version of the Disk Monitor was published in late 1967, but the earliest PAL-D manual I've been able to find has April 1968 as the date of its first edition.

PAL-D came in two forms, the 4K and 8K versions, referring to the minimum amount of core memory each required. Note that the 4K version could assemble for larger machines; it has the FIELD pseudo-op, for example, which is of no use unless your assembled program would run in at least 8K of core.

4K PAL-D added only one new feature to the PDP-8 assembler family: the XLIST pseudo-op, which lets the programmer stop and then re-start the assembly listing in the optional third pass.

4K PAL-D is more notable for the features it brought into the PAL family from MACRO-8:

8K PAL-D added several more features relative to the 4K version:

Note that neither version of PAL-D brought over the DEFINE, DUBL or FLTG pseudo-ops from MACRO-8.

Notice also that the negated forms of the conditional assembly operations did not appear until later. The first reference I've been able to find for IFNZRO and IFNDEF is in connection with PAL8.

We find evidence for a complicated development history in the PUNCH and XLIST pseudo-ops. They do much the same thing, only for different passes of the assembler, yet the form of the ops differ. If this assembler was created by a single mind in a single time and place, or if it were created by a standards committee with a mandate to simplify and unify, you would expect these two sets of pseudo-ops to have similar names and forms, either XPUNCH taking an argument or a NOLIST/ENLIST pair. We can infer different minds behind the 4K and 8K versions of PAL-D.

The PUNCH op names also suggest that the operation is mainly useful for paper tape output, still an important I/O device when PAL-D was new. We can find it mentioned in the PAL8 section of the OS/8 Handbook (1974 ed.), but it was airbrushed out of history by the 1979 edition for OS/78, suggesting that the purpose of these pseudo-ops became effectively redundant after the bulk of the market moved to disk-based PDP-8 systems such as the DECstation 78, a microcontroller-based PDP-8 merged with a VT52 video terminal, which effectively replaced the teletype + paper tape reader/punch most often used with earlier PDP-8 systems. (Thus the renaming of "OS/8" to "OS/78," by the way.)

Like MACRO-8, PAL-D does not have the FIXMRI pseudo-op.

The OS/8 Assemblers

Introduced 1971, collectively
Manual PDF, 38 MB
Delivery part of OS/8

OS/8 was the primary programming system from DEC for the PDP-8 in its later years. It included several different assemblers, all documented in the OS/8 handbook. The 1974 edition is linked above. You may also find the later OS/78 Handbook from 1979 interesting for comparison purposes.

The introduction date is that of OS/8, but OS/8 has a complicated development history that stretches back to 1966 or so. Some of the following assemblers predate OS/8; where the dates for them are known, they are called out below. For those with no additional date information, it is possible that it was first introduced after OS/8 but before 1974, the date of the manual linked above, which talks about all of these assemblers.

PAL8

The earliest attestation to the PAL8 assembler I've been able to find is in the May 1970 printing of Programming Languages, the second in a two-volume set on programming PDP-8 family computers. (The first volume being Introduction to Programming.) On page 14-7 in the section on 8K PAL-D, it gives a Disk Monitor command for saving the in-core copy of 8K PAL-D as "PAL8". It is therefore unsurprising that the OS/8 version of PAL8 is morphologically more similar to PAL-D than to PAL-III.

The earliest reference to PAL8 as a named product of DEC is in the PS/8 8K Programming System User's Guide from October 1970, that product being the immediate predecessor of OS/8.

PAL8 understands the same basic language as PAL-D but generally has more features, most especially related to running under OS/8.

I consider PAL8 the default assembler for OS/8 users. If none of the information below guides you to one of the other assemblers, you should probably start with PAL8. If you have a piece of assembly code that is meant to be assembled under OS/8, and you have no evidence that is for any other assembler, it's probably meant to be assembled by PAL8.

PAL8 has many features not present in 8K PAL-D:

The restoration of the FIXMRI pseudo-op is curious. It is not clear from the manuals whether its use is again required for redefining memory reference instructions (MRI) after an EXPUNGE call or if it is merely for compatibility with certain existing PAL-III programs. (TODO: Try it and see.)

The existence of the RELOC pseudo-op should not be taken as a sign that PAL8 is a relocatable assembler. It simply allows code to be assembled starting at one fixed address which is intended to be run at a different fixed address. A proper relocatable assembler generally tries to hide the fixed addresses it uses when assembling your program; compare SABR, for example, which produces output that cannot be run directly, but which must be processed by a relocating loader (LOADER.SV) to fix up the addresses to their final execution values.

PAL-III has one feature not implemented in PAL8: the ability to get a brief listing of the undefined symbols in a program. The information is instead presented inline in the listing output, not grouped together into a single place, which can make refinement of an in-development program tedious as you need to comb information about undefined symbols out from among all the other output.

RALF

RALF uses approximately the same statement syntax as the PAL family, but it is a very different assembler by nature. We can divide its differences into several groups.

Floating Point Processor Support

RALF is not only an assembler for the PDP-8 processor, it is also an assembler for the FPP12 Floating Point Processor (FPP) peripheral.

RALF is the back-end assembler for the OS/8 FORTRAN IV compiler, which delegates all of the FPP handling to RALF, but at program load time, FRTS checks for the existence of an FPP and switches to a software FPP simulator if there is no FPP present. (FRTS does the same for the EAE, by the way.)

Although the FPP12 was introduced with the PDP-12, it is not restricted to use with the PDP-12: it also works with all of the other PDP-8 family computers contemporaneous with and preceding it, going back to the original PDP-8. This is possible because the FPP is a bus peripheral rather than a feature of the CPU proper. DEC justified this in their documentation, pointing out that it means that a site can upgrade their existing processors with an FPP rather than buy a whole new machine with integrated FPP features when they find that software FP is taking too much time to run.

There was also a follow-on peripheral for the PDP-8/a called the FPP8-A, as well as a later variant called the FPP12A, both of which are instruction-compatible with the original FPP12. The FPP features of RALF allow you to intermix FPP instructions with PDP-8 instructions just as if the FPP were part of the PDP-8 CPU, rather than being a very different external coprocessor.

Here is a summary of the FPP-related features of RALF:

If you see any of those instructions in a PDP-8 assembly language program, it's a pretty good indicator that it's meant to be assembled by RALF or FLAP.

Relocatable Code

RALF generates relocatable output code in *.RL files rather than absolute-addressed output, which means you cannot simply load its output into core and execute it directly. You must use the OS/8 FORTRAN IV loader (LOAD.SV) to load one or more *.RL files into core memory, link them to any external libraries, convert relative addresses to fixed addresses, and write out a loader image (*.LD) which can then be loaded an run by the OS/8 FORTRAN IV Run Time System, FRTS.SV.

The compensation for this complicated scheme is that LOAD.SV and FRTS.SV provide a powerful overlay and linkage loading scheme which allows for programs up to about 300 kWords in size, nearly 10× the maximum core memory size in a standard PDP-8 family computer. It does this by loading code from disk as needed. This scheme is made possible by the relocatable output code from RALF.

(Do not confuse LOAD.SV, the OS/8 FORTRAN IV loader — used by both RALF and F4 — with LOADER.SV, the simpler "linking loader" used by the OS/8 FORTRAN II compiler (FORT.SV) and by SABR. There's a second cause for confusion: both loaders use *.RL as their default input file name extension, so RALF, F4, FORT, and SABR all output files with that extension.)

Further Improvements

RALF also has many other improvements over late-generation PAL family assemblers such as PAL8 which are not directly tied to driving the FPP or to relocatable code:

Some may argue about my grouping of the expression changes here rather than under the Arbitrary Differences section below. I made this choice because the change was necessary to allow an improvement.

Justifiable Restrictions

There are restrictions in RALF relative to late-generation PAL family assemblers that are justifiable on the grounds that they follow from the requirement that the output code be relocatable:

Arbitrary Differences

There are additional differences in RALF relative to the PAL family assemblers that seem entirely arbitrary to me:

FLAP

RALF was based on a simpler assembler called FLAP, which generates absolute-addressed code, just as PAL8 does. This section will simply describe the differences in FLAP relative to RALF. These differences aside, FLAP shares the feature set of RALF.

The primary advantage FLAP has over RALF is that, because it is difficult to ascribe meaning to the "page" concept in relocatable assembler output, RALF doesn't support the PAGE pseudo-op, while FLAP does. FLAP also supports the PAL family's zero-page and current-page literal syntaxes, while RALF does not.

FLAP has one pseudo-op that RALF does not, S for defining a single-word integer constant.

Because FLAP output is absolute-addressed, it writes *.BN files as output rather than *.RL. These are linked and loaded by the OS/8 absolute loader, ABSLDR.SV, rather than by the relocating loader used by RALF, LOAD.SV. You can link and run several files directly this way, or you can just load them into core and SAVE them to a *.SV file for later use by the OS/8 R command. This is both simpler and less powerful than the RALF/FRTS dynamic overlay scheme: FLAP programs cannot be any larger than core memory unless you, the programmer, do the manual work to create your own overlay swapping scheme.

FLAP.SV is currently not provided on the PiDP-8/I project's OS/8 disk packs. It is not present on the OS/8 source media we're using, but we don't know if this is because it was removed from OS/8 by DEC or if we simply missed the FLAP source media when curating the input media we use for building our OS/8 disk packs. When we figure that out, I'll update this article.

There is a FLAP.SAV program on the PiDP-8/I project's TSS/8 boot media, but we have not yet spent the time to figure out how to make it work. None of the TSS/8 manuals we have access to talk about FLAP, so this is not likely to be a quick investigation.

By the way, you may find pages online that claim that FLAP was derived from RALF, rather than the other way around. I'm basing my claim on what DEC's own documentation says: "RALF, an extension of FLAP..." This quote was found in two different OS/8 manuals published about 5 years apart.

SABR

As RALF is to OS/8's FORTRAN IV compiler, so the Symbolic Assembler for Binary Relocatable programs is to OS/8's FORTRAN II compiler.

SABR is a relative assembler, unlike PAL8 or FLAP, meaning that the OS/8 linking loader (LOADER.SV) adjusts all of the addresses in the assembled *.RL output to their final values.

SABR does not have any tie to the FPP, unlike RALF and FLAP.

The most notable feature of SABR is that the programmer normally writes SABR code as if using a flat memory model machine, ceding all control over literal placement and of page/field boundary placement to the assembler. This feature — called automatic paging — frees the programmer from having to deal with the strange PDP-8 memory model, but it also means she is dependent upon the assembler to make smart decisions about all of this, even while SABR must itself run within those same constraints. This makes writing SABR easier than for most other PDP-8 assemblers, but because you can't get a whole lot of optimization smarts into a 6 kWord program, the output code tends to be less efficient than hand-tuned code for other assemblers.

You can see this design decision in several language differences relative to PAL-III or PAL8:

You may be surprised to see the OPDEF and SKPDF pseudo-ops grouped among these consequences of the way SABR works as compared to the PAL type assemblers. This is because the PAL way of defining custom opcodes (e.g. DVI=7407) assumes the programmer knows what she is doing with regard to whether the opcode can cause the next instruction to be skipped. Since the SABR programmer generally doesn't know where the page boundaries are, that means she cannot predict what certain instructions will look like in the final machine code, and thus whether a skip will do what she wants it to. SABR therefore provides two different ways to define custom opcodes, one for each case, which allows the programmer to clue the assembler into the correct output.

SABR has a bunch of pseudo-operators to support SABR's role as the back end of OS/8 FORTRAN II:

If you see any of the above pseudo-ops in a bit of code that otherwise adheres to the PAL-III base syntax, it's probably SABR code.

More features exist in SABR which are not consequent to any of the above design constraints:

Finally, there are some things which are different or missing in SABR as compared to PAL8:

A programmer may use SABR one of three ways:

  1. Implicitly as the back-end of the OS/8 FORTRAN II compiler
  2. Explicitly via inline assembly code in a FORTRAN II program
  3. Directly as a standalone assembler.

The third option still depends upon FORTRAN II because SABR writes its *.RL output files with the assumption that they will be linked to the FORTRAN II runtime and libraries by the OS/8 LOADER program, so the generated code makes use of those facilities. This means SABR programs can always call any FORTRAN II library routine without special effort, since it is always going to be linked to it by LOADER.

Ian Schofield's CC8 compiler compiler operates like the OS/8 FORTRAN II compiler, emitting SABR output and depending upon the FORTRAN II library and runtime. Inline assembly via #asm is sent as-is to SABR.

MACREL

Introduced 1977 (v1), 1979 (v2)
Manual text, 384 kB
Delivery DECtape for OS/8

Although this is technically another OS/8 assembler, its late introduction, separate documentation, and separate distribution from OS/8 proper require that we consider it separately.

Aside from MACRO-8, none of the assemblers above support macros, a very helpful feature in assembly language programming. MACREL was intended to be DEC's offered solution to the want of a macro assembler for the PDP-8 in the disk-based operating system era.

Unfortunately, it never achieved widespread use outside of DEC.

INCOMPLETE

SMAL8

Introduced 1980, 2012
Manual SMAL32, SMAL8
Delivery Internet, same links as above

SMAL is a series of cross-assemblers initially created by Douglas W. Jones. He wrote the initial versions in Pascal but later ported it to C. Some versions of SMAL — such as the SMAL32 version linked above for its wonderful manual — are machine-independent, meaning that it provides only the skeleton of the assembler, which is not able to generate code for any particular machine. Other versions of SMAL were targeted at specific machines, mostly 8-bit microcontrollers. Oddly enough, Dr. Jones did not base his pal.c PDP-8 cross-assembler upon SMAL. (See palbart below for more on that thread of history.)

Vincent Slyngstad took up the challenge of targeting SMAL at the PDP-8, producing SMAL8, offering it as part of his C compiler system for the PDP-8.

Some of the differences listed below mean that although the basic syntax of SMAL8 is solidly in the PAL-III family, SMAL8 is likely to assemble only relatively simple PAL programs as-is. The most critical differences are the loss of inline literals and the difference in conditional assembly. New code would simply be written to avoid these problems.

If you read through the SMAL32 manual and see differences relative to PAL syntax but don't see the difference documented below, chances are that Vincent Slyngstad fixed these differences in SMAL8. For example, like PAL8 but unlike SMAL32, SMAL8 uses / to introduce comments, defaults to octal, allows * to set the output origin, and uses the EJECT pseudo-op rather than PAGE to put a page break into the output listing.

SMAL8 has many feature additions relative to PAL8:

Some things are simply different in SMAL8 as compared to PAL8:

There are a few features removed relative to PAL8:

The data storage specifier feature of SMAL32 was removed in SMAL8, since it doesn't make much sense in the 12-bit world of the PDP-8. (That is, the byte (B), halfword (H), threeword (T), and fullword (W) pseudo-ops.) Use DATA instead.

palbart

Introduced 1996
Manual man page
Delivery Internet, PiDP-8/I project

palbart is a popular fork of the pal.c assembler by Douglas W. Jones and Rich Coon, originally spun off by Gary A. Messenbrink — and later enhanced by several others — to support the fleet of PDP-8/e computers in the Bay Area Rapid Transit system; thus the name.

The PiDP-8/I software distribution includes a version calling itself "pal-2.13, 03 May 2015."

If you place a *.pal file in either the src/asm or examples subdirectories of the software distribution, then type make, the build system will find your file and generate several output files named after it:

This makes for a very smooth code-build-run-debug cycle:

  1. Edit examples/myprg.pal
  2. Run make
  3. Run bin/pidp8i-sim boot/myprog.script
  4. Debug; if still buggy, goto step 1.

There are many different versions of palbart floating around on the Internet, and earlier versions have bugs or features that prevent it from being completely compatible with the PAL family assemblers above. The most likely to affect existing programs is that some versions of palbart do not chop symbols off at 6 characters. Some code depends on this happening; for example, the assembled code may refer to a label with 7+ characters in one location and with 6 in another location, and expect the assembler to refer to the same location in both places. The version of palbart that we ship with the PiDP-8/I software distribution does not have this incompatibility. If you know of an incompatibility relative to PAL8 in particular, please file a bug report ticket.

INCOMPLETE

pdpnasm

Introduced 2006
Manual see its home page
Delivery Internet

This is a PDP-8 assembler accepting the PAL-III syntax for Unix type systems.

I am listing it here along with palbart because there are known weaknesses in palbart. If you know of more, please file them. palbart currently has no central development home, so it is possible that the PiDP-8/I project will end up hosting an improved version.

Meanwhile, it seems like a good idea to me to have a second option at hand when cross-compiling code on a POSIX type platform for a PDP-8.

INCOMPLETE

License

Copyright © 2017-2018 by Warren Young. This document is licensed under the terms of the SIMH license.