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

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

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..1066bf4488
--- /dev/null
+++ b/hw/audio/asc.c
@@ -0,0 +1,463 @@
+/*
+ *  QEMU Apple Sound Chip emulation
+ *
+ *  Apple Sound Chip (ASC) 344S0063
+ *  Enhanced Apple Sound Chip (EASC) 343S1063
+ *
+ *  Copyright (c) 2012-2018 Laurent Vivier <laur...@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 int asc_post_load(void *opaque, int version_id)
+{
+    return 0;
+}
+
+static const VMStateDescription vmstate_asc = {
+    .name = "apple-sound-chip",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .post_load = asc_post_load,
+    .fields      = (VMStateField[]) {
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+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, NULL, &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->vmsd = &vmstate_asc;
+    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 <laur...@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
-- 
2.14.4


Reply via email to