              INTERFACE SPECIFICATIONS FOR LPARL TINY-C (8086)
              ================================================


To install LPARL Tiny-C on an 8086-compatible system, a module containing
interface routines to the host operating system must be linked to the kernel
containing the Tiny-C interpreter.  Some interface functions may be filled by
existing operating system library routines.  In other cases, routines may have
to be written to provide interfacing between operating system fuctions and
Tiny-C requirements.

The Tiny-C kernel is the object module formed by combining the Interpreter
Module, Editor Module, Library Module, Utility Module and the Header Module.
These modules may be physically combined before linking to the interface
module(s), or at the same time the interface module(s) is(are) linked to the
kernel.  The kernel is more of a logical separation of the modules that are
implementation-independant than a separate physical entity, although in
some implementations the kernel may actually be one module.  If the
implementation requires substantial, ongoing modifications of kernel
modules such as the Library Module, then it will not be worth the extra
effort to maintain a separate kernel module.

A Tiny-C interface routine is entered via a short CALL instruction from the
interpreter.  Arguments to the interface routines are passed in registers and
occasionally on the processor stack.  Arguments on the stack may be accessed
using the BP register in a standard fashion.  Currently, a maximum of one
argument is passed on the stack.  To initialize the BP register to point to
the argument, the following 8086 code may be used:

	INTFC:	PUSH	BP		; save caller's BP
		MOV	BP,SP		; get current stack pointer
		MOV	ARG1,[BP+4]	; store argument locally if desired
		   .			; perform interface function
		   .
		   .
		POP	BP
		RET	2		; pop argument off stack when returning

Most Tiny-C interface routines require that the general registers (AX, BX,
CX, DX, BP) be preserved.  The segment registers MUST be preserved.  Index
registers SI and DI may be used as desired.

Segment Definitions for Interface Routines:

The following assembler directives will create segment definitions compatible
with Tiny-C code:

	TCGROUP	GROUP	CODE,DATA,STACK,TCMEM
	ASSUME	CS:TCGROUP,DS:TCGROUP,SS:TCGROUP,ES:TCGROUP

CODE	SEGMENT	WORD PUBLIC 'CODE'
CODE	ENDS

DATA	SEGMENT	WORD PUBLIC 'DATA'
DATA	ENDS

In addition, an interface routine may define any number of local segments,
as long as ALL segment registers (including ES) are restored.

==============================================================================

The Tiny-C interface routine definitions and argument passing conventions are:


	CHRDY:	Tests the console status to see if a key has been pressed.

		If a key has not been pressed, CHRDY returns the 8086 SF flag
		bit set to 1, and the value 80H in the AL register.

		If a key has been pressed, CHRDY returns the SF flag reset
		to 0, and a copy of the character in the AL register.  The most
		significant bit of the character code in the AL register is
		always set to 0 (hence, the SF flag is cleared), so the value
		returned is between 0 and 127 decimal.

		The character buffer is NOT cleared, so that a subsequent call
		to INCH (below) will return immediately with the same character
		code in the accumulator.

		CHRDY provides break detection and all necessary character
		mappings when it has found that a key is pressed:

		The character delete key (RUB OUT or DEL on the keyboard) must
		be returned as the value 127 decimal, or 7FH.  If the system
		returns a DEL key entry as something else, 08H for instance,
                then this character code must be converted to 7FH.

		If the character input is an ESC character, 01BH, then INCH
		does not execute a return to the calling program, but instead
		jumps directly to the public near label BREAK, located in the
		Tiny-C kernel.  If the operating system intercepts ESC key
		entries, another code may be used, such as ^C (03H).

		Inputs:      nothing

		Returns:     If a key has been pressed, then AL <-- char and
		             the SF flag is cleared.  Otherwise, AL <-- 80H and
		             the SF flag is set.

		Conditions:  All general registers (except AL) must be
		             preserved.

		Exceptions:  Control transfers to BREAK immediately upon
		             receipt of an ESC character.

