/*
 * 5799-WZQ (C) COPYRIGHT IBM CORPORATION  1986,1987,1988
 * LICENSED MATERIALS - PROPERTY OF IBM
 * REFER TO COPYRIGHT INSTRUCTIONS FORM NUMBER G120-2083
 */
/* $Header:locore.c 12.0$ */
/* $ACIS:locore.c 12.0$ */
/* $Source: /ibm/acis/usr/sys/ca/RCS/locore.c,v $ */

	.data
#if !defined(NO_RCS_HDRS)
rcsidlocore:	.asciz "$Header:locore.c 12.0$"
#endif

 #  This module contains the low level code for 4.2 BSD Unix (tm) running on the
 #  This is a modification of the Original YORKTOWN port for a PC/XT co-processor
 #  configuration by IRIS at Brown University.
 #  The major functions of this code are:
 #
 #  . Initialize the CPU after a "power on reset"
 #  . Intercept program checks, machine checks, and trap instructions.
 #  . Field and route processor interrupts.
 #  . Provide subroutines to access the special machine registers.
 #  . Provide highly efficient subroutines for some common functions.
 #
 # An interface it provided to a debugger (originally "RDB") that
 # co-operates with the kernel in order to provide a debugging
 # environment with the kernel.
 # the debugger is linked to run in real mode, while the kernel is linked
 # for virtual mode. the addresses are allocated as follows:
 # (when the debugger is present)
 # 0-ff          not really used much (copyright notice)
 # 100-1af       interrupt vectors (shared)
 # 1b0-200       unused
 # 200-800       not used 
 # 800-17ff       POST (must be preserved for control-alt-del to work)
 # 1800-2000     debugger stack
 # 2000-4fff     kernel locore code
 # 5000-9fff     kernel symbol table
 # a000-24fff     debugger (DEBUG, used to be RDB)
 # ...           rest of the kernel (C code)
 #
 # the interrupt vectors are shared as follows:
 # 1. the kernel initializes then statically
 # 2. the debugger takes over 100, 170, 180, 190, 1a0 (pseudo vector)
 # 3. the kernel takes back   100, 170, 180, 190 and remembers the
 #       debuggers values so that it can pass control to the debugger
 #         
 # when the debugger is not present then the original static values
 # remain in effect.
 #         

         .text                   # everything goes into text (even data)
real0:	# this had better be the first location in .text!
	/*
	 * provide a null at location 0 (and a null pointer) in case
	 * anyone needs it
	 */
	.long 0
	/*
	 * The Copyright message has to be within the first 15000 bytes
	 * of the kernel.  We place it right at the front, since we have
	 * 0x100 == 256 bytes of unused space before the first assigned
	 * low memory locations, and the Copyright message is only 102
	 * (decimal) bytes long.
	 */
	.globl _coibm	/* So one can do printf(coibm); from, say, startup */
#ifdef ATR
#include "../ca_atr/copyright.s"
#else
/* make sure there is a copyright */
_coibm:	.ascii	"5799-WZQ (C) Copyright IBM Corporation 1986,1987\n"
	.ascii	"All Rights Reserved\n"
	.asciz	"Licensed Materials - Property of IBM\n"
#endif

 # locore.h defines constants etc. but does not generate any code/data.
#include "../machine/machparam.h"
#include "../machine/scr.h"
#include "../machine/mmu.h"
#include "../machine/pte.h"
#include "assym.s"
#include "../machine/locore.h"
#include "../machine/led.h"	/* for LED values */
#include "../machine/frame.h"	/* for NCS defines */
#include "../machine/fpa.h"	/* for FPA defines */
#include "../machine/cpu.h"	/* for CPU types */
#ifdef ROMPC
#include "../machine/ioim.h"
#endif

         .globl .oVncs
         .set .oVncs , 0
 # lointpsw sets up the interrupt vectors and leaves space for POST,
 # debugger etc.

#include "../machine/rdb.h"

#include "../machine/lointpsw.s"
#ifdef ATR
 # The ATR version uses the POST area (since our post does not use this region)
 # to pass certain hardware paramaters from the PC code to Unix.

	.org real0 + POST_START
		.globl	_config
_config:
_mem_size: 	.short 0     	# Memmory size in MB, 
_pcif_base:	.short 0     	# PC Port Address of PCIF registers
_cbcb_addr:	.long  0	# Address of cbcb in PC memory
_equip: 	.short 0	# The PC's equip byte.    
_ega_info:	.short 0	# Switch settings for the EGA
_memconfig:	.long  0 	# memconfig register initialized by PS/2 
 # Hard disk paramater list drive 0
_hdinfo:	.short 0	# Num cyls per drive
		.short 0	# Num of tracks per cyl
		.short 0	# Num of sectors per track 
		.short 0	# starting cyl of 4.2 partition 
		.short 0	# Num of cyls in 4.2 partition 
		.short 0	# drive status flag
 # Hard disk paramater list drive 1
		.short 0	# Num cyls per drive
                .short 0	# Num of tracks per cyl
		.short 0	# Num of sectors per track 
		.short 0	# starting cyl of 4.2 partition 
		.short 0	# Num of cyls in 4.2 partition 
		.short 0	# drive status flag
#endif ATR
 #
 # the ROS builds a POST (Power On Self Test) table at 0x800 that must
 # be intact for the 'control-alt-pause/break'  
 # to work. The loaders protect this area if one leaves room (zeros)
 # in that area.
 #
 # Move past the POST
	.org real0 + KERNEL_START

 #       JUMP TO the REAL Locore START .....  WHEREEVER THAT MAY BE
 #
 #       following code normally lands at 0x1000 (on Model).
 #
	.globl _start
#ifndef RDB
_start:
#endif
	.using	real0,r0	# tell the assembler to use r0 as base

	cal16	r15,start	# use 'cal16' to ignore segment number
	balr	r15,r15		# transfer control to real 'start'
origin: .short 0x8080            # trap for undefined external references


#ifdef ATR
	.org	real0 + RESTART_ADDR
 # for restarting suspended unix processor
	.globl	_restart_block
_restart_block:	.long	0
		.long	0
#endif ATR

#ifdef RDB
	.org	real0+RDB_STACK	# leave room for debugger stack
#endif RDB
	.align	2

	/* Should be in h/buf.h! */
#ifdef SMALLMEM
#define MAXBUFFERPAGES 128	/* 256K worth of buffers should be enough */
#else
#define MAXBUFFERPAGES 512	/* 1MB worth of buffers should be enough */
#endif
 
 #==================| hardware related constants |==================|
 
#define SEGSIZE  0x10000000		/* 256M bytes per mmu segment */
#define PAGESIZE  2048			/* 2K bytes per mmu page */
#define LOG2PAGESIZE 11		/* shift count for 2K page size */

#define MAXPHYSMEM  0x1000000		/* (bytes) Not more than 16M real mem */
 
#define UPPER 0x10000			/* scale factor for UPPER instructions */

 #==============| HAT-IPT - Located in the last pages of memory |=============|
 
#define HATIPTSIZE 16                  /* size of one HAT IPT entry */
#define LOG2HATIPTSIZE  4               /* shift for HAT IPT entry */
 
 #  fields of a HAT-IPT entry:
 
#define SYS_ADDRTAG MMU_SID_SYSTEM*P1PAGES /* k1,k2 = 0,0; sid = system */

#define IPTADDRTAG  0                   /* offset of IPT ADDR TAG field */
#define IPTHATPTR  4                    /* offset of IPT HAT PTR field */
#define IPTIPTPTR  6                    /* offset of IPT IPT PTR field */
#define IPTLOCK  8                      /* offset of IPT LOCK WORD field */

 #=====================| constants and variables |======================|

	.globl	_u
.set	_u,	USTRUCT		# address of u area for debugging
	.globl	UCORE		# address of U area in core file
