diff mbox series

[v5,05/11] hw/m68k: Apple Sound Chip (ASC) emulation

Message ID 20181029134000.11157-6-mark.cave-ayland@ilande.co.uk
State New
Headers show
Series hw/m68k: add Apple Machintosh Quadra 800 machine | expand

Commit Message

Mark Cave-Ayland Oct. 29, 2018, 1:39 p.m. UTC
From: Laurent Vivier <laurent@vivier.eu>

This is broken as the linux driver seems broken too...

Co-developed-by: Mark Cave-Ayland <mark.cave-ayland@ilande.co.uk>
Signed-off-by: Mark Cave-Ayland <mark.cave-ayland@ilande.co.uk>
Signed-off-by: Laurent Vivier <laurent@vivier.eu>
---
 hw/audio/Makefile.objs |   1 +
 hw/audio/asc.c         | 446 +++++++++++++++++++++++++++++++++++++++++++++++++
 include/hw/audio/asc.h |  48 ++++++
 3 files changed, 495 insertions(+)
 create mode 100644 hw/audio/asc.c
 create mode 100644 include/hw/audio/asc.h

Comments

Hervé Poussineau Oct. 30, 2018, 6:46 a.m. UTC | #1
Le 29/10/2018 à 14:39, Mark Cave-Ayland a écrit :
> From: Laurent Vivier <laurent@vivier.eu>
> 
> This is broken as the linux driver seems broken too...
> 
> Co-developed-by: Mark Cave-Ayland <mark.cave-ayland@ilande.co.uk>
> Signed-off-by: Mark Cave-Ayland <mark.cave-ayland@ilande.co.uk>
> Signed-off-by: Laurent Vivier <laurent@vivier.eu>
> ---
>   hw/audio/Makefile.objs |   1 +
>   hw/audio/asc.c         | 446 +++++++++++++++++++++++++++++++++++++++++++++++++
>   include/hw/audio/asc.h |  48 ++++++
>   3 files changed, 495 insertions(+)
>   create mode 100644 hw/audio/asc.c
>   create mode 100644 include/hw/audio/asc.h

According to above comment, this device is broken.
Is it necessary for Q800 emulation? What happens if you remove it?
Mark Cave-Ayland Oct. 30, 2018, 10:46 a.m. UTC | #2
On 30/10/2018 06:46, Hervé Poussineau wrote:

> Le 29/10/2018 à 14:39, Mark Cave-Ayland a écrit :
>> From: Laurent Vivier <laurent@vivier.eu>
>>
>> This is broken as the linux driver seems broken too...
>>
>> Co-developed-by: Mark Cave-Ayland <mark.cave-ayland@ilande.co.uk>
>> Signed-off-by: Mark Cave-Ayland <mark.cave-ayland@ilande.co.uk>
>> Signed-off-by: Laurent Vivier <laurent@vivier.eu>
>> ---
>>   hw/audio/Makefile.objs |   1 +
>>   hw/audio/asc.c         | 446 +++++++++++++++++++++++++++++++++++++++++++++++++
>>   include/hw/audio/asc.h |  48 ++++++
>>   3 files changed, 495 insertions(+)
>>   create mode 100644 hw/audio/asc.c
>>   create mode 100644 include/hw/audio/asc.h
> 
> According to above comment, this device is broken.
> Is it necessary for Q800 emulation? What happens if you remove it?

A quick test here shows that I can still boot without it, so it doesn't seem to be
essential. Laurent, should we remove this from the next revision of the patchset? The
general consensus seems to be that if a device isn't required (for now) then it
shouldn't be included.


ATB,

Mark.
Laurent Vivier Oct. 30, 2018, 12:05 p.m. UTC | #3
Le 30/10/2018 à 11:46, Mark Cave-Ayland a écrit :
> On 30/10/2018 06:46, Hervé Poussineau wrote:
> 
>> Le 29/10/2018 à 14:39, Mark Cave-Ayland a écrit :
>>> From: Laurent Vivier <laurent@vivier.eu>
>>>
>>> This is broken as the linux driver seems broken too...
>>>
>>> Co-developed-by: Mark Cave-Ayland <mark.cave-ayland@ilande.co.uk>
>>> Signed-off-by: Mark Cave-Ayland <mark.cave-ayland@ilande.co.uk>
>>> Signed-off-by: Laurent Vivier <laurent@vivier.eu>
>>> ---
>>>   hw/audio/Makefile.objs |   1 +
>>>   hw/audio/asc.c         | 446 +++++++++++++++++++++++++++++++++++++++++++++++++
>>>   include/hw/audio/asc.h |  48 ++++++
>>>   3 files changed, 495 insertions(+)
>>>   create mode 100644 hw/audio/asc.c
>>>   create mode 100644 include/hw/audio/asc.h
>>
>> According to above comment, this device is broken.
>> Is it necessary for Q800 emulation? What happens if you remove it?
> 
> A quick test here shows that I can still boot without it, so it doesn't seem to be
> essential. Laurent, should we remove this from the next revision of the patchset? The
> general consensus seems to be that if a device isn't required (for now) then it
> shouldn't be included.

