trunk/src/emu/sound/tms5220.c
| r22818 | r22819 | |
| 251 | 251 | #include "emu.h" |
| 252 | 252 | #include "tms5220.h" |
| 253 | 253 | |
| 254 | static INT16 clip_analog(INT16 cliptemp); |
| 255 | |
| 254 | 256 | /* *****optional defines***** */ |
| 255 | 257 | |
| 256 | 258 | /* Hacky improvements which don't match patent: */ |
| r22818 | r22819 | |
| 258 | 260 | * One of the following two lines should be used, and the other commented |
| 259 | 261 | * The second line is more accurate mathematically but not accurate to the patent |
| 260 | 262 | */ |
| 261 | | #define INTERP_SHIFT >> tms->coeff->interp_coeff[tms->IP] |
| 262 | | //define INTERP_SHIFT / (1<<tms->coeff->interp_coeff[tms->IP]) |
| 263 | #define INTERP_SHIFT >> m_coeff->interp_coeff[m_IP] |
| 264 | //define INTERP_SHIFT / (1<<m_coeff->interp_coeff[m_IP]) |
| 263 | 265 | |
| 264 | 266 | /* Other hacks */ |
| 265 | 267 | /* HACK?: if defined, outputs the low 4 bits of the lattice filter to the i/o |
| r22818 | r22819 | |
| 267 | 269 | * ...actually the tms5220c might legitamately do this! */ |
| 268 | 270 | #undef ALLOW_4_LSB |
| 269 | 271 | |
| 270 | | /* HACK: if defined, uses impossibly perfect 'straight line' interpolation */ |
| 271 | | #undef PERFECT_INTERPOLATION_HACK |
| 272 | 272 | |
| 273 | | |
| 274 | 273 | /* *****configuration of chip connection stuff***** */ |
| 275 | 274 | /* must be defined; if 0, output the waveform as if it was tapped on the speaker pin as usual, if 1, output the waveform as if it was tapped on the i/o pin (volume is much lower in the latter case) */ |
| 276 | 275 | #define FORCE_DIGITAL 0 |
| r22818 | r22819 | |
| 323 | 322 | |
| 324 | 323 | static const UINT8 reload_table[4] = { 0, 2, 4, 6 }; //sample count reload for 5220c only; 5200 and 5220 always reload with 0; keep in mind this is loaded on IP=0 PC=12 subcycle=1 so it immediately will increment after one sample, effectively being 1,3,5,7 as in the comments above. |
| 325 | 324 | |
| 326 | | struct tms5220_state |
| 327 | | { |
| 328 | | /* coefficient tables */ |
| 329 | | int variant; /* Variant of the 5xxx - see tms5110r.h */ |
| 330 | | |
| 331 | | /* coefficient tables */ |
| 332 | | const struct tms5100_coeffs *coeff; |
| 333 | | |
| 334 | | /* callbacks */ |
| 335 | | devcb_resolved_write_line irq_func; |
| 336 | | devcb_resolved_write_line readyq_func; |
| 337 | | |
| 338 | | /* these contain data that describes the 128-bit data FIFO */ |
| 339 | | UINT8 fifo[FIFO_SIZE]; |
| 340 | | UINT8 fifo_head; |
| 341 | | UINT8 fifo_tail; |
| 342 | | UINT8 fifo_count; |
| 343 | | UINT8 fifo_bits_taken; |
| 344 | | |
| 345 | | |
| 346 | | /* these contain global status bits */ |
| 347 | | UINT8 speaking_now; /* True only if actual speech is being generated right now. Is set when a speak vsm command happens OR when speak external happens and buffer low becomes nontrue; Is cleared when speech halts after the last stop frame or the last frame after talk status is otherwise cleared.*/ |
| 348 | | UINT8 speak_external; /* If 1, DDIS is 1, i.e. Speak External command in progress, writes go to FIFO. */ |
| 349 | | UINT8 talk_status; /* If 1, TS status bit is 1, i.e. speak or speak external is in progress and we have not encountered a stop frame yet; talk_status differs from speaking_now in that speaking_now is set as soon as a speak or speak external command is started; talk_status does NOT go active until after 8 bytes are written to the fifo on a speak external command, otherwise the two are the same. TS is cleared by 3 things: 1. when a STOP command has just been processed as a new frame in the speech stream; 2. if the fifo runs out in speak external mode; 3. on power-up/during a reset command; When it gets cleared, speak_external is also cleared, an interrupt is generated, and speaking_now will be cleared when the next frame starts. */ |
| 350 | | UINT8 buffer_low; /* If 1, FIFO has less than 8 bytes in it */ |
| 351 | | UINT8 buffer_empty; /* If 1, FIFO is empty */ |
| 352 | | UINT8 irq_pin; /* state of the IRQ pin (output) */ |
| 353 | | UINT8 ready_pin; /* state of the READY pin (output) */ |
| 354 | | |
| 355 | | /* these contain data describing the current and previous voice frames */ |
| 356 | | #define OLD_FRAME_SILENCE_FLAG tms->OLDE // 1 if E=0, 0 otherwise. |
| 357 | | #define OLD_FRAME_UNVOICED_FLAG tms->OLDP // 1 if P=0 (unvoiced), 0 if voiced |
| 358 | | UINT8 OLDE; |
| 359 | | UINT8 OLDP; |
| 360 | | |
| 361 | | #define NEW_FRAME_STOP_FLAG (tms->new_frame_energy_idx == 0xF) // 1 if this is a stop (Energy = 0xF) frame |
| 362 | | #define NEW_FRAME_SILENCE_FLAG (tms->new_frame_energy_idx == 0) // ditto as above |
| 363 | | #define NEW_FRAME_UNVOICED_FLAG (tms->new_frame_pitch_idx == 0) // ditto as above |
| 364 | | UINT8 new_frame_energy_idx; |
| 365 | | UINT8 new_frame_pitch_idx; |
| 366 | | UINT8 new_frame_k_idx[10]; |
| 367 | | |
| 368 | | |
| 369 | | /* these are all used to contain the current state of the sound generation */ |
| 370 | | #ifndef PERFECT_INTERPOLATION_HACK |
| 371 | | INT16 current_energy; |
| 372 | | INT16 current_pitch; |
| 373 | | INT16 current_k[10]; |
| 374 | | |
| 375 | | INT16 target_energy; |
| 376 | | INT16 target_pitch; |
| 377 | | INT16 target_k[10]; |
| 378 | | #else |
| 379 | | UINT8 old_frame_energy_idx; |
| 380 | | UINT8 old_frame_pitch_idx; |
| 381 | | UINT8 old_frame_k_idx[10]; |
| 382 | | |
| 383 | | INT32 current_energy; |
| 384 | | INT32 current_pitch; |
| 385 | | INT32 current_k[10]; |
| 386 | | |
| 387 | | INT32 target_energy; |
| 388 | | INT32 target_pitch; |
| 389 | | INT32 target_k[10]; |
| 390 | | #endif |
| 391 | | |
| 392 | | UINT16 previous_energy; /* needed for lattice filter to match patent */ |
| 393 | | |
| 394 | | UINT8 subcycle; /* contains the current subcycle for a given PC: 0 is A' (only used on SPKSLOW mode on 51xx), 1 is A, 2 is B */ |
| 395 | | UINT8 subc_reload; /* contains 1 for normal speech, 0 when SPKSLOW is active */ |
| 396 | | UINT8 PC; /* current parameter counter (what param is being interpolated), ranges from 0 to 12 */ |
| 397 | | /* TODO/NOTE: the current interpolation period, counts 1,2,3,4,5,6,7,0 for divide by 8,8,8,4,4,2,2,1 */ |
| 398 | | UINT8 IP; /* the current interpolation period */ |
| 399 | | UINT8 inhibit; /* If 1, interpolation is inhibited until the DIV1 period */ |
| 400 | | UINT8 tms5220c_rate; /* only relevant for tms5220C's multi frame rate feature; is the actual 4 bit value written on a 0x2* or 0x0* command */ |
| 401 | | UINT16 pitch_count; /* pitch counter; provides chirp rom address */ |
| 402 | | |
| 403 | | INT32 u[11]; |
| 404 | | INT32 x[10]; |
| 405 | | |
| 406 | | UINT16 RNG; /* the random noise generator configuration is: 1 + x + x^3 + x^4 + x^13 */ |
| 407 | | INT16 excitation_data; |
| 408 | | |
| 409 | | /* R Nabet : These have been added to emulate speech Roms */ |
| 410 | | UINT8 schedule_dummy_read; /* set after each load address, so that next read operation is preceded by a dummy read */ |
| 411 | | UINT8 data_register; /* data register, used by read command */ |
| 412 | | UINT8 RDB_flag; /* whether we should read data register or status register */ |
| 413 | | |
| 414 | | /* io_ready: page 3 of the datasheet specifies that READY will be asserted until |
| 415 | | * data is available or processed by the system. |
| 416 | | */ |
| 417 | | UINT8 io_ready; |
| 418 | | |
| 419 | | /* flag for "true" timing involving rs/ws */ |
| 420 | | UINT8 true_timing; |
| 421 | | |
| 422 | | /* rsws - state, rs bit 1, ws bit 0 */ |
| 423 | | UINT8 rs_ws; |
| 424 | | UINT8 read_latch; |
| 425 | | UINT8 write_latch; |
| 426 | | |
| 427 | | /* The TMS52xx has two different ways of providing output data: the |
| 428 | | analog speaker pin (which was usually used) and the Digital I/O pin. |
| 429 | | The internal DAC used to feed the analog pin is only 8 bits, and has the |
| 430 | | funny clipping/clamping logic, while the digital pin gives full 10 bit |
| 431 | | resolution of the output data. |
| 432 | | TODO: add a way to set/reset this other than the FORCE_DIGITAL define |
| 433 | | */ |
| 434 | | UINT8 digital_select; |
| 435 | | device_t *device; |
| 436 | | |
| 437 | | const tms5220_interface *intf; |
| 438 | | sound_stream *stream; |
| 439 | | int clock; |
| 440 | | }; |
| 441 | | |
| 442 | | |
| 443 | 325 | // Pull in the ROM tables |
| 444 | 326 | #include "tms5110r.c" |
| 445 | 327 | |
| 446 | 328 | |
| 447 | | INLINE tms5220_state *get_safe_token(device_t *device) |
| 329 | void tms5220_device::set_variant(int variant) |
| 448 | 330 | { |
| 449 | | assert(device != NULL); |
| 450 | | assert(device->type() == TMS5220 || |
| 451 | | device->type() == TMS5220C || |
| 452 | | device->type() == TMC0285 || |
| 453 | | device->type() == TMS5200); |
| 454 | | return (tms5220_state *)downcast<tms5220_device *>(device)->token(); |
| 455 | | } |
| 456 | | |
| 457 | | /* Static function prototypes */ |
| 458 | | static void process_command(tms5220_state *tms, unsigned char data); |
| 459 | | static void parse_frame(tms5220_state *tms); |
| 460 | | static void update_status_and_ints(tms5220_state *tms); |
| 461 | | static void set_interrupt_state(tms5220_state *tms, int state); |
| 462 | | static INT32 lattice_filter(tms5220_state *tms); |
| 463 | | static INT16 clip_analog(INT16 clip); |
| 464 | | static void update_ready_state(tms5220_state *tms); |
| 465 | | static STREAM_UPDATE( tms5220_update ); |
| 466 | | |
| 467 | | static void tms5220_set_variant(tms5220_state *tms, int variant) |
| 468 | | { |
| 469 | 331 | switch (variant) |
| 470 | 332 | { |
| 471 | 333 | case TMS5220_IS_5200: |
| 472 | | tms->coeff = &tms5200_coeff; |
| 334 | m_coeff = &tms5200_coeff; |
| 473 | 335 | break; |
| 474 | 336 | case TMS5220_IS_5220C: |
| 475 | 337 | case TMS5220_IS_5220: |
| 476 | | tms->coeff = &tms5220_coeff; |
| 338 | m_coeff = &tms5220_coeff; |
| 477 | 339 | break; |
| 478 | 340 | default: |
| 479 | 341 | fatalerror("Unknown variant in tms5220_set_variant\n"); |
| 480 | 342 | } |
| 481 | | tms->variant = variant; |
| 343 | m_variant = variant; |
| 482 | 344 | } |
| 483 | 345 | |
| 484 | 346 | |
| 485 | | static void register_for_save_states(tms5220_state *tms) |
| 347 | void tms5220_device::register_for_save_states() |
| 486 | 348 | { |
| 487 | | tms->device->save_item(NAME(tms->fifo)); |
| 488 | | tms->device->save_item(NAME(tms->fifo_head)); |
| 489 | | tms->device->save_item(NAME(tms->fifo_tail)); |
| 490 | | tms->device->save_item(NAME(tms->fifo_count)); |
| 491 | | tms->device->save_item(NAME(tms->fifo_bits_taken)); |
| 349 | save_item(NAME(m_fifo)); |
| 350 | save_item(NAME(m_fifo_head)); |
| 351 | save_item(NAME(m_fifo_tail)); |
| 352 | save_item(NAME(m_fifo_count)); |
| 353 | save_item(NAME(m_fifo_bits_taken)); |
| 492 | 354 | |
| 493 | | tms->device->save_item(NAME(tms->speaking_now)); |
| 494 | | tms->device->save_item(NAME(tms->speak_external)); |
| 495 | | tms->device->save_item(NAME(tms->talk_status)); |
| 496 | | tms->device->save_item(NAME(tms->buffer_low)); |
| 497 | | tms->device->save_item(NAME(tms->buffer_empty)); |
| 498 | | tms->device->save_item(NAME(tms->irq_pin)); |
| 499 | | tms->device->save_item(NAME(tms->ready_pin)); |
| 355 | save_item(NAME(m_speaking_now)); |
| 356 | save_item(NAME(m_speak_external)); |
| 357 | save_item(NAME(m_talk_status)); |
| 358 | save_item(NAME(m_buffer_low)); |
| 359 | save_item(NAME(m_buffer_empty)); |
| 360 | save_item(NAME(m_irq_pin)); |
| 361 | save_item(NAME(m_ready_pin)); |
| 500 | 362 | |
| 501 | | tms->device->save_item(NAME(tms->OLDE)); |
| 502 | | tms->device->save_item(NAME(tms->OLDP)); |
| 363 | save_item(NAME(m_OLDE)); |
| 364 | save_item(NAME(m_OLDP)); |
| 503 | 365 | |
| 504 | | tms->device->save_item(NAME(tms->new_frame_energy_idx)); |
| 505 | | tms->device->save_item(NAME(tms->new_frame_pitch_idx)); |
| 506 | | tms->device->save_item(NAME(tms->new_frame_k_idx)); |
| 366 | save_item(NAME(m_new_frame_energy_idx)); |
| 367 | save_item(NAME(m_new_frame_pitch_idx)); |
| 368 | save_item(NAME(m_new_frame_k_idx)); |
| 507 | 369 | #ifdef PERFECT_INTERPOLATION_HACK |
| 508 | | tms->device->save_item(NAME(tms->old_frame_energy_idx)); |
| 509 | | tms->device->save_item(NAME(tms->old_frame_pitch_idx)); |
| 510 | | tms->device->save_item(NAME(tms->old_frame_k_idx)); |
| 370 | save_item(NAME(m_old_frame_energy_idx)); |
| 371 | save_item(NAME(m_old_frame_pitch_idx)); |
| 372 | save_item(NAME(m_old_frame_k_idx)); |
| 511 | 373 | #endif |
| 512 | | tms->device->save_item(NAME(tms->current_energy)); |
| 513 | | tms->device->save_item(NAME(tms->current_pitch)); |
| 514 | | tms->device->save_item(NAME(tms->current_k)); |
| 374 | save_item(NAME(m_current_energy)); |
| 375 | save_item(NAME(m_current_pitch)); |
| 376 | save_item(NAME(m_current_k)); |
| 515 | 377 | |
| 516 | | tms->device->save_item(NAME(tms->target_energy)); |
| 517 | | tms->device->save_item(NAME(tms->target_pitch)); |
| 518 | | tms->device->save_item(NAME(tms->target_k)); |
| 378 | save_item(NAME(m_target_energy)); |
| 379 | save_item(NAME(m_target_pitch)); |
| 380 | save_item(NAME(m_target_k)); |
| 519 | 381 | |
| 520 | | tms->device->save_item(NAME(tms->previous_energy)); |
| 382 | save_item(NAME(m_previous_energy)); |
| 521 | 383 | |
| 522 | | tms->device->save_item(NAME(tms->subcycle)); |
| 523 | | tms->device->save_item(NAME(tms->subc_reload)); |
| 524 | | tms->device->save_item(NAME(tms->PC)); |
| 525 | | tms->device->save_item(NAME(tms->IP)); |
| 526 | | tms->device->save_item(NAME(tms->inhibit)); |
| 527 | | tms->device->save_item(NAME(tms->tms5220c_rate)); |
| 528 | | tms->device->save_item(NAME(tms->pitch_count)); |
| 384 | save_item(NAME(m_subcycle)); |
| 385 | save_item(NAME(m_subc_reload)); |
| 386 | save_item(NAME(m_PC)); |
| 387 | save_item(NAME(m_IP)); |
| 388 | save_item(NAME(m_inhibit)); |
| 389 | save_item(NAME(m_tms5220c_rate)); |
| 390 | save_item(NAME(m_pitch_count)); |
| 529 | 391 | |
| 530 | | tms->device->save_item(NAME(tms->u)); |
| 531 | | tms->device->save_item(NAME(tms->x)); |
| 392 | save_item(NAME(m_u)); |
| 393 | save_item(NAME(m_x)); |
| 532 | 394 | |
| 533 | | tms->device->save_item(NAME(tms->RNG)); |
| 534 | | tms->device->save_item(NAME(tms->excitation_data)); |
| 395 | save_item(NAME(m_RNG)); |
| 396 | save_item(NAME(m_excitation_data)); |
| 535 | 397 | |
| 536 | | tms->device->save_item(NAME(tms->schedule_dummy_read)); |
| 537 | | tms->device->save_item(NAME(tms->data_register)); |
| 538 | | tms->device->save_item(NAME(tms->RDB_flag)); |
| 539 | | tms->device->save_item(NAME(tms->digital_select)); |
| 398 | save_item(NAME(m_schedule_dummy_read)); |
| 399 | save_item(NAME(m_data_register)); |
| 400 | save_item(NAME(m_RDB_flag)); |
| 401 | save_item(NAME(m_digital_select)); |
| 540 | 402 | |
| 541 | | tms->device->save_item(NAME(tms->io_ready)); |
| 403 | save_item(NAME(m_io_ready)); |
| 542 | 404 | } |
| 543 | 405 | |
| 544 | 406 | |
| r22818 | r22819 | |
| 586 | 448 | |
| 587 | 449 | ***********************************************************************************************/ |
| 588 | 450 | |
| 589 | | static void tms5220_data_write(tms5220_state *tms, int data) |
| 451 | void tms5220_device::data_write(int data) |
| 590 | 452 | { |
| 591 | 453 | #ifdef DEBUG_DUMP_INPUT_DATA |
| 592 | 454 | fprintf(stdout, "%c",data); |
| 593 | 455 | #endif |
| 594 | | if (tms->speak_external) // If we're in speak external mode |
| 456 | if (m_speak_external) // If we're in speak external mode |
| 595 | 457 | { |
| 596 | 458 | // add this byte to the FIFO |
| 597 | | if (tms->fifo_count < FIFO_SIZE) |
| 459 | if (m_fifo_count < FIFO_SIZE) |
| 598 | 460 | { |
| 599 | | tms->fifo[tms->fifo_tail] = data; |
| 600 | | tms->fifo_tail = (tms->fifo_tail + 1) % FIFO_SIZE; |
| 601 | | tms->fifo_count++; |
| 461 | m_fifo[m_fifo_tail] = data; |
| 462 | m_fifo_tail = (m_fifo_tail + 1) % FIFO_SIZE; |
| 463 | m_fifo_count++; |
| 602 | 464 | #ifdef DEBUG_FIFO |
| 603 | | logerror("data_write: Added byte to FIFO (current count=%2d)\n", tms->fifo_count); |
| 465 | logerror("data_write: Added byte to FIFO (current count=%2d)\n", m_fifo_count); |
| 604 | 466 | #endif |
| 605 | | update_status_and_ints(tms); |
| 606 | | if ((tms->talk_status == 0) && (tms->buffer_low == 0)) // we just unset buffer low with that last write, and talk status *was* zero... |
| 467 | update_status_and_ints(); |
| 468 | if ((m_talk_status == 0) && (m_buffer_low == 0)) // we just unset buffer low with that last write, and talk status *was* zero... |
| 607 | 469 | { |
| 608 | 470 | int i; |
| 609 | 471 | #ifdef DEBUG_FIFO |
| r22818 | r22819 | |
| 611 | 473 | #endif |
| 612 | 474 | // ...then we now have enough bytes to start talking; clear out the new frame parameters (it will become old frame just before the first call to parse_frame() ) |
| 613 | 475 | // TODO: the 3 lines below (and others) are needed for victory to not fail its selftest due to a sample ending too late, may require additional investigation |
| 614 | | tms->subcycle = tms->subc_reload; |
| 615 | | tms->PC = 0; |
| 616 | | tms->IP = reload_table[tms->tms5220c_rate&0x3]; // is this correct? should this be always 7 instead, so that the new frame is loaded quickly? |
| 617 | | tms->new_frame_energy_idx = 0; |
| 618 | | tms->new_frame_pitch_idx = 0; |
| 476 | m_subcycle = m_subc_reload; |
| 477 | m_PC = 0; |
| 478 | m_IP = reload_table[m_tms5220c_rate&0x3]; // is this correct? should this be always 7 instead, so that the new frame is loaded quickly? |
| 479 | m_new_frame_energy_idx = 0; |
| 480 | m_new_frame_pitch_idx = 0; |
| 619 | 481 | for (i = 0; i < 4; i++) |
| 620 | | tms->new_frame_k_idx[i] = 0; |
| 482 | m_new_frame_k_idx[i] = 0; |
| 621 | 483 | for (i = 4; i < 7; i++) |
| 622 | | tms->new_frame_k_idx[i] = 0xF; |
| 623 | | for (i = 7; i < tms->coeff->num_k; i++) |
| 624 | | tms->new_frame_k_idx[i] = 0x7; |
| 625 | | tms->talk_status = tms->speaking_now = 1; |
| 484 | m_new_frame_k_idx[i] = 0xF; |
| 485 | for (i = 7; i < m_coeff->num_k; i++) |
| 486 | m_new_frame_k_idx[i] = 0x7; |
| 487 | m_talk_status = m_speaking_now = 1; |
| 626 | 488 | } |
| 627 | 489 | } |
| 628 | 490 | else |
| r22818 | r22819 | |
| 635 | 497 | |
| 636 | 498 | |
| 637 | 499 | } |
| 638 | | else //(! tms->speak_external) |
| 500 | else //(! m_speak_external) |
| 639 | 501 | // R Nabet : we parse commands at once. It is necessary for such commands as read. |
| 640 | | process_command(tms,data); |
| 502 | process_command(data); |
| 641 | 503 | } |
| 642 | 504 | |
| 643 | 505 | /********************************************************************************************** |
| r22818 | r22819 | |
| 663 | 525 | |
| 664 | 526 | ***********************************************************************************************/ |
| 665 | 527 | |
| 666 | | static void update_status_and_ints(tms5220_state *tms) |
| 528 | void tms5220_device::update_status_and_ints() |
| 667 | 529 | { |
| 668 | 530 | /* update flags and set ints if needed */ |
| 669 | 531 | |
| 670 | | update_ready_state(tms); |
| 532 | update_ready_state(); |
| 671 | 533 | |
| 672 | 534 | /* BL is set if neither byte 9 nor 8 of the fifo are in use; this |
| 673 | 535 | translates to having fifo_count (which ranges from 0 bytes in use to 16 |
| 674 | 536 | bytes used) being less than or equal to 8. Victory/Victorba depends on this. */ |
| 675 | | if (tms->fifo_count <= 8) |
| 537 | if (m_fifo_count <= 8) |
| 676 | 538 | { |
| 677 | 539 | // generate an interrupt if necessary; if /BL was inactive and is now active, set int. |
| 678 | | if (!tms->buffer_low) |
| 679 | | set_interrupt_state(tms, 1); |
| 680 | | tms->buffer_low = 1; |
| 540 | if (!m_buffer_low) |
| 541 | set_interrupt_state(1); |
| 542 | m_buffer_low = 1; |
| 681 | 543 | } |
| 682 | 544 | else |
| 683 | | tms->buffer_low = 0; |
| 545 | m_buffer_low = 0; |
| 684 | 546 | |
| 685 | 547 | /* BE is set if neither byte 15 nor 14 of the fifo are in use; this |
| 686 | 548 | translates to having fifo_count equal to exactly 0 */ |
| 687 | | if (tms->fifo_count == 0) |
| 549 | if (m_fifo_count == 0) |
| 688 | 550 | { |
| 689 | 551 | // generate an interrupt if necessary; if /BE was inactive and is now active, set int. |
| 690 | | if (!tms->buffer_empty) |
| 691 | | set_interrupt_state(tms, 1); |
| 692 | | tms->buffer_empty = 1; |
| 552 | if (!m_buffer_empty) |
| 553 | set_interrupt_state(1); |
| 554 | m_buffer_empty = 1; |
| 693 | 555 | } |
| 694 | 556 | else |
| 695 | | tms->buffer_empty = 0; |
| 557 | m_buffer_empty = 0; |
| 696 | 558 | |
| 697 | 559 | /* TS is talk status and is set elsewhere in the fifo parser and in |
| 698 | 560 | the SPEAK command handler; however, if /BE is true during speak external |
| 699 | 561 | mode, it is immediately unset here. */ |
| 700 | | if ((tms->speak_external == 1) && (tms->buffer_empty == 1)) |
| 562 | if ((m_speak_external == 1) && (m_buffer_empty == 1)) |
| 701 | 563 | { |
| 702 | 564 | // generate an interrupt: /TS was active, and is now inactive. |
| 703 | | if (tms->talk_status == 1) |
| 565 | if (m_talk_status == 1) |
| 704 | 566 | { |
| 705 | | tms->talk_status = tms->speak_external = 0; |
| 706 | | set_interrupt_state(tms, 1); |
| 567 | m_talk_status = m_speak_external = 0; |
| 568 | set_interrupt_state(1); |
| 707 | 569 | } |
| 708 | 570 | } |
| 709 | 571 | /* Note that TS being unset will also generate an interrupt when a STOP |
| r22818 | r22819 | |
| 716 | 578 | |
| 717 | 579 | ***********************************************************************************************/ |
| 718 | 580 | |
| 719 | | static int extract_bits(tms5220_state *tms, int count) |
| 581 | int tms5220_device::extract_bits(int count) |
| 720 | 582 | { |
| 721 | 583 | int val = 0; |
| 722 | 584 | |
| 723 | | if (tms->speak_external) |
| 585 | if (m_speak_external) |
| 724 | 586 | { |
| 725 | 587 | // extract from FIFO |
| 726 | 588 | while (count--) |
| 727 | 589 | { |
| 728 | | val = (val << 1) | ((tms->fifo[tms->fifo_head] >> tms->fifo_bits_taken) & 1); |
| 729 | | tms->fifo_bits_taken++; |
| 730 | | if (tms->fifo_bits_taken >= 8) |
| 590 | val = (val << 1) | ((m_fifo[m_fifo_head] >> m_fifo_bits_taken) & 1); |
| 591 | m_fifo_bits_taken++; |
| 592 | if (m_fifo_bits_taken >= 8) |
| 731 | 593 | { |
| 732 | | tms->fifo_count--; |
| 733 | | tms->fifo[tms->fifo_head] = 0; // zero the newly depleted fifo head byte |
| 734 | | tms->fifo_head = (tms->fifo_head + 1) % FIFO_SIZE; |
| 735 | | tms->fifo_bits_taken = 0; |
| 736 | | update_status_and_ints(tms); |
| 594 | m_fifo_count--; |
| 595 | m_fifo[m_fifo_head] = 0; // zero the newly depleted fifo head byte |
| 596 | m_fifo_head = (m_fifo_head + 1) % FIFO_SIZE; |
| 597 | m_fifo_bits_taken = 0; |
| 598 | update_status_and_ints(); |
| 737 | 599 | } |
| 738 | 600 | } |
| 739 | 601 | } |
| 740 | 602 | else |
| 741 | 603 | { |
| 742 | 604 | // extract from VSM (speech ROM) |
| 743 | | if (tms->intf->read) |
| 744 | | val = (* tms->intf->read)(tms->device, count); |
| 605 | if (m_speechrom) |
| 606 | val = m_speechrom->read(count); |
| 745 | 607 | } |
| 746 | 608 | |
| 747 | 609 | return val; |
| r22818 | r22819 | |
| 753 | 615 | |
| 754 | 616 | ***********************************************************************************************/ |
| 755 | 617 | |
| 756 | | static int tms5220_status_read(tms5220_state *tms) |
| 618 | int tms5220_device::status_read() |
| 757 | 619 | { |
| 758 | | if (tms->RDB_flag) |
| 620 | if (m_RDB_flag) |
| 759 | 621 | { /* if last command was read, return data register */ |
| 760 | | tms->RDB_flag = FALSE; |
| 761 | | return(tms->data_register); |
| 622 | m_RDB_flag = FALSE; |
| 623 | return(m_data_register); |
| 762 | 624 | } |
| 763 | 625 | else |
| 764 | 626 | { /* read status */ |
| 765 | 627 | |
| 766 | 628 | /* clear the interrupt pin on status read */ |
| 767 | | set_interrupt_state(tms, 0); |
| 629 | set_interrupt_state(0); |
| 768 | 630 | #ifdef DEBUG_PIN_READS |
| 769 | | logerror("Status read: TS=%d BL=%d BE=%d\n", tms->talk_status, tms->buffer_low, tms->buffer_empty); |
| 631 | logerror("Status read: TS=%d BL=%d BE=%d\n", m_talk_status, m_buffer_low, m_buffer_empty); |
| 770 | 632 | #endif |
| 771 | 633 | |
| 772 | | return (tms->talk_status << 7) | (tms->buffer_low << 6) | (tms->buffer_empty << 5); |
| 634 | return (m_talk_status << 7) | (m_buffer_low << 6) | (m_buffer_empty << 5); |
| 773 | 635 | } |
| 774 | 636 | } |
| 775 | 637 | |
| r22818 | r22819 | |
| 780 | 642 | |
| 781 | 643 | ***********************************************************************************************/ |
| 782 | 644 | |
| 783 | | static int tms5220_ready_read(tms5220_state *tms) |
| 645 | int tms5220_device::ready_read() |
| 784 | 646 | { |
| 785 | 647 | #ifdef DEBUG_PIN_READS |
| 786 | | logerror("ready_read: ready pin read, io_ready is %d, fifo count is %d\n", tms->io_ready, tms->fifo_count); |
| 648 | logerror("ready_read: ready pin read, io_ready is %d, fifo count is %d\n", m_io_ready, m_fifo_count); |
| 787 | 649 | #endif |
| 788 | | return ((tms->fifo_count < FIFO_SIZE)||(!tms->speak_external)) && tms->io_ready; |
| 650 | return ((m_fifo_count < FIFO_SIZE)||(!m_speak_external)) && m_io_ready; |
| 789 | 651 | } |
| 790 | 652 | |
| 791 | 653 | |
| r22818 | r22819 | |
| 797 | 659 | |
| 798 | 660 | ***********************************************************************************************/ |
| 799 | 661 | |
| 800 | | static int tms5220_cycles_to_ready(tms5220_state *tms) |
| 662 | int tms5220_device::cycles_to_ready() |
| 801 | 663 | { |
| 802 | 664 | int answer; |
| 803 | 665 | |
| 804 | 666 | |
| 805 | | if (tms5220_ready_read(tms)) |
| 667 | if (ready_read()) |
| 806 | 668 | answer = 0; |
| 807 | 669 | else |
| 808 | 670 | { |
| 809 | 671 | int val; |
| 810 | | int samples_per_frame = tms->subc_reload?200:304; // either (13 A cycles + 12 B cycles) * 8 interps for normal SPEAK/SPKEXT, or (13*2 A cycles + 12 B cycles) * 8 interps for SPKSLOW |
| 811 | | int current_sample = ((tms->PC*(3-tms->subc_reload))+((tms->subc_reload?38:25)*tms->IP)); |
| 672 | int samples_per_frame = m_subc_reload?200:304; // either (13 A cycles + 12 B cycles) * 8 interps for normal SPEAK/SPKEXT, or (13*2 A cycles + 12 B cycles) * 8 interps for SPKSLOW |
| 673 | int current_sample = ((m_PC*(3-m_subc_reload))+((m_subc_reload?38:25)*m_IP)); |
| 812 | 674 | answer = samples_per_frame-current_sample+8; |
| 813 | 675 | |
| 814 | | // total number of bits available in current byte is (8 - tms->fifo_bits_taken) |
| 676 | // total number of bits available in current byte is (8 - m_fifo_bits_taken) |
| 815 | 677 | // if more than 4 are available, we need to check the energy |
| 816 | | if (tms->fifo_bits_taken < 4) |
| 678 | if (m_fifo_bits_taken < 4) |
| 817 | 679 | { |
| 818 | 680 | // read energy |
| 819 | | val = (tms->fifo[tms->fifo_head] >> tms->fifo_bits_taken) & 0xf; |
| 681 | val = (m_fifo[m_fifo_head] >> m_fifo_bits_taken) & 0xf; |
| 820 | 682 | if (val == 0) |
| 821 | 683 | /* 0 -> silence frame: we will only read 4 bits, and we will |
| 822 | 684 | * therefore need to read another frame before the FIFO is not |
| 823 | 685 | * full any more */ |
| 824 | | answer += tms->subc_reload?200:304; |
| 686 | answer += m_subc_reload?200:304; |
| 825 | 687 | /* 15 -> stop frame, we will only read 4 bits, but the FIFO will |
| 826 | 688 | * we cleared; otherwise, we need to parse the repeat flag (1 bit) |
| 827 | 689 | * and the pitch (6 bits), so everything will be OK. */ |
| r22818 | r22819 | |
| 838 | 700 | |
| 839 | 701 | ***********************************************************************************************/ |
| 840 | 702 | |
| 841 | | static int tms5220_int_read(tms5220_state *tms) |
| 703 | int tms5220_device::int_read() |
| 842 | 704 | { |
| 843 | 705 | #ifdef DEBUG_PIN_READS |
| 844 | | logerror("int_read: irq pin read, state is %d\n", tms->irq_pin); |
| 706 | logerror("int_read: irq pin read, state is %d\n", m_irq_pin); |
| 845 | 707 | #endif |
| 846 | | return tms->irq_pin; |
| 708 | return m_irq_pin; |
| 847 | 709 | } |
| 848 | 710 | |
| 849 | 711 | |
| r22818 | r22819 | |
| 853 | 715 | |
| 854 | 716 | ***********************************************************************************************/ |
| 855 | 717 | |
| 856 | | static void tms5220_process(tms5220_state *tms, INT16 *buffer, unsigned int size) |
| 718 | void tms5220_device::process(INT16 *buffer, unsigned int size) |
| 857 | 719 | { |
| 858 | 720 | int buf_count=0; |
| 859 | 721 | int i, bitout, zpar; |
| r22818 | r22819 | |
| 861 | 723 | |
| 862 | 724 | /* the following gotos are probably safe to remove */ |
| 863 | 725 | /* if we're empty and still not speaking, fill with nothingness */ |
| 864 | | if (!tms->speaking_now) |
| 726 | if (!m_speaking_now) |
| 865 | 727 | goto empty; |
| 866 | 728 | |
| 867 | 729 | /* if speak external is set, but talk status is not (yet) set, |
| 868 | 730 | wait for buffer low to clear */ |
| 869 | | if (!tms->talk_status && tms->speak_external && tms->buffer_low) |
| 731 | if (!m_talk_status && m_speak_external && m_buffer_low) |
| 870 | 732 | goto empty; |
| 871 | 733 | |
| 872 | 734 | /* loop until the buffer is full or we've stopped speaking */ |
| 873 | | while ((size > 0) && tms->speaking_now) |
| 735 | while ((size > 0) && m_speaking_now) |
| 874 | 736 | { |
| 875 | 737 | /* if it is the appropriate time to update the old energy/pitch idxes, |
| 876 | 738 | * i.e. when IP=7, PC=12, T=17, subcycle=2, do so. Since IP=7 PC=12 T=17 |
| 877 | 739 | * is JUST BEFORE the transition to IP=0 PC=0 T=0 sybcycle=(0 or 1), |
| 878 | 740 | * which happens 4 T-cycles later), we change on the latter.*/ |
| 879 | | if ((tms->IP == 0) && (tms->PC == 0) && (tms->subcycle < 2)) |
| 741 | if ((m_IP == 0) && (m_PC == 0) && (m_subcycle < 2)) |
| 880 | 742 | { |
| 881 | | tms->OLDE = (tms->new_frame_energy_idx == 0); |
| 882 | | tms->OLDP = (tms->new_frame_pitch_idx == 0); |
| 743 | m_OLDE = (m_new_frame_energy_idx == 0); |
| 744 | m_OLDP = (m_new_frame_pitch_idx == 0); |
| 883 | 745 | } |
| 884 | 746 | |
| 885 | 747 | /* if we're ready for a new frame to be applied, i.e. when IP=0, PC=12, Sub=1 |
| 886 | 748 | * (In reality, the frame was really loaded incrementally during the entire IP=0 |
| 887 | 749 | * PC=x time period, but it doesn't affect anything until IP=0 PC=12 happens) |
| 888 | 750 | */ |
| 889 | | if ((tms->IP == 0) && (tms->PC == 12) && (tms->subcycle == 1)) |
| 751 | if ((m_IP == 0) && (m_PC == 12) && (m_subcycle == 1)) |
| 890 | 752 | { |
| 891 | 753 | // HACK for regression testing, be sure to comment out before release! |
| 892 | | //tms->RNG = 0x1234; |
| 754 | //m_RNG = 0x1234; |
| 893 | 755 | // end HACK |
| 894 | 756 | |
| 895 | 757 | /* appropriately override the interp count if needed; this will be incremented after the frame parse! */ |
| 896 | | tms->IP = reload_table[tms->tms5220c_rate&0x3]; |
| 758 | m_IP = reload_table[m_tms5220c_rate&0x3]; |
| 897 | 759 | |
| 898 | 760 | #ifdef PERFECT_INTERPOLATION_HACK |
| 899 | 761 | /* remember previous frame energy, pitch, and coefficients */ |
| 900 | | tms->old_frame_energy_idx = tms->new_frame_energy_idx; |
| 901 | | tms->old_frame_pitch_idx = tms->new_frame_pitch_idx; |
| 902 | | for (i = 0; i < tms->coeff->num_k; i++) |
| 903 | | tms->old_frame_k_idx[i] = tms->new_frame_k_idx[i]; |
| 762 | m_old_frame_energy_idx = m_new_frame_energy_idx; |
| 763 | m_old_frame_pitch_idx = m_new_frame_pitch_idx; |
| 764 | for (i = 0; i < m_coeff->num_k; i++) |
| 765 | m_old_frame_k_idx[i] = m_new_frame_k_idx[i]; |
| 904 | 766 | #endif |
| 905 | 767 | |
| 906 | 768 | /* if the talk status was clear last frame, halt speech now. */ |
| 907 | | if (tms->talk_status == 0) |
| 769 | if (m_talk_status == 0) |
| 908 | 770 | { |
| 909 | 771 | #ifdef DEBUG_GENERATION |
| 910 | 772 | fprintf(stderr,"tms5220_process: processing frame: talk status = 0 caused by stop frame or buffer empty, halting speech.\n"); |
| 911 | 773 | #endif |
| 912 | | tms->speaking_now = 0; // finally halt speech |
| 774 | m_speaking_now = 0; // finally halt speech |
| 913 | 775 | goto empty; |
| 914 | 776 | } |
| 915 | 777 | |
| 916 | 778 | |
| 917 | 779 | /* Parse a new frame into the new_target_energy, new_target_pitch and new_target_k[] */ |
| 918 | | parse_frame(tms); |
| 780 | parse_frame(); |
| 919 | 781 | #ifdef DEBUG_PARSE_FRAME_DUMP |
| 920 | 782 | fprintf(stderr,"\n"); |
| 921 | 783 | #endif |
| r22818 | r22819 | |
| 923 | 785 | /* if the new frame is a stop frame, set an interrupt and set talk status to 0 */ |
| 924 | 786 | if (NEW_FRAME_STOP_FLAG == 1) |
| 925 | 787 | { |
| 926 | | tms->talk_status = tms->speak_external = 0; |
| 927 | | set_interrupt_state(tms, 1); |
| 928 | | update_status_and_ints(tms); |
| 788 | m_talk_status = m_speak_external = 0; |
| 789 | set_interrupt_state(1); |
| 790 | update_status_and_ints(); |
| 929 | 791 | } |
| 930 | 792 | |
| 931 | 793 | /* in all cases where interpolation would be inhibited, set the inhibit flag; otherwise clear it. |
| r22818 | r22819 | |
| 937 | 799 | if ( ((OLD_FRAME_UNVOICED_FLAG == 0) && (NEW_FRAME_UNVOICED_FLAG == 1)) |
| 938 | 800 | || ((OLD_FRAME_UNVOICED_FLAG == 1) && (NEW_FRAME_UNVOICED_FLAG == 0)) /* this line needs further investigation, starwars tie fighters may sound better without it */ |
| 939 | 801 | || ((OLD_FRAME_SILENCE_FLAG == 1) && (NEW_FRAME_SILENCE_FLAG == 0)) ) |
| 940 | | tms->inhibit = 1; |
| 802 | m_inhibit = 1; |
| 941 | 803 | else // normal frame, normal interpolation |
| 942 | | tms->inhibit = 0; |
| 804 | m_inhibit = 0; |
| 943 | 805 | |
| 944 | 806 | /* load new frame targets from tables, using parsed indices */ |
| 945 | | tms->target_energy = tms->coeff->energytable[tms->new_frame_energy_idx]; |
| 946 | | tms->target_pitch = tms->coeff->pitchtable[tms->new_frame_pitch_idx]; |
| 807 | m_target_energy = m_coeff->energytable[m_new_frame_energy_idx]; |
| 808 | m_target_pitch = m_coeff->pitchtable[m_new_frame_pitch_idx]; |
| 947 | 809 | zpar = NEW_FRAME_UNVOICED_FLAG; // find out if parameters k5-k10 should be zeroed |
| 948 | 810 | for (i = 0; i < 4; i++) |
| 949 | | tms->target_k[i] = tms->coeff->ktable[i][tms->new_frame_k_idx[i]]; |
| 950 | | for (i = 4; i < tms->coeff->num_k; i++) |
| 951 | | tms->target_k[i] = (tms->coeff->ktable[i][tms->new_frame_k_idx[i]] * (1-zpar)); |
| 811 | m_target_k[i] = m_coeff->ktable[i][m_new_frame_k_idx[i]]; |
| 812 | for (i = 4; i < m_coeff->num_k; i++) |
| 813 | m_target_k[i] = (m_coeff->ktable[i][m_new_frame_k_idx[i]] * (1-zpar)); |
| 952 | 814 | |
| 953 | 815 | #ifdef DEBUG_GENERATION |
| 954 | 816 | /* Debug info for current parsed frame */ |
| 955 | | fprintf(stderr, "OLDE: %d; OLDP: %d; ", tms->OLDE, tms->OLDP); |
| 817 | fprintf(stderr, "OLDE: %d; OLDP: %d; ", m_OLDE, m_OLDP); |
| 956 | 818 | fprintf(stderr,"Processing frame: "); |
| 957 | | if (tms->inhibit == 0) |
| 819 | if (m_inhibit == 0) |
| 958 | 820 | fprintf(stderr, "Normal Frame\n"); |
| 959 | 821 | else |
| 960 | 822 | fprintf(stderr,"Interpolation Inhibited\n"); |
| 961 | | fprintf(stderr,"*** current Energy, Pitch and Ks = %04d, %04d, %04d, %04d, %04d, %04d, %04d, %04d, %04d, %04d, %04d, %04d\n",tms->current_energy, tms->current_pitch, tms->current_k[0], tms->current_k[1], tms->current_k[2], tms->current_k[3], tms->current_k[4], tms->current_k[5], tms->current_k[6], tms->current_k[7], tms->current_k[8], tms->current_k[9]); |
| 962 | | fprintf(stderr,"*** target Energy(idx), Pitch, and Ks = %04d(%x),%04d, %04d, %04d, %04d, %04d, %04d, %04d, %04d, %04d, %04d, %04d\n",tms->target_energy, tms->new_frame_energy_idx, tms->target_pitch, tms->target_k[0], tms->target_k[1], tms->target_k[2], tms->target_k[3], tms->target_k[4], tms->target_k[5], tms->target_k[6], tms->target_k[7], tms->target_k[8], tms->target_k[9]); |
| 823 | fprintf(stderr,"*** current Energy, Pitch and Ks = %04d, %04d, %04d, %04d, %04d, %04d, %04d, %04d, %04d, %04d, %04d, %04d\n",m_current_energy, m_current_pitch, m_current_k[0], m_current_k[1], m_current_k[2], m_current_k[3], m_current_k[4], m_current_k[5], m_current_k[6], m_current_k[7], m_current_k[8], m_current_k[9]); |
| 824 | fprintf(stderr,"*** target Energy(idx), Pitch, and Ks = %04d(%x),%04d, %04d, %04d, %04d, %04d, %04d, %04d, %04d, %04d, %04d, %04d\n",m_target_energy, m_new_frame_energy_idx, m_target_pitch, m_target_k[0], m_target_k[1], m_target_k[2], m_target_k[3], m_target_k[4], m_target_k[5], m_target_k[6], m_target_k[7], m_target_k[8], m_target_k[9]); |
| 963 | 825 | #endif |
| 964 | 826 | |
| 965 | 827 | /* if TS is now 0, ramp the energy down to 0. Is this really correct to hardware? */ |
| 966 | | if (tms->talk_status == 0) |
| 828 | if (m_talk_status == 0) |
| 967 | 829 | { |
| 968 | 830 | #ifdef DEBUG_GENERATION |
| 969 | 831 | fprintf(stderr,"Talk status is 0, forcing target energy to 0\n"); |
| 970 | 832 | #endif |
| 971 | | tms->target_energy = 0; |
| 833 | m_target_energy = 0; |
| 972 | 834 | } |
| 973 | 835 | } |
| 974 | 836 | else // Not a new frame, just interpolate the existing frame. |
| 975 | 837 | { |
| 976 | | int inhibit_state = ((tms->inhibit==1)&&(tms->IP != 0)); // disable inhibit when reaching the last interp period, but don't overwrite the tms->inhibit value |
| 838 | int inhibit_state = ((m_inhibit==1)&&(m_IP != 0)); // disable inhibit when reaching the last interp period, but don't overwrite the m_inhibit value |
| 977 | 839 | #ifdef PERFECT_INTERPOLATION_HACK |
| 978 | | int samples_per_frame = tms->subc_reload?175:266; // either (13 A cycles + 12 B cycles) * 7 interps for normal SPEAK/SPKEXT, or (13*2 A cycles + 12 B cycles) * 7 interps for SPKSLOW |
| 979 | | //int samples_per_frame = tms->subc_reload?200:304; // either (13 A cycles + 12 B cycles) * 8 interps for normal SPEAK/SPKEXT, or (13*2 A cycles + 12 B cycles) * 8 interps for SPKSLOW |
| 980 | | int current_sample = (tms->subcycle - tms->subc_reload)+(tms->PC*(3-tms->subc_reload))+((tms->subc_reload?25:38)*((tms->IP-1)&7)); |
| 840 | int samples_per_frame = m_subc_reload?175:266; // either (13 A cycles + 12 B cycles) * 7 interps for normal SPEAK/SPKEXT, or (13*2 A cycles + 12 B cycles) * 7 interps for SPKSLOW |
| 841 | //int samples_per_frame = m_subc_reload?200:304; // either (13 A cycles + 12 B cycles) * 8 interps for normal SPEAK/SPKEXT, or (13*2 A cycles + 12 B cycles) * 8 interps for SPKSLOW |
| 842 | int current_sample = (m_subcycle - m_subc_reload)+(m_PC*(3-m_subc_reload))+((m_subc_reload?25:38)*((m_IP-1)&7)); |
| 981 | 843 | |
| 982 | 844 | zpar = OLD_FRAME_UNVOICED_FLAG; |
| 983 | 845 | //fprintf(stderr, "CS: %03d", current_sample); |
| 984 | 846 | // reset the current energy, pitch, etc to what it was at frame start |
| 985 | | tms->current_energy = tms->coeff->energytable[tms->old_frame_energy_idx]; |
| 986 | | tms->current_pitch = tms->coeff->pitchtable[tms->old_frame_pitch_idx]; |
| 847 | m_current_energy = m_coeff->energytable[m_old_frame_energy_idx]; |
| 848 | m_current_pitch = m_coeff->pitchtable[m_old_frame_pitch_idx]; |
| 987 | 849 | for (i = 0; i < 4; i++) |
| 988 | | tms->current_k[i] = tms->coeff->ktable[i][tms->old_frame_k_idx[i]]; |
| 989 | | for (i = 4; i < tms->coeff->num_k; i++) |
| 990 | | tms->current_k[i] = (tms->coeff->ktable[i][tms->old_frame_k_idx[i]] * (1-zpar)); |
| 850 | m_current_k[i] = m_coeff->ktable[i][m_old_frame_k_idx[i]]; |
| 851 | for (i = 4; i < m_coeff->num_k; i++) |
| 852 | m_current_k[i] = (m_coeff->ktable[i][m_old_frame_k_idx[i]] * (1-zpar)); |
| 991 | 853 | // now adjust each value to be exactly correct for each of the samples per frame |
| 992 | | if (tms->IP != 0) // if we're still interpolating... |
| 854 | if (m_IP != 0) // if we're still interpolating... |
| 993 | 855 | { |
| 994 | | tms->current_energy += (((tms->target_energy - tms->current_energy)*(1-inhibit_state))*current_sample)/samples_per_frame; |
| 995 | | tms->current_pitch += (((tms->target_pitch - tms->current_pitch)*(1-inhibit_state))*current_sample)/samples_per_frame; |
| 996 | | for (i = 0; i < tms->coeff->num_k; i++) |
| 997 | | tms->current_k[i] += (((tms->target_k[i] - tms->current_k[i])*(1-inhibit_state))*current_sample)/samples_per_frame; |
| 856 | m_current_energy += (((m_target_energy - m_current_energy)*(1-inhibit_state))*current_sample)/samples_per_frame; |
| 857 | m_current_pitch += (((m_target_pitch - m_current_pitch)*(1-inhibit_state))*current_sample)/samples_per_frame; |
| 858 | for (i = 0; i < m_coeff->num_k; i++) |
| 859 | m_current_k[i] += (((m_target_k[i] - m_current_k[i])*(1-inhibit_state))*current_sample)/samples_per_frame; |
| 998 | 860 | } |
| 999 | 861 | else // we're done, play this frame for 1/8 frame. |
| 1000 | 862 | { |
| 1001 | | tms->current_energy = tms->target_energy; |
| 1002 | | tms->current_pitch = tms->target_pitch; |
| 1003 | | for (i = 0; i < tms->coeff->num_k; i++) |
| 1004 | | tms->current_k[i] = tms->target_k[i]; |
| 863 | m_current_energy = m_target_energy; |
| 864 | m_current_pitch = m_target_pitch; |
| 865 | for (i = 0; i < m_coeff->num_k; i++) |
| 866 | m_current_k[i] = m_target_k[i]; |
| 1005 | 867 | } |
| 1006 | 868 | #else |
| 1007 | 869 | //Updates to parameters only happen on subcycle '2' (B cycle) of PCs. |
| 1008 | | if (tms->subcycle == 2) |
| 870 | if (m_subcycle == 2) |
| 1009 | 871 | { |
| 1010 | | switch(tms->PC) |
| 872 | switch(m_PC) |
| 1011 | 873 | { |
| 1012 | 874 | case 0: /* PC = 0, B cycle, write updated energy */ |
| 1013 | | tms->current_energy += (((tms->target_energy - tms->current_energy)*(1-inhibit_state)) INTERP_SHIFT); |
| 875 | m_current_energy += (((m_target_energy - m_current_energy)*(1-inhibit_state)) INTERP_SHIFT); |
| 1014 | 876 | break; |
| 1015 | 877 | case 1: /* PC = 1, B cycle, write updated pitch */ |
| 1016 | | tms->current_pitch += (((tms->target_pitch - tms->current_pitch)*(1-inhibit_state)) INTERP_SHIFT); |
| 878 | m_current_pitch += (((m_target_pitch - m_current_pitch)*(1-inhibit_state)) INTERP_SHIFT); |
| 1017 | 879 | break; |
| 1018 | 880 | case 2: case 3: case 4: case 5: case 6: case 7: case 8: case 9: case 10: case 11: |
| 1019 | 881 | /* PC = 2 through 11, B cycle, write updated K1 through K10 */ |
| 1020 | | tms->current_k[tms->PC-2] += (((tms->target_k[tms->PC-2] - tms->current_k[tms->PC-2])*(1-inhibit_state)) INTERP_SHIFT); |
| 882 | m_current_k[m_PC-2] += (((m_target_k[m_PC-2] - m_current_k[m_PC-2])*(1-inhibit_state)) INTERP_SHIFT); |
| 1021 | 883 | break; |
| 1022 | 884 | case 12: /* PC = 12, do nothing */ |
| 1023 | 885 | break; |
| r22818 | r22819 | |
| 1030 | 892 | if (OLD_FRAME_UNVOICED_FLAG == 1) |
| 1031 | 893 | { |
| 1032 | 894 | // generate unvoiced samples here |
| 1033 | | if (tms->RNG & 1) |
| 1034 | | tms->excitation_data = ~0x3F; /* according to the patent it is (either + or -) half of the maximum value in the chirp table, so either 01000000(0x40) or 11000000(0xC0)*/ |
| 895 | if (m_RNG & 1) |
| 896 | m_excitation_data = ~0x3F; /* according to the patent it is (either + or -) half of the maximum value in the chirp table, so either 01000000(0x40) or 11000000(0xC0)*/ |
| 1035 | 897 | else |
| 1036 | | tms->excitation_data = 0x40; |
| 898 | m_excitation_data = 0x40; |
| 1037 | 899 | } |
| 1038 | 900 | else /* (OLD_FRAME_UNVOICED_FLAG == 0) */ |
| 1039 | 901 | { |
| r22818 | r22819 | |
| 1044 | 906 | * and if the address reaches that point the ADDRESS incrementer is |
| 1045 | 907 | * disabled, forcing all samples beyond 51d to be == 51d |
| 1046 | 908 | */ |
| 1047 | | if (tms->pitch_count >= 51) |
| 1048 | | tms->excitation_data = tms->coeff->chirptable[51]; |
| 1049 | | else /*tms->pitch_count < 51*/ |
| 1050 | | tms->excitation_data = tms->coeff->chirptable[tms->pitch_count]; |
| 909 | if (m_pitch_count >= 51) |
| 910 | m_excitation_data = m_coeff->chirptable[51]; |
| 911 | else /*m_pitch_count < 51*/ |
| 912 | m_excitation_data = m_coeff->chirptable[m_pitch_count]; |
| 1051 | 913 | } |
| 1052 | 914 | |
| 1053 | 915 | // Update LFSR *20* times every sample (once per T cycle), like patent shows |
| 1054 | 916 | for (i=0; i<20; i++) |
| 1055 | 917 | { |
| 1056 | | bitout = ((tms->RNG >> 12) & 1) ^ |
| 1057 | | ((tms->RNG >> 3) & 1) ^ |
| 1058 | | ((tms->RNG >> 2) & 1) ^ |
| 1059 | | ((tms->RNG >> 0) & 1); |
| 1060 | | tms->RNG <<= 1; |
| 1061 | | tms->RNG |= bitout; |
| 918 | bitout = ((m_RNG >> 12) & 1) ^ |
| 919 | ((m_RNG >> 3) & 1) ^ |
| 920 | ((m_RNG >> 2) & 1) ^ |
| 921 | ((m_RNG >> 0) & 1); |
| 922 | m_RNG <<= 1; |
| 923 | m_RNG |= bitout; |
| 1062 | 924 | } |
| 1063 | | this_sample = lattice_filter(tms); /* execute lattice filter */ |
| 925 | this_sample = lattice_filter(); /* execute lattice filter */ |
| 1064 | 926 | #ifdef DEBUG_GENERATION_VERBOSE |
| 1065 | | //fprintf(stderr,"C:%01d; ",tms->subcycle); |
| 1066 | | fprintf(stderr,"IP:%01d PC:%02d X:%04d E:%03d P:%03d Pc:%03d ",tms->IP, tms->PC, tms->excitation_data, tms->current_energy, tms->current_pitch, tms->pitch_count); |
| 1067 | | //fprintf(stderr,"X:%04d E:%03d P:%03d Pc:%03d ", tms->excitation_data, tms->current_energy, tms->current_pitch, tms->pitch_count); |
| 927 | //fprintf(stderr,"C:%01d; ",m_subcycle); |
| 928 | fprintf(stderr,"IP:%01d PC:%02d X:%04d E:%03d P:%03d Pc:%03d ",m_IP, m_PC, m_excitation_data, m_current_energy, m_current_pitch, m_pitch_count); |
| 929 | //fprintf(stderr,"X:%04d E:%03d P:%03d Pc:%03d ", m_excitation_data, m_current_energy, m_current_pitch, m_pitch_count); |
| 1068 | 930 | for (i=0; i<10; i++) |
| 1069 | | fprintf(stderr,"K%d:%04d ", i+1, tms->current_k[i]); |
| 931 | fprintf(stderr,"K%d:%04d ", i+1, m_current_k[i]); |
| 1070 | 932 | fprintf(stderr,"Out:%06d", this_sample); |
| 1071 | 933 | fprintf(stderr,"\n"); |
| 1072 | 934 | #endif |
| 1073 | 935 | /* next, force result to 14 bits (since its possible that the addition at the final (k1) stage of the lattice overflowed) */ |
| 1074 | 936 | while (this_sample > 16383) this_sample -= 32768; |
| 1075 | 937 | while (this_sample < -16384) this_sample += 32768; |
| 1076 | | if (tms->digital_select == 0) // analog SPK pin output is only 8 bits, with clipping |
| 938 | if (m_digital_select == 0) // analog SPK pin output is only 8 bits, with clipping |
| 1077 | 939 | buffer[buf_count] = clip_analog(this_sample); |
| 1078 | 940 | else // digital I/O pin output is 12 bits |
| 1079 | 941 | { |
| r22818 | r22819 | |
| 1092 | 954 | } |
| 1093 | 955 | // Update all counts |
| 1094 | 956 | |
| 1095 | | tms->subcycle++; |
| 1096 | | if ((tms->subcycle == 2) && (tms->PC == 12)) |
| 957 | m_subcycle++; |
| 958 | if ((m_subcycle == 2) && (m_PC == 12)) |
| 1097 | 959 | { |
| 1098 | 960 | /* Circuit 412 in the patent acts a reset, resetting the pitch counter to 0 |
| 1099 | 961 | * if INHIBIT was true during the most recent frame transition. |
| 1100 | 962 | * The exact time this occurs is betwen IP=7, PC=12 sub=0, T=t12 |
| 1101 | | * and tms->IP = 0, PC=0 sub=0, T=t12, a period of exactly 20 cycles, |
| 963 | * and m_IP = 0, PC=0 sub=0, T=t12, a period of exactly 20 cycles, |
| 1102 | 964 | * which overlaps the time OLDE and OLDP are updated at IP=7 PC=12 T17 |
| 1103 | 965 | * (and hence INHIBIT itself 2 t-cycles later). We do it here because it is |
| 1104 | 966 | * convenient and should make no difference in output. |
| 1105 | 967 | */ |
| 1106 | | if ((tms->IP == 7)&&(tms->inhibit==1)) tms->pitch_count = 0; |
| 1107 | | tms->subcycle = tms->subc_reload; |
| 1108 | | tms->PC = 0; |
| 1109 | | tms->IP++; |
| 1110 | | tms->IP&=0x7; |
| 968 | if ((m_IP == 7)&&(m_inhibit==1)) m_pitch_count = 0; |
| 969 | m_subcycle = m_subc_reload; |
| 970 | m_PC = 0; |
| 971 | m_IP++; |
| 972 | m_IP&=0x7; |
| 1111 | 973 | } |
| 1112 | | else if (tms->subcycle == 3) |
| 974 | else if (m_subcycle == 3) |
| 1113 | 975 | { |
| 1114 | | tms->subcycle = tms->subc_reload; |
| 1115 | | tms->PC++; |
| 976 | m_subcycle = m_subc_reload; |
| 977 | m_PC++; |
| 1116 | 978 | } |
| 1117 | | tms->pitch_count++; |
| 1118 | | if (tms->pitch_count >= tms->current_pitch) tms->pitch_count = 0; |
| 1119 | | tms->pitch_count &= 0x1FF; |
| 979 | m_pitch_count++; |
| 980 | if (m_pitch_count >= m_current_pitch) m_pitch_count = 0; |
| 981 | m_pitch_count &= 0x1FF; |
| 1120 | 982 | buf_count++; |
| 1121 | 983 | size--; |
| 1122 | 984 | } |
| r22818 | r22819 | |
| 1125 | 987 | |
| 1126 | 988 | while (size > 0) |
| 1127 | 989 | { |
| 1128 | | tms->subcycle++; |
| 1129 | | if ((tms->subcycle == 2) && (tms->PC == 12)) |
| 990 | m_subcycle++; |
| 991 | if ((m_subcycle == 2) && (m_PC == 12)) |
| 1130 | 992 | { |
| 1131 | | tms->subcycle = tms->subc_reload; |
| 1132 | | tms->PC = 0; |
| 1133 | | tms->IP++; |
| 1134 | | tms->IP&=0x7; |
| 993 | m_subcycle = m_subc_reload; |
| 994 | m_PC = 0; |
| 995 | m_IP++; |
| 996 | m_IP&=0x7; |
| 1135 | 997 | } |
| 1136 | | else if (tms->subcycle == 3) |
| 998 | else if (m_subcycle == 3) |
| 1137 | 999 | { |
| 1138 | | tms->subcycle = tms->subc_reload; |
| 1139 | | tms->PC++; |
| 1000 | m_subcycle = m_subc_reload; |
| 1001 | m_PC++; |
| 1140 | 1002 | } |
| 1141 | 1003 | buffer[buf_count] = -1; /* should be just -1; actual chip outputs -1 every idle sample; (cf note in data sheet, p 10, table 4) */ |
| 1142 | 1004 | buf_count++; |
| r22818 | r22819 | |
| 1215 | 1077 | |
| 1216 | 1078 | ***********************************************************************************************/ |
| 1217 | 1079 | |
| 1218 | | static INT32 lattice_filter(tms5220_state *tms) |
| 1080 | INT32 tms5220_device::lattice_filter() |
| 1219 | 1081 | { |
| 1220 | 1082 | // Lattice filter here |
| 1221 | 1083 | // Aug/05/07: redone as unrolled loop, for clarity - LN |
| 1222 | 1084 | /* Originally Copied verbatim from table I in US patent 4,209,804, now updated to be in same order as the actual chip does it, not that it matters. |
| 1223 | 1085 | notation equivalencies from table: |
| 1224 | | Yn(i) == tms->u[n-1] |
| 1225 | | Kn = tms->current_k[n-1] |
| 1226 | | bn = tms->x[n-1] |
| 1086 | Yn(i) == m_u[n-1] |
| 1087 | Kn = m_current_k[n-1] |
| 1088 | bn = m_x[n-1] |
| 1227 | 1089 | */ |
| 1228 | | tms->u[10] = matrix_multiply(tms->previous_energy, (tms->excitation_data<<6)); //Y(11) |
| 1229 | | tms->u[9] = tms->u[10] - matrix_multiply(tms->current_k[9], tms->x[9]); |
| 1230 | | tms->u[8] = tms->u[9] - matrix_multiply(tms->current_k[8], tms->x[8]); |
| 1231 | | tms->u[7] = tms->u[8] - matrix_multiply(tms->current_k[7], tms->x[7]); |
| 1232 | | tms->u[6] = tms->u[7] - matrix_multiply(tms->current_k[6], tms->x[6]); |
| 1233 | | tms->u[5] = tms->u[6] - matrix_multiply(tms->current_k[5], tms->x[5]); |
| 1234 | | tms->u[4] = tms->u[5] - matrix_multiply(tms->current_k[4], tms->x[4]); |
| 1235 | | tms->u[3] = tms->u[4] - matrix_multiply(tms->current_k[3], tms->x[3]); |
| 1236 | | tms->u[2] = tms->u[3] - matrix_multiply(tms->current_k[2], tms->x[2]); |
| 1237 | | tms->u[1] = tms->u[2] - matrix_multiply(tms->current_k[1], tms->x[1]); |
| 1238 | | tms->u[0] = tms->u[1] - matrix_multiply(tms->current_k[0], tms->x[0]); |
| 1239 | | tms->x[9] = tms->x[8] + matrix_multiply(tms->current_k[8], tms->u[8]); |
| 1240 | | tms->x[8] = tms->x[7] + matrix_multiply(tms->current_k[7], tms->u[7]); |
| 1241 | | tms->x[7] = tms->x[6] + matrix_multiply(tms->current_k[6], tms->u[6]); |
| 1242 | | tms->x[6] = tms->x[5] + matrix_multiply(tms->current_k[5], tms->u[5]); |
| 1243 | | tms->x[5] = tms->x[4] + matrix_multiply(tms->current_k[4], tms->u[4]); |
| 1244 | | tms->x[4] = tms->x[3] + matrix_multiply(tms->current_k[3], tms->u[3]); |
| 1245 | | tms->x[3] = tms->x[2] + matrix_multiply(tms->current_k[2], tms->u[2]); |
| 1246 | | tms->x[2] = tms->x[1] + matrix_multiply(tms->current_k[1], tms->u[1]); |
| 1247 | | tms->x[1] = tms->x[0] + matrix_multiply(tms->current_k[0], tms->u[0]); |
| 1248 | | tms->x[0] = tms->u[0]; |
| 1249 | | tms->previous_energy = tms->current_energy; |
| 1090 | m_u[10] = matrix_multiply(m_previous_energy, (m_excitation_data<<6)); //Y(11) |
| 1091 | m_u[9] = m_u[10] - matrix_multiply(m_current_k[9], m_x[9]); |
| 1092 | m_u[8] = m_u[9] - matrix_multiply(m_current_k[8], m_x[8]); |
| 1093 | m_u[7] = m_u[8] - matrix_multiply(m_current_k[7], m_x[7]); |
| 1094 | m_u[6] = m_u[7] - matrix_multiply(m_current_k[6], m_x[6]); |
| 1095 | m_u[5] = m_u[6] - matrix_multiply(m_current_k[5], m_x[5]); |
| 1096 | m_u[4] = m_u[5] - matrix_multiply(m_current_k[4], m_x[4]); |
| 1097 | m_u[3] = m_u[4] - matrix_multiply(m_current_k[3], m_x[3]); |
| 1098 | m_u[2] = m_u[3] - matrix_multiply(m_current_k[2], m_x[2]); |
| 1099 | m_u[1] = m_u[2] - matrix_multiply(m_current_k[1], m_x[1]); |
| 1100 | m_u[0] = m_u[1] - matrix_multiply(m_current_k[0], m_x[0]); |
| 1101 | m_x[9] = m_x[8] + matrix_multiply(m_current_k[8], m_u[8]); |
| 1102 | m_x[8] = m_x[7] + matrix_multiply(m_current_k[7], m_u[7]); |
| 1103 | m_x[7] = m_x[6] + matrix_multiply(m_current_k[6], m_u[6]); |
| 1104 | m_x[6] = m_x[5] + matrix_multiply(m_current_k[5], m_u[5]); |
| 1105 | m_x[5] = m_x[4] + matrix_multiply(m_current_k[4], m_u[4]); |
| 1106 | m_x[4] = m_x[3] + matrix_multiply(m_current_k[3], m_u[3]); |
| 1107 | m_x[3] = m_x[2] + matrix_multiply(m_current_k[2], m_u[2]); |
| 1108 | m_x[2] = m_x[1] + matrix_multiply(m_current_k[1], m_u[1]); |
| 1109 | m_x[1] = m_x[0] + matrix_multiply(m_current_k[0], m_u[0]); |
| 1110 | m_x[0] = m_u[0]; |
| 1111 | m_previous_energy = m_current_energy; |
| 1250 | 1112 | #ifdef DEBUG_LATTICE |
| 1251 | 1113 | int i; |
| 1252 | | fprintf(stderr,"V:%04d ", tms->u[10]); |
| 1114 | fprintf(stderr,"V:%04d ", m_u[10]); |
| 1253 | 1115 | for (i = 9; i >= 0; i--) |
| 1254 | 1116 | { |
| 1255 | | fprintf(stderr,"Y%d:%04d ", i+1, tms->u[i]); |
| 1256 | | fprintf(stderr,"b%d:%04d ", i+1, tms->x[i]); |
| 1117 | fprintf(stderr,"Y%d:%04d ", i+1, m_u[i]); |
| 1118 | fprintf(stderr,"b%d:%04d ", i+1, m_x[i]); |
| 1257 | 1119 | if ((i % 5) == 0) fprintf(stderr,"\n"); |
| 1258 | 1120 | } |
| 1259 | 1121 | #endif |
| 1260 | | return tms->u[0]; |
| 1122 | return m_u[0]; |
| 1261 | 1123 | } |
| 1262 | 1124 | |
| 1263 | 1125 | |
| r22818 | r22819 | |
| 1267 | 1129 | |
| 1268 | 1130 | ***********************************************************************************************/ |
| 1269 | 1131 | |
| 1270 | | static void process_command(tms5220_state *tms, unsigned char cmd) |
| 1132 | void tms5220_device::process_command(unsigned char cmd) |
| 1271 | 1133 | { |
| 1272 | 1134 | #ifdef DEBUG_COMMAND_DUMP |
| 1273 | 1135 | fprintf(stderr,"process_command called with parameter %02X\n",cmd); |
| r22818 | r22819 | |
| 1276 | 1138 | switch (cmd & 0x70) |
| 1277 | 1139 | { |
| 1278 | 1140 | case 0x10 : /* read byte */ |
| 1279 | | if (tms->talk_status == 0) /* TALKST must be clear for RDBY */ |
| 1141 | if (m_talk_status == 0) /* TALKST must be clear for RDBY */ |
| 1280 | 1142 | { |
| 1281 | | if (tms->schedule_dummy_read) |
| 1143 | if (m_schedule_dummy_read) |
| 1282 | 1144 | { |
| 1283 | | tms->schedule_dummy_read = FALSE; |
| 1284 | | if (tms->intf->read) |
| 1285 | | (*tms->intf->read)(tms->device, 1); |
| 1145 | m_schedule_dummy_read = FALSE; |
| 1146 | if (m_speechrom) |
| 1147 | m_speechrom->read(1); |
| 1286 | 1148 | } |
| 1287 | | if (tms->intf->read) |
| 1288 | | tms->data_register = (*tms->intf->read)(tms->device, 8); /* read one byte from speech ROM... */ |
| 1289 | | tms->RDB_flag = TRUE; |
| 1149 | if (m_speechrom) |
| 1150 | m_data_register = m_speechrom->read(8); /* read one byte from speech ROM... */ |
| 1151 | m_RDB_flag = TRUE; |
| 1290 | 1152 | } |
| 1291 | 1153 | break; |
| 1292 | 1154 | |
| 1293 | 1155 | case 0x00: case 0x20: /* set rate (tms5220c only), otherwise NOP */ |
| 1294 | | if (tms->variant == TMS5220_IS_5220C) |
| 1156 | if (m_variant == TMS5220_IS_5220C) |
| 1295 | 1157 | { |
| 1296 | | tms->tms5220c_rate = cmd&0x0F; |
| 1158 | m_tms5220c_rate = cmd&0x0F; |
| 1297 | 1159 | } |
| 1298 | 1160 | break; |
| 1299 | 1161 | |
| 1300 | 1162 | case 0x30 : /* read and branch */ |
| 1301 | | if (tms->talk_status == 0) /* TALKST must be clear for RB */ |
| 1163 | if (m_talk_status == 0) /* TALKST must be clear for RB */ |
| 1302 | 1164 | { |
| 1303 | 1165 | #ifdef VERBOSE |
| 1304 | 1166 | logerror("read and branch command received\n"); |
| 1305 | 1167 | #endif |
| 1306 | | tms->RDB_flag = FALSE; |
| 1307 | | if (tms->intf->read_and_branch) |
| 1308 | | (*tms->intf->read_and_branch)(tms->device); |
| 1168 | m_RDB_flag = FALSE; |
| 1169 | if (m_speechrom) |
| 1170 | m_speechrom->read_and_branch(); |
| 1309 | 1171 | } |
| 1310 | 1172 | break; |
| 1311 | 1173 | |
| 1312 | 1174 | case 0x40 : /* load address */ |
| 1313 | | if (tms->talk_status == 0) /* TALKST must be clear for LA */ |
| 1175 | if (m_talk_status == 0) /* TALKST must be clear for LA */ |
| 1314 | 1176 | { |
| 1315 | 1177 | /* tms5220 data sheet says that if we load only one 4-bit nibble, it won't work. |
| 1316 | 1178 | This code does not care about this. */ |
| 1317 | | if (tms->intf->load_address) |
| 1318 | | (*tms->intf->load_address)(tms->device, cmd & 0x0f); |
| 1319 | | tms->schedule_dummy_read = TRUE; |
| 1179 | if (m_speechrom) |
| 1180 | m_speechrom->load_address(cmd & 0x0f); |
| 1181 | m_schedule_dummy_read = TRUE; |
| 1320 | 1182 | } |
| 1321 | 1183 | break; |
| 1322 | 1184 | |
| 1323 | 1185 | case 0x50 : /* speak */ |
| 1324 | | if (tms->schedule_dummy_read) |
| 1186 | if (m_schedule_dummy_read) |
| 1325 | 1187 | { |
| 1326 | | tms->schedule_dummy_read = FALSE; |
| 1327 | | if (tms->intf->read) |
| 1328 | | (*tms->intf->read)(tms->device, 1); |
| 1188 | m_schedule_dummy_read = FALSE; |
| 1189 | if (m_speechrom) |
| 1190 | m_speechrom->read(1); |
| 1329 | 1191 | } |
| 1330 | | tms->speaking_now = 1; |
| 1331 | | tms->speak_external = 0; |
| 1332 | | tms->talk_status = 1; /* start immediately */ |
| 1192 | m_speaking_now = 1; |
| 1193 | m_speak_external = 0; |
| 1194 | m_talk_status = 1; /* start immediately */ |
| 1333 | 1195 | /* clear out variables before speaking */ |
| 1334 | 1196 | // TODO: similar to the victory case described above, but for VSM speech |
| 1335 | | tms->subcycle = tms->subc_reload; |
| 1336 | | tms->PC = 0; |
| 1337 | | tms->IP = reload_table[tms->tms5220c_rate&0x3]; |
| 1338 | | tms->new_frame_energy_idx = 0; |
| 1339 | | tms->new_frame_pitch_idx = 0; |
| 1197 | m_subcycle = m_subc_reload; |
| 1198 | m_PC = 0; |
| 1199 | m_IP = reload_table[m_tms5220c_rate&0x3]; |
| 1200 | m_new_frame_energy_idx = 0; |
| 1201 | m_new_frame_pitch_idx = 0; |
| 1340 | 1202 | int i; |
| 1341 | 1203 | for (i = 0; i < 4; i++) |
| 1342 | | tms->new_frame_k_idx[i] = 0; |
| 1204 | m_new_frame_k_idx[i] = 0; |
| 1343 | 1205 | for (i = 4; i < 7; i++) |
| 1344 | | tms->new_frame_k_idx[i] = 0xF; |
| 1345 | | for (i = 7; i < tms->coeff->num_k; i++) |
| 1346 | | tms->new_frame_k_idx[i] = 0x7; |
| 1206 | m_new_frame_k_idx[i] = 0xF; |
| 1207 | for (i = 7; i < m_coeff->num_k; i++) |
| 1208 | m_new_frame_k_idx[i] = 0x7; |
| 1347 | 1209 | break; |
| 1348 | 1210 | |
| 1349 | 1211 | case 0x60 : /* speak external */ |
| 1350 | 1212 | //SPKEXT going active activates SPKEE which clears the fifo |
| 1351 | | tms->fifo_head = tms->fifo_tail = tms->fifo_count = tms->fifo_bits_taken = 0; |
| 1352 | | tms->speak_external = 1; |
| 1353 | | tms->RDB_flag = FALSE; |
| 1213 | m_fifo_head = m_fifo_tail = m_fifo_count = m_fifo_bits_taken = 0; |
| 1214 | m_speak_external = 1; |
| 1215 | m_RDB_flag = FALSE; |
| 1354 | 1216 | break; |
| 1355 | 1217 | |
| 1356 | 1218 | case 0x70 : /* reset */ |
| 1357 | | if (tms->schedule_dummy_read) |
| 1219 | if (m_schedule_dummy_read) |
| 1358 | 1220 | { |
| 1359 | | tms->schedule_dummy_read = FALSE; |
| 1360 | | if (tms->intf->read) |
| 1361 | | (*tms->intf->read)(tms->device, 1); |
| 1221 | m_schedule_dummy_read = FALSE; |
| 1222 | if (m_speechrom) |
| 1223 | m_speechrom->read(1); |
| 1362 | 1224 | } |
| 1363 | | tms->device->reset(); |
| 1225 | reset(); |
| 1364 | 1226 | break; |
| 1365 | 1227 | } |
| 1366 | 1228 | |
| 1367 | 1229 | /* update the buffer low state */ |
| 1368 | | update_status_and_ints(tms); |
| 1230 | update_status_and_ints(); |
| 1369 | 1231 | } |
| 1370 | 1232 | |
| 1371 | 1233 | /****************************************************************************************** |
| r22818 | r22819 | |
| 1374 | 1236 | |
| 1375 | 1237 | ******************************************************************************************/ |
| 1376 | 1238 | |
| 1377 | | static void parse_frame(tms5220_state *tms) |
| 1239 | void tms5220_device::parse_frame() |
| 1378 | 1240 | { |
| 1379 | 1241 | int indx, i, rep_flag; |
| 1380 | 1242 | |
| r22818 | r22819 | |
| 1382 | 1244 | |
| 1383 | 1245 | /* if the chip is a tms5220C, and the rate mode is set to that each frame (0x04 bit set) |
| 1384 | 1246 | has a 2 bit rate preceding it, grab two bits here and store them as the rate; */ |
| 1385 | | if ((tms->variant == TMS5220_IS_5220C) && (tms->tms5220c_rate & 0x04)) |
| 1247 | if ((m_variant == TMS5220_IS_5220C) && (m_tms5220c_rate & 0x04)) |
| 1386 | 1248 | { |
| 1387 | | indx = extract_bits(tms, 2); |
| 1249 | indx = extract_bits(2); |
| 1388 | 1250 | #ifdef DEBUG_PARSE_FRAME_DUMP |
| 1389 | 1251 | printbits(indx,2); |
| 1390 | 1252 | fprintf(stderr," "); |
| 1391 | 1253 | #endif |
| 1392 | | tms->IP = reload_table[indx]; |
| 1254 | m_IP = reload_table[indx]; |
| 1393 | 1255 | } |
| 1394 | 1256 | else // non-5220C and 5220C in fixed rate mode |
| 1395 | | tms->IP = reload_table[tms->tms5220c_rate&0x3]; |
| 1257 | m_IP = reload_table[m_tms5220c_rate&0x3]; |
| 1396 | 1258 | |
| 1397 | | update_status_and_ints(tms); |
| 1398 | | if (!tms->talk_status) goto ranout; |
| 1259 | update_status_and_ints(); |
| 1260 | if (!m_talk_status) goto ranout; |
| 1399 | 1261 | |
| 1400 | 1262 | // attempt to extract the energy index |
| 1401 | | tms->new_frame_energy_idx = extract_bits(tms,tms->coeff->energy_bits); |
| 1263 | m_new_frame_energy_idx = extract_bits(m_coeff->energy_bits); |
| 1402 | 1264 | #ifdef DEBUG_PARSE_FRAME_DUMP |
| 1403 | | printbits(tms->new_frame_energy_idx,tms->coeff->energy_bits); |
| 1265 | printbits(m_new_frame_energy_idx,m_coeff->energy_bits); |
| 1404 | 1266 | fprintf(stderr," "); |
| 1405 | 1267 | #endif |
| 1406 | | update_status_and_ints(tms); |
| 1407 | | if (!tms->talk_status) goto ranout; |
| 1268 | update_status_and_ints(); |
| 1269 | if (!m_talk_status) goto ranout; |
| 1408 | 1270 | // if the energy index is 0 or 15, we're done |
| 1409 | | if ((tms->new_frame_energy_idx == 0) || (tms->new_frame_energy_idx == 15)) |
| 1271 | if ((m_new_frame_energy_idx == 0) || (m_new_frame_energy_idx == 15)) |
| 1410 | 1272 | return; |
| 1411 | 1273 | |
| 1412 | 1274 | |
| 1413 | 1275 | // attempt to extract the repeat flag |
| 1414 | | rep_flag = extract_bits(tms,1); |
| 1276 | rep_flag = extract_bits(1); |
| 1415 | 1277 | #ifdef DEBUG_PARSE_FRAME_DUMP |
| 1416 | 1278 | printbits(rep_flag, 1); |
| 1417 | 1279 | fprintf(stderr," "); |
| 1418 | 1280 | #endif |
| 1419 | 1281 | |
| 1420 | 1282 | // attempt to extract the pitch |
| 1421 | | tms->new_frame_pitch_idx = extract_bits(tms,tms->coeff->pitch_bits); |
| 1283 | m_new_frame_pitch_idx = extract_bits(m_coeff->pitch_bits); |
| 1422 | 1284 | #ifdef DEBUG_PARSE_FRAME_DUMP |
| 1423 | | printbits(tms->new_frame_pitch_idx,tms->coeff->pitch_bits); |
| 1285 | printbits(m_new_frame_pitch_idx,m_coeff->pitch_bits); |
| 1424 | 1286 | fprintf(stderr," "); |
| 1425 | 1287 | #endif |
| 1426 | | update_status_and_ints(tms); |
| 1427 | | if (!tms->talk_status) goto ranout; |
| 1288 | update_status_and_ints(); |
| 1289 | if (!m_talk_status) goto ranout; |
| 1428 | 1290 | // if this is a repeat frame, just do nothing, it will reuse the old coefficients |
| 1429 | 1291 | if (rep_flag) |
| 1430 | 1292 | return; |
| r22818 | r22819 | |
| 1432 | 1294 | // extract first 4 K coefficients |
| 1433 | 1295 | for (i = 0; i < 4; i++) |
| 1434 | 1296 | { |
| 1435 | | tms->new_frame_k_idx[i] = extract_bits(tms,tms->coeff->kbits[i]); |
| 1297 | m_new_frame_k_idx[i] = extract_bits(m_coeff->kbits[i]); |
| 1436 | 1298 | #ifdef DEBUG_PARSE_FRAME_DUMP |
| 1437 | | printbits(tms->new_frame_k_idx[i],tms->coeff->kbits[i]); |
| 1299 | printbits(m_new_frame_k_idx[i],m_coeff->kbits[i]); |
| 1438 | 1300 | fprintf(stderr," "); |
| 1439 | 1301 | #endif |
| 1440 | | update_status_and_ints(tms); |
| 1441 | | if (!tms->talk_status) goto ranout; |
| 1302 | update_status_and_ints(); |
| 1303 | if (!m_talk_status) goto ranout; |
| 1442 | 1304 | } |
| 1443 | 1305 | |
| 1444 | 1306 | // if the pitch index was zero, we only need 4 K's... |
| 1445 | | if (tms->new_frame_pitch_idx == 0) |
| 1307 | if (m_new_frame_pitch_idx == 0) |
| 1446 | 1308 | { |
| 1447 | 1309 | /* and the rest of the coefficients are zeroed, but that's done in the generator code */ |
| 1448 | 1310 | return; |
| 1449 | 1311 | } |
| 1450 | 1312 | |
| 1451 | 1313 | // If we got here, we need the remaining 6 K's |
| 1452 | | for (i = 4; i < tms->coeff->num_k; i++) |
| 1314 | for (i = 4; i < m_coeff->num_k; i++) |
| 1453 | 1315 | { |
| 1454 | | tms->new_frame_k_idx[i] = extract_bits(tms, tms->coeff->kbits[i]); |
| 1316 | m_new_frame_k_idx[i] = extract_bits(m_coeff->kbits[i]); |
| 1455 | 1317 | #ifdef DEBUG_PARSE_FRAME_DUMP |
| 1456 | | printbits(tms->new_frame_k_idx[i],tms->coeff->kbits[i]); |
| 1318 | printbits(m_new_frame_k_idx[i],m_coeff->kbits[i]); |
| 1457 | 1319 | fprintf(stderr," "); |
| 1458 | 1320 | #endif |
| 1459 | | update_status_and_ints(tms); |
| 1460 | | if (!tms->talk_status) goto ranout; |
| 1321 | update_status_and_ints(); |
| 1322 | if (!m_talk_status) goto ranout; |
| 1461 | 1323 | } |
| 1462 | 1324 | #ifdef VERBOSE |
| 1463 | | if (tms->speak_external) |
| 1464 | | logerror("Parsed a frame successfully in FIFO - %d bits remaining\n", (tms->fifo_count*8)-(tms->fifo_bits_taken)); |
| 1325 | if (m_speak_external) |
| 1326 | logerror("Parsed a frame successfully in FIFO - %d bits remaining\n", (m_fifo_count*8)-(m_fifo_bits_taken)); |
| 1465 | 1327 | else |
| 1466 | 1328 | logerror("Parsed a frame successfully in ROM\n"); |
| 1467 | 1329 | #endif |
| r22818 | r22819 | |
| 1480 | 1342 | |
| 1481 | 1343 | ***********************************************************************************************/ |
| 1482 | 1344 | |
| 1483 | | static void set_interrupt_state(tms5220_state *tms, int state) |
| 1345 | void tms5220_device::set_interrupt_state(int state) |
| 1484 | 1346 | { |
| 1485 | 1347 | #ifdef DEBUG_PIN_READS |
| 1486 | 1348 | logerror("irq pin set to state %d\n", state); |
| 1487 | 1349 | #endif |
| 1488 | | if (!tms->irq_func.isnull() && state != tms->irq_pin) |
| 1489 | | tms->irq_func(!state); |
| 1490 | | tms->irq_pin = state; |
| 1350 | if (!m_irq_handler.isnull() && state != m_irq_pin) |
| 1351 | m_irq_handler(!state); |
| 1352 | m_irq_pin = state; |
| 1491 | 1353 | } |
| 1492 | 1354 | |
| 1493 | 1355 | /********************************************************************************************** |
| r22818 | r22819 | |
| 1496 | 1358 | |
| 1497 | 1359 | ***********************************************************************************************/ |
| 1498 | 1360 | |
| 1499 | | static void update_ready_state(tms5220_state *tms) |
| 1361 | void tms5220_device::update_ready_state() |
| 1500 | 1362 | { |
| 1501 | | int state = tms5220_ready_read(tms); |
| 1363 | int state = ready_read(); |
| 1502 | 1364 | #ifdef DEBUG_PIN_READS |
| 1503 | 1365 | logerror("ready pin set to state %d\n", state); |
| 1504 | 1366 | #endif |
| 1505 | | if (!tms->readyq_func.isnull() && state != tms->ready_pin) |
| 1506 | | tms->readyq_func(!state); |
| 1507 | | tms->ready_pin = state; |
| 1367 | if (!m_readyq_handler.isnull() && state != m_ready_pin) |
| 1368 | m_readyq_handler(!state); |
| 1369 | m_ready_pin = state; |
| 1508 | 1370 | } |
| 1509 | 1371 | |
| 1510 | 1372 | |
| r22818 | r22819 | |
| 1514 | 1376 | |
| 1515 | 1377 | ***********************************************************************************************/ |
| 1516 | 1378 | |
| 1517 | | static DEVICE_START( tms5220 ) |
| 1379 | //------------------------------------------------- |
| 1380 | // device_start - device-specific startup |
| 1381 | //------------------------------------------------- |
| 1382 | |
| 1383 | void tms5220_device::device_start() |
| 1518 | 1384 | { |
| 1519 | | static const tms5220_interface dummy = { DEVCB_NULL }; |
| 1520 | | tms5220_state *tms = get_safe_token(device); |
| 1385 | if (m_speechrom_tag) |
| 1386 | { |
| 1387 | m_speechrom = siblingdevice<speechrom_device>( m_speechrom_tag ); |
| 1388 | if( !m_speechrom ) |
| 1389 | { |
| 1390 | throw new emu_fatalerror("Error: %s '%s' can't find speechrom '%s'\n", shortname(), tag(), m_speechrom_tag ); |
| 1391 | } |
| 1392 | } |
| 1393 | else |
| 1394 | { |
| 1395 | m_speechrom = NULL; |
| 1396 | } |
| 1521 | 1397 | |
| 1522 | | tms->intf = device->static_config() ? (const tms5220_interface *)device->static_config() : &dummy; |
| 1523 | | //tms->table = *device->region(); |
| 1398 | set_variant(TMS5220_IS_5220); |
| 1399 | m_clock = clock(); |
| 1524 | 1400 | |
| 1525 | | tms->device = device; |
| 1526 | | tms5220_set_variant(tms, TMS5220_IS_5220); |
| 1527 | | tms->clock = device->clock(); |
| 1528 | | |
| 1529 | | assert_always(tms != NULL, "Error creating TMS5220 chip"); |
| 1530 | | |
| 1531 | 1401 | /* resolve irq and readyq line */ |
| 1532 | | tms->irq_func.resolve(tms->intf->irq_func, *device); |
| 1533 | | tms->readyq_func.resolve(tms->intf->readyq_func, *device); |
| 1402 | m_irq_handler.resolve(); |
| 1403 | m_readyq_handler.resolve(); |
| 1534 | 1404 | |
| 1535 | 1405 | /* initialize a stream */ |
| 1536 | | tms->stream = device->machine().sound().stream_alloc(*device, 0, 1, device->clock() / 80, tms, tms5220_update); |
| 1406 | m_stream = machine().sound().stream_alloc(*this, 0, 1, clock() / 80); |
| 1537 | 1407 | |
| 1538 | | /*if (tms->table == NULL) |
| 1539 | | { |
| 1540 | | assert_always(tms->intf->M0_callback != NULL, "Missing _mandatory_ 'M0_callback' function pointer in the TMS5110 interface\n This function is used by TMS5220 to call for a new single bit\n needed to generate the speech when in VSM mode\n Aborting startup...\n"); |
| 1541 | | tms->M0_callback = tms->intf->M0_callback; |
| 1542 | | tms->set_load_address = tms->intf->load_address; |
| 1543 | | } |
| 1544 | | else |
| 1545 | | { |
| 1546 | | tms->M0_callback = speech_rom_read_bit; |
| 1547 | | tms->set_load_address = speech_rom_set_addr; |
| 1548 | | }*/ |
| 1408 | m_timer_io_ready = timer_alloc(0); |
| 1549 | 1409 | |
| 1550 | 1410 | /* not during reset which is called frm within a write! */ |
| 1551 | | tms->io_ready = 1; |
| 1552 | | tms->true_timing = 0; |
| 1553 | | tms->rs_ws = 0x03; // rs and ws are assumed to be inactive on device startup |
| 1411 | m_io_ready = 1; |
| 1412 | m_true_timing = 0; |
| 1413 | m_rs_ws = 0x03; // rs and ws are assumed to be inactive on device startup |
| 1554 | 1414 | |
| 1555 | | register_for_save_states(tms); |
| 1415 | register_for_save_states(); |
| 1556 | 1416 | } |
| 1557 | 1417 | |
| 1558 | | static DEVICE_START( tms5220c ) |
| 1418 | //------------------------------------------------- |
| 1419 | // device_start - device-specific startup |
| 1420 | //------------------------------------------------- |
| 1421 | |
| 1422 | void tms5220c_device::device_start() |
| 1559 | 1423 | { |
| 1560 | | tms5220_state *tms = get_safe_token(device); |
| 1561 | | DEVICE_START_CALL( tms5220 ); |
| 1562 | | tms5220_set_variant(tms, TMS5220_IS_5220C); |
| 1424 | tms5220_device::device_start(); |
| 1425 | set_variant(TMS5220_IS_5220C); |
| 1563 | 1426 | } |
| 1564 | 1427 | |
| 1565 | | static DEVICE_START( tmc0285 ) |
| 1428 | //------------------------------------------------- |
| 1429 | // device_start - device-specific startup |
| 1430 | //------------------------------------------------- |
| 1431 | |
| 1432 | void tmc0285_device::device_start() |
| 1566 | 1433 | { |
| 1567 | | tms5220_state *tms = get_safe_token(device); |
| 1568 | | DEVICE_START_CALL( tms5220 ); |
| 1569 | | tms5220_set_variant(tms, TMS5220_IS_TMC0285); |
| 1434 | tms5220_device::device_start(); |
| 1435 | set_variant(TMS5220_IS_TMC0285); |
| 1570 | 1436 | } |
| 1571 | 1437 | |
| 1438 | //------------------------------------------------- |
| 1439 | // device_start - device-specific startup |
| 1440 | //------------------------------------------------- |
| 1572 | 1441 | |
| 1573 | | static DEVICE_START( tms5200 ) |
| 1442 | void tms5200_device::device_start() |
| 1574 | 1443 | { |
| 1575 | | tms5220_state *tms = get_safe_token(device); |
| 1576 | | DEVICE_START_CALL( tms5220 ); |
| 1577 | | tms5220_set_variant(tms, TMS5220_IS_5200); |
| 1444 | tms5220_device::device_start(); |
| 1445 | set_variant(TMS5220_IS_5200); |
| 1578 | 1446 | } |
| 1579 | 1447 | |
| 1580 | 1448 | |
| 1581 | | static DEVICE_RESET( tms5220 ) |
| 1582 | | { |
| 1583 | | tms5220_state *tms = get_safe_token(device); |
| 1449 | //------------------------------------------------- |
| 1450 | // device_reset - device-specific reset |
| 1451 | //------------------------------------------------- |
| 1584 | 1452 | |
| 1585 | | tms->digital_select = FORCE_DIGITAL; // assume analog output |
| 1453 | void tms5220_device::device_reset() |
| 1454 | { |
| 1455 | m_digital_select = FORCE_DIGITAL; // assume analog output |
| 1586 | 1456 | /* initialize the FIFO */ |
| 1587 | | /*memset(tms->fifo, 0, sizeof(tms->fifo));*/ |
| 1588 | | tms->fifo_head = tms->fifo_tail = tms->fifo_count = tms->fifo_bits_taken = 0; |
| 1457 | /*memset(m_fifo, 0, sizeof(m_fifo));*/ |
| 1458 | m_fifo_head = m_fifo_tail = m_fifo_count = m_fifo_bits_taken = 0; |
| 1589 | 1459 | |
| 1590 | 1460 | /* initialize the chip state */ |
| 1591 | | /* Note that we do not actually clear IRQ on start-up : IRQ is even raised if tms->buffer_empty or tms->buffer_low are 0 */ |
| 1592 | | tms->speaking_now = tms->speak_external = tms->talk_status = tms->irq_pin = tms->ready_pin = 0; |
| 1593 | | set_interrupt_state(tms, 0); |
| 1594 | | update_ready_state(tms); |
| 1595 | | tms->buffer_empty = tms->buffer_low = 1; |
| 1461 | /* Note that we do not actually clear IRQ on start-up : IRQ is even raised if m_buffer_empty or m_buffer_low are 0 */ |
| 1462 | m_speaking_now = m_speak_external = m_talk_status = m_irq_pin = m_ready_pin = 0; |
| 1463 | set_interrupt_state(0); |
| 1464 | update_ready_state(); |
| 1465 | m_buffer_empty = m_buffer_low = 1; |
| 1596 | 1466 | |
| 1597 | | tms->RDB_flag = FALSE; |
| 1467 | m_RDB_flag = FALSE; |
| 1598 | 1468 | |
| 1599 | 1469 | /* initialize the energy/pitch/k states */ |
| 1600 | 1470 | #ifdef PERFECT_INTERPOLATION_HACK |
| 1601 | | tms->old_frame_energy_idx = tms->old_frame_pitch_idx = 0; |
| 1602 | | memset(tms->old_frame_k_idx, 0, sizeof(tms->old_frame_k_idx)); |
| 1471 | m_old_frame_energy_idx = m_old_frame_pitch_idx = 0; |
| 1472 | memset(m_old_frame_k_idx, 0, sizeof(m_old_frame_k_idx)); |
| 1603 | 1473 | #endif |
| 1604 | | tms->new_frame_energy_idx = tms->current_energy = tms->target_energy = 0; |
| 1605 | | tms->new_frame_pitch_idx = tms->current_pitch = tms->target_pitch = 0; |
| 1606 | | memset(tms->new_frame_k_idx, 0, sizeof(tms->new_frame_k_idx)); |
| 1607 | | memset(tms->current_k, 0, sizeof(tms->current_k)); |
| 1608 | | memset(tms->target_k, 0, sizeof(tms->target_k)); |
| 1474 | m_new_frame_energy_idx = m_current_energy = m_target_energy = 0; |
| 1475 | m_new_frame_pitch_idx = m_current_pitch = m_target_pitch = 0; |
| 1476 | memset(m_new_frame_k_idx, 0, sizeof(m_new_frame_k_idx)); |
| 1477 | memset(m_current_k, 0, sizeof(m_current_k)); |
| 1478 | memset(m_target_k, 0, sizeof(m_target_k)); |
| 1609 | 1479 | |
| 1610 | 1480 | /* initialize the sample generators */ |
| 1611 | | tms->inhibit = 1; |
| 1612 | | tms->subcycle = tms->tms5220c_rate = tms->pitch_count = tms->PC = 0; |
| 1613 | | tms->subc_reload = FORCE_SUBC_RELOAD; |
| 1614 | | tms->OLDE = tms->OLDP = 1; |
| 1615 | | tms->IP = reload_table[tms->tms5220c_rate&0x3]; |
| 1616 | | tms->RNG = 0x1FFF; |
| 1617 | | memset(tms->u, 0, sizeof(tms->u)); |
| 1618 | | memset(tms->x, 0, sizeof(tms->x)); |
| 1481 | m_inhibit = 1; |
| 1482 | m_subcycle = m_tms5220c_rate = m_pitch_count = m_PC = 0; |
| 1483 | m_subc_reload = FORCE_SUBC_RELOAD; |
| 1484 | m_OLDE = m_OLDP = 1; |
| 1485 | m_IP = reload_table[m_tms5220c_rate&0x3]; |
| 1486 | m_RNG = 0x1FFF; |
| 1487 | memset(m_u, 0, sizeof(m_u)); |
| 1488 | memset(m_x, 0, sizeof(m_x)); |
| 1619 | 1489 | |
| 1620 | | if (tms->intf->load_address) |
| 1621 | | (*tms->intf->load_address)(tms->device, 0); |
| 1490 | if (m_speechrom) |
| 1491 | m_speechrom->load_address(0); |
| 1622 | 1492 | |
| 1623 | | tms->schedule_dummy_read = TRUE; |
| 1493 | m_schedule_dummy_read = TRUE; |
| 1624 | 1494 | } |
| 1625 | 1495 | |
| 1626 | 1496 | /********************************************************************************************** |
| r22818 | r22819 | |
| 1629 | 1499 | |
| 1630 | 1500 | ***********************************************************************************************/ |
| 1631 | 1501 | |
| 1632 | | static TIMER_CALLBACK( io_ready_cb ) |
| 1502 | void tms5220_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) |
| 1633 | 1503 | { |
| 1634 | | tms5220_state *tms = (tms5220_state *) ptr; |
| 1635 | | if (param) |
| 1504 | switch(id) |
| 1636 | 1505 | { |
| 1637 | | switch (tms->rs_ws) |
| 1506 | case 0: |
| 1507 | if (param) |
| 1638 | 1508 | { |
| 1639 | | case 0x02: |
| 1640 | | /* Write */ |
| 1641 | | /* bring up to date first */ |
| 1509 | switch (m_rs_ws) |
| 1510 | { |
| 1511 | case 0x02: |
| 1512 | /* Write */ |
| 1513 | /* bring up to date first */ |
| 1642 | 1514 | #ifdef DEBUG_IO_READY |
| 1643 | | logerror("Serviced write: %02x\n", tms->write_latch); |
| 1644 | | //fprintf(stderr, "Processed write data: %02X\n", tms->write_latch); |
| 1515 | logerror("Serviced write: %02x\n", m_write_latch); |
| 1516 | //fprintf(stderr, "Processed write data: %02X\n", m_write_latch); |
| 1645 | 1517 | #endif |
| 1646 | | tms->stream->update(); |
| 1647 | | tms5220_data_write(tms, tms->write_latch); |
| 1648 | | break; |
| 1649 | | case 0x01: |
| 1650 | | /* Read */ |
| 1651 | | /* bring up to date first */ |
| 1652 | | tms->stream->update(); |
| 1653 | | tms->read_latch = tms5220_status_read(tms); |
| 1654 | | break; |
| 1655 | | case 0x03: |
| 1656 | | /* High Impedance */ |
| 1657 | | case 0x00: |
| 1658 | | /* illegal */ |
| 1659 | | break; |
| 1518 | m_stream->update(); |
| 1519 | data_write(m_write_latch); |
| 1520 | break; |
| 1521 | case 0x01: |
| 1522 | /* Read */ |
| 1523 | /* bring up to date first */ |
| 1524 | m_stream->update(); |
| 1525 | m_read_latch = status_read(); |
| 1526 | break; |
| 1527 | case 0x03: |
| 1528 | /* High Impedance */ |
| 1529 | case 0x00: |
| 1530 | /* illegal */ |
| 1531 | break; |
| 1532 | } |
| 1660 | 1533 | } |
| 1534 | |
| 1535 | m_io_ready = param; |
| 1536 | update_ready_state(); |
| 1537 | break; |
| 1661 | 1538 | } |
| 1662 | | tms->io_ready = param; |
| 1663 | | update_ready_state(tms); |
| 1664 | 1539 | } |
| 1665 | 1540 | |
| 1666 | 1541 | /* |
| 1667 | 1542 | * /RS line write handler |
| 1668 | 1543 | */ |
| 1669 | | WRITE_LINE_DEVICE_HANDLER( tms5220_rsq_w ) |
| 1544 | WRITE_LINE_MEMBER( tms5220_device::rsq_w ) |
| 1670 | 1545 | { |
| 1671 | | tms5220_state *tms = get_safe_token(device); |
| 1672 | 1546 | UINT8 new_val; |
| 1673 | 1547 | |
| 1674 | | tms->true_timing = 1; |
| 1548 | m_true_timing = 1; |
| 1675 | 1549 | state &= 0x01; |
| 1676 | 1550 | #ifdef DEBUG_RS_WS |
| 1677 | 1551 | logerror("/RS written with data: %d\n", state); |
| 1678 | 1552 | #endif |
| 1679 | | new_val = (tms->rs_ws & 0x01) | (state<<1); |
| 1680 | | if (new_val != tms->rs_ws) |
| 1553 | new_val = (m_rs_ws & 0x01) | (state<<1); |
| 1554 | if (new_val != m_rs_ws) |
| 1681 | 1555 | { |
| 1682 | | tms->rs_ws = new_val; |
| 1556 | m_rs_ws = new_val; |
| 1683 | 1557 | if (new_val == 0) |
| 1684 | 1558 | { |
| 1685 | | if (tms->variant == TMS5220_IS_5220C) |
| 1686 | | device->reset(); |
| 1559 | if (m_variant == TMS5220_IS_5220C) |
| 1560 | reset(); |
| 1687 | 1561 | #ifdef DEBUG_RS_WS |
| 1688 | 1562 | else |
| 1689 | 1563 | /* illegal */ |
| r22818 | r22819 | |
| 1694 | 1568 | else if ( new_val == 3) |
| 1695 | 1569 | { |
| 1696 | 1570 | /* high impedance */ |
| 1697 | | tms->read_latch = 0xff; |
| 1571 | m_read_latch = 0xff; |
| 1698 | 1572 | return; |
| 1699 | 1573 | } |
| 1700 | 1574 | if (state) |
| r22818 | r22819 | |
| 1708 | 1582 | logerror("Scheduling ready cycle for /RS...\n"); |
| 1709 | 1583 | #endif |
| 1710 | 1584 | /* upon /RS being activated, /READY goes inactive after 100 nsec from data sheet, through 3 asynchronous gates on patent. This is effectively within one clock, so we immediately set io_ready to 0 and activate the callback. */ |
| 1711 | | tms->io_ready = 0; |
| 1712 | | update_ready_state(tms); |
| 1585 | m_io_ready = 0; |
| 1586 | update_ready_state(); |
| 1713 | 1587 | /* How long does /READY stay inactive, when /RS is pulled low? I believe its almost always ~16 clocks (25 usec at 800khz as shown on the datasheet) */ |
| 1714 | | tms->device->machine().scheduler().timer_set(attotime::from_hz(device->clock()/16), FUNC(io_ready_cb), 1, tms); // this should take around 10-16 (closer to ~11?) cycles to complete |
| 1588 | m_timer_io_ready->adjust(attotime::from_hz(clock()/16), 1); // this should take around 10-16 (closer to ~11?) cycles to complete |
| 1715 | 1589 | } |
| 1716 | 1590 | } |
| 1717 | 1591 | } |
| r22818 | r22819 | |
| 1719 | 1593 | /* |
| 1720 | 1594 | * /WS line write handler |
| 1721 | 1595 | */ |
| 1722 | | WRITE_LINE_DEVICE_HANDLER( tms5220_wsq_w ) |
| 1596 | WRITE_LINE_MEMBER( tms5220_device::wsq_w ) |
| 1723 | 1597 | { |
| 1724 | | tms5220_state *tms = get_safe_token(device); |
| 1725 | 1598 | UINT8 new_val; |
| 1726 | 1599 | |
| 1727 | | tms->true_timing = 1; |
| 1600 | m_true_timing = 1; |
| 1728 | 1601 | state &= 0x01; |
| 1729 | 1602 | #ifdef DEBUG_RS_WS |
| 1730 | 1603 | logerror("/WS written with data: %d\n", state); |
| 1731 | 1604 | #endif |
| 1732 | | new_val = (tms->rs_ws & 0x02) | (state<<0); |
| 1733 | | if (new_val != tms->rs_ws) |
| 1605 | new_val = (m_rs_ws & 0x02) | (state<<0); |
| 1606 | if (new_val != m_rs_ws) |
| 1734 | 1607 | { |
| 1735 | | tms->rs_ws = new_val; |
| 1608 | m_rs_ws = new_val; |
| 1736 | 1609 | if (new_val == 0) |
| 1737 | 1610 | { |
| 1738 | | if (tms->variant == TMS5220_IS_5220C) |
| 1739 | | device->reset(); |
| 1611 | if (m_variant == TMS5220_IS_5220C) |
| 1612 | reset(); |
| 1740 | 1613 | #ifdef DEBUG_RS_WS |
| 1741 | 1614 | else |
| 1742 | 1615 | /* illegal */ |
| r22818 | r22819 | |
| 1747 | 1620 | else if ( new_val == 3) |
| 1748 | 1621 | { |
| 1749 | 1622 | /* high impedance */ |
| 1750 | | tms->read_latch = 0xff; |
| 1623 | m_read_latch = 0xff; |
| 1751 | 1624 | return; |
| 1752 | 1625 | } |
| 1753 | 1626 | if (state) |
| r22818 | r22819 | |
| 1761 | 1634 | logerror("Scheduling ready cycle for /WS...\n"); |
| 1762 | 1635 | #endif |
| 1763 | 1636 | /* upon /WS being activated, /READY goes inactive after 100 nsec from data sheet, through 3 asynchronous gates on patent. This is effectively within one clock, so we immediately set io_ready to 0 and activate the callback. */ |
| 1764 | | tms->io_ready = 0; |
| 1765 | | update_ready_state(tms); |
| 1637 | m_io_ready = 0; |
| 1638 | update_ready_state(); |
| 1766 | 1639 | /* Now comes the complicated part: long does /READY stay inactive, when /WS is pulled low? This depends ENTIRELY on the command written, or whether the chip is in speak external mode or not... |
| 1767 | 1640 | Speak external mode: ~16 cycles |
| 1768 | 1641 | Command Mode: |
| r22818 | r22819 | |
| 1774 | 1647 | SET RATE (5220C only): ? cycles (probably ~16) |
| 1775 | 1648 | */ |
| 1776 | 1649 | // TODO: actually HANDLE the timing differences! currently just assuming always 16 cycles |
| 1777 | | tms->device->machine().scheduler().timer_set(attotime::from_hz(device->clock()/16), FUNC(io_ready_cb), 1, tms); // this should take around 10-16 (closer to ~15) cycles to complete for fifo writes, TODO: but actually depends on what command is written if in command mode |
| 1650 | m_timer_io_ready->adjust(attotime::from_hz(clock()/16), 1); // this should take around 10-16 (closer to ~15) cycles to complete for fifo writes, TODO: but actually depends on what command is written if in command mode |
| 1778 | 1651 | } |
| 1779 | 1652 | } |
| 1780 | 1653 | } |
| r22818 | r22819 | |
| 1785 | 1658 | |
| 1786 | 1659 | ***********************************************************************************************/ |
| 1787 | 1660 | |
| 1788 | | WRITE8_DEVICE_HANDLER( tms5220_data_w ) |
| 1661 | WRITE8_MEMBER( tms5220_device::data_w ) |
| 1789 | 1662 | { |
| 1790 | | tms5220_state *tms = get_safe_token(device); |
| 1791 | 1663 | #ifdef DEBUG_RS_WS |
| 1792 | 1664 | logerror("tms5220_data_w: data %02x\n", data); |
| 1793 | 1665 | #endif |
| 1794 | | if (!tms->true_timing) |
| 1666 | if (!m_true_timing) |
| 1795 | 1667 | { |
| 1796 | 1668 | /* bring up to date first */ |
| 1797 | | tms->stream->update(); |
| 1798 | | tms5220_data_write(tms, data); |
| 1669 | m_stream->update(); |
| 1670 | data_write(data); |
| 1799 | 1671 | } |
| 1800 | 1672 | else |
| 1801 | 1673 | { |
| 1802 | 1674 | /* actually in a write ? */ |
| 1803 | 1675 | #ifdef DEBUG_RS_WS |
| 1804 | | if (!(tms->rs_ws == 0x02)) |
| 1805 | | logerror("tms5220_data_w: data written outside ws, status: %02x!\n", tms->rs_ws); |
| 1676 | if (!(m_rs_ws == 0x02)) |
| 1677 | logerror("tms5220_data_w: data written outside ws, status: %02x!\n", m_rs_ws); |
| 1806 | 1678 | #endif |
| 1807 | | tms->write_latch = data; |
| 1679 | m_write_latch = data; |
| 1808 | 1680 | } |
| 1809 | 1681 | } |
| 1810 | 1682 | |
| r22818 | r22819 | |
| 1816 | 1688 | |
| 1817 | 1689 | ***********************************************************************************************/ |
| 1818 | 1690 | |
| 1819 | | READ8_DEVICE_HANDLER( tms5220_status_r ) |
| 1691 | READ8_MEMBER( tms5220_device::status_r ) |
| 1820 | 1692 | { |
| 1821 | | tms5220_state *tms = get_safe_token(device); |
| 1822 | | if (!tms->true_timing) |
| 1693 | if (!m_true_timing) |
| 1823 | 1694 | { |
| 1824 | 1695 | /* bring up to date first */ |
| 1825 | | tms->stream->update(); |
| 1826 | | return tms5220_status_read(tms); |
| 1696 | m_stream->update(); |
| 1697 | return status_read(); |
| 1827 | 1698 | } |
| 1828 | 1699 | else |
| 1829 | 1700 | { |
| 1830 | 1701 | /* actually in a read ? */ |
| 1831 | | if (tms->rs_ws == 0x01) |
| 1832 | | return tms->read_latch; |
| 1702 | if (m_rs_ws == 0x01) |
| 1703 | return m_read_latch; |
| 1833 | 1704 | #ifdef DEBUG_RS_WS |
| 1834 | 1705 | else |
| 1835 | 1706 | logerror("tms5220_status_r: data read outside rs!\n"); |
| r22818 | r22819 | |
| 1846 | 1717 | |
| 1847 | 1718 | ***********************************************************************************************/ |
| 1848 | 1719 | |
| 1849 | | READ_LINE_DEVICE_HANDLER( tms5220_readyq_r ) |
| 1720 | READ_LINE_MEMBER( tms5220_device::readyq_r ) |
| 1850 | 1721 | { |
| 1851 | | tms5220_state *tms = get_safe_token(device); |
| 1852 | 1722 | /* bring up to date first */ |
| 1853 | | tms->stream->update(); |
| 1854 | | return !tms5220_ready_read(tms); |
| 1723 | m_stream->update(); |
| 1724 | return !ready_read(); |
| 1855 | 1725 | } |
| 1856 | 1726 | |
| 1857 | 1727 | |
| r22818 | r22819 | |
| 1862 | 1732 | |
| 1863 | 1733 | ***********************************************************************************************/ |
| 1864 | 1734 | |
| 1865 | | double tms5220_time_to_ready(device_t *device) |
| 1735 | double tms5220_device::time_to_ready() |
| 1866 | 1736 | { |
| 1867 | | tms5220_state *tms = get_safe_token(device); |
| 1868 | 1737 | double cycles; |
| 1869 | 1738 | |
| 1870 | 1739 | /* bring up to date first */ |
| 1871 | | tms->stream->update(); |
| 1872 | | cycles = tms5220_cycles_to_ready(tms); |
| 1873 | | return cycles * 80.0 / tms->clock; |
| 1740 | m_stream->update(); |
| 1741 | cycles = cycles_to_ready(); |
| 1742 | return cycles * 80.0 / m_clock; |
| 1874 | 1743 | } |
| 1875 | 1744 | |
| 1876 | 1745 | |
| r22818 | r22819 | |
| 1881 | 1750 | |
| 1882 | 1751 | ***********************************************************************************************/ |
| 1883 | 1752 | |
| 1884 | | READ_LINE_DEVICE_HANDLER( tms5220_intq_r ) |
| 1753 | READ_LINE_MEMBER( tms5220_device::intq_r ) |
| 1885 | 1754 | { |
| 1886 | | tms5220_state *tms = get_safe_token(device); |
| 1887 | 1755 | /* bring up to date first */ |
| 1888 | | tms->stream->update(); |
| 1889 | | return !tms5220_int_read(tms); |
| 1756 | m_stream->update(); |
| 1757 | return !int_read(); |
| 1890 | 1758 | } |
| 1891 | 1759 | |
| 1892 | 1760 | |
| r22818 | r22819 | |
| 1897 | 1765 | |
| 1898 | 1766 | ***********************************************************************************************/ |
| 1899 | 1767 | |
| 1900 | | static STREAM_UPDATE( tms5220_update ) |
| 1768 | //------------------------------------------------- |
| 1769 | // sound_stream_update - handle a stream update |
| 1770 | //------------------------------------------------- |
| 1771 | |
| 1772 | void tms5220_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples) |
| 1901 | 1773 | { |
| 1902 | | tms5220_state *tms = (tms5220_state *)param; |
| 1903 | 1774 | INT16 sample_data[MAX_SAMPLE_CHUNK]; |
| 1904 | 1775 | stream_sample_t *buffer = outputs[0]; |
| 1905 | 1776 | |
| r22818 | r22819 | |
| 1910 | 1781 | int index; |
| 1911 | 1782 | |
| 1912 | 1783 | /* generate the samples and copy to the target buffer */ |
| 1913 | | tms5220_process(tms, sample_data, length); |
| 1784 | process(sample_data, length); |
| 1914 | 1785 | for (index = 0; index < length; index++) |
| 1915 | 1786 | *buffer++ = sample_data[index]; |
| 1916 | 1787 | |
| r22818 | r22819 | |
| 1927 | 1798 | |
| 1928 | 1799 | ***********************************************************************************************/ |
| 1929 | 1800 | |
| 1930 | | void tms5220_set_frequency(device_t *device, int frequency) |
| 1801 | void tms5220_device::set_frequency(int frequency) |
| 1931 | 1802 | { |
| 1932 | | tms5220_state *tms = get_safe_token(device); |
| 1933 | | tms->stream->set_sample_rate(frequency / 80); |
| 1934 | | tms->clock = frequency; |
| 1803 | m_stream->set_sample_rate(frequency / 80); |
| 1804 | m_clock = frequency; |
| 1935 | 1805 | } |
| 1936 | 1806 | |
| 1937 | 1807 | const device_type TMS5220C = &device_creator<tms5220c_device>; |
| r22818 | r22819 | |
| 1941 | 1811 | { |
| 1942 | 1812 | } |
| 1943 | 1813 | |
| 1944 | | //------------------------------------------------- |
| 1945 | | // device_start - device-specific startup |
| 1946 | | //------------------------------------------------- |
| 1947 | 1814 | |
| 1948 | | void tms5220c_device::device_start() |
| 1949 | | { |
| 1950 | | DEVICE_START_NAME( tms5220c )(this); |
| 1951 | | } |
| 1952 | | |
| 1953 | | //------------------------------------------------- |
| 1954 | | // sound_stream_update - handle a stream update |
| 1955 | | //------------------------------------------------- |
| 1956 | | |
| 1957 | | void tms5220c_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples) |
| 1958 | | { |
| 1959 | | // should never get here |
| 1960 | | fatalerror("sound_stream_update called; not applicable to legacy sound devices\n"); |
| 1961 | | } |
| 1962 | | |
| 1963 | | |
| 1964 | 1815 | const device_type TMS5220 = &device_creator<tms5220_device>; |
| 1965 | 1816 | |
| 1966 | 1817 | tms5220_device::tms5220_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) |
| 1967 | 1818 | : device_t(mconfig, TMS5220, "TMS5220", tag, owner, clock), |
| 1968 | | device_sound_interface(mconfig, *this) |
| 1819 | device_sound_interface(mconfig, *this), |
| 1820 | m_irq_handler(*this), |
| 1821 | m_readyq_handler(*this), |
| 1822 | m_speechrom_tag(NULL) |
| 1969 | 1823 | { |
| 1970 | | m_token = global_alloc_clear(tms5220_state); |
| 1971 | 1824 | } |
| 1825 | |
| 1972 | 1826 | tms5220_device::tms5220_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock) |
| 1973 | 1827 | : device_t(mconfig, type, name, tag, owner, clock), |
| 1974 | | device_sound_interface(mconfig, *this) |
| 1828 | device_sound_interface(mconfig, *this), |
| 1829 | m_irq_handler(*this), |
| 1830 | m_readyq_handler(*this), |
| 1831 | m_speechrom_tag(NULL) |
| 1975 | 1832 | { |
| 1976 | | m_token = global_alloc_clear(tms5220_state); |
| 1977 | 1833 | } |
| 1978 | 1834 | |
| 1979 | 1835 | //------------------------------------------------- |
| r22818 | r22819 | |
| 1986 | 1842 | { |
| 1987 | 1843 | } |
| 1988 | 1844 | |
| 1989 | | //------------------------------------------------- |
| 1990 | | // device_start - device-specific startup |
| 1991 | | //------------------------------------------------- |
| 1992 | 1845 | |
| 1993 | | void tms5220_device::device_start() |
| 1994 | | { |
| 1995 | | DEVICE_START_NAME( tms5220 )(this); |
| 1996 | | } |
| 1997 | | |
| 1998 | | //------------------------------------------------- |
| 1999 | | // device_reset - device-specific reset |
| 2000 | | //------------------------------------------------- |
| 2001 | | |
| 2002 | | void tms5220_device::device_reset() |
| 2003 | | { |
| 2004 | | DEVICE_RESET_NAME( tms5220 )(this); |
| 2005 | | } |
| 2006 | | |
| 2007 | | //------------------------------------------------- |
| 2008 | | // sound_stream_update - handle a stream update |
| 2009 | | //------------------------------------------------- |
| 2010 | | |
| 2011 | | void tms5220_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples) |
| 2012 | | { |
| 2013 | | // should never get here |
| 2014 | | fatalerror("sound_stream_update called; not applicable to legacy sound devices\n"); |
| 2015 | | } |
| 2016 | | |
| 2017 | | |
| 2018 | 1846 | const device_type TMC0285 = &device_creator<tmc0285_device>; |
| 2019 | 1847 | |
| 2020 | 1848 | tmc0285_device::tmc0285_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) |
| r22818 | r22819 | |
| 2022 | 1850 | { |
| 2023 | 1851 | } |
| 2024 | 1852 | |
| 2025 | | //------------------------------------------------- |
| 2026 | | // device_start - device-specific startup |
| 2027 | | //------------------------------------------------- |
| 2028 | 1853 | |
| 2029 | | void tmc0285_device::device_start() |
| 2030 | | { |
| 2031 | | DEVICE_START_NAME( tmc0285 )(this); |
| 2032 | | } |
| 2033 | | |
| 2034 | | //------------------------------------------------- |
| 2035 | | // sound_stream_update - handle a stream update |
| 2036 | | //------------------------------------------------- |
| 2037 | | |
| 2038 | | void tmc0285_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples) |
| 2039 | | { |
| 2040 | | // should never get here |
| 2041 | | fatalerror("sound_stream_update called; not applicable to legacy sound devices\n"); |
| 2042 | | } |
| 2043 | | |
| 2044 | | |
| 2045 | 1854 | const device_type TMS5200 = &device_creator<tms5200_device>; |
| 2046 | 1855 | |
| 2047 | 1856 | tms5200_device::tms5200_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) |
| 2048 | 1857 | : tms5220_device(mconfig, TMS5200, "TMS5200", tag, owner, clock) |
| 2049 | 1858 | { |
| 2050 | 1859 | } |
| 2051 | | |
| 2052 | | //------------------------------------------------- |
| 2053 | | // device_start - device-specific startup |
| 2054 | | //------------------------------------------------- |
| 2055 | | |
| 2056 | | void tms5200_device::device_start() |
| 2057 | | { |
| 2058 | | DEVICE_START_NAME( tms5200 )(this); |
| 2059 | | } |
| 2060 | | |
| 2061 | | //------------------------------------------------- |
| 2062 | | // sound_stream_update - handle a stream update |
| 2063 | | //------------------------------------------------- |
| 2064 | | |
| 2065 | | void tms5200_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples) |
| 2066 | | { |
| 2067 | | // should never get here |
| 2068 | | fatalerror("sound_stream_update called; not applicable to legacy sound devices\n"); |
| 2069 | | } |
| 2070 | | |
| 2071 | | |
| 2072 | | |
| 2073 | | |
| 2074 | | /****************************************************************************** |
| 2075 | | New class implementation |
| 2076 | | ******************************************************************************/ |
| 2077 | | |
| 2078 | | #define M_INTERP_SHIFT >> m_coeff->interp_coeff[m_IP] |
| 2079 | | |
| 2080 | | tms52xx_device::tms52xx_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, const struct tms5100_coeffs* coeffs, const int var, device_t *owner, UINT32 clock) |
| 2081 | | : device_t(mconfig, type, name, tag, owner, clock), |
| 2082 | | device_sound_interface(mconfig, *this), |
| 2083 | | m_variant(var), |
| 2084 | | m_coeff(coeffs) |
| 2085 | | { |
| 2086 | | } |
| 2087 | | |
| 2088 | | |
| 2089 | | tms5220n_device::tms5220n_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) |
| 2090 | | : tms52xx_device(mconfig, TMS5220N, "TMS5220N", tag, &tms5220_coeff, TMS5220_IS_5220, owner, clock) |
| 2091 | | { |
| 2092 | | } |
| 2093 | | |
| 2094 | | tms5220cn_device::tms5220cn_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) |
| 2095 | | : tms52xx_device(mconfig, TMS5220CN, "TMS5220CN", tag, &tms5220_coeff, TMS5220_IS_5220C, owner, clock) |
| 2096 | | { |
| 2097 | | } |
| 2098 | | |
| 2099 | | tmc0285n_device::tmc0285n_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) |
| 2100 | | : tms52xx_device(mconfig, TMC0285N, "TMC0285N", tag, &tms5200_coeff, TMS5220_IS_TMC0285, owner, clock) |
| 2101 | | { |
| 2102 | | } |
| 2103 | | |
| 2104 | | tms5200n_device::tms5200n_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) |
| 2105 | | : tms52xx_device(mconfig, TMS5200N, "TMS5200N", tag, &tms5200_coeff, TMS5220_IS_5200, owner, clock) |
| 2106 | | { |
| 2107 | | } |
| 2108 | | |
| 2109 | | void tms52xx_device::device_start() |
| 2110 | | { |
| 2111 | | const tms52xx_config *conf = reinterpret_cast<const tms52xx_config *>(static_config()); |
| 2112 | | //m_table = region(); |
| 2113 | | |
| 2114 | | /* resolve irq and readyq line */ |
| 2115 | | m_irq_func.resolve(conf->irq_func, *this); |
| 2116 | | m_readyq_func.resolve(conf->readyq_func, *this); |
| 2117 | | m_read_mem.resolve(conf->read_mem, *this); |
| 2118 | | m_load_address.resolve(conf->load_address, *this); |
| 2119 | | m_read_and_branch.resolve(conf->read_and_branch, *this); |
| 2120 | | |
| 2121 | | /* initialize a stream */ |
| 2122 | | m_stream = machine().sound().stream_alloc(*this, 0, 1, clock() / 80, this); |
| 2123 | | |
| 2124 | | /*if (m_table == NULL) |
| 2125 | | { |
| 2126 | | assert_always(m_conf->M0_callback != NULL, "Missing _mandatory_ 'M0_callback' function pointer in the TMS5110 interface\n This function is used by TMS5220 to call for a new single bit\n needed to generate the speech when in VSM mode\n Aborting startup...\n"); |
| 2127 | | m_M0_callback = conf->M0_callback; |
| 2128 | | m_set_load_address = conf->load_address; |
| 2129 | | } |
| 2130 | | else |
| 2131 | | { |
| 2132 | | m_M0_callback = speech_rom_read_bit; |
| 2133 | | m_set_load_address = speech_rom_set_addr; |
| 2134 | | }*/ |
| 2135 | | |
| 2136 | | /* not during reset which is called frm within a write! */ |
| 2137 | | m_io_ready = true; |
| 2138 | | m_true_timing = false; |
| 2139 | | m_rs_ws = 0x03; // rs and ws are assumed to be inactive on device startup |
| 2140 | | |
| 2141 | | m_ready_timer = timer_alloc(0); |
| 2142 | | |
| 2143 | | register_for_save_states(); |
| 2144 | | } |
| 2145 | | |
| 2146 | | void tms52xx_device::device_reset() |
| 2147 | | { |
| 2148 | | m_digital_select = FORCE_DIGITAL; // assume analog output |
| 2149 | | // initialize the FIFO |
| 2150 | | // should we do a memset here to clear the fifo contents? |
| 2151 | | m_fifo_head = 0; |
| 2152 | | m_fifo_tail = 0; |
| 2153 | | m_fifo_count = 0; |
| 2154 | | m_fifo_bits_taken = 0; |
| 2155 | | |
| 2156 | | // initialize the chip state |
| 2157 | | /* Note that we do not actually clear IRQ on start-up: IRQ is even raised |
| 2158 | | * if m_buffer_empty or m_buffer_low are 0 */ |
| 2159 | | m_speaking_now = false; |
| 2160 | | m_speak_external = false; |
| 2161 | | m_talk_status = false; |
| 2162 | | m_irq_pin = 0; // CLEAR_LINE |
| 2163 | | m_ready_pin = 0; // CLEAR_LINE |
| 2164 | | |
| 2165 | | set_interrupt_state(0); // CLEAR_LINE |
| 2166 | | update_ready_state(); |
| 2167 | | m_buffer_empty = true; |
| 2168 | | m_buffer_low = true; |
| 2169 | | |
| 2170 | | m_RDB_flag = false; |
| 2171 | | |
| 2172 | | /* initialize the energy/pitch/k states */ |
| 2173 | | #ifdef PERFECT_INTERPOLATION_HACK |
| 2174 | | m_old_frame_energy_idx = 0; |
| 2175 | | m_old_frame_pitch_idx = 0; |
| 2176 | | memset(m_old_frame_k_idx, 0, sizeof(m_old_frame_k_idx)); |
| 2177 | | #endif |
| 2178 | | |
| 2179 | | m_new_frame_energy_idx = 0; |
| 2180 | | m_current_energy = 0; |
| 2181 | | m_target_energy = 0; |
| 2182 | | m_new_frame_pitch_idx = 0; |
| 2183 | | m_current_pitch = 0; |
| 2184 | | m_target_pitch = 0; |
| 2185 | | |
| 2186 | | memset(m_new_frame_k_idx, 0, sizeof(m_new_frame_k_idx)); |
| 2187 | | memset(m_current_k, 0, sizeof(m_current_k)); |
| 2188 | | memset(m_target_k, 0, sizeof(m_target_k)); |
| 2189 | | |
| 2190 | | /* initialize the sample generators */ |
| 2191 | | m_inhibit = true; |
| 2192 | | m_subcycle = 0; |
| 2193 | | m_tms5220c_rate = 0; |
| 2194 | | m_pitch_count = 0; |
| 2195 | | m_PC = 0; |
| 2196 | | |
| 2197 | | m_subc_reload = FORCE_SUBC_RELOAD; |
| 2198 | | m_OLDE = 1; |
| 2199 | | m_OLDP = 1; |
| 2200 | | |
| 2201 | | m_IP = reload_table[m_tms5220c_rate&0x3]; |
| 2202 | | m_RNG = 0x1FFF; |
| 2203 | | memset(m_u, 0, sizeof(m_u)); |
| 2204 | | memset(m_x, 0, sizeof(m_x)); |
| 2205 | | |
| 2206 | | m_load_address(0, 0); |
| 2207 | | m_schedule_dummy_read = true; |
| 2208 | | } |
| 2209 | | |
| 2210 | | /****************************************************************************** |
| 2211 | | set_interrupt_state -- generate an interrupt |
| 2212 | | *******************************************************************************/ |
| 2213 | | |
| 2214 | | void tms52xx_device::set_interrupt_state(int state) |
| 2215 | | { |
| 2216 | | #ifdef DEBUG_PIN_READS |
| 2217 | | logerror("tms52xx: irq pin set to state %d\n", state); |
| 2218 | | #endif |
| 2219 | | if (state != m_irq_pin) m_irq_func(!state); |
| 2220 | | m_irq_pin = state; |
| 2221 | | } |
| 2222 | | |
| 2223 | | /****************************************************************************** |
| 2224 | | update_ready_state -- update the ready line |
| 2225 | | *******************************************************************************/ |
| 2226 | | |
| 2227 | | void tms52xx_device::update_ready_state() |
| 2228 | | { |
| 2229 | | int state = ready_read(); |
| 2230 | | #ifdef DEBUG_PIN_READS |
| 2231 | | logerror("tms52xx: ready pin set to state %d\n", state); |
| 2232 | | #endif |
| 2233 | | if (state != m_ready_pin) m_readyq_func(!state); |
| 2234 | | m_ready_pin = state; |
| 2235 | | } |
| 2236 | | |
| 2237 | | /****************************************************************************** |
| 2238 | | tms5220_set_frequency -- adjusts the playback frequency |
| 2239 | | *******************************************************************************/ |
| 2240 | | |
| 2241 | | void tms52xx_device::set_frequency(int frequency) |
| 2242 | | { |
| 2243 | | m_stream->set_sample_rate(frequency / 80); |
| 2244 | | m_clock = frequency; |
| 2245 | | } |
| 2246 | | |
| 2247 | | /****************************************************************************** |
| 2248 | | tms5220_update -- update the sound chip so that it is in sync with CPU execution |
| 2249 | | *******************************************************************************/ |
| 2250 | | |
| 2251 | | void tms52xx_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples) |
| 2252 | | { |
| 2253 | | INT16 sample_data[MAX_SAMPLE_CHUNK]; |
| 2254 | | stream_sample_t *buffer = outputs[0]; |
| 2255 | | |
| 2256 | | /* loop while we still have samples to generate */ |
| 2257 | | while (samples>0) |
| 2258 | | { |
| 2259 | | int length = (samples > MAX_SAMPLE_CHUNK) ? MAX_SAMPLE_CHUNK : samples; |
| 2260 | | int index; |
| 2261 | | |
| 2262 | | /* generate the samples and copy to the target buffer */ |
| 2263 | | process(sample_data, length); |
| 2264 | | for (index = 0; index < length; index++) |
| 2265 | | *buffer++ = sample_data[index]; |
| 2266 | | |
| 2267 | | /* account for the samples */ |
| 2268 | | samples -= length; |
| 2269 | | } |
| 2270 | | } |
| 2271 | | |
| 2272 | | /****************************************************************************** |
| 2273 | | process -- fill the buffer with a specific number of samples |
| 2274 | | *******************************************************************************/ |
| 2275 | | |
| 2276 | | void tms52xx_device::process(INT16 *buffer, unsigned int size) |
| 2277 | | { |
| 2278 | | int buf_count=0; |
| 2279 | | int i, bitout; |
| 2280 | | bool zpar; |
| 2281 | | INT32 this_sample; |
| 2282 | | |
| 2283 | | // The following gotos are probably safe to remove |
| 2284 | | // if we're empty and still not speaking, fill with nothingness |
| 2285 | | if (!m_speaking_now) goto empty; |
| 2286 | | |
| 2287 | | // if speak external is set, but talk status is not (yet) set, |
| 2288 | | // wait for buffer low to clear |
| 2289 | | if (!m_talk_status && m_speak_external && m_buffer_low) goto empty; |
| 2290 | | |
| 2291 | | // loop until the buffer is full or we've stopped speaking |
| 2292 | | while ((size > 0) && m_speaking_now) |
| 2293 | | { |
| 2294 | | /* if it is the appropriate time to update the old energy/pitch idxes, |
| 2295 | | * i.e. when IP=7, PC=12, T=17, subcycle=2, do so. Since IP=7 PC=12 T=17 |
| 2296 | | * is JUST BEFORE the transition to IP=0 PC=0 T=0 sybcycle=(0 or 1), |
| 2297 | | * which happens 4 T-cycles later), we change on the latter. |
| 2298 | | */ |
| 2299 | | if ((m_IP == 0) && (m_PC == 0) && (m_subcycle < 2)) |
| 2300 | | { |
| 2301 | | m_OLDE = (m_new_frame_energy_idx == 0); |
| 2302 | | m_OLDP = (m_new_frame_pitch_idx == 0); |
| 2303 | | } |
| 2304 | | |
| 2305 | | /* if we're ready for a new frame to be applied, i.e. when IP=0, PC=12, Sub=1 |
| 2306 | | * (In reality, the frame was really loaded incrementally during the |
| 2307 | | * entire IP=0 PC=x time period, but it doesn't affect anything until IP=0 PC=12 happens) |
| 2308 | | */ |
| 2309 | | if ((m_IP == 0) && (m_PC == 12) && (m_subcycle == 1)) |
| 2310 | | { |
| 2311 | | // HACK for regression testing, be sure to comment out before release! |
| 2312 | | // m_RNG = 0x1234; |
| 2313 | | // end HACK |
| 2314 | | |
| 2315 | | // appropriately override the interp count if needed; this will be incremented after the frame parse! |
| 2316 | | m_IP = reload_table[m_tms5220c_rate & 0x3]; |
| 2317 | | |
| 2318 | | #ifdef PERFECT_INTERPOLATION_HACK |
| 2319 | | // remember previous frame energy, pitch, and coefficients |
| 2320 | | m_old_frame_energy_idx = m_new_frame_energy_idx; |
| 2321 | | m_old_frame_pitch_idx = m_new_frame_pitch_idx; |
| 2322 | | for (i = 0; i < m_coeff->num_k; i++) |
| 2323 | | m_old_frame_k_idx[i] = m_new_frame_k_idx[i]; |
| 2324 | | #endif |
| 2325 | | |
| 2326 | | // if the talk status was clear last frame, halt speech now. |
| 2327 | | if (m_talk_status == false) |
| 2328 | | { |
| 2329 | | #ifdef DEBUG_GENERATION |
| 2330 | | fprintf(stderr,"tms5220_process: processing frame: talk status = 0 caused by stop frame or buffer empty, halting speech.\n"); |
| 2331 | | #endif |
| 2332 | | m_speaking_now = false; // finally halt speech |
| 2333 | | goto empty; |
| 2334 | | } |
| 2335 | | |
| 2336 | | // Parse a new frame into the new_target_energy, new_target_pitch and new_target_k[] |
| 2337 | | parse_frame(); |
| 2338 | | #ifdef DEBUG_PARSE_FRAME_DUMP |
| 2339 | | fprintf(stderr,"\n"); |
| 2340 | | #endif |
| 2341 | | // if the new frame is a stop frame, set an interrupt and set talk status to 0 |
| 2342 | | if (M_NEW_FRAME_STOP_FLAG == 1) |
| 2343 | | { |
| 2344 | | m_talk_status = false; |
| 2345 | | m_speak_external = false; |
| 2346 | | set_interrupt_state(1); |
| 2347 | | update_status_and_ints(); |
| 2348 | | } |
| 2349 | | |
| 2350 | | // in all cases where interpolation would be inhibited, set the inhibit flag; otherwise clear it. |
| 2351 | | // Interpolation inhibit cases: |
| 2352 | | // * Old frame was voiced, new is unvoiced |
| 2353 | | // * Old frame was silence/zero energy, new has nonzero energy |
| 2354 | | // * Old frame was unvoiced, new is voiced |
| 2355 | | |
| 2356 | | if ( ((M_OLD_FRAME_UNVOICED_FLAG == false) && (M_NEW_FRAME_UNVOICED_FLAG == true)) |
| 2357 | | || ((M_OLD_FRAME_UNVOICED_FLAG == true) && (M_NEW_FRAME_UNVOICED_FLAG == false)) /* this line needs further investigation, starwars tie fighters may sound better without it */ |
| 2358 | | || ((M_OLD_FRAME_SILENCE_FLAG == true) && (M_NEW_FRAME_SILENCE_FLAG == false)) ) |
| 2359 | | m_inhibit = true; |
| 2360 | | else // normal frame, normal interpolation |
| 2361 | | m_inhibit = false; |
| 2362 | | |
| 2363 | | // load new frame targets from tables, using parsed indices |
| 2364 | | m_target_energy = m_coeff->energytable[m_new_frame_energy_idx]; |
| 2365 | | m_target_pitch = m_coeff->pitchtable[m_new_frame_pitch_idx]; |
| 2366 | | zpar = M_NEW_FRAME_UNVOICED_FLAG; // find out if parameters k5-k10 should be zeroed |
| 2367 | | for (i = 0; i < 4; i++) |
| 2368 | | m_target_k[i] = m_coeff->ktable[i][m_new_frame_k_idx[i]]; |
| 2369 | | for (i = 4; i < m_coeff->num_k; i++) |
| 2370 | | m_target_k[i] = (m_coeff->ktable[i][m_new_frame_k_idx[i]] * (1-zpar)); |
| 2371 | | |
| 2372 | | #ifdef DEBUG_GENERATION |
| 2373 | | /* Debug info for current parsed frame */ |
| 2374 | | fprintf(stderr, "OLDE: %d; OLDP: %d; ", m_OLDE, m_OLDP); |
| 2375 | | fprintf(stderr,"Processing frame: "); |
| 2376 | | if (m_inhibit == 0) |
| 2377 | | fprintf(stderr, "Normal Frame\n"); |
| 2378 | | else |
| 2379 | | fprintf(stderr,"Interpolation Inhibited\n"); |
| 2380 | | fprintf(stderr,"*** current Energy, Pitch and Ks = %04d, %04d, %04d, %04d, %04d, %04d, %04d, %04d, %04d, %04d, %04d, %04d\n", |
| 2381 | | m_current_energy, m_current_pitch, |
| 2382 | | m_current_k[0], m_current_k[1], m_current_k[2], m_current_k[3], |
| 2383 | | m_current_k[4], m_current_k[5], m_current_k[6], m_current_k[7], |
| 2384 | | m_current_k[8], m_current_k[9]); |
| 2385 | | fprintf(stderr,"*** target Energy(idx), Pitch, and Ks = %04d(%x),%04d, %04d, %04d, %04d, %04d, %04d, %04d, %04d, %04d, %04d, %04d\n", |
| 2386 | | m_target_energy, m_new_frame_energy_idx, m_target_pitch, |
| 2387 | | m_target_k[0], m_target_k[1], m_target_k[2], m_target_k[3], |
| 2388 | | m_target_k[4], m_target_k[5], m_target_k[6], m_target_k[7], |
| 2389 | | m_target_k[8], m_target_k[9]); |
| 2390 | | #endif |
| 2391 | | |
| 2392 | | /* if TS is now 0, ramp the energy down to 0. Is this really correct to hardware? */ |
| 2393 | | if (!m_talk_status) |
| 2394 | | { |
| 2395 | | #ifdef DEBUG_GENERATION |
| 2396 | | fprintf(stderr,"Talk status is 0, forcing target energy to 0\n"); |
| 2397 | | #endif |
| 2398 | | m_target_energy = 0; |
| 2399 | | } |
| 2400 | | } |
| 2401 | | else // Not a new frame, just interpolate the existing frame. |
| 2402 | | { |
| 2403 | | bool inhibit_state = (m_inhibit && (m_IP != 0)); // disable inhibit when reaching the last interp period, but don't overwrite the tms->inhibit value |
| 2404 | | #ifdef PERFECT_INTERPOLATION_HACK |
| 2405 | | int samples_per_frame = (m_subc_reload!=0)? 175:266; // either (13 A cycles + 12 B cycles) * 7 interps for normal SPEAK/SPKEXT, or (13*2 A cycles + 12 B cycles) * 7 interps for SPKSLOW |
| 2406 | | //int samples_per_frame = (m_subc_reload!=0)?200:304; // either (13 A cycles + 12 B cycles) * 8 interps for normal SPEAK/SPKEXT, or (13*2 A cycles + 12 B cycles) * 8 interps for SPKSLOW |
| 2407 | | int current_sample = (m_subcycle - m_subc_reload)+(m_PC*(3-m_subc_reload))+((m_subc_reload?25:38)*((m_IP-1)&7)); |
| 2408 | | |
| 2409 | | zpar = M_OLD_FRAME_UNVOICED_FLAG; |
| 2410 | | //fprintf(stderr, "CS: %03d", current_sample); |
| 2411 | | // reset the current energy, pitch, etc to what it was at frame start |
| 2412 | | m_current_energy = m_coeff->energytable[m_old_frame_energy_idx]; |
| 2413 | | m_current_pitch = m_coeff->pitchtable[m_old_frame_pitch_idx]; |
| 2414 | | for (i = 0; i < 4; i++) |
| 2415 | | m_current_k[i] = m_coeff->ktable[i][m_old_frame_k_idx[i]]; |
| 2416 | | for (i = 4; i < m_coeff->num_k; i++) |
| 2417 | | m_current_k[i] = (m_coeff->ktable[i][m_old_frame_k_idx[i]] * (1-zpar)); |
| 2418 | | // now adjust each value to be exactly correct for each of the samples per frame |
| 2419 | | if (m_IP != 0) // if we're still interpolating... |
| 2420 | | { |
| 2421 | | if (!inhibit_state) |
| 2422 | | { |
| 2423 | | m_current_energy += ((m_target_energy - m_current_energy)*current_sample)/samples_per_frame; |
| 2424 | | m_current_pitch += ((m_target_pitch - m_current_pitch)*current_sample)/samples_per_frame; |
| 2425 | | for (i = 0; i < m_coeff->num_k; i++) |
| 2426 | | { |
| 2427 | | m_current_k[i] += ((m_target_k[i] - m_current_k[i])*current_sample)/samples_per_frame; |
| 2428 | | } |
| 2429 | | } |
| 2430 | | } |
| 2431 | | else // we're done, play this frame for 1/8 frame. |
| 2432 | | { |
| 2433 | | m_current_energy = m_target_energy; |
| 2434 | | m_current_pitch = m_target_pitch; |
| 2435 | | for (i = 0; i < m_coeff->num_k; i++) |
| 2436 | | m_current_k[i] = m_target_k[i]; |
| 2437 | | } |
| 2438 | | #else |
| 2439 | | // Updates to parameters only happen on subcycle '2' (B cycle) of PCs. |
| 2440 | | if (m_subcycle == 2) |
| 2441 | | { |
| 2442 | | if (!inhibit_state) |
| 2443 | | { |
| 2444 | | switch(m_PC) |
| 2445 | | { |
| 2446 | | case 0: // PC = 0, B cycle, write updated energy |
| 2447 | | m_current_energy += ((m_target_energy - m_current_energy) M_INTERP_SHIFT); |
| 2448 | | break; |
| 2449 | | case 1: /* PC = 1, B cycle, write updated pitch */ |
| 2450 | | m_current_pitch += ((m_target_pitch - m_current_pitch) M_INTERP_SHIFT); |
| 2451 | | break; |
| 2452 | | case 2: case 3: case 4: case 5: case 6: case 7: case 8: case 9: case 10: case 11: |
| 2453 | | /* PC = 2 through 11, B cycle, write updated K1 through K10 */ |
| 2454 | | m_current_k[m_PC-2] += ((m_target_k[m_PC-2] - m_current_k[m_PC-2]) M_INTERP_SHIFT); |
| 2455 | | break; |
| 2456 | | case 12: /* PC = 12, do nothing */ |
| 2457 | | break; |
| 2458 | | } |
| 2459 | | } |
| 2460 | | } |
| 2461 | | #endif |
| 2462 | | } |
| 2463 | | |
| 2464 | | // calculate the output |
| 2465 | | if (M_OLD_FRAME_UNVOICED_FLAG == true) |
| 2466 | | { |
| 2467 | | // generate unvoiced samples here |
| 2468 | | if (m_RNG & 1) |
| 2469 | | m_excitation_data = ~0x3F; // according to the patent it is (either + or -) half of the maximum value in the chirp table, so either 01000000(0x40) or 11000000(0xC0) |
| 2470 | | else |
| 2471 | | m_excitation_data = 0x40; |
| 2472 | | } |
| 2473 | | else // (M_OLD_FRAME_UNVOICED_FLAG == false) |
| 2474 | | { |
| 2475 | | // generate voiced samples here |
| 2476 | | // US patent 4331836 Figure 14B shows, and logic would hold, that a pitch based chirp |
| 2477 | | // function has a chirp/peak and then a long chain of zeroes. |
| 2478 | | // The last entry of the chirp rom is at address 0b110011 (51d), the 52nd sample, |
| 2479 | | // and if the address reaches that point the ADDRESS incrementer is |
| 2480 | | // disabled, forcing all samples beyond 51d to be == 51d |
| 2481 | | |
| 2482 | | if (m_pitch_count >= 51) |
| 2483 | | m_excitation_data = m_coeff->chirptable[51]; |
| 2484 | | else // tms->pitch_count < 51 |
| 2485 | | m_excitation_data = m_coeff->chirptable[m_pitch_count]; |
| 2486 | | } |
| 2487 | | |
| 2488 | | // Update LFSR *20* times every sample (once per T cycle), like patent shows |
| 2489 | | for (i=0; i<20; i++) |
| 2490 | | { |
| 2491 | | bitout = ((m_RNG >> 12) & 1) ^ |
| 2492 | | ((m_RNG >> 3) & 1) ^ |
| 2493 | | ((m_RNG >> 2) & 1) ^ |
| 2494 | | ((m_RNG >> 0) & 1); |
| 2495 | | m_RNG <<= 1; |
| 2496 | | m_RNG |= bitout; |
| 2497 | | } |
| 2498 | | |
| 2499 | | this_sample = lattice_filter(); // execute lattice filter |
| 2500 | | #ifdef DEBUG_GENERATION_VERBOSE |
| 2501 | | //fprintf(stderr,"C:%01d; ",tms->subcycle); |
| 2502 | | fprintf(stderr,"IP:%01d PC:%02d X:%04d E:%03d P:%03d Pc:%03d ",m_IP, m_PC, |
| 2503 | | m_excitation_data, m_current_energy, m_current_pitch, m_pitch_count); |
| 2504 | | //fprintf(stderr,"X:%04d E:%03d P:%03d Pc:%03d ", m_excitation_data, m_current_energy, m_current_pitch, m_pitch_count); |
| 2505 | | for (i=0; i<10; i++) |
| 2506 | | fprintf(stderr,"K%d:%04d ", i+1, m_current_k[i]); |
| 2507 | | fprintf(stderr,"Out:%06d", this_sample); |
| 2508 | | fprintf(stderr,"\n"); |
| 2509 | | #endif |
| 2510 | | // next, force result to 14 bits (since its possible that the addition at the final (k1) stage of the lattice overflowed) |
| 2511 | | while (this_sample > 16383) this_sample -= 32768; |
| 2512 | | while (this_sample < -16384) this_sample += 32768; |
| 2513 | | if (!m_digital_select) // analog SPK pin output is only 8 bits, with clipping |
| 2514 | | buffer[buf_count] = clip_analog(this_sample); |
| 2515 | | else // digital I/O pin output is 12 bits |
| 2516 | | { |
| 2517 | | #ifdef ALLOW_4_LSB |
| 2518 | | // input: ssss ssss ssss ssss ssnn nnnn nnnn nnnn |
| 2519 | | // N taps: ^ = 0x2000; |
| 2520 | | // output: ssss ssss ssss ssss snnn nnnn nnnn nnnN |
| 2521 | | buffer[buf_count] = (this_sample<<1)|((this_sample&0x2000)>>13); |
| 2522 | | #else |
| 2523 | | this_sample &= ~0xF; |
| 2524 | | // input: ssss ssss ssss ssss ssnn nnnn nnnn 0000 |
| 2525 | | // N taps: ^^ ^^^ = 0x3E00; |
| 2526 | | // output: ssss ssss ssss ssss snnn nnnn nnnN NNNN |
| 2527 | | buffer[buf_count] = (this_sample<<1)|((this_sample&0x3E00)>>9); |
| 2528 | | #endif |
| 2529 | | } |
| 2530 | | |
| 2531 | | // Update all counts |
| 2532 | | m_subcycle++; |
| 2533 | | if ((m_subcycle == 2) && (m_PC == 12)) |
| 2534 | | { |
| 2535 | | /* Circuit 412 in the patent acts a reset, resetting the pitch counter to 0 |
| 2536 | | * if INHIBIT was true during the most recent frame transition. |
| 2537 | | * The exact time this occurs is betwen IP=7, PC=12 sub=0, T=t12 |
| 2538 | | * and tms->IP = 0, PC=0 sub=0, T=t12, a period of exactly 20 cycles, |
| 2539 | | * which overlaps the time OLDE and OLDP are updated at IP=7 PC=12 T17 |
| 2540 | | * (and hence INHIBIT itself 2 t-cycles later). We do it here because it is |
| 2541 | | * convenient and should make no difference in output. |
| 2542 | | */ |
| 2543 | | if ((m_IP == 7) && m_inhibit) m_pitch_count = 0; |
| 2544 | | m_subcycle = m_subc_reload; |
| 2545 | | m_PC = 0; |
| 2546 | | m_IP++; |
| 2547 | | m_IP &= 0x7; |
| 2548 | | } |
| 2549 | | else if (m_subcycle == 3) |
| 2550 | | { |
| 2551 | | m_subcycle = m_subc_reload; |
| 2552 | | m_PC++; |
| 2553 | | } |
| 2554 | | m_pitch_count++; |
| 2555 | | if (m_pitch_count >= m_current_pitch) m_pitch_count = 0; |
| 2556 | | m_pitch_count &= 0x1FF; |
| 2557 | | buf_count++; |
| 2558 | | size--; |
| 2559 | | } |
| 2560 | | |
| 2561 | | empty: |
| 2562 | | |
| 2563 | | while (size > 0) |
| 2564 | | { |
| 2565 | | m_subcycle++; |
| 2566 | | if ((m_subcycle == 2) && (m_PC == 12)) |
| 2567 | | { |
| 2568 | | m_subcycle = m_subc_reload; |
| 2569 | | m_PC = 0; |
| 2570 | | m_IP++; |
| 2571 | | m_IP &= 0x7; |
| 2572 | | } |
| 2573 | | else if (m_subcycle == 3) |
| 2574 | | { |
| 2575 | | m_subcycle = m_subc_reload; |
| 2576 | | m_PC++; |
| 2577 | | } |
| 2578 | | buffer[buf_count] = -1; // should be just -1; actual chip outputs -1 every idle sample; (cf note in data sheet, p 10, table 4) |
| 2579 | | buf_count++; |
| 2580 | | size--; |
| 2581 | | } |
| 2582 | | } |
| 2583 | | |
| 2584 | | /****************************************************************************** |
| 2585 | | lattice_filter -- executes one 'full run' of the lattice filter on a |
| 2586 | | specific byte of excitation data, and specific values of all the current k |
| 2587 | | constants, and returns the resulting sample. |
| 2588 | | ******************************************************************************/ |
| 2589 | | |
| 2590 | | INT32 tms52xx_device::lattice_filter() |
| 2591 | | { |
| 2592 | | /* Lattice filter here */ |
| 2593 | | /* Aug/05/07: redone as unrolled loop, for clarity - LN |
| 2594 | | * Originally Copied verbatim from table I in US patent 4,209,804, now updated |
| 2595 | | * to be in same order as the actual chip does it, not that it matters. |
| 2596 | | * notation equivalencies from table: |
| 2597 | | * Yn(i) == m_u[n-1] |
| 2598 | | * Kn = m_current_k[n-1] |
| 2599 | | * bn = m_x[n-1] |
| 2600 | | */ |
| 2601 | | |
| 2602 | | m_u[10] = matrix_multiply(m_previous_energy, (m_excitation_data<<6)); //Y(11) |
| 2603 | | m_u[9] = m_u[10] - matrix_multiply(m_current_k[9], m_x[9]); |
| 2604 | | m_u[8] = m_u[9] - matrix_multiply(m_current_k[8], m_x[8]); |
| 2605 | | m_u[7] = m_u[8] - matrix_multiply(m_current_k[7], m_x[7]); |
| 2606 | | m_u[6] = m_u[7] - matrix_multiply(m_current_k[6], m_x[6]); |
| 2607 | | m_u[5] = m_u[6] - matrix_multiply(m_current_k[5], m_x[5]); |
| 2608 | | m_u[4] = m_u[5] - matrix_multiply(m_current_k[4], m_x[4]); |
| 2609 | | m_u[3] = m_u[4] - matrix_multiply(m_current_k[3], m_x[3]); |
| 2610 | | m_u[2] = m_u[3] - matrix_multiply(m_current_k[2], m_x[2]); |
| 2611 | | m_u[1] = m_u[2] - matrix_multiply(m_current_k[1], m_x[1]); |
| 2612 | | m_u[0] = m_u[1] - matrix_multiply(m_current_k[0], m_x[0]); |
| 2613 | | m_x[9] = m_x[8] + matrix_multiply(m_current_k[8], m_u[8]); |
| 2614 | | m_x[8] = m_x[7] + matrix_multiply(m_current_k[7], m_u[7]); |
| 2615 | | m_x[7] = m_x[6] + matrix_multiply(m_current_k[6], m_u[6]); |
| 2616 | | m_x[6] = m_x[5] + matrix_multiply(m_current_k[5], m_u[5]); |
| 2617 | | m_x[5] = m_x[4] + matrix_multiply(m_current_k[4], m_u[4]); |
| 2618 | | m_x[4] = m_x[3] + matrix_multiply(m_current_k[3], m_u[3]); |
| 2619 | | m_x[3] = m_x[2] + matrix_multiply(m_current_k[2], m_u[2]); |
| 2620 | | m_x[2] = m_x[1] + matrix_multiply(m_current_k[1], m_u[1]); |
| 2621 | | m_x[1] = m_x[0] + matrix_multiply(m_current_k[0], m_u[0]); |
| 2622 | | m_x[0] = m_u[0]; |
| 2623 | | m_previous_energy = m_current_energy; |
| 2624 | | #ifdef DEBUG_LATTICE |
| 2625 | | |
| 2626 | | int i; |
| 2627 | | fprintf(stderr,"V:%04d ", m_u[10]); |
| 2628 | | for (i = 9; i >= 0; i--) |
| 2629 | | { |
| 2630 | | fprintf(stderr,"Y%d:%04d ", i+1, m_u[i]); |
| 2631 | | fprintf(stderr,"b%d:%04d ", i+1, m_x[i]); |
| 2632 | | if ((i % 5) == 0) fprintf(stderr,"\n"); |
| 2633 | | } |
| 2634 | | #endif |
| 2635 | | return m_u[0]; |
| 2636 | | } |
| 2637 | | |
| 2638 | | /********************************************************************************************** |
| 2639 | | data_write -- handle a write to the TMS5220 |
| 2640 | | ***********************************************************************************************/ |
| 2641 | | |
| 2642 | | void tms52xx_device::data_write(int data) |
| 2643 | | { |
| 2644 | | #ifdef DEBUG_DUMP_INPUT_DATA |
| 2645 | | fprintf(stdout, "%c",data); |
| 2646 | | #endif |
| 2647 | | if (m_speak_external) // If we're in speak external mode |
| 2648 | | { |
| 2649 | | /* add this byte to the FIFO */ |
| 2650 | | if (m_fifo_count < FIFO_SIZE) |
| 2651 | | { |
| 2652 | | m_fifo[m_fifo_tail] = data; |
| 2653 | | m_fifo_tail = (m_fifo_tail + 1) % FIFO_SIZE; |
| 2654 | | m_fifo_count++; |
| 2655 | | #ifdef DEBUG_FIFO |
| 2656 | | logerror("tms52xx: data_write: Added byte to FIFO (current count=%2d)\n", m_fifo_count); |
| 2657 | | #endif |
| 2658 | | update_status_and_ints(); |
| 2659 | | if ((!m_talk_status) && (!m_buffer_low)) // we just unset buffer low with that last write, and talk status *was* zero... |
| 2660 | | { |
| 2661 | | int i; |
| 2662 | | #ifdef DEBUG_FIFO |
| 2663 | | logerror("tms52xx: data_write triggered talk status to go active!\n"); |
| 2664 | | #endif |
| 2665 | | /* ...then we now have enough bytes to start talking; clear out |
| 2666 | | * the new frame parameters (it will become old frame just before the first call to parse_frame()) |
| 2667 | | * TODO: the 3 lines below (and others) are needed for victory |
| 2668 | | * to not fail its selftest due to a sample ending too late, may require additional investigation */ |
| 2669 | | m_subcycle = m_subc_reload; |
| 2670 | | m_PC = 0; |
| 2671 | | m_IP = reload_table[m_tms5220c_rate & 0x3]; // is this correct? should this be always 7 instead, so that the new frame is loaded quickly? |
| 2672 | | m_new_frame_energy_idx = 0; |
| 2673 | | m_new_frame_pitch_idx = 0; |
| 2674 | | |
| 2675 | | for (i = 0; i < 4; i++) |
| 2676 | | m_new_frame_k_idx[i] = 0; |
| 2677 | | for (i = 4; i < 7; i++) |
| 2678 | | m_new_frame_k_idx[i] = 0xF; |
| 2679 | | for (i = 7; i < m_coeff->num_k; i++) |
| 2680 | | m_new_frame_k_idx[i] = 0x7; |
| 2681 | | m_talk_status = m_speaking_now = true; |
| 2682 | | } |
| 2683 | | } |
| 2684 | | else |
| 2685 | | { |
| 2686 | | #ifdef DEBUG_FIFO |
| 2687 | | logerror("tms52xx: data_write: Ran out of room in the tms52xx FIFO! this should never happen!\n"); |
| 2688 | | // at this point, /READY should remain HIGH/inactive until the fifo has at least one byte open in it. |
| 2689 | | #endif |
| 2690 | | } |
| 2691 | | |
| 2692 | | |
| 2693 | | } |
| 2694 | | else //(! m_speak_external) |
| 2695 | | /* R Nabet : we parse commands at once. It is necessary for such commands as read. */ |
| 2696 | | process_command(data); |
| 2697 | | } |
| 2698 | | |
| 2699 | | /****************************************************************************** |
| 2700 | | process_command -- extract a byte from the FIFO and interpret it as a command |
| 2701 | | *******************************************************************************/ |
| 2702 | | |
| 2703 | | void tms52xx_device::process_command(unsigned char cmd) |
| 2704 | | { |
| 2705 | | #ifdef DEBUG_COMMAND_DUMP |
| 2706 | | fprintf(stderr,"process_command called with parameter %02X\n",cmd); |
| 2707 | | #endif |
| 2708 | | // parse the command |
| 2709 | | switch (cmd & 0x70) |
| 2710 | | { |
| 2711 | | case 0x10 : // read byte |
| 2712 | | if (!m_talk_status) // TALKST must be clear for RDBY |
| 2713 | | { |
| 2714 | | if (m_schedule_dummy_read) |
| 2715 | | { |
| 2716 | | m_schedule_dummy_read = false; |
| 2717 | | (void)m_read_mem(1); |
| 2718 | | } |
| 2719 | | m_data_register = m_read_mem(8); // read one byte from speech ROM... |
| 2720 | | m_RDB_flag = true; |
| 2721 | | } |
| 2722 | | break; |
| 2723 | | |
| 2724 | | case 0x00: |
| 2725 | | case 0x20: // set rate (tms5220c only), otherwise NOP |
| 2726 | | if (m_variant == TMS5220_IS_5220C) |
| 2727 | | { |
| 2728 | | m_tms5220c_rate = cmd & 0x0F; |
| 2729 | | } |
| 2730 | | break; |
| 2731 | | |
| 2732 | | case 0x30: // read and branch |
| 2733 | | if (!m_talk_status) // TALKST must be clear for RB |
| 2734 | | { |
| 2735 | | #ifdef VERBOSE |
| 2736 | | logerror("tms5520: read and branch command received\n"); |
| 2737 | | #endif |
| 2738 | | m_RDB_flag = false; |
| 2739 | | m_read_and_branch(0, 0); |
| 2740 | | } |
| 2741 | | break; |
| 2742 | | |
| 2743 | | case 0x40 : // load address |
| 2744 | | if (!m_talk_status) // TALKST must be clear for LA |
| 2745 | | { |
| 2746 | | // tms5220 data sheet says that if we load only one 4-bit nibble, it won't work. |
| 2747 | | // This code does not care about this. |
| 2748 | | m_load_address(0, cmd & 0x0f); |
| 2749 | | m_schedule_dummy_read = true; |
| 2750 | | } |
| 2751 | | break; |
| 2752 | | |
| 2753 | | case 0x50: // speak |
| 2754 | | if (m_schedule_dummy_read) |
| 2755 | | { |
| 2756 | | m_schedule_dummy_read = false; |
| 2757 | | (void)m_read_mem(1); |
| 2758 | | } |
| 2759 | | m_speaking_now = true; |
| 2760 | | m_speak_external = false; |
| 2761 | | m_talk_status = true; // start immediately |
| 2762 | | // clear out variables before speaking |
| 2763 | | // TODO: similar to the victory case described above, but for VSM speech |
| 2764 | | m_subcycle = m_subc_reload; |
| 2765 | | m_PC = 0; |
| 2766 | | m_IP = reload_table[m_tms5220c_rate & 0x3]; |
| 2767 | | m_new_frame_energy_idx = 0; |
| 2768 | | m_new_frame_pitch_idx = 0; |
| 2769 | | |
| 2770 | | int i; |
| 2771 | | for (i = 0; i < 4; i++) |
| 2772 | | m_new_frame_k_idx[i] = 0; |
| 2773 | | for (i = 4; i < 7; i++) |
| 2774 | | m_new_frame_k_idx[i] = 0xF; |
| 2775 | | for (i = 7; i < m_coeff->num_k; i++) |
| 2776 | | m_new_frame_k_idx[i] = 0x7; |
| 2777 | | break; |
| 2778 | | |
| 2779 | | case 0x60: // speak external |
| 2780 | | //SPKEXT going active activates SPKEE which clears the fifo |
| 2781 | | m_fifo_head = m_fifo_tail = 0; |
| 2782 | | m_fifo_count = m_fifo_bits_taken = 0; |
| 2783 | | m_speak_external = true; |
| 2784 | | m_RDB_flag = false; |
| 2785 | | break; |
| 2786 | | |
| 2787 | | case 0x70: // reset |
| 2788 | | if (m_schedule_dummy_read) |
| 2789 | | { |
| 2790 | | m_schedule_dummy_read = false; |
| 2791 | | (void)m_read_mem(1); |
| 2792 | | } |
| 2793 | | device_reset(); |
| 2794 | | break; |
| 2795 | | } |
| 2796 | | |
| 2797 | | // update the buffer low state |
| 2798 | | update_status_and_ints(); |
| 2799 | | } |
| 2800 | | |
| 2801 | | /****************************************************************************** |
| 2802 | | parse_frame -- parse a new frame's worth of data; returns 0 if not |
| 2803 | | enough bits in buffer |
| 2804 | | *******************************************************************************/ |
| 2805 | | |
| 2806 | | void tms52xx_device::parse_frame() |
| 2807 | | { |
| 2808 | | int indx, i, rep_flag; |
| 2809 | | |
| 2810 | | /* We actually don't care how many bits are left in the fifo here; the |
| 2811 | | * frame subpart will be processed normally, and any bits extracted 'past |
| 2812 | | * the end' of the fifo will be read as zeroes; the fifo being emptied will |
| 2813 | | * set the /BE latch which will halt speech exactly as if a stop frame had |
| 2814 | | * been encountered (instead of whatever partial frame was read); the same |
| 2815 | | * exact circuitry is used for both on the real chip, see us patent 4335277 |
| 2816 | | * sheet 16, gates 232a (decode stop frame) and 232b (decode /BE plus DDIS |
| 2817 | | * (decode disable) which is active during speak external). */ |
| 2818 | | |
| 2819 | | /* if the chip is a tms5220C, and the rate mode is set to that each frame (0x04 bit set) |
| 2820 | | * has a 2 bit rate preceding it, grab two bits here and store them as the rate; */ |
| 2821 | | if ((m_variant == TMS5220_IS_5220C) && (m_tms5220c_rate & 0x04)) |
| 2822 | | { |
| 2823 | | indx = extract_bits(2); |
| 2824 | | #ifdef DEBUG_PARSE_FRAME_DUMP |
| 2825 | | printbits(indx,2); |
| 2826 | | fprintf(stderr," "); |
| 2827 | | #endif |
| 2828 | | m_IP = reload_table[indx]; |
| 2829 | | } |
| 2830 | | else // non-5220C and 5220C in fixed rate mode |
| 2831 | | m_IP = reload_table[m_tms5220c_rate & 0x3]; |
| 2832 | | |
| 2833 | | update_status_and_ints(); |
| 2834 | | if (!m_talk_status) goto ranout; |
| 2835 | | |
| 2836 | | // attempt to extract the energy index |
| 2837 | | m_new_frame_energy_idx = extract_bits(m_coeff->energy_bits); |
| 2838 | | #ifdef DEBUG_PARSE_FRAME_DUMP |
| 2839 | | printbits(m_new_frame_energy_idx,m_coeff->energy_bits); |
| 2840 | | fprintf(stderr," "); |
| 2841 | | #endif |
| 2842 | | update_status_and_ints(); |
| 2843 | | if (!m_talk_status) goto ranout; |
| 2844 | | // if the energy index is 0 or 15, we're done |
| 2845 | | if ((m_new_frame_energy_idx == 0) || (m_new_frame_energy_idx == 15)) |
| 2846 | | return; |
| 2847 | | |
| 2848 | | // attempt to extract the repeat flag |
| 2849 | | rep_flag = extract_bits(1); |
| 2850 | | #ifdef DEBUG_PARSE_FRAME_DUMP |
| 2851 | | printbits(rep_flag, 1); |
| 2852 | | fprintf(stderr," "); |
| 2853 | | #endif |
| 2854 | | |
| 2855 | | // attempt to extract the pitch |
| 2856 | | m_new_frame_pitch_idx = extract_bits(m_coeff->pitch_bits); |
| 2857 | | #ifdef DEBUG_PARSE_FRAME_DUMP |
| 2858 | | printbits(m_new_frame_pitch_idx, m_coeff->pitch_bits); |
| 2859 | | fprintf(stderr," "); |
| 2860 | | #endif |
| 2861 | | update_status_and_ints(); |
| 2862 | | if (!m_talk_status) goto ranout; |
| 2863 | | /* if this is a repeat frame, just do nothing, it will reuse the |
| 2864 | | * old coefficients */ |
| 2865 | | if (rep_flag) return; |
| 2866 | | |
| 2867 | | // extract first 4 K coefficients |
| 2868 | | for (i = 0; i < 4; i++) |
| 2869 | | { |
| 2870 | | m_new_frame_k_idx[i] = extract_bits(m_coeff->kbits[i]); |
| 2871 | | #ifdef DEBUG_PARSE_FRAME_DUMP |
| 2872 | | printbits(m_new_frame_k_idx[i], m_coeff->kbits[i]); |
| 2873 | | fprintf(stderr," "); |
| 2874 | | #endif |
| 2875 | | update_status_and_ints(); |
| 2876 | | if (!m_talk_status) goto ranout; |
| 2877 | | } |
| 2878 | | |
| 2879 | | // if the pitch index was zero, we only need 4 K's... |
| 2880 | | if (m_new_frame_pitch_idx == 0) |
| 2881 | | { |
| 2882 | | // and the rest of the coefficients are zeroed, but that's done in the generator code |
| 2883 | | return; |
| 2884 | | } |
| 2885 | | |
| 2886 | | // If we got here, we need the remaining 6 K's |
| 2887 | | for (i = 4; i < m_coeff->num_k; i++) |
| 2888 | | { |
| 2889 | | m_new_frame_k_idx[i] = extract_bits(m_coeff->kbits[i]); |
| 2890 | | #ifdef DEBUG_PARSE_FRAME_DUMP |
| 2891 | | printbits(m_new_frame_k_idx[i], m_coeff->kbits[i]); |
| 2892 | | fprintf(stderr," "); |
| 2893 | | #endif |
| 2894 | | update_status_and_ints(); |
| 2895 | | if (!m_talk_status) goto ranout; |
| 2896 | | } |
| 2897 | | #ifdef VERBOSE |
| 2898 | | if (m_speak_external) |
| 2899 | | logerror("tms52xx: Parsed a frame successfully in FIFO - %d bits remaining\n", (m_fifo_count*8)-(m_fifo_bits_taken)); |
| 2900 | | else |
| 2901 | | logerror("tms52xx: Parsed a frame successfully in ROM\n"); |
| 2902 | | #endif |
| 2903 | | return; |
| 2904 | | |
| 2905 | | ranout: |
| 2906 | | #ifdef DEBUG_FRAME_ERRORS |
| 2907 | | logerror("tms52xx: Ran out of bits on a parse!\n"); |
| 2908 | | #endif |
| 2909 | | return; |
| 2910 | | } |
| 2911 | | |
| 2912 | | /********************************************************************************************** |
| 2913 | | |
| 2914 | | update_status_and_ints -- check to see if the various flags should be on or off |
| 2915 | | Description of flags, and their position in the status register: |
| 2916 | | From the data sheet: |
| 2917 | | bit D0(bit 7) = TS - Talk Status is active (high) when the VSP is processing speech data. |
| 2918 | | Talk Status goes active at the initiation of a Speak command or after nine |
| 2919 | | bytes of data are loaded into the FIFO following a Speak External command. It |
| 2920 | | goes inactive (low) when the stop code (Energy=1111) is processed, or |
| 2921 | | immediately by a buffer empty condition or a reset command. |
| 2922 | | bit D1(bit 6) = BL - Buffer Low is active (high) when the FIFO buffer is more than half empty. |
| 2923 | | Buffer Low is set when the "Last-In" byte is shifted down past the half-full |
| 2924 | | boundary of the stack. Buffer Low is cleared when data is loaded to the stack |
| 2925 | | so that the "Last-In" byte lies above the half-full boundary and becomes the |
| 2926 | | eighth data byte of the stack. |
| 2927 | | bit D2(bit 5) = BE - Buffer Empty is active (high) when the FIFO buffer has run out of data |
| 2928 | | while executing a Speak External command. Buffer Empty is set when the last bit |
| 2929 | | of the "Last-In" byte is shifted out to the Synthesis Section. This causes |
| 2930 | | Talk Status to be cleared. Speech is terminated at some abnormal point and the |
| 2931 | | Speak External command execution is terminated. |
| 2932 | | |
| 2933 | | ***********************************************************************************************/ |
| 2934 | | |
| 2935 | | void tms52xx_device::update_status_and_ints() |
| 2936 | | { |
| 2937 | | // update flags and set ints if needed |
| 2938 | | update_ready_state(); |
| 2939 | | |
| 2940 | | /* BL is set if neither byte 9 nor 8 of the fifo are in use; this |
| 2941 | | * translates to having fifo_count (which ranges from 0 bytes in use to 16 |
| 2942 | | * bytes used) being less than or equal to 8. Victory/Victorba depends on this. */ |
| 2943 | | if (m_fifo_count <= 8) |
| 2944 | | { |
| 2945 | | // generate an interrupt if necessary; if /BL was inactive and is now active, set int. |
| 2946 | | if (!m_buffer_low) set_interrupt_state(1); |
| 2947 | | m_buffer_low = true; |
| 2948 | | } |
| 2949 | | else |
| 2950 | | m_buffer_low = false; |
| 2951 | | |
| 2952 | | /* BE is set if neither byte 15 nor 14 of the fifo are in use; this |
| 2953 | | * translates to having fifo_count equal to exactly 0 */ |
| 2954 | | if (m_fifo_count == 0) |
| 2955 | | { |
| 2956 | | // generate an interrupt if necessary; if /BE was inactive and is now active, set int. |
| 2957 | | if (!m_buffer_empty) set_interrupt_state(1); |
| 2958 | | m_buffer_empty = true; |
| 2959 | | } |
| 2960 | | else |
| 2961 | | m_buffer_empty = false; |
| 2962 | | |
| 2963 | | /* TS is talk status and is set elsewhere in the fifo parser and in |
| 2964 | | * the SPEAK command handler; however, if /BE is true during speak external |
| 2965 | | * mode, it is immediately unset here. */ |
| 2966 | | if (m_speak_external && m_buffer_empty) |
| 2967 | | { |
| 2968 | | // generate an interrupt: /TS was active, and is now inactive. |
| 2969 | | if (m_talk_status) |
| 2970 | | { |
| 2971 | | m_talk_status = m_speak_external = false; |
| 2972 | | set_interrupt_state(1); |
| 2973 | | } |
| 2974 | | } |
| 2975 | | /* Note that TS being unset will also generate an interrupt when a STOP |
| 2976 | | * frame is encountered; this is handled in the sample generator code and not here */ |
| 2977 | | } |
| 2978 | | |
| 2979 | | /****************************************************************************** |
| 2980 | | extract_bits -- extract a specific number of bits from the current input stream (FIFO or VSM) |
| 2981 | | *******************************************************************************/ |
| 2982 | | |
| 2983 | | int tms52xx_device::extract_bits(int count) |
| 2984 | | { |
| 2985 | | int val = 0; |
| 2986 | | |
| 2987 | | if (m_speak_external) |
| 2988 | | { |
| 2989 | | // extract from FIFO |
| 2990 | | while (count--) |
| 2991 | | { |
| 2992 | | val = (val << 1) | ((m_fifo[m_fifo_head] >> m_fifo_bits_taken) & 1); |
| 2993 | | m_fifo_bits_taken++; |
| 2994 | | if (m_fifo_bits_taken >= 8) |
| 2995 | | { |
| 2996 | | m_fifo_count--; |
| 2997 | | m_fifo[m_fifo_head] = 0; // zero the newly depleted fifo head byte |
| 2998 | | m_fifo_head = (m_fifo_head + 1) % FIFO_SIZE; |
| 2999 | | m_fifo_bits_taken = 0; |
| 3000 | | update_status_and_ints(); |
| 3001 | | } |
| 3002 | | } |
| 3003 | | } |
| 3004 | | else |
| 3005 | | { |
| 3006 | | // extract from VSM (speech ROM) |
| 3007 | | val = m_read_mem(count); |
| 3008 | | } |
| 3009 | | return val; |
| 3010 | | } |
| 3011 | | |
| 3012 | | /****************************************************************************** |
| 3013 | | status_read -- read status or data from the TMS5220 |
| 3014 | | *******************************************************************************/ |
| 3015 | | |
| 3016 | | int tms52xx_device::status_read() |
| 3017 | | { |
| 3018 | | if (m_RDB_flag) |
| 3019 | | { // if last command was read, return data register |
| 3020 | | m_RDB_flag = false; |
| 3021 | | return m_data_register; |
| 3022 | | } |
| 3023 | | else |
| 3024 | | { // read status |
| 3025 | | // clear the interrupt pin on status read |
| 3026 | | set_interrupt_state(0); |
| 3027 | | #ifdef DEBUG_PIN_READS |
| 3028 | | logerror("tms52xx: Status read: TS=%d BL=%d BE=%d\n", m_talk_status, m_buffer_low, m_buffer_empty); |
| 3029 | | #endif |
| 3030 | | |
| 3031 | | int retvalue = 0; |
| 3032 | | if (m_talk_status) retvalue |= 0x80; |
| 3033 | | if (m_buffer_low) retvalue |= 0x40; |
| 3034 | | if (m_buffer_empty) retvalue |= 0x20; |
| 3035 | | return retvalue; |
| 3036 | | } |
| 3037 | | } |
| 3038 | | |
| 3039 | | /****************************************************************************** |
| 3040 | | ready_read -- returns the ready state of the TMS5220 |
| 3041 | | *******************************************************************************/ |
| 3042 | | |
| 3043 | | inline bool tms52xx_device::ready_read() |
| 3044 | | { |
| 3045 | | #ifdef DEBUG_PIN_READS |
| 3046 | | logerror("tms52xx: ready_read: ready pin read, io_ready is %d, fifo count is %d\n", m_io_ready, m_fifo_count); |
| 3047 | | #endif |
| 3048 | | return ((m_fifo_count < FIFO_SIZE)||(!m_speak_external)) && m_io_ready; |
| 3049 | | } |
| 3050 | | |
| 3051 | | /****************************************************************************** |
| 3052 | | int_read -- returns the interrupt state of the TMS5220 |
| 3053 | | *******************************************************************************/ |
| 3054 | | |
| 3055 | | inline int tms52xx_device::int_read() |
| 3056 | | { |
| 3057 | | #ifdef DEBUG_PIN_READS |
| 3058 | | logerror("tms52xx: int_read: irq pin read, state is %d\n", m_irq_pin); |
| 3059 | | #endif |
| 3060 | | return m_irq_pin; |
| 3061 | | } |
| 3062 | | |
| 3063 | | /****************************************************************************** |
| 3064 | | cycles_to_ready -- returns the number of cycles until ready is asserted |
| 3065 | | NOTE: this function is deprecated and is known to be VERY inaccurate. |
| 3066 | | Use at your own peril! |
| 3067 | | *******************************************************************************/ |
| 3068 | | |
| 3069 | | int tms52xx_device::cycles_to_ready() |
| 3070 | | { |
| 3071 | | int answer; |
| 3072 | | |
| 3073 | | if (ready_read()) answer = 0; |
| 3074 | | else |
| 3075 | | { |
| 3076 | | int val; |
| 3077 | | int samples_per_frame = (m_subc_reload!=0)?200:304; // either (13 A cycles + 12 B cycles) * 8 interps for normal SPEAK/SPKEXT, or (13*2 A cycles + 12 B cycles) * 8 interps for SPKSLOW |
| 3078 | | int current_sample = ((m_PC * (3-m_subc_reload)) + (((m_subc_reload!=0)? 38:25) * m_IP)); |
| 3079 | | answer = samples_per_frame - current_sample + 8; |
| 3080 | | |
| 3081 | | // total number of bits available in current byte is (8 - tms->fifo_bits_taken) |
| 3082 | | // if more than 4 are available, we need to check the energy |
| 3083 | | if (m_fifo_bits_taken < 4) |
| 3084 | | { |
| 3085 | | // read energy |
| 3086 | | val = (m_fifo[m_fifo_head] >> m_fifo_bits_taken) & 0xf; |
| 3087 | | if (val == 0) |
| 3088 | | // 0 -> silence frame: we will only read 4 bits, and we will |
| 3089 | | // therefore need to read another frame before the FIFO is not |
| 3090 | | // full any more |
| 3091 | | answer += (m_subc_reload!=0)?200:304; |
| 3092 | | // 15 -> stop frame, we will only read 4 bits, but the FIFO will |
| 3093 | | // we cleared |
| 3094 | | //otherwise, we need to parse the repeat flag (1 bit) and the |
| 3095 | | // pitch (6 bits), so everything will be OK. |
| 3096 | | } |
| 3097 | | } |
| 3098 | | return answer; |
| 3099 | | } |
| 3100 | | |
| 3101 | | /****************************************************************************** |
| 3102 | | True timing |
| 3103 | | *******************************************************************************/ |
| 3104 | | |
| 3105 | | void tms52xx_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) |
| 3106 | | { |
| 3107 | | if (param) |
| 3108 | | { |
| 3109 | | switch (m_rs_ws) |
| 3110 | | { |
| 3111 | | case 0x02: |
| 3112 | | // Write |
| 3113 | | // bring up to date first |
| 3114 | | #ifdef DEBUG_IO_READY |
| 3115 | | logerror("tms52xx: Serviced write: %02x\n", tms->write_latch); |
| 3116 | | //fprintf(stderr, "Processed write data: %02X\n", tms->write_latch); |
| 3117 | | #endif |
| 3118 | | m_stream->update(); |
| 3119 | | data_write(m_write_latch); |
| 3120 | | break; |
| 3121 | | case 0x01: |
| 3122 | | // Read |
| 3123 | | // bring up to date first |
| 3124 | | m_stream->update(); |
| 3125 | | m_read_latch = status_read(); |
| 3126 | | break; |
| 3127 | | case 0x03: |
| 3128 | | // High Impedance |
| 3129 | | case 0x00: |
| 3130 | | // illegal |
| 3131 | | break; |
| 3132 | | } |
| 3133 | | } |
| 3134 | | m_io_ready = param; |
| 3135 | | update_ready_state(); |
| 3136 | | } |
| 3137 | | |
| 3138 | | /***************************************************** |
| 3139 | | /RS line write handler |
| 3140 | | *****************************************************/ |
| 3141 | | |
| 3142 | | WRITE_LINE_MEMBER( tms52xx_device::rsq_w ) |
| 3143 | | { |
| 3144 | | UINT8 new_val; |
| 3145 | | |
| 3146 | | m_true_timing = true; |
| 3147 | | state &= 0x01; |
| 3148 | | #ifdef DEBUG_RS_WS |
| 3149 | | logerror("tms52xx: /RS written with data: %d\n", state); |
| 3150 | | #endif |
| 3151 | | new_val = (m_rs_ws & 0x01) | (state<<1); |
| 3152 | | if (new_val != m_rs_ws) |
| 3153 | | { |
| 3154 | | m_rs_ws = new_val; |
| 3155 | | if (new_val == 0) |
| 3156 | | { |
| 3157 | | if (m_variant == TMS5220_IS_5220C) |
| 3158 | | reset(); |
| 3159 | | #ifdef DEBUG_RS_WS |
| 3160 | | else |
| 3161 | | // illegal |
| 3162 | | logerror("tms52xx: illegal line setting /RS=0 and /WS=0\n"); |
| 3163 | | #endif |
| 3164 | | return; |
| 3165 | | } |
| 3166 | | else if (new_val == 3) |
| 3167 | | { |
| 3168 | | // high impedance |
| 3169 | | m_read_latch = 0xff; |
| 3170 | | return; |
| 3171 | | } |
| 3172 | | if (state) |
| 3173 | | { |
| 3174 | | // low to high |
| 3175 | | } |
| 3176 | | else |
| 3177 | | { |
| 3178 | | // high to low - schedule ready cycle |
| 3179 | | #ifdef DEBUG_RS_WS |
| 3180 | | logerror("tms52xx: Scheduling ready cycle for /RS...\n"); |
| 3181 | | #endif |
| 3182 | | /* upon /RS being activated, /READY goes inactive after 100 nsec from |
| 3183 | | * data sheet, through 3 asynchronous gates on patent. This is effectively |
| 3184 | | * within one clock, so we immediately set io_ready to 0 and activate the callback. */ |
| 3185 | | m_io_ready = 0; |
| 3186 | | update_ready_state(); |
| 3187 | | /* How long does /READY stay inactive, when /RS is pulled low? |
| 3188 | | * I believe its almost always ~16 clocks (25 usec at 800khz as shown on the datasheet) */ |
| 3189 | | m_ready_timer->adjust(attotime::from_hz(clock()/16)); |
| 3190 | | } |
| 3191 | | } |
| 3192 | | } |
| 3193 | | |
| 3194 | | /***************************************************** |
| 3195 | | /WS line write handler |
| 3196 | | *****************************************************/ |
| 3197 | | |
| 3198 | | WRITE_LINE_MEMBER( tms52xx_device::wsq_w ) |
| 3199 | | { |
| 3200 | | UINT8 new_val; |
| 3201 | | |
| 3202 | | m_true_timing = true; |
| 3203 | | state &= 0x01; |
| 3204 | | #ifdef DEBUG_RS_WS |
| 3205 | | logerror("tms52xx: /WS written with data: %d\n", state); |
| 3206 | | #endif |
| 3207 | | new_val = (m_rs_ws & 0x02) | (state<<0); |
| 3208 | | if (new_val != m_rs_ws) |
| 3209 | | { |
| 3210 | | m_rs_ws = new_val; |
| 3211 | | if (new_val == 0) |
| 3212 | | { |
| 3213 | | if (m_variant == TMS5220_IS_5220C) |
| 3214 | | reset(); |
| 3215 | | #ifdef DEBUG_RS_WS |
| 3216 | | else |
| 3217 | | // illegal |
| 3218 | | logerror("tms52xx: illegal line setting /RS=0 and /WS=0\n"); |
| 3219 | | #endif |
| 3220 | | return; |
| 3221 | | } |
| 3222 | | else if ( new_val == 3) |
| 3223 | | { |
| 3224 | | // high impedance |
| 3225 | | m_read_latch = 0xff; |
| 3226 | | return; |
| 3227 | | } |
| 3228 | | if (state) |
| 3229 | | { |
| 3230 | | // low to high |
| 3231 | | } |
| 3232 | | else |
| 3233 | | { |
| 3234 | | // high to low - schedule ready cycle |
| 3235 | | #ifdef DEBUG_RS_WS |
| 3236 | | logerror("tms52xx: Scheduling ready cycle for /WS...\n"); |
| 3237 | | #endif |
| 3238 | | /* upon /WS being activated, /READY goes inactive after 100 nsec |
| 3239 | | * from data sheet, through 3 asynchronous gates on patent. |
| 3240 | | * This is effectively within one clock, so we immediately set |
| 3241 | | * io_ready to 0 and activate the callback. */ |
| 3242 | | m_io_ready = 0; |
| 3243 | | update_ready_state(); |
| 3244 | | /* Now comes the complicated part: long does /READY stay inactive |
| 3245 | | * when /WS is pulled low? This depends ENTIRELY on the command written, |
| 3246 | | * or whether the chip is in speak external mode or not... |
| 3247 | | * Speak external mode: ~16 cycles |
| 3248 | | * Command Mode: |
| 3249 | | * SPK: ? cycles |
| 3250 | | * SPKEXT: ? cycles |
| 3251 | | * RDBY: between 60 and 140 cycles |
| 3252 | | * RB: ? cycles (80?) |
| 3253 | | * RST: between 60 and 140 cycles |
| 3254 | | * SET RATE (5220C only): ? cycles (probably ~16) */ |
| 3255 | | |
| 3256 | | // TODO: actually HANDLE the timing differences! currently just assuming always 16 cycles |
| 3257 | | m_ready_timer->adjust(attotime::from_hz(clock()/16)); |
| 3258 | | } |
| 3259 | | } |
| 3260 | | } |
| 3261 | | |
| 3262 | | /***************************************************************************** |
| 3263 | | write -- write data to the sound chip |
| 3264 | | *******************************************************************************/ |
| 3265 | | |
| 3266 | | WRITE8_MEMBER( tms52xx_device::write ) |
| 3267 | | { |
| 3268 | | #ifdef DEBUG_RS_WS |
| 3269 | | logerror("tms52xx: write data %02x\n", data); |
| 3270 | | #endif |
| 3271 | | if (!m_true_timing) |
| 3272 | | { |
| 3273 | | // bring up to date first |
| 3274 | | m_stream->update(); |
| 3275 | | data_write(data); |
| 3276 | | } |
| 3277 | | else |
| 3278 | | { |
| 3279 | | // actually in a write? |
| 3280 | | #ifdef DEBUG_RS_WS |
| 3281 | | if (!(m_rs_ws == 0x02)) |
| 3282 | | logerror("tms52xx: write data written outside ws, status: %02x!\n", m_rs_ws); |
| 3283 | | #endif |
| 3284 | | m_write_latch = data; |
| 3285 | | } |
| 3286 | | } |
| 3287 | | |
| 3288 | | /****************************************************************************** |
| 3289 | | read -- read status or data from the sound chip |
| 3290 | | *******************************************************************************/ |
| 3291 | | |
| 3292 | | READ8_MEMBER( tms52xx_device::read ) |
| 3293 | | { |
| 3294 | | if (!m_true_timing) |
| 3295 | | { |
| 3296 | | // bring up-to-date first |
| 3297 | | m_stream->update(); |
| 3298 | | return status_read(); |
| 3299 | | } |
| 3300 | | else |
| 3301 | | { |
| 3302 | | // actually in a read? |
| 3303 | | if (m_rs_ws == 0x01) |
| 3304 | | return m_read_latch; |
| 3305 | | #ifdef DEBUG_RS_WS |
| 3306 | | else |
| 3307 | | logerror("tms52xx: data read outside rs!\n"); |
| 3308 | | #endif |
| 3309 | | return 0xff; |
| 3310 | | } |
| 3311 | | } |
| 3312 | | |
| 3313 | | /******************************************************************************* |
| 3314 | | ready -- return the not ready status from the sound chip |
| 3315 | | *******************************************************************************/ |
| 3316 | | |
| 3317 | | READ_LINE_MEMBER( tms52xx_device::readyq ) |
| 3318 | | { |
| 3319 | | // bring up-to-date first |
| 3320 | | m_stream->update(); |
| 3321 | | return !ready_read(); |
| 3322 | | } |
| 3323 | | |
| 3324 | | |
| 3325 | | /****************************************************************************** |
| 3326 | | time_to_ready -- return the time in seconds until the |
| 3327 | | ready line is asserted |
| 3328 | | *******************************************************************************/ |
| 3329 | | |
| 3330 | | double tms52xx_device::time_to_ready() |
| 3331 | | { |
| 3332 | | double cycles; |
| 3333 | | |
| 3334 | | // bring up-to-date |
| 3335 | | m_stream->update(); |
| 3336 | | cycles = cycles_to_ready(); |
| 3337 | | return cycles * 80.0 / m_clock; |
| 3338 | | } |
| 3339 | | |
| 3340 | | /****************************************************************************** |
| 3341 | | intq -- return the interrupt status from the sound chip |
| 3342 | | ******************************************************************************/ |
| 3343 | | |
| 3344 | | READ_LINE_MEMBER( tms52xx_device::intq ) |
| 3345 | | { |
| 3346 | | // bring up-to-date first |
| 3347 | | m_stream->update(); |
| 3348 | | return !int_read(); |
| 3349 | | } |
| 3350 | | |
| 3351 | | void tms52xx_device::register_for_save_states() |
| 3352 | | { |
| 3353 | | save_item(NAME(m_fifo)); |
| 3354 | | save_item(NAME(m_fifo_head)); |
| 3355 | | save_item(NAME(m_fifo_tail)); |
| 3356 | | save_item(NAME(m_fifo_count)); |
| 3357 | | save_item(NAME(m_fifo_bits_taken)); |
| 3358 | | |
| 3359 | | save_item(NAME(m_speaking_now)); |
| 3360 | | save_item(NAME(m_speak_external)); |
| 3361 | | save_item(NAME(m_talk_status)); |
| 3362 | | save_item(NAME(m_buffer_low)); |
| 3363 | | save_item(NAME(m_buffer_empty)); |
| 3364 | | save_item(NAME(m_irq_pin)); |
| 3365 | | save_item(NAME(m_ready_pin)); |
| 3366 | | |
| 3367 | | save_item(NAME(m_OLDE)); |
| 3368 | | save_item(NAME(m_OLDP)); |
| 3369 | | |
| 3370 | | save_item(NAME(m_new_frame_energy_idx)); |
| 3371 | | save_item(NAME(m_new_frame_pitch_idx)); |
| 3372 | | save_item(NAME(m_new_frame_k_idx)); |
| 3373 | | #ifdef PERFECT_INTERPOLATION_HACK |
| 3374 | | save_item(NAME(m_old_frame_energy_idx)); |
| 3375 | | save_item(NAME(m_old_frame_pitch_idx)); |
| 3376 | | save_item(NAME(m_old_frame_k_idx)); |
| 3377 | | #endif |
| 3378 | | save_item(NAME(m_current_energy)); |
| 3379 | | save_item(NAME(m_current_pitch)); |
| 3380 | | save_item(NAME(m_current_k)); |
| 3381 | | |
| 3382 | | save_item(NAME(m_target_energy)); |
| 3383 | | save_item(NAME(m_target_pitch)); |
| 3384 | | save_item(NAME(m_target_k)); |
| 3385 | | |
| 3386 | | save_item(NAME(m_previous_energy)); |
| 3387 | | |
| 3388 | | save_item(NAME(m_subcycle)); |
| 3389 | | save_item(NAME(m_subc_reload)); |
| 3390 | | save_item(NAME(m_PC)); |
| 3391 | | save_item(NAME(m_IP)); |
| 3392 | | save_item(NAME(m_inhibit)); |
| 3393 | | save_item(NAME(m_tms5220c_rate)); |
| 3394 | | save_item(NAME(m_pitch_count)); |
| 3395 | | |
| 3396 | | save_item(NAME(m_u)); |
| 3397 | | save_item(NAME(m_x)); |
| 3398 | | |
| 3399 | | save_item(NAME(m_RNG)); |
| 3400 | | save_item(NAME(m_excitation_data)); |
| 3401 | | |
| 3402 | | save_item(NAME(m_schedule_dummy_read)); |
| 3403 | | save_item(NAME(m_data_register)); |
| 3404 | | save_item(NAME(m_RDB_flag)); |
| 3405 | | save_item(NAME(m_digital_select)); |
| 3406 | | |
| 3407 | | save_item(NAME(m_io_ready)); |
| 3408 | | } |
| 3409 | | |
| 3410 | | |
| 3411 | | const device_type TMS5220N = &device_creator<tms5220n_device>; |
| 3412 | | const device_type TMS5220CN = &device_creator<tms5220cn_device>; |
| 3413 | | const device_type TMC0285N = &device_creator<tmc0285n_device>; |
| 3414 | | const device_type TMS5200N = &device_creator<tms5200n_device>; |