summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--.travis.yml7
-rw-r--r--MAINTAINERS7
-rw-r--r--default-configs/m68k-softmmu.mak1
-rw-r--r--hw/char/escc.c30
-rw-r--r--hw/display/Makefile.objs1
-rw-r--r--hw/display/next-fb.c146
-rw-r--r--hw/m68k/Kconfig5
-rw-r--r--hw/m68k/Makefile.objs1
-rw-r--r--hw/m68k/next-cube.c978
-rw-r--r--hw/m68k/next-kbd.c291
-rw-r--r--include/hw/char/escc.h1
-rw-r--r--include/hw/m68k/next-cube.h47
-rw-r--r--tests/acceptance/machine_m68k_nextcube.py121
-rw-r--r--tests/boot-serial-test.c12
14 files changed, 1641 insertions, 7 deletions
diff --git a/.travis.yml b/.travis.yml
index 92b00927d4..106f9b5d01 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -232,15 +232,20 @@ matrix:
 
     # Acceptance (Functional) tests
     - env:
-        - CONFIG="--python=/usr/bin/python3 --target-list=x86_64-softmmu,mips-softmmu,mips64el-softmmu,aarch64-softmmu,arm-softmmu,s390x-softmmu,alpha-softmmu,ppc64-softmmu"
+        - CONFIG="--python=/usr/bin/python3 --target-list=x86_64-softmmu,mips-softmmu,mips64el-softmmu,aarch64-softmmu,arm-softmmu,s390x-softmmu,alpha-softmmu,ppc64-softmmu,m68k-softmmu"
         - TEST_CMD="make check-acceptance"
       after_failure:
         - cat tests/results/latest/job.log
       addons:
         apt:
           packages:
+            - python3-pil
             - python3-pip
             - python3.5-venv
+            - tesseract-ocr
+            - tesseract-ocr-eng
+
+
     # Using newer GCC with sanitizers
     - addons:
         apt:
diff --git a/MAINTAINERS b/MAINTAINERS
index 65d8a7c2d2..50eaf005f4 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -910,6 +910,13 @@ F: hw/char/mcf_uart.c
 F: hw/net/mcf_fec.c
 F: include/hw/m68k/mcf*.h
 
+NeXTcube
+M: Thomas Huth <huth@tuxfamily.org>
+S: Odd Fixes
+F: hw/m68k/next-*.c
+F: hw/display/next-fb.c
+F: include/hw/m68k/next-cube.h
+
 MicroBlaze Machines
 -------------------
 petalogix_s3adsp1800
diff --git a/default-configs/m68k-softmmu.mak b/default-configs/m68k-softmmu.mak
index 4049a8f2ba..d67ab8b96d 100644
--- a/default-configs/m68k-softmmu.mak
+++ b/default-configs/m68k-softmmu.mak
@@ -6,3 +6,4 @@ CONFIG_SEMIHOSTING=y
 #
 CONFIG_AN5206=y
 CONFIG_MCF5208=y
+CONFIG_NEXTCUBE=y
diff --git a/hw/char/escc.c b/hw/char/escc.c
index e185522e27..8f7bf322cb 100644
--- a/hw/char/escc.c
+++ b/hw/char/escc.c
@@ -45,14 +45,21 @@
  * mouse and keyboard ports don't implement all functions and they are
  * only asynchronous. There is no DMA.
  *
- * Z85C30 is also used on PowerMacs. There are some small differences
- * between Sparc version (sunzilog) and PowerMac (pmac):
+ * Z85C30 is also used on PowerMacs and m68k Macs.
+ *
+ * There are some small differences between Sparc version (sunzilog)
+ * and PowerMac (pmac):
  *  Offset between control and data registers
  *  There is some kind of lockup bug, but we can ignore it
  *  CTS is inverted
  *  DMA on pmac using DBDMA chip
  *  pmac can do IRDA and faster rates, sunzilog can only do 38400
  *  pmac baud rate generator clock is 3.6864 MHz, sunzilog 4.9152 MHz
+ *
+ * Linux driver for m68k Macs is the same as for PowerMac (pmac_zilog),
+ * but registers are grouped by type and not by channel:
+ * channel is selected by bit 0 of the address (instead of bit 1)
+ * and register is selected by bit 1 of the address (instead of bit 0).
  */
 
 /*
@@ -172,6 +179,16 @@ static void handle_kbd_command(ESCCChannelState *s, int val);
 static int serial_can_receive(void *opaque);
 static void serial_receive_byte(ESCCChannelState *s, int ch);
 
+static int reg_shift(ESCCState *s)
+{
+    return s->bit_swap ? s->it_shift + 1 : s->it_shift;
+}
+
+static int chn_shift(ESCCState *s)
+{
+    return s->bit_swap ? s->it_shift : s->it_shift + 1;
+}
+
 static void clear_queue(void *opaque)
 {
     ESCCChannelState *s = opaque;
@@ -436,8 +453,8 @@ static void escc_mem_write(void *opaque, hwaddr addr,
     int newreg, channel;
 
     val &= 0xff;
-    saddr = (addr >> serial->it_shift) & 1;
-    channel = (addr >> (serial->it_shift + 1)) & 1;
+    saddr = (addr >> reg_shift(serial)) & 1;
+    channel = (addr >> chn_shift(serial)) & 1;
     s = &serial->chn[channel];
     switch (saddr) {
     case SERIAL_CTRL:
@@ -547,8 +564,8 @@ static uint64_t escc_mem_read(void *opaque, hwaddr addr,
     uint32_t ret;
     int channel;
 
-    saddr = (addr >> serial->it_shift) & 1;
-    channel = (addr >> (serial->it_shift + 1)) & 1;
+    saddr = (addr >> reg_shift(serial)) & 1;
+    channel = (addr >> chn_shift(serial)) & 1;
     s = &serial->chn[channel];
     switch (saddr) {
     case SERIAL_CTRL:
@@ -832,6 +849,7 @@ static void escc_realize(DeviceState *dev, Error **errp)
 static Property escc_properties[] = {
     DEFINE_PROP_UINT32("frequency", ESCCState, frequency,   0),
     DEFINE_PROP_UINT32("it_shift",  ESCCState, it_shift,    0),
+    DEFINE_PROP_BOOL("bit_swap",    ESCCState, bit_swap,    false),
     DEFINE_PROP_UINT32("disabled",  ESCCState, disabled,    0),
     DEFINE_PROP_UINT32("chnBtype",  ESCCState, chn[0].type, 0),
     DEFINE_PROP_UINT32("chnAtype",  ESCCState, chn[1].type, 0),
diff --git a/hw/display/Makefile.objs b/hw/display/Makefile.objs
index 0f11d55b14..5a4066383b 100644
--- a/hw/display/Makefile.objs
+++ b/hw/display/Makefile.objs
@@ -38,6 +38,7 @@ common-obj-$(CONFIG_RASPI) += bcm2835_fb.o
 common-obj-$(CONFIG_SM501) += sm501.o
 common-obj-$(CONFIG_TCX) += tcx.o
 common-obj-$(CONFIG_CG3) += cg3.o
+common-obj-$(CONFIG_NEXTCUBE) += next-fb.o
 
 obj-$(CONFIG_VGA) += vga.o
 
diff --git a/hw/display/next-fb.c b/hw/display/next-fb.c
new file mode 100644
index 0000000000..2b726a10f8
--- /dev/null
+++ b/hw/display/next-fb.c
@@ -0,0 +1,146 @@
+/*
+ * NeXT Cube/Station Framebuffer Emulation
+ *
+ * Copyright (c) 2011 Bryce Lanham
+ *
+ * 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 "qemu/osdep.h"
+#include "qapi/error.h"
+#include "ui/console.h"
+#include "hw/hw.h"
+#include "hw/boards.h"
+#include "hw/loader.h"
+#include "hw/display/framebuffer.h"
+#include "ui/pixel_ops.h"
+#include "hw/m68k/next-cube.h"
+
+#define NEXTFB(obj) OBJECT_CHECK(NeXTFbState, (obj), TYPE_NEXTFB)
+
+struct NeXTFbState {
+    SysBusDevice parent_obj;
+
+    MemoryRegion fb_mr;
+    MemoryRegionSection fbsection;
+    QemuConsole *con;
+
+    uint32_t cols;
+    uint32_t rows;
+    int invalidate;
+};
+typedef struct NeXTFbState NeXTFbState;
+
+static void nextfb_draw_line(void *opaque, uint8_t *d, const uint8_t *s,
+                             int width, int pitch)
+{
+    NeXTFbState *nfbstate = NEXTFB(opaque);
+    static const uint32_t pal[4] = {
+        0xFFFFFFFF, 0xFFAAAAAA, 0xFF555555, 0xFF000000
+    };
+    uint32_t *buf = (uint32_t *)d;
+    int i = 0;
+
+    for (i = 0; i < nfbstate->cols / 4; i++) {
+        int j = i * 4;
+        uint8_t src = s[i];
+        buf[j + 3] = pal[src & 0x3];
+        src >>= 2;
+        buf[j + 2] = pal[src & 0x3];
+        src >>= 2;
+        buf[j + 1] = pal[src & 0x3];
+        src >>= 2;
+        buf[j + 0] = pal[src & 0x3];
+    }
+}
+
+static void nextfb_update(void *opaque)
+{
+    NeXTFbState *s = NEXTFB(opaque);
+    int dest_width = 4;
+    int src_width;
+    int first = 0;
+    int last  = 0;
+    DisplaySurface *surface = qemu_console_surface(s->con);
+
+    src_width = s->cols / 4 + 8;
+    dest_width = s->cols * 4;
+
+    if (s->invalidate) {
+        framebuffer_update_memory_section(&s->fbsection, &s->fb_mr, 0,
+                                          s->cols, src_width);
+        s->invalidate = 0;
+    }
+
+    framebuffer_update_display(surface, &s->fbsection, s->cols, s->rows,
+                               src_width, dest_width, 0, 1, nextfb_draw_line,
+                               s, &first, &last);
+
+    dpy_gfx_update(s->con, 0, 0, s->cols, s->rows);
+}
+
+static void nextfb_invalidate(void *opaque)
+{
+    NeXTFbState *s = NEXTFB(opaque);
+    s->invalidate = 1;
+}
+
+static const GraphicHwOps nextfb_ops = {
+    .invalidate  = nextfb_invalidate,
+    .gfx_update  = nextfb_update,
+};
+
+static void nextfb_realize(DeviceState *dev, Error **errp)
+{
+    NeXTFbState *s = NEXTFB(dev);
+
+    memory_region_init_ram(&s->fb_mr, OBJECT(dev), "next-video", 0x1CB100,
+                           &error_fatal);
+    sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->fb_mr);
+
+    s->invalidate = 1;
+    s->cols = 1120;
+    s->rows = 832;
+
+    s->con = graphic_console_init(dev, 0, &nextfb_ops, s);
+    qemu_console_resize(s->con, s->cols, s->rows);
+}
+
+static void nextfb_class_init(ObjectClass *oc, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(oc);
+
+    set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories);
+    dc->realize = nextfb_realize;
+
+    /* Note: This device does not any state that we have to reset or migrate */
+}
+
+static const TypeInfo nextfb_info = {
+    .name          = TYPE_NEXTFB,
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(NeXTFbState),
+    .class_init    = nextfb_class_init,
+};
+
+static void nextfb_register_types(void)
+{
+    type_register_static(&nextfb_info);
+}
+
+type_init(nextfb_register_types)
diff --git a/hw/m68k/Kconfig b/hw/m68k/Kconfig
index 49ef0b3f6d..a74fac5abd 100644
--- a/hw/m68k/Kconfig
+++ b/hw/m68k/Kconfig
@@ -7,3 +7,8 @@ config MCF5208
     bool
     select COLDFIRE
     select PTIMER
