diff options
Diffstat (limited to 'src/mame/drivers/tm990189.cpp')
-rw-r--r-- | src/mame/drivers/tm990189.cpp | 1051 |
1 files changed, 1051 insertions, 0 deletions
diff --git a/src/mame/drivers/tm990189.cpp b/src/mame/drivers/tm990189.cpp new file mode 100644 index 00000000000..f6c778dd305 --- /dev/null +++ b/src/mame/drivers/tm990189.cpp @@ -0,0 +1,1051 @@ +// license:GPL-2.0+ +// copyright-holders:Raphael Nabet, Robbbert +/* + Experimental tm990/189 ("University Module") driver. + + The tm990/189 is a simple board built around a tms9980 at 2.0 MHz. + The board features: + * a calculator-like alphanumeric keyboard, a 10-digit 8-segment display, + a sound buzzer and 4 status LEDs + * a 4kb ROM socket (0x3000-0x3fff), and a 2kb ROM socket (0x0800-0x0fff) + * 1kb of RAM expandable to 2kb (0x0000-0x03ff and 0x0400-0x07ff) + * a tms9901 controlling a custom parallel I/O port (available for + expansion) + * an optional on-board serial interface (either TTY or RS232): TI ROMs + support a terminal connected to this port + * an optional tape interface + * an optional bus extension port for adding additional custom devices (TI + sold a video controller expansion built around a tms9918, which was + supported by University Basic) + + One tms9901 is set up so that it handles tms9980 interrupts. The other + tms9901, the tms9902, and extension cards can trigger interrupts on the + interrupt-handling tms9901. + + TI sold two ROM sets for this machine: a monitor and assembler ("UNIBUG", + packaged as one 4kb EPROM) and a Basic interpreter ("University BASIC", + packaged as a 4kb and a 2kb EPROM). Users could burn and install custom + ROM sets, too. + + This board was sold to universities to learn either assembly language or + BASIC programming. + + A few hobbyists may have bought one of these, too. This board can actually + be used as a development kit for the tms9980, but it was not supported as + such (there was no EPROM programmer or mass storage system for the + tm990/189, though you could definitively design your own and attach them to + the extension port). + + - Raphael Nabet 2003 + + Bug - The input buffer of character segments isn't fully cleared. If you + press Shift, then Z enough times, garbage appears. This is because + the boot process should have set 18C-1CB to FF, but only sets up to 1B3. + + Need a dump of the UNIBUG rom. + + Demo programs for the 990189v: You can get impressive colour displays (with + sprites) from the 4 included demos. Press the Enter key after each instruction, + and wait for the READY prompt before proceeding to the next. + + NEW + LOADx (where x = 0,1,2,3) + RUN + + University BASIC fully supports the tms9918 videocard option. For example, enter + COLOR x (where x = 1 to 15), to get a coloured background. + + - Robbbert 2011 + +******************************************************************************************/ + +#include "emu.h" +#include "cpu/tms9900/tms9980a.h" +#include "machine/tms9901.h" +#include "machine/tms9902.h" +#include "video/tms9928a.h" +#include "imagedev/cassette.h" +#include "sound/speaker.h" +#include "sound/wave.h" +#include "tm990189.lh" +#include "tm990189v.lh" + +#define TMS9901_0_TAG "tms9901_usr" +#define TMS9901_1_TAG "tms9901_sys" + +class tm990189_state : public driver_device +{ +public: + tm990189_state(const machine_config &mconfig, device_type type, const char *tag) + : driver_device(mconfig, type, tag), + m_tms9980a(*this, "maincpu"), + m_speaker(*this, "speaker"), + m_cass(*this, "cassette"), + m_tms9918(*this, "tms9918" ), + m_maincpu(*this, "maincpu"), + m_cassette(*this, "cassette"), + m_tms9901_usr(*this, TMS9901_0_TAG), + m_tms9901_sys(*this, TMS9901_1_TAG) { } + + required_device<tms9980a_device> m_tms9980a; + required_device<speaker_sound_device> m_speaker; + required_device<cassette_image_device> m_cass; + optional_device<tms9918_device> m_tms9918; + + DECLARE_READ8_MEMBER( interrupt_level ); + + DECLARE_READ8_MEMBER(video_vdp_r); + DECLARE_WRITE8_MEMBER(video_vdp_w); + DECLARE_READ8_MEMBER(video_joy_r); + DECLARE_WRITE8_MEMBER(video_joy_w); + int m_load_state; + + int m_digitsel; + int m_segment; + emu_timer *m_displayena_timer; + UINT8 m_segment_state[10]; + UINT8 m_old_segment_state[10]; + UINT8 m_LED_state; + emu_timer *m_joy1x_timer; + emu_timer *m_joy1y_timer; + emu_timer *m_joy2x_timer; + emu_timer *m_joy2y_timer; + device_image_interface *m_rs232_fp; + UINT8 m_rs232_rts; + emu_timer *m_rs232_input_timer; + UINT8 m_bogus_read_save; + + DECLARE_WRITE8_MEMBER( external_operation ); + + DECLARE_INPUT_CHANGED_MEMBER( load_interrupt ); + + DECLARE_WRITE_LINE_MEMBER(usr9901_led0_w); + DECLARE_WRITE_LINE_MEMBER(usr9901_led1_w); + DECLARE_WRITE_LINE_MEMBER(usr9901_led2_w); + DECLARE_WRITE_LINE_MEMBER(usr9901_led3_w); + DECLARE_WRITE8_MEMBER(usr9901_interrupt_callback); + + DECLARE_WRITE8_MEMBER(sys9901_interrupt_callback); + DECLARE_READ8_MEMBER(sys9901_r); + DECLARE_WRITE_LINE_MEMBER(sys9901_digitsel0_w); + DECLARE_WRITE_LINE_MEMBER(sys9901_digitsel1_w); + DECLARE_WRITE_LINE_MEMBER(sys9901_digitsel2_w); + DECLARE_WRITE_LINE_MEMBER(sys9901_digitsel3_w); + + DECLARE_WRITE_LINE_MEMBER(sys9901_segment0_w); + DECLARE_WRITE_LINE_MEMBER(sys9901_segment1_w); + DECLARE_WRITE_LINE_MEMBER(sys9901_segment2_w); + DECLARE_WRITE_LINE_MEMBER(sys9901_segment3_w); + DECLARE_WRITE_LINE_MEMBER(sys9901_segment4_w); + DECLARE_WRITE_LINE_MEMBER(sys9901_segment5_w); + DECLARE_WRITE_LINE_MEMBER(sys9901_segment6_w); + DECLARE_WRITE_LINE_MEMBER(sys9901_segment7_w); + + DECLARE_WRITE_LINE_MEMBER(sys9901_dsplytrgr_w); + DECLARE_WRITE_LINE_MEMBER(sys9901_shiftlight_w); + DECLARE_WRITE_LINE_MEMBER(sys9901_spkrdrive_w); + DECLARE_WRITE_LINE_MEMBER(sys9901_tapewdata_w); + + DECLARE_WRITE8_MEMBER( xmit_callback ); + DECLARE_MACHINE_START(tm990_189); + DECLARE_MACHINE_RESET(tm990_189); + DECLARE_MACHINE_START(tm990_189_v); + DECLARE_MACHINE_RESET(tm990_189_v); + + TIMER_DEVICE_CALLBACK_MEMBER(display_callback); + TIMER_CALLBACK_MEMBER(clear_load); + void hold_load(); +private: + void draw_digit(void); + void led_set(int number, bool state); + void segment_set(int offset, bool state); + void digitsel(int offset, bool state); + required_device<cpu_device> m_maincpu; + required_device<cassette_image_device> m_cassette; + required_device<tms9901_device> m_tms9901_usr; + required_device<tms9901_device> m_tms9901_sys; +}; + + +#define displayena_duration attotime::from_usec(4500) /* Can anyone confirm this? 74LS123 connected to C=0.1uF and R=100kOhm */ + +MACHINE_RESET_MEMBER(tm990189_state,tm990_189) +{ + m_tms9980a->set_ready(ASSERT_LINE); + m_tms9980a->set_hold(CLEAR_LINE); + hold_load(); +} + +MACHINE_START_MEMBER(tm990189_state,tm990_189) +{ + m_displayena_timer = machine().scheduler().timer_alloc(FUNC_NULL); +} + +MACHINE_START_MEMBER(tm990189_state,tm990_189_v) +{ + m_displayena_timer = machine().scheduler().timer_alloc(FUNC_NULL); + + m_joy1x_timer = machine().scheduler().timer_alloc(FUNC_NULL); + m_joy1y_timer = machine().scheduler().timer_alloc(FUNC_NULL); + m_joy2x_timer = machine().scheduler().timer_alloc(FUNC_NULL); + m_joy2y_timer = machine().scheduler().timer_alloc(FUNC_NULL); +} + +MACHINE_RESET_MEMBER(tm990189_state,tm990_189_v) +{ + m_tms9980a->set_ready(ASSERT_LINE); + m_tms9980a->set_hold(CLEAR_LINE); + hold_load(); +} + +/* + hold and debounce load line (emulation is inaccurate) +*/ + +TIMER_CALLBACK_MEMBER(tm990189_state::clear_load) +{ + m_load_state = FALSE; + m_tms9980a->set_input_line(INT_9980A_LOAD, CLEAR_LINE); +} + +void tm990189_state::hold_load() +{ + m_load_state = TRUE; + m_tms9980a->set_input_line(INT_9980A_LOAD, ASSERT_LINE); + machine().scheduler().timer_set(attotime::from_msec(100), timer_expired_delegate(FUNC(tm990189_state::clear_load),this)); +} + +/* + LOAD interrupt switch +*/ +INPUT_CHANGED_MEMBER( tm990189_state::load_interrupt ) +{ + // When depressed, fire LOAD (neg logic) + if (newval==CLEAR_LINE) hold_load(); +} + + +/* + tm990_189 video emulation. + + Has an integrated 10 digit 8-segment display. + Supports EIA and TTY terminals, and an optional 9918 controller. +*/ + +void tm990189_state::draw_digit() +{ + m_segment_state[m_digitsel] |= ~m_segment; +} + + +TIMER_DEVICE_CALLBACK_MEMBER(tm990189_state::display_callback) +{ + UINT8 i; + char ledname[8]; + // since the segment data is cleared after being used, the old_segment is there + // in case the segment data hasn't been refreshed yet. + for (i = 0; i < 10; i++) + { + m_old_segment_state[i] |= m_segment_state[i]; + sprintf(ledname,"digit%d",i); + output_set_digit_value(i, m_old_segment_state[i]); + m_old_segment_state[i] = m_segment_state[i]; + m_segment_state[i] = 0; + } + + for (i = 0; i < 7; i++) + { + sprintf(ledname,"led%d",i); + output_set_value(ledname, !BIT(m_LED_state, i)); + } +} + +/* + tms9901 code +*/ + +WRITE8_MEMBER( tm990189_state::usr9901_interrupt_callback ) +{ + // Triggered by internal timer (set by ROM to 1.6 ms cycle) on level 3 + // or by keyboard interrupt (level 6) + if (!m_load_state) + { + m_tms9980a->set_input_line(offset & 7, ASSERT_LINE); + } +} + +void tm990189_state::led_set(int offset, bool state) +{ + if (state) + m_LED_state |= (1 << offset); + else + m_LED_state &= ~(1 << offset); +} + +WRITE_LINE_MEMBER( tm990189_state::usr9901_led0_w ) +{ + led_set(0, state); +} + +WRITE_LINE_MEMBER( tm990189_state::usr9901_led1_w ) +{ + led_set(1, state); +} + +WRITE_LINE_MEMBER( tm990189_state::usr9901_led2_w ) +{ + led_set(2, state); +} + +WRITE_LINE_MEMBER( tm990189_state::usr9901_led3_w ) +{ + led_set(3, state); +} + +WRITE8_MEMBER( tm990189_state::sys9901_interrupt_callback ) +{ + // machine().device<tms9901_device>("tms9901_0")->set_single_int(5, (data!=0)? ASSERT_LINE:CLEAR_LINE); + // TODO: Check this + m_tms9901_usr->set_single_int(5, (data!=0)? ASSERT_LINE:CLEAR_LINE); +} + +READ8_MEMBER( tm990189_state::sys9901_r ) +{ + UINT8 data = 0; + if (offset == TMS9901_CB_INT7) + { + static const char *const keynames[] = { "LINE0", "LINE1", "LINE2", "LINE3", "LINE4", "LINE5", "LINE6", "LINE7", "LINE8" }; + + /* keyboard read */ + if (m_digitsel < 9) + data |= ioport(keynames[m_digitsel])->read() << 1; + + /* tape input */ + if (m_cass->input() > 0.0) + data |= 0x40; + } + + return data; +} + +void tm990189_state::digitsel(int offset, bool state) +{ + if (state) + m_digitsel |= 1 << offset; + else + m_digitsel &= ~ (1 << offset); +} + +WRITE_LINE_MEMBER( tm990189_state::sys9901_digitsel0_w ) +{ + digitsel(0, state); +} +WRITE_LINE_MEMBER( tm990189_state::sys9901_digitsel1_w ) +{ + digitsel(1, state); +} +WRITE_LINE_MEMBER( tm990189_state::sys9901_digitsel2_w ) +{ + digitsel(2, state); +} +WRITE_LINE_MEMBER( tm990189_state::sys9901_digitsel3_w ) +{ + digitsel(3, state); +} + + +void tm990189_state::segment_set(int offset, bool state) +{ + if (state) + m_segment |= 1 << offset; + else + { + m_segment &= ~ (1 << offset); + if ((m_displayena_timer->remaining() > attotime::zero) && (m_digitsel < 10)) + draw_digit(); + } +} + +WRITE_LINE_MEMBER( tm990189_state::sys9901_segment0_w ) +{ + segment_set(0, state); +} +WRITE_LINE_MEMBER( tm990189_state::sys9901_segment1_w ) +{ + segment_set(1, state); +} +WRITE_LINE_MEMBER( tm990189_state::sys9901_segment2_w ) +{ + segment_set(2, state); +} +WRITE_LINE_MEMBER( tm990189_state::sys9901_segment3_w ) +{ + segment_set(3, state); +} +WRITE_LINE_MEMBER( tm990189_state::sys9901_segment4_w ) +{ + segment_set(4, state); +} +WRITE_LINE_MEMBER( tm990189_state::sys9901_segment5_w ) +{ + segment_set(5, state); +} +WRITE_LINE_MEMBER( tm990189_state::sys9901_segment6_w ) +{ + segment_set(6, state); +} +WRITE_LINE_MEMBER( tm990189_state::sys9901_segment7_w ) +{ + segment_set(7, state); +} + +WRITE_LINE_MEMBER( tm990189_state::sys9901_dsplytrgr_w ) +{ + if ((!state) && (m_digitsel < 10)) + { + m_displayena_timer->reset(displayena_duration); + draw_digit(); + } +} + +WRITE_LINE_MEMBER( tm990189_state::sys9901_shiftlight_w ) +{ + if (state) + m_LED_state |= 0x10; + else + m_LED_state &= ~0x10; +} + +WRITE_LINE_MEMBER( tm990189_state::sys9901_spkrdrive_w ) +{ + m_speaker->level_w(state); +} + +WRITE_LINE_MEMBER( tm990189_state::sys9901_tapewdata_w ) +{ + m_cassette->output(state ? +1.0 : -1.0); +} + +class tm990_189_rs232_image_device : public device_t, + public device_image_interface +{ +public: + // construction/destruction + tm990_189_rs232_image_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // image-level overrides + virtual iodevice_t image_type() const { return IO_SERIAL; } + + virtual bool is_readable() const { return 1; } + virtual bool is_writeable() const { return 1; } + virtual bool is_creatable() const { return 1; } + virtual bool must_be_loaded() const { return 0; } + virtual bool is_reset_on_load() const { return 0; } + virtual const char *image_interface() const { return NULL; } + virtual const char *file_extensions() const { return ""; } + virtual const option_guide *create_option_guide() const { return NULL; } + + virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr); + + virtual bool call_load(); + virtual void call_unload(); +protected: + // device-level overrides + virtual void device_config_complete(); + virtual void device_start(); +}; + +const device_type TM990_189_RS232 = &device_creator<tm990_189_rs232_image_device>; + +tm990_189_rs232_image_device::tm990_189_rs232_image_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : device_t(mconfig, TM990_189_RS232, "TM990/189 RS232 port", tag, owner, clock, "tm990_189_rs232_image", __FILE__), + device_image_interface(mconfig, *this) +{ +} + +void tm990_189_rs232_image_device::device_config_complete() +{ + update_names(); +} + +void tm990_189_rs232_image_device::device_start() +{ +} + +void tm990_189_rs232_image_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) +{ + //tm990189_state *state = machine.driver_data<tm990189_state>(); + UINT8 buf; + if (/*state->m_rs232_rts &&*/ /*(mame_ftell(state->m_rs232_fp) < mame_fsize(state->m_rs232_fp))*/1) + { + tms9902_device* tms9902 = static_cast<tms9902_device*>(machine().device("tms9902")); + if (fread(&buf, 1) == 1) + tms9902->rcv_data(buf); + } +} + +bool tm990_189_rs232_image_device::call_load() +{ + tm990189_state *state = machine().driver_data<tm990189_state>(); + tms9902_device* tms9902 = static_cast<tms9902_device*>(machine().device("tms9902")); + tms9902->rcv_dsr(ASSERT_LINE); + state->m_rs232_input_timer = timer_alloc(); + state->m_rs232_input_timer->adjust(attotime::zero, 0, attotime::from_msec(10)); + return IMAGE_INIT_PASS; +} + + +void tm990_189_rs232_image_device::call_unload() +{ + tm990189_state *state = machine().driver_data<tm990189_state>(); + tms9902_device* tms9902 = static_cast<tms9902_device*>(machine().device("tms9902")); + tms9902->rcv_dsr(CLEAR_LINE); + + state->m_rs232_input_timer->reset(); /* FIXME - timers should only be allocated once */ +} + +#define MCFG_TM990_189_RS232_ADD(_tag) \ + MCFG_DEVICE_ADD(_tag, TM990_189_RS232, 0) + + +/* static TMS9902_RTS_CALLBACK( rts_callback ) +{ + tm990189 *state = device->machine().driver_data<tm990189>(); + state->m_rs232_rts = RTS; + tms9902->set_cts(RTS); +} */ + +WRITE8_MEMBER( tm990189_state::xmit_callback ) +{ + UINT8 buf = data; + if (m_rs232_fp) m_rs232_fp->fwrite(&buf, 1); +} + +/* + External instruction decoding +*/ +WRITE8_MEMBER( tm990189_state::external_operation ) +{ + switch (offset) + { + case 2: // IDLE + if (data) + m_LED_state |= 0x40; + else + m_LED_state &= ~0x40; + break; + case 3: // RSET + // Not used on the default board + break; + case 5: // CKON: set DECKCONTROL + m_LED_state |= 0x20; + m_cass->change_state(CASSETTE_MOTOR_ENABLED,CASSETTE_MASK_MOTOR); + break; + case 6: // CKOF: clear DECKCONTROL + m_LED_state &= ~0x20; + m_cass->change_state(CASSETTE_MOTOR_DISABLED,CASSETTE_MASK_MOTOR); + break; + case 7: // LREX: trigger LOAD + hold_load(); + break; + default: // undefined + break; + } +} + +/* + Video Board handling +*/ + +READ8_MEMBER( tm990189_state::video_vdp_r ) +{ + int reply = 0; + + /* When the tms9980 reads @>2000 or @>2001, it actually does a word access: + it reads @>2000 first, then @>2001. According to schematics, both access + are decoded to the VDP: read accesses are therefore bogus, all the more so + since the two reads are too close (1us) for the VDP to be able to reload + the read buffer: the read address pointer is probably incremented by 2, but + only the first byte is valid. There is a work around for this problem: all + you need is reloading the address pointer before each read. However, + software always uses the second byte, which is very weird, particularly + for the status port. Presumably, since the read buffer has not been + reloaded, the second byte read from the memory read port is equal to the + first; however, this explanation is not very convincing for the status + port. Still, I do not have any better explanation, so I will stick with + it. */ + + if (offset & 2) + reply = m_tms9918->register_read(space, 0); + else + reply = m_tms9918->vram_read(space, 0); + + if (!(offset & 1)) + m_bogus_read_save = reply; + else + reply = m_bogus_read_save; + + return reply; +} + +WRITE8_MEMBER( tm990189_state::video_vdp_w ) +{ + if (offset & 1) + { + if (offset & 2) + m_tms9918->register_write(space, 0, data); + else + m_tms9918->vram_write(space, 0, data); + } +} + +READ8_MEMBER( tm990189_state::video_joy_r ) +{ + UINT8 data = ioport("BUTTONS")->read(); + + if (m_joy1x_timer->remaining() < attotime::zero) + data |= 0x01; + + if (m_joy1y_timer->remaining() < attotime::zero) + data |= 0x02; + + if (m_joy2x_timer->remaining() < attotime::zero) + data |= 0x08; + + if (m_joy2y_timer->remaining() < attotime::zero) + data |= 0x10; + + return data; +} + +WRITE8_MEMBER( tm990189_state::video_joy_w ) +{ + m_joy1x_timer->reset(attotime::from_usec(ioport("JOY1_X")->read()*28+28)); + m_joy1y_timer->reset(attotime::from_usec(ioport("JOY1_Y")->read()*28+28)); + m_joy2x_timer->reset(attotime::from_usec(ioport("JOY2_X")->read()*28+28)); + m_joy2y_timer->reset(attotime::from_usec(ioport("JOY2_Y")->read()*28+28)); +} + +/* +// user tms9901 setup +static const tms9901_interface usr9901reset_param = +{ + TMS9901_INT1 | TMS9901_INT2 | TMS9901_INT3 | TMS9901_INT4 | TMS9901_INT5 | TMS9901_INT6, // only input pins whose state is always known + + // Read handler. Covers all input lines (see tms9901.h) + DEVCB_NULL, + + // write handlers + { + DEVCB_DRIVER_LINE_MEMBER(tm990189_state, usr9901_led0_w), + DEVCB_DRIVER_LINE_MEMBER(tm990189_state, usr9901_led1_w), + DEVCB_DRIVER_LINE_MEMBER(tm990189_state, usr9901_led2_w), + DEVCB_DRIVER_LINE_MEMBER(tm990189_state, usr9901_led3_w), + DEVCB_NULL, + DEVCB_NULL, + DEVCB_NULL, + DEVCB_NULL, + DEVCB_NULL, + DEVCB_NULL, + DEVCB_NULL, + DEVCB_NULL, + DEVCB_NULL, + DEVCB_NULL, + DEVCB_NULL, + DEVCB_NULL + }, + + // interrupt handler + DEVCB_DRIVER_MEMBER(tm990189_state, usr9901_interrupt_callback) +}; +*/ + +/* +// system tms9901 setup +static const tms9901_interface sys9901reset_param = +{ + 0, // only input pins whose state is always known + + // Read handler. Covers all input lines (see tms9901.h) + DEVCB_DRIVER_MEMBER(tm990189_state, sys9901_r), + + // write handlers + { + DEVCB_DRIVER_LINE_MEMBER(tm990189_state, sys9901_digitsel0_w), + DEVCB_DRIVER_LINE_MEMBER(tm990189_state, sys9901_digitsel1_w), + DEVCB_DRIVER_LINE_MEMBER(tm990189_state, sys9901_digitsel2_w), + DEVCB_DRIVER_LINE_MEMBER(tm990189_state, sys9901_digitsel3_w), + DEVCB_DRIVER_LINE_MEMBER(tm990189_state, sys9901_segment0_w), + DEVCB_DRIVER_LINE_MEMBER(tm990189_state, sys9901_segment1_w), + DEVCB_DRIVER_LINE_MEMBER(tm990189_state, sys9901_segment2_w), + DEVCB_DRIVER_LINE_MEMBER(tm990189_state, sys9901_segment3_w), + DEVCB_DRIVER_LINE_MEMBER(tm990189_state, sys9901_segment4_w), + DEVCB_DRIVER_LINE_MEMBER(tm990189_state, sys9901_segment5_w), + DEVCB_DRIVER_LINE_MEMBER(tm990189_state, sys9901_segment6_w), + DEVCB_DRIVER_LINE_MEMBER(tm990189_state, sys9901_segment7_w), + DEVCB_DRIVER_LINE_MEMBER(tm990189_state, sys9901_dsplytrgr_w), + DEVCB_DRIVER_LINE_MEMBER(tm990189_state, sys9901_shiftlight_w), + DEVCB_DRIVER_LINE_MEMBER(tm990189_state, sys9901_spkrdrive_w), + DEVCB_DRIVER_LINE_MEMBER(tm990189_state, sys9901_tapewdata_w) + }, + + // interrupt handler + DEVCB_DRIVER_MEMBER(tm990189_state, sys9901_interrupt_callback) +}; + +*/ + +/* + Memory map: + + 0x0000-0x03ff: 1kb RAM + 0x0400-0x07ff: 1kb onboard expansion RAM + 0x0800-0x0bff or 0x0800-0x0fff: 1kb or 2kb onboard expansion ROM + 0x1000-0x2fff: reserved for offboard expansion + Only known card: Color Video Board + 0x1000-0x17ff (R): Video board ROM 1 + 0x1800-0x1fff (R): Video board ROM 2 + 0x2000-0x27ff (R): tms9918 read ports (bogus) + (address & 2) == 0: data port (bogus) + (address & 2) == 1: status register (bogus) + 0x2800-0x2fff (R): joystick read port + d2: joy 2 switch + d3: joy 2 Y (length of pulse after retrigger is proportional to axis position) + d4: joy 2 X (length of pulse after retrigger is proportional to axis position) + d2: joy 1 switch + d3: joy 1 Y (length of pulse after retrigger is proportional to axis position) + d4: joy 1 X (length of pulse after retrigger is proportional to axis position) + 0x1801-0x1fff ((address & 1) == 1) (W): joystick write port (retrigger) + 0x2801-0x2fff ((address & 1) == 1) (W): tms9918 write ports + (address & 2) == 0: data port + (address & 2) == 1: control register + 0x3000-0x3fff: 4kb onboard ROM +*/ + +static ADDRESS_MAP_START( tm990_189_memmap, AS_PROGRAM, 8, tm990189_state ) + AM_RANGE(0x0000, 0x07ff) AM_RAM /* RAM */ + AM_RANGE(0x0800, 0x0fff) AM_ROM /* extra ROM - application programs with unibug, remaining 2kb of program for university basic */ + AM_RANGE(0x1000, 0x2fff) AM_NOP /* reserved for expansion (RAM and/or tms9918 video controller) */ + AM_RANGE(0x3000, 0x3fff) AM_ROM /* main ROM - unibug or university basic */ +ADDRESS_MAP_END + +static ADDRESS_MAP_START( tm990_189_v_memmap, AS_PROGRAM, 8, tm990189_state ) + AM_RANGE(0x0000, 0x07ff) AM_RAM /* RAM */ + AM_RANGE(0x0800, 0x0fff) AM_ROM /* extra ROM - application programs with unibug, remaining 2kb of program for university basic */ + + AM_RANGE(0x1000, 0x17ff) AM_ROM AM_WRITENOP /* video board ROM 1 */ + AM_RANGE(0x1800, 0x1fff) AM_ROM AM_WRITE(video_joy_w) /* video board ROM 2 and joystick write port*/ + AM_RANGE(0x2000, 0x27ff) AM_READ(video_vdp_r) AM_WRITENOP /* video board tms9918 read ports (bogus) */ + AM_RANGE(0x2800, 0x2fff) AM_READWRITE(video_joy_r, video_vdp_w) /* video board joystick read port and tms9918 write ports */ + + AM_RANGE(0x3000, 0x3fff) AM_ROM /* main ROM - unibug or university basic */ +ADDRESS_MAP_END + +/* + CRU map + + The board features one tms9901 for keyboard and sound I/O, another tms9901 + to drive the parallel port and a few LEDs and handle tms9980 interrupts, + and one optional tms9902 for serial I/O. + + * bits >000->1ff (R12=>000->3fe): first TMS9901: parallel port, a few LEDs, + interrupts + - CRU bits 1-5: UINT1* through UINT5* (jumper connectable to parallel + port or COMINT from tms9902) + - CRU bit 6: KBINT (interrupt request from second tms9901) + - CRU bits 7-15: mirrors P15-P7 + - CRU bits 16-31: P0-P15 (parallel port) + (bits 16, 17, 18 and 19 control LEDs numbered 1, 2, 3 and 4, too) + * bits >200->3ff (R12=>400->7fe): second TMS9901: display, keyboard and + sound + - CRU bits 1-5: KB1*-KB5* (inputs from current keyboard row) + - CRU bit 6: RDATA (tape in) + - CRU bits 7-15: mirrors P15-P7 + - CRU bits 16-19: DIGITSELA-DIGITSELD (select current display digit and + keyboard row) + - CRU bits 20-27: SEGMENTA*-SEGMENTG* and SEGMENTP* (drives digit + segments) + - CRU bit 28: DSPLYTRGR*: used as an alive signal for a watchdog circuit + which turns off the display when the program hangs and + LED segments would continuously emit light + + - bit 29: SHIFTLIGHT (controls shift light) + - bit 30: SPKRDRIVE (controls buzzer) + - bit 31: WDATA (tape out) + * bits >400->5ff (R12=>800->bfe): optional TMS9902 + * bits >600->7ff (R12=>c00->ffe): reserved for expansion + * write 0 to bits >1000->17ff: IDLE: flash IDLE LED + * write 0 to bits >1800->1fff: RSET: not connected, but it is decoded and + hackers can connect any device they like to this pin + * write 1 to bits >0800->0fff: CKON: light FWD LED and activates + DECKCONTROL* signal (part of tape interface) + * write 1 to bits >1000->17ff: CKOF: light off FWD LED and deactivates + DECKCONTROL* signal (part of tape interface) + * write 1 to bits >1800->1fff: LREX: trigger load interrupt + + Keyboard map: see input ports + + Display segment designation: + a + ___ + | | + f| |b + |___| + | g | + e| |c + |___| .p + d +*/ + +static ADDRESS_MAP_START( tm990_189_cru_map, AS_IO, 8, tm990189_state ) + AM_RANGE(0x0000, 0x003f) AM_DEVREAD(TMS9901_0_TAG, tms9901_device, read) /* user I/O tms9901 */ + AM_RANGE(0x0040, 0x006f) AM_DEVREAD(TMS9901_1_TAG, tms9901_device, read) /* system I/O tms9901 */ + AM_RANGE(0x0080, 0x00cf) AM_DEVREAD("tms9902", tms9902_device, cruread) /* optional tms9902 */ + + AM_RANGE(0x0000, 0x01ff) AM_DEVWRITE(TMS9901_0_TAG, tms9901_device, write) /* user I/O tms9901 */ + AM_RANGE(0x0200, 0x03ff) AM_DEVWRITE(TMS9901_1_TAG, tms9901_device, write) /* system I/O tms9901 */ + AM_RANGE(0x0400, 0x05ff) AM_DEVWRITE("tms9902", tms9902_device, cruwrite) /* optional tms9902 */ +ADDRESS_MAP_END + +static MACHINE_CONFIG_START( tm990_189, tm990189_state ) + /* basic machine hardware */ + MCFG_TMS99xx_ADD("maincpu", TMS9980A, 2000000, tm990_189_memmap, tm990_189_cru_map) + MCFG_TMS99xx_EXTOP_HANDLER( WRITE8(tm990189_state, external_operation) ) + + MCFG_MACHINE_START_OVERRIDE(tm990189_state, tm990_189 ) + MCFG_MACHINE_RESET_OVERRIDE(tm990189_state, tm990_189 ) + + /* Video hardware */ + MCFG_DEFAULT_LAYOUT(layout_tm990189) + + /* sound hardware */ + MCFG_SPEAKER_STANDARD_MONO("mono") + MCFG_SOUND_WAVE_ADD(WAVE_TAG, "cassette") + MCFG_SOUND_ROUTE(ALL_OUTPUTS, "mono", 0.25) + MCFG_SOUND_ADD("speaker", SPEAKER_SOUND, 0) + MCFG_SOUND_ROUTE(ALL_OUTPUTS, "mono", 0.50) + + /* Devices */ + MCFG_CASSETTE_ADD( "cassette" ) + + MCFG_DEVICE_ADD(TMS9901_0_TAG, TMS9901, 2000000) + MCFG_TMS9901_P0_HANDLER( WRITELINE( tm990189_state, usr9901_led0_w) ) + MCFG_TMS9901_P1_HANDLER( WRITELINE( tm990189_state, usr9901_led1_w) ) + MCFG_TMS9901_P2_HANDLER( WRITELINE( tm990189_state, usr9901_led2_w) ) + MCFG_TMS9901_P3_HANDLER( WRITELINE( tm990189_state, usr9901_led3_w) ) + MCFG_TMS9901_INTLEVEL_HANDLER( WRITE8( tm990189_state, usr9901_interrupt_callback) ) + + MCFG_DEVICE_ADD(TMS9901_1_TAG, TMS9901, 2000000) + MCFG_TMS9901_READBLOCK_HANDLER( READ8(tm990189_state, sys9901_r) ) + MCFG_TMS9901_P0_HANDLER( WRITELINE( tm990189_state, sys9901_digitsel0_w) ) + MCFG_TMS9901_P1_HANDLER( WRITELINE( tm990189_state, sys9901_digitsel1_w) ) + MCFG_TMS9901_P2_HANDLER( WRITELINE( tm990189_state, sys9901_digitsel2_w) ) + MCFG_TMS9901_P3_HANDLER( WRITELINE( tm990189_state, sys9901_digitsel3_w) ) + MCFG_TMS9901_P4_HANDLER( WRITELINE( tm990189_state, sys9901_segment0_w) ) + MCFG_TMS9901_P5_HANDLER( WRITELINE( tm990189_state, sys9901_segment1_w) ) + MCFG_TMS9901_P6_HANDLER( WRITELINE( tm990189_state, sys9901_segment2_w) ) + MCFG_TMS9901_P7_HANDLER( WRITELINE( tm990189_state, sys9901_segment3_w) ) + MCFG_TMS9901_P8_HANDLER( WRITELINE( tm990189_state, sys9901_segment4_w) ) + MCFG_TMS9901_P9_HANDLER( WRITELINE( tm990189_state, sys9901_segment5_w) ) + MCFG_TMS9901_P10_HANDLER( WRITELINE( tm990189_state, sys9901_segment6_w) ) + MCFG_TMS9901_P11_HANDLER( WRITELINE( tm990189_state, sys9901_segment7_w) ) + MCFG_TMS9901_P12_HANDLER( WRITELINE( tm990189_state, sys9901_dsplytrgr_w) ) + MCFG_TMS9901_P13_HANDLER( WRITELINE( tm990189_state, sys9901_shiftlight_w) ) + MCFG_TMS9901_P14_HANDLER( WRITELINE( tm990189_state, sys9901_spkrdrive_w) ) + MCFG_TMS9901_P15_HANDLER( WRITELINE( tm990189_state, sys9901_tapewdata_w) ) + MCFG_TMS9901_INTLEVEL_HANDLER( WRITE8( tm990189_state, sys9901_interrupt_callback) ) + + MCFG_DEVICE_ADD("tms9902", TMS9902, 2000000) // MZ: needs to be fixed once the RS232 support is complete + MCFG_TMS9902_XMIT_CB(WRITE8(tm990189_state, xmit_callback)) /* called when a character is transmitted */ + MCFG_TM990_189_RS232_ADD("rs232") + MCFG_TIMER_DRIVER_ADD_PERIODIC("display_timer", tm990189_state, display_callback, attotime::from_hz(30)) + // Need to delay the timer, or it will spoil the initial LOAD + // TODO: Fix this, probably inside CPU + MCFG_TIMER_START_DELAY(attotime::from_msec(150)) +MACHINE_CONFIG_END + +static MACHINE_CONFIG_START( tm990_189_v, tm990189_state ) + /* basic machine hardware */ + MCFG_TMS99xx_ADD("maincpu", TMS9980A, 2000000, tm990_189_v_memmap, tm990_189_cru_map) + MCFG_TMS99xx_EXTOP_HANDLER( WRITE8(tm990189_state, external_operation) ) + + MCFG_MACHINE_START_OVERRIDE(tm990189_state, tm990_189_v ) + MCFG_MACHINE_RESET_OVERRIDE(tm990189_state, tm990_189_v ) + + /* video hardware */ + MCFG_DEVICE_ADD( "tms9918", TMS9918, XTAL_10_738635MHz / 2 ) + MCFG_TMS9928A_VRAM_SIZE(0x4000) + MCFG_TMS9928A_SCREEN_ADD_NTSC( "screen" ) + MCFG_SCREEN_UPDATE_DEVICE( "tms9918", tms9918_device, screen_update ) + MCFG_DEFAULT_LAYOUT(layout_tm990189v) + + /* sound hardware */ + MCFG_SPEAKER_STANDARD_MONO("mono") + MCFG_SOUND_WAVE_ADD(WAVE_TAG, "cassette") + MCFG_SOUND_ROUTE(ALL_OUTPUTS, "mono", 0.25) + MCFG_SOUND_ADD("speaker", SPEAKER_SOUND, 0) /* one two-level buzzer */ + MCFG_SOUND_ROUTE(ALL_OUTPUTS, "mono", 0.50) + + /* Devices */ + MCFG_CASSETTE_ADD( "cassette" ) + MCFG_DEVICE_ADD(TMS9901_0_TAG, TMS9901, 2000000) + MCFG_TMS9901_P0_HANDLER( WRITELINE( tm990189_state, usr9901_led0_w) ) + MCFG_TMS9901_P1_HANDLER( WRITELINE( tm990189_state, usr9901_led1_w) ) + MCFG_TMS9901_P2_HANDLER( WRITELINE( tm990189_state, usr9901_led2_w) ) + MCFG_TMS9901_P3_HANDLER( WRITELINE( tm990189_state, usr9901_led3_w) ) + MCFG_TMS9901_INTLEVEL_HANDLER( WRITE8( tm990189_state, usr9901_interrupt_callback) ) + + MCFG_DEVICE_ADD(TMS9901_1_TAG, TMS9901, 2000000) + MCFG_TMS9901_READBLOCK_HANDLER( READ8(tm990189_state, sys9901_r) ) + MCFG_TMS9901_P0_HANDLER( WRITELINE( tm990189_state, sys9901_digitsel0_w) ) + MCFG_TMS9901_P1_HANDLER( WRITELINE( tm990189_state, sys9901_digitsel1_w) ) + MCFG_TMS9901_P2_HANDLER( WRITELINE( tm990189_state, sys9901_digitsel2_w) ) + MCFG_TMS9901_P3_HANDLER( WRITELINE( tm990189_state, sys9901_digitsel3_w) ) + MCFG_TMS9901_P4_HANDLER( WRITELINE( tm990189_state, sys9901_segment0_w) ) + MCFG_TMS9901_P5_HANDLER( WRITELINE( tm990189_state, sys9901_segment1_w) ) + MCFG_TMS9901_P6_HANDLER( WRITELINE( tm990189_state, sys9901_segment2_w) ) + MCFG_TMS9901_P7_HANDLER( WRITELINE( tm990189_state, sys9901_segment3_w) ) + MCFG_TMS9901_P8_HANDLER( WRITELINE( tm990189_state, sys9901_segment4_w) ) + MCFG_TMS9901_P9_HANDLER( WRITELINE( tm990189_state, sys9901_segment5_w) ) + MCFG_TMS9901_P10_HANDLER( WRITELINE( tm990189_state, sys9901_segment6_w) ) + MCFG_TMS9901_P11_HANDLER( WRITELINE( tm990189_state, sys9901_segment7_w) ) + MCFG_TMS9901_P12_HANDLER( WRITELINE( tm990189_state, sys9901_dsplytrgr_w) ) + MCFG_TMS9901_P13_HANDLER( WRITELINE( tm990189_state, sys9901_shiftlight_w) ) + MCFG_TMS9901_P14_HANDLER( WRITELINE( tm990189_state, sys9901_spkrdrive_w) ) + MCFG_TMS9901_P15_HANDLER( WRITELINE( tm990189_state, sys9901_tapewdata_w) ) + MCFG_TMS9901_INTLEVEL_HANDLER( WRITE8( tm990189_state, sys9901_interrupt_callback) ) + + MCFG_DEVICE_ADD("tms9902", TMS9902, 2000000) // MZ: needs to be fixed once the RS232 support is complete + MCFG_TMS9902_XMIT_CB(WRITE8(tm990189_state, xmit_callback)) /* called when a character is transmitted */ + MCFG_TM990_189_RS232_ADD("rs232") + MCFG_TIMER_DRIVER_ADD_PERIODIC("display_timer", tm990189_state, display_callback, attotime::from_hz(30)) + MCFG_TIMER_START_DELAY(attotime::from_msec(150)) +MACHINE_CONFIG_END + + +/* + ROM loading +*/ +ROM_START(990189) + /*CPU memory space*/ + ROM_REGION(0x4000, "maincpu", 0 ) + + /* extra ROM */ + ROM_LOAD("990-469.u32", 0x0800, 0x0800, CRC(08df7edb) SHA1(fa9751fd2e3e5d7ae03819fc9c7099e2ddd9fb53)) + + /* boot ROM */ + ROM_LOAD("990-469.u33", 0x3000, 0x1000, CRC(e9b4ac1b) SHA1(96e88f4cb7a374033cdf3af0dc26ca5b1d55b9f9)) +ROM_END + +ROM_START(990189v) + /*CPU memory space*/ + ROM_REGION(0x4000, "maincpu", 0 ) + + /* extra ROM */ + ROM_LOAD("990-469.u32", 0x0800, 0x0800, CRC(08df7edb) SHA1(fa9751fd2e3e5d7ae03819fc9c7099e2ddd9fb53)) + + /* extension ROM */ + ROM_LOAD_OPTIONAL("demo1000.u13", 0x1000, 0x0800, CRC(c0e16685) SHA1(d0d314134c42fa4682aafbace67f539f67f6ba65)) + + /* extension ROM */ + ROM_LOAD_OPTIONAL("demo1800.u11", 0x1800, 0x0800, CRC(8737dc4b) SHA1(b87da7aa4d3f909e70f885c4b36999cc1abf5764)) + + /* boot ROM */ + ROM_LOAD("990-469.u33", 0x3000, 0x1000, CRC(e9b4ac1b) SHA1(96e88f4cb7a374033cdf3af0dc26ca5b1d55b9f9)) + /*ROM_LOAD("unibasic.bin", 0x3000, 0x1000, CRC(de4d9744))*/ /* older, partial dump of university BASIC */ +ROM_END + +#define JOYSTICK_DELTA 10 +#define JOYSTICK_SENSITIVITY 100 + +static INPUT_PORTS_START(tm990_189) + + PORT_START( "LOADINT ") + PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Load interrupt") PORT_CODE(KEYCODE_PRTSCR) PORT_CHANGED_MEMBER(DEVICE_SELF, tm990189_state, load_interrupt, 1) + + /* 45-key calculator-like alphanumeric keyboard... */ + PORT_START("LINE0") /* row 0 */ + PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Shift") PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1) + PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Sp *") PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ') PORT_CHAR('*') + PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Ret '") PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13) PORT_CHAR('\'') + PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("$ =") PORT_CODE(KEYCODE_STOP) PORT_CHAR('$') PORT_CHAR('=') + PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(", <") PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR('<') + + PORT_START("LINE1") /* row 1 */ + PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("+ (") PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('+') PORT_CHAR('(') + PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("- )") PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR('-') PORT_CHAR(')') + PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("@ /") PORT_CODE(KEYCODE_MINUS) PORT_CHAR('@') PORT_CHAR('/') + PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("> %") PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('>') PORT_CHAR('%') + PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("0 ^") PORT_CODE(KEYCODE_0) PORT_CHAR('0') PORT_CHAR('^') + + PORT_START("LINE2") /* row 2 */ + PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("1 .") PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('.') + PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("2 ;") PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR(';') + PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("3 :") PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR(':') + PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("4 ?") PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR('?') + PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("5 !") PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('!') + + PORT_START("LINE3") /* row 3 */ + PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("6 _") PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('_') + PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("7 \"") PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR('\"') + PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("8 #") PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('#') + PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("9 (ESC)") PORT_CODE(KEYCODE_9) PORT_CHAR('9') + PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("A (SOH)") PORT_CODE(KEYCODE_A) PORT_CHAR('A') + + PORT_START("LINE4") /* row 4 */ + PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("B (STH)") PORT_CODE(KEYCODE_B) PORT_CHAR('B') + PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("C (ETX)") PORT_CODE(KEYCODE_C) PORT_CHAR('C') + PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("D (EOT)") PORT_CODE(KEYCODE_D) PORT_CHAR('D') + PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("E (ENQ)") PORT_CODE(KEYCODE_E) PORT_CHAR('E') + PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F (ACK)") PORT_CODE(KEYCODE_F) PORT_CHAR('F') + + PORT_START("LINE5") /* row 5 */ + PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("G (BEL)") PORT_CODE(KEYCODE_G) PORT_CHAR('G') + PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("H (BS)") PORT_CODE(KEYCODE_H) PORT_CHAR('H') + PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("I (HT)") PORT_CODE(KEYCODE_I) PORT_CHAR('I') + PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("J (LF)") PORT_CODE(KEYCODE_J) PORT_CHAR('J') + PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("K (VT)") PORT_CODE(KEYCODE_K) PORT_CHAR('K') + + PORT_START("LINE6") /* row 6 */ + PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("L (FF)") PORT_CODE(KEYCODE_L) PORT_CHAR('L') + PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("M (DEL)") PORT_CODE(KEYCODE_M) PORT_CHAR('M') + PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("N (SO)") PORT_CODE(KEYCODE_N) PORT_CHAR('N') + PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("O (SI)") PORT_CODE(KEYCODE_O) PORT_CHAR('O') + PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("P (DLE)") PORT_CODE(KEYCODE_P) PORT_CHAR('P') + + PORT_START("LINE7") /* row 7 */ + PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Q (DC1)") PORT_CODE(KEYCODE_Q) PORT_CHAR('Q') + PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("R (DC2)") PORT_CODE(KEYCODE_R) PORT_CHAR('R') + PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("S (DC3)") PORT_CODE(KEYCODE_S) PORT_CHAR('S') + PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("T (DC4)") PORT_CODE(KEYCODE_T) PORT_CHAR('T') + PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("U (NAK)") PORT_CODE(KEYCODE_U) PORT_CHAR('U') + + PORT_START("LINE8") /* row 8 */ + PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("V <-D") PORT_CODE(KEYCODE_V) PORT_CHAR('V') + PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("W (ETB)") PORT_CODE(KEYCODE_W) PORT_CHAR('W') + PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("X (CAN)") PORT_CODE(KEYCODE_X) PORT_CHAR('X') + PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Y (EM)") PORT_CODE(KEYCODE_Y) PORT_CHAR('Y') + PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Z ->D") PORT_CODE(KEYCODE_Z) PORT_CHAR('Z') + + /* analog joysticks (video board only) */ + + PORT_START("BUTTONS") /* joystick 1 & 2 buttons */ + PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_BUTTON1) PORT_PLAYER(1) + PORT_BIT(0x0020, IP_ACTIVE_HIGH, IPT_BUTTON1) PORT_PLAYER(2) + + PORT_START("JOY1_X") /* joystick 1, X axis */ + PORT_BIT( 0x3ff, 0x1aa, IPT_AD_STICK_X) PORT_SENSITIVITY(JOYSTICK_SENSITIVITY) PORT_KEYDELTA(JOYSTICK_DELTA) PORT_MINMAX(0xd2,0x282 ) PORT_PLAYER(1) + + PORT_START("JOY1_Y") /* joystick 1, Y axis */ + PORT_BIT( 0x3ff, 0x1aa, IPT_AD_STICK_Y) PORT_SENSITIVITY(JOYSTICK_SENSITIVITY) PORT_KEYDELTA(JOYSTICK_DELTA) PORT_MINMAX(0xd2,0x282 ) PORT_PLAYER(1) PORT_REVERSE + + PORT_START("JOY2_X") /* joystick 2, X axis */ + PORT_BIT( 0x3ff, 0x180, IPT_AD_STICK_X) PORT_SENSITIVITY(JOYSTICK_SENSITIVITY) PORT_KEYDELTA(JOYSTICK_DELTA) PORT_MINMAX(0xd2,0x180 ) PORT_PLAYER(2) + + PORT_START("JOY2_Y") /* joystick 2, Y axis */ + PORT_BIT( 0x3ff, 0x1aa, IPT_AD_STICK_Y) PORT_SENSITIVITY(JOYSTICK_SENSITIVITY) PORT_KEYDELTA(JOYSTICK_DELTA) PORT_MINMAX(0xd2,0x282 ) PORT_PLAYER(2) PORT_REVERSE +INPUT_PORTS_END + +/* YEAR NAME PARENT COMPAT MACHINE INPUT INIT COMPANY FULLNAME */ +COMP( 1978, 990189, 0, 0, tm990_189, tm990_189, driver_device, 0, "Texas Instruments", "TM 990/189 University Board microcomputer" , 0) +COMP( 1980, 990189v, 990189, 0, tm990_189_v, tm990_189, driver_device, 0, "Texas Instruments", "TM 990/189 University Board microcomputer with Video Board Interface" , 0) |