It can be removed.

I've just checked the kernel and in the case of the Quadra 800 the ASC
is not supported at all.

Thanks,
Laurent
diff mbox series

Patch

diff --git a/hw/audio/Makefile.objs b/hw/audio/Makefile.objs
index 63db383709..44d1ada7b0 100644
--- a/hw/audio/Makefile.objs
+++ b/hw/audio/Makefile.objs
@@ -16,3 +16,4 @@  common-obj-$(CONFIG_MARVELL_88W8618) += marvell_88w8618.o
 common-obj-$(CONFIG_MILKYMIST) += milkymist-ac97.o
 
 common-obj-y += soundhw.o
+common-obj-$(CONFIG_ASC) += asc.o
diff --git a/hw/audio/asc.c b/hw/audio/asc.c
new file mode 100644
index 0000000000..4fd23eb8e1
--- /dev/null
+++ b/hw/audio/asc.c
@@ -0,0 +1,446 @@ 
+/*
+ *  QEMU Apple Sound Chip emulation
+ *
+ *  Apple Sound Chip (ASC) 344S0063
+ *  Enhanced Apple Sound Chip (EASC) 343S1063
+ *
+ *  Copyright (c) 2012-2018 Laurent Vivier <laurent@vivier.eu>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ *
+ */
+
+#include "qemu/osdep.h"
+#include "hw/sysbus.h"
+#include "audio/audio.h"
+#include "hw/audio/asc.h"
+
+/*
+ * Linux doesn't provide information about ASC, see arch/m68k/mac/macboing.c
+ * and arch/m68k/include/asm/mac_asc.h
+ *
+ * best information is coming from MAME:
+ *   http://mamedev.org/source/src/emu/sound/asc.h.html
+ *   http://mamedev.org/source/src/emu/sound/asc.c.html
+ *   Emulation by R. Belmont
+ *
+ *     0x800: VERSION
+ *     0x801: MODE
+ *            1=FIFO mode,
+ *            2=wavetable mode
+ *     0x802: CONTROL
+ *            bit 0=analog or PWM output,
+ *                1=stereo/mono,
+ *                7=processing time exceeded
+ *     0x803: FIFO MODE
+ *            bit 7=clear FIFO,
+ *            bit 1="non-ROM companding",
+ *            bit 0="ROM companding")
+ *     0x804: FIFO IRQ STATUS
+ *            bit 0=ch A 1/2 full,
+ *                1=ch A full,
+ *                2=ch B 1/2 full,
+ *                3=ch B full)
+ *     0x805: WAVETABLE CONTROL
+ *            bits 0-3 wavetables 0-3 start
+ *     0x806: VOLUME
+ *            bits 2-4 = 3 bit internal ASC volume,
+ *            bits 5-7 = volume control sent to Sony sound chip
+ *     0x807: CLOCK RATE
+ *            0 = Mac 22257 Hz,
+ *            1 = undefined,
+ *            2 = 22050 Hz,
+ *            3 = 44100 Hz
+ *     0x80a: PLAY REC A
+ *     0x80f: TEST
+ *            bits 6-7 = digital test,
+ *            bits 4-5 = analog test
+ *     0x810: WAVETABLE 0 PHASE
+ *            big-endian 9.15 fixed-point, only 24 bits valid
+ *     0x814: WAVETABLE 0 INCREMENT
+ *            big-endian 9.15 fixed-point, only 24 bits valid
+ *     0x818: WAVETABLE 1 PHASE
+ *     0x81C: WAVETABLE 1 INCREMENT
+ *     0x820: WAVETABLE 2 PHASE
+ *     0x824: WAVETABLE 2 INCREMENT
+ *     0x828: WAVETABLE 3 PHASE
+ *     0x82C: WAVETABLE 3 INCREMENT
+ */
+
+#define ASC_LENGTH   0x2000
+#define ASC_BUF_SIZE 0x0800
+
+#define ASC_REG_BASE 0x0800
+enum {
+    ASC_VERSION     = 0x00,
+    ASC_MODE        = 0x01,
+    ASC_CONTROL     = 0x02,
+    ASC_FIFOMODE    = 0x03,
+    ASC_FIFOIRQ     = 0x04,
+    ASC_WAVECTRL    = 0x05,
+    ASC_VOLUME      = 0x06,
+    ASC_CLOCK       = 0x07,
+    ASC_PLAYRECA    = 0x0a,
+    ASC_TEST        = 0x0f,
+    ASC_WAVETABLE   = 0x10
+};
+
+static inline uint32_t get_phase(ASCState *s, int channel)
+{
+    return be32_to_cpu(*(uint32_t *)(s->regs + ASC_WAVETABLE + channel * 8));
+}
+
+static inline void set_phase(ASCState *s, int channel, uint32_t phase)
+{
+    *(uint32_t *)(s->regs + ASC_WAVETABLE + channel * 8) = cpu_to_be32(phase);
+}
+
+static inline uint32_t get_incr(ASCState *s, int channel)
+{
+    return be32_to_cpu(*(uint32_t *)(s->regs + ASC_WAVETABLE + 4 +
+                                     channel * 8));
+}
+
+static inline uint32_t incr_phase(ASCState *s, int channel)
+{
+    uint32_t incr = get_incr(s, channel);
+    uint32_t phase = get_phase(s, channel);
+
+    set_phase(s, channel, phase + incr);
+
+    return get_phase(s, channel);
+}
+
+static void generate_fifo(ASCState *s, int free_b)
+{
+    int8_t buf[2048];
+    int i;
+    int to_copy;
+
+    do {
+        to_copy = audio_MIN(sizeof(buf), free_b);
+        for (i = 0; i < (to_copy >> 1); to_copy++) {
+            int8_t left, right;
+
+            left = s->fifo[s->a_rptr] ^ 0x80;
+            right = s->fifo[s->b_rptr + 0x400] ^ 0x80;
+
+            if (s->a_cnt) {
+                s->a_rptr++;
+                s->a_rptr &= 0x3ff;
+                s->a_cnt--;
+            }
+
+            if (s->b_cnt) {
+                s->b_rptr++;
+                s->b_rptr &= 0x3ff;
+                s->b_cnt--;
+            }
+
+            if (s->type == ASC_TYPE_SONORA) {
+                if (s->a_cnt < 0x200) {
+                    s->regs[ASC_FIFOIRQ] |= 4; /* FIFO A less than half full */
+                    qemu_irq_raise(s->irq);
+                }
+                if (s->b_cnt < 0x200) {
+                    s->regs[ASC_FIFOIRQ] |= 8; /* FIFO B less than half full */
+                    qemu_irq_raise(s->irq);
+                }
+            } else {
+                if (s->a_cnt == 0x1ff) {
+                    s->regs[ASC_FIFOIRQ] |= 1; /* FIFO A half empty */
+                    qemu_irq_raise(s->irq);
+                } else if (s->a_cnt == 0x001) {
+                    s->regs[ASC_FIFOIRQ] |= 2; /* FIFO A half empty */
+                    qemu_irq_raise(s->irq);
+                }
+                if (s->b_cnt == 0x1ff) {
+                    s->regs[ASC_FIFOIRQ] |= 4; /* FIFO A half empty */
+                    qemu_irq_raise(s->irq);
+                } else if (s->b_cnt == 0x001) {
+                    s->regs[ASC_FIFOIRQ] |= 8; /* FIFO A half empty */
+                    qemu_irq_raise(s->irq);
+                }
+            }
+            buf[i * 2] = left;
+            buf[i * 2 + 1] = right;
+        }
+        AUD_write(s->channel, buf, to_copy);
+        free_b -= to_copy;
+    } while (free_b);
+}
+
+static void generate_wavetable(ASCState *s, int free_b)
+{
+    int8_t buf[2048];
+    int i;
+    int channel;
+    int to_copy;
+    int control = s->regs[ASC_WAVECTRL];
+
+    do {
+        to_copy = audio_MIN(sizeof(buf), free_b);
+        for (i = 0; i < (to_copy >> 1); i++) {
+                int32_t left, right;
+                int8_t sample;
+
+                left = 0;
+                right = 0;
+
+                if (control) { /* FIXME: how to use it ? */
+                    for (channel = 0; channel < 4; channel++) {
+                        uint32_t phase = incr_phase(s, channel);
+
+                        phase = (phase >> 15) & 0x1ff;
+                        sample = s->fifo[0x200 * channel + phase] ^ 0x80;
+
+                        left += sample;
+                        right += sample;
+                    }
+                    buf[i * 2] = left >> 2;
+                    buf[i * 2 + 1] = right >> 2;
+                } else {
+                    /* FIXME: only works with linux macboing.c */
+                    uint32_t phase = incr_phase(s, 0);
+                    phase = (phase >> 15) & 0x7ff;
+                    sample = s->fifo[phase];
+                    buf[i * 2] = sample;
+                    buf[i * 2 + 1] = sample;
+                }
+        }
+        AUD_write(s->channel, buf, to_copy);
+        free_b -= to_copy;
+    } while (free_b);
+}
+
+static void asc_out_cb(void *opaque, int free_b)
+{
+    ASCState *s = opaque;
+
+    switch (s->regs[ASC_MODE] & 3) {
+    case 0: /* Off */
+        break;
+    case 1: /* FIFO mode */
+        generate_fifo(s, free_b);
+        break;
+    case 2: /* Wave table mode */
+        generate_wavetable(s, free_b);
+        break;
+    }
+}
+
+static uint64_t asc_read(void *opaque, hwaddr addr,
+                              unsigned size)
+{
+    ASCState *s = opaque;
+    uint64_t prev;
+
+    if (addr < 0x800) {
+        return s->fifo[addr];
+    }
+
+    addr -= 0x800;
+
+    if (addr >= 0x030) {
+        return 0;
+    }
+
+    switch (addr) {
+    case ASC_VERSION:
+        switch (s->type) {
+        case ASC_TYPE_ASC:
+            return 0;
+        case ASC_TYPE_V8:
+        case ASC_TYPE_EAGLE:
+        case ASC_TYPE_SPICE:
+        case ASC_TYPE_VASP:
+            return 0xe8;
+        case ASC_TYPE_SONORA:
+            return 0xbc;
+        default:
+            break;
+        }
+        break;
+    case ASC_MODE:
+        switch (s->type) {
+        case ASC_TYPE_V8:
+        case ASC_TYPE_EAGLE:
+        case ASC_TYPE_SPICE:
+        case ASC_TYPE_VASP:
+            return 1;
+        default:
+            break;
+        }
+        break;
+    case ASC_CONTROL:
+        switch (s->type) {
+        case ASC_TYPE_V8:
+        case ASC_TYPE_EAGLE:
+        case ASC_TYPE_SPICE:
+        case ASC_TYPE_VASP:
+            return 1;
+        default:
+            break;
+        }
+        break;
+    case ASC_FIFOIRQ:
+        if (s->type == ASC_TYPE_V8) {
+            prev = 3;
+        } else {
+            prev = s->regs[ASC_FIFOIRQ];
+        }
+        s->regs[ASC_FIFOIRQ] = 0;
+        qemu_irq_lower(s->irq);
+        return prev;
+    default:
+        break;
+    }
+
+    return s->regs[addr];
+}
+
+static void asc_write(void *opaque, hwaddr addr, uint64_t value,
+                           unsigned size)
+{
+    ASCState *s = opaque;
+
+    if (addr < 0x800) {
+        if (s->regs[ASC_MODE] == 1) {
+            if (addr < 0x400) {
+                /* FIFO A */
+                s->fifo[s->a_wptr++] = value;
+                s->a_cnt++;
+                if (s->a_cnt == 0x3ff) {
+                    s->regs[ASC_FIFOIRQ] |= 2; /* FIFO A Full */
+                }
+                s->a_wptr &= 0x3ff;
+            } else {
+                /* FIFO B */
+                s->fifo[s->b_wptr++ + 0x400] = value;
+                s->b_cnt++;
+                if (s->b_cnt == 0x3ff) {
+                    s->regs[ASC_FIFOIRQ] |= 8; /* FIFO B Full */
+                }
+                s->b_wptr &= 0x3ff;
+            }
+        } else {
+            s->fifo[addr] = value;
+        }
+        return;
+    }
+
+    addr -= 0x800;
+    if (addr >= 0x30) {
+        return;
+    }
+    switch (addr) {
+    case ASC_MODE:
+        value &= 3;
+        if (value != s->regs[ASC_MODE]) {
+            s->a_rptr = 0;
+            s->a_wptr = 0;
+            s->a_cnt = 0;
+            s->b_rptr = 0;
+            s->b_wptr = 0;
+            s->b_cnt = 0;
+            if (value != 0) {
+                AUD_set_active_out(s->channel, 1);
+            } else {
+                AUD_set_active_out(s->channel, 0);
+            }
+        }
+        break;
+    case ASC_FIFOMODE:
+        if (value & 0x80) {
+            s->a_rptr = 0;
+            s->a_wptr = 0;
+            s->a_cnt = 0;
+            s->b_rptr = 0;
+            s->b_wptr = 0;
+            s->b_cnt = 0;
+        }
+        break;
+    case ASC_WAVECTRL:
+        break;
+    }
+    s->regs[addr] = value;
+}
+
+static const MemoryRegionOps asc_mmio_ops = {
+    .read = asc_read,
+    .write = asc_write,
+    .impl = {
+        .min_access_size = 1,
+        .max_access_size = 1,
+    },
+    .endianness = DEVICE_BIG_ENDIAN,
+};
+
+static void asc_reset(DeviceState *d)
+{
+    ASCState *s = ASC(d);
+
+    AUD_set_active_out(s->channel, 0);
+
+    memset(s->regs, 0, sizeof(s->regs));
+    s->a_wptr = 0;
+    s->a_rptr = 0;
+    s->a_cnt = 0;
+    s->b_wptr = 0;
+    s->b_rptr = 0;
+    s->b_cnt = 0;
+}
+
+static void asc_init(Object *obj)
+{
+    ASCState *s = ASC(obj);
+    SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
+    struct audsettings as;
+
+    AUD_register_card("Apple Sound Chip", &s->card);
+
+    as.freq = 22257;
+    as.nchannels = 2;
+    as.fmt = AUD_FMT_S8;
+    as.endianness = 0;
+
+    s->channel = AUD_open_out(&s->card, s->channel, "asc.out",
+                              s, asc_out_cb, &as);
+
+    s->fifo = g_malloc0(ASC_BUF_SIZE);
+
+    memory_region_init_io(&s->mem_regs, obj, &asc_mmio_ops, s, "asc",
+                          ASC_LENGTH);
+
+    sysbus_init_irq(sbd, &s->irq);
+    sysbus_init_mmio(sbd, &s->mem_regs);
+}
+
+static Property asc_properties[] = {
+    DEFINE_PROP_UINT8("asctype", ASCState, type, ASC_TYPE_ASC),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void asc_class_init(ObjectClass *oc, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(oc);
+
+    dc->reset = asc_reset;
+    dc->props = asc_properties;
+}
+
+static TypeInfo asc_info = {
+    .name = TYPE_ASC,
+    .parent = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(ASCState),
+    .instance_init = asc_init,
+    .class_init = asc_class_init,
+};
+
+static void asc_register_types(void)
+{
+    type_register_static(&asc_info);
+}
+
+type_init(asc_register_types)
diff --git a/include/hw/audio/asc.h b/include/hw/audio/asc.h
new file mode 100644
index 0000000000..3540e32f69
--- /dev/null
+++ b/include/hw/audio/asc.h
@@ -0,0 +1,48 @@ 
+/*
+ *  Copyright (c) 2012-2018 Laurent Vivier <laurent@vivier.eu>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ *
+ */
+
+#ifndef HW_AUDIO_ASC_H
+#define HW_AUDIO_ASC_H
+
+#include "qemu/osdep.h"
+#include "hw/sysbus.h"
+#include "audio/audio.h"
+
+enum {
+    ASC_TYPE_ASC    = 0,  /* original discrete Apple Sound Chip */
+    ASC_TYPE_EASC   = 1,  /* discrete Enhanced Apple Sound Chip */
+    ASC_TYPE_V8     = 2,  /* ASC included in the V8 ASIC (LC/LCII) */
+    ASC_TYPE_EAGLE  = 3,  /* ASC included in the Eagle ASIC (Classic II) */
+    ASC_TYPE_SPICE  = 4,  /* ASC included in the Spice ASIC (Color Classic) */
+    ASC_TYPE_SONORA = 5,  /* ASC included in the Sonora ASIC (LCIII) */
+    ASC_TYPE_VASP   = 6,  /* ASC included in the VASP ASIC  (IIvx/IIvi) */
+    ASC_TYPE_ARDBEG = 7   /* ASC included in the Ardbeg ASIC (LC520) */
+};
+
+typedef struct ASCState {
+    SysBusDevice parent_obj;
+
+    MemoryRegion mem_regs;
+    QEMUSoundCard card;
+    SWVoiceOut *channel;
+
+    qemu_irq irq;
+
+    uint8_t type;
+    int a_wptr, a_rptr, a_cnt;
+    int b_wptr, b_rptr, b_cnt;
+
+    uint8_t *fifo;
+
+    uint8_t regs[48];
+} ASCState;
+
+#define TYPE_ASC "apple-sound-chip"
+#define ASC(obj) OBJECT_CHECK(ASCState, (obj), TYPE_ASC)
+
+#endif