.set	UCORE,	UPAGES*NBPG-USIZE
	.globl	_USER_FPM
.set	_USER_FPM, USER_FPM	# symbol for location of user's FP_MACH
         .globl _MMU_HATIPT
	 .globl _MMU_HASHMASK
         .globl _firstaddr
	 .globl _maxmem
	 .globl _physmem
	 .globl _freemem
         .globl _nrpages
         .globl _noproc
	 .globl _runrun
	 .globl _whichqs
	 .globl _qs
	 .globl _boothowto
	 .globl _bootdev
	 .globl	_memconfig
	 .globl	_delay_addr
	.globl	__trap
	.globl	_trap
	.globl	_cnt
	.globl	_ser
	.globl	_sear
#ifdef ATR
	.globl _mem_size
	.globl _pcif_base
	.globl _cbcb_addr
	.globl _equip
	.globl _ega_info
	.globl _hdinfo
	.globl _pcif_addr	# Address of PCIF space
	.globl _current_128_w	# Current value of the ROMP->PC windows.
	.globl _current_512_w
	.globl _pcif_512_fw
	.globl _pcif_128_fw
	.globl _pcif_128_hw
	.globl _pcif_128_b
	.globl _pcif_io_b
	.globl _pcif_io_hw
	.globl	_ioim2
#endif ATR
 # create some symbols for programs that want to verify that they 
 # have the correct struct user and struct proc definitions
 # see also userSIZE and procSIZE in param.c for variables with
 # these values.
	.globl	userSIZE
	.set	userSIZE,USIZE
	.globl	procSIZE
	.set	procSIZE,PSIZE
         .align 2		# *tjm
#ifdef ROMPC
 # place the kernel exception stack here where it is in read-write
 # memory
	.globl	_ecrstack
_ecrlimit:
	.fill	4*4,4,0		# room for 4 exceptions 
_ecrstack:
	.long	0		# spacer
#define ECR_STACK (_ecrstack-real0)
#endif ROMPC
 # locore locations for various registers important to dumps
 # placed here as pages 0 and 1 are read-only to kernel
	.globl _dumpregs
	.globl _dumpsysregs
	.globl _dumpmmregs
_dumpsysregs:
	.fill	16,4,0		# room for 16 system control registers
_dumpregs:
	.fill	16,4,0		# room for 16 general registers
_dumpmmregs:
	.fill	0x19,4,0	# room for first 0x19 MMU registers
	.globl _scb
	.set _scb,_dumpregs+8	# just so adb user can find our stack
 # 
 #  Masterpaddr is the p->p_addr of the running process on the master
 #  processor.  When a multiprocessor system, the slave processors will have
 #  an array of slavepaddr's.
 #
	.globl	_masterpaddr
_masterpaddr:
	.long	0

 #
 # note: autoconfig will change the value of _trap to intercept
 # traps during autoconfig that would confuse the regular
 # trap routine.
 # it will reset it back before returning control to the caller
 #
__trap:	.long	_trap		# address of trap routine
 # hatipt is used in non-translated code, _MMU_HATIPT when translate on
 # note: following 16 words are set to zero at 'start' time.
 #

hatipt:		.long	0		# real address of hatipt
_MMU_HATIPT:	.long	0		# virtual address of hatipt
_MMU_HASHMASK:	.long	0		# mask for virt addr hashing
_nrpages:	.long	0		# number of pages installed
_firstaddr:	.long	0		# first page past static kernel
_maxmem:	.long	0		# top of available memory (pages)
_physmem:	.long	0		# size of installed memory (pages)
_freemem:	.long	0		# size of allocatable memory (pages)
_noproc:	.long	0		# no process running now
_runrun:	.long	0		# something to run
_whichqs:	.long	0		# "queue non-empty" flags
_qs:     .fill 2*4*16,4,0		# 16 queue headers: RLINK, LINK
_boothowto:	.long	0		# passed from boot to init (in r11)
_bootdev:	.long	0		# passed from boot 
#ifdef IBMRTPC
_memconfig:	.long	0x00000000      # memory configuration info
#endif IBMRTPC
_delay_addr:	.long	0x00000000	 
_badsp:		.long	0		# for recursive stack problems

#ifdef FPA
		.globl	_fpa_curreg
_fpa_curreg:	.long	FPA_NONE	# current fpa register set 
#endif
#ifdef	ROMPC
		.globl	_mc881_proc	# proc using 68881
_mc881_proc:	.long	0		# nobody using it now
#endif	/* ROMPC */
_ser:		.long	0		# SER from last trap
_sear:		.long	0		# SEAR from last trap
#ifdef ATR
 # For the ATR version of the system we leave it to the PC to simulate
 # the memory configuration register on the RT. We do this by defining
 # the variable _memconfig in the PC to ROMP data area located at
 # 0x800. The PC code will initialize this variable to a value that
 # the RT would use for the memory (ie 0x000000FD for 2MB memory.)
_pcif_addr:	.long	0x00200000	# Address of PCIF (default at 2M)
_current_128_w:	.long	0		# current value of ROMP->PC windows.
_current_512_w:	.long	0
_pcif_512_fw:	.long	0		# Address of ROMP->PC 512K window
_pcif_128_fw:	.long	0		# Address of ROMP->PC 128K FW Window
_pcif_128_hw:	.long	0		# Address of ROMP->PC 128K HW Window
_pcif_128_b:	.long	0		# Address of ROMP->PC 128K Byte Wind
_pcif_io_b:	.long	0		# Address of ROMP->PC byte I/O window
_pcif_io_hw:	.long	0		# Address of ROMP->PC half word I/O 
pcif_base:	.long	0		# REAL address of PCIF
_ioim2:		.long	0
#endif ATR
_cnt:		.fill 32,4,0		# meter counts (see ../h/vmmeter.h)
	.globl	_intrcnt		# interrupt counts 
	.globl	_eintrcnt		# end of counts
_intrcnt:	.fill	26,4,0
_eintrcnt:	.long	0
	.globl	_cpu
	.globl	_cpu_model
_cpu:	.long	CPU_RTPC		# assume RTPC
_cpu_model: .long	CPU_SGP		# assume old cpu
	 .globl	_dma_in_use
_dma_in_use:	.byte	0		# flag for DMA 
		.align	2
	 
 
callmain_ps:
         .long  _.main              # new iar
         .short TRANS_ICS+INT_PRI0  # new ics: xlate on, int level 0
         .short 0                   # reserved
go_callslih:
         .long  callslih            # new iar
         .short TRANS_ICS           # new ics: xlate on
         .short 0                   # reserved
go_afterslih:
         .long  afterslih-real0     # new iar
         .short NOTRANS_ICS+INT_PRI1# new ics: xlate off, PRIO 1
         .short 0                   # reserved
go_ps:
         .long  go                  # new iar
         .short TRANS_ICS+INTMASK_ICS # new ics: xlate off, interrupts off
         .short 0                   # reserved
realgo_ps:
         .long  realgo-real0            # new iar
         .short NOTRANS_ICS+INTMASK_ICS # new ics: xlate off, interrupts off
         .short 0                       # reserved
#ifdef ROMPC
go2_ps:
         .long  go2                  # new iar
         .short TRANS_ICS+INTMASK_ICS # new ics: xlate off, interrupts off
         .short 0                   # reserved
realgo2_ps:
         .long  realgo2-real0            # new iar
         .short NOTRANS_ICS+INTMASK_ICS # new ics: xlate off, interrupts off
         .short 0                       # reserved
#endif ROMPC
fault_ps:
         .long  fault               # new iar
         .short TRANS_ICS+INTMASK_ICS # new ics: xlate on, interrupts off
         .short 0                   # reserved
svc_ps2:
         .long  _svc                 # new iar
         .short TRANS_ICS+INTMASK_ICS+INT_PRI7 # new ics: xlate on, interrupts off
         .short 0                   # reserved
