--- /dev/null
+/* 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);
+ }
+}