/* QEMU Emulation PALcode.

   Copyright (C) 2011 Richard Henderson

   This file is part of QEMU PALcode.

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the text
   of the GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; see the file COPYING.  If not see
   <http://www.gnu.org/licenses/>.  */

	.set		noat
	.set		nomacro
	.text

#include "pal.h"
#include "osf.h"
#include SYSTEM_H

/*
 * Create a standard kernel entry stack frame.
 */

.macro	STACK_FRAME save_ps, save_pc, temp, do_ps
	// Test if we're currently in user mode
	and	\save_ps, PS_M_CM, \temp
	beq	\temp, 0f
	// Switch to kernel mode
.ifne \do_ps
	mtpr	$31, qemu_ps
.endif
	mtpr	$sp, qemu_usp
	mfpr	$sp, ptKsp
	// Allocate the stack frame
0:	lda	$sp, -FRM_K_SIZE($sp)
	stq	\save_ps, FRM_Q_PS($sp)
	stq	\save_pc, FRM_Q_PC($sp)
	stq	$gp, FRM_Q_GP($sp)
	stq	a0, FRM_Q_A0($sp)
	stq	a1, FRM_Q_A1($sp)
	stq	a2, FRM_Q_A2($sp)
.endm

/*
 * Allocate a 1 page stack for use by the console.
 */
#define STACK_SIZE	8192

/*
 * QEMU emulator "hardware" entry points.
 */

/*
 * Reset
 * 
 * INPUT PARAMETERS:
 * 
 *	trap_arg0 = Memory size
 *	trap_arg1 = Kernel entry (if loaded)
 */ 
        .org	0x0000
	.globl	__start
__start:
	// Initialize GP.
	br	$gp, .+4
	ldah	$gp, 0($gp)			!gpdisp!1
	lda	$gp, 0($gp)			!gpdisp!1
	mtpr	$gp, ptPgp

	// Disable interrupts; kernel mode
	lda	t0, IPL_K_HIGH
	mtpr	t0, qemu_ps

	// Initialize Stack.
	SYS_WHAMI a0
	lda	t0, STACK_SIZE
	addq	a0, 1, t1
	mull	t0, t1, t0
	ldah	t1, stack($gp)			!gprelhigh
	lda	t1, stack(t1)			!gprellow
	addq	t0, t1, $sp

	// Do any necessary system setup required for PALmode,
	// e.g. setting up ptSys[01].
	bsr	$26, Sys_Setup

	// Non-boot CPUs can go wait now.
	bne	a0, 1f

	// Load boot arguments
	mfpr	a0, qemu_trap_arg0		// memsize
	mfpr	a1, qemu_trap_arg1		// kernel entry
	mfpr	a2, qemu_trap_arg2		// ncpus

	// Continue in do_start, outside PALmode.
	ldah	$27, do_start($gp)		!gprelhigh
	lda	$27, do_start($27)		!gprellow
	hw_ret	($27)

1:	ldah	$27, do_start_wait($gp)		!gprelhigh
	lda	$27, do_start_wait($27)		!gprellow
	hw_ret	($27)
ENDFN	__start

/*
 * Machine Check
 *
 * INPUT PARAMETERS:
 * 
 *	trap_arg0 = 
 *	trap_arg1 = 
 *	trap_arg2 = 
 */
	.org	0x0080
Pal_Mchk:
	mfpr	p6, qemu_exc_addr	// Check for palcode mchk
	blbs	p6, MchkFromPal
	mfpr	p0, ptMces		// Check for double mchk
	blbs	p0, MchkDouble

	// Since this is QEMU, the only MCHK we raise spontaneously
	// is for access to non-existent memory.
	//
	// ??? With MemTxResult we could perhaps distinguish
	// between MEMTX_DECODE_ERROR (no memory) and
	// MEMTX_ERROR (device error), but it's not clear whether
	// "device error" corresponds to "PCI target abort" or whatnot.
	// However the main reason to handle mchk at all is to allow
	// the guest OS to probe for devices, which is just "no memory".

	lda	t0, MCHK_K_SYS_NOMEM
	br	MchkLogOut
ENDFN	Pal_Mchk

/*
 * Interprocessor Interrupt
 *
 * INPUT PARAMETERS:
 *
 *	trap_arg0 = 
 *	trap_arg1 =
 *	trap_arg2 =
 *
 * The interprocessor interrupt is special, in that PALcode is supposed
 * to clear the interupt and not wait for the OS to do it.
 */
	.org	0x0100
Pal_Smp_Interrupt:
	mfpr	p6, qemu_exc_addr

	SYS_ACK_SMP p0, p1, p2

	mfpr	p0, qemu_ps

	STACK_FRAME p0, p6, p2, 0

	mov	IPL_K_IP, p0		// Raise IPL
	mtpr	p0, qemu_ps

	mfpr	p6, ptEntInt
	mfpr	$gp, ptKgp
	lda	a0, INT_K_IP
	lda	a1, 0
	lda	a2, 0

	hw_ret	(p6)
ENDFN	Pal_Smp_Interrupt

/*
 * Clock Interrupt
 *
 * INPUT PARAMETERS:
 *
 *	trap_arg0 = 
 *	trap_arg1 =
 *	trap_arg2 =
 *
 * The clock interrupt is special, in that PALcode is supposed
 * to clear the interupt and not wait for the OS to do it.
 */
	.org	0x0180
Pal_Clk_Interrupt:
	mfpr	p6, qemu_exc_addr

	SYS_ACK_CLK p0, p1, p2

	mfpr	p0, qemu_ps

	STACK_FRAME p0, p6, p2, 0

	mov	IPL_K_CLK, p0		// Raise IPL
	mtpr	p0, qemu_ps

	mfpr	p6, ptEntInt
	mfpr	$gp, ptKgp
	lda	a0, INT_K_CLK
	lda	a1, 0
	lda	a2, 0

9:	hw_ret	(p6)
ENDFN	Pal_Clk_Interrupt

/*
 * Device Interrupt
 *
 * INPUT PARAMETERS:
 *
 *	trap_arg0 = 
 *	trap_arg1 =
 *	trap_arg2 =
 */ 
	.org	0x0200
Pal_Dev_Interrupt:
	mfpr	p6, qemu_exc_addr
	mfpr	p0, qemu_ps

	STACK_FRAME p0, p6, p2, 0

	mov	IPL_K_DEV1, p0		// Raise IPL
	mtpr	p0, qemu_ps

	bsr	p7, Sys_Dev_Vector

	mfpr	p7, ptEntInt
	mfpr	$gp, ptKgp
	lda	a0, INT_K_DEV
	lda	a2, 0
	hw_ret	(p7)
ENDFN	Pal_Dev_Interrupt

/*
 * Memory Fault
 *
 * INPUT PARAMETERS:
 *
 *	trap_arg0 = faulting address
 *	trap_arg1 = fault type (TNV, ACV, FOR, FOW, FOE)
 *	trap_arg2 = access type (exec=-1, read=0, write=1)
 */ 
	.org	0x0280
Pal_MMFault:
	mfpr	p0, qemu_ps
	mfpr	p6, qemu_exc_addr
	blbs	p6, MchkBugCheck

	STACK_FRAME p0, p6, p2, 1

	mfpr	p0, ptEntMM
	mfpr	$gp, ptKgp
	mfpr	a0, qemu_trap_arg0
	mfpr	a1, qemu_trap_arg1
	mfpr	a2, qemu_trap_arg2
	hw_ret	(p0)
ENDFN	Pal_MMFault

/*
 * Unaligned Data
 * 
 * INPUT PARAMETERS:
 *
 *	trap_arg0 = faulting address
 *	trap_arg1 = opcode of faulting insn
 *	trap_arg2 = src/dst register number
 */ 
	.org	0x0300
