/*-=-=-=-=-=-=-=-=-=-=-=- RCCL/RCI Notice of Copyright -=-=-=-=-=-=-=-=-=-=-*
 *									    *
 *	Copyright (C) 1987, 1991 by John E. Lloyd.			    *
 *									    *
 *	Information on copyright permissions, as well as a complete	    *
 *	disclaimer of warranty, is given in the file COPYRIGHT.		    *
 *									    *
 *-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/

#define PUSHR		mov r0,-(sp); mov r1,-(sp); mov r2,-(sp); \
			mov r3,-(sp); mov r4,-(sp); mov r5,-(sp);

#define POPR		mov (sp)+,r5; mov (sp)+,r4; mov (sp)+,r3; \
			mov (sp)+,r2; mov (sp)+,r1; mov (sp)+,r0;

.globl  __trap          / system call handler (not used anymore)
.globl  _zexit          / system exit
.globl  _main           / user entry point
.globl  _booted		/ set when the processor boots
.globl _freevecs
.globl _sysCpu		/ system cpu definition
.globl _onexit		/ set up an onexit routine
.globl  _panic		/ panic routine

.globl _dsptabend
.globl _dsptabbeg

	movpsw =    0106427     / move to psw (immediate)

/ list of built in trap locations.

	timeout	= 	 04	/ bad memory access (bus error or something)
	illegal	=	010	/ illegal instruction
	break =		014	/ break point trap instruction
	iott =		020	/ IOT instruction
	pwr = 		024	/ power fail
	emtt =		030	/ emulator trap instruction
	trap_ =		034	/ system call trap
	kb = 		060	/ keyboard interrupt
	pr =		064	/ console output interrupt
	clock =	       0100	/ line clock (BEVNT)

	endvec =      01000

/*
   program startup sequence. Go through 'boot' if we are starting 
   cold. Startup sequence initializes the stack and default vectors,
   turns on the clock if necessary (some LSI11's have the clock on
   by default), and then calls main.
*/

	.text
boot:				/ cold start address
	movpsw	
	00340			/ max priority
	mov	$1,_booted	/ set cold start flag so we know whats up
	jmp	start		/ go to start
start:
	movpsw			/ set priority once more for restarts
	00340

	mov	$boot,sp		/ set up stack
	mov	$_end,nextmem		/ set up memory allocator
	mov	$endvec	, _dsptabbeg	/ clear interrupt table
	mov	$endvec, _dsptabend	/ 
 
	clr	_freevecs		/ free interrupt vector blocks

	clr	r0			/ 
1:					/ clear all vectors
	cmp	r0, $endvec		/
	bhis	2f
	mov	r0, (r0)
	add	$2, (r0)+
	mov	$2, (r0)+
	br	1b
2:
	mov	$vtab, r0		/ 
1:					/ load default vectors
 	mov	(r0)+, r1		/
	beq	2f
	mov	(r0)+, (r1)+
 	mov	(r0)+, (r1)+
	br	1b
2:
	clr	-(sp)
	jsr	pc, _onexit		/ clear the 'on exit' routines
	tst	(sp)+
3:
	mov     $-1,-(sp)       
	mov     $sname,-(sp)    	/ simulate exec with dummy name
	mov     $1,-(sp)

	mov	sp,r0
	mov	(r0),-(sp)
	tst	(r0)+
	mov	r0,2(sp)

	movpsw  
	0			/ lower priority

	cmp	_sysCpu, $3	/ $3 = LSI11_73 (defined in sys.h)
	bne	1f
	bis	$0100,*$0177546	/ enable the clock (lsi11 73 only!)
1:
	jsr	pc,_main
	clr     (sp)            / if we get here, main has returned, so
	jsr     pc,_zexit	/ we just exit with code 0.
	cmp	(sp)+,(sp)+
	mov	r0,(sp)

/*
/ trap handler code. This isn't used anymore.
*/

traph:
	mov     r0,-(sp)
	mov     r1,-(sp)
	jsr     pc,__trap
	mov     (sp)+,r1
	mov     (sp)+,r0
	rti

.data

/*
  vtab (vector table). List of interrupt vectors, jump addresses,
  and psw values. The contents of the table are loaded into the
  actual vector space by the code above notated with 'load default
  vectors'.
*/

vtab:
	.word	timeout;	.word	panic1;		.word	0340
	.word	illegal;	.word	panic2;		.word	0340
	.word	break;		.word	panic3;		.word	0340
	.word	iott;		.word	panic4;		.word	0340
	.word	pwr;		.word	panic5;		.word	0340
	.word	emtt;		.word	panic6;		.word	0340
	.word	trap_;		.word	traph;		.word	0
	.word	clock;		.word	service;	.word	0340
	.word	0

