V10/cmd/spitbol/osint.s

Compare this file to the similar file:
Show the results in this format:

#
#	Operating System Interface to Berkeley VAX/UNIX
#		              for
#		       Macro SPITBOL V3.5
#	-----------------------------------------------
#
#		Copyright 1982
#		Dewar Information Systems Corporation
#		221 West Lake Street
#		Oak Park, Illinois 60302
#
#	this operating system interface, commonly referred to as osint,
#	provides the Macro SPITBOL compiler with a means to do all i/o
#	and other OS dependent activities. 
#
#	osint is composed of one medium sized assembly module, with
#	multiple entry points, and a collection of c functions. the
#	entire interface is not in c due to the compiler's use of
#	registers to pass parameters and other technicalities. all
#	entries, except for sbchk, have five letter names starting
#	with sys.
#
#	when spitbol first starts execution, control passes to function
#	main, just like in a normal c program. main processes any options
#	and does other initialization chores. after completing
#	initialization, main jumps to label sec04 to start
#	the compiler itself. from that point, the compiler makes calls
#	to sysxx routines to get service.
#
#	the c functions are called from this module to do most of the
#	hard and/or unix dependent work. an effort has been made to
#	do all the system calls from c functions. this goal has not
#	been entirely achieved.
#	
#
#	register masks
#
	.set	mr0,1
	.set	mr1,2
	.set	mr2,4
	.set	mr3,8
	.set	mr4,16
	.set	mr5,32
	.set	mr6,64
	.set	mr7,128
	.set	mr8,256
	.set	mr9,512
	.set	mr10,1024
	.set	mr11,2048
#
#	c functions assume that r0 -> r5 are scratch.  however, spitbol
#	uses r2, r3, and r5.  so, need to save these registers across
#	function calls.
#
	.set	mr235,mr2+mr3+mr5
#
#	mask for all registers used by compiler. necessary for dealing
#	with load modules.
#
	.set	cmpreg,mr2+mr3+mr5+mr6+mr7+mr8+mr9+mr10
#
#	miscellaneous equates
#
	.set	ch$lk,107	# upper case letter k
	.set	ch$mn,45	# dash (minus sign)
	.set	k,1024
	.set	kwords,k*4
#
#	internal spitbol blocks
#	-----------------------
#
#	these equates describe compiler control blocks used by osint.
#
#	chain of file control blocks
#
	.set	chtyp,0		# type word
	.set	chlen,4		# block length
	.set	chnxt,8		# -> next chblk
	.set	chfcb,12	# -> fcb
	.set	chsize,16	# size of chblk
#
#	icblk - integer block
#
	.set	ictyp,0		# type word
	.set	icval,4		# integer value
#
#	scblk - string block
#
	.set	sctyp,0		# type word
	.set	sclen,4		# string length
	.set	scstr,8		# start of string
#
#	interface control blocks
#	------------------------
#
#	these control blocks are built by the interface to handle i/o.
#	for files other than standard input, standard output, and the
#	terminal, these control blocks reside in the compiler's 
#	dynamic area. this requires appropriate setting of the type
#	words to allow proper garbage collection.
#
#	blocks with type b$xnt contain non-relocatable addresses - that
#	is, no word may contain a pointer to another block in the
#	dynamic area.
#
#	blocks with type b$xrt contain relocatable addresses - that is
#	words may contain a pointer to another block in the dynamic area.
#
#	the basic structure is:
#
#	for each input or output associated file there is:
#
#		1 ioblk	 containing all global information about
#			 the file:  filename, buffer block pointer,
#			 file descriptor number, flags
#
#		1 bfblk  containing the file buffer
#
#		1 or more fcblks  containing the access mode (line or
#			 raw), the record length, and a pointer to 
#			 the ioblk
#
#	the first INPUT() or OUTPUT() call for a file creates one block
#	of each type. subsequent calls to INPUT() or OUTPUT() for a 
#	previously associated file, may cause the creation of a new fcblk.
#	allowing multiple fcblks provides the program with different
#	ways of accessing the same file. for example, one type of access
#	can be a character at a time and another entire records.
#
#	the compiler keeps track of all active fcblks, and at times
#	like end-of-job provides the inteface with a chain of all fcblks.
#
#
#	bfblk - i/o buffer control block
#
	.set	bftyp,0		# type word - b$xnt
	.set	bflen,4		# block length
	.set	bfsiz,8		# buffer size in bytes
	.set	bfrem,12	# bytes remaining
	.set	bfoff,16	# offset to next remaining byte
	.set	bfsize,20	# end of fixed portion
#
#	fcblk - file control block
#
	.set	fctyp,0		# type word - b$xrt
	.set	fclen,4		# block length
	.set	fcrsz,8		# record size ( >0 line mode / <0 raw mode) 
	.set	fciob,12	# -> ioblk 
	.set	fcsize,16	# size of fcblk
#
#	ioblk - i/o control block
#
	.set	iotyp,0		# type word - b$xrt
	.set	iolen,4		# block length
	.set	iofnm,8		# -> filename scblk
	.set	iopid,12	# pid (if one end of pipe)
	.set	iobuf,16	# -> bfblk
	.set	iofdn,20	# file descriptor number
	.set	ioflg,24	# flags
	.set	iosize,28	# size of ioelt
#
#	defines that match "spitio.h" for flags in ioflg
#
	.set	IO_INP,1	# input associated
	.set	IO_OUP,2	# output associated
	.set	IO_APP,4	# output open for append
	.set	IO_OPN,8	# file is open
	.set	IO_EOF,16	# eof on input file
	.set	IO_ERR,32	# i/o error
	.set	IO_SYS,64	# this is an osint file
	.set	IO_WRC,128	# don't do buffering
	.set	IO_PIP,256	# this is one end of a pipe
	.set	IO_DED,512	# other end of pipe died
	.set	IO_ILL,1024	# i/o illegal according to sysfc
