trunk/src/mame/audio/phoenix.c
| r23880 | r23881 | |
| 9 | 9 | |
| 10 | 10 | |
| 11 | 11 | #include "emu.h" |
| 12 | | #include "sound/tms36xx.h" |
| 13 | 12 | #include "includes/phoenix.h" |
| 14 | 13 | |
| 15 | 14 | /**************************************************************************** |
| r23880 | r23881 | |
| 45 | 44 | #define VMIN 0 |
| 46 | 45 | #define VMAX 32767 |
| 47 | 46 | |
| 48 | | struct c_state |
| 49 | | { |
| 50 | | INT32 counter; |
| 51 | | INT32 level; |
| 52 | | }; |
| 53 | 47 | |
| 54 | | struct n_state |
| 48 | |
| 49 | const device_type PHOENIX = &device_creator<phoenix_sound_device>; |
| 50 | |
| 51 | phoenix_sound_device::phoenix_sound_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) |
| 52 | : device_t(mconfig, PHOENIX, "Phoenix Custom", tag, owner, clock, "phoenix_sound", __FILE__), |
| 53 | device_sound_interface(mconfig, *this) |
| 55 | 54 | { |
| 56 | | INT32 counter; |
| 57 | | INT32 polyoffs; |
| 58 | | INT32 polybit; |
| 59 | | INT32 lowpass_counter; |
| 60 | | INT32 lowpass_polybit; |
| 61 | | }; |
| 55 | } |
| 62 | 56 | |
| 63 | | struct phoenix_sound_state |
| 57 | //------------------------------------------------- |
| 58 | // device_config_complete - perform any |
| 59 | // operations now that the configuration is |
| 60 | // complete |
| 61 | //------------------------------------------------- |
| 62 | |
| 63 | void phoenix_sound_device::device_config_complete() |
| 64 | 64 | { |
| 65 | | struct c_state m_c24_state; |
| 66 | | struct c_state m_c25_state; |
| 67 | | struct n_state m_noise_state; |
| 68 | | UINT8 m_sound_latch_a; |
| 69 | | sound_stream * m_channel; |
| 70 | | UINT32 * m_poly18; |
| 71 | | device_t *m_discrete; |
| 72 | | tms36xx_device *m_tms; |
| 73 | | }; |
| 65 | } |
| 74 | 66 | |
| 75 | | INLINE phoenix_sound_state *get_safe_token( device_t *device ) |
| 67 | //------------------------------------------------- |
| 68 | // device_start - device-specific startup |
| 69 | //------------------------------------------------- |
| 70 | |
| 71 | void phoenix_sound_device::device_start() |
| 76 | 72 | { |
| 77 | | assert(device != NULL); |
| 78 | | assert(device->type() == PHOENIX); |
| 73 | int i, j; |
| 74 | UINT32 shiftreg; |
| 79 | 75 | |
| 80 | | return (phoenix_sound_state *)downcast<phoenix_sound_device *>(device)->token(); |
| 76 | m_sound_latch_a = 0; |
| 77 | memset(&m_c24_state, 0, sizeof(m_c24_state)); |
| 78 | memset(&m_c25_state, 0, sizeof(m_c25_state)); |
| 79 | memset(&m_noise_state, 0, sizeof(m_noise_state)); |
| 80 | |
| 81 | m_discrete = machine().device("discrete"); |
| 82 | m_tms = machine().device<tms36xx_device>("tms"); |
| 83 | |
| 84 | m_poly18 = auto_alloc_array(machine(), UINT32, 1ul << (18-5)); |
| 85 | |
| 86 | shiftreg = 0; |
| 87 | for( i = 0; i < (1ul << (18-5)); i++ ) |
| 88 | { |
| 89 | UINT32 bits = 0; |
| 90 | for( j = 0; j < 32; j++ ) |
| 91 | { |
| 92 | bits = (bits >> 1) | (shiftreg << 31); |
| 93 | if( ((shiftreg >> 16) & 1) == ((shiftreg >> 17) & 1) ) |
| 94 | shiftreg = (shiftreg << 1) | 1; |
| 95 | else |
| 96 | shiftreg <<= 1; |
| 97 | } |
| 98 | m_poly18[i] = bits; |
| 99 | } |
| 100 | |
| 101 | m_channel = machine().sound().stream_alloc(*this, 0, 1, machine().sample_rate(), this); |
| 102 | |
| 103 | save_item(NAME(m_sound_latch_a)); |
| 104 | save_item(NAME(m_c24_state.counter)); |
| 105 | save_item(NAME(m_c24_state.level)); |
| 106 | save_item(NAME(m_c25_state.counter)); |
| 107 | save_item(NAME(m_c25_state.level)); |
| 108 | save_item(NAME(m_noise_state.counter)); |
| 109 | save_item(NAME(m_noise_state.polybit)); |
| 110 | save_item(NAME(m_noise_state.polyoffs)); |
| 111 | save_item(NAME(m_noise_state.lowpass_counter)); |
| 112 | save_item(NAME(m_noise_state.lowpass_polybit)); |
| 113 | save_pointer(NAME(m_poly18), (1ul << (18-5))); |
| 81 | 114 | } |
| 82 | 115 | |
| 83 | | INLINE int update_c24(phoenix_sound_state *state, int samplerate) |
| 116 | int phoenix_sound_device::update_c24(int samplerate) |
| 84 | 117 | { |
| 85 | 118 | /* |
| 86 | 119 | * Noise frequency control (Port B): |
| r23880 | r23881 | |
| 93 | 126 | #define R51 330 |
| 94 | 127 | #define R52 20000 |
| 95 | 128 | |
| 96 | | c_state *c24_state = &state->m_c24_state; |
| 97 | | |
| 98 | | if( state->m_sound_latch_a & 0x40 ) |
| 129 | if( m_sound_latch_a & 0x40 ) |
| 99 | 130 | { |
| 100 | | if (c24_state->level > VMIN) |
| 131 | if (m_c24_state.level > VMIN) |
| 101 | 132 | { |
| 102 | | c24_state->counter -= (int)((c24_state->level - VMIN) / (R52 * C24)); |
| 103 | | if( c24_state->counter <= 0 ) |
| 133 | m_c24_state.counter -= (int)((m_c24_state.level - VMIN) / (R52 * C24)); |
| 134 | if( m_c24_state.counter <= 0 ) |
| 104 | 135 | { |
| 105 | | int n = -c24_state->counter / samplerate + 1; |
| 106 | | c24_state->counter += n * samplerate; |
| 107 | | if( (c24_state->level -= n) < VMIN) |
| 108 | | c24_state->level = VMIN; |
| 136 | int n = -m_c24_state.counter / samplerate + 1; |
| 137 | m_c24_state.counter += n * samplerate; |
| 138 | if( (m_c24_state.level -= n) < VMIN) |
| 139 | m_c24_state.level = VMIN; |
| 109 | 140 | } |
| 110 | 141 | } |
| 111 | 142 | } |
| 112 | 143 | else |
| 113 | 144 | { |
| 114 | | if (c24_state->level < VMAX) |
| 145 | if (m_c24_state.level < VMAX) |
| 115 | 146 | { |
| 116 | | c24_state->counter -= (int)((VMAX - c24_state->level) / ((R51+R49) * C24)); |
| 117 | | if( c24_state->counter <= 0 ) |
| 147 | m_c24_state.counter -= (int)((VMAX - m_c24_state.level) / ((R51+R49) * C24)); |
| 148 | if( m_c24_state.counter <= 0 ) |
| 118 | 149 | { |
| 119 | | int n = -c24_state->counter / samplerate + 1; |
| 120 | | c24_state->counter += n * samplerate; |
| 121 | | if( (c24_state->level += n) > VMAX) |
| 122 | | c24_state->level = VMAX; |
| 150 | int n = -m_c24_state.counter / samplerate + 1; |
| 151 | m_c24_state.counter += n * samplerate; |
| 152 | if( (m_c24_state.level += n) > VMAX) |
| 153 | m_c24_state.level = VMAX; |
| 123 | 154 | } |
| 124 | 155 | } |
| 125 | 156 | } |
| 126 | | return VMAX - c24_state->level; |
| 157 | return VMAX - m_c24_state.level; |
| 127 | 158 | } |
| 128 | 159 | |
| 129 | | INLINE int update_c25(phoenix_sound_state *state, int samplerate) |
| 160 | int phoenix_sound_device::update_c25(int samplerate) |
| 130 | 161 | { |
| 131 | 162 | /* |
| 132 | 163 | * Bit 7 hi charges C25 (6.8u) over a R50 (1k) and R53 (330) and when |
| r23880 | r23881 | |
| 138 | 169 | #define R53 330 |
| 139 | 170 | #define R54 47000 |
| 140 | 171 | |
| 141 | | c_state *c25_state = &state->m_c25_state; |
| 142 | | |
| 143 | | if( state->m_sound_latch_a & 0x80 ) |
| 172 | if( m_sound_latch_a & 0x80 ) |
| 144 | 173 | { |
| 145 | | if (c25_state->level < VMAX) |
| 174 | if (m_c25_state.level < VMAX) |
| 146 | 175 | { |
| 147 | | c25_state->counter -= (int)((VMAX - c25_state->level) / ((R50+R53) * C25)); |
| 148 | | if( c25_state->counter <= 0 ) |
| 176 | m_c25_state.counter -= (int)((VMAX - m_c25_state.level) / ((R50+R53) * C25)); |
| 177 | if( m_c25_state.counter <= 0 ) |
| 149 | 178 | { |
| 150 | | int n = -c25_state->counter / samplerate + 1; |
| 151 | | c25_state->counter += n * samplerate; |
| 152 | | if( (c25_state->level += n) > VMAX ) |
| 153 | | c25_state->level = VMAX; |
| 179 | int n = -m_c25_state.counter / samplerate + 1; |
| 180 | m_c25_state.counter += n * samplerate; |
| 181 | if( (m_c25_state.level += n) > VMAX ) |
| 182 | m_c25_state.level = VMAX; |
| 154 | 183 | } |
| 155 | 184 | } |
| 156 | 185 | } |
| 157 | 186 | else |
| 158 | 187 | { |
| 159 | | if (c25_state->level > VMIN) |
| 188 | if (m_c25_state.level > VMIN) |
| 160 | 189 | { |
| 161 | | c25_state->counter -= (int)((c25_state->level - VMIN) / (R54 * C25)); |
| 162 | | if( c25_state->counter <= 0 ) |
| 190 | m_c25_state.counter -= (int)((m_c25_state.level - VMIN) / (R54 * C25)); |
| 191 | if( m_c25_state.counter <= 0 ) |
| 163 | 192 | { |
| 164 | | int n = -c25_state->counter / samplerate + 1; |
| 165 | | c25_state->counter += n * samplerate; |
| 166 | | if( (c25_state->level -= n) < VMIN ) |
| 167 | | c25_state->level = VMIN; |
| 193 | int n = -m_c25_state.counter / samplerate + 1; |
| 194 | m_c25_state.counter += n * samplerate; |
| 195 | if( (m_c25_state.level -= n) < VMIN ) |
| 196 | m_c25_state.level = VMIN; |
| 168 | 197 | } |
| 169 | 198 | } |
| 170 | 199 | } |
| 171 | | return c25_state->level; |
| 200 | return m_c25_state.level; |
| 172 | 201 | } |
| 173 | 202 | |
| 174 | 203 | |
| 175 | | INLINE int noise(phoenix_sound_state *state, int samplerate) |
| 204 | int phoenix_sound_device::noise(int samplerate) |
| 176 | 205 | { |
| 177 | | int vc24 = update_c24(state, samplerate); |
| 178 | | int vc25 = update_c25(state, samplerate); |
| 206 | int vc24 = update_c24(samplerate); |
| 207 | int vc25 = update_c25(samplerate); |
| 179 | 208 | int sum = 0, level, frequency; |
| 180 | | n_state *noise_state = &state->m_noise_state; |
| 181 | | |
| 209 | |
| 182 | 210 | /* |
| 183 | 211 | * The voltage levels are added and control I(CE) of transistor TR1 |
| 184 | 212 | * (NPN) which then controls the noise clock frequency (linearily?). |
| r23880 | r23881 | |
| 197 | 225 | * R71 (2700 Ohms) parallel to R73 (47k Ohms) = approx. 2553 Ohms |
| 198 | 226 | * maxfreq = 1.44 / ((2553+2*1000) * 0.05e-6) = approx. 6325 Hz |
| 199 | 227 | */ |
| 200 | | noise_state->counter -= frequency; |
| 201 | | if( noise_state->counter <= 0 ) |
| 228 | m_noise_state.counter -= frequency; |
| 229 | if( m_noise_state.counter <= 0 ) |
| 202 | 230 | { |
| 203 | | int n = (-noise_state->counter / samplerate) + 1; |
| 204 | | noise_state->counter += n * samplerate; |
| 205 | | noise_state->polyoffs = (noise_state->polyoffs + n) & 0x3ffff; |
| 206 | | noise_state->polybit = (state->m_poly18[noise_state->polyoffs>>5] >> (noise_state->polyoffs & 31)) & 1; |
| 231 | int n = (-m_noise_state.counter / samplerate) + 1; |
| 232 | m_noise_state.counter += n * samplerate; |
| 233 | m_noise_state.polyoffs = (m_noise_state.polyoffs + n) & 0x3ffff; |
| 234 | m_noise_state.polybit = (m_poly18[m_noise_state.polyoffs>>5] >> (m_noise_state.polyoffs & 31)) & 1; |
| 207 | 235 | } |
| 208 | | if (!noise_state->polybit) |
| 236 | if (!m_noise_state.polybit) |
| 209 | 237 | sum += vc24; |
| 210 | 238 | |
| 211 | 239 | /* 400Hz crude low pass filter: this is only a guess!! */ |
| 212 | | noise_state->lowpass_counter -= 400; |
| 213 | | if( noise_state->lowpass_counter <= 0 ) |
| 240 | m_noise_state.lowpass_counter -= 400; |
| 241 | if( m_noise_state.lowpass_counter <= 0 ) |
| 214 | 242 | { |
| 215 | | noise_state->lowpass_counter += samplerate; |
| 216 | | noise_state->lowpass_polybit = noise_state->polybit; |
| 243 | m_noise_state.lowpass_counter += samplerate; |
| 244 | m_noise_state.lowpass_polybit = m_noise_state.polybit; |
| 217 | 245 | } |
| 218 | | if (!noise_state->lowpass_polybit) |
| 246 | if (!m_noise_state.lowpass_polybit) |
| 219 | 247 | sum += vc25; |
| 220 | 248 | |
| 221 | 249 | return sum; |
| 222 | 250 | } |
| 223 | 251 | |
| 224 | | static STREAM_UPDATE( phoenix_sound_update ) |
| 225 | | { |
| 226 | | phoenix_sound_state *state = get_safe_token(device); |
| 227 | | int samplerate = device->machine().sample_rate(); |
| 228 | | stream_sample_t *buffer = outputs[0]; |
| 229 | 252 | |
| 230 | | while( samples-- > 0 ) |
| 231 | | { |
| 232 | | int sum = 0; |
| 233 | | sum = noise(state, samplerate) / 2; |
| 234 | | *buffer++ = sum < 32768 ? sum > -32768 ? sum : -32768 : 32767; |
| 235 | | } |
| 236 | | } |
| 237 | | |
| 238 | | |
| 239 | 253 | /************************************************************************/ |
| 240 | 254 | /* phoenix Sound System Analog emulation */ |
| 241 | 255 | /* */ |
| r23880 | r23881 | |
| 490 | 504 | DISCRETE_OUTPUT(NODE_90, 1) |
| 491 | 505 | DISCRETE_SOUND_END |
| 492 | 506 | |
| 493 | | WRITE8_DEVICE_HANDLER( phoenix_sound_control_a_w ) |
| 507 | WRITE8_MEMBER( phoenix_sound_device::control_a_w ) |
| 494 | 508 | { |
| 495 | | phoenix_sound_state *state = get_safe_token(device); |
| 496 | | |
| 497 | | discrete_sound_w(state->m_discrete, space, PHOENIX_EFFECT_2_DATA, data & 0x0f); |
| 498 | | discrete_sound_w(state->m_discrete, space, PHOENIX_EFFECT_2_FREQ, (data & 0x30) >> 4); |
| 509 | discrete_sound_w(m_discrete, space, PHOENIX_EFFECT_2_DATA, data & 0x0f); |
| 510 | discrete_sound_w(m_discrete, space, PHOENIX_EFFECT_2_FREQ, (data & 0x30) >> 4); |
| 499 | 511 | #if 0 |
| 500 | 512 | /* future handling of noise sounds */ |
| 501 | | discrete_sound_w(state->m_discrete, space, PHOENIX_EFFECT_3_EN , data & 0x40); |
| 502 | | discrete_sound_w(state->m_discrete, space, PHOENIX_EFFECT_4_EN , data & 0x80); |
| 513 | discrete_sound_w(m_discrete, space, PHOENIX_EFFECT_3_EN , data & 0x40); |
| 514 | discrete_sound_w(m_discrete, space, PHOENIX_EFFECT_4_EN , data & 0x80); |
| 503 | 515 | #endif |
| 504 | | state->m_channel->update(); |
| 505 | | state->m_sound_latch_a = data; |
| 516 | m_channel->update(); |
| 517 | m_sound_latch_a = data; |
| 506 | 518 | } |
| 507 | 519 | |
| 508 | | static void register_state(device_t *device) |
| 520 | WRITE8_MEMBER( phoenix_sound_device::control_b_w ) |
| 509 | 521 | { |
| 510 | | phoenix_sound_state *state = get_safe_token(device); |
| 522 | discrete_sound_w(m_discrete, space, PHOENIX_EFFECT_1_DATA, data & 0x0f); |
| 523 | discrete_sound_w(m_discrete, space, PHOENIX_EFFECT_1_FILT, data & 0x20); |
| 524 | discrete_sound_w(m_discrete, space, PHOENIX_EFFECT_1_FREQ, data & 0x10); |
| 511 | 525 | |
| 512 | | device->save_item(NAME(state->m_sound_latch_a)); |
| 513 | | device->save_item(NAME(state->m_c24_state.counter)); |
| 514 | | device->save_item(NAME(state->m_c24_state.level)); |
| 515 | | device->save_item(NAME(state->m_c25_state.counter)); |
| 516 | | device->save_item(NAME(state->m_c25_state.level)); |
| 517 | | device->save_item(NAME(state->m_noise_state.counter)); |
| 518 | | device->save_item(NAME(state->m_noise_state.polybit)); |
| 519 | | device->save_item(NAME(state->m_noise_state.polyoffs)); |
| 520 | | device->save_item(NAME(state->m_noise_state.lowpass_counter)); |
| 521 | | device->save_item(NAME(state->m_noise_state.lowpass_polybit)); |
| 522 | | device->save_pointer(NAME(state->m_poly18), (1ul << (18-5))); |
| 523 | | } |
| 524 | | |
| 525 | | WRITE8_DEVICE_HANDLER( phoenix_sound_control_b_w ) |
| 526 | | { |
| 527 | | phoenix_sound_state *state = get_safe_token(device); |
| 528 | | |
| 529 | | discrete_sound_w(state->m_discrete, space, PHOENIX_EFFECT_1_DATA, data & 0x0f); |
| 530 | | discrete_sound_w(state->m_discrete, space, PHOENIX_EFFECT_1_FILT, data & 0x20); |
| 531 | | discrete_sound_w(state->m_discrete, space, PHOENIX_EFFECT_1_FREQ, data & 0x10); |
| 532 | | |
| 533 | 526 | /* update the tune that the MM6221AA is playing */ |
| 534 | | state->m_tms->mm6221aa_tune_w(data >> 6); |
| 527 | m_tms->mm6221aa_tune_w(data >> 6); |
| 535 | 528 | } |
| 536 | 529 | |
| 537 | | static DEVICE_START( phoenix_sound ) |
| 538 | | { |
| 539 | | phoenix_sound_state *state = get_safe_token(device); |
| 540 | | int i, j; |
| 541 | | UINT32 shiftreg; |
| 542 | 530 | |
| 543 | | state->m_sound_latch_a = 0; |
| 544 | | memset(&state->m_c24_state, 0, sizeof(state->m_c24_state)); |
| 545 | | memset(&state->m_c25_state, 0, sizeof(state->m_c25_state)); |
| 546 | | memset(&state->m_noise_state, 0, sizeof(state->m_noise_state)); |
| 547 | | |
| 548 | | state->m_discrete = device->machine().device("discrete"); |
| 549 | | state->m_tms = device->machine().device<tms36xx_device>("tms"); |
| 550 | | |
| 551 | | state->m_poly18 = auto_alloc_array(device->machine(), UINT32, 1ul << (18-5)); |
| 552 | | |
| 553 | | shiftreg = 0; |
| 554 | | for( i = 0; i < (1ul << (18-5)); i++ ) |
| 555 | | { |
| 556 | | UINT32 bits = 0; |
| 557 | | for( j = 0; j < 32; j++ ) |
| 558 | | { |
| 559 | | bits = (bits >> 1) | (shiftreg << 31); |
| 560 | | if( ((shiftreg >> 16) & 1) == ((shiftreg >> 17) & 1) ) |
| 561 | | shiftreg = (shiftreg << 1) | 1; |
| 562 | | else |
| 563 | | shiftreg <<= 1; |
| 564 | | } |
| 565 | | state->m_poly18[i] = bits; |
| 566 | | } |
| 567 | | |
| 568 | | state->m_channel = device->machine().sound().stream_alloc(*device, 0, 1, device->machine().sample_rate(), 0, phoenix_sound_update); |
| 569 | | |
| 570 | | register_state(device); |
| 571 | | } |
| 572 | | |
| 573 | | const device_type PHOENIX = &device_creator<phoenix_sound_device>; |
| 574 | | |
| 575 | | phoenix_sound_device::phoenix_sound_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) |
| 576 | | : device_t(mconfig, PHOENIX, "Phoenix Custom", tag, owner, clock, "phoenix_sound", __FILE__), |
| 577 | | device_sound_interface(mconfig, *this) |
| 578 | | { |
| 579 | | m_token = global_alloc_clear(phoenix_sound_state); |
| 580 | | } |
| 581 | | |
| 582 | 531 | //------------------------------------------------- |
| 583 | | // device_config_complete - perform any |
| 584 | | // operations now that the configuration is |
| 585 | | // complete |
| 586 | | //------------------------------------------------- |
| 587 | | |
| 588 | | void phoenix_sound_device::device_config_complete() |
| 589 | | { |
| 590 | | } |
| 591 | | |
| 592 | | //------------------------------------------------- |
| 593 | | // device_start - device-specific startup |
| 594 | | //------------------------------------------------- |
| 595 | | |
| 596 | | void phoenix_sound_device::device_start() |
| 597 | | { |
| 598 | | DEVICE_START_NAME( phoenix_sound )(this); |
| 599 | | } |
| 600 | | |
| 601 | | //------------------------------------------------- |
| 602 | 532 | // sound_stream_update - handle a stream update |
| 603 | 533 | //------------------------------------------------- |
| 604 | 534 | |
| 605 | 535 | void phoenix_sound_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples) |
| 606 | 536 | { |
| 607 | | // should never get here |
| 608 | | fatalerror("sound_stream_update called; not applicable to legacy sound devices\n"); |
| 537 | int samplerate = machine().sample_rate(); |
| 538 | stream_sample_t *buffer = outputs[0]; |
| 539 | |
| 540 | while( samples-- > 0 ) |
| 541 | { |
| 542 | int sum = 0; |
| 543 | sum = noise(samplerate) / 2; |
| 544 | *buffer++ = sum < 32768 ? sum > -32768 ? sum : -32768 : 32767; |
| 545 | } |
| 609 | 546 | } |