The EXEC module (my designation) follows the FILSER routines
which were discussed in the last article. This module contains the
executive program and the remainder of the supervisor calls.
The first routine in EXEC is EXIT. When a job is allocated to
the system, the JOBS program sets EXIT as the first program the job
will execute. EXIT first enables interrupts (in case the user program
left the processor LOCKed) and then determines if the EXIT call was
forced by a CTRLC call. If a Control C is waiting (J.CCC set in
JOBSTS), "^C" is sent to the job's terminal and the JOBCMZ word is
cleared, which aborts any remaining commands in a command file. All
flags except J.ALC are cleared from the JOBSTS word and the J.MON flag
is set.
The DDBCHN is then scanned to determine if the job has any I/O
queued. If it does, the remainder of the job's CPU time is used to
process it. (Queued interrupt driven I/O is not supported in the 4.2
release of the monitor).
The error control intercept (JOBERC) and the breakpoint vector
address (JOBBPT) are both cleared and the job's stack pointer is
restored to the top of the job's stack. If the job has memory
assigned (the J.NUL bit is not set in the JOBTYP word) the DEVTBL is
scanned and any devices assigned to the job are deassigned and the
following memory tests are performed.
If the job has a new memory allocation the first word of the
job's memory will be cleared, insuring the memory partition contains
no garbage. The partition and any memory modules in it are tested for
address errors. The system error message, "[MEMORY MAP DESTROYED]", is
returned if the partition's base address or a module's address is odd,
or the partition's base address or a module's address is above MEMEND.
If none of the module flag codes: FIL, FGD, or LOK are set the module
is deleted.
The memory calls, GETMEM and CHGMEM will always return an even
module size and clear the word following the module, but memory module
errors can be caused by a program which itself modifies the module
size word, either intentionally or by accident. The location of
ascending modules is determined by adding the module size word to the
first housekeeping word. If the contents of this location are zero,
the end of memory modules is assumed to have been reached. If the
address is odd, caused by an odd byte count, or the address is beyond
then end of user memory (MEMEND), the error is reported and the
contents of the offending address are cleared.
If a program leaves some garbage modules in the partition (VUE
used to) which cannot be deleted with DEL, a MEMORY 0 command followed
by MEMORY XXXXX will restore the partition.
Normally, this type of memory error should not be fatal to the
job or the system, but there are two bugs in EXIT which can cause the
job or the system to crash. The first is that no test is made to
determine if a module, either real or erroneous, extends beyond the
end of the job's partition. The second is in the method by which EXIT
clears the erroneous address. The current module's address is saved
in R3 before the next module's address is calculated. If the module
creates an error the contents of R3 are cleared, however R3 is not set
until after the first module is tested. Therefore, if the first
module creates an error, the contents of an undetermined R3 are
cleared.
If the job has terminal output in progress, it will loop in EXIT
until all terminal output has been processed after which the terminal
status flags will be reset (restoring normal terminal I/O) and control
will pass to the executive program.
The executive program (hereafter called EXEC) is AMOS command
level, which upon entry issues the monitor prompt, ".", and calls KBD
where the job is descheduled until terminal input is available, or
where, as described in the first of these articles, if a command file
is being processed, the input line buffer is filled from the command
file data area. After a command is entered, EXEC resets the job stack
pointer to the top of the stack and determines if the job has any
memory allocated. If the job does not have any memory, EXEC will
allocate to the job all of the available memory in the job's bank. If
at least 2000 decimal bytes are not available, the "[NO MEMORY
AVAILABLE]" message is issued and EXIT is called.
A FSPEC call with a null default extension is performed on the
command line with the job run block (JOBRBK - partial DDB in the JCB)
indexed to receive the file specification. The file name is copied
into the JOBNAM words, the J.MON bit cleared and the J.LOD bit set in
the JOBSTS word, and a search for the file is made. The Alpha Micro
document, "AMOS USER'S GUIDE" (DWM-00100-35), is somewhat ambiguous on
what format is allowed for system commands (page 7-3), however full
file specifications are allowed. The file search order is documented,
although not accurately, in Appendix B of that manual, and is briefly
summarized below. If the Device, Drive, Extension, or PPN are
supplied on the command line, the appropriate search is bypassed.
DEVICE EXTENSION PPN
------ --------- ---
system memory PRG N/A
user memory PRG N/A
DSK0: PRG [1,4]
DSK0: CMD [2,2]
user PRG user
user CMD user
user PRG user library
If the search failed up to this point, DSK0:MDO.PRG[1,4] is
loaded which attempts to locate the file in following order:
DEVICE EXTENSION PPN
------ --------- ---
user DO user
user DO user library
DSK0: DO [2,2]
If the file is found it is loaded into the job's memory,
providing enough memory is available. If the file is not found, the
command is echoed to the job's terminal, bracketed with question
marks. The message "?Insufficient memory for program load" is
returned if the job did not have enough memory.
After the file is loaded, EXEC tests its first word to determine
if the program can be run not logged in. If the first word of the
file is non-zero, the JOBUSR word is tested and if it is zero (the job
is not logged in), the "[LOGIN PLEASE]" prompt is issued and the
executive program exits.
The file's extension is tested and if it is "PRG", program
execution begins with MOV R3,PC. Upon entry into the program the
registers contain the following:
R0 - base address of JCB
R1 - cleared
R2 - remainder of input line buffer
R3 - base address of program
R4 - cleared
R5 - cleared
A file with an extension other than "PRG" is assumed to be a
command file and EXEC moves the file in reverse order to the top of
the job's partition, sets the C.SIL bit in the JOBCMS word, and
branches to the entry point of EXEC. If the file was loaded under
control of MDO, the specified parameters are inserted into the command
file and a block move places the command file at the top of the job's
partition after which MDO EXITs.
The numeric conversion calls, DCVT and OCVT (or HCVT - hex
convert), follow EXEC. They perform binary to ASCII conversions based
on table entries, which limit the magnitude of the output.
Following the conversion routines is FSPEC which is well
documented in the "AMOS MONITOR CALLS MANUAL" (pp 6-8 through 6-9).
Lower to upper case ASCII conversions are not made in FSPEC, therefore
whatever R2 points to (terminal input line buffer or program buffer)
must be in upper case.
PFILE follows FSPEC and performs in essentially the opposite
manner to FSPEC. Its output is directed only to the job's terminal
(via a TTYL) and PPN output is always in octal reqardless of the
setting of the J.HEX flag in the JOBSTS word.
The JOBIDX, JOBGET, and JOBSET require more words to describe
than the amount of code they occupy in the monitor. They are all SVCB
calls and require a considerable amount of time to decode in the SVCB
processing module. If speed of execution for a program is a
consideration the following macro definition will mimic these calls
saving over sixty instructions to be executed:
J.IDX = -1 ; JOBIDX control flag
J.GET = 0 ; JOBGET control flag
J.SET = 1 ; JOBSET control flag
DEFINE JCB TAG,ITEM,CTRL
PUSH #ITEM ; load JCB index
ADD @#JOBCUR,@SP ; build job table entry
IF LT,CTRL, POP TAG ; JOBIDX
IF EQ,CTRL, MOV @(SP)+,TAG ; JOBGET
IF GT,CTRL, MOV TAG,@(SP)+ ; JOBSET
ENDM
; JOBIDX R0,JOBTRM ; is duplicated by
JCB R0,JOBTRM,J.IDX
; JOBGET R0,JOBTRM ; is duplicated by
JCB R0,JOBTRM,J.GET
; JOBSET R0,JOBTRM ; is duplicated by
JCB R0,JOBTRM,J.SET
The object code resulting from this macro will use between 62 and
77 machine cycles, while just the SVCB instruction and the ensuing
RSVC instruction alone consume 135 machine cycles (which doesn't
include any of the decoding timing).
The three calls USRBAS, USREND, and USRFRE follow next in the
monitor. They function as described in the AMOS MONITOR CALLS MANUAL.
The CTRLC call was briefly described in the previous article on
TRMSER. It tests the J.CCC bit in the JOBSTS word and, if set, a
Control C was entered at the job's terminal (or by the KILL program)
and the argument address of CTRLC is placed in the saved PC.
The PRNAM call sends its output to the job's terminal via a TTY
call as does PRPPN. Like PFILE, PRPPN displays the PPN in octal
regardless of the setting of the J.HEX flag in the JOBSTS word.
The terminal input line processing calls BYP, ALF, NUM, TRM and
LIN occupy the next section of the monitor and are unremarkable except
to note that ALF tests for upper case alphabetic characters only.
The FILNAM call functions exactly as described in the AMOS
MONITOR CALLS MANUAL.
The GTOCT call follows FILNAM and is really two calls; GTOCT and
GTHEX. The GTHEX code is used if the J.HEX bit is set in the JOBSTS
word. If the leading character is alpha, it does not need to be
preceded by zero. If the input is greater than 177777 octal (FFFF
hex), causing an error to be reported (N flag set), the result
contained in R1 will be meaningless.
GTDEC, unlike GTOCT will stop processing the input line if the
next character will cause the result to be greater the 65,535.
The GTPPN call will always process the input PPN on an octal
basis regardless of the setting of the J.HEX bit.
The final two calls in this section of the monitor are the PACK
and UNPACK calls for which no description will be attempted except to
note that UNPACK uses a small (two word) table for unpacking.
The next section of the monitor is DSKSER which is mentioned by
Alpha Micro only briefly in FILSER. DSKSER is the generalized device
driver for file structured devices such as floppy disks and hard
disks. It simplifies the actual code needed for a disk driver and
allows the system to access several devices without a great amount of
code. Like other device drivers it has a comminucation area at its
beginning which was defined in the last article. The attribute word
of DSKSER is zero, but all functions are supported, except ASSIGN.
File structured devices cannot be assigned to one job, but for obvious
reasons, two jobs cannot be given access to the same file structured
device simultaneously. The controlling job cannot lock other jobs out
by disabling interrupts because other I/O devices might lose data.
The solution is to increase the controlling job's priority
considerably until it is done with the disk. In DSKSER, this is
accomplished by setting the job's priority counter to 177777 octal
(about 18 minutes).
Before discussing DSKSER's routines, the structure of a disk
driver communication area will be described as well as the three
support routines used by DSKSER.
Disk drivers, like non-file structured device drivers have a
communication area at their beginning, which contains some differences
from other drivers. The 200DVR.DVR disk driver communication area is
described below as an example:
WORD 1 - logical record size
WORD 2 - driver attributes
WORD 3 - driver entry address offset
WORD 4 - physical sector size
WORD 5 - physical sectors per logical record
WORD 6 - maximum record number
WORD 7-16 - not used
WORD 17 - maximum record number
WORD 18 - directory entries per record
WORD 19 - bitmap size
The remaining five words are specific to the physical drive type.
DSKSER includes three support routines for locating devices and
files. The first of these is a routine (DEVTST) to determine if the
device specified in the DDB exists and is mounted. If the device is
not specified in the DDB, DEVTST will pick up the job's default device
and drive. If the device is specified, but the drive is not (DDB+23 =
377), device 0 is used. The appropriate error code is set in DDB+1 if
the device is not found in the DEVTBL or the device is not mounted.
The second routine (FILTST) is used to locate the UFD entry of
the file specified in the DDB. The calling sequence to FILTST is
somewhat different than a normal subroutine call in that control flags
are set to limit the actions FILTST can perform. The sequence is:
CALL R5,FILTST ; R5 is used as the linkage register
WORD FLAGS ; control flags
Control flags:
Bit 0 - locate file
Bit 1 - return error if file already exists
Bit 2 - test for programmer number match
Bit 3 - lock directory (DSKDRL)
Various combinations of flags can be used. When FILTST returns, R5 is
incremented past the control flags.
The third routine (MFDTST) reads the MFD (record one) of the
device to locate the specified UFD. If the PPN is not specified in
the DDB, JOBUSR is used to locate the UFD link.
The following is a discussion of the DSKSER routines. Keep in
mind that these routines are called by FILSER and are not directly
entered with an SVCB 0. FILSER can call any devi
ce driver on the
system, of which DSKSER is only one.
The first routine in DSKSER is the physical READ/WRITE routine.
This is the only section of DSKSER which actually makes calls to the
device driver. A call to DEVTST insures that the device exists and is
mounted. The specified record number (DDB+10) is tested to determine
if it is within the range of the device. If the READ/WRITE call is
valid, the device is locked to this job by resetting the job's
priority counter as described above. The DDB record number is
multiplied by the number of sectors per logical block for the device
to obtain the physical sector for the device and the transfer is
initiated. Upon return from the device driver the job's priority
counter is reset to 1 and, if no other jobs are currently scheduled,
the routine returns. If other jobs are scheduled, the job is put to
SLEEP for one clock tick (presumably to give other jobs CPU time).
The OPEN routine handles all four OPEN calls: LOOKUP, OPENI,
OPENO, and OPENR. The OPENO call insures that the user has access to
the PPN and that the file does not already exist with a call to FILTST
and then allocates a disk record for file data. The physical record
number is returned in DDB+10 and DDB+42. The buffer index (DDB+6) is
set to two. The OPENI and OPENR calls locate the file and if it
exists insure that it is the same type as specified in the open code
of the DDB (DDB+35). If the file type matches, the number of records
in the file is placed in DDB+36, the byte count of the last record (or
-1 if a random file) is set in DDB+40, and the first record number is
set in DDB+42 and DDB+10. The LOOKUP call does not distinguish between
random and sequential files.
The CLOSE call locks the disk directory and then locates the UFD
of the PPN specified in DDB+32 with an MFDTST call. If a directory
for the PPN is not allocated, CLOSE will allocate a record. If a
directory for the PPN already exists, CLOSE locates the first empty or
deleted entry, or if the record is full, allocates another directory
record. Once an empty directory entry is found, the file name and the
file directory parameters are inserted into the directory, the bitmap
is updated, and the directory is unlocked. If it was neccessary to
allocate a directory record, the unused words in the record are
cleared to nulls to insure there are no spurious entries.
The INPUT routine follows CLOSE. For files open for sequential
input, INPUT executes a READ based on the record number in DDB+10.
The link to the next record is updated in DDB+10 and the buffer index,
DDB+6, is set to two, bypassing the link word. For files open for
random processing, INPUT uses the data in DDB+10 as an offset from the
base of the file contained in DDB+42. DDB+10 is not updated.
The DSKSER OUTPUT routine for files open for sequential output
can be called on two levels (DDB+22 - call level). A level one call
is made with a normal user program OUPUT call which first allocates
another disk record for the next call, inserts that record number link
into the first word of the DDB buffer, writes the current record,
increments the record count (DDB+36), resets the buffer index to two,
and sets the next record number in DDB+10. A level two call is made
through a FILSER CLOSE call, which in turn calls the FILSER OUTPUT
routine to write the last record. This call does not allocate another
record, instead it determines the amount data in the DDB buffer and
fills out to the record size with nulls before writing the record.
The OUTPUT routine for files open for random processing insures
that the user has access to the PPN with a FILTST call and then writes
to the record using the file base offset in DDB+10 added to the file
record base in DDB+42 as the current record number.
The DELETE routine locks the directory, insuring the user's PPN
grants access to the file, sets the first word of the file name in the
directory entry to -1 and then deallocates each record the file had
used from the bitmap.
The RENAME routine also locks the directory, insures the user's
PPN allows access to the directory, determines that a file of the same
name does not already exist and then enters the new name, contained in
the three words following the DDB, into the directory in place of the
previous name.
The three support routines for DSKSER, described above, follow
the above routines in the monitor.
The FILSER SPECL routines are SVCB 0, function code 14 octal,
calls: the bitmap service calls DSKDRL, DSKDRU, DSKALC, DSKDEA,
DSKBMR, DSKBMW, and DSKCTG. They reside in the monitor directly after
the above described DSKSER routines. These calls are fully described
in the AMOS MONITOR CALLS MANUAL in the section entitled "DISK SERVICE
MONITOR CALLS" (pp 6-17 through 6-21) and the reader is advised to
refer to that document for detailed information.
Directly following these seven calls is a bitmap service routine.
The function of this routine is to locate the device's bitmap (if
any), rewrite it if neccessary, and recompute the hash total to insure
the current bitmap is correct. If the bitmap is currently locked by
some other job, this routine will stall until the bitmap is free. If
the bitmap hash total is not correct, the drive is apparantly disabled
by setting the drive number contained in the bitmap's partial DDB to
177.
Following this routine is the supervisor call HTIM which is
called by 200DVR.DVR if PERSCI drives are being used. HTIM copies the
HLDTIM argument into HLDTIM+2 and inserts a clock tick counter routine
into the CLKQUE which will decrement the HLDTIM+2 argument until it
reaches zero. Upon reaching zero, the routine sends the command to
unload the heads of the disk drive and then deschedules itself. The
routine will not be scheduled again if it is already running.
The next area of the system monitor is a 2000 (decimal) byte area
reserved for the system disk driver. MONGEN.PRG inserts the driver
routine here and updates the MEMBAS word in the system communication
area to reflect the end of the driver. Describing the functioning of
a disk driver is beyond the scope of this article. However, in
passing, it should be noted that disk drivers maintain data storage
areas within their own code.
The last section of the monitor is INITIA, the initialization
program. INITIA is not actually part of the monitor, it merely
defines the initial system parameters and starts the first job running
the SYSTEM.INI command file; it is eventually overwritten as it
resides above the base of system memory.
As described in the first article, after one of the various
monitor loaders, the controller board's PROM routine, MONTST, WNGLOD,
HWKLOD, etc., load the system, interrupts are disabled and a CLR PC is
executed which sets PC to absolute memory location 0. This location
initially contains a JMP instruction to INITIA. INITIA branches
around its own stack and starts a memory test to determine how much
memory the system has available in BANK 0, starting at the end of
INITIA and testing in 1K increments. When the end of memory is found,
it is set into MEMEND. JOBTBL and JOBCUR both get the address of
MEMBAS, JOBESZ is defined (currently 292 decimal bytes), and the first
JCB entry is cleared. MEMBAS is reset to the end of this first JOBTBL
entry.
An 8K partition is then set up at the end of memory which
contains a temporary terminal line table for a pseudo terminal with a
pseudo interface driver. This terminal is attached to the first job
and allows it to communicate until the first terminal is defined. A
temporary device table (DEVTBL) entry is also allocated in the
partition for DSK0: and has no bitmap. The various JCB entries are
then inserted into the first JOBTBL entry including logging the job
into [1,4].
"SYSTEM.INI<CR><LF>" is entered in the command file buffer at the
top of the partition and the address of EXIT is set on the job's stack
as the saved PC of the first program the job will run. The job is
then scheduled for CPU time with a JRUN, after which INITIA enables
interrupts and loops, waiting for the first clock interrupt to start
the job with system initialization.