#
#	osint( argc,argv,environ ) is called just like the main
#	function of a c program.
#
	.globl	_main
_main:	.word	0
#
#	normal start - process all command arguments.
#
	movl	4(ap),r4	# get number of options
	movl	r4,argc		#    and save
	movl	8(ap),r6	# -> option pointers
	movl	r6,argv		#    and save
#
#	if this is a restart from an load module, go handle.
#
	tstl	lmodstk		# if load module stack != 0 then
	jnequ	rstart		#    go handle
#
	tstl	(r6)+		# program name not interesting
	brb	gtcont		# process other args
gtarg:	movl	(r6)+,r0	# -> option text
	cmpb	$ch$mn,(r0)	# if no leading - then
	bnequ	gtinp		#    treat as input filename
	incl	r0		# bump over -
1:	movb	(r0)+,curopt+1	# set option character in table
	movl	$opttbl,r1	# -> option table
2:	cmpl	curopt,(r1)	# if we have found it then
	beqlu	3f		#    process it
	addl2	$optsiz,r1	# -> next table entry
	brb	2b		# loop until found
3:	movl	optrtn(r1),r11	# -> option routine
	jsb	(r11)		# call option routine
4:	tstb	(r0)		# if char is binary zeros then
	beqlu	gtcont		#    done with this arg
	cmpb	$040,(r0)	# if next char is blank then
	bnequ	5f
	incl	r0		#    ignore it
	brb	4b		#    and check next char
5:	cmpb	$ch$mn,(r0)	# if next char is not - then
	bnequ	1b		#    treat as option character
	incl	r0		# else skip over -
	brb	1b		#     and treat next char as option
gtinp:	jsb	optinp		# process input filename(s)
gtcont:	sobgtr	r4,gtarg	# loop thru all options
#
#	switch to proper input file
#
	pushl	inpptr
	pushl	inpcnt
	calls	$2,_swcinp
#
#	call to do initial switch of output files.
#
	pushl	oupptr
	calls	$1,_swcoup
#
#	see if standard output is tty or not.
#
	pushl	$1		# file descriptor 1
	calls	$1,_testty	# call to check i/o chcaracteristics
	tstl	r0		# if r0 not 0 then
	bnequ	0f		#    not a tty
	clrl	lnsppg		# reset # lines per page
	bisl2	$prtich,sptflg	# else tell compiler
0:
#
#	set signals for execution
#
	.globl	_setsigs
	calls	$0,_setsigs	# trap overflow signals
#
#	allocate initial dynamic memory
#
	mull3	meminc,$4,meminb# convert meminc to bytes
	pushl	meminb		# memory request increment
	calls	$1,_sbrk	# call to system call
	movl	r0,basmem	# save base of memory
	addl3	r0,meminb,topmem# computer top of memory
	mull3	datwds,$4,r1	# convert max data words to bytes
	addl3	r0,r1,maxmem	# computer top of memory
0:	clrl	(r0)+		# clear initial allocation
	cmpl	r0,topmem	# loop until all cleared
	blssu	0b
#
#	set up lowest legal sp value, so that stack overflow can be detected,
#
	mull3	$4,stksiz,r0	# convert words to bytes
	subl3	r0,sp,lowsp	#    and compute lowest sp
	movl	sp,initsp	# save initial sp
#
#	clear registers, set dynamic area pointers, and jump off
#	to compiler
#
	clrl	r2
	clrl	r3
	clrl	r4
	clrl	r5
	clrl	r6
	clrl	r7
	clrl	r8
	movl	basmem,r9
	subl3	$4,topmem,r10
	jmp	sec04
#
#	here to restart program after EXIT() call. this means we are now
#	executing from an a.out file created by the interface.
#
rstart:
#
#	before restoring stack, set up values for proper checking of
#	stack overflow. (initial sp here will most probably differ
#	from initial sp when compile was done.)
#
	mull3	$4,stksiz,r0	# convert words to bytes
	subl3	r0,sp,lowsp	#    and compute lowest sp
	movl	sp,initsp	# save initial sp
	jsb	streloc		# relocate pointers into stack
#
#	restore stack from tscblk.
#
	movl	lmodstk,r0	# -> bottom word of stack
	movab	tscblk+scstr,r1	# -> top word of stack
0:	movl	-(r0),-(sp)	# relocate word of stack
	cmpl	r0,r1		# if not at end of relocation then
	bgtru	0b		#    loop back
#
#	if restarting, we can always access the command line arguments
#
	movl	$1,cmdcnt
#
#	the system break will not be what it should, so reset it
#
	pushl	topmem
	calls	$1,_brk
#
#	reset signals to what they should be.
#
	calls	$0,_setsigs
#
#	forget about files open during compilation -
#
	clrl	inpptr		# no input files
	clrl	inpcnt		#    so count is 0 too
	clrl	oupptr		# no output file
	clrl	errfdn		# no error file
#
#	reset standard input buffer
#
	clrl	inpbf+bfrem	# no remaining chars
	clrl	inpbf+bfoff	# offset to next char
#
#	restore compiler's registers and off we go.
#
	popr	$cmpreg		# restore compiler's registers
	addl2	$8,(sp)
	rsb
#
#	sbchk is called by the compiler to check for stack overflow.
#
	.globl	sbchk
sbchk:	cmpl	sp,lowsp	# if sp is ok then
	blssu	0f
	rsb			#    return
0:	tstl	(sp)+		# else pop stack
	jmp	sec05		#    and go to stack overflow section
#
#	unsupported entries that simply return.
#
	.globl	sysdc
sysdc:
	.globl	sysdm
sysdm:
	.globl	systt
systt:
	.globl	sysul
sysul:
	rsb
#
#	unsupported that take error returns
#
	.globl	sysex
sysex:
	.globl	sysld
sysld:
	pushr	$mr235		# save ye registers
	jbr	erxit1