zzswun_ps:
         .long  zzswun-real0        # new iar
         .short NOTRANS_ICS+INTMASK_ICS # new ics: xlate off, interrupts off
         .short 0                   # reserved
zzswxl_ps:
         .long  zzswxl              # new iar
         .short TRANS_ICS+INTMASK_ICS # new ics: xlate on, interrupts off
         .short 0                   # reserved
csegxlat_ps:
         .long  csegxlat            # new iar
         .short TRANS_ICS           # new ics: xlate on
         .short 0                   # reserved
flipxps:	# for flipping xlate bit 
	.long	0		# filled in with return address
	.short	0,0		# ics also filled in
 
 #  register save areas:
 
low_ps     :.long 0,0           # addressable space for lps
low_save0  :.long 0             # save for r0
low_save1  :.long 0             # save for r1
low_save2  :.long 0             # save for r2
low_save3  :.long 0             # save for r3
low_save4  :.long 0             # save for r4
low_save5  :.long 0             # save for r5
low_save6  :.long 0             # save for r6
low_save7  :.long 0             # save for r7
low_save8  :.long 0             # save for r8
low_save9  :.long 0             # save for r9
low_save10 :.long 0             # save for r10
low_save11 :.long 0             # save for r11
low_save12 :.long 0             # save for r12
low_save13 :.long 0             # save for r13
low_save14 :.long 0             # save for r14
low_save15 :.long 0             # save for r15

low_savesp :.long 0		# save for bad sp
#ifdef VTL_RELOCATE
pck_save9  :.long 0             # save for r9 for pck
pck_save10 :.long 0             # save for r10 for pck
pck_save11 :.long 0,0,0,0,0     # save for r11,12,13,14,15 for pck
#endif
flipsave   :.long 0,0,0,0       # save for flipmode subroutine

 # Signal trampoline code
sigcode: svc   139(r0)              # template moved to u area for signals

 # External I/O Interrupt Save Area Storage
 # Moved here from lointr for addressability
	.align 2
iosavep:	.long	iounmask-real0	# Interrupt Level Stack Pointer
					# (Initially Points to iounmask)
iosavea:	.space 19*IOSAVEL 	# Interrupt Level Save Area Stack
iounmask:	.long	0xFFFFFFFF	# Main Level "Interrupt Enable" Mask
					# (Must Immediately Follow iosavea)
ioenable:	.long   SETIO		# Master Interrupt Enable Word
iosave6:	.long	0x00000000	# R6 save Area for I/O Interrupts

	.globl	_kern_fpm
	.globl  KERN_FPM
_kern_fpm:
KERN_FPM: .space FPM_STATUS # save space for FP emulator
	     .long  FP_S_UNSGD  # initialize fpm.status
	     .space FPMACHSIZE-FPM_STATUS-4
 
 #  literals:

_edata$    :.long  _edata
_end$      :.long  _end
_usrpt$    :.long  _usrpt
aftermain$ :.long  aftermain
 

 #========== SYSTEM PAGE TABLES AND DYNAMICALLY MAPPED DATA AREAS ===========|

/* Assign the specified virtual address to the given label */
#define SET_ADDR(name, addr) \
	.globl _/**/name; \
	.set _/**/name , addr
	
/* Allocate some pages in system segment, location unimportant */
#define GRAB_SOME_VMEM(vname, npages) \
	SET_ADDR(vname, nextvaddr); \
	.set nextvaddr , npages*PAGESIZE + nextvaddr

/* Allocate some VAX page table space in locore data area */
#define GRAB_SOME_PT(ptname, npte) \
	.globl _/**/ptname; \
	_/**/ptname:; \
	.fill	npte,4,0

/* Dedicate npages virtual address space, assign to given label */
#define MAPSYS(vname, npages) \
	GRAB_SOME_VMEM(vname, npages)

/* Same as above, except also allocate a VAX page table to map it */
#define MAPSYS_PT(ptname, vname, npages) \
	GRAB_SOME_VMEM(vname, npages); \
	GRAB_SOME_PT(ptname, npages)
/* Simply equate the given label with the given address */
#define MAPSYS_ADDR(vname, vaddr) \
	SET_ADDR(vname, vaddr)

/* Declare that npages at vaddr are dedicated to vname;
   also allocate enough VAX page table to map it */
#define MAPSYS_PT_ADDR(ptname, vname, npages, vaddr) \
	SET_ADDR(vname, vaddr); \
	GRAB_SOME_PT(ptname, npages)

.set nextvaddr , SYS_ORG + MAXPHYSMEM

/* Rationale behind this is at most MAXBUFFERPAGES buffers (one page
   per buffer) times size of buffer (MAXBSIZE) */
#define BUFFERSPACE MAXBSIZE/PAGESIZE*MAXBUFFERPAGES

#ifdef GPROF
#define CASIZE	1024		/* for profiling structures */
#else
#define CASIZE	128		/* not as much needed if no profiling */
#endif

	.align 2
 #                 PT NAME   VNAME    SIZE (PAGES)        MAP AT VADDR
 #		  -------   -----    ------------        ------------
          MAPSYS           (bufbase  ,BUFFERSPACE                         )
	  MAPSYS           (buflimit ,0                                   )
       MAPSYS_PT (Usrptmap ,usrpt    ,USRPTSIZE                           )
       MAPSYS_PT (camap    ,cabase   ,CASIZE*CLSIZE                       )
          MAPSYS           (calimit  ,0                                   )
       MAPSYS_PT (Mbmap    ,mbutl    ,NMBCLUSTERS*CLSIZE                  )
          MAPSYS           (msgbuf   ,MSGBUFPAGES                         )
  MAPSYS_PT_ADDR (Forkmap  ,forkutl  ,UPAGES            ,1*SEGSIZE+USTRUCT)
  MAPSYS_PT_ADDR (Xswapmap ,xswaputl ,UPAGES            ,2*SEGSIZE+USTRUCT)
  MAPSYS_PT_ADDR (Xswap2map,xswap2utl,UPAGES            ,3*SEGSIZE+USTRUCT)
  MAPSYS_PT_ADDR (Swapmap  ,swaputl  ,UPAGES            ,4*SEGSIZE+USTRUCT)
  MAPSYS_PT_ADDR (Pushmap  ,pushutl  ,UPAGES            ,5*SEGSIZE+USTRUCT)
  MAPSYS_PT_ADDR (Vfmap    ,vfutl    ,UPAGES            ,6*SEGSIZE+USTRUCT)
     MAPSYS_ADDR           (copybase                    ,8*SEGSIZE        )

     .globl _sys_seg_end
.set _sys_seg_end , nextvaddr

	.globl _buffers
_buffers:	.long	_bufbase		# pointer to buffer pool

 
 #====================== USERS PAGE TABLES ========================|
 
 #  virtual address of the user page table
 
.set USRPT   , _usrpt - SYS_ORG  # offset of user page table in segment
.set USRPT_PAGE , USRPT / PAGESIZE # page number of user page table
.set USRPT_TLB , USRPT_PAGE/MMU_NTLBS*MMU_NTLBS*-1+USRPT_PAGE # low 6 bits of pg
.set USRPT_TLBW1 , USRPT_PAGE / MMU_NTLBS * 0x200 + 0x00000000 # sid 000
.set USRPT_TLBW2 , 0xffffc000 # lock bits 1's, w=1,k=1,0 (public r/w)
.set USRPT_ADDRTAG , SYS_ADDRTAG + USRPT_PAGE
 
 #  virtual address of the user structure and system stack
 
