V10/cmd/spitbol/cint/vaxinter.s

#
#	VAXINTER.S
#
#	This file contains the assembly language routines that interface
#	the Macro SPITBOL compiler written in VAX assembly language to its
#	operating system interface functions written in C.
#
#	Contents:
#
#	o Overview
#	o Global variables accessed by OSINT functions
#	o Interface routines between compiler and OSINT functions
#	o C callable function get_sp
#	o C callable function startup
#	o Compiler callable function sbchk
#
#-----------
#
#	Overview
#
#	The Macro SPITBOL compiler relies on a set of operating system
#	interface functions to provide all interaction with the host
#	operating system.  These functions are referred to as OSINT
#	functions.  A typical call to one of these OSINT functions takes
#	the following form in the VAX version of the compiler:
#
#		...code to put arguments in registers...
#		jsb	sysxx		; call osint function
#		.long	exit_1		; address of exit point 1
#		.long	exit_2		; address of exit point 2
#		...	...		; ...
#		.long	exit_n		; address of exit point n
#		...instruction following call...
#
#	The OSINT function 'sysxx' can then return in one of n+1 ways:
#	to one of the n exit points or to the instruction following the
#	last exit.  This is not really very complicated - the jsb places
#	the return address on the stack, so all the interface function has
#	to do is add the appropriate offset to the return address and then
#	pick up the exit address and jump to it OR do a normal return via
#	an rsb instruction.
#
#	Unfortunately, a C function cannot handle this scheme.  So, an
#	intermediary set of routines have been established to allow the
#	interfacing of C functions.  The mechanism is as follows:
#
#	(1) The compiler calls OSINT functions as described above.
#
#	(2) A set of assembly language interface routines is established,
#	    one per OSINT function, named accordingly.  Each interface
#	    routine ...
#
#	    (a) saves all compiler registers in global variables
#	        accessible by C functions
#	    (b) calls the OSINT function written in C
#	    (c) restores all compiler registers from the global variables
#	    (d) inspects the OSINT function's return value to determine
#	        which of the n+1 returns should be taken and does so
#
#	(3) A set of C language OSINT functions is established, one per
#	    OSINT function, named differently than the interface routines.
#	    Each OSINT function can access compiler registers via global
#	    variables.  NO arguments are passed via the call.
#
#	    When an OSINT function returns, it must return a value indicating
#	    which of the n+1 exits should be taken.  These return values are
#	    defined in header file 'inter.h'.
#
#	Note:  in the actual implementation below, the saving and restoring
#	of registers is actually done in one common routine accessed by all
#	interface routines.
#
#	Other notes:
#
#	The Un*x VAX cC ompilers transform "internal" global names to
#	"external" global names by adding a leading underscore at the front
#	of the internal name.  Thus, the function name 'osopen' becomes
#	'_osopen'.
#
#	Acknowledgement:
#
#	This interfacing scheme is based on an idea put forth by Andy Koenig.
#
#-----------
#
#	Global Variables
#
#	The compiler uses 8 registers as described below.
#
	.data
	.globl	_reg_ra
	.globl	_reg_cp
	.globl	_reg_ia
	.globl	_reg_wa
	.globl	_reg_wb
	.globl	_reg_wc
	.globl	_reg_xr
	.globl	_reg_xl
	.align	2
_reg_ra:	.space	4	# Register RA (R2)
_reg_cp:	.space	4	# Register CP (R3)
_reg_ia:	.space	4	# Register IA (R5)
_reg_wa:	.space	4	# Register WA (R6)
_reg_wb:	.space	4	# Register WB (R7)
_reg_wc:	.space	4	# Register WC (R8)
_reg_xr:	.space	4	# Register XR (R9)
_reg_xl:	.space	4	# Register XL (R10)
#
	.globl	_rsboff
_rsboff:	.space	4	# Normal rsb return offset
#
#	The following pointers address those cells in the compiler
#	that point into the stack when a load module might be written,
#	and which must therefore be relocated.
	.globl	_strellst
_strellst:
	.long	flptr
	.long	stbas
	.long	gtcef
	.long	0		# end of list marker
#
#	Setup a number of internal addresses in the compiler that cannot
#	be directly accessed from within C because of naming difficulties.
#
	.globl	_ic_type
_ic_type:	.long	b$icl
	.globl	_sc_type
_sc_type:	.long	b$scl
	.globl	_xn_type
_xn_type:	.long	b$xnt
	.globl	_xr_type
_xr_type:	.long	b$xrt
#
	.globl	_id1
_id1:	.long	0,id1l
	.ascii	"(2.0)"
id1e:
	.set	id1l,id1e-_id1-8
	.align	2
