aboutsummaryrefslogtreecommitdiffstats
path: root/hw/m68k/next-cube.c
diff options
context:
space:
mode:
Diffstat (limited to 'hw/m68k/next-cube.c')
-rw-r--r--hw/m68k/next-cube.c1056
1 files changed, 1056 insertions, 0 deletions
diff --git a/hw/m68k/next-cube.c b/hw/m68k/next-cube.c
new file mode 100644
index 000000000..e0d4a94f9
--- /dev/null
+++ b/hw/m68k/next-cube.c
@@ -0,0 +1,1056 @@
+/*
+ * 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 "exec/hwaddr.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 "qom/object.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"
+#include "migration/vmstate.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")
+OBJECT_DECLARE_SIMPLE_TYPE(NeXTState, 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 NextRtc {
+ uint8_t ram[32];
+ uint8_t command;
+ uint8_t value;
+ uint8_t status;
+ uint8_t control;
+ uint8_t retval;
+} NextRtc;
+
+struct NeXTState {
+ MachineState parent;
+
+ next_dma dma[10];
+};
+
+#define TYPE_NEXT_PC "next-pc"
+OBJECT_DECLARE_SIMPLE_TYPE(NeXTPC, NEXT_PC)
+
+/* NeXT Peripheral Controller */
+struct NeXTPC {
+ SysBusDevice parent_obj;
+
+ M68kCPU *cpu;
+
+ MemoryRegion mmiomem;
+ MemoryRegion scrmem;
+
+ uint32_t scr1;
+ uint32_t scr2;
+ uint8_t scsi_csr_1;
+ uint8_t scsi_csr_2;
+ uint32_t int_mask;
+ uint32_t int_status;
+
+ NextRtc rtc;
+};
+
+/* 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(NeXTPC *s, uint32_t val, int size)
+{
+ static int led;
+ static int phase;
+ static uint8_t old_scr2;
+ uint8_t scr2_2;
+ NextRtc *rtc = &s->rtc;
+
+ 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 (rtc->ram[rtc->command] & (0x80 >> (phase - 8))) {
+ scr2_2 |= SCR2_RTDATA;
+ }
+
+ rtc->retval = (rtc->retval << 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->retval = (rtc->retval << 1) |
+ ((scr2_2 & SCR2_RTDATA) ? 1 : 0);
+ }
+ /* read the status 0x31 */
+ if (rtc->command == 0x31) {
+ scr2_2 = scr2_2 & (~SCR2_RTDATA);
+ if (rtc->control & (0x80 >> (phase - 8))) {
+ scr2_2 |= SCR2_RTDATA;
+ }
+ rtc->retval = (rtc->retval << 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->retval = (rtc->retval << 1) |
+ ((scr2_2 & SCR2_RTDATA) ? 1 : 0);
+ }
+
+ }
+
+ phase++;
+ if (phase == 16) {
+ if (rtc->command >= 0x80 && rtc->command <= 0x9F) {
+ 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(NeXTPC *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(NeXTPC *s, hwaddr addr)
+{
+ switch (addr) {
+ default:
+ DPRINTF("MMIO Read W @ %"HWADDR_PRIx"\n", addr);
+ return 0x0;
+ }
+}
+
+static uint32_t mmio_readl(NeXTPC *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(NeXTPC *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(NeXTPC *s, hwaddr addr, uint32_t val)
+{
+ DPRINTF("MMIO Write W\n");
+}
+
+static void mmio_writel(NeXTPC *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)
+{
+ NeXTPC *s = NEXT_PC(opaque);
+
+ switch (size) {
+ case 1:
+ return mmio_readb(s, addr);
+ case 2:
+ return mmio_readw(s, addr);
+ case 4:
+ return mmio_readl(s, addr);
+ default:
+ g_assert_not_reached();
+ }
+}
+
+static void mmio_writefn(void *opaque, hwaddr addr, uint64_t value,
+ unsigned size)
+{
+ NeXTPC *s = NEXT_PC(opaque);
+
+ switch (size) {
+ case 1:
+ mmio_writeb(s, addr, value);
+ break;
+ case 2:
+ mmio_writew(s, addr, value);
+ break;
+ case 4:
+ mmio_writel(s, 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(NeXTPC *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(NeXTPC *s, hwaddr addr)
+{
+ DPRINTF("BMAP Read W @ %x\n", (unsigned int)addr);
+ return 0;
+}
+
+static uint32_t scr_readl(NeXTPC *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(NeXTPC *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(NeXTPC *s, hwaddr addr, uint32_t value)
+{
+ DPRINTF("BMAP Write W @ %x with %x\n", (unsigned int)addr, value);
+}
+
+static void scr_writel(NeXTPC *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)
+{
+ NeXTPC *s = NEXT_PC(opaque);
+
+ switch (size) {
+ case 1:
+ return scr_readb(s, addr);
+ case 2:
+ return scr_readw(s, addr);
+ case 4:
+ return scr_readl(s, addr);
+ default:
+ g_assert_not_reached();
+ }
+}
+
+static void scr_writefn(void *opaque, hwaddr addr, uint64_t value,
+ unsigned size)
+{
+ NeXTPC *s = NEXT_PC(opaque);
+
+ switch (size) {
+ case 1:
+ scr_writeb(s, addr, value);
+ break;
+ case 2:
+ scr_writew(s, addr, value);
+ break;
+ case 4:
+ scr_writel(s, 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,
+};
+
+static void next_irq(void *opaque, int number, int level)
+{
+ NeXTPC *s = NEXT_PC(opaque);
+ M68kCPU *cpu = s->cpu;
+ int shift = 0;
+
+ /* 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 (s->int_mask & (1 << shift)) {
+ DPRINTF("%x interrupt masked @ %x\n", 1 << shift, cpu->env.pc);
+ /* return; */
+ }
+
+ /* second switch triggers the correct interrupt */
+ if (level) {
+ s->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 {
+ s->int_status &= ~(1 << shift);
+ cpu_reset_interrupt(CPU(cpu), CPU_INTERRUPT_HARD);
+ }
+}
+
+static void next_escc_init(DeviceState *pcdev)
+{
+ DeviceState *dev;
+ SysBusDevice *s;
+
+ dev = qdev_new(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);
+
+ s = SYS_BUS_DEVICE(dev);
+ sysbus_realize_and_unref(s, &error_fatal);
+ sysbus_connect_irq(s, 0, qdev_get_gpio_in(pcdev, NEXT_SCC_I));
+ sysbus_connect_irq(s, 1, qdev_get_gpio_in(pcdev, NEXT_SCC_DMA_I));
+ sysbus_mmio_map(s, 0, 0x2118000);
+}
+
+static void next_pc_reset(DeviceState *dev)
+{
+ NeXTPC *s = NEXT_PC(dev);
+
+ /* Set internal registers to initial values */
+ /* 0x0000XX00 << vital bits */
+ s->scr1 = 0x00011102;
+ s->scr2 = 0x00ff0c80;
+
+ s->rtc.status = 0x90;
+
+ /* Load RTC RAM - TODO: provide possibility to load contents from file */
+ memcpy(s->rtc.ram, rtc_ram2, 32);
+}
+
+static void next_pc_realize(DeviceState *dev, Error **errp)
+{
+ NeXTPC *s = NEXT_PC(dev);
+ SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
+
+ qdev_init_gpio_in(dev, next_irq, NEXT_NUM_IRQS);
+
+ memory_region_init_io(&s->mmiomem, OBJECT(s), &mmio_ops, s,
+ "next.mmio", 0xD0000);
+ memory_region_init_io(&s->scrmem, OBJECT(s), &scr_ops, s,
+ "next.scr", 0x20000);
+ sysbus_init_mmio(sbd, &s->mmiomem);
+ sysbus_init_mmio(sbd, &s->scrmem);
+}
+
+/*
+ * If the m68k CPU implemented its inbound irq lines as GPIO lines
+ * rather than via the m68k_set_irq_level() function we would not need
+ * this cpu link property and could instead provide outbound IRQ lines
+ * that the board could wire up to the CPU.
+ */
+static Property next_pc_properties[] = {
+ DEFINE_PROP_LINK("cpu", NeXTPC, cpu, TYPE_M68K_CPU, M68kCPU *),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static const VMStateDescription next_rtc_vmstate = {
+ .name = "next-rtc",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT8_ARRAY(ram, NextRtc, 32),
+ VMSTATE_UINT8(command, NextRtc),
+ VMSTATE_UINT8(value, NextRtc),
+ VMSTATE_UINT8(status, NextRtc),
+ VMSTATE_UINT8(control, NextRtc),
+ VMSTATE_UINT8(retval, NextRtc),
+ VMSTATE_END_OF_LIST()
+ },
+};
+
+static const VMStateDescription next_pc_vmstate = {
+ .name = "next-pc",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(scr1, NeXTPC),
+ VMSTATE_UINT32(scr2, NeXTPC),
+ VMSTATE_UINT32(int_mask, NeXTPC),
+ VMSTATE_UINT32(int_status, NeXTPC),
+ VMSTATE_UINT8(scsi_csr_1, NeXTPC),
+ VMSTATE_UINT8(scsi_csr_2, NeXTPC),
+ VMSTATE_STRUCT(rtc, NeXTPC, 0, next_rtc_vmstate, NextRtc),
+ VMSTATE_END_OF_LIST()
+ },
+};
+
+static void next_pc_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->desc = "NeXT Peripheral Controller";
+ dc->realize = next_pc_realize;
+ dc->reset = next_pc_reset;
+ device_class_set_props(dc, next_pc_properties);
+ dc->vmsd = &next_pc_vmstate;
+}
+
+static const TypeInfo next_pc_info = {
+ .name = TYPE_NEXT_PC,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(NeXTPC),
+ .class_init = next_pc_class_init,
+};
+
+static void next_cube_init(MachineState *machine)
+{
+ M68kCPU *cpu;
+ CPUM68KState *env;
+ MemoryRegion *rom = 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();
+ const char *bios_name = machine->firmware ?: ROM_FILE;
+ DeviceState *dev;
+ DeviceState *pcdev;
+
+ /* 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;
+
+ /* Peripheral Controller */
+ pcdev = qdev_new(TYPE_NEXT_PC);
+ object_property_set_link(OBJECT(pcdev), "cpu", OBJECT(cpu), &error_abort);
+ sysbus_realize_and_unref(SYS_BUS_DEVICE(pcdev), &error_fatal);
+
+ /* 64MB RAM starting at 0x04000000 */
+ memory_region_add_subregion(sysmem, 0x04000000, machine->ram);
+
+ /* Framebuffer */
+ dev = qdev_new(TYPE_NEXTFB);
+ sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
+ sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, 0x0B000000);
+
+ /* MMIO */
+ sysbus_mmio_map(SYS_BUS_DEVICE(pcdev), 0, 0x02000000);
+
+ /* BMAP IO - acts as a catch-all for now */
+ sysbus_mmio_map(SYS_BUS_DEVICE(pcdev), 1, 0x02100000);
+
+ /* BMAP memory */
+ memory_region_init_ram_flags_nomigrate(bmapm1, NULL, "next.bmapmem", 64,
+ RAM_SHARED, &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);
+
+ /* KBD */
+ dev = qdev_new(TYPE_NEXTKBD);
+ sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
+ sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, 0x0200e000);
+
+ /* Load ROM here */
+ /* 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(pcdev);
+
+ /* 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_ram_id = "next.ram";
+ 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_register_static(&next_pc_info);
+}
+
+type_init(next_register_type)