Add aica firmware
authorJason Self <j@jxself.org>
Sun, 20 May 2018 22:36:41 +0000 (15:36 -0700)
committerJason Self <j@jxself.org>
Sun, 20 May 2018 22:36:41 +0000 (15:36 -0700)
This is used to provide sound on the Sega Dreamcast.

12 files changed:
INSTALL
Makefile
WHENCE
aica/AUTHORS [new file with mode: 0644]
aica/Dreamcast_sound.txt [new file with mode: 0644]
aica/README.KOS [new file with mode: 0644]
aica/arm/Makefile [new file with mode: 0644]
aica/arm/aica.c [new file with mode: 0644]
aica/arm/aica.h [new file with mode: 0644]
aica/arm/aica_cmd_iface.h [new file with mode: 0644]
aica/arm/crt0.s [new file with mode: 0644]
aica/arm/main.c [new file with mode: 0644]

diff --git a/INSTALL b/INSTALL
index dfabf2aa0887b97610652d58455a3706a84596ce..538a5df5ca39fb10234fd4f1296fdcefa2e8fb99 100644 (file)
--- a/INSTALL
+++ b/INSTALL
@@ -25,13 +25,16 @@ system:
                 - arm-linux-gnueabi-gcc
                 - arm-linux-gnueabi-ld
                 - arm-linux-gnueabi-objcopy
+               - arm-none-eabi-gcc
+               - arm-none-eabi-objcopy
+               - arm-none-eabi-as
 
 CARL9170 Firmware Configuration
 When building the carl9170 firmware you will be prompted with
 configuration questions.
 
 -----
-Copyright (C) 2017 Jason Self <j@jxself.org>
+Copyright (C) 2017, 2018 Jason Self <j@jxself.org>
 Copying and distribution of this file, with or without modification,
 are permitted in any medium without royalty provided the copyright
 notice and this notice are preserved.  This file is offered as-is,
index 122f659c13a83eda6d4bac50f2278378cc7467ec..393831ce41d6207487dfafd09b1d5c297b9c2ab6 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,4 +1,4 @@
-# Copyright (C) 2017 Jason Self <j@jxself.org>
+# Copyright (C) 2017, 2018 Jason Self <j@jxself.org>
 #
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
@@ -17,9 +17,9 @@ shell=/bin/sh
 prefix=/lib/firmware
 install_program=install
 
-.PHONY:        all test clean install a56 as31 ath9k_htc_toolchain ath9k_htc_firmware av7110 b43-tools carl9170fw-toolchain carl9170fw cis-tools cis dsp56k ihex2fw isci keyspan_pda openfwwf usbdux
+.PHONY:        all test clean install a56 as31 aica ath9k_htc_toolchain ath9k_htc_firmware av7110 b43-tools carl9170fw-toolchain carl9170fw cis-tools cis dsp56k ihex2fw isci keyspan_pda openfwwf usbdux
 
-all: ath9k_htc av7110 carl9170fw cis dsp56k isci keyspan_pda openfwwf usbdux
+all: aica ath9k_htc av7110 carl9170fw cis dsp56k isci keyspan_pda openfwwf usbdux
 
 a56:
        cd a56 && $(MAKE)
@@ -27,6 +27,9 @@ a56:
 as31:
        cd as31 && ./configure && $(MAKE)
 
+aica:
+       cd aica/arm && $(MAKE)
+
 ath9k_htc_toolchain:
        cd ath9k_htc && $(MAKE) toolchain
 
@@ -73,6 +76,7 @@ test:
        @echo This function is not implemented.
 
 clean:
+       cd aica/arm && $(MAKE) clean
        cd a56 && $(MAKE) clean
        if [ -a as31/Makefile ]; then cd as31 && $(MAKE) clean; fi;
        cd ath9k_htc && $(MAKE) toolchain-clean
@@ -90,6 +94,7 @@ clean:
        cd usbdux && $(MAKE) -f Makefile_dux clean
 
 install:
+        if [ -a aica/arm/aica_firmware.bin ]; then $(install_program) -D aica/arm/aica_firmware.bin $(prefix)/aica_firmware.bin; fi;
        if [ -a ath9k_htc/target_firmware/htc_9271.fw ]; then $(install_program) -D ath9k_htc/target_firmware/htc_9271.fw $(prefix)/htc_9271.fw; fi;
        if [ -a ath9k_htc/target_firmware/htc_7010.fw ]; then $(install_program) -D ath9k_htc/target_firmware/htc_7010.fw $(prefix)/htc_7010.fw; fi;
        if [ -a av7110/bootcode.bin ]; then $(install_program) -D av7110/bootcode.bin $(prefix)/av7110/bootcode.bin; fi;
diff --git a/WHENCE b/WHENCE
index ab77bb10fb13202ccbf81f491365f4588ee4954f..09dd5e9c2cf9552963f959a7ad3f7eeb19b01297 100644 (file)
--- a/WHENCE
+++ b/WHENCE
@@ -46,6 +46,22 @@ From http://www.zdomain.com/a56.html
 
 --------------------------------------------------------------------------
 