Pal_Unalign:
	mfpr	p0, qemu_ps
	mfpr	p6, qemu_exc_addr
	blbs	p6, MchkBugCheck
	addq	p6, 4, p6		// increment past the faulting insn

	STACK_FRAME p0, p1, p2, 1

	mfpr	p0, ptEntUna
	mfpr	$gp, ptKgp
	mfpr	a0, qemu_trap_arg0
	mfpr	a1, qemu_trap_arg1
	mfpr	a2, qemu_trap_arg2
	hw_ret	(p0)
ENDFN	Pal_Unalign

/*
 * Illegal Opcode
 *
 * INPUT PARAMETERS:
 *
 *	trap_arg0 = UNDEFINED
 *	trap_arg1 = UNDEFINED
 *	trap_arg2 = UNDEFINED
 *
 * OUTPUT PARAMETERS:
 *
 *	r16 (a0) = Instruction fault code
 *	r17 (a1) = UNPREDICTABLE
 *	r18 (a2) = UNPREDICTABLE
 */ 
	.org	0x0380
Pal_OpcDec:
	mfpr	p0, qemu_ps
	mfpr	p6, qemu_exc_addr
	blbs	p6, MchkBugCheck

	STACK_FRAME p0, p6, p2, 1

	mfpr	p0, ptEntIF
	mfpr	$gp, ptKgp
	mov	IF_K_OPCDEC, a0
	hw_ret	(p0)
ENDFN	Pal_OpcDec

/*
 * Arithmetic Trap
 *
 * INPUT PARAMETERS:
 *
 *	trap_arg0 = exception type
 *	trap_arg1 = register modification mask
 *	trap_arg2 = UNDEFINED
 */
	.org	0x0400
Pal_Arith:
	mfpr	p0, qemu_ps
	mfpr	p6, qemu_exc_addr
	blbs	p6, MchkBugCheck

	STACK_FRAME p0, p6, p2, 1

	mfpr	p0, ptEntArith
	mfpr	$gp, ptKgp
	mfpr	a0, qemu_trap_arg0
	mfpr	a1, qemu_trap_arg1
	hw_ret	(p0)
ENDFN	Pal_Arith

/*
 * Floating Point Disabled
 *
 * INPUT PARAMETERS:
 *
 *	trap_arg0 = UNDEFINED
 *	trap_arg1 = UNDEFINED
 *	trap_arg2 = UNDEFINED
 *
 * OUTPUT PARAMETERS:
 *
 *	r16 (a0) = Instruction fault code
 *	r17 (a1) = UNPREDICTABLE
 *	r18 (a2) = UNPREDICTABLE
 */ 
	.org	0x0480
Pal_Fen:
	mfpr	p0, qemu_ps
	mfpr	p6, qemu_exc_addr
	blbs	p6, MchkBugCheck

	STACK_FRAME p0, p6, p2, 1

	mfpr	p0, ptEntIF
	mfpr	$gp, ptKgp
	mov	IF_K_FEN, a0
	hw_ret	(p0)
ENDFN	Pal_Fen

/*
 * OSF/1 Privileged CALL_PAL Entry Points
 */

#define ORG_CALL_PAL_PRIV(X)	.org	0x1000+64*X

/*
 * Halt
 *
 * SIDE EFFECTS:
 *
 *	We either power down the system or re-enter the console.
 *	But given that we're not returning to the kernel, there's
 *	no reason to continue processing in assembler.  Go to C.
 */
	ORG_CALL_PAL_PRIV(0x00)
CallPal_Halt:
	bsr	p7, UpdatePCB		// Save kernel data
	lda	v0, HLT_K_SW_HALT	// FIXME store this somewhere.

	mtpr	$31, qemu_halt

	br	Sys_EnterConsole
ENDFN	CallPal_Halt

/*
 * Cache Flush
 *
 * For QEMU, this is of course a no-op.
 */
	ORG_CALL_PAL_PRIV(0x01)
CallPal_Cflush:
	hw_rei
ENDFN	CallPal_Cflush

/*
 * Drain Aborts
 *
 * For QEMU, this is of course a no-op.
 */
        ORG_CALL_PAL_PRIV(0x02)
CallPal_Draina:
	hw_rei
ENDFN	CallPal_Draina

/*
 * Delay for N nanoseconds.
 *
 * This is unique to QEMU, used only within PALcode itself.
 */
	ORG_CALL_PAL_PRIV(0x03)
CallPal_Ndelay:
	mfpr	p0, qemu_vmtime
	addq	p0, a0, p0
	mtpr	p0, qemu_alarm

	mtpr	$31, qemu_wait

	SYS_ACK_CLK p2, p3, p4

	mfpr	v0, qemu_vmtime
	subq	p0, v0, v0
	hw_rei
ENDFN	CallPal_Ndelay

	ORG_CALL_PAL_PRIV(0x04)
CallPal_OpcDec04:
	br	CallPal_OpcDec
ENDFN	CallPal_OpcDec04

	ORG_CALL_PAL_PRIV(0x05)
CallPal_OpcDec05:
	br	CallPal_OpcDec
ENDFN	CallPal_OpcDec05

	ORG_CALL_PAL_PRIV(0x06)
CallPal_OpcDec06:
	br	CallPal_OpcDec
ENDFN	CallPal_OpcDec06

	ORG_CALL_PAL_PRIV(0x07)
CallPal_OpcDec07:
	br	CallPal_OpcDec
ENDFN	CallPal_OpcDec07

	ORG_CALL_PAL_PRIV(0x08)
CallPal_OpcDec08:
	br	CallPal_OpcDec
ENDFN	CallPal_OpcDec08

/*
 * Console Service
 * 
 * INPUT PARAMETERS:
 * 
 *	r16 (a0)          = Option selector
 *	r17..r21 (a1..a5) = Implementation specific entry parameters
 *
 * SIDE EFFECTS:
 *
 *	Registers a0..a5, and v0 are UNPREDICTABLE upon return.
 */ 
	ORG_CALL_PAL_PRIV(0x09)
CallPal_Cserve:
	// Most of the entries are densely clustered around 0.
	mov	0, v0
	cmpule	a0, 7, p0
	cmovne	p0, a0, v0
	br	p0, 1f
1:	lda	p0, Cserve_Table-1b(p0)
	s8addq	v0, p0, p0
	jmp	$31, (p0), 0
ENDFN	CallPal_Cserve

	.text	1
	.align	3
/* Note that the entries in the following table are all 2 insns.
   The first entry is unused, and is also where all out-of-range
   commands are vectored.  */
Cserve_Table:
	br	CallPal_Cserve_Cont
	nop
Cserve_Ldqp:
	ldq_p	v0, 0(a1)
	hw_rei
ENDFN	Cserve_Ldqp
Cserve_Stqp:
	stq_p	a2, 0(a1)
	hw_rei
ENDFN	Cserve_Stqp
Cserve_Get_Wall_Time:
	mfpr	v0, qemu_walltime
	hw_rei
ENDFN	Cserve_Get_Wall_Time
Cserve_Get_Alarm:
	mfpr	v0, qemu_alarm
	hw_rei
ENDFN	Cserve_Get_Alarm
Cserve_Set_Alarm_Rel:
	// Cheating here: create the absolute time and fall thru.
	mfpr	p0, qemu_vmtime
	addq	p0, a1, a1
ENDFN	Cserve_Set_Alarm_Rel
Cserve_Set_Alarm_Abs:
	mtpr	a1, qemu_alarm
	hw_rei
ENDFN	Cserve_Set_Alarm_Abs
Cserve_Get_VM_Time:
	mfpr	v0, qemu_vmtime
	hw_rei
ENDFN	Cserve_Get_VM_Time


CallPal_Cserve_Cont:
	// ??? For SRM compatibility and their use within Linux, use 52/53
	// for these.  Anyone know what other "standard" SRM Cserve entry
	// points are?  Certainly we don't want to be compatible with MILO,
	// which puts the selector at A2.
	cmpeq	a0, 52, v0
	bne	v0, Cserve_Ena
	cmpeq	a0, 53, v0
	bne	v0, Cserve_Dis
	hw_rei