#
#	sysax - after execution call
#
#	sysax is called immediately after execution, so that the interface
#	can do any cleanup. here, the standard output file is switched, if
#	necessary back to the listing file.
#
	.globl	sysax
sysax:
	pushr	$mr235
	pushl	oupptr
	calls	$1,_swcoup
	popr	$mr235
	rsb
#
#	sysbx - before execution call
#
#	sysbx is called after compilation and before execution begins.
#	sysbx allows the inteface to do any cleanup. here, the standard
#	listing file is switched to the standard output file.
#
	.globl	sysbx
sysbx:
	pushr	$mr235
	pushl	oupptr
	calls	$1,_swcoup
	popr	$mr235
	rsb
#
#	sysdt - get current date
#
#	return both date and time
#
#	jsb	sysdt			# call to get date
#	(r10)				# -> scblk
#
	.globl	sysdt
sysdt:
	pushr	$mr235
	movl	$tscblk,r10
	movl	$17,sclen(r10)		# dd/mm/yy hh.mm.ss
	pushl	$tscblk+scstr
	calls	$1,_getdate
	popr	$mr235
	rsb
#
#	sysef - eject file
#
	.globl	sysef
sysef:
	pushr	$mr235
	pushl	fciob(r6)	# -> ioblk
	calls	$1,_osopen	# call to do open
	tstl	r0		# if open error then
	jnequ	erxit1		#    take error exit
	pushl	$ffscb		# -> ff scblk
	pushl	fciob(r6)	# -> ioblk
	pushl	ffscb+sclen	# record length
	mnegl	fcrsz(r6),-(sp)	# i/o mode - raw or line
	calls	$4,_oswrite	# call to do write
	tstl	r0		# if output error then
	jneq	erxit3		#    signal failure
	popr	$mr235
	addl2	$12,(sp)
	rsb
#
#	sysej - end of job
#
	.globl	sysej
sysej:
	movl	r10,_rzfcb	# copy head of fcb chain
	beqlu	1f		# if empty then nothing to close
0:	movl	chfcb(r10),r1	# -> fcb
	pushl	fciob(r1)	# -> ioblk
	calls	$1,_osclose	# call to do close
	movl	chnxt(r10),r10	# -> next on chain
	bnequ	0b		#    and loop back for more
1:	pushl	r7		# return &code
	calls	$1,__exit
#
#	sysem - get error message text
#
	.globl	sysem
sysem:
	pushr	$mr235
	movl	$tscblk,r9	# -> temp scblk
	clrl	sclen(r9)	# default error text is null string
	tstl	errfdn		# if error fd exists then
	bnequ	0f		#    skip open
	calls	$0,_openerr	# else open error text file
	movl	r0,errfdn	#    and save fd
0:	tstl	errfdn		# if no error text file then
	blss	emxit		#    return null string
	decl	r6		# errors start at 1
	blss	emxit		#    but don't have error 0
	mull2	$49,r6		# compute byte offset of error text
	pushl	$0		# whence
	pushl	r6		# offset
	pushl	errfdn		# file descriptor number
	calls	$3,_lseek	# call to do seek
	tstl	r0		# if lseek fails then
	blss	emxit		#    return null string
	pushl	$48		# error text length w/out nl
	pushl	$tscblk+8	# buffer address
	pushl	errfdn		# file descriptor number
	calls	$3,_read	# call to do read
	tstl	r0		# if read failed then
	blss	emxit		#    return null string
1:	cmpb	$' ,tscblk+7(r0)# if last character is
	bneq	2f		#   nonblank, return length
	sobgeq	r0,1b		# otherwise discard the blank, try again
2:	movl	r0,sclen(r9)	# set actual length
emxit:
	popr	$mr235
	rsb			# return
#
#	sysen - endfile
#
	.globl	sysen
sysen:
	pushr	$mr235
	movl	r$fcb,_rzfcb	# copy fcb chain head
	pushl	fciob(r6)	# -> ioblk
	calls	$1,_osclose	# call to do close
	popr	$mr235
	addl2	$12,(sp)
	rsb
#
#	sysej - eject standard output
#
	.globl	sysep
sysep:
	pushr	$mr235
	pushl	$1		# 1 character
	pushl	$ffstr		# -> ff
	pushl	$1		# fd 1
	calls	$3,_write	# call to do write
	popr	$mr235
	rsb
#
#	sysfc
#
	.globl	sysfc
sysfc:
	movl	(sp)+,(sp)	# remove stacked scblk
	pushr	$mr235
	tstl	sclen(r10)	# if null filearg1 then
	jeqlu	erxit1		#    error
#
#	get length of filename and scan off options.
#
	movl	r9,-(sp)	# -> filename scblk
	calls	$1,_lenfnm	# call to get filename length
	movl	r0,lenfname	# save length for later use
	jlss	erxit1		# length must not be negative
	movl	r9,-(sp)	# -> filename scblk
	movl	$tioblk,-(sp)	# -> temporary ioblk
	movl	r7,-(sp)	# input/output association flag
	calls	$3,_sioarg	# call to scan i/o args
	tstl	r0		# if error in args then
	jlss	erxit1		#    take error return
#
#	check for consistency of calls. cannot have both input
#	and output to same channel. if this happens, though,
#	set flag and let sysio take proper error exit.
#
	tstl	r6		# if no previous fcblk then
	beqlu	0f		#    skip
	movl	fciob(r6),r0	# -> ioblk
	movl	ioflg(r0),r0	# get previous flags
	mcoml	$IO_INP+IO_OUP,r1 # get mask for bicl
	bicl2	r1,r0		# remove all bits but INP&OUP
	bitl	tioblk+ioflg,r0	# if bits are not same then
	bnequ	0f
	bisl2	$IO_ILL,tioblk+ioflg	# then set error flag
