summary refs log tree commit diff stats
path: root/hw
diff options
context:
space:
mode:
Diffstat (limited to 'hw')
-rw-r--r--hw/fdc.c35
-rw-r--r--hw/iommu.c54
-rw-r--r--hw/lance.c127
-rw-r--r--hw/m48t08.c161
-rw-r--r--hw/m48t08.h8
-rw-r--r--hw/magic-load.c179
-rw-r--r--hw/sched.c268
-rw-r--r--hw/slavio_intctl.c299
-rw-r--r--hw/slavio_serial.c364
-rw-r--r--hw/slavio_timer.c289
-rw-r--r--hw/sun4m.c163
-rw-r--r--hw/tcx.c297
-rw-r--r--hw/timer.c97
13 files changed, 1567 insertions, 774 deletions
diff --git a/hw/fdc.c b/hw/fdc.c
index d512b1ca98..ee07328481 100644
--- a/hw/fdc.c
+++ b/hw/fdc.c
@@ -21,6 +21,10 @@
  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  * THE SOFTWARE.
  */
+/*
+ * The controller is used in Sun4m systems in a slightly different
+ * way. There are changes in DOR register and DMA is not available.
+ */
 #include "vl.h"
 
 /********************************************************/
@@ -90,6 +94,16 @@ typedef struct fdrive_t {
     uint8_t ro;               /* Is read-only           */
 } fdrive_t;
 
+#ifdef TARGET_SPARC
+#define DMA_read_memory(a,b,c,d)
+#define DMA_write_memory(a,b,c,d)
+#define DMA_register_channel(a,b,c)
+#define DMA_hold_DREQ(a)
+#define DMA_release_DREQ(a)
+#define DMA_get_channel_mode(a) (0)
+#define DMA_schedule(a)
+#endif
+
 static void fd_init (fdrive_t *drv, BlockDriverState *bs)
 {
     /* Drive */
@@ -455,6 +469,18 @@ static void fdctrl_write (void *opaque, uint32_t reg, uint32_t value)
     }
 }
 
