Re-org for Clipper system.
diff --git a/Makefile b/Makefile
index ea2eac2..f62888f 100644
--- a/Makefile
+++ b/Makefile
@@ -2,25 +2,28 @@
 CC = $(CROSS)gcc
 LD = $(CROSS)ld
 
+CORE = typhoon
+SYSTEM = clipper
+
+ASFLAGS = -Wa,-m21264 -Wa,--noexecstack
 CFLAGS = -Os -g -Wall -fvisibility=hidden -fno-strict-aliasing \
   -msmall-text -msmall-data -mno-fp-regs -mbuild-constants
+CPPFLAGS = -DSYSTEM_H='"sys-$(SYSTEM).h"'
 
-CFLAGS += -mcpu=pca56
+CFLAGS += -mcpu=ev67
 
-OBJS = pal.o init.o uart.o memset.o printf.o
+OBJS = pal.o sys-$(SYSTEM).o init.o uart.o memset.o printf.o
 
-all: palcode-sx164
+all: palcode-$(SYSTEM)
 
-palcode-sx164: palcode.ld $(OBJS)
+palcode-$(SYSTEM): palcode.ld $(OBJS)
 	$(LD) -relax -o $@ -T palcode.ld -Map $@.map $(OBJS)
 
 clean:
 	rm -f *.o
-	rm -f palcode palcode.map
+	rm -f palcode-*
 
-pal.o: pal.S osf.h uart.h
-	$(CC) $(CFLAGS) -c -Wa,-m21264 -Wa,--noexecstack -o $@ $<
-
-init.o: init.c hwrpb.h osf.h uart.h
+pal.o: pal.S osf.h sys-$(SYSTEM).h core-$(CORE).h
+init.o: init.c hwrpb.h osf.h uart.h sys-$(SYSTEM).h core-$(CORE).h
 printf.o: printf.c uart.h
-uart.o: uart.c uart.h io.h cia.h
+uart.o: uart.c uart.h protos.h
diff --git a/cia.h b/core-cia.h
similarity index 80%
rename from cia.h
rename to core-cia.h
index cea81b6..a42cea0 100644
--- a/cia.h
+++ b/core-cia.h
@@ -1,63 +1,8 @@
-#ifndef __ALPHA_CIA__H__
-#define __ALPHA_CIA__H__
+#ifndef CIA_H
+#define CIA_H
 
 #define IDENT_ADDR 0xfffffc0000000000UL
 
-/*
- * CIA is the internal name for the 21171 chipset which provides
- * memory controller and PCI access for the 21164 chip based systems.
- * Also supported here is the 21172 (CIA-2) and 21174 (PYXIS).
- *
- * The lineage is a bit confused, since the 21174 was reportedly started
- * from the 21171 Pass 1 mask, and so is missing bug fixes that appear
- * in 21171 Pass 2 and 21172, but it also contains additional features.
- *
- * This file is based on:
- *
- * DECchip 21171 Core Logic Chipset
- * Technical Reference Manual
- *
- * EC-QE18B-TE
- *
- * david.rusling@reo.mts.dec.com Initial Version.
- *
- */
-
-/*
- * CIA ADDRESS BIT DEFINITIONS
- *
- *  3333 3333 3322 2222 2222 1111 1111 11
- *  9876 5432 1098 7654 3210 9876 5432 1098 7654 3210
- *  ---- ---- ---- ---- ---- ---- ---- ---- ---- ----
- *  1                                             000
- *  ---- ---- ---- ---- ---- ---- ---- ---- ---- ----
- *  |                                             |\|
- *  |                               Byte Enable --+ |
- *  |                             Transfer Length --+
- *  +-- IO space, not cached
- *
- *   Byte      Transfer
- *   Enable    Length    Transfer  Byte    Address
- *   adr<6:5>  adr<4:3>  Length    Enable  Adder
- *   ---------------------------------------------
- *      00        00      Byte      1110   0x000
- *      01        00      Byte      1101   0x020
- *      10        00      Byte      1011   0x040
- *      11        00      Byte      0111   0x060
- *
- *      00        01      Word      1100   0x008
- *      01        01      Word      1001   0x028 <= Not supported in this code.
- *      10        01      Word      0011   0x048
- *
- *      00        10      Tribyte   1000   0x010
- *      01        10      Tribyte   0001   0x030
- *
- *      10        11      Longword  0000   0x058
- *
- *      Note that byte enables are asserted low.
- *
- */
-
 #define CIA_MEM_R1_MASK 0x1fffffff  /* SPARSE Mem region 1 mask is 29 bits */
 #define CIA_MEM_R2_MASK 0x07ffffff  /* SPARSE Mem region 2 mask is 27 bits */
 #define CIA_MEM_R3_MASK 0x03ffffff  /* SPARSE Mem region 3 mask is 26 bits */
@@ -265,4 +210,42 @@
 /* Offset between ram physical addresses and pci64 DAC bus addresses.  */
 #define PYXIS_DAC_OFFSET		(1UL << 40)
 