0:
#
#	handle null filenames here - must either have a previous
#	fcblk or specify -f arg.
#
	tstl	lenfname	# if non-null filename then
	bgtr	fcfnam		#    go handle below
	bitl	$IO_OPN,tioblk+ioflg
	bnequ	fcfarf		# if -f specified then merge w/non-null
	tstl	r6		# if no previous fcblk then
	jeqlu	erxit1		#    error
	clrl	r6		# assume that no new fcblk needed
	clrl	ioblkptr	# no ioblk ptr to stuff
	tstl	tioblk		# if however i/o args indicate one
	jeqlu	fcxit
	movl	fciob(r6),ioblkptr
	movl	$fcsize,r6	#    allocate one
	jbr	fcxit
#
#	handle real filenames and null filenames with -f arg here.
#	note that they're mutually exclusive.
#
fcfnam:	bitl	$IO_OPN,tioblk+ioflg
	jnequ	erxit1		# can't have -f arg too
fcfarf:	tstl	r6		# if previous fcblk passed then
	jnequ	erxit1		#    error
	clrl	ioblkptr
	addl3	$bfsize+3,tioblk+iopid,r6
	bicl2	$3,r6
	movl	r6,bfblksiz
	addl2	$fcsize+iosize,r6
	movl	$1,tioblk	# set newfcb flag
#
fcxit:	clrl	r10		# no private fcblk
	clrl	r8		# xrblk please
	popr	$mr235
	addl2	$4,(sp)
	rsb
#
#	syshs
#
	.globl	syshs
syshs:
	pushr	$mr235
	cmpl	$b$icl,(r6)	# if arg1 not integer then
	jnequ	9f		#    return host string
	casel	icval(r6),$0,$5
0:
	.word	1f-0b		#  0: return -u argument
	.word	2f-0b		#  1: do system call
	.word	3f-0b		#  2: return command arg
	.word	4f-0b		#  3: return number of first #! arg
	.word	5f-0b		#  4: get environment variable
	.word	6f-0b		#  5: manipulate signals
	jbr	erxit1		#  else arg error
#
1:	tstl	uarg		# if uarg is 0 then
	jeqlu	erxit4		#    return null string
	pushl	tscblk		# push scblk string length
	pushl	$tscblk		# -> temp scblk
	clrl	-(sp)		# ending character is 0
	pushl	uarg		# -> -u argument
	calls	$4,_cpys2sc	# copy string to scblk
	movab	tscblk,r10	# -> temp scblk
	jbr	erxit3		# return
#
2:	cmpl	$b$scl,(r10)	# if 2nd arg not string then
	jnequ	erxit1		#    return error
	tstl	sclen(r10)	# if null string then
	jeqlu	erxit4		#    return null string
	pushl	r10		# -> command string
	calls	$1,_dosys	# call to do system call
	jbr	erxit4		# return null string
#
3:	cmpl	$b$icl,(r10)	# if 2nd arg not integer then
	jnequ	erxit1		#    return error
	movl	tscblk,tscblk+sclen	# set max length of scblk
	pushab	tscblk		# push -> tscblk
	pushl	argv		# push -> pointers
	pushl	argc		# push number of args
	pushl	icval(r10)	# arg requested
	calls	$4,_arg2scb	# call to do real move
	tstl	r0		# if out of range then
	jlss	erxit6		#    fail
	jeqlu	erxit4		#    (if 0) return null string
	movab	tscblk,r10	# -> tscblk
	jbr	erxit3		# return
#
4:	tstl	cmdcnt		# if not invoked by #! then
	jeqlu	erxit6		#    fail
	movab	temp1,r9	# -> temp icblk
	movl	$b$icl,(r9)	# set integer block
	movl	cmdcnt,icval(r9)# set value
	jbr	erxit5		# return result
#
5:	cmpl	$b$scl,(r10)	# if 2nd arg not string then
	jnequ	erxit1		#    return error
	tstl	sclen(r10)	# if null string then
	jeqlu	erxit1		#    return error
	movl	tscblk,tscblk+sclen	# set max length of scblk
	pushab	tscblk		# push -> tscblk
	pushl	r10		# -> environment variable requested
	calls	$2,_rdenv	# fetch the environment variable
	tstl	r0		# if it couldn't be found
	jlss	erxit6		#    fail
	movab	tscblk,r10	# else return tscblk
	jbr	erxit3
6:	cmpl	$b$icl,(r10)	# if second arg not integer then
	jnequ	erxit1		#    return error
	pushl	icval(r10)	# get the value
	calls	$1,_sigtrap	# call the routine
	movab	temp1,r9	# -> temp icblk
	movl	$b$icl,(r9)	# set integer block
	movl	r0,icval(r9)	# set value
	jbr	erxit5		# return result
#
#
9:	pushl	hststr		# push length of host string
	pushab	hststr		# push -> host string scblk
	calls	$2,_gethost	# call to get host string
	tstl	hststr+sclen	# if null host string then
	jeqlu	erxit4		#    return null string
	movl	$hststr,r10	# -> host string
	jbr	erxit3		# return
#
#	sysid - return system id
#
	.globl	sysid
sysid:
	movl	$id1,r9
	movl	$id2,r10
	rsb
#
#	sysil - get input record length
#
	.globl	sysil
sysil:
	movl	fcrsz(r6),r6
	bgtr	0f
	mnegl	r6,r6
0:
	rsb
#
#	sysin - read input record
#
	.globl	sysin
sysin:
	pushr	$mr235
	pushl	fciob(r6)	# -> ioblk
	calls	$1,_osopen	# call to open file
	tstl	r0		# if open unsuccessful then
	jnequ	erxit3		#    take error exit
	pushl	r9		# -> scblk
	pushl	fciob(r6)	# -> ioblk
	pushl	fcrsz(r6)	# push record length
	bgtr	0f
	mnegl	(sp),(sp)	# if negative then make it positive
