359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
|
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
|
+
+
+
+
+
+
+
+
+
+
+
+
+
|
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 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 does have some special accommodations for page zero. The `ABSYM` directive can place values on page zero, and the use of small literal constants in instructions like an indirect `TAD` is understood to refer to page zero:
ABSYM AINC0 K10 / define name for auto-increment register 10 octal
........ / load a core address into AC somehow
CLA
TAD I AINC0 / pseudo-C: ac = *ainc0++;
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
|