.text

/*
/ dispsave: I/O device vector dispatch which does a register save before
/ calling the user routine. This saves the user routine from having to
/ save registers (since this is an interrupt, all registers must be saved)
*/

.globl _dispsave
_dispsave:
	PUSHR
	mov	12.(sp), -(sp)
	jsr	pc, *16.(sp)
	tst	(sp)+
	POPR
	add	$4, sp
	rti

/*
/ dispfast: I/O device vector dispatch which does NOT save registers. This
/ is useful for speed, if the defined vector routine either does not use
/ any registers, or saves what it does use.
*/

.globl _dispfast
_dispfast:
	jsr	pc, *2(sp)
	add	$4, sp
	rti

/*
/ basic clock service routine. The clock interrupt is tied to the BEVNT
/ line on the Q-bus. The routine increments a system time counter (kept
/ as a 32 bit word), and them looks to see if any clock AST's (timeout
/ routines or regular AST's) are pending, and calls them if they are.
*/

service:
	add	$1,_systime+2		/ increment system time.
	adc	_systime

	tst	timoutFxn		/ Check to see if timeout AST
	beq	2f			/ is pending. If so, check # counts
	tst	timoutCnt		/ remaining to fire time. If count
	beq	1f			/ is 0, fire the AST.
	dec	timoutCnt
	bne	2f
1:	
	PUSHR				/ Fire timeout AST. Save regsiters,
	mov	timoutFxn, r0		/ and call the AST function so the
	clr	timoutFxn		/ the AST has actually been cleared
	mov     timoutArg,-(sp)		/ by the time it is called.
	jsr     pc, (r0)
	tst	(sp)+
	POPR
2:	
	tst	clkastFxn		/ Test to see if a clock AST is
	beq	2f			/ pending, and if it should be fired.
	tst	clkastCnt		/ This is done in the same way as
	beq	1f			/ timeout ASTs are.
	dec	clkastCnt
	bne	2f
1:	
	PUSHR				/ Fire clock AST, same way as timeout
	mov	clkastFxn, r0		/ AST is fired.
	clr	clkastFxn
	mov     clkastArg,-(sp)
	jsr     pc, (r0)
	tst	(sp)+
	POPR
2:	
	rti				/ end of clock service routine

/*
/ Panic service routine. These panics are ``tripped'' as the result of 
/ different exceptions detected by the LSI11.
*/

panic1: mov     $1,code
	br      1f
panic2: mov     $2,code
	br      1f
panic3: mov     $3,code
	br      1f
panic4: mov     $4,code
	br      1f
panic5: mov     $5,code
	br      1f
panic6: mov     $6,code
	br      1f

1:      movpsw				/ mask out interrupts.
	0340			
	mov     r0,__regSave		/ save registers into a special
	mov     r1,__regSave+02		/ area where the can be printed 
	mov     r2,__regSave+04		/ by the panic routine.
	mov     r3,__regSave+06
	mov     r4,__regSave+010
	mov     r5,__regSave+012
	mov     sp,__regSave+014
	add	$4,__regSave+014	
	mov	(sp),__regSave+016

	mov     code,-(sp)
	jsr     pc,_panic		/ call panic routine.
	tst     (sp)+

	mov     __regSave+012,r5        / We should never get here, since
	mov     __regSave+010,r4        / the panic routine should halt, 
	mov     __regSave+06,r3		/ but we restore registers anyway.
	mov     __regSave+04,r2
	mov     __regSave+02,r1
	mov     __regSave,r0
	clr     code
	rti				/ end of panic service routine

/* System service routines available to the user. */

.globl _spl7
_spl7:	
	0106700			/ Set maximum priority and return
	movpsw			/ the previous priority
	0340
	rts   pc

.globl	_splx
_splx:				/ Set the priority specified by the
	0106700			/ argument and return the previous
	0106466			/ priority
	02
	rts   pc

.globl _wait
_wait:                          / wait for bus interrupt and return
	wait
	rts   pc

.globl _halt
_halt:				/ halt the CPU
	halt

.globl _restart
_restart:			/ restart the program
	jmp	start

.globl _setjmp
_setjmp:			/ standard set jump call
	mov	2(sp),r0	/ r0 <- buffer
	0106710			/ mfps 	(r0)
	mov	r1, 2(r0)	
	mov	r2, 4(r0)	/ save all registers in buffer
	mov	r3, 6(r0)
	mov	r4, 010(r0)
	mov	r5, 012(r0)
	mov	sp, 014(r0)
	add	$4, 014(r0)
	mov	(sp), 016(r0)
	clr	r0		/ return with 0 so we know this is setjmp
	rts	pc
	