-------------------------------------------------------------------------------

	INCH:	Inputs a character from the console keyboard.  It does not
		return until a key has been pressed.  The character buffer is
		cleared, and a subsequent call to INCH will return a new
		character.

		A call to INCH does not echo the character returned.  That
		is done explicitly via a call to OUTCH.

		INCH must provide the same break detection and character
		mapping as CHRDY, above.  In most cases, this can be
		accomplished by using CHRDY to test for a character input.

		Inputs:      nothing

		Returns:     AL <-- character code

		Conditions:  All general registers (except AL) must be
		              preserved.

-------------------------------------------------------------------------------

	OUTCH:	Outputs a character to the console screen.  Before output, the
		keyboard status should be checked for ESC key entries using
		CHRDY.

		If the system does not provide XON/XOFF capability, then OUTCH
		must check keyboard status before it outputs the character to
		see if an XOFF character has been entered from the keyboard.
		This is a ^S.  If an XOFF has been entered, OUTCH should clear
		the character buffer using INCH and not output its character
		until an XON character (^Q) has been read from the keyboard.
		If the operating system provides this function, then XON/XOFF
		keys may be ignored.

		Inputs:      AL contains character to output

		Returns:     nothing

		Conditions:  All general registers, including Al, must
		             be preserved.

-------------------------------------------------------------------------------

	EXIT:	Provides an orderly return to the host's operating system.

		Inputs:      nothing

		Returns:     nothing

-------------------------------------------------------------------------------

	FREAD:	Read a text string from a mass storage device.  The string may
		be assumed to be continuous, and may be composed of all ASCII
		characters except RUB OUT, ESC and ^Z (end of file).  Upon
		entry, the stack contains the address of a text string which
		may be used as a file name.  This string is terminated by a 
		null byte.  If the host system does not require a file name,
		this argument may be ignored (but must be removed from the
		stack before FREAD may return).  The CX register
		contains the address of the buffer to place the text string
		into.  The DX register contains a number which is the
		maximum number of characters that can be read into the buffer.
		This number should not be exceeded.

		When FREAD is invoked, it should open a file, using the file
		name if required, and read the entire contents of the file
		into the text buffer, not exceeding the maximum character
		limit.  If this limit is exceeded, the last character in the
		buffer should be changed to a carriage return (0DH).  A
		message should be output to the console screen if this occurs.

		When FREAD returns, the number of characters read into the
		buffer is returned in the BX register.  This number will
		never exceed the maximum limit sent in DX.  If an error occurs
		during the read operation, FREAD should output a diagnostic
		message to the console screen.  In the case of error, FREAD
		returns the value 0 in BX.

		Inputs:      <stack> <-- address of file name string
		             CX      <-- address of input buffer
                             DX      <-- length of input buffer, in bytes

		Returns:     BX      <-- number of bytes read into input buffer

-------------------------------------------------------------------------------

	FWRITE:	Outputs a text string to mass storage device.  The address of
		a string containing a file name is passed on the stack, as in
		FREAD, above.  The CX register contains the address of the
		first byte to output.  The DX register contains the
		address of the last byte to output.  The total number of bytes
		to output is <DX> - <CX> + 1.  The string may consist of any
		ASCII character except ESC, RUB, and ^Z.  If any errors occur
		during the write operation, a diagnostic message should be
		output.

		Inputs:      <stack> <-- address of file name string
		             CX      <-- address of first byte to output
		             DX      <-- address of last byte to output

		Returns:     nothing

-------------------------------------------------------------------------------

	LPOUT:	Outputs a text string to the system list device (usually a
		line printer).  If the line printer does not automatically
		generate a line feed character (0AH) following a carriage
		return (0DH), then LPOUT must provide them.

		Inputs:      CX <-- the address of the first byte to output
		             DX <-- the address of the last byte to output

		Returns:     nothing

-------------------------------------------------------------------------------

	SYS_INIT: Performs system initializations of operating parameters and
		hardware as required by the implementation.  It is invoked
		immediately after a Tiny-C cold start.  Unless the interpreter
		is being used in a stand-alone configuration, SYS_INIT will
		generally not be required to perform much.

		Inputs:       nothing

		Outputs:      nothing

