nkeynes@11 | 1 | /**
|
nkeynes@561 | 2 | * $Id$
|
nkeynes@11 | 3 | *
|
nkeynes@465 | 4 | * This module implements the AICA's IO interfaces, as well
|
nkeynes@465 | 5 | * as providing the core AICA module to the system.
|
nkeynes@11 | 6 | *
|
nkeynes@11 | 7 | * Copyright (c) 2005 Nathan Keynes.
|
nkeynes@11 | 8 | *
|
nkeynes@11 | 9 | * This program is free software; you can redistribute it and/or modify
|
nkeynes@11 | 10 | * it under the terms of the GNU General Public License as published by
|
nkeynes@11 | 11 | * the Free Software Foundation; either version 2 of the License, or
|
nkeynes@11 | 12 | * (at your option) any later version.
|
nkeynes@11 | 13 | *
|
nkeynes@11 | 14 | * This program is distributed in the hope that it will be useful,
|
nkeynes@11 | 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
nkeynes@11 | 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
nkeynes@11 | 17 | * GNU General Public License for more details.
|
nkeynes@11 | 18 | */
|
nkeynes@11 | 19 |
|
nkeynes@35 | 20 | #define MODULE aica_module
|
nkeynes@35 | 21 |
|
nkeynes@131 | 22 | #include <time.h>
|
nkeynes@11 | 23 | #include "dream.h"
|
nkeynes@66 | 24 | #include "dreamcast.h"
|
nkeynes@15 | 25 | #include "mem.h"
|
nkeynes@106 | 26 | #include "aica/aica.h"
|
nkeynes@61 | 27 | #include "armcore.h"
|
nkeynes@106 | 28 | #include "aica/audio.h"
|
nkeynes@11 | 29 | #define MMIO_IMPL
|
nkeynes@11 | 30 | #include "aica.h"
|
nkeynes@11 | 31 |
|
nkeynes@11 | 32 | MMIO_REGION_READ_DEFFN( AICA0 )
|
nkeynes@11 | 33 | MMIO_REGION_READ_DEFFN( AICA1 )
|
nkeynes@11 | 34 |
|
nkeynes@23 | 35 | void aica_init( void );
|
nkeynes@23 | 36 | void aica_reset( void );
|
nkeynes@23 | 37 | void aica_start( void );
|
nkeynes@23 | 38 | void aica_stop( void );
|
nkeynes@35 | 39 | void aica_save_state( FILE *f );
|
nkeynes@35 | 40 | int aica_load_state( FILE *f );
|
nkeynes@30 | 41 | uint32_t aica_run_slice( uint32_t );
|
nkeynes@23 | 42 |
|
nkeynes@23 | 43 | struct dreamcast_module aica_module = { "AICA", aica_init, aica_reset,
|
nkeynes@736 | 44 | aica_start, aica_run_slice, aica_stop,
|
nkeynes@736 | 45 | aica_save_state, aica_load_state };
|
nkeynes@15 | 46 |
|
nkeynes@465 | 47 | struct aica_state_struct {
|
nkeynes@465 | 48 | uint32_t time_of_day;
|
nkeynes@465 | 49 | /**
|
nkeynes@465 | 50 | * Keep track of what we've done so far this second, to try to keep the
|
nkeynes@465 | 51 | * precision of samples/second.
|
nkeynes@465 | 52 | */
|
nkeynes@465 | 53 | uint32_t samples_done;
|
nkeynes@465 | 54 | uint32_t nanosecs_done;
|
nkeynes@465 | 55 | /**
|
nkeynes@465 | 56 | * Event (IRQ) state
|
nkeynes@465 | 57 | */
|
nkeynes@465 | 58 | int event_pending;
|
nkeynes@465 | 59 | int clear_count;
|
nkeynes@465 | 60 | };
|
nkeynes@465 | 61 |
|
nkeynes@465 | 62 | static struct aica_state_struct aica_state;
|
nkeynes@465 | 63 |
|
nkeynes@173 | 64 |
|
nkeynes@11 | 65 | /**
|
nkeynes@11 | 66 | * Initialize the AICA subsystem. Note requires that
|
nkeynes@11 | 67 | */
|
nkeynes@11 | 68 | void aica_init( void )
|
nkeynes@11 | 69 | {
|
nkeynes@11 | 70 | register_io_regions( mmio_list_spu );
|
nkeynes@11 | 71 | MMIO_NOTRACE(AICA0);
|
nkeynes@11 | 72 | MMIO_NOTRACE(AICA1);
|
nkeynes@11 | 73 | arm_mem_init();
|
nkeynes@66 | 74 | aica_reset();
|
nkeynes@11 | 75 | }
|
nkeynes@11 | 76 |
|
nkeynes@11 | 77 | void aica_reset( void )
|
nkeynes@11 | 78 | {
|
nkeynes@35 | 79 | arm_reset();
|
nkeynes@465 | 80 | aica_state.time_of_day = 0x5bfc8900;
|
nkeynes@465 | 81 | aica_state.samples_done = 0;
|
nkeynes@465 | 82 | aica_state.nanosecs_done = 0;
|
nkeynes@465 | 83 | aica_state.event_pending = 0;
|
nkeynes@465 | 84 | aica_state.clear_count = 0;
|
nkeynes@736 | 85 | // aica_event(2); /* Pre-deliver a timer interrupt */
|
nkeynes@11 | 86 | }
|
nkeynes@11 | 87 |
|
nkeynes@23 | 88 | void aica_start( void )
|
nkeynes@23 | 89 | {
|
nkeynes@23 | 90 |
|
nkeynes@23 | 91 | }
|
nkeynes@23 | 92 |
|
nkeynes@30 | 93 | uint32_t aica_run_slice( uint32_t nanosecs )
|
nkeynes@23 | 94 | {
|
nkeynes@23 | 95 | /* Run arm instructions */
|
nkeynes@35 | 96 | int reset = MMIO_READ( AICA2, AICA_RESET );
|
nkeynes@66 | 97 | if( (reset & 1) == 0 ) { /* Running */
|
nkeynes@736 | 98 | int num_samples = (int)((uint64_t)AICA_SAMPLE_RATE * (aica_state.nanosecs_done + nanosecs) / 1000000000) - aica_state.samples_done;
|
nkeynes@736 | 99 | num_samples = arm_run_slice( num_samples );
|
nkeynes@736 | 100 | audio_mix_samples( num_samples );
|
nkeynes@73 | 101 |
|
nkeynes@736 | 102 | aica_state.samples_done += num_samples;
|
nkeynes@736 | 103 | aica_state.nanosecs_done += nanosecs;
|
nkeynes@35 | 104 | }
|
nkeynes@465 | 105 | if( aica_state.nanosecs_done > 1000000000 ) {
|
nkeynes@736 | 106 | aica_state.samples_done -= AICA_SAMPLE_RATE;
|
nkeynes@736 | 107 | aica_state.nanosecs_done -= 1000000000;
|
nkeynes@736 | 108 | aica_state.time_of_day++;
|
nkeynes@73 | 109 | }
|
nkeynes@43 | 110 | return nanosecs;
|
nkeynes@23 | 111 | }
|
nkeynes@23 | 112 |
|
nkeynes@23 | 113 | void aica_stop( void )
|
nkeynes@23 | 114 | {
|
nkeynes@23 | 115 |
|
nkeynes@23 | 116 | }
|
nkeynes@23 | 117 |
|
nkeynes@35 | 118 | void aica_save_state( FILE *f )
|
nkeynes@35 | 119 | {
|
nkeynes@465 | 120 | fwrite( &aica_state, sizeof(struct aica_state_struct), 1, f );
|
nkeynes@35 | 121 | arm_save_state( f );
|
nkeynes@465 | 122 | audio_save_state(f);
|
nkeynes@35 | 123 | }
|
nkeynes@35 | 124 |
|
nkeynes@35 | 125 | int aica_load_state( FILE *f )
|
nkeynes@35 | 126 | {
|
nkeynes@465 | 127 | fread( &aica_state, sizeof(struct aica_state_struct), 1, f );
|
nkeynes@465 | 128 | arm_load_state( f );
|
nkeynes@465 | 129 | return audio_load_state(f);
|
nkeynes@35 | 130 | }
|
nkeynes@35 | 131 |
|
nkeynes@61 | 132 | /* Note: This is probably not necessarily technically correct but it should
|
nkeynes@61 | 133 | * work in the meantime.
|
nkeynes@61 | 134 | */
|
nkeynes@61 | 135 |
|
nkeynes@61 | 136 | void aica_event( int event )
|
nkeynes@61 | 137 | {
|
nkeynes@465 | 138 | if( aica_state.event_pending == 0 )
|
nkeynes@736 | 139 | armr.int_pending |= CPSR_F;
|
nkeynes@465 | 140 | aica_state.event_pending |= (1<<event);
|
nkeynes@736 | 141 |
|
nkeynes@61 | 142 | int pending = MMIO_READ( AICA2, AICA_IRQ );
|
nkeynes@61 | 143 | if( pending == 0 || event < pending )
|
nkeynes@736 | 144 | MMIO_WRITE( AICA2, AICA_IRQ, event );
|
nkeynes@61 | 145 | }
|
nkeynes@61 | 146 |
|
nkeynes@61 | 147 | void aica_clear_event( )
|
nkeynes@61 | 148 | {
|
nkeynes@465 | 149 | aica_state.clear_count++;
|
nkeynes@465 | 150 | if( aica_state.clear_count == 4 ) {
|
nkeynes@736 | 151 | int i;
|
nkeynes@736 | 152 | aica_state.clear_count = 0;
|
nkeynes@61 | 153 |
|
nkeynes@736 | 154 | for( i=0; i<8; i++ ) {
|
nkeynes@736 | 155 | if( aica_state.event_pending & (1<<i) ) {
|
nkeynes@736 | 156 | aica_state.event_pending &= ~(1<<i);
|
nkeynes@736 | 157 | break;
|
nkeynes@736 | 158 | }
|
nkeynes@736 | 159 | }
|
nkeynes@736 | 160 | for( ;i<8; i++ ) {
|
nkeynes@736 | 161 | if( aica_state.event_pending & (1<<i) ) {
|
nkeynes@736 | 162 | MMIO_WRITE( AICA2, AICA_IRQ, i );
|
nkeynes@736 | 163 | break;
|
nkeynes@736 | 164 | }
|
nkeynes@736 | 165 | }
|
nkeynes@736 | 166 | if( aica_state.event_pending == 0 )
|
nkeynes@736 | 167 | armr.int_pending &= ~CPSR_F;
|
nkeynes@61 | 168 | }
|
nkeynes@61 | 169 | }
|
nkeynes@66 | 170 |
|
nkeynes@86 | 171 | void aica_enable( void )
|
nkeynes@86 | 172 | {
|
nkeynes@86 | 173 | mmio_region_AICA2_write( AICA_RESET, MMIO_READ(AICA2,AICA_RESET) & ~1 );
|
nkeynes@86 | 174 | }
|
nkeynes@86 | 175 |
|
nkeynes@11 | 176 | /** Channel register structure:
|
nkeynes@43 | 177 | * 00 4 Channel config
|
nkeynes@43 | 178 | * 04 4 Waveform address lo (16 bits)
|
nkeynes@11 | 179 | * 08 4 Loop start address
|
nkeynes@11 | 180 | * 0C 4 Loop end address
|
nkeynes@11 | 181 | * 10 4 Volume envelope
|
nkeynes@43 | 182 | * 14 4 Init to 0x1F
|
nkeynes@43 | 183 | * 18 4 Frequency (floating point)
|
nkeynes@43 | 184 | * 1C 4 ??
|
nkeynes@43 | 185 | * 20 4 ??
|
nkeynes@11 | 186 | * 24 1 Pan
|
nkeynes@11 | 187 | * 25 1 ??
|
nkeynes@11 | 188 | * 26
|
nkeynes@11 | 189 | * 27
|
nkeynes@11 | 190 | * 28 1 ??
|
nkeynes@11 | 191 | * 29 1 Volume
|
nkeynes@11 | 192 | * 2C
|
nkeynes@11 | 193 | * 30
|
nkeynes@431 | 194 | */
|
nkeynes@11 | 195 |
|
nkeynes@11 | 196 | /* Write to channels 0-31 */
|
nkeynes@929 | 197 | MMIO_REGION_WRITE_FN( AICA0, reg, val )
|
nkeynes@11 | 198 | {
|
nkeynes@929 | 199 | reg &= 0xFFF;
|
nkeynes@35 | 200 | MMIO_WRITE( AICA0, reg, val );
|
nkeynes@66 | 201 | aica_write_channel( reg >> 7, reg % 128, val );
|
nkeynes@37 | 202 | // DEBUG( "AICA0 Write %08X => %08X", val, reg );
|
nkeynes@11 | 203 | }
|
nkeynes@11 | 204 |
|
nkeynes@11 | 205 | /* Write to channels 32-64 */
|
nkeynes@929 | 206 | MMIO_REGION_WRITE_FN( AICA1, reg, val )
|
nkeynes@11 | 207 | {
|
nkeynes@929 | 208 | reg &= 0xFFF;
|
nkeynes@35 | 209 | MMIO_WRITE( AICA1, reg, val );
|
nkeynes@66 | 210 | aica_write_channel( (reg >> 7) + 32, reg % 128, val );
|
nkeynes@37 | 211 | // DEBUG( "AICA1 Write %08X => %08X", val, reg );
|
nkeynes@11 | 212 | }
|
nkeynes@11 | 213 |
|
nkeynes@66 | 214 | /**
|
nkeynes@66 | 215 | * AICA control registers
|
nkeynes@66 | 216 | */
|
nkeynes@929 | 217 | MMIO_REGION_WRITE_FN( AICA2, reg, val )
|
nkeynes@11 | 218 | {
|
nkeynes@35 | 219 | uint32_t tmp;
|
nkeynes@929 | 220 | reg &= 0xFFF;
|
nkeynes@929 | 221 |
|
nkeynes@35 | 222 | switch( reg ) {
|
nkeynes@35 | 223 | case AICA_RESET:
|
nkeynes@736 | 224 | tmp = MMIO_READ( AICA2, AICA_RESET );
|
nkeynes@736 | 225 | if( (tmp & 1) == 1 && (val & 1) == 0 ) {
|
nkeynes@736 | 226 | /* ARM enabled - execute a core reset */
|
nkeynes@736 | 227 | DEBUG( "ARM enabled" );
|
nkeynes@736 | 228 | arm_reset();
|
nkeynes@736 | 229 | aica_state.samples_done = 0;
|
nkeynes@736 | 230 | aica_state.nanosecs_done = 0;
|
nkeynes@736 | 231 | } else if( (tmp&1) == 0 && (val&1) == 1 ) {
|
nkeynes@736 | 232 | DEBUG( "ARM disabled" );
|
nkeynes@736 | 233 | }
|
nkeynes@736 | 234 | MMIO_WRITE( AICA2, AICA_RESET, val );
|
nkeynes@736 | 235 | break;
|
nkeynes@61 | 236 | case AICA_IRQCLEAR:
|
nkeynes@736 | 237 | aica_clear_event();
|
nkeynes@736 | 238 | break;
|
nkeynes@829 | 239 | case AICA_FIFOIN: /* Read-only */
|
nkeynes@829 | 240 | break;
|
nkeynes@35 | 241 | default:
|
nkeynes@736 | 242 | MMIO_WRITE( AICA2, reg, val );
|
nkeynes@736 | 243 | break;
|
nkeynes@35 | 244 | }
|
nkeynes@11 | 245 | }
|
nkeynes@66 | 246 |
|
nkeynes@929 | 247 | MMIO_REGION_READ_FN( AICA2, reg )
|
nkeynes@463 | 248 | {
|
nkeynes@463 | 249 | audio_channel_t channel;
|
nkeynes@463 | 250 | uint32_t channo;
|
nkeynes@463 | 251 | int32_t val;
|
nkeynes@929 | 252 | reg &= 0xFFF;
|
nkeynes@463 | 253 | switch( reg ) {
|
nkeynes@463 | 254 | case AICA_CHANSTATE:
|
nkeynes@736 | 255 | channo = (MMIO_READ( AICA2, AICA_CHANSEL ) >> 8) & 0x3F;
|
nkeynes@736 | 256 | channel = audio_get_channel(channo);
|
nkeynes@736 | 257 | if( channel->loop == LOOP_LOOPED ) {
|
nkeynes@736 | 258 | val = 0x8000;
|
nkeynes@736 | 259 | channel->loop = LOOP_ON;
|
nkeynes@736 | 260 | } else {
|
nkeynes@736 | 261 | val = 0;
|
nkeynes@736 | 262 | }
|
nkeynes@736 | 263 | return val;
|
nkeynes@463 | 264 | case AICA_CHANPOSN:
|
nkeynes@736 | 265 | channo = (MMIO_READ( AICA2, AICA_CHANSEL ) >> 8) & 0x3F;
|
nkeynes@736 | 266 | channel = audio_get_channel(channo);
|
nkeynes@736 | 267 | return channel->posn;
|
nkeynes@463 | 268 | default:
|
nkeynes@736 | 269 | return MMIO_READ( AICA2, reg );
|
nkeynes@463 | 270 | }
|
nkeynes@463 | 271 | }
|
nkeynes@131 | 272 |
|
nkeynes@929 | 273 | MMIO_REGION_READ_FN( AICARTC, reg )
|
nkeynes@131 | 274 | {
|
nkeynes@173 | 275 | int32_t rv = 0;
|
nkeynes@929 | 276 | reg &= 0xFFF;
|
nkeynes@131 | 277 | switch( reg ) {
|
nkeynes@131 | 278 | case AICA_RTCHI:
|
nkeynes@465 | 279 | rv = (aica_state.time_of_day >> 16) & 0xFFFF;
|
nkeynes@736 | 280 | break;
|
nkeynes@131 | 281 | case AICA_RTCLO:
|
nkeynes@736 | 282 | rv = aica_state.time_of_day & 0xFFFF;
|
nkeynes@736 | 283 | break;
|
nkeynes@131 | 284 | }
|
nkeynes@241 | 285 | // DEBUG( "Read AICA RTC %d => %08X", reg, rv );
|
nkeynes@173 | 286 | return rv;
|
nkeynes@131 | 287 | }
|
nkeynes@131 | 288 |
|
nkeynes@929 | 289 | MMIO_REGION_WRITE_FN( AICARTC, reg, val )
|
nkeynes@301 | 290 | {
|
nkeynes@929 | 291 | reg &= 0xFFF;
|
nkeynes@301 | 292 | switch( reg ) {
|
nkeynes@301 | 293 | case AICA_RTCEN:
|
nkeynes@736 | 294 | MMIO_WRITE( AICARTC, reg, val&0x01 );
|
nkeynes@736 | 295 | break;
|
nkeynes@301 | 296 | case AICA_RTCLO:
|
nkeynes@736 | 297 | if( MMIO_READ( AICARTC, AICA_RTCEN ) & 0x01 ) {
|
nkeynes@736 | 298 | aica_state.time_of_day = (aica_state.time_of_day & 0xFFFF0000) | (val & 0xFFFF);
|
nkeynes@736 | 299 | }
|
nkeynes@736 | 300 | break;
|
nkeynes@301 | 301 | case AICA_RTCHI:
|
nkeynes@736 | 302 | if( MMIO_READ( AICARTC, AICA_RTCEN ) & 0x01 ) {
|
nkeynes@736 | 303 | aica_state.time_of_day = (aica_state.time_of_day & 0xFFFF) | (val<<16);
|
nkeynes@736 | 304 | MMIO_WRITE( AICARTC, AICA_RTCEN, 0 );
|
nkeynes@736 | 305 | }
|
nkeynes@736 | 306 | break;
|
nkeynes@301 | 307 | }
|
nkeynes@301 | 308 | }
|
nkeynes@736 | 309 |
|
nkeynes@66 | 310 | /**
|
nkeynes@66 | 311 | * Translate the channel frequency to a sample rate. The frequency is a
|
nkeynes@66 | 312 | * 14-bit floating point number, where bits 0..9 is the mantissa,
|
nkeynes@66 | 313 | * 11..14 is the signed exponent (-8 to +7). Bit 10 appears to
|
nkeynes@66 | 314 | * be unused.
|
nkeynes@66 | 315 | *
|
nkeynes@66 | 316 | * @return sample rate in samples per second.
|
nkeynes@66 | 317 | */
|
nkeynes@66 | 318 | uint32_t aica_frequency_to_sample_rate( uint32_t freq )
|
nkeynes@66 | 319 | {
|
nkeynes@66 | 320 | uint32_t exponent = (freq & 0x3800) >> 11;
|
nkeynes@66 | 321 | uint32_t mantissa = freq & 0x03FF;
|
nkeynes@82 | 322 | uint32_t rate;
|
nkeynes@66 | 323 | if( freq & 0x4000 ) {
|
nkeynes@736 | 324 | /* neg exponent - rate < 44100 */
|
nkeynes@736 | 325 | exponent = 8 - exponent;
|
nkeynes@736 | 326 | rate = (44100 >> exponent) +
|
nkeynes@736 | 327 | ((44100 * mantissa) >> (10+exponent));
|
nkeynes@66 | 328 | } else {
|
nkeynes@736 | 329 | /* pos exponent - rate > 44100 */
|
nkeynes@736 | 330 | rate = (44100 << exponent) +
|
nkeynes@736 | 331 | ((44100 * mantissa) >> (10-exponent));
|
nkeynes@66 | 332 | }
|
nkeynes@82 | 333 | return rate;
|
nkeynes@66 | 334 | }
|
nkeynes@66 | 335 |
|
nkeynes@434 | 336 | void aica_start_stop_channels()
|
nkeynes@434 | 337 | {
|
nkeynes@434 | 338 | int i;
|
nkeynes@434 | 339 | for( i=0; i<32; i++ ) {
|
nkeynes@736 | 340 | uint32_t val = MMIO_READ( AICA0, i<<7 );
|
nkeynes@736 | 341 | audio_start_stop_channel(i, val&0x4000);
|
nkeynes@434 | 342 | }
|
nkeynes@434 | 343 | for( ; i<64; i++ ) {
|
nkeynes@736 | 344 | uint32_t val = MMIO_READ( AICA1, (i-32)<<7 );
|
nkeynes@736 | 345 | audio_start_stop_channel(i, val&0x4000);
|
nkeynes@434 | 346 | }
|
nkeynes@434 | 347 | }
|
nkeynes@434 | 348 |
|
nkeynes@82 | 349 | /**
|
nkeynes@82 | 350 | * Derived directly from Dan Potter's log table
|
nkeynes@82 | 351 | */
|
nkeynes@82 | 352 | uint8_t aica_volume_table[256] = {
|
nkeynes@736 | 353 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
|
nkeynes@736 | 354 | 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 4,
|
nkeynes@736 | 355 | 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 7, 7, 7, 8, 8, 9,
|
nkeynes@736 | 356 | 9, 9, 10, 10, 11, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 16,
|
nkeynes@736 | 357 | 16, 17, 17, 18, 18, 19, 19, 20, 20, 21, 22, 22, 23, 23, 24, 25,
|
nkeynes@736 | 358 | 25, 26, 27, 27, 28, 29, 29, 30, 31, 31, 32, 33, 34, 34, 35, 36,
|
nkeynes@736 | 359 | 37, 37, 38, 39, 40, 40, 41, 42, 43, 44, 45, 45, 46, 47, 48, 49,
|
nkeynes@736 | 360 | 50, 51, 52, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64,
|
nkeynes@736 | 361 | 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 76, 77, 78, 79, 80, 81,
|
nkeynes@736 | 362 | 82, 83, 85, 86, 87, 88, 89, 90, 92, 93, 94, 95, 97, 98, 99, 100,
|
nkeynes@736 | 363 | 102, 103, 104, 105, 107, 108, 109, 111, 112, 113, 115, 116, 117, 119, 120, 121,
|
nkeynes@736 | 364 | 123, 124, 126, 127, 128, 130, 131, 133, 134, 136, 137, 139, 140, 142, 143, 145,
|
nkeynes@736 | 365 | 146, 148, 149, 151, 152, 154, 155, 157, 159, 160, 162, 163, 165, 167, 168, 170,
|
nkeynes@736 | 366 | 171, 173, 175, 176, 178, 180, 181, 183, 185, 187, 188, 190, 192, 194, 195, 197,
|
nkeynes@736 | 367 | 199, 201, 202, 204, 206, 208, 210, 211, 213, 215, 217, 219, 221, 223, 224, 226,
|
nkeynes@736 | 368 | 228, 230, 232, 234, 236, 238, 240, 242, 244, 246, 248, 250, 252, 253, 254, 255 };
|
nkeynes@82 | 369 |
|
nkeynes@82 | 370 |
|
nkeynes@66 | 371 | void aica_write_channel( int channelNo, uint32_t reg, uint32_t val )
|
nkeynes@66 | 372 | {
|
nkeynes@66 | 373 | val &= 0x0000FFFF;
|
nkeynes@66 | 374 | audio_channel_t channel = audio_get_channel(channelNo);
|
nkeynes@66 | 375 | switch( reg ) {
|
nkeynes@66 | 376 | case 0x00: /* Config + high address bits*/
|
nkeynes@736 | 377 | channel->start = (channel->start & 0xFFFF) | ((val&0x1F) << 16);
|
nkeynes@736 | 378 | if( val & 0x200 )
|
nkeynes@736 | 379 | channel->loop = LOOP_ON;
|
nkeynes@736 | 380 | else
|
nkeynes@736 | 381 | channel->loop = LOOP_OFF;
|
nkeynes@736 | 382 | switch( (val >> 7) & 0x03 ) {
|
nkeynes@736 | 383 | case 0:
|
nkeynes@736 | 384 | channel->sample_format = AUDIO_FMT_16BIT;
|
nkeynes@736 | 385 | break;
|
nkeynes@736 | 386 | case 1:
|
nkeynes@736 | 387 | channel->sample_format = AUDIO_FMT_8BIT;
|
nkeynes@736 | 388 | break;
|
nkeynes@736 | 389 | case 2:
|
nkeynes@736 | 390 | case 3:
|
nkeynes@736 | 391 | channel->sample_format = AUDIO_FMT_ADPCM;
|
nkeynes@736 | 392 | break;
|
nkeynes@736 | 393 | }
|
nkeynes@736 | 394 | if( val & 0x8000 ) {
|
nkeynes@736 | 395 | aica_start_stop_channels();
|
nkeynes@736 | 396 | }
|
nkeynes@736 | 397 | break;
|
nkeynes@736 | 398 | case 0x04: /* Low 16 address bits */
|
nkeynes@736 | 399 | channel->start = (channel->start & 0x001F0000) | val;
|
nkeynes@736 | 400 | break;
|
nkeynes@736 | 401 | case 0x08: /* Loop start */
|
nkeynes@736 | 402 | channel->loop_start = val;
|
nkeynes@736 | 403 | break;
|
nkeynes@736 | 404 | case 0x0C: /* End */
|
nkeynes@736 | 405 | channel->end = val;
|
nkeynes@736 | 406 | break;
|
nkeynes@736 | 407 | case 0x10: /* Envelope register 1 */
|
nkeynes@736 | 408 | break;
|
nkeynes@736 | 409 | case 0x14: /* Envelope register 2 */
|
nkeynes@736 | 410 | break;
|
nkeynes@736 | 411 | case 0x18: /* Frequency */
|
nkeynes@736 | 412 | channel->sample_rate = aica_frequency_to_sample_rate ( val );
|
nkeynes@736 | 413 | break;
|
nkeynes@736 | 414 | case 0x1C: /* ??? */
|
nkeynes@736 | 415 | case 0x20: /* ??? */
|
nkeynes@736 | 416 | case 0x24: /* Volume? /pan */
|
nkeynes@736 | 417 | val = val & 0x1F;
|
nkeynes@736 | 418 | if( val <= 0x0F )
|
nkeynes@736 | 419 | val = 0x0F - val; /* Convert to smooth pan over 0..31 */
|
nkeynes@736 | 420 | channel->pan = val;
|
nkeynes@736 | 421 | break;
|
nkeynes@736 | 422 | case 0x28: /* Volume */
|
nkeynes@736 | 423 | // This isn't remotely correct, but it will have to suffice until I have
|
nkeynes@736 | 424 | // time to figure out what's actually going on here...
|
nkeynes@736 | 425 | channel->vol = aica_volume_table[max((val & 0xFF),((val>>8)&0xFF))];
|
nkeynes@736 | 426 | break;
|
nkeynes@736 | 427 | default: /* ??? */
|
nkeynes@736 | 428 | break;
|
nkeynes@66 | 429 | }
|
nkeynes@66 | 430 |
|
nkeynes@66 | 431 | }
|