# 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:

*   It must be readily available online.

*   It must offer at least one of the following capabilities to the end user:

    *   Be the back end assembler for some other language which is itself important.

    *   Be required to build an important body of code which is readily available online.

    *   Be the best available assembler for its primary platform, or at least be an important secondary option.

*   It must run on the PiDP-8/I, either under Raspbian Linux or under one of the PDP-8 OSes we distribute.

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][pml] or [SIMH][sml] mailing lists, or send me [private email][email].

Keep the criteria above in mind when contributing.

[email]:
[pml]:
[sml]:


# The Assemblers


## <a id="pal-iii" name="pal3"></a>PAL-III

| **Introduced** | August 1965
| **Manual** | [PDF, 790
| **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][pc5] 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][p83s] 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][pdp5b] 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 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][os8m], 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][hlc] 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][fma].

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

*   Full support of the PDP-8 instruction set:
    *   All basic processor instructions and several microcoded combinations (`CIA`, `SPA`, etc.) predefined
    *   Many peripheral IOT instructions predefined:
        *   [Teletype Model 33 ASR][asr33] keyboard/printer and tape punch/reader
        *   [high-speed paper tape punch][pp8i] and reader
        *   DECtape types [555][dt555] and [TU55][tu55]
        *   auto-restart type [KR01][kr01]
        *   [Type 182 EAE][t182]
        *   interrupt controller
        *   line printer type 645 (Analex?)
        *   and many lesser-known peripherals!
    *   Zero-page addressing
    *   Indirect addressing
*   `EXPUNGE`, `FIXTAB`, and `FIXMRI` pseudo-ops to augment or redefine the predefined instruction set
*   `PAUSE` assembly output, giving the user time to do manual things while the assembler waits
*   `DECIMAL` and `OCTAL` pseudo-ops for radix selection
*   `FIELD` pseudo-op to select the core memory field

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.

[asr33]:
[dt555]:
[hlc]:
[fma]:
[kr01]:
[p83s]:
[pc5]:
[pdp5b]:
[pp8i]:
[t182]:
[tu55]:


## <a id="macro-8"></a>MACRO-8

| **Introduced** | October 1965
| **Manual** | [PDF, 1.7
| **Delivery** | paper tape

MACRO-8 was another paper tape assembler for the PDP-8. Its eponymous advantage over PAL-III is that it added 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:

*   `DEFINE` macro definition system
*   automatic off-page link generation
*   literals in the current page via `(NN)` syntax or in page zero via `[NN]`; closing paren/bracket optional
*   more operators in expressions:
    *    `!` for Boolean OR (the ASR-33 terminal character set [did not include the &brvbar; character][cs33])
    *    `&` for Boolean AND
*   single-character ASCII constants: `"A"`; closing quote optional
*   other new pseudo-ops:
    *   `DUBL`: define double-precision integer constant
    *   `FLTG`: define double-precision floating-point constant
    *   `PAGE`: [re]set the location counter to the top of the given page
    *   `TEXT`: convert a literal string in the program text to 6-bit packed ASCII in the binary output

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][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".

[ascii]:


## <a id="pal-d"></a>PAL-D

| **Introduced** | April 1968
| **Manual** | [PDF, 1.9
| **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][dms] 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](#macro-8):

*   literals
*   off-page link generation
*   Boolean operators in expressions
*   the `PAGE` and `TEXT` pseudo-ops

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

*   ability to run in 8K or 12K of core, allowing assembly of larger programs
*   more pseudo-operations:
    *   `EJECT`: emit a form feed in the listing output
    *   `IFDEF` and `IFZERO`: conditional assembly tests; affected statements in angle brackets
    *   `NOPUNCH` and `ENPUNCH`: stop and restart assembled machine code output
    *   `ZBLOCK`: reserve memory, setting initial values to zero

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](#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.)][os8m], but it was airbrushed out of history by the [1979 edition for OS/78][os78m], 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][ds78], 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.

[cs33]:
[dms]:
[ds78]:


## <a id="os8"></a>The OS/8 Assemblers

| **Introduced** | 1971, collectively
| **Manual** | [PDF, 38 MB][os8m]
| **Delivery** | part of OS/8

was [the primary programming system from DEC][faq10] 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][os78m] from 1979 interesting for comparison purposes.