===============================================================================


Notes on implementing the interface routines:

All the above routines must be implemented in some form.  If the host system
does not support certain features, such as a line printer, or even a mass
storage system, the interface routine may just output a message such as

	LINE PRINTER NOT PRESENT

and return.
		LIBRARY INTERFACE TO MACHINE-CODED APPLICATIONS

The following describes how the built-in function "mc" is implemented on
an 8086-based system.  Refer to "SYNTACTICAL DESCRIPTION OF TINY-C" for
an explanation of how "vectors", "registers", and "stack" relate to the
model for the mc function.

Small, relatively uncomplicated machine code functions may be interfaced to
Tiny-C via the mc call.  In order to use mc, a vector for each machine-code
function must be entered in the memory array.  A vector is four bytes in
length, so vector n occupies memory[4*n] to memory[4*n+3].  The first two
bytes of the vector are the least and most significant bytes, respectively,
of the index into the memory array where the function is to be found.  The
last two bytes of the vector must be initialized to the built-in value,
"membase", which contains offset information required for proper linkage.
For example, if vector 25 is to be used to access a function that will be
placed starting at memory[0x200], then the following Tiny-C statements will
initialize the vector:

	memory[100]= 0;
	memory[101]= 2;
	memory[102]= membase;  /* have to initialize high word with membase
	memory[103]= shr(membase,8);  /* get high byte of membase

Alternatively, an integer array may be used to simplify matters:

	int memoryword[*]; 
	memoryword= memory;  /* access memory by words
	memoryword[50]= 0x200;
	memoryword[51]= membase;

When the function at location 0x200 is executed by mc(25,...), the AX
register will contain the number of arguments passed to the function.
All arguments should be popped, either using POP or a long RET n instruction,
before returning to Tiny-C.  Since the CALL to the function is a long CALL
through a DWORD vector, the RET instruction used must be a "long" RET.  Upon
return, the value in the AX register is used as the value returned by mc.

A sample program that might be executed by mc is

		SEG	EQU	6	; first parameter
		OFFSET	EQU	4	; second parameter

	GET_WORD PROC FAR	; return a word given a pointer
		PUSH	BP
		MOV	BP,SP	; point to arguments passed
		MOV	AX,[BP]SEG	; get segment
		MOV	ES,AX
		MOV	BX,[BP]OFFSET	; get offset
		MOV	AX,ES:[BX]	; get word to return
		POP	BP
		RET	4		; discard arguments
	GET_WORD ENDP

If this program were hand-coded and loaded into memory[0x200] . . .,
then it would be called as (assuming the vector is at memory[0x100] and
already initialized):

	mem_word= mc(25,seg,offset);

Note that no argument checking is done.  If Tiny-C sends the wrong number
of parameters to mc, no error is raised.  Actually, in the 8086
implementation of Tiny-C, it does not matter whether or not the arguments
are removed from the stack by mc, since the stack is restored to its state
prior to the mc call by Tiny-C.  However, care should be taken to send at
least as many parameters to an mc call as it requires, since otherwise
the machine-coded routine might manipulate what it thinks is an input
argument on the processor stack but what is in fact a prior entry on
the stack which should not be changed.

Large, complicated machine-code functions are difficult to implement
using mc, but it is possible to create additional "built-in" routines
to implement machine-code functions that supplement the ones supplied
with Tiny-C.  Assembly-language subroutines may be added directly to
the Library Module code using an editor, and then the Library Module
is assembled and a new Tiny-C interpreter is constructed.  However,
the suggested way of adding new built-in library functions is to code
them in a separate module and link them to the library using a single
entry point.  Defined within the Library Module source are macros to
facilitate this linkage.  They assume that the entry point in the
external Application Library Module is named LNKVEC.  Refer to the
Library Module source (in TCLIB.A86) for instructions and details of
the linkage mechanism.  There is a reserved area in the source file
where external linkages are placed.  They have the form,

	%LINK(fname,access,param_cnt)

where "fname" is a legal Tiny-C name (which is automatically canonicalized
by the assembler), "access" is an expression whose value will be placed
in the BX register and sent to the Application Module via LNKVEC, and
"param_cnt" is an expression whose value is used to verify that the
proper number of parameters have been passed by Tiny-C.

Once BX is loaded and the number of parameters to be passed is verified,
a far CALL to LNKVEC enters the Application Module.  The followig code
might be linked to the Application Module to provide access to various
functions (and may be found in the file LNKMOD.A86).
$TITLE(Tiny-C to Machine Code Interface Module) XREF

;*****************************************************************************;
;                                                                             ;
; This module provides linkage to Tiny-C when linked into a program module    ;
; developed in executable code, such as ASM-86 or PL/M-86.  Depending on the  ;
; function number passed in BX, a function from the linkage table LNKTAB is   ;
; selected and executed.  The Tiny-C environment is preserved.  Since all     ;
; segment registers except SS are saved, the application routine being        ;
; executed does not have to lie in the same 64k segment that Tiny-C requires. ;
;                                                                             ;
; NOTE:  Any PL/M procedures linked with this module MUST be compiled using   ;
; the COMPACT switch.  This is necessary because Tiny-C defines its own stack ;
; which is not accessible from the DS register.  PL/M procedures compiled     ;
; using SMALL assume that DS is the correct segment for all pointers,         ;
; including pointers to arguments on the stack.  If the PL/M modules linked   ;
; to LINK_MOD are compiled using MEDIUM or LARGE, then the pointers in LNKTAB ;
; must be converted to DWORD pointers (and the index in BX must be shifted    ;
; left again) for linkage to occur properly.                                  ;
;                                                                             ;
; When linking this module to the rest of the application, it should be the   ;
; first module in the link list.  This way, a change in the application code  ;
; will not affect the entry point of this module, since only the link table's ;
; addresses will be changed.                                                  ;
;                                                                             ;
; Since no checking is made concerning the legality of a function number,     ;
; care should be taken to not enter this module with an invalid function      ;
; number in BX.                                                               ;
;                                                                             ;
;*****************************************************************************;

	NAME	LINK_MOD

CGROUP	GROUP	CODE,CONST	; assumes ROM model groups for PL/M-86
DGROUP	GROUP	DATA		; (if RAM is used, CONST must go here)

ASSUME	CS:CGROUP,DS:DGROUP

DATA	SEGMENT	WORD PUBLIC 'DATA'

RETADDR	DD	?

DATA	ENDS
CODE	SEGMENT	WORD PUBLIC 'CODE'

; Place EXTRN function definitions here, like so:

;	EXTRN	XXX:NEAR

LNKTAB	LABEL	WORD		; table of pointers to external routines

; table entries have the form:
;	DW	OFFSET CGROUP:XXX

	PUBLIC	LNKVEC

; Upon entry, BX contains the number of the function to invoke

LNKVEC	PROC	FAR			; this is where we come from Tiny-C
	MOV	AX,DGROUP		; initialize DS register
	MOV	DS,AX
	POP	WORD PTR RETADDR	; save return address off stack: IP
	POP	WORD PTR RETADDR+2	;    followed by CS
	SHL	BX,1			; function # points to table entry
	CALL	LNKTAB[BX]		; invoke function
	JMP	RETADDR			; then return to Tiny-C
LNKVEC	ENDP

CODE	ENDS

	END

-----------------------------------------

Upon return, the AX register contains whatever is returned by the application
routine (note that PL/M-86 does not return pointer values in the AX register).

The linkage mechanism operates as follows:

Suppose an application subroutine is to be accessed using the name "foo"
from within the Tiny-C language.  Further, suppose that the subroutine
accessed by "foo" requires 2 parameters.  Then the following changes
are made to the Tiny-C Library Module (by using the macro LINK):

1.  A stub Tiny-C library routine that will respond to "foo" is added to the
    CODE segment.  The stub must do three things:  set BX to the number that
    will cause the application module to access the proper routine, set
    CX to "2" (the number of parameters required), and JMP to INTFC.  The
    following code accomplishes this (assuming the function "foo" has the
    access code "6"):

	APP00:	MOV	BX,6		; access code required by application
		MOV	CX,2		; number of parameters required
		JMP	INTFC		; that's all

2.  An entry in the Tiny-C library symbol table is required.  A list of
    entries is constructed by LINK as it is invoked, and expanded in the
    symbol table section of the code, where all symbol table entries are
    defined.  If LINK is used, the proper symbol table entry for each
    stub (using the name passed as the first parameter) is created.

    Lower case names are recommended, as they will be easier to enter from
    the keyboard.  However, upper case may be used as well, and will be
    distinguished from lower case.

Due to the nature of the linking mechanism, the module(s) containing
application routines do not have to be actually combined with the Tiny-C
interpreter code.  First the Application Module is constructed, which
involves linking and locating all application functions and some code
similar to LNKMOD, above.  Then a Tiny-C interpreter is constructed, and
linkage to the Application Module is accomplished by using the LINK86
PUBLICSONLY(app_module) switch.  The Application Module (app_module) is
not (and should not be) actually combined with the interpreter,
only the reference to LNKVEC from within the interpreter is resolved.
This will produce a Tiny-C interpreter linked to a specific application.
Since the application functions and the interpreter are not combined,
the application may be changed and re-located without necessarily
affecting the LNKVEC entry point, thus requiring no changes or re-linking
of the Tiny-C interpreter.
              LINKING AND PRODUCING TINY-C

In addition to the five Tiny-C kernel modules and the interface module(s),
a space definition module is required for linking.  This module defines
the space available for the various Tiny-C data structures.  The following
code, which contains sample definitions for an 86/12 computer, is an
example of a space definition module.
$TITLE(Space Definitions for SBC-86/12A)

	NAME	DEFNS

TCGROUP	GROUP	CODE,DATA,STACK,TCMEM
	ASSUME	CS:TCGROUP,DS:TCGROUP,SS:TCGROUP,ES:TCGROUP

	PUBLIC	BPR,EPR,BFUN,EFUN,BSTACK,ESTACK,MSTACK
	PUBLIC	LINE,CMDLN

; space allocation templates:

PSTK	EQU	800H		; length of processor stack required (words)
TSTK	EQU	30		; number of elements in Tiny-C stack
FUN	EQU	21		; number of elements in function table
PR	EQU	2800H		; program/symbol table space used by Tiny-C

CMDLN	EQU	80		; length of command line/initial statment buff

DATA	SEGMENT WORD PUBLIC 'DATA'

BSTACK	DB	TSTK*5 DUP (?)	; Tiny-C stack
ESTACK	LABEL	BYTE		; end of TC stack

BFUN	DB	FUN*6 DUP (?)	; function table
EFUN	LABEL	BYTE		; end of function table

LINE	DB	CMDLN DUP (?)	; buffer for command line/initial statement

BPR	DB	PR DUP (?)	; start of program space
EPR	DW	?		; EPR contains pointer to 1 + last byte of
				;    of program space, set at run time
DATA	ENDS
STACK	SEGMENT WORD STACK 'STACK'

	DW	PSTK DUP (?)	; allocate processor stack
MSTACK	LABEL	WORD		; top of stack

STACK	ENDS
CODE	SEGMENT WORD PUBLIC 'CODE'

	PUBLIC	MEMCK

MEMCK	PROC			; kernel subroutine
	LEA	BX,BPR+PR	; rtn ptr to last byte of program space + 1
	RET
MEMCK	ENDP

CODE	ENDS
TCMEM	SEGMENT WORD PUBLIC
TCMEM	ENDS
	END
The allocation variables which may change with different implementations are:

	PSTK	The length of the processor stack required by Tiny-C
		*AND* any external routines it may access.  Generally
		speaking, 2k words of stack (PSTK = 800H) is sufficient
		for most implementations.

	TSTK	The length of the Tiny-C stack.  Since a Tiny-C stack
		element is 5 bytes in length, the actual length of the
		Tiny-C stack in bytes will be TSTK*5.  Generally acceptable
		values for TSTK are between 15 and 30 (15 to 30 Tiny-C
		stack elements).  The size of the stack governs complexity
		of expressions that may be evaluated.

	FUN	The length of the Tiny-C active function table.
		Since each element of the function table is 6 bytes in
		lenghth, the actual length of the table in bytes will be
		FUN*6.  Generally acceptable values for FUN are between 10
		and 25.  One element is required for each nested function
		call, plus one extra element for the main program.  Thus a 
		function table size of 21 will allow 20 nested function
		calls.

	PR	The size of the Tiny-C program buffer, in bytes.
		Approximately 15% of this space will be used for the
		dynamic symbol table.  The space allocated by PR should
		be at least 1k byte, and may be as large as RAM allows.

(The value CMDLN, also listed with the other definable parameters, should
not be changed.)

Note that in addition to defining Tiny-C data structures, the definition
module contains a routine MEMCK.  This routine must return a pointer to
the last byte of program space, plus one, in BX.  Symbolically, this is the
location BPR+PR.  In a stand-alone configuration, this routine will not have
to be modified from the SBC-86/12 version.  In the event that an operating
system under which Tiny-C may run provides such a function, MEMCK may link
to the operating system function, or be omitted, as appropriate.

The definition module contains segment definitions for all segments in
TCGROUP.  For this reason, it must be first in the LINK list.  A sample
LINK86 command to produce a relocatable Tiny-C object module is:

LINK86 :F1:TCDF86.O86,:F1:TCINTP.O86,:F1:TCEDIT.O86,:F1:TCLIB.O86,&
:F1:TCUTIL.O86,:F1:TCHEAD.O86,:F1:INTFC.O86, PUBLICSONLY(:F1:APPMOD)&
TO :F1:TC86.LNK NOMAP

In the above example, ":F1:TCDF86.O86" is the definition module, and
":F1:TCINTFC.O86" is the interface module.  The remaining modules
comprise the Tiny-C/86 kernel.  ":F1:APPMOD" is the application module
containing external library routines.  If there is no external module
for a particular application, the PUBLICSONLY control may be omitted.

Locating Tiny-C:

There are four segments to be located once Tiny-C has been linked:

	TCMEM  (no classname)
	CODE   (classname 'CODE')
	DATA   (classname 'DATA')
	STACK  (classname 'STACK')

These segments are all combined into the group TCGROUP.  DATA and STACK
segments are typically located in RAM, and CODE in ROM.  The TCMEM segment
may be located anywhere in the 64k bytes spanned by TCGROUP.  What it does
is provide a starting reference address for the "memory" built-in array.
The location of TCMEM also determines the starting address of the "mc"
call vectors.  Normally, TCMEM is located at absolute memory location 0,
unless the memory spanned by TCGROUP does not include location 0.
Even though TCGROUP may only span 64k bytes, it may "wrap-around"
through high memory, so that DATA and STACK may be located at 200H, for
example, while CODE may be located at FE000H.  Locating the CODE segment
at E0000H, though, would be illegal.

A sample LOC86 command, which locates Tiny-C appropriately for an SBC 86/12
computer, is:

LOC86 :F1:TC86.LNK TO :F1:TC86  OD(SM(STACK,DATA)) AD(SM(TCMEM(0),STACK(200H),&
CODE(FE000H))) BS NOSYMBOLS

Here, the space referenced by "memory" and "mc" starts at 0 (so memory[0]
is mapped to physical location 0), STACK begins at 200H, DATA immediately
follows STACK, and CODE begins at FE000H.  Note that the BOOTSTRAP option
(BS) has been specified, which indicates that this Tiny-C implementation
will be a stand-alone configuration.
