filename | src/aica/aica.c |
changeset | 106:9048bac046c3 |
prev | 86:f151e63f9754 |
next | 131:4c25f1b20664 |
author | nkeynes |
date | Tue Mar 14 12:45:53 2006 +0000 (17 years ago) |
permissions | -rw-r--r-- |
last change | Move driver selection out to main at long last. Add video NULL driver for headless operation Make dcload exit() actually exit |
file | annotate | diff | log | raw |
nkeynes@11 | 1 | /** |
nkeynes@106 | 2 | * $Id: aica.c,v 1.17 2006-03-14 12:45:53 nkeynes Exp $ |
nkeynes@11 | 3 | * |
nkeynes@11 | 4 | * This is the core sound system (ie the bit which does the actual work) |
nkeynes@11 | 5 | * |
nkeynes@11 | 6 | * Copyright (c) 2005 Nathan Keynes. |
nkeynes@11 | 7 | * |
nkeynes@11 | 8 | * This program is free software; you can redistribute it and/or modify |
nkeynes@11 | 9 | * it under the terms of the GNU General Public License as published by |
nkeynes@11 | 10 | * the Free Software Foundation; either version 2 of the License, or |
nkeynes@11 | 11 | * (at your option) any later version. |
nkeynes@11 | 12 | * |
nkeynes@11 | 13 | * This program is distributed in the hope that it will be useful, |
nkeynes@11 | 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
nkeynes@11 | 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
nkeynes@11 | 16 | * GNU General Public License for more details. |
nkeynes@11 | 17 | */ |
nkeynes@11 | 18 | |
nkeynes@35 | 19 | #define MODULE aica_module |
nkeynes@35 | 20 | |
nkeynes@11 | 21 | #include "dream.h" |
nkeynes@66 | 22 | #include "dreamcast.h" |
nkeynes@15 | 23 | #include "mem.h" |
nkeynes@106 | 24 | #include "aica/aica.h" |
nkeynes@61 | 25 | #include "armcore.h" |
nkeynes@106 | 26 | #include "aica/audio.h" |
nkeynes@11 | 27 | #define MMIO_IMPL |
nkeynes@11 | 28 | #include "aica.h" |
nkeynes@11 | 29 | |
nkeynes@11 | 30 | MMIO_REGION_READ_DEFFN( AICA0 ) |
nkeynes@11 | 31 | MMIO_REGION_READ_DEFFN( AICA1 ) |
nkeynes@11 | 32 | MMIO_REGION_READ_DEFFN( AICA2 ) |
nkeynes@11 | 33 | |
nkeynes@23 | 34 | void aica_init( void ); |
nkeynes@23 | 35 | void aica_reset( void ); |
nkeynes@23 | 36 | void aica_start( void ); |
nkeynes@23 | 37 | void aica_stop( void ); |
nkeynes@35 | 38 | void aica_save_state( FILE *f ); |
nkeynes@35 | 39 | int aica_load_state( FILE *f ); |
nkeynes@30 | 40 | uint32_t aica_run_slice( uint32_t ); |
nkeynes@23 | 41 | |
nkeynes@23 | 42 | struct dreamcast_module aica_module = { "AICA", aica_init, aica_reset, |
nkeynes@23 | 43 | aica_start, aica_run_slice, aica_stop, |
nkeynes@35 | 44 | aica_save_state, aica_load_state }; |
nkeynes@15 | 45 | |
nkeynes@11 | 46 | /** |
nkeynes@11 | 47 | * Initialize the AICA subsystem. Note requires that |
nkeynes@11 | 48 | */ |
nkeynes@11 | 49 | void aica_init( void ) |
nkeynes@11 | 50 | { |
nkeynes@11 | 51 | register_io_regions( mmio_list_spu ); |
nkeynes@11 | 52 | MMIO_NOTRACE(AICA0); |
nkeynes@11 | 53 | MMIO_NOTRACE(AICA1); |
nkeynes@11 | 54 | arm_mem_init(); |
nkeynes@66 | 55 | aica_reset(); |
nkeynes@11 | 56 | } |
nkeynes@11 | 57 | |
nkeynes@11 | 58 | void aica_reset( void ) |
nkeynes@11 | 59 | { |
nkeynes@35 | 60 | arm_reset(); |
nkeynes@66 | 61 | aica_event(2); /* Pre-deliver a timer interrupt */ |
nkeynes@11 | 62 | } |
nkeynes@11 | 63 | |
nkeynes@23 | 64 | void aica_start( void ) |
nkeynes@23 | 65 | { |
nkeynes@23 | 66 | |
nkeynes@23 | 67 | } |
nkeynes@23 | 68 | |
nkeynes@66 | 69 | /** |
nkeynes@66 | 70 | * Keep track of what we've done so far this second, to try to keep the |
nkeynes@66 | 71 | * precision of samples/second. |
nkeynes@66 | 72 | */ |
nkeynes@66 | 73 | int samples_done = 0; |
nkeynes@66 | 74 | uint32_t nanosecs_done = 0; |
nkeynes@66 | 75 | |
nkeynes@30 | 76 | uint32_t aica_run_slice( uint32_t nanosecs ) |
nkeynes@23 | 77 | { |
nkeynes@23 | 78 | /* Run arm instructions */ |
nkeynes@35 | 79 | int reset = MMIO_READ( AICA2, AICA_RESET ); |
nkeynes@66 | 80 | if( (reset & 1) == 0 ) { /* Running */ |
nkeynes@76 | 81 | int num_samples = (int)((uint64_t)AICA_SAMPLE_RATE * (nanosecs_done + nanosecs) / 1000000000) - samples_done; |
nkeynes@73 | 82 | num_samples = arm_run_slice( num_samples ); |
nkeynes@73 | 83 | audio_mix_samples( num_samples ); |
nkeynes@73 | 84 | |
nkeynes@66 | 85 | samples_done += num_samples; |
nkeynes@66 | 86 | nanosecs_done += nanosecs; |
nkeynes@35 | 87 | } |
nkeynes@73 | 88 | if( nanosecs_done > 1000000000 ) { |
nkeynes@73 | 89 | samples_done -= AICA_SAMPLE_RATE; |
nkeynes@73 | 90 | nanosecs_done -= 1000000000; |
nkeynes@73 | 91 | } |
nkeynes@43 | 92 | return nanosecs; |
nkeynes@23 | 93 | } |
nkeynes@23 | 94 | |
nkeynes@23 | 95 | void aica_stop( void ) |
nkeynes@23 | 96 | { |
nkeynes@23 | 97 | |
nkeynes@23 | 98 | } |
nkeynes@23 | 99 | |
nkeynes@35 | 100 | void aica_save_state( FILE *f ) |
nkeynes@35 | 101 | { |
nkeynes@35 | 102 | arm_save_state( f ); |
nkeynes@35 | 103 | } |
nkeynes@35 | 104 | |
nkeynes@35 | 105 | int aica_load_state( FILE *f ) |
nkeynes@35 | 106 | { |
nkeynes@35 | 107 | return arm_load_state( f ); |
nkeynes@35 | 108 | } |
nkeynes@35 | 109 | |
nkeynes@61 | 110 | int aica_event_pending = 0; |
nkeynes@61 | 111 | int aica_clear_count = 0; |
nkeynes@61 | 112 | |
nkeynes@61 | 113 | /* Note: This is probably not necessarily technically correct but it should |
nkeynes@61 | 114 | * work in the meantime. |
nkeynes@61 | 115 | */ |
nkeynes@61 | 116 | |
nkeynes@61 | 117 | void aica_event( int event ) |
nkeynes@61 | 118 | { |
nkeynes@61 | 119 | if( aica_event_pending == 0 ) |
nkeynes@61 | 120 | armr.int_pending |= CPSR_F; |
nkeynes@61 | 121 | aica_event_pending |= (1<<event); |
nkeynes@61 | 122 | |
nkeynes@61 | 123 | int pending = MMIO_READ( AICA2, AICA_IRQ ); |
nkeynes@61 | 124 | if( pending == 0 || event < pending ) |
nkeynes@61 | 125 | MMIO_WRITE( AICA2, AICA_IRQ, event ); |
nkeynes@61 | 126 | } |
nkeynes@61 | 127 | |
nkeynes@61 | 128 | void aica_clear_event( ) |
nkeynes@61 | 129 | { |
nkeynes@61 | 130 | aica_clear_count++; |
nkeynes@61 | 131 | if( aica_clear_count == 4 ) { |
nkeynes@61 | 132 | int i; |
nkeynes@61 | 133 | aica_clear_count = 0; |
nkeynes@61 | 134 | |
nkeynes@61 | 135 | for( i=0; i<8; i++ ) { |
nkeynes@61 | 136 | if( aica_event_pending & (1<<i) ) { |
nkeynes@61 | 137 | aica_event_pending &= ~(1<<i); |
nkeynes@61 | 138 | break; |
nkeynes@61 | 139 | } |
nkeynes@61 | 140 | } |
nkeynes@61 | 141 | for( ;i<8; i++ ) { |
nkeynes@61 | 142 | if( aica_event_pending & (1<<i) ) { |
nkeynes@61 | 143 | MMIO_WRITE( AICA2, AICA_IRQ, i ); |
nkeynes@61 | 144 | break; |
nkeynes@61 | 145 | } |
nkeynes@61 | 146 | } |
nkeynes@61 | 147 | if( aica_event_pending == 0 ) |
nkeynes@61 | 148 | armr.int_pending &= ~CPSR_F; |
nkeynes@61 | 149 | } |
nkeynes@61 | 150 | } |
nkeynes@66 | 151 | |
nkeynes@86 | 152 | void aica_enable( void ) |
nkeynes@86 | 153 | { |
nkeynes@86 | 154 | mmio_region_AICA2_write( AICA_RESET, MMIO_READ(AICA2,AICA_RESET) & ~1 ); |
nkeynes@86 | 155 | } |
nkeynes@86 | 156 | |
nkeynes@11 | 157 | /** Channel register structure: |
nkeynes@43 | 158 | * 00 4 Channel config |
nkeynes@43 | 159 | * 04 4 Waveform address lo (16 bits) |
nkeynes@11 | 160 | * 08 4 Loop start address |
nkeynes@11 | 161 | * 0C 4 Loop end address |
nkeynes@11 | 162 | * 10 4 Volume envelope |
nkeynes@43 | 163 | * 14 4 Init to 0x1F |
nkeynes@43 | 164 | * 18 4 Frequency (floating point) |
nkeynes@43 | 165 | * 1C 4 ?? |
nkeynes@43 | 166 | * 20 4 ?? |
nkeynes@11 | 167 | * 24 1 Pan |
nkeynes@11 | 168 | * 25 1 ?? |
nkeynes@11 | 169 | * 26 |
nkeynes@11 | 170 | * 27 |
nkeynes@11 | 171 | * 28 1 ?? |
nkeynes@11 | 172 | * 29 1 Volume |
nkeynes@11 | 173 | * 2C |
nkeynes@11 | 174 | * 30 |
nkeynes@11 | 175 | * |
nkeynes@11 | 176 | |
nkeynes@11 | 177 | /* Write to channels 0-31 */ |
nkeynes@11 | 178 | void mmio_region_AICA0_write( uint32_t reg, uint32_t val ) |
nkeynes@11 | 179 | { |
nkeynes@35 | 180 | MMIO_WRITE( AICA0, reg, val ); |
nkeynes@66 | 181 | aica_write_channel( reg >> 7, reg % 128, val ); |
nkeynes@37 | 182 | // DEBUG( "AICA0 Write %08X => %08X", val, reg ); |
nkeynes@11 | 183 | } |
nkeynes@11 | 184 | |
nkeynes@11 | 185 | /* Write to channels 32-64 */ |
nkeynes@11 | 186 | void mmio_region_AICA1_write( uint32_t reg, uint32_t val ) |
nkeynes@11 | 187 | { |
nkeynes@35 | 188 | MMIO_WRITE( AICA1, reg, val ); |
nkeynes@66 | 189 | aica_write_channel( (reg >> 7) + 32, reg % 128, val ); |
nkeynes@37 | 190 | // DEBUG( "AICA1 Write %08X => %08X", val, reg ); |
nkeynes@11 | 191 | } |
nkeynes@11 | 192 | |
nkeynes@66 | 193 | /** |
nkeynes@66 | 194 | * AICA control registers |
nkeynes@66 | 195 | */ |
nkeynes@11 | 196 | void mmio_region_AICA2_write( uint32_t reg, uint32_t val ) |
nkeynes@11 | 197 | { |
nkeynes@35 | 198 | uint32_t tmp; |
nkeynes@35 | 199 | switch( reg ) { |
nkeynes@35 | 200 | case AICA_RESET: |
nkeynes@35 | 201 | tmp = MMIO_READ( AICA2, AICA_RESET ); |
nkeynes@37 | 202 | if( (tmp & 1) == 1 && (val & 1) == 0 ) { |
nkeynes@35 | 203 | /* ARM enabled - execute a core reset */ |
nkeynes@37 | 204 | DEBUG( "ARM enabled" ); |
nkeynes@35 | 205 | arm_reset(); |
nkeynes@66 | 206 | samples_done = 0; |
nkeynes@66 | 207 | nanosecs_done = 0; |
nkeynes@37 | 208 | } else if( (tmp&1) == 0 && (val&1) == 1 ) { |
nkeynes@37 | 209 | DEBUG( "ARM disabled" ); |
nkeynes@35 | 210 | } |
nkeynes@35 | 211 | MMIO_WRITE( AICA2, AICA_RESET, val ); |
nkeynes@35 | 212 | break; |
nkeynes@61 | 213 | case AICA_IRQCLEAR: |
nkeynes@61 | 214 | aica_clear_event(); |
nkeynes@61 | 215 | break; |
nkeynes@35 | 216 | default: |
nkeynes@35 | 217 | MMIO_WRITE( AICA2, reg, val ); |
nkeynes@35 | 218 | break; |
nkeynes@35 | 219 | } |
nkeynes@11 | 220 | } |
nkeynes@66 | 221 | |
nkeynes@66 | 222 | /** |
nkeynes@66 | 223 | * Translate the channel frequency to a sample rate. The frequency is a |
nkeynes@66 | 224 | * 14-bit floating point number, where bits 0..9 is the mantissa, |
nkeynes@66 | 225 | * 11..14 is the signed exponent (-8 to +7). Bit 10 appears to |
nkeynes@66 | 226 | * be unused. |
nkeynes@66 | 227 | * |
nkeynes@66 | 228 | * @return sample rate in samples per second. |
nkeynes@66 | 229 | */ |
nkeynes@66 | 230 | uint32_t aica_frequency_to_sample_rate( uint32_t freq ) |
nkeynes@66 | 231 | { |
nkeynes@66 | 232 | uint32_t exponent = (freq & 0x3800) >> 11; |
nkeynes@66 | 233 | uint32_t mantissa = freq & 0x03FF; |
nkeynes@82 | 234 | uint32_t rate; |
nkeynes@66 | 235 | if( freq & 0x4000 ) { |
nkeynes@66 | 236 | /* neg exponent - rate < 44100 */ |
nkeynes@66 | 237 | exponent = 8 - exponent; |
nkeynes@82 | 238 | rate = (44100 >> exponent) + |
nkeynes@66 | 239 | ((44100 * mantissa) >> (10+exponent)); |
nkeynes@66 | 240 | } else { |
nkeynes@66 | 241 | /* pos exponent - rate > 44100 */ |
nkeynes@82 | 242 | rate = (44100 << exponent) + |
nkeynes@66 | 243 | ((44100 * mantissa) >> (10-exponent)); |
nkeynes@66 | 244 | } |
nkeynes@82 | 245 | return rate; |
nkeynes@66 | 246 | } |
nkeynes@66 | 247 | |
nkeynes@82 | 248 | /** |
nkeynes@82 | 249 | * Derived directly from Dan Potter's log table |
nkeynes@82 | 250 | */ |
nkeynes@82 | 251 | uint8_t aica_volume_table[256] = { |
nkeynes@82 | 252 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, |
nkeynes@82 | 253 | 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 4, |
nkeynes@82 | 254 | 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 7, 7, 7, 8, 8, 9, |
nkeynes@82 | 255 | 9, 9, 10, 10, 11, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 16, |
nkeynes@82 | 256 | 16, 17, 17, 18, 18, 19, 19, 20, 20, 21, 22, 22, 23, 23, 24, 25, |
nkeynes@82 | 257 | 25, 26, 27, 27, 28, 29, 29, 30, 31, 31, 32, 33, 34, 34, 35, 36, |
nkeynes@82 | 258 | 37, 37, 38, 39, 40, 40, 41, 42, 43, 44, 45, 45, 46, 47, 48, 49, |
nkeynes@82 | 259 | 50, 51, 52, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, |
nkeynes@82 | 260 | 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 76, 77, 78, 79, 80, 81, |
nkeynes@82 | 261 | 82, 83, 85, 86, 87, 88, 89, 90, 92, 93, 94, 95, 97, 98, 99, 100, |
nkeynes@82 | 262 | 102, 103, 104, 105, 107, 108, 109, 111, 112, 113, 115, 116, 117, 119, 120, 121, |
nkeynes@82 | 263 | 123, 124, 126, 127, 128, 130, 131, 133, 134, 136, 137, 139, 140, 142, 143, 145, |
nkeynes@82 | 264 | 146, 148, 149, 151, 152, 154, 155, 157, 159, 160, 162, 163, 165, 167, 168, 170, |
nkeynes@82 | 265 | 171, 173, 175, 176, 178, 180, 181, 183, 185, 187, 188, 190, 192, 194, 195, 197, |
nkeynes@82 | 266 | 199, 201, 202, 204, 206, 208, 210, 211, 213, 215, 217, 219, 221, 223, 224, 226, |
nkeynes@82 | 267 | 228, 230, 232, 234, 236, 238, 240, 242, 244, 246, 248, 250, 252, 253, 254, 255 }; |
nkeynes@82 | 268 | |
nkeynes@82 | 269 | |
nkeynes@66 | 270 | void aica_write_channel( int channelNo, uint32_t reg, uint32_t val ) |
nkeynes@66 | 271 | { |
nkeynes@66 | 272 | val &= 0x0000FFFF; |
nkeynes@66 | 273 | audio_channel_t channel = audio_get_channel(channelNo); |
nkeynes@66 | 274 | switch( reg ) { |
nkeynes@66 | 275 | case 0x00: /* Config + high address bits*/ |
nkeynes@66 | 276 | channel->start = (channel->start & 0xFFFF) | ((val&0x1F) << 16); |
nkeynes@66 | 277 | if( val & 0x200 ) |
nkeynes@73 | 278 | channel->loop = TRUE; |
nkeynes@66 | 279 | else |
nkeynes@73 | 280 | channel->loop = FALSE; |
nkeynes@66 | 281 | switch( (val >> 7) & 0x03 ) { |
nkeynes@66 | 282 | case 0: |
nkeynes@66 | 283 | channel->sample_format = AUDIO_FMT_16BIT; |
nkeynes@66 | 284 | break; |
nkeynes@66 | 285 | case 1: |
nkeynes@66 | 286 | channel->sample_format = AUDIO_FMT_8BIT; |
nkeynes@66 | 287 | break; |
nkeynes@66 | 288 | case 2: |
nkeynes@66 | 289 | case 3: |
nkeynes@66 | 290 | channel->sample_format = AUDIO_FMT_ADPCM; |
nkeynes@66 | 291 | break; |
nkeynes@66 | 292 | } |
nkeynes@66 | 293 | switch( (val >> 14) & 0x03 ) { |
nkeynes@66 | 294 | case 2: |
nkeynes@66 | 295 | audio_stop_channel( channelNo ); |
nkeynes@66 | 296 | break; |
nkeynes@66 | 297 | case 3: |
nkeynes@66 | 298 | audio_start_channel( channelNo ); |
nkeynes@66 | 299 | break; |
nkeynes@66 | 300 | default: |
nkeynes@66 | 301 | break; |
nkeynes@66 | 302 | /* Hrmm... */ |
nkeynes@66 | 303 | } |
nkeynes@66 | 304 | break; |
nkeynes@66 | 305 | case 0x04: /* Low 16 address bits */ |
nkeynes@66 | 306 | channel->start = (channel->start & 0x001F0000) | val; |
nkeynes@66 | 307 | break; |
nkeynes@66 | 308 | case 0x08: /* Loop start */ |
nkeynes@66 | 309 | channel->loop_start = val; |
nkeynes@66 | 310 | break; |
nkeynes@73 | 311 | case 0x0C: /* End */ |
nkeynes@73 | 312 | channel->end = val; |
nkeynes@66 | 313 | break; |
nkeynes@66 | 314 | case 0x10: /* Envelope register 1 */ |
nkeynes@66 | 315 | break; |
nkeynes@66 | 316 | case 0x14: /* Envelope register 2 */ |
nkeynes@66 | 317 | break; |
nkeynes@66 | 318 | case 0x18: /* Frequency */ |
nkeynes@66 | 319 | channel->sample_rate = aica_frequency_to_sample_rate ( val ); |
nkeynes@66 | 320 | break; |
nkeynes@66 | 321 | case 0x1C: /* ??? */ |
nkeynes@66 | 322 | case 0x20: /* ??? */ |
nkeynes@66 | 323 | case 0x24: /* Volume? /pan */ |
nkeynes@82 | 324 | val = val & 0x1F; |
nkeynes@82 | 325 | if( val <= 0x0F ) |
nkeynes@82 | 326 | val = 0x0F - val; /* Convert to smooth pan over 0..31 */ |
nkeynes@82 | 327 | channel->pan = val; |
nkeynes@66 | 328 | break; |
nkeynes@66 | 329 | case 0x28: /* Volume */ |
nkeynes@82 | 330 | channel->vol = aica_volume_table[val & 0xFF]; |
nkeynes@66 | 331 | break; |
nkeynes@66 | 332 | default: /* ??? */ |
nkeynes@66 | 333 | break; |
nkeynes@66 | 334 | } |
nkeynes@66 | 335 | |
nkeynes@66 | 336 | } |
.