-#endif /* __ALPHA_CIA__H__ */
+#ifdef __ASSEMBLER__
+
+/* Unfortunately, GAS doesn't attempt any interesting constructions of
+   64-bit constants, dropping them all into the .lit8 section.  It is
+   better for us to build these by hand.  */
+.macro	LOAD_PHYS_PYXIS_INT ret
+	lda	\ret, 0x87a
+	sll	\ret, 28, \ret
+.endm
+
+.macro	LOAD_KSEG_PCI_IO ret
+	lda	\ret, -887
+	sll	\ret, 32, \ret
+.endm
+
+.macro	SYS_WHAMI	ret
+	mov	0, \ret
+.endm
+
+.macro	SYS_ACK_SMP	t0, t1, t2
+	br	MchkBugCheck
+.endm
+
+#else
+
+static inline unsigned long inb(unsigned long port)
+{
+  return *(volatile unsigned char *)(CIA_BW_IO + port);
+}
+
+static inline void outb(unsigned long port, unsigned char val)
+{
+  *(volatile unsigned char *)(CIA_BW_IO + port) = val;
+}
+
+
+#endif
+
+#endif /* CIA_H */
diff --git a/core-typhoon.h b/core-typhoon.h
new file mode 100644
index 0000000..70d85af
--- /dev/null
+++ b/core-typhoon.h
@@ -0,0 +1,163 @@
+#ifndef TYPHOON_H
+#define TYPHOON_H 1
+
+/* Assume a 43-bit KSEG for now.  */
+#define PIO_PHYS_ADDR   0x80000000000
+#define PIO_KSEG_ADDR   (0xfffffc0000000000 + 0x10000000000)
+
+/* CCHIP REGISTERS */
+
+#define TYPHOON_CCHIP		0x1a0000000
+
+#define TYPHOON_CCHIP_CSC	0x0000
+#define TYPHOON_CCHIP_MTR	0x0040
+#define TYPHOON_CCHIP_MISC	0x0080
+#define TYPHOON_CCHIP_MPD	0x00c0
+#define TYPHOON_CCHIP_AAR0	0x0100
+#define TYPHOON_CCHIP_AAR1	0x0140
+#define TYPHOON_CCHIP_AAR2	0x0180
+#define TYPHOON_CCHIP_AAR3	0x01c0
+#define TYPHOON_CCHIP_DIM0	0x0200
+#define TYPHOON_CCHIP_DIM1	0x0240
+#define TYPHOON_CCHIP_DIR0	0x0280
+#define TYPHOON_CCHIP_DIR1	0x02c0
+#define TYPHOON_CCHIP_DRIR	0x0300
+#define TYPHOON_CCHIP_PRBEN	0x0340
+#define TYPHOON_CCHIP_IIC0	0x0380
+#define TYPHOON_CCHIP_IIC1	0x03c0
+#define TYPHOON_CCHIP_MPR0	0x0400
+#define TYPHOON_CCHIP_MPR1	0x0440
+#define TYPHOON_CCHIP_MPR2	0x0480
+#define TYPHOON_CCHIP_MPR3	0x04c0
+#define TYPHOON_CCHIP_TTR	0x0580
+#define TYPHOON_CCHIP_TDR	0x05c0
+#define TYPHOON_CCHIP_DIM2	0x0600
+#define TYPHOON_CCHIP_DIM3	0x0640
+#define TYPHOON_CCHIP_DIR2	0x0680
+#define TYPHOON_CCHIP_DIR3	0x06c0
+#define TYPHOON_CCHIP_IIC2	0x0700
+#define TYPHOON_CCHIP_IIC3	0x0740
+#define TYPHOON_CCHIP_PWR	0x0780
+#define TYPHOON_CCHIP_CMONCTLA	0x0c00
+#define TYPHOON_CCHIP_CMONCTLB	0x0c40
+#define TYPHOON_CCHIP_CMONCNT01	0x0c80
+#define TYPHOON_CCHIP_CMONCNT23	0x0cc0
+
+/* DCHIP REGISTERS */
+
+#define TYPHOON_DCHIP		0x1b0000000
+
+#define TYPHOON_DCHIP_DSC	0x0800
+#define TYPHOON_DCHIP_STR	0x0840
+#define TYPHOON_DCHIP_DREV	0x0880
+#define TYPHOON_DCHIP_DSC2	0x08c0
+
+/* PCHIP REGISTERS */
+
+#define TYPHOON_PCHIP0		0x180000000
+#define TYPHOON_PCHIP1		0x380000000
+
+#define TYPHOON_PCHIP_WSBA0	0x0000
+#define TYPHOON_PCHIP_WSBA1	0x0040
+#define TYPHOON_PCHIP_WSBA2	0x0080
+#define TYPHOON_PCHIP_WSBA3	0x00c0
+#define TYPHOON_PCHIP_WSM0	0x0100
+#define TYPHOON_PCHIP_WSM1	0x0140
+#define TYPHOON_PCHIP_WSM2	0x0180
+#define TYPHOON_PCHIP_WSM3	0x01c0
+#define TYPHOON_PCHIP_TBA0	0x0200
+#define TYPHOON_PCHIP_TBA1	0x0240
+#define TYPHOON_PCHIP_TBA2	0x0280
+#define TYPHOON_PCHIP_TBA3	0x02c0
+#define TYPHOON_PCHIP_PCTL	0x0300
+#define TYPHOON_PCHIP_PLAT	0x0340
+#define TYPHOON_PCHIP_PERROR	0x03c0
+#define TYPHOON_PCHIP_PERRMASK	0x0400
+#define TYPHOON_PCHIP_PERRSET	0x0440
+#define TYPHOON_PCHIP_TLBIV	0x0480
+#define TYPHOON_PCHIP_TLBIA	0x04c0
+#define TYPHOON_PCHIP_PMONCTL	0x0500
+#define TYPHOON_PCHIP_PMONCNT	0x0540
+#define TYPHOON_PCHIP_SPRST	0x0800
+
+/* PCI ADDRESSES */
+
+#define TYPHOON_PCHIP0_PCI_MEM	0
+#define TYPHOON_PCHIP0_PCI_IO	0x1fc000000
+#define TYPHOON_PCHIP0_PCI_CONF	0x1fe000000
+
+#ifdef __ASSEMBLER__
+
+#include "pal.h"
+
+#define	ptCpuDIR	ptSys0
+#define	ptCpuIIC	ptSys1
+
+/* Unfortunately, GAS doesn't attempt any interesting constructions of
+   64-bit constants, dropping them all into the .lit8 section.  It is
+   better for us to build these by hand.  */
+.macro	LOAD_PHYS_CCHIP ret
+	lda	\ret, 0x400d
+	sll	\ret, 29, \ret
+.endm
+
+.macro	LOAD_PHYS_PCHIP0 ret
+	lda	\ret, 0x400c
+	sll	\ret, 29, \ret
+.endm
+
+.macro	LOAD_KSEG_PCI_IO ret
+	ldah	\ret, -48
+	lda	\ret, 0x1fc0(\ret)
+	sll	\ret, 20, \ret
+.endm
+
+.macro	SYS_WHAMI	ret
+	LOAD_PHYS_CCHIP	\ret
+	ldq_p		\ret, TYPHOON_CCHIP_MISC(\ret)
+	and		\ret, 3, \ret
+.endm
+
+/* ACK the Interprocessor Interrupt.  */
+.macro	SYS_ACK_SMP	t0, t1, t2
+	LOAD_PHYS_CCHIP	\t0
+	ldq_p		\t1, TYPHOON_CCHIP_MISC(\t0)
+	and		\t1, 3, \t1
+	addq		\t1, 8, \t1
+	lda		\t2, 1
+	sll		\t2, \t1, \t2
+	stq_p		\t2, TYPHOON_CCHIP_MISC(\t0)
+.endm
+
+/* ACK the Clock Interrupt.  */
+.macro	SYS_ACK_CLK	t0, t1, t2
+	LOAD_PHYS_CCHIP	\t0
+	ldq_p		\t1, TYPHOON_CCHIP_MISC(\t0)
+	and		\t1, 3, \t1
+	addq		\t1, 4, \t1
+	lda		\t2, 1
+	sll		\t2, \t1, \t2
+	stq_p		\t2, TYPHOON_CCHIP_MISC(\t0)
+.endm
+
+/* Load the device interrupt vector.  */
+.macro	SYS_DEV_VECTOR	ret
+	mfpr		\ret, ptCpuDIR
+	ldq_p		\ret, 0(\ret)
+	cttz		\ret, \ret
+	sll		\ret, 4, \ret
+	lda		\ret, 0x800(\ret)
+.endm
+
+/* Interrupt another CPU.  */
+.macro SYS_WRIPIR	target, t0, t1, t2
+	LOAD_PHYS_CCHIP	\t0
+	mov		1, \t1
+	and		\target, 3, \t2
+	addq		\t2, 12, \t2
+	sll		\t1, \t2, \t1
+	stq_p		\t1, TYPHOON_CCHIP_MISC(\t0)
+.endm
+
+#endif /* ASSEMBLER */
+#endif /* TYPHOON_H */
diff --git a/init.c b/init.c
index 169ae07..535f92b 100644
--- a/init.c
+++ b/init.c
@@ -3,6 +3,7 @@
 #include "hwrpb.h"
 #include "osf.h"
 #include "uart.h"