ENDFN	CallPal_Cserve_Cont
	.previous

/*
 * Swap PALcode
 *
 * FUNCTIONAL DESCRIPTION:
 *
 *	The swap PALcode (swppal) function replaces the current 
 *	(active) PALcode by the specified new PALcode image.  
 *	This function is intended for use by operating systems 
 *	only during bootstraps and restarts, or during transitions 
 *	to console I/O mode.
 * 
 *	The PALcode descriptor passed in a0 is interpreted as
 *	either a PALcode variant or the base physical address
 *	of the new PALcode image.  If a variant, the PALcode
 *	image must have been previously loaded.  No PALcode
 *	loading occurs as a result of this function. 
 *
 *	NOTE:
 *	This implementation of SWPPAL does not support PALcode
 *	variants.  If a variant is specified in a0, a check is
 *	performed to determine whether the variant is OSF/1 or
 *	not and the returned status is either unknown variant
 *	(if not OSF/1) or variant not loaded.
 *
 * INPUT PARAMETERS:
 *
 *	r16 (a0) = New PALcode variant or base physical address
 *	r17 (a1) = New PC
 *	r18 (a2) = New PCB
 *	r19 (a3) = New VptPtr
 * 
 * OUTPUT PARAMETERS:
 *
 *	r0 (v0) = Returned status indicating:
 *			0 - Success (PALcode was switched)
 *			1 - Unknown PALcode variant
 *			2 - Known PALcode variant, but PALcode not loaded
 *
 *	r26 (ra) = r27 (pv) = New PC
 *		Note that this is non-architected, but is relied on by
 *		the usage of SwpPal within our own console code in order
 *		to simplify its use within C code.
 *
 */
	ORG_CALL_PAL_PRIV(0x0A)
CallPal_SwpPal:
	// Save a copy of the return address in case of machine check.
	mfpr	p6, qemu_exc_addr

	// Accept swapping to OSF PALcode.  The side effect here is to
	// load the other parameters for the kernel.
	cmpeq	a0, 2, v0
	bne	v0, CallPal_SwpPal_Cont

	// Return as an unknown PALcode variant
	mov	1, v0
	hw_rei
ENDFN	CallPal_SwpPal

	.text	1
CallPal_SwpPal_Cont:
	rpcc	p0
	mtpr	a2, ptPcbb
	mtpr	a3, qemu_vptptr

	ldq_p	$sp, PCB_Q_KSP(a2)
	ldq_p	t0, PCB_Q_USP(a2)
	ldq_p	t1, PCB_Q_PTBR(a2)
	ldl_p	t2, PCB_L_PCC(a2)
	ldq_p	t3, PCB_Q_UNIQUE(a2)
	ldq_p	t4, PCB_Q_FEN(a2)

	mtpr	t0, qemu_usp

	sll	t1, VA_S_OFF, t1
	mtpr	t1, qemu_ptbr

	subl	t2, p0, t2
	mtpr	t2, qemu_pcc_ofs

	mtpr	t3, qemu_unique

	and	t4, 1, t4
	mtpr	t4, qemu_fen

	mtpr	$31, qemu_tbia		// Flush TLB for new PTBR

	mov	a1, $26
	mov	a1, $27
	hw_ret	(a1)
ENDFN	CallPal_SwpPal_Cont
	.previous

	ORG_CALL_PAL_PRIV(0x0B)
CallPal_OpcDec0B:
	br	CallPal_OpcDec
ENDFN	CallPal_OpcDec0B

	ORG_CALL_PAL_PRIV(0x0C)
CallPal_OpcDec0C:
	br	CallPal_OpcDec
ENDFN	CallPal_OpcDec0C

/*
 * Write Interprocessor Interrupt Request
 *
 * INPUT PARAMETERS:
 *
 *	r16 (a0) = target processor number 
 * 
 * OUTPUT PARAMETERS:
 *
 * SIDE EFFECTS:
 * 
 */ 
        ORG_CALL_PAL_PRIV(0x0D)
CallPal_WrIpir:
	// Save a copy of the return address in case of machine check.
	mfpr	p6, qemu_exc_addr

	SYS_WRIPIR	a0, p0, p1, p2

	hw_rei
ENDFN	CallPal_WrIpir

	ORG_CALL_PAL_PRIV(0x0E)
CallPal_OpcDec0E:
	br	CallPal_OpcDec
ENDFN	CallPal_OpcDec0E

	ORG_CALL_PAL_PRIV(0x0F)
CallPal_OpcDec0F:
	br	CallPal_OpcDec
ENDFN	CallPal_OpcDec0F

/*
 * Read Machine Check Error Summary
 *
 * INPUT PARAMETERS:
 * 
 * OUTPUT PARAMETERS:
 *
 *	r0 (v0) = returned MCES value
 * 
 * SIDE EFFECTS:
 *
 */ 
        ORG_CALL_PAL_PRIV(0x10)
CallPal_RdMces:
	mfpr	v0, ptMces		// Get current MCES value
	and	v0, MCES_M_ALL, v0	// Clear all other bits
	hw_rei
ENDFN	CallPal_RdMces

/*
 * Write Machine Check Error Summary
 *
 * INPUT PARAMETERS:
 * 
 *	r16 (a0) = MCES<DPC> <- a0<3>,  MCES<DSC> <- a0<4>
 * 
 * OUTPUT PARAMETERS:
 *
 * SIDE EFFECTS:
 *
 *	Registers t0, t8..t11, and a0 are UNPREDICTABLE upon return.
 */ 
        ORG_CALL_PAL_PRIV(0x11)
CallPal_WrMces:
	// Clear MIP, SCE, PCE
	and	a0, (MCES_M_MIP | MCES_M_SCE | MCES_M_PCE), p0
	mfpr	p1, ptMces
	bic	p1, p0, p1

	// Copy DPC and DSC
	and	a0, (MCES_M_DPC | MCES_M_DSC), p0
	bic	p1, (MCES_M_DPC | MCES_M_DSC), p1
	or	p1, p0, p1

	mtpr	p1, ptMces
	hw_rei
ENDFN	CallPal_WrMces

	ORG_CALL_PAL_PRIV(0x12)
CallPal_OpcDec12:
	br	CallPal_OpcDec
ENDFN	CallPal_OpcDec12

	ORG_CALL_PAL_PRIV(0x13)
CallPal_OpcDec13:
	br	CallPal_OpcDec
ENDFN	CallPal_OpcDec13

	ORG_CALL_PAL_PRIV(0x14)
CallPal_OpcDec14:
	br	CallPal_OpcDec
ENDFN	CallPal_OpcDec14

	ORG_CALL_PAL_PRIV(0x15)
CallPal_OpcDec15:
	br	CallPal_OpcDec
ENDFN	CallPal_OpcDec15

	ORG_CALL_PAL_PRIV(0x16)
CallPal_OpcDec16:
	br	CallPal_OpcDec
ENDFN	CallPal_OpcDec16

	ORG_CALL_PAL_PRIV(0x17)
CallPal_OpcDec17:
	br	CallPal_OpcDec
ENDFN	CallPal_OpcDec17

	ORG_CALL_PAL_PRIV(0x18)
CallPal_OpcDec18:
	br	CallPal_OpcDec
ENDFN	CallPal_OpcDec18

	ORG_CALL_PAL_PRIV(0x19)
CallPal_OpcDec19:
	br	CallPal_OpcDec
ENDFN	CallPal_OpcDec19

	ORG_CALL_PAL_PRIV(0x1A)
CallPal_OpcDec1A:
	br	CallPal_OpcDec
ENDFN	CallPal_OpcDec1A

	ORG_CALL_PAL_PRIV(0x1B)