0:	pushl	fcrsz(r6)	# i/o mode - raw or line
	calls	$4,_osread	# call to do read
	cmpl	r0,$-1		# check for eof or input error
	jeql	erxit1		#   take eof exit
	jlss	erxit2		#   take error exit
	movl	r0,sclen(r9)	# set record length
	popr	$mr235
	addl2	$12,(sp)
	rsb
#
#	sysio
#
	.globl	sysio
sysio:
	pushr	$mr235
	bitl	$IO_ILL,tioblk+ioflg
	jnequ	erxit2		# if illegal then take error exit
	movl	r6,fcblkptr	# copy fcblk pointer for exit
#
#	fill in fcblk.
#
	tstl	tioblk+iotyp	# if no new fcb to build then
	jeqlu	iodon		#    done
	movl	$fcsize,fclen(r6)
	movl	tioblk+iolen,fcrsz(r6)
	movl	ioblkptr,fciob(r6)
	jnequ	iodon
	movab	fcsize(r6),fciob(r6)
#
#	fill in ioblk.
#
	movab	fcsize(r6),r6	# -> ioblk
	movl	$b$xrt,(r6)
	movl	$iosize,iolen(r6)
	movl	r9,iofnm(r6)
	clrl	iopid(r6)
	movab	iosize(r6),iobuf(r6)
	movl	tioblk+iofdn,iofdn(r6)
	movl	tioblk+ioflg,ioflg(r6)
#
#	if -f0 or -f1 specified then
#
#		for -f0 ensure that buffer is same as osint's
#
#		for -f1 no buffering should be done
#
	bitl	$IO_SYS,ioflg(r6)
	beqlu	9f
	cmpl	$1,iofdn(r6)
	blssu	9f
	beqlu	1f
	movl	$inpbf,iobuf(r6)
	jbr	9f
1:	clrl	iobuf(r6)
	bisl2	$IO_WRC,ioflg(r6)
9:
#
#	fill in bfblk
#
	movab	iosize(r6),r6	# -> bfblk
	movl	$b$xnt,(r6)
	movl	bfblksiz,bflen(r6)
	movl	tioblk+iopid,bfsiz(r6)
	clrl	bfrem(r6)
	clrl	bfoff(r6)
#
#	try to open the file
#
	movl	fcblkptr,r0
	pushl	fciob(r0)
	calls	$1,_osopen
	tstl	r0
	jneq	erxit2		# if open failed, indicate error
#
iodon:	movl	fcblkptr,r10
	clrl	r8
	popr	$mr235
	addl2	$8,(sp)
	rsb
#
#	sysmm - get more memory
#
	.globl	sysmm
sysmm:
	cmpl	topmem,maxmem	# if already at top of memory then
	blssu	0f
	clrl	r9		#    no more to be had
	rsb
0:				# else {alloc some more}
	pushr	$mr235
	pushl	meminb		# size in bytes of memory request
	calls	$1,_sbrk	# call to get memory
	popr	$mr235
	tstl	r0		# if memory obtained then
	blss	1f
	addl2	meminb,topmem	#    adjust current top
	movl	meminc,r9	#    set number of words in block
	rsb			#    return
1:	clrl	r9		# else nothing to get
	rsb
#
#	sysmx - get maximum size of created objects
#
	.globl	sysmx
sysmx:
	mull3	$4,maxsiz,r6
	rsb
#
#	sysou - output record
#
	.globl	sysou
sysou:
	pushr	$mr235
	pushl	fciob(r6)	# -> ioblk
	calls	$1,_osopen	# call to do open
	tstl	r0		# if open error then
	jnequ	erxit1		#    take error exit
	pushl	r9		# -> scblk
	pushl	fciob(r6)	# -> ioblk
	pushl	sclen(r9)	# record length
	pushl	fcrsz(r6)	# i/o mode - raw or line
	calls	$4,_oswrite	# call to do write
	tstl	r0		# if output error,
	jneq	erxit2		#    take error exit
	popr	$mr235
	addl2	$8,(sp)
	rsb
#
#	syspi - print on interactive channel (terminal)
#
	.globl	syspi
syspi:
	movl	$ttyiob,r11
	jbr	piprt
#
#	syspp - return print parameters
#
	.globl	syspp
syspp:
	movl	pagwid,r6
	movl	lnsppg,r7
	movl	sptflg,r8
	movl	defcas,kvcas
	rsb
#
#	syspr - print on standard output
#
	.globl	syspr
syspr:
	movl	$oupiob,r11
#
#	handle both syspi and syspr here.
#
piprt:
	pushr	$mr235
	bisl2	$IO_WRC,ioflg(r11) # briefly set no buffering
	pushl	r9		# -> scblk
	pushl	r11		# -> ioblk
	pushl	r6		# number characters
	pushl	$1		# line mode
	calls	$4,_oswrite	# call to do write
	bicl2	$IO_WRC,ioflg(r11) # back to buffering
	tstl	r0		# if output error then
	jneq	erxit1		#    indicate error return
	popr	$mr235
	addl2	$4,(sp)
	rsb
#
#	sysrd - read from standard input
#
	.globl	sysrd
sysrd:
	pushr	$mr235
	movl	$inpiob,ioblkptr
#
#	handle both sysrd and sysri here.
#
rdmrg:
	pushl	r9		# -> scblk
	pushl	ioblkptr	# -> ioblk
	pushl	r8		# read length
	pushl	r8		# line mode
	calls	$4,_osread	# call to do read
	cmpl	r0,$-1		# check for eof or input error
	jeql	rdeof		#    take eof exit
	jlss	erxit1		#    take error exit
	movl	r0,sclen(r9)	# set read length