+
+config NEXTCUBE
+    bool
+    select FRAMEBUFFER
+    select ESCC
diff --git a/hw/m68k/Makefile.objs b/hw/m68k/Makefile.objs
index 482f8477b4..f25854730d 100644
--- a/hw/m68k/Makefile.objs
+++ b/hw/m68k/Makefile.objs
@@ -1,2 +1,3 @@
 obj-$(CONFIG_AN5206) += an5206.o mcf5206.o
 obj-$(CONFIG_MCF5208) += mcf5208.o mcf_intc.o
+obj-$(CONFIG_NEXTCUBE) += next-kbd.o next-cube.o
diff --git a/hw/m68k/next-cube.c b/hw/m68k/next-cube.c
new file mode 100644
index 0000000000..9a4a7328f9
--- /dev/null
+++ b/hw/m68k/next-cube.c
@@ -0,0 +1,978 @@
+/*
+ * NeXT Cube System Driver
+ *
+ * Copyright (c) 2011 Bryce Lanham
+ *
+ * This code is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2 of the License,
+ * or (at your option) any later version.
+ */
+
+#include "qemu/osdep.h"
+#include "cpu.h"
+#include "exec/hwaddr.h"
+#include "exec/address-spaces.h"
+#include "sysemu/sysemu.h"
+#include "sysemu/qtest.h"
+#include "hw/irq.h"
+#include "hw/m68k/next-cube.h"
+#include "hw/boards.h"
+#include "hw/loader.h"
+#include "hw/scsi/esp.h"
+#include "hw/sysbus.h"
+#include "hw/char/escc.h" /* ZILOG 8530 Serial Emulation */
+#include "hw/block/fdc.h"
+#include "hw/qdev-properties.h"
+#include "qapi/error.h"
+#include "ui/console.h"
+#include "target/m68k/cpu.h"
+
+/* #define DEBUG_NEXT */
+#ifdef DEBUG_NEXT
+#define DPRINTF(fmt, ...) \
+    do { printf("NeXT: " fmt , ## __VA_ARGS__); } while (0)
+#else
+#define DPRINTF(fmt, ...) do { } while (0)
+#endif
+
+#define TYPE_NEXT_MACHINE MACHINE_TYPE_NAME("next-cube")
+#define NEXT_MACHINE(obj) OBJECT_CHECK(NeXTState, (obj), TYPE_NEXT_MACHINE)
+
+#define ENTRY       0x0100001e
+#define RAM_SIZE    0x4000000
+#define ROM_FILE    "Rev_2.5_v66.bin"
+
+typedef struct next_dma {
+    uint32_t csr;
+
+    uint32_t saved_next;
+    uint32_t saved_limit;
+    uint32_t saved_start;
+    uint32_t saved_stop;
+
+    uint32_t next;
+    uint32_t limit;
+    uint32_t start;
+    uint32_t stop;
+
+    uint32_t next_initbuf;
+    uint32_t size;
+} next_dma;
+
+typedef struct {
+    MachineState parent;
+
+    uint32_t int_mask;
+    uint32_t int_status;
+
+    uint8_t scsi_csr_1;
+    uint8_t scsi_csr_2;
+    next_dma dma[10];
+    qemu_irq *scsi_irq;
+    qemu_irq scsi_dma;
+    qemu_irq scsi_reset;
+    qemu_irq *fd_irq;
+
+    uint32_t scr1;
+    uint32_t scr2;
+
+    uint8_t rtc_ram[32];
+} NeXTState;
+
+/* Thanks to NeXT forums for this */
+/*
+static const uint8_t rtc_ram3[32] = {
+    0x94, 0x0f, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0xfb, 0x6d, 0x00, 0x00, 0x7B, 0x00,
+    0x00, 0x00, 0x65, 0x6e, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x13
+};
+*/
+static const uint8_t rtc_ram2[32] = {
+    0x94, 0x0f, 0x40, 0x03, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0xfb, 0x6d, 0x00, 0x00, 0x4b, 0x00,
+    0x41, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x84, 0x7e,
+};
+
+#define SCR2_RTCLK 0x2
+#define SCR2_RTDATA 0x4
+#define SCR2_TOBCD(x) (((x / 10) << 4) + (x % 10))
+
+static void nextscr2_write(NeXTState *s, uint32_t val, int size)
+{
+    static int led;
+    static int phase;
+    static uint8_t old_scr2;
+    static uint8_t rtc_command;
+    static uint8_t rtc_value;
+    static uint8_t rtc_status = 0x90;
+    static uint8_t rtc_return;
+    uint8_t scr2_2;
+
+    if (size == 4) {
+        scr2_2 = (val >> 8) & 0xFF;
+    } else {
+        scr2_2 = val & 0xFF;
+    }
+
+    if (val & 0x1) {
+        DPRINTF("fault!\n");
+        led++;
+        if (led == 10) {
+            DPRINTF("LED flashing, possible fault!\n");
+            led = 0;
+        }
+    }
+
+    if (scr2_2 & 0x1) {
+        /* DPRINTF("RTC %x phase %i\n", scr2_2, phase); */
+        if (phase == -1) {
+            phase = 0;
+        }
+        /* If we are in going down clock... do something */
+        if (((old_scr2 & SCR2_RTCLK) != (scr2_2 & SCR2_RTCLK)) &&
+                ((scr2_2 & SCR2_RTCLK) == 0)) {
+            if (phase < 8) {
+                rtc_command = (rtc_command << 1) |
+                              ((scr2_2 & SCR2_RTDATA) ? 1 : 0);
+            }
+            if (phase >= 8 && phase < 16) {
+                rtc_value = (rtc_value << 1) | ((scr2_2 & SCR2_RTDATA) ? 1 : 0);
+
+                /* if we read RAM register, output RT_DATA bit */
+                if (rtc_command <= 0x1F) {
+                    scr2_2 = scr2_2 & (~SCR2_RTDATA);
+                    if (s->rtc_ram[rtc_command] & (0x80 >> (phase - 8))) {
+                        scr2_2 |= SCR2_RTDATA;
+                    }
+
+                    rtc_return = (rtc_return << 1) |
+                                 ((scr2_2 & SCR2_RTDATA) ? 1 : 0);
+                }
+                /* read the status 0x30 */
+                if (rtc_command == 0x30) {
+                    scr2_2 = scr2_2 & (~SCR2_RTDATA);
+                    /* for now status = 0x98 (new rtc + FTU) */
+                    if (rtc_status & (0x80 >> (phase - 8))) {
+                        scr2_2 |= SCR2_RTDATA;
+                    }
+
+                    rtc_return = (rtc_return << 1) |
+                                 ((scr2_2 & SCR2_RTDATA) ? 1 : 0);
+                }
+                /* read the status 0x31 */
+                if (rtc_command == 0x31) {
+                    scr2_2 = scr2_2 & (~SCR2_RTDATA);
+                    /* for now 0x00 */
+                    if (0x00 & (0x80 >> (phase - 8))) {
+                        scr2_2 |= SCR2_RTDATA;
+                    }
+                    rtc_return = (rtc_return << 1) |
+                                 ((scr2_2 & SCR2_RTDATA) ? 1 : 0);
+                }
+
+                if ((rtc_command >= 0x20) && (rtc_command <= 0x2F)) {
+                    scr2_2 = scr2_2 & (~SCR2_RTDATA);
+                    /* for now 0x00 */
+                    time_t time_h = time(NULL);
+                    struct tm *info = localtime(&time_h);
+                    int ret = 0;
+
+                    switch (rtc_command) {
+                    case 0x20:
+                        ret = SCR2_TOBCD(info->tm_sec);
+                        break;
+                    case 0x21:
+                        ret = SCR2_TOBCD(info->tm_min);
+                        break;
+                    case 0x22:
+                        ret = SCR2_TOBCD(info->tm_hour);
+                        break;
+                    case 0x24:
+                        ret = SCR2_TOBCD(info->tm_mday);
+                        break;
+                    case 0x25:
+                        ret = SCR2_TOBCD((info->tm_mon + 1));
+                        break;
+                    case 0x26:
+                        ret = SCR2_TOBCD((info->tm_year - 100));
+                        break;
+
+                    }
+
+                    if (ret & (0x80 >> (phase - 8))) {
+                        scr2_2 |= SCR2_RTDATA;
+                    }
+                    rtc_return = (rtc_return << 1) |
+                                 ((scr2_2 & SCR2_RTDATA) ? 1 : 0);
+                }
+
+            }
+
+            phase++;
+            if (phase == 16) {
+                if (rtc_command >= 0x80 && rtc_command <= 0x9F) {
+                    s->rtc_ram[rtc_command - 0x80] = rtc_value;
+                }
+                /* write to x30 register */
+                if (rtc_command == 0xB1) {
+                    /* clear FTU */
+                    if (rtc_value & 0x04) {
+                        rtc_status = rtc_status & (~0x18);
+                        s->int_status = s->int_status & (~0x04);
+                    }
+                }
+            }
+        }
+    } else {
+        /* else end or abort */
+        phase = -1;
+        rtc_command = 0;
+        rtc_value = 0;
+    }
+    s->scr2 = val & 0xFFFF00FF;
+    s->scr2 |= scr2_2 << 8;
+    old_scr2 = scr2_2;
+}
+
+static uint32_t mmio_readb(NeXTState *s, hwaddr addr)
+{
+    switch (addr) {
+    case 0xc000:
+        return (s->scr1 >> 24) & 0xFF;
+    case 0xc001:
+        return (s->scr1 >> 16) & 0xFF;
+    case 0xc002:
+        return (s->scr1 >> 8)  & 0xFF;
+    case 0xc003:
+        return (s->scr1 >> 0)  & 0xFF;
+
+    case 0xd000:
+        return (s->scr2 >> 24) & 0xFF;
+    case 0xd001:
+        return (s->scr2 >> 16) & 0xFF;
+    case 0xd002:
+        return (s->scr2 >> 8)  & 0xFF;
+    case 0xd003:
+        return (s->scr2 >> 0)  & 0xFF;
+    case 0x14020:
+        DPRINTF("MMIO Read 0x4020\n");
+        return 0x7f;
+
+    default:
+        DPRINTF("MMIO Read B @ %"HWADDR_PRIx"\n", addr);
+        return 0x0;
+    }
+}
+
+static uint32_t mmio_readw(NeXTState *s, hwaddr addr)
+{
+    switch (addr) {
+    default:
+        DPRINTF("MMIO Read W @ %"HWADDR_PRIx"\n", addr);
+        return 0x0;
+    }
+}
+
+static uint32_t mmio_readl(NeXTState *s, hwaddr addr)
+{
+    switch (addr) {
+    case 0x7000:
+        /* DPRINTF("Read INT status: %x\n", s->int_status); */
+        return s->int_status;
+
+    case 0x7800:
+        DPRINTF("MMIO Read INT mask: %x\n", s->int_mask);
+        return s->int_mask;
+
+    case 0xc000:
+        return s->scr1;
+
+    case 0xd000:
+        return s->scr2;
+
+    default:
+        DPRINTF("MMIO Read L @ %"HWADDR_PRIx"\n", addr);
+        return 0x0;
+    }
+}
+
+static void mmio_writeb(NeXTState *s, hwaddr addr, uint32_t val)
+{
+    switch (addr) {
+    case 0xd003:
+        nextscr2_write(s, val, 1);
+        break;
+    default:
+        DPRINTF("MMIO Write B @ %x with %x\n", (unsigned int)addr, val);
+    }
+
+}
+
+static void mmio_writew(NeXTState *s, hwaddr addr, uint32_t val)
+{
+    DPRINTF("MMIO Write W\n");
+}
+
+static void mmio_writel(NeXTState *s, hwaddr addr, uint32_t val)
+{
+    switch (addr) {
+    case 0x7000:
+        DPRINTF("INT Status old: %x new: %x\n", s->int_status, val);
+        s->int_status = val;
+        break;
+    case 0x7800:
+        DPRINTF("INT Mask old: %x new: %x\n", s->int_mask, val);
+        s->int_mask  = val;
+        break;
+    case 0xc000:
+        DPRINTF("SCR1 Write: %x\n", val);
+        break;
+    case 0xd000:
+        nextscr2_write(s, val, 4);
+        break;
+
+    default:
+        DPRINTF("MMIO Write l @ %x with %x\n", (unsigned int)addr, val);
+    }
+}
+
+static uint64_t mmio_readfn(void *opaque, hwaddr addr, unsigned size)
+{
+    NeXTState *ns = NEXT_MACHINE(opaque);
+
+    switch (size) {
+    case 1:
+        return mmio_readb(ns, addr);
+    case 2:
+        return mmio_readw(ns, addr);
+    case 4:
+        return mmio_readl(ns, addr);
+    default:
+        g_assert_not_reached();
+    }
+}
+
+static void mmio_writefn(void *opaque, hwaddr addr, uint64_t value,
+                         unsigned size)
+{
+    NeXTState *ns = NEXT_MACHINE(opaque);
+
+    switch (size) {
+    case 1:
+        mmio_writeb(ns, addr, value);
+        break;
+    case 2:
+        mmio_writew(ns, addr, value);
+        break;
+    case 4:
+        mmio_writel(ns, addr, value);
+        break;
+    default:
+        g_assert_not_reached();
+    }
+}
+
+static const MemoryRegionOps mmio_ops = {
+    .read = mmio_readfn,
+    .write = mmio_writefn,
+    .valid.min_access_size = 1,
+    .valid.max_access_size = 4,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static uint32_t scr_readb(NeXTState *s, hwaddr addr)
+{
+    switch (addr) {
+    case 0x14108:
+        DPRINTF("FD read @ %x\n", (unsigned int)addr);
+        return 0x40 | 0x04 | 0x2 | 0x1;
+    case 0x14020:
+        DPRINTF("SCSI 4020  STATUS READ %X\n", s->scsi_csr_1);
+        return s->scsi_csr_1;
+
+    case 0x14021:
+        DPRINTF("SCSI 4021 STATUS READ %X\n", s->scsi_csr_2);
+        return 0x40;
+
+    /*
+     * These 4 registers are the hardware timer, not sure which register
+     * is the latch instead of data, but no problems so far
+     */
+    case 0x1a000:
+        return 0xff & (clock() >> 24);
+    case 0x1a001:
+        return 0xff & (clock() >> 16);
+    case 0x1a002:
+        return 0xff & (clock() >> 8);
+    case 0x1a003:
+        /* Hack: We need to have this change consistently to make it work */
+        return 0xFF & clock();
+
+    default:
+        DPRINTF("BMAP Read B @ %x\n", (unsigned int)addr);
+        return 0;
+    }
+}
+
+static uint32_t scr_readw(NeXTState *s, hwaddr addr)
+{
+    DPRINTF("BMAP Read W @ %x\n", (unsigned int)addr);
+    return 0;
+}
+
+static uint32_t scr_readl(NeXTState *s, hwaddr addr)
+{
+    DPRINTF("BMAP Read L @ %x\n", (unsigned int)addr);
+    return 0;
+}
+
+#define SCSICSR_ENABLE  0x01
+#define SCSICSR_RESET   0x02  /* reset scsi dma */
+#define SCSICSR_FIFOFL  0x04
+#define SCSICSR_DMADIR  0x08  /* if set, scsi to mem */
+#define SCSICSR_CPUDMA  0x10  /* if set, dma enabled */
+#define SCSICSR_INTMASK 0x20  /* if set, interrupt enabled */
+
+static void scr_writeb(NeXTState *s, hwaddr addr, uint32_t value)
+{
+    switch (addr) {
+    case 0x14108:
+        DPRINTF("FDCSR Write: %x\n", value);
+
+        if (value == 0x0) {
+            /* qemu_irq_raise(s->fd_irq[0]); */
+        }
+        break;
+    case 0x14020: /* SCSI Control Register */
+        if (value & SCSICSR_FIFOFL) {
+            DPRINTF("SCSICSR FIFO Flush\n");
+            /* will have to add another irq to the esp if this is needed */
+            /* esp_puflush_fifo(esp_g); */
+            /* qemu_irq_pulse(s->scsi_dma); */
+        }
+
+        if (value & SCSICSR_ENABLE) {
+            DPRINTF("SCSICSR Enable\n");
+            /*
+             * qemu_irq_raise(s->scsi_dma);
+             * s->scsi_csr_1 = 0xc0;
+             * s->scsi_csr_1 |= 0x1;
+             * qemu_irq_pulse(s->scsi_dma);
+             */
+        }
+        /*
+         * else
+         *     s->scsi_csr_1 &= ~SCSICSR_ENABLE;
+         */
+
+        if (value & SCSICSR_RESET) {
+            DPRINTF("SCSICSR Reset\n");
+            /* I think this should set DMADIR. CPUDMA and INTMASK to 0 */
+            /* qemu_irq_raise(s->scsi_reset); */
+            /* s->scsi_csr_1 &= ~(SCSICSR_INTMASK |0x80|0x1); */
+
+        }
+        if (value & SCSICSR_DMADIR) {
+            DPRINTF("SCSICSR DMAdir\n");
+        }
+        if (value & SCSICSR_CPUDMA) {
+            DPRINTF("SCSICSR CPUDMA\n");
+            /* qemu_irq_raise(s->scsi_dma); */
+
+            s->int_status |= 0x4000000;
+        } else {
+            s->int_status &= ~(0x4000000);
+        }
+        if (value & SCSICSR_INTMASK) {
+            DPRINTF("SCSICSR INTMASK\n");
+            /*
+             * int_mask &= ~0x1000;
+             * s->scsi_csr_1 |= value;
+             * s->scsi_csr_1 &= ~SCSICSR_INTMASK;
+             * if (s->scsi_queued) {
+             *     s->scsi_queued = 0;
+             *     next_irq(s, NEXT_SCSI_I, level);
+             * }
+             */
+        } else {
+            /* int_mask |= 0x1000; */
+        }
+        if (value & 0x80) {
+            /* int_mask |= 0x1000; */
+            /* s->scsi_csr_1 |= 0x80; */
+        }
+        DPRINTF("SCSICSR Write: %x\n", value);
+        /* s->scsi_csr_1 = value; */
+        return;
+    /* Hardware timer latch - not implemented yet */
+    case 0x1a000:
+    default:
+        DPRINTF("BMAP Write B @ %x with %x\n", (unsigned int)addr, value);
+    }
+}
+
+static void scr_writew(NeXTState *s, hwaddr addr, uint32_t value)
+{
+    DPRINTF("BMAP Write W @ %x with %x\n", (unsigned int)addr, value);
+}
+
+static void scr_writel(NeXTState *s, hwaddr addr, uint32_t value)
+{
+    DPRINTF("BMAP Write L @ %x with %x\n", (unsigned int)addr, value);
+}
+
+static uint64_t scr_readfn(void *opaque, hwaddr addr, unsigned size)
+{
+    NeXTState *ns = NEXT_MACHINE(opaque);
+
+    switch (size) {
+    case 1:
+        return scr_readb(ns, addr);
+    case 2:
+        return scr_readw(ns, addr);
+    case 4:
+        return scr_readl(ns, addr);
+    default:
+        g_assert_not_reached();
+    }
+}
+
+static void scr_writefn(void *opaque, hwaddr addr, uint64_t value,
+                        unsigned size)
+{
+    NeXTState *ns = NEXT_MACHINE(opaque);
+
+    switch (size) {
+    case 1:
+        scr_writeb(ns, addr, value);
+        break;
+    case 2:
+        scr_writew(ns, addr, value);
+        break;
+    case 4:
+        scr_writel(ns, addr, value);
+        break;
+    default:
+        g_assert_not_reached();
+    }
+}
+
+static const MemoryRegionOps scr_ops = {
+    .read = scr_readfn,
+    .write = scr_writefn,
+    .valid.min_access_size = 1,
+    .valid.max_access_size = 4,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+#define NEXTDMA_SCSI(x)      (0x10 + x)
+#define NEXTDMA_FD(x)        (0x10 + x)
+#define NEXTDMA_ENTX(x)      (0x110 + x)
+#define NEXTDMA_ENRX(x)      (0x150 + x)
+#define NEXTDMA_CSR          0x0
+#define NEXTDMA_NEXT         0x4000
+#define NEXTDMA_LIMIT        0x4004
+#define NEXTDMA_START        0x4008
+#define NEXTDMA_STOP         0x400c
+#define NEXTDMA_NEXT_INIT    0x4200
+#define NEXTDMA_SIZE         0x4204
+
+static void dma_writel(void *opaque, hwaddr addr, uint64_t value,
+                       unsigned int size)
+{
+    NeXTState *next_state = NEXT_MACHINE(opaque);
+
+    switch (addr) {
+    case NEXTDMA_ENRX(NEXTDMA_CSR):
+        if (value & DMA_DEV2M) {
+            next_state->dma[NEXTDMA_ENRX].csr |= DMA_DEV2M;
+        }
+
+        if (value & DMA_SETENABLE) {
+            /* DPRINTF("SCSI DMA ENABLE\n"); */
+            next_state->dma[NEXTDMA_ENRX].csr |= DMA_ENABLE;
+        }
+        if (value & DMA_SETSUPDATE) {
+            next_state->dma[NEXTDMA_ENRX].csr |= DMA_SUPDATE;
+        }
+        if (value & DMA_CLRCOMPLETE) {
+            next_state->dma[NEXTDMA_ENRX].csr &= ~DMA_COMPLETE;
+        }
+
+        if (value & DMA_RESET) {
+            next_state->dma[NEXTDMA_ENRX].csr &= ~(DMA_COMPLETE | DMA_SUPDATE |
+                                                  DMA_ENABLE | DMA_DEV2M);
+        }
+        /* DPRINTF("RXCSR \tWrite: %x\n",value); */
+        break;
+    case NEXTDMA_ENRX(NEXTDMA_NEXT_INIT):
+        next_state->dma[NEXTDMA_ENRX].next_initbuf = value;
+        break;
+    case NEXTDMA_ENRX(NEXTDMA_NEXT):
+        next_state->dma[NEXTDMA_ENRX].next = value;
+        break;
+    case NEXTDMA_ENRX(NEXTDMA_LIMIT):
+        next_state->dma[NEXTDMA_ENRX].limit = value;
+        break;
+    case NEXTDMA_SCSI(NEXTDMA_CSR):
+        if (value & DMA_DEV2M) {
+            next_state->dma[NEXTDMA_SCSI].csr |= DMA_DEV2M;
+        }
+        if (value & DMA_SETENABLE) {
+            /* DPRINTF("SCSI DMA ENABLE\n"); */
+            next_state->dma[NEXTDMA_SCSI].csr |= DMA_ENABLE;
+        }
+        if (value & DMA_SETSUPDATE) {
+            next_state->dma[NEXTDMA_SCSI].csr |= DMA_SUPDATE;
+        }
+        if (value & DMA_CLRCOMPLETE) {
+            next_state->dma[NEXTDMA_SCSI].csr &= ~DMA_COMPLETE;
+        }
+
+        if (value & DMA_RESET) {
+            next_state->dma[NEXTDMA_SCSI].csr &= ~(DMA_COMPLETE | DMA_SUPDATE |
+                                                  DMA_ENABLE | DMA_DEV2M);
+            /* DPRINTF("SCSI DMA RESET\n"); */
+        }
+        /* DPRINTF("RXCSR \tWrite: %x\n",value); */
+        break;
+
+    case NEXTDMA_SCSI(NEXTDMA_NEXT):
+        next_state->dma[NEXTDMA_SCSI].next = value;
+        break;
+
+    case NEXTDMA_SCSI(NEXTDMA_LIMIT):
+        next_state->dma[NEXTDMA_SCSI].limit = value;
+        break;
+
+    case NEXTDMA_SCSI(NEXTDMA_START):
+        next_state->dma[NEXTDMA_SCSI].start = value;
+        break;
+
+    case NEXTDMA_SCSI(NEXTDMA_STOP):
+        next_state->dma[NEXTDMA_SCSI].stop = value;
+        break;
+
+    case NEXTDMA_SCSI(NEXTDMA_NEXT_INIT):
+        next_state->dma[NEXTDMA_SCSI].next_initbuf = value;
+        break;
+
+    default:
+        DPRINTF("DMA write @ %x w/ %x\n", (unsigned)addr, (unsigned)value);
+    }
+}
+
+static uint64_t dma_readl(void *opaque, hwaddr addr, unsigned int size)
+{
+    NeXTState *next_state = NEXT_MACHINE(opaque);
+
+    switch (addr) {
+    case NEXTDMA_SCSI(NEXTDMA_CSR):
+        DPRINTF("SCSI DMA CSR READ\n");
+        return next_state->dma[NEXTDMA_SCSI].csr;
+    case NEXTDMA_ENRX(NEXTDMA_CSR):
+        return next_state->dma[NEXTDMA_ENRX].csr;
+    case NEXTDMA_ENRX(NEXTDMA_NEXT_INIT):
+        return next_state->dma[NEXTDMA_ENRX].next_initbuf;
+    case NEXTDMA_ENRX(NEXTDMA_NEXT):
+        return next_state->dma[NEXTDMA_ENRX].next;
+    case NEXTDMA_ENRX(NEXTDMA_LIMIT):
+        return next_state->dma[NEXTDMA_ENRX].limit;
+
+    case NEXTDMA_SCSI(NEXTDMA_NEXT):
+        return next_state->dma[NEXTDMA_SCSI].next;
+    case NEXTDMA_SCSI(NEXTDMA_NEXT_INIT):
+        return next_state->dma[NEXTDMA_SCSI].next_initbuf;
+    case NEXTDMA_SCSI(NEXTDMA_LIMIT):
+        return next_state->dma[NEXTDMA_SCSI].limit;
+    case NEXTDMA_SCSI(NEXTDMA_START):
+        return next_state->dma[NEXTDMA_SCSI].start;
+    case NEXTDMA_SCSI(NEXTDMA_STOP):
+        return next_state->dma[NEXTDMA_SCSI].stop;
+
+    default:
+        DPRINTF("DMA read @ %x\n", (unsigned int)addr);
+        return 0;
+    }
+
+    /*
+     * once the csr's are done, subtract 0x3FEC from the addr, and that will
+     * normalize the upper registers
+     */
+}
+
+static const MemoryRegionOps dma_ops = {
+    .read = dma_readl,
+    .write = dma_writel,
+    .impl.min_access_size = 4,
+    .valid.min_access_size = 4,
+    .valid.max_access_size = 4,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+/*
+ * TODO: set the shift numbers as values in the enum, so the first switch
+ * will not be needed
+ */
+void next_irq(void *opaque, int number, int level)
+{
+    M68kCPU *cpu = opaque;
+    int shift = 0;
+    NeXTState *ns = NEXT_MACHINE(qdev_get_machine());
+
+    /* first switch sets interupt status */
+    /* DPRINTF("IRQ %i\n",number); */
+    switch (number) {
+    /* level 3 - floppy, kbd/mouse, power, ether rx/tx, scsi, clock */
+    case NEXT_FD_I:
+        shift = 7;;
+        break;
+    case NEXT_KBD_I:
+        shift = 3;
+        break;
+    case NEXT_PWR_I:
+        shift = 2;
+        break;
+    case NEXT_ENRX_I:
+        shift = 9;
+        break;
+    case NEXT_ENTX_I:
+        shift = 10;
+        break;
+    case NEXT_SCSI_I:
+        shift = 12;
+        break;
+    case NEXT_CLK_I:
+        shift = 5;
+        break;
+
+    /* level 5 - scc (serial) */
+    case NEXT_SCC_I:
+        shift = 17;
+        break;
+
+    /* level 6 - audio etherrx/tx dma */
+    case NEXT_ENTX_DMA_I:
+        shift = 28;
+        break;
+    case NEXT_ENRX_DMA_I:
+        shift = 27;
+        break;
+    case NEXT_SCSI_DMA_I:
+        shift = 26;
+        break;
+    case NEXT_SND_I:
+        shift = 23;
+        break;
+    case NEXT_SCC_DMA_I:
+        shift = 21;
+        break;
+
+    }
+    /*
+     * this HAS to be wrong, the interrupt handlers in mach and together
+     * int_status and int_mask and return if there is a hit
+     */
+    if (ns->int_mask & (1 << shift)) {
+        DPRINTF("%x interrupt masked @ %x\n", 1 << shift, cpu->env.pc);
+        /* return; */
+    }
+
+    /* second switch triggers the correct interrupt */
+    if (level) {
+        ns->int_status |= 1 << shift;
+
+        switch (number) {
+        /* level 3 - floppy, kbd/mouse, power, ether rx/tx, scsi, clock */
+        case NEXT_FD_I:
+        case NEXT_KBD_I:
+        case NEXT_PWR_I:
+        case NEXT_ENRX_I:
+        case NEXT_ENTX_I:
+        case NEXT_SCSI_I:
+        case NEXT_CLK_I:
+            m68k_set_irq_level(cpu, 3, 27);
+            break;
+
+        /* level 5 - scc (serial) */
+        case NEXT_SCC_I:
+            m68k_set_irq_level(cpu, 5, 29);
+            break;
+
+        /* level 6 - audio etherrx/tx dma */
+        case NEXT_ENTX_DMA_I:
+        case NEXT_ENRX_DMA_I:
+        case NEXT_SCSI_DMA_I:
+        case NEXT_SND_I:
+        case NEXT_SCC_DMA_I:
+            m68k_set_irq_level(cpu, 6, 30);
+            break;
+        }
+    } else {
+        ns->int_status &= ~(1 << shift);
+        cpu_reset_interrupt(CPU(cpu), CPU_INTERRUPT_HARD);
+    }
+}
+
+static void next_serial_irq(void *opaque, int n, int level)
+{
+    /* DPRINTF("SCC IRQ NUM %i\n",n); */
+    if (n) {
+        next_irq(opaque, NEXT_SCC_DMA_I, level);
+    } else {
+        next_irq(opaque, NEXT_SCC_I, level);
+    }
+}
+
+static void next_escc_init(M68kCPU *cpu)
+{
+    qemu_irq *ser_irq = qemu_allocate_irqs(next_serial_irq, cpu, 2);
+    DeviceState *dev;
+    SysBusDevice *s;
+
+    dev = qdev_create(NULL, TYPE_ESCC);
+    qdev_prop_set_uint32(dev, "disabled", 0);
+    qdev_prop_set_uint32(dev, "frequency", 9600 * 384);
+    qdev_prop_set_uint32(dev, "it_shift", 0);
+    qdev_prop_set_bit(dev, "bit_swap", true);
+    qdev_prop_set_chr(dev, "chrB", serial_hd(1));
+    qdev_prop_set_chr(dev, "chrA", serial_hd(0));
+    qdev_prop_set_uint32(dev, "chnBtype", escc_serial);
+    qdev_prop_set_uint32(dev, "chnAtype", escc_serial);
+    qdev_init_nofail(dev);
+
+    s = SYS_BUS_DEVICE(dev);
+    sysbus_connect_irq(s, 0, ser_irq[0]);
+    sysbus_connect_irq(s, 1,  ser_irq[1]);
+    sysbus_mmio_map(s, 0, 0x2118000);
+}
+
+static void next_cube_init(MachineState *machine)
+{
+    M68kCPU *cpu;
+    CPUM68KState *env;
+    MemoryRegion *ram = g_new(MemoryRegion, 1);
+    MemoryRegion *rom = g_new(MemoryRegion, 1);
+    MemoryRegion *mmiomem = g_new(MemoryRegion, 1);
+    MemoryRegion *scrmem = g_new(MemoryRegion, 1);
+    MemoryRegion *dmamem = g_new(MemoryRegion, 1);
+    MemoryRegion *bmapm1 = g_new(MemoryRegion, 1);
+    MemoryRegion *bmapm2 = g_new(MemoryRegion, 1);
+    MemoryRegion *sysmem = get_system_memory();
+    NeXTState *ns = NEXT_MACHINE(machine);
+    DeviceState *dev;
+
+    /* Initialize the cpu core */
+    cpu = M68K_CPU(cpu_create(machine->cpu_type));
+    if (!cpu) {
+        error_report("Unable to find m68k CPU definition");
+        exit(1);
+    }
+    env = &cpu->env;
+
+    /* Initialize CPU registers.  */
+    env->vbr = 0;
+    env->sr  = 0x2700;
+
+    /* Set internal registers to initial values */
+    /*     0x0000XX00 << vital bits */
+    ns->scr1 = 0x00011102;
+    ns->scr2 = 0x00ff0c80;
+
+    /* Load RTC RAM - TODO: provide possibility to load contents from file */
+    memcpy(ns->rtc_ram, rtc_ram2, 32);
+
+    /* 64MB RAM starting at 0x04000000  */
+    memory_region_allocate_system_memory(ram, NULL, "next.ram", ram_size);
+    memory_region_add_subregion(sysmem, 0x04000000, ram);
+
+    /* Framebuffer */
+    dev = qdev_create(NULL, TYPE_NEXTFB);
+    qdev_init_nofail(dev);
+    sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, 0x0B000000);
+
+    /* MMIO */
+    memory_region_init_io(mmiomem, NULL, &mmio_ops, machine, "next.mmio",
+                          0xD0000);
+    memory_region_add_subregion(sysmem, 0x02000000, mmiomem);
+
+    /* BMAP memory */
+    memory_region_init_ram_shared_nomigrate(bmapm1, NULL, "next.bmapmem", 64,
+                                            true, &error_fatal);
+    memory_region_add_subregion(sysmem, 0x020c0000, bmapm1);
+    /* The Rev_2.5_v66.bin firmware accesses it at 0x820c0020, too */
+    memory_region_init_alias(bmapm2, NULL, "next.bmapmem2", bmapm1, 0x0, 64);
+    memory_region_add_subregion(sysmem, 0x820c0000, bmapm2);
+
+    /* BMAP IO - acts as a catch-all for now */
+    memory_region_init_io(scrmem, NULL, &scr_ops, machine, "next.scr",
+                          0x20000);
+    memory_region_add_subregion(sysmem, 0x02100000, scrmem);
+
+    /* KBD */
+    dev = qdev_create(NULL, TYPE_NEXTKBD);
+    qdev_init_nofail(dev);
+    sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, 0x0200e000);
+
+    /* Load ROM here */
+    if (bios_name == NULL) {
+        bios_name = ROM_FILE;
+    }
+    /* still not sure if the rom should also be mapped at 0x0*/
+    memory_region_init_rom(rom, NULL, "next.rom", 0x20000, &error_fatal);
+    memory_region_add_subregion(sysmem, 0x01000000, rom);
+    if (load_image_targphys(bios_name, 0x01000000, 0x20000) < 8) {
+        if (!qtest_enabled()) {
+            error_report("Failed to load firmware '%s'.", bios_name);
+        }
+    } else {
+        uint8_t *ptr;
+        /* Initial PC is always at offset 4 in firmware binaries */
+        ptr = rom_ptr(0x01000004, 4);
+        g_assert(ptr != NULL);
+        env->pc = ldl_p(ptr);
+        if (env->pc >= 0x01020000) {
+            error_report("'%s' does not seem to be a valid firmware image.",
+                         bios_name);
+            exit(1);
+        }
+    }
+
+    /* Serial */
+    next_escc_init(cpu);
+
+    /* TODO: */
+    /* Network */
+    /* SCSI */
+
+    /* DMA */
+    memory_region_init_io(dmamem, NULL, &dma_ops, machine, "next.dma", 0x5000);
+    memory_region_add_subregion(sysmem, 0x02000000, dmamem);
+}
+
+static void next_machine_class_init(ObjectClass *oc, void *data)
+{
+    MachineClass *mc = MACHINE_CLASS(oc);
+
+    mc->desc = "NeXT Cube";
+    mc->init = next_cube_init;
+    mc->default_ram_size = RAM_SIZE;
+    mc->default_cpu_type = M68K_CPU_TYPE_NAME("m68040");
+}
+
+static const TypeInfo next_typeinfo = {
+    .name = TYPE_NEXT_MACHINE,
+    .parent = TYPE_MACHINE,
+    .class_init = next_machine_class_init,
+    .instance_size = sizeof(NeXTState),
+};
+
+static void next_register_type(void)
+{
+    type_register_static(&next_typeinfo);
+}
+
+type_init(next_register_type)
diff --git a/hw/m68k/next-kbd.c b/hw/m68k/next-kbd.c
new file mode 100644
index 0000000000..2dff87be15
--- /dev/null
+++ b/hw/m68k/next-kbd.c
@@ -0,0 +1,291 @@
+/*
+ * QEMU NeXT Keyboard/Mouse emulation
+ *
+ * Copyright (c) 2011 Bryce Lanham
+ *
+ * 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.
+ */
+
+/*
+ * This is admittedly hackish, but works well enough for basic input. Mouse
+ * support will be added once we can boot something that needs the mouse.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/log.h"
+#include "exec/address-spaces.h"
+#include "hw/hw.h"
+#include "hw/sysbus.h"
+#include "hw/m68k/next-cube.h"
+#include "ui/console.h"
+#include "sysemu/sysemu.h"
+#include "migration/vmstate.h"
+
+#define NEXTKBD(obj) OBJECT_CHECK(NextKBDState, (obj), TYPE_NEXTKBD)
+
+/* following defintions from next68k netbsd */
+#define CSR_INT 0x00800000
+#define CSR_DATA 0x00400000
+
+#define KD_KEYMASK    0x007f
+#define KD_DIRECTION  0x0080 /* pressed or released */
+#define KD_CNTL       0x0100
+#define KD_LSHIFT     0x0200
+#define KD_RSHIFT     0x0400
+#define KD_LCOMM      0x0800
+#define KD_RCOMM      0x1000
+#define KD_LALT       0x2000
+#define KD_RALT       0x4000
+#define KD_VALID      0x8000 /* only set for scancode keys ? */
+#define KD_MODS       0x4f00
+
+#define KBD_QUEUE_SIZE 256
+
+typedef struct {
+    uint8_t data[KBD_QUEUE_SIZE];
+    int rptr, wptr, count;
+} KBDQueue;
+
+
+typedef struct NextKBDState {
+    SysBusDevice sbd;
+    MemoryRegion mr;
+    KBDQueue queue;
+    uint16_t shift;
+} NextKBDState;
+
+static void queue_code(void *opaque, int code);
+
+/* lots of magic numbers here */
+static uint32_t kbd_read_byte(void *opaque, hwaddr addr)
+{
+    switch (addr & 0x3) {
+    case 0x0:   /* 0xe000 */
+        return 0x80 | 0x20;
+
+    case 0x1:   /* 0xe001 */
+        return 0x80 | 0x40 | 0x20 | 0x10;
+
+    case 0x2:   /* 0xe002 */
+        /* returning 0x40 caused mach to hang */
+        return 0x10 | 0x2 | 0x1;
+
+    default:
+        qemu_log_mask(LOG_UNIMP, "NeXT kbd read byte %"HWADDR_PRIx"\n", addr);
+    }
+
+    return 0;
+}
+
+static uint32_t kbd_read_word(void *opaque, hwaddr addr)
+{
+    qemu_log_mask(LOG_UNIMP, "NeXT kbd read word %"HWADDR_PRIx"\n", addr);
+    return 0;
+}
+
+/* even more magic numbers */
+static uint32_t kbd_read_long(void *opaque, hwaddr addr)
+{
+    int key = 0;
+    NextKBDState *s = NEXTKBD(opaque);
+    KBDQueue *q = &s->queue;
+
+    switch (addr & 0xf) {
+    case 0x0:   /* 0xe000 */
+        return 0xA0F09300;
+
+    case 0x8:   /* 0xe008 */
+        /* get keycode from buffer */
+        if (q->count > 0) {
+            key = q->data[q->rptr];
+            if (++q->rptr == KBD_QUEUE_SIZE) {
+                q->rptr = 0;
+            }
+
+            q->count--;
+
+            if (s->shift) {
+                key |= s->shift;
+            }
+
+            if (key & 0x80) {
+                return 0;
+            } else {
+                return 0x10000000 | KD_VALID | key;
+            }
+        } else {
+            return 0;
+        }
+
+    default:
+        qemu_log_mask(LOG_UNIMP, "NeXT kbd read long %"HWADDR_PRIx"\n", addr);
+        return 0;
+    }
+}
+
+static uint64_t kbd_readfn(void *opaque, hwaddr addr, unsigned size)
+{
+    switch (size) {
+    case 1:
+        return kbd_read_byte(opaque, addr);
+    case 2:
+        return kbd_read_word(opaque, addr);
+    case 4:
+        return kbd_read_long(opaque, addr);
+    default:
+        g_assert_not_reached();
+    }
+}
+
+static void kbd_writefn(void *opaque, hwaddr addr, uint64_t value,
+                        unsigned size)
+{
+    qemu_log_mask(LOG_UNIMP, "NeXT kbd write: size=%u addr=0x%"HWADDR_PRIx
+                  "val=0x%"PRIx64"\n", size, addr, value);
+}
+
+static const MemoryRegionOps kbd_ops = {
+    .read = kbd_readfn,
+    .write = kbd_writefn,
+    .valid.min_access_size = 1,
+    .valid.max_access_size = 4,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static void nextkbd_event(void *opaque, int ch)
+{
+    /*
+     * Will want to set vars for caps/num lock
+     * if (ch & 0x80) -> key release
+     * there's also e0 escaped scancodes that might need to be handled
+     */
+    queue_code(opaque, ch);
+}
+
+static const unsigned char next_keycodes[128] = {
+    0x00, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x50, 0x4F,
+    0x4E, 0x1E, 0x1F, 0x20, 0x1D, 0x1C, 0x1B, 0x00,
+    0x42, 0x43, 0x44, 0x45, 0x48, 0x47, 0x46, 0x06,
+    0x07, 0x08, 0x00, 0x00, 0x2A, 0x00, 0x39, 0x3A,
+    0x3B, 0x3C, 0x3D, 0x40, 0x3F, 0x3E, 0x2D, 0x2C,
+    0x2B, 0x26, 0x00, 0x00, 0x31, 0x32, 0x33, 0x34,
+    0x35, 0x37, 0x36, 0x2e, 0x2f, 0x30, 0x00, 0x00,
+    0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+static void queue_code(void *opaque, int code)
+{
+    NextKBDState *s = NEXTKBD(opaque);
+    KBDQueue *q = &s->queue;
+    int key = code & KD_KEYMASK;
+    int release = code & 0x80;
+    static int ext;
+
+    if (code == 0xE0) {
+        ext = 1;
+    }
+
+    if (code == 0x2A || code == 0x1D || code == 0x36) {
+        if (code == 0x2A) {
+            s->shift = KD_LSHIFT;
+        } else if (code == 0x36) {
+            s->shift = KD_RSHIFT;
+            ext = 0;
+        } else if (code == 0x1D && !ext) {
+            s->shift = KD_LCOMM;
+        } else if (code == 0x1D && ext) {
+            ext = 0;
+            s->shift = KD_RCOMM;
+        }
+        return;
+    } else if (code == (0x2A | 0x80) || code == (0x1D | 0x80) ||
+               code == (0x36 | 0x80)) {
+        s->shift = 0;
+        return;
+    }
+
+    if (q->count >= KBD_QUEUE_SIZE) {
+        return;
+    }
+
+    q->data[q->wptr] = next_keycodes[key] | release;
+
+    if (++q->wptr == KBD_QUEUE_SIZE) {
+        q->wptr = 0;
+    }
+
+    q->count++;
+
+    /*
+     * might need to actually trigger the NeXT irq, but as the keyboard works
+     * at the moment, I'll worry about it later
+     */
+    /* s->update_irq(s->update_arg, 1); */
+}
+
+static void nextkbd_reset(DeviceState *dev)
+{
+    NextKBDState *nks = NEXTKBD(dev);
+
+    memset(&nks->queue, 0, sizeof(KBDQueue));
+    nks->shift = 0;
+}
+
+static void nextkbd_realize(DeviceState *dev, Error **errp)
+{
+    NextKBDState *s = NEXTKBD(dev);
+
+    memory_region_init_io(&s->mr, OBJECT(dev), &kbd_ops, s, "next.kbd", 0x1000);
+    sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->mr);
+
+    qemu_add_kbd_event_handler(nextkbd_event, s);
+}
+
+static const VMStateDescription nextkbd_vmstate = {
+    .name = TYPE_NEXTKBD,
+    .unmigratable = 1,    /* TODO: Implement this when m68k CPU is migratable */
+};
+
+static void nextkbd_class_init(ObjectClass *oc, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(oc);
+
+    set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
+    dc->vmsd = &nextkbd_vmstate;
+    dc->realize = nextkbd_realize;
+    dc->reset = nextkbd_reset;
+}
+
+static const TypeInfo nextkbd_info = {
+    .name          = TYPE_NEXTKBD,
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(NextKBDState),
+    .class_init    = nextkbd_class_init,
+};
+
+static void nextkbd_register_types(void)
+{
+    type_register_static(&nextkbd_info);
+}
+
+type_init(nextkbd_register_types)
diff --git a/include/hw/char/escc.h b/include/hw/char/escc.h
index d5196c53e6..794b653484 100644
--- a/include/hw/char/escc.h
+++ b/include/hw/char/escc.h
@@ -51,6 +51,7 @@ typedef struct ESCCState {
 
     struct ESCCChannelState chn[2];
     uint32_t it_shift;
+    bool bit_swap;
     MemoryRegion mmio;
     uint32_t disabled;
     uint32_t frequency;
diff --git a/include/hw/m68k/next-cube.h b/include/hw/m68k/next-cube.h
new file mode 100644
index 0000000000..a3be2b32ab
--- /dev/null
+++ b/include/hw/m68k/next-cube.h
@@ -0,0 +1,47 @@
+
+#ifndef NEXT_CUBE_H
+#define NEXT_CUBE_H
+
+#define TYPE_NEXTFB "next-fb"
+
+#define TYPE_NEXTKBD "next-kbd"
+
+enum next_dma_chan {
+    NEXTDMA_FD,
+    NEXTDMA_ENRX,
+    NEXTDMA_ENTX,
+    NEXTDMA_SCSI,
+    NEXTDMA_SCC,
+    NEXTDMA_SND
+};
+
+#define DMA_ENABLE      0x01000000
+#define DMA_SUPDATE     0x02000000
+#define DMA_COMPLETE    0x08000000
+
+#define DMA_M2DEV       0x0
+#define DMA_SETENABLE   0x00010000
+#define DMA_SETSUPDATE  0x00020000
+#define DMA_DEV2M       0x00040000
+#define DMA_CLRCOMPLETE 0x00080000
+#define DMA_RESET       0x00100000
+
+enum next_irqs {
+    NEXT_FD_I,
+    NEXT_KBD_I,
+    NEXT_PWR_I,
+    NEXT_ENRX_I,
+    NEXT_ENTX_I,
+    NEXT_SCSI_I,
+    NEXT_CLK_I,
+    NEXT_SCC_I,
+    NEXT_ENTX_DMA_I,
+    NEXT_ENRX_DMA_I,
+    NEXT_SCSI_DMA_I,
+    NEXT_SCC_DMA_I,
+    NEXT_SND_I
+};
+
+void next_irq(void *opaque, int number, int level);
+
+#endif /* NEXT_CUBE_H */
diff --git a/tests/acceptance/machine_m68k_nextcube.py b/tests/acceptance/machine_m68k_nextcube.py
new file mode 100644
index 0000000000..e09cab9f20
--- /dev/null
+++ b/tests/acceptance/machine_m68k_nextcube.py
@@ -0,0 +1,121 @@
+# Functional test that boots a VM and run OCR on the framebuffer
+#
+# Copyright (c) Philippe Mathieu-Daudé <f4bug@amsat.org>
+#
+# This work is licensed under the terms of the GNU GPL, version 2 or
+# later.  See the COPYING file in the top-level directory.
+
+import os
+import re
+import time
+import logging
+import distutils.spawn
+
+from avocado_qemu import Test
+from avocado import skipUnless
+from avocado.utils import process
+from avocado.utils.path import find_command, CmdNotFoundError
+
+PIL_AVAILABLE = True
+try:
+    from PIL import Image
+except ImportError:
+    PIL_AVAILABLE = False
+
+
+def tesseract_available(expected_version):
+    try:
+        find_command('tesseract')
+    except CmdNotFoundError:
+        return False
+    res = process.run('tesseract --version')
+    try:
+        version = res.stdout_text.split()[1]
+    except IndexError:
+        version = res.stderr_text.split()[1]
+    return int(version.split('.')[0]) == expected_version
+
+    match = re.match(r'tesseract\s(\d)', res)
+    if match is None:
+        return False
+    # now this is guaranteed to be a digit
+    return int(match.groups()[0]) == expected_version
+
+
+class NextCubeMachine(Test):
+
+    timeout = 15
+
+    def check_bootrom_framebuffer(self, screenshot_path):
+        rom_url = ('http://www.nextcomputers.org/NeXTfiles/Software/ROM_Files/'
+                   '68040_Non-Turbo_Chipset/Rev_2.5_v66.BIN')
+        rom_hash = 'b3534796abae238a0111299fc406a9349f7fee24'
+        rom_path = self.fetch_asset(rom_url, asset_hash=rom_hash)
+
+        self.vm.set_machine('next-cube')
+        self.vm.add_args('-bios', rom_path)
+        self.vm.launch()
+
+        self.log.info('VM launched, waiting for display')
+        # TODO: Use avocado.utils.wait.wait_for to catch the
+        #       'displaysurface_create 1120x832' trace-event.
+        time.sleep(2)
+
+        self.vm.command('human-monitor-command',
+                        command_line='screendump %s' % screenshot_path)
+
+    @skipUnless(PIL_AVAILABLE, 'Python PIL not installed')
+    def test_bootrom_framebuffer_size(self):
+        """
+        :avocado: tags=arch:m68k
+        :avocado: tags=machine:next_cube
+        :avocado: tags=device:framebuffer
+        """
+        screenshot_path = os.path.join(self.workdir, "dump.png")
+        self.check_bootrom_framebuffer(screenshot_path)
+
+        width, height = Image.open(screenshot_path).size
+        self.assertEqual(width, 1120)
+        self.assertEqual(height, 832)
+
+    @skipUnless(tesseract_available(3), 'tesseract v3 OCR tool not available')
+    def test_bootrom_framebuffer_ocr_with_tesseract_v3(self):
+        """
+        :avocado: tags=arch:m68k
+        :avocado: tags=machine:next_cube
+        :avocado: tags=device:framebuffer
+        """
+        screenshot_path = os.path.join(self.workdir, "dump.png")
+        self.check_bootrom_framebuffer(screenshot_path)
+
+        console_logger = logging.getLogger('console')
+        text = process.run("tesseract %s stdout" % screenshot_path).stdout_text
+        for line in text.split('\n'):
+            if len(line):
+                console_logger.debug(line)
+        self.assertIn('Backplane', text)
+        self.assertIn('Ethernet address', text)
+
+    # Tesseract 4 adds a new OCR engine based on LSTM neural networks. The
+    # new version is faster and more accurate than version 3. The drawback is
+    # that it is still alpha-level software.
+    @skipUnless(tesseract_available(4), 'tesseract v4 OCR tool not available')
+    def test_bootrom_framebuffer_ocr_with_tesseract_v4(self):
+        """
+        :avocado: tags=arch:m68k
+        :avocado: tags=machine:next_cube
+        :avocado: tags=device:framebuffer
+        """
+        screenshot_path = os.path.join(self.workdir, "dump.png")
+        self.check_bootrom_framebuffer(screenshot_path)
+
+        console_logger = logging.getLogger('console')
+        proc = process.run("tesseract --oem 1 %s stdout" % screenshot_path)
+        text = proc.stdout_text
+        for line in text.split('\n'):
+            if len(line):
+                console_logger.debug(line)
+        self.assertIn('Testing the FPU, SCC', text)
+        self.assertIn('System test failed. Error code 51', text)
+        self.assertIn('Boot command', text)
+        self.assertIn('Next>', text)
diff --git a/tests/boot-serial-test.c b/tests/boot-serial-test.c
index a54d007298..d3a54a0ba5 100644
--- a/tests/boot-serial-test.c
+++ b/tests/boot-serial-test.c
@@ -24,6 +24,17 @@ static const uint8_t kernel_mcf5208[] = {
     0x60, 0xfa                              /* bra.s  loop */
 };
 
+static const uint8_t bios_nextcube[] = {
+    0x06, 0x00, 0x00, 0x00,                 /* Initial SP */
+    0x01, 0x00, 0x00, 0x08,                 /* Initial PC */
+    0x41, 0xf9, 0x02, 0x11, 0x80, 0x00,     /* lea 0x02118000,%a0 */
+    0x10, 0x3c, 0x00, 0x54,                 /* move.b #'T',%d0 */
+    0x11, 0x7c, 0x00, 0x05, 0x00, 0x01,     /* move.b #5,1(%a0)    Sel TXCTRL */
+    0x11, 0x7c, 0x00, 0x68, 0x00, 0x01,     /* move.b #0x68,1(%a0) Enable TX */
+    0x11, 0x40, 0x00, 0x03,                 /* move.b %d0,3(%a0)   Print 'T' */
+    0x60, 0xfa                              /* bra.s  loop */
+};
+
 static const uint8_t kernel_pls3adsp1800[] = {
     0xb0, 0x00, 0x84, 0x00,                 /* imm   0x8400 */
     0x30, 0x60, 0x00, 0x04,                 /* addik r3,r0,4 */
@@ -117,6 +128,7 @@ static testdef_t tests[] = {
     { "sparc64", "sun4u", "", "UltraSPARC" },
     { "s390x", "s390-ccw-virtio", "", "device" },
     { "m68k", "mcf5208evb", "", "TT", sizeof(kernel_mcf5208), kernel_mcf5208 },
+    { "m68k", "next-cube", "", "TT", sizeof(bios_nextcube), 0, bios_nextcube },
     { "microblaze", "petalogix-s3adsp1800", "", "TT",
       sizeof(kernel_pls3adsp1800), kernel_pls3adsp1800 },
     { "microblazeel", "petalogix-ml605", "", "TT",