CallPal_OpcDec1B:
	br	CallPal_OpcDec
ENDFN	CallPal_OpcDec1B

	ORG_CALL_PAL_PRIV(0x1C)
CallPal_OpcDec1C:
	br	CallPal_OpcDec
ENDFN	CallPal_OpcDec1C

	ORG_CALL_PAL_PRIV(0x1D)
CallPal_OpcDec1D:
	br	CallPal_OpcDec
ENDFN	CallPal_OpcDec1D

	ORG_CALL_PAL_PRIV(0x1E)
CallPal_OpcDec1E:
	br	CallPal_OpcDec
ENDFN	CallPal_OpcDec1E

	ORG_CALL_PAL_PRIV(0x1F)
CallPal_OpcDec1F:
	br	CallPal_OpcDec
ENDFN	CallPal_OpcDec1F

	ORG_CALL_PAL_PRIV(0x20)
CallPal_OpcDec20:
	br	CallPal_OpcDec
ENDFN	CallPal_OpcDec20

	ORG_CALL_PAL_PRIV(0x21)
CallPal_OpcDec21:
	br	CallPal_OpcDec
ENDFN	CallPal_OpcDec21

	ORG_CALL_PAL_PRIV(0x22)
CallPal_OpcDec22:
	br	CallPal_OpcDec
ENDFN	CallPal_OpcDec22

	ORG_CALL_PAL_PRIV(0x23)
CallPal_OpcDec23:
	br	CallPal_OpcDec
ENDFN	CallPal_OpcDec23

	ORG_CALL_PAL_PRIV(0x24)
CallPal_OpcDec24:
	br	CallPal_OpcDec
ENDFN	CallPal_OpcDec24

	ORG_CALL_PAL_PRIV(0x25)
CallPal_OpcDec25:
	br	CallPal_OpcDec
ENDFN	CallPal_OpcDec25

	ORG_CALL_PAL_PRIV(0x26)
CallPal_OpcDec26:
	br	CallPal_OpcDec
ENDFN	CallPal_OpcDec26

	ORG_CALL_PAL_PRIV(0x27)
CallPal_OpcDec27:
	br	CallPal_OpcDec
ENDFN	CallPal_OpcDec27

	ORG_CALL_PAL_PRIV(0x28)
CallPal_OpcDec28:
	br	CallPal_OpcDec
ENDFN	CallPal_OpcDec28

	ORG_CALL_PAL_PRIV(0x29)
CallPal_OpcDec29:
	br	CallPal_OpcDec
ENDFN	CallPal_OpcDec29

	ORG_CALL_PAL_PRIV(0x2A)
CallPal_OpcDec2A:
	br	CallPal_OpcDec
ENDFN	CallPal_OpcDec2A

/*
 * Write Floating Point Enable
 *
 * INPUT PARAMETERS:
 * 
 *	r16 (a0) = ICSR<FPE> <- a0<0>
 * 
 * SIDE EFFECTS:
 *
 *	Registers t0, t8..t11, and a0 are UNPREDICTABLE upon return.
 */ 
        ORG_CALL_PAL_PRIV(0x2B)
CallPal_WrFen:
	mfpr	p0, ptPcbb		// Get PCBB
	and	a0, 1, a0		// Clean new FEN value to single bit
	mtpr	a0, qemu_fen
	stl_p	a0, PCB_Q_FEN(p0)	// Write new PCB<FEN>
	hw_rei
ENDFN	CallPal_WrFen

	ORG_CALL_PAL_PRIV(0x2C)
CallPal_OpcDec2C:
	br	CallPal_OpcDec
ENDFN	CallPal_OpcDec2C

/*
 * Write Virtual Page Table Pointer
 *
 * INPUT PARAMETERS:
 *
 *	r16 (a0) = New virtual page table pointer 
 *
 * SIDE EFFECTS:
 *
 *	Registers t0, t8..t11, and a0 are UNPREDICTABLE upon return.
 */ 
        ORG_CALL_PAL_PRIV(0x2D)
CallPal_WrVptPtr:
	mtpr	a0, qemu_vptptr
	hw_rei
ENDFN	CallPal_WrVptPtr

	ORG_CALL_PAL_PRIV(0x2E)
CallPal_OpcDec2E:
	br	CallPal_OpcDec
ENDFN	CallPal_OpcDec2E

	ORG_CALL_PAL_PRIV(0x2F)
CallPal_OpcDec2F:
	br	CallPal_OpcDec
ENDFN	CallPal_OpcDec2F

/*
 * Swap Process Context
 *
 * FUNCTIONAL DESCRIPTION:
 *
 *	The swap process context (swpctx) function saves 
 *	the current process data in the current PCB, then 
 *	switches to the PCB passed in a0 and loads the
 *	new process context.  The old PCB is returned in v0.	
 * 
 * INPUT PARAMETERS:
 * 
 *	r16 (a0) = New PCBB 
 * 
 * OUTPUT PARAMETERS:
 *
 *	r0  (v0) = Old PCBB
 * 
 * SIDE EFFECTS:
 *
 *	Registers t0, t8..t11, and a0 are UNPREDICTABLE upon return.
 */ 
	ORG_CALL_PAL_PRIV(0x30)
CallPal_SwpCtx:
	rpcc	p5			// Get cycle counter
	mfpr	p6, qemu_exc_addr	// Save exc_addr for machine check

	mfpr	v0, ptPcbb		// Get current PCBB
	mtpr	a0, ptPcbb		// Save new PCBB
	srl	p5, 32, p7		// Move CC<OFFSET> to low longword

	addl	p5, p7, p7		// Accumulate time for old pcb
	stl_p	p7, PCB_L_PCC(v0)

	ldl_p	t9, PCB_L_PCC(a0)	// Get new PCC
	subl	t9, p5, p5		// Generate and ...
	mtpr	p5, qemu_pcc_ofs	// .. set new CC<OFFSET> bits

	stq_p	$sp, PCB_Q_KSP(v0)	// Store old kernel stack pointer
	mfpr	t10, qemu_usp		// Save old user stack pointer
	stq_p	t10, PCB_Q_USP(v0)

	br	CallPal_SwpCtx_Cont
ENDFN	CallPal_SwpCtx

	.text	1
CallPal_SwpCtx_Cont:
	ldq_p	$sp, PCB_Q_KSP(a0)	// Install new stack pointers
	ldq_p	t10, PCB_Q_USP(a0)
	mtpr	t10, qemu_usp

	mfpr	t10, qemu_unique	// Save old unique value
	stq_p	t10, PCB_Q_UNIQUE(v0)
	ldq_p	t10, PCB_Q_UNIQUE(a0)	// Install new unique value
	mtpr	t10, qemu_unique

	ldq_p	t8, PCB_Q_FEN(a0)	// Install new FEN
	and	t8, 1, t8
	mtpr	t8, qemu_fen

	// QEMU does not implement an ASN; skip that.

	ldq_p	t10, PCB_Q_PTBR(a0)	// Install new page tables
	sll	t10, VA_S_OFF, t10
	mtpr	t10, qemu_ptbr
	mtpr	$31, qemu_tbia		// Flush TLB, since we don't do ASNs

	hw_rei
ENDFN	CallPal_SwpCtx_Cont
	.previous

/*
 * Write System Value
 *
 * INPUT PARAMETERS:
 *
 *	r16 (a0) = New system value 
 * 
 * SIDE EFFECTS:
 *
 *	Registers t0, t8..t11, and a0 are UNPREDICTABLE upon return.
 */ 
        ORG_CALL_PAL_PRIV(0x31)
CallPal_WrVal:
	mtpr	a0, qemu_sysval
	hw_rei
ENDFN	CallPal_WrVal

