/*
 *	Copyright (c) 1988, 1989  Motorola Inc.
 *	All Rights Reserved
 *
 *	THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF MOTOROLA INC.
 *
 *	The copyright notice above does not evidence any
 *	actual or intended publication of such source code.
 */

dacchand:

The following code is used for data exception handling.  It was extracted
from the UNIX exception handling code written by the Motorola Computer Division.
The author has attached the following disclaimers:

1)  This code is substantially undebbuged.  Although it assembles without
error and he believes his exception handling algorithms are sound, this code
has had no actual exposure on 88000 hardware and only portions of it have been
debugged on the simulator.

2)  This is his first pass at the code.  To speed debugging on initial 
hardware, he has written the package with mainly simplicity of flow
in mind.  When we have stable hardware and some experience with a real
88000, he may rewrite these packages with speed of execution
in mind so this source may be rewritten in the future.


This code is not intended to be assembled into a working handler,
but it is intended to provide a logic flowchart which can be modified
to work on individual systems.

If there are any questions, comments, or concerns, please call

			Jeff Hopkins
			(512) 440-2617 


m88kvec.s follows: ===================================================

; Motorola M88000 vector table

	text
	global	_M88Kvec
_M88Kvec:
	global	M88Kvec
M88Kvec:
	br	nullvect	; 0 Reset vector
	stcr	r1,cr18	; nop

	br.n	interrupt	; 8 interrupt vector
	stcr	r1,cr18

	br.n	Xcaccflt	; 10 Code fault vector
	stcr	r1,cr18

	br.n	Xdaccflt	; 18 Data fault vector
	stcr	r1,cr18

	br.n	Xaddr		; 20 Misaligned access vector
	stcr	r1,cr18

	br.n	Xinst		; 28 Unimplemented instruction
	stcr	r1,cr18

	br.n	Xpriv		; 30 Privelege violation vector
	stcr	r1,cr18

	br.n	Xtbnd		; 38 Trap on bounds violation vector
	stcr	r1,cr18

	br.n	Xdivz		; 40 Integer divide by zero vector
	stcr	r1,cr18

	br.n	Xiovf		; 48 Integer overflow vector
	stcr	r1,cr18

	br.n	Xtrap		; 50 Unexpected exception.
	stcr	r1,cr18


;#########################################################################
Xdaccflt:
;
;  Control comes here when a bus error occurs.  This can be either due
; to an access of a faulted page or a write access of a write protected
; page.
;
;
;	stcr	r1,cr18		; This is actually in vector table.
	xcr	r2,r2,cr2
	bsr.n	getmsps		; Switch to master stack and save regs.
	set	r2,r2,1<PS_DEXC> ;  This bit is not set on only data faults.
;
;  getmsps handled the call to buserr() for us.
;  All we got to do is return.

	br	getregs		; Return to user process.


;#########################################################################
getmsps:
;    This subroutine will switch to the master stack (if not already on
;  the master stack) and save all registers on the stack.  Data fault 
; registers are also saved on the stack if a data fault is pending.
; buserr() is called, if neccessary, to complete the faulted access and
; fix up the register images on the stack.
;
;  Assumes:
;   r1 is saved in cr18
;   r2 and spsr are swapped.
;
;   Normally called as:
;	stcr	r1,cr18
;	bsr.n	getmsps
;	xcr	r2,r2,cr2
;
;   Returns:
;	r2  through r7 are preserved (argument registers.)
;	other registers may be trashed.
;
	bb1.n	PS_SUP,r2,ONSUP
	stcr	r31,cr20		; Save sp in scratch reg.
	ldcr	r31,cr17		; Get top of master stack.
ONSUP:
	sub	r31,r31,EXPFRAME 	; Create exception frame.
	st	r2,r31,PS		; Save psr on it.
;
;   This subroutine saves all registers on the stack.
;
	bb0.n	PS_DEXC,r2,noflts 	; Pending faults?
	st	r10,r31,HOMESP+40	; Save r10
	ldcr	r10,cr8			; Pass bus error information also.
	st	r10,r31,HOMESP+TRAN1
	ldcr	r10,cr9			; Pass bus error information also.
	st	r10,r31,HOMESP+DATA1
	ldcr	r10,cr10		; Pass bus error information also.
	st	r10,r31,HOMESP+ADDR1
	ldcr	r10,cr11		; Pass bus error information also.
	st	r10,r31,HOMESP+TRAN2
	ldcr	r10,cr12		; Pass bus error information also.
	st	r10,r31,HOMESP+DATA2
	ldcr	r10,cr13		; Pass bus error information also.
	st	r10,r31,HOMESP+ADDR2
	ldcr	r10,cr14		; Pass bus error information also.
	st	r10,r31,HOMESP+TRAN3
	ldcr	r10,cr15		; Pass bus error information also.
	st	r10,r31,HOMESP+DATA3
	ldcr	r10,cr16		; Pass bus error information also.
	st	r10,r31,HOMESP+ADDR3
