**** N.B. THIS IS OUT OF DATE IN A NUMBER OF RESPECTS **** POPLOG Porting Guide ==================== John Gibson School of Cognitive and Computing Sciences, University of Sussex July 1986 Slightly modifed by Aaron Sloman, University of Birmingham 8 Jun 2012 Contents -------- 1. Compiling and Building the POPLOG Core 2. Machine-Specific Tasks 2.1 General Considerations 2.1.1 Representation of POPLOG Items 2.1.2 Memory Resources 2.1.3 Procedures 2.1.4 Control Stack Frames 2.1.5 The User Stack 2.1.6 Register Allocation in General 2.2 Changes to the Source Code 2.2.1 Hand-coded Assembler Files 2.2.2 POPC Code Generation 2.2.3 Run-time Code Generation 3. Operating System-Specific Tasks 3.1 I/O in POPLOG 3.2 Other O/S Utilities 3.3 External Procedures This document is a guide to porting the Sussex University POPLOG system to new machine/operating system environments. It should be read in conjunction with the POPLOG files REF VMCODE and REF SYSPOP11. While describing the architectural features of the system that are generally relevant to the porting enterprise, it is not a manual and therefore does not (necessarily) address issues in detail; it will certainly not cover many contingencies encountered in doing an actual port. It is a regrettable fact that most modern machines are designed around the needs of languages like FORTRAN, PASCAL, C, etc, and not around those of systems like POPLOG; for this reason, porting the system may have something of the flavour of fitting a square peg into a round hole. Essentially, the work involved splits into machine-specific tasks and operating system-specific tasks; a port to a given host may involve either or both of these, depending on whether an implementation already exists for one or the other. The table below subdivides the activities and gives approximate timescales for the components parts: Machine-Specific Man-months ---------------- ---------- Hand-Coded Assembler : ? POPC Code Generation : ? Run-Time Code Generation : ? O/S-Specific Man-months ------------ ---------- I/O Interface : ? Other O/S Utilities : ? External Procedures : ? 1. Compiling and Building the POPLOG Core ----------------------------------------- The core POPLOG system (that is, excluding libraries, PROLOG and Common LISP sources) comprises roughly 140 source files, sub-divided as follows: Type Extension Files Lines ---- --------- ------ ----- Assembler source .s 10 2000 C source .c 1 300 sysPOP-11 source .p 326 60000 sysPOP-11 header .ph 7 2000 The system is built by compiling the Assembler, C and sysPOP-11 sources into object files, which are then linked to produce the base executable binary image. For the Assembler and C source, compilation is done with the host system's assembler and C compiler. Compilation of the sysPOP-11 sources (which 'include' the sysPOP-11 header files) is more complicated, and we shall describe this in detail. All POPLOG compilers, including POP-11, compile source code into POPLOG Virtual Machine (VM) instructions; in the normal run-time system, the resulting list of VM code is further compiled into host machine-code inside procedure records (which are what actually get executed). However, the VM has a facility whereby the instruction list can be diverted from its normal route, and passed instead to a special user- defined procedure, which can then process it in any desired way. This facility is used by the system compiler POPC to compile VM code into symbolic assembler (the principal reason for providing the facility). POPC takes a system source file, compiles it with the ordinary POPLOG POP-11 compiler, and then translates the resulting VM instruction list into a file of assembler (which can then be assembled into object format in the normal way). In fact, POPC does rather more than this: by allowing some additional POP-11 syntax constructs, and by recognising and trapping references to certain special identifier names, it defines the "sys-" extended dialect of POP-11. The sysPOP-11 dialect enables system source code to perform operations that are not possible in standard POP-11, such as manipulating raw machine-level values and pointers (in much the same way as a C program can, for example). POPC produces in-line code for many of these operations, and does a large amount of optimisation (see REF SYSPOP11). In addition to outputting a file of symbolic assembler (extension '.a'), POPC also produces a POPLOG 'symbol table' file (extension '.w') that records the usage of permanent variables and constants in the source being compiled. After all sysPOP-11 sources have been processed, the set of '.w' files is input to a linking program POPLINK, which outputs (in several more assembler files) all necessary definitions for identifier cells and word records used, together with the POPLOG word dictionary. The compilation process for sysPOP-11 files is summarised in the diagram below: ------------------ < sysPOP-11 Source > ------------------ | \|/ ------------------ | POP-11 Compiler| ------------------ | VM Code | ------------------ | POPC | ------- ------------------ | | | | | | \|/ \|/ | ------------- ------------- | < Symbol File > < Symbol File > | ------------- ------------- | | | | ------------------- | | | \|/ | ------------ | | POPLINK | | ------------ | | \|/ \|/ ----------- ----------- < Assembler > < Assembler > ----------- ----------- Note that this process requires a POPLOG system to run both POPC and POPLINK (both of which are ordinary POP-11 programs). Thus any new port necessitates an initial cross-compilation stage, where the sources are compiled on a system already running POPLOG, and the complete set of assembler files transferred to the target machine for assembly and linking. Once the target POPLOG has been made to work, it can be used to compile its own source files. [N.B. POPLOG uses a large number of global linker symbols, currently 5000-6000; a recurring problem in past ports has been the inability of linkers to cope with this many. It is advisable to check the capabilities of the target system linker at an early stage.] The source files for the POPC and POPLINK programs reside in the "syscomp" subdirectory of the source directory; these are standard POP- 11 (extension '.p'). Of the 13 or so files, upto 4 may need changing for a particular implementation (see 'POPC Code Generation' below). In particular, the file 'sysdefs.p' is specific to each POPLOG system, and defines various constants of the implementation. 2. Machine-Specific Tasks ------------------------- In porting POPLOG to a new CPU, there are a number of design decisions that must be made at the outset, in particular the representation of POPLOG items and the allocation of machine registers. While the source code for the system is designed to be as portable as possible (and that portability is being improved all the time), the complexities of the system and the need for efficient execution make total generality virtually impossible. Thus certain assumptions have to made regarding e.g., the layout of procedures and data in memory, the execution protocol for procedures, control stack format, and so on. 2.1 General Considerations -------------------------- 2.1.1 Representation of POPLOG Items POPLOG manipulates all data in the form of fixed-length items, whose size should correspond to the 'natural' wordlength of the target machine (e.g. 32 bits). An item must be self-identifying, and must be capable of representing both simple objects (implicit data, i.e. integers and single-length floating point), and compound objects (pointers to data structures in memory), with the additional requirement that compound items are directly useable as machine addresses. Thus the encoding scheme for items is restricted to mapping pointers to themselves, but not integers or single-length floating point. For example, in all current implementations (all of which are on 32-bit byte-addressable machines), encoding is achieved with one tag bit to distinguish simple from compound, and a second to distinguish integers from single floats. Since all POPLOG data structures are rounded to an exact number of 32-bit words, and begin on a word boundary, the address of any structure is a multiple of 4, having 2 zero bits at the bottom; bit 0 is then used for the first tag (0 = compound, 1 = simple), and bit 1 for the second (0 = single float, 1 = integer). pointer single float integer ----------------- ---------------- ---------------- | |0|0| | |0|1| | |1|1| ----------------- ---------------- ---------------- This does mean, of course, that two bits are lost from the normal machine representation of integers and single floats. A machine integer M is mapped to a POPLOG integer P by the relation P = 4M+3, while the mapping for a single float depends on the machine and its floating-point format (essentially, the single float has to be massaged into a 30-bit format with the 2 least significant bits lost from the mantissa). See the sections below for a discussion of arithmetic on integers and floats. This encoding scheme would still be valid on a machine having addresses in multiples of 16-bit units, where addresses of 32-bit words are only a multiple of 2; bit 0 can still be used to distinguish a simple object from a pointer. On the other hand, it would not be viable on a machine that addressed 32-bit words in 32-bit units, where word addresses would go up in ones. Assuming that addresses on such a machine were positive as machine integers, an alternative scheme would be to have simple items encoded as negative integers. As yet, POPLOG has not been implemented on a machine that provides direct support for tagging. Making full use of a tagged architecture would probably require some changes to the source code. 2.1.2 Memory Resources POPLOG assumes two main areas of contiguous memory, sub-divided as follows: Area A Area B ------ ------ heap control stack user stack PROLOG trail PROLOG continuation stack In Area A, the heap is space for creating general data structures, while the user stack is used for passing procedure arguments and results. The user stack is at the top of the area (higher memory addresses) growing downwards, while the heap is at the bottom, growing upwards; the area is assumed to be extensible (not automatically, but by explicit operating system call), so that more space can be allotted to either part when necessary. -------------- - high address (extends this way) | user stack | | - - - - - | | | | - - - - - | | | | heap | | | -------------- - low address Overflow detection in this area happens in two ways. First, the heap allocator always checks that sufficient room is available below the user stack when allocating space for a record. Second, the source code supports a default system of maintaining a buffer area between the two parts, which allows the user stack some leeway in overflowing; checks for user stack overflow into the buffer area are then performed at appropriate places (such as on entry to, or on backward jumps in, user- defined procedures, and in system procedures that push a large number of the things on the stack). However, where the host machine/operating system supports it, the user stack overflow checks can be eliminated by creating an inaccessible memory page between the user stack and the heap, and dealing with overflow as a memory access violation. (Currently, this technique is used only in the VAX VMS implementation of POPLOG.) The other important consideration in regard to Area A is user stack underflow. Because explicity-coded checks for this would degrade running efficiency by too great an amount, it is simply assumed that the area (and therefore the user stack) is followed by inaccessible memory in such a way that any attempt to access a non-existent item on the stack will cause an access violation. This can be achieved either by aligning the end of the area at the boundary of the memory space allocated by the host system, or (as above, if the host permits it) by creating an inaccessible page there. [For example, in Unix implementations, Area A is the normal data segment, extended by means of the brk system call. An attempt to access over the end results in a segmentation violation (signal 11), which the POPLOG signal handler then interprets as user stack underflow. A small wrinkle in this scheme is that the brk call usually rounds up the requested quantity of memory to be a multiple of the page size (or memory mangagement segment size, etc), and unless this is accounted for the user stack will not actually reside at the end of available memory. The relevant part of the POPLOG source allows for the insertion of an implementation-dependent code fragment to handle this.] We turn now to Area B, containing the control stack for holding procedure execution stack frames, and two stacks for PROLOG, the trail and the continuation stack. In this coexistence regime, the trail and the continuation stack grow towards each other, while the control stack grows away from both of them, as shown: ---------------------- | continuation stack | | - - - - - - - - - -| | | | - - - - - - - - - -| | trail | |--------------------| | | | control stack | | | | - - - - - - - - - -| | | (extends this way) It is assumed that the area as a whole will correspond to the normal execution stack space of the host system (i.e. where subroutine jump instructions push return addresses, etc), and will be extended automatically by the operating system when necessary; to allow compatibility with the conventional direction of growth, the area may be situated either way up in memory (i.e. with high addresses at the top of the diagram and low at the bottom, or vice-versa). This apart, all stack mangagement in the area is controlled by explicit checks in the source code, which allows for the control stack and trail to be shifted together in the growth direction to create more room for the PROLOG stacks. 2.1.3 Procedures All code in POPLOG is packaged up inside procedure records which, aside from being 'executable', have exactly the status as all other data structures in the system (i.e. they are 'first-class' objects). In addition to the executable code it contains, a procedure has a header which maintains various associated data (such as its pdprops and updater fields, details of its stack frame layout, etc), and a literal table containing constant values used in the code. This said, we must however distinguish two (potentially) different classes: system procedures and user procedures. One reason for this distinction is that POPLOG performs incremental compilation, source code from user programs being compiled through the VM to produce procedure records in the heap at run-time (see Run-time Code Generation below). Like all other heap objects, procedures so manufactured must be relocatable by the garbage collector, and this means that all code in such a procedure must be position-independent, and must reference other relocatable structures only via the literal table (which the garbage collector knows about, and can update appropriately). The constraint does not, however, apply to built-in system procedures generated by POPC, because they never reference relocatable heap structures directly. Another reason concerns the nature of the executable code in user procedures: it is assumed that system procedures will always use native machine code, and, from the efficiency point of view, this is the most desirable choice for user procedures also. But this may not be possible if, for example, the machine enforces separate instruction and data spaces, and in this case an interpreted code must be used instead. [Although one (now redundant) POPLOG implementation (for the Zilog Z8000) used this method, there is currently no standard for such an interpreted code because all current POPLOG implementations generate machine code.] In all current systems, procedures are laid out shown below; the length of the literal table varies between different procedures, and the (fixed-length) header contains a pointer to the start of the code: ---------------------- | header --|----- (pointer to code start) | information | | |--------------------| | | literal table | | |--------------------| | | |--<-- | executable | | code | | | ---------------------- For system procedures containing machine code, a potential problem with this scheme is that some host systems may not allow data (i.e. the header and literal table) to be in the same memory space as the code (either because the machine enforces separate I/D spaces, or because the operating system will not allow data in a shareable code segment). An alternative scheme is to have the the header in data space pointing to a separate code segment, but this will require that the execution protocol for a procedure loads the literal table address into a known register, etc, before calling the code. 2.1.4 Control Stack Frames It is assumed that the machine supports an execution stack protocol for function/procedure argument passing, local variables and return addresses, and that it maintains a stack pointer register for this purpose. POPLOG cannot use this protocol, but must instead use the stack pointer to create control stack frames according to its own standard. There are several good reasons for this: first, POPLOG procedures support dynamic binding of local variables, and the host protocol will not normally cater for this; second, POPLOG procedures pass arguments on the user stack, and will not therefore require the apparatus for passing them on the control stack that the host protocol makes available. The most important reason, however, is that various parts of POPLOG (e.g. the garbage collector, abnormal procedure exit mechanisms, etc) work with stack frames as explicit data structures, which must therefore be in a standard format. The format is shown in the diagram below: if the stack grows downwards high addresses are at the top and low at the bottom, and the other way round if it grows upwards. -------------------------------- | saved non-pop registers | |------------------------------| | saved pop registers | |------------------------------| | saved pop dynamic locals | |------------------------------| | saved non-pop dynamic locals | |------------------------------| | pop on-stack vars | |------------------------------| | non-pop on-stack vars | |------------------------------| | owner procedure address | |------------------------------| | return address | -------------------------------- The return address and owner procedure fields are single-word, while each of the others is an arbitrary number of words long; the lengths of these parts (plus the overall length of the frame) can be determined from information held in the owner procedure header. Note the distinction here between 'pop' and 'non-pop' variable values: 'pop' values are cells containing proper POPLOG objects (encoded as discussed above), and are processed by the garbage collector; 'non-pop' values on the other hand are things which the garbage collector should leave alone, and may contain arbitrary machine integers, offsets, addresses, etc. (Non-pop variables are one of the facilities provided by the sysPOP-11 dialect.) Because the length of a stack frame for a particular procedure is fixed, and is available from the header information of the procedure, their is no need for the 'frame pointer' mechanism used by many standard calling protocols to access the frame base and to chain each frame to its predecessor. This may mean that a dedicated frame pointer register can be used for other purposes. Stack frames are created by the prologue code in POPLOG procedures, this code itself being generated by POPC (for system procedures) and the Run-time assembler (for user procedures). If for any reason the target machine does not permit the (efficient) creation of a stack frames in the required format, additional modifications to the source code may be necessary. 2.1.5 The User Stack The control stack and the user stack are the two most important stacks in POPLOG; while the host system will almost certainly provide directly for the former, it is unlikely to do so for the latter. Since fast pushing and popping of user stack items is critical to the efficiency of a POPLOG implementation, it is essential for the user stack pointer to be held in a global register or other fast storage location (and preferably one for which the machine supports push/pop instructions or autoincrement/autodecrement operations). 2.1.6 Register Allocation in General Assuming the target machine has registers (where by this we mean any kind of fast global storage locations), a decision must be made as to how these should best be employed. Since they will probably be in short supply, the priorities in this respect can be summarised as: provide first for the control/user stack pointers and for any implementation- dependent global usage; then allow sufficient registers for temporary working use; finally allocate any remaining registers as local variables for POPLOG procedures. By 'implementation-dependent global usage' here we mean, for example, the need to maintain one or more registers for base addressing on machines that do not support PC-relative addressing. To date POPLOG has been implemented on only one such machine (the GEC Series 63): in this implementation one register is designated to hold the current procedure address, which each procedure sets up locally on entry and thereafter uses as the base for accessing its literal table, executing relative branches, etc. (In fact, this register is treated simply as a 'pop'-type local possessed by every procedure; it has to be, because its saved values in stack frames may need relocation by the garbage collector.) Another possible example of such global usage concerns the constant false, which, because all conditionals in POPLOG test equal/not equal to it, is generally the most important constant in the system. For compatibility with the representation of other data types, it has to be a proper pointer to a data structure in memory; yet for efficiency, one would like its actual value to be zero, so that comparisions with it are reduced to a zero/non-zero test. This is only possible if false can be located at address 0, but many systems do not allow this. An acceptable alternative therefore is to cache its value in a global register. As regards registers for temporary working use, the number of these required will depend on the code sequences generated by POPC and the run-time assembler, and on the needs of the hand-coded assembler routines. Usually, this will be at least 4. Other registers remaining can be allocated as procedure local variables, either as 'pop'-type or 'non-pop'-type. (As a rule, POPLOG source code assumes that the first two 'pop' and the first three or four 'non-pop' lvars declared in a procedure will reside in registers, although there is no requirement for this other than efficiency.) This allocation must take into account the efficient saving and restoring of register values in stack frames, in the order shown above. 2.2 Changes to the Source Code ------------------------------ This section overviews the actual source files that require changing to port POPLOG to a new machine, taking into account the considerations of section 2.1. 2.2.1 Hand-coded Assembler Files The hand-coded assembler files contain a set of subroutines. These are either called explicitly from the sysPOP-11 source files, or implicitly from code generated by POPC or the Run-time assembler. They perform operations which are either too primitive or too machine/operating system dependent to be coded in sysPOP, or which are so central that they need to be as efficient as possible. Naturally, the routines in these files will require recoding for a new CPU; the detail of many of them also depends on the issues already discussed, such as the representation of POPLOG items and register allocation, etc. The following is a brief overview of the routines contained in each file: aarith.s (250 lines) Subroutines concerned with arithmetic and bitwise-logical operations on integers, e.g. multiplying and dividing simple integers in POPLOG representation, adding, subtracting, multiplying and dividing POPLOG big (arbitrary-precision) integers. aextern.s (70 lines) Routines that provide an interface for POPLOG to call 'external' procedures, i.e. procedures that obey the standard machine/operating system calling protocol (normally encompassing operating system calls and procedures/functions written in conventional languages.) A central part of this interface is moving argument values from the POPLOG user stack (which is where POPLOG procedures pass them) to the system control stack (where the standard protocol expects them). afloat.s (300 lines) A set of routines to perform arithmetic and other operations on floating-point data. These depend both on the host system floating- point formats and the machinery for processing them it makes available; a few of them also depend on the chosen encoding for POPLOG single-floats discussed above. amain.s (500 lines) The largest file, containing a mixture of routines for various purposes. These include: the general mechanisms for 'calling' an object in POPLOG (i.e. execute it if it is a procedure, or execute its class_apply procedure otherwise), control routines concerned with abnormal procedure exits, and a number of others. Also contains the start-up routine for the system (main in Unix). amove.s (350 lines) This is concerned with move and compare operations on blocks of memory, implementing them in the most efficient way possible on the host machine. It also contains two subroutines used by the VED editor for efficient displaying of screen lines. aprocess.s (200 lines) Contains core routines for the POPLOG process mechanism. The principal routine in this file is one that swaps a process control stack section in or out (and is by far and away the most complicated of the hand-coded assembler routines). Other routines are concerned with saving and restoring the POPLOG user stack. aprolog.s (200 lines) Defines core subroutines used by POPLOG PROLOG, including simple unification operations (e.g. unify against a constant), and operations that save and restore the current state of the PROLOG global variables. Some of these routines are optional in the sense that they are just optimised versions of sysPOP-11 procedures (and so can be omitted in an initial version of a port). arestore.s (30 lines) A few routines concerned with resetting the memory configuration of the system when restoring a POPLOG saved image. asignals.s (200 lines) Routines for handling signals and exception conditions; the details of these will depend entirely on the host operating system. In Unix, this file defines the routine specified to the signal system call (which is then called to process interrupts, memory access violations, etc). 2.2.2 POPC Code Generation As described earlier, POPC is a (standard) POP-11 program which takes the rerouted output of the VM compiler and produces from it a file of symbolic assembler; we shall describe here only those aspects of the process which are relevant to a port. POPC takes the list of VM instructions for each procedure being compiled and transforms it into a list of instructions in an intermediate representation, called M-code, which corresponds roughly to a multiple-operand machine instruction set with generalised addressing modes (in fact, based orginally on the VAX architecture). The interpretation of the sysPOP-11 dialect is performed concurrently with this translation (so that, for example, a VM CALL instruction for the procedure _add is mapped directly onto the M-code instruction ADD). Following a considerable amount of optimisation (special attention being given to the elimination of unnecessary user stack pushes and pops), the M-code list is handed to the back-end code generator for translation to target machine assembler code. The back-end code generator has then to be rewritten for a new port. It is defined in terms of a set of procedures, each of which handles the translation of instructions for a given M-opcode, producing appropriate sequences of actual machine instructions. The final assembler code list (possibly after further optimisation passes) is then written to the output file. The POPC source files that will/may require changing are as follows, all in $usepop/pop/src/syscomp asmout.p (497 lines) Defines procedures for outputting data (not instructions) in the appropriate symbolic assembler format (instruction output is dealt with solely by 'genproc.p'). It also defines the procedure which maps POPLOG identifier names onto assembler/linker symbols (the hand-coded assembler files must then follow the same conventions when referring to POPLOG identifiers). Used by all parts of POPC and POPLINK to generate structures other than procedures. genfloat.p (137 lines) This file deals with the generation of floating-point constants, and may or may not need changing depending on the format of floats in the target system. Current versions handle IEEE, VAX and GEC Series-63 formats. (See also 'afloat.s' in 2.2.1.) genproc.p (1988 lines) The back-end code generator, as described above. It performs the translation of M-code to target assembler, and writing of the resultant code to the output file. It must make various definitions required by 'm_trans.p' (the VM code to M-code translator), and in this respect is able to exercise some control over the exact definition of M-code instructions, particularly the registers and addressing modes used. The definitions also include the M-code meanings for sysPOP-11 operations like _int, _pint, _issimple, _iscompound, etc, which depend on the representation of POPLOG items discussed in 2.1.1. sysdefs.p (111 lines) This file is unique to each POPLOG system, and defines (as POP-11 macros) various constants of the implementation. These include such things as machine word size and addressing units, virtual memory page sizes, etc, and conditional compilation switches for selecting alternate parts of the sysPOP source code where necessary. [NB. I am not sure this is a complete list of files that may have to be changed in $popsrc/syscomp. Aaron Sloman 8 Jun 2012] 2.2.3 Run-time Code Generation There are currently three files concerned with the run-time generation of procedures containing executable code (as discussed in 2.1.3). These are: All in $usepop/pop/src/ ass.p (1714 lines) This is the largest and by far the most complicated of the three; it consists of a set of procedures that plant executable code for each VM instruction, as well as the code required for creating stack frames on procedure entry and unwinding them on exit. In normal run-time mode the VM compiler builds a list of instructions for each procedure being processed and, when a list is complete for a given procedure, hands it over to the run-time assembler. (At this level, the VM instruction set is an expanded version of that which appears in REF VMCODE, since it differentiates the basic instructions according to their actual arguments, e.g. there are different versions of the CALL instruction for calling variable, procedure-type variable or constant procedures.) The interface to run-time assembly is the procedure Consprocedure, which takes as input the code list and other relevant information (such as details of the procedure's local variables, etc) and produces the final procedure record as its result. Consprocedure makes one or more passes on the code list, and each time it does so it calls the procedures in ass.p corresponding to the instructions in the list. These procedures assume that a global pointer to a region of memory has already been set up, and each plants its piece of executable code at that place, incrementing the pointer appropriately. As far as possible, the setup is such that ass.p need only contain those instructions which are machine specific; other details are dealt with by Consprocedure. arrays.p (264 lines) This file deals with the construction of array procedures by the procedure newanyarray. An array procedure is called with N subscripts as arguments, where N is the dimensionality of the array; its job is to compute the total subscript value for accessing the desired value in the 1-dimensional vector underlying the array, and then supply both subscript and vector to the appropriate subscripting procedure (which actually performs the access or update). Although array procedures could be implemented by other means that do not require generation of executable code, this would be less efficient; the code generated to compute the total subscript can be highly optimised, and can make use of special machine instructions where appropriate (e.g. the VAX "index" instruction). partapply.p (no longer here) NB this is now in a short library file which uses consclosure. consclosure.p (117 lines) This defines the procedure consclosure, which constructs a closure procedure from a given base procedure and a number of 'frozen-value' arguments. The executable code inside the closure pushes the frozen values onto the user stack and then calls the base procedure (its pdpart). (Note that closures do not alter the control stack, i.e. they don't create stack frames.) 3. Operating System-Specific Tasks ---------------------------------- This section considers issues that arise in porting POPLOG to a new operating system, whether in combination with a new CPU port or not. In fact, some of the matters already dealt with in previous sections may depend more on the details of the host O/S than on the CPU. This applies particularly to memory organisation, where it is the O/S that will determine what address space is available, how it is paged or segmented, how it can be extended or contracted, etc; also important is the O/S handling of things like exception conditions and interrupts. Variation in these areas can mean that a not insubstantial number of modifications may be necessary even when transferring the system to a slightly different version of an existing O/S (Unix being a classic example). Otherwise, the majority of operating system dependencies in POPLOG are related to I/O facilities and file handling, with a number of others in areas such as process control, O/S utility calls and external procedure loading. We shall deal with each of these in turn. 3.1 I/O in POPLOG ----------------- Essentially, the I/O facilities in POPLOG are modelled on those of the Unix operating system, providing uniform byte-stream access to all kinds of files and devices. The lowest-level I/O procedures available in the system ( sysopen, syscreate, sysread, syswrite, sysseek, sysclose) closely parallel Unix system calls ( open, creat, read, write, seek, close) and take similar arguments, except that instead of Unix file descriptors, the procedures sysopen and syscreate return a device record, which holds all necessary information for performing I/O on the file or host system device opened. In particular, the device record contains a set of host device-dependent procedures, one for each of the allowable operations (reading, writing, flushing, seeking and closing); the action of each "sys-" procedure applied to a given device is then to call the corresponding device procedure for that operation. Thus for I/O in a new O/S environment, the basic work necessary is to provide suitable procedure definitions for each of the above operations on each different kind of host device. In a non-Unix system where I/O is not organised around the byte-stream paradigm, this can be far from straightforward; even in Unix these procedures do not (as might be expected) consist merely of calls to the corresponding Unix routines, because they provide a further level of buffering on top. The relevant POPLOG source files in this area are: (all in $usepop/pop/src ) devio.p (1398 lines) Defines device procedures for each combination of operation and host device type, as well as procedures that actually construct device records. sysio.p (107 lines) Definitions of the sys- interface procedures for each operation, that take devices as arguments and call the appropriate 'devio.p' procedures stored in the devices. sysopen.p (145 lines) opening devices sysreadwrite.p (68 lines) reading and writing to devices Additional files for the VMS implementation of POPLOG (where the byte- stream interface has to be simulated) are: blkio.p (100 lines) Device procedures that simulate full byte-stream and random access on non-text disk files. rmsio.p (500 lines) A set of procedures that serve as an interface between POPLOG and the RMS (Record Management System) software layer in VMS. 3.2 Other O/S Utilities ----------------------- POPLOG makes available a number of other procedures that are closely linked to operating system utilities (again, usually based around Unix). Some of these are sufficiently general that they will probably have suitable counterparts in a given O/S (and will therefore be supportable), while others may not. Some are even specific to particular hosts (e.g. VMS, Berkeley 4.2 Unix, etc). The following source files and the procedures they contain will therefore need individual consideration: Also in $usepop/pop/src systime.p (59 lines) Procedures concerned with dates and times, e.g. get the current date/time in Unix format (seconds since 1 Jan 1970), convert a time in this format to an ASCII string, return the CPU time since the start of the process, etc. sys_timer.p (278 lines) Deals with setting and clearing interval-timer interrupts. sysutil.p (428 lines) Miscellaneous procedures: POPLOG equivalents of Unix fork, wait and exec, etc, and corresponding routines in the VMS version of this file. 3.3 External Procedures ----------------------- The external procedure facility in POPLOG enables functions and procedures in non-POPLOG languages to be loaded into and called from within the system, all at run-time. Roughly speaking, this is achieved by calling the host system linker (as a separate O/S process) to link one or more user object modules with the POPLOG base image. This results in the creation of an image file in which the code and data for the external procedures are located at an address determined by POPLOG, which then loads it into memory at the appropriate place. Naturally, much of this process depends on the details of the host O/S, including object module/symbol table formats and the functionality and capabilities of the linker. The sysPOP source code for this area of the system attempts to be as general as possible, however, and is divided into two files, one being (at least, nominally) host-independent and the other encapsulating the dependencies: All in $usepop/pop/src -rw-r--r-- 1 axs staff 9908 Nov 25 1994 extern_callback.p -rw-r--r-- 1 axs staff 12163 Aug 9 1996 aextern.s -rw-r--r-- 1 axs staff 16405 Aug 16 1996 extern_load.p -rw-r--r-- 1 axs staff 16120 Apr 30 1998 extern_ptr.p -rw-r--r-- 1 axs staff 2951 May 13 1998 external.ph -rw-r--r-- 1 axs staff 52498 Apr 9 1999 unixextern.p.orig -rw-r--r-- 1 axs staff 9349 Dec 23 2004 extern_symbols.p -rw-r--r-- 1 axs staff 52829 Sep 23 2010 unixextern.p Defunct: extern.p (600 lines) This file contains all the interface procedures ( external_load, external_apply, etc) for using external procedure in POPLOG. and calls procedures in one of the files below to perform all operations which depend on the details of the host O/S. unixextern.p (1629 lines) [ vmsextern.p (400 lines) ] The host-dependent parts for the Unix and VMS implementations respectively. These deal with object module/symbol table formats, calling the O/S linker, and loading the resultant image sections into memory. malloc.c (300 lines) NOW DEFUNCT The one and only C source file in the system. It redefines the O/S library routines for allocating and freeing blocks of dynamic memory so that they use areas set aside by POPLOG, and are thus under POPLOG's control. In Unix, these are the C library routines malloc, free, etc; in VMS they are the same plus lib$get_vm and lib$free_vm. May need modification for a new O/S. NB: There are now several C files in $popexternlib, i.e. $usepop/pop/extern/lib/*.c XtPoplog.c c_callback.c c_core.c c_sysinit.c pop_encoding.c pop_poll.c pop_stat.c pop_timer.c These are used by the command $usepop/pop/extern/lib/mklibpop to create this statically linked library: $usepop/pop/extern/lib/libpop.a The command file $usepop/pop/com/mkXpw is used to create $usepop/pop/extern/lib/libXpw.so which is needed if poplog is linked with X11 facilities. John Gibson July 86