#
	.globl	_id2
_id2:	.long	0,id2l
	.ascii	"VAX/UNIX Version"
id2e:
	.set	id2l,id2e-_id2-8
	.align	2
#
	.globl  _tscblk
	.globl  _ticblk
_ticblk: .space 8
_tscblk: .space 108
#
#-----------
#
#	Interface routines
#
#	Each interface routine takes the following form:
#
#		sysxx:	jsb	ccaller		# call common interface
#			.long	_sysxx		# address of C OSINT function
#			.long	n		# offset to instruction after
#						#   last procedure exit
#
#	We take advantage of the "internal" to "external" transformation
#	of names by the C compiler.  Specifically, the assembly language
#	interface routine names match those directly called by the compiler:
#	'sysxx'.  The corresponding C OSINT functions are also called 'sysxx',
#	but the C compiler nicely transforms these names into '_sysxx'.
#	
	.text
	.globl	sysax
sysax:	jsb	ccaller
	.long	_sysax
	.long	0
#
	.globl	sysbx
sysbx:	jsb	ccaller
	.long	_sysbx
	.long	0
#
	.globl	sysdc
sysdc:	jsb	ccaller
	.long	_sysdc
	.long	0
#
	.globl	sysdm
sysdm:	jsb	ccaller
	.long	_sysdm
	.long	0
#
	.globl	sysdt
sysdt:	jsb	ccaller
	.long	_sysdt
	.long	0
#
	.globl	sysef
sysef:	jsb	ccaller
	.long	_sysef
	.long	12
#
#	.globl	sysej
#sysej:	jsb	ccaller
#	.long	_sysej
#	.long	0
#
	.globl	sysem
sysem:	jsb	ccaller
	.long	_sysem
	.long	0
#
	.globl	sysen
sysen:	jsb	ccaller
	.long	_sysen
	.long	12
#
	.globl	sysep
sysep:	jsb	ccaller
	.long	_sysep
	.long	0
#
	.globl	sysex
sysex:	jsb	ccaller
	.long	_sysex
	.long	4
#
#	.globl	sysfc
#sysfc:	jsb	ccaller
#	.long	_sysfc
#	.long	4
#
	.globl	syshs
syshs:	jsb	ccaller
	.long	_syshs
	.long	24
#
	.globl	sysid
sysid:	jsb	ccaller
	.long	_sysid
	.long	0
#
#	.globl	sysil
#sysil:	jsb	ccaller
#	.long	_sysil
#	.long	0
#
#	.globl	sysin
#sysin:	jsb	ccaller
#	.long	_sysin
#	.long	12
#
#	.globl	sysio
#sysio:	jsb	ccaller
#	.long	_sysio
#
	.globl	sysld
sysld:	jsb	ccaller
	.long	_sysld
	.long	8
#
	.globl	sysmm
sysmm:	jsb	ccaller
	.long	_sysmm
	.long	0
#
	.globl	sysmx
sysmx:	jsb	ccaller
	.long	_sysmx
	.long	0
#
	.globl	sysou
sysou:	jsb	ccaller
	.long	_sysou
	.long	8
#
#	.globl	syspi
#syspi:	jsb	ccaller
#	.long	_syspi
#	.long	0
#
	.globl	syspp
syspp:	jsb	ccaller
	.long	_syspp
	.long	0
#
#	.globl	syspr
#syspr:	jsb	ccaller
#	.long	_syspr
#	.long	4
#
#	.globl	sysrd
#sysrd:	jsb	ccaller
#	.long	_sysrd
#	.long	4
#
#	.globl	sysri
#sysri:	jsb	ccaller
#	.long	_sysri
#	.long	4
#
	.globl	sysrw
sysrw:	jsb	ccaller
	.long	_sysrw
	.long	12
#
#	.globl	sysst
#sysst:	jsb	ccaller
#	.long	_sysst
#	.long	20
#
#	.globl	systm
#systm:	jsb	ccaller
#	.long	_systm
#	.long	0
#
	.globl	systt
systt:	jsb	ccaller
	.long	_systt
	.long	0
#
	.globl	sysul
sysul:	jsb	ccaller
	.long	_sysul
	.long	0
