trunk/src/mame/audio/exidy.c
| r24635 | r24636 | |
| 8 | 8 | #include "cpu/z80/z80.h" |
| 9 | 9 | #include "machine/rescap.h" |
| 10 | 10 | #include "cpu/m6502/m6502.h" |
| 11 | | #include "machine/6821pia.h" |
| 12 | | #include "machine/6532riot.h" |
| 13 | | #include "sound/hc55516.h" |
| 14 | | #include "sound/tms5220.h" |
| 15 | 11 | #include "audio/exidy.h" |
| 16 | | #include "devlegcy.h" |
| 17 | 12 | |
| 18 | 13 | |
| 19 | 14 | |
| r24635 | r24636 | |
| 39 | 34 | }; |
| 40 | 35 | |
| 41 | 36 | |
| 42 | | |
| 43 | 37 | /************************************* |
| 44 | 38 | * |
| 45 | | * Local variables |
| 46 | | * |
| 47 | | *************************************/ |
| 48 | | |
| 49 | | /* 6840 variables */ |
| 50 | | struct sh6840_timer_channel |
| 51 | | { |
| 52 | | UINT8 cr; |
| 53 | | UINT8 state; |
| 54 | | UINT8 leftovers; |
| 55 | | UINT16 timer; |
| 56 | | UINT32 clocks; |
| 57 | | union |
| 58 | | { |
| 59 | | #ifdef LSB_FIRST |
| 60 | | struct { UINT8 l, h; } b; |
| 61 | | #else |
| 62 | | struct { UINT8 h, l; } b; |
| 63 | | #endif |
| 64 | | UINT16 w; |
| 65 | | } counter; |
| 66 | | }; |
| 67 | | |
| 68 | | struct sh8253_timer_channel |
| 69 | | { |
| 70 | | UINT8 clstate; |
| 71 | | UINT8 enable; |
| 72 | | UINT16 count; |
| 73 | | UINT32 step; |
| 74 | | UINT32 fraction; |
| 75 | | }; |
| 76 | | |
| 77 | | struct exidy_sound_state |
| 78 | | { |
| 79 | | cpu_device *m_maincpu; |
| 80 | | |
| 81 | | /* IRQ variable */ |
| 82 | | UINT8 m_riot_irq_state; |
| 83 | | |
| 84 | | /* 6532 variables */ |
| 85 | | riot6532_device *m_riot; |
| 86 | | |
| 87 | | struct sh6840_timer_channel m_sh6840_timer[3]; |
| 88 | | INT16 m_sh6840_volume[3]; |
| 89 | | UINT8 m_sh6840_MSB_latch; |
| 90 | | UINT8 m_sh6840_LSB_latch; |
| 91 | | UINT8 m_sh6840_LFSR_oldxor; |
| 92 | | UINT32 m_sh6840_LFSR_0; |
| 93 | | UINT32 m_sh6840_LFSR_1; |
| 94 | | UINT32 m_sh6840_LFSR_2; |
| 95 | | UINT32 m_sh6840_LFSR_3; |
| 96 | | UINT32 m_sh6840_clocks_per_sample; |
| 97 | | UINT32 m_sh6840_clock_count; |
| 98 | | |
| 99 | | UINT8 m_sfxctrl; |
| 100 | | |
| 101 | | /* 8253 variables */ |
| 102 | | struct sh8253_timer_channel m_sh8253_timer[3]; |
| 103 | | int m_has_sh8253; |
| 104 | | |
| 105 | | /* 5220/CVSD variables */ |
| 106 | | hc55516_device *m_cvsd; |
| 107 | | tms5220_device *m_tms; |
| 108 | | pia6821_device *m_pia1; |
| 109 | | |
| 110 | | /* sound streaming variables */ |
| 111 | | sound_stream *m_stream; |
| 112 | | double m_freq_to_step; |
| 113 | | |
| 114 | | UINT8 m_victory_sound_response_ack_clk; /* 7474 @ F4 */ |
| 115 | | }; |
| 116 | | |
| 117 | | |
| 118 | | INLINE exidy_sound_state *get_safe_token(device_t *device) |
| 119 | | { |
| 120 | | assert(device != NULL); |
| 121 | | assert(device->type() == EXIDY || device->type() == EXIDY_VENTURE || device->type() == EXIDY_VICTORY); |
| 122 | | |
| 123 | | return (exidy_sound_state *)downcast<exidy_sound_device *>(device)->token(); |
| 124 | | } |
| 125 | | |
| 126 | | /************************************* |
| 127 | | * |
| 128 | 39 | * Interrupt generation helper |
| 129 | 40 | * |
| 130 | 41 | *************************************/ |
| 131 | 42 | |
| 132 | | static WRITE_LINE_DEVICE_HANDLER( update_irq_state ) |
| 43 | WRITE_LINE_MEMBER( exidy_sound_device::update_irq_state ) |
| 133 | 44 | { |
| 134 | | exidy_sound_state *sndstate = get_safe_token(device); |
| 135 | | device->machine().device("audiocpu")->execute().set_input_line(M6502_IRQ_LINE, (sndstate->m_pia1->irq_b_state() | sndstate->m_riot_irq_state) ? ASSERT_LINE : CLEAR_LINE); |
| 45 | machine().device("audiocpu")->execute().set_input_line(M6502_IRQ_LINE, (m_pia1->irq_b_state() | m_riot_irq_state) ? ASSERT_LINE : CLEAR_LINE); |
| 136 | 46 | } |
| 137 | 47 | |
| 138 | 48 | |
| r24635 | r24636 | |
| 198 | 108 | * |
| 199 | 109 | *************************************/ |
| 200 | 110 | |
| 201 | | INLINE int sh6840_update_noise(exidy_sound_state *state, int clocks) |
| 111 | inline int exidy_sound_device::sh6840_update_noise(int clocks) |
| 202 | 112 | { |
| 203 | 113 | UINT32 newxor; |
| 204 | 114 | int noise_clocks = 0; |
| r24635 | r24636 | |
| 212 | 122 | * first we grab new sample, then shift the high bits, |
| 213 | 123 | * then the low ones; finally or in the result and see if we've |
| 214 | 124 | * had a 0->1 transition */ |
| 215 | | newxor = (state->m_sh6840_LFSR_3 ^ state->m_sh6840_LFSR_2) >> 31; /* high bits of 3 and 2 xored is new xor */ |
| 216 | | state->m_sh6840_LFSR_3 <<= 1; |
| 217 | | state->m_sh6840_LFSR_3 |= state->m_sh6840_LFSR_2 >> 31; |
| 218 | | state->m_sh6840_LFSR_2 <<= 1; |
| 219 | | state->m_sh6840_LFSR_2 |= state->m_sh6840_LFSR_1 >> 31; |
| 220 | | state->m_sh6840_LFSR_1 <<= 1; |
| 221 | | state->m_sh6840_LFSR_1 |= state->m_sh6840_LFSR_0 >> 31; |
| 222 | | state->m_sh6840_LFSR_0 <<= 1; |
| 223 | | state->m_sh6840_LFSR_0 |= newxor ^ state->m_sh6840_LFSR_oldxor; |
| 224 | | state->m_sh6840_LFSR_oldxor = newxor; |
| 125 | newxor = (m_sh6840_LFSR_3 ^ m_sh6840_LFSR_2) >> 31; /* high bits of 3 and 2 xored is new xor */ |
| 126 | m_sh6840_LFSR_3 <<= 1; |
| 127 | m_sh6840_LFSR_3 |= m_sh6840_LFSR_2 >> 31; |
| 128 | m_sh6840_LFSR_2 <<= 1; |
| 129 | m_sh6840_LFSR_2 |= m_sh6840_LFSR_1 >> 31; |
| 130 | m_sh6840_LFSR_1 <<= 1; |
| 131 | m_sh6840_LFSR_1 |= m_sh6840_LFSR_0 >> 31; |
| 132 | m_sh6840_LFSR_0 <<= 1; |
| 133 | m_sh6840_LFSR_0 |= newxor ^ m_sh6840_LFSR_oldxor; |
| 134 | m_sh6840_LFSR_oldxor = newxor; |
| 225 | 135 | /*printf("LFSR: %4x, %4x, %4x, %4x\n", sh6840_LFSR_3, sh6840_LFSR_2, sh6840_LFSR_1, sh6840_LFSR_0);*/ |
| 226 | 136 | /* if we clocked 0->1, that will serve as an external clock */ |
| 227 | | if ((state->m_sh6840_LFSR_2 & 0x03) == 0x01) /* tap is at 96th bit */ |
| 137 | if ((m_sh6840_LFSR_2 & 0x03) == 0x01) /* tap is at 96th bit */ |
| 228 | 138 | { |
| 229 | 139 | noise_clocks++; |
| 230 | 140 | } |
| r24635 | r24636 | |
| 240 | 150 | * |
| 241 | 151 | *************************************/ |
| 242 | 152 | |
| 243 | | static void sh6840_register_state_globals(device_t *device) |
| 153 | void exidy_sound_device::sh6840_register_state_globals() |
| 244 | 154 | { |
| 245 | | exidy_sound_state *state = get_safe_token(device); |
| 246 | | |
| 247 | | device->save_item(NAME(state->m_sh6840_volume)); |
| 248 | | device->save_item(NAME(state->m_sh6840_MSB_latch)); |
| 249 | | device->save_item(NAME(state->m_sh6840_LSB_latch)); |
| 250 | | device->save_item(NAME(state->m_sh6840_LFSR_oldxor)); |
| 251 | | device->save_item(NAME(state->m_sh6840_LFSR_0)); |
| 252 | | device->save_item(NAME(state->m_sh6840_LFSR_1)); |
| 253 | | device->save_item(NAME(state->m_sh6840_LFSR_2)); |
| 254 | | device->save_item(NAME(state->m_sh6840_LFSR_3)); |
| 255 | | device->save_item(NAME(state->m_sh6840_clock_count)); |
| 256 | | device->save_item(NAME(state->m_sfxctrl)); |
| 257 | | device->save_item(NAME(state->m_sh6840_timer[0].cr)); |
| 258 | | device->save_item(NAME(state->m_sh6840_timer[0].state)); |
| 259 | | device->save_item(NAME(state->m_sh6840_timer[0].leftovers)); |
| 260 | | device->save_item(NAME(state->m_sh6840_timer[0].timer)); |
| 261 | | device->save_item(NAME(state->m_sh6840_timer[0].clocks)); |
| 262 | | device->save_item(NAME(state->m_sh6840_timer[0].counter.w)); |
| 263 | | device->save_item(NAME(state->m_sh6840_timer[1].cr)); |
| 264 | | device->save_item(NAME(state->m_sh6840_timer[1].state)); |
| 265 | | device->save_item(NAME(state->m_sh6840_timer[1].leftovers)); |
| 266 | | device->save_item(NAME(state->m_sh6840_timer[1].timer)); |
| 267 | | device->save_item(NAME(state->m_sh6840_timer[1].clocks)); |
| 268 | | device->save_item(NAME(state->m_sh6840_timer[1].counter.w)); |
| 269 | | device->save_item(NAME(state->m_sh6840_timer[2].cr)); |
| 270 | | device->save_item(NAME(state->m_sh6840_timer[2].state)); |
| 271 | | device->save_item(NAME(state->m_sh6840_timer[2].leftovers)); |
| 272 | | device->save_item(NAME(state->m_sh6840_timer[2].timer)); |
| 273 | | device->save_item(NAME(state->m_sh6840_timer[2].clocks)); |
| 274 | | device->save_item(NAME(state->m_sh6840_timer[2].counter.w)); |
| 155 | save_item(NAME(m_sh6840_volume)); |
| 156 | save_item(NAME(m_sh6840_MSB_latch)); |
| 157 | save_item(NAME(m_sh6840_LSB_latch)); |
| 158 | save_item(NAME(m_sh6840_LFSR_oldxor)); |
| 159 | save_item(NAME(m_sh6840_LFSR_0)); |
| 160 | save_item(NAME(m_sh6840_LFSR_1)); |
| 161 | save_item(NAME(m_sh6840_LFSR_2)); |
| 162 | save_item(NAME(m_sh6840_LFSR_3)); |
| 163 | save_item(NAME(m_sh6840_clock_count)); |
| 164 | save_item(NAME(m_sfxctrl)); |
| 165 | save_item(NAME(m_sh6840_timer[0].cr)); |
| 166 | save_item(NAME(m_sh6840_timer[0].state)); |
| 167 | save_item(NAME(m_sh6840_timer[0].leftovers)); |
| 168 | save_item(NAME(m_sh6840_timer[0].timer)); |
| 169 | save_item(NAME(m_sh6840_timer[0].clocks)); |
| 170 | save_item(NAME(m_sh6840_timer[0].counter.w)); |
| 171 | save_item(NAME(m_sh6840_timer[1].cr)); |
| 172 | save_item(NAME(m_sh6840_timer[1].state)); |
| 173 | save_item(NAME(m_sh6840_timer[1].leftovers)); |
| 174 | save_item(NAME(m_sh6840_timer[1].timer)); |
| 175 | save_item(NAME(m_sh6840_timer[1].clocks)); |
| 176 | save_item(NAME(m_sh6840_timer[1].counter.w)); |
| 177 | save_item(NAME(m_sh6840_timer[2].cr)); |
| 178 | save_item(NAME(m_sh6840_timer[2].state)); |
| 179 | save_item(NAME(m_sh6840_timer[2].leftovers)); |
| 180 | save_item(NAME(m_sh6840_timer[2].timer)); |
| 181 | save_item(NAME(m_sh6840_timer[2].clocks)); |
| 182 | save_item(NAME(m_sh6840_timer[2].counter.w)); |
| 275 | 183 | } |
| 276 | 184 | |
| 277 | | |
| 278 | | |
| 279 | 185 | /************************************* |
| 280 | 186 | * |
| 281 | | * Core sound generation |
| 187 | * Audio startup routines |
| 282 | 188 | * |
| 283 | 189 | *************************************/ |
| 284 | 190 | |
| 285 | | static STREAM_UPDATE( exidy_stream_update ) |
| 191 | void exidy_sound_device::common_sh_start() |
| 286 | 192 | { |
| 287 | | exidy_sound_state *state = get_safe_token(device); |
| 288 | | struct sh6840_timer_channel *sh6840_timer = state->m_sh6840_timer; |
| 193 | int sample_rate = SH8253_CLOCK; |
| 289 | 194 | |
| 195 | m_sh6840_clocks_per_sample = (int)((double)SH6840_CLOCK / (double)sample_rate * (double)(1 << 24)); |
| 196 | |
| 197 | /* allocate the stream */ |
| 198 | m_stream = machine().sound().stream_alloc(*this, 0, 1, sample_rate, this); |
| 199 | m_maincpu = machine().device<cpu_device>("maincpu"); |
| 200 | |
| 201 | sh6840_register_state_globals(); |
| 202 | } |
| 203 | |
| 204 | const device_type EXIDY = &device_creator<exidy_sound_device>; |
| 205 | |
| 206 | exidy_sound_device::exidy_sound_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) |
| 207 | : device_t(mconfig, EXIDY, "Exidy SFX", tag, owner, clock, "exidy_sfx", __FILE__), |
| 208 | device_sound_interface(mconfig, *this) |
| 209 | { |
| 210 | } |
| 211 | |
| 212 | exidy_sound_device::exidy_sound_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source) |
| 213 | : device_t(mconfig, type, name, tag, owner, clock, shortname, source), |
| 214 | device_sound_interface(mconfig, *this), |
| 215 | m_riot_irq_state(0), |
| 216 | m_stream(NULL), |
| 217 | m_freq_to_step(0), |
| 218 | m_sh6840_MSB_latch(0), |
| 219 | m_sh6840_LSB_latch(0), |
| 220 | m_sh6840_LFSR_oldxor(0), |
| 221 | m_sh6840_LFSR_0(0xffffffff), |
| 222 | m_sh6840_LFSR_1(0xffffffff), |
| 223 | m_sh6840_LFSR_2(0xffffffff), |
| 224 | m_sh6840_LFSR_3(0xffffffff), |
| 225 | m_sh6840_clocks_per_sample(0), |
| 226 | m_sh6840_clock_count(0), |
| 227 | m_sfxctrl(0) |
| 228 | { |
| 229 | } |
| 230 | |
| 231 | //------------------------------------------------- |
| 232 | // device_config_complete - perform any |
| 233 | // operations now that the configuration is |
| 234 | // complete |
| 235 | //------------------------------------------------- |
| 236 | |
| 237 | void exidy_sound_device::device_config_complete() |
| 238 | { |
| 239 | } |
| 240 | |
| 241 | //------------------------------------------------- |
| 242 | // device_start - device-specific startup |
| 243 | //------------------------------------------------- |
| 244 | |
| 245 | void exidy_sound_device::device_start() |
| 246 | { |
| 247 | /* indicate no additional hardware */ |
| 248 | m_has_sh8253 = FALSE; |
| 249 | m_tms = NULL; |
| 250 | m_cvsd = NULL; |
| 251 | |
| 252 | common_sh_start(); |
| 253 | } |
| 254 | |
| 255 | //------------------------------------------------- |
| 256 | // device_reset - device-specific reset |
| 257 | //------------------------------------------------- |
| 258 | |
| 259 | void exidy_sound_device::device_reset() |
| 260 | { |
| 261 | common_sh_reset(); |
| 262 | } |
| 263 | |
| 264 | //------------------------------------------------- |
| 265 | // sound_stream_update - handle a stream update |
| 266 | //------------------------------------------------- |
| 267 | |
| 268 | void exidy_sound_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples) |
| 269 | { |
| 270 | struct sh6840_timer_channel *sh6840_timer = m_sh6840_timer; |
| 271 | |
| 290 | 272 | /* hack to skip the expensive lfsr noise generation unless at least one of the 3 channels actually depends on it being generated */ |
| 291 | 273 | int noisy = ((sh6840_timer[0].cr & sh6840_timer[1].cr & sh6840_timer[2].cr & 0x02) == 0); |
| 292 | 274 | stream_sample_t *buffer = outputs[0]; |
| r24635 | r24636 | |
| 301 | 283 | INT16 sample = 0; |
| 302 | 284 | |
| 303 | 285 | /* determine how many 6840 clocks this sample */ |
| 304 | | state->m_sh6840_clock_count += state->m_sh6840_clocks_per_sample; |
| 305 | | clocks_this_sample = state->m_sh6840_clock_count >> 24; |
| 306 | | state->m_sh6840_clock_count &= (1 << 24) - 1; |
| 286 | m_sh6840_clock_count += m_sh6840_clocks_per_sample; |
| 287 | clocks_this_sample = m_sh6840_clock_count >> 24; |
| 288 | m_sh6840_clock_count &= (1 << 24) - 1; |
| 307 | 289 | |
| 308 | 290 | /* skip if nothing enabled */ |
| 309 | 291 | if ((sh6840_timer[0].cr & 0x01) == 0) |
| r24635 | r24636 | |
| 312 | 294 | UINT32 chan0_clocks; |
| 313 | 295 | |
| 314 | 296 | /* generate E-clocked noise if configured to do so */ |
| 315 | | if (noisy && !(state->m_sfxctrl & 0x01)) |
| 316 | | noise_clocks_this_sample = sh6840_update_noise(state, clocks_this_sample); |
| 297 | if (noisy && !(m_sfxctrl & 0x01)) |
| 298 | noise_clocks_this_sample = sh6840_update_noise(clocks_this_sample); |
| 317 | 299 | |
| 318 | 300 | /* handle timer 0 if enabled */ |
| 319 | 301 | t = &sh6840_timer[0]; |
| 320 | 302 | chan0_clocks = t->clocks; |
| 321 | 303 | clocks = (t->cr & 0x02) ? clocks_this_sample : noise_clocks_this_sample; |
| 322 | 304 | sh6840_apply_clock(t, clocks); |
| 323 | | if (t->state && !(state->m_sfxctrl & 0x02) && (t->cr & 0x80)) |
| 324 | | sample += state->m_sh6840_volume[0]; |
| 305 | if (t->state && !(m_sfxctrl & 0x02) && (t->cr & 0x80)) |
| 306 | sample += m_sh6840_volume[0]; |
| 325 | 307 | |
| 326 | 308 | /* generate channel 0-clocked noise if configured to do so */ |
| 327 | | if (noisy && (state->m_sfxctrl & 0x01)) |
| 328 | | noise_clocks_this_sample = sh6840_update_noise(state, t->clocks - chan0_clocks); |
| 309 | if (noisy && (m_sfxctrl & 0x01)) |
| 310 | noise_clocks_this_sample = sh6840_update_noise(t->clocks - chan0_clocks); |
| 329 | 311 | |
| 330 | 312 | /* handle timer 1 if enabled */ |
| 331 | 313 | t = &sh6840_timer[1]; |
| 332 | 314 | clocks = (t->cr & 0x02) ? clocks_this_sample : noise_clocks_this_sample; |
| 333 | 315 | sh6840_apply_clock(t, clocks); |
| 334 | 316 | if (t->state && (t->cr & 0x80)) |
| 335 | | sample += state->m_sh6840_volume[1]; |
| 317 | sample += m_sh6840_volume[1]; |
| 336 | 318 | |
| 337 | 319 | /* handle timer 2 if enabled */ |
| 338 | 320 | t = &sh6840_timer[2]; |
| r24635 | r24636 | |
| 346 | 328 | } |
| 347 | 329 | sh6840_apply_clock(t, clocks); |
| 348 | 330 | if (t->state && (t->cr & 0x80)) |
| 349 | | sample += state->m_sh6840_volume[2]; |
| 331 | sample += m_sh6840_volume[2]; |
| 350 | 332 | } |
| 351 | 333 | |
| 352 | 334 | /* music (if present) */ |
| 353 | | if (state->m_has_sh8253) |
| 335 | if (m_has_sh8253) |
| 354 | 336 | { |
| 355 | 337 | /* music channel 0 */ |
| 356 | | c = &state->m_sh8253_timer[0]; |
| 338 | c = &m_sh8253_timer[0]; |
| 357 | 339 | if (c->enable) |
| 358 | 340 | { |
| 359 | 341 | c->fraction += c->step; |
| r24635 | r24636 | |
| 362 | 344 | } |
| 363 | 345 | |
| 364 | 346 | /* music channel 1 */ |
| 365 | | c = &state->m_sh8253_timer[1]; |
| 347 | c = &m_sh8253_timer[1]; |
| 366 | 348 | if (c->enable) |
| 367 | 349 | { |
| 368 | 350 | c->fraction += c->step; |
| r24635 | r24636 | |
| 371 | 353 | } |
| 372 | 354 | |
| 373 | 355 | /* music channel 2 */ |
| 374 | | c = &state->m_sh8253_timer[2]; |
| 356 | c = &m_sh8253_timer[2]; |
| 375 | 357 | if (c->enable) |
| 376 | 358 | { |
| 377 | 359 | c->fraction += c->step; |
| r24635 | r24636 | |
| 389 | 371 | |
| 390 | 372 | /************************************* |
| 391 | 373 | * |
| 392 | | * Audio startup routines |
| 393 | | * |
| 394 | | *************************************/ |
| 395 | | |
| 396 | | static DEVICE_START( common_sh_start ) |
| 397 | | { |
| 398 | | exidy_sound_state *state = get_safe_token(device); |
| 399 | | int sample_rate = SH8253_CLOCK; |
| 400 | | |
| 401 | | state->m_sh6840_clocks_per_sample = (int)((double)SH6840_CLOCK / (double)sample_rate * (double)(1 << 24)); |
| 402 | | |
| 403 | | /* allocate the stream */ |
| 404 | | state->m_stream = device->machine().sound().stream_alloc(*device, 0, 1, sample_rate, NULL, exidy_stream_update); |
| 405 | | state->m_maincpu = device->machine().device<cpu_device>("maincpu"); |
| 406 | | |
| 407 | | sh6840_register_state_globals(device); |
| 408 | | } |
| 409 | | |
| 410 | | static DEVICE_START( exidy_sound ) |
| 411 | | { |
| 412 | | exidy_sound_state *state = get_safe_token(device); |
| 413 | | |
| 414 | | /* indicate no additional hardware */ |
| 415 | | state->m_has_sh8253 = FALSE; |
| 416 | | state->m_tms = NULL; |
| 417 | | state->m_cvsd = NULL; |
| 418 | | |
| 419 | | DEVICE_START_CALL(common_sh_start); |
| 420 | | } |
| 421 | | |
| 422 | | const device_type EXIDY = &device_creator<exidy_sound_device>; |
| 423 | | |
| 424 | | exidy_sound_device::exidy_sound_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) |
| 425 | | : device_t(mconfig, EXIDY, "Exidy SFX", tag, owner, clock, "exidy_sfx", __FILE__), |
| 426 | | device_sound_interface(mconfig, *this) |
| 427 | | { |
| 428 | | m_token = global_alloc_clear(exidy_sound_state); |
| 429 | | } |
| 430 | | |
| 431 | | exidy_sound_device::exidy_sound_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source) |
| 432 | | : device_t(mconfig, type, name, tag, owner, clock, shortname, source), |
| 433 | | device_sound_interface(mconfig, *this) |
| 434 | | { |
| 435 | | m_token = global_alloc_clear(exidy_sound_state); |
| 436 | | } |
| 437 | | |
| 438 | | //------------------------------------------------- |
| 439 | | // device_config_complete - perform any |
| 440 | | // operations now that the configuration is |
| 441 | | // complete |
| 442 | | //------------------------------------------------- |
| 443 | | |
| 444 | | void exidy_sound_device::device_config_complete() |
| 445 | | { |
| 446 | | } |
| 447 | | |
| 448 | | //------------------------------------------------- |
| 449 | | // device_start - device-specific startup |
| 450 | | //------------------------------------------------- |
| 451 | | |
| 452 | | void exidy_sound_device::device_start() |
| 453 | | { |
| 454 | | DEVICE_START_NAME( exidy_sound )(this); |
| 455 | | } |
| 456 | | |
| 457 | | //------------------------------------------------- |
| 458 | | // device_reset - device-specific reset |
| 459 | | //------------------------------------------------- |
| 460 | | |
| 461 | | static DEVICE_RESET( exidy_sound ); |
| 462 | | |
| 463 | | void exidy_sound_device::device_reset() |
| 464 | | { |
| 465 | | DEVICE_RESET_NAME( exidy_sound )(this); |
| 466 | | } |
| 467 | | |
| 468 | | //------------------------------------------------- |
| 469 | | // sound_stream_update - handle a stream update |
| 470 | | //------------------------------------------------- |
| 471 | | |
| 472 | | void exidy_sound_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples) |
| 473 | | { |
| 474 | | // should never get here |
| 475 | | fatalerror("sound_stream_update called; not applicable to legacy sound devices\n"); |
| 476 | | } |
| 477 | | |
| 478 | | |
| 479 | | |
| 480 | | /************************************* |
| 481 | | * |
| 482 | 374 | * Audio reset routines |
| 483 | 375 | * |
| 484 | 376 | *************************************/ |
| 485 | 377 | |
| 486 | | static DEVICE_RESET( common_sh_reset ) |
| 378 | void exidy_sound_device::common_sh_reset() |
| 487 | 379 | { |
| 488 | | exidy_sound_state *state = get_safe_token(device); |
| 489 | | |
| 490 | 380 | /* 6840 */ |
| 491 | | memset(state->m_sh6840_timer, 0, sizeof(state->m_sh6840_timer)); |
| 492 | | state->m_sh6840_MSB_latch = 0; |
| 493 | | state->m_sh6840_LSB_latch = 0; |
| 494 | | state->m_sh6840_volume[0] = 0; |
| 495 | | state->m_sh6840_volume[1] = 0; |
| 496 | | state->m_sh6840_volume[2] = 0; |
| 497 | | state->m_sh6840_clock_count = 0; |
| 498 | | state->m_sfxctrl = 0; |
| 381 | memset(m_sh6840_timer, 0, sizeof(m_sh6840_timer)); |
| 382 | m_sh6840_MSB_latch = 0; |
| 383 | m_sh6840_LSB_latch = 0; |
| 384 | m_sh6840_volume[0] = 0; |
| 385 | m_sh6840_volume[1] = 0; |
| 386 | m_sh6840_volume[2] = 0; |
| 387 | m_sh6840_clock_count = 0; |
| 388 | m_sfxctrl = 0; |
| 499 | 389 | |
| 500 | 390 | /* LFSR */ |
| 501 | | state->m_sh6840_LFSR_oldxor = 0; |
| 502 | | state->m_sh6840_LFSR_0 = 0xffffffff; |
| 503 | | state->m_sh6840_LFSR_1 = 0xffffffff; |
| 504 | | state->m_sh6840_LFSR_2 = 0xffffffff; |
| 505 | | state->m_sh6840_LFSR_3 = 0xffffffff; |
| 391 | m_sh6840_LFSR_oldxor = 0; |
| 392 | m_sh6840_LFSR_0 = 0xffffffff; |
| 393 | m_sh6840_LFSR_1 = 0xffffffff; |
| 394 | m_sh6840_LFSR_2 = 0xffffffff; |
| 395 | m_sh6840_LFSR_3 = 0xffffffff; |
| 506 | 396 | } |
| 507 | 397 | |
| 508 | | static DEVICE_RESET( exidy_sound ) |
| 509 | | { |
| 510 | | DEVICE_RESET_CALL(common_sh_reset); |
| 511 | | } |
| 512 | 398 | |
| 513 | | |
| 514 | 399 | /************************************* |
| 515 | 400 | * |
| 516 | 401 | * 6532 interface |
| 517 | 402 | * |
| 518 | 403 | *************************************/ |
| 519 | 404 | |
| 520 | | static void r6532_irq(device_t *device, int state) |
| 405 | void exidy_sound_device::r6532_irq(int state) |
| 521 | 406 | { |
| 522 | | exidy_sound_state *sndstate = get_safe_token(device); |
| 523 | | sndstate->m_riot_irq_state = (state == ASSERT_LINE) ? 1 : 0; |
| 524 | | update_irq_state(device, 0); |
| 407 | m_riot_irq_state = (state == ASSERT_LINE) ? 1 : 0; |
| 408 | update_irq_state(0); |
| 525 | 409 | } |
| 526 | 410 | |
| 527 | 411 | |
| 528 | | static WRITE8_DEVICE_HANDLER( r6532_porta_w ) |
| 412 | WRITE8_MEMBER( exidy_sound_device::r6532_porta_w ) |
| 529 | 413 | { |
| 530 | | exidy_sound_state *state = get_safe_token(device); |
| 531 | | if (state->m_cvsd != NULL) |
| 414 | if (m_cvsd != NULL) |
| 532 | 415 | space.machine().device("cvsdcpu")->execute().set_input_line(INPUT_LINE_RESET, (data & 0x10) ? CLEAR_LINE : ASSERT_LINE); |
| 533 | 416 | |
| 534 | | if (state->m_tms != NULL) |
| 417 | if (m_tms != NULL) |
| 535 | 418 | { |
| 536 | | logerror("(%f)%s:TMS5220 data write = %02X\n", space.machine().time().as_double(), space.machine().describe_context(), state->m_riot->porta_out_get()); |
| 537 | | state->m_tms->data_w(space, 0, data); |
| 419 | logerror("(%f)%s:TMS5220 data write = %02X\n", space.machine().time().as_double(), space.machine().describe_context(), m_riot->porta_out_get()); |
| 420 | m_tms->data_w(space, 0, data); |
| 538 | 421 | } |
| 539 | 422 | } |
| 540 | 423 | |
| 541 | | static READ8_DEVICE_HANDLER( r6532_porta_r ) |
| 424 | READ8_MEMBER( exidy_sound_device::r6532_porta_r ) |
| 542 | 425 | { |
| 543 | | exidy_sound_state *state = get_safe_token(device); |
| 544 | | if (state->m_tms != NULL) |
| 426 | if (m_tms != NULL) |
| 545 | 427 | { |
| 546 | | logerror("(%f)%s:TMS5220 status read = %02X\n", space.machine().time().as_double(), space.machine().describe_context(), state->m_tms->status_r(space, 0)); |
| 547 | | return state->m_tms->status_r(space, 0); |
| 428 | logerror("(%f)%s:TMS5220 status read = %02X\n", space.machine().time().as_double(), space.machine().describe_context(), m_tms->status_r(space, 0)); |
| 429 | return m_tms->status_r(space, 0); |
| 548 | 430 | } |
| 549 | 431 | else |
| 550 | 432 | return 0xff; |
| 551 | 433 | } |
| 552 | 434 | |
| 553 | | static WRITE8_DEVICE_HANDLER( r6532_portb_w ) |
| 435 | WRITE8_MEMBER( exidy_sound_device::r6532_portb_w ) |
| 554 | 436 | { |
| 555 | | exidy_sound_state *state = get_safe_token(device); |
| 556 | | if (state->m_tms != NULL) |
| 437 | if (m_tms != NULL) |
| 557 | 438 | { |
| 558 | | state->m_tms->rsq_w(data & 0x01); |
| 559 | | state->m_tms->wsq_w((data >> 1) & 0x01); |
| 439 | m_tms->rsq_w(data & 0x01); |
| 440 | m_tms->wsq_w((data >> 1) & 0x01); |
| 560 | 441 | } |
| 561 | 442 | } |
| 562 | 443 | |
| 563 | 444 | |
| 564 | | static READ8_DEVICE_HANDLER( r6532_portb_r ) |
| 445 | READ8_MEMBER( exidy_sound_device::r6532_portb_r ) |
| 565 | 446 | { |
| 566 | | exidy_sound_state *state = get_safe_token(device); |
| 567 | | UINT8 newdata = state->m_riot->portb_in_get(); |
| 568 | | if (state->m_tms != NULL) |
| 447 | UINT8 newdata = m_riot->portb_in_get(); |
| 448 | if (m_tms != NULL) |
| 569 | 449 | { |
| 570 | 450 | newdata &= ~0x0c; |
| 571 | | if (state->m_tms->readyq_r()) newdata |= 0x04; |
| 572 | | if (state->m_tms->intq_r()) newdata |= 0x08; |
| 451 | if (m_tms->readyq_r()) newdata |= 0x04; |
| 452 | if (m_tms->intq_r()) newdata |= 0x08; |
| 573 | 453 | } |
| 574 | 454 | return newdata; |
| 575 | 455 | } |
| r24635 | r24636 | |
| 577 | 457 | |
| 578 | 458 | static const riot6532_interface r6532_interface = |
| 579 | 459 | { |
| 580 | | DEVCB_DEVICE_HANDLER("custom", r6532_porta_r), /* port A read handler */ |
| 581 | | DEVCB_DEVICE_HANDLER("custom", r6532_portb_r), /* port B read handler */ |
| 582 | | DEVCB_DEVICE_HANDLER("custom", r6532_porta_w), /* port A write handler */ |
| 583 | | DEVCB_DEVICE_HANDLER("custom", r6532_portb_w), /* port B write handler */ |
| 584 | | DEVCB_DEVICE_LINE("custom", r6532_irq) /* IRQ callback */ |
| 460 | DEVCB_DEVICE_MEMBER("custom", exidy_sound_device, r6532_porta_r), /* port A read handler */ |
| 461 | DEVCB_DEVICE_MEMBER("custom", exidy_sound_device, r6532_portb_r), /* port B read handler */ |
| 462 | DEVCB_DEVICE_MEMBER("custom", exidy_sound_device, r6532_porta_w), /* port A write handler */ |
| 463 | DEVCB_DEVICE_MEMBER("custom", exidy_sound_device, r6532_portb_w), /* port B write handler */ |
| 464 | DEVCB_DEVICE_LINE_MEMBER("custom", exidy_sound_device, r6532_irq) /* IRQ callback */ |
| 585 | 465 | }; |
| 586 | 466 | |
| 587 | 467 | |
| r24635 | r24636 | |
| 593 | 473 | *************************************/ |
| 594 | 474 | |
| 595 | 475 | |
| 596 | | static void sh8253_register_state_globals(device_t *device) |
| 476 | void exidy_sound_device::sh8253_register_state_globals() |
| 597 | 477 | { |
| 598 | | exidy_sound_state *state = get_safe_token(device); |
| 599 | | |
| 600 | | device->save_item(NAME(state->m_sh8253_timer[0].clstate)); |
| 601 | | device->save_item(NAME(state->m_sh8253_timer[0].enable)); |
| 602 | | device->save_item(NAME(state->m_sh8253_timer[0].count)); |
| 603 | | device->save_item(NAME(state->m_sh8253_timer[0].step)); |
| 604 | | device->save_item(NAME(state->m_sh8253_timer[0].fraction)); |
| 605 | | device->save_item(NAME(state->m_sh8253_timer[1].clstate)); |
| 606 | | device->save_item(NAME(state->m_sh8253_timer[1].enable)); |
| 607 | | device->save_item(NAME(state->m_sh8253_timer[1].count)); |
| 608 | | device->save_item(NAME(state->m_sh8253_timer[1].step)); |
| 609 | | device->save_item(NAME(state->m_sh8253_timer[1].fraction)); |
| 610 | | device->save_item(NAME(state->m_sh8253_timer[2].clstate)); |
| 611 | | device->save_item(NAME(state->m_sh8253_timer[2].enable)); |
| 612 | | device->save_item(NAME(state->m_sh8253_timer[2].count)); |
| 613 | | device->save_item(NAME(state->m_sh8253_timer[2].step)); |
| 614 | | device->save_item(NAME(state->m_sh8253_timer[2].fraction)); |
| 478 | save_item(NAME(m_sh8253_timer[0].clstate)); |
| 479 | save_item(NAME(m_sh8253_timer[0].enable)); |
| 480 | save_item(NAME(m_sh8253_timer[0].count)); |
| 481 | save_item(NAME(m_sh8253_timer[0].step)); |
| 482 | save_item(NAME(m_sh8253_timer[0].fraction)); |
| 483 | save_item(NAME(m_sh8253_timer[1].clstate)); |
| 484 | save_item(NAME(m_sh8253_timer[1].enable)); |
| 485 | save_item(NAME(m_sh8253_timer[1].count)); |
| 486 | save_item(NAME(m_sh8253_timer[1].step)); |
| 487 | save_item(NAME(m_sh8253_timer[1].fraction)); |
| 488 | save_item(NAME(m_sh8253_timer[2].clstate)); |
| 489 | save_item(NAME(m_sh8253_timer[2].enable)); |
| 490 | save_item(NAME(m_sh8253_timer[2].count)); |
| 491 | save_item(NAME(m_sh8253_timer[2].step)); |
| 492 | save_item(NAME(m_sh8253_timer[2].fraction)); |
| 615 | 493 | } |
| 616 | 494 | |
| 617 | 495 | /************************************* |
| r24635 | r24636 | |
| 620 | 498 | * |
| 621 | 499 | *************************************/ |
| 622 | 500 | |
| 623 | | static WRITE8_DEVICE_HANDLER( exidy_sh8253_w ) |
| 501 | WRITE8_MEMBER( exidy_sound_device::sh8253_w ) |
| 624 | 502 | { |
| 625 | | exidy_sound_state *state = get_safe_token(device); |
| 626 | 503 | int chan; |
| 627 | 504 | |
| 628 | | state->m_stream->update(); |
| 505 | m_stream->update(); |
| 629 | 506 | |
| 630 | 507 | switch (offset) |
| 631 | 508 | { |
| r24635 | r24636 | |
| 633 | 510 | case 1: |
| 634 | 511 | case 2: |
| 635 | 512 | chan = offset; |
| 636 | | if (!state->m_sh8253_timer[chan].clstate) |
| 513 | if (!m_sh8253_timer[chan].clstate) |
| 637 | 514 | { |
| 638 | | state->m_sh8253_timer[chan].clstate = 1; |
| 639 | | state->m_sh8253_timer[chan].count = (state->m_sh8253_timer[chan].count & 0xff00) | (data & 0x00ff); |
| 515 | m_sh8253_timer[chan].clstate = 1; |
| 516 | m_sh8253_timer[chan].count = (m_sh8253_timer[chan].count & 0xff00) | (data & 0x00ff); |
| 640 | 517 | } |
| 641 | 518 | else |
| 642 | 519 | { |
| 643 | | state->m_sh8253_timer[chan].clstate = 0; |
| 644 | | state->m_sh8253_timer[chan].count = (state->m_sh8253_timer[chan].count & 0x00ff) | ((data << 8) & 0xff00); |
| 645 | | if (state->m_sh8253_timer[chan].count) |
| 646 | | state->m_sh8253_timer[chan].step = state->m_freq_to_step * (double)SH8253_CLOCK / (double)state->m_sh8253_timer[chan].count; |
| 520 | m_sh8253_timer[chan].clstate = 0; |
| 521 | m_sh8253_timer[chan].count = (m_sh8253_timer[chan].count & 0x00ff) | ((data << 8) & 0xff00); |
| 522 | if (m_sh8253_timer[chan].count) |
| 523 | m_sh8253_timer[chan].step = m_freq_to_step * (double)SH8253_CLOCK / (double)m_sh8253_timer[chan].count; |
| 647 | 524 | else |
| 648 | | state->m_sh8253_timer[chan].step = 0; |
| 525 | m_sh8253_timer[chan].step = 0; |
| 649 | 526 | } |
| 650 | 527 | break; |
| 651 | 528 | |
| 652 | 529 | case 3: |
| 653 | 530 | chan = (data & 0xc0) >> 6; |
| 654 | | state->m_sh8253_timer[chan].enable = ((data & 0x0e) != 0); |
| 531 | m_sh8253_timer[chan].enable = ((data & 0x0e) != 0); |
| 655 | 532 | break; |
| 656 | 533 | } |
| 657 | 534 | } |
| 658 | 535 | |
| 659 | 536 | |
| 660 | | static READ8_DEVICE_HANDLER( exidy_sh8253_r ) |
| 537 | READ8_MEMBER( exidy_sound_device::sh8253_r ) |
| 661 | 538 | { |
| 662 | 539 | logerror("8253(R): %x\n",offset); |
| 663 | 540 | |
| r24635 | r24636 | |
| 672 | 549 | * |
| 673 | 550 | *************************************/ |
| 674 | 551 | |
| 675 | | READ8_DEVICE_HANDLER( exidy_sh6840_r ) |
| 552 | READ8_MEMBER( exidy_sound_device::sh6840_r ) |
| 676 | 553 | { |
| 677 | | exidy_sound_state *state = get_safe_token(device); |
| 678 | | |
| 679 | 554 | /* force an update of the stream */ |
| 680 | | state->m_stream->update(); |
| 555 | m_stream->update(); |
| 681 | 556 | |
| 682 | 557 | switch (offset) |
| 683 | 558 | { |
| r24635 | r24636 | |
| 686 | 561 | return 0; |
| 687 | 562 | /* offset 1 reads the status register: bits 2 1 0 correspond to ints on channels 2,1,0, and bit 7 is an 'OR' of bits 2,1,0 */ |
| 688 | 563 | case 1: |
| 689 | | logerror("%04X:exidy_sh6840_r - unexpected read, status register is TODO!\n", state->m_maincpu->pc()); |
| 564 | logerror("%04X:exidy_sh6840_r - unexpected read, status register is TODO!\n", m_maincpu->pc()); |
| 690 | 565 | return 0; |
| 691 | 566 | /* offsets 2,4,6 read channel 0,1,2 MSBs and latch the LSB*/ |
| 692 | 567 | case 2: case 4: case 6: |
| 693 | | state->m_sh6840_LSB_latch = state->m_sh6840_timer[((offset>>1)-1)].counter.b.l; |
| 694 | | return state->m_sh6840_timer[((offset>>1)-1)].counter.b.h; |
| 568 | m_sh6840_LSB_latch = m_sh6840_timer[((offset>>1)-1)].counter.b.l; |
| 569 | return m_sh6840_timer[((offset>>1)-1)].counter.b.h; |
| 695 | 570 | /* offsets 3,5,7 read the LSB latch*/ |
| 696 | 571 | default: /* case 3,5,7 */ |
| 697 | | return state->m_sh6840_LSB_latch; |
| 572 | return m_sh6840_LSB_latch; |
| 698 | 573 | } |
| 699 | 574 | } |
| 700 | 575 | |
| 701 | 576 | |
| 702 | | WRITE8_DEVICE_HANDLER( exidy_sh6840_w ) |
| 577 | WRITE8_MEMBER( exidy_sound_device::sh6840_w ) |
| 703 | 578 | { |
| 704 | | exidy_sound_state *state = get_safe_token(device); |
| 705 | | struct sh6840_timer_channel *sh6840_timer = state->m_sh6840_timer; |
| 579 | struct sh6840_timer_channel *sh6840_timer = m_sh6840_timer; |
| 706 | 580 | |
| 707 | 581 | /* force an update of the stream */ |
| 708 | | state->m_stream->update(); |
| 582 | m_stream->update(); |
| 709 | 583 | |
| 710 | 584 | switch (offset) |
| 711 | 585 | { |
| r24635 | r24636 | |
| 734 | 608 | case 2: |
| 735 | 609 | case 4: |
| 736 | 610 | case 6: |
| 737 | | state->m_sh6840_MSB_latch = data; |
| 611 | m_sh6840_MSB_latch = data; |
| 738 | 612 | break; |
| 739 | 613 | |
| 740 | 614 | /* offsets 3/5/7 write to the LSB controls */ |
| r24635 | r24636 | |
| 744 | 618 | { |
| 745 | 619 | /* latch the timer value */ |
| 746 | 620 | int ch = (offset - 3) / 2; |
| 747 | | sh6840_timer[ch].timer = (state->m_sh6840_MSB_latch << 8) | (data & 0xff); |
| 621 | sh6840_timer[ch].timer = (m_sh6840_MSB_latch << 8) | (data & 0xff); |
| 748 | 622 | |
| 749 | 623 | /* if CR4 is clear, the value is loaded immediately */ |
| 750 | 624 | if (!(sh6840_timer[ch].cr & 0x10)) |
| r24635 | r24636 | |
| 762 | 636 | * |
| 763 | 637 | *************************************/ |
| 764 | 638 | |
| 765 | | WRITE8_DEVICE_HANDLER( exidy_sfxctrl_w ) |
| 639 | WRITE8_MEMBER( exidy_sound_device::sfxctrl_w ) |
| 766 | 640 | { |
| 767 | | exidy_sound_state *state = get_safe_token(device); |
| 641 | m_stream->update(); |
| 768 | 642 | |
| 769 | | state->m_stream->update(); |
| 770 | | |
| 771 | 643 | switch (offset) |
| 772 | 644 | { |
| 773 | 645 | case 0: |
| 774 | | state->m_sfxctrl = data; |
| 646 | m_sfxctrl = data; |
| 775 | 647 | break; |
| 776 | 648 | |
| 777 | 649 | case 1: |
| 778 | 650 | case 2: |
| 779 | 651 | case 3: |
| 780 | | state->m_sh6840_volume[offset - 1] = ((data & 7) * BASE_VOLUME) / 7; |
| 652 | m_sh6840_volume[offset - 1] = ((data & 7) * BASE_VOLUME) / 7; |
| 781 | 653 | break; |
| 782 | 654 | } |
| 783 | 655 | } |
| r24635 | r24636 | |
| 790 | 662 | * |
| 791 | 663 | *************************************/ |
| 792 | 664 | |
| 793 | | static WRITE8_DEVICE_HANDLER( exidy_sound_filter_w ) |
| 665 | WRITE8_MEMBER( venture_sound_device::filter_w ) |
| 794 | 666 | { |
| 795 | 667 | logerror("exidy_sound_filter_w = %02X\n", data); |
| 796 | 668 | } |
| r24635 | r24636 | |
| 833 | 705 | DEVCB_DEVICE_LINE_MEMBER("pia0", pia6821_device, cb1_w), /* line CA2 out */ |
| 834 | 706 | DEVCB_DEVICE_LINE_MEMBER("pia0", pia6821_device, ca1_w), /* port CB2 out */ |
| 835 | 707 | DEVCB_NULL, /* IRQA */ |
| 836 | | DEVCB_DEVICE_LINE("custom", update_irq_state) /* IRQB */ |
| 708 | DEVCB_DEVICE_LINE_MEMBER("custom", exidy_sound_device, update_irq_state) /* IRQB */ |
| 837 | 709 | }; |
| 838 | 710 | |
| 839 | 711 | |
| 840 | | static DEVICE_START( venture_common_sh_start ) |
| 841 | | { |
| 842 | | exidy_sound_state *state = get_safe_token(device); |
| 843 | | running_machine &machine = device->machine(); |
| 844 | | |
| 845 | | DEVICE_START_CALL(common_sh_start); |
| 846 | | |
| 847 | | state->m_riot = machine.device<riot6532_device>("riot"); |
| 848 | | |
| 849 | | state->m_has_sh8253 = TRUE; |
| 850 | | state->m_tms = NULL; |
| 851 | | state->m_pia1 = device->machine().device<pia6821_device>("pia1"); |
| 852 | | |
| 853 | | /* determine which sound hardware is installed */ |
| 854 | | state->m_cvsd = device->machine().device<hc55516_device>("cvsd"); |
| 855 | | |
| 856 | | /* 8253 */ |
| 857 | | state->m_freq_to_step = (double)(1 << 24) / (double)SH8253_CLOCK; |
| 858 | | |
| 859 | | device->save_item(NAME(state->m_riot_irq_state)); |
| 860 | | sh8253_register_state_globals(device); |
| 861 | | } |
| 862 | | |
| 863 | | |
| 864 | | static DEVICE_START( venture_sound ) |
| 865 | | { |
| 866 | | DEVICE_START_CALL(venture_common_sh_start); |
| 867 | | } |
| 868 | | |
| 869 | | |
| 870 | | static DEVICE_RESET( venture_sound ) |
| 871 | | { |
| 872 | | exidy_sound_state *state = get_safe_token(device); |
| 873 | | |
| 874 | | DEVICE_RESET_CALL(common_sh_reset); |
| 875 | | |
| 876 | | /* PIA */ |
| 877 | | device->machine().device("pia0")->reset(); |
| 878 | | device->machine().device("pia1")->reset(); |
| 879 | | |
| 880 | | /* 6532 */ |
| 881 | | state->m_riot->reset(); |
| 882 | | |
| 883 | | /* 8253 */ |
| 884 | | memset(state->m_sh8253_timer, 0, sizeof(state->m_sh8253_timer)); |
| 885 | | } |
| 886 | | |
| 887 | | |
| 888 | 712 | const device_type EXIDY_VENTURE = &device_creator<venture_sound_device>; |
| 889 | 713 | |
| 890 | 714 | venture_sound_device::venture_sound_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) |
| r24635 | r24636 | |
| 908 | 732 | |
| 909 | 733 | void venture_sound_device::device_start() |
| 910 | 734 | { |
| 911 | | DEVICE_START_NAME( venture_sound )(this); |
| 735 | common_sh_start(); |
| 736 | |
| 737 | m_riot = machine().device<riot6532_device>("riot"); |
| 738 | |
| 739 | m_has_sh8253 = TRUE; |
| 740 | m_tms = NULL; |
| 741 | m_pia0 = machine().device<pia6821_device>("pia0"); |
| 742 | m_pia1 = machine().device<pia6821_device>("pia1"); |
| 743 | |
| 744 | /* determine which sound hardware is installed */ |
| 745 | m_cvsd = machine().device<hc55516_device>("cvsd"); |
| 746 | |
| 747 | /* 8253 */ |
| 748 | m_freq_to_step = (double)(1 << 24) / (double)SH8253_CLOCK; |
| 749 | |
| 750 | save_item(NAME(m_riot_irq_state)); |
| 751 | sh8253_register_state_globals(); |
| 912 | 752 | } |
| 913 | 753 | |
| 914 | 754 | //------------------------------------------------- |
| r24635 | r24636 | |
| 917 | 757 | |
| 918 | 758 | void venture_sound_device::device_reset() |
| 919 | 759 | { |
| 920 | | DEVICE_RESET_NAME( venture_sound )(this); |
| 760 | common_sh_reset(); |
| 761 | |
| 762 | /* PIA */ |
| 763 | //machine().device("pia0")->reset(); |
| 764 | m_pia0->reset(); |
| 765 | //machine().device("pia1")->reset(); |
| 766 | m_pia1->reset(); |
| 767 | |
| 768 | /* 6532 */ |
| 769 | m_riot->reset(); |
| 770 | |
| 771 | /* 8253 */ |
| 772 | memset(m_sh8253_timer, 0, sizeof(m_sh8253_timer)); |
| 921 | 773 | } |
| 922 | 774 | |
| 923 | 775 | //------------------------------------------------- |
| r24635 | r24636 | |
| 926 | 778 | |
| 927 | 779 | void venture_sound_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples) |
| 928 | 780 | { |
| 929 | | // should never get here |
| 930 | | fatalerror("sound_stream_update called; not applicable to legacy sound devices\n"); |
| 781 | exidy_sound_device::sound_stream_update(stream, inputs, outputs, samples); |
| 931 | 782 | } |
| 932 | 783 | |
| 933 | 784 | |
| r24635 | r24636 | |
| 938 | 789 | AM_RANGE(0x0000, 0x007f) AM_MIRROR(0x0780) AM_RAM |
| 939 | 790 | AM_RANGE(0x0800, 0x087f) AM_MIRROR(0x0780) AM_DEVREADWRITE("riot", riot6532_device, read, write) |
| 940 | 791 | AM_RANGE(0x1000, 0x1003) AM_MIRROR(0x07fc) AM_DEVREADWRITE("pia1", pia6821_device, read, write) |
| 941 | | AM_RANGE(0x1800, 0x1803) AM_MIRROR(0x07fc) AM_DEVREADWRITE_LEGACY("custom", exidy_sh8253_r, exidy_sh8253_w) |
| 942 | | AM_RANGE(0x2000, 0x27ff) AM_DEVWRITE_LEGACY("custom", exidy_sound_filter_w) |
| 943 | | AM_RANGE(0x2800, 0x2807) AM_MIRROR(0x07f8) AM_DEVREADWRITE_LEGACY("custom", exidy_sh6840_r, exidy_sh6840_w) |
| 944 | | AM_RANGE(0x3000, 0x3003) AM_MIRROR(0x07fc) AM_DEVWRITE_LEGACY("custom", exidy_sfxctrl_w) |
| 792 | AM_RANGE(0x1800, 0x1803) AM_MIRROR(0x07fc) AM_DEVREADWRITE("custom", venture_sound_device, sh8253_r, sh8253_w) |
| 793 | AM_RANGE(0x2000, 0x27ff) AM_DEVWRITE("custom", venture_sound_device, filter_w) |
| 794 | AM_RANGE(0x2800, 0x2807) AM_MIRROR(0x07f8) AM_DEVREADWRITE("custom", venture_sound_device, sh6840_r, sh6840_w) |
| 795 | AM_RANGE(0x3000, 0x3003) AM_MIRROR(0x07fc) AM_DEVWRITE("custom", venture_sound_device, sfxctrl_w) |
| 945 | 796 | AM_RANGE(0x5800, 0x7fff) AM_ROM |
| 946 | 797 | ADDRESS_MAP_END |
| 947 | 798 | |
| r24635 | r24636 | |
| 970 | 821 | * |
| 971 | 822 | *************************************/ |
| 972 | 823 | |
| 973 | | static WRITE8_DEVICE_HANDLER( mtrap_voiceio_w ) |
| 824 | WRITE8_MEMBER( venture_sound_device::mtrap_voiceio_w ) |
| 974 | 825 | { |
| 975 | | exidy_sound_state *state = get_safe_token(device); |
| 976 | | |
| 977 | 826 | if (!(offset & 0x10)) |
| 978 | | state->m_cvsd->digit_w(data & 1); |
| 827 | m_cvsd->digit_w(data & 1); |
| 979 | 828 | |
| 980 | 829 | if (!(offset & 0x20)) |
| 981 | | state->m_riot->portb_in_set(data & 1, 0xff); |
| 830 | m_riot->portb_in_set(data & 1, 0xff); |
| 982 | 831 | } |
| 983 | 832 | |
| 984 | 833 | |
| 985 | | static READ8_DEVICE_HANDLER( mtrap_voiceio_r ) |
| 834 | READ8_MEMBER( venture_sound_device::mtrap_voiceio_r ) |
| 986 | 835 | { |
| 987 | | exidy_sound_state *state = get_safe_token(device); |
| 988 | | |
| 989 | 836 | if (!(offset & 0x80)) |
| 990 | 837 | { |
| 991 | | UINT8 porta = state->m_riot->porta_out_get(); |
| 838 | UINT8 porta = m_riot->porta_out_get(); |
| 992 | 839 | UINT8 data = (porta & 0x06) >> 1; |
| 993 | 840 | data |= (porta & 0x01) << 2; |
| 994 | 841 | data |= (porta & 0x08); |
| r24635 | r24636 | |
| 996 | 843 | } |
| 997 | 844 | |
| 998 | 845 | if (!(offset & 0x40)) |
| 999 | | return state->m_cvsd->clock_state_r() << 7; |
| 846 | return m_cvsd->clock_state_r() << 7; |
| 1000 | 847 | |
| 1001 | 848 | return 0; |
| 1002 | 849 | } |
| r24635 | r24636 | |
| 1010 | 857 | |
| 1011 | 858 | static ADDRESS_MAP_START( cvsd_iomap, AS_IO, 8, driver_device ) |
| 1012 | 859 | ADDRESS_MAP_GLOBAL_MASK(0xff) |
| 1013 | | AM_RANGE(0x00, 0xff) AM_DEVREADWRITE_LEGACY("custom", mtrap_voiceio_r, mtrap_voiceio_w) |
| 860 | AM_RANGE(0x00, 0xff) AM_DEVREADWRITE("custom", venture_sound_device, mtrap_voiceio_r, mtrap_voiceio_w) |
| 1014 | 861 | ADDRESS_MAP_END |
| 1015 | 862 | |
| 1016 | 863 | |
| r24635 | r24636 | |
| 1038 | 885 | |
| 1039 | 886 | |
| 1040 | 887 | |
| 1041 | | READ8_DEVICE_HANDLER( victory_sound_response_r ) |
| 888 | READ8_MEMBER( victory_sound_device::response_r ) |
| 1042 | 889 | { |
| 1043 | | exidy_sound_state *state = get_safe_token(device); |
| 1044 | | UINT8 ret = state->m_pia1->b_output(); |
| 890 | UINT8 ret = m_pia1->b_output(); |
| 1045 | 891 | |
| 1046 | | if (VICTORY_LOG_SOUND) logerror("%04X:!!!! Sound response read = %02X\n", state->m_maincpu->pcbase(), ret); |
| 892 | if (VICTORY_LOG_SOUND) logerror("%04X:!!!! Sound response read = %02X\n", m_maincpu->pcbase(), ret); |
| 1047 | 893 | |
| 1048 | | state->m_pia1->cb1_w(0); |
| 894 | m_pia1->cb1_w(0); |
| 1049 | 895 | |
| 1050 | 896 | return ret; |
| 1051 | 897 | } |
| 1052 | 898 | |
| 1053 | 899 | |
| 1054 | | READ8_DEVICE_HANDLER( victory_sound_status_r ) |
| 900 | READ8_MEMBER( victory_sound_device::status_r ) |
| 1055 | 901 | { |
| 1056 | | exidy_sound_state *state = get_safe_token(device); |
| 1057 | | UINT8 ret = (state->m_pia1->ca1_r() << 7) | (state->m_pia1->cb1_r() << 6); |
| 902 | UINT8 ret = (m_pia1->ca1_r() << 7) | (m_pia1->cb1_r() << 6); |
| 1058 | 903 | |
| 1059 | | if (VICTORY_LOG_SOUND) logerror("%04X:!!!! Sound status read = %02X\n", state->m_maincpu->pcbase(), ret); |
| 904 | if (VICTORY_LOG_SOUND) logerror("%04X:!!!! Sound status read = %02X\n", m_maincpu->pcbase(), ret); |
| 1060 | 905 | |
| 1061 | 906 | return ret; |
| 1062 | 907 | } |
| 1063 | 908 | |
| 1064 | 909 | |
| 1065 | | static TIMER_CALLBACK( delayed_command_w ) |
| 910 | TIMER_CALLBACK_MEMBER( victory_sound_device::delayed_command_w ) |
| 1066 | 911 | { |
| 1067 | | pia6821_device *pia1 = (pia6821_device *)ptr; |
| 1068 | | pia1->set_a_input(param, 0); |
| 1069 | | pia1->ca1_w(0); |
| 912 | m_pia1->set_a_input(param, 0); |
| 913 | m_pia1->ca1_w(0); |
| 1070 | 914 | } |
| 1071 | 915 | |
| 1072 | | WRITE8_DEVICE_HANDLER( victory_sound_command_w ) |
| 916 | WRITE8_MEMBER( victory_sound_device::command_w ) |
| 1073 | 917 | { |
| 1074 | | exidy_sound_state *state = get_safe_token(device); |
| 918 | if (VICTORY_LOG_SOUND) logerror("%04X:!!!! Sound command = %02X\n", m_maincpu->pcbase(), data); |
| 1075 | 919 | |
| 1076 | | if (VICTORY_LOG_SOUND) logerror("%04X:!!!! Sound command = %02X\n", state->m_maincpu->pcbase(), data); |
| 1077 | | |
| 1078 | | space.machine().scheduler().synchronize(FUNC(delayed_command_w), data, state->m_pia1); |
| 920 | space.machine().scheduler().synchronize(timer_expired_delegate(FUNC(victory_sound_device::delayed_command_w), this), data); |
| 1079 | 921 | } |
| 1080 | 922 | |
| 1081 | 923 | |
| 1082 | | static WRITE8_DEVICE_HANDLER( victory_sound_irq_clear_w ) |
| 924 | WRITE8_MEMBER( victory_sound_device::irq_clear_w ) |
| 1083 | 925 | { |
| 1084 | | exidy_sound_state *state = get_safe_token(device); |
| 1085 | | |
| 1086 | 926 | if (VICTORY_LOG_SOUND) logerror("%s:!!!! Sound IRQ clear = %02X\n", space.machine().describe_context(), data); |
| 1087 | 927 | |
| 1088 | | if (!data) state->m_pia1->ca1_w(1); |
| 928 | if (!data) m_pia1->ca1_w(1); |
| 1089 | 929 | } |
| 1090 | 930 | |
| 1091 | 931 | |
| 1092 | | static WRITE8_DEVICE_HANDLER( victory_main_ack_w ) |
| 932 | WRITE8_MEMBER( victory_sound_device::main_ack_w ) |
| 1093 | 933 | { |
| 1094 | | exidy_sound_state *state = get_safe_token(device); |
| 1095 | | |
| 1096 | 934 | if (VICTORY_LOG_SOUND) logerror("%s:!!!! Sound Main ACK W = %02X\n", space.machine().describe_context(), data); |
| 1097 | 935 | |
| 1098 | | if (state->m_victory_sound_response_ack_clk && !data) |
| 1099 | | state->m_pia1->cb1_w(1); |
| 936 | if (m_victory_sound_response_ack_clk && !data) |
| 937 | m_pia1->cb1_w(1); |
| 1100 | 938 | |
| 1101 | | state->m_victory_sound_response_ack_clk = data; |
| 939 | m_victory_sound_response_ack_clk = data; |
| 1102 | 940 | } |
| 1103 | 941 | |
| 1104 | 942 | |
| r24635 | r24636 | |
| 1112 | 950 | DEVCB_NULL, /* line CB2 in */ |
| 1113 | 951 | DEVCB_NULL, /* port A out */ |
| 1114 | 952 | DEVCB_NULL, /* port B out */ |
| 1115 | | DEVCB_DEVICE_HANDLER("custom", victory_sound_irq_clear_w), /* line CA2 out */ |
| 1116 | | DEVCB_DEVICE_HANDLER("custom", victory_main_ack_w), /* port CB2 out */ |
| 953 | DEVCB_DEVICE_MEMBER("custom", victory_sound_device, irq_clear_w), /* line CA2 out */ |
| 954 | DEVCB_DEVICE_MEMBER("custom", victory_sound_device, main_ack_w), /* port CB2 out */ |
| 1117 | 955 | DEVCB_NULL, /* IRQA */ |
| 1118 | | DEVCB_DEVICE_LINE("custom", update_irq_state) /* IRQB */ |
| 956 | DEVCB_DEVICE_LINE_MEMBER("custom", exidy_sound_device, update_irq_state) /* IRQB */ |
| 1119 | 957 | }; |
| 1120 | 958 | |
| 1121 | | |
| 1122 | | |
| 1123 | | static DEVICE_START( victory_sound ) |
| 1124 | | { |
| 1125 | | exidy_sound_state *state = get_safe_token(device); |
| 1126 | | |
| 1127 | | device->save_item(NAME(state->m_victory_sound_response_ack_clk)); |
| 1128 | | |
| 1129 | | DEVICE_START_CALL(venture_common_sh_start); |
| 1130 | | state->m_tms = device->machine().device<tms5220_device>("tms"); |
| 1131 | | } |
| 1132 | | |
| 1133 | | |
| 1134 | | static DEVICE_RESET( victory_sound ) |
| 1135 | | { |
| 1136 | | exidy_sound_state *state = get_safe_token(device); |
| 1137 | | pia6821_device *pia1 = state->m_pia1; |
| 1138 | | |
| 1139 | | DEVICE_RESET_CALL(common_sh_reset); |
| 1140 | | pia1->reset(); |
| 1141 | | state->m_riot->reset(); |
| 1142 | | memset(state->m_sh8253_timer, 0, sizeof(state->m_sh8253_timer)); |
| 1143 | | |
| 1144 | | /* the flip-flop @ F4 is reset */ |
| 1145 | | state->m_victory_sound_response_ack_clk = 0; |
| 1146 | | pia1->cb1_w(1); |
| 1147 | | |
| 1148 | | /* these two lines shouldn't be needed, but it avoids the log entry |
| 1149 | | as the sound CPU checks port A before the main CPU ever writes to it */ |
| 1150 | | pia1->set_a_input(0, 0); |
| 1151 | | pia1->ca1_w(1); |
| 1152 | | } |
| 1153 | | |
| 1154 | | |
| 1155 | 959 | const device_type EXIDY_VICTORY = &device_creator<victory_sound_device>; |
| 1156 | 960 | |
| 1157 | 961 | victory_sound_device::victory_sound_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) |
| 1158 | | : exidy_sound_device(mconfig, EXIDY_VICTORY, "Exidy SFX+PSG+Speech", tag, owner, clock, "victory_sound", __FILE__) |
| 962 | : exidy_sound_device(mconfig, EXIDY_VICTORY, "Exidy SFX+PSG+Speech", tag, owner, clock, "victory_sound", __FILE__), |
| 963 | m_victory_sound_response_ack_clk(0) |
| 1159 | 964 | { |
| 1160 | 965 | } |
| 1161 | 966 | |
| r24635 | r24636 | |
| 1175 | 980 | |
| 1176 | 981 | void victory_sound_device::device_start() |
| 1177 | 982 | { |
| 1178 | | DEVICE_START_NAME( victory_sound )(this); |
| 983 | save_item(NAME(m_victory_sound_response_ack_clk)); |
| 984 | |
| 985 | common_sh_start(); |
| 986 | |
| 987 | m_riot = machine().device<riot6532_device>("riot"); |
| 988 | |
| 989 | m_has_sh8253 = TRUE; |
| 990 | m_tms = NULL; |
| 991 | m_pia0 = machine().device<pia6821_device>("pia0"); |
| 992 | m_pia1 = machine().device<pia6821_device>("pia1"); |
| 993 | |
| 994 | /* determine which sound hardware is installed */ |
| 995 | m_cvsd = machine().device<hc55516_device>("cvsd"); |
| 996 | |
| 997 | /* 8253 */ |
| 998 | m_freq_to_step = (double)(1 << 24) / (double)SH8253_CLOCK; |
| 999 | |
| 1000 | save_item(NAME(m_riot_irq_state)); |
| 1001 | sh8253_register_state_globals(); |
| 1002 | |
| 1003 | m_tms = machine().device<tms5220_device>("tms"); |
| 1179 | 1004 | } |
| 1180 | 1005 | |
| 1181 | 1006 | //------------------------------------------------- |
| r24635 | r24636 | |
| 1184 | 1009 | |
| 1185 | 1010 | void victory_sound_device::device_reset() |
| 1186 | 1011 | { |
| 1187 | | DEVICE_RESET_NAME( victory_sound )(this); |
| 1012 | common_sh_reset(); |
| 1013 | m_pia1->reset(); |
| 1014 | m_riot->reset(); |
| 1015 | memset(m_sh8253_timer, 0, sizeof(m_sh8253_timer)); |
| 1016 | |
| 1017 | /* the flip-flop @ F4 is reset */ |
| 1018 | m_victory_sound_response_ack_clk = 0; |
| 1019 | m_pia1->cb1_w(1); |
| 1020 | |
| 1021 | /* these two lines shouldn't be needed, but it avoids the log entry |
| 1022 | as the sound CPU checks port A before the main CPU ever writes to it */ |
| 1023 | m_pia1->set_a_input(0, 0); |
| 1024 | m_pia1->ca1_w(1); |
| 1188 | 1025 | } |
| 1189 | 1026 | |
| 1190 | 1027 | //------------------------------------------------- |
| r24635 | r24636 | |
| 1193 | 1030 | |
| 1194 | 1031 | void victory_sound_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples) |
| 1195 | 1032 | { |
| 1196 | | // should never get here |
| 1197 | | fatalerror("sound_stream_update called; not applicable to legacy sound devices\n"); |
| 1033 | exidy_sound_device::sound_stream_update(stream, inputs, outputs, samples); |
| 1198 | 1034 | } |
| 1199 | 1035 | |
| 1200 | 1036 | |
| r24635 | r24636 | |
| 1204 | 1040 | AM_RANGE(0x0000, 0x00ff) AM_MIRROR(0x0f00) AM_RAM |
| 1205 | 1041 | AM_RANGE(0x1000, 0x107f) AM_MIRROR(0x0f80) AM_DEVREADWRITE("riot", riot6532_device, read, write) |
| 1206 | 1042 | AM_RANGE(0x2000, 0x2003) AM_MIRROR(0x0ffc) AM_DEVREADWRITE("pia1", pia6821_device, read, write) |
| 1207 | | AM_RANGE(0x3000, 0x3003) AM_MIRROR(0x0ffc) AM_DEVREADWRITE_LEGACY("custom", exidy_sh8253_r, exidy_sh8253_w) |
| 1043 | AM_RANGE(0x3000, 0x3003) AM_MIRROR(0x0ffc) AM_DEVREADWRITE("custom", victory_sound_device, sh8253_r, sh8253_w) |
| 1208 | 1044 | AM_RANGE(0x4000, 0x4fff) AM_NOP |
| 1209 | | AM_RANGE(0x5000, 0x5007) AM_MIRROR(0x0ff8) AM_DEVREADWRITE_LEGACY("custom", exidy_sh6840_r, exidy_sh6840_w) |
| 1210 | | AM_RANGE(0x6000, 0x6003) AM_MIRROR(0x0ffc) AM_DEVWRITE_LEGACY("custom", exidy_sfxctrl_w) |
| 1045 | AM_RANGE(0x5000, 0x5007) AM_MIRROR(0x0ff8) AM_DEVREADWRITE("custom", victory_sound_device, sh6840_r, sh6840_w) |
| 1046 | AM_RANGE(0x6000, 0x6003) AM_MIRROR(0x0ffc) AM_DEVWRITE("custom", victory_sound_device, sfxctrl_w) |
| 1211 | 1047 | AM_RANGE(0x7000, 0xafff) AM_NOP |
| 1212 | 1048 | AM_RANGE(0xb000, 0xffff) AM_ROM |
| 1213 | 1049 | ADDRESS_MAP_END |
trunk/src/emu/sound/s14001a.c
| r24635 | r24636 | |
| 240 | 240 | |
| 241 | 241 | #include "emu.h" |
| 242 | 242 | #include "s14001a.h" |
| 243 | | #include "devlegcy.h" |
| 244 | 243 | |
| 245 | | struct S14001AChip |
| 246 | | { |
| 247 | | sound_stream * stream; |
| 248 | 244 | |
| 249 | | UINT8 WordInput; // value on word input bus |
| 250 | | UINT8 LatchedWord; // value latched from input bus |
| 251 | | UINT16 SyllableAddress; // address read from word table |
| 252 | | UINT16 PhoneAddress; // starting/current phone address from syllable table |
| 253 | | UINT8 PlayParams; // playback parameters from syllable table |
| 254 | | UINT8 PhoneOffset; // offset within phone |
| 255 | | UINT8 LengthCounter; // 4-bit counter which holds the inverted length of the word in phones, leftshifted by 1 |
| 256 | | UINT8 RepeatCounter; // 3-bit counter which holds the inverted number of repeats per phone, leftshifted by 1 |
| 257 | | UINT8 OutputCounter; // 2-bit counter to determine forward/backward and output/silence state. |
| 258 | | UINT8 machineState; // chip state machine state |
| 259 | | UINT8 nextstate; // chip state machine's new state |
| 260 | | UINT8 laststate; // chip state machine's previous state, needed for mirror increment masking |
| 261 | | UINT8 resetState; // reset line state |
| 262 | | UINT8 oddeven; // odd versus even cycle toggle |
| 263 | | UINT8 GlobalSilenceState; // same as above but for silent syllables instead of silent portions of mirrored syllables |
| 264 | | UINT8 OldDelta; // 2-bit old delta value |
| 265 | | UINT8 DACOutput; // 4-bit DAC Accumulator/output |
| 266 | | UINT8 audioout; // filtered audio output |
| 267 | | UINT8 *SpeechRom; // array to hold rom contents, mame will not need this, will use a pointer |
| 268 | | INT16 filtervals[8]; |
| 269 | | UINT8 VSU1000_amp; // amplitude setting on VSU-1000 board |
| 270 | | }; |
| 271 | | |
| 272 | | INLINE S14001AChip *get_safe_token(device_t *device) |
| 273 | | { |
| 274 | | assert(device != NULL); |
| 275 | | assert(device->type() == S14001A); |
| 276 | | return (S14001AChip *)downcast<s14001a_device *>(device)->token(); |
| 277 | | } |
| 278 | | |
| 279 | | |
| 280 | 245 | //#define DEBUGSTATE |
| 281 | 246 | |
| 282 | 247 | #define SILENCE 0x7 // value output when silent |
| 283 | 248 | #define ALTFLAG 0xFF // value to tell renderer that this frame's output is the average of the 8 prior frames and not directly used. |
| 284 | 249 | |
| 285 | | #define LASTSYLLABLE ((chip->PlayParams & 0x80)>>7) |
| 286 | | #define MIRRORMODE ((chip->PlayParams & 0x40)>>6) |
| 287 | | #define SILENCEFLAG ((chip->PlayParams & 0x20)>>5) |
| 288 | | #define LENGTHCOUNT ((chip->PlayParams & 0x1C)>>1) // remember: its 4 bits and the bottom bit is always zero! |
| 289 | | #define REPEATCOUNT ((chip->PlayParams<<1)&0x6) // remember: its 3 bits and the bottom bit is always zero! |
| 290 | | #define LOCALSILENCESTATE ((chip->OutputCounter & 0x2) && (MIRRORMODE)) // 1 when silent output, 0 when DAC output. |
| 250 | #define LASTSYLLABLE ((m_PlayParams & 0x80)>>7) |
| 251 | #define MIRRORMODE ((m_PlayParams & 0x40)>>6) |
| 252 | #define SILENCEFLAG ((m_PlayParams & 0x20)>>5) |
| 253 | #define LENGTHCOUNT ((m_PlayParams & 0x1C)>>1) // remember: its 4 bits and the bottom bit is always zero! |
| 254 | #define REPEATCOUNT ((m_PlayParams<<1)&0x6) // remember: its 3 bits and the bottom bit is always zero! |
| 255 | #define LOCALSILENCESTATE ((m_OutputCounter & 0x2) && (MIRRORMODE)) // 1 when silent output, 0 when DAC output. |
| 291 | 256 | |
| 292 | 257 | static const INT8 DeltaTable[4][4] = |
| 293 | 258 | { |
| r24635 | r24636 | |
| 298 | 263 | }; |
| 299 | 264 | |
| 300 | 265 | #ifdef ACCURATE_SQUEAL |
| 301 | | static INT16 audiofilter(S14001AChip *chip) /* rewrite me to better match the real filter! */ |
| 266 | INT16 s14001a_device::audiofilter() /* rewrite me to better match the real filter! */ |
| 302 | 267 | { |
| 303 | 268 | UINT8 temp1; |
| 304 | 269 | INT16 temp2 = 0; |
| 305 | 270 | /* mean averaging filter! 1/n exponential *would* be somewhat better, but I'm lazy... */ |
| 306 | | for (temp1 = 0; temp1 < 8; temp1++) { temp2 += chip->filtervals[temp1]; } |
| 271 | for (temp1 = 0; temp1 < 8; temp1++) { temp2 += m_filtervals[temp1]; } |
| 307 | 272 | temp2 >>= 3; |
| 308 | 273 | return temp2; |
| 309 | 274 | } |
| 310 | 275 | |
| 311 | | static void shiftIntoFilter(S14001AChip *chip, INT16 inputvalue) |
| 276 | void s14001a_device::shiftIntoFilter(INT16 inputvalue) |
| 312 | 277 | { |
| 313 | 278 | UINT8 temp1; |
| 314 | 279 | for (temp1 = 7; temp1 > 0; temp1--) |
| 315 | 280 | { |
| 316 | | chip->filtervals[temp1] = chip->filtervals[(temp1 - 1)]; |
| 281 | m_filtervals[temp1] = m_filtervals[(temp1 - 1)]; |
| 317 | 282 | } |
| 318 | | chip->filtervals[0] = inputvalue; |
| 283 | m_filtervals[0] = inputvalue; |
| 319 | 284 | } |
| 320 | 285 | #endif |
| 321 | 286 | |
| 322 | | static void PostPhoneme(S14001AChip *chip) /* figure out what the heck to do after playing a phoneme */ |
| 287 | void s14001a_device::PostPhoneme() /* figure out what the heck to do after playing a phoneme */ |
| 323 | 288 | { |
| 324 | 289 | #ifdef DEBUGSTATE |
| 325 | 290 | fprintf(stderr,"0: entered PostPhoneme\n"); |
| 326 | 291 | #endif |
| 327 | | chip->RepeatCounter++; // increment the repeat counter |
| 328 | | chip->OutputCounter++; // increment the output counter |
| 292 | m_RepeatCounter++; // increment the repeat counter |
| 293 | m_OutputCounter++; // increment the output counter |
| 329 | 294 | if (MIRRORMODE) // if mirroring is enabled |
| 330 | 295 | { |
| 331 | 296 | #ifdef DEBUGSTATE |
| 332 | 297 | fprintf(stderr,"1: MIRRORMODE was on\n"); |
| 333 | 298 | #endif |
| 334 | | if (chip->RepeatCounter == 0x8) // exceeded 3 bits? |
| 299 | if (m_RepeatCounter == 0x8) // exceeded 3 bits? |
| 335 | 300 | { |
| 336 | 301 | #ifdef DEBUGSTATE |
| 337 | 302 | fprintf(stderr,"2: RepeatCounter was == 8\n"); |
| 338 | 303 | #endif |
| 339 | 304 | // reset repeat counter, increment length counter |
| 340 | 305 | // but first check if lowest bit is set |
| 341 | | chip->RepeatCounter = REPEATCOUNT; // reload repeat counter with reload value |
| 342 | | if (chip->LengthCounter & 0x1) // if low bit is 1 (will carry after increment) |
| 306 | m_RepeatCounter = REPEATCOUNT; // reload repeat counter with reload value |
| 307 | if (m_LengthCounter & 0x1) // if low bit is 1 (will carry after increment) |
| 343 | 308 | { |
| 344 | 309 | #ifdef DEBUGSTATE |
| 345 | 310 | fprintf(stderr,"3: LengthCounter's low bit was 1\n"); |
| 346 | 311 | #endif |
| 347 | | chip->PhoneAddress+=8; // go to next phone in this syllable |
| 312 | m_PhoneAddress+=8; // go to next phone in this syllable |
| 348 | 313 | } |
| 349 | | chip->LengthCounter++; |
| 350 | | if (chip->LengthCounter == 0x10) // if Length counter carried out of 4 bits |
| 314 | m_LengthCounter++; |
| 315 | if (m_LengthCounter == 0x10) // if Length counter carried out of 4 bits |
| 351 | 316 | { |
| 352 | 317 | #ifdef DEBUGSTATE |
| 353 | 318 | fprintf(stderr,"3: LengthCounter overflowed\n"); |
| 354 | 319 | #endif |
| 355 | | chip->SyllableAddress += 2; // go to next syllable |
| 356 | | chip->nextstate = LASTSYLLABLE ? 13 : 3; // if we're on the last syllable, go to end state, otherwise go and load the next syllable. |
| 320 | m_SyllableAddress += 2; // go to next syllable |
| 321 | m_nextstate = LASTSYLLABLE ? 13 : 3; // if we're on the last syllable, go to end state, otherwise go and load the next syllable. |
| 357 | 322 | } |
| 358 | 323 | else |
| 359 | 324 | { |
| 360 | 325 | #ifdef DEBUGSTATE |
| 361 | 326 | fprintf(stderr,"3: LengthCounter's low bit wasn't 1 and it didn't overflow\n"); |
| 362 | 327 | #endif |
| 363 | | chip->PhoneOffset = (chip->OutputCounter&1) ? 7 : 0; |
| 364 | | chip->nextstate = (chip->OutputCounter&1) ? 9 : 5; |
| 328 | m_PhoneOffset = (m_OutputCounter&1) ? 7 : 0; |
| 329 | m_nextstate = (m_OutputCounter&1) ? 9 : 5; |
| 365 | 330 | } |
| 366 | 331 | } |
| 367 | 332 | else // repeatcounter did NOT carry out of 3 bits so leave length counter alone |
| 368 | 333 | { |
| 369 | 334 | #ifdef DEBUGSTATE |
| 370 | | fprintf(stderr,"2: RepeatCounter is less than 8 (its actually %d)\n", chip->RepeatCounter); |
| 335 | fprintf(stderr,"2: RepeatCounter is less than 8 (its actually %d)\n", m_RepeatCounter); |
| 371 | 336 | #endif |
| 372 | | chip->PhoneOffset = (chip->OutputCounter&1) ? 7 : 0; |
| 373 | | chip->nextstate = (chip->OutputCounter&1) ? 9 : 5; |
| 337 | m_PhoneOffset = (m_OutputCounter&1) ? 7 : 0; |
| 338 | m_nextstate = (m_OutputCounter&1) ? 9 : 5; |
| 374 | 339 | } |
| 375 | 340 | } |
| 376 | 341 | else // if mirroring is NOT enabled |
| r24635 | r24636 | |
| 378 | 343 | #ifdef DEBUGSTATE |
| 379 | 344 | fprintf(stderr,"1: MIRRORMODE was off\n"); |
| 380 | 345 | #endif |
| 381 | | if (chip->RepeatCounter == 0x8) // exceeded 3 bits? |
| 346 | if (m_RepeatCounter == 0x8) // exceeded 3 bits? |
| 382 | 347 | { |
| 383 | 348 | #ifdef DEBUGSTATE |
| 384 | 349 | fprintf(stderr,"2: RepeatCounter was == 8\n"); |
| 385 | 350 | #endif |
| 386 | 351 | // reset repeat counter, increment length counter |
| 387 | | chip->RepeatCounter = REPEATCOUNT; // reload repeat counter with reload value |
| 388 | | chip->LengthCounter++; |
| 389 | | if (chip->LengthCounter == 0x10) // if Length counter carried out of 4 bits |
| 352 | m_RepeatCounter = REPEATCOUNT; // reload repeat counter with reload value |
| 353 | m_LengthCounter++; |
| 354 | if (m_LengthCounter == 0x10) // if Length counter carried out of 4 bits |
| 390 | 355 | { |
| 391 | 356 | #ifdef DEBUGSTATE |
| 392 | 357 | fprintf(stderr,"3: LengthCounter overflowed\n"); |
| 393 | 358 | #endif |
| 394 | | chip->SyllableAddress += 2; // go to next syllable |
| 395 | | chip->nextstate = LASTSYLLABLE ? 13 : 3; // if we're on the last syllable, go to end state, otherwise go and load the next syllable. |
| 359 | m_SyllableAddress += 2; // go to next syllable |
| 360 | m_nextstate = LASTSYLLABLE ? 13 : 3; // if we're on the last syllable, go to end state, otherwise go and load the next syllable. |
| 396 | 361 | #ifdef DEBUGSTATE |
| 397 | | fprintf(stderr,"nextstate is now %d\n", chip->nextstate); // see line below, same reason. |
| 362 | fprintf(stderr,"nextstate is now %d\n", m_nextstate); // see line below, same reason. |
| 398 | 363 | #endif |
| 399 | 364 | return; // need a return here so we don't hit the 'nextstate = 5' line below |
| 400 | 365 | } |
| 401 | 366 | } |
| 402 | | chip->PhoneAddress += 8; // regardless of counters, the phone address always increments in non-mirrored mode |
| 403 | | chip->PhoneOffset = 0; |
| 404 | | chip->nextstate = 5; |
| 367 | m_PhoneAddress += 8; // regardless of counters, the phone address always increments in non-mirrored mode |
| 368 | m_PhoneOffset = 0; |
| 369 | m_nextstate = 5; |
| 405 | 370 | } |
| 406 | 371 | #ifdef DEBUGSTATE |
| 407 | | fprintf(stderr,"nextstate is now %d\n", chip->nextstate); |
| 372 | fprintf(stderr,"nextstate is now %d\n", m_nextstate); |
| 408 | 373 | #endif |
| 409 | 374 | } |
| 410 | 375 | |
| 411 | | static void s14001a_clock(S14001AChip *chip) /* called once per clock */ |
| 376 | void s14001a_device::s14001a_clock() /* called once per clock */ |
| 412 | 377 | { |
| 413 | 378 | UINT8 CurDelta; // Current delta |
| 414 | 379 | |
| 415 | 380 | /* on even clocks, audio output is floating, /romen is low so rom data bus is driven |
| 416 | 381 | * on odd clocks, audio output is driven, /romen is high, state machine 2 is clocked |
| 417 | 382 | */ |
| 418 | | chip->oddeven = !(chip->oddeven); // invert the clock |
| 419 | | if (chip->oddeven == 0) // even clock |
| 383 | m_oddeven = !(m_oddeven); // invert the clock |
| 384 | if (m_oddeven == 0) // even clock |
| 420 | 385 | { |
| 421 | 386 | #ifdef ACCURATE_SQUEAL |
| 422 | | chip->audioout = ALTFLAG; // flag to the renderer that this output should be the average of the last 8 |
| 387 | m_audioout = ALTFLAG; // flag to the renderer that this output should be the average of the last 8 |
| 423 | 388 | #endif |
| 424 | 389 | // DIGITAL INPUT *MIGHT* occur on the test pins occurs on this cycle? |
| 425 | 390 | } |
| r24635 | r24636 | |
| 427 | 392 | { |
| 428 | 393 | // fix dac output between samples. theoretically this might be unnecessary but it would require some messy logic in state 5 on the first sample load. |
| 429 | 394 | // Note: this behavior is NOT accurate, and needs to be fixed. see TODO. |
| 430 | | if (chip->GlobalSilenceState || LOCALSILENCESTATE) |
| 395 | if (m_GlobalSilenceState || LOCALSILENCESTATE) |
| 431 | 396 | { |
| 432 | | chip->DACOutput = SILENCE; |
| 433 | | chip->OldDelta = 2; |
| 397 | m_DACOutput = SILENCE; |
| 398 | m_OldDelta = 2; |
| 434 | 399 | } |
| 435 | | chip->audioout = (chip->GlobalSilenceState || LOCALSILENCESTATE) ? SILENCE : chip->DACOutput; // when either silence state is 1, output silence. |
| 400 | m_audioout = (m_GlobalSilenceState || LOCALSILENCESTATE) ? SILENCE : m_DACOutput; // when either silence state is 1, output silence. |
| 436 | 401 | // DIGITAL OUTPUT *might* be driven onto the test pins on this cycle? |
| 437 | | switch(chip->machineState) // HUUUUUGE switch statement |
| 402 | switch(m_machineState) // HUUUUUGE switch statement |
| 438 | 403 | { |
| 439 | 404 | case 0: // idle state |
| 440 | | chip->nextstate = 0; |
| 405 | m_nextstate = 0; |
| 441 | 406 | break; |
| 442 | 407 | case 1: // read starting syllable high byte from word table |
| 443 | | chip->SyllableAddress = 0; // clear syllable address |
| 444 | | chip->SyllableAddress |= chip->SpeechRom[(chip->LatchedWord<<1)]<<4; |
| 445 | | chip->nextstate = chip->resetState ? 1 : 2; |
| 408 | m_SyllableAddress = 0; // clear syllable address |
| 409 | m_SyllableAddress |= m_SpeechRom[(m_LatchedWord<<1)]<<4; |
| 410 | m_nextstate = m_resetState ? 1 : 2; |
| 446 | 411 | break; |
| 447 | 412 | case 2: // read starting syllable low byte from word table |
| 448 | | chip->SyllableAddress |= chip->SpeechRom[(chip->LatchedWord<<1)+1]>>4; |
| 449 | | chip->nextstate = 3; |
| 413 | m_SyllableAddress |= m_SpeechRom[(m_LatchedWord<<1)+1]>>4; |
| 414 | m_nextstate = 3; |
| 450 | 415 | break; |
| 451 | 416 | case 3: // read starting phone address |
| 452 | | chip->PhoneAddress = chip->SpeechRom[chip->SyllableAddress]<<4; |
| 453 | | chip->nextstate = 4; |
| 417 | m_PhoneAddress = m_SpeechRom[m_SyllableAddress]<<4; |
| 418 | m_nextstate = 4; |
| 454 | 419 | break; |
| 455 | 420 | case 4: // read playback parameters and prepare for play |
| 456 | | chip->PlayParams = chip->SpeechRom[chip->SyllableAddress+1]; |
| 457 | | chip->GlobalSilenceState = SILENCEFLAG; // load phone silence flag |
| 458 | | chip->LengthCounter = LENGTHCOUNT; // load length counter |
| 459 | | chip->RepeatCounter = REPEATCOUNT; // load repeat counter |
| 460 | | chip->OutputCounter = 0; // clear output counter and disable mirrored phoneme silence indirectly via LOCALSILENCESTATE |
| 461 | | chip->PhoneOffset = 0; // set offset within phone to zero |
| 462 | | chip->OldDelta = 0x2; // set old delta to 2 <- is this right? |
| 463 | | chip->DACOutput = SILENCE ; // set DAC output to center/silence position |
| 464 | | chip->nextstate = 5; |
| 421 | m_PlayParams = m_SpeechRom[m_SyllableAddress+1]; |
| 422 | m_GlobalSilenceState = SILENCEFLAG; // load phone silence flag |
| 423 | m_LengthCounter = LENGTHCOUNT; // load length counter |
| 424 | m_RepeatCounter = REPEATCOUNT; // load repeat counter |
| 425 | m_OutputCounter = 0; // clear output counter and disable mirrored phoneme silence indirectly via LOCALSILENCESTATE |
| 426 | m_PhoneOffset = 0; // set offset within phone to zero |
| 427 | m_OldDelta = 0x2; // set old delta to 2 <- is this right? |
| 428 | m_DACOutput = SILENCE ; // set DAC output to center/silence position |
| 429 | m_nextstate = 5; |
| 465 | 430 | break; |
| 466 | 431 | case 5: // Play phone forward, shift = 0 (also load) |
| 467 | | CurDelta = (chip->SpeechRom[(chip->PhoneAddress)+chip->PhoneOffset]&0xc0)>>6; // grab current delta from high 2 bits of high nybble |
| 468 | | chip->DACOutput += DeltaTable[CurDelta][chip->OldDelta]; // send data to forward delta table and add result to accumulator |
| 469 | | chip->OldDelta = CurDelta; // Move current delta to old |
| 470 | | chip->nextstate = 6; |
| 432 | CurDelta = (m_SpeechRom[(m_PhoneAddress)+m_PhoneOffset]&0xc0)>>6; // grab current delta from high 2 bits of high nybble |
| 433 | m_DACOutput += DeltaTable[CurDelta][m_OldDelta]; // send data to forward delta table and add result to accumulator |
| 434 | m_OldDelta = CurDelta; // Move current delta to old |
| 435 | m_nextstate = 6; |
| 471 | 436 | break; |
| 472 | 437 | case 6: // Play phone forward, shift = 2 |
| 473 | | CurDelta = (chip->SpeechRom[(chip->PhoneAddress)+chip->PhoneOffset]&0x30)>>4; // grab current delta from low 2 bits of high nybble |
| 474 | | chip->DACOutput += DeltaTable[CurDelta][chip->OldDelta]; // send data to forward delta table and add result to accumulator |
| 475 | | chip->OldDelta = CurDelta; // Move current delta to old |
| 476 | | chip->nextstate = 7; |
| 438 | CurDelta = (m_SpeechRom[(m_PhoneAddress)+m_PhoneOffset]&0x30)>>4; // grab current delta from low 2 bits of high nybble |
| 439 | m_DACOutput += DeltaTable[CurDelta][m_OldDelta]; // send data to forward delta table and add result to accumulator |
| 440 | m_OldDelta = CurDelta; // Move current delta to old |
| 441 | m_nextstate = 7; |
| 477 | 442 | break; |
| 478 | 443 | case 7: // Play phone forward, shift = 4 |
| 479 | | CurDelta = (chip->SpeechRom[(chip->PhoneAddress)+chip->PhoneOffset]&0xc)>>2; // grab current delta from high 2 bits of low nybble |
| 480 | | chip->DACOutput += DeltaTable[CurDelta][chip->OldDelta]; // send data to forward delta table and add result to accumulator |
| 481 | | chip->OldDelta = CurDelta; // Move current delta to old |
| 482 | | chip->nextstate = 8; |
| 444 | CurDelta = (m_SpeechRom[(m_PhoneAddress)+m_PhoneOffset]&0xc)>>2; // grab current delta from high 2 bits of low nybble |
| 445 | m_DACOutput += DeltaTable[CurDelta][m_OldDelta]; // send data to forward delta table and add result to accumulator |
| 446 | m_OldDelta = CurDelta; // Move current delta to old |
| 447 | m_nextstate = 8; |
| 483 | 448 | break; |
| 484 | 449 | case 8: // Play phone forward, shift = 6 (increment address if needed) |
| 485 | | CurDelta = chip->SpeechRom[(chip->PhoneAddress)+chip->PhoneOffset]&0x3; // grab current delta from low 2 bits of low nybble |
| 486 | | chip->DACOutput += DeltaTable[CurDelta][chip->OldDelta]; // send data to forward delta table and add result to accumulator |
| 487 | | chip->OldDelta = CurDelta; // Move current delta to old |
| 488 | | chip->PhoneOffset++; // increment phone offset |
| 489 | | if (chip->PhoneOffset == 0x8) // if we're now done this phone |
| 450 | CurDelta = m_SpeechRom[(m_PhoneAddress)+m_PhoneOffset]&0x3; // grab current delta from low 2 bits of low nybble |
| 451 | m_DACOutput += DeltaTable[CurDelta][m_OldDelta]; // send data to forward delta table and add result to accumulator |
| 452 | m_OldDelta = CurDelta; // Move current delta to old |
| 453 | m_PhoneOffset++; // increment phone offset |
| 454 | if (m_PhoneOffset == 0x8) // if we're now done this phone |
| 490 | 455 | { |
| 491 | 456 | /* call the PostPhoneme Function */ |
| 492 | | PostPhoneme(chip); |
| 457 | PostPhoneme(); |
| 493 | 458 | } |
| 494 | 459 | else |
| 495 | 460 | { |
| 496 | | chip->nextstate = 5; |
| 461 | m_nextstate = 5; |
| 497 | 462 | } |
| 498 | 463 | break; |
| 499 | 464 | case 9: // Play phone backward, shift = 6 (also load) |
| 500 | | CurDelta = (chip->SpeechRom[(chip->PhoneAddress)+chip->PhoneOffset]&0x3); // grab current delta from low 2 bits of low nybble |
| 501 | | if (chip->laststate != 8) // ignore first (bogus) dac change in mirrored backwards mode. observations and the patent show this. |
| 465 | CurDelta = (m_SpeechRom[(m_PhoneAddress)+m_PhoneOffset]&0x3); // grab current delta from low 2 bits of low nybble |
| 466 | if (m_laststate != 8) // ignore first (bogus) dac change in mirrored backwards mode. observations and the patent show this. |
| 502 | 467 | { |
| 503 | | chip->DACOutput -= DeltaTable[chip->OldDelta][CurDelta]; // send data to forward delta table and subtract result from accumulator |
| 468 | m_DACOutput -= DeltaTable[m_OldDelta][CurDelta]; // send data to forward delta table and subtract result from accumulator |
| 504 | 469 | } |
| 505 | | chip->OldDelta = CurDelta; // Move current delta to old |
| 506 | | chip->nextstate = 10; |
| 470 | m_OldDelta = CurDelta; // Move current delta to old |
| 471 | m_nextstate = 10; |
| 507 | 472 | break; |
| 508 | 473 | case 10: // Play phone backward, shift = 4 |
| 509 | | CurDelta = (chip->SpeechRom[(chip->PhoneAddress)+chip->PhoneOffset]&0xc)>>2; // grab current delta from high 2 bits of low nybble |
| 510 | | chip->DACOutput -= DeltaTable[chip->OldDelta][CurDelta]; // send data to forward delta table and subtract result from accumulator |
| 511 | | chip->OldDelta = CurDelta; // Move current delta to old |
| 512 | | chip->nextstate = 11; |
| 474 | CurDelta = (m_SpeechRom[(m_PhoneAddress)+m_PhoneOffset]&0xc)>>2; // grab current delta from high 2 bits of low nybble |
| 475 | m_DACOutput -= DeltaTable[m_OldDelta][CurDelta]; // send data to forward delta table and subtract result from accumulator |
| 476 | m_OldDelta = CurDelta; // Move current delta to old |
| 477 | m_nextstate = 11; |
| 513 | 478 | break; |
| 514 | 479 | case 11: // Play phone backward, shift = 2 |
| 515 | | CurDelta = (chip->SpeechRom[(chip->PhoneAddress)+chip->PhoneOffset]&0x30)>>4; // grab current delta from low 2 bits of high nybble |
| 516 | | chip->DACOutput -= DeltaTable[chip->OldDelta][CurDelta]; // send data to forward delta table and subtract result from accumulator |
| 517 | | chip->OldDelta = CurDelta; // Move current delta to old |
| 518 | | chip->nextstate = 12; |
| 480 | CurDelta = (m_SpeechRom[(m_PhoneAddress)+m_PhoneOffset]&0x30)>>4; // grab current delta from low 2 bits of high nybble |
| 481 | m_DACOutput -= DeltaTable[m_OldDelta][CurDelta]; // send data to forward delta table and subtract result from accumulator |
| 482 | m_OldDelta = CurDelta; // Move current delta to old |
| 483 | m_nextstate = 12; |
| 519 | 484 | break; |
| 520 | 485 | case 12: // Play phone backward, shift = 0 (increment address if needed) |
| 521 | | CurDelta = (chip->SpeechRom[(chip->PhoneAddress)+chip->PhoneOffset]&0xc0)>>6; // grab current delta from high 2 bits of high nybble |
| 522 | | chip->DACOutput -= DeltaTable[chip->OldDelta][CurDelta]; // send data to forward delta table and subtract result from accumulator |
| 523 | | chip->OldDelta = CurDelta; // Move current delta to old |
| 524 | | chip->PhoneOffset--; // decrement phone offset |
| 525 | | if (chip->PhoneOffset == 0xFF) // if we're now done this phone |
| 486 | CurDelta = (m_SpeechRom[(m_PhoneAddress)+m_PhoneOffset]&0xc0)>>6; // grab current delta from high 2 bits of high nybble |
| 487 | m_DACOutput -= DeltaTable[m_OldDelta][CurDelta]; // send data to forward delta table and subtract result from accumulator |
| 488 | m_OldDelta = CurDelta; // Move current delta to old |
| 489 | m_PhoneOffset--; // decrement phone offset |
| 490 | if (m_PhoneOffset == 0xFF) // if we're now done this phone |
| 526 | 491 | { |
| 527 | 492 | /* call the PostPhoneme() function */ |
| 528 | | PostPhoneme(chip); |
| 493 | PostPhoneme(); |
| 529 | 494 | } |
| 530 | 495 | else |
| 531 | 496 | { |
| 532 | | chip->nextstate = 9; |
| 497 | m_nextstate = 9; |
| 533 | 498 | } |
| 534 | 499 | break; |
| 535 | 500 | case 13: // For those pedantic among us, consume an extra two clocks like the real chip does. |
| 536 | | chip->nextstate = 0; |
| 501 | m_nextstate = 0; |
| 537 | 502 | break; |
| 538 | 503 | } |
| 539 | 504 | #ifdef DEBUGSTATE |
| 540 | | fprintf(stderr, "Machine state is now %d, was %d, PhoneOffset is %d\n", chip->nextstate, chip->machineState, chip->PhoneOffset); |
| 505 | fprintf(stderr, "Machine state is now %d, was %d, PhoneOffset is %d\n", m_nextstate, m_machineState, m_PhoneOffset); |
| 541 | 506 | #endif |
| 542 | | chip->laststate = chip->machineState; |
| 543 | | chip->machineState = chip->nextstate; |
| 507 | m_laststate = m_machineState; |
| 508 | m_machineState = m_nextstate; |
| 544 | 509 | |
| 545 | 510 | /* the dac is 4 bits wide. if a delta step forced it outside of 4 bits, mask it back over here */ |
| 546 | | chip->DACOutput &= 0xF; |
| 511 | m_DACOutput &= 0xF; |
| 547 | 512 | } |
| 548 | 513 | } |
| 549 | 514 | |
| r24635 | r24636 | |
| 551 | 516 | MAME glue code |
| 552 | 517 | **************************************************************************/ |
| 553 | 518 | |
| 554 | | static STREAM_UPDATE( s14001a_pcm_update ) |
| 519 | int s14001a_device::bsy_r() |
| 555 | 520 | { |
| 556 | | S14001AChip *chip = (S14001AChip *)param; |
| 557 | | int i; |
| 558 | | |
| 559 | | for (i = 0; i < samples; i++) |
| 560 | | { |
| 561 | | s14001a_clock(chip); |
| 562 | | #ifdef ACCURATE_SQUEAL |
| 563 | | if (chip->audioout == ALTFLAG) // input from test pins -> output |
| 564 | | { |
| 565 | | shiftIntoFilter(chip, audiofilter(chip)); // shift over the previous outputs and stick in audioout. |
| 566 | | outputs[0][i] = audiofilter(chip)*chip->VSU1000_amp; |
| 567 | | } |
| 568 | | else // normal, dac-driven output |
| 569 | | { |
| 570 | | shiftIntoFilter(chip, ((((INT16)chip->audioout)-8)<<9)); // shift over the previous outputs and stick in audioout 4 times. note <<9 instead of <<10, to prevent clipping, and to simulate that the filtered output normally has a somewhat lower amplitude than the driven one. |
| 571 | | #endif |
| 572 | | outputs[0][i] = ((((INT16)chip->audioout)-8)<<10)*chip->VSU1000_amp; |
| 573 | | #ifdef ACCURATE_SQUEAL |
| 574 | | } |
| 575 | | #endif |
| 576 | | } |
| 577 | | } |
| 578 | | |
| 579 | | static DEVICE_START( s14001a ) |
| 580 | | { |
| 581 | | S14001AChip *chip = get_safe_token(device); |
| 582 | | int i; |
| 583 | | |
| 584 | | chip->GlobalSilenceState = 1; |
| 585 | | chip->OldDelta = 0x02; |
| 586 | | chip->DACOutput = SILENCE; |
| 587 | | |
| 588 | | for (i = 0; i < 8; i++) |
| 589 | | { |
| 590 | | chip->filtervals[i] = SILENCE; |
| 591 | | } |
| 592 | | |
| 593 | | chip->SpeechRom = *device->region(); |
| 594 | | |
| 595 | | chip->stream = device->machine().sound().stream_alloc(*device, 0, 1, device->clock() ? device->clock() : device->machine().sample_rate(), chip, s14001a_pcm_update); |
| 596 | | } |
| 597 | | |
| 598 | | int s14001a_bsy_r(device_t *device) |
| 599 | | { |
| 600 | | S14001AChip *chip = get_safe_token(device); |
| 601 | | chip->stream->update(); |
| 521 | m_stream->update(); |
| 602 | 522 | #ifdef DEBUGSTATE |
| 603 | | fprintf(stderr,"busy state checked: %d\n",(chip->machineState != 0) ); |
| 523 | fprintf(stderr,"busy state checked: %d\n",(m_machineState != 0) ); |
| 604 | 524 | #endif |
| 605 | | return (chip->machineState != 0); |
| 525 | return (m_machineState != 0); |
| 606 | 526 | } |
| 607 | 527 | |
| 608 | | void s14001a_reg_w(device_t *device, int data) |
| 528 | void s14001a_device::reg_w(int data) |
| 609 | 529 | { |
| 610 | | S14001AChip *chip = get_safe_token(device); |
| 611 | | chip->stream->update(); |
| 612 | | chip->WordInput = data; |
| 530 | m_stream->update(); |
| 531 | m_WordInput = data; |
| 613 | 532 | } |
| 614 | 533 | |
| 615 | | void s14001a_rst_w(device_t *device, int data) |
| 534 | void s14001a_device::rst_w(int data) |
| 616 | 535 | { |
| 617 | | S14001AChip *chip = get_safe_token(device); |
| 618 | | chip->stream->update(); |
| 619 | | chip->LatchedWord = chip->WordInput; |
| 620 | | chip->resetState = (data==1); |
| 621 | | chip->machineState = chip->resetState ? 1 : chip->machineState; |
| 536 | m_stream->update(); |
| 537 | m_LatchedWord = m_WordInput; |
| 538 | m_resetState = (data==1); |
| 539 | m_machineState = m_resetState ? 1 : m_machineState; |
| 622 | 540 | } |
| 623 | 541 | |
| 624 | | void s14001a_set_clock(device_t *device, int clock) |
| 542 | void s14001a_device::set_clock(int clock) |
| 625 | 543 | { |
| 626 | | S14001AChip *chip = get_safe_token(device); |
| 627 | | chip->stream->set_sample_rate(clock); |
| 544 | m_stream->set_sample_rate(clock); |
| 628 | 545 | } |
| 629 | 546 | |
| 630 | | void s14001a_set_volume(device_t *device, int volume) |
| 547 | void s14001a_device::set_volume(int volume) |
| 631 | 548 | { |
| 632 | | S14001AChip *chip = get_safe_token(device); |
| 633 | | chip->stream->update(); |
| 634 | | chip->VSU1000_amp = volume; |
| 549 | m_stream->update(); |
| 550 | m_VSU1000_amp = volume; |
| 635 | 551 | } |
| 636 | 552 | |
| 637 | 553 | const device_type S14001A = &device_creator<s14001a_device>; |
| 638 | 554 | |
| 639 | 555 | s14001a_device::s14001a_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) |
| 640 | 556 | : device_t(mconfig, S14001A, "S14001A", tag, owner, clock, "s14001a", __FILE__), |
| 641 | | device_sound_interface(mconfig, *this) |
| 557 | device_sound_interface(mconfig, *this), |
| 558 | m_stream(NULL), |
| 559 | m_WordInput(0), |
| 560 | m_LatchedWord(0), |
| 561 | m_SyllableAddress(0), |
| 562 | m_PhoneAddress(0), |
| 563 | m_PlayParams(0), |
| 564 | m_PhoneOffset(0), |
| 565 | m_LengthCounter(0), |
| 566 | m_RepeatCounter(0), |
| 567 | m_OutputCounter(0), |
| 568 | m_machineState(0), |
| 569 | m_nextstate(0), |
| 570 | m_laststate(0), |
| 571 | m_resetState(0), |
| 572 | m_oddeven(0), |
| 573 | m_GlobalSilenceState(1), |
| 574 | m_OldDelta(0x02), |
| 575 | m_DACOutput(SILENCE), |
| 576 | m_audioout(0), |
| 577 | m_SpeechRom(NULL), |
| 578 | m_VSU1000_amp(0) |
| 642 | 579 | { |
| 643 | | m_token = global_alloc_clear(S14001AChip); |
| 644 | 580 | } |
| 645 | 581 | |
| 646 | 582 | //------------------------------------------------- |
| r24635 | r24636 | |
| 659 | 595 | |
| 660 | 596 | void s14001a_device::device_start() |
| 661 | 597 | { |
| 662 | | DEVICE_START_NAME( s14001a )(this); |
| 598 | int i; |
| 599 | |
| 600 | m_GlobalSilenceState = 1; |
| 601 | m_OldDelta = 0x02; |
| 602 | m_DACOutput = SILENCE; |
| 603 | |
| 604 | for (i = 0; i < 8; i++) |
| 605 | { |
| 606 | m_filtervals[i] = SILENCE; |
| 607 | } |
| 608 | |
| 609 | m_SpeechRom = *region(); |
| 610 | |
| 611 | m_stream = machine().sound().stream_alloc(*this, 0, 1, clock() ? clock() : machine().sample_rate(), this); |
| 663 | 612 | } |
| 664 | 613 | |
| 665 | 614 | //------------------------------------------------- |
| r24635 | r24636 | |
| 668 | 617 | |
| 669 | 618 | void s14001a_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples) |
| 670 | 619 | { |
| 671 | | // should never get here |
| 672 | | fatalerror("sound_stream_update called; not applicable to legacy sound devices\n"); |
| 620 | int i; |
| 621 | |
| 622 | for (i = 0; i < samples; i++) |
| 623 | { |
| 624 | s14001a_clock(); |
| 625 | #ifdef ACCURATE_SQUEAL |
| 626 | if (m_audioout == ALTFLAG) // input from test pins -> output |
| 627 | { |
| 628 | shiftIntoFilter(chip, audiofilter(chip)); // shift over the previous outputs and stick in audioout. |
| 629 | outputs[0][i] = audiofilter(chip)*m_VSU1000_amp; |
| 630 | } |
| 631 | else // normal, dac-driven output |
| 632 | { |
| 633 | shiftIntoFilter(chip, ((((INT16)m_audioout)-8)<<9)); // shift over the previous outputs and stick in audioout 4 times. note <<9 instead of <<10, to prevent clipping, and to simulate that the filtered output normally has a somewhat lower amplitude than the driven one. |
| 634 | #endif |
| 635 | outputs[0][i] = ((((INT16)m_audioout)-8)<<10)*m_VSU1000_amp; |
| 636 | #ifdef ACCURATE_SQUEAL |
| 637 | } |
| 638 | #endif |
| 639 | } |
| 673 | 640 | } |