noflts:
	ldcr	r10,cr18			; Save r1
	st	r10,r31,HOMESP+4
	ldcr	r10,cr20			; Save r31
	st	r10,r31,HOMESP+SP
	ldcr	r10,cr4			; Save cip.
	st	r10,r31,HOMESP+SCIP
	ldcr	r10,cr5			; Save nip.
	st	r10,r31,HOMESP+SNIP
	ldcr	r10,cr6			; Save fip.
	st	r10,r31,HOMESP+SFIP
	ldcr	r10,cr3			; Save ssb (needed?)
	st	r10,r31,HOMESP+SSB
	ldcr	r10,cr2			; Get old r2.
	st	r10,r31,HOMESP+8
;
;  Shadow registers have been saved, reenable interrupts.
;
	ldcr	r10,cr1			; Reenable interrupts.
	clr	r10,r10,2<PS_EXP> 
	stcr	r10,cr1

;Save all user registers.	
	st	r3,r31,HOMESP+12	; Save r3
	st	r4,r31,HOMESP+16	; Save r4
	st	r5,r31,HOMESP+20	; Save r5
	st	r6,r31,HOMESP+24	; Save r6
	st	r7,r31,HOMESP+28	; Save r7
	st	r8,r31,HOMESP+32	; Save r8
	st	r9,r31,HOMESP+36	; Save r9
	st	r11,r31,HOMESP+44	; Save r11
	st	r12,r31,HOMESP+48	; Save r12
	st	r13,r31,HOMESP+52	; Save r13
	st	r14,r31,HOMESP+56	; Save r14
	st	r15,r31,HOMESP+60	; Save r15
	st	r16,r31,HOMESP+64	; Save r16
	st	r17,r31,HOMESP+68	; Save r17
	st	r18,r31,HOMESP+72	; Save r18
	st	r19,r31,HOMESP+76	; Save r19
	st	r20,r31,HOMESP+80	; Save r20
	st	r21,r31,HOMESP+84	; Save r21
	st	r22,r31,HOMESP+88	; Save r22
	st	r23,r31,HOMESP+92	; Save r23
	st	r24,r31,HOMESP+96	; Save r24
	st	r25,r31,HOMESP+100	; Save r25
	st	r26,r31,HOMESP+104	; Save r26
	st	r27,r31,HOMESP+108	; Save r27
	st	r28,r31,HOMESP+112	; Save r28
	st	r29,r31,HOMESP+116	; Save r29
	ld	r3,r31,HOMESP+SCIP
	bb1.n	0,r3,codeflt
	st	r30,r31,HOMESP+120	; Save r30
	bb0	PS_DEXC,r2,getmspsnoflt  ; Check for faults.

codeflt:
;   Code or Data faults are pending, handle the bus error.
;   buserr() fixes the register images on the stack.
	st	r1,r31,HOMESP-4		; Save return add.
	add	r2,r31,HOMESP
	bsr.n	_buserr			; process faults.
	sub	r31,r31,8

	add	r31,r31,8
	bcnd.n	eq0,r2,getnewregs	; Paged in?
	ld	r1,r31,HOMESP-4
;
	ld	r2,r0,_saveflag		; Do we have to save fault info.
	ld.b	r3,r2,0
	bcnd	eq0,r3,getmspsnoflt
	st.b	r0,r2,0

	ld	r2,r0,_saveaddr		; Get address of save area.
	add	r3,r31,HOMESP+TRAN1
	add	r4,r0,3*3
saveberr:
	sub	r4,r4,1			; Save fault information.
	ld	r5,r3[r4]		; 
	bcnd.n	ne0,r4,saveberr
	st	r5,r2[r4]

getnewregs:
; Reload parameter registers - they may have been destroyed.
	ld	r3,r31,HOMESP+12	; Restore r3
	ld	r4,r31,HOMESP+16	; Restore r4
	ld	r5,r31,HOMESP+20	; Restore r5
	ld	r6,r31,HOMESP+24	; Restore r6
	ld	r7,r31,HOMESP+28	; Restore r7