+#include SYSTEM_H
 
 #define PAGE_SHIFT	13
 #define PAGE_SIZE	(1ul << PAGE_SHIFT)
@@ -141,6 +142,7 @@
 init_hwrpb (unsigned long memsize)
 {
   unsigned long pal_pages;
+  unsigned long cpu_type;
   
   hwrpb.hwrpb.phys_addr = PA(&hwrpb);
 
@@ -167,14 +169,15 @@
   ((int *)hwrpb.hwrpb.ssn)[2] = ( 'U' << 0);
 
   /* For now, hard-code emulation of sx164.  */
-  hwrpb.hwrpb.cpuid = PCA56_CPU;
+  cpu_type = init_cpuid();
+  hwrpb.hwrpb.cpuid = cpu_type;
   hwrpb.hwrpb.pagesize = PAGE_SIZE;
   hwrpb.hwrpb.pa_bits = 40;
   hwrpb.hwrpb.max_asn = 127;
-  hwrpb.hwrpb.sys_type = ST_DEC_EB164;
-  hwrpb.hwrpb.sys_variation = 15 << 10;
-  hwrpb.hwrpb.sys_revision = 0;
-  hwrpb.processor.type = PCA56_CPU;
+  hwrpb.hwrpb.sys_type = SYS_TYPE;
+  hwrpb.hwrpb.sys_variation = SYS_VARIATION;
+  hwrpb.hwrpb.sys_revision = SYS_REVISION;
+  hwrpb.processor.type = cpu_type;
 
   hwrpb.hwrpb.intr_freq = HZ * 4096;
   hwrpb.hwrpb.cycle_freq = 250000000;	/* QEMU architects 250MHz.  */
@@ -220,7 +223,7 @@
 }
 
 void