/*
 * Read System Value
 *
 * OUTPUT PARAMETERS:
 *
 *	r0 (v0) = Returned system value
 * 
 * SIDE EFFECTS:
 *
 *	Registers t0 and t8..t11 are UNPREDICTABLE upon return.
 */ 
        ORG_CALL_PAL_PRIV(0x32)
CallPal_RdVal:
	mfpr	v0, qemu_sysval
	hw_rei
ENDFN	CallPal_RdVal

/*
 * Translation Buffer Invalidate
 * 
 * INPUT PARAMETERS:
 * 
 *	r16 (a0) = tbi selector type:
 *
 *		-2 - Flush all TB entries (tbia)
 *		-1 - Invalidate all TB entries with ASM=0 (tbiap)
 *		 1 - Invalidate ITB entry for va=a1 (tbisi)
 *		 2 - Invalidate DTB entry for va=a1 (tbisd)
 *		 3 - Invalidate both ITB and DTB entry for va=a1 (tbis)
 *
 *	r17 (a1) = VA for TBISx types
 *
 * Qemu does not implement ASNs or split I/D tlbs.  Therefore these
 * collapse to tbia and tbis.
 * 
 * SIDE EFFECTS:
 *
 *	Registers t0, t8..t11, and a0 are UNPREDICTABLE upon return.
 */ 
        ORG_CALL_PAL_PRIV(0x33)
CallPal_Tbi:
	bge	a0, 1f

	mtpr	$31, qemu_tbia
	hw_rei

1:	mtpr	a1, qemu_tbis
	hw_rei
ENDFN	CallPal_Tbi

/*
 * Write System Entry Address
 *
 * INPUT PARAMETERS:
 *
 *	r16 (a0) = VA of system entry point
 *	r17 (a1) = System entry point selector 
 * 
 * SIDE EFFECTS:
 *
 *	Registers t0, t8..t11, and a0..a1 are UNPREDICTABLE
 *	upon return.
 */ 
        ORG_CALL_PAL_PRIV(0x34)
CallPal_WrEnt:
	andnot	a0, 3, a0		// Clean PC<1:0>

	cmpult	a1, 6, t8		// Bound the input
	cmoveq	t8, 6, a1

	br	t0, 1f
1:	lda	t0, WrEnt_Table-1b(t0)
	s8addq	a1, t0, t0
	jmp	$31, (t0), 0
ENDFN	CallPal_WrEnt

	.text	1
WrEnt_Table:
0:	mtpr	a0, ptEntInt
	hw_rei
1:	mtpr	a0, ptEntArith
	hw_rei
2:	mtpr	a0, ptEntMM
	hw_rei
3:	mtpr	a0, ptEntIF
	hw_rei
4:	mtpr	a0, ptEntUna
	hw_rei
5:	mtpr	a0, ptEntSys
	hw_rei
6:	nop
	hw_rei
ENDFN	WrEnt_Table
	.previous

/*
 * Swap Interrupt Priority Level
 * 
 * INPUT PARAMETERS:
 * 
 *	r16 (a0) = New IPL
 * 
 * OUTPUT PARAMETERS:
 *
 *	r0  (v0) = Old IPL
 * 
 * SIDE EFFECTS:
 *
 *	Registers t0, t8..t11, and a0 are UNPREDICTABLE upon return.
 */ 
        ORG_CALL_PAL_PRIV(0x35)
CallPal_SwpIpl:
	mfpr	v0, qemu_ps
	and	a0, PS_M_IPL, a0
	and	v0, PS_M_IPL, v0
	mtpr	a0, qemu_ps
	hw_rei
ENDFN	CallPal_SwpIpl

/*
 * Read Processor Status
 *
 * OUTPUT PARAMETERS:
 *
 *	r0 (v0) = Current PS
 * 
 * SIDE EFFECTS:
 *
 *	Registers t0, t8..t11 are UNPREDICTABLE upon return.
 */ 
        ORG_CALL_PAL_PRIV(0x36)
CallPal_RdPs:
	mfpr	v0, qemu_ps
	hw_rei
ENDFN	CallPal_RdPs

/*
 * Write Kernel Global Pointer
 *
 * INPUT PARAMETERS:
 * 
 *	r16 (a0) = New KGP value
 *
 * SIDE EFFECTS:
 *
 *	Registers t0, t8..t11, and a0 are UNPREDICTABLE upon return.
 */ 
        ORG_CALL_PAL_PRIV(0x37)
CallPal_WrKgp:
	mtpr	a0, ptKgp
	hw_rei
ENDFN	CallPal_WrKgp

/*
 * Write User Stack Pointer
 * 
 * INPUT PARAMETERS:
 * 
 *	r16 (a0) = New user stack pointer value
 * 
 * SIDE EFFECTS:
 *
 *	Registers t0, t8..t11, and a0 are UNPREDICTABLE upon return.
 */ 
        ORG_CALL_PAL_PRIV(0x38)
CallPal_WrUsp:
	mtpr	a0, qemu_usp
	hw_rei
ENDFN	CallPal_WrUsp

/*
 * Write Performance Monitor
 *
 * INPUT PARAMETERS:
 * 
 *	r16 (a0) = New user stack pointer value
 * 
 * SIDE EFFECTS:
 *
 *	Registers t0, t8..t11, and a0 are UNPREDICTABLE upon return.
 */
	ORG_CALL_PAL_PRIV(0x39)
CallPal_WrPerfMon:
	// Not implemented
	hw_rei
ENDFN	CallPal_WrPerfMon

/*
 * Read User Stack Pointer
 * 
 * OUTPUT PARAMETERS:
 *
 *	r0 (v0) = User stack pointer value
 * 
 * SIDE EFFECTS:
 *
 *	Registers t0, and t8..t11 are UNPREDICTABLE upon return.
 */ 
        ORG_CALL_PAL_PRIV(0x3A)
CallPal_RdUsp:
	mfpr	v0, qemu_usp
	hw_rei
ENDFN	CallPal_RdUsp

	ORG_CALL_PAL_PRIV(0x3B)
CallPal_OpcDec3B:
	br	CallPal_OpcDec
ENDFN	CallPal_OpcDec3B

/*
 * Who Am I
 * 
 * OUTPUT PARAMETERS:
 *
 *	r0 (v0) = Current processor number
 * 
 * SIDE EFFECTS:
 *
 *	Registers t0 and t8..t11 are UNPREDICTABLE upon return.
 */ 
        ORG_CALL_PAL_PRIV(0x3C)
CallPal_Whami:
	SYS_WHAMI v0
	hw_rei
ENDFN	CallPal_Whami

/*
 * Return From System Call
 *
 * INPUT PARAMETERS:
 * 
 *	r30 (sp) = Pointer to the top of the kernel stack
 * 
 * OUTPUT PARAMETERS:
 *
 *	r29 (gp) = Restored user mode global pointer
 *	r30 (sp) = User stack pointer
 * 
 * SIDE EFFECTS:
 *
 *	Registers t0 and t8..t11 are UNPREDICTABLE upon return.
 */ 
        ORG_CALL_PAL_PRIV(0x3D)
CallPal_RetSys:
	ldq	t9, FRM_Q_PC($sp)	// Pop the return address
	ldq	$gp, FRM_Q_GP($sp)	// Get the user mode global pointer
	lda	t8, FRM_K_SIZE($sp)
	mtpr	t8, ptKsp

	mov	PS_K_USER, t8		// Set new mode to user
	mtpr	t8, qemu_ps

	mfpr	$sp, qemu_usp		// Get the user stack pointer

	andnot	t9, 3, t9		// Clean return PC<1:0>
	hw_ret	(t9)
ENDFN	CallPal_RetSys