getmspsnoflt:
	jmp.n	r1
	ld	r2,r31,HOMESP+8		; Restore r2.
;


;###############################################################
getregs:
;   This subroutine gets all user registers from the stack.
;   Then rte (no return)
;
;   Reload all user registers.
	ld	r1,r31,HOMESP+4		; Get r1
	ld	r3,r31,HOMESP+12	; Get r3
	ld	r4,r31,HOMESP+16	; Get r4
	ld	r5,r31,HOMESP+20	; Get r5
	ld	r6,r31,HOMESP+24	; Get r6
	ld	r7,r31,HOMESP+28	; Get r7
	ld	r8,r31,HOMESP+32	; Get r8
	ld	r9,r31,HOMESP+36	; Get r9
	ld	r10,r31,HOMESP+40	; Get r10
	ld	r11,r31,HOMESP+44	; Get r11
	ld	r12,r31,HOMESP+48	; Get r12
	ld	r13,r31,HOMESP+52	; Get r13
	ld	r14,r31,HOMESP+56	; Get r14
	ld	r15,r31,HOMESP+60	; Get r15
	ld	r16,r31,HOMESP+64	; Get r16
	ld	r17,r31,HOMESP+68	; Get r17
	ld	r18,r31,HOMESP+72	; Get r18
	ld	r19,r31,HOMESP+76	; Get r19
	ld	r20,r31,HOMESP+80	; Get r20
	ld	r21,r31,HOMESP+84	; Get r21
	ld	r22,r31,HOMESP+88	; Get r22
	ld	r23,r31,HOMESP+92	; Get r23
	ld	r24,r31,HOMESP+96	; Get r24
	ld	r25,r31,HOMESP+100	; Get r25
	ld	r26,r31,HOMESP+104	; Get r26
	ld	r27,r31,HOMESP+108	; Get r27
	ld	r28,r31,HOMESP+112	; Get r28
	ld	r29,r31,HOMESP+116	; Get r29
	ld	r30,r31,HOMESP+120	; Get r30
;
	ldcr	r2,cr1			; Inhibit interrupts.
	set	r2,r2,1<PS_IPL>		; While loading shadows.
	stcr	r2,cr1
;
;  Reload all shadow registers.
	ld	r2,r31,HOMESP+SPSR
	stcr	r2,cr2			; Get psr.
	ld	r2,r31,HOMESP+SNIP
	stcr	r2,cr5			; Get nip.
	ld	r2,r31,HOMESP+SFIP
	stcr	r2,cr6			; Get fip.
	ld	r2,r31,HOMESP+SSB
	stcr	r2,cr3			; Get ssb.
;
	ld	r2,r31,HOMESP+8		; Get r2
	ld	r31,r31,HOMESP+SP	; Get r31
;
	rte