-do_start(unsigned long memsize, void (*kernel_entry)(void))
+do_start(unsigned long memsize, void (*kernel_entry)(void), long cpus)
 {
   last_alloc = _end;
 
@@ -242,3 +245,16 @@
   }
   __builtin_unreachable ();
 }
+
+void
+do_start_wait(void)
+{
+  while (1)
+    {
+      // WtInt with interrupts off.  Rely on the fact that QEMU will
+      // un-halt the CPU when an interrupt arrives.
+      asm("lda $16,-1\n\tcall_pal 0x3e" : : : "$0", "$16");
+
+      // FIXME do something with the IPI.
+    }
+}
diff --git a/io.h b/io.h
deleted file mode 100644
index c58c271..0000000
--- a/io.h
+++ /dev/null
@@ -1,18 +0,0 @@
-#ifndef IO_H
-#define IO_H
-
-#include "cia.h"
-
-static inline unsigned long
-inb(unsigned long port)
-{
-  return *(volatile unsigned char *)(CIA_BW_IO + port);
-}
-
-static inline void
-outb(unsigned char val, unsigned long port)
-{
-  *(volatile unsigned char *)(CIA_BW_IO + port) = val;
-}
-
-#endif
diff --git a/osf.h b/osf.h
index c7e18c8..78e2e70 100644
--- a/osf.h
+++ b/osf.h
@@ -59,6 +59,7 @@
 #define IPL_K_DEV0      0x3
 #define IPL_K_DEV1      0x4
 #define IPL_K_CLK       0x5
+#define IPL_K_IP        0x6
 #define IPL_K_RT        0x6
 #define IPL_K_PERF      0x6
 #define IPL_K_PFAIL     0x6
diff --git a/pal.S b/pal.S
index d604666..124763a 100644
--- a/pal.S
+++ b/pal.S
@@ -2,129 +2,9 @@
 	.set		nomacro
 	.text
 
+#include "pal.h"
 #include "osf.h"
-#include "uart.h"
-
-/* General Purpose Registers.  */
-#define	v0	$0
-#define t0	$1
-#define t1	$2
-#define t2	$3
-#define t3	$4
-#define t4	$5
-#define t5	$6
-#define a0	$16
-#define a1	$17
-#define a2	$18
-#define a3	$19
-#define a4	$20
-#define a5	$21
-#define t8	$22
-#define t9	$23
-#define t10	$24
-
-/* PALcode Shadow Registers.  These registers are swapped out when
-   QEMU is in PALmode.  Unlike real hardware, there is no enable bit.
-   However, also unlike real hardware, the originals can be accessed
-   via MTPR/MFPR.  */
-#define p0	$8
-#define p1	$9
-#define p2	$10
-#define p3	$11
-#define p4	$12
-#define p5	$13
-#define p6	$14		// Used to save exc_addr for machine check
-#define p7	$25
-
-/* QEMU Processor Registers.  */
-#define	qemu_ps		0
-#define qemu_fen	1
-#define qemu_pcc_ofs	2
-#define qemu_trap_arg0	3
-#define qemu_trap_arg1	4
-#define qemu_trap_arg2	5
-#define qemu_exc_addr	6
-#define qemu_palbr	7
-#define qemu_ptbr	8
-#define qemu_vptptr	9
-#define qemu_unique	10
-#define qemu_sysval	11
-#define qemu_usp	12
-
-#define qemu_shadow0	32
-#define qemu_shadow1	33
-#define qemu_shadow2	34
-#define qemu_shadow3	35
-#define qemu_shadow4	36
-#define qemu_shadow5	37
-#define qemu_shadow6	38
-#define qemu_shadow7	39
-
-/* PALcode Processor Register Private Storage.  */
-#define pt0		40
-#define pt1		41
-#define pt2		42
-#define pt3		43
-#define pt4		44
-#define pt5		45
-#define pt6		46
-#define pt7		47
-#define pt8		48
-#define pt9		49
-#define pt10		50
-#define pt11		51
-#define pt12		52
-#define pt13		53
-#define pt14		54
-#define pt15		55
-#define pt16		56
-#define pt17		57
-#define pt18		58
-#define pt19		59
-#define pt20		60
-#define pt21		61
-#define pt22		62
-#define pt23		63
-
-/* QEMU function calls, via mtpr.  */
-#define qemu_tbia	255
-#define qemu_tbis	254
-#define qemu_wait	253
-
-/* PALcode uses of the private storage slots.  */
-#define ptEntUna	pt0
-#define ptEntIF		pt1
-#define ptEntSys	pt2
-#define ptEntInt	pt3
-#define ptEntArith	pt4
-#define ptEntMM		pt5
-#define ptMces		pt6
-#define ptKsp		pt7
-#define ptKgp		pt8
-#define ptPcbb		pt9
-#define ptPgp		pt10
-#define ptMisc		pt11
-#define ptMchk0		pt12
-#define ptMchk1		pt13
-#define ptMchk2		pt14
-#define ptMchk3		pt15
-#define ptMchk4		pt16
-#define ptMchk5		pt17
-
-/*
- * Shortcuts for various PALmode instructions.
- */
-#define mtpr	hw_mtpr
-#define mfpr	hw_mfpr
-#define stq_p	hw_stq/p
-#define stl_p	hw_stl/p
-#define ldl_p	hw_ldl/p
-#define ldq_p	hw_ldq/p
-
-/* QEMU recognizes the EV4/EV5 HW_REI instruction as a special case of
-   the EV6 HW_RET instruction.  This pulls the destination address from
-   the EXC_ADDR processor register.  */
-#define hw_rei	hw_ret ($31)
+#include SYSTEM_H
 
 /*
  * Create a standard kernel entry stack frame.
@@ -158,11 +38,6 @@
 	stq	a2, FRM_Q_A2($sp)
 .endm
 
-.macro	ENDFN	function
-	.type	\function, @function
-	.size	\function, . - \function
-.endm
-
 /*
  * Allocate a 1 page stack for use by the console.
  */
