diff options
Diffstat (limited to 'hw/m68k/next-cube.c')
-rw-r--r-- | hw/m68k/next-cube.c | 1056 |
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) |