* Copyright (c) 1988 University of Utah.
* Copyright (c) 1980, 1990 The Regents of the University of California.
* This code is derived from software contributed to Berkeley by
* the Systems Programming Group of the University of Utah Computer
* %sccs.include.redist.c%
* from: Utah $Hdr: locore.s 1.47 89/10/08$
* @(#)locore.s 7.3 (Berkeley) %G%
* This is where we wind up if the kernel jumps to location 0.
* (i.e. a bogus PC) This is known to immediately follow the vector
* table and is hence at 0x400 (see reset vector in vectors.s).
.asciz "kernel jump to zero"
* Called by auto-restart.
* Trap/interrupt vector routines
.globl _trap, _nofault, _longjmp
tstl _nofault | device probe?
jeq _addrerr | no, handle as usual
movl _nofault,sp@- | yes,
jbsr _longjmp | longjmp(nofault)
clrw sp@- | pad SR to longword
moveml #0xFFFF,sp@- | save user registers
movl usp,a0 | save the user SP
movl a0,sp@(60) | in the savearea
lea sp@(64),a1 | grab base of HW berr frame
movw a1@(12),d0 | grab SSW for fault processing
bset #14,d0 | yes, must set FB
movw d0,a1@(12) | for hardware too
bset #15,d0 | yes, must set FC
movw d0,a1@(12) | for hardware too
jeq Lbe0 | no, check for hard cases
movl a1@(18),d1 | fault address is as given in frame
btst #4,a1@(8) | long (type B) stack frame?
jne Lbe4 | yes, go handle
movl a1@(4),d1 | no, can use save PC
addql #4,d1 | yes, adjust address
addql #2,d1 | yes, adjust address
movl a1@(38),d1 | long format, use stage B address
subql #2,d1 | yes, adjust address
movl d1,sp@- | push fault VA
clrw sp@- | padded to longword
movw a1@(8),d0 | get frame format/vector offset
andw #0x0FFF,d0 | clear out frame format
cmpw #12,d0 | address error vector?
jeq Lisaerr | yes, go to it
#if defined(HP330) || defined(HP360) || defined(HP370)
movl d1,a0 | fault address
.long 0xf0109e11 | ptestr #1,a0@,#7
.long 0xf0176200 | pmove psr,sp@
btst #7,sp@ | bus error bit set?
jeq Lismerr | no, must be MMU fault
clrw sp@ | yes, re-clear pad word
jra Lisberr | and process as normal bus error
#if defined(HP320) || defined(HP350)
lea _IObase+MMUSTAT,a0 | no, get addr of MMU status
movl a0@,d0 | read status
jeq Lisberr | no, just a non-MMU bus error so skip
andl #~MMU_FAULT,a0@ | yes, clear fault bits
movw d0,sp@ | pass MMU stat in upper half of code
movl #T_MMUFLT,sp@- | show that we are an MMU fault
jra Lbexit | and deal with it
movl #T_ADDRERR,sp@- | mark address error
jra Lbexit | and deal with it
movl #T_BUSERR,sp@- | mark bus error
jbsr _trap | handle the error
lea sp@(12),sp | pop value args
movl sp@(60),a0 | restore user SP
movl a0,usp | from save area
moveml sp@+,#0x7FFF | restore most user regs
tstw sp@+ | do we need to clean up stack?
jeq rei | no, just continue
btst #7,sp@(6) | type 9/10/11 frame?
jeq rei | no, nothing to do
movw sp@,sp@(12) | yes, push down SR
movl sp@(2),sp@(14) | and PC
clrw sp@(18) | and mark as type 0 frame
lea sp@(12),sp | clean the excess
btst #4,sp@(6) | type 10?
movw sp@,sp@(24) | yes, push down SR
movl sp@(2),sp@(26) | and PC
clrw sp@(30) | and mark as type 0 frame
lea sp@(24),sp | clean the excess
movw sp@,sp@(84) | type 11, push down SR
movl sp@(2),sp@(86) | and PC
clrw sp@(90) | and mark as type 0 frame
lea sp@(84),sp | clean the excess
clrw sp@- | pad SR to longword
moveml #0xFFFF,sp@- | save user registers
movl a0,sp@(60) | the user stack pointer
lea _u+PCB_FPCTX,a0 | address of FP savearea
tstb a0@ | null state frame?
clrw d0 | no, need to tweak BIU
movb a0@(1),d0 | get frame size
bset #3,a0@(0,d0:w) | set exc_pend bit of BIU
.word 0xf227,0xa800 | fmovem fpsr,sp@- (code arg)
.word 0xf350 | frestore a0@
movl #T_FPERR,sp@- | push type arg
lea sp@(12),sp | pop value args
movl sp@(60),a0 | restore
moveml sp@+,#0x7FFF | and remaining user registers
addql #6,sp | pop SSP and align word
jra _badtrap | treat as an unexpected trap
movl usp,a0 | get and save
movl a0,sp@(60) | the user stack pointer
movl d0,sp@- | push trap type
lea sp@(12),sp | pop value args
movl sp@(60),a0 | restore
moveml sp@+,#0x7FFF | restore most user regs
addql #6,sp | pop SP and pad word
clrw sp@- | pad SR to longword
moveml #0xFFFF,sp@- | save user registers
movl usp,a0 | save the user SP
movl a0,sp@(60) | in the savearea
movl d0,sp@- | push syscall number
jbsr _syscall | handle it
addql #4,sp | pop syscall arg
movl sp@(60),a0 | grab and restore
moveml sp@+,#0x7FFF | restore most registers
addql #6,sp | pop SSP and align word
* Routines for traps 1 and 2. The meaning of the two traps depends
* on whether we are an HPUX compatible process or a native 4.3 process.
* Our native 4.3 implementation uses trap 1 as sigreturn() and trap 2
* as a breakpoint trap. HPUX uses trap 1 for a breakpoint, so we have
* to make adjustments so that trap 2 is used for sigreturn.
btst #PCB_TRCB,_u+PCB_FLAGS+1| being traced by an HPUX process?
jeq sigreturn | no, trap1 is sigreturn
jra _trace | yes, trap1 is breakpoint
btst #PCB_TRCB,_u+PCB_FLAGS+1| being traced by an HPUX process?
jeq _trace | no, trap2 is breakpoint
jra sigreturn | yes, trap2 is sigreturn
* - trace traps for SUN binaries (not fully supported yet)
* We just pass it on and let trap() sort it all out
* Hit a breakpoint (trap 1 or 2) instruction.
* Push the code and treat as a normal fault.
* The sigreturn() syscall comes here. It requires special handling
* because we must open a hole in the stack to fill in the (possibly much
* larger) original stack frame.
lea sp@(-84),sp | leave enough space for largest frame
movl sp@(84),sp@ | move up current 8 byte frame
movw #0xFFFF,sp@- | default: must clean stack
moveml #0xFFFF,sp@- | save user registers
movl usp,a0 | save the user SP
movl a0,sp@(60) | in the savearea
movl #SYS_sigreturn,sp@- | push syscall number
jbsr _syscall | handle it
addql #4,sp | pop syscall#
movl sp@(60),a0 | grab and restore
lea sp@(64),a1 | pointer to HW frame
tstw a1@+ | do we need to clean up stack?
jeq Lsigr1 | no, just continue
movb a1@(6),d0 | grab format byte
lsrb #4,d0 | get rid of excess
cmpb #10,d0 | type 10 frame?
jne Lsigr2 | no, continue
movw #32,d1 | yes, frame size is 32 bytes
jne Lsigr3 | no, continue
movw #20,d1 | yes, frame size is 20 bytes
jne Lsigr4 | no, continue
movw #12,d1 | yes, frame size is 12 bytes
movw #8,d1 | must be type 0/1, size is 8 bytes
lea a1@(92),a0 | destination
lsrw #1,d1 | convert to word count
subqw #1,d1 | minus 1 for dbf
movw a1@-,a0@- | copy a word
dbf d1,Lsigrlp | continue
movl a0,a1 | new HW frame base
movl a1,sp@(60) | new SP value
moveml sp@+,#0x7FFF | restore user registers
* All DIO device interrupts are auto-vectored. Most can be configured
* to interrupt in the range IPL3 to IPL5. Here are our assignments:
* Level 0: Spurious: ignored.
* Level 3: Internal HP-IB
* Level 4: "Fast" HP-IBs, SCSI
* Level 5: DMA, Ethernet, Built-in RS232
* Level 7: Non-maskable: parity errors, RESET key
.globl _hilint, _intrhand, _hardclock, _nmihand
/* check for DMA first to reduce overhead */
movw sp@(24),d0 | use vector offset
andw #0xfff,d0 | sans frame type
addql #1,a0@(-0x60,d0:w) | to increment apropos counter
movw sr,sp@- | push current SR value
clrw sp@- | padded to longword
jbsr _intrhand | handle interrupt
.globl _panicstr, _regdump, _panic
tstl timebomb | set to go off?
jeq Lnobomb | no, skip it
subql #1,timebomb | decrement
jne Lnobomb | not ready to go off
moveml sp@+,#0x0303 | temporarily restore regs
cmpl #_u+NBPG+NBPG,sp | our we still in stack page?
jcc Lstackok | yes, continue normally
tstl _panicstr | have we paniced?
jne Lstackok | yes, do not re-panic
moveml sp@+,#0x0303 | no, temporarily restore regs
cmpl #_u+NBPG+0x400,sp | our we safely in redzone?
jcc Luseours | yes, panic with this stack
lea tmpstk,sp | no, switch to tmpstk
moveml #0xFFFF,sp@- | push all registers
movl sp,a0 | remember this spot
movl #256,sp@- | longword count
movl a0,sp@- | and reg pointer
jbsr _regdump | dump core
movl #Lstkrip,sp@- | push panic message
.asciz "k-stack overflow"
movb _IObase+CLKSR,d0 | read clock status
tstb _profon | profile clock on?
jeq Ltimer1 | no, then must be timer1 interrupt
btst #2,d0 | timer3 interrupt?
jeq Ltimer1 | no, must be timer1
movb _IObase+CLKMSB3,d1 | clear timer3 interrupt
lea sp@(16),a1 | get pointer to PS
movl d0,sp@- | save status so jsr will not clobber
movl a1@,sp@- | push padded PS
movl a1@(4),sp@- | push PC
jbsr _profclock | profclock(pc, ps)
btst #5,a1@(2) | saved PS in user mode?
jne Lttimer1 | no, go check timer1
tstl _u+U_PROFSCALE | process being profiled?
jeq Lttimer1 | no, go check timer1
movl d0,sp@- | save status so jsr will not clobber
jbsr _addupc | addupc(pc, &u.u_prof, 1)
lea sp@(12),sp | pop params
addql #1,_intrcnt+32 | add another profile clock interrupt
movl sp@+,d0 | get saved clock status
btst #0,d0 | timer1 interrupt?
jeq Ltimend | no, check state of kernel profiling
movb _IObase+CLKMSB1,d1 | clear timer1 interrupt
lea sp@(16),a1 | get pointer to PS
movl a1@,sp@- | push padded PS
movl a1@(4),sp@- | push PC
jbsr _hardclock | call generic clock int routine
addql #1,_intrcnt+28 | add another system clock interrupt
.globl _profiling, _startprofclock
tstl _profiling | kernel profiling desired?
jne Ltimdone | no, all done
bset #7,_profon | mark continuous timing
jne Ltimdone | was already enabled, all done
jbsr _startprofclock | else turn it on
moveml sp@+,#0x0303 | restore scratch regs
addql #2,sp | pop pad word
addql #1,_cnt+V_INTR | chalk up another interrupt
clrw sp@- | pad SR to longword
moveml #0xFFFF,sp@- | save registers
movl a0,sp@(60) | the user stack pointer
jbsr _nmihand | call handler
movl sp@(60),a0 | restore
moveml sp@+,#0x7FFF | and remaining registers
addql #6,sp | pop SSP and align word
* Emulation of VAX REI instruction.
* This code deals with checking for and servicing ASTs
* (profiling, scheduling) and software interrupts (network, softclock).
* We check for ASTs first, just like the VAX. To avoid excess overhead
* the T_ASTFLT handling code will also check for software interrupts so we
* do not have to do it here.
* This code is complicated by the fact that sendsig may have been called
* necessitating a stack cleanup. A cleanup should only be needed at this
* point for coprocessor mid-instruction frames (type 9), but we also test
* for bus error frames (type 10 and 11).
tstl _panicstr | have we paniced?
jne Ldorte | yes, do not make matters worse
btst #PCB_ASTB,_u+PCB_FLAGS+1| AST pending?
jeq Lchksir | no, go check for SIR
btst #5,sp@ | yes, are we returning to user mode?
jne Lchksir | no, go check for SIR
clrw sp@- | pad SR to longword
moveml #0xFFFF,sp@- | save all registers
movl a1,sp@(60) | the users SP
movl #T_ASTFLT,sp@- | type == async system trap
jbsr _trap | go handle it
lea sp@(12),sp | pop value args
movl sp@(60),a0 | restore
moveml sp@+,#0x7FFF | and all remaining registers
tstw sp@+ | do we need to clean up stack?
jeq Ldorte | no, just continue
btst #7,sp@(6) | type 9/10/11 frame?
jeq Ldorte | no, nothing to do
movw sp@,sp@(12) | yes, push down SR
movl sp@(2),sp@(14) | and PC
clrw sp@(18) | and mark as type 0 frame
lea sp@(12),sp | clean the excess
btst #4,sp@(6) | type 10?
movw sp@,sp@(24) | yes, push down SR
movl sp@(2),sp@(26) | and PC
clrw sp@(30) | and mark as type 0 frame
lea sp@(24),sp | clean the excess
movw sp@,sp@(84) | type 11, push down SR
movl sp@(2),sp@(86) | and PC
clrw sp@(90) | and mark as type 0 frame
lea sp@(84),sp | clean the excess
tstb _ssir | SIR pending?
jeq Ldorte | no, all done
movl d0,sp@- | need a scratch register
andw #PSL_IPL7,d0 | mask all but IPL
jne Lnosir | came from interrupt, no can do
movl sp@+,d0 | restore scratch register
movw #SPL1,sr | prevent others from servicing int
jeq Ldorte | yes, oh well...
clrw sp@- | pad SR to longword
moveml #0xFFFF,sp@- | save all registers
movl a1,sp@(60) | the users SP
movl #T_SSIR,sp@- | type == software interrupt
jbsr _trap | go handle it
lea sp@(12),sp | pop value args
movl sp@(60),a0 | restore
moveml sp@+,#0x7FFF | and all remaining registers
addql #6,sp | pop SSP and align word
movl sp@+,d0 | restore scratch register
* Mbmap, Usrptmap, and Usriomap are enlarged by CLSIZE entries
* as they are managed by resource maps starting with index 1 or CLSIZE.
* Usrptmap is allocated last so that we can also use the pad space up
* to eSysmap. (no point in wasting it!)
#define vaddr(x) x-_Sysmap/4*NBPG
#define SYSMAP(mname,vname,size) \
.globl _/**/mname,_/**/vname; \
_/**/vname = vaddr(_/**/mname)
#define ADDMAP(npte) .space npte*4
SYSMAP(Sysmap ,Sysbase ,SYSPTSIZE )
SYSMAP(Forkmap ,forkutl ,UPAGES )
SYSMAP(Xswapmap ,xswaputl ,UPAGES )
SYSMAP(Xswap2map,xswap2utl ,UPAGES )
SYSMAP(Swapmap ,swaputl ,UPAGES )
SYSMAP(Pushmap ,pushutl ,UPAGES )
SYSMAP(Vfmap ,vfutl ,UPAGES )
SYSMAP(CMAP1 ,CADDR1 ,1 )
SYSMAP(CMAP2 ,CADDR2 ,1 )
SYSMAP(msgbufmap,msgbuf ,MSGBUFPTECNT )
SYSMAP(Mbmap ,mbutl ,NMBCLUSTERS*MCLBYTES/NBPG+CLSIZE )
* This is the map used by the kernel memory allocator.
* It is expanded as necessary by the special features
* XXX: NEED way to compute kmem size from maxusers,
SYSMAP(kmempt ,kmembase ,NKMEMCLUSTERS*CLSIZE )
SYSMAP(ekmempt ,kmemlimit ,0 )
SYSMAP(IOmap ,IObase ,IOMAPSIZE ) /* map DIO space */
SYSMAP(eIOmap ,IOlimit ,0 )
#if defined(HP360) || defined(HP370)
SYSMAP(Grfmap ,grfregs ,1024 ) /* 340 @ SC132 */
SYSMAP(Usriomap ,usrio ,USRIOSIZE+CLSIZE ) /* for PHYSIO */
SYSMAP(Usrptmap ,usrpt ,USRPTSIZE+CLSIZE )
. = . + NBPG - 1 & -NBPG /* align to page boundry */
* System segment table. 1 page is sufficient to map the entire
* 4Gb address space. (funny how that works out...)
.globl _Syssize, _Usrptsize
_Syssize = eSysmap-_Sysmap/4
_Usrptsize = eSysmap-_Usrptmap/4
* A5 contains physical load point from boot
* VBR contains zero from ROM. Exceptions will continue to vector
* through ROM until MMU is turned on at which time they will vector
* through our table (vectors.s).
movw #PSL_HIGHIPL,sr | no interrupts
movl a5,a0@ | store start of physical memory
movc d0,cacr | clear and disable on-chip cache(s)
/* determine our CPU/MMU combo - check for all regardless of kernel config */
movl #0x200,d0 | data freeze bit
movc d0,cacr | only exists on 68030
movc cacr,d0 | read it back
jeq Lis68020 | yes, we have 68020
lea _mmutype,a0 | no, we have 68030
movl #-1,a0@ | set to reflect 68030 PMMU
movl #0x80,IOBASE+MMUCMD | set magic cookie
movl IOBASE+MMUCMD,d0 | read it back
btst #7,d0 | cookie still on?
jeq Lnot370 | no, 360 or 375
movl #0,IOBASE+MMUCMD | clear magic cookie
movl IOBASE+MMUCMD,d0 | read it back
jeq Lisa370 | no, must be a 370
movl #5,a0@ | yes, must be a 340
movl #3,a0@ | type is at least a 360
movl #0,IOBASE+MMUCMD | clear magic cookie2
movl IOBASE+MMUCMD,d0 | read it back
jeq Lstart1 | no, must be a 360
movl #6,a0@ | yes, must be a 345/375
movl #-1,a0@ | also has a physical address cache
movl #1,IOBASE+MMUCMD | a 68020, write HP MMU location
movl IOBASE+MMUCMD,d0 | read it back
jne Lishpmmu | yes, we have HP MMU
movl #1,a0@ | no, we have PMMU
movl #1,a0@ | and 330 CPU
lea _ectype,a0 | 320 or 350
movl #1,a0@ | both have a virtual address cache
movl #0x80,IOBASE+MMUCMD | set magic cookie
movl IOBASE+MMUCMD,d0 | read it back
btst #7,d0 | cookie still on?
jeq Lstart1 | no, just a 320
movl #0,IOBASE+MMUCMD | clear out MMU again
/* initialize source/destination control registers for movs */
moveq #FC_USERD,d0 | user space
movc d0,dfc | and destination of transfers
/* initialize proc. 0 (system) page table */
movl #_Sysmap,a0 | SYSPT map addr
/* text pages are read-only */
clrl d0 | assume load at VA 0
andl #PG_FRAME,d1 | convert to a page frame
orl #PG_RO+PG_V,d1 | mark as valid and RO
movl #_etext,a1 | go til end of text
addl #NBPG,d1 | increment page frame number
addl #NBPG,d0 | and address counter
jcs Lispt1 | no, keep going
/* data and bss are read/write */
andl #PG_FRAME,d1 | mask out old prot bits
orl #PG_RW+PG_V,d1 | mark as valid and RW
movl #_end,a1 | go til end of data/bss
addl #NBPG,d1 | increment page frame number
addl #NBPG,d0 | and address counter
jcs Lispt2 | no, keep going
/* invalidate remainder of system page table */
movl #eSysmap,a1 | end of map
movl #PG_NV,a0@+ | invalidate PTE
jcs Lispt3 | no, keep going
/* go back and initialize IOmap */
movl #_IOmap,a0 | IO map addr
movl #_eIOmap,a1 | end of map
movl #IOBASE,d1 | physical IO base
andl #PG_FRAME,d1 | mask to frame number
orl #PG_RW+PG_CI+PG_V,d1 | mark valid, RW and CI
addl #NBPG,d1 | increment page frame number
jcs Lispt4 | no, keep going
/* initialize proc. 0 (system) segment table */
movl #_Sysseg,a0 | segment table
movl #eSysmap-_Sysmap/NBPG*4,a1 | bytes of PTEs for Sysmap
addl a0,a1 | make an end count
movl #_Sysmap,d1 | system PT addr
andl #SG_FRAME,d1 | mask to frame number
orl #SG_RW+SG_V,d1 | mark as RW and valid
addl #NBPG,d1 | increment page frame number
jcs Lispt5 | no, keep going
/* invalidate the unused part */
movl #eSysseg,a1 | end of segment table
movl #SG_NV,a0@+ | invalidate STE
jcs Lispt6 | no, keep going
* Setup page table for process 0.
* We set up page table access for the kernel via Usrptmap (usrpt)
* and access to the u-area itself via Umap (u). First page beyond
* kernel BSS (d0) is used for proc0 page table. Next UPAGES pages
* following are for u-area.
addl a5,d0 | relocate PT address
andl #PG_FRAME,d1 | mask to page frame number
orl #PG_RW+PG_V,d1 | RW and valid
movl #_Usrptmap,a1 | get PT map address
movl d1,a1@ | validate PTE for proc0 PT
movl d0,a0 | base of proc0 PT
addl #NBPG,d0 | plus one page yields
movl d0,a2 | base of u-area
/* invalidate entire page table */
movl #PG_NV,a0@+ | invalidate PTE
jcs Liudot1 | no, keep going
/* now go back and validate u-area PTEs */
subl #HIGHPAGES*4,a0 | base of PTEs for u-area (p_addr)
addl #UPAGES*4,a1 | end of PTEs for u-area
movl d0,d1 | get base of u-area
andl #PG_FRAME,d1 | mask to page frame number
orl #PG_RW+PG_V,d1 | add valid and writable
movl #_Umap,a3 | address of u
movl d1,a0@+ | validate p_addr PTE
movl d1,a3@+ | validate u PTE
addl #NBPG,d1 | to next page
jcs Liudot2 | no, keep going
/* clear process 0 u-area */
addl #NBPG*UPAGES,d0 | end of u-area
jcs Lclru1 | no, keep going
movl a2,a4 | save addr of first avail page
* Since the kernel is not mapped logical == physical we must insure
* that when the MMU is turned on, all prefetched addresses (including
* the PC) are valid. In order guarentee that, we map the last page of
* memory logical == physical and load up that page with enough code to
* defeat the prefetch, then we execute the jump back to here.
* Is this all really necessary, or am I paranoid??
movl #_Sysseg,d1 | system segment table addr
movl #0x80000202,a0@ | nolimit + share global + 4 byte PTEs
movl d1,a0@(4) | + segtable address
.long 0xf0104800 | pmove a0@,srp
movl #0x80000002,a0@ | reinit upper half for CRP loads
lsrl d2,d1 | convert to page frame
movl d1,IOBASE+MMUSSTP | load in sysseg table register
movl #eSysseg-4,a1 | last entry in sysseg table
movl d0,d1 | use page after proc0 u for tmp PT
andl #SG_FRAME,d1 | mask to page frame
orl #SG_RW+SG_V,d1 | mark valid and writable
movl d1,a1@ | load in segment table
movl d0,a1 | page table address
addl #NBPG-4,a1 | move to last entry
movl #MAXADDR,d1 | address of last page of memory
andl #PG_FRAME,d1 | mask to page frame
orl #PG_RW+PG_V,d1 | mark valid and writable
movl d1,a1@ | store PTE in table
movl #Lhighcode,a1 | addr of high code
movl #Lehighcode,a3 | end addr
movw a1@+,a2@+ | copy a word
jcs Lcodecopy | no, keep going
movl #MMU_IEN+MMU_FPE,IOBASE+MMUCMD | enable 68881 and i-cache
movl #0x82c0aa00,a2@ | value to load TC with
.long 0xf0124000 | pmove a2@,tc
movl #0,IOBASE+MMUCMD | clear external cache
movl #MMU_ENAB,IOBASE+MMUCMD | turn on MMU
jmp Lenab1 | jmp to mapped code
* Should be running mapped from this point on
/* while the ROM scratch page is mapped, check for internal HP-IB in SYSFLAG */
btst #5,0xfffffed2 | internal HP-IB?
jeq Linitmem | yes, have HP-IB continue normally
clrl _internalhpib | no, clear associated address
movl #MAXADDR,d1 | last page
lsrl d2,d1 | convert to page (click) number
movl d1,_maxmem | save as maxmem
movl _lowram,d0 | lowram value from ROM via boot
lsrl d2,d0 | convert to page number
subl d0,d1 | compute amount of RAM present
movl d1,_freemem | save as freemem
movl d1,_physmem | and physmem
/* initialize (slightly) the pcb */
movl #_u,a1 | proc0 u-area
addl #UPAGES*NBPG-4,sp | set kernel stack to end of u-area
movl a2,usp | init user SP
movl #_usrpt,a1@(PCB_P0BR) | p0br: SVA of text/data user PT
clrl a1@(PCB_P0LR) | p0lr: 0 (does not really exist)
movl #_usrpt+NBPG,d0 | addr of end of PT
subl #P1PAGES*4,d0 | backup size of P1 region
movl d0,a1@(PCB_P1BR) | p1br: P1PAGES from end of PT
movl #P1PAGES-HIGHPAGES,a1@(PCB_P1LR) | p1lr: vax style
movl #CLSIZE,a1@(PCB_SZPT) | page table size
clrw a1@(PCB_FLAGS) | clear flags
clrl a1@(PCB_FPCTX) | ensure null FP context
jbsr _m68881_restore | restore it (does not kill a1)
addl #PCB_SIGC,a1 | address of proc0 sig trampoline code
movl #Lsigcode,a2 | address of sig trampoline proto
cmpl #Lesigcode,a2 | done yet?
jcs Lsigc | no, keep going
/* flush TLB and turn on caches */
movl #PG_NV,eSysseg-4 | invalidate last page
jbsr _TBIA | invalidate TLB
movc d0,cacr | clear cache(s)
orl #MMU_CEN,_IObase+MMUCMD | turn on external cache
/* final setup for C code */
movw #PSL_LOWIPL,sr | lower SPL
movl d7,_boothowto | save reboot flags
movl d6,_bootdev | and boot device
movl a4,d1 | addr of first available RAM
lsrl d2,d1 | convert to click
movl d1,sp@- | param to main
jbsr _main | main(firstaddr)
/* proc[1] == init now running here;
* create a null exception frame and return to user mode in icode
clrw sp@- | vector offset/frame type
clrl sp@- | return to icode location 0
movw #PSL_USER,sp@- | in user mode
* Signal "trampoline" code (18 bytes). Invoked from RTE setup by sendsig().
* sp+4 signal specific code
* sp+8 pointer to signal context frame (scp)
* sp+12 address of handler
* sp+16 saved hardware state
* scp+0-> beginning of signal context frame
movl sp@(12),a0 | signal handler addr (4 bytes)
jsr a0@ | call signal handler (2 bytes)
addql #4,sp | pop signo (2 bytes)
trap #1 | special syscall entry (2 bytes)
movl d0,sp@(4) | save errno (4 bytes)
moveq #1,d0 | syscall == exit (2 bytes)
trap #0 | exit(errno) (2 bytes)
* Icode is copied out to process 1 to exec init.
* If the exec fails, process 1 exits.
.globl _icode,_initflags,_szicode
.globl _/**/name; _/**/name: link a6,#0; jbsr mcount; unlk a6
#define ALTENTRY(name, rname) \
ENTRY(name); jra rname+12
.globl _/**/name; _/**/name: jbsr mcount
#define ALTENTRY(name, rname) \
.globl _/**/name; _/**/name:
#define ALTENTRY(name, rname) \
.globl _/**/name; _/**/name:
* update profiling information for the user
* addupc(pc, &u.u_prof, ticks)
movl a2,sp@- | scratch register
movl sp@(12),a2 | get &u.u_prof
movl sp@(8),d0 | get user pc
subl a2@(8),d0 | pc -= pr->pr_off
jlt Lauexit | less than 0, skip it
movl a2@(12),d1 | get pr->pr_scale
mulul d1,d0 | pc /= scale
cmpl a2@(4),d0 | too big for buffer?
jge Lauexit | yes, screw it
addl a2@,d0 | no, add base
movl d0,sp@- | push address
jbsr _fusword | grab old value
movl sp@+,a0 | grab address back
jeq Lauerror | no, skip out
addw sp@(18),d0 | add tick to current value
movl d0,sp@- | push value
movl a0,sp@- | push address
jbsr _susword | write back new value
jeq Lauexit | no, all done
clrl a2@(12) | clear scale (turn off prof)
movl sp@+,a2 | restore scratch reg
* copyinstr(fromaddr, toaddr, maxlength, &lencopied)
* Copy a null terminated string from the user address space into
* the kernel address space.
* NOTE: maxlength must be < 64K
movl sp@(4),a0 | a0 = fromaddr
movl sp@(8),a1 | a1 = toaddr
movw sp@(14),d0 | d0 = maxlength
jlt Lcisflt1 | negative count, error
jeq Lcisdone | zero count, all done
movl #Lcisflt1,_u+PCB_ONFAULT | set up to catch faults
subql #1,d0 | set up for dbeq
movsb a0@+,d1 | grab a byte
dbeq d0,Lcisloop | if !null and more, continue
jne Lcisflt2 | ran out of room, error
moveq #0,d0 | got a null, all done
tstl sp@(16) | return length desired?
jeq Lcisret | no, just return
subl sp@(4),a0 | determine how much was copied
movl sp@(16),a1 | return location
clrl _u+PCB_ONFAULT | clear fault addr
moveq #EFAULT,d0 | copy fault
moveq #ENOENT,d0 | ran out of space
* copyoutstr(fromaddr, toaddr, maxlength, &lencopied)
* Copy a null terminated string from the kernel
* address space to the user address space.
* NOTE: maxlength must be < 64K
movl sp@(4),a0 | a0 = fromaddr
movl sp@(8),a1 | a1 = toaddr
movw sp@(14),d0 | d0 = maxlength
jlt Lcosflt1 | negative count, error
jeq Lcosdone | zero count, all done
movl #Lcosflt1,_u+PCB_ONFAULT| set up to catch faults
subql #1,d0 | set up for dbeq
movb a0@+,d1 | grab a byte
dbeq d0,Lcosloop | if !null and more, continue
jne Lcosflt2 | ran out of room, error
moveq #0,d0 | got a null, all done
tstl sp@(16) | return length desired?
jeq Lcosret | no, just return
subl sp@(4),a0 | determine how much was copied
movl sp@(16),a1 | return location
clrl _u+PCB_ONFAULT | clear fault addr
moveq #EFAULT,d0 | copy fault
moveq #ENOENT,d0 | ran out of space
* copystr(fromaddr, toaddr, maxlength, &lencopied)
* Copy a null terminated string from one point to another in
* the kernel address space.
* NOTE: maxlength must be < 64K
movl sp@(4),a0 | a0 = fromaddr
movl sp@(8),a1 | a1 = toaddr
movw sp@(14),d0 | d0 = maxlength
jlt Lcsflt1 | negative count, error
jeq Lcsdone | zero count, all done
movl #Lcsflt1,_u+PCB_ONFAULT | set up to catch faults
subql #1,d0 | set up for dbeq
movb a0@+,a1@+ | copy a byte
dbeq d0,Lcsloop | if !null and more, continue
jne Lcsflt2 | ran out of room, error
moveq #0,d0 | got a null, all done
tstl sp@(16) | return length desired?
jeq Lcsret | no, just return
subl sp@(4),a0 | determine how much was copied
movl sp@(16),a1 | return location
clrl _u+PCB_ONFAULT | clear fault addr
moveq #EFAULT,d0 | copy fault
moveq #ENOENT,d0 | ran out of space
* Copy specified amount of data from user space into the kernel.
* NOTE: len must be < 64K
movl d2,sp@- | scratch register
movl #Lciflt,_u+PCB_ONFAULT | catch faults
movl sp@(16),d2 | check count
jlt Lciflt | negative, error
movl sp@(8),a0 | src address
movl sp@(12),a1 | dest address
btst #0,d0 | src address odd?
jeq Lcieven | no, go check dest
movsb a0@+,d1 | yes, get a byte
movb d1,a1@+ | put a byte
subql #1,d2 | adjust count
jeq Lcidone | exit if done
btst #0,d0 | dest address odd?
jne Lcibyte | yes, must copy by bytes
movl d2,d0 | no, get count
lsrl #2,d0 | convert to longwords
jeq Lcibyte | no longwords, copy bytes
subql #1,d0 | set up for dbf
movsl a0@+,d1 | get a long
movl d1,a1@+ | put a long
dbf d0,Lcilloop | til done
andl #3,d2 | what remains
subql #1,d2 | set up for dbf
movsb a0@+,d1 | get a byte
movb d1,a1@+ | put a byte
dbf d2,Lcibloop | til done
clrl _u+PCB_ONFAULT | reset fault catcher
movl sp@+,d2 | restore scratch reg
moveq #EFAULT,d0 | got a fault
* Copy specified amount of data from kernel to the user space
* NOTE: len must be < 64K
movl d2,sp@- | scratch register
movl #Lcoflt,_u+PCB_ONFAULT | catch faults
movl sp@(16),d2 | check count
jlt Lcoflt | negative, error
movl sp@(8),a0 | src address
movl sp@(12),a1 | dest address
btst #0,d0 | src address odd?
jeq Lcoeven | no, go check dest
movb a0@+,d1 | yes, get a byte
movsb d1,a1@+ | put a byte
subql #1,d2 | adjust count
jeq Lcodone | exit if done
btst #0,d0 | dest address odd?
jne Lcobyte | yes, must copy by bytes
movl d2,d0 | no, get count
lsrl #2,d0 | convert to longwords
jeq Lcobyte | no longwords, copy bytes
subql #1,d0 | set up for dbf
movl a0@+,d1 | get a long
movsl d1,a1@+ | put a long
dbf d0,Lcolloop | til done
andl #3,d2 | what remains
subql #1,d2 | set up for dbf
movb a0@+,d1 | get a byte
movsb d1,a1@+ | put a byte
dbf d2,Lcobloop | til done
clrl _u+PCB_ONFAULT | reset fault catcher
movl sp@+,d2 | restore scratch reg
moveq #EFAULT,d0 | got a fault
ALTENTRY(savectx, _setjmp)
movl sp@(4),a0 | savearea pointer
moveml #0xFCFC,a0@ | save d2-d7/a2-a7
movl sp@,a0@(48) | and return address
movl sp@(4),a0 | savearea pointer
lea a0@(40),a0 | skip regs we do not save
movl sp@,a0@ | and return address
* The following primitives manipulate the run queues.
* _whichqs tells which of the 32 queues _qs
* have processes in them. Setrq puts processes into queues, Remrq
* removes them from queues. The running process is on no queue,
* other processes are on a queue related to p->p_pri, divided by 4
* actually to shrink the 0-127 range of priorities into the 32 available
.globl _whichqs,_qs,_cnt,_panic
* Call should be made at spl6(), and p->p_stat should be SRUN
movl a1@(P_RLINK),a0@(P_RLINK)
* Call should be made at spl6().
movl a0@(P_RLINK),a1@(P_RLINK)
movl a0@(P_LINK),a1@(P_LINK)
* When no processes are on the runq, Swtch branches to idle
* to wait for something to come ready.
movl a0@(P_LINK),a1@(P_LINK)
movl a0@(P_RLINK),a1@(P_RLINK)
* NOTE: on the PMMU we attempt to avoid flushing the entire TAC.
* The effort involved in selective flushing may not be worth it,
* maybe we should just flush the whole thing?
movl a0,a1@(PCB_USP) | and save it
moveml #0xFCFC,a1@(PCB_REGS) | save non-scratch registers
lea a1@(PCB_FPCTX),a0 | pointer to FP save area
tstb a0@ | null state frame?
jeq Lresnofpsave | yes, all done
.word 0xf228,0xf0ff,0x00d8 | fmovem fp0-fp7,a0@(216)
.word 0xf228,0xbc00,0x0138 | fmovem fpcr/fpsr/fpiar,a0@(312)
movw #SPL6,sr | protect against clock interrupts
bclr #0,_profon | clear user profiling bit, was set?
jeq Lskipoff | no, clock off or doing kernel only
tstb _profon | kernel profiling also enabled?
jlt Lskipoff | yes, nothing more to do
movb #0,_IObase+CLKCR2 | no, just user, select CR3
movb #0,_IObase+CLKCR3 | and turn it off
movl _CMAP2,a1@(PCB_CMAP2) | save temporary map PTE
movw #PSL_HIGHIPL,sr | go crit while changing PTEs
lea tmpstk,sp | now goto a tmp stack for NMI
movl d0,a0 | address of new context
lea _Umap,a1 | address of PTEs for u
moveq #UPAGES-1,d0 | sizeof u
andl #~PG_PROT,d1 | mask out old protection
orl #PG_RW+PG_V,d1 | ensure valid and writable
movl d1,a1@+ | load it up
movc d0,cacr | invalidate cache(s)
#if defined(HP330) || defined(HP360) || defined(HP370)
jmi Lnot68851a | must flush all on 68030 MMU
tstl fullflush | 68851, conservative?
jne Lnot68851a | yes, go flush all
.long 0xf0003494 | no, pflushs #4,#4 (flush super side)
.long 0xf0002400 | pflusha
movl a1@(PCB_USTP),d0 | get USTP
lsll d1,d0 | convert to addr
lea _protorp,a0 | CRP prototype
movl d0,a0@(4) | stash USTP
.long 0xf0104C00 | pmove a0@,crp
#if defined(HP320) || defined(HP350)
movl _IObase+MMUTBINVAL,d1 | invalidate TLB
tstl _ectype | got external VAC?
movl #_IObase+MMUCMD,a0 | addr of MMU command register
andl #~MMU_CEN,a0@ | toggle cache enable
orl #MMU_CEN,a0@ | to clear data cache
movl a1@(PCB_USTP),_IObase+MMUUSTP | context switch
movl a1@(U_PROCP),a0 | u.u_procp
bclr #SPTECHGB-24,a0@(P_FLAG)| clear SPTECHG bit
jeq Lnot68851b | if set need to flush user TLB
tstl _mmutype | 68851 PMMU?
jle Lnot68851b | no, skip
.long 0xf0003490 | pflushs #0,#4
movl a1@(PCB_CMAP2),_CMAP2 | reload tmp map
moveml a1@(PCB_REGS),#0xFCFC | and registers
tstl a1@(U_PROFSCALE) | process being profiled?
jeq Lskipon | no, do nothing
orb #1,_profon | turn on user profiling bit
jlt Lskipon | already profiling kernel, all done
lea _IObase+CLKMSB3,a0 | address timer 3 counter
movl _profint,d1 | profiling interval
movepw d1,a0@(0) | set interval
movb #0,_IObase+CLKCR2 | select CR3
movb #64,_IObase+CLKCR3 | turn it on
lea a1@(PCB_FPCTX),a0 | pointer to FP save area
tstb a0@ | null state frame?
jeq Lresfprest | yes, easy
.word 0xf228,0x9c00,0x0138 | fmovem a0@(312),fpcr/fpsr/fpiar
.word 0xf228,0xd0ff,0x00d8 | fmovem a0@(216),fp0-fp7
.word 0xf350 | frestore a0@
tstl a1@(PCB_SSWAP) | do an alternate return?
jne Lres3 | yes, go reload regs
movw a1@(PCB_PS),sr | no, restore PS
movl a1@(PCB_SSWAP),a0 | addr of saved context
clrl a1@(PCB_SSWAP) | clear flag
moveml a0@+,#0x7CFC | restore registers
jge Lres4 | ...must be popping, yes?
lea tmpstk,sp | no! set up a legit stack
movl #Lres5,sp@- | push a panic message
moveq #1,d0 | arrange for non-zero return
movw #PSL_LOWIPL,sr | lower SPL
* {fu,su},{byte,sword,word}
ALTENTRY(fuiword, _fuword)
movl sp@(4),d0 | address to read
jne Lfserr | yes, a fault
movl #Lfserr,_u+PCB_ONFAULT | where to return to on a fault
movsl a0@,d0 | do read from user space
btst #0,d0 | is address odd?
jne Lfserr | yes, a fault
movl #Lfserr,_u+PCB_ONFAULT | where to return to on a fault
movl d0,a0 | address to read
movsw a0@,d0 | do read from user space
ALTENTRY(fuibyte, _fubyte)
movl #Lfserr,_u+PCB_ONFAULT | where to return to on a fault
movl sp@(4),a0 | address to read
movsb a0@,d0 | do read from user space
moveq #-1,d0 | error indicator
clrl _u+PCB_ONFAULT | clear fault address
ALTENTRY(suiword, _suword)
movl sp@(4),d0 | address to write
jne Lfserr | yes, a fault
movl #Lfserr,_u+PCB_ONFAULT | where to return to on a fault
movl d0,a0 | address to write
movl sp@(8),d0 | value to put there
movsl d0,a0@ | do write to user space
moveq #0,d0 | indicate no fault
movl sp@(4),d0 | address to write
jne Lfserr | yes, a fault
movl #Lfserr,_u+PCB_ONFAULT | where to return to on a fault
movl d0,a0 | address to write
movw sp@(10),d0 | value to put there
movsw d0,a0@ | do write to user space
moveq #0,d0 | indicate no fault
ALTENTRY(suibyte, _subyte)
movl #Lfserr,_u+PCB_ONFAULT | where to return to on a fault
movl sp@(4),a0 | address to write
movb sp@(11),d0 | value to put there
movsb d0,a0@ | do write to user space
moveq #0,d0 | indicate no fault
* Copy 1 relocation unit (NBPG bytes)
* from user virtual address to physical address
movl sp@(8),d0 | destination page number
lsll d1,d0 | convert to address
orl #PG_CI+PG_RW+PG_V,d0 | make sure valid and writable
movl d0,_CMAP2 | load in page table
movl #_CADDR2,sp@- | destination kernel VA
jbsr _TBIS | invalidate any old mapping
movl #_CADDR2,a1 | destination addr
movl sp@(4),a0 | source addr
movl #NBPG/4-1,d0 | count
movl #Lcpydone,_u+PCB_ONFAULT | where to go on a fault
movsl a0@+,d1 | read longword
movl d1,a1@+ | write longword
dbf d0,Lcpyloop | continue until done
clrl _u+PCB_ONFAULT | clear error catch
* zero out physical memory
* specified in relocation units (NBPG bytes)
movl sp@(4),d0 | destination page number
lsll d1,d0 | convert to address
orl #PG_CI+PG_RW+PG_V,d0 | make sure valid and writable
movl d0,_CMAP1 | load in page map
movl #_CADDR1,sp@- | destination kernel VA
jbsr _TBIS | invalidate any old mapping
movl #_CADDR1,a1 | destination addr
movl #NBPG/4-1,d0 | count
/* simple clear loop is fastest on 68020 */
clrl a1@+ | clear a longword
dbf d0,Lclrloop | continue til done
#if defined(HP330) || defined(HP360) || defined(HP370)
.long 0xf0002400 | no, pflusha
#if defined(HP360) || defined(HP370)
jpl Lmc68851a | 68851 implies no d-cache
movc d0,cacr | invalidate on-chip d-cache
#if defined(HP320) || defined(HP350)
movl _IObase+MMUTBINVAL,sp@- | do not ask me, this
addql #4,sp | is how hpux does it
jra __DCIA | XXX: invalidate entire cache
* Invalidate any TLB entry for given VA (TB Invalidate Single)
tstl fullflush | being conservative?
jne __TBIA | yes, flush entire TLB
#if defined(HP330) || defined(HP360) || defined(HP370)
movl sp@(4),a0 | get addr to flush
#if defined(HP360) || defined(HP370)
jpl Lmc68851b | is 68851?
.long 0xf0103810 | pflush #0,#0,a0@
movc d0,cacr | invalidate on-chip data cache
.long 0xf0103c10 | pflushs #0,#0,a0@
#if defined(HP320) || defined(HP350)
movl sp@(4),d0 | VA to invalidate
movw #PSL_HIGHIPL,sr | while in purge space
moveq #FC_PURGE,d0 | change address space
movc d0,dfc | for destination
moveq #0,d0 | zero to invalidate?
moveq #FC_USERD,d0 | back to old
movc d0,dfc | address space
* Invalidate supervisor side of TLB
tstl fullflush | being conservative?
jne __TBIA | yes, flush everything
#if defined(HP330) || defined(HP360) || defined(HP370)
#if defined(HP360) || defined(HP370)
.long 0xf0003094 | pflush #4,#4
movc d0,cacr | invalidate on-chip d-cache
.long 0xf0003494 | pflushs #4,#4
#if defined(HP320) || defined(HP350)
movl d0,_IObase+MMUTBINVAL | HP magic
jra __DCIS | XXX: invalidate entire sup. cache
* Invalidate user side of TLB
tstl fullflush | being conservative?
jne __TBIA | yes, flush everything
#if defined(HP330) || defined(HP360) || defined(HP370)
#if defined(HP360) || defined(HP370)
.long 0xf0003090 | pflush #0,#4
movc d0,cacr | invalidate on-chip d-cache
.long 0xf0003490 | pflushs #0,#4
#if defined(HP320) || defined(HP350)
movl d0,_IObase+MMUTBINVAL | HP magic
jra __DCIU | XXX: invalidate entire user cache
* Invalidate instruction cache
movc d0,cacr | invalidate i-cache
* HP external cache allows for invalidation of user/supervisor portions.
#if defined(HP360) || defined(HP370)
movc d0,cacr | invalidate on-chip d-cache
#if defined(HP320) || defined(HP350)
tstl _ectype | got external VAC?
jle Lnocache2 | no, all done
movl #_IObase+MMUCMD,a0 | MMU control reg
andl #~MMU_CEN,a0@ | disable cache
orl #MMU_CEN,a0@ | reenable cache
#if defined(HP360) || defined(HP370)
movc d0,cacr | invalidate on-chip d-cache
#if defined(HP320) || defined(HP350)
tstl _ectype | got external VAC?
jle Lnocache3 | no, all done
movl _IObase+MMUSSTP,d0 | read the supervisor STP
movl d0,_IObase+MMUSSTP | write it back
#if defined(HP360) || defined(HP370)
movc d0,cacr | invalidate on-chip d-cache
#if defined(HP320) || defined(HP350)
tstl _ectype | got external VAC?
jle Lnocache4 | no, all done
movl _IObase+MMUUSTP,d0 | read the user STP
movl d0,_IObase+MMUUSTP | write it back
tstl _ectype | got external PAC?
jge Lnocache6 | no, all done
movl #_IObase+MMUCMD,a0 | MMU control reg
andl #~MMU_CEN,a0@ | disable cache
orl #MMU_CEN,a0@ | reenable cache
* Load a new user segment table pointer.
#if defined(HP330) || defined(HP360) || defined(HP370)
movl sp@(4),d0 | new USTP
lsll d1,d0 | convert to addr
lea _protorp,a0 | CRP prototype
movl d0,a0@(4) | stash USTP
.long 0xf0104C00 | pmove a0@,crp
movc d0,cacr | invalidate on-chip d-cache
rts | since pmove flushes TLB
#if defined(HP320) || defined(HP350)
movl sp@(4),_IObase+MMUUSTP | load a new USTP
* Flush any hardware context associated with given USTP.
* Only does something for HP330 where we must flush RPT
* and ATC entries in PMMU.
tstl _mmutype | 68851 PMMU?
jle Lnot68851 | no, nothing to do
movl sp@(4),d0 | get USTP to flush
lsll d1,d0 | convert to address
movl d0,_protorp+4 | stash USTP
.long 0xf039a000,_protorp | pflushr _protorp
#if defined(HP330) || defined(HP360) || defined(HP370)
movl sp@(4),a0 | address to load
.long 0xf0102011 | pload #1,a0@
* Set processor priority level calls. Most could (should) be replaced
* by inline asm expansions. However, SPL0 and SPLX require special
* handling. If we are returning to the base processor priority (SPL0)
* we need to check for our emulated software interrupts.
movw sr,d0 | get old SR for return
movw #PSL_LOWIPL,sr | restore new SR
movw sr,d0 | get current SR for return
movw sp@(6),d1 | get new value
movw d1,sr | restore new SR
andw #PSL_IPL7,d1 | mask all but PSL_IPL
jne Lspldone | non-zero, all done
tstb _ssir | software interrupt pending?
jeq Lspldone | no, all done
subql #4,sp | make room for RTE frame
movl sp@(4),sp@(2) | position return address
clrw sp@(6) | set frame type 0
movw #PSL_LOWIPL,sp@ | and new SR
jra Lgotsir | go handle it
ALTENTRY(splsoftclock, _spl1)
ALTENTRY(splclock, _spl6)
* Special versions of splhigh and splx called by mcount().
* Note that __splx does not check for software interrupts.
movw sr,d0 | get current SR for return
movw sp@(6),d1 | get new value
movw d1,sr | restore new SR
movw #PSL_HIGHIPL,sr | atomic
movl sp@(8),a0 | where to insert (after)
movl sp@(4),a1 | element to insert (e)
movl a0@,a1@ | e->next = after->next
movl a0,a1@(4) | e->prev = after
movl a1,a0@ | after->next = e
movl a1,a0@(4) | e->next->prev = e
movw #PSL_HIGHIPL,sr | atomic
movl sp@(4),a0 | element to remove (e)
movl a0,a1@(4) | e->next->prev = e->prev
movl a1,a0@ | e->prev->next = e->next
clrl a0@+; clrl a0@+; clrl a0@+; clrl a0@+;
clrl a0@+; clrl a0@+; clrl a0@+; clrl a0@+;
addql #1,d0 | increment count
jne Lslloop | no, keep going
* WARNING! This guy only works with counts up to 64K
movl sp@(4),a0 | string 1
movl sp@(8),a1 | string 2
jeq Lcmpdone | if zero, nothing to do
subqw #1,d0 | set up for DBcc loop
dbne d0,Lcmploop | yes, keep going
addqw #1,d0 | +1 gives zero on match
* {ov}bcopy(from, to, len)
* Works for counts up to 128K.
ALTENTRY(ovbcopy, _bcopy)
movl sp@(12),d0 | get count
jeq Lcpyexit | if zero, return
movl sp@(4),a0 | src address
movl sp@(8),a1 | dest address
cmpl a1,a0 | src before dest?
jlt Lcpyback | yes, copy backwards (avoids overlap)
btst #0,d1 | src address odd?
jeq Lcfeven | no, go check dest
movb a0@+,a1@+ | yes, copy a byte
subql #1,d0 | update count
jeq Lcpyexit | exit if done
btst #0,d1 | dest address odd?
jne Lcfbyte | yes, must copy by bytes
movl d0,d1 | no, get count
lsrl #2,d1 | convert to longwords
jeq Lcfbyte | no longwords, copy bytes
subql #1,d1 | set up for dbf
movl a0@+,a1@+ | copy longwords
dbf d1,Lcflloop | til done
andl #3,d0 | get remaining count
jeq Lcpyexit | done if none
subql #1,d0 | set up for dbf
movb a0@+,a1@+ | copy bytes
dbf d0,Lcfbloop | til done
addl d0,a0 | add count to src
addl d0,a1 | add count to dest
btst #0,d1 | src address odd?
jeq Lcbeven | no, go check dest
movb a0@-,a1@- | yes, copy a byte
subql #1,d0 | update count
jeq Lcpyexit | exit if done
btst #0,d1 | dest address odd?
jne Lcbbyte | yes, must copy by bytes
movl d0,d1 | no, get count
lsrl #2,d1 | convert to longwords
jeq Lcbbyte | no longwords, copy bytes
subql #1,d1 | set up for dbf
movl a0@-,a1@- | copy longwords
dbf d1,Lcblloop | til done
andl #3,d0 | get remaining count
jeq Lcpyexit | done if none
subql #1,d0 | set up for dbf
movb a0@-,a1@- | copy bytes
dbf d0,Lcbbloop | til done
* Emulate fancy VAX string operations:
* scanc(count, startc, table, mask)
* skpc(mask, count, startc)
* locc(mask, count, startc)
movl sp@(4),d0 | get length
jeq Lscdone | nothing to do, return
movl sp@(8),a0 | start of scan
movl sp@(12),a1 | table to compare with
movb sp@(19),d1 | and mask to use
movw d2,sp@- | need a scratch register
subqw #1,d0 | adjust for dbra
movb a0@+,d2 | get character
movb a1@(0,d2:w),d2 | get table entry
dbne d0,Lscloop | keep going til no more or non-zero
addqw #1,d0 | overshot by one
movw sp@+,d2 | restore scratch
movl sp@(8),d0 | get length
jeq Lskdone | nothing to do, return
movb sp@(7),d1 | mask to use
movl sp@(12),a0 | where to start
subqw #1,d0 | adjust for dbcc
cmpb a0@+,d1 | compate with mask
dbne d0,Lskloop | keep going til no more or zero
addqw #1,d0 | overshot by one
movl sp@(8),d0 | get length
jeq Llcdone | nothing to do, return
movb sp@(7),d1 | mask to use
movl sp@(12),a0 | where to start
subqw #1,d0 | adjust for dbcc
cmpb a0@+,d1 | compate with mask
dbeq d0,Llcloop | keep going til no more or non-zero
addqw #1,d0 | overshot by one
* Emulate VAX FFS (find first set) instruction.
* Save and restore 68881 state.
* Pretty awful looking since our assembler does not
* recognize FP mnemonics.
movl sp@(4),a0 | save area pointer
tstb a0@ | null state frame?
jeq Lm68881sdone | yes, all done
.word 0xf228,0xf0ff,0x00d8 | fmovem fp0-fp7,a0@(216)
.word 0xf228,0xbc00,0x0138 | fmovem fpcr/fpsr/fpiar,a0@(312)
movl sp@(4),a0 | save area pointer
tstb a0@ | null state frame?
jeq Lm68881rdone | yes, easy
.word 0xf228,0x9c00,0x0138 | fmovem a0@(312),fpcr/fpsr/fpiar
.word 0xf228,0xd0ff,0x00d8 | fmovem a0@(216),fp0-fp7
.word 0xf350 | frestore a0@
* Handle the nitty-gritty of rebooting the machine.
* Basically we just turn off the MMU and jump to the appropriate ROM routine.
* Note that we must be running in an address range that is mapped one-to-one
* logical to physical so that the PC is still valid immediately after the MMU
movc d0,cacr | disable on-chip cache(s)
#if defined(HP320) || defined(HP350) || defined(HP370)
andl #~MMU_CEN,_IObase+MMUCMD| disable external cache
/* one-to-one map the last page of memory */
movl #MAXADDR,d0 | last page of RAM used to pass params
orl #PG_RW+PG_V,d0 | create a PTE
movl d0,_mmap | to access that page
jbsr _TBIA | invalidate TLB
movl #MAXADDR,d0 | last page of RAM is also used
orl #SG_RW+SG_V,d0 | as the page table for itself
movl d0,eSysseg-4 | (ok since it needs only last word)
movl _vmmap+NBPG-4,d2 | save old contents
movl _mmap,_vmmap+NBPG-4 | store PTE in new page table
jbsr _TBIA | invalidate again
lea MAXADDR,a0 | can now access last page
movl _boothowto,a0@+ | store howto
movl _bootdev,a0@+ | and devtype
lea Lbootcode,a1 | start of boot code
lea Lebootcode,a3 | end of boot code
movw a1@+,a0@+ | copy a word
jcs Lbootcopy | no, keep going
jmp MAXADDR+8 | jump to last page
lea MAXADDR+0x800,sp | physical SP in case of NMI
#if defined(HP330) || defined(HP360) || defined(HP370)
movl #0,a0@ | value for pmove to TC (turn off MMU)
.long 0xf0104000 | pmove a0@,tc
movl d2,MAXADDR+NBPG-4 | restore old high page contents
jmp 0x1A4 | goto REQ_REBOOT
#if defined(HP320) || defined(HP350)
movl #0xFFFF0000,_IObase+MMUCMD | totally disable MMU
movl d2,MAXADDR+NBPG-4 | restore old high page contents
jmp 0x1A4 | goto REQ_REBOOT
.long 0 | default to HP MMU
.long 0,0 | prototype root pointer
.long 0 | external cache type, default to none
.long 1 | cold start flag
.globl _intrcnt,_eintrcnt,_intrnames,_eintrnames
.long 0,0,0,0,0,0,0,0,0,0