@@ -183,26 +58,30 @@
         .org	0x0000
 	.globl	__start
 __start:
-	// Initialize GP and stack.
+	// Initialize GP.
 	br	$gp, .+4
 	ldah	$gp, 0($gp)			!gpdisp!1
 	lda	$gp, 0($gp)			!gpdisp!1
 	mtpr	$gp, ptPgp
 
-	lda	$sp, stack+STACK_SIZE($gp)	!gprel
-
 	// Disable interrupts; kernel mode
 	lda	t0, IPL_K_HIGH
 	mtpr	t0, qemu_ps
 
-	// Make sure kernel entry points are invalid.
-	lda	t0, -1
-	mtpr	t0, ptEntUna
-	mtpr	t0, ptEntIF
-	mtpr	t0, ptEntSys
-	mtpr	t0, ptEntInt
-	mtpr	t0, ptEntArith
-	mtpr	t0, ptEntMM
+	// Initialize Stack.
+	SYS_WHAMI a0
+	lda	t0, STACK_SIZE
+	mull	t0, a0, 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
@@ -213,6 +92,10 @@
 	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
 
 /*
@@ -242,6 +125,40 @@
 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:
@@ -253,23 +170,11 @@
  * The clock interrupt is special, in that PALcode is supposed
  * to clear the interupt and not wait for the OS to do it.
  */
-	.org	0x0100
+	.org	0x0180
 Pal_Clk_Interrupt:
 	mfpr	p6, qemu_exc_addr
 
-	// Load CIA_BW_IO.  Note that this is the KSEG address,
-	// since there is no hw_stb with physical address access.
-	lda	p0, -887
-	sll	p0, 32, p0
-
-	mov	0xc, p1			// Set RTCADD (0x70) to index register 0xC
-	stb	p1, 0x70(p0)
-	ldbu	p1, 0x71(p0)		// Read RTCDAT to clear interrupt.
-
-#if 0
-	and	p1, 0x40, p1		// Check for real interrupt.
-	bne	p1, 9f			// If not, exit now, no stack frame.
-#endif
+	SYS_ACK_CLK p0, p1, p2
 
 	mfpr	p0, qemu_ps
 
@@ -296,7 +201,7 @@
  *	trap_arg1 =
  *	trap_arg2 =
  */ 
-	.org	0x0180
+	.org	0x0200
 Pal_Dev_Interrupt:
 	mfpr	p6, qemu_exc_addr
 	mfpr	p0, qemu_ps
@@ -308,9 +213,12 @@
 
 	mfpr	p7, ptEntInt
 	mfpr	$gp, ptKgp
+
 	lda	a0, INT_K_DEV
-	lda	a1, 0x800
 	lda	a2, 0
+
+	SYS_DEV_VECTOR a1
+
 	hw_ret	(p7)
 ENDFN	Pal_Dev_Interrupt
 
@@ -323,7 +231,7 @@
  *	trap_arg1 = fault type (TNV, ACV, FOR, FOW, FOE)
  *	trap_arg2 = access type (exec=-1, read=0, write=1)
  */ 
-	.org	0x0200
+	.org	0x0280
 Pal_MMFault:
 	mfpr	p0, qemu_ps
 	mfpr	p6, qemu_exc_addr
@@ -348,7 +256,7 @@
  *	trap_arg1 = opcode of faulting insn
  *	trap_arg2 = src/dst register number
  */ 
-	.org	0x0280
+	.org	0x0300
 Pal_Unalign:
 	mfpr	p0, qemu_ps
 	mfpr	p6, qemu_exc_addr
