qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Qemu-devel] [PATCH pic32 v3 09/16] pic32: add file mips_pic32mz.c


From: Serge Vakulenko
Subject: [Qemu-devel] [PATCH pic32 v3 09/16] pic32: add file mips_pic32mz.c
Date: Sun, 5 Jul 2015 23:14:57 -0700

This file implements a platform for Microchip PIC32MZ microcontroller,
with three boards (machine types) supported:

pic32mz-explorer16   PIC32MZ microcontroller on Microchip Explorer-16 board
pic32mz-meb2         PIC32MZ microcontroller on Microchip MEB-II board
pic32mz-wifire       PIC32MZ microcontroller on chipKIT WiFire board

Signed-off-by: Serge Vakulenko <address@hidden>
---
 hw/mips/mips_pic32mz.c | 2829 ++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 2829 insertions(+)
 create mode 100644 hw/mips/mips_pic32mz.c

diff --git a/hw/mips/mips_pic32mz.c b/hw/mips/mips_pic32mz.c
new file mode 100644
index 0000000..79f98b0
--- /dev/null
+++ b/hw/mips/mips_pic32mz.c
@@ -0,0 +1,2829 @@
+/*
+ * QEMU support for Microchip PIC32MZ microcontroller.
+ *
+ * Copyright (c) 2015 Serge Vakulenko
+ *
+ * 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 "hw/i386/pc.h"
+#include "hw/char/serial.h"
+#include "hw/mips/cpudevs.h"
+#include "sysemu/char.h"
+#include "hw/loader.h"
+#include "qemu/error-report.h"
+#include "hw/empty_slot.h"
+#include <termios.h>
+
+#include "pic32mz.h"
+#include "pic32_peripherals.h"
+
+/* Hardware addresses */
+#define PROGRAM_FLASH_START 0x1d000000
+#define BOOT_FLASH_START    0x1fc00000
+#define DATA_MEM_START      0x00000000
+#define IO_MEM_START        0x1f800000
+
+#define PROGRAM_FLASH_SIZE  (2*1024*1024)       /* 2 Mbytes */
+#define BOOT_FLASH_SIZE     (64*1024)           /* 64 kbytes */
+#define DATA_MEM_SIZE       (512*1024)          /* 512 kbytes */
+
+#define TYPE_MIPS_PIC32     "mips-pic32mz"
+
+/*
+ * Board variants.
+ */
+enum {
+    BOARD_WIFIRE,           /* chipKIT WiFire board */
+    BOARD_MEBII,            /* Microchip MEB-II board */
+    BOARD_EXPLORER16,       /* Microchip Explorer-16 board */
+};
+
+static const char *board_name[] = {
+    "chipKIT WiFire",
+    "Microchip MEB-II",
+    "Microchip Explorer16",
+};
+
+/*
+ * Pointers to Flash memory contents.
+ */
+static char *prog_ptr;
+static char *boot_ptr;
+
+#define BOOTMEM(addr) ((uint32_t *) boot_ptr)[(addr & 0xffff) >> 2]
+
+/*
+ * TODO: add option to enable tracing.
+ */
+#define TRACE   0
+
+static void update_irq_status(pic32_t *s)
+{
+    /* Assume no interrupts pending. */
+    int cause_ripl = 0;
+    int vector = 0;
+    CPUMIPSState *env = &s->cpu->env;
+    int current_ripl = (env->CP0_Cause >> (CP0Ca_IP + 2)) & 0x3f;
+
+    VALUE(INTSTAT) = 0;
+
+    if ((VALUE(IFS0) & VALUE(IEC0)) ||
+        (VALUE(IFS1) & VALUE(IEC1)) ||
+        (VALUE(IFS2) & VALUE(IEC2)) ||
+        (VALUE(IFS3) & VALUE(IEC3)) ||
+        (VALUE(IFS4) & VALUE(IEC4)) ||
+        (VALUE(IFS5) & VALUE(IEC5)))
+    {
+        /* Find the most prioritive pending interrupt,
+         * it's vector and level. */
+        int irq;
+        for (irq = 0; irq <= PIC32_IRQ_LAST; irq++) {
+            int n = irq >> 5;
+
+            if (((VALUE(IFS(n)) & VALUE(IEC(n))) >> (irq & 31)) & 1) {
+                /* Interrupt is pending. */
+                int level = VALUE(IPC(irq >> 2));
+                level >>= 2 + (irq & 3) * 8;
+                level &= 7;
+                if (level > cause_ripl) {
+                    vector = irq;
+                    cause_ripl = level;
+                }
+            }
+        }
+        VALUE(INTSTAT) = vector | (cause_ripl << 8);
+    }
+
+    if (cause_ripl == current_ripl) {
+        return;
+    }
+
+    if (TRACE) {
+        fprintf(qemu_logfile, "--- Priority level Cause.RIPL = %u\n",
+            cause_ripl);
+    }
+
+    /*
+     * Modify Cause.RIPL field and take EIC interrupt.
+     */
+    env->CP0_Cause &= ~(0x3f << (CP0Ca_IP + 2));
+    env->CP0_Cause |= cause_ripl << (CP0Ca_IP + 2);
+    cpu_interrupt(CPU(s->cpu), CPU_INTERRUPT_HARD);
+}
+
+/*
+ * Set interrupt flag status
+ */
+static void irq_raise(pic32_t *s, int irq)
+{
+    if (VALUE(IFS(irq >> 5)) & (1 << (irq & 31))) {
+        return;
+    }
+
+    VALUE(IFS(irq >> 5)) |= 1 << (irq & 31);
+    update_irq_status(s);
+}
+
+/*
+ * Clear interrupt flag status
+ */
+static void irq_clear(pic32_t *s, int irq)
+{
+    if (!(VALUE(IFS(irq >> 5)) & (1 << (irq & 31)))) {
+        return;
+    }
+
+    VALUE(IFS(irq >> 5)) &= ~(1 << (irq & 31));
+    update_irq_status(s);
+}
+
+/*
+ * Timer interrupt or soft interrupt.
+ */
+static void pic32_irq_request(void *opaque, int num, int raise)
+{
+    pic32_t *s = opaque;
+    CPUMIPSState *env = &s->cpu->env;
+
+    if (raise) {
+        if (TRACE) {
+            if (num == 0)
+                fprintf(qemu_logfile, "--- %08x: Timer interrupt\n",
+                    env->active_tc.PC);
+            else
+                fprintf(qemu_logfile, "--- %08x: Soft interrupt %u\n",
+                    env->active_tc.PC, num-1);
+        }
+        irq_raise(s, num);
+    } else {
+        if (TRACE) {
+            if (num == 0)
+                fprintf(qemu_logfile, "--- %08x: Clear timer interrupt\n",
+                    env->active_tc.PC);
+            else
+                fprintf(qemu_logfile, "--- %08x: Clear soft interrupt %u\n",
+                    env->active_tc.PC, num-1);
+        }
+        irq_clear(s, num);
+    }
+}
+
+/*
+ * Perform an assign/clear/set/invert operation.
+ */
+static inline unsigned write_op(int a, int b, int op)
+{
+    switch (op & 0xc) {
+    case 0x0:                   /* Assign */
+        a = b;
+        break;
+    case 0x4:                   /* Clear */
+        a &= ~b;
+        break;
+    case 0x8:                   /* Set */
+        a |= b;
+        break;
+    case 0xc:                   /* Invert */
+        a ^= b;
+        break;
+    }
+    return a;
+}
+
+static void io_reset(pic32_t *s)
+{
+    int i;
+
+    /*
+     * Prefetch controller.
+     */
+    VALUE(PRECON) = 0x00000007;
+
+    /*
+     * System controller.
+     */
+    s->syskey_unlock = 0;
+    VALUE(CFGCON)   = PIC32_CFGCON_ECC_DISWR | PIC32_CFGCON_TDOEN;
+    VALUE(SYSKEY)   = 0;
+    VALUE(RCON)     = 0;
+    VALUE(RSWRST)   = 0;
+    VALUE(OSCTUN)   = 0;
+    VALUE(SPLLCON)  = 0x01310201;
+    VALUE(PB1DIV)   = 0x00008801;
+    VALUE(PB2DIV)   = 0x00008801;
+    VALUE(PB3DIV)   = 0x00008801;
+    VALUE(PB4DIV)   = 0x00008801;
+    VALUE(PB5DIV)   = 0x00008801;
+    VALUE(PB7DIV)   = 0x00008800;
+    VALUE(PB8DIV)   = 0x00008801;
+
+    /*
+     * General purpose IO signals.
+     * All pins are inputs, high, open drains and pullups disabled.
+     * No interrupts on change.
+     */
+    VALUE(ANSELA)   = 0xFFFF;           /* Port A: analog select */
+    VALUE(TRISA)    = 0xFFFF;           /* Port A: mask of inputs */
+    VALUE(PORTA)    = 0xFFCF;           /* Port A: read inputs, write outputs 
*/
+    VALUE(LATA)     = 0xFFFF;           /* Port A: read/write outputs */
+    VALUE(ODCA)     = 0;                /* Port A: open drain configuration */
+    VALUE(CNPUA)    = 0;                /* Input pin pull-up */
+    VALUE(CNPDA)    = 0;                /* Input pin pull-down */
+    VALUE(CNCONA)   = 0;                /* Interrupt-on-change control */
+    VALUE(CNENA)    = 0;                /* Input change interrupt enable */
+    VALUE(CNSTATA)  = 0;                /* Input change status */
+
+    VALUE(ANSELB)   = 0xFFFF;           /* Port B: analog select */
+    VALUE(TRISB)    = 0xFFFF;           /* Port B: mask of inputs */
+    VALUE(PORTB)    = 0xFFFF;           /* Port B: read inputs, write outputs 
*/
+    if (s->board_type == BOARD_MEBII) {
+        VALUE(PORTB) ^= 1 << 12;        /* Disable pin RB12 - button 1 */
+    }
+    VALUE(LATB)     = 0xFFFF;           /* Port B: read/write outputs */
+    VALUE(ODCB)     = 0;                /* Port B: open drain configuration */
+    VALUE(CNPUB)    = 0;                /* Input pin pull-up */
+    VALUE(CNPDB)    = 0;                /* Input pin pull-down */
+    VALUE(CNCONB)   = 0;                /* Interrupt-on-change control */
+    VALUE(CNENB)    = 0;                /* Input change interrupt enable */
+    VALUE(CNSTATB)  = 0;                /* Input change status */
+
+    VALUE(ANSELC)   = 0xFFFF;           /* Port C: analog select */
+    VALUE(TRISC)    = 0xFFFF;           /* Port C: mask of inputs */
+    VALUE(PORTC)    = 0xFFFF;           /* Port C: read inputs, write outputs 
*/
+    VALUE(LATC)     = 0xFFFF;           /* Port C: read/write outputs */
+    if (s->board_type == BOARD_WIFIRE) {
+        /* Disable latc[15] for the chipKIT bootloader. */
+        VALUE(LATC) ^= 0x1000;
+    }
+    VALUE(ODCC)     = 0;                /* Port C: open drain configuration */
+    VALUE(CNPUC)    = 0;                /* Input pin pull-up */
+    VALUE(CNPDC)    = 0;                /* Input pin pull-down */
+    VALUE(CNCONC)   = 0;                /* Interrupt-on-change control */
+    VALUE(CNENC)    = 0;                /* Input change interrupt enable */
+    VALUE(CNSTATC)  = 0;                /* Input change status */
+
+    VALUE(ANSELD)   = 0xFFFF;           /* Port D: analog select */
+    VALUE(TRISD)    = 0xFFFF;           /* Port D: mask of inputs */
+    VALUE(PORTD)    = 0xFFFF;           /* Port D: read inputs, write outputs 
*/
+    VALUE(LATD)     = 0xFFFF;           /* Port D: read/write outputs */
+    VALUE(ODCD)     = 0;                /* Port D: open drain configuration */
+    VALUE(CNPUD)    = 0;                /* Input pin pull-up */
+    VALUE(CNPDD)    = 0;                /* Input pin pull-down */
+    VALUE(CNCOND)   = 0;                /* Interrupt-on-change control */
+    VALUE(CNEND)    = 0;                /* Input change interrupt enable */
+    VALUE(CNSTATD)  = 0;                /* Input change status */
+
+    VALUE(ANSELE)   = 0xFFFF;           /* Port E: analog select */
+    VALUE(TRISE)    = 0xFFFF;           /* Port E: mask of inputs */
+    VALUE(PORTE)    = 0xFFFF;           /* Port E: read inputs, write outputs 
*/
+    VALUE(LATE)     = 0xFFFF;           /* Port E: read/write outputs */
+    VALUE(ODCE)     = 0;                /* Port E: open drain configuration */
+    VALUE(CNPUE)    = 0;                /* Input pin pull-up */
+    VALUE(CNPDE)    = 0;                /* Input pin pull-down */
+    VALUE(CNCONE)   = 0;                /* Interrupt-on-change control */
+    VALUE(CNENE)    = 0;                /* Input change interrupt enable */
+    VALUE(CNSTATE)  = 0;                /* Input change status */
+
+    VALUE(ANSELF)   = 0xFFFF;           /* Port F: analog select */
+    VALUE(TRISF)    = 0xFFFF;           /* Port F: mask of inputs */
+    VALUE(PORTF)    = 0xFFFF;           /* Port F: read inputs, write outputs 
*/
+    VALUE(LATF)     = 0xFFFF;           /* Port F: read/write outputs */
+    VALUE(ODCF)     = 0;                /* Port F: open drain configuration */
+    VALUE(CNPUF)    = 0;                /* Input pin pull-up */
+    VALUE(CNPDF)    = 0;                /* Input pin pull-down */
+    VALUE(CNCONF)   = 0;                /* Interrupt-on-change control */
+    VALUE(CNENF)    = 0;                /* Input change interrupt enable */
+    VALUE(CNSTATF)  = 0;                /* Input change status */
+
+    VALUE(ANSELG)   = 0xFFFF;           /* Port G: analog select */
+    VALUE(TRISG)    = 0xFFFF;           /* Port G: mask of inputs */
+    VALUE(PORTG)    = 0xFFFF;           /* Port G: read inputs, write outputs 
*/
+    VALUE(LATG)     = 0xFFFF;           /* Port G: read/write outputs */
+    VALUE(ODCG)     = 0;                /* Port G: open drain configuration */
+    VALUE(CNPUG)    = 0;                /* Input pin pull-up */
+    VALUE(CNPDG)    = 0;                /* Input pin pull-down */
+    VALUE(CNCONG)   = 0;                /* Interrupt-on-change control */
+    VALUE(CNENG)    = 0;                /* Input change interrupt enable */
+    VALUE(CNSTATG)  = 0;                /* Input change status */
+
+    VALUE(ANSELH)   = 0xFFFF;           /* Port H: analog select */
+    VALUE(TRISH)    = 0xFFFF;           /* Port H: mask of inputs */
+    VALUE(PORTH)    = 0xFFFF;           /* Port H: read inputs, write outputs 
*/
+    VALUE(LATH)     = 0xFFFF;           /* Port H: read/write outputs */
+    VALUE(ODCH)     = 0;                /* Port H: open drain configuration */
+    VALUE(CNPUH)    = 0;                /* Input pin pull-up */
+    VALUE(CNPDH)    = 0;                /* Input pin pull-down */
+    VALUE(CNCONH)   = 0;                /* Interrupt-on-change control */
+    VALUE(CNENH)    = 0;                /* Input change interrupt enable */
+    VALUE(CNSTATH)  = 0;                /* Input change status */
+
+    VALUE(ANSELJ)   = 0xFFFF;           /* Port J: analog select */
+    VALUE(TRISJ)    = 0xFFFF;           /* Port J: mask of inputs */
+    VALUE(PORTJ)    = 0xFFFF;           /* Port J: read inputs, write outputs 
*/
+    VALUE(LATJ)     = 0xFFFF;           /* Port J: read/write outputs */
+    VALUE(ODCJ)     = 0;                /* Port J: open drain configuration */
+    VALUE(CNPUJ)    = 0;                /* Input pin pull-up */
+    VALUE(CNPDJ)    = 0;                /* Input pin pull-down */
+    VALUE(CNCONJ)   = 0;                /* Interrupt-on-change control */
+    VALUE(CNENJ)    = 0;                /* Input change interrupt enable */
+    VALUE(CNSTATJ)  = 0;                /* Input change status */
+
+    VALUE(TRISK)    = 0xFFFF;           /* Port K: mask of inputs */
+    VALUE(PORTK)    = 0xFFFF;           /* Port K: read inputs, write outputs 
*/
+    VALUE(LATK)     = 0xFFFF;           /* Port K: read/write outputs */
+    VALUE(ODCK)     = 0;                /* Port K: open drain configuration */
+    VALUE(CNPUK)    = 0;                /* Input pin pull-up */
+    VALUE(CNPDK)    = 0;                /* Input pin pull-down */
+    VALUE(CNCONK)   = 0;                /* Interrupt-on-change control */
+    VALUE(CNENK)    = 0;                /* Input change interrupt enable */
+    VALUE(CNSTATK)  = 0;                /* Input change status */
+
+    /*
+     * Reset UARTs.
+     */
+    VALUE(U1MODE)  = 0;
+    VALUE(U1STA)   = PIC32_USTA_RIDLE | PIC32_USTA_TRMT;
+    VALUE(U1TXREG) = 0;
+    VALUE(U1RXREG) = 0;
+    VALUE(U1BRG)   = 0;
+    VALUE(U2MODE)  = 0;
+    VALUE(U2STA)   = PIC32_USTA_RIDLE | PIC32_USTA_TRMT;
+    VALUE(U2TXREG) = 0;
+    VALUE(U2RXREG) = 0;
+    VALUE(U2BRG)   = 0;
+    VALUE(U3MODE)  = 0;
+    VALUE(U3STA)   = PIC32_USTA_RIDLE | PIC32_USTA_TRMT;
+    VALUE(U3TXREG) = 0;
+    VALUE(U3RXREG) = 0;
+    VALUE(U3BRG)   = 0;
+    VALUE(U4MODE)  = 0;
+    VALUE(U4STA)   = PIC32_USTA_RIDLE | PIC32_USTA_TRMT;
+    VALUE(U4TXREG) = 0;
+    VALUE(U4RXREG) = 0;
+    VALUE(U4BRG)   = 0;
+    VALUE(U5MODE)  = 0;
+    VALUE(U5STA)   = PIC32_USTA_RIDLE | PIC32_USTA_TRMT;
+    VALUE(U5TXREG) = 0;
+    VALUE(U5RXREG) = 0;
+    VALUE(U5BRG)   = 0;
+    VALUE(U6MODE)  = 0;
+    VALUE(U6STA)   = PIC32_USTA_RIDLE | PIC32_USTA_TRMT;
+    VALUE(U6TXREG) = 0;
+    VALUE(U6RXREG) = 0;
+    VALUE(U6BRG)   = 0;
+
+    /*
+     * Reset SPI.
+     */
+    VALUE(SPI1CON)  = 0;
+    VALUE(SPI1STAT) = PIC32_SPISTAT_SPITBE;     /* Transmit buffer is empty */
+    VALUE(SPI1BRG)  = 0;
+
+    VALUE(SPI2CON)  = 0;
+    VALUE(SPI2STAT) = PIC32_SPISTAT_SPITBE;     /* Transmit buffer is empty */
+    VALUE(SPI2BRG)  = 0;
+
+    VALUE(SPI3CON)  = 0;
+    VALUE(SPI3STAT) = PIC32_SPISTAT_SPITBE;     /* Transmit buffer is empty */
+    VALUE(SPI3BRG)  = 0;
+
+    VALUE(SPI4CON)  = 0;
+    VALUE(SPI4STAT) = PIC32_SPISTAT_SPITBE;     /* Transmit buffer is empty */
+    VALUE(SPI4BRG)  = 0;
+
+    VALUE(SPI5CON)  = 0;
+    VALUE(SPI5STAT) = PIC32_SPISTAT_SPITBE;     /* Transmit buffer is empty */
+    VALUE(SPI5BRG)  = 0;
+
+    VALUE(SPI6CON)  = 0;
+    VALUE(SPI6STAT) = PIC32_SPISTAT_SPITBE;     /* Transmit buffer is empty */
+    VALUE(SPI6BRG)  = 0;
+
+    VALUE(SPI1CON2) = 0;
+    VALUE(SPI2CON2) = 0;
+    VALUE(SPI3CON2) = 0;
+    VALUE(SPI4CON2) = 0;
+    VALUE(SPI5CON2) = 0;
+    VALUE(SPI6CON2) = 0;
+
+    for (i = 0; i < NUM_SPI; i++) {
+        s->spi[i].rfifo = 0;
+        s->spi[i].wfifo = 0;
+    }
+
+    /*
+     * Reset timers.
+     */
+    VALUE(T1CON)    = 0;
+    VALUE(TMR1)     = 0;
+    VALUE(PR1)      = 0xffff;
+    VALUE(T2CON)    = 0;
+    VALUE(TMR2)     = 0;
+    VALUE(PR2)      = 0xffff;
+    VALUE(T3CON)    = 0;
+    VALUE(TMR3)     = 0;
+    VALUE(PR3)      = 0xffff;
+    VALUE(T4CON)    = 0;
+    VALUE(TMR4)     = 0;
+    VALUE(PR4)      = 0xffff;
+    VALUE(T5CON)    = 0;
+    VALUE(TMR5)     = 0;
+    VALUE(PR5)      = 0xffff;
+    VALUE(T6CON)    = 0;
+    VALUE(TMR6)     = 0;
+    VALUE(PR6)      = 0xffff;
+    VALUE(T7CON)    = 0;
+    VALUE(TMR7)     = 0;
+    VALUE(PR7)      = 0xffff;
+    VALUE(T8CON)    = 0;
+    VALUE(TMR8)     = 0;
+    VALUE(PR8)      = 0xffff;
+    VALUE(T9CON)    = 0;
+    VALUE(TMR9)     = 0;
+    VALUE(PR9)      = 0xffff;
+
+    /*
+     * Reset Ethernet.
+     */
+    VALUE(ETHCON1)      = 0;        /* Control 1 */
+    VALUE(ETHCON2)      = 0;        /* Control 2: RX data buffer size */
+    VALUE(ETHTXST)      = 0;        /* Tx descriptor start address */
+    VALUE(ETHRXST)      = 0;        /* Rx descriptor start address */
+    VALUE(ETHHT0)       = 0;        /* Hash tasble 0 */
+    VALUE(ETHHT1)       = 0;        /* Hash tasble 1 */
+    VALUE(ETHPMM0)      = 0;        /* Pattern match mask 0 */
+    VALUE(ETHPMM1)      = 0;        /* Pattern match mask 1 */
+    VALUE(ETHPMCS)      = 0;        /* Pattern match checksum */
+    VALUE(ETHPMO)       = 0;        /* Pattern match offset */
+    VALUE(ETHRXFC)      = 0;        /* Receive filter configuration */
+    VALUE(ETHRXWM)      = 0;        /* Receive watermarks */
+    VALUE(ETHIEN)       = 0;        /* Interrupt enable */
+    VALUE(ETHIRQ)       = 0;        /* Interrupt request */
+    VALUE(ETHSTAT)      = 0;        /* Status */
+    VALUE(ETHRXOVFLOW)  = 0;        /* Receive overflow statistics */
+    VALUE(ETHFRMTXOK)   = 0;        /* Frames transmitted OK statistics */
+    VALUE(ETHSCOLFRM)   = 0;        /* Single collision frames statistics */
+    VALUE(ETHMCOLFRM)   = 0;        /* Multiple collision frames statistics */
+    VALUE(ETHFRMRXOK)   = 0;        /* Frames received OK statistics */
+    VALUE(ETHFCSERR)    = 0;        /* Frame check sequence error statistics */
+    VALUE(ETHALGNERR)   = 0;        /* Alignment errors statistics */
+    VALUE(EMAC1CFG1)    = 0x800d;   /* MAC configuration 1 */
+    VALUE(EMAC1CFG2)    = 0x4082;   /* MAC configuration 2 */
+    VALUE(EMAC1IPGT)    = 0x0012;   /* MAC back-to-back interpacket gap */
+    VALUE(EMAC1IPGR)    = 0x0c12;   /* MAC non-back-to-back interpacket gap */
+    VALUE(EMAC1CLRT)    = 0x370f;   /* MAC collision window/retry limit */
+    VALUE(EMAC1MAXF)    = 0x05ee;   /* MAC maximum frame length */
+    VALUE(EMAC1SUPP)    = 0x1000;   /* MAC PHY support */
+    VALUE(EMAC1TEST)    = 0;        /* MAC test */
+    VALUE(EMAC1MCFG)    = 0x0020;   /* MII configuration */
+    VALUE(EMAC1MCMD)    = 0;        /* MII command */
+    VALUE(EMAC1MADR)    = 0x0100;   /* MII address */
+    VALUE(EMAC1MWTD)    = 0;        /* MII write data */
+    VALUE(EMAC1MRDD)    = 0;        /* MII read data */
+    VALUE(EMAC1MIND)    = 0;        /* MII indicators */
+    VALUE(EMAC1SA0)     = 0x79c1;   /* MAC station address 0 */
+    VALUE(EMAC1SA1)     = 0xcbc0;   /* MAC station address 1 */
+    VALUE(EMAC1SA2)     = 0x1e00;   /* MAC station address 2 */
+
+    /*
+     * Reset USB.
+     */
+    VALUE(USBCSR0)      = 0x2000;
+    VALUE(USBCSR1)      = 0x00ff0000;
+    VALUE(USBCSR2)      = 0x060000fe;
+    VALUE(USBCSR3)      = 0;
+    VALUE(USBIENCSR0)   = 0;
+    VALUE(USBIENCSR1)   = 0;
+    VALUE(USBIENCSR2)   = 0;
+    VALUE(USBIENCSR3)   = 0;
+    VALUE(USBFIFO0)     = 0;
+    VALUE(USBFIFO1)     = 0;
+    VALUE(USBFIFO2)     = 0;
+    VALUE(USBFIFO3)     = 0;
+    VALUE(USBFIFO4)     = 0;
+    VALUE(USBFIFO5)     = 0;
+    VALUE(USBFIFO6)     = 0;
+    VALUE(USBFIFO7)     = 0;
+    VALUE(USBOTG)       = 0x0080;
+    VALUE(USBFIFOA)     = 0;
+    VALUE(USBHWVER)     = 0x0800;
+    VALUE(USBINFO)      = 0x3C5C8C77;
+    VALUE(USBEOFRST)    = 0x00727780;
+    VALUE(USBE0TXA)     = 0;
+    VALUE(USBE0RXA)     = 0;
+    VALUE(USBE1TXA)     = 0;
+    VALUE(USBE1RXA)     = 0;
+    VALUE(USBE2TXA)     = 0;
+    VALUE(USBE2RXA)     = 0;
+    VALUE(USBE3TXA)     = 0;
+    VALUE(USBE3RXA)     = 0;
+    VALUE(USBE4TXA)     = 0;
+    VALUE(USBE4RXA)     = 0;
+    VALUE(USBE5TXA)     = 0;
+    VALUE(USBE5RXA)     = 0;
+    VALUE(USBE6TXA)     = 0;
+    VALUE(USBE6RXA)     = 0;
+    VALUE(USBE7TXA)     = 0;
+    VALUE(USBE7RXA)     = 0;
+    VALUE(USBE0CSR0)    = 0;
+    VALUE(USBE0CSR2)    = 0;
+    VALUE(USBE0CSR3)    = 0;
+    VALUE(USBE1CSR0)    = 0;
+    VALUE(USBE1CSR1)    = 0;
+    VALUE(USBE1CSR2)    = 0;
+    VALUE(USBE1CSR3)    = 0;
+    VALUE(USBE2CSR0)    = 0;
+    VALUE(USBE2CSR1)    = 0;
+    VALUE(USBE2CSR2)    = 0;
+    VALUE(USBE2CSR3)    = 0;
+    VALUE(USBE3CSR0)    = 0;
+    VALUE(USBE3CSR1)    = 0;
+    VALUE(USBE3CSR2)    = 0;
+    VALUE(USBE3CSR3)    = 0;
+    VALUE(USBE4CSR0)    = 0;
+    VALUE(USBE4CSR1)    = 0;
+    VALUE(USBE4CSR2)    = 0;
+    VALUE(USBE4CSR3)    = 0;
+    VALUE(USBE5CSR0)    = 0;
+    VALUE(USBE5CSR1)    = 0;
+    VALUE(USBE5CSR2)    = 0;
+    VALUE(USBE5CSR3)    = 0;
+    VALUE(USBE6CSR0)    = 0;
+    VALUE(USBE6CSR1)    = 0;
+    VALUE(USBE6CSR2)    = 0;
+    VALUE(USBE6CSR3)    = 0;
+    VALUE(USBE7CSR0)    = 0;
+    VALUE(USBE7CSR1)    = 0;
+    VALUE(USBE7CSR2)    = 0;
+    VALUE(USBE7CSR3)    = 0;
+    VALUE(USBDMAINT)    = 0;
+    VALUE(USBDMA1C)     = 0;
+    VALUE(USBDMA1A)     = 0;
+    VALUE(USBDMA1N)     = 0;
+    VALUE(USBDMA2C)     = 0;
+    VALUE(USBDMA2A)     = 0;
+    VALUE(USBDMA2N)     = 0;
+    VALUE(USBDMA3C)     = 0;
+    VALUE(USBDMA3A)     = 0;
+    VALUE(USBDMA3N)     = 0;
+    VALUE(USBDMA4C)     = 0;
+    VALUE(USBDMA4A)     = 0;
+    VALUE(USBDMA4N)     = 0;
+    VALUE(USBDMA5C)     = 0;
+    VALUE(USBDMA5A)     = 0;
+    VALUE(USBDMA5N)     = 0;
+    VALUE(USBDMA6C)     = 0;
+    VALUE(USBDMA6A)     = 0;
+    VALUE(USBDMA6N)     = 0;
+    VALUE(USBDMA7C)     = 0;
+    VALUE(USBDMA7A)     = 0;
+    VALUE(USBDMA7N)     = 0;
+    VALUE(USBDMA8C)     = 0;
+    VALUE(USBDMA8A)     = 0;
+    VALUE(USBDMA8N)     = 0;
+    VALUE(USBE1RPC)     = 0;
+    VALUE(USBE2RPC)     = 0;
+    VALUE(USBE3RPC)     = 0;
+    VALUE(USBE4RPC)     = 0;
+    VALUE(USBE5RPC)     = 0;
+    VALUE(USBE6RPC)     = 0;
+    VALUE(USBE7RPC)     = 0;
+    VALUE(USBDPBFD)     = 0;
+    VALUE(USBTMCON1)    = 0x05E64074;
+    VALUE(USBTMCON2)    = 0;
+    VALUE(USBLPMR1)     = 0;
+    VALUE(USBLMPR2)     = 0;
+}
+
+static unsigned io_read32(pic32_t *s, unsigned offset, const char **namep)
+{
+    unsigned *bufp = &VALUE(offset);
+
+    switch (offset) {
+    /*-------------------------------------------------------------------------
+     * Interrupt controller registers.
+     */
+    STORAGE(INTCON); break;     /* Interrupt Control */
+    STORAGE(INTSTAT); break;    /* Interrupt Status */
+    STORAGE(IFS0); break;       /* IFS(0..2) - Interrupt Flag Status */
+    STORAGE(IFS1); break;
+    STORAGE(IFS2); break;
+    STORAGE(IFS3); break;
+    STORAGE(IFS4); break;
+    STORAGE(IFS5); break;
+    STORAGE(IEC0); break;       /* IEC(0..2) - Interrupt Enable Control */
+    STORAGE(IEC1); break;
+    STORAGE(IEC2); break;
+    STORAGE(IEC3); break;
+    STORAGE(IEC4); break;
+    STORAGE(IEC5); break;
+
+    /* IPC(0..11) - Interrupt Priority Control */
+    STORAGE(IPC0); break;       STORAGE(IPC1); break;
+    STORAGE(IPC2); break;       STORAGE(IPC3); break;
+    STORAGE(IPC4); break;       STORAGE(IPC5); break;
+    STORAGE(IPC6); break;       STORAGE(IPC7); break;
+    STORAGE(IPC8); break;       STORAGE(IPC9); break;
+    STORAGE(IPC10); break;      STORAGE(IPC11); break;
+    STORAGE(IPC12); break;      STORAGE(IPC13); break;
+    STORAGE(IPC14); break;      STORAGE(IPC15); break;
+    STORAGE(IPC16); break;      STORAGE(IPC17); break;
+    STORAGE(IPC18); break;      STORAGE(IPC19); break;
+    STORAGE(IPC20); break;      STORAGE(IPC21); break;
+    STORAGE(IPC22); break;      STORAGE(IPC23); break;
+    STORAGE(IPC24); break;      STORAGE(IPC25); break;
+    STORAGE(IPC26); break;      STORAGE(IPC27); break;
+    STORAGE(IPC28); break;      STORAGE(IPC29); break;
+    STORAGE(IPC30); break;      STORAGE(IPC31); break;
+    STORAGE(IPC32); break;      STORAGE(IPC33); break;
+    STORAGE(IPC34); break;      STORAGE(IPC35); break;
+    STORAGE(IPC36); break;      STORAGE(IPC37); break;
+    STORAGE(IPC38); break;      STORAGE(IPC39); break;
+    STORAGE(IPC40); break;      STORAGE(IPC41); break;
+    STORAGE(IPC42); break;      STORAGE(IPC43); break;
+    STORAGE(IPC44); break;      STORAGE(IPC45); break;
+    STORAGE(IPC46); break;      STORAGE(IPC47); break;
+
+    /* OFF000..OFF190 - Interrupt Vector Address Offset */
+    STORAGE(OFF(0)); break;     STORAGE(OFF(1)); break;
+    STORAGE(OFF(2)); break;     STORAGE(OFF(3)); break;
+    STORAGE(OFF(4)); break;     STORAGE(OFF(5)); break;
+    STORAGE(OFF(6)); break;     STORAGE(OFF(7)); break;
+    STORAGE(OFF(8)); break;     STORAGE(OFF(9)); break;
+    STORAGE(OFF(10)); break;    STORAGE(OFF(11)); break;
+    STORAGE(OFF(12)); break;    STORAGE(OFF(13)); break;
+    STORAGE(OFF(14)); break;    STORAGE(OFF(15)); break;
+    STORAGE(OFF(16)); break;    STORAGE(OFF(17)); break;
+    STORAGE(OFF(18)); break;    STORAGE(OFF(19)); break;
+    STORAGE(OFF(20)); break;    STORAGE(OFF(21)); break;
+    STORAGE(OFF(22)); break;    STORAGE(OFF(23)); break;
+    STORAGE(OFF(24)); break;    STORAGE(OFF(25)); break;
+    STORAGE(OFF(26)); break;    STORAGE(OFF(27)); break;
+    STORAGE(OFF(28)); break;    STORAGE(OFF(29)); break;
+    STORAGE(OFF(30)); break;    STORAGE(OFF(31)); break;
+    STORAGE(OFF(32)); break;    STORAGE(OFF(33)); break;
+    STORAGE(OFF(34)); break;    STORAGE(OFF(35)); break;
+    STORAGE(OFF(36)); break;    STORAGE(OFF(37)); break;
+    STORAGE(OFF(38)); break;    STORAGE(OFF(39)); break;
+    STORAGE(OFF(40)); break;    STORAGE(OFF(41)); break;
+    STORAGE(OFF(42)); break;    STORAGE(OFF(43)); break;
+    STORAGE(OFF(44)); break;    STORAGE(OFF(45)); break;
+    STORAGE(OFF(46)); break;    STORAGE(OFF(47)); break;
+    STORAGE(OFF(48)); break;    STORAGE(OFF(49)); break;
+    STORAGE(OFF(50)); break;    STORAGE(OFF(51)); break;
+    STORAGE(OFF(52)); break;    STORAGE(OFF(53)); break;
+    STORAGE(OFF(54)); break;    STORAGE(OFF(55)); break;
+    STORAGE(OFF(56)); break;    STORAGE(OFF(57)); break;
+    STORAGE(OFF(58)); break;    STORAGE(OFF(59)); break;
+    STORAGE(OFF(60)); break;    STORAGE(OFF(61)); break;
+    STORAGE(OFF(62)); break;    STORAGE(OFF(63)); break;
+    STORAGE(OFF(64)); break;    STORAGE(OFF(65)); break;
+    STORAGE(OFF(66)); break;    STORAGE(OFF(67)); break;
+    STORAGE(OFF(68)); break;    STORAGE(OFF(69)); break;
+    STORAGE(OFF(70)); break;    STORAGE(OFF(71)); break;
+    STORAGE(OFF(72)); break;    STORAGE(OFF(73)); break;
+    STORAGE(OFF(74)); break;    STORAGE(OFF(75)); break;
+    STORAGE(OFF(76)); break;    STORAGE(OFF(77)); break;
+    STORAGE(OFF(78)); break;    STORAGE(OFF(79)); break;
+    STORAGE(OFF(80)); break;    STORAGE(OFF(81)); break;
+    STORAGE(OFF(82)); break;    STORAGE(OFF(83)); break;
+    STORAGE(OFF(84)); break;    STORAGE(OFF(85)); break;
+    STORAGE(OFF(86)); break;    STORAGE(OFF(87)); break;
+    STORAGE(OFF(88)); break;    STORAGE(OFF(89)); break;
+    STORAGE(OFF(90)); break;    STORAGE(OFF(91)); break;
+    STORAGE(OFF(92)); break;    STORAGE(OFF(93)); break;
+    STORAGE(OFF(94)); break;    STORAGE(OFF(95)); break;
+    STORAGE(OFF(96)); break;    STORAGE(OFF(97)); break;
+    STORAGE(OFF(98)); break;    STORAGE(OFF(99)); break;
+    STORAGE(OFF(100)); break;   STORAGE(OFF(101)); break;
+    STORAGE(OFF(102)); break;   STORAGE(OFF(103)); break;
+    STORAGE(OFF(104)); break;   STORAGE(OFF(105)); break;
+    STORAGE(OFF(106)); break;   STORAGE(OFF(107)); break;
+    STORAGE(OFF(108)); break;   STORAGE(OFF(109)); break;
+    STORAGE(OFF(110)); break;   STORAGE(OFF(111)); break;
+    STORAGE(OFF(112)); break;   STORAGE(OFF(113)); break;
+    STORAGE(OFF(114)); break;   STORAGE(OFF(115)); break;
+    STORAGE(OFF(116)); break;   STORAGE(OFF(117)); break;
+    STORAGE(OFF(118)); break;   STORAGE(OFF(119)); break;
+    STORAGE(OFF(120)); break;   STORAGE(OFF(121)); break;
+    STORAGE(OFF(122)); break;   STORAGE(OFF(123)); break;
+    STORAGE(OFF(124)); break;   STORAGE(OFF(125)); break;
+    STORAGE(OFF(126)); break;   STORAGE(OFF(127)); break;
+    STORAGE(OFF(128)); break;   STORAGE(OFF(129)); break;
+    STORAGE(OFF(130)); break;   STORAGE(OFF(131)); break;
+    STORAGE(OFF(132)); break;   STORAGE(OFF(133)); break;
+    STORAGE(OFF(134)); break;   STORAGE(OFF(135)); break;
+    STORAGE(OFF(136)); break;   STORAGE(OFF(137)); break;
+    STORAGE(OFF(138)); break;   STORAGE(OFF(139)); break;
+    STORAGE(OFF(140)); break;   STORAGE(OFF(141)); break;
+    STORAGE(OFF(142)); break;   STORAGE(OFF(143)); break;
+    STORAGE(OFF(144)); break;   STORAGE(OFF(145)); break;
+    STORAGE(OFF(146)); break;   STORAGE(OFF(147)); break;
+    STORAGE(OFF(148)); break;   STORAGE(OFF(149)); break;
+    STORAGE(OFF(150)); break;   STORAGE(OFF(151)); break;
+    STORAGE(OFF(152)); break;   STORAGE(OFF(153)); break;
+    STORAGE(OFF(154)); break;   STORAGE(OFF(155)); break;
+    STORAGE(OFF(156)); break;   STORAGE(OFF(157)); break;
+    STORAGE(OFF(158)); break;   STORAGE(OFF(159)); break;
+    STORAGE(OFF(160)); break;   STORAGE(OFF(161)); break;
+    STORAGE(OFF(162)); break;   STORAGE(OFF(163)); break;
+    STORAGE(OFF(164)); break;   STORAGE(OFF(165)); break;
+    STORAGE(OFF(166)); break;   STORAGE(OFF(167)); break;
+    STORAGE(OFF(168)); break;   STORAGE(OFF(169)); break;
+    STORAGE(OFF(170)); break;   STORAGE(OFF(171)); break;
+    STORAGE(OFF(172)); break;   STORAGE(OFF(173)); break;
+    STORAGE(OFF(174)); break;   STORAGE(OFF(175)); break;
+    STORAGE(OFF(176)); break;   STORAGE(OFF(177)); break;
+    STORAGE(OFF(178)); break;   STORAGE(OFF(179)); break;
+    STORAGE(OFF(180)); break;   STORAGE(OFF(181)); break;
+    STORAGE(OFF(182)); break;   STORAGE(OFF(183)); break;
+    STORAGE(OFF(184)); break;   STORAGE(OFF(185)); break;
+    STORAGE(OFF(186)); break;   STORAGE(OFF(187)); break;
+    STORAGE(OFF(188)); break;   STORAGE(OFF(189)); break;
+    STORAGE(OFF(190)); break;
+
+    /*-------------------------------------------------------------------------
+     * Prefetch controller.
+     */
+    STORAGE(PRECON); break;     /* Prefetch Control */
+    STORAGE(PRESTAT); break;    /* Prefetch Status */
+
+    /*-------------------------------------------------------------------------
+     * System controller.
+     */
+    STORAGE(CFGCON); break;     /* Configuration Control */
+    STORAGE(DEVID); break;      /* Device Identifier */
+    STORAGE(SYSKEY); break;     /* System Key */
+    STORAGE(RCON); break;       /* Reset Control */
+    STORAGE(RSWRST);            /* Software Reset */
+        if ((VALUE(RSWRST) & 1) && s->stop_on_reset) {
+            exit(0);
+        }
+        break;
+    STORAGE(OSCCON); break;     /* Oscillator Control */
+    STORAGE(OSCTUN); break;     /* Oscillator Tuning */
+    STORAGE(SPLLCON); break;    /* System PLL Control */
+    STORAGE(REFO1CON); break;
+    STORAGE(REFO1CONCLR); *bufp = 0; break;
+    STORAGE(REFO1CONSET); *bufp = 0; break;
+    STORAGE(REFO1CONINV); *bufp = 0; break;
+    STORAGE(REFO2CON); break;
+    STORAGE(REFO2CONCLR); *bufp = 0; break;
+    STORAGE(REFO2CONSET); *bufp = 0; break;
+    STORAGE(REFO2CONINV); *bufp = 0; break;
+    STORAGE(REFO3CON); break;
+    STORAGE(REFO3CONCLR); *bufp = 0; break;
+    STORAGE(REFO3CONSET); *bufp = 0; break;
+    STORAGE(REFO3CONINV); *bufp = 0; break;
+    STORAGE(REFO4CON); break;
+    STORAGE(REFO4CONCLR); *bufp = 0; break;
+    STORAGE(REFO4CONSET); *bufp = 0; break;
+    STORAGE(REFO4CONINV); *bufp = 0; break;
+    STORAGE(PB1DIV); break;     /* Peripheral bus 1 divisor */
+    STORAGE(PB1DIVCLR); *bufp = 0; break;
+    STORAGE(PB1DIVSET); *bufp = 0; break;
+    STORAGE(PB1DIVINV); *bufp = 0; break;
+    STORAGE(PB2DIV); break;     /* Peripheral bus 2 divisor */
+    STORAGE(PB2DIVCLR); *bufp = 0; break;
+    STORAGE(PB2DIVSET); *bufp = 0; break;
+    STORAGE(PB2DIVINV); *bufp = 0; break;
+    STORAGE(PB3DIV); break;     /* Peripheral bus 3 divisor */
+    STORAGE(PB3DIVCLR); *bufp = 0; break;
+    STORAGE(PB3DIVSET); *bufp = 0; break;
+    STORAGE(PB3DIVINV); *bufp = 0; break;
+    STORAGE(PB4DIV); break;     /* Peripheral bus 4 divisor */
+    STORAGE(PB4DIVCLR); *bufp = 0; break;
+    STORAGE(PB4DIVSET); *bufp = 0; break;
+    STORAGE(PB4DIVINV); *bufp = 0; break;
+    STORAGE(PB5DIV); break;     /* Peripheral bus 5 divisor */
+    STORAGE(PB5DIVCLR); *bufp = 0; break;
+    STORAGE(PB5DIVSET); *bufp = 0; break;
+    STORAGE(PB5DIVINV); *bufp = 0; break;
+    STORAGE(PB7DIV); break;     /* Peripheral bus 7 divisor */
+    STORAGE(PB7DIVCLR); *bufp = 0; break;
+    STORAGE(PB7DIVSET); *bufp = 0; break;
+    STORAGE(PB7DIVINV); *bufp = 0; break;
+    STORAGE(PB8DIV); break;     /* Peripheral bus 8 divisor */
+    STORAGE(PB8DIVCLR); *bufp = 0; break;
+    STORAGE(PB8DIVSET); *bufp = 0; break;
+    STORAGE(PB8DIVINV); *bufp = 0; break;
+
+    /*-------------------------------------------------------------------------
+     * Peripheral port select registers: input.
+     */
+    STORAGE(INT1R); break;
+    STORAGE(INT2R); break;
+    STORAGE(INT3R); break;
+    STORAGE(INT4R); break;
+    STORAGE(T2CKR); break;
+    STORAGE(T3CKR); break;
+    STORAGE(T4CKR); break;
+    STORAGE(T5CKR); break;
+    STORAGE(T6CKR); break;
+    STORAGE(T7CKR); break;
+    STORAGE(T8CKR); break;
+    STORAGE(T9CKR); break;
+    STORAGE(IC1R); break;
+    STORAGE(IC2R); break;
+    STORAGE(IC3R); break;
+    STORAGE(IC4R); break;
+    STORAGE(IC5R); break;
+    STORAGE(IC6R); break;
+    STORAGE(IC7R); break;
+    STORAGE(IC8R); break;
+    STORAGE(IC9R); break;
+    STORAGE(OCFAR); break;
+    STORAGE(U1RXR); break;
+    STORAGE(U1CTSR); break;
+    STORAGE(U2RXR); break;
+    STORAGE(U2CTSR); break;
+    STORAGE(U3RXR); break;
+    STORAGE(U3CTSR); break;
+    STORAGE(U4RXR); break;
+    STORAGE(U4CTSR); break;
+    STORAGE(U5RXR); break;
+    STORAGE(U5CTSR); break;
+    STORAGE(U6RXR); break;
+    STORAGE(U6CTSR); break;
+    STORAGE(SDI1R); break;
+    STORAGE(SS1R); break;
+    STORAGE(SDI2R); break;
+    STORAGE(SS2R); break;
+    STORAGE(SDI3R); break;
+    STORAGE(SS3R); break;
+    STORAGE(SDI4R); break;
+    STORAGE(SS4R); break;
+    STORAGE(SDI5R); break;
+    STORAGE(SS5R); break;
+    STORAGE(SDI6R); break;
+    STORAGE(SS6R); break;
+    STORAGE(C1RXR); break;
+    STORAGE(C2RXR); break;
+    STORAGE(REFCLKI1R); break;
+    STORAGE(REFCLKI3R); break;
+    STORAGE(REFCLKI4R); break;
+
+    /*-------------------------------------------------------------------------
+     * Peripheral port select registers: output.
+     */
+    STORAGE(RPA14R); break;
+    STORAGE(RPA15R); break;
+    STORAGE(RPB0R); break;
+    STORAGE(RPB1R); break;
+    STORAGE(RPB2R); break;
+    STORAGE(RPB3R); break;
+    STORAGE(RPB5R); break;
+    STORAGE(RPB6R); break;
+    STORAGE(RPB7R); break;
+    STORAGE(RPB8R); break;
+    STORAGE(RPB9R); break;
+    STORAGE(RPB10R); break;
+    STORAGE(RPB14R); break;
+    STORAGE(RPB15R); break;
+    STORAGE(RPC1R); break;
+    STORAGE(RPC2R); break;
+    STORAGE(RPC3R); break;
+    STORAGE(RPC4R); break;
+    STORAGE(RPC13R); break;
+    STORAGE(RPC14R); break;
+    STORAGE(RPD0R); break;
+    STORAGE(RPD1R); break;
+    STORAGE(RPD2R); break;
+    STORAGE(RPD3R); break;
+    STORAGE(RPD4R); break;
+    STORAGE(RPD5R); break;
+    STORAGE(RPD6R); break;
+    STORAGE(RPD7R); break;
+    STORAGE(RPD9R); break;
+    STORAGE(RPD10R); break;
+    STORAGE(RPD11R); break;
+    STORAGE(RPD12R); break;
+    STORAGE(RPD14R); break;
+    STORAGE(RPD15R); break;
+    STORAGE(RPE3R); break;
+    STORAGE(RPE5R); break;
+    STORAGE(RPE8R); break;
+    STORAGE(RPE9R); break;
+    STORAGE(RPF0R); break;
+    STORAGE(RPF1R); break;
+    STORAGE(RPF2R); break;
+    STORAGE(RPF3R); break;
+    STORAGE(RPF4R); break;
+    STORAGE(RPF5R); break;
+    STORAGE(RPF8R); break;
+    STORAGE(RPF12R); break;
+    STORAGE(RPF13R); break;
+    STORAGE(RPG0R); break;
+    STORAGE(RPG1R); break;
+    STORAGE(RPG6R); break;
+    STORAGE(RPG7R); break;
+    STORAGE(RPG8R); break;
+    STORAGE(RPG9R); break;
+
+    /*-------------------------------------------------------------------------
+     * General purpose IO signals.
+     */
+    STORAGE(ANSELA); break;     /* Port A: analog select */
+    STORAGE(TRISA); break;      /* Port A: mask of inputs */
+    STORAGE(PORTA); break;      /* Port A: read inputs */
+    STORAGE(LATA); break;       /* Port A: read outputs */
+    STORAGE(ODCA); break;       /* Port A: open drain configuration */
+    STORAGE(CNPUA); break;      /* Input pin pull-up */
+    STORAGE(CNPDA); break;      /* Input pin pull-down */
+    STORAGE(CNCONA); break;     /* Interrupt-on-change control */
+    STORAGE(CNENA); break;      /* Input change interrupt enable */
+    STORAGE(CNSTATA); break;    /* Input change status */
+
+    STORAGE(ANSELB); break;     /* Port B: analog select */
+    STORAGE(TRISB); break;      /* Port B: mask of inputs */
+    STORAGE(PORTB); break;      /* Port B: read inputs */
+    STORAGE(LATB); break;       /* Port B: read outputs */
+    STORAGE(ODCB); break;       /* Port B: open drain configuration */
+    STORAGE(CNPUB); break;      /* Input pin pull-up */
+    STORAGE(CNPDB); break;      /* Input pin pull-down */
+    STORAGE(CNCONB); break;     /* Interrupt-on-change control */
+    STORAGE(CNENB); break;      /* Input change interrupt enable */
+    STORAGE(CNSTATB); break;    /* Input change status */
+
+    STORAGE(ANSELC); break;     /* Port C: analog select */
+    STORAGE(TRISC); break;      /* Port C: mask of inputs */
+    STORAGE(PORTC); break;      /* Port C: read inputs */
+    STORAGE(LATC); break;       /* Port C: read outputs */
+    STORAGE(ODCC); break;       /* Port C: open drain configuration */
+    STORAGE(CNPUC); break;      /* Input pin pull-up */
+    STORAGE(CNPDC); break;      /* Input pin pull-down */
+    STORAGE(CNCONC); break;     /* Interrupt-on-change control */
+    STORAGE(CNENC); break;      /* Input change interrupt enable */
+    STORAGE(CNSTATC); break;    /* Input change status */
+
+    STORAGE(ANSELD); break;     /* Port D: analog select */
+    STORAGE(TRISD); break;      /* Port D: mask of inputs */
+    STORAGE(PORTD); break;      /* Port D: read inputs */
+    STORAGE(LATD); break;       /* Port D: read outputs */
+    STORAGE(ODCD); break;       /* Port D: open drain configuration */
+    STORAGE(CNPUD); break;      /* Input pin pull-up */
+    STORAGE(CNPDD); break;      /* Input pin pull-down */
+    STORAGE(CNCOND); break;     /* Interrupt-on-change control */
+    STORAGE(CNEND); break;      /* Input change interrupt enable */
+    STORAGE(CNSTATD); break;    /* Input change status */
+
+    STORAGE(ANSELE); break;     /* Port E: analog select */
+    STORAGE(TRISE); break;      /* Port E: mask of inputs */
+    STORAGE(PORTE); break;      /* Port E: read inputs */
+    STORAGE(LATE); break;       /* Port E: read outputs */
+    STORAGE(ODCE); break;       /* Port E: open drain configuration */
+    STORAGE(CNPUE); break;      /* Input pin pull-up */
+    STORAGE(CNPDE); break;      /* Input pin pull-down */
+    STORAGE(CNCONE); break;     /* Interrupt-on-change control */
+    STORAGE(CNENE); break;      /* Input change interrupt enable */
+    STORAGE(CNSTATE); break;    /* Input change status */
+
+    STORAGE(ANSELF); break;     /* Port F: analog select */
+    STORAGE(TRISF); break;      /* Port F: mask of inputs */
+    STORAGE(PORTF); break;      /* Port F: read inputs */
+    STORAGE(LATF); break;       /* Port F: read outputs */
+    STORAGE(ODCF); break;       /* Port F: open drain configuration */
+    STORAGE(CNPUF); break;      /* Input pin pull-up */
+    STORAGE(CNPDF); break;      /* Input pin pull-down */
+    STORAGE(CNCONF); break;     /* Interrupt-on-change control */
+    STORAGE(CNENF); break;      /* Input change interrupt enable */
+    STORAGE(CNSTATF); break;    /* Input change status */
+
+    STORAGE(ANSELG); break;     /* Port G: analog select */
+    STORAGE(TRISG); break;      /* Port G: mask of inputs */
+    STORAGE(PORTG); break;      /* Port G: read inputs */
+    STORAGE(LATG); break;       /* Port G: read outputs */
+    STORAGE(ODCG); break;       /* Port G: open drain configuration */
+    STORAGE(CNPUG); break;      /* Input pin pull-up */
+    STORAGE(CNPDG); break;      /* Input pin pull-down */
+    STORAGE(CNCONG); break;     /* Interrupt-on-change control */
+    STORAGE(CNENG); break;      /* Input change interrupt enable */
+    STORAGE(CNSTATG); break;    /* Input change status */
+
+    STORAGE(ANSELH); break;     /* Port H: analog select */
+    STORAGE(TRISH); break;      /* Port H: mask of inputs */
+    STORAGE(PORTH); break;      /* Port H: read inputs */
+    STORAGE(LATH); break;       /* Port H: read outputs */
+    STORAGE(ODCH); break;       /* Port H: open drain configuration */
+    STORAGE(CNPUH); break;      /* Input pin pull-up */
+    STORAGE(CNPDH); break;      /* Input pin pull-down */
+    STORAGE(CNCONH); break;     /* Interrupt-on-change control */
+    STORAGE(CNENH); break;      /* Input change interrupt enable */
+    STORAGE(CNSTATH); break;    /* Input change status */
+
+    STORAGE(ANSELJ); break;     /* Port J: analog select */
+    STORAGE(TRISJ); break;      /* Port J: mask of inputs */
+    STORAGE(PORTJ); break;      /* Port J: read inputs */
+    STORAGE(LATJ); break;       /* Port J: read outputs */
+    STORAGE(ODCJ); break;       /* Port J: open drain configuration */
+    STORAGE(CNPUJ); break;      /* Input pin pull-up */
+    STORAGE(CNPDJ); break;      /* Input pin pull-down */
+    STORAGE(CNCONJ); break;     /* Interrupt-on-change control */
+    STORAGE(CNENJ); break;      /* Input change interrupt enable */
+    STORAGE(CNSTATJ); break;    /* Input change status */
+
+    STORAGE(TRISK); break;      /* Port K: mask of inputs */
+    STORAGE(PORTK); break;      /* Port K: read inputs */
+    STORAGE(LATK); break;       /* Port K: read outputs */
+    STORAGE(ODCK); break;       /* Port K: open drain configuration */
+    STORAGE(CNPUK); break;      /* Input pin pull-up */
+    STORAGE(CNPDK); break;      /* Input pin pull-down */
+    STORAGE(CNCONK); break;     /* Interrupt-on-change control */
+    STORAGE(CNENK); break;      /* Input change interrupt enable */
+    STORAGE(CNSTATK); break;    /* Input change status */
+
+    /*-------------------------------------------------------------------------
+     * UART 1.
+     */
+    STORAGE(U1RXREG);                           /* Receive data */
+        *bufp = pic32_uart_get_char(s, 0);
+        break;
+    STORAGE(U1BRG); break;                      /* Baud rate */
+    STORAGE(U1MODE); break;                     /* Mode */
+    STORAGE(U1STA);                             /* Status and control */
+        pic32_uart_poll_status(s, 0);
+        break;
+    STORAGE(U1TXREG);   *bufp = 0; break;       /* Transmit */
+    STORAGE(U1MODECLR); *bufp = 0; break;
+    STORAGE(U1MODESET); *bufp = 0; break;
+    STORAGE(U1MODEINV); *bufp = 0; break;
+    STORAGE(U1STACLR);  *bufp = 0; break;
+    STORAGE(U1STASET);  *bufp = 0; break;
+    STORAGE(U1STAINV);  *bufp = 0; break;
+    STORAGE(U1BRGCLR);  *bufp = 0; break;
+    STORAGE(U1BRGSET);  *bufp = 0; break;
+    STORAGE(U1BRGINV);  *bufp = 0; break;
+
+    /*-------------------------------------------------------------------------
+     * UART 2.
+     */
+    STORAGE(U2RXREG);                           /* Receive data */
+        *bufp = pic32_uart_get_char(s, 1);
+        break;
+    STORAGE(U2BRG); break;                      /* Baud rate */
+    STORAGE(U2MODE); break;                     /* Mode */
+    STORAGE(U2STA);                             /* Status and control */
+        pic32_uart_poll_status(s, 1);
+        break;
+    STORAGE(U2TXREG);   *bufp = 0; break;       /* Transmit */
+    STORAGE(U2MODECLR); *bufp = 0; break;
+    STORAGE(U2MODESET); *bufp = 0; break;
+    STORAGE(U2MODEINV); *bufp = 0; break;
+    STORAGE(U2STACLR);  *bufp = 0; break;
+    STORAGE(U2STASET);  *bufp = 0; break;
+    STORAGE(U2STAINV);  *bufp = 0; break;
+    STORAGE(U2BRGCLR);  *bufp = 0; break;
+    STORAGE(U2BRGSET);  *bufp = 0; break;
+    STORAGE(U2BRGINV);  *bufp = 0; break;
+
+    /*-------------------------------------------------------------------------
+     * UART 3.
+     */
+    STORAGE(U3RXREG);                           /* Receive data */
+        *bufp = pic32_uart_get_char(s, 2);
+        break;
+    STORAGE(U3BRG); break;                      /* Baud rate */
+    STORAGE(U3MODE); break;                     /* Mode */
+    STORAGE(U3STA);                             /* Status and control */
+        pic32_uart_poll_status(s, 2);
+        break;
+    STORAGE(U3TXREG);   *bufp = 0; break;       /* Transmit */
+    STORAGE(U3MODECLR); *bufp = 0; break;
+    STORAGE(U3MODESET); *bufp = 0; break;
+    STORAGE(U3MODEINV); *bufp = 0; break;
+    STORAGE(U3STACLR);  *bufp = 0; break;
+    STORAGE(U3STASET);  *bufp = 0; break;
+    STORAGE(U3STAINV);  *bufp = 0; break;
+    STORAGE(U3BRGCLR);  *bufp = 0; break;
+    STORAGE(U3BRGSET);  *bufp = 0; break;
+    STORAGE(U3BRGINV);  *bufp = 0; break;
+
+    /*-------------------------------------------------------------------------
+     * UART 4.
+     */
+    STORAGE(U4RXREG);                           /* Receive data */
+        *bufp = pic32_uart_get_char(s, 3);
+        break;
+    STORAGE(U4BRG); break;                      /* Baud rate */
+    STORAGE(U4MODE); break;                     /* Mode */
+    STORAGE(U4STA);                             /* Status and control */
+        pic32_uart_poll_status(s, 3);
+        break;
+    STORAGE(U4TXREG);   *bufp = 0; break;       /* Transmit */
+    STORAGE(U4MODECLR); *bufp = 0; break;
+    STORAGE(U4MODESET); *bufp = 0; break;
+    STORAGE(U4MODEINV); *bufp = 0; break;
+    STORAGE(U4STACLR);  *bufp = 0; break;
+    STORAGE(U4STASET);  *bufp = 0; break;
+    STORAGE(U4STAINV);  *bufp = 0; break;
+    STORAGE(U4BRGCLR);  *bufp = 0; break;
+    STORAGE(U4BRGSET);  *bufp = 0; break;
+    STORAGE(U4BRGINV);  *bufp = 0; break;
+
+    /*-------------------------------------------------------------------------
+     * UART 5.
+     */
+    STORAGE(U5RXREG);                           /* Receive data */
+        *bufp = pic32_uart_get_char(s, 4);
+        break;
+    STORAGE(U5BRG); break;                      /* Baud rate */
+    STORAGE(U5MODE); break;                     /* Mode */
+    STORAGE(U5STA);                             /* Status and control */
+        pic32_uart_poll_status(s, 4);
+        break;
+    STORAGE(U5TXREG);   *bufp = 0; break;       /* Transmit */
+    STORAGE(U5MODECLR); *bufp = 0; break;
+    STORAGE(U5MODESET); *bufp = 0; break;
+    STORAGE(U5MODEINV); *bufp = 0; break;
+    STORAGE(U5STACLR);  *bufp = 0; break;
+    STORAGE(U5STASET);  *bufp = 0; break;
+    STORAGE(U5STAINV);  *bufp = 0; break;
+    STORAGE(U5BRGCLR);  *bufp = 0; break;
+    STORAGE(U5BRGSET);  *bufp = 0; break;
+    STORAGE(U5BRGINV);  *bufp = 0; break;
+
+    /*-------------------------------------------------------------------------
+     * UART 6.
+     */
+    STORAGE(U6RXREG);                           /* Receive data */
+        *bufp = pic32_uart_get_char(s, 5);
+        break;
+    STORAGE(U6BRG); break;                      /* Baud rate */
+    STORAGE(U6MODE); break;                     /* Mode */
+    STORAGE(U6STA);                             /* Status and control */
+        pic32_uart_poll_status(s, 5);
+        break;
+    STORAGE(U6TXREG);   *bufp = 0; break;       /* Transmit */
+    STORAGE(U6MODECLR); *bufp = 0; break;
+    STORAGE(U6MODESET); *bufp = 0; break;
+    STORAGE(U6MODEINV); *bufp = 0; break;
+    STORAGE(U6STACLR);  *bufp = 0; break;
+    STORAGE(U6STASET);  *bufp = 0; break;
+    STORAGE(U6STAINV);  *bufp = 0; break;
+    STORAGE(U6BRGCLR);  *bufp = 0; break;
+    STORAGE(U6BRGSET);  *bufp = 0; break;
+    STORAGE(U6BRGINV);  *bufp = 0; break;
+
+    /*-------------------------------------------------------------------------
+     * SPI 1.
+     */
+    STORAGE(SPI1CON); break;                    /* Control */
+    STORAGE(SPI1CONCLR); *bufp = 0; break;
+    STORAGE(SPI1CONSET); *bufp = 0; break;
+    STORAGE(SPI1CONINV); *bufp = 0; break;
+    STORAGE(SPI1STAT); break;                   /* Status */
+    STORAGE(SPI1STATCLR); *bufp = 0; break;
+    STORAGE(SPI1STATSET); *bufp = 0; break;
+    STORAGE(SPI1STATINV); *bufp = 0; break;
+    STORAGE(SPI1BUF);                           /* Buffer */
+        *bufp = pic32_spi_readbuf(s, 0);
+        break;
+    STORAGE(SPI1BRG); break;                    /* Baud rate */
+    STORAGE(SPI1BRGCLR); *bufp = 0; break;
+    STORAGE(SPI1BRGSET); *bufp = 0; break;
+    STORAGE(SPI1BRGINV); *bufp = 0; break;
+    STORAGE(SPI1CON2); break;                   /* Control 2 */
+    STORAGE(SPI1CON2CLR); *bufp = 0; break;
+    STORAGE(SPI1CON2SET); *bufp = 0; break;
+    STORAGE(SPI1CON2INV); *bufp = 0; break;
+
+    /*-------------------------------------------------------------------------
+     * SPI 2.
+     */
+    STORAGE(SPI2CON); break;                    /* Control */
+    STORAGE(SPI2CONCLR); *bufp = 0; break;
+    STORAGE(SPI2CONSET); *bufp = 0; break;
+    STORAGE(SPI2CONINV); *bufp = 0; break;
+    STORAGE(SPI2STAT); break;                   /* Status */
+    STORAGE(SPI2STATCLR); *bufp = 0; break;
+    STORAGE(SPI2STATSET); *bufp = 0; break;
+    STORAGE(SPI2STATINV); *bufp = 0; break;
+    STORAGE(SPI2BUF);                           /* Buffer */
+        *bufp = pic32_spi_readbuf(s, 1);
+        break;
+    STORAGE(SPI2BRG); break;                    /* Baud rate */
+    STORAGE(SPI2BRGCLR); *bufp = 0; break;
+    STORAGE(SPI2BRGSET); *bufp = 0; break;
+    STORAGE(SPI2BRGINV); *bufp = 0; break;
+    STORAGE(SPI2CON2); break;                   /* Control 2 */
+    STORAGE(SPI2CON2CLR); *bufp = 0; break;
+    STORAGE(SPI2CON2SET); *bufp = 0; break;
+    STORAGE(SPI2CON2INV); *bufp = 0; break;
+
+    /*-------------------------------------------------------------------------
+     * SPI 3.
+     */
+    STORAGE(SPI3CON); break;                    /* Control */
+    STORAGE(SPI3CONCLR); *bufp = 0; break;
+    STORAGE(SPI3CONSET); *bufp = 0; break;
+    STORAGE(SPI3CONINV); *bufp = 0; break;
+    STORAGE(SPI3STAT); break;                   /* Status */
+    STORAGE(SPI3STATCLR); *bufp = 0; break;
+    STORAGE(SPI3STATSET); *bufp = 0; break;
+    STORAGE(SPI3STATINV); *bufp = 0; break;
+    STORAGE(SPI3BUF);                           /* SPIx Buffer */
+        *bufp = pic32_spi_readbuf(s, 2);
+        break;
+    STORAGE(SPI3BRG); break;                    /* Baud rate */
+    STORAGE(SPI3BRGCLR); *bufp = 0; break;
+    STORAGE(SPI3BRGSET); *bufp = 0; break;
+    STORAGE(SPI3BRGINV); *bufp = 0; break;
+    STORAGE(SPI3CON2); break;                   /* Control 2 */
+    STORAGE(SPI3CON2CLR); *bufp = 0; break;
+    STORAGE(SPI3CON2SET); *bufp = 0; break;
+    STORAGE(SPI3CON2INV); *bufp = 0; break;
+
+    /*-------------------------------------------------------------------------
+     * SPI 4.
+     */
+    STORAGE(SPI4CON); break;                    /* Control */
+    STORAGE(SPI4CONCLR); *bufp = 0; break;
+    STORAGE(SPI4CONSET); *bufp = 0; break;
+    STORAGE(SPI4CONINV); *bufp = 0; break;
+    STORAGE(SPI4STAT); break;                   /* Status */
+    STORAGE(SPI4STATCLR); *bufp = 0; break;
+    STORAGE(SPI4STATSET); *bufp = 0; break;
+    STORAGE(SPI4STATINV); *bufp = 0; break;
+    STORAGE(SPI4BUF);                           /* Buffer */
+        *bufp = pic32_spi_readbuf(s, 3);
+        break;
+    STORAGE(SPI4BRG); break;                    /* Baud rate */
+    STORAGE(SPI4BRGCLR); *bufp = 0; break;
+    STORAGE(SPI4BRGSET); *bufp = 0; break;
+    STORAGE(SPI4BRGINV); *bufp = 0; break;
+    STORAGE(SPI4CON2); break;                   /* Control 2 */
+    STORAGE(SPI4CON2CLR); *bufp = 0; break;
+    STORAGE(SPI4CON2SET); *bufp = 0; break;
+    STORAGE(SPI4CON2INV); *bufp = 0; break;
+
+    /*-------------------------------------------------------------------------
+     * Timers.
+     */
+    STORAGE(T1CON); break;
+    STORAGE(TMR1); break;
+    STORAGE(PR1); break;
+    STORAGE(T2CON); break;
+    STORAGE(TMR2); break;
+    STORAGE(PR2); break;
+    STORAGE(T3CON); break;
+    STORAGE(TMR3); break;
+    STORAGE(PR3); break;
+    STORAGE(T4CON); break;
+    STORAGE(TMR4); break;
+    STORAGE(PR4); break;
+    STORAGE(T5CON); break;
+    STORAGE(TMR5); break;
+    STORAGE(PR5); break;
+    STORAGE(T6CON); break;
+    STORAGE(TMR6); break;
+    STORAGE(PR6); break;
+    STORAGE(T7CON); break;
+    STORAGE(TMR7); break;
+    STORAGE(PR7); break;
+    STORAGE(T8CON); break;
+    STORAGE(TMR8); break;
+    STORAGE(PR8); break;
+    STORAGE(T9CON); break;
+    STORAGE(TMR9); break;
+    STORAGE(PR9); break;
+
+    /*-------------------------------------------------------------------------
+     * Ethernet.
+     */
+    STORAGE(ETHCON1); break;            /* Control 1 */
+    STORAGE(ETHCON2); break;            /* Control 2: RX data buffer size */
+    STORAGE(ETHTXST); break;            /* Tx descriptor start address */
+    STORAGE(ETHRXST); break;            /* Rx descriptor start address */
+    STORAGE(ETHHT0); break;             /* Hash tasble 0 */
+    STORAGE(ETHHT1); break;             /* Hash tasble 1 */
+    STORAGE(ETHPMM0); break;            /* Pattern match mask 0 */
+    STORAGE(ETHPMM1); break;            /* Pattern match mask 1 */
+    STORAGE(ETHPMCS); break;            /* Pattern match checksum */
+    STORAGE(ETHPMO); break;             /* Pattern match offset */
+    STORAGE(ETHRXFC); break;            /* Receive filter configuration */
+    STORAGE(ETHRXWM); break;            /* Receive watermarks */
+    STORAGE(ETHIEN); break;             /* Interrupt enable */
+    STORAGE(ETHIRQ); break;             /* Interrupt request */
+    STORAGE(ETHSTAT); break;            /* Status */
+    STORAGE(ETHRXOVFLOW); break;        /* Receive overflow statistics */
+    STORAGE(ETHFRMTXOK); break;         /* Frames transmitted OK statistics */
+    STORAGE(ETHSCOLFRM); break;         /* Single collision frames statistics 
*/
+    STORAGE(ETHMCOLFRM); break;         /* Multiple collision statistics */
+    STORAGE(ETHFRMRXOK); break;         /* Frames received OK statistics */
+    STORAGE(ETHFCSERR); break;          /* Frame check error statistics */
+    STORAGE(ETHALGNERR); break;         /* Alignment errors statistics */
+    STORAGE(EMAC1CFG1); break;          /* MAC configuration 1 */
+    STORAGE(EMAC1CFG2); break;          /* MAC configuration 2 */
+    STORAGE(EMAC1IPGT); break;          /* MAC back-to-back interpacket gap */
+    STORAGE(EMAC1IPGR); break;          /* MAC non-back-to-back gap */
+    STORAGE(EMAC1CLRT); break;          /* MAC collision window/retry limit */
+    STORAGE(EMAC1MAXF); break;          /* MAC maximum frame length */
+    STORAGE(EMAC1SUPP); break;          /* MAC PHY support */
+    STORAGE(EMAC1TEST); break;          /* MAC test */
+    STORAGE(EMAC1MCFG); break;          /* MII configuration */
+    STORAGE(EMAC1MCMD); break;          /* MII command */
+    STORAGE(EMAC1MADR); break;          /* MII address */
+    STORAGE(EMAC1MWTD); break;          /* MII write data */
+    STORAGE(EMAC1MRDD); break;          /* MII read data */
+    STORAGE(EMAC1MIND); break;          /* MII indicators */
+    STORAGE(EMAC1SA0); break;           /* MAC station address 0 */
+    STORAGE(EMAC1SA1); break;           /* MAC station address 1 */
+    STORAGE(EMAC1SA2); break;           /* MAC station address 2 */
+
+    /*-------------------------------------------------------------------------
+     * USB.
+     */
+    STORAGE(USBCSR0); break;
+    STORAGE(USBCSR1); break;
+    STORAGE(USBCSR2); break;
+    STORAGE(USBCSR3); break;
+    STORAGE(USBIENCSR0); break;
+    STORAGE(USBIENCSR1); break;
+    STORAGE(USBIENCSR2); break;
+    STORAGE(USBIENCSR3); break;
+    STORAGE(USBFIFO0); break;
+    STORAGE(USBFIFO1); break;
+    STORAGE(USBFIFO2); break;
+    STORAGE(USBFIFO3); break;
+    STORAGE(USBFIFO4); break;
+    STORAGE(USBFIFO5); break;
+    STORAGE(USBFIFO6); break;
+    STORAGE(USBFIFO7); break;
+    STORAGE(USBOTG); break;
+    STORAGE(USBFIFOA); break;
+    STORAGE(USBHWVER); break;
+    STORAGE(USBINFO); break;
+    STORAGE(USBEOFRST); break;
+    STORAGE(USBE0TXA); break;
+    STORAGE(USBE0RXA); break;
+    STORAGE(USBE1TXA); break;
+    STORAGE(USBE1RXA); break;
+    STORAGE(USBE2TXA); break;
+    STORAGE(USBE2RXA); break;
+    STORAGE(USBE3TXA); break;
+    STORAGE(USBE3RXA); break;
+    STORAGE(USBE4TXA); break;
+    STORAGE(USBE4RXA); break;
+    STORAGE(USBE5TXA); break;
+    STORAGE(USBE5RXA); break;
+    STORAGE(USBE6TXA); break;
+    STORAGE(USBE6RXA); break;
+    STORAGE(USBE7TXA); break;
+    STORAGE(USBE7RXA); break;
+    STORAGE(USBE0CSR0); break;
+    STORAGE(USBE0CSR2); break;
+    STORAGE(USBE0CSR3); break;
+    STORAGE(USBE1CSR0); break;
+    STORAGE(USBE1CSR1); break;
+    STORAGE(USBE1CSR2); break;
+    STORAGE(USBE1CSR3); break;
+    STORAGE(USBE2CSR0); break;
+    STORAGE(USBE2CSR1); break;
+    STORAGE(USBE2CSR2); break;
+    STORAGE(USBE2CSR3); break;
+    STORAGE(USBE3CSR0); break;
+    STORAGE(USBE3CSR1); break;
+    STORAGE(USBE3CSR2); break;
+    STORAGE(USBE3CSR3); break;
+    STORAGE(USBE4CSR0); break;
+    STORAGE(USBE4CSR1); break;
+    STORAGE(USBE4CSR2); break;
+    STORAGE(USBE4CSR3); break;
+    STORAGE(USBE5CSR0); break;
+    STORAGE(USBE5CSR1); break;
+    STORAGE(USBE5CSR2); break;
+    STORAGE(USBE5CSR3); break;
+    STORAGE(USBE6CSR0); break;
+    STORAGE(USBE6CSR1); break;
+    STORAGE(USBE6CSR2); break;
+    STORAGE(USBE6CSR3); break;
+    STORAGE(USBE7CSR0); break;
+    STORAGE(USBE7CSR1); break;
+    STORAGE(USBE7CSR2); break;
+    STORAGE(USBE7CSR3); break;
+    STORAGE(USBDMAINT); break;
+    STORAGE(USBDMA1C); break;
+    STORAGE(USBDMA1A); break;
+    STORAGE(USBDMA1N); break;
+    STORAGE(USBDMA2C); break;
+    STORAGE(USBDMA2A); break;
+    STORAGE(USBDMA2N); break;
+    STORAGE(USBDMA3C); break;
+    STORAGE(USBDMA3A); break;
+    STORAGE(USBDMA3N); break;
+    STORAGE(USBDMA4C); break;
+    STORAGE(USBDMA4A); break;
+    STORAGE(USBDMA4N); break;
+    STORAGE(USBDMA5C); break;
+    STORAGE(USBDMA5A); break;
+    STORAGE(USBDMA5N); break;
+    STORAGE(USBDMA6C); break;
+    STORAGE(USBDMA6A); break;
+    STORAGE(USBDMA6N); break;
+    STORAGE(USBDMA7C); break;
+    STORAGE(USBDMA7A); break;
+    STORAGE(USBDMA7N); break;
+    STORAGE(USBDMA8C); break;
+    STORAGE(USBDMA8A); break;
+    STORAGE(USBDMA8N); break;
+    STORAGE(USBE1RPC); break;
+    STORAGE(USBE2RPC); break;
+    STORAGE(USBE3RPC); break;
+    STORAGE(USBE4RPC); break;
+    STORAGE(USBE5RPC); break;
+    STORAGE(USBE6RPC); break;
+    STORAGE(USBE7RPC); break;
+    STORAGE(USBDPBFD); break;
+    STORAGE(USBTMCON1); break;
+    STORAGE(USBTMCON2); break;
+    STORAGE(USBLPMR1); break;
+    STORAGE(USBLMPR2); break;
+
+    default:
+        printf("--- Read 1f8%05x: peripheral register not supported\n",
+            offset);
+        if (TRACE) {
+            fprintf(qemu_logfile,
+                "--- Read 1f8%05x: peripheral register not supported\n",
+                offset);
+        }
+        exit(1);
+    }
+    return *bufp;
+}
+
+static void pps_input_group1(unsigned address, unsigned data)
+{
+    /* 0000 = RPD1 */
+    /* 0001 = RPG9 */
+    /* 0010 = RPB14 */
+    /* 0011 = RPD0 */
+    /* 0101 = RPB6 */
+    /* 0110 = RPD5 */
+    /* 0111 = RPB2 */
+    /* 1000 = RPF3 */
+    /* 1001 = RPF13 */
+    /* 1011 = RPF2 */
+    /* 1100 = RPC2 */
+    /* 1101 = RPE8 */
+}
+
+static void pps_input_group2(unsigned address, unsigned data)
+{
+    /* 0000 = RPD9 */
+    /* 0001 = RPG6 */
+    /* 0010 = RPB8 */
+    /* 0011 = RPB15 */
+    /* 0100 = RPD4 */
+    /* 0101 = RPB0 */
+    /* 0110 = RPE3 */
+    /* 0111 = RPB7 */
+    /* 1001 = RPF12 */
+    /* 1010 = RPD12 */
+    /* 1011 = RPF8 */
+    /* 1100 = RPC3 */
+    /* 1101 = RPE9 */
+}
+
+static void pps_input_group3(unsigned address, unsigned data)
+{
+    /* 0000 = RPD2 */
+    /* 0001 = RPG8 */
+    /* 0010 = RPF4 */
+    /* 0011 = RPD10 */
+    /* 0100 = RPF1 */
+    /* 0101 = RPB9 */
+    /* 0110 = RPB10 */
+    /* 0111 = RPC14 */
+    /* 1000 = RPB5 */
+    /* 1010 = RPC1 */
+    /* 1011 = RPD14 */
+    /* 1100 = RPG1 */
+    /* 1101 = RPA14 */
+    /* 1110 = RPD6 */
+}
+
+static void pps_input_group4(unsigned address, unsigned data)
+{
+    /* 0000 = RPD3 */
+    /* 0001 = RPG7 */
+    /* 0010 = RPF5 */
+    /* 0011 = RPD11 */
+    /* 0100 = RPF0 */
+    /* 0101 = RPB1 */
+    /* 0110 = RPE5 */
+    /* 0111 = RPC13 */
+    /* 1000 = RPB3 */
+    /* 1010 = RPC4 */
+    /* 1011 = RPD15 */
+    /* 1100 = RPG0 */
+    /* 1101 = RPA15 */
+    /* 1110 = RPD7 */
+}
+
+static void pps_output_group1(unsigned address, unsigned data)
+{
+    /* 0000 = No Connect */
+    /* 0001 = U1TX */
+    /* 0010 = U2RTS */
+    /* 0011 = U5TX */
+    /* 0100 = U6RTS */
+    /* 0101 = SDO1 */
+    /* 0110 = SDO2 */
+    /* 0111 = SDO3 */
+    /* 1000 = SDO4 */
+    /* 1001 = SDO5 */
+    /* 1011 = OC4 */
+    /* 1100 = OC7 */
+    /* 1111 = REFCLKO1 */
+}
+
+static void pps_output_group2(unsigned address, unsigned data)
+{
+    /* 0000 = No Connect */
+    /* 0001 = U1RTS */
+    /* 0010 = U2TX */
+    /* 0011 = U5RTS */
+    /* 0100 = U6TX */
+    /* 0110 = SS2 */
+    /* 1000 = SDO4 */
+    /* 1010 = SDO6 */
+    /* 1011 = OC2 */
+    /* 1100 = OC1 */
+    /* 1101 = OC9 */
+    /* 1111 = C2TX */
+}
+
+static void pps_output_group3(unsigned address, unsigned data)
+{
+    /* 0000 = No Connect */
+    /* 0001 = U3TX */
+    /* 0010 = U4RTS */
+    /* 0101 = SDO1 */
+    /* 0110 = SDO2 */
+    /* 0111 = SDO3 */
+    /* 1001 = SDO5 */
+    /* 1010 = SS6 */
+    /* 1011 = OC3 */
+    /* 1100 = OC6 */
+    /* 1101 = REFCLKO4 */
+    /* 1110 = C2OUT */
+    /* 1111 = C1TX */
+}
+
+static void pps_output_group4(unsigned address, unsigned data)
+{
+    /* 0000 = No Connect */
+    /* 0001 = U3RTS */
+    /* 0010 = U4TX */
+    /* 0100 = U6TX */
+    /* 0101 = SS1 */
+    /* 0111 = SS3 */
+    /* 1000 = SS4 */
+    /* 1001 = SS5 */
+    /* 1010 = SDO6 */
+    /* 1011 = OC5 */
+    /* 1100 = OC8 */
+    /* 1110 = C1OUT */
+    /* 1111 = REFCLKO3 */
+}
+
+static void io_write32(pic32_t *s, unsigned offset, unsigned data,
+    const char **namep)
+{
+    unsigned *bufp = &VALUE(offset);
+    unsigned mask;
+
+    switch (offset) {
+    /*-------------------------------------------------------------------------
+     * Interrupt controller registers.
+     */
+    WRITEOP(INTCON); return;    /* Interrupt Control */
+    READONLY(INTSTAT);          /* Interrupt Status */
+    WRITEOP(IPTMR);  return;    /* Temporal Proximity Timer */
+    WRITEOP(IFS0); goto irq;    /* IFS(0..2) - Interrupt Flag Status */
+    WRITEOP(IFS1); goto irq;
+    WRITEOP(IFS2); goto irq;
+    WRITEOP(IFS3); goto irq;
+    WRITEOP(IFS4); goto irq;
+    WRITEOP(IFS5); goto irq;
+    WRITEOP(IEC0); goto irq;    /* IEC(0..2) - Interrupt Enable Control */
+    WRITEOP(IEC1); goto irq;
+    WRITEOP(IEC2); goto irq;
+    WRITEOP(IEC3); goto irq;
+    WRITEOP(IEC4); goto irq;
+    WRITEOP(IEC5); goto irq;
+
+    /* IPC(0..11) - Interrupt Priority Control */
+    WRITEOP(IPC0); goto irq;    WRITEOP(IPC1); goto irq;
+    WRITEOP(IPC2); goto irq;    WRITEOP(IPC3); goto irq;
+    WRITEOP(IPC4); goto irq;    WRITEOP(IPC5); goto irq;
+    WRITEOP(IPC6); goto irq;    WRITEOP(IPC7); goto irq;
+    WRITEOP(IPC8); goto irq;    WRITEOP(IPC9); goto irq;
+    WRITEOP(IPC10); goto irq;   WRITEOP(IPC11); goto irq;
+    WRITEOP(IPC12); goto irq;   WRITEOP(IPC13); goto irq;
+    WRITEOP(IPC14); goto irq;   WRITEOP(IPC15); goto irq;
+    WRITEOP(IPC16); goto irq;   WRITEOP(IPC17); goto irq;
+    WRITEOP(IPC18); goto irq;   WRITEOP(IPC19); goto irq;
+    WRITEOP(IPC20); goto irq;   WRITEOP(IPC21); goto irq;
+    WRITEOP(IPC22); goto irq;   WRITEOP(IPC23); goto irq;
+    WRITEOP(IPC24); goto irq;   WRITEOP(IPC25); goto irq;
+    WRITEOP(IPC26); goto irq;   WRITEOP(IPC27); goto irq;
+    WRITEOP(IPC28); goto irq;   WRITEOP(IPC29); goto irq;
+    WRITEOP(IPC30); goto irq;   WRITEOP(IPC31); goto irq;
+    WRITEOP(IPC32); goto irq;   WRITEOP(IPC33); goto irq;
+    WRITEOP(IPC34); goto irq;   WRITEOP(IPC35); goto irq;
+    WRITEOP(IPC36); goto irq;   WRITEOP(IPC37); goto irq;
+    WRITEOP(IPC38); goto irq;   WRITEOP(IPC39); goto irq;
+    WRITEOP(IPC40); goto irq;   WRITEOP(IPC41); goto irq;
+    WRITEOP(IPC42); goto irq;   WRITEOP(IPC43); goto irq;
+    WRITEOP(IPC44); goto irq;   WRITEOP(IPC45); goto irq;
+    WRITEOP(IPC46); goto irq;   WRITEOP(IPC47);
+irq:    update_irq_status(s);
+        return;
+
+    /* OFF000..OFF190 - Interrupt Vector Address Offset */
+    STORAGE(OFF(0)); break;     STORAGE(OFF(1)); break;
+    STORAGE(OFF(2)); break;     STORAGE(OFF(3)); break;
+    STORAGE(OFF(4)); break;     STORAGE(OFF(5)); break;
+    STORAGE(OFF(6)); break;     STORAGE(OFF(7)); break;
+    STORAGE(OFF(8)); break;     STORAGE(OFF(9)); break;
+    STORAGE(OFF(10)); break;    STORAGE(OFF(11)); break;
+    STORAGE(OFF(12)); break;    STORAGE(OFF(13)); break;
+    STORAGE(OFF(14)); break;    STORAGE(OFF(15)); break;
+    STORAGE(OFF(16)); break;    STORAGE(OFF(17)); break;
+    STORAGE(OFF(18)); break;    STORAGE(OFF(19)); break;
+    STORAGE(OFF(20)); break;    STORAGE(OFF(21)); break;
+    STORAGE(OFF(22)); break;    STORAGE(OFF(23)); break;
+    STORAGE(OFF(24)); break;    STORAGE(OFF(25)); break;
+    STORAGE(OFF(26)); break;    STORAGE(OFF(27)); break;
+    STORAGE(OFF(28)); break;    STORAGE(OFF(29)); break;
+    STORAGE(OFF(30)); break;    STORAGE(OFF(31)); break;
+    STORAGE(OFF(32)); break;    STORAGE(OFF(33)); break;
+    STORAGE(OFF(34)); break;    STORAGE(OFF(35)); break;
+    STORAGE(OFF(36)); break;    STORAGE(OFF(37)); break;
+    STORAGE(OFF(38)); break;    STORAGE(OFF(39)); break;
+    STORAGE(OFF(40)); break;    STORAGE(OFF(41)); break;
+    STORAGE(OFF(42)); break;    STORAGE(OFF(43)); break;
+    STORAGE(OFF(44)); break;    STORAGE(OFF(45)); break;
+    STORAGE(OFF(46)); break;    STORAGE(OFF(47)); break;
+    STORAGE(OFF(48)); break;    STORAGE(OFF(49)); break;
+    STORAGE(OFF(50)); break;    STORAGE(OFF(51)); break;
+    STORAGE(OFF(52)); break;    STORAGE(OFF(53)); break;
+    STORAGE(OFF(54)); break;    STORAGE(OFF(55)); break;
+    STORAGE(OFF(56)); break;    STORAGE(OFF(57)); break;
+    STORAGE(OFF(58)); break;    STORAGE(OFF(59)); break;
+    STORAGE(OFF(60)); break;    STORAGE(OFF(61)); break;
+    STORAGE(OFF(62)); break;    STORAGE(OFF(63)); break;
+    STORAGE(OFF(64)); break;    STORAGE(OFF(65)); break;
+    STORAGE(OFF(66)); break;    STORAGE(OFF(67)); break;
+    STORAGE(OFF(68)); break;    STORAGE(OFF(69)); break;
+    STORAGE(OFF(70)); break;    STORAGE(OFF(71)); break;
+    STORAGE(OFF(72)); break;    STORAGE(OFF(73)); break;
+    STORAGE(OFF(74)); break;    STORAGE(OFF(75)); break;
+    STORAGE(OFF(76)); break;    STORAGE(OFF(77)); break;
+    STORAGE(OFF(78)); break;    STORAGE(OFF(79)); break;
+    STORAGE(OFF(80)); break;    STORAGE(OFF(81)); break;
+    STORAGE(OFF(82)); break;    STORAGE(OFF(83)); break;
+    STORAGE(OFF(84)); break;    STORAGE(OFF(85)); break;
+    STORAGE(OFF(86)); break;    STORAGE(OFF(87)); break;
+    STORAGE(OFF(88)); break;    STORAGE(OFF(89)); break;
+    STORAGE(OFF(90)); break;    STORAGE(OFF(91)); break;
+    STORAGE(OFF(92)); break;    STORAGE(OFF(93)); break;
+    STORAGE(OFF(94)); break;    STORAGE(OFF(95)); break;
+    STORAGE(OFF(96)); break;    STORAGE(OFF(97)); break;
+    STORAGE(OFF(98)); break;    STORAGE(OFF(99)); break;
+    STORAGE(OFF(100)); break;   STORAGE(OFF(101)); break;
+    STORAGE(OFF(102)); break;   STORAGE(OFF(103)); break;
+    STORAGE(OFF(104)); break;   STORAGE(OFF(105)); break;
+    STORAGE(OFF(106)); break;   STORAGE(OFF(107)); break;
+    STORAGE(OFF(108)); break;   STORAGE(OFF(109)); break;
+    STORAGE(OFF(110)); break;   STORAGE(OFF(111)); break;
+    STORAGE(OFF(112)); break;   STORAGE(OFF(113)); break;
+    STORAGE(OFF(114)); break;   STORAGE(OFF(115)); break;
+    STORAGE(OFF(116)); break;   STORAGE(OFF(117)); break;
+    STORAGE(OFF(118)); break;   STORAGE(OFF(119)); break;
+    STORAGE(OFF(120)); break;   STORAGE(OFF(121)); break;
+    STORAGE(OFF(122)); break;   STORAGE(OFF(123)); break;
+    STORAGE(OFF(124)); break;   STORAGE(OFF(125)); break;
+    STORAGE(OFF(126)); break;   STORAGE(OFF(127)); break;
+    STORAGE(OFF(128)); break;   STORAGE(OFF(129)); break;
+    STORAGE(OFF(130)); break;   STORAGE(OFF(131)); break;
+    STORAGE(OFF(132)); break;   STORAGE(OFF(133)); break;
+    STORAGE(OFF(134)); break;   STORAGE(OFF(135)); break;
+    STORAGE(OFF(136)); break;   STORAGE(OFF(137)); break;
+    STORAGE(OFF(138)); break;   STORAGE(OFF(139)); break;
+    STORAGE(OFF(140)); break;   STORAGE(OFF(141)); break;
+    STORAGE(OFF(142)); break;   STORAGE(OFF(143)); break;
+    STORAGE(OFF(144)); break;   STORAGE(OFF(145)); break;
+    STORAGE(OFF(146)); break;   STORAGE(OFF(147)); break;
+    STORAGE(OFF(148)); break;   STORAGE(OFF(149)); break;
+    STORAGE(OFF(150)); break;   STORAGE(OFF(151)); break;
+    STORAGE(OFF(152)); break;   STORAGE(OFF(153)); break;
+    STORAGE(OFF(154)); break;   STORAGE(OFF(155)); break;
+    STORAGE(OFF(156)); break;   STORAGE(OFF(157)); break;
+    STORAGE(OFF(158)); break;   STORAGE(OFF(159)); break;
+    STORAGE(OFF(160)); break;   STORAGE(OFF(161)); break;
+    STORAGE(OFF(162)); break;   STORAGE(OFF(163)); break;
+    STORAGE(OFF(164)); break;   STORAGE(OFF(165)); break;
+    STORAGE(OFF(166)); break;   STORAGE(OFF(167)); break;
+    STORAGE(OFF(168)); break;   STORAGE(OFF(169)); break;
+    STORAGE(OFF(170)); break;   STORAGE(OFF(171)); break;
+    STORAGE(OFF(172)); break;   STORAGE(OFF(173)); break;
+    STORAGE(OFF(174)); break;   STORAGE(OFF(175)); break;
+    STORAGE(OFF(176)); break;   STORAGE(OFF(177)); break;
+    STORAGE(OFF(178)); break;   STORAGE(OFF(179)); break;
+    STORAGE(OFF(180)); break;   STORAGE(OFF(181)); break;
+    STORAGE(OFF(182)); break;   STORAGE(OFF(183)); break;
+    STORAGE(OFF(184)); break;   STORAGE(OFF(185)); break;
+    STORAGE(OFF(186)); break;   STORAGE(OFF(187)); break;
+    STORAGE(OFF(188)); break;   STORAGE(OFF(189)); break;
+    STORAGE(OFF(190)); break;
+
+    /*-------------------------------------------------------------------------
+     * Prefetch controller.
+     */
+    WRITEOP(PRECON); return;    /* Prefetch Control */
+    WRITEOP(PRESTAT); return;   /* Prefetch Status */
+
+    /*-------------------------------------------------------------------------
+     * System controller.
+     */
+    STORAGE(CFGCON);            /* Configuration Control */
+        /* TODO: use unlock sequence */
+        mask = PIC32_CFGCON_DMAPRI | PIC32_CFGCON_CPUPRI |
+            PIC32_CFGCON_ICACLK | PIC32_CFGCON_OCACLK |
+            PIC32_CFGCON_IOLOCK | PIC32_CFGCON_PMDLOCK |
+            PIC32_CFGCON_PGLOCK | PIC32_CFGCON_USBSSEN |
+            PIC32_CFGCON_ECC_MASK | PIC32_CFGCON_JTAGEN |
+            PIC32_CFGCON_TROEN | PIC32_CFGCON_TDOEN;
+        data = (data & mask) | (*bufp & ~mask);
+        break;
+    READONLY(DEVID);            /* Device Identifier */
+    STORAGE(SYSKEY);            /* System Key */
+        /* Unlock state machine. */
+        if (s->syskey_unlock == 0 && VALUE(SYSKEY) == 0xaa996655) {
+            s->syskey_unlock = 1;
+        }
+        if (s->syskey_unlock == 1 && VALUE(SYSKEY) == 0x556699aa) {
+            s->syskey_unlock = 2;
+        } else {
+            s->syskey_unlock = 0;
+        }
+        break;
+    STORAGE(RCON); break;       /* Reset Control */
+    WRITEOP(RSWRST);            /* Software Reset */
+        if (s->syskey_unlock == 2 && (VALUE(RSWRST) & 1)) {
+            /* Reset CPU. */
+            qemu_system_reset_request();
+
+            /* Reset all devices */
+            io_reset(s);
+            pic32_sdcard_reset(s);
+        }
+        break;
+    STORAGE(OSCCON); break;     /* Oscillator Control */
+    STORAGE(OSCTUN); break;     /* Oscillator Tuning */
+    STORAGE(SPLLCON); break;    /* System PLL Control */
+    WRITEOP(REFO1CON); return;
+    WRITEOP(REFO2CON); return;
+    WRITEOP(REFO3CON); return;
+    WRITEOP(REFO4CON); return;
+    WRITEOP(PB1DIV); return;    /* Peripheral bus 1 divisor */
+    WRITEOP(PB2DIV); return;    /* Peripheral bus 2 divisor */
+    WRITEOP(PB3DIV); return;    /* Peripheral bus 3 divisor */
+    WRITEOP(PB4DIV); return;    /* Peripheral bus 4 divisor */
+    WRITEOP(PB5DIV); return;    /* Peripheral bus 5 divisor */
+    WRITEOP(PB7DIV); return;    /* Peripheral bus 7 divisor */
+    WRITEOP(PB8DIV); return;    /* Peripheral bus 8 divisor */
+
+    /*-------------------------------------------------------------------------
+     * Peripheral port select registers: input.
+     */
+    STORAGE(INT1R);    pps_input_group1(offset, data); break;
+    STORAGE(T4CKR);    pps_input_group1(offset, data); break;
+    STORAGE(T9CKR);    pps_input_group1(offset, data); break;
+    STORAGE(IC1R);     pps_input_group1(offset, data); break;
+    STORAGE(IC6R);     pps_input_group1(offset, data); break;
+    STORAGE(U3CTSR);   pps_input_group1(offset, data); break;
+    STORAGE(U4RXR);    pps_input_group1(offset, data); break;
+    STORAGE(U6RXR);    pps_input_group1(offset, data); break;
+    STORAGE(SS2R);     pps_input_group1(offset, data); break;
+    STORAGE(SDI6R);    pps_input_group1(offset, data); break;
+    STORAGE(OCFAR);    pps_input_group1(offset, data); break;
+    STORAGE(REFCLKI3R); pps_input_group1(offset, data); break;
+
+    STORAGE(INT2R);    pps_input_group2(offset, data); break;
+    STORAGE(T3CKR);    pps_input_group2(offset, data); break;
+    STORAGE(T8CKR);    pps_input_group2(offset, data); break;
+    STORAGE(IC2R);     pps_input_group2(offset, data); break;
+    STORAGE(IC5R);     pps_input_group2(offset, data); break;
+    STORAGE(IC9R);     pps_input_group2(offset, data); break;
+    STORAGE(U1CTSR);   pps_input_group2(offset, data); break;
+    STORAGE(U2RXR);    pps_input_group2(offset, data); break;
+    STORAGE(U5CTSR);   pps_input_group2(offset, data); break;
+    STORAGE(SS1R);     pps_input_group2(offset, data); break;
+    STORAGE(SS3R);     pps_input_group2(offset, data); break;
+    STORAGE(SS4R);     pps_input_group2(offset, data); break;
+    STORAGE(SS5R);     pps_input_group2(offset, data); break;
+    STORAGE(C2RXR);    pps_input_group2(offset, data); break;
+
+    STORAGE(INT3R);    pps_input_group3(offset, data); break;
+    STORAGE(T2CKR);    pps_input_group3(offset, data); break;
+    STORAGE(T6CKR);    pps_input_group3(offset, data); break;
+    STORAGE(IC3R);     pps_input_group3(offset, data); break;
+    STORAGE(IC7R);     pps_input_group3(offset, data); break;
+    STORAGE(U1RXR);    pps_input_group3(offset, data); break;
+    STORAGE(U2CTSR);   pps_input_group3(offset, data); break;
+    STORAGE(U5RXR);    pps_input_group3(offset, data); break;
+    STORAGE(U6CTSR);   pps_input_group3(offset, data); break;
+    STORAGE(SDI1R);    pps_input_group3(offset, data); break;
+    STORAGE(SDI3R);    pps_input_group3(offset, data); break;
+    STORAGE(SDI5R);    pps_input_group3(offset, data); break;
+    STORAGE(SS6R);     pps_input_group3(offset, data); break;
+    STORAGE(REFCLKI1R); pps_input_group3(offset, data); break;
+
+    STORAGE(INT4R);    pps_input_group4(offset, data); break;
+    STORAGE(T5CKR);    pps_input_group4(offset, data); break;
+    STORAGE(T7CKR);    pps_input_group4(offset, data); break;
+    STORAGE(IC4R);     pps_input_group4(offset, data); break;
+    STORAGE(IC8R);     pps_input_group4(offset, data); break;
+    STORAGE(U3RXR);    pps_input_group4(offset, data); break;
+    STORAGE(U4CTSR);   pps_input_group4(offset, data); break;
+    STORAGE(SDI2R);    pps_input_group4(offset, data); break;
+    STORAGE(SDI4R);    pps_input_group4(offset, data); break;
+    STORAGE(C1RXR);    pps_input_group4(offset, data); break;
+    STORAGE(REFCLKI4R); pps_input_group4(offset, data); break;
+
+    /*-------------------------------------------------------------------------
+     * Peripheral port select registers: output.
+     */
+    STORAGE(RPA15R);   pps_output_group1(offset, data); break;
+    STORAGE(RPB1R);    pps_output_group1(offset, data); break;
+    STORAGE(RPB3R);    pps_output_group1(offset, data); break;
+    STORAGE(RPC4R);    pps_output_group1(offset, data); break;
+    STORAGE(RPC13R);   pps_output_group1(offset, data); break;
+    STORAGE(RPD3R);    pps_output_group1(offset, data); break;
+    STORAGE(RPD7R);    pps_output_group1(offset, data); break;
+    STORAGE(RPD11R);   pps_output_group1(offset, data); break;
+    STORAGE(RPD15R);   pps_output_group1(offset, data); break;
+    STORAGE(RPE5R);    pps_output_group1(offset, data); break;
+    STORAGE(RPF0R);    pps_output_group1(offset, data); break;
+    STORAGE(RPF5R);    pps_output_group1(offset, data); break;
+    STORAGE(RPG0R);    pps_output_group1(offset, data); break;
+    STORAGE(RPG7R);    pps_output_group1(offset, data); break;
+
+    STORAGE(RPB2R);    pps_output_group2(offset, data); break;
+    STORAGE(RPB6R);    pps_output_group2(offset, data); break;
+    STORAGE(RPB14R);   pps_output_group2(offset, data); break;
+    STORAGE(RPC2R);    pps_output_group2(offset, data); break;
+    STORAGE(RPD0R);    pps_output_group2(offset, data); break;
+    STORAGE(RPD1R);    pps_output_group2(offset, data); break;
+    STORAGE(RPD5R);    pps_output_group2(offset, data); break;
+    STORAGE(RPE8R);    pps_output_group2(offset, data); break;
+    STORAGE(RPF2R);    pps_output_group2(offset, data); break;
+    STORAGE(RPF3R);    pps_output_group2(offset, data); break;
+    STORAGE(RPF13R);   pps_output_group2(offset, data); break;
+    STORAGE(RPG9R);    pps_output_group2(offset, data); break;
+
+    STORAGE(RPA14R);   pps_output_group3(offset, data); break;
+    STORAGE(RPB5R);    pps_output_group3(offset, data); break;
+    STORAGE(RPB9R);    pps_output_group3(offset, data); break;
+    STORAGE(RPB10R);   pps_output_group3(offset, data); break;
+    STORAGE(RPC1R);    pps_output_group3(offset, data); break;
+    STORAGE(RPC14R);   pps_output_group3(offset, data); break;
+    STORAGE(RPD2R);    pps_output_group3(offset, data); break;
+    STORAGE(RPD6R);    pps_output_group3(offset, data); break;
+    STORAGE(RPD10R);   pps_output_group3(offset, data); break;
+    STORAGE(RPD14R);   pps_output_group3(offset, data); break;
+    STORAGE(RPF1R);    pps_output_group3(offset, data); break;
+    STORAGE(RPF4R);    pps_output_group3(offset, data); break;
+    STORAGE(RPG1R);    pps_output_group3(offset, data); break;
+    STORAGE(RPG8R);    pps_output_group3(offset, data); break;
+
+    STORAGE(RPB0R);    pps_output_group4(offset, data); break;
+    STORAGE(RPB7R);    pps_output_group4(offset, data); break;
+    STORAGE(RPB8R);    pps_output_group4(offset, data); break;
+    STORAGE(RPB15R);   pps_output_group4(offset, data); break;
+    STORAGE(RPC3R);    pps_output_group4(offset, data); break;
+    STORAGE(RPD4R);    pps_output_group4(offset, data); break;
+    STORAGE(RPD9R);    pps_output_group4(offset, data); break;
+    STORAGE(RPD12R);   pps_output_group4(offset, data); break;
+    STORAGE(RPE3R);    pps_output_group4(offset, data); break;
+    STORAGE(RPE9R);    pps_output_group4(offset, data); break;
+    STORAGE(RPF8R);    pps_output_group4(offset, data); break;
+    STORAGE(RPF12R);   pps_output_group4(offset, data); break;
+    STORAGE(RPG6R);    pps_output_group4(offset, data); break;
+
+    /*-------------------------------------------------------------------------
+     * General purpose IO signals.
+     */
+    WRITEOP(ANSELA); return;        /* Port A: analog select */
+    WRITEOP(TRISA); return;         /* Port A: mask of inputs */
+    WRITEOPX(PORTA, LATA);          /* Port A: write outputs */
+    WRITEOP(LATA);                  /* Port A: write outputs */
+        pic32_gpio_write(s, 0, VALUE(LATA));
+        return;
+    WRITEOP(ODCA); return;          /* Port A: open drain configuration */
+    WRITEOP(CNPUA); return;         /* Input pin pull-up */
+    WRITEOP(CNPDA); return;         /* Input pin pull-down */
+    WRITEOP(CNCONA); return;        /* Interrupt-on-change control */
+    WRITEOP(CNENA); return;         /* Input change interrupt enable */
+    WRITEOP(CNSTATA); return;       /* Input change status */
+
+    WRITEOP(ANSELB); return;        /* Port B: analog select */
+    WRITEOP(TRISB); return;         /* Port B: mask of inputs */
+    WRITEOPX(PORTB, LATB);          /* Port B: write outputs */
+    WRITEOP(LATB);                  /* Port B: write outputs */
+        pic32_gpio_write(s, 1, VALUE(LATB));
+        return;
+    WRITEOP(ODCB); return;          /* Port B: open drain configuration */
+    WRITEOP(CNPUB); return;         /* Input pin pull-up */
+    WRITEOP(CNPDB); return;         /* Input pin pull-down */
+    WRITEOP(CNCONB); return;        /* Interrupt-on-change control */
+    WRITEOP(CNENB); return;         /* Input change interrupt enable */
+    WRITEOP(CNSTATB); return;       /* Input change status */
+
+    WRITEOP(ANSELC); return;        /* Port C: analog select */
+    WRITEOP(TRISC); return;         /* Port C: mask of inputs */
+    WRITEOPX(PORTC, LATC);          /* Port C: write outputs */
+    WRITEOP(LATC);                  /* Port C: write outputs */
+        pic32_gpio_write(s, 2, VALUE(LATC));
+        return;
+    WRITEOP(ODCC); return;          /* Port C: open drain configuration */
+    WRITEOP(CNPUC); return;         /* Input pin pull-up */
+    WRITEOP(CNPDC); return;         /* Input pin pull-down */
+    WRITEOP(CNCONC); return;        /* Interrupt-on-change control */
+    WRITEOP(CNENC); return;         /* Input change interrupt enable */
+    WRITEOP(CNSTATC); return;       /* Input change status */
+
+    WRITEOP(ANSELD); return;        /* Port D: analog select */
+    WRITEOP(TRISD); return;         /* Port D: mask of inputs */
+    WRITEOPX(PORTD, LATD);          /* Port D: write outputs */
+    WRITEOP(LATD);                  /* Port D: write outputs */
+        pic32_gpio_write(s, 3, VALUE(LATD));
+        return;
+    WRITEOP(ODCD); return;          /* Port D: open drain configuration */
+    WRITEOP(CNPUD); return;         /* Input pin pull-up */
+    WRITEOP(CNPDD); return;         /* Input pin pull-down */
+    WRITEOP(CNCOND); return;        /* Interrupt-on-change control */
+    WRITEOP(CNEND); return;         /* Input change interrupt enable */
+    WRITEOP(CNSTATD); return;       /* Input change status */
+
+    WRITEOP(ANSELE); return;        /* Port E: analog select */
+    WRITEOP(TRISE); return;         /* Port E: mask of inputs */
+    WRITEOPX(PORTE, LATE);          /* Port E: write outputs */
+    WRITEOP(LATE);                  /* Port E: write outputs */
+        pic32_gpio_write(s, 4, VALUE(LATE));
+        return;
+    WRITEOP(ODCE); return;          /* Port E: open drain configuration */
+    WRITEOP(CNPUE); return;         /* Input pin pull-up */
+    WRITEOP(CNPDE); return;         /* Input pin pull-down */
+    WRITEOP(CNCONE); return;        /* Interrupt-on-change control */
+    WRITEOP(CNENE); return;         /* Input change interrupt enable */
+    WRITEOP(CNSTATE); return;       /* Input change status */
+
+    WRITEOP(ANSELF); return;        /* Port F: analog select */
+    WRITEOP(TRISF); return;         /* Port F: mask of inputs */
+    WRITEOPX(PORTF, LATF);          /* Port F: write outputs */
+    WRITEOP(LATF);                  /* Port F: write outputs */
+        pic32_gpio_write(s, 5, VALUE(LATF));
+        return;
+    WRITEOP(ODCF); return;          /* Port F: open drain configuration */
+    WRITEOP(CNPUF); return;         /* Input pin pull-up */
+    WRITEOP(CNPDF); return;         /* Input pin pull-down */
+    WRITEOP(CNCONF); return;        /* Interrupt-on-change control */
+    WRITEOP(CNENF); return;         /* Input change interrupt enable */
+    WRITEOP(CNSTATF); return;       /* Input change status */
+
+    WRITEOP(ANSELG); return;        /* Port G: analog select */
+    WRITEOP(TRISG); return;         /* Port G: mask of inputs */
+    WRITEOPX(PORTG, LATG);          /* Port G: write outputs */
+    WRITEOP(LATG);                  /* Port G: write outputs */
+        pic32_gpio_write(s, 6, VALUE(LATG));
+        return;
+    WRITEOP(ODCG); return;          /* Port G: open drain configuration */
+    WRITEOP(CNPUG); return;         /* Input pin pull-up */
+    WRITEOP(CNPDG); return;         /* Input pin pull-down */
+    WRITEOP(CNCONG); return;        /* Interrupt-on-change control */
+    WRITEOP(CNENG); return;         /* Input change interrupt enable */
+    WRITEOP(CNSTATG); return;       /* Input change status */
+
+    WRITEOP(ANSELH); return;        /* Port H: analog select */
+    WRITEOP(TRISH); return;         /* Port H: mask of inputs */
+    WRITEOPX(PORTH, LATH);          /* Port H: write outputs */
+    WRITEOP(LATH);                  /* Port H: write outputs */
+        pic32_gpio_write(s, 7, VALUE(LATH));
+        return;
+    WRITEOP(ODCH); return;          /* Port H: open drain configuration */
+    WRITEOP(CNPUH); return;         /* Input pin pull-up */
+    WRITEOP(CNPDH); return;         /* Input pin pull-down */
+    WRITEOP(CNCONH); return;        /* Interrupt-on-change control */
+    WRITEOP(CNENH); return;         /* Input change interrupt enable */
+    WRITEOP(CNSTATH); return;       /* Input change status */
+
+    WRITEOP(ANSELJ); return;        /* Port J: analog select */
+    WRITEOP(TRISJ); return;         /* Port J: mask of inputs */
+    WRITEOPX(PORTJ, LATJ);          /* Port J: write outputs */
+    WRITEOP(LATJ);                  /* Port J: write outputs */
+        pic32_gpio_write(s, 8, VALUE(LATJ));
+        return;
+    WRITEOP(ODCJ); return;          /* Port J: open drain configuration */
+    WRITEOP(CNPUJ); return;         /* Input pin pull-up */
+    WRITEOP(CNPDJ); return;         /* Input pin pull-down */
+    WRITEOP(CNCONJ); return;        /* Interrupt-on-change control */
+    WRITEOP(CNENJ); return;         /* Input change interrupt enable */
+    WRITEOP(CNSTATJ); return;       /* Input change status */
+
+    WRITEOP(TRISK); return;         /* Port K: mask of inputs */
+    WRITEOPX(PORTK, LATK);          /* Port K: write outputs */
+    WRITEOP(LATK);                  /* Port K: write outputs */
+        pic32_gpio_write(s, 9, VALUE(LATK));
+        return;
+    WRITEOP(ODCK); return;          /* Port K: open drain configuration */
+    WRITEOP(CNPUK); return;         /* Input pin pull-up */
+    WRITEOP(CNPDK); return;         /* Input pin pull-down */
+    WRITEOP(CNCONK); return;        /* Interrupt-on-change control */
+    WRITEOP(CNENK); return;         /* Input change interrupt enable */
+    WRITEOP(CNSTATK); return;       /* Input change status */
+
+    /*-------------------------------------------------------------------------
+     * UART 1.
+     */
+    STORAGE(U1TXREG);                               /* Transmit */
+        pic32_uart_put_char(s, 0, data);
+        break;
+    WRITEOP(U1MODE);                                /* Mode */
+        pic32_uart_update_mode(s, 0);
+        return;
+    WRITEOPR(U1STA,                                 /* Status and control */
+        PIC32_USTA_URXDA | PIC32_USTA_FERR | PIC32_USTA_PERR |
+        PIC32_USTA_RIDLE | PIC32_USTA_TRMT | PIC32_USTA_UTXBF);
+        pic32_uart_update_status(s, 0);
+        return;
+    WRITEOP(U1BRG); return;                         /* Baud rate */
+    READONLY(U1RXREG);                              /* Receive */
+
+    /*-------------------------------------------------------------------------
+     * UART 2.
+     */
+    STORAGE(U2TXREG);                               /* Transmit */
+        pic32_uart_put_char(s, 1, data);
+        break;
+    WRITEOP(U2MODE);                                /* Mode */
+        pic32_uart_update_mode(s, 1);
+        return;
+    WRITEOPR(U2STA,                                 /* Status and control */
+        PIC32_USTA_URXDA | PIC32_USTA_FERR | PIC32_USTA_PERR |
+        PIC32_USTA_RIDLE | PIC32_USTA_TRMT | PIC32_USTA_UTXBF);
+        pic32_uart_update_status(s, 1);
+        return;
+    WRITEOP(U2BRG); return;                         /* Baud rate */
+    READONLY(U2RXREG);                              /* Receive */
+
+    /*-------------------------------------------------------------------------
+     * UART 3.
+     */
+    STORAGE(U3TXREG);                               /* Transmit */
+        pic32_uart_put_char(s, 2, data);
+        break;
+    WRITEOP(U3MODE);                                /* Mode */
+        pic32_uart_update_mode(s, 2);
+        return;
+    WRITEOPR(U3STA,                                 /* Status and control */
+        PIC32_USTA_URXDA | PIC32_USTA_FERR | PIC32_USTA_PERR |
+        PIC32_USTA_RIDLE | PIC32_USTA_TRMT | PIC32_USTA_UTXBF);
+        pic32_uart_update_status(s, 2);
+        return;
+    WRITEOP(U3BRG); return;                         /* Baud rate */
+    READONLY(U3RXREG);                              /* Receive */
+
+    /*-------------------------------------------------------------------------
+     * UART 4.
+     */
+    STORAGE(U4TXREG);                               /* Transmit */
+        pic32_uart_put_char(s, 3, data);
+        break;
+    WRITEOP(U4MODE);                                /* Mode */
+        pic32_uart_update_mode(s, 3);
+        return;
+    WRITEOPR(U4STA,                                 /* Status and control */
+        PIC32_USTA_URXDA | PIC32_USTA_FERR | PIC32_USTA_PERR |
+        PIC32_USTA_RIDLE | PIC32_USTA_TRMT | PIC32_USTA_UTXBF);
+        pic32_uart_update_status(s, 3);
+        return;
+    WRITEOP(U4BRG); return;                         /* Baud rate */
+    READONLY(U4RXREG);                              /* Receive */
+
+    /*-------------------------------------------------------------------------
+     * UART 5.
+     */
+    STORAGE(U5TXREG);                               /* Transmit */
+        pic32_uart_put_char(s, 4, data);
+        break;
+    WRITEOP(U5MODE);                                /* Mode */
+        pic32_uart_update_mode(s, 4);
+        return;
+    WRITEOPR(U5STA,                                 /* Status and control */
+        PIC32_USTA_URXDA | PIC32_USTA_FERR | PIC32_USTA_PERR |
+        PIC32_USTA_RIDLE | PIC32_USTA_TRMT | PIC32_USTA_UTXBF);
+        pic32_uart_update_status(s, 4);
+        return;
+    WRITEOP(U5BRG); return;                         /* Baud rate */
+    READONLY(U5RXREG);                              /* Receive */
+
+    /*-------------------------------------------------------------------------
+     * UART 6.
+     */
+    STORAGE(U6TXREG);                               /* Transmit */
+        pic32_uart_put_char(s, 5, data);
+        break;
+    WRITEOP(U6MODE);                                /* Mode */
+        pic32_uart_update_mode(s, 5);
+        return;
+    WRITEOPR(U6STA,                                 /* Status and control */
+        PIC32_USTA_URXDA | PIC32_USTA_FERR | PIC32_USTA_PERR |
+        PIC32_USTA_RIDLE | PIC32_USTA_TRMT | PIC32_USTA_UTXBF);
+        pic32_uart_update_status(s, 5);
+        return;
+    WRITEOP(U6BRG); return;                         /* Baud rate */
+    READONLY(U6RXREG);                              /* Receive */
+
+    /*-------------------------------------------------------------------------
+     * SPI.
+     */
+    WRITEOP(SPI1CON);                               /* Control */
+        pic32_spi_control(s, 0);
+        return;
+    WRITEOPR(SPI1STAT, ~PIC32_SPISTAT_SPIROV);      /* Status */
+        return;                                     /* Only ROV bit writable */
+    STORAGE(SPI1BUF);                               /* Buffer */
+        pic32_spi_writebuf(s, 0, data);
+        return;
+    WRITEOP(SPI1BRG); return;                       /* Baud rate */
+    WRITEOP(SPI1CON2); return;                      /* Control 2 */
+
+    WRITEOP(SPI2CON);                               /* Control */
+        pic32_spi_control(s, 1);
+        return;
+    WRITEOPR(SPI2STAT, ~PIC32_SPISTAT_SPIROV);      /* Status */
+        return;                                     /* Only ROV bit writable */
+    STORAGE(SPI2BUF);                               /* Buffer */
+        pic32_spi_writebuf(s, 1, data);
+        return;
+    WRITEOP(SPI2BRG); return;                       /* Baud rate */
+    WRITEOP(SPI2CON2); return;                      /* Control 2 */
+
+    WRITEOP(SPI3CON);                               /* Control */
+        pic32_spi_control(s, 2);
+        return;
+    WRITEOPR(SPI3STAT, ~PIC32_SPISTAT_SPIROV);      /* Status */
+        return;                                     /* Only ROV bit writable */
+    STORAGE(SPI3BUF);                               /* Buffer */
+        pic32_spi_writebuf(s, 2, data);
+        return;
+    WRITEOP(SPI3BRG); return;                       /* Baud rate */
+    WRITEOP(SPI3CON2); return;                      /* Control 2 */
+
+    WRITEOP(SPI4CON);                               /* Control */
+        pic32_spi_control(s, 3);
+        return;
+    WRITEOPR(SPI4STAT, ~PIC32_SPISTAT_SPIROV);      /* Status */
+        return;                                     /* Only ROV bit writable */
+    STORAGE(SPI4BUF);                               /* Buffer */
+        pic32_spi_writebuf(s, 3, data);
+        return;
+    WRITEOP(SPI4BRG); return;                       /* Baud rate */
+    WRITEOP(SPI4CON2); return;                      /* Control 2 */
+
+    /*
+     * Timers 1-9.
+     */
+    WRITEOP(T1CON); return;
+    WRITEOP(TMR1); return;
+    WRITEOP(PR1); return;
+    WRITEOP(T2CON); return;
+    WRITEOP(TMR2); return;
+    WRITEOP(PR2); return;
+    WRITEOP(T3CON); return;
+    WRITEOP(TMR3); return;
+    WRITEOP(PR3); return;
+    WRITEOP(T4CON); return;
+    WRITEOP(TMR4); return;
+    WRITEOP(PR4); return;
+    WRITEOP(T5CON); return;
+    WRITEOP(TMR5); return;
+    WRITEOP(PR5); return;
+    WRITEOP(T6CON); return;
+    WRITEOP(TMR6); return;
+    WRITEOP(PR6); return;
+    WRITEOP(T7CON); return;
+    WRITEOP(TMR7); return;
+    WRITEOP(PR7); return;
+    WRITEOP(T8CON); return;
+    WRITEOP(TMR8); return;
+    WRITEOP(PR8); return;
+    WRITEOP(T9CON); return;
+    WRITEOP(TMR9); return;
+    WRITEOP(PR9); return;
+
+    /*-------------------------------------------------------------------------
+     * Ethernet.
+     */
+    WRITEOP(ETHCON1);                   /* Control 1 */
+        pic32_eth_control(s);
+        return;
+    WRITEOP(ETHCON2); return;           /* Control 2: RX data buffer size */
+    WRITEOP(ETHTXST); return;           /* Tx descriptor start address */
+    WRITEOP(ETHRXST); return;           /* Rx descriptor start address */
+    WRITEOP(ETHHT0); return;            /* Hash tasble 0 */
+    WRITEOP(ETHHT1); return;            /* Hash tasble 1 */
+    WRITEOP(ETHPMM0); return;           /* Pattern match mask 0 */
+    WRITEOP(ETHPMM1); return;           /* Pattern match mask 1 */
+    WRITEOP(ETHPMCS); return;           /* Pattern match checksum */
+    WRITEOP(ETHPMO); return;            /* Pattern match offset */
+    WRITEOP(ETHRXFC); return;           /* Receive filter configuration */
+    WRITEOP(ETHRXWM); return;           /* Receive watermarks */
+    WRITEOP(ETHIEN); return;            /* Interrupt enable */
+    WRITEOP(ETHIRQ); return;            /* Interrupt request */
+    STORAGE(ETHSTAT); break;            /* Status */
+    WRITEOP(ETHRXOVFLOW); return;       /* Receive overflow statistics */
+    WRITEOP(ETHFRMTXOK); return;        /* Frames transmitted OK statistics */
+    WRITEOP(ETHSCOLFRM); return;        /* Single collision frames statistics 
*/
+    WRITEOP(ETHMCOLFRM); return;        /* Multiple collision statistics */
+    WRITEOP(ETHFRMRXOK); return;        /* Frames received OK statistics */
+    WRITEOP(ETHFCSERR); return;         /* Frame check sequence statistics */
+    WRITEOP(ETHALGNERR); return;        /* Alignment errors statistics */
+    WRITEOP(EMAC1CFG1); return;         /* MAC configuration 1 */
+    WRITEOP(EMAC1CFG2); return;         /* MAC configuration 2 */
+    WRITEOP(EMAC1IPGT); return;         /* MAC back-to-back interpacket gap */
+    WRITEOP(EMAC1IPGR); return;         /* MAC non-back-to-back gap */
+    WRITEOP(EMAC1CLRT); return;         /* MAC collision window/retry limit */
+    WRITEOP(EMAC1MAXF); return;         /* MAC maximum frame length */
+    WRITEOP(EMAC1SUPP); return;         /* MAC PHY support */
+    WRITEOP(EMAC1TEST); return;         /* MAC test */
+    WRITEOP(EMAC1MCFG); return;         /* MII configuration */
+    WRITEOP(EMAC1MCMD);                 /* MII command */
+        pic32_mii_command(s);
+        return;
+    WRITEOP(EMAC1MADR); return;         /* MII address */
+    WRITEOP(EMAC1MWTD);                 /* MII write data */
+        pic32_mii_write(s);
+        return;
+    WRITEOP(EMAC1MRDD); return;         /* MII read data */
+    WRITEOP(EMAC1MIND); return;         /* MII indicators */
+    WRITEOP(EMAC1SA0); return;          /* MAC station address 0 */
+    WRITEOP(EMAC1SA1); return;          /* MAC station address 1 */
+    WRITEOP(EMAC1SA2); return;          /* MAC station address 2 */
+
+    /*-------------------------------------------------------------------------
+     * USB.
+     */
+    STORAGE(USBCSR0); break;
+    STORAGE(USBCSR1); break;
+    STORAGE(USBCSR2); break;
+    STORAGE(USBCSR3); break;
+    STORAGE(USBIENCSR0); break;
+    STORAGE(USBIENCSR1); break;
+    STORAGE(USBIENCSR2); break;
+    STORAGE(USBIENCSR3); break;
+    STORAGE(USBFIFO0); break;
+    STORAGE(USBFIFO1); break;
+    STORAGE(USBFIFO2); break;
+    STORAGE(USBFIFO3); break;
+    STORAGE(USBFIFO4); break;
+    STORAGE(USBFIFO5); break;
+    STORAGE(USBFIFO6); break;
+    STORAGE(USBFIFO7); break;
+    STORAGE(USBOTG); break;
+    STORAGE(USBFIFOA); break;
+    STORAGE(USBHWVER); break;
+    STORAGE(USBINFO); break;
+    STORAGE(USBEOFRST); break;
+    STORAGE(USBE0TXA); break;
+    STORAGE(USBE0RXA); break;
+    STORAGE(USBE1TXA); break;
+    STORAGE(USBE1RXA); break;
+    STORAGE(USBE2TXA); break;
+    STORAGE(USBE2RXA); break;
+    STORAGE(USBE3TXA); break;
+    STORAGE(USBE3RXA); break;
+    STORAGE(USBE4TXA); break;
+    STORAGE(USBE4RXA); break;
+    STORAGE(USBE5TXA); break;
+    STORAGE(USBE5RXA); break;
+    STORAGE(USBE6TXA); break;
+    STORAGE(USBE6RXA); break;
+    STORAGE(USBE7TXA); break;
+    STORAGE(USBE7RXA); break;
+    STORAGE(USBE0CSR0); break;
+    STORAGE(USBE0CSR2); break;
+    STORAGE(USBE0CSR3); break;
+    STORAGE(USBE1CSR0); break;
+    STORAGE(USBE1CSR1); break;
+    STORAGE(USBE1CSR2); break;
+    STORAGE(USBE1CSR3); break;
+    STORAGE(USBE2CSR0); break;
+    STORAGE(USBE2CSR1); break;
+    STORAGE(USBE2CSR2); break;
+    STORAGE(USBE2CSR3); break;
+    STORAGE(USBE3CSR0); break;
+    STORAGE(USBE3CSR1); break;
+    STORAGE(USBE3CSR2); break;
+    STORAGE(USBE3CSR3); break;
+    STORAGE(USBE4CSR0); break;
+    STORAGE(USBE4CSR1); break;
+    STORAGE(USBE4CSR2); break;
+    STORAGE(USBE4CSR3); break;
+    STORAGE(USBE5CSR0); break;
+    STORAGE(USBE5CSR1); break;
+    STORAGE(USBE5CSR2); break;
+    STORAGE(USBE5CSR3); break;
+    STORAGE(USBE6CSR0); break;
+    STORAGE(USBE6CSR1); break;
+    STORAGE(USBE6CSR2); break;
+    STORAGE(USBE6CSR3); break;
+    STORAGE(USBE7CSR0); break;
+    STORAGE(USBE7CSR1); break;
+    STORAGE(USBE7CSR2); break;
+    STORAGE(USBE7CSR3); break;
+    STORAGE(USBDMAINT); break;
+    STORAGE(USBDMA1C); break;
+    STORAGE(USBDMA1A); break;
+    STORAGE(USBDMA1N); break;
+    STORAGE(USBDMA2C); break;
+    STORAGE(USBDMA2A); break;
+    STORAGE(USBDMA2N); break;
+    STORAGE(USBDMA3C); break;
+    STORAGE(USBDMA3A); break;
+    STORAGE(USBDMA3N); break;
+    STORAGE(USBDMA4C); break;
+    STORAGE(USBDMA4A); break;
+    STORAGE(USBDMA4N); break;
+    STORAGE(USBDMA5C); break;
+    STORAGE(USBDMA5A); break;
+    STORAGE(USBDMA5N); break;
+    STORAGE(USBDMA6C); break;
+    STORAGE(USBDMA6A); break;
+    STORAGE(USBDMA6N); break;
+    STORAGE(USBDMA7C); break;
+    STORAGE(USBDMA7A); break;
+    STORAGE(USBDMA7N); break;
+    STORAGE(USBDMA8C); break;
+    STORAGE(USBDMA8A); break;
+    STORAGE(USBDMA8N); break;
+    STORAGE(USBE1RPC); break;
+    STORAGE(USBE2RPC); break;
+    STORAGE(USBE3RPC); break;
+    STORAGE(USBE4RPC); break;
+    STORAGE(USBE5RPC); break;
+    STORAGE(USBE6RPC); break;
+    STORAGE(USBE7RPC); break;
+    STORAGE(USBDPBFD); break;
+    STORAGE(USBTMCON1); break;
+    STORAGE(USBTMCON2); break;
+    STORAGE(USBLPMR1); break;
+    STORAGE(USBLMPR2); break;
+
+    default:
+        printf("--- Write %08x to 1f8%05x: peripheral register not 
supported\n",
+            data, offset);
+        if (TRACE) {
+            fprintf(qemu_logfile,
+            "--- Write %08x to 1f8%05x: peripheral register not supported\n",
+                data, offset);
+        }
+        exit(1);
+readonly:
+        printf("--- Write %08x to %s: readonly register\n",
+            data, *namep);
+        if (TRACE) {
+            fprintf(qemu_logfile, "--- Write %08x to %s: readonly register\n",
+                data, *namep);
+        }
+        *namep = 0;
+        return;
+    }
+    *bufp = data;
+}
+
+static uint64_t pic32_io_read(void *opaque, hwaddr addr, unsigned bytes)
+{
+    pic32_t *s = opaque;
+    uint32_t offset = addr & 0xfffff;
+    const char *name = "???";
+    uint32_t data = 0;
+
+    data = io_read32(s, offset & ~3, &name);
+    switch (bytes) {
+    case 1:
+        offset &= 3;
+        if (offset != 0) {
+            /* Unaligned read. */
+            data >>= offset * 8;
+        }
+        data = (uint8_t) data;
+        if (TRACE) {
+            fprintf(qemu_logfile, "--- I/O Read  %02x from %s\n", data, name);
+        }
+        break;
+    case 2:
+        if (offset & 2) {
+            /* Unaligned read. */
+            data >>= 16;
+        }
+        data = (uint16_t) data;
+        if (TRACE) {
+            fprintf(qemu_logfile, "--- I/O Read  %04x from %s\n", data, name);
+        }
+        break;
+    default:
+        if (TRACE) {
+            fprintf(qemu_logfile, "--- I/O Read  %08x from %s\n", data, name);
+        }
+        break;
+    }
+    return data;
+}
+
+static void pic32_io_write(void *opaque, hwaddr addr, uint64_t data,
+    unsigned bytes)
+{
+    pic32_t *s = opaque;
+    uint32_t offset = addr & 0xfffff;
+    const char *name = "???";
+
+    /* Fetch data and align to word format. */
+    switch (bytes) {
+    case 1:
+        data = (uint8_t) data;
+        data <<= (offset & 3) * 8;
+        break;
+    case 2:
+        data = (uint16_t) data;
+        data <<= (offset & 2) * 8;
+        break;
+    }
+    io_write32(s, offset & ~3, data, &name);
+
+    if (TRACE && name != 0) {
+        fprintf(qemu_logfile, "--- I/O Write %08x to %s\n",
+            (uint32_t) data, name);
+    }
+}
+
+/*
+ * This function is called when the processor is stopped
+ * on WAIT instruction, waiting for external interrupt.
+ */
+static void pic32_exec_exit(CPUState *cs)
+{
+    MIPSCPU *cpu = MIPS_CPU(cs);
+    CPUMIPSState *env = &cpu->env;
+
+    if (cs->halted && !(env->CP0_Status & (1 << CP0St_IE))) {
+        /* WAIT instruction with interrupts disabled - halt the simulation. */
+        qemu_system_shutdown_request();
+    }
+}
+
+static const MemoryRegionOps pic32_io_ops = {
+    .read       = pic32_io_read,
+    .write      = pic32_io_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static void main_cpu_reset(void *opaque)
+{
+    MIPSCPU *cpu = opaque;
+    CPUMIPSState *env = &cpu->env;
+    int i;
+
+    cpu_reset(CPU(cpu));
+
+    /* Adjust the initial configuration for microAptivUP core. */
+    env->CP0_IntCtl = 0x00030000;
+    env->CP0_Debug = (1 << CP0DB_CNT) | (5 << CP0DB_VER);
+    env->CP0_Performance0 = 0x80000000;
+    for (i = 0; i < 7; i++) {
+        env->CP0_WatchHi[i] = (i < 3) ? 0x80000000 : 0;
+    }
+}
+
+static void store_byte(unsigned address, unsigned char byte)
+{
+    if (address >= PROGRAM_FLASH_START &&
+        address < PROGRAM_FLASH_START + PROGRAM_FLASH_SIZE) {
+        /*printf("Store %02x to program memory %08x\n", byte, address);*/
+        prog_ptr[address & 0xfffff] = byte;
+        return;
+    }
+
+    if (address >= BOOT_FLASH_START &&
+        address < BOOT_FLASH_START + BOOT_FLASH_SIZE) {
+        /*printf("Store %02x to boot memory %08x\n", byte, address);*/
+        boot_ptr[address & 0xffff] = byte;
+        return;
+    }
+
+    printf("Bad hex file: "
+        "incorrect address %08X, must be %08X-%08X or %08X-%08X\n",
+        address, PROGRAM_FLASH_START,
+        PROGRAM_FLASH_START + PROGRAM_FLASH_SIZE - 1,
+        BOOT_FLASH_START, BOOT_FLASH_START + BOOT_FLASH_SIZE - 1);
+    exit(1);
+}
+
+/*
+ * Ignore ^C and ^\ signals and pass these characters to the target.
+ */
+static void pic32_pass_signal_chars(void)
+{
+    struct termios tty;
+
+    tcgetattr(0, &tty);
+    tty.c_lflag &= ~ISIG;
+    tcsetattr(0, TCSANOW, &tty);
+}
+
+static void pic32_init(MachineState *machine, int board_type)
+{
+    const char *cpu_model = machine->cpu_model;
+    unsigned ram_size = DATA_MEM_SIZE;
+    MemoryRegion *system_memory = get_system_memory();
+    MemoryRegion *ram_main = g_new(MemoryRegion, 1);
+    MemoryRegion *prog_mem = g_new(MemoryRegion, 1);
+    MemoryRegion *boot_mem = g_new(MemoryRegion, 1);
+    MemoryRegion *io_mem = g_new(MemoryRegion, 1);
+    MIPSCPU *cpu;
+    CPUMIPSState *env;
+
+    DeviceState *dev = qdev_create(NULL, TYPE_MIPS_PIC32);
+    pic32_t *s = OBJECT_CHECK(pic32_t, dev, TYPE_MIPS_PIC32);
+    s->board_type = board_type;
+    s->stop_on_reset = 1;               /* halt simulation on soft reset */
+    s->iomem = g_malloc0(IO_MEM_SIZE);  /* backing storage for I/O area */
+
+    qdev_init_nofail(dev);
+
+    /* Init CPU. */
+    if (!cpu_model) {
+        cpu_model = "microAptivUP";
+    }
+    printf("Board: %s\n", board_name[board_type]);
+    if (qemu_logfile) {
+        fprintf(qemu_logfile, "Board: %s\n", board_name[board_type]);
+    }
+    printf("Processor: %s\n", cpu_model);
+    if (qemu_logfile) {
+        fprintf(qemu_logfile, "Processor: %s\n", cpu_model);
+    }
+    cpu = cpu_mips_init(cpu_model);
+    if (!cpu) {
+        fprintf(stderr, "Unable to find CPU definition\n");
+        exit(1);
+    }
+    s->cpu = cpu;
+    env = &cpu->env;
+
+    /* Register RAM */
+    printf("RAM size: %u kbytes\n", ram_size / 1024);
+    if (qemu_logfile) {
+        fprintf(qemu_logfile, "RAM size: %u kbytes\n", ram_size / 1024);
+    }
+    memory_region_init_ram(ram_main, NULL, "kernel.ram",
+        ram_size, &error_abort);
+    vmstate_register_ram_global(ram_main);
+    memory_region_add_subregion(system_memory, DATA_MEM_START, ram_main);
+
+    /* Special function registers. */
+    memory_region_init_io(io_mem, NULL, &pic32_io_ops, s,
+                          "io", IO_MEM_SIZE);
+    memory_region_add_subregion(system_memory, IO_MEM_START, io_mem);
+
+    /*
+     * Map the flash memory.
+     */
+    memory_region_init_ram(boot_mem, NULL, "boot.flash",
+        BOOT_FLASH_SIZE, &error_abort);
+    memory_region_init_ram(prog_mem, NULL, "prog.flash",
+        PROGRAM_FLASH_SIZE, &error_abort);
+
+    /* Load a Flash memory image. */
+    if (!machine->kernel_filename) {
+        error_report("No -kernel argument was specified.");
+        exit(1);
+    }
+    prog_ptr = memory_region_get_ram_ptr(prog_mem);
+    boot_ptr = memory_region_get_ram_ptr(boot_mem);
+    if (bios_name) {
+        pic32_load_hex_file(bios_name, store_byte);
+    }
+    pic32_load_hex_file(machine->kernel_filename, store_byte);
+
+    memory_region_set_readonly(boot_mem, true);
+    memory_region_set_readonly(prog_mem, true);
+    memory_region_add_subregion(system_memory, BOOT_FLASH_START, boot_mem);
+    memory_region_add_subregion(system_memory, PROGRAM_FLASH_START, prog_mem);
+
+    /* Init internal devices */
+    s->irq_raise = irq_raise;
+    s->irq_clear = irq_clear;
+    qemu_register_reset(main_cpu_reset, cpu);
+
+    /* Setup interrupt controller in EIC mode. */
+    qemu_irq *qi = qemu_allocate_irqs(pic32_irq_request, s, 3);
+    env->eic_timer_irq = qi[0];
+    env->eic_soft_irq[0] = qi[1];
+    env->eic_soft_irq[1] = qi[2];
+    env->CP0_Config3 |= 1 << CP0C3_VEIC;
+
+    /* CPU runs at 200MHz.
+     * Count register increases at half this rate. */
+    cpu_mips_clock_init(env, 100*1000*1000);
+
+    /* Setup callback for CPU halt. */
+    MIPSCPUClass *mcc = MIPS_CPU_GET_CLASS(cpu);
+    CPUClass *cc = CPU_CLASS(mcc);
+    cc->cpu_exec_exit = pic32_exec_exit;
+
+    /*
+     * Initialize board-specific parameters.
+     */
+    int cs0_port, cs0_pin, cs1_port, cs1_pin;
+    switch (board_type) {
+    default:
+    case BOARD_WIFIRE:                      /* console on UART4 */
+        BOOTMEM(DEVCFG0) = 0xfffffff7;
+        BOOTMEM(DEVCFG1) = 0x7f743cb9;
+        BOOTMEM(DEVCFG2) = 0xfff9b11a;
+        BOOTMEM(DEVCFG3) = 0xbeffffff;
+        VALUE(DEVID)     = 0x4510e053;      /* MZ2048ECG100 rev A4 */
+        VALUE(OSCCON)    = 0x00001120;      /* external oscillator 24MHz */
+        s->sdcard_spi_port = 2;             /* SD card at SPI3, */
+        cs0_port = 2;  cs0_pin = 3;         /* select0 at C3, */
+        cs1_port = -1; cs1_pin = -1;        /* select1 not available */
+        break;
+    case BOARD_MEBII:                       /* console on UART1 */
+        BOOTMEM(DEVCFG0) = 0x7fffffdb;
+        BOOTMEM(DEVCFG1) = 0x0000fc81;
+        BOOTMEM(DEVCFG2) = 0x3ff8b11a;
+        BOOTMEM(DEVCFG3) = 0x86ffffff;
+        VALUE(DEVID)     = 0x45127053;      /* MZ2048ECH144 rev A4 */
+        VALUE(OSCCON)    = 0x00001120;      /* external oscillator 24MHz */
+        s->sdcard_spi_port = 1;             /* SD card at SPI2, */
+        cs0_port = 1;  cs0_pin = 14;        /* select0 at B14, */
+        cs1_port = -1; cs1_pin = -1;        /* select1 not available */
+        break;
+    case BOARD_EXPLORER16:                  /* console on UART1 */
+        BOOTMEM(DEVCFG0) = 0x7fffffdb;
+        BOOTMEM(DEVCFG1) = 0x0000fc81;
+        BOOTMEM(DEVCFG2) = 0x3ff8b11a;
+        BOOTMEM(DEVCFG3) = 0x86ffffff;
+        VALUE(DEVID)     = 0x35113053;      /* MZ2048ECH100 rev A3 */
+        VALUE(OSCCON)    = 0x00001120;      /* external oscillator 24MHz */
+        s->sdcard_spi_port = 0;             /* SD card at SPI1, */
+        cs0_port = 1;  cs0_pin = 1;         /* select0 at B1, */
+        cs1_port = 1;  cs1_pin = 2;         /* select1 at B2 */
+        break;
+    }
+
+    /* UARTs */
+    pic32_uart_init(s, 0, PIC32_IRQ_U1E, U1STA, U1MODE);
+    pic32_uart_init(s, 1, PIC32_IRQ_U2E, U2STA, U2MODE);
+    pic32_uart_init(s, 2, PIC32_IRQ_U3E, U3STA, U3MODE);
+    pic32_uart_init(s, 3, PIC32_IRQ_U4E, U4STA, U4MODE);
+    pic32_uart_init(s, 4, PIC32_IRQ_U5E, U5STA, U5MODE);
+    pic32_uart_init(s, 5, PIC32_IRQ_U6E, U6STA, U6MODE);
+
+    /* SPIs */
+    pic32_spi_init(s, 0, PIC32_IRQ_SPI1E, SPI1CON, SPI1STAT);
+    pic32_spi_init(s, 1, PIC32_IRQ_SPI2E, SPI2CON, SPI2STAT);
+    pic32_spi_init(s, 2, PIC32_IRQ_SPI3E, SPI3CON, SPI3STAT);
+    pic32_spi_init(s, 3, PIC32_IRQ_SPI4E, SPI4CON, SPI4STAT);
+    pic32_spi_init(s, 4, PIC32_IRQ_SPI5E, SPI5CON, SPI5STAT);
+    pic32_spi_init(s, 5, PIC32_IRQ_SPI6E, SPI6CON, SPI6STAT);
+
+    /*
+     * Load SD card images.
+     * Use options:
+     *      -sd filename
+     * or   -hda filename
+     * and  -hdb filename
+     */
+    const char *sd0_file = 0, *sd1_file = 0;
+    DriveInfo *dinfo = drive_get(IF_IDE, 0, 0);
+    if (dinfo) {
+        sd0_file = qemu_opt_get(dinfo->opts, "file");
+        dinfo->is_default = 1;
+
+        dinfo = drive_get(IF_IDE, 0, 1);
+        if (dinfo) {
+            sd1_file = qemu_opt_get(dinfo->opts, "file");
+            dinfo->is_default = 1;
+        }
+    }
+    if (!sd0_file) {
+        dinfo = drive_get(IF_SD, 0, 0);
+        if (dinfo) {
+            sd0_file = qemu_opt_get(dinfo->opts, "file");
+            dinfo->is_default = 1;
+        }
+    }
+    pic32_sdcard_init(s, 0, "sd0", sd0_file, cs0_port, cs0_pin);
+    pic32_sdcard_init(s, 1, "sd1", sd1_file, cs1_port, cs1_pin);
+
+    /* Ethernet. */
+    if (nd_table[0].used) {
+        pic32_eth_init(s, &nd_table[0]);
+    }
+    io_reset(s);
+    pic32_sdcard_reset(s);
+    pic32_pass_signal_chars();
+}
+
+static void pic32_init_wifire(MachineState *machine)
+{
+    pic32_init(machine, BOARD_WIFIRE);
+}
+
+static void pic32_init_meb2(MachineState *machine)
+{
+    pic32_init(machine, BOARD_MEBII);
+}
+
+static void pic32_init_explorer16(MachineState *machine)
+{
+    pic32_init(machine, BOARD_EXPLORER16);
+}
+
+static int pic32_sysbus_device_init(SysBusDevice *sysbusdev)
+{
+    return 0;
+}
+
+static void pic32_class_init(ObjectClass *klass, void *data)
+{
+    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+    k->init = pic32_sysbus_device_init;
+}
+
+static const TypeInfo pic32_device = {
+    .name          = TYPE_MIPS_PIC32,
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(pic32_t),
+    .class_init    = pic32_class_init,
+};
+
+static void pic32_register_types(void)
+{
+    type_register_static(&pic32_device);
+}
+
+static QEMUMachine pic32_board[3] = {
+    {
+        .name       = "pic32mz-wifire",
+        .desc       = "PIC32MZ microcontroller on chipKIT WiFire board",
+        .init       = pic32_init_wifire,
+        .max_cpus   = 1,
+    },
+    {
+        .name       = "pic32mz-meb2",
+        .desc       = "PIC32MZ microcontroller on Microchip MEB-II board",
+        .init       = pic32_init_meb2,
+        .max_cpus   = 1,
+    },
+    {
+        .name       = "pic32mz-explorer16",
+        .desc       = "PIC32MZ microcontroller on Microchip Explorer-16 board",
+        .init       = pic32_init_explorer16,
+        .max_cpus   = 1,
+    },
+};
+
+static void pic32_machine_init(void)
+{
+    qemu_register_machine(&pic32_board[0]);
+    qemu_register_machine(&pic32_board[1]);
+    qemu_register_machine(&pic32_board[2]);
+}
+
+type_init(pic32_register_types)
+machine_init(pic32_machine_init);
-- 
2.2.2




reply via email to

[Prev in Thread] Current Thread [Next in Thread]