[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [Qemu-devel] [PATCH v2 3/4] m68k: Add NeXTcube machine
From: |
Philippe Mathieu-Daudé |
Subject: |
Re: [Qemu-devel] [PATCH v2 3/4] m68k: Add NeXTcube machine |
Date: |
Sat, 29 Jun 2019 14:26:23 +0200 |
User-agent: |
Mozilla/5.0 (X11; Linux x86_64; rv:60.0) Gecko/20100101 Thunderbird/60.7.0 |
On 6/28/19 8:15 PM, Thomas Huth wrote:
> It is still quite incomplete (no SCSI, no floppy emulation, no network,
> etc.), but the firmware already shows up the debug monitor prompt in the
> framebuffer display, so at least the very basics are already working.
>
> This code has been taken from Bryce Lanham's GSoC 2011 NeXT branch at
>
> https://github.com/blanham/qemu-NeXT/blob/next-cube/hw/next-cube.c
>
> and altered quite a bit to fit the latest interface and coding conventions
> of the current QEMU.
>
> Signed-off-by: Thomas Huth <address@hidden>
> ---
> hw/m68k/Makefile.objs | 2 +-
> hw/m68k/next-cube.c | 988 ++++++++++++++++++++++++++++++++++++
> include/hw/m68k/next-cube.h | 38 ++
> 3 files changed, 1027 insertions(+), 1 deletion(-)
> create mode 100644 hw/m68k/next-cube.c
>
> diff --git a/hw/m68k/Makefile.objs b/hw/m68k/Makefile.objs
> index 688002cac1..f25854730d 100644
> --- a/hw/m68k/Makefile.objs
> +++ b/hw/m68k/Makefile.objs
> @@ -1,3 +1,3 @@
> obj-$(CONFIG_AN5206) += an5206.o mcf5206.o
> obj-$(CONFIG_MCF5208) += mcf5208.o mcf_intc.o
> -obj-$(CONFIG_NEXTCUBE) += next-kbd.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..700d386fb9
> --- /dev/null
> +++ b/hw/m68k/next-cube.c
> @@ -0,0 +1,988 @@
> +/*
> + * 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 "exec/address-spaces.h"
> +#include "sysemu/sysemu.h"
> +#include "sysemu/qtest.h"
> +#include "hw/hw.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 "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 "rom66.bin"
Where can we find this file to test your work?
> +
> +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;
> +#ifdef READ_RTC
> + FILE *fp = fopen("rtc.ram", "wb+");
> + int ret = fwrite(s->rtc_ram, 1, 32, fp);
> + if (ret != 32) {
> + abort();
> + }
> + fclose(fp);
> +#endif
> + }
> + /* 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;
So you have a 32-bit implementation (DMA accessed device?).
memory::access_with_adjusted_size() already does this work
for you if you use:
.impl.min_access_size = 4,
.valid.min_access_size = 1,
.valid.max_access_size = 4,
> +
> + 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;
Hmmm again the weird 16-bit case...
> + }
> +}
> +
> +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 0xc800:
> + * return 0x01000000;
> + */
> +
> + 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);
So it seems we have .valid.max_access_size = 1, and you can simplify
a lot of code.
> +}
> +
> +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_SAVED_NEXT 0x3FF0
> +#define NEXTDMA_SAVED_LIMIT 0x3FF4
> +#define NEXTDMA_SAVED_START 0x3FF8
> +#define NEXTDMA_SAVED_STOP 0x3FFc
> +#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(NeXTState *next_state, hwaddr addr, uint32_t value)
> +{
> + /*
> + * uint16_t reg = addr;
> + * int channel = 0;
> + * switch (reg) {
> + * case SCSI"all registers:
> + * channel = NEXTDMA_SCSI;
> + * addr = addr - NEXTDMA_SCSI(0);
> + * }
> + */
> +
> + 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 (!(next_state->dma[NEXTDMA_ENRX].csr & DMA_DEV2M))
> + * DPRINTF("DMA TO DEVICE\n");
> + * else
> + * DPRINTF("DMA TO CPU\n");
> + * if (next_state->scsi_csr_1 & 1<<3)
> + * DPRINTF("SCSI DIR\n");
> + */
> + }
> + 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 (!(next_state->dma[NEXTDMA_SCSI].csr & DMA_DEV2M)) {
> + * DPRINTF("DMA TO DEVICE\n");
> + * } else {
> + * DPRINTF("DMA TO CPU\n");
> + * }
> + * if (next_state->scsi_csr_1 & 1<<3) {
> + * DPRINTF("SCSI DIR\n");
> + * }
> + */
> + }
> + 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 int)addr, value);
> + }
> +}
> +
> +static uint32_t dma_readl(NeXTState *next_state, hwaddr addr)
> +{
> + 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 uint64_t dma_readfn(void *opaque, hwaddr addr, unsigned size)
> +{
> + NeXTState *ns = NEXT_MACHINE(opaque);
> +
> + switch (size) {
> + case 4:
> + return dma_readl(ns, addr);
Well, maybe you can directly use dma_readfn prototype for dma_readl, and
remove this function and dma_writefn (you know we'll ever get 32-bit
accesses here due to .valid.min/max_access_size = 4).
> + default:
> + g_assert_not_reached();
> + }
> +}
> +
> +static void dma_writefn(void *opaque, hwaddr addr, uint64_t value,
> + unsigned size)
> +{
> + NeXTState *ns = NEXT_MACHINE(opaque);
> +
> + switch (size) {
> + case 4:
> + dma_writel(ns, addr, value);
> + break;
> + default:
> + g_assert_not_reached();
> + }
> +}
> +
> +static const MemoryRegionOps dma_ops = {
> + .read = dma_readfn,
> + .write = dma_writefn,
> + .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_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 *unknownmem = g_new(MemoryRegion, 1);
> + MemoryRegion *sysmem = get_system_memory();
> + NeXTState *ns = NEXT_MACHINE(machine);
> +
> + /* 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->pc = 0x100001e; /* technically should read vector */
> + env->sr = 0x2700;
> +
> + /* Set internal registers to initial values */
> + /* 0x0000XX00 << vital bits */
> + ns->scr1 = 0x00011102;
> + ns->scr2 = 0x00ff0c80;
> +
> + ns->int_mask = 0x0; /* 88027640; */
> + ns->int_status = 0x0; /* 200; */
What mean those comments?
> +
> + /* Load RTC RAM - TODO: provide possibility to load contents from file */
> + memcpy(ns->rtc_ram, rtc_ram2, 32);
> +
> + /* 64MB RAM starting at 0x4000000 */
> + memory_region_allocate_system_memory(ram, NULL, "next.ram", ram_size);
> + memory_region_add_subregion(sysmem, 0x4000000, ram);
0x04000000
> +
> + /* Framebuffer */
> + nextfb_init();
Todays QEMU style seems to create device in place (here).
> +
> + /* MMIO */
> + memory_region_init_io(mmiomem, NULL, &mmio_ops, machine, "next.mmio",
> + 0xD0000);
> + memory_region_add_subregion(sysmem, 0x2000000, mmiomem);
0x02000000
> +
> + /* BMAP - acts as a catch-all for now */
> + memory_region_init_io(scrmem, NULL, &scr_ops, machine, "next.scr",
> + 0x3A7FF);
0x3A7FF? 0x3a800 at least, but why not use a 256 * KiB full range?
Wait... Isn't this I/O range of 128KB?
> + memory_region_add_subregion(sysmem, 0x2100000, scrmem);
0x02100000
> +
> + /* KBD */
> + nextkbd_init();
Ditto (create in place).
> +
> + /* 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, 0x1000000, rom);
0x01000000
> + if (load_image_targphys(bios_name, 0x1000000, 0x20000) < 0) {
> + if (!qtest_enabled()) {
> + error_report("Failed to load firmware '%s'", bios_name);
> + }
> + }
> +
> + /* TODO: */
> + /* Serial */
> + /* Network */
> + /* SCSI */
Can you use create_unimplemented_device() here?
> +
> + /* DMA */
> + memory_region_init_io(dmamem, NULL, &dma_ops, machine, "next.dma",
> 0x5000);
> + memory_region_add_subregion(sysmem, 0x2000000, dmamem);
0x02000000
> +
> + /* FIXME: Why does the bios access this memory area? */
> + memory_region_allocate_system_memory(unknownmem, NULL, "next.unknown",
> 16);
> + memory_region_add_subregion(sysmem, 0x820c0020, unknownmem);
Isn't this uncached access to 0x020c0000?
> +}
> +
> +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/include/hw/m68k/next-cube.h b/include/hw/m68k/next-cube.h
> index 88e94f6595..d7df6a223b 100644
> --- a/include/hw/m68k/next-cube.h
> +++ b/include/hw/m68k/next-cube.h
> @@ -2,6 +2,44 @@
> #ifndef NEXT_CUBE_H
> #define NEXT_CUBE_H
>
> +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
The DMA code is consequent enough to deserve its own file IMO.
Regards,
Phil.
> +
> +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);
> +
> /* next-fb.c */
> void nextfb_init(void);
>
>
- [Qemu-devel] [PATCH v2 0/4] m68k: Add basic support for the NeXTcube machine, Thomas Huth, 2019/06/28
- [Qemu-devel] [PATCH v2 3/4] m68k: Add NeXTcube machine, Thomas Huth, 2019/06/28
- Re: [Qemu-devel] [PATCH v2 3/4] m68k: Add NeXTcube machine,
Philippe Mathieu-Daudé <=
- [Qemu-devel] [PATCH v2 2/4] m68k: Add NeXTcube keyboard device, Thomas Huth, 2019/06/28
- [Qemu-devel] [PATCH v2 1/4] m68k: Add NeXTcube framebuffer device emulation, Thomas Huth, 2019/06/28
- [Qemu-devel] [PATCH v2 4/4] m68k: Add an entry for the NeXTcube machine to the MAINTAINERS file, Thomas Huth, 2019/06/28
- Re: [Qemu-devel] [PATCH v2 0/4] m68k: Add basic support for the NeXTcube machine, Philippe Mathieu-Daudé, 2019/06/29