@@ -380,7 +288,7 @@
  *	r17 (a1) = UNPREDICTABLE
  *	r18 (a2) = UNPREDICTABLE
  */ 
-	.org	0x0300
+	.org	0x0380
 Pal_OpcDec:
 	mfpr	p0, qemu_ps
 	mfpr	p6, qemu_exc_addr
@@ -404,7 +312,7 @@
  *	trap_arg1 = register modification mask
  *	trap_arg2 = UNDEFINED
  */
-	.org	0x0380
+	.org	0x0400
 Pal_Arith:
 	mfpr	p0, qemu_ps
 	mfpr	p6, qemu_exc_addr
@@ -434,7 +342,7 @@
  *	r17 (a1) = UNPREDICTABLE
  *	r18 (a2) = UNPREDICTABLE
  */ 
-	.org	0x0400
+	.org	0x0480
 Pal_Fen:
 	mfpr	p0, qemu_ps
 	mfpr	p6, qemu_exc_addr
@@ -466,14 +374,9 @@
 	ORG_CALL_PAL_PRIV(0x00)
 CallPal_Halt:
 	bsr	p7, UpdatePCB		// Save kernel data
-	lda	v0, HLT_K_SW_HALT
+	lda	v0, HLT_K_SW_HALT	// FIXME store this somewhere.
 
-	ldah	p0, 1			// Store 0xdead into CIA RESET reg
-	lda	p0, -(0x10000 - 0xdead)(p0)
-	lda	p1, 0x878
-	sll	p1, 28, p1
-	lda	p1, 0x900(p1)
-	stl_p	p0, 0(p1)
+	mtpr	$31, qemu_halt
 
 	br	Sys_EnterConsole
 ENDFN	CallPal_Halt
@@ -535,69 +438,40 @@
  * 
  *	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:
-	cmpeq	a0, 15, v0
-	bne	v0, Cserve_putc
+	// ??? For SRM compatibility, 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
+	bne	v0, Cserve_Ena
 	cmpeq	a0, 53, v0
-	bne	v0, Cserve_dis
+	bne	v0, Cserve_Dis
+
+	// All the rest of the Cserve entry points are compressed from 1.
+	cmpeq	a0, 1, v0
+	bne	v0, Cserve_Ldqp
+	cmpeq	a0, 2, v0
+	bne	v0, Cserve_Stqp
+
 	hw_rei
 ENDFN	CallPal_Cserve
 
 	.text	1
-Cserve_putc:
-	// Load CIA_BW_IO.  Note that this is the KSEG address,
-	// since there is no hw_stb with physical address access.
-	lda	p0, -887
-	sll	p0, 32, p0
-
-	ldbu	v0, com1Lsr(p0)		// Get Transmit Holding Register Empty
-	and	v0, 0x20, v0
-	beq	v0, 1f
-
-	stb	a1, com1Thr(p0)		// Output the byte
-	mov	1, v0
-1:	hw_rei
-ENDFN	Cserve_putc
-
-Cserve_ena:
-	lda	p0, 0x87a		// Load PYXIS INT REG base.
-	sll	p0, 28, p0
-
-	lda	p1, 1			// Shift the interrupt line in place.
-	sll	p1, a1, p1
-
-	ldq_p	p2, 0x40(p0)		// Load PYXIS_INT_MASK
-	sll	p2, a1, v0		// Return the current setting
-	and	v0, 1, v0
-	andnot	p2, p1, p2		// Clear the bit
-	stq_p	p2, 0x40(p0)		// Store PYXIS_INT_MASK
-
+Cserve_Ldqp:
+	ldq_p	v0, 0(a1)
 	hw_rei
-ENDFN	Cserve_ena
+ENDFN	Cserve_Ldqp
 
-Cserve_dis:
-	lda	p0, 0x87a		// Load PYXIS INT REG base.
-	sll	p0, 28, p0
-
-	lda	p1, 1			// Shift the interrupt line in place.
-	sll	p1, a1, p1
-
-	ldq_p	p2, 0x40(p0)		// Load PYXIS_INT_MASK
-	sll	p2, a1, v0		// Return the current setting
-	and	v0, 1, v0
-	or	p2, p1, p2		// Set the bit
-	stq_p	p2, 0x40(p0)		// Store PYXIS_INT_MASK
-
+Cserve_Stqp:
+	stq_p	a2, 0(a1)
 	hw_rei
-ENDFN	Cserve_dis
+ENDFN	Cserve_Stqp
 	.previous
 
 /*
@@ -717,7 +591,11 @@
  */ 
         ORG_CALL_PAL_PRIV(0x0D)
 CallPal_WrIpir:
-	// We do not currently support more cpus
+	// 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
 
@@ -1266,8 +1144,7 @@
  */ 
         ORG_CALL_PAL_PRIV(0x3C)
 CallPal_Whami:
-	// We do not currently support more cpus
-	mov	0, v0
+	SYS_WHAMI v0
 	hw_rei
 ENDFN	CallPal_Whami
 
@@ -2177,6 +2054,7 @@
 $laf_size		= . - laf_base
 	.size	laf_base, . - laf_base
 
+	.section .bss
 	.align 3
 	.globl	stack
 	.type	stack,@object
