The Pascal Compiler implicitly emits references to global variables exported from module SYSGLOBALS. These variables are used for error codes, TRY.. RECOVER management, and heap management. Global variables are referenced through offsets from 680x0 register A5 in the Pascal environment. The variables require a total of 22 bytes of space. Refer to the System Internals Document, "Pascal Listings," file INITLOAD.TEXT, module SYSGLOBALS for more information. These variables require the first 22 bytes of all SYSGLOBALS variables (INTEGER sysioresult is at offset -22).
The BASIC Operating System (OS) is written in MODCAL (a superset of the HP standard Pascal compilers; it is an internal HP tool). The Modcal compiler uses the same implicit global variables. While building the BASIC (bootable) system, an area in high RAM is reserved for these globals (and others); it can be referred to as BASIC Globals. The BASIC operating system uses the first 22 bytes of this area for the implicit compiler globals (TRY..RECOVER, etc). When running BASIC, register A5 points to the base of this area.
If there are no explicitly declared global variables in a CSUB, this BASIC Globals area will also be used for any implicit compiler globals needed by the CSUB. BUILDC will not issue a prompt for a global COM name.
When a CSUB is called (from BASIC), execution control is eventually passed to CSUBENTRY. CSUBENTRY (with no explicit globals declared) leaves A5 pointing to the base of the BASIC Globals area. CSUBENTRY, after setting up the stack correctly, will then transfer control to the actual user code.
If the CSUB requires additional globals, the BASIC Globals space does not have room for the CSUB globals in addition to the 22 bytes required for implicit SYSGLOBALS. It is necessary to use COM for the entire global space. The size of the COM area (in bytes) is equal to the 22 bytes of implicit globals (the SYSGLOBALS area) plus the CSUB globals. BUILDC adds the 22 bytes whenever it detects explicit global space.
The 22 bytes of global space are actually reserved by module CSUBENTRY. This is the first module linked in (by streaming GENC.TEXT) on the first link pass. When modifying GENC to add/remove modules, CSUBENTRY must remain as the first module linked. The Librarian will offset the CSUB globals down 22 bytes to account for this. If the link order is changed CSUB globals will occupy the first 22 bytes. However, the CSUB utilities will always locate the implicit compiler globals to these first 22 bytes; thus a space conflict will be encountered. Remember, CSUBENTRY must be the first module linked in the GENC.TEXT stream file!
Now when CSUBENTRY is called, it sets A5 to the base of the appropriate COM area before transferring to user code. If this is not done correctly (i.e., because modules were not linked before running BUILDC), assignment to CSUB globals will overwrite BASIC system variables.
The Pascal ESCAPE statement may be used to do error reporting from a CSUB.
ESCAPE(some_number);
This statement causes one of three things to happen.
The following table shows the possible error numbers and how they are interpreted by the system:
Pascal Errors | BASIC Definition |
---|---|
-32767...-891 | SYSTEM ERROR |
reserved by operating system | |
SYSTEM ERROR | |
ERROR (escape_code = 400) | |
Pascal exit, no error | |
ERROR (escape_code MOD 1000) |
CSUBs may be written in assembly language either as Pascal-type modules or as stand-alone routines. Pascal calling sequences for passing parameters must be observed.
Information about writing assembly language routines can be found in the Pascal Workstation System manual. "The Assembler" chapter, particularly "The Programming System" section, should be read before attempting to write a routine.
Assembly language routines are limited in the same way as Pascal routines. They must be relocatable at link time.
This module performs a palindrome check on a BASIC string. A palindrome is a string that says the same thing forward and backwards (e.g., "radar").
The BASIC declaration is:
CSUB Palindrome(Test$, INTEGER Result)
The result will be "1" if Test$ is a palindrome, and "0" otherwise. This is a simple algorithm and therefore ignorant of case, punctuation, and spaces.
The main algorithm sets up an address register pointing to each end of the string. It then compares the current characters working its way to the center of the string. This is repeated until:
For this assembly routine, give BUILDC a null name (just press [Enter]) when asked for a module name.
* Define some handy mnemonics
front equ a0 Pointer to the front half of the string
back equ a1 Pointer to the back half of the string
result equ a2 Pointer to a 16 bit INTEGER
return equ a3 The return address
temp equ d0 Used for calculations and comparisons
* Define the entry point
def palindrome
* Go for it. First unload the stack
palindrome equ *
movea.l (sp)=,return
movea.l (sp)=,result
movea.l (sp)=,front
addq.l #4,sp The dimentryptr is not used in this CSUB
* Get the current string length and use this to set up
* the front and back pointers. Remember, a BASIC string is
* a 16-bit integer followed by a packed array of characters.
clr.l temp
move.w (front),temp Get string length
addq.l #2,front The first character is always here
lea 0(front,temp),back Back points to last character=1
* Use the pre-increment and post-decrement of the address registers
* to compare characters and update pointers.
loop move.b -(back),temp Get next "back" character;
cmp.b (front)=,temp compare it to the "front";
bne.s false quit if there is a mismatch
cmpa front,back If "back" is still greater...
bgt loop ...keep trying
true move.w #1,(result) Set it true
jmp (return)
false clr.w (result)
jmp (return)
end
Normally, when you attempt to purge a file, BASIC checks to see if it is open before allowing the PURGE. Also, files are automatically closed when the @Name (I/O path name) assigned to it is going to be de-allocated or reused.
These actions are implemented by a search through the BASIC symbol tables for @Names. Obviously, this search cannot find the memory used for a file control block by a CSUB using the FAL. The way to get around this is to use a file control block which is the value area for an I/O path. There are two ways to do this. First, you can pass the I/O path as a parameter to the CSUB (see the "Writing CSUBs" chapter in this manual for details). Second, you can create a COM which includes an I/O path, then access it by using FIND_COM, and use the value area corresponding to the @Name as the file control block. Be careful to direct the fcb_ptr to the correct memory location so that fields in the file control block will be aligned with the correct fields in the @Name.
Value areas occur in the common block in the reverse order of the declaration of the variable name in BASIC. Thus, FIND_COM returns the address of the last variable in the declaration of the common block, the next-to-last variable lies just above it in memory, etc. The first variable in the common declaration occupies the highest addresses in the value area for the common block.
This technique will enable FAL_PURGE to check if a file has been FAL_OPENed, which would otherwise not happen.
NOTE |
---|
If you use this technique, do not zero the fcb before using it. The BASIC system will take care of any necessary initializations. If the fcb has been used as an I/O path in BASIC, zeroing it in the CSUB may cause incorrect behavior. |
Some special considerations may occur when using an I/O path as an idtable (file control block). Both FAL_CLOSE and FAL_OPEN will close an I/O path which is already open, including I/O paths for files, devices, and buffers. Attempting to use any other FAL routines with an idtable which is closed or which is open (as an I/O path) on a device or buffer will give an error 51 (file not assigned). While using an I/O path as a file control block (idtable) provides some additional protection in the areas of automatic file closure and improper file PURGEs, it is recommended that the program explicitly close files when done using them and before purging them.