From e02aa6cf5cbd5680016beaf879d7e1118124cb61 Mon Sep 17 00:00:00 2001 From: Jason Self Date: Sun, 20 May 2018 15:36:41 -0700 Subject: [PATCH] Add aica firmware This is used to provide sound on the Sega Dreamcast. --- INSTALL | 5 +- Makefile | 11 +- WHENCE | 16 +++ aica/AUTHORS | 118 +++++++++++++++++++ aica/Dreamcast_sound.txt | 17 +++ aica/README.KOS | 70 ++++++++++++ aica/arm/Makefile | 22 ++++ aica/arm/aica.c | 232 ++++++++++++++++++++++++++++++++++++++ aica/arm/aica.h | 27 +++++ aica/arm/aica_cmd_iface.h | 136 ++++++++++++++++++++++ aica/arm/crt0.s | 131 +++++++++++++++++++++ aica/arm/main.c | 206 +++++++++++++++++++++++++++++++++ 12 files changed, 987 insertions(+), 4 deletions(-) create mode 100644 aica/AUTHORS create mode 100644 aica/Dreamcast_sound.txt create mode 100644 aica/README.KOS create mode 100644 aica/arm/Makefile create mode 100644 aica/arm/aica.c create mode 100644 aica/arm/aica.h create mode 100644 aica/arm/aica_cmd_iface.h create mode 100644 aica/arm/crt0.s create mode 100644 aica/arm/main.c diff --git a/INSTALL b/INSTALL index dfabf2a..538a5df 100644 --- 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 +Copyright (C) 2017, 2018 Jason Self 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, diff --git a/Makefile b/Makefile index 122f659..393831c 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -# Copyright (C) 2017 Jason Self +# Copyright (C) 2017, 2018 Jason Self # # 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 ab77bb1..09dd5e9 100644 --- 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 index 0000000..08eb927 --- /dev/null +++ b/aica/AUTHORS @@ -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 . + * + * 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 + * + * 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 index 0000000..08c3bac --- /dev/null +++ b/aica/Dreamcast_sound.txt @@ -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 index 0000000..cd3bf69 --- /dev/null +++ b/aica/README.KOS @@ -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 index 0000000..d2c49c6 --- /dev/null +++ b/aica/arm/Makefile @@ -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 index 0000000..2ff2ca1 --- /dev/null +++ b/aica/arm/aica.c @@ -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 index 0000000..15c45ea --- /dev/null +++ b/aica/arm/aica.h @@ -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 index 0000000..aba0a51 --- /dev/null +++ b/aica/arm/aica_cmd_iface.h @@ -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 index 0000000..80857b2 --- /dev/null +++ b/aica/arm/crt0.s @@ -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 index 0000000..6979182 --- /dev/null +++ b/aica/arm/main.c @@ -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 + +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); + } +} -- 2.31.1