.set UAREA , -UPAGES*NBPG + ENDOFP1 # address of u area
.set UAREA_PAGE , (UAREA - SEGSIZE) / PAGESIZE # page number of u area
.set UAREA_TLB , UAREA_PAGE/MMU_NTLBS*MMU_NTLBS*-1+UAREA_PAGE # low 6 bits of pg 1
.set UAREA_TLBW1 , UAREA_PAGE / MMU_NTLBS * 0x200 + 0x00000000 # sid 000
.set UAREA_TLBW2 , 0xffffc000 # lock bits 1's, w=1,k=1,0 (public r/w)
.set UAREA_ADDRTAG , UAREA_PAGE
 
 #========================| execution starts here |======================|
 
        .globl start
eye_catcher(start):

#ifdef ATR

#define WS1	 0x80000
#define WS2	 0x20000
#define WS3	 0x10000
#define MEG	 20
#define MEM4MEG  0xf8
#define MEM2MEG  0xfd
#define PCIF_PAGES 0x200

 # *** Set up the start of PCIF (in real memory).
 # The addressing variables are set in machdep.c in
 # startup() (when the pcif gets mapped in).
 #
 #   +-> +--------+
 #   |	 +--------+ ROMP -> PC 64K Byte I/O Window
 #   |   +--------+ ROMP -> PC 64K Half Word I/O window
 #   |   |	  |
 #   1MB +--------+ ROMP -> PC 128K Byte  window
 #   |   |	  |
 #   |   +--------+ ROMP -> PC 128K Half Word window
 #   | 	 |	  |
 #   | 	 +--------+ ROMP -> PC 128K Full Word window
 #   | 	 |	  |
 #   +-> +--------+ ROMP -> PC 512K Full Word Window
 #       |	  | end of physical memmory

	 geth	r1,POST_START(r0)
	 sli16	r1,MEG-16
	 st	r1,_pcif_addr
 # calculate the real memory address of the byte I/O ports
	 cau	r1,PCIF_IO_B_OFFSET/UPPER(r1)
	 geth	r6,_pcif_base	# get pcif base address
	 a	r1,r6
	 st	r1,pcif_base
#endif ATR

 #  zero registers (as after a POR reset)
 
	st	r2,_boothowto		# save first arg for init
	st	r3,_bootdev		# second arg is bootdev
 s r0,r0;s r1,r1;s r2,r2;s r3,r3;s r4,r4;s r5,r5;s r6,r6
 s r7,r7;s r8,r8;s r9,r9;s r10,r10;s r11,r11;s r12,r12
 s r13,r13;s r14,r14;s r15,r15
 
         mts scr_ts,r15			# reset ibm032 timer status
#if defined(RDB)
	stm	r0,hatipt		# reset initial values
	stm	r0,_qs			# clear 16 entries
	stm	r0,_qs+(64*1)		# clear 16 entries
	stm	r0,_qs+(64*2)		# clear 16 entries
	stm	r0,_qs+(64*3)		# clear 16 entries
	stm	r0,_qs+(64*4)		# clear 16 entries
	stm	r0,_qs+(64*5)		# clear 16 entries
	stm	r0,_qs+(64*6)		# clear 16 entries
	stm	r0,_qs+(64*7)		# clear 16 entries
	cal16	r0,iounmask-real0(r0)	# get the interrupt stack base
	st	r0,iosavep		# more re-entrant than before
#else
 # following is not done for RDB so that one can step thru locore
 # using the debugger (otherwise the following turns of IRQ_0)
	mts scr_irb,r15			# reset ibm032 interrupt request buffer
#endif
	cau r2,(MMUBASE)>>16(r0)     	# i/o base address of MMU
	oil r2,r2,(MMUBASE)&0xffff     # i/o base address of MMU
        iow   r14,MMU_SER(r2) 		# clear the exception register

 
#ifdef RDB
 #  save pck new iar (only once per memory load)
 #  the interrupt vectors were set by us, and have been taken
 #  over by the debugger.
 #  we will take back the program check and save the debugger's new iar
 #  so that we can call it when we have to.
 #  interrupt level 0 and svc areas are treated similarly.
 #
 
         l     r0,pck_ps+new_iar       # pick up existing pck iar
         cal16 r1,pck0                 # pick up my pck iar
         c     r0,r1                   # ?. points to us?
         je    pck_ok                  # no need to insert our pck addr
         cal16 r2,pck_jump_to_dan      # address of br instr.
         s     r0,r2                   # offset to him
         sri   r0,1                    # divide by 2
         niuo  r0,r0,0x000f               # clear top 12 bits
         oiu   r0,r0,0x8880               # branch uncond
         sts   r0,0(r2)                # make it jump to him
#if !defined(LORDB)
         st    r1,pck_ps+new_iar       #  set up pck new iar
#endif
pck_ok:
         l     r0,mck_ps+new_iar       # pick up existing mck iar
         cal16 r1,mck0                 # pick up my mck iar
         c     r0,r1                   # ?. points to us?
         je    mck_ok                  # no need to insert our mck addr
         cal16 r2,mck_jump_to_dan      # address of br instr.
         s     r0,r2                   # offset to him
         sri   r0,1                    # divide by 2
         niuo  r0,r0,0x000f               # clear top 12 bits
         oiu   r0,r0,0x8880               # branch uncond
         sts   r0,0(r2)                # make it jump to him
#if !defined(LORDB)
         st    r1,mck_ps+new_iar       #  set up mck new iar
#endif
mck_ok:
 
 #  set up svc new ps:  svc0, no translation, interrupts masked, level 0
 
        cal16 r0,svc0                  # address of svc handler
        st    r0,svc_ps+new_iar        # store in new ps
	cau   r0,(NOTRANS_ICS+INTMASK_ICS)>>16(r0) #ics: no transl, ints masked
	oil   r0,r0,(NOTRANS_ICS+INTMASK_ICS) &0xffff
        sth   r0,svc_ps+new_ics        # store in new ps
 
 #  save int0 new iar (only once per memory load)
 
         l     r0,int0_ps+new_iar      # pick up existing int 0 iar
         cal16 r1,int0                 # pick up my int 0 iar
         c     r0,r1                   # ?. points to us?
         je    int0_ok                 # no need to insert our int addr
         cal16 r2,int0_jump_to_dan     # address of br instr.
         s     r0,r2                   # offset to him
         sri   r0,1                    # divide by 2
         niuo  r0,r0,0x000f               # clear top 12 bits
         oiu   r0,r0,0x8880               # branch uncond
         sts   r0,0(r2)                # make it jump to him
#if !defined(LORDB)
	 st	r1,int0_ps+new_iar	# set up int 0 new iar
#endif
int0_ok:
#endif RDB
 
#ifdef ROMPC
/*
 * determine the processor model (either SGP or ROMPC) by setting
 * the Interrupt On Unaligned Access bit which is always 0 on the SGP
 * processor.
 */
	mfs	scr_ics,r0		# get old ics
	setsb	scr_ics,18-16		# set Interrupt On Unaligned Access
	mfs	scr_ics,r4		# get new ics
	c	r0,r4
	jeq	2f			# same - old cpu
	mts	scr_ics,r0		# restore old ics
#ifdef MOD135
/*
 * If bit 20 in register IOIM_MCC of ZIC IOIM1 == 1 then APC
 * else MOD135.
 */
	get	r4,$IOIM1		# get base of IOIM1
	ior	r4,IOIM_MCC(r4)		# get chip configuration register
	nilz	r4,r4,0x800		# if 1 then APC, if 0 then MOD135
	jne	3f			# branch if APC
	lis	r0,CPU_MOD135		# else its a MOD135
	st	r0,_cpu_model		# store cpu model number
 # if old ZIC then apply fix
	get	r6,$IOIM1		# get base of IOIM1
	ior	r4,IOIM_GSR(r6)		# get GSR to clear it
	ior	r4,IOIM_GSR(r6)		# get GSR to see if bit 20 stuck at 1
	nilz	r4,r4,0x800		# is bit 20 set?
	jeq	4f			# no, new ZIC
	ior	r4,IOIM_GCR(r6)		# yes bit 20 set, old ZIC, apply fix
	setbl	r4,6			# turn on bit 22
	iow	r4,IOIM_GCR(r6)		# write to GCR