#
#	check for 1st record of standard input coming from a file specified
#	on the command line. if all of these conditions are true, allow
#	the program to access any arguments following the file name.
#
	tstl	rdrec1		# if already ready record 1 then
	bnequ	rdskp		#    skip
	cmpl	$inpiob,ioblkptr
	bnequ	rdskp		# if sysri entry then skip
	incl	rdrec1		# indicate read 1st record from std input
	tstl	inpptr		# if not file from command line then
	beqlu	rdskp		#    skip
	cmpb	$'#,scstr(r9)	# if 1st char not # then
	bnequ	rdskp		#    skip
	cmpb	$'!,scstr+1(r9)	# if 2nd char not ! then
	bnequ	rdskp		#    skip
	subl3	inpcnt,argc,cmdcnt
	incl	cmdcnt		# compute # args after filename
	movl	$1,inpcnt	# reset input count
	brb	rdmrg		# ignore 1st record and try again
rdskp:
	popr	$mr235
	addl2	$4,(sp)
	rsb
#
#	come here to handle eof for both sysrd and sysri. if eof
#	is for sysrd, standard input, switch to next input file
#	if one exists.
#
rdeof:	movl	ioblkptr,r4	# -> ioblk
	tstl	iofdn(r4)	# if not file descriptor 0 then
	jnequ	erxit1		#    real eof
	pushl	inpptr		# push -> array of pointers
	pushl	inpcnt		# push size of areray
	calls	$2,_swcinp	# call to switch input files
	tstl	r0		# if more to read then
	jeqlu	rdmrg		#    read it
	jmp	erxit1		# else signal eof
#
#	sysri - read from interactive channel (terminal)
#
	.globl	sysri
sysri:
	pushr	$mr235
	movl	$ttyiob,ioblkptr
	jbr	rdmrg
#
#	sysrw - rewind file
#
	.globl	sysrw
sysrw:
	pushr	$mr235
	pushl	fciob(r6)	# -> ioblk
	calls	$1,_osopen	# call to do open
	tstl	r0		# if open error then
	jnequ	erxit1		#    take error exit
	movl	fciob(r6),r1	# -> ioblk
	bitl	$IO_PIP,ioflg(r1)
	jnequ	erxit2		# if pipe then rewind not allowed
	cmpl	iofdn(r1),$2	# if fd < 2 then
	jlssu	erxit2		#    rewind not allowed
	pushl	$0		# whence
	pushl	$0		# offset
	pushl	fciob(r6)	# -> ioblk
	calls	$3,_doset	# call to do set
	popr	$mr235
	addl2	$12,(sp)
	rsb
#
#	sysst - set file pointer
#
	.globl	sysst
sysst:
	pushr	$mr235
	pushl	fciob(r6)	# -> ioblk
	calls	$1,_osopen	# call to do open
	tstl	r0		# if file open error then
	jnequ	erxit3		#    return error
#
	movl	fciob(r6),r1	# -> ioblk
	bitl	$IO_PIP,ioflg(r1)
	jnequ	erxit4		# if pipe then set not allowed
	cmpl	iofdn(r1),$2	# if fd < 2 then
	jlssu	erxit4		#    set not allowed
#
	cmpl	$b$icl,(r7)	# if already integer then
	bnequ	0f
	movl	icval(r7),temp1	#    grab value
	brb	1f
0:	cmpl	$b$scl,(r7)	# else if not a string then
	jnequ	erxit1		#    error
	clrl	temp3		# clear scnint character count
	pushl	$temp3		# -> temp3
	pushl	sclen(r7)	# string length
	pushab	scstr(r7)	# -> string
	calls	$3,_scnint	# call to scan integer
	movl	r0,temp1	# and save
1:
#
	cmpl	$b$icl,(r8)	# if already integer then
	bnequ	0f
	movl	icval(r8),temp2	#    grab value
	brb	1f
0:	cmpl	$b$scl,(r8)	# else if not a string then
	jnequ	erxit1		#    error
	clrl	temp3		# clear scnint character count
	pushl	$temp3		# -> temp3
	pushl	sclen(r8)	# string length
	pushab	scstr(r8)	# -> string
	calls	$3,_scnint	# call to scan integer
	movl	r0,temp2	# and save
1:
#
	pushl	temp2		# whence
	pushl	temp1		# offset
	pushl	fciob(r6)	# -> ioblk
	calls	$3,_doset	# call to do set
	popr	$mr235
	addl2	$20,(sp)
	rsb
#
#	systm - get execution time so far
#
	.globl	systm
systm:
	pushr	$mr2+mr3
	movl	$tscblk+8,-(sp)	# -> times buffer
	calls	$1,_times	# call to do times
	movl	tscblk+8,r5	# get user time in 60ths
	mull2	$100,r5		#    mulitply by 100 to get 6000ths
	divl2	$6,r5		#    divide by 6 to get 1000ths
	popr	$mr2+mr3
	rsb
#
#	sysxi - exit from executing program
#
	.globl	sysxi
sysxi:
	tstl	r10		# if 0 instead of scblk then
	jeqlu	xilmod		#    try to write load module 
	pushr	$mr235
	cmpl	$b$scl,(r10)	# if not scblk then
	jnequ	erxit1		#    error
	pushl	r10		# push scblk pointer
	calls	$1,_doexec	# go do exit
	jmp	erxit2		# should never return
#
#	write load module
#
xilmod:	tstl	r5		# if r5 <= 0 then
	bgtr	0f
	pushr	$mr235		#    save regs for error exits
	jbr	erxit1		#    and take error exit
0:	pushr	$cmpreg		# else save all compiler regs
#
#	need to save stack contents, so that when load module is
#	invoked, stack can be recreated.
#
	subl3	sp,initsp,r0	# compute depth of stack
	cmpl	r0,tscblk	# if stack won't fit in tscblk then
	jgtru	xi2big		#    big trouble
	movl	sp,r0		# -> into real stack
	movab	tscblk+scstr,r1	# -> save stack area
1:	movl	(r0)+,(r1)+	# copy word of stack ...
	cmpl	r0,initsp	#    until hit top word
	blssu	1b
	movl	r1,lmodstk	# set top of saved stack