/*
 * Wait For Interrupt
 *
 * FUNCTIONAL DESCRIPTION:
 *
 *	If possible, wait for the first of either of the following
 *	conditions before returning: any interrupt other than a clock
 *	tick; or the first clock tick after a specified number of clock
 *	ticks have bbeen skipped.
 *
 * INPUT PARAMETERS:
 * 
 *	r16 (a0) = Maximum number of clock ticks to skip
 * 
 * OUTPUT PARAMETERS:
 *
 *	r0 (v0) = Number of clock ticks actually skipped.
 */
	ORG_CALL_PAL_PRIV(0x3E)
CallPal_WtInt:
	mtpr	$31, qemu_wait
	mov	0, v0
	hw_rei
ENDFN	CallPal_WtInt

/*
 * Return From Trap, Fault, or Interrupt
 *
 * INPUT PARAMETERS:
 * 
 *	r30 (sp) = Pointer to the top of the kernel stack
 * 
 * OUTPUT PARAMETERS:
 *
 *	ps       <- (sp+00)
 *	pc       <- (sp+08)
 *	r29 (gp) <- (sp+16)
 *	r16 (a0) <- (sp+24)
 *	r17 (a1) <- (sp+32)
 *	r18 (a2) <- (sp+40)
 */ 
        ORG_CALL_PAL_PRIV(0x3F)
	.globl	CallPal_Rti
CallPal_Rti:
	mfpr	p6, qemu_exc_addr	// Save exc_addr for machine check

	ldq	p4, FRM_Q_PS($sp)	// Get the PS
	ldq	p5, FRM_Q_PC($sp)	// Get the return PC
	ldq	$gp, FRM_Q_GP($sp)	// Get gp
	ldq	a0, FRM_Q_A0($sp)	// Get a0
	ldq	a1, FRM_Q_A1($sp)	// Get a1
	ldq	a2, FRM_Q_A2($sp)	// Get a2
	lda	$sp, FRM_K_SIZE($sp)	// Pop the stack

	andnot	p5, 3, p5		// Clean return PC<1:0>

	and	p4, PS_M_CM, p3
	bne	p3, CallPal_Rti_ToUser

	and	p4, PS_M_IPL, p4
	mtpr	p4, qemu_ps
	hw_ret	(p5)
ENDFN	CallPal_Rti

	.text	1
CallPal_Rti_ToUser:
	mtpr	p3, qemu_ps
	mtpr	$sp, ptKsp
	mfpr	$sp, qemu_usp
	hw_ret	(p5)
ENDFN	CallPal_Rti_ToUser
	.previous

/*
 * OSF/1 Unprivileged CALL_PAL Entry Points
 */

#define ORG_CALL_PAL_UNPRIV(X)	.org	0x2000+64*(X-0x80)

/*
 * A helper routine for the unprivaledged kernel entry points, since the
 * actual stack frame setup code is just a tad too large to fit inline.
 *
 * INPUT PARAMETERS:
 *
 *	p5 = ps
 *	p6 = exc_addr
 *	p7 = return address
 *
 * SIDE EFFECTS:
 *
 *	p0 is clobbered
 *
 */
	.text	1
CallPal_Stack_Frame:
	// Test if we're currently in user mode
	and	p5, PS_M_CM, p0
	beq	p0, 0f
CallPal_Stack_Frame_FromUser:
	// Switch to kernel mode
	mtpr	$31, qemu_ps
	mtpr	$sp, qemu_usp
	mfpr	$sp, ptKsp
0:
	// Allocate the stack frame
	lda	$sp, -FRM_K_SIZE($sp)
	stq	p5, FRM_Q_PS($sp)
	stq	p6, FRM_Q_PC($sp)
	stq	$gp, FRM_Q_GP($sp)
	stq	a0, FRM_Q_A0($sp)
	stq	a1, FRM_Q_A1($sp)
	stq	a2, FRM_Q_A2($sp)
	ret	$31, (p7), 0
ENDFN	CallPal_Stack_Frame
	.previous

/*
 * Breakpoint Trap
 *
 * OUTPUT PARAMETERS:
 *
 *	r16 (a0) = Code for bpt (0)
 *	r17 (a1) = UNPREDICTABLE
 *	r18 (a2) = UNPREDICTABLE
 */ 
        ORG_CALL_PAL_UNPRIV(0x80)
CallPal_Bpt:
	mfpr	p5, qemu_ps
	mfpr	p6, qemu_exc_addr
	bsr	p7, CallPal_Stack_Frame

	mfpr	p0, ptEntIF
	mfpr	$gp, ptKgp
	mov	IF_K_BPT, a0
	hw_ret	(p0)
ENDFN	CallPal_Bpt

/*
 * Bugcheck Trap
 * 
 * OUTPUT PARAMETERS:
 *
 *	r16 (a0) = Code for bugchk (1)
 *	r17 (a1) = UNPREDICTABLE
 *	r18 (a2) = UNPREDICTABLE
 */ 
        ORG_CALL_PAL_UNPRIV(0x81)
CallPal_BugChk:
	mfpr	p5, qemu_ps
	mfpr	p6, qemu_exc_addr
	bsr	p7, CallPal_Stack_Frame

	mfpr	p0, ptEntIF
	mfpr	$gp, ptKgp
	mov	IF_K_BUGCHK, a0
	hw_ret	(p0)
ENDFN	CallPal_BugChk


	ORG_CALL_PAL_UNPRIV(0x82)
CallPal_OpcDec82:
	br	CallPal_OpcDec
ENDFN	CallPal_OpcDec82

/*
 * System Call
 */ 
        ORG_CALL_PAL_UNPRIV(0x83)
CallPal_CallSys:
	mfpr	p5, qemu_ps
	mfpr	p6, qemu_exc_addr

	and	p5, PS_M_CM, p0
	beq	p0, 0f

	bsr	p7, CallPal_Stack_Frame_FromUser

	mfpr	p0, ptEntSys
	mfpr	$gp, ptKgp
	hw_ret	(p0)

0:	subq	p6, 4, p6		// Get PC of CALL_PAL insn
	lda	p0, MCHK_K_OS_BUGCHECK
	br	MchkLogOut
ENDFN	CallPal_CallSys

	ORG_CALL_PAL_UNPRIV(0x84)
CallPal_OpcDec84:
	br	CallPal_OpcDec
ENDFN	CallPal_OpcDec84

	ORG_CALL_PAL_UNPRIV(0x85)
CallPal_OpcDec85:
	br	CallPal_OpcDec
ENDFN	CallPal_OpcDec85


/*
 * I-Stream Memory Barrier
 *
 * For QEMU, this is of course a no-op.
 */ 
        ORG_CALL_PAL_UNPRIV(0x86)
CallPal_Imb:
	mb
	hw_rei
ENDFN	CallPal_Imb


	ORG_CALL_PAL_UNPRIV(0x87)
CallPal_OpcDec87:
	br	CallPal_OpcDec
ENDFN	CallPal_OpcDec87

	ORG_CALL_PAL_UNPRIV(0x88)
CallPal_OpcDec88:
	br	CallPal_OpcDec
ENDFN	CallPal_OpcDec88

	ORG_CALL_PAL_UNPRIV(0x89)
CallPal_OpcDec89:
	br	CallPal_OpcDec
ENDFN	CallPal_OpcDec89

	ORG_CALL_PAL_UNPRIV(0x8A)
CallPal_OpcDec8A:
	br	CallPal_OpcDec
ENDFN	CallPal_OpcDec8A

	ORG_CALL_PAL_UNPRIV(0x8B)
CallPal_OpcDec8B:
	br	CallPal_OpcDec
ENDFN	CallPal_OpcDec8B

	ORG_CALL_PAL_UNPRIV(0x8C)
CallPal_OpcDec8C:
	br	CallPal_OpcDec
ENDFN	CallPal_OpcDec8C

	ORG_CALL_PAL_UNPRIV(0x8D)
CallPal_OpcDec8D:
	br	CallPal_OpcDec
ENDFN	CallPal_OpcDec8D

	ORG_CALL_PAL_UNPRIV(0x8E)
CallPal_OpcDec8E:
	br	CallPal_OpcDec