diff --git a/pal.h b/pal.h
new file mode 100644
index 0000000..a2ee6cf
--- /dev/null
+++ b/pal.h
@@ -0,0 +1,134 @@
+#ifndef PAL_H
+#define PAL_H 1
+
+/* General Purpose Registers.  */
+#define	v0	$0
+#define t0	$1
+#define t1	$2
+#define t2	$3
+#define t3	$4
+#define t4	$5
+#define t5	$6
+#define a0	$16
+#define a1	$17
+#define a2	$18
+#define a3	$19
+#define a4	$20
+#define a5	$21
+#define t8	$22
+#define t9	$23
+#define t10	$24
+
+/* PALcode Shadow Registers.  These registers are swapped out when
+   QEMU is in PALmode.  Unlike real hardware, there is no enable bit.
+   However, also unlike real hardware, the originals can be accessed
+   via MTPR/MFPR.  */
+#define p0	$8
+#define p1	$9
+#define p2	$10
+#define p3	$11
+#define p4	$12
+#define p5	$13
+#define p6	$14		// Used to save exc_addr for machine check
+#define p7	$25
+
+/* QEMU Processor Registers.  */
+#define	qemu_ps		0
+#define qemu_fen	1
+#define qemu_pcc_ofs	2
+#define qemu_trap_arg0	3
+#define qemu_trap_arg1	4
+#define qemu_trap_arg2	5
+#define qemu_exc_addr	6
+#define qemu_palbr	7
+#define qemu_ptbr	8
+#define qemu_vptptr	9
+#define qemu_unique	10
+#define qemu_sysval	11
+#define qemu_usp	12
+
+#define qemu_shadow0	32
+#define qemu_shadow1	33
+#define qemu_shadow2	34
+#define qemu_shadow3	35
+#define qemu_shadow4	36
+#define qemu_shadow5	37
+#define qemu_shadow6	38
+#define qemu_shadow7	39
+
+/* PALcode Processor Register Private Storage.  */
+#define pt0		40
+#define pt1		41
+#define pt2		42
+#define pt3		43
+#define pt4		44
+#define pt5		45
+#define pt6		46
+#define pt7		47
+#define pt8		48
+#define pt9		49
+#define pt10		50
+#define pt11		51
+#define pt12		52
+#define pt13		53
+#define pt14		54
+#define pt15		55
+#define pt16		56
+#define pt17		57
+#define pt18		58
+#define pt19		59
+#define pt20		60
+#define pt21		61
+#define pt22		62
+#define pt23		63
+
+/* QEMU function calls, via mtpr.  */
+#define qemu_tbia	255
+#define qemu_tbis	254
+#define qemu_wait	253
+#define qemu_halt	252
+
+/* PALcode uses of the private storage slots.  */
+#define ptEntUna	pt0
+#define ptEntIF		pt1
+#define ptEntSys	pt2
+#define ptEntInt	pt3
+#define ptEntArith	pt4
+#define ptEntMM		pt5
+#define ptMces		pt6
+#define ptKsp		pt7
+#define ptKgp		pt8
+#define ptPcbb		pt9
+#define ptPgp		pt10
+#define ptMisc		pt11
+#define ptMchk0		pt12
+#define ptMchk1		pt13
+#define ptMchk2		pt14
+#define ptMchk3		pt15
+#define ptMchk4		pt16
+#define ptMchk5		pt17
+#define ptSys0		pt18
+#define ptSys1		pt19
+
+/*
+ * Shortcuts for various PALmode instructions.
+ */
+#define mtpr	hw_mtpr
+#define mfpr	hw_mfpr
+#define stq_p	hw_stq/p
+#define stl_p	hw_stl/p
+#define ldl_p	hw_ldl/p
+#define ldq_p	hw_ldq/p
+
+/* QEMU recognizes the EV4/EV5 HW_REI instruction as a special case of
+   the EV6 HW_RET instruction.  This pulls the destination address from
+   the EXC_ADDR processor register.  */
+#define hw_rei	hw_ret ($31)
+
+
+.macro	ENDFN	function
+	.type	\function, @function
+	.size	\function, . - \function
+.endm
+
+#endif /* PAL_H */
diff --git a/protos.h b/protos.h
new file mode 100644
index 0000000..6bff767
--- /dev/null
+++ b/protos.h
@@ -0,0 +1,42 @@
+#ifndef PROTOS_H
+#define PROTOS_H 1
+
+/* 
+ * Cserve functions.
+ */
+
+static inline unsigned long ldq_p(unsigned long addr)
+{
+  register unsigned long v0 __asm__("$0");
+  register unsigned long a0 __asm__("$16") = 1;
+  register unsigned long a1 __asm__("$17") = addr;
+
+  asm volatile ("call_pal 9"
+		: "=r"(v0), "+r"(a0), "+r"(a1) :
+		: "$18", "$19", "$20", "$21");
+
+  return v0;
+}
+
+static inline unsigned long stq_p(unsigned long port, unsigned long val)
+{
+  register unsigned long v0 __asm__("$0");
+  register unsigned long a0 __asm__("$16") = 4;
+  register unsigned long a1 __asm__("$17") = port;
+  register unsigned long a2 __asm__("$18") = val;
+
+  asm volatile ("call_pal 9"
+		: "=r"(v0), "+r"(a0), "+r"(a1), "+r"(a2) :
+		: "$19", "$20", "$21");
+
+  return v0;
+}
+
+/*
+ * I/O functions
+ */
+
+extern unsigned long inb(unsigned long port);
+extern unsigned long outb(unsigned char val, unsigned long port);
+
+#endif /* PROTOS_H */
diff --git a/sys-clipper.S b/sys-clipper.S
new file mode 100644
index 0000000..e3fc6f5
--- /dev/null
+++ b/sys-clipper.S
@@ -0,0 +1,90 @@
+#include "pal.h"
+#include SYSTEM_H
+
+/*
+ * Sys_Setup
+ *
+ * Do any system specific setup necessary.
+ *
+ * INPUT PARAMETERS
+ *
+ *	r16 (a0) = whami
+ *	r26 (ra) = return address
+ */
+	.globl	Sys_Setup
+	.ent	Sys_Setup
+Sys_Setup:
+	.frame	$sp, 0, p7, 0
+	lda		t0, TYPHOON_CCHIP_DIM0
+	lda		t1, TYPHOON_CCHIP_DIM2
+	lda		t2, TYPHOON_CCHIP_IIC0
+	lda		t3, TYPHOON_CCHIP_IIC2
+	and		a0, 2, t4
+	cmovne		t4, t1, t0
+	cmovne		t4, t3, t2
+	and		a0, 1, t4
+	sll		t4, 6, t4
+	addq		t0, t4, t0
+	addq		t2, t4, t2
+	LOAD_PHYS_CCHIP	t4
+	addq		t0, t4, t0
+	addq		t2, t4, t2
+	mtpr		t0, ptCpuDIR
+	mtpr		t2, ptCpuIIC
+	ret
+	.end	Sys_Setup
+
+/*
+ * Cserve_Ena
+ *
+ * Unmask a PCI interrupt
+ */
+	.globl	Cserve_Ena
+Cserve_Ena:
+	// FIXME
+	hw_rei
+ENDFN	Cserve_Ena
+
+/*
+ * Cserve_Dis
+ *
+ * Mask a PCI interrupt
+ */
+	.globl	Cserve_Dis
+Cserve_Dis:
+	// FIXME
+	hw_rei
+ENDFN	Cserve_Dis
+
+/*
+ * inb
+ *
+ * Read a byte from the PCI I/O bus.
+ */
+	.globl	inb
+	.ent	inb
+inb:
+	.frame	$sp, 0, $26, 0
+	.prologue 0
+	LOAD_KSEG_PCI_IO v0
+	addq	v0, a0, v0
+	ldbu	v0, 0(v0)
+	ret
+	.end	inb
+
+/*
+ * outb
+ *
+ * Write a byte to the PCI I/O bus.
+ */
+	.globl	outb
+	.ent	outb
+outb:
+	.frame	$sp, 0, $26, 0
+	.prologue 0
+	LOAD_KSEG_PCI_IO p0
+	addq	p0, a1, p0
+	stb	a0, 0(p0)
+	ret
+	.end	outb
+
diff --git a/sys-clipper.h b/sys-clipper.h
new file mode 100644
index 0000000..beea346
--- /dev/null
+++ b/sys-clipper.h
@@ -0,0 +1,10 @@
+#ifndef SYS_CLIPPER_H
+#define SYS_CLIPPER_H 1
+
+#include "core-typhoon.h"
+
+#define SYS_TYPE	ST_DEC_TSUNAMI
+#define SYS_VARIATION	(5 << 10)
+#define SYS_REVISION	0
+
+#endif
diff --git a/sys-sx164.h b/sys-sx164.h
new file mode 100644
index 0000000..8198ae8
--- /dev/null
+++ b/sys-sx164.h
@@ -0,0 +1,25 @@
+#ifndef SYS_SX164_H
+#define SYS_SX164_H 1
+
+#include "core_cia.h"
+
+#ifdef __ASSEMBLER__
+
+.macro	SYS_ACK_CLK	t0, t1, t2
+	LOAD_KSEG_PCI_IO \t0		// Set RTCADD (0x70) to index reg 0xC
+	mov	0xc, \t1
+	stb	\t1, 0x70(\t0)
+	ldbu	\t1, 0x71(\t0)		// Read RTCDAT to clear interrupt
+.endm
+
+.macro	SYS_DEV_VECTOR	ret
+	FIXME
+.endm
+
+#endif /* ASSEMBLER */
+
+#define SYS_TYPE	ST_DEC_EB164
+#define SYS_VARIATION	(15 << 10)
+#define SYS_REVISION	0
+
+#endif /* SYS_SX164_H */
diff --git a/uart.c b/uart.c
index d3fd848..2e93482 100644
--- a/uart.c
+++ b/uart.c
@@ -30,7 +30,7 @@
  * Modified for QEMU PALcode by rth@twiddle.net.
  */
 
-#include "io.h"
+#include "protos.h"
 #include "uart.h"
 
 #ifndef SERIAL_SPEED