4:	get	r0,$0x08c8		# we have 16 meg (ala APC card)
	sth	r0,_memconfig+2		# flag it in lower half of memconfig
	j	0f			# end of MOD135 specific stuff
3:
#endif MOD135
	lis	r0,CPU_ROMPC
	st	r0,_cpu_model		# store cpu model information
#ifdef IBMRTPC
	get	r4,$IOIM1		# get base of IOIM1
	ior	r4,IOIM_MCC(r4)		# get chip configuration register
	nilz	r4,r4,0x1000		# test for -RAM 
	jne	0f			# yes we have no RAM
	get	r0,$0x8			# we have 4meg on-card
	stc	r0,_memconfig+2		# flag it in third byte
0:
#endif IBMRTPC
	get	r0,$ECR_STACK
	mts	scr_ecr,r0	# reset exception stack
2:
#endif ROMPC

	.globl _clrloop
_clrloop:


#include "../machine/lohatipt.s"

 # initialize the pcb's
	.globl  _lopcb
_lopcb:
 #  now r2 is page number of first u area page.
 
         slpi  r2,LOG2PAGESIZE      # r3 -> u area
	 cau   r0,(UPAGES*NBPG-USIZE)>>16(r0) # offset in the u area of the u struct
	 oil   r0,r0,(UPAGES*NBPG-USIZE)&0xffff
         a     r3,r0                # r3 -> u struct
 #  initialize (slightly) the first pcb
	 cau   sp,(KERNSTACK)>>16(r0) # load kernel stack for call to main
	 oil   sp,sp,(KERNSTACK)&0xffff
         st    r1,PCB_KSP(r3)       # kernal stack pointer
         st    r3,PCB_USP(r3)       # not exactly correct!!!
         l     r13,_usrpt$          # address of page table map
         st    r13,PCB_P0BR(r3)     # point to user page table map
         s     r15,r15              # no p0 pages
         st    r15,PCB_P0LR(r3)     # set length register
	 cau   r14,(P1PAGES-UPAGES)>>16(r0) # number of unused page in P1
	 oil   r14,r14,(P1PAGES-UPAGES)&0xffff
         st    r14,PCB_P1LR(r3)     # P1 page tables for all of it !?
         sli   r14,2                # times 4 bytes per pte
         cal   r15,-4*UPAGES+NBPG(r13) # offset of ptes for u area
         s     r15,r14              # back up by size of p1 page table
         st    r15,PCB_P1BR(r3)     # thats p1 base
	 cau   r0,(CLSIZE)>>16(r0)   # number of pages in a click
	 oil   r0,r0,(CLSIZE)&0xffff
         st    r0,PCB_SZPT(r3)      # number of ptes
 #  initialize signal trampoline in u area
         l     r0,sigcode           # svc 139 instruction
         st    r0,PCB_SIGC(r3)      # place to return to after a handler
 #  compute page number past u area
         ai    r2,r2,UPAGES         # past u area
 #  set return address to continue after calling main
         l     r15,aftermain$
         get     r0,$_main	  # load constant table into r0.;
 #  set up r11 to contain the 'howto' value from boot to be passed to init
	l       r11,_boothowto
 #  go to translate mode, interrupts disabled, level 0
         lps   0,callmain_ps        # translate mode, routine _main
 #         .cnop 0,4 **MOVED TO LOWER CORE FOR ADDRESSABILITY**
 #sigcode: svc   139(r0)              | template moved to u area for signals
1:       .using 1b
 #
 #  resume here after _main, in proc 0 (/etc/init);  jump to icode
 #
eye_catcher(aftermain):
         .using aftermain,r15       # use the return register as base
         lis   r0,0                 # begin at address zero
         st    r0,low_ps            # place in low_ps

	 # transl on, prob stt, lev 7
	 cau   r0,(TRANS_ICS+PROBSTATE_ICS+INT_PRI7)>>16(r0)
	 oil   r0,r0,(TRANS_ICS+PROBSTATE_ICS+INT_PRI7)&0xffff
         sth   r0,low_ps+4          # place in low_ps
         lps   0,go_ps              # jump to go with interrupts off
1:       .using 1b                  # drop base register
 #
 #  flipmode - flip the segment registers from user/kernel to kernel/user
 #      on entry:
 #          interrupts are masked
 #          translation is off
 #          sp contains the return address
 #  segment 0, 1, and 14's SEG_K bit are flipped so that the kernel has
 #  read/write (except kernel text which is public readonly so the user can
 #  execute it.
#ifdef ATR
 #  Segment MMU_IO_SEG flips the SEG_S bit.
#endif
 #
         .using real0,r0
	.globl _flipmode
eye_catcher(flipmode):

_flipmode:

         stm   r14,flipsave            # save caller registers

#define FLIPSEG(segreg,rdata,raddr,how) ior rdata,segreg(raddr); \
			xi rdata,rdata,how; \
 			iow rdata,segreg(raddr)	/* flip one segment register */
#ifdef ATR
#define FLIPMODE(rdata,raddr)  get	raddr,$MMU_SEGREG0 ; \
	FLIPSEG(0,rdata,raddr,MMU_SEG_K) ; \
	 FLIPSEG(1,rdata,raddr,MMU_SEG_K); \
	 FLIPSEG(14,rdata,raddr,MMU_SEG_K) ; \
	 FLIPSEG(MMU_IO_SEG,rdata,raddr,MMU_SEG_S) ; \
	 get	rdata,$MMU_IO_SEG<<28; \
	 iow	rdata,MMU_SEG_TLB-MMU_SEGR(raddr); /* invalidate MMU_IO_SEG */
#endif
#ifdef IBMRTPC
#define FLIPMODE(rdata,raddr)  get	raddr,$MMU_SEGREG0 ; \
	FLIPSEG(0,rdata,raddr,MMU_SEG_K) ; \
	 FLIPSEG(1,rdata,raddr,MMU_SEG_K); \
	 FLIPSEG(14,rdata,raddr,MMU_SEG_K)
#endif
	 FLIPMODE(r14,r15)	# flip it

         brx   sp                      # return to caller...
         lm    r14,flipsave            # with caller's registers restored
1:	.using 1b                      # tell assembler no base register
 #
 #  go - reload registers and old ps from low core
 #      on entry:
 #          interrupts are masked <=== VERY IMPORTANT AS SP INVALIDATED
 #          translation is on
 #          low_save1,low_save15, and low_ps are set
 #	   r0, r2 ... r14 have the required final values (lm already done)
eye_catcher(go):
	 get	sp,$(USTRUCT+PCB_ICSCS) # sp-> icscs field of pcb
         ls    sp,0(sp)             # sp = icscs field of pcb
         mfs   scr_iar,r15          # use r15 as base register
1:	 .using 1b,r15              # tell assembler r15 is base register
         lps   0,realgo_ps          # turn off translation, interrupts off
         .using real0,r0            # tell assembler r0 is base register
 # now translation is off, interrupts disabled
realgo:
         l     r15,low_ps+old_ics_cs# get old icscs
         mttbiu r15,PROBSTATE-16    # problem state?
         jntb  go_now               # no...returning to system
         mttbiu sp,AST_USER_BIT     # if VAX AST schedlued
         jtb   go_ast               # then jump
         mttbiu r15,INSTSTEP-16     # instruction step?
         jtb  0f		    # yes...do it
 # no...turn on user full blast - go_now code copied inline for efficiency
	FLIPMODE(sp,r15)		# flip into user mode
         l     r1,low_save1         # restore old r1
         l     r15,low_save15       # restore old r15
         lps   0,low_ps             # reload old ps, immediate interrupts ok
