qemu-devel
[Top][All Lists]
Advanced

[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);
>  
> 



reply via email to

[Prev in Thread] Current Thread [Next in Thread]