#
#	before creating the load module, we must relativize the
#	compiler cells that point into the stack.  We do this by
#	temporarily negating initsp, calling streloc, and then
#	restoring initsp.  After the load module has been written,
#	another call to streloc will restore the stack pointers.
#
	mnegl	initsp,initsp	# negate initsp so streloc will subtract
	jsb	streloc		# relativize the compiler cells
	mnegl	initsp,initsp	# restore initsp to its previous value
#
#	create a.out header in hststr scblk.
#
	addl3	$1023,dnamp,r1	# round current memory in use
	bicl3	$0x3ff,r1,-(sp)	#   to a multiple of the page size
	movab	hststr+scstr,r0	# -> a.out header block
	pushl	r0		#   which will be the other argument
	movl	$0413,(r0)+	# set magic number
	bicl3	$0x3ff,$_etext,r1 # get text size, rounded down
	movl	r1,(r0)+	#   and place it in a.out header
	subl3	r1,4(sp),(r0)+	# data size = total - text size
	clrl	(r0)+		# we will use no bss
	clrl	(r0)+
	clrl	(r0)+		# set starting address
	clrl	(r0)+
	clrl	(r0)+
#
#	call a workhorse c routine to actually write a.out file.
#	the amount of memory to write has already been pushed.
#
	calls	$2,_wrtaout	# call to write a.out
#
#	restore compiler cells to their previous values
#
	jsb	streloc		# unrelativize stack pointers
#
	tstl	r0		# if error creating a.out then
	blss	xi2big		#    return error
#
#	pop registers and set up call to sysej
#
	popr	$cmpreg		# restore all registers
	movl	r7,r10		# -> chain of fcbs
	clrl	r7		# set &CODE = 0
	jsb	sysej		# call to end run
#
#	if stack too big
#
xi2big:	popr	$cmpreg		# restore all regs
	pushr	$mr235		# push correct regs
	jbr	erxit2		# take error exit
#
#	error/ppm exits - pick up n-th word following jsb and return
#	to address contained in that word.
#
erxit1:
	popr	$mr235
	movl	(sp)+,r11
	jmp	*(r11)+
#
erxit2:
	popr	$mr235
	addl3	$4,(sp)+,r11
	jmp	*(r11)+
#
erxit3:
	popr	$mr235
	addl3	$8,(sp)+,r11
	jmp	*(r11)+
#
erxit4:
	popr	$mr235
	addl3	$12,(sp)+,r11
	jmp	*(r11)+
#
erxit5:
	popr	$mr235
	addl3	$16,(sp)+,r11
	jmp	*(r11)+
#
erxit6:
	popr	$mr235
	addl3	$20,(sp)+,r11
	jmp	*(r11)+
#
#	streloc - relocate stack pointers.  this routine adds
#	initsp to every cell whose address appears in strellst.
#
streloc:
	pushr	$mr0+mr1
	moval	strellst,r1	# start of list of thing to relocate
	jbr	strel1		# jump into the loop
strel0:	addl2	initsp,(r0)	# relocate a pointer
strel1:	movl	(r1)+,r0	# fetch a pointer to a cell
	jneq	strel0		# if zero, we're done
	popr	$mr0+mr1
	rsb	
#
#	option routines
#
#	optclr	clears a flag
#	opterr	signals an error
#	optfld	sets defcas to 0 for no folding
#	optnum	get numeric value
#	optset	set option value
#
#	optclr
#
optclr:	bicl2	optflg(r1),sptflg
	rsb
#
#	opterr
#
opterr:	pushr	$mr0+mr1+mr2+mr3+mr4+mr5
	pushl	$6
	pushl	$curopt
	pushl	$2
	calls	$3,_write
	popr	$mr0+mr1+mr2+mr3+mr4+mr5
	rsb
#
#	optinp
#
optinp:	tstl	inpptr		# if already processed input filenames then
	bnequ	opterr		#    error
	subl3	$4,r6,inpptr	# -> first input filename
	movl	r4,inpcnt	# set number of filenames
	movl	$1,r4		# done scanning options
	rsb			# return
#
#	optfld
#
optfld:	clrl	defcas
	rsb
#
#	optnum
#
optnum:	pushl	r0		# -> number
	jsb	getnum		# get number
	movl	(sp)+,r0	# -> byte past last digit
	movzbl	(r0),r2		# get byte past last digit
	bisb2	$040,r2		# fold to lower case
	cmpb	$ch$lk,r2	# if number followed by k then
	bnequ	0f
	mull2	$1024,r5	#    mulitply by 1024
	incl	r0		#    skip over k
0:	tstl	r5		# if number zero or negative
	bleq	opterr		#    treat as error
	movl	r5,*optflg(r1)	# store option
	rsb			# return
#
#	optoup
#
optoup:	cmpl	$2,r4		# if no option after -o then
	bgtru	opterr		#    error
	movl	(r6),r1		# -> output filename
	cmpb	$ch$mn,(r1)	# if filename starts with - then
	beqlu	opterr		#    error
	movl	(r6)+,oupptr	# save pointer to output filename
	decl	r4		# one less option to process
	rsb			# return
#
#	optset
#
optset:	bisl2	optflg(r1),sptflg
	rsb
#
#	optusr
#
optusr:	cmpl	$2,r4		# if fewer than 2 options then
	jgtru	opterr		#    can't have argument
	movl	(r6)+,uarg	# save -> argument
	decl	r4		# dec number of remaining options
	rsb
#
#	getnum
#
#	(sp)			-> string to convert
#	jsb	getnum
#	(sp)			-> char after last digit
#	(r5)			converted number
#
getnum:
	movl	4(sp),r7		# -> string
	clrl	r5		# clear accumulator