ENDFN	CallPal_OpcDec8E

	ORG_CALL_PAL_UNPRIV(0x8F)
CallPal_OpcDec8F:
	br	CallPal_OpcDec
ENDFN	CallPal_OpcDec8F

	ORG_CALL_PAL_UNPRIV(0x90)
CallPal_OpcDec90:
	br	CallPal_OpcDec
ENDFN	CallPal_OpcDec90

	ORG_CALL_PAL_UNPRIV(0x91)
CallPal_OpcDec91:
	br	CallPal_OpcDec
ENDFN	CallPal_OpcDec91

	ORG_CALL_PAL_UNPRIV(0x92)
CallPal_OpcDec92:
	br	CallPal_OpcDec
ENDFN	CallPal_OpcDec92

	ORG_CALL_PAL_UNPRIV(0x93)
CallPal_OpcDec93:
	br	CallPal_OpcDec
ENDFN	CallPal_OpcDec93

	ORG_CALL_PAL_UNPRIV(0x94)
CallPal_OpcDec94:
	br	CallPal_OpcDec
ENDFN	CallPal_OpcDec94

	ORG_CALL_PAL_UNPRIV(0x95)
CallPal_OpcDec95:
	br	CallPal_OpcDec
ENDFN	CallPal_OpcDec95

	ORG_CALL_PAL_UNPRIV(0x96)
CallPal_OpcDec96:
	br	CallPal_OpcDec
ENDFN	CallPal_OpcDec96

	ORG_CALL_PAL_UNPRIV(0x97)
CallPal_OpcDec97:
	br	CallPal_OpcDec
ENDFN	CallPal_OpcDec97

	ORG_CALL_PAL_UNPRIV(0x98)
CallPal_OpcDec98:
	br	CallPal_OpcDec
ENDFN	CallPal_OpcDec98

	ORG_CALL_PAL_UNPRIV(0x99)
CallPal_OpcDec99:
	br	CallPal_OpcDec
ENDFN	CallPal_OpcDec99

	ORG_CALL_PAL_UNPRIV(0x9A)
CallPal_OpcDec9A:
	br	CallPal_OpcDec
ENDFN	CallPal_OpcDec9A

	ORG_CALL_PAL_UNPRIV(0x9B)
CallPal_OpcDec9B:
	br	CallPal_OpcDec
ENDFN	CallPal_OpcDec9B

	ORG_CALL_PAL_UNPRIV(0x9C)
CallPal_OpcDec9C:
	br	CallPal_OpcDec
ENDFN	CallPal_OpcDec9C

	ORG_CALL_PAL_UNPRIV(0x9D)
CallPal_OpcDec9D:
	br	CallPal_OpcDec
ENDFN	CallPal_OpcDec9D

/*
 * Read Unique Value
 * 
 * OUTPUT PARAMETERS:
 *
 *	r0 (v0) = Returned process unique value
*/ 
        ORG_CALL_PAL_UNPRIV(0x9E)
CallPal_RdUnique:
	mfpr	v0, qemu_unique
	hw_rei
ENDFN	CallPal_RdUnique

/*
 * Write Unique Value
 * 
 * INPUT PARAMETERS:
 * 
 *	r16 (a0) = New process unique value
 */ 
        ORG_CALL_PAL_UNPRIV(0x9F)
CallPal_WrUnique:
	mtpr	a0, qemu_unique
	hw_rei
ENDFN	CallPal_WrUnique

	ORG_CALL_PAL_UNPRIV(0xA0)
CallPal_OpcDecA0:
	br	CallPal_OpcDec
ENDFN	CallPal_OpcDecA0

	ORG_CALL_PAL_UNPRIV(0xA1)
CallPal_OpcDecA1:
	br	CallPal_OpcDec
ENDFN	CallPal_OpcDecA1

	ORG_CALL_PAL_UNPRIV(0xA2)
CallPal_OpcDecA2:
	br	CallPal_OpcDec
ENDFN	CallPal_OpcDecA2

	ORG_CALL_PAL_UNPRIV(0xA3)
CallPal_OpcDecA3:
	br	CallPal_OpcDec
ENDFN	CallPal_OpcDecA3

	ORG_CALL_PAL_UNPRIV(0xA4)
CallPal_OpcDecA4:
	br	CallPal_OpcDec
ENDFN	CallPal_OpcDecA4

	ORG_CALL_PAL_UNPRIV(0xA5)
CallPal_OpcDecA5:
	br	CallPal_OpcDec
ENDFN	CallPal_OpcDecA5

	ORG_CALL_PAL_UNPRIV(0xA6)
CallPal_OpcDecA6:
	br	CallPal_OpcDec
ENDFN	CallPal_OpcDecA6

	ORG_CALL_PAL_UNPRIV(0xA7)
CallPal_OpcDecA7:
	br	CallPal_OpcDec
ENDFN	CallPal_OpcDecA7

	ORG_CALL_PAL_UNPRIV(0xA8)
CallPal_OpcDecA8:
	br	CallPal_OpcDec
ENDFN	CallPal_OpcDecA8

	ORG_CALL_PAL_UNPRIV(0xA9)
CallPal_OpcDecA9:
	br	CallPal_OpcDec
ENDFN	CallPal_OpcDecA9

/*
 * Generate Trap
 *
 * OUTPUT PARAMETERS:
 *
 *	r16 (a0) = Code for gentrap (2)
 *	r17 (a1) = UNPREDICTABLE
 *	r18 (a2) = UNPREDICTABLE
 */ 
        ORG_CALL_PAL_UNPRIV(0xAA)
CallPal_GenTrap:
	mfpr	p5, qemu_ps
	mfpr	p6, qemu_exc_addr
	bsr	p7, CallPal_Stack_Frame

	mfpr	p0, ptEntIF
	mfpr	$gp, ptKgp
	mov	IF_K_GENTRAP, a0
	hw_ret	(p0)
ENDFN	CallPal_GenTrap

	ORG_CALL_PAL_UNPRIV(0xAB)
CallPal_OpcDecAB:
	br	CallPal_OpcDec
ENDFN	CallPal_OpcDecAB

	ORG_CALL_PAL_UNPRIV(0xAC)
CallPal_OpcDecAC:
	br	CallPal_OpcDec
ENDFN	CallPal_OpcDecAC

	ORG_CALL_PAL_UNPRIV(0xAD)
CallPal_OpcDecAD:
	br	CallPal_OpcDec
ENDFN	CallPal_OpcDecAD

	ORG_CALL_PAL_UNPRIV(0xAE)
CallPal_OpcDecAE:
	br	CallPal_OpcDec
ENDFN	CallPal_OpcDecAE

	ORG_CALL_PAL_UNPRIV(0xAF)
CallPal_OpcDecAF:
	br	CallPal_OpcDec
ENDFN	CallPal_OpcDecAF

	ORG_CALL_PAL_UNPRIV(0xB0)
CallPal_OpcDecB0:
	br	CallPal_OpcDec
ENDFN	CallPal_OpcDecB0

	ORG_CALL_PAL_UNPRIV(0xB1)
CallPal_OpcDecB1:
	br	CallPal_OpcDec
ENDFN	CallPal_OpcDecB1

	ORG_CALL_PAL_UNPRIV(0xB2)
CallPal_OpcDecB2:
	br	CallPal_OpcDec
ENDFN	CallPal_OpcDecB2

	ORG_CALL_PAL_UNPRIV(0xB3)
CallPal_OpcDecB3:
	br	CallPal_OpcDec
ENDFN	CallPal_OpcDecB3

	ORG_CALL_PAL_UNPRIV(0xB4)
CallPal_OpcDecB4:
	br	CallPal_OpcDec
ENDFN	CallPal_OpcDecB4

	ORG_CALL_PAL_UNPRIV(0xB5)
CallPal_OpcDecB5:
	br	CallPal_OpcDec