+static CPUReadMemoryFunc *fdctrl_mem_read[3] = {
+    fdctrl_read,
+    fdctrl_read,
+    fdctrl_read,
+};
+
+static CPUWriteMemoryFunc *fdctrl_mem_write[3] = {
+    fdctrl_write,
+    fdctrl_write,
+    fdctrl_write,
+};
+
 static void fd_change_cb (void *opaque)
 {
     fdrive_t *drv = opaque;
@@ -473,7 +499,7 @@ fdctrl_t *fdctrl_init (int irq_lvl, int dma_chann, int mem_mapped,
                        BlockDriverState **fds)
 {
     fdctrl_t *fdctrl;
-//    int io_mem;
+    int io_mem;
     int i;
 
     FLOPPY_DPRINTF("init controller\n");
@@ -504,11 +530,8 @@ fdctrl_t *fdctrl_init (int irq_lvl, int dma_chann, int mem_mapped,
     fdctrl_reset(fdctrl, 0);
     fdctrl->state = FD_CTRL_ACTIVE;
     if (mem_mapped) {
-        FLOPPY_ERROR("memory mapped floppy not supported by now !\n");
-#if 0
-        io_mem = cpu_register_io_memory(0, fdctrl_mem_read, fdctrl_mem_write);
-        cpu_register_physical_memory(base, 0x08, io_mem);
-#endif
+        io_mem = cpu_register_io_memory(0, fdctrl_mem_read, fdctrl_mem_write, fdctrl);
+        cpu_register_physical_memory(io_base, 0x08, io_mem);
     } else {
         register_ioport_read(io_base + 0x01, 5, 1, &fdctrl_read, fdctrl);
         register_ioport_read(io_base + 0x07, 1, 1, &fdctrl_read, fdctrl);
diff --git a/hw/iommu.c b/hw/iommu.c
index a9249c4ba7..62927acd54 100644
--- a/hw/iommu.c
+++ b/hw/iommu.c
@@ -117,8 +117,6 @@ typedef struct IOMMUState {
     uint32_t iostart;
 } IOMMUState;
 
-static IOMMUState *ps;
-
 static uint32_t iommu_mem_readw(void *opaque, target_phys_addr_t addr)
 {
     IOMMUState *s = opaque;
@@ -187,25 +185,61 @@ static CPUWriteMemoryFunc *iommu_mem_write[3] = {
     iommu_mem_writew,
 };
 
-uint32_t iommu_translate(uint32_t addr)
+uint32_t iommu_translate_local(void *opaque, uint32_t addr)
 {
-    uint32_t *iopte = (void *)(ps->regs[1] << 4), pa;
+    IOMMUState *s = opaque;
+    uint32_t *iopte = (void *)(s->regs[1] << 4), pa;
 
-    iopte += ((addr - ps->iostart) >> PAGE_SHIFT);
-    cpu_physical_memory_rw((uint32_t)iopte, (void *) &pa, 4, 0);
+    iopte += ((addr - s->iostart) >> PAGE_SHIFT);
+    cpu_physical_memory_read((uint32_t)iopte, (void *) &pa, 4);
     bswap32s(&pa);
     pa = (pa & IOPTE_PAGE) << 4;		/* Loose higher bits of 36 */
     return pa + (addr & PAGE_MASK);
 }
 
-void iommu_init(uint32_t addr)
+static void iommu_save(QEMUFile *f, void *opaque)
+{
+    IOMMUState *s = opaque;
+    int i;
+    
+    qemu_put_be32s(f, &s->addr);
+    for (i = 0; i < sizeof(struct iommu_regs); i += 4)
+	qemu_put_be32s(f, &s->regs[i]);
+    qemu_put_be32s(f, &s->iostart);
+}
+
+static int iommu_load(QEMUFile *f, void *opaque, int version_id)
+{
+    IOMMUState *s = opaque;
+    int i;
+    
+    if (version_id != 1)
+        return -EINVAL;
+
+    qemu_get_be32s(f, &s->addr);
+    for (i = 0; i < sizeof(struct iommu_regs); i += 4)
+	qemu_put_be32s(f, &s->regs[i]);
+    qemu_get_be32s(f, &s->iostart);
+
+    return 0;
+}
+
+static void iommu_reset(void *opaque)
+{
+    IOMMUState *s = opaque;
+
+    memset(s->regs, 0, sizeof(struct iommu_regs));
+    s->iostart = 0;
+}
+
+void *iommu_init(uint32_t addr)
 {
     IOMMUState *s;
     int iommu_io_memory;
 
     s = qemu_mallocz(sizeof(IOMMUState));
     if (!s)
-        return;
+        return NULL;
 
     s->addr = addr;
 
@@ -213,6 +247,8 @@ void iommu_init(uint32_t addr)
     cpu_register_physical_memory(addr, sizeof(struct iommu_regs),
                                  iommu_io_memory);
     
-    ps = s;
+    register_savevm("iommu", addr, 1, iommu_save, iommu_load, s);
+    qemu_register_reset(iommu_reset, s);
+    return s;
 }
 
diff --git a/hw/lance.c b/hw/lance.c
index 25ad8c45b2..c594c52e83 100644
--- a/hw/lance.c
+++ b/hw/lance.c
@@ -147,6 +147,7 @@ struct lance_init_block {
 };
 
 #define LEDMA_REGS 4
+#define LEDMA_MAXADDR (LEDMA_REGS * 4 - 1)
 #if 0
 /* Structure to describe the current status of DMA registers on the Sparc */
 struct sparc_dma_registers {
@@ -157,32 +158,28 @@ struct sparc_dma_registers {
 };
 #endif
 
-typedef struct LEDMAState {
-    uint32_t addr;
-    uint32_t regs[LEDMA_REGS];
-} LEDMAState;
-
 typedef struct LANCEState {
-    uint32_t paddr;
     NetDriverState *nd;
     uint32_t leptr;
     uint16_t addr;
     uint16_t regs[LE_MAXREG];
     uint8_t phys[6]; /* mac address */
     int irq;
-    LEDMAState *ledma;
+    unsigned int rxptr, txptr;
+    uint32_t ledmaregs[LEDMA_REGS];
 } LANCEState;
 
-static unsigned int rxptr, txptr;
-
 static void lance_send(void *opaque);
 
-static void lance_reset(LANCEState *s)
+static void lance_reset(void *opaque)
 {
+    LANCEState *s = opaque;
     memcpy(s->phys, s->nd->macaddr, 6);
-    rxptr = 0;
-    txptr = 0;
+    s->rxptr = 0;
+    s->txptr = 0;
+    memset(s->regs, 0, LE_MAXREG * 2);
     s->regs[LE_CSR0] = LE_C0_STOP;
+    memset(s->ledmaregs, 0, LEDMA_REGS * 4);
 }
 
 static uint32_t lance_mem_readw(void *opaque, target_phys_addr_t addr)
@@ -190,7 +187,7 @@ static uint32_t lance_mem_readw(void *opaque, target_phys_addr_t addr)
     LANCEState *s = opaque;
     uint32_t saddr;
 
-    saddr = addr - s->paddr;
+    saddr = addr & LE_MAXREG;
     switch (saddr >> 1) {
     case LE_RDP:
 	return s->regs[s->addr];
@@ -208,7 +205,7 @@ static void lance_mem_writew(void *opaque, target_phys_addr_t addr, uint32_t val
     uint32_t saddr;
     uint16_t reg;
 
-    saddr = addr - s->paddr;
+    saddr = addr & LE_MAXREG;
     switch (saddr >> 1) {
     case LE_RDP:
 	switch(s->addr) {
@@ -292,7 +289,7 @@ static CPUWriteMemoryFunc *lance_mem_write[3] = {
 static int lance_can_receive(void *opaque)
 {
     LANCEState *s = opaque;
-    void *dmaptr = (void *) (s->leptr + s->ledma->regs[3]);
+    uint32_t dmaptr = s->leptr + s->ledmaregs[3];
     struct lance_init_block *ib;
     int i;
     uint16_t temp;
@@ -303,7 +300,7 @@ static int lance_can_receive(void *opaque)
     ib = (void *) iommu_translate(dmaptr);
 
     for (i = 0; i < RX_RING_SIZE; i++) {
-	cpu_physical_memory_read(&ib->brx_ring[i].rmd1_bits, (void *) &temp, 1);
+	cpu_physical_memory_read((uint32_t)&ib->brx_ring[i].rmd1_bits, (void *) &temp, 1);
 	temp &= 0xff;
 	if (temp == (LE_R1_OWN)) {
 #ifdef DEBUG_LANCE
@@ -323,7 +320,7 @@ static int lance_can_receive(void *opaque)
 static void lance_receive(void *opaque, const uint8_t *buf, int size)
 {
     LANCEState *s = opaque;
-    void *dmaptr = (void *) (s->leptr + s->ledma->regs[3]);
+    uint32_t dmaptr = s->leptr + s->ledmaregs[3];
     struct lance_init_block *ib;
     unsigned int i, old_rxptr, j;
     uint16_t temp;
@@ -333,23 +330,23 @@ static void lance_receive(void *opaque, const uint8_t *buf, int size)
 
     ib = (void *) iommu_translate(dmaptr);
 
-    old_rxptr = rxptr;
-    for (i = rxptr; i != ((old_rxptr - 1) & RX_RING_MOD_MASK); i = (i + 1) & RX_RING_MOD_MASK) {
-	cpu_physical_memory_read(&ib->brx_ring[i].rmd1_bits, (void *) &temp, 1);
+    old_rxptr = s->rxptr;
+    for (i = s->rxptr; i != ((old_rxptr - 1) & RX_RING_MOD_MASK); i = (i + 1) & RX_RING_MOD_MASK) {
+	cpu_physical_memory_read((uint32_t)&ib->brx_ring[i].rmd1_bits, (void *) &temp, 1);
 	if (temp == (LE_R1_OWN)) {
-	    rxptr = (rxptr + 1) & RX_RING_MOD_MASK;
+	    s->rxptr = (s->rxptr + 1) & RX_RING_MOD_MASK;
 	    temp = size;
 	    bswap16s(&temp);
-	    cpu_physical_memory_write(&ib->brx_ring[i].mblength, (void *) &temp, 2);
+	    cpu_physical_memory_write((uint32_t)&ib->brx_ring[i].mblength, (void *) &temp, 2);
 #if 0
-	    cpu_physical_memory_write(&ib->rx_buf[i], buf, size);
+	    cpu_physical_memory_write((uint32_t)&ib->rx_buf[i], buf, size);
 #else
 	    for (j = 0; j < size; j++) {
-		cpu_physical_memory_write(((void *)&ib->rx_buf[i]) + j, &buf[j], 1);
+		cpu_physical_memory_write(((uint32_t)&ib->rx_buf[i]) + j, &buf[j], 1);
 	    }
 #endif
 	    temp = LE_R1_POK;
-	    cpu_physical_memory_write(&ib->brx_ring[i].rmd1_bits, (void *) &temp, 1);
+	    cpu_physical_memory_write((uint32_t)&ib->brx_ring[i].rmd1_bits, (void *) &temp, 1);
 	    s->regs[LE_CSR0] |= LE_C0_RINT | LE_C0_INTR;
 	    if ((s->regs[LE_CSR0] & LE_C0_INTR) && (s->regs[LE_CSR0] & LE_C0_INEA))
 		pic_set_irq(s->irq, 1);
@@ -364,7 +361,7 @@ static void lance_receive(void *opaque, const uint8_t *buf, int size)
 static void lance_send(void *opaque)
 {
     LANCEState *s = opaque;
-    void *dmaptr = (void *) (s->leptr + s->ledma->regs[3]);
+    uint32_t dmaptr = s->leptr + s->ledmaregs[3];
     struct lance_init_block *ib;
     unsigned int i, old_txptr, j;
     uint16_t temp;
@@ -375,18 +372,18 @@ static void lance_send(void *opaque)
 
     ib = (void *) iommu_translate(dmaptr);
 
-    old_txptr = txptr;
-    for (i = txptr; i != ((old_txptr - 1) & TX_RING_MOD_MASK); i = (i + 1) & TX_RING_MOD_MASK) {
-	cpu_physical_memory_read(&ib->btx_ring[i].tmd1_bits, (void *) &temp, 1);
+    old_txptr = s->txptr;
+    for (i = s->txptr; i != ((old_txptr - 1) & TX_RING_MOD_MASK); i = (i + 1) & TX_RING_MOD_MASK) {
+	cpu_physical_memory_read((uint32_t)&ib->btx_ring[i].tmd1_bits, (void *) &temp, 1);
 	if (temp == (LE_T1_POK|LE_T1_OWN)) {
-	    cpu_physical_memory_read(&ib->btx_ring[i].length, (void *) &temp, 2);
+	    cpu_physical_memory_read((uint32_t)&ib->btx_ring[i].length, (void *) &temp, 2);
 	    bswap16s(&temp);
 	    temp = (~temp) + 1;
 #if 0
-	    cpu_physical_memory_read(&ib->tx_buf[i], pkt_buf, temp);
+	    cpu_physical_memory_read((uint32_t)&ib->tx_buf[i], pkt_buf, temp);
 #else
 	    for (j = 0; j < temp; j++) {
-		cpu_physical_memory_read(((void *)&ib->tx_buf[i]) + j, &pkt_buf[j], 1);
+		cpu_physical_memory_read((uint32_t)&ib->tx_buf[i] + j, &pkt_buf[j], 1);
 	    }
 #endif
 
@@ -395,8 +392,8 @@ static void lance_send(void *opaque)
 #endif
 	    qemu_send_packet(s->nd, pkt_buf, temp);
 	    temp = LE_T1_POK;
-	    cpu_physical_memory_write(&ib->btx_ring[i].tmd1_bits, (void *) &temp, 1);
-	    txptr = (txptr + 1) & TX_RING_MOD_MASK;
+	    cpu_physical_memory_write((uint32_t)&ib->btx_ring[i].tmd1_bits, (void *) &temp, 1);
+	    s->txptr = (s->txptr + 1) & TX_RING_MOD_MASK;
 	    s->regs[LE_CSR0] |= LE_C0_TINT | LE_C0_INTR;
 	}
     }
@@ -404,24 +401,20 @@ static void lance_send(void *opaque)
 
 static uint32_t ledma_mem_readl(void *opaque, target_phys_addr_t addr)
 {
-    LEDMAState *s = opaque;
+    LANCEState *s = opaque;
     uint32_t saddr;
 
-    saddr = (addr - s->addr) >> 2;
-    if (saddr < LEDMA_REGS)
-	return s->regs[saddr];
-    else
-	return 0;
+    saddr = (addr & LEDMA_MAXADDR) >> 2;
+    return s->ledmaregs[saddr];
 }
 
 static void ledma_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
 {
-    LEDMAState *s = opaque;
+    LANCEState *s = opaque;
     uint32_t saddr;
 
-    saddr = (addr - s->addr) >> 2;
-    if (saddr < LEDMA_REGS)
-	s->regs[saddr] = val;
+    saddr = (addr & LEDMA_MAXADDR) >> 2;
+    s->ledmaregs[saddr] = val;
 }
 
 static CPUReadMemoryFunc *ledma_mem_read[3] = {
@@ -436,33 +429,61 @@ static CPUWriteMemoryFunc *ledma_mem_write[3] = {
     ledma_mem_writel,
 };
 
+static void lance_save(QEMUFile *f, void *opaque)
+{
+    LANCEState *s = opaque;
+    int i;
+    
+    qemu_put_be32s(f, &s->leptr);
+    qemu_put_be16s(f, &s->addr);
+    for (i = 0; i < LE_MAXREG; i ++)
+	qemu_put_be16s(f, &s->regs[i]);
+    qemu_put_buffer(f, s->phys, 6);
+    qemu_put_be32s(f, &s->irq);
+    for (i = 0; i < LEDMA_REGS; i ++)
+	qemu_put_be32s(f, &s->ledmaregs[i]);
+}
+
+static int lance_load(QEMUFile *f, void *opaque, int version_id)
+{
+    LANCEState *s = opaque;
+    int i;
+    
+    if (version_id != 1)
+        return -EINVAL;
+
+    qemu_get_be32s(f, &s->leptr);
+    qemu_get_be16s(f, &s->addr);
+    for (i = 0; i < LE_MAXREG; i ++)
+	qemu_get_be16s(f, &s->regs[i]);
+    qemu_get_buffer(f, s->phys, 6);
+    qemu_get_be32s(f, &s->irq);
+    for (i = 0; i < LEDMA_REGS; i ++)
+	qemu_get_be32s(f, &s->ledmaregs[i]);
+    return 0;
+}
+
 void lance_init(NetDriverState *nd, int irq, uint32_t leaddr, uint32_t ledaddr)
 {
     LANCEState *s;
-    LEDMAState *led;
     int lance_io_memory, ledma_io_memory;
 
     s = qemu_mallocz(sizeof(LANCEState));
     if (!s)
         return;
 
-    s->paddr = leaddr;
     s->nd = nd;
     s->irq = irq;
 
     lance_io_memory = cpu_register_io_memory(0, lance_mem_read, lance_mem_write, s);
     cpu_register_physical_memory(leaddr, 8, lance_io_memory);
 
-    led = qemu_mallocz(sizeof(LEDMAState));
-    if (!led)
-        return;
-
-    s->ledma = led;
-    led->addr = ledaddr;
-    ledma_io_memory = cpu_register_io_memory(0, ledma_mem_read, ledma_mem_write, led);
+    ledma_io_memory = cpu_register_io_memory(0, ledma_mem_read, ledma_mem_write, s);
     cpu_register_physical_memory(ledaddr, 16, ledma_io_memory);
 
     lance_reset(s);
     qemu_add_read_packet(nd, lance_can_receive, lance_receive, s);
+    register_savevm("lance", leaddr, 1, lance_save, lance_load, s);
+    qemu_register_reset(lance_reset, s);
 }
 
diff --git a/hw/m48t08.c b/hw/m48t08.c
index 46ec665570..0945879532 100644
--- a/hw/m48t08.c
+++ b/hw/m48t08.c
@@ -32,19 +32,14 @@
 #define NVRAM_PRINTF(fmt, args...) do { } while (0)
 #endif
 
-#define NVRAM_MAX_MEM 0xfff0
+#define NVRAM_MAX_MEM 0x1ff0
+#define NVRAM_MAXADDR 0x1fff
 
 struct m48t08_t {
-    /* Hardware parameters */
-    int mem_index;
-    uint32_t mem_base;
-    uint16_t size;
     /* RTC management */
     time_t   time_offset;
     time_t   stop_time;
     /* NVRAM storage */
-    uint8_t  lock;
-    uint16_t addr;
     uint8_t *buffer;
 };
 
@@ -83,14 +78,13 @@ static void set_time (m48t08_t *NVRAM, struct tm *tm)
 }
 
 /* Direct access to NVRAM */
-void m48t08_write (m48t08_t *NVRAM, uint32_t val)
+void m48t08_write (m48t08_t *NVRAM, uint32_t addr, uint8_t val)
 {
     struct tm tm;
     int tmp;
 
-    if (NVRAM->addr > NVRAM_MAX_MEM && NVRAM->addr < 0x2000)
-	NVRAM_PRINTF("%s: 0x%08x => 0x%08x\n", __func__, NVRAM->addr, val);
-    switch (NVRAM->addr) {
+    addr &= NVRAM_MAXADDR;
+    switch (addr) {
     case 0x1FF8:
         /* control */
 	NVRAM->buffer[0x1FF8] = (val & ~0xA0) | 0x90;
@@ -167,25 +161,18 @@ void m48t08_write (m48t08_t *NVRAM, uint32_t val)
 	}
         break;
     default:
-        /* Check lock registers state */
-        if (NVRAM->addr >= 0x20 && NVRAM->addr <= 0x2F && (NVRAM->lock & 1))
-            break;
-        if (NVRAM->addr >= 0x30 && NVRAM->addr <= 0x3F && (NVRAM->lock & 2))
-            break;
-        if (NVRAM->addr < NVRAM_MAX_MEM ||
-	    (NVRAM->addr > 0x1FFF && NVRAM->addr < NVRAM->size)) {
-            NVRAM->buffer[NVRAM->addr] = val & 0xFF;
-	}
+	NVRAM->buffer[addr] = val & 0xFF;
         break;
     }
 }
 
-uint32_t m48t08_read (m48t08_t *NVRAM)
+uint8_t m48t08_read (m48t08_t *NVRAM, uint32_t addr)
 {
     struct tm tm;
-    uint32_t retval = 0xFF;
+    uint8_t retval = 0xFF;
 
-    switch (NVRAM->addr) {
+    addr &= NVRAM_MAXADDR;
+    switch (addr) {
     case 0x1FF8:
         /* control */
 	goto do_read;
@@ -225,65 +212,36 @@ uint32_t m48t08_read (m48t08_t *NVRAM)
         retval = toBCD(tm.tm_year);
         break;
     default:
-        /* Check lock registers state */
-        if (NVRAM->addr >= 0x20 && NVRAM->addr <= 0x2F && (NVRAM->lock & 1))
-            break;
-        if (NVRAM->addr >= 0x30 && NVRAM->addr <= 0x3F && (NVRAM->lock & 2))
-            break;
-        if (NVRAM->addr < NVRAM_MAX_MEM ||
-	    (NVRAM->addr > 0x1FFF && NVRAM->addr < NVRAM->size)) {
-	do_read:
-            retval = NVRAM->buffer[NVRAM->addr];
-	}
+    do_read:
+	retval = NVRAM->buffer[addr];
         break;
     }
-    if (NVRAM->addr > NVRAM_MAX_MEM + 1 && NVRAM->addr < 0x2000)
-	NVRAM_PRINTF("0x%08x <= 0x%08x\n", NVRAM->addr, retval);
-
     return retval;
 }
 
-void m48t08_set_addr (m48t08_t *NVRAM, uint32_t addr)
-{
-    NVRAM->addr = addr;
-}
-
-void m48t08_toggle_lock (m48t08_t *NVRAM, int lock)
-{
-    NVRAM->lock ^= 1 << lock;
-}
-
 static void nvram_writeb (void *opaque, target_phys_addr_t addr, uint32_t value)
 {
     m48t08_t *NVRAM = opaque;
     
-    addr -= NVRAM->mem_base;
-    if (addr < NVRAM_MAX_MEM)
-        NVRAM->buffer[addr] = value;
+    m48t08_write(NVRAM, addr, value);
 }
 
 static void nvram_writew (void *opaque, target_phys_addr_t addr, uint32_t value)
 {
     m48t08_t *NVRAM = opaque;
     
-    addr -= NVRAM->mem_base;
-    if (addr < NVRAM_MAX_MEM) {
-        NVRAM->buffer[addr] = value >> 8;
-        NVRAM->buffer[addr + 1] = value;
-    }
+    m48t08_write(NVRAM, addr, value);
+    m48t08_write(NVRAM, addr + 1, value >> 8);
 }
 
 static void nvram_writel (void *opaque, target_phys_addr_t addr, uint32_t value)
 {
     m48t08_t *NVRAM = opaque;
     
-    addr -= NVRAM->mem_base;
-    if (addr < NVRAM_MAX_MEM) {
-        NVRAM->buffer[addr] = value >> 24;
-        NVRAM->buffer[addr + 1] = value >> 16;
-        NVRAM->buffer[addr + 2] = value >> 8;
-        NVRAM->buffer[addr + 3] = value;
-    }
+    m48t08_write(NVRAM, addr, value);
+    m48t08_write(NVRAM, addr + 1, value >> 8);
+    m48t08_write(NVRAM, addr + 2, value >> 16);
+    m48t08_write(NVRAM, addr + 3, value >> 24);
 }
 
 static uint32_t nvram_readb (void *opaque, target_phys_addr_t addr)
@@ -291,10 +249,7 @@ static uint32_t nvram_readb (void *opaque, target_phys_addr_t addr)
     m48t08_t *NVRAM = opaque;
     uint32_t retval = 0;
     
-    addr -= NVRAM->mem_base;
-    if (addr < NVRAM_MAX_MEM)
-        retval = NVRAM->buffer[addr];
-
+    retval = m48t08_read(NVRAM, addr);
     return retval;
 }
 
@@ -303,12 +258,8 @@ static uint32_t nvram_readw (void *opaque, target_phys_addr_t addr)
     m48t08_t *NVRAM = opaque;
     uint32_t retval = 0;
     
-    addr -= NVRAM->mem_base;
-    if (addr < NVRAM_MAX_MEM) {
-        retval = NVRAM->buffer[addr] << 8;
-        retval |= NVRAM->buffer[addr + 1];
-    }
-
+    retval = m48t08_read(NVRAM, addr) << 8;
+    retval |= m48t08_read(NVRAM, addr + 1);
     return retval;
 }
 
@@ -317,14 +268,10 @@ static uint32_t nvram_readl (void *opaque, target_phys_addr_t addr)
     m48t08_t *NVRAM = opaque;
     uint32_t retval = 0;
     
-    addr -= NVRAM->mem_base;
-    if (addr < NVRAM_MAX_MEM) {
-        retval = NVRAM->buffer[addr] << 24;
-        retval |= NVRAM->buffer[addr + 1] << 16;
-        retval |= NVRAM->buffer[addr + 2] << 8;
-        retval |= NVRAM->buffer[addr + 3];
-    }
-
+    retval = m48t08_read(NVRAM, addr) << 24;
+    retval |= m48t08_read(NVRAM, addr + 1) << 16;
+    retval |= m48t08_read(NVRAM, addr + 2) << 8;
+    retval |= m48t08_read(NVRAM, addr + 3);
     return retval;
 }
 
@@ -340,12 +287,42 @@ static CPUReadMemoryFunc *nvram_read[] = {
     &nvram_readl,
 };
 
+static void nvram_save(QEMUFile *f, void *opaque)
+{
+    m48t08_t *s = opaque;
+    
+    qemu_put_be32s(f, (uint32_t *)&s->time_offset);
+    qemu_put_be32s(f, (uint32_t *)&s->stop_time);
+    qemu_put_buffer(f, s->buffer, 0x2000);
+}
+
+static int nvram_load(QEMUFile *f, void *opaque, int version_id)
+{
+    m48t08_t *s = opaque;
+    
+    if (version_id != 1)
+        return -EINVAL;
+
+    qemu_get_be32s(f, (uint32_t *)&s->time_offset);
+    qemu_get_be32s(f, (uint32_t *)&s->stop_time);
+    qemu_get_buffer(f, s->buffer, 0x2000);
+    return 0;
+}
+
+static void m48t08_reset(void *opaque)
+{
+    m48t08_t *s = opaque;
+
+    s->time_offset = 0;
+    s->stop_time = 0;
+}
+
+
 /* Initialisation routine */
-m48t08_t *m48t08_init(uint32_t mem_base, uint16_t size, uint8_t *macaddr)
+m48t08_t *m48t08_init(uint32_t mem_base, uint16_t size)
 {
     m48t08_t *s;
-    int i;
-    unsigned char tmp = 0;
+    int mem_index;
 
     s = qemu_mallocz(sizeof(m48t08_t));
     if (!s)
@@ -355,25 +332,13 @@ m48t08_t *m48t08_init(uint32_t mem_base, uint16_t size, uint8_t *macaddr)
         qemu_free(s);
         return NULL;
     }
-    s->size = size;
-    s->mem_base = mem_base;
-    s->addr = 0;
     if (mem_base != 0) {
-        s->mem_index = cpu_register_io_memory(0, nvram_read, nvram_write, s);
-        cpu_register_physical_memory(mem_base, 0x4000, s->mem_index);
+        mem_index = cpu_register_io_memory(0, nvram_read, nvram_write, s);
+        cpu_register_physical_memory(mem_base, 0x2000, mem_index);
     }
-    s->lock = 0;
 
-    i = 0x1fd8;
-    s->buffer[i++] = 0x01;
-    s->buffer[i++] = 0x80; /* Sun4m OBP */
-    memcpy(&s->buffer[i], macaddr, 6);
-
-    /* Calculate checksum */
-    for (i = 0x1fd8; i < 0x1fe7; i++) {
-	tmp ^= s->buffer[i];
-    }
-    s->buffer[0x1fe7] = tmp;
+    register_savevm("nvram", mem_base, 1, nvram_save, nvram_load, s);
+    qemu_register_reset(m48t08_reset, s);
     return s;
 }
 
diff --git a/hw/m48t08.h b/hw/m48t08.h
index 9b44bc0d16..985116a099 100644
--- a/hw/m48t08.h
+++ b/hw/m48t08.h
@@ -3,10 +3,8 @@
 
 typedef struct m48t08_t m48t08_t;
 
-void m48t08_write (m48t08_t *NVRAM, uint32_t val);
-uint32_t m48t08_read (m48t08_t *NVRAM);
-void m48t08_set_addr (m48t08_t *NVRAM, uint32_t addr);
-void m48t08_toggle_lock (m48t08_t *NVRAM, int lock);
-m48t08_t *m48t08_init(uint32_t mem_base, uint16_t size, uint8_t *macaddr);
+void m48t08_write (m48t08_t *NVRAM, uint32_t addr, uint8_t val);
+uint8_t m48t08_read (m48t08_t *NVRAM, uint32_t addr);
+m48t08_t *m48t08_init(uint32_t mem_base, uint16_t size);
 
 #endif /* !defined (__M48T08_H__) */
diff --git a/hw/magic-load.c b/hw/magic-load.c
index 06a5f743af..713343a758 100644
--- a/hw/magic-load.c
+++ b/hw/magic-load.c
@@ -1,5 +1,54 @@
 #include "vl.h"
 #include "disas.h"
+#include "exec-all.h"
+
+struct exec
+{
+  uint32_t a_info;   /* Use macros N_MAGIC, etc for access */
+  uint32_t a_text;   /* length of text, in bytes */
+  uint32_t a_data;   /* length of data, in bytes */
+  uint32_t a_bss;    /* length of uninitialized data area, in bytes */
+  uint32_t a_syms;   /* length of symbol table data in file, in bytes */
+  uint32_t a_entry;  /* start address */
+  uint32_t a_trsize; /* length of relocation info for text, in bytes */
+  uint32_t a_drsize; /* length of relocation info for data, in bytes */
+};
+
+#ifdef BSWAP_NEEDED
+static void bswap_ahdr(struct exec *e)
+{
+    bswap32s(&e->a_info);
+    bswap32s(&e->a_text);
+    bswap32s(&e->a_data);
+    bswap32s(&e->a_bss);
+    bswap32s(&e->a_syms);
+    bswap32s(&e->a_entry);
+    bswap32s(&e->a_trsize);
+    bswap32s(&e->a_drsize);
+}
+#else
+#define bswap_ahdr(x) do { } while (0)
+#endif
+
+#define N_MAGIC(exec) ((exec).a_info & 0xffff)
+#define OMAGIC 0407
+#define NMAGIC 0410
+#define ZMAGIC 0413
+#define QMAGIC 0314
+#define _N_HDROFF(x) (1024 - sizeof (struct exec))
+#define N_TXTOFF(x)							\
+    (N_MAGIC(x) == ZMAGIC ? _N_HDROFF((x)) + sizeof (struct exec) :	\
+     (N_MAGIC(x) == QMAGIC ? 0 : sizeof (struct exec)))
+#define N_TXTADDR(x) (N_MAGIC(x) == QMAGIC ? TARGET_PAGE_SIZE : 0)
+#define N_DATOFF(x) (N_TXTOFF(x) + (x).a_text)
+#define _N_SEGMENT_ROUND(x) (((x) + TARGET_PAGE_SIZE - 1) & ~(TARGET_PAGE_SIZE - 1))
+
+#define _N_TXTENDADDR(x) (N_TXTADDR(x)+(x).a_text)
+
+#define N_DATADDR(x) \
+    (N_MAGIC(x)==OMAGIC? (_N_TXTENDADDR(x)) \
+     : (_N_SEGMENT_ROUND (_N_TXTENDADDR(x))))
+
 
 #define ELF_CLASS   ELFCLASS32
 #define ELF_DATA    ELFDATA2MSB
@@ -103,27 +152,27 @@ static void *find_shdr(struct elfhdr *ehdr, int fd, struct elf_shdr *shdr, uint3
     return NULL;
 }
 
-static int find_strtab(struct elfhdr *ehdr, int fd, struct elf_shdr *shdr, struct elf_shdr *symtab)
+static void *find_strtab(struct elfhdr *ehdr, int fd, struct elf_shdr *shdr, struct elf_shdr *symtab)
 {
     int retval;
 
     retval = lseek(fd, ehdr->e_shoff + sizeof(struct elf_shdr) * symtab->sh_link, SEEK_SET);
     if (retval < 0)
-	return -1;
+	return NULL;
 
     retval = read(fd, shdr, sizeof(*shdr));
     if (retval < 0)
-	return -1;
+	return NULL;
     bswap_shdr(shdr);
     if (shdr->sh_type == SHT_STRTAB)
 	return qemu_malloc(shdr->sh_size);;
-    return 0;
+    return NULL;
 }
 
-static int read_program(int fd, struct elf_phdr *phdr, void *dst)
+static int read_program(int fd, struct elf_phdr *phdr, void *dst, uint32_t entry)
 {
     int retval;
-    retval = lseek(fd, 0x4000, SEEK_SET);
+    retval = lseek(fd, phdr->p_offset + entry - phdr->p_vaddr, SEEK_SET);
     if (retval < 0)
 	return -1;
     return read(fd, dst, phdr->p_filesz);
@@ -178,6 +227,7 @@ static void load_symbols(struct elfhdr *ehdr, int fd)
 {
     struct elf_shdr symtab, strtab;
     struct elf_sym *syms;
+    struct syminfo *s;
     int nsyms, i;
     char *str;
 
@@ -196,20 +246,19 @@ static void load_symbols(struct elfhdr *ehdr, int fd)
 	goto error_freesyms;
 
     /* Commit */
-    if (disas_symtab)
-	qemu_free(disas_symtab); /* XXX Merge with old symbols? */
-    if (disas_strtab)
-	qemu_free(disas_strtab);
-    disas_symtab = syms;
-    disas_num_syms = nsyms;
-    disas_strtab = str;
+    s = qemu_mallocz(sizeof(*s));
+    s->disas_symtab = syms;
+    s->disas_num_syms = nsyms;
+    s->disas_strtab = str;
+    s->next = syminfos;
+    syminfos = s;
     return;
  error_freesyms:
     qemu_free(syms);
     return;
 }
 
-int load_elf(const char * filename, uint8_t *addr)
+int load_elf(const char *filename, uint8_t *addr)
 {
     struct elfhdr ehdr;
     struct elf_phdr phdr;
@@ -227,12 +276,13 @@ int load_elf(const char * filename, uint8_t *addr)
 
     if (ehdr.e_ident[0] != 0x7f || ehdr.e_ident[1] != 'E'
 	|| ehdr.e_ident[2] != 'L' || ehdr.e_ident[3] != 'F'
-	|| ehdr.e_machine != EM_SPARC)
+	|| (ehdr.e_machine != EM_SPARC
+	    && ehdr.e_machine != EM_SPARC32PLUS))
 	goto error;
 
     if (find_phdr(&ehdr, fd, &phdr, PT_LOAD))
 	goto error;
-    retval = read_program(fd, &phdr, addr);
+    retval = read_program(fd, &phdr, addr, ehdr.e_entry);
     if (retval < 0)
 	goto error;
 
@@ -245,17 +295,45 @@ int load_elf(const char * filename, uint8_t *addr)
     return -1;
 }
 
-int load_kernel(const char *filename, uint8_t *addr)
+int load_aout(const char *filename, uint8_t *addr)
 {
-    int fd, size;
+    int fd, size, ret;
+    struct exec e;
+    uint32_t magic;
 
     fd = open(filename, O_RDONLY | O_BINARY);
     if (fd < 0)
         return -1;
-    /* load 32 bit code */
-    size = read(fd, addr, 16 * 1024 * 1024);
+
+    size = read(fd, &e, sizeof(e));
     if (size < 0)
         goto fail;
+
+    bswap_ahdr(&e);
+
+    magic = N_MAGIC(e);
+    switch (magic) {
+    case ZMAGIC:
+    case QMAGIC:
+    case OMAGIC:
+	lseek(fd, N_TXTOFF(e), SEEK_SET);
+	size = read(fd, addr, e.a_text + e.a_data);
+	if (size < 0)
+	    goto fail;
+	break;
+    case NMAGIC:
+	lseek(fd, N_TXTOFF(e), SEEK_SET);
+	size = read(fd, addr, e.a_text);
+	if (size < 0)
+	    goto fail;
+	ret = read(fd, addr + N_DATADDR(e), e.a_data);
+	if (ret < 0)
+	    goto fail;
+	size += ret;
+	break;
+    default:
+	goto fail;
+    }
     close(fd);
     return size;
  fail:
@@ -263,64 +341,3 @@ int load_kernel(const char *filename, uint8_t *addr)
     return -1;
 }
 
-typedef struct MAGICState {
-    uint32_t addr;
-    uint32_t saved_addr;
-    int magic_state;
-    char saved_kfn[1024];
-} MAGICState;
-
-static uint32_t magic_mem_readl(void *opaque, target_phys_addr_t addr)
-{
-    int ret;
-    MAGICState *s = opaque;
-
-    if (s->magic_state == 0) {
-        ret = load_elf(s->saved_kfn, (uint8_t *)s->saved_addr);
-	if (ret < 0)
-	    ret = load_kernel(s->saved_kfn, (uint8_t *)s->saved_addr);
-        if (ret < 0) {
-            fprintf(stderr, "qemu: could not load kernel '%s'\n", 
-                    s->saved_kfn);
-        }
-	s->magic_state = 1; /* No more magic */
-	tb_flush();
-	return bswap32(ret);
-    }
-    return 0;
-}
-
-static void magic_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
-{
-}
-
-
-static CPUReadMemoryFunc *magic_mem_read[3] = {
-    magic_mem_readl,
-    magic_mem_readl,
-    magic_mem_readl,
-};
-
-static CPUWriteMemoryFunc *magic_mem_write[3] = {
-    magic_mem_writel,
-    magic_mem_writel,
-    magic_mem_writel,
-};
-
-void magic_init(const char *kfn, int kloadaddr, uint32_t addr)
-{
-    int magic_io_memory;
-    MAGICState *s;
-
-    s = qemu_mallocz(sizeof(MAGICState));
-    if (!s)
-        return;
-
-    strcpy(s->saved_kfn, kfn);
-    s->saved_addr = kloadaddr;
-    s->magic_state = 0;
-    s->addr = addr;
-    magic_io_memory = cpu_register_io_memory(0, magic_mem_read, magic_mem_write, s);
-    cpu_register_physical_memory(addr, 4, magic_io_memory);
-}
-
diff --git a/hw/sched.c b/hw/sched.c
deleted file mode 100644
index 2ab966de4c..0000000000
--- a/hw/sched.c
+++ /dev/null
@@ -1,268 +0,0 @@
-/*
- * QEMU interrupt controller emulation
- * 
- * Copyright (c) 2003-2004 Fabrice Bellard
- * 
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-#include "vl.h"
-//#define DEBUG_IRQ_COUNT
-
-/* These registers are used for sending/receiving irqs from/to
- * different cpu's.
- */
-struct sun4m_intreg_percpu {
-	unsigned int tbt;        /* Intrs pending for this cpu, by PIL. */
-	/* These next two registers are WRITE-ONLY and are only
-	 * "on bit" sensitive, "off bits" written have NO affect.
-	 */
-	unsigned int clear;  /* Clear this cpus irqs here. */
-	unsigned int set;    /* Set this cpus irqs here. */
-};
-/*
- * djhr
- * Actually the clear and set fields in this struct are misleading..
- * according to the SLAVIO manual (and the same applies for the SEC)
- * the clear field clears bits in the mask which will ENABLE that IRQ
- * the set field sets bits in the mask to DISABLE the IRQ.
- *
- * Also the undirected_xx address in the SLAVIO is defined as
- * RESERVED and write only..
- *
- * DAVEM_NOTE: The SLAVIO only specifies behavior on uniprocessor
- *             sun4m machines, for MP the layout makes more sense.
- */
-struct sun4m_intreg_master {
-	unsigned int tbt;        /* IRQ's that are pending, see sun4m masks. */
-	unsigned int irqs;       /* Master IRQ bits. */
-
-	/* Again, like the above, two these registers are WRITE-ONLY. */
-	unsigned int clear;      /* Clear master IRQ's by setting bits here. */
-	unsigned int set;        /* Set master IRQ's by setting bits here. */
-
-	/* This register is both READ and WRITE. */
-	unsigned int undirected_target;  /* Which cpu gets undirected irqs. */
-};
-
-#define SUN4M_INT_ENABLE        0x80000000
-#define SUN4M_INT_E14           0x00000080
-#define SUN4M_INT_E10           0x00080000
-
-#define SUN4M_HARD_INT(x)       (0x000000001 << (x))
-#define SUN4M_SOFT_INT(x)       (0x000010000 << (x))
-
-#define SUN4M_INT_MASKALL       0x80000000        /* mask all interrupts */
-#define SUN4M_INT_MODULE_ERR    0x40000000        /* module error */
-#define SUN4M_INT_M2S_WRITE     0x20000000        /* write buffer error */
-#define SUN4M_INT_ECC           0x10000000        /* ecc memory error */
-#define SUN4M_INT_FLOPPY        0x00400000        /* floppy disk */
-#define SUN4M_INT_MODULE        0x00200000        /* module interrupt */
-#define SUN4M_INT_VIDEO         0x00100000        /* onboard video */
-#define SUN4M_INT_REALTIME      0x00080000        /* system timer */
-#define SUN4M_INT_SCSI          0x00040000        /* onboard scsi */
-#define SUN4M_INT_AUDIO         0x00020000        /* audio/isdn */
-#define SUN4M_INT_ETHERNET      0x00010000        /* onboard ethernet */
-#define SUN4M_INT_SERIAL        0x00008000        /* serial ports */
-#define SUN4M_INT_SBUSBITS      0x00003F80        /* sbus int bits */
-
-#define SUN4M_INT_SBUS(x)       (1 << (x+7))
-#define SUN4M_INT_VME(x)        (1 << (x))
-
-typedef struct SCHEDState {
-    uint32_t addr, addrg;
-    uint32_t intreg_pending;
-    uint32_t intreg_enabled;
-    uint32_t intregm_pending;
-    uint32_t intregm_enabled;
-} SCHEDState;
-
-static SCHEDState *ps;
-
-#ifdef DEBUG_IRQ_COUNT
-static uint64_t irq_count[32];
-#endif
-
-static uint32_t intreg_mem_readl(void *opaque, target_phys_addr_t addr)
-{
-    SCHEDState *s = opaque;
-    uint32_t saddr;
-
-    saddr = (addr - s->addr) >> 2;
-    switch (saddr) {
-    case 0:
-	return s->intreg_pending;
-	break;
-    default:
-	break;
-    }
-    return 0;
-}
-
-static void intreg_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
-{
-    SCHEDState *s = opaque;
-    uint32_t saddr;
-
-    saddr = (addr - s->addr) >> 2;
-    switch (saddr) {
-    case 0:
-	s->intreg_pending = val;
-	break;
-    case 1: // clear
-	s->intreg_enabled &= ~val;
-	break;
-    case 2: // set
-	s->intreg_enabled |= val;
-	break;
-    default:
-	break;
-    }
-}
-
-static CPUReadMemoryFunc *intreg_mem_read[3] = {
-    intreg_mem_readl,
-    intreg_mem_readl,
-    intreg_mem_readl,
-};
-
-static CPUWriteMemoryFunc *intreg_mem_write[3] = {
-    intreg_mem_writel,
-    intreg_mem_writel,
-    intreg_mem_writel,
-};
-
-static uint32_t intregm_mem_readl(void *opaque, target_phys_addr_t addr)
-{
-    SCHEDState *s = opaque;
-    uint32_t saddr;
-
-    saddr = (addr - s->addrg) >> 2;
-    switch (saddr) {
-    case 0:
-	return s->intregm_pending;
-	break;
-    case 1:
-	return s->intregm_enabled;
-	break;
-    default:
-	break;
-    }
-    return 0;
-}
-
-static void intregm_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
-{
-    SCHEDState *s = opaque;
-    uint32_t saddr;
-
-    saddr = (addr - s->addrg) >> 2;
-    switch (saddr) {
-    case 0:
-	s->intregm_pending = val;
-	break;
-    case 1:
-	s->intregm_enabled = val;
-	break;
-    case 2: // clear
-	s->intregm_enabled &= ~val;
-	break;
-    case 3: // set
-	s->intregm_enabled |= val;
-	break;
-    default:
-	break;
-    }
-}
-
-static CPUReadMemoryFunc *intregm_mem_read[3] = {
-    intregm_mem_readl,
-    intregm_mem_readl,
-    intregm_mem_readl,
-};
-
-static CPUWriteMemoryFunc *intregm_mem_write[3] = {
-    intregm_mem_writel,
-    intregm_mem_writel,
-    intregm_mem_writel,
-};
-
-void pic_info(void)
-{
-    term_printf("per-cpu: pending 0x%08x, enabled 0x%08x\n", ps->intreg_pending, ps->intreg_enabled);
-    term_printf("master: pending 0x%08x, enabled 0x%08x\n", ps->intregm_pending, ps->intregm_enabled);
-}
-
-void irq_info(void)
-{
-#ifndef DEBUG_IRQ_COUNT
-    term_printf("irq statistic code not compiled.\n");
-#else
-    int i;
-    int64_t count;
-
-    term_printf("IRQ statistics:\n");
-    for (i = 0; i < 32; i++) {
-        count = irq_count[i];
-        if (count > 0)
-            term_printf("%2d: %lld\n", i, count);
-    }
-#endif
-}
-
-static const unsigned int intr_to_mask[16] = {
-	0,	0,	0,	0,	0,	0, SUN4M_INT_ETHERNET,	0,
-	0,	0,	0,	0,	0,	0,	0,	0,
-};
-
-void pic_set_irq(int irq, int level)
-{
-    if (irq < 16) {
-	unsigned int mask = intr_to_mask[irq];
-	ps->intreg_pending |= 1 << irq;
-	if (ps->intregm_enabled & mask) {
-	    cpu_single_env->interrupt_index = irq;
-	    cpu_interrupt(cpu_single_env, CPU_INTERRUPT_HARD);
-	}
-    }
-#ifdef DEBUG_IRQ_COUNT
-    if (level == 1)
-	irq_count[irq]++;
-#endif
-}
-
-void sched_init(uint32_t addr, uint32_t addrg)
-{
-    int intreg_io_memory, intregm_io_memory;
-    SCHEDState *s;
-
-    s = qemu_mallocz(sizeof(SCHEDState));
-    if (!s)
-        return;
-    s->addr = addr;
-    s->addrg = addrg;
-
-    intreg_io_memory = cpu_register_io_memory(0, intreg_mem_read, intreg_mem_write, s);
-    cpu_register_physical_memory(addr, 3, intreg_io_memory);
-
-    intregm_io_memory = cpu_register_io_memory(0, intregm_mem_read, intregm_mem_write, s);
-    cpu_register_physical_memory(addrg, 5, intregm_io_memory);
-
-    ps = s;
-}
-
diff --git a/hw/slavio_intctl.c b/hw/slavio_intctl.c
new file mode 100644
index 0000000000..745467214b
--- /dev/null
+++ b/hw/slavio_intctl.c
@@ -0,0 +1,299 @@
+/*
+ * QEMU Sparc SLAVIO interrupt controller emulation
+ * 
+ * Copyright (c) 2003-2004 Fabrice Bellard
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "vl.h"
+//#define DEBUG_IRQ_COUNT
+
+/*
+ * Registers of interrupt controller in sun4m.
+ *
+ * This is the interrupt controller part of chip STP2001 (Slave I/O), also
+ * produced as NCR89C105. See
+ * http://www.ibiblio.org/pub/historic-linux/early-ports/Sparc/NCR/NCR89C105.txt
+ *
+ * There is a system master controller and one for each cpu.
+ * 
+ */
+
+#define MAX_CPUS 16
+
+typedef struct SLAVIO_INTCTLState {
+    uint32_t intreg_pending[MAX_CPUS];
+    uint32_t intregm_pending;
+    uint32_t intregm_disabled;
+    uint32_t target_cpu;
+#ifdef DEBUG_IRQ_COUNT
+    uint64_t irq_count[32];
+#endif
+} SLAVIO_INTCTLState;
+
+#define INTCTL_MAXADDR 0xf
+#define INTCTLM_MAXADDR 0xf
+
+// per-cpu interrupt controller
+static uint32_t slavio_intctl_mem_readl(void *opaque, target_phys_addr_t addr)
+{
+    SLAVIO_INTCTLState *s = opaque;
+    uint32_t saddr;
+    int cpu;
+
+    cpu = (addr & (MAX_CPUS - 1) * TARGET_PAGE_SIZE) >> 12;
+    saddr = (addr & INTCTL_MAXADDR) >> 2;
+    switch (saddr) {
+    case 0:
+	return s->intreg_pending[cpu];
+    default:
+	break;
+    }
+    return 0;
+}
+
+static void slavio_intctl_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+    SLAVIO_INTCTLState *s = opaque;
+    uint32_t saddr;
+    int cpu;
+
+    cpu = (addr & (MAX_CPUS - 1) * TARGET_PAGE_SIZE) >> 12;
+    saddr = (addr & INTCTL_MAXADDR) >> 2;
+    switch (saddr) {
+    case 1: // clear pending softints
+	if (val & 0x4000)
+	    val |= 80000000;
+	val &= 0xfffe0000;
+	s->intreg_pending[cpu] &= ~val;
+	break;
+    case 2: // set softint
+	val &= 0xfffe0000;
+	s->intreg_pending[cpu] |= val;
+	break;
+    default:
+	break;
+    }
+}
+
+static CPUReadMemoryFunc *slavio_intctl_mem_read[3] = {
+    slavio_intctl_mem_readl,
+    slavio_intctl_mem_readl,
+    slavio_intctl_mem_readl,
+};
+
+static CPUWriteMemoryFunc *slavio_intctl_mem_write[3] = {
+    slavio_intctl_mem_writel,
+    slavio_intctl_mem_writel,
+    slavio_intctl_mem_writel,
+};
+
+// master system interrupt controller
+static uint32_t slavio_intctlm_mem_readl(void *opaque, target_phys_addr_t addr)
+{
+    SLAVIO_INTCTLState *s = opaque;
+    uint32_t saddr;
+
+    saddr = (addr & INTCTLM_MAXADDR) >> 2;
+    switch (saddr) {
+    case 0:
+	return s->intregm_pending;
+    case 1:
+	return s->intregm_disabled;
+    case 4:
+	return s->target_cpu;
+    default:
+	break;
+    }
+    return 0;
+}
+
+static void slavio_intctlm_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+    SLAVIO_INTCTLState *s = opaque;
+    uint32_t saddr;
+
+    saddr = (addr & INTCTLM_MAXADDR) >> 2;
+    switch (saddr) {
+    case 2: // clear (enable)
+	// Force unused bits
+	val |= 0x7fb2007f;
+	s->intregm_disabled &= ~val;
+	break;
+    case 3: // set (disable, clear pending)
+	// Force unused bits
+	val &= ~0x7fb2007f;
+	s->intregm_disabled |= val;
+	s->intregm_pending &= ~val;
+	break;
+    case 4:
+	s->target_cpu = val & (MAX_CPUS - 1);
+	break;
+    default:
+	break;
+    }
+}
+
+static CPUReadMemoryFunc *slavio_intctlm_mem_read[3] = {
+    slavio_intctlm_mem_readl,
+    slavio_intctlm_mem_readl,
+    slavio_intctlm_mem_readl,
+};
+
+static CPUWriteMemoryFunc *slavio_intctlm_mem_write[3] = {
+    slavio_intctlm_mem_writel,
+    slavio_intctlm_mem_writel,
+    slavio_intctlm_mem_writel,
+};
+
+void slavio_pic_info(void *opaque)
+{
+    SLAVIO_INTCTLState *s = opaque;
+    int i;
+
+    for (i = 0; i < MAX_CPUS; i++) {
+	term_printf("per-cpu %d: pending 0x%08x\n", i, s->intreg_pending[i]);
+    }
+    term_printf("master: pending 0x%08x, disabled 0x%08x\n", s->intregm_pending, s->intregm_disabled);
+}
+
+void slavio_irq_info(void *opaque)
+{
+#ifndef DEBUG_IRQ_COUNT
+    term_printf("irq statistic code not compiled.\n");
+#else
+    SLAVIO_INTCTLState *s = opaque;
+    int i;
+    int64_t count;
+
+    term_printf("IRQ statistics:\n");
+    for (i = 0; i < 32; i++) {
+        count = s->irq_count[i];
+        if (count > 0)
+            term_printf("%2d: %lld\n", i, count);
+    }
+#endif
+}
+
+static const uint32_t intbit_to_level[32] = {
+    2, 3, 5, 7, 9, 11, 0, 14,	3, 5, 7, 9, 11, 13, 12, 12,
+    6, 0, 4, 10, 8, 0, 11, 0,	0, 0, 0, 0, 15, 0, 0, 0,
+};
+
+/*
+ * "irq" here is the bit number in the system interrupt register to
+ * separate serial and keyboard interrupts sharing a level.
+ */
+void slavio_pic_set_irq(void *opaque, int irq, int level)
+{
+    SLAVIO_INTCTLState *s = opaque;
+
+    if (irq < 32) {
+	uint32_t mask = 1 << irq;
+	uint32_t pil = intbit_to_level[irq];
+	if (pil > 0) {
+	    if (level) {
+		s->intregm_pending |= mask;
+		s->intreg_pending[s->target_cpu] |= 1 << pil;
+	    }
+	    else {
+		s->intregm_pending &= ~mask;
+		s->intreg_pending[s->target_cpu] &= ~(1 << pil);
+	    }
+	    if (level &&
+		!(s->intregm_disabled & mask) &&
+		!(s->intregm_disabled & 0x80000000) &&
+		(pil == 15 || (pil > cpu_single_env->psrpil && cpu_single_env->psret == 1))) {
+#ifdef DEBUG_IRQ_COUNT
+		if (level == 1)
+		    s->irq_count[pil]++;
+#endif
+		cpu_single_env->interrupt_index = TT_EXTINT | pil;
+		cpu_interrupt(cpu_single_env, CPU_INTERRUPT_HARD);
+	    }
+	}
+    }
+}
+
+static void slavio_intctl_save(QEMUFile *f, void *opaque)
+{
+    SLAVIO_INTCTLState *s = opaque;
+    int i;
+    
+    for (i = 0; i < MAX_CPUS; i++) {
+	qemu_put_be32s(f, &s->intreg_pending[i]);
+    }
+    qemu_put_be32s(f, &s->intregm_pending);
+    qemu_put_be32s(f, &s->intregm_disabled);
+    qemu_put_be32s(f, &s->target_cpu);
+}
+
+static int slavio_intctl_load(QEMUFile *f, void *opaque, int version_id)
+{
+    SLAVIO_INTCTLState *s = opaque;
+    int i;
+
+    if (version_id != 1)
+        return -EINVAL;
+
+    for (i = 0; i < MAX_CPUS; i++) {
+	qemu_get_be32s(f, &s->intreg_pending[i]);
+    }
+    qemu_get_be32s(f, &s->intregm_pending);
+    qemu_get_be32s(f, &s->intregm_disabled);
+    qemu_get_be32s(f, &s->target_cpu);
+    return 0;
+}
+
+static void slavio_intctl_reset(void *opaque)
+{
+    SLAVIO_INTCTLState *s = opaque;
+    int i;
+
+    for (i = 0; i < MAX_CPUS; i++) {
+	s->intreg_pending[i] = 0;
+    }
+    s->intregm_disabled = 0xffffffff;
+    s->intregm_pending = 0;
+    s->target_cpu = 0;
+}
+
+void *slavio_intctl_init(uint32_t addr, uint32_t addrg)
+{
+    int slavio_intctl_io_memory, slavio_intctlm_io_memory, i;
+    SLAVIO_INTCTLState *s;
+
+    s = qemu_mallocz(sizeof(SLAVIO_INTCTLState));
+    if (!s)
+        return NULL;
+
+    for (i = 0; i < MAX_CPUS; i++) {
+	slavio_intctl_io_memory = cpu_register_io_memory(0, slavio_intctl_mem_read, slavio_intctl_mem_write, s);
+	cpu_register_physical_memory(addr + i * TARGET_PAGE_SIZE, INTCTL_MAXADDR, slavio_intctl_io_memory);
+    }
+
+    slavio_intctlm_io_memory = cpu_register_io_memory(0, slavio_intctlm_mem_read, slavio_intctlm_mem_write, s);
+    cpu_register_physical_memory(addrg, INTCTLM_MAXADDR, slavio_intctlm_io_memory);
+
+    register_savevm("slavio_intctl", addr, 1, slavio_intctl_save, slavio_intctl_load, s);
+    qemu_register_reset(slavio_intctl_reset, s);
+    slavio_intctl_reset(s);
+    return s;
+}
+
diff --git a/hw/slavio_serial.c b/hw/slavio_serial.c
new file mode 100644
index 0000000000..348f328f5a
--- /dev/null
+++ b/hw/slavio_serial.c
@@ -0,0 +1,364 @@
+/*
+ * QEMU Sparc SLAVIO serial port emulation
+ * 
+ * Copyright (c) 2003-2004 Fabrice Bellard
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "vl.h"
+
+//#define DEBUG_SERIAL
+
+/* debug keyboard */
+//#define DEBUG_KBD
+
+/* debug keyboard : only mouse */
+//#define DEBUG_MOUSE
+
+/*
+ * This is the serial port, mouse and keyboard part of chip STP2001
+ * (Slave I/O), also produced as NCR89C105. See
+ * http://www.ibiblio.org/pub/historic-linux/early-ports/Sparc/NCR/NCR89C105.txt
+ * 
+ * The serial ports implement full AMD AM8530 or Zilog Z8530 chips,
+ * mouse and keyboard ports don't implement all functions and they are
+ * only asynchronous. There is no DMA.
+ *
+ */
+
+typedef struct ChannelState {
+    int irq;
+    int reg;
+    int rxint, txint;
+    uint8_t rx, tx, wregs[16], rregs[16];
+    CharDriverState *chr;
+} ChannelState;
+
+struct SerialState {
+    struct ChannelState chn[2];
+};
+
+#define SERIAL_MAXADDR 7
+
+static void slavio_serial_update_irq(ChannelState *s)
+{
+    if ((s->wregs[1] & 1) && // interrupts enabled
+	(((s->wregs[1] & 2) && s->txint == 1) || // tx ints enabled, pending
+	 ((((s->wregs[1] & 0x18) == 8) || ((s->wregs[1] & 0x18) == 0x10)) &&
+	  s->rxint == 1) || // rx ints enabled, pending
+	 ((s->wregs[15] & 0x80) && (s->rregs[0] & 0x80)))) { // break int e&p
+        pic_set_irq(s->irq, 1);
+    } else {
+        pic_set_irq(s->irq, 0);
+    }
+}
+
+static void slavio_serial_reset_chn(ChannelState *s)
+{
+    int i;
+
+    s->reg = 0;
+    for (i = 0; i < SERIAL_MAXADDR; i++) {
+	s->rregs[i] = 0;
+	s->wregs[i] = 0;
+    }
+    s->wregs[4] = 4;
+    s->wregs[9] = 0xc0;
+    s->wregs[11] = 8;
+    s->wregs[14] = 0x30;
+    s->wregs[15] = 0xf8;
+    s->rregs[0] = 0x44;
+    s->rregs[1] = 6;
+
+    s->rx = s->tx = 0;
+    s->rxint = s->txint = 0;
+}
+
+static void slavio_serial_reset(void *opaque)
+{
+    SerialState *s = opaque;
+    slavio_serial_reset_chn(&s->chn[0]);
+    slavio_serial_reset_chn(&s->chn[1]);
+}
+
+static void slavio_serial_mem_writeb(void *opaque, uint32_t addr, uint32_t val)
+{
+    SerialState *ser = opaque;
+    ChannelState *s;
+    uint32_t saddr;
+    int newreg, channel;
+
+    val &= 0xff;
+    saddr = (addr & 3) >> 1;
+    channel = (addr & SERIAL_MAXADDR) >> 2;
+    s = &ser->chn[channel];
+    switch (saddr) {
+    case 0:
+	newreg = 0;
+	switch (s->reg) {
+	case 0:
+	    newreg = val & 7;
+	    val &= 0x38;
+	    switch (val) {
+	    case 8:
+		s->reg |= 0x8;
+		break;
+	    case 0x20:
+		s->rxint = 0;
+		break;
+	    case 0x28:
+		s->txint = 0;
+		break;
+	    default:
+		break;
+	    }
+	    break;
+	case 1 ... 8:
+	case 10 ... 15:
+	    s->wregs[s->reg] = val;
+	    break;
+	case 9:
+	    switch (val & 0xc0) {
+	    case 0:
+	    default:
+		break;
+	    case 0x40:
+		slavio_serial_reset_chn(&ser->chn[1]);
+		return;
+	    case 0x80:
+		slavio_serial_reset_chn(&ser->chn[0]);
+		return;
+	    case 0xc0:
+		slavio_serial_reset(ser);
+		return;
+	    }
+	    break;
+	default:
+	    break;
+	}
+	if (s->reg == 0)
+	    s->reg = newreg;
+	else
+	    s->reg = 0;
+	break;
+    case 1:
+	if (s->wregs[5] & 8) { // tx enabled
+	    s->tx = val;
+	    if (s->chr)
+		qemu_chr_write(s->chr, &s->tx, 1);
+	    s->txint = 1;
+	}
+	break;
+    default:
+	break;
+    }
+}
+
+static uint32_t slavio_serial_mem_readb(void *opaque, uint32_t addr)
+{
+    SerialState *ser = opaque;
+    ChannelState *s;
+    uint32_t saddr;
+    uint32_t ret;
+    int channel;
+
+    saddr = (addr & 3) >> 1;
+    channel = (addr & SERIAL_MAXADDR) >> 2;
+    s = &ser->chn[channel];
+    switch (saddr) {
+    case 0:
+	ret = s->rregs[s->reg];
+	s->reg = 0;
+	return ret;
+    case 1:
+	s->rregs[0] &= ~1;
+	return s->rx;
+    default:
+	break;
+    }
+    return 0;
+}
+
+static int serial_can_receive(void *opaque)
+{
+    ChannelState *s = opaque;
+    if (((s->wregs[3] & 1) == 0) // Rx not enabled
+	|| ((s->rregs[0] & 1) == 1)) // char already available
+	return 0;
+    else
+	return 1;
+}
+
+static void serial_receive_byte(ChannelState *s, int ch)
+{
+    s->rregs[0] |= 1;
+    s->rx = ch;
+    s->rxint = 1;
+    slavio_serial_update_irq(s);
+}
+
+static void serial_receive_break(ChannelState *s)
+{
+    s->rregs[0] |= 0x80;
+    slavio_serial_update_irq(s);
+}
+
+static void serial_receive1(void *opaque, const uint8_t *buf, int size)
+{
+    ChannelState *s = opaque;
+    serial_receive_byte(s, buf[0]);
+}
+
+static void serial_event(void *opaque, int event)
+{
+    ChannelState *s = opaque;
+    if (event == CHR_EVENT_BREAK)
+        serial_receive_break(s);
+}
+
+static CPUReadMemoryFunc *slavio_serial_mem_read[3] = {
+    slavio_serial_mem_readb,
+    slavio_serial_mem_readb,
+    slavio_serial_mem_readb,
+};
+
+static CPUWriteMemoryFunc *slavio_serial_mem_write[3] = {
+    slavio_serial_mem_writeb,
+    slavio_serial_mem_writeb,
+    slavio_serial_mem_writeb,
+};
+
+static void slavio_serial_save_chn(QEMUFile *f, ChannelState *s)
+{
+    qemu_put_be32s(f, &s->irq);
+    qemu_put_be32s(f, &s->reg);
+    qemu_put_be32s(f, &s->rxint);
+    qemu_put_be32s(f, &s->txint);
+    qemu_put_8s(f, &s->rx);
+    qemu_put_8s(f, &s->tx);
+    qemu_put_buffer(f, s->wregs, 16);
+    qemu_put_buffer(f, s->rregs, 16);
+}
+
+static void slavio_serial_save(QEMUFile *f, void *opaque)
+{
+    SerialState *s = opaque;
+
+    slavio_serial_save_chn(f, &s->chn[0]);
+    slavio_serial_save_chn(f, &s->chn[1]);
+}
+
+static int slavio_serial_load_chn(QEMUFile *f, ChannelState *s, int version_id)
+{
+    if (version_id != 1)
+        return -EINVAL;
+
+    qemu_get_be32s(f, &s->irq);
+    qemu_get_be32s(f, &s->reg);
+    qemu_get_be32s(f, &s->rxint);
+    qemu_get_be32s(f, &s->txint);
+    qemu_get_8s(f, &s->rx);
+    qemu_get_8s(f, &s->tx);
+    qemu_get_buffer(f, s->wregs, 16);
+    qemu_get_buffer(f, s->rregs, 16);
+    return 0;
+}
+
+static int slavio_serial_load(QEMUFile *f, void *opaque, int version_id)
+{
+    SerialState *s = opaque;
+    int ret;
+
+    ret = slavio_serial_load_chn(f, &s->chn[0], version_id);
+    if (ret != 0)
+	return ret;
+    ret = slavio_serial_load_chn(f, &s->chn[1], version_id);
+    return ret;
+
+}
+
+SerialState *slavio_serial_init(int base, int irq, CharDriverState *chr1, CharDriverState *chr2)
+{
+    int slavio_serial_io_memory;
+    SerialState *s;
+
+    s = qemu_mallocz(sizeof(SerialState));
+    if (!s)
+        return NULL;
+    s->chn[0].irq = irq;
+    s->chn[1].irq = irq;
+    s->chn[0].chr = chr1;
+    s->chn[1].chr = chr2;
+
+    slavio_serial_io_memory = cpu_register_io_memory(0, slavio_serial_mem_read, slavio_serial_mem_write, s);
+    cpu_register_physical_memory(base, SERIAL_MAXADDR, slavio_serial_io_memory);
+
+    if (chr1) {
+	qemu_chr_add_read_handler(chr1, serial_can_receive, serial_receive1, &s->chn[0]);
+	qemu_chr_add_event_handler(chr1, serial_event);
+    }
+    if (chr2) {
+	qemu_chr_add_read_handler(chr2, serial_can_receive, serial_receive1, &s->chn[1]);
+	qemu_chr_add_event_handler(chr2, serial_event);
+    }
+    register_savevm("slavio_serial", base, 1, slavio_serial_save, slavio_serial_load, s);
+    qemu_register_reset(slavio_serial_reset, s);
+    slavio_serial_reset(s);
+    return s;
+}
+
+static void sunkbd_event(void *opaque, int ch)
+{
+    ChannelState *s = opaque;
+    // XXX: PC -> Sun Type 5 translation?
+    serial_receive_byte(s, ch);
+}
+
+static void sunmouse_event(void *opaque, 
+                               int dx, int dy, int dz, int buttons_state)
+{
+    ChannelState *s = opaque;
+    int ch;
+
+    // XXX
+    ch = 0x42;
+    serial_receive_byte(s, ch);
+}
+
+void slavio_serial_ms_kbd_init(int base, int irq)
+{
+    int slavio_serial_io_memory;
+    SerialState *s;
+
+    s = qemu_mallocz(sizeof(SerialState));
+    if (!s)
+        return;
+    s->chn[0].irq = irq;
+    s->chn[1].irq = irq;
+    s->chn[0].chr = NULL;
+    s->chn[1].chr = NULL;
+
+    slavio_serial_io_memory = cpu_register_io_memory(0, slavio_serial_mem_read, slavio_serial_mem_write, s);
+    cpu_register_physical_memory(base, SERIAL_MAXADDR, slavio_serial_io_memory);
+
+    qemu_add_kbd_event_handler(sunkbd_event, &s->chn[0]);
+    qemu_add_mouse_event_handler(sunmouse_event, &s->chn[1]);
+    qemu_register_reset(slavio_serial_reset, s);
+    slavio_serial_reset(s);
+}
diff --git a/hw/slavio_timer.c b/hw/slavio_timer.c
new file mode 100644
index 0000000000..43f59d2750
--- /dev/null
+++ b/hw/slavio_timer.c
@@ -0,0 +1,289 @@
+/*
+ * QEMU Sparc SLAVIO timer controller emulation
+ *
+ * Copyright (c) 2003-2004 Fabrice Bellard
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "vl.h"
+
+//#define DEBUG_TIMER
+
+/*
+ * Registers of hardware timer in sun4m.
+ *
+ * This is the timer/counter part of chip STP2001 (Slave I/O), also
+ * produced as NCR89C105. See
+ * http://www.ibiblio.org/pub/historic-linux/early-ports/Sparc/NCR/NCR89C105.txt
+ * 
+ * The 31-bit counter is incremented every 500ns by bit 9. Bits 8..0
+ * are zero. Bit 31 is 1 when count has been reached.
+ *
+ */
+
+typedef struct SLAVIO_TIMERState {
+    uint32_t limit, count, counthigh;
+    int64_t count_load_time;
+    int64_t expire_time;
+    int64_t stop_time, tick_offset;
+    QEMUTimer *irq_timer;
+    int irq;
+    int reached, stopped;
+    int mode; // 0 = processor, 1 = user, 2 = system
+} SLAVIO_TIMERState;
+
+#define TIMER_MAXADDR 0x1f
+#define CNT_FREQ 2000000
+#define MAX_CPUS 16
+
+// Update count, set irq, update expire_time
+static void slavio_timer_get_out(SLAVIO_TIMERState *s)
+{
+    int out;
+    int64_t diff, ticks, count;
+    uint32_t limit;
+
+    // There are three clock tick units: CPU ticks, register units
+    // (nanoseconds), and counter ticks (500 ns).
+    if (s->mode == 1 && s->stopped)
+	ticks = s->stop_time;
+    else
+	ticks = qemu_get_clock(vm_clock) - s->tick_offset;
+
+    out = (ticks >= s->expire_time);
+    if (out)
+	s->reached = 0x80000000;
+    if (!s->limit)
+	limit = 0x7fffffff;
+    else
+	limit = s->limit;
+
+    // Convert register units to counter ticks
+    limit = limit >> 9;
+
+    // Convert cpu ticks to counter ticks
+    diff = muldiv64(ticks - s->count_load_time, CNT_FREQ, ticks_per_sec);
+
+    // Calculate what the counter should be, convert to register
+    // units
+    count = diff % limit;
+    s->count = count << 9;
+    s->counthigh = count >> 22;
+
+    // Expire time: CPU ticks left to next interrupt
+    // Convert remaining counter ticks to CPU ticks
+    s->expire_time = ticks + muldiv64(limit - count, ticks_per_sec, CNT_FREQ);
+
+#ifdef DEBUG_TIMER
+    term_printf("timer: irq %d limit %d reached %d d %lld count %d s->c %x diff %lld stopped %d mode %d\n", s->irq, limit, s->reached?1:0, (ticks-s->count_load_time), count, s->count, s->expire_time - ticks, s->stopped, s->mode);
+#endif
+    if (s->mode != 1)
+	pic_set_irq(s->irq, out);
+}
+
+// timer callback
+static void slavio_timer_irq(void *opaque)
+{
+    SLAVIO_TIMERState *s = opaque;
+
+    if (!s->irq_timer)
+        return;
+    slavio_timer_get_out(s);
+    if (s->mode != 1)
+	qemu_mod_timer(s->irq_timer, s->expire_time);
+}
+
+static uint32_t slavio_timer_mem_readl(void *opaque, target_phys_addr_t addr)
+{
+    SLAVIO_TIMERState *s = opaque;
+    uint32_t saddr;
+
+    saddr = (addr & TIMER_MAXADDR) >> 2;
+    switch (saddr) {
+    case 0:
+	// read limit (system counter mode) or read most signifying
+	// part of counter (user mode)
+	if (s->mode != 1) {
+	    // clear irq
+	    pic_set_irq(s->irq, 0);
+	    s->count_load_time = qemu_get_clock(vm_clock);
+	    s->reached = 0;
+	    return s->limit;
+	}
+	else {
+	    slavio_timer_get_out(s);
+	    return s->counthigh & 0x7fffffff;
+	}
+    case 1:
+	// read counter and reached bit (system mode) or read lsbits
+	// of counter (user mode)
+	slavio_timer_get_out(s);
+	if (s->mode != 1)
+	    return (s->count & 0x7fffffff) | s->reached;
+	else
+	    return s->count;
+    case 3:
+	// read start/stop status
+	return s->stopped;
+    case 4:
+	// read user/system mode
+	return s->mode & 1;
+    default:
+	return 0;
+    }
+}
+
+static void slavio_timer_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+    SLAVIO_TIMERState *s = opaque;
+    uint32_t saddr;
+
+    saddr = (addr & TIMER_MAXADDR) >> 2;
+    switch (saddr) {
+    case 0:
+	// set limit, reset counter
+	s->count_load_time = qemu_get_clock(vm_clock);
+	// fall through
+    case 2:
+	// set limit without resetting counter
+	if (!val)
+	    s->limit = 0x7fffffff;
+	else
+	    s->limit = val & 0x7fffffff;
+	slavio_timer_irq(s);
+	break;
+    case 3:
+	// start/stop user counter
+	if (s->mode == 1) {
+	    if (val & 1) {
+		s->stop_time = qemu_get_clock(vm_clock);
+		s->stopped = 1;
+	    }
+	    else {
+		if (s->stopped)
+		    s->tick_offset += qemu_get_clock(vm_clock) - s->stop_time;
+		s->stopped = 0;
+	    }
+	}
+	break;
+    case 4:
+	// bit 0: user (1) or system (0) counter mode
+	if (s->mode == 0 || s->mode == 1)
+	    s->mode = val & 1;
+	break;
+    default:
+	break;
+    }
+}
+
+static CPUReadMemoryFunc *slavio_timer_mem_read[3] = {
+    slavio_timer_mem_readl,
+    slavio_timer_mem_readl,
+    slavio_timer_mem_readl,
+};
+
+static CPUWriteMemoryFunc *slavio_timer_mem_write[3] = {
+    slavio_timer_mem_writel,
+    slavio_timer_mem_writel,
+    slavio_timer_mem_writel,
+};
+
+static void slavio_timer_save(QEMUFile *f, void *opaque)
+{
+    SLAVIO_TIMERState *s = opaque;
+
+    qemu_put_be32s(f, &s->limit);
+    qemu_put_be32s(f, &s->count);
+    qemu_put_be32s(f, &s->counthigh);
+    qemu_put_be64s(f, &s->count_load_time);
+    qemu_put_be64s(f, &s->expire_time);
+    qemu_put_be64s(f, &s->stop_time);
+    qemu_put_be64s(f, &s->tick_offset);
+    qemu_put_be32s(f, &s->irq);
+    qemu_put_be32s(f, &s->reached);
+    qemu_put_be32s(f, &s->stopped);
+    qemu_put_be32s(f, &s->mode);
+}
+
+static int slavio_timer_load(QEMUFile *f, void *opaque, int version_id)
+{
+    SLAVIO_TIMERState *s = opaque;
+    
+    if (version_id != 1)
+        return -EINVAL;
+
+    qemu_get_be32s(f, &s->limit);
+    qemu_get_be32s(f, &s->count);
+    qemu_get_be32s(f, &s->counthigh);
+    qemu_get_be64s(f, &s->count_load_time);
+    qemu_get_be64s(f, &s->expire_time);
+    qemu_get_be64s(f, &s->stop_time);
+    qemu_get_be64s(f, &s->tick_offset);
+    qemu_get_be32s(f, &s->irq);
+    qemu_get_be32s(f, &s->reached);
+    qemu_get_be32s(f, &s->stopped);
+    qemu_get_be32s(f, &s->mode);
+    return 0;
+}
+
+static void slavio_timer_reset(void *opaque)
+{
+    SLAVIO_TIMERState *s = opaque;
+
+    s->limit = 0;
+    s->count = 0;
+    s->count_load_time = qemu_get_clock(vm_clock);;
+    s->stop_time = s->count_load_time;
+    s->tick_offset = 0;
+    s->reached = 0;
+    s->mode &= 2;
+    s->stopped = 1;
+    slavio_timer_get_out(s);
+}
+
+static void slavio_timer_init_internal(uint32_t addr, int irq, int mode)
+{
+    int slavio_timer_io_memory;
+    SLAVIO_TIMERState *s;
+
+    s = qemu_mallocz(sizeof(SLAVIO_TIMERState));
+    if (!s)
+        return;
+    s->irq = irq;
+    s->mode = mode;
+    s->irq_timer = qemu_new_timer(vm_clock, slavio_timer_irq, s);
+
+    slavio_timer_io_memory = cpu_register_io_memory(0, slavio_timer_mem_read,
+						    slavio_timer_mem_write, s);
+    cpu_register_physical_memory(addr, TIMER_MAXADDR, slavio_timer_io_memory);
+    register_savevm("slavio_timer", addr, 1, slavio_timer_save, slavio_timer_load, s);
+    qemu_register_reset(slavio_timer_reset, s);
+    slavio_timer_reset(s);
+}
+
+void slavio_timer_init(uint32_t addr1, int irq1, uint32_t addr2, int irq2)
+{
+    int i;
+
+    for (i = 0; i < MAX_CPUS; i++) {
+	slavio_timer_init_internal(addr1 + i * TARGET_PAGE_SIZE, irq1, 0);
+    }
+
+    slavio_timer_init_internal(addr2, irq2, 2);
+}
diff --git a/hw/sun4m.c b/hw/sun4m.c
index 80305e09c3..7cc5bd8d9c 100644
--- a/hw/sun4m.c
+++ b/hw/sun4m.c
@@ -25,29 +25,32 @@
 #include "m48t08.h"
 
 #define KERNEL_LOAD_ADDR     0x00004000
-#define MMU_CONTEXT_TBL      0x00003000
-#define MMU_L1PTP            (MMU_CONTEXT_TBL + 0x0400)
-#define MMU_L2PTP            (MMU_CONTEXT_TBL + 0x0800)
-#define PROM_ADDR	     0xffd04000
+#define PROM_ADDR	     0xffd00000
 #define PROM_FILENAMEB	     "proll.bin"
 #define PROM_FILENAMEE	     "proll.elf"
-#define PROLL_MAGIC_ADDR 0x20000000
-#define PHYS_JJ_EEPROM	0x71200000	/* [2000] MK48T08 */
+#define PHYS_JJ_EEPROM	0x71200000	/* m48t08 */
 #define PHYS_JJ_IDPROM_OFF	0x1FD8
 #define PHYS_JJ_EEPROM_SIZE	0x2000
-#define PHYS_JJ_IOMMU	0x10000000	/* First page of sun4m IOMMU */
+// IRQs are not PIL ones, but master interrupt controller register
+// bits
+#define PHYS_JJ_IOMMU	0x10000000	/* I/O MMU */
 #define PHYS_JJ_TCX_FB	0x50800000	/* Start address, frame buffer body */
-#define PHYS_JJ_TCX_0E	0x5E000000	/* Top address, one byte used. */
-#define PHYS_JJ_IOMMU	0x10000000	/* First page of sun4m IOMMU */
-#define PHYS_JJ_LEDMA   0x78400010      /* ledma, off by 10 from unused SCSI */
-#define PHYS_JJ_LE      0x78C00000      /* LANCE, typical sun4m */
-#define PHYS_JJ_LE_IRQ  6
-#define PHYS_JJ_CLOCK	0x71D00000
-#define PHYS_JJ_CLOCK_IRQ  10
-#define PHYS_JJ_CLOCK1	0x71D10000
-#define PHYS_JJ_CLOCK1_IRQ  14
-#define PHYS_JJ_INTR0	0x71E00000	/* CPU0 interrupt control registers */
+#define PHYS_JJ_LEDMA   0x78400010      /* Lance DMA controller */
+#define PHYS_JJ_LE      0x78C00000      /* Lance ethernet */
+#define PHYS_JJ_LE_IRQ     16
+#define PHYS_JJ_CLOCK	0x71D00000      /* Per-CPU timer/counter, L14 */
+#define PHYS_JJ_CLOCK_IRQ  7
+#define PHYS_JJ_CLOCK1	0x71D10000      /* System timer/counter, L10 */
+#define PHYS_JJ_CLOCK1_IRQ 19
+#define PHYS_JJ_INTR0	0x71E00000	/* Per-CPU interrupt control registers */
 #define PHYS_JJ_INTR_G	0x71E10000	/* Master interrupt control registers */
+#define PHYS_JJ_MS_KBD	0x71000000	/* Mouse and keyboard */
+#define PHYS_JJ_MS_KBD_IRQ    14
+#define PHYS_JJ_SER	0x71100000	/* Serial */
+#define PHYS_JJ_SER_IRQ    15
+#define PHYS_JJ_SCSI_IRQ   18
+#define PHYS_JJ_FDC	0x71400000	/* Floppy */
+#define PHYS_JJ_FLOPPY_IRQ 22
 
 /* TSC handling */
 
@@ -57,13 +60,73 @@ uint64_t cpu_get_tsc()
 }
 
 void DMA_run() {}
-void SB16_run() {}
-int serial_can_receive(SerialState *s) { return 0; }
-void serial_receive_byte(SerialState *s, int ch) {}
-void serial_receive_break(SerialState *s) {}
 
 static m48t08_t *nvram;
 
+static void nvram_init(m48t08_t *nvram, uint8_t *macaddr)
+{
+    unsigned char tmp = 0;
+    int i, j;
+
+    i = 0x1fd8;
+    m48t08_write(nvram, i++, 0x01);
+    m48t08_write(nvram, i++, 0x80); /* Sun4m OBP */
+    j = 0;
+    m48t08_write(nvram, i++, macaddr[j++]);
+    m48t08_write(nvram, i++, macaddr[j++]);
+    m48t08_write(nvram, i++, macaddr[j++]);
+    m48t08_write(nvram, i++, macaddr[j++]);
+    m48t08_write(nvram, i++, macaddr[j++]);
+    m48t08_write(nvram, i, macaddr[j]);
+
+    /* Calculate checksum */
+    for (i = 0x1fd8; i < 0x1fe7; i++) {
+	tmp ^= m48t08_read(nvram, i);
+    }
+    m48t08_write(nvram, 0x1fe7, tmp);
+}
+
+static void *slavio_intctl;
+
+void pic_info()
+{
+    slavio_pic_info(slavio_intctl);
+}
+
+void irq_info()
+{
+    slavio_irq_info(slavio_intctl);
+}
+
+void pic_set_irq(int irq, int level)
+{
+    slavio_pic_set_irq(slavio_intctl, irq, level);
+}
+
+static void *tcx;
+
+void vga_update_display()
+{
+    tcx_update_display(tcx);
+}
+
+void vga_invalidate_display()
+{
+    tcx_invalidate_display(tcx);
+}
+
+void vga_screen_dump(const char *filename)
+{
+    tcx_screen_dump(tcx, filename);
+}
+
+static void *iommu;
+
+uint32_t iommu_translate(uint32_t addr)
+{
+    return iommu_translate_local(iommu, addr);
+}
+
 /* Sun4m hardware initialisation */
 void sun4m_init(int ram_size, int vga_ram_size, int boot_device,
              DisplayState *ds, const char **fd_filename, int snapshot,
@@ -72,42 +135,50 @@ void sun4m_init(int ram_size, int vga_ram_size, int boot_device,
 {
     char buf[1024];
     int ret, linux_boot;
-    unsigned long bios_offset;
+    unsigned long vram_size = 0x100000, prom_offset;
 
     linux_boot = (kernel_filename != NULL);
 
     /* allocate RAM */
     cpu_register_physical_memory(0, ram_size, 0);
-    bios_offset = ram_size;
 
-    iommu_init(PHYS_JJ_IOMMU);
-    sched_init(PHYS_JJ_INTR0, PHYS_JJ_INTR_G);
-    tcx_init(ds, PHYS_JJ_TCX_FB);
+    iommu = iommu_init(PHYS_JJ_IOMMU);
+    slavio_intctl = slavio_intctl_init(PHYS_JJ_INTR0, PHYS_JJ_INTR_G);
+    tcx = tcx_init(ds, PHYS_JJ_TCX_FB, phys_ram_base + ram_size, ram_size, vram_size);
     lance_init(&nd_table[0], PHYS_JJ_LE_IRQ, PHYS_JJ_LE, PHYS_JJ_LEDMA);
-    nvram = m48t08_init(PHYS_JJ_EEPROM, PHYS_JJ_EEPROM_SIZE, &nd_table[0].macaddr);
-    timer_init(PHYS_JJ_CLOCK, PHYS_JJ_CLOCK_IRQ);
-    timer_init(PHYS_JJ_CLOCK1, PHYS_JJ_CLOCK1_IRQ);
-    magic_init(kernel_filename, phys_ram_base + KERNEL_LOAD_ADDR, PROLL_MAGIC_ADDR);
+    nvram = m48t08_init(PHYS_JJ_EEPROM, PHYS_JJ_EEPROM_SIZE);
+    nvram_init(nvram, (uint8_t *)&nd_table[0].macaddr);
+    slavio_timer_init(PHYS_JJ_CLOCK, PHYS_JJ_CLOCK_IRQ, PHYS_JJ_CLOCK1, PHYS_JJ_CLOCK1_IRQ);
+    slavio_serial_ms_kbd_init(PHYS_JJ_MS_KBD, PHYS_JJ_MS_KBD_IRQ);
+    slavio_serial_init(PHYS_JJ_SER, PHYS_JJ_SER_IRQ, serial_hds[0], serial_hds[1]);
+    fdctrl_init(PHYS_JJ_FLOPPY_IRQ, 0, 1, PHYS_JJ_FDC, fd_table);
 
-    /* We load Proll as the kernel and start it. It will issue a magic
-       IO to load the real kernel */
-    if (linux_boot) {
+    prom_offset = ram_size + vram_size;
+
+    snprintf(buf, sizeof(buf), "%s/%s", bios_dir, PROM_FILENAMEE);
+    ret = load_elf(buf, phys_ram_base + prom_offset);
+    if (ret < 0) {
 	snprintf(buf, sizeof(buf), "%s/%s", bios_dir, PROM_FILENAMEB);
-        ret = load_kernel(buf, 
-		       phys_ram_base + KERNEL_LOAD_ADDR);
+	ret = load_image(buf, phys_ram_base + prom_offset);
+    }
+    if (ret < 0) {
+	fprintf(stderr, "qemu: could not load prom '%s'\n", 
+		buf);
+	exit(1);
+    }
+    cpu_register_physical_memory(PROM_ADDR, (ret + TARGET_PAGE_SIZE) & TARGET_PAGE_MASK, 
+                                 prom_offset | IO_MEM_ROM);
+
+    if (linux_boot) {
+        ret = load_elf(kernel_filename, phys_ram_base + KERNEL_LOAD_ADDR);
+        if (ret < 0)
+	    ret = load_aout(kernel_filename, phys_ram_base + KERNEL_LOAD_ADDR);
+	if (ret < 0)
+	    ret = load_image(kernel_filename, phys_ram_base + KERNEL_LOAD_ADDR);
         if (ret < 0) {
             fprintf(stderr, "qemu: could not load kernel '%s'\n", 
-                    buf);
-            exit(1);
+                    kernel_filename);
+	    exit(1);
         }
     }
-    /* Setup a MMU entry for entire address space */
-    stl_raw(phys_ram_base + MMU_CONTEXT_TBL, (MMU_L1PTP >> 4) | 1);
-    stl_raw(phys_ram_base + MMU_L1PTP, (MMU_L2PTP >> 4) | 1);
-    stl_raw(phys_ram_base + MMU_L1PTP + (0x01 << 2), (MMU_L2PTP >> 4) | 1); // 01.. == 00..
-    stl_raw(phys_ram_base + MMU_L1PTP + (0xff << 2), (MMU_L2PTP >> 4) | 1); // ff.. == 00..
-    stl_raw(phys_ram_base + MMU_L1PTP + (0xf0 << 2), (MMU_L2PTP >> 4) | 1); // f0.. == 00..
-    /* 3 = U:RWX S:RWX */
-    stl_raw(phys_ram_base + MMU_L2PTP, (3 << PTE_ACCESS_SHIFT) | 2);
-    stl_raw(phys_ram_base + MMU_L2PTP, ((0x01 << PTE_PPN_SHIFT) >> 4 ) | (3 << PTE_ACCESS_SHIFT) | 2);
 }
diff --git a/hw/tcx.c b/hw/tcx.c
index 7f979946fc..ccac0bffa6 100644
--- a/hw/tcx.c
+++ b/hw/tcx.c
@@ -25,179 +25,254 @@
 
 #define MAXX 1024
 #define MAXY 768
+/*
+ * Proll uses only small part of display, we need to switch to full
+ * display when we get linux framebuffer console or X11 running. For
+ * now it's just slower and awkward.
+*/
+#if 1
 #define XSZ (8*80)
 #define YSZ (24*11)
 #define XOFF (MAXX-XSZ)
 #define YOFF (MAXY-YSZ)
+#else
+#define XSZ MAXX
+#define YSZ MAXY
+#define XOFF 0
+#define YOFF 0
+#endif
 
 typedef struct TCXState {
     uint32_t addr;
     DisplayState *ds;
     uint8_t *vram;
+    unsigned long vram_offset;
+    uint8_t r[256], g[256], b[256];
 } TCXState;
 
-static TCXState *ts;
-
-void vga_update_display()
+static void tcx_draw_line32(TCXState *s1, uint8_t *d, 
+			    const uint8_t *s, int width)
 {
-    dpy_update(ts->ds, 0, 0, XSZ, YSZ);
+    int x;
+    uint8_t val;
+
+    for(x = 0; x < width; x++) {
+	val = *s++;
+	*d++ = s1->r[val];
+	*d++ = s1->g[val];
+	*d++ = s1->b[val];
+	d++;
+    }
 }
 
-void vga_invalidate_display() {}
+static void tcx_draw_line24(TCXState *s1, uint8_t *d, 
+			    const uint8_t *s, int width)
+{
+    int x;
+    uint8_t val;
 
-static uint32_t tcx_mem_readb(void *opaque, target_phys_addr_t addr)
+    for(x = 0; x < width; x++) {
+	val = *s++;
+	*d++ = s1->r[val];
+	*d++ = s1->g[val];
+	*d++ = s1->b[val];
+    }
+}
+
+static void tcx_draw_line8(TCXState *s1, uint8_t *d, 
+			   const uint8_t *s, int width)
 {
-    TCXState *s = opaque;
-    uint32_t saddr;
-    unsigned int x, y;
-
-    saddr = addr - s->addr - YOFF*MAXX - XOFF;
-    y = saddr / MAXX;
-    x = saddr - y * MAXX;
-    if (x < XSZ && y < YSZ) {
-	return s->vram[y * XSZ + x];
+    int x;
+    uint8_t val;
+
+    for(x = 0; x < width; x++) {
+	val = *s++;
+	/* XXX translate between palettes? */
+	*d++ = val;
     }
-    return 0;
 }
 
-static uint32_t tcx_mem_readw(void *opaque, target_phys_addr_t addr)
+/* Fixed line length 1024 allows us to do nice tricks not possible on
+   VGA... */
+void tcx_update_display(void *opaque)
 {
-    uint32_t v;
-#ifdef TARGET_WORDS_BIGENDIAN
-    v = tcx_mem_readb(opaque, addr) << 8;
-    v |= tcx_mem_readb(opaque, addr + 1);
+    TCXState *ts = opaque;
+    uint32_t page;
+    int y, page_min, page_max, y_start, dd, ds;
+    uint8_t *d, *s;
+    void (*f)(TCXState *s1, uint8_t *d, const uint8_t *s, int width);
+
+    if (ts->ds->depth == 0)
+	return;
+#ifdef LD_BYPASS_OK
+    page = ts->vram_offset + YOFF*MAXX;
 #else
-    v = tcx_mem_readb(opaque, addr);
-    v |= tcx_mem_readb(opaque, addr + 1) << 8;
+    page = ts->addr + YOFF*MAXX;
 #endif
-    return v;
+    y_start = -1;
+    page_min = 0x7fffffff;
+    page_max = -1;
+    d = ts->ds->data;
+    s = ts->vram + YOFF*MAXX + XOFF;
+    dd = ts->ds->linesize;
+    ds = 1024;
+
+    switch (ts->ds->depth) {
+    case 32:
+	f = tcx_draw_line32;
+	break;
+    case 24:
+	f = tcx_draw_line24;
+	break;
+    default:
+    case 8:
+	f = tcx_draw_line8;
+	break;
+    case 0:
+	return;
+    }
+
+    for(y = 0; y < YSZ; y += 4, page += TARGET_PAGE_SIZE) {
+	if (cpu_physical_memory_is_dirty(page)) {
+	    if (y_start < 0)
+                y_start = y;
+            if (page < page_min)
+                page_min = page;
+            if (page > page_max)
+                page_max = page;
+	    f(ts, d, s, XSZ);
+	    d += dd;
+	    s += ds;
+	    f(ts, d, s, XSZ);
+	    d += dd;
+	    s += ds;
+	    f(ts, d, s, XSZ);
+	    d += dd;
+	    s += ds;
+	    f(ts, d, s, XSZ);
+	    d += dd;
+	    s += ds;
+	} else {
+            if (y_start >= 0) {
+                /* flush to display */
+                dpy_update(ts->ds, 0, y_start, 
+                           XSZ, y - y_start);
+                y_start = -1;
+            }
+	    d += dd * 4;
+	    s += ds * 4;
+	}
+    }
+    if (y_start >= 0) {
+	/* flush to display */
+	dpy_update(ts->ds, 0, y_start, 
+		   XSZ, y - y_start);
+    }
+    /* reset modified pages */
+    if (page_max != -1) {
+        cpu_physical_memory_reset_dirty(page_min, page_max + TARGET_PAGE_SIZE);
+    }
 }
 
-static uint32_t tcx_mem_readl(void *opaque, target_phys_addr_t addr)
+void tcx_invalidate_display(void *opaque)
 {
-    uint32_t v;
-#ifdef TARGET_WORDS_BIGENDIAN
-    v = tcx_mem_readb(opaque, addr) << 24;
-    v |= tcx_mem_readb(opaque, addr + 1) << 16;
-    v |= tcx_mem_readb(opaque, addr + 2) << 8;
-    v |= tcx_mem_readb(opaque, addr + 3);
+    TCXState *s = opaque;
+    int i;
+
+    for (i = 0; i < MAXX*MAXY; i += TARGET_PAGE_SIZE) {
+#ifdef LD_BYPASS_OK
+	cpu_physical_memory_set_dirty(s->vram_offset + i);
 #else
-    v = tcx_mem_readb(opaque, addr);
-    v |= tcx_mem_readb(opaque, addr + 1) << 8;
-    v |= tcx_mem_readb(opaque, addr + 2) << 16;
-    v |= tcx_mem_readb(opaque, addr + 3) << 24;
+	cpu_physical_memory_set_dirty(s->addr + i);
 #endif
-    return v;
+    }
 }
 
-static void tcx_mem_writeb(void *opaque, target_phys_addr_t addr, uint32_t val)
+static void tcx_save(QEMUFile *f, void *opaque)
 {
     TCXState *s = opaque;
-    uint32_t saddr;
-    unsigned int x, y;
-    char *sptr;
-
-    saddr = addr - s->addr - YOFF*MAXX - XOFF;
-    y = saddr / MAXX;
-    x = saddr - y * MAXX;
-    if (x < XSZ && y < YSZ) {
-	sptr = 	s->ds->data;
-	if (sptr) {
-	    if (s->ds->depth == 24 || s->ds->depth == 32) {
-		/* XXX need to do CLUT translation */
-		sptr[y * s->ds->linesize + x*4] = val & 0xff;
-		sptr[y * s->ds->linesize + x*4+1] = val & 0xff;
-		sptr[y * s->ds->linesize + x*4+2] = val & 0xff;
-	    }
-	    else if (s->ds->depth == 8) {
-		sptr[y * s->ds->linesize + x] = val & 0xff;
-	    }
-	}
-	cpu_physical_memory_set_dirty(addr);
-	s->vram[y * XSZ + x] = val & 0xff;
-    }
+    
+    qemu_put_be32s(f, (uint32_t *)&s->addr);
+    qemu_put_be32s(f, (uint32_t *)&s->vram);
+    qemu_put_buffer(f, s->r, 256);
+    qemu_put_buffer(f, s->g, 256);
+    qemu_put_buffer(f, s->b, 256);
 }
 
-static void tcx_mem_writew(void *opaque, target_phys_addr_t addr, uint32_t val)
+static int tcx_load(QEMUFile *f, void *opaque, int version_id)
 {
-#ifdef TARGET_WORDS_BIGENDIAN
-    tcx_mem_writeb(opaque, addr, (val >> 8) & 0xff);
-    tcx_mem_writeb(opaque, addr + 1, val & 0xff);
-#else
-    tcx_mem_writeb(opaque, addr, val & 0xff);
-    tcx_mem_writeb(opaque, addr + 1, (val >> 8) & 0xff);
-#endif
+    TCXState *s = opaque;
+    
+    if (version_id != 1)
+        return -EINVAL;
+
+    qemu_get_be32s(f, (uint32_t *)&s->addr);
+    qemu_get_be32s(f, (uint32_t *)&s->vram);
+    qemu_get_buffer(f, s->r, 256);
+    qemu_get_buffer(f, s->g, 256);
+    qemu_get_buffer(f, s->b, 256);
+    return 0;
 }
 
-static void tcx_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
+static void tcx_reset(void *opaque)
 {
-#ifdef TARGET_WORDS_BIGENDIAN
-    tcx_mem_writeb(opaque, addr, (val >> 24) & 0xff);
-    tcx_mem_writeb(opaque, addr + 1, (val >> 16) & 0xff);
-    tcx_mem_writeb(opaque, addr + 2, (val >> 8) & 0xff);
-    tcx_mem_writeb(opaque, addr + 3, val & 0xff);
-#else
-    tcx_mem_writeb(opaque, addr, val & 0xff);
-    tcx_mem_writeb(opaque, addr + 1, (val >> 8) & 0xff);
-    tcx_mem_writeb(opaque, addr + 2, (val >> 16) & 0xff);
-    tcx_mem_writeb(opaque, addr + 3, (val >> 24) & 0xff);
+    TCXState *s = opaque;
+
+    /* Initialize palette */
+    memset(s->r, 0, 256);
+    memset(s->g, 0, 256);
+    memset(s->b, 0, 256);
+    s->r[255] = s->g[255] = s->b[255] = 255;
+    memset(s->vram, 0, MAXX*MAXY);
+#ifdef LD_BYPASS_OK
+    cpu_physical_memory_reset_dirty(s->vram_offset, s->vram_offset + MAXX*MAXY - 1);
 #endif
 }
 
-static CPUReadMemoryFunc *tcx_mem_read[3] = {
-    tcx_mem_readb,
-    tcx_mem_readw,
-    tcx_mem_readl,
-};
-
-static CPUWriteMemoryFunc *tcx_mem_write[3] = {
-    tcx_mem_writeb,
-    tcx_mem_writew,
-    tcx_mem_writel,
-};
-
-void tcx_init(DisplayState *ds, uint32_t addr)
+void *tcx_init(DisplayState *ds, uint32_t addr, uint8_t *vram_base,
+	      unsigned long vram_offset, int vram_size)
 {
     TCXState *s;
-    int tcx_io_memory;
 
     s = qemu_mallocz(sizeof(TCXState));
     if (!s)
-        return;
+        return NULL;
     s->ds = ds;
     s->addr = addr;
-    ts = s;
-    tcx_io_memory = cpu_register_io_memory(0, tcx_mem_read, tcx_mem_write, s);
-    cpu_register_physical_memory(addr, 0x100000, 
-                                 tcx_io_memory);
-    s->vram = qemu_mallocz(XSZ*YSZ);
+    s->vram = vram_base;
+    s->vram_offset = vram_offset;
+
+    cpu_register_physical_memory(addr, vram_size, vram_offset);
+
+    register_savevm("tcx", addr, 1, tcx_save, tcx_load, s);
+    qemu_register_reset(tcx_reset, s);
+    tcx_reset(s);
     dpy_resize(s->ds, XSZ, YSZ);
+    return s;
 }
 
-void vga_screen_dump(const char *filename)
+void tcx_screen_dump(void *opaque, const char *filename)
 {
-    TCXState *s = ts;
+    TCXState *s = opaque;
     FILE *f;
-    uint8_t *d, *d1;
-    unsigned int v;
+    uint8_t *d, *d1, v;
     int y, x;
 
     f = fopen(filename, "wb");
     if (!f)
-        return -1;
-    fprintf(f, "P6\n%d %d\n%d\n",
-            XSZ, YSZ, 255);
-    d1 = s->vram;
+        return;
+    fprintf(f, "P6\n%d %d\n%d\n", XSZ, YSZ, 255);
+    d1 = s->vram + YOFF*MAXX + XOFF;
     for(y = 0; y < YSZ; y++) {
         d = d1;
         for(x = 0; x < XSZ; x++) {
             v = *d;
-            fputc((v) & 0xff, f);
-            fputc((v) & 0xff, f);
-            fputc((v) & 0xff, f);
+            fputc(s->r[v], f);
+            fputc(s->g[v], f);
+            fputc(s->b[v], f);
             d++;
         }
-        d1 += XSZ;
+        d1 += MAXX;
     }
     fclose(f);
     return;
diff --git a/hw/timer.c b/hw/timer.c
deleted file mode 100644
index e393fa36fd..0000000000
--- a/hw/timer.c
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * QEMU Sparc timer controller emulation
- * 
- * Copyright (c) 2003-2004 Fabrice Bellard
- * 
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-#include "vl.h"
-
-/*
- * Registers of hardware timer in sun4m.
- */
-struct sun4m_timer_percpu {
-	volatile unsigned int l14_timer_limit; /* Initial value is 0x009c4000 */
-	volatile unsigned int l14_cur_count;
-};
-
-struct sun4m_timer_global {
-        volatile unsigned int l10_timer_limit;
-        volatile unsigned int l10_cur_count;
-};
-
-typedef struct TIMERState {
-    uint32_t addr;
-    uint32_t timer_regs[2];
-    int irq;
-} TIMERState;
-
-static uint32_t timer_mem_readl(void *opaque, target_phys_addr_t addr)
-{
-    TIMERState *s = opaque;
-    uint32_t saddr;
-
-    saddr = (addr - s->addr) >> 2;
-    switch (saddr) {
-    default:
-	return s->timer_regs[saddr];
-	break;
-    }
-    return 0;
-}
-
-static void timer_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
-{
-    TIMERState *s = opaque;
-    uint32_t saddr;
-
-    saddr = (addr - s->addr) >> 2;
-    switch (saddr) {
-    default:
-	s->timer_regs[saddr] = val;
-	break;
-    }
-}
-
-static CPUReadMemoryFunc *timer_mem_read[3] = {
-    timer_mem_readl,
-    timer_mem_readl,
-    timer_mem_readl,
-};
-
-static CPUWriteMemoryFunc *timer_mem_write[3] = {
-    timer_mem_writel,
-    timer_mem_writel,
-    timer_mem_writel,
-};
-
-void timer_init(uint32_t addr, int irq)
-{
-    int timer_io_memory;
-    TIMERState *s;
-
-    s = qemu_mallocz(sizeof(TIMERState));
-    if (!s)
-        return;
-    s->addr = addr;
-    s->irq = irq;
-
-    timer_io_memory = cpu_register_io_memory(0, timer_mem_read, timer_mem_write, s);
-    cpu_register_physical_memory(addr, 2, timer_io_memory);
-}