0:	cmpb	$060,(r7)	# if not a decimal digit then
	bgtru	1f		#    done with conversion
	cmpb	$071,(r7)	# 
	blssu	1f
	movzbl	(r7)+,r8	# load digit
	subl2	$060,r8		# remove  unnecessary bits
	mull2	$10,r5		# accum * 10
	addl2	r8,r5		# add in this digit
	brb	0b
1:	movl	r7,4(sp)		# return address of next byte
	rsb			# return
#
#	interface data area
#	-------------------
#
	.data	1
#
#	flags for compiler
#
	.set	errors,1	# send errors to terminal
	.set	prtich,2	# standard printer is terminal
	.set	nolist,4	# suppress compilation listing
	.set	nocmps,8	# suppress compilation statistics
	.set	noexcs,16	# suppress execution statistics
	.set	lnglst,32	# generate page ejects
	.set	noexec,64	# suppress program execution
	.set	trmnal,128	# terminal i/o association
	.set	stdlst,256	# standard listing (intermediate)
	.set	nohedr,512	# suppress sysid header
#
	.set	deflag,errors+nolist+nocmps+noexcs+trmnal+nohedr
#
#	option table
#
	.set	opttxt,0	# option characters
	.set	optflg,4	# option flag - flags or address
	.set	optrtn,8	# -> option processing routine
	.set	optsiz,12	# size in bytes of entry
#
opttbl:
	.ascii	"-f  "
	.long	0,optfld
#
	.ascii	"-e  "
	.long	errors,optclr
#
	.ascii	"-l  "
	.long	nolist,optclr
#
	.ascii	"-c  "
	.long	nocmps,optclr
#
	.ascii	"-x  "
	.long	noexcs,optclr
#
	.ascii	"-a  "
	.long	nolist+nocmps+noexcs,optclr
#
	.ascii	"-p  "
	.long	lnglst,optset
#
	.ascii	"-z  "
	.long	stdlst,optset
#
	.ascii	"-h  "
	.long	nohedr,optclr
#
	.ascii	"-n  "
	.long	noexec,optset
#
	.ascii	"-m  "
	.long	maxsiz,optnum
#
	.ascii	"-s  "
	.long	stksiz,optnum
#
	.ascii	"-d  "
	.long	datwds,optnum
#
	.ascii	"-i  "
	.long	meminc,optnum
#
	.ascii	"-o  "
	.long	0,optoup
#
	.ascii	"-u  "
	.long	0,optusr
#
curopt:	.ascii	"-   "
	.ascii	"?\n  "
	.long	opterr
	.align	2
#
#	standard input/output pointers
#
inpcnt:	.long	0
inpptr:	.long	0
oupptr:	.long	0
#
#	pointer to -u arg
#
uarg:	.long	0
#
#	save argc and argv from initial call
#
argc:	.long	0
argv:	.long	0
#
#	#! data areas
#
cmdcnt:	.long	0		# number of  command args
rdrec1:	.long	0		# read record 1 from std in flag
#
#	standard ioblks
#
inpiob:	.space	iobuf
	.long	inpbf		# -> input bfblk
	.long	0		# file descriptor
	.long	IO_INP|IO_OPN|IO_SYS
#
inpbf:	.space	bfsiz
	.long	4096		# buffer size
	.long	0		# remaining chars to read
	.long	0		# offset to next character to read
	.space	4096		# buffer
#
#
oupiob:	.space	iobuf
	.long	0		# no buffer
	.long	1		# file descriptor number
	.long	IO_OUP|IO_OPN|IO_SYS
#
#
ttyiob:	.space	iobuf
	.long	ttybf		# -> tty buffer input
	.long	2		# file descriptor number
	.long	IO_INP|IO_OUP|IO_OPN|IO_SYS
#
ttybf:	.space	bfsiz
	.long	258		# buffer size
	.long	0		# remaining chars to read
	.long	0		# offset to next char to read
	.space	258		# buffer
	.align	2
#
	.globl	_rzfcb
_rzfcb:	.long	0
#
fildes:	.long	0
pr_len:	.long	0
rd_len:	.long	0
lenfname: .long	0
ioblkptr: .long 0
bfblkptr: .long	0
bfblksiz: .long	0
fcblkptr: .long	0
tioblk:	.space	iosize
#
#	memory allocation variables
#
meminc:	.long	4*k		# increment in words for sbrk
meminb:	.long	0		# meminc * 4 (to get bytes)
datwds:	.long	256*k		# max size in words of dynamic area
basmem:	.long	0		# base of dynamic memory
topmem:	.long	0		# current top of dynamic memory
maxmem:	.long	0		# maximum top of dynamic memory
maxsiz:	.long	8*k		# maximum object size in words
stksiz:	.long	2*k		# stack size in words
initsp:	.long	0		# initial value of sp on entry to sec04
lowsp:	.long	0		# lowest legal sp value
#
#	default value for &case
#
defcas:	.long	1
#
#	values given to syspp for print parameters
#
lnsppg:	.long	60		# lines per page
pagwid:	.long	120		# page width
sptflg:	.long	deflag		# flags
#
#	flag that indicates that this is a load module. also, serves
#	the dual purpose of indicating size of saved stack.
#
lmodstk: .long	0
#
temp1:	.long	0
temp2:	.long	0
temp3:	.long	0
#
nulstr:	.long	0,0
#
tscblk:	.long	512,0
	.space	512
#
hststr:	.long	128,0
	.space	128
#
id1:	.long	0,id1l
	.ascii	"(0.0)"
id1e:
	.set	id1l,id1e-id1-8
	.align	2
#
id2:	.long	0,id2l
	.ascii	"VAX/UNIX Version"
id2e:
	.set	id2l,id2e-id2-8
	.align	2
#
ffscb:	.long	0,1
ffstr:	.byte	12
#
nlstr:	.ascii	"\n"
	.align	2
#
errfdn:	.long	0
#
#	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.
strellst:
	.long	flptr
	.long	stbas
	.long	gtcef
	.long	0		# end of list marker