+aica: Firmware for the sound card in the Sega Dreamcast
+
+Version: Based on commit 832ea65b43c8b402f19f3b6b3ecb8804f73c948a
+dated 17 May 2018
+
+License: KOS License (see README.KOS)
+
+From:
+Firmware from KallistiOS:
+https://sourceforge.net/p/cadcdev/kallistios/
+Dreamcast_sound.txt from:
+ftp://ftp.alsa-project.org/pub/firmware/alsa-firmware-1.0.29.tar.bz2
+(With typo corrections)
+
+--------------------------------------------------------------------------
+
 as31: An Intel 8031/8051 assembler
 
 Version: 2.3.1 with some additional patches from the Debian as31 
diff --git a/aica/AUTHORS b/aica/AUTHORS
new file mode 100644 (file)
index 0000000..08eb927
--- /dev/null
@@ -0,0 +1,118 @@
+KallistiOS Authors List
+-----------------------
+The following is a list of all of the attributed authors for KallistiOS, as well
+as the year(s) of their contributions. As KallistiOS is copyrighted computer
+software, any use of any parts of KallistiOS must give attribution to the
+authors who produced it. The names here are listed in no particular order. It is
+the responsibility of any contributors to KallistiOS to add themselves to this
+file in order to document their contributions. Note that this list covers only
+KallistiOS itself, and not any utilities that are bundled with it nor any
+external libraries that it may use on any given platform (such as Newlib or
+libgcc). Please consult a legal professional if you intend to use KallistiOS for
+any commercial purposes to clarify the licensing restrictions that may be placed
+on your software due to its use of KallistiOS or other libraries.
+
+Also, keep in mind that libraries included in kos-ports are also not covered by
+this list, and may have differing license restrictions than KallistiOS itself.
+Once again, if in doubt, contact a legal professional.
+
+For more information about the license that KallistiOS is distributed under,
+please see the README.KOS file in the doc directory.
+
+Contributors list (under the normal KOS license):
+-------------------------------------------------
+Dan Potter: 1997, 2000, 2001, 2002, 2003, 2004
+Lawrence Sebald: 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018
+Sam Steele: 2004
+Gil Megidish: 2002
+Florian Schulze: 2002
+Walter van Niftrik: 2005
+Donald Haase: 2008, 2014
+Andrew Kieschnick: 2000, 2001, 2002, 2003
+Jordan DeLong: 2000, 2001, 2002
+Bero: 2002
+Kazuaki Matsumoto: 2002
+Anders Clerwall (scav): 2001
+Nick Kochakian: 2002, 2004
+Vincent Penne: 2004
+Roger Cattermole: 2002
+Paul Boese: 2002
+Brian Paul: 1999, 2000, 2001
+Josh Pearson: 2013, 2014, 2015, 2016
+Joe Fenton: 2016
+Stefan Galowicz: 2016, 2017
+
+Files with Specific licenses:
+-----------------------------
+include/pthread.h:
+/*  pthread.h
+ *
+ *  Written by Joel Sherrill <joel@OARcorp.com>.
+ *
+ *  COPYRIGHT (c) 1989-2000.
+ *  On-Line Applications Research Corporation (OAR).
+ *
+ *  Permission to use, copy, modify, and distribute this software for any
+ *  purpose without fee is hereby granted, provided that this entire notice
+ *  is included in all copies of any software which is or includes a copy
+ *  or modification of this software.
+ *
+ *  THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
+ *  WARRANTY.  IN PARTICULAR,  THE AUTHOR MAKES NO REPRESENTATION
+ *  OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY OF THIS
+ *  SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
+ *
+ */
+
+kernel/libc/koslib/realpath.c:
+/*
+ * Copyright (c) 2003 Constantin S. Svintsoff <kostik@iclub.nsu.ru>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The names of the authors may not be used to endorse or promote
+ *    products derived from this software without specific prior written
+ *    permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+kernel/arch/dreamcast/kernel/gdb_stub.c:
+/*   This is originally based on an m68k software stub written by Glenn
+     Engel at HP, but has changed quite a bit.
+
+     Modifications for the SH by Ben Lee and Steve Chamberlain
+
+     Modifications for KOS by Dan Potter (earlier) and Richard Moats (more
+     recently).
+*/
+
+/****************************************************************************
+
+        THIS SOFTWARE IS NOT COPYRIGHTED
+
+   HP offers the following for use in the public domain.  HP makes no
+   warranty with regard to the software or it's performance and the
+   user accepts the software "AS IS" with all faults.
+
+   HP DISCLAIMS ANY WARRANTIES, EXPRESS OR IMPLIED, WITH REGARD
+   TO THIS SOFTWARE INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+   OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+
+****************************************************************************/
diff --git a/aica/Dreamcast_sound.txt b/aica/Dreamcast_sound.txt
new file mode 100644 (file)
index 0000000..08c3bac
--- /dev/null
@@ -0,0 +1,17 @@
+SOUND ON THE SEGA DREAMCAST
+
+The Sega Dreamcast includes an ARM7 processor that separately handles sound
+playback. The ARM7 processor has its own address space which is only partially
+accessible to the main SH4 processor.
+
+The ALSA driver loads data to the ARM7 address space using DMA (which will
+run at a good speed). The old OSS driver did not use DMA and so showed
+very poor performance at high bit rates.
+
+The driver also loads some simple firmware (separately licenced under a 
+modified BSD licence) which controls the ARM7 processor. Without the 
+firmware the driver would merely fill the ARM7's address space and would 
+not playback any sound.
+
+The firmware, by default, should be loaded to /lib/firmware/ and should
+be named aica_firmware.bin.
diff --git a/aica/README.KOS b/aica/README.KOS
new file mode 100644 (file)
index 0000000..cd3bf69
--- /dev/null
@@ -0,0 +1,70 @@
+Most of the code of KallistiOS proper is currently covered under the KOS
+License, which are the terms of the *new* BSD license with our names
+inserted as the copyright holders and the "advertising clause" removed
+entirely. In all files that state that they are part of the KallistiOS
+operating system, you can assume that the following text is inserted in
+the header:
+
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the KOS License.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+* KOS License (README.KOS) for more details.
+*
+* You should have received a copy of the KOS License along with this
+* program; if not, please visit Cryptic Allusion Game Dev at:
+*
+*   http://gamedev.allusion.net/
+*
+
+The text of that license follows. In layman's terms, all it really
+says is that you have to give credit where credit is due (both in
+derived source files and binary compilations; a credit in the
+documentation is ok) and there is no warranty.
+
+                                                       Dan Potter
+
+
+Giving credit means giving credit to everyone involved. The AUTHORS file in
+the root directory of the KOS source tree is there to help. It should note all
+of the named contributors (those who have provided copyright notices in any
+files) to the KOS codebase, along with the years of their contributions. It's
+there to make it pretty easy to give credit where credit is due. :)
+
+                                                       Lawrence Sebald
+
+The actual license terms begin below this line:
+--------------------------------------------------------------------------------
+
+All of the documentation and software included in the KallistiOS Releases
+is copyrighted (C) 1997-2016 by Dan Potter, Lawrence Sebald, and others (as
+noted in each file).
+
+Copyright (C) 1997-2016 KallistiOS Contributors. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+3. Neither the name of Cryptic Allusion nor the names of its contributors
+   may be used to endorse or promote products derived from this software
+   without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
diff --git a/aica/arm/Makefile b/aica/arm/Makefile
new file mode 100644 (file)
index 0000000..d2c49c6
--- /dev/null
@@ -0,0 +1,22 @@
+CROSS_COMPILE = arm-none-eabi-
+export DC_ARM_CFLAGS="-O2"
+export DC_ARM_OBJCOPY="$(CROSS_COMPILE)objcopy"
+export DC_ARM_CC="$(CROSS_COMPILE)gcc"
+export DC_ARM_AS="$(CROSS_COMPILE)as"
+
+all: aica_firmware.bin
+
+aica_firmware.bin: prog.elf
+        $(DC_ARM_OBJCOPY) -O binary prog.elf aica_firmware.bin
+
+prog.elf: crt0.o main.o aica.o
+        $(DC_ARM_CC) -Wl,-Ttext,0x00000000,-Map,prog.map,-N -nostartfiles -nostdlib -e reset -o prog.elf crt0.o main.o aica.o -lgcc
+
+%.o: %.c
+        $(DC_ARM_CC) $(DC_ARM_CFLAGS) -c $< -o $@
+
+%.o: %.s
+        $(DC_ARM_AS) $(DC_ARM_AFLAGS) $< -o $@
+
+clean:
+        -rm -f *.o *.srec *.elf 1ST_READ.BIN prog.bin *.bck prog.map aica_firmware.bin
diff --git a/aica/arm/aica.c b/aica/arm/aica.c
new file mode 100644 (file)
index 0000000..2ff2ca1
--- /dev/null
@@ -0,0 +1,232 @@
+/* KallistiOS ##version##
+
+   aica.c
+   (c)2000-2002 Dan Potter
+
+   ARM support routines for using the wavetable channels
+*/
+
+#include "aica_cmd_iface.h"
+#include "aica.h"
+
+extern volatile aica_channel_t *chans;
+
+void aica_init() {
+    int i, j;
+
+    /* Initialize AICA channels */
+    SNDREG32(0x2800) = 0x0000;
+
+    for(i = 0; i < 64; i++) {
+        CHNREG32(i, 0) = 0x8000;
+
+        for(j = 4; j < 0x80; j += 4)
+            CHNREG32(i, j) = 0;
+
+        CHNREG32(i, 20) = 0x1f;
+    }
+
+    SNDREG32(0x2800) = 0x000f;
+}
+
+/* Translates a volume from linear form to logarithmic form (required by
+   the AICA chip */
+static int logs[] = {
+    0, 15, 22, 27, 31, 35, 39, 42, 45, 47, 50, 52, 55, 57, 59, 61,
+    63, 65, 67, 69, 71, 73, 74, 76, 78, 79, 81, 82, 84, 85, 87, 88,
+    90, 91, 92, 94, 95, 96, 98, 99, 100, 102, 103, 104, 105, 106,
+    108, 109, 110, 111, 112, 113, 114, 116, 117, 118, 119, 120, 121,
+    122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134,
+    135, 136, 137, 138, 138, 139, 140, 141, 142, 143, 144, 145, 146,
+    146, 147, 148, 149, 150, 151, 152, 152, 153, 154, 155, 156, 156,
+    157, 158, 159, 160, 160, 161, 162, 163, 164, 164, 165, 166, 167,
+    167, 168, 169, 170, 170, 171, 172, 173, 173, 174, 175, 176, 176,
+    177, 178, 178, 179, 180, 181, 181, 182, 183, 183, 184, 185, 185,
+    186, 187, 187, 188, 189, 189, 190, 191, 191, 192, 193, 193, 194,
+    195, 195, 196, 197, 197, 198, 199, 199, 200, 200, 201, 202, 202,
+    203, 204, 204, 205, 205, 206, 207, 207, 208, 209, 209, 210, 210,
+    211, 212, 212, 213, 213, 214, 215, 215, 216, 216, 217, 217, 218,
+    219, 219, 220, 220, 221, 221, 222, 223, 223, 224, 224, 225, 225,
+    226, 227, 227, 228, 228, 229, 229, 230, 230, 231, 232, 232, 233,
+    233, 234, 234, 235, 235, 236, 236, 237, 237, 238, 239, 239, 240,
+    240, 241, 241, 242, 242, 243, 243, 244, 244, 245, 245, 246, 246,
+    247, 247, 248, 248, 249, 249, 250, 250, 251, 251, 252, 252, 253, 254, 255
+};
+
+static inline int calc_aica_vol(int x) {
+    return 0xff - logs[x & 0xff];
+}
+
+static inline int calc_aica_pan(int x) {
+    if(x == 0x80)
+        return 0;
+    else if(x < 0x80) {
+        return 0x10 | ((0x7f - x) >> 3);
+    }
+    else {
+        return (x - 0x80) >> 3;
+    }
+}
+
+/* Sets up a sound channel completely. This is generally good if you want
+   a quick and dirty way to play notes. If you want a more comprehensive
+   set of routines (more like PC wavetable cards) see below.
+
+   ch is the channel to play on (0 - 63)
+   smpptr is the pointer to the sound data; if you're running off the
+     SH4, then this ought to be (ptr - 0xa0800000); otherwise it's just
+     ptr. Basically, it's an offset into sound ram.
+   mode is one of the mode constants (16 bit, 8 bit, ADPCM)
+   nsamp is the number of samples to play (not number of bytes!)
+   freq is the sampling rate of the sound
+   vol is the volume, 0 to 0xff (0xff is louder)
+   pan is a panning constant -- 0 is left, 128 is center, 255 is right.
+
+   This routine (and the similar ones) owe a lot to Marcus' sound example --
+   I hadn't gotten quite this far into dissecting the individual regs yet. */
+void aica_play(int ch, int delay) {
+    uint32 smpptr   = chans[ch].base;
+    uint32 mode     = chans[ch].type;
+    uint32 loopst   = chans[ch].loopstart;
+    uint32 loopend  = chans[ch].loopend;
+    uint32 freq     = chans[ch].freq;
+    uint32 vol      = chans[ch].vol;
+    uint32 pan      = chans[ch].pan;
+    uint32 loopflag = chans[ch].loop;
+
+    uint32 freq_lo, freq_base = 5644800;
+    int freq_hi = 7;
+    uint32 i;
+    uint32 playCont;
+
+    /* Stop the channel (if it's already playing) */
+    aica_stop(ch);
+
+    /* Need to convert frequency to floating point format
+       (freq_hi is exponent, freq_lo is mantissa)
+       Formula is freq = 44100*2^freq_hi*(1+freq_lo/1024) */
+    while(freq < freq_base && freq_hi > -8) {
+        freq_base >>= 1;
+        --freq_hi;
+    }
+
+    freq_lo = (freq << 10) / freq_base;
+
+    /* Envelope setup. The first of these is the loop point,
+       e.g., where the sample starts over when it loops. The second
+       is the loop end. This is the full length of the sample when
+       you are not looping, or the loop end point when you are (though
+       storing more than that is a waste of memory if you're not doing
+       volume enveloping). */
+    CHNREG32(ch, 8) = loopst & 0xffff;
+    CHNREG32(ch, 12) = loopend & 0xffff;
+
+    /* Write resulting values */
+    CHNREG32(ch, 24) = (freq_hi << 11) | (freq_lo & 1023);
+
+    /* Set volume, pan */
+    CHNREG8(ch, 36) = calc_aica_pan(pan);
+    CHNREG8(ch, 37) = 0xf;
+    /* turn off Low Pass Filter (LPF) */
+    CHNREG8(ch, 40) = 0x24;
+    /* Convert the incoming volume and pan into hardware values */
+    /* Vol starts at zero so we can ramp */
+    CHNREG8(ch, 41) = 0xff;
+
+    /* If we supported volume envelopes (which we don't yet) then
+       this value would set that up. The top 4 bits determine the
+       envelope speed. f is the fastest, 1 is the slowest, and 0
+       seems to be an invalid value and does weird things). The
+       default (below) sets it into normal mode (play and terminate/loop).
+    CHNREG32(ch, 16) = 0xf010;
+    */
+    CHNREG32(ch, 16) = 0x1f;    /* No volume envelope */
+
+
+    /* Set sample format, buffer address, and looping control. If
+       0x0200 mask is set on reg 0, the sample loops infinitely. If
+       it's not set, the sample plays once and terminates. We'll
+       also set the bits to start playback here. */
+    CHNREG32(ch, 4) = smpptr & 0xffff;
+    playCont = (mode << 7) | (smpptr >> 16);
+    vol = calc_aica_vol(vol);
+
+    if(loopflag)
+        playCont |= 0x0200;
+
+    if(delay) {
+        CHNREG32(ch, 0) = playCont;         /* key off */
+        CHNREG8(ch, 41) = vol;
+    }
+    else {
+        CHNREG32(ch, 0) = 0xc000 | playCont;    /* key on */
+
+        /* ramp up the volume */
+        for(i = 0xff; i >= vol; i--)
+            CHNREG8(ch, 41) = i;
+    }
+}
+
+/* Start sound on all channels specified by chmap bitmap */
+void aica_sync_play(uint32 chmap) {
+    int i = 0;
+
+    while(chmap) {
+        if(chmap & 0x1)
+            CHNREG32(i, 0) = CHNREG32(i, 0) | 0xc000;
+
+        i++;
+        chmap >>= 1;
+    }
+}
+
+/* Stop the sound on a given channel */
+void aica_stop(int ch) {
+    CHNREG32(ch, 0) = (CHNREG32(ch, 0) & ~0x4000) | 0x8000;
+}
+
+
+/* The rest of these routines can change the channel in mid-stride so you
+   can do things like vibrato and panning effects. */
+
+/* Set channel volume */
+void aica_vol(int ch) {
+    CHNREG8(ch, 41) = calc_aica_vol(chans[ch].vol);
+}
+
+/* Set channel pan */
+void aica_pan(int ch) {
+    CHNREG8(ch, 36) = calc_aica_pan(chans[ch].pan);
+}
+
+/* Set channel frequency */
+void aica_freq(int ch) {
+    uint32 freq = chans[ch].freq;
+    uint32 freq_lo, freq_base = 5644800;
+    int freq_hi = 7;
+
+    while(freq < freq_base && freq_hi > -8) {
+        freq_base >>= 1;
+        freq_hi--;
+    }
+
+    freq_lo = (freq << 10) / freq_base;
+    CHNREG32(ch, 24) = (freq_hi << 11) | (freq_lo & 1023);
+}
+
+/* Get channel position */
+int aica_get_pos(int ch) {
+    int i;
+
+    /* Observe channel ch */
+    SNDREG8(0x280d) = ch;
+
+    /* Wait a while */
+    for(i = 0; i < 20; i++)
+        __asm__ volatile ("nop");  /* Prevent loop from being optimized out */
+
+    /* Update position counters */
+    chans[ch].pos = SNDREG32(0x2814) & 0xffff;
+
+    return chans[ch].pos;
+}
diff --git a/aica/arm/aica.h b/aica/arm/aica.h
new file mode 100644 (file)
index 0000000..15c45ea
--- /dev/null
@@ -0,0 +1,27 @@
+#ifndef __AICA_H
+#define __AICA_H
+
+/* volatile unsigned char *dc_snd_base = (unsigned char *)0x00800000; */
+#define dc_snd_base ((volatile unsigned char *)0x00800000)
+
+/* Some convienence macros */
+#define SNDREG32A(x) ((volatile unsigned long *)(dc_snd_base + (x)))
+#define SNDREG32(x) (*SNDREG32A(x))
+#define SNDREG8A(x) (dc_snd_base + (x))
+#define SNDREG8(x) (*SNDREG8A(x))
+#define CHNREG32A(chn, x) SNDREG32A(0x80*(chn) + (x))
+#define CHNREG32(chn, x) (*CHNREG32A(chn, x))
+#define CHNREG8A(chn, x) SNDREG8A(0x80*(chn) + (x))
+#define CHNREG8(chn, x) (*CHNREG8A(chn, x))
+
+void aica_init();
+void aica_play(int ch, int delay);
+void aica_sync_play(uint32 chmap);
+void aica_stop(int ch);
+void aica_vol(int ch);
+void aica_pan(int ch);
+void aica_freq(int ch);
+int aica_get_pos(int ch);
+
+#endif  /* __AICA_H */
+
diff --git a/aica/arm/aica_cmd_iface.h b/aica/arm/aica_cmd_iface.h
new file mode 100644 (file)
index 0000000..aba0a51
--- /dev/null
@@ -0,0 +1,136 @@
+/* KallistiOS ##version##
+
+   aica_cmd_iface.h
+   (c)2000-2002 Dan Potter
+
+   Definitions for the SH-4/AICA interface. This file is meant to be
+   included from both the ARM and SH-4 sides of the fence.
+*/
+
+#ifndef __ARM_AICA_CMD_IFACE_H
+#define __ARM_AICA_CMD_IFACE_H
+
+#ifndef __ARCH_TYPES_H
+typedef unsigned long uint8;
+typedef unsigned long uint32;
+#endif
+
+/* Command queue; one of these for passing data from the SH-4 to the
+   AICA, and another for the other direction. If a command is written
+   to the queue and it is longer than the amount of space between the
+   head point and the queue size, the command will wrap around to
+   the beginning (i.e., queue commands _can_ be split up). */
+typedef struct aica_queue {
+    uint32      head;       /* Insertion point offset (in bytes) */
+    uint32      tail;       /* Removal point offset (in bytes) */
+    uint32      size;       /* Queue size (in bytes) */
+    uint32      valid;      /* 1 if the queue structs are valid */
+    uint32      process_ok; /* 1 if it's ok to process the data */
+    uint32      data;       /* Pointer to queue data buffer */
+} aica_queue_t;
+
+/* Command queue struct for commanding the AICA from the SH-4 */
+typedef struct aica_cmd {
+    uint32      size;       /* Command data size in dwords */
+    uint32      cmd;        /* Command ID */
+    uint32      timestamp;  /* When to execute the command (0 == now) */
+    uint32      cmd_id;     /* Command ID, for cmd/response pairs, or channel id */
+    uint32      misc[4];    /* Misc Parameters / Padding */
+    uint8       cmd_data[]; /* Command data */
+} aica_cmd_t;
+
+/* Maximum command size -- 256 dwords */
+#define AICA_CMD_MAX_SIZE   256
+
+/* This is the cmd_data for AICA_CMD_CHAN. Make this 16 dwords long
+   for two aica bus queues. */
+typedef struct aica_channel {
+    uint32      cmd;        /* Command ID */
+    uint32      base;       /* Sample base in RAM */
+    uint32      type;       /* (8/16bit/ADPCM) */
+    uint32      length;     /* Sample length */
+    uint32      loop;       /* Sample looping */
+    uint32      loopstart;  /* Sample loop start */
+    uint32      loopend;    /* Sample loop end */
+    uint32      freq;       /* Frequency */
+    uint32      vol;        /* Volume 0-255 */
+    uint32      pan;        /* Pan 0-255 */
+    uint32      pos;        /* Sample playback pos */
+    uint32      pad[5];     /* Padding */
+} aica_channel_t;
+
+/* Declare an aica_cmd_t big enough to hold an aica_channel_t
+   using temp name T, aica_cmd_t name CMDR, and aica_channel_t name CHANR */
+#define AICA_CMDSTR_CHANNEL(T, CMDR, CHANR) \
+    uint8   T[sizeof(aica_cmd_t) + sizeof(aica_channel_t)]; \
+    aica_cmd_t  * CMDR = (aica_cmd_t *)T; \
+    aica_channel_t  * CHANR = (aica_channel_t *)(CMDR->cmd_data);
+#define AICA_CMDSTR_CHANNEL_SIZE    ((sizeof(aica_cmd_t) + sizeof(aica_channel_t))/4)
+
+/* Command values (for aica_cmd_t) */
+#define AICA_CMD_NONE       0x00000000  /* No command (dummy packet)    */
+#define AICA_CMD_PING       0x00000001  /* Check for signs of life  */
+#define AICA_CMD_CHAN       0x00000002  /* Perform a wavetable action   */
+#define AICA_CMD_SYNC_CLOCK 0x00000003  /* Reset the millisecond clock  */
+
+/* Response values (for aica_cmd_t) */
+#define AICA_RESP_NONE      0x00000000
+#define AICA_RESP_PONG      0x00000001  /* Response to CMD_PING             */
+#define AICA_RESP_DBGPRINT  0x00000002  /* Entire payload is a null-terminated string   */
+
+/* Command values (for aica_channel_t commands) */
+#define AICA_CH_CMD_MASK    0x0000000f
+
+#define AICA_CH_CMD_NONE    0x00000000
+#define AICA_CH_CMD_START   0x00000001
+#define AICA_CH_CMD_STOP    0x00000002
+#define AICA_CH_CMD_UPDATE  0x00000003
+
+/* Start values */
+#define AICA_CH_START_MASK  0x00300000
+
+#define AICA_CH_START_DELAY 0x00100000 /* Set params, but delay key-on */
+#define AICA_CH_START_SYNC  0x00200000 /* Set key-on for all selected channels */
+
+/* Update values */
+#define AICA_CH_UPDATE_MASK 0x000ff000
+
+#define AICA_CH_UPDATE_SET_FREQ 0x00001000 /* frequency     */
+#define AICA_CH_UPDATE_SET_VOL  0x00002000 /* volume        */
+#define AICA_CH_UPDATE_SET_PAN  0x00004000 /* panning       */
+
+/* Sample types */
+#define AICA_SM_8BIT    1
+#define AICA_SM_16BIT   0
+#define AICA_SM_ADPCM   2
+
+
+/* This is where our SH-4/AICA comm variables go... */
+
+/* 0x000000 - 0x010000 are reserved for the program */
+
+/* Location of the SH-4 to AICA queue; commands from here will be
+   periodically processed by the AICA and then removed from the queue. */
+#define AICA_MEM_CMD_QUEUE  0x010000    /* 32K */
+
+/* Location of the AICA to SH-4 queue; commands from here will be
+   periodically processed by the SH-4 and then removed from the queue. */
+#define AICA_MEM_RESP_QUEUE 0x018000    /* 32K */
+
+/* This is the channel base, which holds status structs for all the
+   channels. This is READ-ONLY from the SH-4 side. */
+#define AICA_MEM_CHANNELS   0x020000    /* 64 * 16*4 = 4K */
+
+/* The clock value (in milliseconds) */
+#define AICA_MEM_CLOCK      0x021000    /* 4 bytes */
+
+/* 0x021004 - 0x030000 are reserved for future expansion */
+
+/* Open ram for sample data */
+#define AICA_RAM_START      0x030000
+#define AICA_RAM_END        0x200000
+
+/* Quick access to the AICA channels */
+#define AICA_CHANNEL(x)     (AICA_MEM_CHANNELS + (x) * sizeof(aica_channel_t))
+
+#endif  /* __ARM_AICA_CMD_IFACE_H */
diff --git a/aica/arm/crt0.s b/aica/arm/crt0.s
new file mode 100644 (file)
index 0000000..80857b2
--- /dev/null
@@ -0,0 +1,131 @@
+# KallistiOS ##version##
+#
+#  crt0.s
+#  (c)2000-2002 Dan Potter
+#
+#  Startup for ARM program
+#  Adapted from Marcus' AICA example among a few other sources =)
+
+.text
+.globl arm_main
+.globl jps
+
+# Meaningless but makes the linker shut up
+.globl reset
+reset:
+
+# Exception vectors
+       b       start
+       b       undef
+       b       softint
+       b       pref_abort
+       b       data_abort
+       b       rsrvd
+       b       irq
+
+
+# FIQ code adapted from the Marcus AICA example
+fiq:
+       # Save regs
+       #stmdb  sp!, {r0-r14}
+
+       # Grab interrupt type (store as parameter)
+       ldr     r8,intreq
+       ldr     r9,[r8]
+       and     r9,r9,#7
+
+       # Timer interupt?
+       cmp     r9,#2
+       beq     fiq_timer
+
+       # Bus request?
+       cmp     r9,#5
+       beq     fiq_busreq
+
+       # Dunno -- ack and skip
+       b       fiq_done
+
+fiq_busreq:
+       # Type 5 is bus request. Wait until the INTBusRequest register
+       # goes back from 0x100.
+       ldr     r8,busreq_control
+fiq_busreq_loop:
+       # This could probably be done more efficiently, but I'm
+       # no ARM assembly expert...
+       ldr     r9,[r8]
+       and     r9,r9,#0x100
+       cmp     r9,#0
+       bne     fiq_busreq_loop
+
+       b       fiq_done
+
+fiq_timer:
+       # Type 2 is timer interrupt. Increment timer variable.
+       # Update the next line to AICA_MEM_CLOCK if you change AICA_CMD_IFACE
+       mov     r8,#0x21000
+       ldr     r9,[r8]
+       add     r9,r9,#1
+       str     r9,[r8]
+       
+       # Request a new timer interrupt. We'll calculate the number
+       # put in here based on the "jps" (jiffies per second). 
+       ldr     r8, timer_control
+       mov     r9,#256-(44100/4410)
+#      ldr     r9,jps
+       str     r9,[r8,#0x10]
+       mov     r9,#0x40
+       str     r9,[r8,#0x24]
+#      b       fiq_done
+       
+       # Return from interrupt
+fiq_done:
+
+       # Clear interrupt
+       ldr     r8,intclr
+       mov     r9,#1
+       str     r9,[r8]
+       str     r9,[r8]
+       str     r9,[r8]
+       str     r9,[r8]
+
+       # Restore regs and return
+       #ldmdb  sp!, {r0-r14}
+       subs    pc,r14,#4
+
+intreq:
+       .long   0x00802d00
+intclr:
+       .long   0x00802d04
+timer_control:
+       .long   0x00802880
+busreq_control:
+       .long   0x00802808
+jps:
+       # 1000 jiffies per second
+       .long   256-(44100/1000)
+
+
+start:
+       # Setup a basic stack, disable IRQ, enable FIQ
+       mov     sp,#0xb000
+       mrs     r10,CPSR
+       orr     r10,r10,#0x80
+       bic     r10,r10,#0x40
+       msr     CPSR_all,r10
+
+       # Call the main for the SPU
+       bl      arm_main
+
+       # Loop infinitely if we get here
+here:  b       here
+
+
+# Handlers we don't bother to catch
+undef:
+softint:
+       mov     pc,r14
+pref_abort:
+data_abort:
+irq:
+rsrvd:
+       sub     pc,r14,#4
diff --git a/aica/arm/main.c b/aica/arm/main.c
new file mode 100644 (file)
index 0000000..6979182
--- /dev/null
@@ -0,0 +1,206 @@
+/* KallistiOS ##version##
+
+   main.c
+   (c)2000-2002 Dan Potter
+
+   Generic sound driver with streaming capabilities
+
+   This slightly more complicated version allows for sound effect channels,
+   and full sampling rate, panning, and volume control for each.
+
+*/
+
+#include "aica_cmd_iface.h"
+#include "aica.h"
+
+/****************** Timer *******************************************/
+
+#define timer (*((volatile uint32 *)AICA_MEM_CLOCK))
+
+void timer_wait(uint32 jiffies) {
+    uint32 fin = timer + jiffies;
+
+    while(timer <= fin)
+        ;
+}
+
+/****************** Tiny Libc ***************************************/
+
+#include <stddef.h>
+
+void * memcpy(void *dest, const void *src, size_t count) {
+    unsigned char *tmp = (unsigned char *) dest;
+    unsigned char *s = (unsigned char *) src;
+
+    while(count--)
+        *tmp++ = *s++;
+
+    return dest;
+}
+
+/****************** Main Program ************************************/
+
+/* Our SH-4 interface (statically placed memory structures) */
+volatile aica_queue_t   *q_cmd = (volatile aica_queue_t *)AICA_MEM_CMD_QUEUE;
+volatile aica_queue_t   *q_resp = (volatile aica_queue_t *)AICA_MEM_RESP_QUEUE;
+volatile aica_channel_t *chans = (volatile aica_channel_t *)AICA_MEM_CHANNELS;
+
+/* Process a CHAN command */
+void process_chn(uint32 chn, aica_channel_t *chndat) {
+    switch(chndat->cmd & AICA_CH_CMD_MASK) {
+        case AICA_CH_CMD_NONE:
+            break;
+        case AICA_CH_CMD_START:
+
+            if(chndat->cmd & AICA_CH_START_SYNC) {
+                aica_sync_play(chn);
+            }
+            else {
+                memcpy((void*)(chans + chn), chndat, sizeof(aica_channel_t));
+                chans[chn].pos = 0;
+                aica_play(chn, chndat->cmd & AICA_CH_START_DELAY);
+            }
+
+            break;
+        case AICA_CH_CMD_STOP:
+            aica_stop(chn);
+            break;
+        case AICA_CH_CMD_UPDATE:
+
+            if(chndat->cmd & AICA_CH_UPDATE_SET_FREQ) {
+                chans[chn].freq = chndat->freq;
+                aica_freq(chn);
+            }
+
+            if(chndat->cmd & AICA_CH_UPDATE_SET_VOL) {
+                chans[chn].vol = chndat->vol;
+                aica_vol(chn);
+            }
+
+            if(chndat->cmd & AICA_CH_UPDATE_SET_PAN) {
+                chans[chn].pan = chndat->pan;
+                aica_pan(chn);
+            }
+
+            break;
+        default:
+            /* error */
+            break;
+    }
+}
+
+/* Process one packet of queue data */
+uint32 process_one(uint32 tail) {
+    uint32      pktdata[AICA_CMD_MAX_SIZE], *pdptr, size, i;
+    volatile uint32 * src;
+    aica_cmd_t  * pkt;
+
+    src = (volatile uint32 *)(q_cmd->data + tail);
+    pkt = (aica_cmd_t *)pktdata;
+    pdptr = pktdata;
+
+    /* Get the size field */
+    size = *src;
+
+    if(size > AICA_CMD_MAX_SIZE)
+        size = AICA_CMD_MAX_SIZE;
+
+    /* Copy out the packet data */
+    for(i = 0; i < size; i++) {
+        *pdptr++ = *src++;
+
+        if((uint32)src >= (q_cmd->data + q_cmd->size))
+            src = (volatile uint32 *)q_cmd->data;
+    }
+
+    /* Figure out what type of packet it is */
+    switch(pkt->cmd) {
+        case AICA_CMD_NONE:
+            break;
+        case AICA_CMD_PING:
+            /* Not implemented yet */
+            break;
+        case AICA_CMD_CHAN:
+            process_chn(pkt->cmd_id, (aica_channel_t *)pkt->cmd_data);
+            break;
+        case AICA_CMD_SYNC_CLOCK:
+            /* Reset our timer clock to zero */
+            timer = 0;
+            break;
+        default:
+            /* error */
+            break;
+    }
+
+    return size;
+}
+
+/* Look for an available request in the command queue; if one is there
+   then process it and move the tail pointer. */
+void process_cmd_queue() {
+    uint32      head, tail, tsloc, ts;
+
+    /* Grab these values up front in case SH-4 changes head */
+    head = q_cmd->head;
+    tail = q_cmd->tail;
+
+    /* Do we have anything to process? */
+    while(head != tail) {
+        /* Look at the next packet. If our clock isn't there yet, then
+           we won't process anything yet either. */
+        tsloc = tail + offsetof(aica_cmd_t, timestamp);
+
+        if(tsloc >= q_cmd->size)
+            tsloc -= q_cmd->size;
+
+        ts = *((volatile uint32*)(q_cmd->data + tsloc));
+
+        if(ts > 0 && ts >= timer)
+            return;
+
+        /* Process it */
+        ts = process_one(tail);
+
+        /* Ok, skip over the packet */
+        tail += ts * 4;
+
+        if(tail >= q_cmd->size)
+            tail -= q_cmd->size;
+
+        q_cmd->tail = tail;
+    }
+}
+
+int arm_main() {
+    int i;
+
+    /* Setup our queues */
+    q_cmd->head = q_cmd->tail = 0;
+    q_cmd->data = AICA_MEM_CMD_QUEUE + sizeof(aica_queue_t);
+    q_cmd->size = AICA_MEM_RESP_QUEUE - q_cmd->data;
+    q_cmd->process_ok = 1;
+    q_cmd->valid = 1;
+
+    q_resp->head = q_resp->tail = 0;
+    q_resp->data = AICA_MEM_RESP_QUEUE + sizeof(aica_queue_t);
+    q_resp->size = AICA_MEM_CHANNELS - q_resp->data;
+    q_resp->process_ok = 1;
+    q_resp->valid = 1;
+
+    /* Initialize the AICA part of the SPU */
+    aica_init();
+
+    /* Wait for a command */
+    for(; ;) {
+        /* Update channel position counters */
+        for(i = 0; i < 64; i++)
+            aica_get_pos(i);
+
+        /* Check for a command */
+        if(q_cmd->process_ok)
+            process_cmd_queue();
+
+        /* Little delay to prevent memory lock */
+        timer_wait(10);
+    }
+}