#
#	.globl	sysxi
#sysxi:	jsb	ccaller
#	.long	_sysxi
#	.long	8
#
#-----------
#
#	CCALLER is called by the OS interface routines to call the
#	real C OS interface function.
#
#	General calling sequence is:
#
#		jsb	ccaller
#		.long	address_of_C_function
#		.long	4*number_of_exit_points
#
#	Control IS NEVER returned to a interface routine.  Instead, control
#	is returned to the compiler (THE caller of the interface routine).
#
#	The C function that is called MUST ALWAYS return an integer
#	indicating the proecedure exit to take or that a normal return
#	is to be performed.
#
#		C function      Interpretation
#		return value
#		------------	-------------------------------------------
#		     <0		Do normal return to instruction past
#				last procedure exit (distance passed
#				in by dummy routine and saved in _rsboff)
#		      0		Take procedure exit 1
#		      4		Take procedure exit 2
#		      8		Take procedure exit 3
#		     ...	...
#	
ccaller:
#
#	(1) Save registers in global variables
#
	movl	r2,_reg_ra	# save RA (R2)
	movl	r3,_reg_cp	# save CP (R3)
	movl	r5,_reg_ia	# save IA (R5)
	movl	r6,_reg_wa	# save WA (R6)
	movl	r7,_reg_wb	# save WB (R7)
	movl	r8,_reg_wc	# save WC (R8)
	movl	r9,_reg_xr	# save XR (R9)
	movl	r10,_reg_xl	# save XL (R10)
#
#	(2) Fetch address of C function, fetch offset to 1st instruction
#	    past last procedure exit, and call C function.
#
	movl	(sp)+,r0	# point to arg list
	movl	(r0)+,r1	# point to C function entry point
	movl	(r0),_rsboff	# get adjustment for normal exit
	calls	$0,(r1)		# call C interface function
#
#	(3) Restore registers after C function returns.
#
	movl	_reg_ra,r2	# restore RA (R2)
	movl	_reg_cp,r3	# restore CP (R3)
	movl	_reg_ia,r5	# restore IA (R5)
	movl	_reg_wa,r6	# restore WA (R6)
	movl	_reg_wb,r7	# restore WB (R7)
	movl	_reg_wc,r8	# restore WC (R8)
	movl	_reg_xr,r9	# restore XR (R9)
	movl	_reg_xl,r10	# restore XL (R10)
#
#	(4) Based on returned value from C function either do a normal
#	    return or take a procedure exit.
#
	tstl	r0		# if normal return ...
	bgeq	erexit
	addl2	_rsboff,(sp)	#   point to instruction following exits
	rsb			#   return
#				# else (take procedure exit n)
erexit:	addl3	r0,(sp)+,r11	#   point to address of exit
	jmp	*(r11)+		#   take procedure exit
#
#-----------
#
#	_get_sp	- get C caller's SP
#
#	_get_sp() returns the current value of the stack pointer from
#	the point of reference of a C function.
#
#	From C this procedure is called by
#
#		our_sp = get_sp();
#
#	The generated code will look like
#
#		calls	#0,_get_sp
#		movl	r0,our_sp
#
#	On entry to _get_sp, the stack looks like
#
#		+---------------+  (<-- SP before 'calls' executed)
#		|  arg cnt (0)	|
#		|---------------|  <-- AP
#		|    old PC	|
#		|---------------|
#		|    old FP	|
#		|---------------|
#		|    old AP	|
#		|---------------|
#		|   mask/PSW	|
#		|---------------|
#		|      0	|
#		+---------------+  <-- SP  <-- FP
#
#	On exit, all these words will be removed from the stack.  Therefore,
#	the current value of the SP from the caller's perspective is AP+4.
#
	.text
	.globl	_get_sp
_get_sp:
	.word	0		# no registers to save
	addl3	$4,ap,r0	# compute caller's SP
	ret			# done
#
#-----------
#
#	_startup - startup compiler
#
#	An OSINT C function calls _startup to transfer control
#	to the compiler.
#
	.text
	.globl	_startup
_startup:
	.word	0
	movl	_reg_ra,r2	# initialize RA (R2)
	movl	_reg_cp,r3	# initialize CP (R3)
	movl	_reg_ia,r5	# initialize IA (R5)
	movl	_reg_wa,r6	# initialize WA (R6)
	movl	_reg_wb,r7	# initialize WB (R7)
	movl	_reg_wc,r8	# initialize WC (R8)
	movl	_reg_xr,r9	# initialize XR (R9)
	movl	_reg_xl,r10	# initialize XL (R10)
#
	movl	sp,_initsp	# save initial sp
	subl3	_stacksiz,sp,_lowsp
				# set lowest legal sp for sbchk
#
	movl	_dfltcase,kvcas	# set case flag IN compiler
#
	jmp	sec04		# transfer control to compiler
#
#-----------
#
#	sbchk - check for stack overflow
#
	.text
	.globl	sbchk
sbchk:	cmpl	sp,_lowsp	# if sp is ok then
	blssu	sb100
	rsb			#    return
sb100:	tstl	(sp)+		# else pop stack
	jmp	sec05		#    and go to stack overflow section