ENDFN	CallPal_OpcDecB5

	ORG_CALL_PAL_UNPRIV(0xB6)
CallPal_OpcDecB6:
	br	CallPal_OpcDec
ENDFN	CallPal_OpcDecB6

	ORG_CALL_PAL_UNPRIV(0xB7)
CallPal_OpcDecB7:
	br	CallPal_OpcDec
ENDFN	CallPal_OpcDecB7

	ORG_CALL_PAL_UNPRIV(0xB8)
CallPal_OpcDecB8:
	br	CallPal_OpcDec
ENDFN	CallPal_OpcDecB8

	ORG_CALL_PAL_UNPRIV(0xB9)
CallPal_OpcDecB9:
	br	CallPal_OpcDec
ENDFN	CallPal_OpcDecB9

	ORG_CALL_PAL_UNPRIV(0xBA)
CallPal_OpcDecBA:
	br	CallPal_OpcDec
ENDFN	CallPal_OpcDecBA

	ORG_CALL_PAL_UNPRIV(0xBB)
CallPal_OpcDecBB:
	br	CallPal_OpcDec
ENDFN	CallPal_OpcDecBB

	ORG_CALL_PAL_UNPRIV(0xBC)
CallPal_OpcDecBC:
	br	CallPal_OpcDec
ENDFN	CallPal_OpcDecBC

	ORG_CALL_PAL_UNPRIV(0xBD)
CallPal_OpcDecBD:
	br	CallPal_OpcDec
ENDFN	CallPal_OpcDecBD

	ORG_CALL_PAL_UNPRIV(0xBE)
CallPal_OpcDecBE:
	br	CallPal_OpcDec
ENDFN	CallPal_OpcDecBE

	ORG_CALL_PAL_UNPRIV(0xBF)
CallPal_OpcDec:
	mfpr	p5, qemu_ps
	mfpr	p6, qemu_exc_addr
	bsr	p7, CallPal_Stack_Frame

	mfpr	p0, ptEntIF
	mfpr	$gp, ptKgp
	mov	IF_K_OPCDEC, a0
	hw_ret	(p0)
ENDFN	CallPal_OpcDec

	.org	0x3000
	.text	1
/*
 * Build Machine Check Logout Frame
 *
 *      This portion of the  machine check handler builds a logout frame
 *      in the PAL impure scratch area, builds a stack frame on the kernel
 *      stack (already built if there was an interrupt machine check),
 *      loads the GP with the KGP, loads the machine check entry 
 *      code in a0, loads a platform-specific interrupt vector 
 *      (typically the same value as the SCB offset) in a1, loads 
 *      the kseg address of the logout area in a2, and dispatches 
 *      to the kernel interrupt handler pointed to by the entInt 
 *      operating system entry point.
 *
 * INPUT PARAMETERS:
 *
 *      r8  (p0) = Machine check reason
 *      r14 (p6) = Exception address 
 *
 * OUTPUT PARAMETERS:
 * 
 *      a0 (r16) = Machine check entry type
 *      a1 (r17) = Platform-specific interrupt vector
 *      a2 (r18) = Pointer to logout area
 */

#define MCHK_COMMON_SIZE  24
#define MCHK_LOGOUT_SIZE  144

MchkLogOut:
	mfpr	p1, qemu_ps

	// ??? Apparently we skip the insn that caused the mchk.
	// ??? This is used by the kernel for mcheck_expected.
	addq	p6, 4, p6

	STACK_FRAME p1, p6, p2, 0

	// Restore the real EXC_ADDR for the logout frame.
	subq	p6, 4, p6

	// Locate logout frame
	br	$gp, .+4
	ldah	$gp, 0($gp)			!gpdisp!2
	lda	$gp, 0($gp)			!gpdisp!2
	ldah	p1, mchk_logout($gp)		!gprelhigh
	lda	p1, mchk_logout(p1)		!gprellow

	SYS_WHAMI p2
	mull	p2, MCHK_LOGOUT_SIZE, p2
	addq	p2, p1, a2

	// Populate the minimal logout frame
	lda	p2, MCHK_LOGOUT_SIZE
	stl	p2, 0(a2)			// size
	stl	$31, 4(a2)			// flags
	lda	p1, MCHK_COMMON_SIZE
	stl	p1, 8(a2)			// proc_offset
	stl	p2, 12(a2)			// sys_offset
	stl	p0, 16(a2)			// mchk_code
	stl	$31, 20(a2)			// frame_rev

	// EV6 portion, format copied from Linux kernel
	// although there's not much we can fill in.
	stq	$31, 24(a2)			// I_STAT
	stq	$31, 32(a2)			// DC_STAT
	stq	$31, 40(a2)			// C_ADDR
	stq	$31, 48(a2)			// DC1_SYNDROME
	stq	$31, 56(a2)			// DC0_SYNDROME
	stq	$31, 64(a2)			// C_STAT
	stq	$31, 72(a2)			// C_STS
	stq	$31, 80(a2)			// MM_STAT
	stq	p6, 88(a2)			// EXC_ADDR
	stq	$31, 96(a2)			// IER_CM
	stq	$31, 104(a2)			// ISUM
	stq	$31, 112(a2)			// RESERVED0
	mfpr	p2, qemu_palbr
	stq	p2, 120(a2)			// PAL_BASE
	stq	$31, 128(a2)			// I_CTL
	stq	$31, 136(a2)			// PCTX

	mov	IPL_K_MCHK, p1			// Raise IPL
	mtpr	p1, qemu_ps

	mfpr	p6, ptEntInt
	mfpr	$gp, ptKgp
	lda	a0, INT_K_MCHK
	lda	a1, 0				// "vector"

	hw_ret	(p6)
ENDFN	MchkLogOut

MchkDouble:
	bsr	p7, UpdatePCB
	lda	v0, HLT_K_DBL_MCHK
	br	Sys_EnterConsole
ENDFN	MchkDouble

MchkBugCheck:
MchkFromPal:
	bsr	p7, UpdatePCB
	lda	v0, HLT_K_MCHK_FROM_PAL
	br	Sys_EnterConsole
ENDFN	MchkFromPal

MchkKspInvalid:
	bsr	p7, UpdatePCB
	lda	v0, HLT_K_KSP_INVAL
	br	Sys_EnterConsole
ENDFN	MchkKspInvalid

/*
 * Update the current PCB with new SP and CC info.
 *
 * INPUT PARAMETERS:
 *
 *	p7	= return linkage
 */

UpdatePCB:
	rpcc	p5
	mfpr	p4, ptPcbb

	mfpr	p3, qemu_ps		// Check current mode
	and	p3, PS_M_CM, p3
	beq	p3, 1f

	mtpr	$sp, qemu_usp		// Save user stack pointer
	stq_p	$sp, PCB_Q_USP(p4)
	br	2f

1:	mtpr	$sp, ptKsp		// Save kernel stack pointer
	stq_p	$sp, PCB_Q_KSP(p4)

2:	srl	p5, 32, p3		// Merge for new time
	addl	p5, p3, p3
	stl_p	p3, PCB_L_PCC(p4)	// Store new time

	mfpr	p5, qemu_unique		// Save unique
	stq_p	p5, PCB_Q_UNIQUE(p4)

	ret	$31, (p7), 0
ENDFN	UpdatePCB

/*
 * FIXME
 */
Sys_EnterConsole:
	halt

/*
 * Allocate the initial bootup stack.
 */

	.section .bss.stack
	.align 3
	.globl	stack
	.type	stack,@object
	.size	stack,STACK_SIZE * 4
stack:	.skip	STACK_SIZE * 4

	.globl	mchk_logout
	.type	mchk_logout,@object
	.size	mchk_logout,MCHK_LOGOUT_SIZE * 4
mchk_logout:
	.skip	MCHK_LOGOUT_SIZE * 4