0:
	FLIPMODE(sp,r15)		# flip into user mode
         setsb scr_irb,IRB_IRQ_0    # request level 0 interrupt
         mfs   scr_ics,r15          # copy processor level into r15
         nilz  r15,r15,0x07             # if on level 0
         jz    go_step              # then jump
go_stop:
         l     r1,low_save1         # restore old r1
         l     r15,low_save15       # restore old r15
         lps   0,low_ps             # reload old ps, interrupt before inst
go_step:
         l     r1,low_save1         # restore old r1
         l     r15,low_save15       # restore old r15
         lps   1,low_ps             # reload old ps, no interrupts for 1 inst
go_now:
         l     r1,low_save1         # restore old r1
         l     r15,low_save15       # restore old r15
         lps   0,low_ps             # reload old ps, immediate interrupts ok
go_ast:
	 get	sp,$KERNSTACK # switch to kernal stack
         l     r15,low_save15       # restore old r15
         stm   r10,low_save10       # assure low_save1 and low_save10 thru 15
	 get	r10,$VAST		# mcs_pcs value for VAX AST
         x     r11,r11              # no more info needed
         l     r12,low_ps+old_iar   # retieve old iar
         l     r13,low_ps+old_ics_cs# retieve old ics_cs
         lps   0,fault_ps           # short circuit interrupt, handle fault
1:       .using 1b
#ifdef ROMPC
 #
 #  go2 - reload registers and old ps from low core
 #      on entry:
 #          interrupts are masked <=== VERY IMPORTANT AS SP INVALIDATED
 #          translation is on
 #          low_save1,low_save15, and low_ps are set
 #	   r0, r2 ... r14 have the required final values (lm already done)
eye_catcher(go2):
	 get	sp,$(USTRUCT+PCB_ICSCS) # sp-> icscs field of pcb
         ls    sp,0(sp)             # sp = icscs field of pcb
         mfs   scr_iar,r15          # use r15 as base register
1:	 .using 1b,r15              # tell assembler r15 is base register
         lps   0,realgo2_ps          # turn off translation, interrupts off
         .using real0,r0            # tell assembler r0 is base register
 # now translation is off, interrupts disabled
realgo2:
         l     r15,low_ps+old_ics_cs# get old icscs
         mttbiu r15,PROBSTATE-16    # problem state?
         jntb  go2_now               # no...returning to system
         mttbiu sp,AST_USER_BIT     # if VAX AST schedlued
         jtb   go2_ast               # then jump
         mttbiu r15,INSTSTEP-16     # instruction step?
         jtb  0f		    # yes...do it
 # no...turn on user full blast - go2_now code copied inline for efficiency
	FLIPMODE(sp,r15)		# flip into user mode
         l     r1,low_save1         # restore old r1
         l     r15,low_save15       # restore old r15
         lps   2,low_ps             # reload old ps, immediate interrupts ok
0:
	FLIPMODE(sp,r15)		# flip into user mode
         setsb scr_irb,IRB_IRQ_0    # request level 0 interrupt
 # we always do a lps 2 (interrupt 0 intr. before instruction) because
 # the IAR will already have been advanced when the original exception
 # was delivered.
go2_stop:
         l     r1,low_save1         # restore old r1
         l     r15,low_save15       # restore old r15
         lps   2,low_ps             # reload old ps, interrupt before inst
go2_now:
         l     r1,low_save1         # restore old r1
         l     r15,low_save15       # restore old r15
         lps   2,low_ps             # reload old ps, immediate interrupts ok
go2_ast:
	 get	sp,$KERNSTACK # switch to kernal stack
         l     r15,low_save15       # restore old r15
         stm   r10,low_save10       # assure low_save1 and low_save10 thru 15
	 get	r10,$VAST		# mcs_pcs value for VAX AST
         x     r11,r11              # no more info needed
         l     r12,low_ps+old_iar   # retieve old iar
         l     r13,low_ps+old_ics_cs# retieve old ics_cs
         lps   0,fault_ps           # short circuit interrupt, handle fault
1:       .using 1b
#endif ROMPC
 #
 #  svc level 0 interrupt service routine
 #  on entry interrupts are inhibited, priority = 7, and 
 #  translate is off.
 #
	.globl  svc0
         .using real0,r0
svc0:
         st    r1,low_save1      # save r1
         stm   r12,low_save12    # save r12-r15
 # 	cnt.v_syscall++
	 l	r1,_cnt+V_SYSCALL # count system calls
 	 FLIPMODE(r14,r15)	# switch into kernel mode
	 ais	r1,1
	 st	r1,_cnt+V_SYSCALL
         lm	r12,svc_ps+old_iar     # get interrupt time iar, ics_cs and code
	 get	sp,$KERNSTACK	# switch to kernal stack
         lps   0,svc_ps2        # goto svc: translation on, interrupts off
1:       .using 1b

 #
 # come here with interrupts inhibited, level 7 (user level)
 # and translation on.
 #
 # sp = kernel stack
 # r12 = interrupt time iar
 # r13 = ics_cs at interrupt time
 # r14 = svc new iar (not used)
 # r15 = new ics (not used) and svc code
 #
 # r9 is used to save/restore the mq
 #
 # call syscall(0, svc_code, ics_cs, regs)
 #
	.globl	_svc
_svc:
         ai    sp,sp,-FAULTSAVE  # make room for context
svcstm:	stm   r2,FAULT_R2(sp)   # save all registers (some incorrect)
				   # r0 is passed to Syscall and stored on the stack
				   # there.
         put	r12,FAULT_IAR(sp) # save iar at time of interrupt
         lis	r2,0	         # 1st parm to syscall
         nilz	r3,r15,0xffff         # 2nd parm to syscall
         cas	r4,r13,r0         # 3rd parm to syscall
	  cas r5,r0,r0		   # 4th param to syscall (syscall stores this on stack)
 
         mfs   scr_mq,r9        # get current multiplier/quotient
         st    r9,FAULT_MQ(sp)  # save context
         mfs   scr_iar,r6        # get addressability to low storage
1:	 .using 1b,r6            # tell the assembler (C preserves R6)

         l     r15,low_save1     # value of r1 at time of interrupt
         sts   r15,FAULT_R1(sp)  # correct value on stack
         lm    r12,low_save12    # values of r12-r15 at time of interrupt
         stm   r12,FAULT_R12(sp) # correct values on stack
#ifdef ROMPC
	lis	r15,0		# get value for FAULT_ECR
	st	r15,FAULT_CNT(sp) # and mark no exception packets
#endif ROMPC
 
#if !defined(LORDB)
         mfs   scr_irb,r15       # get copy of irb
         mttbil r15,IRB_IRQ_0    # get int level 0 bit
         mftbiu r4,INSTSTEP-16    # set as instruction step bit in icscs
         clrsb scr_irb,IRB_IRQ_0 # cancel level 0 interrupt request if any
#endif
 #
 #  call syscall() to handle the svc
 #
svc_call_c:
	 clrsb scr_ics,INTMASK-16# enable interrupts now
	   balix r15,_.syscall		 # call syscall
	   	get	   r0,$.long(_syscall)	 # load constant table for syscall
 #
 #  restore complete context and return
 #
         setsb scr_ics,INTMASK-16# disable interrupts
         lm     r14,FAULT_IAR(sp) # get old_iar (MAY BE CHANGED) and FAULT_MQ
         mts   scr_mq,r15        # restore multiplier/quotient
         get    r15,FAULT_ICS_CS(sp)  # get old_ics_cs (MAY BE CHANGED)
         stm   r14,low_ps        # save in low core
         get    r15,FAULT_R1(sp)  # get old r1 value (UNCHANGED)
         st    r15,low_save1     # save in low core for go
         get     r15,FAULT_R15(sp) # get old r15 value (UNCHANGED)
         st    r15,low_save15    # save in low core for go
         get    r0,FAULT_R0(sp)   # restore 0 (UNCHANGED)