buserr(regptr)
  int *regptr;
{
  register typesig = 0;  /* signal type    */
  register proc_t  *p;  /* faulting process  */
  register int  ssw;  /* special status work  */
  register int  vaddr;  /* faulting virt addr  */
  register int  berrsrc;/* STAT2 bus error reg  */
  register int  write;  /* write flag    */
  register int  *ppc;  /* pointer to pc  */
  int   i;
  time_t syst;    /* time of fault  */
  int *old_ar0;    /* old ar0    */
  int old_hold;    /* old held signals  */
  struct fltinfo *fltptr;

  minfo.buserr++;
  p = u.u_procp;
  u.u_traptype = TRAPNORM;
  old_ar0 = u.u_ar0;  /* save old ar0 and restore on return        */
  u.u_ar0 = regptr;  /* set up new ar0 for current regs        */
  ppc = &u.u_ar0[SCIP];  /* address of pc - referenced very often      */

  /*
   * Calculate the address of the bus error 
   *  This is the logical address to the MMU
   */

  berrsrc = *SSR;  /* Get the contents  of the system status reg*/
  syst = u.u_stime;

/*  Check for code faults. */
  if(*ppc & EXCEPT_IP){
    if(USERMODE(u.u_ar0[PS])){   /*  User code fault */
      regptr[SFIP] = regptr[SNIP] & ~EXCEPT_IP;
      regptr[SNIP] = *ppc & ~EXCEPT_IP;  /* Rerun fault*/
      if(typesig = fault(*ppc,p,0)){
        psignal(p, typesig);
        old_hold = p->p_hold;
        p->p_hold = ~( (1L<<(SIGSEGV-1)) | (1L<<(SIGKILL-1)) );
          /* except SIGSEGV & SIGKILL */
        if ((p->p_flag & SPRSTOP) || (p->p_sig && issig()))
          psig(*ppc);
        p->p_hold = old_hold;  /* reset held signals */
        goto out;
      }
    }
    else {  /* Kernel mode code fault. */
      cmn_err(CE_WARN,"pc   = 0x%x, ps   = 0x%x",*ppc,u.u_ar0[PS]);
      cmn_err(CE_PANIC,"       *** Kernel Code fault *** ");
      
    }
  }

/* Check for data access faults. */
  if(u.u_ar0[PS] & PS_DEXC){  /* Fault? */

    if(regptr[TRAN1] & TRN_MD) {   /* kernel data address fault? */
      cmn_err(CE_WARN,"pc   = 0x%x, ps   = 0x%x, vaddr = %x",
      *ppc, u.u_ar0[PS],regptr[ADDR1]);
      cmn_err(CE_PANIC,"       *** Kernel data fault *** ");
    }

    u.u_ar0[PS] &= ~PS_DEXC;    /* clear it */
    for(i = 0, fltptr = &regptr[TRAN1]; i < NUMTRAN; i++, fltptr++){
      if(fltptr->tran & TRN_VALID) {    /* If not NOP */
        if(!(fltptr->tran & TRN_MD) && (typesig = fault(fltptr->addr, p,~(fltptr ->tran & TRN_RW)))) {
          if(u.u_ar0[PS] & PS_SUP && *ppc >= nofulow && *ppc <= nofuhigh){
            regptr[SNIP] = (int)errret | VALIDIP;
            regptr[SFIP] = ~VALIDIP;
            if (u.u_caddrflt){
              regptr[SNIP] = u.u_caddrflt | VALIDIP;
              regptr[SFIP] = ~VALIDIP;
            }
            
          }
          else{
            
            u.u_traptype = TRAPBUS;  /* bus error trap type */
            u.u_trapflag = 1;  /* copy stack frame to u area */

            psignal(p, typesig);  /* post bus error signal */
            old_hold = p->p_hold;  /* Hold off all other signals */
            p->p_hold = ~( (1L<<(SIGSEGV-1)) | (1L<<(SIGKILL-1)) );
            /* except SIGSEGV & SIGKILL */
            if ((p->p_flag & SPRSTOP) || (p->p_sig && issig()))
              psig(fltptr->addr);

            p->p_hold = old_hold;  /* reset held signals */
            goto out;
          }
        }
        else {  /* paged in, complete the access */
          if(fltptr->tran & TRN_LK /* If xmem */
             && i!=2){  /* on stage 0 or 1, */
            /* combine into 1 transaction */
            fltptr->data=fltptr+1->data;
            fltptr+1->tran &= ~TRN_VALID;
            fltptr->tran & =
               ~(TRN_RW|TRN_SIGN);
          }
          dotran(regptr,fltptr);
        }
      }
    }
  }


dotran(regptr,fltptr)
/* Complete a memory transaction */
/*  This subroutine will complete a pending memory transaction based
   on the information present in the shadow transaction registers. 
   The register image on the stack is corrected if neccessary */

/*  This is a heavily used subroutine.  Candidate for ml. */

	struct fltinfo *fltptr;
	int *regptr;
{
	int size;
	int write;
	size = fltptr->tran & STROBES;
	write = fltptr->tran & TRN_RW;
	if(write){  /* Complete a st */
		switch (size){

		case TRN_WORD:
		if(fltptr->tran & TRN_MD)
			*(int *)(fltptr->addr) = fltptr->data;
		else
			suword(fltptr->addr,fltptr->data);
		break;

		case UPHALF:
		if(fltptr->tran & TRN_MD)
			*(short *)(fltptr->addr) = fltptr->data;
		else
			suhalf(fltptr->addr,fltptr->data);
		break;

		case LOHALF:
		if(fltptr->tran & TRN_MD)
			*(short *)(fltptr->addr+2) = fltptr->data;
		else
			suhalf(fltptr->addr+2,fltptr->data);
		break;

		case HIBYTE:
		if(fltptr->tran & TRN_MD)
			*(fltptr->addr) = fltptr->data;
		else
			subyte(fltptr->addr,fltptr->data);
		break;

		case MHBYTE:
		if(fltptr->tran & TRN_MD)
			*(fltptr->addr+1) = fltptr->data;
		else
			subyte(fltptr->addr+1,fltptr->data);
		break;

		case MLBYTE:
		if(fltptr->tran & TRN_MD)
			*(fltptr->addr+2) = fltptr->data;
		else
			subyte(fltptr->addr+2,fltptr->data);
		break;

		case LOBYTE:
		if(fltptr->tran & TRN_MD)
			*(fltptr->addr+3) = fltptr->data;
		else
			subyte(fltptr->addr+3,fltptr->data);
		break;

		}
		
	}
	else {  /* Complete ld */
		switch (size){

		case TRN_WORD:
		if(fltptr->tran & TRN_MD)
			fltptr->data = *(int *)(fltptr->addr);
		else{
			if(fltptr->tran & TRN_LK)    /* If xmem word */
				fltptr->data = xmem(fltptr->addr, fltptr->data, 1)
			else
				fltptr->data = fuword(fltptr->addr);
		}
		break;

		case UPHALF:
		if(fltptr->tran & TRN_MD)
			fltptr->data = *(short *)(fltptr->addr) & 0xFFFF;
		else
			fltptr->data=fuhalf(fltptr->addr);
		break;

		case LOHALF:
		if(fltptr->tran & TRN_MD)
			fltptr->data = *(short *)(fltptr->addr+2) & 0xFFFF;
		else
			fltptr->data=fuhalf(fltptr->addr+2);
		break;

		case HIBYTE:
		if(fltptr->tran & TRN_MD)
			fltptr->data = *(fltptr->addr) & 0xFF;
		else{
			if(fltptr->tran & TRN_LK)    /* If xmem word */
				fltptr->data = xmem(fltptr->addr, fltptr->data, 0)
			else
				fltptr->data=fubyte(fltptr->addr);
		}
		break;

		case MHBYTE:
		if(fltptr->tran & TRN_MD)
			fltptr->data = *(fltptr->addr+1) & 0xFF;
		else{
			if(fltptr->tran & TRN_LK)    /* If xmem word */
				fltptr->data = xmem(fltptr->addr+1, fltptr->data, 0)
			else
				fltptr->data=fubyte(fltptr->addr+1);
		}
		break;

		case MLBYTE:
		if(fltptr->tran & TRN_MD)
			fltptr->data = *(fltptr->addr+2) & 0xFF;
		else
		{
			if(fltptr->tran & TRN_LK)    /* If xmem word */
				fltptr->data = xmem(fltptr->addr+2, fltptr->data, 0)
			else
				fltptr->data=fubyte(fltptr->addr+2);
		}
		break;

		case LOBYTE:
		if(fltptr->tran & TRN_MD)
			fltptr->data = *(fltptr->addr+3) & 0xFF;
		else
		{
			if(fltptr->tran & TRN_LK)    /* If xmem word */
				fltptr->data = xmem(fltptr->addr+3, fltptr->data, 0)
			else
				fltptr->data=fubyte(fltptr->addr+3);
		}
		break;

		}
		if(fltptr -> tran & TRN_SIGN){
			switch (size){
			case UPHALF:
			case LOHALF:
				if(fltptr->data & 0x8000)
					fltptr->data |= 0xFFFF0000;
			break;
			case HIBYTE:
			case MHBYTE:
			case MLBYTE:
			case LOBYTE:
				if(fltptr->data & 0x80)
					fltptr->data |= 0xFFFFFF00;
			break;
			}
		}
		/* Load register with result and clear scoreboard bit */
		regptr[R0+((fltptr->tran & TRN_REG)>>7)] = fltptr->data;
		regptr[SSB] &= exp2((fltptr->tran &TRN_REG)>>7);
		fltptr->tran = 0;
	}
}


; oldval=xmem(addr,data, wordflag);
;     
;     addr= address to xmem
;     data= data to xmem
;     wordflag = 0 for byte, 1 for word.
;
;     returns oldval = original memory contents at addr.
;
;   This subroutine performs an xmem instruction for continuation of
; a faulted xmem access.
	global	xmem
xmem:
	bcnd	eq0,r4,xmembyte
	xmem.usr	r3,r2,r0	; Exchange a word.
	jmp.n	r1			; Return value.
	add	r2,r3,r0

xmembyte:
	xmem.bu.usr r3,r2,r0		; Exchange a byte.
	jmp.n	r1			; Return value.
	add	r2,r3,r0
