The source code for MACRO SPITBOL contains extensive documentation. I have extracted the specification of the MINIMAL (Machine Independent Macro Assembly Language) and the specification of the OSINT (Operatint System INterface) and converted the plain text to HTML, resulting in what is now the “MINIMAL Reference Manual.
As part of this effort I wrote an introduction in order to give a sense of the flavor of the code. Here is that introduction:
Introduction
The implementation of MACRO SPITBOL is written in three languages: MINIMAL, C, and assembler.
The SPITBOL compiler and runtime is written in MINIMAL, a machine-independent portable assembly language.
The runtime is augmented by procedures written in C that collectively comprise OSINT (Operating System INTerface). These procedures provides such functions as input and output, system initialization and termination, management of UNIX pipes, the loading of external functions, the writing and reading of save files and load modules, and so forth.
The implementation also includes assembly code. This size of this code varies according to the target machine. About 1500 lines are needed for the x86 architecture running UNIX.
This code provides such functions as macros that define the translation of MINIMAL instructions that take more than a few machine-level instructions, support for calling C procedures from MINIMAL, for calling MINIMAL procedures from C, for creating save files and load modules, and for resuming execution from save files or load modules.
To give some idea of the flavor of the code, consider the following simple SPITBOL program that copies standard input to standard output.
loop output = input :s(loop)
end
By default, the variable input is input-associated to standard input, so each attempt to get its value results in reading in a line from standard input and returning the line as a string. The read fails if there are no more lines, and succeeds otherwise.
Similarly, the variable output is output-associated with standard output, so each assignment to output causes the assigned value to be written to the standard output file.
The osint procedure for writing a line is SYSOU. It is called from within SPITBOL as part of assignment, as shown in the follwing excerpt from the MINIMAL source:
* here for output association
asg10 bze kvoup,asg07 ignore output assoc if output off
asg1b mov xl,xr copy trblk pointer
mov xr,trnxt(xr) point to next trblk
beq (xr),=b_trt,asg1b loop back if another trblk
mov xr,xl else point back to last trblk
.if .cnbf
mov -(xs),trval(xr) stack value to output
.else
mov xr,trval(xr) get value to output
beq (xr),=b_bct,asg11 branch if buffer
mov -(xs),xr stack value to output
.fi
jsr gtstg convert to string
ppm asg12 get datatype name if unconvertible
* merge with string or buffer to output in xr
asg11 mov wa,trfpt(xl) fcblk ptr
bze wa,asg13 jump if standard output file
* here for output to file
asg1a jsr sysou call system output routine
err 206,output caused file overflow
err 207,output caused non-recoverable error
exi else all done, return to caller
From the OSINT C code (the C procedure name starts with ‘z’ since there is intermediate code (shown below) to call from MINIMAL to C at runtime):
zysou()
{
REGISTER struct fcblk *fcb = WA(struct fcblk *);
REGISTER union block *blk = XR(union block *);
int result;
if (blk->scb.typ == type_scl) {
/* called with string, get length from SCBLK */
SET_WA(blk->scb.len);
} else {
/* called with buffer, get length from BCBLK, and treat BSBLK
* like an SCBLK
*/
SET_WA(blk->bcb.len);
SET_XR(blk->bcb.bcbuf);
}
if (fcb == (struct fcblk *) 0 || fcb == (struct fcblk *) 1) {
if (!fcb)
result = zyspi();
else
result = zyspr();
if (result == EXI_0)
return EXI_0;
else
return EXI_2;
}
/* ensure iob is open, fail if unsuccessful */
if (!(MK_MP(fcb->iob, struct ioblk *)->flg1 & IO_OPN)) {
return EXI_1;
}
/* write the data, fail if unsuccessful */
if (oswrite
(fcb->mode, fcb->rsz, WA(word), MK_MP(fcb->iob, struct ioblk *),
XR(struct scblk *)) != 0)
return EXI_2;
/* normal return */
return EXI_0;
}
Here is the assembly code that is used to call a C procedure from MINIMAL. The code is for 32-bit X86
and is written using NASM (Netwide Assembler) syntax.
%macro mtoc 1
extern %1
; save minimal registers to make their values available to called procedure
mov dword [reg_wa],ecx
mov dword [reg_wb],ebx
mov dword [reg_wc],edx ; (also reg_ia)
mov dword [reg_xr],edi
mov dword [reg_xl],esi
mov dword [reg_cp],ebp ; Needed in image saved by sysxi
call %1 ; call c interface function
; restore minimal registers since called procedure may have changed them
mov ecx, dword [reg_wa] ; restore registers
mov ebx, dword [reg_wb]
mov edx, dword [reg_wc] ; (also reg_ia)
mov edi, dword [reg_xr]
mov esi, dword [reg_xl]
mov ebp, dword [reg_cp]
; restore direction flag in (the unlikely) case that it was changed
cld
; note that the called procedure must return exi action in eax
ret
%endmacro
...
global sysou ; output record
sysou:
mtoc zysou