The introduction date is that of OS/8, but OS/8 has a [complicated development history][faq10] 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.

[faq10]:
[os8m]:
[os78m]:


### <a id="pal8" name="pal-8"></a>PAL8

The earliest attestation to the PAL8 assembler I've been able to find is in the May 1970 printing of [Programming Languages][pl], the second in a two-volume set on programming PDP-8 family computers. (The first volume being [Introduction to Programming][ip].) 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](#pal-d) than to [PAL-III](#pal-iii).

The earliest reference to PAL8 as a named product of DEC is in the [PS/8 8K Programming System User's Guide][ps8ug] 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:

*   assembly options set via OS/8 CCL flags rather than via front-panel switches
*   output option for producing input to [CREF][os8m]
*   more operators in expressions:
    *    `^` for 12-bit unsigned multiplication (`*` was already taken for setting the location counter)
    *    `%` for 12-bit unsigned division (`/` was already taken as the comment start character)
*   the `FIXMRI` pseudo-op is back
*   `EJECT` pseudo-op extended to allow the page header to be changed for the next and following pages
*   new pseudo-operations:
    *   `DEVICE` and `FILENAME` alternatives to `TEXT` for passing arguments to OS/8 service routines
    *   `DTORG`: set DECtape block number in output for the [Typeset-8][ts8m] computerized typesetting system ([photo][ts8p])
    *   `IFNDEF` and `IFNZRO` negated forms of the conditional assembly tests
    *   `RELOC`: assemble code for relocation after loading

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](#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.

[ip]:
[pl]:
[ps8ug]:
[ts8m]:
[ts8p]:


### <a id="ralf"></a>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][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][fpp8a], 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:

*   More instructions and pseudo-ops:
    *   Arithmetic: `FADD`, `FADDM`, `FDIV`, `FMUL`, `FMULM`, `FSUB`
    *   Conditional jumps: `JEQ`, `JGE`, `JLE`, `JA`, `JNE`, `JGT`, `JAL`
    *   FPP to PDP-8 jumps: `JXN`, `TRAP3`, `TRAP4`
    *   Indexing: `ADDX`, `INDEX`, `LDX`
    *   Mark module as requiring double-precision hardware: `DPCHK`
    *   No-op: `NOP`
    *   Pointer moves: `JSA`, `JSR`, `SETB`, `SETX`
    *   Processor operations: `FCLA`, `FEXIT`, `FNEG`, `FNORM`, `FPAUSE`, `JAC`, `STARTD`, `STARTF`
    *   Register manipulation: `ALN`, `ATX`, `FLDA`, `FSTA`, `XTA`
    *   Set single-word addressing mode base address: `BASE`
*   Use 15-bit (double-word) addresses directly in FPP instructions, rather than the [PDP-8's memory model][mm8]

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](#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&times; 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](#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:

*   Expressions:
    *   Multiplication is `*` rather than `^`; this is possible because RALF offers the `ORG` pseudo-op to set the location counter, where PAL family assemblers use `*`
    *   Division is `/` rather than `%`, achieved by requiring whitespace before `/` if used to start a comment and disallowing spaces in expressions
*   Many new and changed pseudo-ops:
    *   `ADDR`: define address constant
    *   `E` and `F`: define floating-point constants
    *   `IFPOS`, `IFNEG`, `IFSW`, `IFNSW`, `IFFLAP`, and `IFRALF` conditional assembly pseudo-ops
    *   `IFREF`: replaces `IFDEF` pseudo-op, having a superset of its functionality
    *   `LISTOF`/`LISTON`: stop and start listing output rather than the PAL family's `XLIST` pseudo-op
    *   `REPEAT`: assemble the following line multiple times
    *   `COMMON`, `COMMZ`, `ENTRY`, `EXTERN`, `FIELD1`, `SECT`, and `SECT8` pseudo-ops to support FORTRAN IV code

Some may argue about my grouping of the expression changes here rather than under the [Arbitrary Differences](#ralf-ad) section below. I made this choice because the change was necessary to allow an improvement.

[fpp8a]:
[fpp12]:


#### <a id="ralf-jr"></a>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:

*   It does not understand the `PAGE` pseudo-op: the PAL form of this feature — where the page number is given as an argument to the pseudo-op — inherently goes against the nature of relocatable output. The inability to accept the argument-less version understood by [SABR](#sabr) is harder to justify.

*   It does not support current-page literals — `(42)` — since allowing it would complicate relocation.


#### <a id="ralf-ad"></a>Arbitrary Differences

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

*   Indirect addressing is done differently:

        TAD I A       / PAL syntax
        TAD% A        / RALF syntax

*   The programmer must tell the assembler when zero-page addressing is needed:

        TAD A         / PAL syntax, implicit Z flag based on known location of A
        TAD Z A       / PAL lets you be explicit if you want; space is not optional
        TADZ A        / RALF syntax: RALF needs to be told; no space can be given

*   Its expression syntax has no "and" operator, `&`

*   It does not support zero-page literals: `[20]`

    This restriction does not seem to have a good justification, even in relocatable assembler output, since the nature of the feature at both at the assembly and machine code levels is that the memory reference is inherently absolute. Thus, generation of a zero-page literal cannot affect the ability of the loader to relocate the generated code.


### <a id="flap"></a>FLAP

[RALF](#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.


### <a id="sabr"></a>SABR

As [RALF](#ralf) is to OS/8's FORTRAN IV compiler, so the [Symbolic Assembler for Binary Relocatable is to OS/8's FORTRAN II compiler.

SABR is a relative assembler, unlike [PAL8](#pal8) or [FLAP](#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][mm8], 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&nbsp;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:

*   No `FIELD` pseudo-op; not needed, given relocatable output
*   The `PAGE` pseudo-op takes no argument: it simply forces the assembler to go to the next page
*   Off-page indirection is allowed; the assembler will generate instructions to jump between pages as needed
*   No square bracket syntax for placing literals on page zero
*   Several new pseudo-ops to get around the fallout of all this programmer freedom:
    *   `ABSYM`: define a symbolic name for an absolute core memory location
    *   `CPAGE`: reserve N words of core on the current page if space is available, else next page
    *   `LAP` and `EAP`: leave and re-enter automatic paging mode
    *   `OPDEF` and `SKPDF`: define custom op-codes; `INC` non-skip version of `ISZ` predefined
    *   `REORG`: similar to `*` feature of PAL, except that it's forced to the top of the page
*   Arithmetic expressions banned in operands; simple `N+1` case replaced by the `#` feature

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:

*   `ARG`: define address/argument value pairs for calling FORTRAN subroutines
*   `CALL`: call external subroutine
*   `COMMN`: set aside storage space in field 1, which OS/8 FORTRAN II uses for `COMMON` storage
*    `DUMMY`: used in the calling convention for FORTRAN subroutines
*   `END`: mark end of program or subprogram; replaces meaning of `$` in PAL
*   `ENTRY`: mark subprogram entry point
*   `FORTR`: assemble FORTRAN tape
*   `RETRN`: return from external subroutine

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:

*   `ACH`, `ACM`, `ACL`: address sub-words in a multi-word value
*   `D` prefix on numeric constants cause decimal interpretation where `OCTAL` is otherwise in effect
*   `K` prefix for octal-in-decimal-context flip case
*   `IF`: conditionally skip assembling some number of the following instructions

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

*   `BLOCK`: same as `ZBLOCK` in PAL8 and PAL-D
*   `IF` not as flexible as `IFDEF`, `IFNDEF`, `IFZERO` and `IFNZRO`
*    Missing pseudo-ops:
     *   `DEVICE` and `FILENAME`: not needed, call thru FORTRAN II library instead of direct to OS/8 USR
     *   `DTORG`, `EJECT`, `XLIST`, `NOPUNCH`, and `ENPUNCH` output controls
     *   `RELOC`: not needed, SABR output is relocatable by default

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][cc8] 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.

[cc8]:
[mm8]:


## <a id="macrel" name="macrel-2"></a>MACREL

| Introduced | 1977 (v1), 1979 (v2)
| Manual | [text, 384
| 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**


## <a id="smal" name="smal8"></a>SMAL8

| **Introduced** | 1980, 2012
| **Manual** | [SMAL32][smal32], [SMAL8][smal8]
| **Delivery** | Internet, same links as above

SMAL is a series of cross-assemblers initially created by [Douglas W. Jones][dwj]. 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](#palbart) below for more on that thread of history.)

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

Some of the differences listed below mean that although the basic syntax of SMAL8 is solidly in the [PAL-III](#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:

*   relocatable output
*   `MACRO` and `ENDMAC`: MACREL-like macro feature
*   `COMMON`, `EXTERNAL`, `GLOBAL`: module and linkage support
*   `<` and `>` comparison operators in expressions
*   `<<`, `>>`, and `>>>` bit shifting operators in expressions
*   double and single quoted strings; PAL8 supports only double-quoted single-character constants
*   labels separated from the code by either comma (PAL8 style) or colon (SMAL32 style)
*   arbitrary radices in numeric constants using `r#n` syntax, which gives `n` base `r`
*   the &brvbar; operator is allowed as an alias for PAL8's `!` form of Boolean OR
*   end assembly with either `$`  (PAL8 style) or `END` ([SABR](#sabr) style)
*   `ABS`, `DEF`, `FWD`, `LEN`, `REL`, and `TYP` assembly-time functions
*   `TITLE` and `SUBTITLE` pseudo-ops for setting listing page titles
*   `ASCII` pseudo-op: like `TEXT` but stores real unpacked ASCII
*   many more pseudo-instructions defined: DF32, DECtape, etc.

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

*   powerful expressions in SMAL allow modern `IF`, `ELSE`, `ELSEIF` and `ENDIF` for conditional assembly
*   `PAGE` takes no argument, as in [SABR](#sabr)

There are a few features removed relative to PAL8:

*   multiplication is not allowed in expressions; `*` was given the PAL8 meaning, and `^` is not implemented
*   division is not allowed in expressions; `/` was given the PAL8 meaning, and `%` is not implemented
*   neither the `(N)` nor `[N]` syntaxes for inline literals work; parens used for groups and lists instead
*   some IOT instructions PAL8 knows do not have predefined symbols: `KIE`, `RPE`, etc.

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.

[dwj]:
[smal8]:
[smal32]:


## <a id="palbart"></a>palbart

| **Introduced** | 1996
| **Manual** | [man
| **Delivery** | Internet, PiDP-8/I project

`palbart` is a popular fork of the `pal.c` assembler by [Douglas W. 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 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:

*   — the assembler's text listing output, giving the assembled machine code alongside the source code, plus any error messages
*   — the machine code output in RIM paper tape format, suitable for loading into SIMH's PDP-8 simulator
*   — a SIMH init script that deposits the machine code into the PDP-8 simulator's core memory and starts it running; pass the file name to `bin/pidp8i-sim`

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][tkt].

**INCOMPLETE**

[tkt]:


## <a id="pdpnasm"></a>pdpnasm

| **Introduced** | 2006
| **Manual** | see its [home page][pdpnasm]
| **Delivery** | [Internet][pdpnasm]

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`](/rptview?rn=4). If you know of more, please [file them](/tktnew). `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.

[pdpnasm]:

**INCOMPLETE**


## License

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

[sl]: