*****************************************************************
*                                                               *
*               UNIX First-level Interrupt Handler              *
*                                                               *
*               Perkin-Elmer 7/32, 8/32, 3220, 3240             *
*                                                               *
*****************************************************************
	entry   stkovflo
	extrn   trap
	extrn   kisa,uisa
	extrn   runrun
	extrn   u

* PSW bit definitions

ps.flm  equ     y'40000'        floating-point masked (3200)
ps.iip  equ     y'20000'        interruptible instruction in progress (3200)
ps.wait equ     x'8000'         wait state
ps.io   equ     x'4000'         immediate interrupt mask
ps.mm   equ     x'2000'         machine malfunction interrupt mask
ps.af   equ     x'1000'         arith fault interrupt mask
ps.il   equ     x'0800'         multi level interrupts (8/32 & 3200)
ps.rp   equ     x'0400'         memory relocation / protection
ps.sq   equ     x'0200'         system queue service mask
ps.prot equ     x'0100'         protect mode
ps.ureg equ     x'00f0'         user register set

* PSW definitions

ps.user equ     ps.io+ps.mm+ps.af+ps.rp+ps.prot+ps.ureg
ps.idle equ     ps.wait+ps.io+ps.mm+ps.af+ps.rp+ps.ureg
ps.kern equ     ps.io+ps.mm+ps.af+ps.rp+ps.ureg
ps.disb equ     ps.mm+ps.af+ps.rp+ps.ureg
ps.trap equ     ps.mm+ps.af

* Register definitions

r0      equ     0
r1      equ     1
r2      equ     2
r3      equ     3
r4      equ     4
r5      equ     5
r6      equ     6
r7      equ     7
r8      equ     8
r9      equ     9
ra      equ     10
rb      equ     11
rc      equ     12
rd      equ     13
re      equ     14
rf      equ     15
sp      equ     r7

*****************************************************************
*                                                               *
* call:         interface to C interrupt handlers               *
*                                                               *
*       re-rf   - old PSW                                       *
*       rd      - current (interrupt) PSW status                *
*       rc      - interrupt routine address                     *
*       rb      - interrupt routine PSW status                  *
*       r3      - device status ( or SVC arg address )          *
*       r2      - device address ( or trap code )               *
*                                                               *
*****************************************************************

	pure
	align   2
	entry   call
call    equ     *

    ifnz PIC
	bal     r6,rdpic        get last pic timing interval
    endc

* if trap from user mode, switch to kernel address space

	thi     re,ps.prot      user mode?
	bz      kernel          no - kernel already

    ifnz PIC
	am      r5,utime        add new interval to user time
    endc

	l       r1,kisa         kernel seg regs
	bal     r6,addrsw       switch address space
	b       nkernel

* else trap from kernel mode -- get stack pointer from register set f

kernel  equ     *

    ifnz PIC
	thi     re,ps.wait
	bz      kernel2         not in wait
	am      r5,itime        if wait add current interval to idle time
	b       kernel3
kernel2 am      r5,sytim        if kernel and not wait increment sys time
kernel3 equ     *
    endc

	st      rd,nr.psw       set up resume psw
	la      r1,nkernel
	st      r1,nr.psw+adc
	lr      r1,rd           current psw
	ohi     r1,ps.ureg      switch to reg set f
	epsr    r0,r1
	st      sp,ksp          save stack pointer
	lpsw    nr.psw          back to reg set 0
nkernel equ     *

* enable memory relocation / protection

	ohi     rd,ps.rp        enable relocation
	epsr    r0,rd

* if stack pointer is out of range, set it to top of u area and force a panic

	l       sp,ksp          get kernel stack pointer
	ci      sp,u            below bottom of u area?
	bl      badsp
	c       sp,maxsp        above top of u area?
	bnp     goodsp
badsp   l       sp,maxsp        set to top of u area
	la      rc,stkovflo     force a panic
goodsp  equ     *

    ifnz        M3200
* save microcode scratchpad registers if necessary (3200 only)

	ti      re,ps.iip               interruptible instruction in progress?
	bz      nsiip                     no - skip
	shi     sp,16*adc               space on stack for scratchpad regs
	psf     5,0(sp)                 save scratchpad regs
nsiip   equ     *
    endc

* save psw & status on kernel stack

	shi     sp,14*adc       space for 14 words
	stm     re,11*adc(sp)   save old psw
	st      rb,13*adc(sp)   save new psw
	st      r2,0(sp)        save dev code
	st      r3,adc(sp)      save status

* switch to user register set, and save regs

	st      rc,nr.intp      save routine address
	st      sp,ksp          save stack pointer
	ohi     rd,ps.ureg      switch to user regs
	epsr    r0,rd
	stm     r0,nr.regs      save all regs
	l       sp,ksp          restore stack pointer
	lm      r8,nr.regs      stack regs r0-sp
	stm     r8,2*adc(sp)

* reload user high regs ( to be saved by standard c linkage )
*  and call c trap handler

	lm      r8,8*adc+nr.regs        restore regs r8-rf
	st      rf,10*adc(sp)   stack link reg
	l       r1,nr.intp      trap routine address
	l       r0,13*adc(sp)   new psw
	epsr    r2,r0
	balr    rf,r1           call trap routine

* on return from trap routine, check whether higher-priority process
* is now ready to run

	l       r1,11*adc(sp)   old psw
	thi     r1,ps.prot      user mode ?
	bz      noswtch         no - don't switch kernel process

switch  equ     *
	li      r0,ps.disb      disable interrupts
	epsr    r1,r0
	lb      r1,runrun       higher-priority process waiting?
	lr      r1,r1
	bz      nswtch          no - restore interrupted process
	li      r0,ps.kern      enable interrupts
	epsr    r1,r0
	li      r0,12           trap 12 is give up cpu
	st      r0,0(sp)
	bal     rf,trap         call trap routine again
nswtch  equ     *

* restore status of interrupted process

noswtch equ     *
	li      r0,ps.disb      disable interrupts
	epsr    r1,r0
	l       rf,10*adc(sp)   restore link reg
	stm     r8,8*adc+nr.regs        save r8-rf
	lm      r8,2*adc(sp)    save r0-sp
	stm     r8,nr.regs
	lm      re,11*adc(sp)   old psw
	ahi     sp,14*adc       pop stack

    ifnz        M3200
* restore microcode scratchpad registers (3200 only)

	ti      re,ps.iip       interruptible instruction interrupted?
	bz      noriip            no - skip
	psf     6,0(sp)         restore scratchpad regs
	ahi     sp,16*adc       pop stack
noriip  equ     *
    endc

* if previous mode was user, switch back to user address space

	thi     re,ps.prot      user mode?
	bz      kernel1         no - stay in kernel mode
	epsr    rd,rd           current psw
	nhi     rd,x'ffff'-ps.rp        disable relocation
	epsr    r0,rd
	l       r1,uisa         user seg regs
	bal     r6,addrsw
kernel1 equ     *

* return to previous status

	st      sp,ksp          save kernel stack pointer
	ni      re,y'ffffffff'-ps.wait  turn off 'wait' bit
	stm     re,nr.psw       save return psw

    ifnz PIC
	bal     r6,rdpic        get last pic timing interval
	am      r5,sytim        add interval to sys time
    endc

	lm      r0,nr.regs      restore all regs
	lpsw    nr.psw          back to previous mode

* stack overflow panic - called instead of 'trap' or interrupt routine
*       to force a panic when kernel stack overflows

stkovflo equ    *
    ifnz M3200
	dc      y'88008800'     breakpoint on 3200, ill instr on other only
    else
	b       *               hang in a loop
    endc

	impur
	align   adc

* Non-reentrant save area for use while switching register sets

	align   adc
nr.intp das     1               interrupt handler address save
	align   adc*2
nr.psw  das     2               psw save
nr.regs das     16              register save

* Kernel mode stack pointer save

	entry   ksp,maxsp
	align   adc
ksp     dc      0       save stack pointer during user mode execution
maxsp   dc      0       pointer to top of stack segment (initialized by start)

*****************************************************************
*                                                               *
* addrsw:       Switch Address Space Mapping                    *
*                                                               *
* input: r1 = &(new segment table descriptor)                   *
*        r6 = return address                                    *
*                                                               *
* Must be called with relocation & interrupts disabled          *
*                                                               *
*****************************************************************

	pure
	align   2
	entry   addrsw
addrsw  equ     *
    ifnz        M3200
	psf     1,0(r1)         lpstd   process table descriptor
	psf     2,4(r1)         lsstd   shared table descriptor
    else
	extrn   macregs
	l       r1,0(r1)        address of seg registers
	ni      r1,y'fffff'     address part
	lhi     r4,15*adc       start at last reg
seglp   equ     *
	l       r0,0(r1,r4)     next seg value
	st      r0,macregs(r4)  store in mac reg
	sis     r4,adc          back up
	bnm     seglp           repeat for all seg regs
    endc
	br      r6

*****************************************************************
*                                                               *
*               Floating-point Register Save / Restore          *
*                                                               *
*****************************************************************

	entry   savfp,restfp

* save floating point registers

savfp   equ     *
  ifnz  FPREGS!DPREGS
	l       r1,0(sp)        fp save area in u
    ifnz  FPREGS
	stme    r0,0(r1)        save single-precision regs
    endc
    ifnz  DPREGS
	stmd    r0,8*adc(r1)    save double-precision regs
    endc
  endc
	br      rf

* restore floating point registers

restfp  equ     *
  ifnz  FPREGS!DPREGS
	l       r1,0(sp)        fp save area in u
    ifnz  FPREGS
	lme     r0,0(r1)        save single-precision regs
    endc
    ifnz  DPREGS
	lmd     r0,8*adc(r1)    save double-precision regs
    endc
  endc
	br      rf

    ifnz        PIC

*****************************************************************
*                                                               *
* rdpic:        Get length of last precision timing interval    *
*                                                               *
*       r5      - returned value                                *
*       r6      - return address                                *
*       r0-r1   - work regs                                     *
*                                                               *
*****************************************************************

	pure
	align   2
	extrn   clockaddr
	entry   rdpic
rdpic   equ     *
	lb      r0,clockaddr    address of pic
	rhr     r0,r1           read interval
	l       r5,pictim       previous count
	sr      r5,r1           real current clock interval
	bp      ras1            positive?
	ai      r5,1000           no - must have wrapped around
ras1    st      r1,pictim       save current count
	br      r6              return

* PIC timing information

	impur
	align   adc
	entry sytim
pictim  dac     1000    last PIC interval count
sytim   dac     0       accumulated system time | MUST BE
utime   dac     0       user time               |   KEPT
itime   dac     0       idle time               | TOGETHER
    endc
	db      c'@(#)call.s	3.4',0
	pure
	align adc*2
	impur
	align adc*2