#ifdef ROMPC
	l	r11,FAULT_CNT(sp) # get exception count 
	cis	r11,0
	bne	ecr_restore		# re-issue exception packets
#endif ROMPC
	 bx	go		 # restart interrupted program
          lm    r2,FAULT_R2(sp)   # restore 2 through 15 (UNCHANGED)
1:       .using 1b               # tell assembler no base register

 #  fault handler, called from level 0 interrupt service routines
 #
 #  on entry:
 #     r1 = sp
 #     r10 = mcs_pcs (check) or zero (svc)
 #     r11 = exception information (check) or svc number (svc)
 #     r12 = interrupt old_iar
 #     r13 = interrupt old_ics_cs
 #     r14 = unused
 #     r15 = unused
 #     low_save1,10,11,12,13,14,15 are values at time of interrupt
 #
 #  function:
 #     save complete context on the stack
 #     call trap
 #     if called from user mode
 #       reschedule
 #     else
 #       restore complete context and return
 #
 #  parameters passed to trap (stack offsets):
 #
 
.set FAULT_ICS_CS , -8
.set FAULT_R0 , -4
.set FAULT_R1      , 1*4 + FAULT_R0
.set FAULT_R2      , 2*4 + FAULT_R0
.set FAULT_R10     ,10*4 + FAULT_R0
.set FAULT_R11     ,11*4 + FAULT_R0
.set FAULT_R12     ,12*4 + FAULT_R0
.set FAULT_R15     ,15*4 + FAULT_R0
.set FAULT_IAR     , 1*4 + FAULT_R15
.set FAULT_MQ      , 2*4 + FAULT_R15
#ifdef ROMPC
.set FAULT_CNT     , 3*4 + FAULT_R15	/* count of exceptions */
.set FAULT_EX1     , 4*4 + FAULT_R15	/* four words for exception */
.set FAULT_EX2     , 8*4 + FAULT_R15	/* four words for exception */
.set FAULT_GSR1	   , 12*4 + FAULT_R15	/* one word for IOIM1 GSR */
.set FAULT_REP1	   , 13*4 + FAULT_R15	/* one word for IOIM1 reply */
.set FAULT_GSR2	   , 14*4 + FAULT_R15	/* one word for IOIM2 GSR */
.set FAULT_REP2	   , 15*4 + FAULT_R15	/* one word for IOIM2 reply */

.set FAULTSAVE     , 4 + FAULT_REP2 # size of stack area needed for parameters to trap
#else

.set FAULTSAVE     , 4 + FAULT_MQ # size of stack area needed for parameters to trap
#endif ROMPC

eye_catcher(FAULT):

#ifdef RDB
	ai	sp,sp,FRM_PROTECT	# for the debugger's information 
#endif RDB
	.globl	_fault
_fault:
	.globl	fault
fault:
faultstm:stm   r2,FAULT_R2-FAULTSAVE(sp)   # save all registers (some incorrect)
         ai    sp,sp,-FAULTSAVE  # make room for context
				   # r0 is saved on the stack by xxtrap()
         st    r12,FAULT_IAR(sp) # save iar at time of interrupt
         cas   r2,r10,r0         # 1st parm to trap
         cas   r3,r11,r0         # 2nd parm to trap
         cas   r4,r13,r0         # 3rd parm to trap
         cas   r5,r0,r0	    # 4th param to trap
 
         shr   r13,16            # shift ics to low order bits
         nilz  r13,r13,7             # use only level bits
         mfs   scr_ics,r15       # get current ics
         nilo  r15,r15,0xfff8        # zero only level bits
         o     r15,r13           # set level at time of fault
         mts   scr_ics,r15       # change processor level

         mfs   scr_mq,r15        # get current multiplier/quotient
         st    r15,FAULT_MQ(sp)  # save context
         mfs   scr_iar,r6        # get addressability to low storage
1:	 .using 1b,r6            # tell the assembler (C preserves R6)
	 l	r15,_cnt+V_TRAP # count trap calls
	 ais	r15,1
	 st	r15,_cnt+V_TRAP
#ifdef ROMPC
#ifdef SGP
 # note that we set FAULT_CNT to 0 (by relying on CPU_SGP == 0 -
 # which is a bit of a kludge) when running on a SGP so that
 # the rest of the code can do one test (on FAULT_CNT) instead
 # of two.
	l	r11,_cpu_model	# see if running on ROMPC
	cis	r11,CPU_SGP
	jeq	1f		# nope (NOTE: CPU_SGP == 0)
#endif SGP
#ifndef ATR
 # read the IOIM1 and IOIM2 General Status registers to allow use of IOIM1 & 2
 # we do this in all cases (even when the exceptions may not be in 
 # segment F because it is faster to do the IORs than to do the conditional
 # branches).
	cau	r12,IOIM1/UPPER(r0)	# get IOIM 1 base
	ior	r13,IOIM_REPLY(r12)	# get reply 
	st	r13,FAULT_REP1(sp)	# store IOIM1 reply 
	ior	r13,IOIM_GSR(r12)	# get status
	st	r13,FAULT_GSR1(sp)	# store IOIM1 GSR 
#endif ATR
 # get IOIM2 reply and GSR
#if	(defined(ATR) || defined(MOD135))
	load	r12,_ioim2		# test if IOIM 2 present
	cis	r12,0			# zero if not
	jeq	4f
#endif	/* defined(ATR) || defined(MOD135) */
	cau	r12,IOIM2/UPPER(r0)	# get IOIM 2 base
	ior	r13,IOIM_REPLY(r12)	# get reply 
	st	r13,FAULT_REP2(sp)	# store IOIM2 reply 
	ior	r13,IOIM_GSR(r12)	# get status
	st	r13,FAULT_GSR2(sp)	# store IOIM2 GSR 
4:
 # if anything on the exception stack copy it to kernel stack
	mfs	scr_ecr,r11	# get ECR
	niuo	r10,r11,0x00ff	# get exception stack address in r10
	sri16	r11,24-16	# get exception count in r11
	cis	r11,0
	jeq	3f		#nothing to do
 # copy first exception status
	oiu	r10,r10,SYS_ORG/UPPER	# get virtual address
	lm	r12,0(r10)	# get first exception (4 words)
	stm	r12,FAULT_EX1(sp)	# store first exception (4 words)
	cis	r11,2
	jl	2f
	lm	r12,4*4(r10)	# get second exception (4 words)
	stm	r12,FAULT_EX2(sp)	# store second exception (4 words)
 # reset exception stack to original position
2:
	get	r0,$ECR_STACK
	mts	scr_ecr,r0	# reset exception stack
3:
1:
	st	r11,FAULT_CNT(sp) # save it
#endif ROMPC
 
         l     r15,low_save1     # value of r1 at time of interrupt
         sts   r15,FAULT_R1(sp)  # correct value on stack
         lm    r10,low_save10    # values of r10-r15 at time of interrupt
         stm   r10,FAULT_R10(sp) # correct values on stack
 
#if !defined(LORDB)
         mfs   scr_irb,r15       # get copy of irb
         mttbil r15,IRB_IRQ_0    # get int level 0 bit
         jntb  fault_call_c      # jump if no instruction step pending
         clrsb scr_irb,IRB_IRQ_0 # cancel level 0 interrupt request
         oiu   r4,r4,ICSCS_INSTSTEP/UPPER  # repeat inst step after fault
#endif
 #
 #  call trap() to handle the fault
 #
