trunk/src/mess/drivers/ticalc1x.c
| r245223 | r245224 | |
| 2 | 2 | // copyright-holders:hap, Sean Riddle |
| 3 | 3 | /*************************************************************************** |
| 4 | 4 | |
| 5 | | ** subclass of hh_tms1k_state (includes/hh_tms1k.h, drivers/hh_tms1k.c) ** |
| 6 | | |
| 7 | 5 | Texas Instruments TMS1xxx/0970/0980 handheld calculators (mostly single-chip) |
| 8 | 6 | |
| 9 | 7 | Refer to their official manuals on how to use them. |
| r245223 | r245224 | |
| 15 | 13 | |
| 16 | 14 | ***************************************************************************/ |
| 17 | 15 | |
| 18 | | #include "includes/hh_tms1k.h" |
| 16 | #include "emu.h" |
| 17 | #include "cpu/tms0980/tms0980.h" |
| 18 | #include "sound/speaker.h" |
| 19 | 19 | |
| 20 | 20 | // internal artwork |
| 21 | 21 | #include "ti1270.lh" |
| r245223 | r245224 | |
| 24 | 24 | #include "wizatron.lh" |
| 25 | 25 | |
| 26 | 26 | |
| 27 | | class ticalc1x_state : public hh_tms1k_state |
| 27 | class ticalc1x_state : public driver_device |
| 28 | 28 | { |
| 29 | 29 | public: |
| 30 | 30 | ticalc1x_state(const machine_config &mconfig, device_type type, const char *tag) |
| 31 | | : hh_tms1k_state(mconfig, type, tag) |
| 31 | : driver_device(mconfig, type, tag), |
| 32 | m_maincpu(*this, "maincpu"), |
| 33 | m_inp_matrix(*this, "IN"), |
| 34 | m_speaker(*this, "speaker"), |
| 35 | m_display_wait(33), |
| 36 | m_display_maxy(1), |
| 37 | m_display_maxx(0) |
| 32 | 38 | { } |
| 33 | 39 | |
| 40 | // devices |
| 41 | required_device<cpu_device> m_maincpu; |
| 42 | optional_ioport_array<11> m_inp_matrix; // max 11 |
| 43 | optional_device<speaker_sound_device> m_speaker; |
| 44 | |
| 45 | // misc common |
| 46 | UINT16 m_r; // MCU R-pins data |
| 47 | UINT16 m_o; // MCU O-pins data |
| 48 | UINT16 m_inp_mux; // multiplexed inputs mask |
| 49 | bool m_power_on; |
| 50 | |
| 51 | UINT8 read_inputs(int columns); |
| 52 | DECLARE_INPUT_CHANGED_MEMBER(power_button); |
| 53 | DECLARE_WRITE_LINE_MEMBER(auto_power_off); |
| 54 | |
| 55 | virtual void machine_reset(); |
| 56 | virtual void machine_start(); |
| 57 | |
| 58 | // display common |
| 59 | int m_display_wait; // led/lamp off-delay in microseconds (default 33ms) |
| 60 | int m_display_maxy; // display matrix number of rows |
| 61 | int m_display_maxx; // display matrix number of columns |
| 62 | |
| 63 | UINT32 m_display_state[0x20]; // display matrix rows data |
| 64 | UINT16 m_display_segmask[0x20]; // if not 0, display matrix row is a digit, mask indicates connected segments |
| 65 | UINT32 m_display_cache[0x20]; // (internal use) |
| 66 | UINT8 m_display_decay[0x20][0x20]; // (internal use) |
| 67 | |
| 68 | TIMER_DEVICE_CALLBACK_MEMBER(display_decay_tick); |
| 69 | void display_update(); |
| 34 | 70 | void display_matrix_seg(int maxx, int maxy, UINT32 setx, UINT32 sety, UINT16 segmask); |
| 35 | 71 | |
| 36 | 72 | // calculator-specific handlers |
| r245223 | r245224 | |
| 57 | 93 | DECLARE_WRITE16_MEMBER(ti30_write_o); |
| 58 | 94 | DECLARE_WRITE16_MEMBER(ti30_write_r); |
| 59 | 95 | DECLARE_READ8_MEMBER(ti30_read_k); |
| 60 | | |
| 61 | | protected: |
| 62 | | virtual void machine_start(); |
| 63 | 96 | }; |
| 64 | 97 | |
| 65 | 98 | |
| 99 | // machine_start/reset |
| 100 | |
| 66 | 101 | void ticalc1x_state::machine_start() |
| 67 | 102 | { |
| 68 | | hh_tms1k_state::machine_start(); |
| 103 | // zerofill |
| 104 | memset(m_display_state, 0, sizeof(m_display_state)); |
| 105 | memset(m_display_cache, ~0, sizeof(m_display_cache)); |
| 106 | memset(m_display_decay, 0, sizeof(m_display_decay)); |
| 69 | 107 | memset(m_display_segmask, ~0, sizeof(m_display_segmask)); // ! |
| 108 | |
| 109 | m_o = 0; |
| 110 | m_r = 0; |
| 111 | m_inp_mux = 0; |
| 112 | m_power_on = false; |
| 113 | |
| 114 | // register for savestates |
| 115 | save_item(NAME(m_display_maxy)); |
| 116 | save_item(NAME(m_display_maxx)); |
| 117 | save_item(NAME(m_display_wait)); |
| 118 | |
| 119 | save_item(NAME(m_display_state)); |
| 120 | /* save_item(NAME(m_display_cache)); */ // don't save! |
| 121 | save_item(NAME(m_display_decay)); |
| 122 | save_item(NAME(m_display_segmask)); |
| 123 | |
| 124 | save_item(NAME(m_o)); |
| 125 | save_item(NAME(m_r)); |
| 126 | save_item(NAME(m_inp_mux)); |
| 127 | save_item(NAME(m_power_on)); |
| 70 | 128 | } |
| 71 | 129 | |
| 130 | void ticalc1x_state::machine_reset() |
| 131 | { |
| 132 | m_power_on = true; |
| 133 | } |
| 134 | |
| 135 | |
| 136 | |
| 137 | /*************************************************************************** |
| 138 | |
| 139 | Helper Functions |
| 140 | |
| 141 | ***************************************************************************/ |
| 142 | |
| 143 | // The device may strobe the outputs very fast, it is unnoticeable to the user. |
| 144 | // To prevent flickering here, we need to simulate a decay. |
| 145 | |
| 146 | void ticalc1x_state::display_update() |
| 147 | { |
| 148 | UINT32 active_state[0x20]; |
| 149 | |
| 150 | for (int y = 0; y < m_display_maxy; y++) |
| 151 | { |
| 152 | active_state[y] = 0; |
| 153 | |
| 154 | for (int x = 0; x < m_display_maxx; x++) |
| 155 | { |
| 156 | // turn on powered segments |
| 157 | if (m_power_on && m_display_state[y] >> x & 1) |
| 158 | m_display_decay[y][x] = m_display_wait; |
| 159 | |
| 160 | // determine active state |
| 161 | int ds = (m_display_decay[y][x] != 0) ? 1 : 0; |
| 162 | active_state[y] |= (ds << x); |
| 163 | } |
| 164 | } |
| 165 | |
| 166 | // on difference, send to output |
| 167 | for (int y = 0; y < m_display_maxy; y++) |
| 168 | if (m_display_cache[y] != active_state[y]) |
| 169 | { |
| 170 | if (m_display_segmask[y] != 0) |
| 171 | output_set_digit_value(y, active_state[y] & m_display_segmask[y]); |
| 172 | |
| 173 | const int mul = (m_display_maxx <= 10) ? 10 : 100; |
| 174 | for (int x = 0; x < m_display_maxx; x++) |
| 175 | { |
| 176 | int state = active_state[y] >> x & 1; |
| 177 | output_set_lamp_value(y * mul + x, state); |
| 178 | |
| 179 | // bit coords for svg2lay |
| 180 | char buf[10]; |
| 181 | sprintf(buf, "%d.%d", y, x); |
| 182 | output_set_value(buf, state); |
| 183 | } |
| 184 | } |
| 185 | |
| 186 | memcpy(m_display_cache, active_state, sizeof(m_display_cache)); |
| 187 | } |
| 188 | |
| 189 | TIMER_DEVICE_CALLBACK_MEMBER(ticalc1x_state::display_decay_tick) |
| 190 | { |
| 191 | // slowly turn off unpowered segments |
| 192 | for (int y = 0; y < m_display_maxy; y++) |
| 193 | for (int x = 0; x < m_display_maxx; x++) |
| 194 | if (m_display_decay[y][x] != 0) |
| 195 | m_display_decay[y][x]--; |
| 196 | |
| 197 | display_update(); |
| 198 | } |
| 199 | |
| 72 | 200 | void ticalc1x_state::display_matrix_seg(int maxx, int maxy, UINT32 setx, UINT32 sety, UINT16 segmask) |
| 73 | 201 | { |
| 202 | m_display_maxx = maxx; |
| 203 | m_display_maxy = maxy; |
| 204 | |
| 205 | // update current state |
| 206 | UINT32 colmask = (1 << maxx) - 1; |
| 74 | 207 | for (int y = 0; y < maxy; y++) |
| 208 | { |
| 75 | 209 | m_display_segmask[y] &= segmask; |
| 210 | m_display_state[y] = (sety >> y & 1) ? (setx & colmask) : 0; |
| 211 | } |
| 76 | 212 | |
| 77 | | display_matrix(maxx, maxy, setx, sety); |
| 213 | display_update(); |
| 78 | 214 | } |
| 79 | 215 | |
| 80 | 216 | |
| 217 | UINT8 ticalc1x_state::read_inputs(int columns) |
| 218 | { |
| 219 | UINT8 ret = 0; |
| 81 | 220 | |
| 221 | // read selected input rows |
| 222 | for (int i = 0; i < columns; i++) |
| 223 | if (m_inp_mux >> i & 1) |
| 224 | ret |= m_inp_matrix[i]->read(); |
| 225 | |
| 226 | return ret; |
| 227 | } |
| 228 | |
| 229 | |
| 230 | // devices with a TMS0980 can auto power-off |
| 231 | |
| 232 | WRITE_LINE_MEMBER(ticalc1x_state::auto_power_off) |
| 233 | { |
| 234 | if (state) |
| 235 | { |
| 236 | m_power_on = false; |
| 237 | m_maincpu->set_input_line(INPUT_LINE_RESET, ASSERT_LINE); |
| 238 | } |
| 239 | } |
| 240 | |
| 241 | INPUT_CHANGED_MEMBER(ticalc1x_state::power_button) |
| 242 | { |
| 243 | m_power_on = (bool)(FPTR)param; |
| 244 | m_maincpu->set_input_line(INPUT_LINE_RESET, m_power_on ? CLEAR_LINE : ASSERT_LINE); |
| 245 | } |
| 246 | |
| 247 | |
| 248 | |
| 82 | 249 | /*************************************************************************** |
| 83 | 250 | |
| 84 | 251 | Minidrivers (I/O, Inputs, Machine Config) |
| r245223 | r245224 | |
| 102 | 269 | m_display_state[11] = m_display_state[10] << 5 & 0x40; |
| 103 | 270 | m_display_state[10] &= 0x40; |
| 104 | 271 | |
| 105 | | set_display_size(8, 12); |
| 272 | m_display_maxx = 8; |
| 273 | m_display_maxy = 12; |
| 106 | 274 | display_update(); |
| 107 | 275 | } |
| 108 | 276 | |
| r245223 | r245224 | |
| 204 | 372 | MCFG_TMS1XXX_WRITE_O_CB(WRITE16(ticalc1x_state, tisr16_write_o)) |
| 205 | 373 | MCFG_TMS1XXX_WRITE_R_CB(WRITE16(ticalc1x_state, tisr16_write_r)) |
| 206 | 374 | |
| 207 | | MCFG_TIMER_DRIVER_ADD_PERIODIC("display_decay", hh_tms1k_state, display_decay_tick, attotime::from_msec(1)) |
| 375 | MCFG_TIMER_DRIVER_ADD_PERIODIC("display_decay", ticalc1x_state, display_decay_tick, attotime::from_msec(1)) |
| 208 | 376 | MCFG_DEFAULT_LAYOUT(layout_tisr16) |
| 209 | 377 | |
| 210 | 378 | /* no video! */ |
| r245223 | r245224 | |
| 380 | 548 | MCFG_TMS1XXX_WRITE_O_CB(WRITE16(ticalc1x_state, ti1270_write_o)) |
| 381 | 549 | MCFG_TMS1XXX_WRITE_R_CB(WRITE16(ticalc1x_state, ti1270_write_r)) |
| 382 | 550 | |
| 383 | | MCFG_TIMER_DRIVER_ADD_PERIODIC("display_decay", hh_tms1k_state, display_decay_tick, attotime::from_msec(1)) |
| 551 | MCFG_TIMER_DRIVER_ADD_PERIODIC("display_decay", ticalc1x_state, display_decay_tick, attotime::from_msec(1)) |
| 384 | 552 | MCFG_DEFAULT_LAYOUT(layout_ti1270) |
| 385 | 553 | |
| 386 | 554 | /* no video! */ |
| r245223 | r245224 | |
| 464 | 632 | MCFG_TMS1XXX_WRITE_O_CB(WRITE16(ticalc1x_state, wizatron_write_o)) |
| 465 | 633 | MCFG_TMS1XXX_WRITE_R_CB(WRITE16(ticalc1x_state, wizatron_write_r)) |
| 466 | 634 | |
| 467 | | MCFG_TIMER_DRIVER_ADD_PERIODIC("display_decay", hh_tms1k_state, display_decay_tick, attotime::from_msec(1)) |
| 635 | MCFG_TIMER_DRIVER_ADD_PERIODIC("display_decay", ticalc1x_state, display_decay_tick, attotime::from_msec(1)) |
| 468 | 636 | MCFG_DEFAULT_LAYOUT(layout_wizatron) |
| 469 | 637 | |
| 470 | 638 | /* no video! */ |
| r245223 | r245224 | |
| 524 | 692 | MCFG_TMS1XXX_WRITE_O_CB(WRITE16(ticalc1x_state, lilprof_write_o)) |
| 525 | 693 | MCFG_TMS1XXX_WRITE_R_CB(WRITE16(ticalc1x_state, wizatron_write_r)) |
| 526 | 694 | |
| 527 | | MCFG_TIMER_DRIVER_ADD_PERIODIC("display_decay", hh_tms1k_state, display_decay_tick, attotime::from_msec(1)) |
| 695 | MCFG_TIMER_DRIVER_ADD_PERIODIC("display_decay", ticalc1x_state, display_decay_tick, attotime::from_msec(1)) |
| 528 | 696 | MCFG_DEFAULT_LAYOUT(layout_wizatron) |
| 529 | 697 | |
| 530 | 698 | /* no video! */ |
| r245223 | r245224 | |
| 538 | 706 | |
| 539 | 707 | /*************************************************************************** |
| 540 | 708 | |
| 541 | | TI Little Professor (1978 version) |
| 709 | TI Little Professor (1978 version, same as 1980 version) |
| 542 | 710 | * TMS1990 MCU labeled TMC1993NL. die labeled 1990C-c3C |
| 543 | | |
| 544 | | 1978 re-release, with on/off and level select on buttons instead of |
| 545 | | switches. The casing was slightly revised in 1980 again, but same rom. |
| 546 | 711 | |
| 547 | 712 | ***************************************************************************/ |
| 548 | 713 | |
| r245223 | r245224 | |
| 561 | 726 | // 6th digit is a custom 7seg for math symbols (see wizatron_write_r) |
| 562 | 727 | m_display_state[6] = BITSWAP8(m_display_state[6],7,6,1,4,2,3,5,0); |
| 563 | 728 | |
| 564 | | set_display_size(7, 9); |
| 729 | m_display_maxx = 7; |
| 730 | m_display_maxy = 9; |
| 565 | 731 | display_update(); |
| 566 | 732 | } |
| 567 | 733 | |
| r245223 | r245224 | |
| 622 | 788 | MCFG_TMS1XXX_WRITE_O_CB(WRITE16(ticalc1x_state, lilprof78_write_o)) |
| 623 | 789 | MCFG_TMS1XXX_WRITE_R_CB(WRITE16(ticalc1x_state, lilprof78_write_r)) |
| 624 | 790 | |
| 625 | | MCFG_TIMER_DRIVER_ADD_PERIODIC("display_decay", hh_tms1k_state, display_decay_tick, attotime::from_msec(1)) |
| 791 | MCFG_TIMER_DRIVER_ADD_PERIODIC("display_decay", ticalc1x_state, display_decay_tick, attotime::from_msec(1)) |
| 626 | 792 | MCFG_DEFAULT_LAYOUT(layout_wizatron) |
| 627 | 793 | |
| 628 | 794 | /* no video! */ |
| r245223 | r245224 | |
| 857 | 1023 | MCFG_TMS1XXX_WRITE_R_CB(WRITE16(ticalc1x_state, ti30_write_r)) |
| 858 | 1024 | MCFG_TMS1XXX_POWER_OFF_CB(WRITELINE(ticalc1x_state, auto_power_off)) |
| 859 | 1025 | |
| 860 | | MCFG_TIMER_DRIVER_ADD_PERIODIC("display_decay", hh_tms1k_state, display_decay_tick, attotime::from_msec(1)) |
| 1026 | MCFG_TIMER_DRIVER_ADD_PERIODIC("display_decay", ticalc1x_state, display_decay_tick, attotime::from_msec(1)) |
| 861 | 1027 | MCFG_DEFAULT_LAYOUT(layout_ti30) |
| 862 | 1028 | |
| 863 | 1029 | /* no video! */ |
trunk/src/mess/drivers/tispeak.c
| r245223 | r245224 | |
| 2 | 2 | // copyright-holders:hap, Lord Nightmare |
| 3 | 3 | /*************************************************************************** |
| 4 | 4 | |
| 5 | | ** subclass of hh_tms1k_state (includes/hh_tms1k.h, drivers/hh_tms1k.c) ** |
| 6 | | |
| 7 | 5 | Texas Instruments 1st-gen. handheld speech devices, |
| 8 | 6 | |
| 9 | 7 | These devices, mostly edu-toys, are based around an MCU(TMS0270/TMS1100), |
| r245223 | r245224 | |
| 277 | 275 | |
| 278 | 276 | ***************************************************************************/ |
| 279 | 277 | |
| 280 | | #include "includes/hh_tms1k.h" |
| 278 | #include "emu.h" |
| 279 | #include "cpu/tms0980/tms0980.h" |
| 281 | 280 | #include "sound/tms5110.h" |
| 282 | 281 | #include "machine/tms6100.h" |
| 283 | 282 | #include "bus/generic/slot.h" |
| 284 | 283 | #include "bus/generic/carts.h" |
| 285 | 284 | |
| 286 | | // internal artwork |
| 287 | 285 | #include "lantutor.lh" |
| 288 | 286 | #include "snspell.lh" |
| 289 | 287 | |
| r245223 | r245224 | |
| 296 | 294 | #define MASTER_CLOCK (640000) |
| 297 | 295 | |
| 298 | 296 | |
| 299 | | class tispeak_state : public hh_tms1k_state |
| 297 | class tispeak_state : public driver_device |
| 300 | 298 | { |
| 301 | 299 | public: |
| 302 | 300 | tispeak_state(const machine_config &mconfig, device_type type, const char *tag) |
| 303 | | : hh_tms1k_state(mconfig, type, tag), |
| 301 | : driver_device(mconfig, type, tag), |
| 302 | m_maincpu(*this, "maincpu"), |
| 304 | 303 | m_tms5100(*this, "tms5100"), |
| 305 | 304 | m_tms6100(*this, "tms6100"), |
| 306 | | m_cart(*this, "cartslot") |
| 305 | m_cart(*this, "cartslot"), |
| 306 | m_button_matrix(*this, "IN"), |
| 307 | m_display_wait(33), |
| 308 | m_display_maxy(1), |
| 309 | m_display_maxx(0) |
| 307 | 310 | { } |
| 308 | 311 | |
| 309 | 312 | // devices |
| 313 | required_device<tms0270_cpu_device> m_maincpu; |
| 310 | 314 | required_device<tms5100_device> m_tms5100; |
| 311 | 315 | required_device<tms6100_device> m_tms6100; |
| 312 | 316 | optional_device<generic_slot_device> m_cart; |
| 317 | required_ioport_array<9> m_button_matrix; |
| 313 | 318 | |
| 319 | // misc common |
| 320 | UINT16 m_r; // MCU R-pins data |
| 321 | UINT16 m_o; // MCU O-pins data |
| 322 | int m_power_on; |
| 323 | int m_filament_on; |
| 324 | |
| 325 | // display common |
| 326 | int m_display_wait; // led/lamp off-delay in microseconds (default 33ms) |
| 327 | int m_display_maxy; // display matrix number of rows |
| 328 | int m_display_maxx; // display matrix number of columns |
| 329 | |
| 330 | UINT32 m_display_state[0x20]; // display matrix rows data |
| 331 | UINT16 m_display_segmask[0x20]; // if not 0, display matrix row is a digit, mask indicates connected segments |
| 332 | UINT32 m_display_cache[0x20]; // (internal use) |
| 333 | UINT8 m_display_decay[0x20][0x20]; // (internal use) |
| 334 | |
| 335 | TIMER_DEVICE_CALLBACK_MEMBER(display_decay_tick); |
| 336 | void display_update(); |
| 337 | void display_matrix_seg(int maxx, int maxy, UINT32 setx, UINT32 sety, UINT16 segmask); |
| 338 | |
| 314 | 339 | // cartridge |
| 315 | 340 | UINT32 m_cart_max_size; |
| 316 | 341 | UINT8* m_cart_base; |
| r245223 | r245224 | |
| 324 | 349 | DECLARE_WRITE16_MEMBER(snspell_write_r); |
| 325 | 350 | DECLARE_WRITE16_MEMBER(lantutor_write_r); |
| 326 | 351 | |
| 327 | | DECLARE_INPUT_CHANGED_MEMBER(snspell_power_button); |
| 328 | | void snspell_power_off(); |
| 329 | | void snspell_display(); |
| 352 | DECLARE_INPUT_CHANGED_MEMBER(power_button); |
| 353 | void power_off(); |
| 330 | 354 | |
| 331 | | protected: |
| 355 | virtual void machine_reset(); |
| 332 | 356 | virtual void machine_start(); |
| 333 | 357 | }; |
| 334 | 358 | |
| r245223 | r245224 | |
| 373 | 397 | |
| 374 | 398 | /*************************************************************************** |
| 375 | 399 | |
| 376 | | I/O |
| 400 | VFD Display |
| 377 | 401 | |
| 378 | 402 | ***************************************************************************/ |
| 379 | 403 | |
| 380 | | // common/snspell |
| 404 | // The device may strobe the outputs very fast, it is unnoticeable to the user. |
| 405 | // To prevent flickering here, we need to simulate a decay. |
| 381 | 406 | |
| 382 | | void tispeak_state::snspell_display() |
| 407 | void tispeak_state::display_update() |
| 383 | 408 | { |
| 384 | | for (int y = 0; y < 16; y++) |
| 385 | | m_display_segmask[y] = 0x3fff; |
| 409 | UINT32 active_state[0x20]; |
| 386 | 410 | |
| 387 | | display_matrix(16, 16, m_o, (m_r & 0x8000) ? (m_r & 0x21ff) : 0); |
| 411 | for (int y = 0; y < m_display_maxy; y++) |
| 412 | { |
| 413 | active_state[y] = 0; |
| 414 | |
| 415 | for (int x = 0; x < m_display_maxx; x++) |
| 416 | { |
| 417 | // turn on powered segments |
| 418 | if (m_power_on && m_filament_on && m_display_state[y] >> x & 1) |
| 419 | m_display_decay[y][x] = m_display_wait; |
| 420 | |
| 421 | // determine active state |
| 422 | int ds = (m_display_decay[y][x] != 0) ? 1 : 0; |
| 423 | active_state[y] |= (ds << x); |
| 424 | } |
| 425 | } |
| 426 | |
| 427 | // on difference, send to output |
| 428 | for (int y = 0; y < m_display_maxy; y++) |
| 429 | if (m_display_cache[y] != active_state[y]) |
| 430 | { |
| 431 | if (m_display_segmask[y] != 0) |
| 432 | output_set_digit_value(y, active_state[y] & m_display_segmask[y]); |
| 433 | |
| 434 | const int mul = (m_display_maxx <= 10) ? 10 : 100; |
| 435 | for (int x = 0; x < m_display_maxx; x++) |
| 436 | { |
| 437 | int state = active_state[y] >> x & 1; |
| 438 | output_set_lamp_value(y * mul + x, state); |
| 439 | |
| 440 | // bit coords for svg2lay |
| 441 | char buf[10]; |
| 442 | sprintf(buf, "%d.%d", y, x); |
| 443 | output_set_value(buf, state); |
| 444 | } |
| 445 | } |
| 446 | |
| 447 | memcpy(m_display_cache, active_state, sizeof(m_display_cache)); |
| 388 | 448 | } |
| 389 | 449 | |
| 450 | TIMER_DEVICE_CALLBACK_MEMBER(tispeak_state::display_decay_tick) |
| 451 | { |
| 452 | // slowly turn off unpowered segments |
| 453 | for (int y = 0; y < m_display_maxy; y++) |
| 454 | for (int x = 0; x < m_display_maxx; x++) |
| 455 | if (m_display_decay[y][x] != 0) |
| 456 | m_display_decay[y][x]--; |
| 457 | |
| 458 | display_update(); |
| 459 | } |
| 460 | |
| 461 | void tispeak_state::display_matrix_seg(int maxx, int maxy, UINT32 setx, UINT32 sety, UINT16 segmask) |
| 462 | { |
| 463 | m_display_maxx = maxx; |
| 464 | m_display_maxy = maxy; |
| 465 | |
| 466 | // update current state |
| 467 | UINT32 colmask = (1 << maxx) - 1; |
| 468 | for (int y = 0; y < maxy; y++) |
| 469 | { |
| 470 | m_display_segmask[y] &= segmask; |
| 471 | m_display_state[y] = (sety >> y & 1) ? (setx & colmask) : 0; |
| 472 | } |
| 473 | |
| 474 | display_update(); |
| 475 | } |
| 476 | |
| 477 | |
| 478 | |
| 479 | /*************************************************************************** |
| 480 | |
| 481 | I/O |
| 482 | |
| 483 | ***************************************************************************/ |
| 484 | |
| 485 | // common/snspell |
| 486 | |
| 390 | 487 | READ8_MEMBER(tispeak_state::snspell_read_k) |
| 391 | 488 | { |
| 392 | | // note: the Vss row is always on |
| 393 | | return m_inp_matrix[8]->read() | read_inputs(8); |
| 489 | // the Vss row is always on |
| 490 | UINT8 k = m_button_matrix[8]->read(); |
| 491 | |
| 492 | // read selected button rows |
| 493 | for (int i = 0; i < 8; i++) |
| 494 | if (m_r >> i & 1) |
| 495 | k |= m_button_matrix[i]->read(); |
| 496 | |
| 497 | return k; |
| 394 | 498 | } |
| 395 | 499 | |
| 396 | 500 | WRITE16_MEMBER(tispeak_state::snspell_write_r) |
| 397 | 501 | { |
| 502 | // R15: filament on |
| 503 | m_filament_on = data & 0x8000; |
| 504 | |
| 398 | 505 | // R13: power-off request, on falling edge |
| 399 | 506 | if ((m_r >> 13 & 1) && !(data >> 13 & 1)) |
| 400 | | snspell_power_off(); |
| 507 | power_off(); |
| 401 | 508 | |
| 402 | 509 | // R0-R7: input mux and select digit (+R8 if the device has 9 digits) |
| 403 | | // R15: filament on |
| 404 | 510 | // other bits: MCU internal use |
| 405 | | m_r = m_inp_mux = data; |
| 406 | | snspell_display(); |
| 511 | m_r = data & 0x21ff; |
| 512 | display_matrix_seg(16, 16, m_o, m_r, 0x3fff); |
| 407 | 513 | } |
| 408 | 514 | |
| 409 | 515 | WRITE16_MEMBER(tispeak_state::snspell_write_o) |
| r245223 | r245224 | |
| 411 | 517 | // reorder opla to led14seg, plus DP as d14 and AP as d15: |
| 412 | 518 | // E,D,C,G,B,A,I,M,L,K,N,J,[AP],H,F,[DP] (sidenote: TI KLMN = MAME MLNK) |
| 413 | 519 | m_o = BITSWAP16(data,12,15,10,7,8,9,11,6,13,3,14,0,1,2,4,5); |
| 414 | | snspell_display(); |
| 520 | display_matrix_seg(16, 16, m_o, m_r, 0x3fff); |
| 415 | 521 | } |
| 416 | 522 | |
| 417 | 523 | |
| 418 | | void tispeak_state::snspell_power_off() |
| 524 | void tispeak_state::power_off() |
| 419 | 525 | { |
| 420 | 526 | m_maincpu->set_input_line(INPUT_LINE_RESET, ASSERT_LINE); |
| 421 | 527 | m_tms5100->reset(); |
| 422 | 528 | m_tms6100->reset(); |
| 423 | 529 | |
| 424 | | m_power_on = false; |
| 530 | m_power_on = 0; |
| 425 | 531 | } |
| 426 | 532 | |
| 427 | 533 | |
| r245223 | r245224 | |
| 432 | 538 | // reorder opla to led14seg, plus DP as d14 and AP as d15: |
| 433 | 539 | // [DP],D,C,H,F,B,I,M,L,K,N,J,[AP],E,G,A (sidenote: TI KLMN = MAME MLNK) |
| 434 | 540 | m_o = BITSWAP16(data,12,0,10,7,8,9,11,6,3,14,4,13,1,2,5,15); |
| 435 | | snspell_display(); |
| 541 | display_matrix_seg(16, 16, m_o, m_r, 0x3fff); |
| 436 | 542 | } |
| 437 | 543 | |
| 438 | 544 | |
| r245223 | r245224 | |
| 441 | 547 | WRITE16_MEMBER(tispeak_state::lantutor_write_r) |
| 442 | 548 | { |
| 443 | 549 | // same as default, except R13 is used for an extra digit |
| 444 | | m_r = m_inp_mux = data; |
| 445 | | snspell_display(); |
| 550 | m_filament_on = data & 0x8000; |
| 551 | m_r = data & 0x21ff; |
| 552 | display_matrix_seg(16, 16, m_o, m_r, 0x3fff); |
| 446 | 553 | } |
| 447 | 554 | |
| 448 | 555 | |
| r245223 | r245224 | |
| 453 | 560 | |
| 454 | 561 | ***************************************************************************/ |
| 455 | 562 | |
| 456 | | INPUT_CHANGED_MEMBER(tispeak_state::snspell_power_button) |
| 563 | INPUT_CHANGED_MEMBER(tispeak_state::power_button) |
| 457 | 564 | { |
| 458 | 565 | int on = (int)(FPTR)param; |
| 459 | 566 | |
| 460 | 567 | if (on && !m_power_on) |
| 461 | 568 | { |
| 462 | | m_power_on = true; |
| 569 | m_power_on = 1; |
| 463 | 570 | m_maincpu->set_input_line(INPUT_LINE_RESET, CLEAR_LINE); |
| 464 | 571 | } |
| 465 | 572 | else if (!on && m_power_on) |
| 466 | | snspell_power_off(); |
| 573 | power_off(); |
| 467 | 574 | } |
| 468 | 575 | |
| 469 | 576 | static INPUT_PORTS_START( snspell ) |
| r245223 | r245224 | |
| 524 | 631 | PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_6) PORT_NAME("Secret Code") |
| 525 | 632 | PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_7) PORT_NAME("Letter") |
| 526 | 633 | PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_8) PORT_NAME("Say It") |
| 527 | | PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_PGUP) PORT_NAME("Spell/On") PORT_CHANGED_MEMBER(DEVICE_SELF, tispeak_state, snspell_power_button, (void *)true) |
| 634 | PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_PGUP) PORT_NAME("Spell/On") PORT_CHANGED_MEMBER(DEVICE_SELF, tispeak_state, power_button, (void *)1) |
| 528 | 635 | INPUT_PORTS_END |
| 529 | 636 | |
| 530 | 637 | |
| r245223 | r245224 | |
| 576 | 683 | PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_U) PORT_NAME("Write It") |
| 577 | 684 | PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Y) PORT_NAME("Greater/Less") |
| 578 | 685 | PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_T) PORT_NAME("Word Problems") |
| 579 | | PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_R) PORT_CODE(KEYCODE_PGUP) PORT_NAME("Solve It/On") PORT_CHANGED_MEMBER(DEVICE_SELF, tispeak_state, snspell_power_button, (void *)true) |
| 686 | PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_R) PORT_CODE(KEYCODE_PGUP) PORT_NAME("Solve It/On") PORT_CHANGED_MEMBER(DEVICE_SELF, tispeak_state, power_button, (void *)1) |
| 580 | 687 | |
| 581 | 688 | PORT_START("IN.7") |
| 582 | 689 | PORT_BIT( 0x1f, IP_ACTIVE_HIGH, IPT_UNUSED ) |
| r245223 | r245224 | |
| 598 | 705 | PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_6) PORT_NAME("Picture Read") |
| 599 | 706 | PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_7) PORT_NAME("Letter Stumper") |
| 600 | 707 | PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_8) PORT_NAME("Hear It") |
| 601 | | PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_PGUP) PORT_NAME("Word Zap/On") PORT_CHANGED_MEMBER(DEVICE_SELF, tispeak_state, snspell_power_button, (void *)true) |
| 708 | PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_PGUP) PORT_NAME("Word Zap/On") PORT_CHANGED_MEMBER(DEVICE_SELF, tispeak_state, power_button, (void *)1) |
| 602 | 709 | INPUT_PORTS_END |
| 603 | 710 | |
| 604 | 711 | |
| r245223 | r245224 | |
| 639 | 746 | |
| 640 | 747 | ***************************************************************************/ |
| 641 | 748 | |
| 749 | void tispeak_state::machine_reset() |
| 750 | { |
| 751 | m_power_on = 1; |
| 752 | } |
| 753 | |
| 642 | 754 | void tispeak_state::machine_start() |
| 643 | 755 | { |
| 644 | | hh_tms1k_state::machine_start(); |
| 756 | // zerofill |
| 757 | memset(m_display_state, 0, sizeof(m_display_state)); |
| 758 | memset(m_display_cache, ~0, sizeof(m_display_cache)); |
| 759 | memset(m_display_decay, 0, sizeof(m_display_decay)); |
| 760 | memset(m_display_segmask, ~0, sizeof(m_display_segmask)); // ! |
| 645 | 761 | |
| 762 | m_r = 0; |
| 763 | m_o = 0; |
| 764 | m_power_on = 0; |
| 765 | m_filament_on = 0; |
| 766 | |
| 767 | // register for savestates |
| 768 | save_item(NAME(m_display_maxy)); |
| 769 | save_item(NAME(m_display_maxx)); |
| 770 | save_item(NAME(m_display_wait)); |
| 771 | |
| 772 | save_item(NAME(m_display_state)); |
| 773 | /* save_item(NAME(m_display_cache)); */ // don't save! |
| 774 | save_item(NAME(m_display_decay)); |
| 775 | save_item(NAME(m_display_segmask)); |
| 776 | |
| 777 | save_item(NAME(m_r)); |
| 778 | save_item(NAME(m_o)); |
| 779 | save_item(NAME(m_power_on)); |
| 780 | save_item(NAME(m_filament_on)); |
| 781 | |
| 646 | 782 | // init cartridge |
| 647 | 783 | if (m_cart != NULL && m_cart->exists()) |
| 648 | 784 | { |
| r245223 | r245224 | |
| 666 | 802 | MCFG_TMS0270_WRITE_CTL_CB(DEVWRITE8("tms5100", tms5100_device, ctl_w)) |
| 667 | 803 | MCFG_TMS0270_WRITE_PDC_CB(DEVWRITELINE("tms5100", tms5100_device, pdc_w)) |
| 668 | 804 | |
| 669 | | MCFG_TIMER_DRIVER_ADD_PERIODIC("display_decay", hh_tms1k_state, display_decay_tick, attotime::from_msec(1)) |
| 805 | MCFG_TIMER_DRIVER_ADD_PERIODIC("display_decay", tispeak_state, display_decay_tick, attotime::from_msec(1)) |
| 670 | 806 | MCFG_DEFAULT_LAYOUT(layout_snspell) // max 9 digits |
| 671 | 807 | |
| 672 | 808 | /* no video! */ |