src/mess/machine/ti99/spchsyn.c
| r15495 | r15496 | |
| 28 | 28 | |
| 29 | 29 | #define SPEECHROM_TAG "speechrom" |
| 30 | 30 | |
| 31 | #define REAL_TIMING 0 |
| 32 | |
| 31 | 33 | /****************************************************************************/ |
| 32 | 34 | |
| 33 | 35 | ti_speech_synthesizer_device::ti_speech_synthesizer_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) |
| r15495 | r15496 | |
| 37 | 39 | } |
| 38 | 40 | |
| 39 | 41 | /* |
| 42 | Comments on real timing in the TMS5200 emulation |
| 43 | |
| 44 | Real timing means that the synthesizer clears the /READY line (puts high) |
| 45 | whenever a read or write access is in progress. This is done by setting |
| 46 | /RS and /WS lines, according to the read or write operation. The /READY line |
| 47 | is asserted again after some time. Real timing is used once the tms5220_wsq_w |
| 48 | and tms5220_rsq_w are called. |
| 49 | |
| 50 | Within the TI systems, the /RS and /WS lines are controlled directly by the |
| 51 | address bus. There is currently no way to insert wait states between |
| 52 | the address setting and the data bus sampling, since this is an atomic |
| 53 | operation in the emulator (read_byte). It would be necessary to somehow |
| 54 | announce the pending read before it actually happens so that devices |
| 55 | like this VSP may insert wait states before the read. |
| 56 | |
| 57 | The TMS5220 implementation assumes that wait states are respected and |
| 58 | therefore delivers bad values when queried too early. It uses a latch that |
| 59 | gets the new value after some time has expired. |
| 60 | |
| 61 | To fix this we have to modify the RS/WS methods in TMS5200 to immediately |
| 62 | set the status (after updating the sound status, or the status will be |
| 63 | outdated too early). |
| 64 | |
| 65 | Also note that the /RS and /WS lines must be cleared (put to high) when the |
| 66 | read is done. Again, this is not possible with the current implementation. |
| 67 | So we do this in the ready callback. |
| 68 | |
| 69 | On the bottom line we will stay with the not-REAL_TIMING for now and wait |
| 70 | for the core to allow for split read accesses. |
| 71 | */ |
| 72 | |
| 73 | |
| 74 | /* |
| 40 | 75 | Memory read |
| 41 | 76 | */ |
| 77 | #if REAL_TIMING |
| 78 | // ====== This is the version with real timing ======= |
| 42 | 79 | READ8Z_MEMBER( ti_speech_synthesizer_device::readz ) |
| 43 | 80 | { |
| 44 | 81 | if ((offset & m_select_mask)==m_select_value) |
| 45 | 82 | { |
| 83 | tms5220_wsq_w(m_vsp, TRUE); |
| 84 | tms5220_rsq_w(m_vsp, FALSE); |
| 85 | *value = tms5220_status_r(m_vsp, offset) & 0xff; |
| 86 | if (VERBOSE>4) LOG("spchsyn: read value = %02x\n", *value); |
| 87 | } |
| 88 | } |
| 89 | |
| 90 | /* |
| 91 | Memory write |
| 92 | */ |
| 93 | WRITE8_MEMBER( ti_speech_synthesizer_device::write ) |
| 94 | { |
| 95 | if ((offset & m_select_mask)==(m_select_value | 0x0400)) |
| 96 | { |
| 97 | tms5220_rsq_w(m_vsp, TRUE); |
| 98 | tms5220_wsq_w(m_vsp, FALSE); |
| 99 | if (VERBOSE>4) LOG("spchsyn: write value = %02x\n", data); |
| 100 | tms5220_data_w(m_vsp, offset, data); |
| 101 | } |
| 102 | } |
| 103 | |
| 104 | #else |
| 105 | // ====== This is the version without real timing ======= |
| 106 | |
| 107 | READ8Z_MEMBER( ti_speech_synthesizer_device::readz ) |
| 108 | { |
| 109 | if ((offset & m_select_mask)==m_select_value) |
| 110 | { |
| 46 | 111 | device_adjust_icount(machine().device("maincpu"),-(18+3)); /* this is just a minimum, it can be more */ |
| 47 | 112 | *value = tms5220_status_r(m_vsp, offset) & 0xff; |
| 113 | if (VERBOSE>4) LOG("spchsyn: read value = %02x\n", *value); |
| 48 | 114 | } |
| 49 | 115 | } |
| 50 | 116 | |
| r15495 | r15496 | |
| 70 | 136 | device_adjust_icount(machine().device("maincpu"),-cycles_to_ready); |
| 71 | 137 | machine().scheduler().timer_set(attotime::zero, FUNC_NULL); |
| 72 | 138 | } |
| 139 | if (VERBOSE>4) LOG("spchsyn: write value = %02x\n", data); |
| 73 | 140 | tms5220_data_w(m_vsp, offset, data); |
| 74 | 141 | } |
| 75 | 142 | } |
| 143 | #endif |
| 76 | 144 | |
| 77 | 145 | /**************************************************************************** |
| 78 | 146 | Callbacks from TMS5220 |
| r15495 | r15496 | |
| 175 | 243 | |
| 176 | 244 | WRITE_LINE_MEMBER( ti_speech_synthesizer_device::speech_ready ) |
| 177 | 245 | { |
| 178 | | // TODO: Fix this in TMS5220 |
| 246 | // The TMS5200 implementation uses TRUE/FALSE, not ASSERT/CLEAR semantics |
| 247 | // and we have to adapt a /READY to a READY line. |
| 248 | // The real synthesizer board uses a transistor for that purpose. |
| 179 | 249 | m_slot->set_ready((state==0)? ASSERT_LINE : CLEAR_LINE); |
| 250 | if (VERBOSE>5) LOG("spchsyn: READY = %d\n", (state==0)); |
| 251 | |
| 252 | #if REAL_TIMING |
| 253 | // Need to do that here (see explanations above) |
| 254 | if (state==0) |
| 255 | { |
| 256 | tms5220_rsq_w(m_vsp, TRUE); |
| 257 | tms5220_wsq_w(m_vsp, TRUE); |
| 258 | } |
| 259 | #endif |
| 180 | 260 | } |
| 181 | 261 | |
| 182 | 262 | void ti_speech_synthesizer_device::device_start() |
| r15495 | r15496 | |
| 210 | 290 | |
| 211 | 291 | MACHINE_CONFIG_FRAGMENT( ti99_speech ) |
| 212 | 292 | MCFG_SPEAKER_STANDARD_MONO("mono") |
| 213 | | MCFG_SOUND_ADD("speechsyn", TMC0285, 680000L) |
| 293 | MCFG_SOUND_ADD("speechsyn", TMC0285, 640000L) |
| 214 | 294 | MCFG_SOUND_CONFIG(ti99_4x_tms5200interface) |
| 215 | 295 | MCFG_SOUND_ROUTE(ALL_OUTPUTS, "mono", 0.50) |
| 216 | 296 | MACHINE_CONFIG_END |
src/mess/drivers/ti99_4x.c
| r15495 | r15496 | |
| 56 | 56 | #include "machine/ti99/peribox.h" |
| 57 | 57 | |
| 58 | 58 | #define LOG logerror |
| 59 | | #define VERBOSE |
| 59 | #define VERBOSE 1 |
| 60 | 60 | |
| 61 | 61 | /* |
| 62 | 62 | The console. |
| r15495 | r15496 | |
| 96 | 96 | ti99_handset_device* m_handset; |
| 97 | 97 | ti99_datamux_device* m_datamux; |
| 98 | 98 | |
| 99 | | int m_ready_line, m_ready_line1; |
| 99 | int m_ready_line, m_ready_line_dmux; |
| 100 | 100 | |
| 101 | 101 | // Connections with the system interface TMS9901 |
| 102 | 102 | DECLARE_READ8_MEMBER(read_by_9901); |
| r15495 | r15496 | |
| 115 | 115 | void set_key(int number, int data); |
| 116 | 116 | int m_keyboard_column; |
| 117 | 117 | int m_alphalock_line; |
| 118 | int m_ready_prev; // for debugging purposes only |
| 118 | 119 | }; |
| 119 | 120 | |
| 120 | 121 | /* |
| r15495 | r15496 | |
| 379 | 380 | |
| 380 | 381 | WRITE8_MEMBER( ti99_4x::cruwrite ) |
| 381 | 382 | { |
| 382 | | if (VERBOSE>6) LOG("write access to CRU address %04x\n", offset << 1); |
| 383 | if (VERBOSE>6) LOG("ti99_4x: write access to CRU address %04x\n", offset << 1); |
| 383 | 384 | m_gromport->cruwrite(offset<<1, data); |
| 384 | 385 | m_peribox->cruwrite(offset<<1, data); |
| 385 | 386 | } |
| r15495 | r15496 | |
| 387 | 388 | WRITE8_MEMBER( ti99_4x::external_operation ) |
| 388 | 389 | { |
| 389 | 390 | static const char* extop[8] = { "inv1", "inv2", "IDLE", "RSET", "inv3", "CKON", "CKOF", "LREX" }; |
| 390 | | if (VERBOSE>1) LOG("External operation %s not implemented on TI-99 board\n", extop[offset]); |
| 391 | // Some games (e.g. Slymoids) actually use IDLE for synchronization |
| 392 | if (offset == IDLE_OP) return; |
| 393 | else |
| 394 | { |
| 395 | if (VERBOSE>1) LOG("ti99_4x: External operation %s not implemented on TI-99 board\n", extop[offset]); |
| 396 | } |
| 391 | 397 | } |
| 392 | 398 | |
| 393 | 399 | /*************************************************************************** |
| r15495 | r15496 | |
| 626 | 632 | */ |
| 627 | 633 | WRITE_LINE_MEMBER( ti99_4x::set_tms9901_INT2 ) |
| 628 | 634 | { |
| 629 | | if (VERBOSE>6) LOG("ti99_4x: VDP int 2 on tms9901, level=%02x\n", state); |
| 635 | if (VERBOSE>6) LOG("ti99_4x: VDP int 2 on tms9901, level=%d\n", state); |
| 630 | 636 | m_tms9901->set_single_int(2, state); |
| 631 | 637 | } |
| 632 | 638 | |
| r15495 | r15496 | |
| 655 | 661 | */ |
| 656 | 662 | WRITE_LINE_MEMBER( ti99_4x::console_ready ) |
| 657 | 663 | { |
| 658 | | if (VERBOSE>6) LOG("ti99_4x: READY level = %02x\n", state); |
| 659 | 664 | m_ready_line = state; |
| 665 | int combined = (m_ready_line == ASSERT_LINE && m_ready_line_dmux == ASSERT_LINE)? ASSERT_LINE : CLEAR_LINE; |
| 660 | 666 | |
| 661 | | m_cpu->set_ready((m_ready_line == ASSERT_LINE && m_ready_line1 == ASSERT_LINE)? ASSERT_LINE : CLEAR_LINE); |
| 667 | if (VERBOSE>6) |
| 668 | { |
| 669 | if (m_ready_prev != combined) LOG("ti99_4x: READY level = %d\n", combined); |
| 670 | } |
| 671 | m_ready_prev = combined; |
| 672 | m_cpu->set_ready(combined); |
| 662 | 673 | } |
| 663 | 674 | |
| 664 | 675 | /* |
| r15495 | r15496 | |
| 668 | 679 | */ |
| 669 | 680 | WRITE_LINE_MEMBER( ti99_4x::console_ready_dmux ) |
| 670 | 681 | { |
| 671 | | if (VERBOSE>6) LOG("ti99_4x: READY dmux level = %02x\n", state); |
| 672 | | m_ready_line1 = state; |
| 673 | | m_cpu->set_ready((m_ready_line == ASSERT_LINE && m_ready_line1 == ASSERT_LINE)? ASSERT_LINE : CLEAR_LINE); |
| 682 | m_ready_line_dmux = state; |
| 683 | int combined = (m_ready_line == ASSERT_LINE && m_ready_line_dmux == ASSERT_LINE)? ASSERT_LINE : CLEAR_LINE; |
| 684 | |
| 685 | if (VERBOSE>7) |
| 686 | { |
| 687 | if (m_ready_prev != combined) LOG("ti99_4x: READY dmux level = %d\n", state); |
| 688 | } |
| 689 | m_ready_prev = combined; |
| 690 | m_cpu->set_ready(combined); |
| 674 | 691 | } |
| 675 | 692 | |
| 676 | 693 | WRITE_LINE_MEMBER( ti99_4x::extint ) |
| r15495 | r15496 | |
| 837 | 854 | driver->m_peribox->senila(CLEAR_LINE); |
| 838 | 855 | driver->m_peribox->senilb(CLEAR_LINE); |
| 839 | 856 | |
| 840 | | driver->m_ready_line = driver->m_ready_line1 = ASSERT_LINE; |
| 857 | driver->m_ready_line = driver->m_ready_line_dmux = ASSERT_LINE; |
| 841 | 858 | } |
| 842 | 859 | |
| 843 | 860 | MACHINE_RESET( ti99_4 ) |
| r15495 | r15496 | |
| 867 | 884 | MCFG_MACHINE_START( ti99_4 ) |
| 868 | 885 | MCFG_MACHINE_RESET( ti99_4 ) |
| 869 | 886 | |
| 870 | | MCFG_TI_TMS991x_ADD_NTSC(VIDEO_SYSTEM_TAG, TMS9118, ti99_4_tms9928a_interface) |
| 887 | MCFG_TI_TMS991x_ADD_NTSC(VIDEO_SYSTEM_TAG, TMS9918, ti99_4_tms9928a_interface) |
| 871 | 888 | |
| 872 | 889 | /* Main board */ |
| 873 | 890 | MCFG_TMS9901_ADD(TMS9901_TAG, tms9901_wiring_ti99_4, 3000000) |
| r15495 | r15496 | |
| 969 | 986 | |
| 970 | 987 | driver->m_peribox->senila(CLEAR_LINE); |
| 971 | 988 | driver->m_peribox->senilb(CLEAR_LINE); |
| 972 | | driver->m_ready_line = driver->m_ready_line1 = ASSERT_LINE; |
| 989 | driver->m_ready_line = driver->m_ready_line_dmux = ASSERT_LINE; |
| 973 | 990 | } |
| 974 | 991 | |
| 975 | 992 | MACHINE_RESET( ti99_4a ) |