fault_call_c:
         clrsb scr_ics,INTMASK-16# enable interrupts now
 	   l    r13,__trap	 # store constant pointer
	   ls	 r15,0(r13)	 # get address of routine
         balrx r15,r15        # call trap
		 mr r0,r13
 #
 #  restore complete context and return
 #
         setsb scr_ics,INTMASK-16# disable interrupts
         l     r14,FAULT_IAR(sp) # get old_iar (MAY BE CHANGED)
         l     r15,FAULT_ICS_CS(sp)  # get old_ics_cs (MAY BE CHANGED)
         stm   r14,low_ps        # save in low core
         l     r15,FAULT_MQ(sp)  # get old multiplier/quotient (UNCHANGED)
         mts   scr_mq,r15        # restore multiplier/quotient
         ls    r15,FAULT_R1(sp)  # get old r1 value (UNCHANGED)
         st    r15,low_save1     # save in low core for go
         l     r15,FAULT_R15(sp) # get old r15 value (UNCHANGED)
         st    r15,low_save15    # save in low core for go
         l     r0,FAULT_R0(sp)   # restore 0 (UNCHANGED)
#ifdef ROMPC
	l	r11,FAULT_CNT(sp) # get exception count 
	cis	r11,0
	jeq	3f		#nothing to do
 # SVC code also comes here if exception packets are present
 # (which may happen after a sigcleanup)
ecr_restore:	
	mfs	scr_ecr,r10	# get ECR
	niuo	r10,r10,0x00ff	# get exception stack address in r10
	oiu	r10,r10,SYS_ORG/UPPER	# get kernel virtual address
	ai	r10,r10,-16	# adjust exception stack
	cis	r11,1
	jeq	2f		# just one exception
 # copy second exception status
	ai	r10,r10,-16	# adjust exception stack
	lm	r12,FAULT_EX2(sp)	# get second exception (4 words)
	stm	r12,16(r10)	# store second exception (4 words)
 # copy first exception status
2:
	lm	r12,FAULT_EX1(sp)	# get first exception (4 words)
	stm	r12,0(r10)	# store first exception (4 words)
 # reset exception stack to original position
	niuo	r10,r10,0x00ffffff/UPPER	# get 24 bit address
	sli16	r11,24-16	# get count to proper place
	o	r10,r11		# get new ecr 
	mts	scr_ecr,r10	# get new ecr value
	bx	go2
         lm    r2,FAULT_R2(sp)   # restore 2 through 15 (UNCHANGED)
 # nothing on execption stack - proceed as before
3:
1:
#endif ROMPC
	bx	go
         lm    r2,FAULT_R2(sp)   # restore 2 through 15 (UNCHANGED)
1:       .using 1b               # tell assembler no base register

 #  program check that is really a page fault
 #
 #  on entry:
 #     r10 = mcs_pcs
 #     r11 = exception information
 #     interrupt old_iar
 #     interrupt old_ics_cs
 #     low_save1,10,11,12,13,14,15 are values at time of interrupt
 
         .using real0,r0
	 .globl pck_fault
pck_fault:
         l     r12,pck_ps+old_iar     # get interrupt time iar
         l     r13,pck_ps+old_ics_cs  # get interrupt time ics and cs
mck_fault:			      # come here after machine check
         mttbiu r13,PROBSTATE-16 # copy problem state bit from old ics
         jtb   user_fault        # jump if we were in problem state

	  ai	sp,sp,FRM_PROTECT	#protect the stack frame before servicing fault

	 get	r15,$UAREA	# address of 1st byte of kernel stack
         cl    sp,r15            # if kernel stack invalid
         bl    pck_badsp          # then jump
	 get   r15,$KERNSTACK	# address of last byte of kernel stack
         cl    sp,r15            # if kernel stack invalid
         bh    pck_badsp          # then jump
         cal16 r15,faultstm      # this inst catches bad kernel segments
         cl    r12,r15           # if fault anywhere else
         jne   goto_fault        # then jump
         ai    sp,sp,FAULTSAVE      # undo subi before faultstm
         b     pck_badsp          # deep trouble


user_fault:
         bali  sp,flipmode       # switch to kernel mode
	get	sp,$KERNSTACK		# switch to kernel stack
goto_fault:
#ifdef RDB
	cas	r15,r12,r0	# get iar into r15 for traceback
#endif RDB
         lps   0,fault_ps        # goto fault: translation on, interrupts off

 #
 # we come here if the kernel stack pointer is now invalid (outside of
 # kernel stack). This presumably was what caused the current exception
 # (probably should check), so we just reset the kernel stack to it's base
 # and let events follow their normal course - we will get a 'kernel trap' 
 # because pagein will reject this page (not in user address space).
 # the clue is that the r1 value at the time of the trap will be in
 # the red zone.
 #
 # we could switch to another stack but this would cause other problems with 
 # the checks for valid stacks in the interrupt routines (int_comm).
 # for now this should be enough to track down the stack overflow cause 
 # as we can look at the top of stack and registers to see where we are
 #
pck_badsp:
	st	sp,low_savesp-real0(r0) # save the bad sp for inspection
	get	sp,$KERNSTACK	# reset kernel stack 
	cal	r2,LED_BAD_SP(r0)		# value for the lights 
	bali	r5,display	# display it in the lights 
#ifdef RDB
	tsh	r0,_badsp-real0(r0)	# test if already been here
	ti	5,r0,0		# trap if not equal to zero
#endif RDB
	j	goto_fault	# and goto fault for eventual panic


1:       .using 1b               # tell assembler no base register


#include "../machine/lopckrt0.s"
 
 # Data moved to locore.c for addressability.
#include "../machine/loslih.h"
#include "../machine/lointr.s"
#include "../machine/loswap.s"
#include "../machine/loutil.s"
#include "../machine/lofp.s"


#define fpglue _fpglue		/* make name visable */
#include "../machine/fpglue.s"

	.text
	.ltorg
 
	.globl _endlocore
_endlocore:                             # last of locore

#ifdef RDB
	.globl	_symsize


	.org	real0+RDB_SYMTAB		# to symbol table 
	.globl	_rdb_symtab
_rdb_symtab:

	.org	real0+RDB_RELOC			# to start of debugger area
	.globl	_rdb_start
_rdb_start:

1: .set _symsize,	RDB_SYMLEN		# size of symbol table
_start:
rdb:
        .using real0,r0          # tell the assembler to use r0 as base
	cal16	r15,start	# use 'cal16' to ignore segment number
	balr	r15,r15		# transfer control to real 'start'
 # This value must be bigger than the _end of rdb.out
	.set rdb_end, RDB_END	#compare with _end in rdb.out
	.org	real0+rdb_end		# to end of debugger area
 # _stext is the start of the normal C code (text)
	.globl	_stext
	.set	_stext,real0+rdb_end
#else RDB
	.globl	_stext
	.set	_stext,_endlocore
#endif RDB

 #
 # following is a rather kludgy way of building _fpget and _fpput 
 # from fpget and fpput.
 #
 # this is done so that the _fpget and _fpput routines are in the kernel text
 # (which is readable from user programs and hence when we get a use of the
 # fpa directly from user mode we can redirect it to the appropriate routines.
 # (what we do to be nice to users!)
 #
#define fpget _fpget		/* make it accessable from c */
#define fpput _fpput		/* both of them */
#include "../machine/lofp.s"

	.data
	.align	2
 #
 #  build icode using assembler rather than by doing by hand
 #  it is in the data segment so that we can modify "howtoarg"
 #
	.globl	_icode
	.globl	_szicode
	.globl	_howtoarg
	.set	stack,0x7f0
_icode:
	.using	_icode,r0
	cal	r1,stack(r0)
	cal	r2,init
	cal	r3,args
	cal	r4,envp
	svc	59(r0)	# execve
	svc	1(r0)	# exit with errno in r2
	j	_icode

init:	.asciz	"/etc/init"
_howtoarg:	.ascii	"-\0\0\0"		# changed by startup code
	.align	2
args:	.long	init-_icode
	.long	_howtoarg-_icode
envp:	.long	0
	.align	2
_szicode:	.long	_szicode-_icode

