aboutsummaryrefslogtreecommitdiff
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.c1037
1 files changed, 665 insertions, 372 deletions
diff --git a/hw/m68k/next-cube.c b/hw/m68k/next-cube.c
index 9f6f90d..957644b 100644
--- a/hw/m68k/next-cube.c
+++ b/hw/m68k/next-cube.c
@@ -2,6 +2,7 @@
* NeXT Cube System Driver
*
* Copyright (c) 2011 Bryce Lanham
+ * Copyright (c) 2024 Mark Cave-Ayland
*
* This code is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published
@@ -11,8 +12,9 @@
#include "qemu/osdep.h"
#include "exec/hwaddr.h"
-#include "sysemu/sysemu.h"
-#include "sysemu/qtest.h"
+#include "exec/cpu-interrupt.h"
+#include "system/system.h"
+#include "system/qtest.h"
#include "hw/irq.h"
#include "hw/m68k/next-cube.h"
#include "hw/boards.h"
@@ -22,6 +24,7 @@
#include "qom/object.h"
#include "hw/char/escc.h" /* ZILOG 8530 Serial Emulation */
#include "hw/block/fdc.h"
+#include "hw/misc/empty_slot.h"
#include "hw/qdev-properties.h"
#include "qapi/error.h"
#include "qemu/error-report.h"
@@ -37,31 +40,17 @@
#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;
+#define TYPE_NEXT_RTC "next-rtc"
+OBJECT_DECLARE_SIMPLE_TYPE(NeXTRTC, NEXT_RTC)
- uint32_t next;
- uint32_t limit;
- uint32_t start;
- uint32_t stop;
-
- uint32_t next_initbuf;
- uint32_t size;
-} next_dma;
+struct NeXTRTC {
+ SysBusDevice parent_obj;
-typedef struct NextRtc {
int8_t phase;
uint8_t ram[32];
uint8_t command;
@@ -69,18 +58,25 @@ typedef struct NextRtc {
uint8_t status;
uint8_t control;
uint8_t retval;
-} NextRtc;
-struct NeXTState {
- MachineState parent;
+ qemu_irq data_out_irq;
+ qemu_irq power_irq;
+};
- MemoryRegion rom;
- MemoryRegion rom2;
- MemoryRegion dmamem;
- MemoryRegion bmapm1;
- MemoryRegion bmapm2;
+#define TYPE_NEXT_SCSI "next-scsi"
+OBJECT_DECLARE_SIMPLE_TYPE(NeXTSCSI, NEXT_SCSI)
- next_dma dma[10];
+/* NeXT SCSI Controller */
+struct NeXTSCSI {
+ SysBusDevice parent_obj;
+
+ MemoryRegion scsi_mem;
+
+ SysBusESPState sysbus_esp;
+
+ MemoryRegion scsi_csr_mem;
+ uint8_t scsi_csr_1;
+ uint8_t scsi_csr_2;
};
#define TYPE_NEXT_PC "next-pc"
@@ -92,6 +88,9 @@ struct NeXTPC {
M68kCPU *cpu;
+ MemoryRegion floppy_mem;
+ MemoryRegion timer_mem;
+ MemoryRegion dummyen_mem;
MemoryRegion mmiomem;
MemoryRegion scrmem;
@@ -101,13 +100,49 @@ struct NeXTPC {
uint32_t int_mask;
uint32_t int_status;
uint32_t led;
- uint8_t scsi_csr_1;
- uint8_t scsi_csr_2;
+
+ NeXTSCSI next_scsi;
qemu_irq scsi_reset;
qemu_irq scsi_dma;
- NextRtc rtc;
+ ESCCState escc;
+
+ NeXTRTC rtc;
+ qemu_irq rtc_data_irq;
+ qemu_irq rtc_cmd_reset_irq;
+};
+
+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;
+
+#define TYPE_NEXT_MACHINE MACHINE_TYPE_NAME("next-cube")
+OBJECT_DECLARE_SIMPLE_TYPE(NeXTState, NEXT_MACHINE)
+
+struct NeXTState {
+ MachineState parent;
+
+ MemoryRegion rom;
+ MemoryRegion rom2;
+ MemoryRegion dmamem;
+ MemoryRegion bmapm1;
+ MemoryRegion bmapm2;
+
+ next_dma dma[10];
};
/* Thanks to NeXT forums for this */
@@ -144,120 +179,26 @@ static void next_scr2_led_update(NeXTPC *s)
static void next_scr2_rtc_update(NeXTPC *s)
{
- uint8_t old_scr2, scr2_2;
- NextRtc *rtc = &s->rtc;
+ uint8_t old_scr2_rtc, scr2_rtc;
- old_scr2 = extract32(s->old_scr2, 8, 8);
- scr2_2 = extract32(s->scr2, 8, 8);
+ old_scr2_rtc = extract32(s->old_scr2, 8, 8);
+ scr2_rtc = extract32(s->scr2, 8, 8);
- if (scr2_2 & 0x1) {
+ if (scr2_rtc & 0x1) {
/* DPRINTF("RTC %x phase %i\n", scr2_2, rtc->phase); */
- if (rtc->phase == -1) {
- rtc->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 (rtc->phase < 8) {
- rtc->command = (rtc->command << 1) |
- ((scr2_2 & SCR2_RTDATA) ? 1 : 0);
- }
- if (rtc->phase >= 8 && rtc->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 >> (rtc->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 >> (rtc->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 >> (rtc->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 >> (rtc->phase - 8))) {
- scr2_2 |= SCR2_RTDATA;
- }
- rtc->retval = (rtc->retval << 1) |
- ((scr2_2 & SCR2_RTDATA) ? 1 : 0);
- }
-
- }
-
- rtc->phase++;
- if (rtc->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);
- }
- }
+ if (((old_scr2_rtc & SCR2_RTCLK) != (scr2_rtc & SCR2_RTCLK)) &&
+ ((scr2_rtc & SCR2_RTCLK) == 0)) {
+ if (scr2_rtc & SCR2_RTDATA) {
+ qemu_irq_raise(s->rtc_data_irq);
+ } else {
+ qemu_irq_lower(s->rtc_data_irq);
}
}
} else {
/* else end or abort */
- rtc->phase = -1;
- rtc->command = 0;
- rtc->value = 0;
+ qemu_irq_raise(s->rtc_cmd_reset_irq);
}
-
- s->scr2 = deposit32(s->scr2, 8, 8, scr2_2);
}
static uint64_t next_mmio_read(void *opaque, hwaddr addr, unsigned size)
@@ -266,30 +207,26 @@ static uint64_t next_mmio_read(void *opaque, hwaddr addr, unsigned size)
uint64_t val;
switch (addr) {
- case 0x7000:
+ case 0x2000: /* 0x2007000 */
/* DPRINTF("Read INT status: %x\n", s->int_status); */
val = s->int_status;
break;
- case 0x7800:
+ case 0x2800: /* 0x2007800 */
DPRINTF("MMIO Read INT mask: %x\n", s->int_mask);
val = s->int_mask;
break;
- case 0xc000 ... 0xc003:
- val = extract32(s->scr1, (4 - (addr - 0xc000) - size) << 3,
+ case 0x7000 ... 0x7003: /* 0x200c000 */
+ val = extract32(s->scr1, (4 - (addr - 0x7000) - size) << 3,
size << 3);
break;
- case 0xd000 ... 0xd003:
- val = extract32(s->scr2, (4 - (addr - 0xd000) - size) << 3,
+ case 0x8000 ... 0x8003: /* 0x200d000 */
+ val = extract32(s->scr2, (4 - (addr - 0x8000) - size) << 3,
size << 3);
break;
- case 0x14020:
- val = 0x7f;
- break;
-
default:
val = 0;
DPRINTF("MMIO Read @ 0x%"HWADDR_PRIx" size %d\n", addr, size);
@@ -305,25 +242,25 @@ static void next_mmio_write(void *opaque, hwaddr addr, uint64_t val,
NeXTPC *s = NEXT_PC(opaque);
switch (addr) {
- case 0x7000:
+ case 0x2000: /* 0x2007000 */
DPRINTF("INT Status old: %x new: %x\n", s->int_status,
(unsigned int)val);
s->int_status = val;
break;
- case 0x7800:
+ case 0x2800: /* 0x2007800 */
DPRINTF("INT Mask old: %x new: %x\n", s->int_mask, (unsigned int)val);
s->int_mask = val;
break;
- case 0xc000 ... 0xc003:
+ case 0x7000 ... 0x7003: /* 0x200c000 */
DPRINTF("SCR1 Write: %x\n", (unsigned int)val);
- s->scr1 = deposit32(s->scr1, (4 - (addr - 0xc000) - size) << 3,
+ s->scr1 = deposit32(s->scr1, (4 - (addr - 0x7000) - size) << 3,
size << 3, val);
break;
- case 0xd000 ... 0xd003:
- s->scr2 = deposit32(s->scr2, (4 - (addr - 0xd000) - size) << 3,
+ case 0x8000 ... 0x8003: /* 0x200d000 */
+ s->scr2 = deposit32(s->scr2, (4 - (addr - 0x8000) - size) << 3,
size << 3, val);
next_scr2_led_update(s);
next_scr2_rtc_update(s);
@@ -351,143 +288,6 @@ static const MemoryRegionOps next_mmio_ops = {
#define SCSICSR_CPUDMA 0x10 /* if set, dma enabled */
#define SCSICSR_INTMASK 0x20 /* if set, interrupt enabled */
-static uint64_t next_scr_readfn(void *opaque, hwaddr addr, unsigned size)
-{
- NeXTPC *s = NEXT_PC(opaque);
- uint64_t val;
-
- switch (addr) {
- case 0x14108:
- DPRINTF("FD read @ %x\n", (unsigned int)addr);
- val = 0x40 | 0x04 | 0x2 | 0x1;
- break;
-
- case 0x14020:
- DPRINTF("SCSI 4020 STATUS READ %X\n", s->scsi_csr_1);
- val = s->scsi_csr_1;
- break;
-
- case 0x14021:
- DPRINTF("SCSI 4021 STATUS READ %X\n", s->scsi_csr_2);
- val = 0x40;
- break;
-
- /*
- * These 4 registers are the hardware timer, not sure which register
- * is the latch instead of data, but no problems so far.
- *
- * Hack: We need to have the LSB change consistently to make it work
- */
- case 0x1a000 ... 0x1a003:
- val = extract32(clock(), (4 - (addr - 0x1a000) - size) << 3,
- size << 3);
- break;
-
- /* For now return dummy byte to allow the Ethernet test to timeout */
- case 0x6000:
- val = 0xff;
- break;
-
- default:
- DPRINTF("BMAP Read @ 0x%x size %u\n", (unsigned int)addr, size);
- val = 0;
- break;
- }
-
- return val;
-}
-
-static void next_scr_writefn(void *opaque, hwaddr addr, uint64_t val,
- unsigned size)
-{
- NeXTPC *s = NEXT_PC(opaque);
-
- switch (addr) {
- case 0x14108:
- DPRINTF("FDCSR Write: %x\n", value);
- if (val == 0x0) {
- /* qemu_irq_raise(s->fd_irq[0]); */
- }
- break;
-
- case 0x14020: /* SCSI Control Register */
- if (val & 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); */
- }
-
- if (val & 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 (val & 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);
- qemu_irq_lower(s->scsi_reset);
- }
- if (val & SCSICSR_DMADIR) {
- DPRINTF("SCSICSR DMAdir\n");
- }
- if (val & SCSICSR_CPUDMA) {
- DPRINTF("SCSICSR CPUDMA\n");
- /* qemu_irq_raise(s->scsi_dma); */
- s->int_status |= 0x4000000;
- } else {
- /* fprintf(stderr,"SCSICSR CPUDMA disabled\n"); */
- s->int_status &= ~(0x4000000);
- /* qemu_irq_lower(s->scsi_dma); */
- }
- if (val & SCSICSR_INTMASK) {
- DPRINTF("SCSICSR INTMASK\n");
- /*
- * int_mask &= ~0x1000;
- * s->scsi_csr_1 |= val;
- * 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 (val & 0x80) {
- /* int_mask |= 0x1000; */
- /* s->scsi_csr_1 |= 0x80; */
- }
- DPRINTF("SCSICSR Write: %x\n", val);
- /* s->scsi_csr_1 = val; */
- break;
-
- /* Hardware timer latch - not implemented yet */
- case 0x1a000:
- default:
- DPRINTF("BMAP Write @ 0x%x with 0x%x size %u\n", (unsigned int)addr,
- val, size);
- }
-}
-
-static const MemoryRegionOps next_scr_ops = {
- .read = next_scr_readfn,
- .write = next_scr_writefn,
- .valid.min_access_size = 1,
- .valid.max_access_size = 4,
- .endianness = DEVICE_BIG_ENDIAN,
-};
-
#define NEXTDMA_SCSI(x) (0x10 + x)
#define NEXTDMA_FD(x) (0x10 + x)
#define NEXTDMA_ENTX(x) (0x110 + x)
@@ -585,7 +385,7 @@ static void next_dma_write(void *opaque, hwaddr addr, uint64_t val,
break;
default:
- DPRINTF("DMA write @ %x w/ %x\n", (unsigned)addr, (unsigned)value);
+ DPRINTF("DMA write @ %x w/ %x\n", (unsigned)addr, (unsigned)val);
}
}
@@ -828,84 +628,579 @@ static void nextscsi_write(void *opaque, uint8_t *buf, int size)
nextdma_write(opaque, buf, size, NEXTDMA_SCSI);
}
-static void next_scsi_init(DeviceState *pcdev, M68kCPU *cpu)
+static void next_scsi_csr_write(void *opaque, hwaddr addr, uint64_t val,
+ unsigned size)
+{
+ NeXTSCSI *s = NEXT_SCSI(opaque);
+ NeXTPC *pc = NEXT_PC(container_of(s, NeXTPC, next_scsi));
+
+ switch (addr) {
+ case 0:
+ if (val & 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); */
+ }
+
+ if (val & 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 (val & SCSICSR_RESET) {
+ DPRINTF("SCSICSR Reset\n");
+ /* I think this should set DMADIR. CPUDMA and INTMASK to 0 */
+ qemu_irq_raise(pc->scsi_reset);
+ s->scsi_csr_1 &= ~(SCSICSR_INTMASK | 0x80 | 0x1);
+ qemu_irq_lower(pc->scsi_reset);
+ }
+ if (val & SCSICSR_DMADIR) {
+ DPRINTF("SCSICSR DMAdir\n");
+ }
+ if (val & SCSICSR_CPUDMA) {
+ DPRINTF("SCSICSR CPUDMA\n");
+ /* qemu_irq_raise(s->scsi_dma); */
+ pc->int_status |= 0x4000000;
+ } else {
+ /* fprintf(stderr,"SCSICSR CPUDMA disabled\n"); */
+ pc->int_status &= ~(0x4000000);
+ /* qemu_irq_lower(s->scsi_dma); */
+ }
+ if (val & SCSICSR_INTMASK) {
+ DPRINTF("SCSICSR INTMASK\n");
+ /*
+ * int_mask &= ~0x1000;
+ * s->scsi_csr_1 |= val;
+ * 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 (val & 0x80) {
+ /* int_mask |= 0x1000; */
+ /* s->scsi_csr_1 |= 0x80; */
+ }
+ DPRINTF("SCSICSR1 Write: %"PRIx64 "\n", val);
+ s->scsi_csr_1 = val;
+ break;
+
+ case 1:
+ DPRINTF("SCSICSR2 Write: %"PRIx64 "\n", val);
+ s->scsi_csr_2 = val;
+ break;
+
+ default:
+ g_assert_not_reached();
+ }
+}
+
+static uint64_t next_scsi_csr_read(void *opaque, hwaddr addr, unsigned size)
+{
+ NeXTSCSI *s = NEXT_SCSI(opaque);
+ uint64_t val;
+
+ switch (addr) {
+ case 0:
+ DPRINTF("SCSI 4020 STATUS READ %X\n", s->scsi_csr_1);
+ val = s->scsi_csr_1;
+ break;
+
+ case 1:
+ DPRINTF("SCSI 4021 STATUS READ %X\n", s->scsi_csr_2);
+ val = s->scsi_csr_2;
+ break;
+
+ default:
+ g_assert_not_reached();
+ }
+
+ return val;
+}
+
+static const MemoryRegionOps next_scsi_csr_ops = {
+ .read = next_scsi_csr_read,
+ .write = next_scsi_csr_write,
+ .valid.min_access_size = 1,
+ .valid.max_access_size = 1,
+ .endianness = DEVICE_BIG_ENDIAN,
+};
+
+static void next_scsi_init(Object *obj)
{
- struct NeXTPC *next_pc = NEXT_PC(pcdev);
- DeviceState *dev;
- SysBusDevice *sysbusdev;
+ NeXTSCSI *s = NEXT_SCSI(obj);
+ SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
+
+ object_initialize_child(obj, "esp", &s->sysbus_esp, TYPE_SYSBUS_ESP);
+
+ memory_region_init_io(&s->scsi_csr_mem, obj, &next_scsi_csr_ops,
+ s, "csrs", 2);
+
+ memory_region_init(&s->scsi_mem, obj, "next.scsi", 0x40);
+ sysbus_init_mmio(sbd, &s->scsi_mem);
+}
+
+static void next_scsi_realize(DeviceState *dev, Error **errp)
+{
+ NeXTSCSI *s = NEXT_SCSI(dev);
SysBusESPState *sysbus_esp;
+ SysBusDevice *sbd;
ESPState *esp;
+ NeXTPC *pcdev;
+
+ pcdev = NEXT_PC(container_of(s, NeXTPC, next_scsi));
- dev = qdev_new(TYPE_SYSBUS_ESP);
- sysbus_esp = SYSBUS_ESP(dev);
+ /* ESP */
+ sysbus_esp = SYSBUS_ESP(&s->sysbus_esp);
esp = &sysbus_esp->esp;
esp->dma_memory_read = nextscsi_read;
esp->dma_memory_write = nextscsi_write;
esp->dma_opaque = pcdev;
sysbus_esp->it_shift = 0;
esp->dma_enabled = 1;
- sysbusdev = SYS_BUS_DEVICE(dev);
- sysbus_realize_and_unref(sysbusdev, &error_fatal);
- sysbus_connect_irq(sysbusdev, 0, qdev_get_gpio_in(pcdev, NEXT_SCSI_I));
- sysbus_mmio_map(sysbusdev, 0, 0x2114000);
+ sbd = SYS_BUS_DEVICE(sysbus_esp);
+ if (!sysbus_realize(sbd, errp)) {
+ return;
+ }
+ memory_region_add_subregion(&s->scsi_mem, 0x0,
+ sysbus_mmio_get_region(sbd, 0));
- next_pc->scsi_reset = qdev_get_gpio_in(dev, 0);
- next_pc->scsi_dma = qdev_get_gpio_in(dev, 1);
+ /* SCSI CSRs */
+ memory_region_add_subregion(&s->scsi_mem, 0x20, &s->scsi_csr_mem);
- scsi_bus_legacy_handle_cmdline(&esp->bus);
+ scsi_bus_legacy_handle_cmdline(&s->sysbus_esp.esp.bus);
}
-static void next_escc_init(DeviceState *pcdev)
+static const VMStateDescription next_scsi_vmstate = {
+ .name = "next-scsi",
+ .version_id = 0,
+ .minimum_version_id = 0,
+ .fields = (const VMStateField[]) {
+ VMSTATE_UINT8(scsi_csr_1, NeXTSCSI),
+ VMSTATE_UINT8(scsi_csr_2, NeXTSCSI),
+ VMSTATE_END_OF_LIST()
+ },
+};
+
+static void next_scsi_class_init(ObjectClass *klass, const void *data)
{
- 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);
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->desc = "NeXT SCSI Controller";
+ dc->realize = next_scsi_realize;
+ dc->vmsd = &next_scsi_vmstate;
}
-static void next_pc_reset(DeviceState *dev)
+static const TypeInfo next_scsi_info = {
+ .name = TYPE_NEXT_SCSI,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_init = next_scsi_init,
+ .instance_size = sizeof(NeXTSCSI),
+ .class_init = next_scsi_class_init,
+};
+
+static void next_floppy_write(void *opaque, hwaddr addr, uint64_t val,
+ unsigned size)
{
- NeXTPC *s = NEXT_PC(dev);
+ switch (addr) {
+ case 0:
+ DPRINTF("FDCSR Write: %"PRIx64 "\n", val);
+ if (val == 0x0) {
+ /* qemu_irq_raise(s->fd_irq[0]); */
+ }
+ break;
+
+ default:
+ g_assert_not_reached();
+ }
+}
+
+static uint64_t next_floppy_read(void *opaque, hwaddr addr, unsigned size)
+{
+ uint64_t val;
+
+ switch (addr) {
+ case 0:
+ DPRINTF("FD read @ %x\n", (unsigned int)addr);
+ val = 0x40 | 0x04 | 0x2 | 0x1;
+ break;
+
+ default:
+ g_assert_not_reached();
+ }
+
+ return val;
+}
+
+static const MemoryRegionOps next_floppy_ops = {
+ .read = next_floppy_read,
+ .write = next_floppy_write,
+ .valid.min_access_size = 1,
+ .valid.max_access_size = 4,
+ .endianness = DEVICE_BIG_ENDIAN,
+};
+
+static void next_timer_write(void *opaque, hwaddr addr, uint64_t val,
+ unsigned size)
+{
+ switch (addr) {
+ case 0 ... 3:
+ /* Hardware timer latch - not implemented yet */
+ break;
+
+ default:
+ g_assert_not_reached();
+ }
+}
+
+static uint64_t next_timer_read(void *opaque, hwaddr addr, unsigned size)
+{
+ uint64_t val;
+
+ switch (addr) {
+ case 0 ... 3:
+ /*
+ * These 4 registers are the hardware timer, not sure which register
+ * is the latch instead of data, but no problems so far.
+ *
+ * Hack: We need to have the LSB change consistently to make it work
+ */
+ val = extract32(clock(), (4 - addr - size) << 3,
+ size << 3);
+ break;
+
+ default:
+ g_assert_not_reached();
+ }
+
+ return val;
+}
+
+static const MemoryRegionOps next_timer_ops = {
+ .read = next_timer_read,
+ .write = next_timer_write,
+ .valid.min_access_size = 1,
+ .valid.max_access_size = 4,
+ .endianness = DEVICE_BIG_ENDIAN,
+};
+
+static void next_dummy_en_write(void *opaque, hwaddr addr, uint64_t val,
+ unsigned size)
+{
+ /* Do nothing */
+}
+
+static uint64_t next_dummy_en_read(void *opaque, hwaddr addr, unsigned size)
+{
+ uint64_t val;
+
+ switch (addr) {
+ case 0:
+ /* For now return dummy byte to allow the Ethernet test to timeout */
+ val = 0xff;
+ break;
+
+ default:
+ val = 0;
+ }
+
+ return val;
+}
+
+static const MemoryRegionOps next_dummy_en_ops = {
+ .read = next_dummy_en_read,
+ .write = next_dummy_en_write,
+ .valid.min_access_size = 1,
+ .valid.max_access_size = 4,
+ .endianness = DEVICE_BIG_ENDIAN,
+};
+
+static bool next_rtc_cmd_is_write(uint8_t cmd)
+{
+ return (cmd >= 0x80 && cmd <= 0x9f) ||
+ (cmd == 0xb1);
+}
+
+static void next_rtc_data_in_irq(void *opaque, int n, int level)
+{
+ NeXTRTC *rtc = NEXT_RTC(opaque);
+
+ if (rtc->phase < 8) {
+ rtc->command = (rtc->command << 1) | level;
+
+ if (rtc->phase == 7 && !next_rtc_cmd_is_write(rtc->command)) {
+ if (rtc->command <= 0x1f) {
+ /* RAM registers */
+ rtc->retval = rtc->ram[rtc->command];
+ }
+ if ((rtc->command >= 0x20) && (rtc->command <= 0x2f)) {
+ /* RTC */
+ time_t time_h = time(NULL);
+ struct tm *info = localtime(&time_h);
+ rtc->retval = 0;
+
+ switch (rtc->command) {
+ case 0x20:
+ rtc->retval = SCR2_TOBCD(info->tm_sec);
+ break;
+ case 0x21:
+ rtc->retval = SCR2_TOBCD(info->tm_min);
+ break;
+ case 0x22:
+ rtc->retval = SCR2_TOBCD(info->tm_hour);
+ break;
+ case 0x24:
+ rtc->retval = SCR2_TOBCD(info->tm_mday);
+ break;
+ case 0x25:
+ rtc->retval = SCR2_TOBCD((info->tm_mon + 1));
+ break;
+ case 0x26:
+ rtc->retval = SCR2_TOBCD((info->tm_year - 100));
+ break;
+ }
+ }
+ if (rtc->command == 0x30) {
+ /* read the status 0x30 */
+ rtc->retval = rtc->status;
+ }
+ if (rtc->command == 0x31) {
+ /* read the control 0x31 */
+ rtc->retval = rtc->control;
+ }
+ }
+ }
+ if (rtc->phase >= 8 && rtc->phase < 16) {
+ if (next_rtc_cmd_is_write(rtc->command)) {
+ /* Shift in value to write */
+ rtc->value = (rtc->value << 1) | level;
+ } else {
+ /* Shift out value to read */
+ if (rtc->retval & (0x80 >> (rtc->phase - 8))) {
+ qemu_irq_raise(rtc->data_out_irq);
+ } else {
+ qemu_irq_lower(rtc->data_out_irq);
+ }
+ }
+ }
+
+ rtc->phase++;
+ if (rtc->phase == 16 && next_rtc_cmd_is_write(rtc->command)) {
+ if (rtc->command >= 0x80 && rtc->command <= 0x9f) {
+ /* RAM registers */
+ rtc->ram[rtc->command - 0x80] = rtc->value;
+ }
+ if (rtc->command == 0xb1) {
+ /* write to 0x30 register */
+ if (rtc->value & 0x04) {
+ /* clear FTU */
+ rtc->status = rtc->status & (~0x18);
+ qemu_irq_lower(rtc->power_irq);
+ }
+ }
+ }
+}
+
+static void next_rtc_cmd_reset_irq(void *opaque, int n, int level)
+{
+ NeXTRTC *rtc = NEXT_RTC(opaque);
+
+ if (level) {
+ rtc->phase = 0;
+ rtc->command = 0;
+ rtc->value = 0;
+ }
+}
+
+static void next_rtc_reset_hold(Object *obj, ResetType type)
+{
+ NeXTRTC *rtc = NEXT_RTC(obj);
+
+ rtc->status = 0x90;
+
+ /* Load RTC RAM - TODO: provide possibility to load contents from file */
+ memcpy(rtc->ram, rtc_ram2, 32);
+}
+
+static void next_rtc_init(Object *obj)
+{
+ NeXTRTC *rtc = NEXT_RTC(obj);
+
+ qdev_init_gpio_in_named(DEVICE(obj), next_rtc_data_in_irq,
+ "rtc-data-in", 1);
+ qdev_init_gpio_out_named(DEVICE(obj), &rtc->data_out_irq,
+ "rtc-data-out", 1);
+ qdev_init_gpio_in_named(DEVICE(obj), next_rtc_cmd_reset_irq,
+ "rtc-cmd-reset", 1);
+ qdev_init_gpio_out_named(DEVICE(obj), &rtc->power_irq,
+ "rtc-power-out", 1);
+}
+
+static const VMStateDescription next_rtc_vmstate = {
+ .name = "next-rtc",
+ .version_id = 3,
+ .minimum_version_id = 3,
+ .fields = (const VMStateField[]) {
+ VMSTATE_INT8(phase, NeXTRTC),
+ 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 void next_rtc_class_init(ObjectClass *klass, const void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ ResettableClass *rc = RESETTABLE_CLASS(klass);
+
+ dc->desc = "NeXT RTC";
+ dc->vmsd = &next_rtc_vmstate;
+ rc->phases.hold = next_rtc_reset_hold;
+}
+
+static const TypeInfo next_rtc_info = {
+ .name = TYPE_NEXT_RTC,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_init = next_rtc_init,
+ .instance_size = sizeof(NeXTRTC),
+ .class_init = next_rtc_class_init,
+};
+
+static void next_pc_rtc_data_in_irq(void *opaque, int n, int level)
+{
+ NeXTPC *s = NEXT_PC(opaque);
+ uint8_t scr2_2 = extract32(s->scr2, 8, 8);
+
+ if (level) {
+ scr2_2 |= SCR2_RTDATA;
+ } else {
+ scr2_2 &= ~SCR2_RTDATA;
+ }
+
+ s->scr2 = deposit32(s->scr2, 8, 8, scr2_2);
+}
+
+static void next_pc_reset_hold(Object *obj, ResetType type)
+{
+ NeXTPC *s = NEXT_PC(obj);
/* Set internal registers to initial values */
/* 0x0000XX00 << vital bits */
s->scr1 = 0x00011102;
s->scr2 = 0x00ff0c80;
s->old_scr2 = s->scr2;
-
- 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);
+ SysBusDevice *sbd;
+ DeviceState *d;
+
+ /* SCSI */
+ sbd = SYS_BUS_DEVICE(&s->next_scsi);
+ if (!sysbus_realize(sbd, errp)) {
+ return;
+ }
- qdev_init_gpio_in(dev, next_irq, NEXT_NUM_IRQS);
+ d = DEVICE(object_resolve_path_component(OBJECT(&s->next_scsi), "esp"));
+ sysbus_connect_irq(SYS_BUS_DEVICE(d), 0,
+ qdev_get_gpio_in(DEVICE(s), NEXT_SCSI_I));
+
+ s->scsi_reset = qdev_get_gpio_in(d, 0);
+ s->scsi_dma = qdev_get_gpio_in(d, 1);
+
+ /* ESCC */
+ d = DEVICE(&s->escc);
+ qdev_prop_set_uint32(d, "disabled", 0);
+ qdev_prop_set_uint32(d, "frequency", 9600 * 384);
+ qdev_prop_set_uint32(d, "it_shift", 0);
+ qdev_prop_set_bit(d, "bit_swap", true);
+ qdev_prop_set_chr(d, "chrB", serial_hd(1));
+ qdev_prop_set_chr(d, "chrA", serial_hd(0));
+ qdev_prop_set_uint32(d, "chnBtype", escc_serial);
+ qdev_prop_set_uint32(d, "chnAtype", escc_serial);
+
+ sbd = SYS_BUS_DEVICE(d);
+ if (!sysbus_realize(sbd, errp)) {
+ return;
+ }
+ sysbus_connect_irq(sbd, 0, qdev_get_gpio_in(dev, NEXT_SCC_I));
+ sysbus_connect_irq(sbd, 1, qdev_get_gpio_in(dev, NEXT_SCC_DMA_I));
+
+ /* RTC */
+ d = DEVICE(&s->rtc);
+ if (!sysbus_realize(SYS_BUS_DEVICE(d), errp)) {
+ return;
+ }
+ /* Data from NeXTPC to RTC */
+ qdev_connect_gpio_out_named(dev, "rtc-data-out", 0,
+ qdev_get_gpio_in_named(d, "rtc-data-in", 0));
+ /* Data from RTC to NeXTPC */
+ qdev_connect_gpio_out_named(d, "rtc-data-out", 0,
+ qdev_get_gpio_in_named(dev,
+ "rtc-data-in", 0));
+ qdev_connect_gpio_out_named(dev, "rtc-cmd-reset", 0,
+ qdev_get_gpio_in_named(d, "rtc-cmd-reset", 0));
+ qdev_connect_gpio_out_named(d, "rtc-power-out", 0,
+ qdev_get_gpio_in(dev, NEXT_PWR_I));
+}
+
+static void next_pc_init(Object *obj)
+{
+ NeXTPC *s = NEXT_PC(obj);
+ SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
+
+ qdev_init_gpio_in(DEVICE(obj), next_irq, NEXT_NUM_IRQS);
memory_region_init_io(&s->mmiomem, OBJECT(s), &next_mmio_ops, s,
- "next.mmio", 0xd0000);
- memory_region_init_io(&s->scrmem, OBJECT(s), &next_scr_ops, s,
- "next.scr", 0x20000);
+ "next.mmio", 0x9000);
sysbus_init_mmio(sbd, &s->mmiomem);
- sysbus_init_mmio(sbd, &s->scrmem);
+
+ memory_region_init_io(&s->dummyen_mem, OBJECT(s), &next_dummy_en_ops, s,
+ "next.en", 0x20);
+ sysbus_init_mmio(sbd, &s->dummyen_mem);
+
+ object_initialize_child(obj, "next-scsi", &s->next_scsi, TYPE_NEXT_SCSI);
+ sysbus_init_mmio(sbd,
+ sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->next_scsi), 0));
+
+ memory_region_init_io(&s->floppy_mem, OBJECT(s), &next_floppy_ops, s,
+ "next.floppy", 4);
+ sysbus_init_mmio(sbd, &s->floppy_mem);
+
+ object_initialize_child(obj, "escc", &s->escc, TYPE_ESCC);
+ sysbus_init_mmio(sbd,
+ sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->escc), 0));
+
+ memory_region_init_io(&s->timer_mem, OBJECT(s), &next_timer_ops, s,
+ "next.timer", 4);
+ sysbus_init_mmio(sbd, &s->timer_mem);
+
+ object_initialize_child(obj, "rtc", &s->rtc, TYPE_NEXT_RTC);
+
+ qdev_init_gpio_in_named(DEVICE(obj), next_pc_rtc_data_in_irq,
+ "rtc-data-in", 1);
+ qdev_init_gpio_out_named(DEVICE(obj), &s->rtc_data_irq,
+ "rtc-data-out", 1);
+ qdev_init_gpio_out_named(DEVICE(obj), &s->rtc_cmd_reset_irq,
+ "rtc-cmd-reset", 1);
}
/*
@@ -914,31 +1209,14 @@ static void next_pc_realize(DeviceState *dev, Error **errp)
* 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[] = {
+static const 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 = 2,
- .minimum_version_id = 2,
- .fields = (const VMStateField[]) {
- VMSTATE_INT8(phase, NextRtc),
- 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 = 2,
- .minimum_version_id = 2,
+ .version_id = 4,
+ .minimum_version_id = 4,
.fields = (const VMStateField[]) {
VMSTATE_UINT32(scr1, NeXTPC),
VMSTATE_UINT32(scr2, NeXTPC),
@@ -946,27 +1224,26 @@ static const VMStateDescription next_pc_vmstate = {
VMSTATE_UINT32(int_mask, NeXTPC),
VMSTATE_UINT32(int_status, NeXTPC),
VMSTATE_UINT32(led, 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)
+static void next_pc_class_init(ObjectClass *klass, const void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
+ ResettableClass *rc = RESETTABLE_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;
+ rc->phases.hold = next_pc_reset_hold;
}
static const TypeInfo next_pc_info = {
.name = TYPE_NEXT_PC,
.parent = TYPE_SYS_BUS_DEVICE,
+ .instance_init = next_pc_init,
.instance_size = sizeof(NeXTPC),
.class_init = next_pc_class_init,
};
@@ -1004,11 +1281,32 @@ static void next_cube_init(MachineState *machine)
sysbus_create_simple(TYPE_NEXTFB, 0x0B000000, NULL);
/* MMIO */
- sysbus_mmio_map(SYS_BUS_DEVICE(pcdev), 0, 0x02000000);
+ sysbus_mmio_map(SYS_BUS_DEVICE(pcdev), 0, 0x02005000);
/* BMAP IO - acts as a catch-all for now */
sysbus_mmio_map(SYS_BUS_DEVICE(pcdev), 1, 0x02100000);
+ /* en network (dummy) */
+ sysbus_mmio_map(SYS_BUS_DEVICE(pcdev), 1, 0x02106000);
+
+ /* unknown: Brightness control register? */
+ empty_slot_init("next.unknown.0", 0x02110000, 0x10);
+ /* unknown: Magneto-Optical drive controller? */
+ empty_slot_init("next.unknown.1", 0x02112000, 0x10);
+
+ /* SCSI */
+ sysbus_mmio_map(SYS_BUS_DEVICE(pcdev), 2, 0x02114000);
+ /* Floppy */
+ sysbus_mmio_map(SYS_BUS_DEVICE(pcdev), 3, 0x02114108);
+ /* ESCC */
+ sysbus_mmio_map(SYS_BUS_DEVICE(pcdev), 4, 0x02118000);
+
+ /* unknown: Serial clock configuration register? */
+ empty_slot_init("next.unknown.2", 0x02118004, 0x10);
+
+ /* Timer */
+ sysbus_mmio_map(SYS_BUS_DEVICE(pcdev), 5, 0x0211a000);
+
/* BMAP memory */
memory_region_init_ram_flags_nomigrate(&m->bmapm1, NULL, "next.bmapmem",
64, RAM_SHARED, &error_fatal);
@@ -1036,7 +1334,7 @@ static void next_cube_init(MachineState *machine)
/* Initial PC is always at offset 4 in firmware binaries */
ptr = rom_ptr(0x01000004, 4);
g_assert(ptr != NULL);
- env->pc = ldl_p(ptr);
+ env->pc = ldl_be_p(ptr);
if (env->pc >= 0x01020000) {
error_report("'%s' does not seem to be a valid firmware image.",
bios_name);
@@ -1044,21 +1342,13 @@ static void next_cube_init(MachineState *machine)
}
}
- /* Serial */
- next_escc_init(pcdev);
-
- /* TODO: */
- /* Network */
- /* SCSI */
- next_scsi_init(pcdev, cpu);
-
/* DMA */
memory_region_init_io(&m->dmamem, NULL, &next_dma_ops, machine,
"next.dma", 0x5000);
memory_region_add_subregion(sysmem, 0x02000000, &m->dmamem);
}
-static void next_machine_class_init(ObjectClass *oc, void *data)
+static void next_machine_class_init(ObjectClass *oc, const void *data)
{
MachineClass *mc = MACHINE_CLASS(oc);
@@ -1068,6 +1358,7 @@ static void next_machine_class_init(ObjectClass *oc, void *data)
mc->default_ram_size = RAM_SIZE;
mc->default_ram_id = "next.ram";
mc->default_cpu_type = M68K_CPU_TYPE_NAME("m68040");
+ mc->no_cdrom = true;
}
static const TypeInfo next_typeinfo = {
@@ -1081,6 +1372,8 @@ static void next_register_type(void)
{
type_register_static(&next_typeinfo);
type_register_static(&next_pc_info);
+ type_register_static(&next_scsi_info);
+ type_register_static(&next_rtc_info);
}
type_init(next_register_type)