.globl _longjmp
_longjmp:
	mov	2(sp),r0	/ r0 <- buffer
	0106410			/ mtps (r0)
	cmp	014(r0), sp	/ check for botch
	bhi	1f		/ make sure saved sp is greater than sp
	mov	$123., -(sp)
	jsr	pc, _zexit
1:
	mov	2(r0), r1	/ restore regsiters from buffer
	mov	4(r0), r2
	mov	6(r0), r3
	mov	8.(r0), r4
	mov	012(r0), r5
	mov	4(sp), 020(r0)
	mov	014(r0), sp
	tst	-(sp)
	mov	016(r0), -(sp)
	mov	020(r0), r0	/ return with argument to longjmp
	rts	pc

.globl _gettime	
_gettime:			/ get system time
	mov	_systime, r0
	mov	_systime+2, r1
	rts	pc

.globl _clrtime	
_clrtime:			/ clear system time
	0106700				/ save psw (with interrupt mask) in r0
	movpsw				/ mask interrupts
	0340
	clr	_systime
	clr	_systime+2
	0106400				/ restore psw (with interrupt mask)
	rts	pc

/* clkast. Set up a clock AST. */

.globl _clkast
_clkast:
	0106700				/ save psw (with interrupt mask) in r0
	movpsw				/ mask interrupts
	0340
	mov	clkastFxn, -(sp)	/ save any pending clock AST
	mov	4(sp), clkastFxn	/ set up new clock AST
	beq	1f
	mov	6(sp), clkastArg	/ if new AST is not NULL, set up 
	mov	8.(sp), clkastCnt	/ arguments and time count
1:	
	0106400				/ restore psw (with interrupt mask)
	mov	(sp)+, r0		/ return old clock AST, if any
	rts	pc

/* timeout. Set up a timeout AST. */

.globl _timeout
_timeout:
	0106700				/ save psw (with interrupt mask) in r0
	movpsw				/ mask interrupts
	0340
	mov	clkastFxn, -(sp)	/ save any pending timeout AST
	mov	4(sp), timoutFxn	/ set up new timeout AST
	beq	1f
	mov	6(sp), timoutArg	/ if new AST is not NULL, set up 
	mov	8.(sp), timoutCnt	/ arguments and time count
1:	
	0106400				/ restore psw (with interrupt mask)
	mov	(sp)+, r0		/ return old clock AST, if any
	rts	pc

/*
/ getmem. Memory allocator routine like UNIX sbrk(). Memory is allocated
/ from the last part of the bss segment, denoted by the location of
/ the symbol 'end'
*/

.globl _getmem
_getmem:
	mov	nextmem, r0		/ compute where the requested memory
	add	2(sp), nextmem		/ allocation will take us. If it goes
	cmp	nextmem, _memlimit	/ beyond the memory pool, return 0.
	blos	1f
	mov	r0, nextmem
	clr	r0
	rts	pc
1:
	bit	$1, nextmem		/ Align the memory pointer on a word
	beq	1f			/ boundary.
	inc	nextmem
1:
	rts	pc			

.data
.globl _memlimit
.globl _end

_memlimit:
	.word	0x8000
nextmem:
	.word	_end
.text

/*
/ pushr. Routine to save registers on the stack. When this routine
/ returns, nothing has changed except that stack has grown by 6
/ words which contain a dump or registers 0 - 5.
*/

.globl	_pushr			
_pushr:
	mov	r4, -(sp)
	mov	r3, -(sp)
	mov	r2, -(sp)
	mov	r1, -(sp)
	mov	r0, -(sp)
	mov	012(sp), -(sp)	
	mov	r5, 014(sp)
	rts	pc

/* popr. Restores registers 0 - 5 from the stack. */

.globl _popr
_popr:
	mov	014(sp), r5
	mov	(sp)+, 012(sp)
	mov	(sp)+, r0
	mov	(sp)+, r1
	mov	(sp)+, r2
	mov	(sp)+, r3
	mov	(sp)+, r4
	rts	pc

.bss
.globl	savr5		/ C run time save area
savr5:	.space 2
.data
	.space 2	/ loc 0 for ID; null ptr points here.
sname:  .asciz "main"	/ fake name for calling exec.
	.align 2
.globl __regSave	/ register save area for panic
__regSave:
	.space 16.	/ space for registers
.globl _systime	/ system time regsiter
_systime:  0	
	   0
clkastFxn: 0		/ clkast function
clkastArg: 0		/ clkast argument	 
clkastCnt: 0		/ clkast count	
timoutFxn: 0		/ timout function
timoutArg: 0		/ timout argument	 
timoutCnt: 0		/ timout count	
ctimeout:  0		/ timeout counter
code: 0
_booted: 0		/ determine whether we have started cold or restarted
