1 // license:BSD-3-Clause
2 // copyright-holders:hap, Tim Lindner
3 /*****************************************************************************
4 
5   Texas Instruments TMS7000
6 
7   TODO:
8   - dump CROM and emulate cpu at microinstruction level
9   - memory modes with IOCNT0, currently always running in faked full expansion mode
10   - timer event counter mode (timer control register, bit 6)
11   - TMS70x1/2 serial port and timer 3
12   - TMS70C46 DOCK-BUS comms with external pins
13   - TMS70C46 external memory mode is via "E" bus instead of configuring IOCNT0
14   - TMS70C46 clock divider
15   - TMS70C46 INT3 on keypress
16   - when they're needed, add TMS70Cx2, TMS7742, TMS77C82, SE70xxx
17 
18 *****************************************************************************/
19 
20 #include "emu.h"
21 #include "tms7000.h"
22 #include "7000dasm.h"
23 
24 // TMS7000 is the most basic one, 128 bytes internal RAM and no internal ROM.
25 // TMS7020 and TMS7040 are same, but with 2KB and 4KB internal ROM respectively.
26 DEFINE_DEVICE_TYPE(TMS7000, tms7000_device, "tms7000", "Texas Instruments TMS7000")
27 DEFINE_DEVICE_TYPE(TMS7020, tms7020_device, "tms7020", "Texas Instruments TMS7020")
28 DEFINE_DEVICE_TYPE(TMS7040, tms7040_device, "tms7040", "Texas Instruments TMS7040")
29 
30 // Exelvision (spinoff of TI) TMS7020 added one custom opcode.
31 DEFINE_DEVICE_TYPE(TMS7020_EXL, tms7020_exl_device, "tms7020_exl", "Texas Instruments TMS7020 (Exelvision)")
32 
33 // CMOS devices biggest difference in a 'real world' setting is that the power
34 // requirements are much lower. This obviously has no use in software emulation.
35 DEFINE_DEVICE_TYPE(TMS70C00, tms70c00_device, "tms70c00", "Texas Instruments TMS70C00")
36 DEFINE_DEVICE_TYPE(TMS70C20, tms70c20_device, "tms70c20", "Texas Instruments TMS70C20")
37 DEFINE_DEVICE_TYPE(TMS70C40, tms70c40_device, "tms70c40", "Texas Instruments TMS70C40")
38 
39 // TMS70x1 features more peripheral I/O, the main addition being a serial port.
40 // TMS70x2 is the same, just with twice more RAM (256 bytes)
41 DEFINE_DEVICE_TYPE(TMS7001, tms7001_device, "tms7001", "Texas Instruments TMS7001")
42 DEFINE_DEVICE_TYPE(TMS7041, tms7041_device, "tms7041", "Texas Instruments TMS7041")
43 DEFINE_DEVICE_TYPE(TMS7002, tms7002_device, "tms7002", "Texas Instruments TMS7002")
44 DEFINE_DEVICE_TYPE(TMS7042, tms7042_device, "tms7042", "Texas Instruments TMS7042")
45 
46 // TMS70C46 is literally a shell around a TMS70C40, with support for external
47 // memory bus, auto external clock divider on slow memory, and wake-up on keypress.
48 DEFINE_DEVICE_TYPE(TMS70C46, tms70c46_device, "tms70c46", "Texas Instruments TMS70C46")
49 
50 // TMS70Cx2 is an update to TMS70x2 with some extra features. Due to some changes
51 // in peripheral file I/O, it is not backward compatible to TMS70x2.
52 
53 
54 // internal memory maps
tms7000_mem(address_map & map)55 void tms7000_device::tms7000_mem(address_map &map)
56 {
57 	map(0x0000, 0x007f).ram().share("rf"); // 128 bytes internal RAM
58 	map(0x0080, 0x00ff).rw(FUNC(tms7000_device::tms7000_unmapped_rf_r), FUNC(tms7000_device::tms7000_unmapped_rf_w));
59 	map(0x0100, 0x010b).rw(FUNC(tms7000_device::tms7000_pf_r), FUNC(tms7000_device::tms7000_pf_w));
60 	map(0x0104, 0x0105).nopw(); // no port A write or ddr
61 }
62 
tms7001_mem(address_map & map)63 void tms7000_device::tms7001_mem(address_map &map)
64 {
65 	map(0x0000, 0x007f).ram().share("rf"); // 128 bytes internal RAM
66 	map(0x0080, 0x00ff).rw(FUNC(tms7000_device::tms7000_unmapped_rf_r), FUNC(tms7000_device::tms7000_unmapped_rf_w));
67 	map(0x0100, 0x010b).rw(FUNC(tms7000_device::tms7000_pf_r), FUNC(tms7000_device::tms7000_pf_w));
68 	map(0x0110, 0x0117).rw(FUNC(tms7000_device::tms7002_pf_r), FUNC(tms7000_device::tms7002_pf_w));
69 }
70 
tms7002_mem(address_map & map)71 void tms7000_device::tms7002_mem(address_map &map)
72 {
73 	map(0x0000, 0x00ff).ram().share("rf"); // 256 bytes internal RAM
74 	map(0x0100, 0x010b).rw(FUNC(tms7000_device::tms7000_pf_r), FUNC(tms7000_device::tms7000_pf_w));
75 	map(0x0110, 0x0117).rw(FUNC(tms7000_device::tms7002_pf_r), FUNC(tms7000_device::tms7002_pf_w));
76 }
77 
tms7020_mem(address_map & map)78 void tms7000_device::tms7020_mem(address_map &map)
79 {
80 	tms7000_mem(map);
81 	map(0xf800, 0xffff).rom().region(DEVICE_SELF, 0); // 2kB internal ROM
82 }
83 
tms7040_mem(address_map & map)84 void tms7000_device::tms7040_mem(address_map &map)
85 {
86 	tms7000_mem(map);
87 	map(0xf000, 0xffff).rom().region(DEVICE_SELF, 0); // 4kB internal ROM
88 }
89 
tms7041_mem(address_map & map)90 void tms7000_device::tms7041_mem(address_map &map)
91 {
92 	tms7001_mem(map);
93 	map(0xf000, 0xffff).rom().region(DEVICE_SELF, 0);
94 }
95 
tms7042_mem(address_map & map)96 void tms7000_device::tms7042_mem(address_map &map)
97 {
98 	tms7002_mem(map);
99 	map(0xf000, 0xffff).rom().region(DEVICE_SELF, 0);
100 }
101 
tms70c46_mem(address_map & map)102 void tms70c46_device::tms70c46_mem(address_map &map)
103 {
104 	tms7040_mem(map);
105 	map(0x010c, 0x010c).rw(FUNC(tms70c46_device::e_bus_data_r), FUNC(tms70c46_device::e_bus_data_w));
106 	map(0x010d, 0x010d).noprw(); // ? always writes $FF before checking keyboard... maybe INT3 ack?
107 	map(0x010e, 0x010e).rw(FUNC(tms70c46_device::dockbus_data_r), FUNC(tms70c46_device::dockbus_data_w));
108 	map(0x010f, 0x010f).rw(FUNC(tms70c46_device::dockbus_status_r), FUNC(tms70c46_device::dockbus_status_w));
109 	map(0x0118, 0x0118).rw(FUNC(tms70c46_device::control_r), FUNC(tms70c46_device::control_w));
110 }
111 
112 
113 // device definitions
tms7000_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)114 tms7000_device::tms7000_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
115 	tms7000_device(mconfig, TMS7000, tag, owner, clock, address_map_constructor(FUNC(tms7000_device::tms7000_mem), this), 0)
116 {
117 }
118 
tms7000_device(const machine_config & mconfig,device_type type,const char * tag,device_t * owner,uint32_t clock,address_map_constructor internal,uint32_t info_flags)119 tms7000_device::tms7000_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock, address_map_constructor internal, uint32_t info_flags) :
120 	cpu_device(mconfig, type, tag, owner, clock),
121 	m_program_config("program", ENDIANNESS_BIG, 8, 16, 0, internal),
122 	m_port_in_cb(*this),
123 	m_port_out_cb(*this),
124 	m_info_flags(info_flags),
125 	m_divider(2)
126 {
127 }
128 
tms7020_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)129 tms7020_device::tms7020_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
130 	tms7000_device(mconfig, TMS7020, tag, owner, clock, address_map_constructor(FUNC(tms7020_device::tms7020_mem), this), 0)
131 {
132 }
133 
tms7020_exl_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)134 tms7020_exl_device::tms7020_exl_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
135 	tms7000_device(mconfig, TMS7020_EXL, tag, owner, clock, address_map_constructor(FUNC(tms7020_exl_device::tms7020_mem), this), 0)
136 {
137 }
138 
tms7040_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)139 tms7040_device::tms7040_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
140 	tms7000_device(mconfig, TMS7040, tag, owner, clock, address_map_constructor(FUNC(tms7040_device::tms7040_mem), this), 0)
141 {
142 }
143 
tms70c00_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)144 tms70c00_device::tms70c00_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
145 	tms7000_device(mconfig, TMS70C00, tag, owner, clock, address_map_constructor(FUNC(tms70c00_device::tms7000_mem), this), CHIP_IS_CMOS)
146 {
147 }
148 
tms70c20_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)149 tms70c20_device::tms70c20_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
150 	tms7000_device(mconfig, TMS70C20, tag, owner, clock, address_map_constructor(FUNC(tms70c20_device::tms7020_mem), this), CHIP_IS_CMOS)
151 {
152 }
153 
tms70c40_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)154 tms70c40_device::tms70c40_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
155 	tms7000_device(mconfig, TMS70C40, tag, owner, clock, address_map_constructor(FUNC(tms70c40_device::tms7040_mem), this), CHIP_IS_CMOS)
156 {
157 }
158 
tms7001_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)159 tms7001_device::tms7001_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
160 	tms7000_device(mconfig, TMS7001, tag, owner, clock, address_map_constructor(FUNC(tms7001_device::tms7001_mem), this), CHIP_FAMILY_70X2)
161 {
162 }
163 
tms7041_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)164 tms7041_device::tms7041_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
165 	tms7000_device(mconfig, TMS7041, tag, owner, clock, address_map_constructor(FUNC(tms7041_device::tms7041_mem), this), CHIP_FAMILY_70X2)
166 {
167 }
168 
tms7002_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)169 tms7002_device::tms7002_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
170 	tms7000_device(mconfig, TMS7002, tag, owner, clock, address_map_constructor(FUNC(tms7002_device::tms7002_mem), this), CHIP_FAMILY_70X2)
171 {
172 }
173 
tms7042_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)174 tms7042_device::tms7042_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
175 	tms7000_device(mconfig, TMS7042, tag, owner, clock, address_map_constructor(FUNC(tms7042_device::tms7042_mem), this), CHIP_FAMILY_70X2)
176 {
177 }
178 
tms70c46_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)179 tms70c46_device::tms70c46_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
180 	tms7000_device(mconfig, TMS70C46, tag, owner, clock, address_map_constructor(FUNC(tms70c46_device::tms70c46_mem), this), CHIP_IS_CMOS)
181 {
182 }
183 
memory_space_config() const184 device_memory_interface::space_config_vector tms7000_device::memory_space_config() const
185 {
186 	return space_config_vector {
187 		std::make_pair(AS_PROGRAM, &m_program_config)
188 	};
189 }
190 
191 
192 //-------------------------------------------------
193 //  device_start - device-specific startup
194 //-------------------------------------------------
195 
device_start()196 void tms7000_device::device_start()
197 {
198 	// init/zerofill
199 	space(AS_PROGRAM).cache(m_cache);
200 	space(AS_PROGRAM).specific(m_program);
201 
202 	set_icountptr(m_icount);
203 
204 	m_irq_state[TMS7000_INT1_LINE] = false;
205 	m_irq_state[TMS7000_INT3_LINE] = false;
206 
207 	m_port_in_cb.resolve_all_safe(0xff);
208 	m_port_out_cb.resolve_all_safe();
209 
210 	m_idle_state = false;
211 	m_idle_halt = false;
212 	m_pc = 0;
213 	m_sp = 0;
214 	m_sr = 0;
215 	m_op = 0;
216 
217 	memset(m_io_control, 0, 3);
218 
219 	memset(m_port_latch, 0, 4);
220 	memset(m_port_ddr, 0, 4);
221 	m_port_ddr[1] = 0xff; // !
222 
223 	for (int tmr = 0; tmr < 2; tmr++)
224 	{
225 		m_timer_handle[tmr] = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(tms7000_device::simple_timer_cb), this));
226 		m_timer_handle[tmr]->adjust(attotime::never, tmr);
227 
228 		m_timer_data[tmr] = 0;
229 		m_timer_control[tmr] = 0;
230 		m_timer_decrementer[tmr] = 0;
231 		m_timer_prescaler[tmr] = 0;
232 		m_timer_capture_latch[tmr] = 0;
233 	}
234 
235 	// register for savestates
236 	save_item(NAME(m_irq_state));
237 	save_item(NAME(m_idle_state));
238 	save_item(NAME(m_pc));
239 	save_item(NAME(m_sp));
240 	save_item(NAME(m_sr));
241 	save_item(NAME(m_op));
242 
243 	save_item(NAME(m_io_control));
244 	save_item(NAME(m_port_latch));
245 	save_item(NAME(m_port_ddr));
246 	save_item(NAME(m_timer_data));
247 	save_item(NAME(m_timer_control));
248 	save_item(NAME(m_timer_decrementer));
249 	save_item(NAME(m_timer_prescaler));
250 	save_item(NAME(m_timer_capture_latch));
251 
252 	// register for debugger
253 	state_add(TMS7000_PC, "PC", m_pc).formatstr("%04X");
254 	state_add(TMS7000_SP, "SP", m_sp).formatstr("%02X");
255 	state_add(TMS7000_ST, "ST", m_sr).formatstr("%02X");
256 
257 	uint8_t *rf = static_cast<uint8_t *>(memshare("rf")->ptr());
258 	state_add(TMS7000_A, "A", rf[0]).formatstr("%02X");
259 	state_add(TMS7000_B, "B", rf[1]).formatstr("%02X");
260 
261 	state_add(STATE_GENPC, "GENPC", m_pc).formatstr("%04X").noshow();
262 	state_add(STATE_GENPCBASE, "CURPC", m_pc).formatstr("%04X").noshow();
263 	state_add(STATE_GENSP, "GENSP", m_sp).formatstr("%02X").noshow();
264 	state_add(STATE_GENFLAGS, "GENFLAGS", m_sr).formatstr("%8s").noshow();
265 }
266 
state_string_export(const device_state_entry & entry,std::string & str) const267 void tms7000_device::state_string_export(const device_state_entry &entry, std::string &str) const
268 {
269 	switch (entry.index())
270 	{
271 		case STATE_GENFLAGS:
272 			str = string_format("%c%c%c%c%c%c%c%c",
273 				m_sr & 0x80 ? 'C':'c',
274 				m_sr & 0x40 ? 'N':'n',
275 				m_sr & 0x20 ? 'Z':'z',
276 				m_sr & 0x10 ? 'I':'i',
277 				m_sr & 0x08 ? '?':'.',
278 				m_sr & 0x04 ? '?':'.',
279 				m_sr & 0x02 ? '?':'.',
280 				m_sr & 0x01 ? '?':'.'
281 			);
282 			break;
283 
284 		default: break;
285 	}
286 }
287 
create_disassembler()288 std::unique_ptr<util::disasm_interface> tms7000_device::create_disassembler()
289 {
290 	return std::make_unique<tms7000_disassembler>();
291 }
292 
293 
294 //-------------------------------------------------
295 //  device_reset - device-specific reset
296 //-------------------------------------------------
297 
device_reset()298 void tms7000_device::device_reset()
299 {
300 	if (m_idle_state)
301 	{
302 		m_pc++;
303 		m_idle_state = false;
304 	}
305 
306 	// while _RESET is asserted:
307 	// clear ports
308 	write_p(0x04, 0xff); // port a
309 	write_p(0x06, 0xff); // port b
310 
311 	write_p(0x05, 0x00); // ddr a
312 	write_p(0x09, 0x00); // ddr c
313 	write_p(0x0b, 0x00); // ddr d
314 
315 	if (!chip_is_cmos())
316 	{
317 		write_p(0x08, 0xff); // port c
318 		write_p(0x0a, 0xff); // port d
319 	}
320 
321 	// when _RESET goes inactive (0 to 1)
322 	m_sr = 0;
323 
324 	write_p(0x00, 0x00); // IOCNT0
325 	if (chip_is_family_70x2())
326 		write_p(0x10, 0x00); // IOCNT1
327 
328 	m_sp = 0xff;
329 	m_op = 0xff;
330 	execute_one(m_op);
331 	m_icount -= 3; // 17 total
332 }
333 
334 
335 //-------------------------------------------------
336 //  interrupts
337 //-------------------------------------------------
338 
execute_set_input(int extline,int state)339 void tms7000_device::execute_set_input(int extline, int state)
340 {
341 	if (extline != TMS7000_INT1_LINE && extline != TMS7000_INT3_LINE)
342 		return;
343 
344 	bool irqstate = (state == CLEAR_LINE) ? false : true;
345 
346 	// reverse polarity (TMS70cx2-only)
347 	if (m_io_control[2] & (0x01 << (4 * extline)))
348 		irqstate = !irqstate;
349 
350 	if (m_irq_state[extline] != irqstate)
351 	{
352 		m_irq_state[extline] = irqstate;
353 
354 		// set/clear internal irq flag
355 		flag_ext_interrupt(extline);
356 
357 		if (m_irq_state[extline])
358 		{
359 			// latch timer 1 on INT3
360 			if (extline == TMS7000_INT3_LINE)
361 				m_timer_capture_latch[0] = m_timer_decrementer[0];
362 
363 			// on TMS70cx2, latch timer 2 on INT1
364 			if (extline == TMS7000_INT1_LINE && chip_is_family_70cx2())
365 				m_timer_capture_latch[1] = m_timer_decrementer[1];
366 
367 			// clear external if it's edge-triggered (TMS70cx2-only)
368 			if (m_io_control[2] & (0x02 << (4 * extline)))
369 				m_irq_state[extline] = false;
370 
371 			check_interrupts();
372 		}
373 	}
374 }
375 
flag_ext_interrupt(int extline)376 void tms7000_device::flag_ext_interrupt(int extline)
377 {
378 	if (extline != TMS7000_INT1_LINE && extline != TMS7000_INT3_LINE)
379 		return;
380 
381 	// set/clear for pending external interrupt
382 	if (m_irq_state[extline])
383 		m_io_control[0] |= (0x02 << (4 * extline));
384 	else
385 		m_io_control[0] &= ~(0x02 << (4 * extline));
386 }
387 
check_interrupts()388 void tms7000_device::check_interrupts()
389 {
390 	// global interrupt bit
391 	if (!(m_sr & SR_I))
392 		return;
393 
394 	// check for and handle interrupt
395 	for (int irqline = 0; irqline < 5; irqline++)
396 	{
397 		// INT 1,2,3 are in IOCNT0 d0-d5
398 		// INT 4,5 are in IOCNT1 d0-d3
399 		int shift = (irqline > 2) ? irqline * 2 - 6 : irqline * 2;
400 		if ((m_io_control[irqline > 2] >> shift & 3) == 3)
401 		{
402 			// ack
403 			m_io_control[irqline > 2] &= ~(0x02 << shift);
404 			if (irqline == 0 || irqline == 2)
405 				flag_ext_interrupt(irqline / 2);
406 
407 			do_interrupt(irqline);
408 			return;
409 		}
410 	}
411 }
412 
do_interrupt(int irqline)413 void tms7000_device::do_interrupt(int irqline)
414 {
415 	if (m_idle_state)
416 	{
417 		m_icount -= 17;
418 		m_pc++;
419 		m_idle_state = false;
420 	}
421 	else
422 		m_icount -= 19;
423 
424 	push8(m_sr);
425 	push16(m_pc);
426 	m_sr = 0;
427 	m_pc = read_mem16(0xfffc - irqline * 2);
428 
429 	standard_irq_callback(irqline);
430 }
431 
432 
433 //-------------------------------------------------
434 //  timers
435 //-------------------------------------------------
436 
timer_run(int tmr)437 void tms7000_device::timer_run(int tmr)
438 {
439 	m_timer_prescaler[tmr] = m_timer_control[tmr] & 0x1f;
440 
441 	// run automatic timer if source is internal
442 	if ((m_timer_control[tmr] & 0xe0) == 0x80)
443 	{
444 		attotime period = attotime::from_hz(clock()) * 16 * (m_timer_prescaler[tmr] + 1); // fOSC/16 - fOSC is freq _before_ internal clockdivider
445 		m_timer_handle[tmr]->adjust(period, tmr);
446 	}
447 }
448 
timer_reload(int tmr)449 void tms7000_device::timer_reload(int tmr)
450 {
451 	// stop possible running timer
452 	m_timer_handle[tmr]->adjust(attotime::never, tmr);
453 
454 	if (m_timer_control[tmr] & 0x80)
455 	{
456 		m_timer_decrementer[tmr] = m_timer_data[tmr];
457 		timer_run(tmr);
458 	}
459 }
460 
timer_tick_pre(int tmr)461 void tms7000_device::timer_tick_pre(int tmr)
462 {
463 	// timer prescaler underflow
464 	if (--m_timer_prescaler[tmr] < 0)
465 	{
466 		m_timer_prescaler[tmr] = m_timer_control[tmr] & 0x1f;
467 		timer_tick_low(tmr);
468 	}
469 }
470 
timer_tick_low(int tmr)471 void tms7000_device::timer_tick_low(int tmr)
472 {
473 	// timer decrementer underflow
474 	if (--m_timer_decrementer[tmr] < 0)
475 	{
476 		timer_reload(tmr);
477 
478 		// set INT2/INT5
479 		m_io_control[tmr] |= 0x08;
480 
481 		// cascaded timer
482 		if (tmr == 0 && (m_timer_control[1] & 0xa0) == 0xa0)
483 			timer_tick_pre(tmr + 1);
484 	}
485 }
486 
TIMER_CALLBACK_MEMBER(tms7000_device::simple_timer_cb)487 TIMER_CALLBACK_MEMBER(tms7000_device::simple_timer_cb)
488 {
489 	int tmr = param;
490 
491 	// tick and restart timer
492 	timer_tick_low(tmr);
493 	timer_run(tmr);
494 }
495 
496 
497 //-------------------------------------------------
498 //  peripheral file - read/write internal ports
499 //  note: TMS7000 family is from $00 to $0b, TMS7002 family adds $10 to $17
500 //-------------------------------------------------
501 
tms7000_pf_r(offs_t offset)502 uint8_t tms7000_device::tms7000_pf_r(offs_t offset)
503 {
504 	switch (offset)
505 	{
506 		// i/o control
507 		case 0x00: case 0x10:
508 			return m_io_control[offset >> 4];
509 
510 		// timer 1/2 data
511 		case 0x02: case 0x12:
512 			// current decrementer value
513 			return m_timer_decrementer[offset >> 4];
514 
515 		// timer 1 control
516 		case 0x03:
517 			// timer capture (latched by INT3)
518 			return m_timer_capture_latch[0];
519 
520 		// port data
521 		case 0x04: case 0x06: case 0x08: case 0x0a:
522 		{
523 			// note: port B is write-only, reading it returns the output value as if ddr is 0xff
524 			int port = offset / 2 - 2;
525 			if (!machine().side_effects_disabled())
526 				return (m_port_in_cb[port]() & ~m_port_ddr[port]) | (m_port_latch[port] & m_port_ddr[port]);
527 			break;
528 		}
529 
530 		// port direction (note: TMS7000 doesn't support it for port A)
531 		case 0x05: case 0x09: case 0x0b:
532 			return m_port_ddr[offset / 2 - 2];
533 
534 		default:
535 			if (!machine().side_effects_disabled())
536 				logerror("'%s' (%04X): tms7000_pf_r @ $%04x\n", tag(), m_pc, offset);
537 			break;
538 	}
539 
540 	return 0;
541 }
542 
tms7000_pf_w(offs_t offset,uint8_t data)543 void tms7000_device::tms7000_pf_w(offs_t offset, uint8_t data)
544 {
545 	switch (offset)
546 	{
547 		// i/o control (IOCNT0)
548 		case 0x00:
549 			// d0,d2,d4: INT1,2,3 enable
550 			// d1,d3,d5: INT1,2,3 flag (write 1 to clear flag)
551 			// d6-d7: memory mode (currently not implemented)
552 			m_io_control[0] = (m_io_control[0] & (~data & 0x2a)) | (data & 0xd5);
553 
554 			// possibly need to reactivate flags
555 			if (data & 0x02)
556 				flag_ext_interrupt(TMS7000_INT1_LINE);
557 			if (data & 0x20)
558 				flag_ext_interrupt(TMS7000_INT3_LINE);
559 
560 			check_interrupts();
561 			break;
562 
563 		// i/o control (IOCNT1)
564 		case 0x10:
565 			// d0,d2: INT4,5 enable
566 			// d1,d3: INT4,5 flag (write 1 to clear flag)
567 			m_io_control[1] = (m_io_control[1] & (~data & 0x0a)) | (data & 0x05);
568 			check_interrupts();
569 			break;
570 
571 		// timer 1/2 data
572 		case 0x02: case 0x12:
573 			// decrementer reload value
574 			m_timer_data[offset >> 4] = data;
575 			break;
576 
577 		// timer 1/2 control
578 		case 0x03:
579 			// d5: t1: cmos low-power mode when IDLE opcode is used (not emulated)
580 			// 0(normal), or 1(halt) - indicating it can only wake up with RESET or external interrupt
581 			if (chip_is_cmos())
582 			{
583 				m_idle_halt = (data & 0x20) ? true : false;
584 				if (m_idle_halt)
585 					logerror("%s: CMOS low-power halt mode enabled\n", tag());
586 			}
587 			data &= ~0x20;
588 		case 0x13:
589 			// d0-d4: prescaler reload value
590 			// d5: t2: cascade from t1
591 			// d6: source (internal/external)
592 			// d7: stop/start timer
593 			m_timer_control[offset >> 4] = data;
594 			timer_reload(offset >> 4);
595 
596 			// on cmos chip, clear INT2/INT5 as well
597 			if (~data & 0x80 && chip_is_cmos())
598 				m_io_control[offset >> 4] &= ~0x08;
599 
600 			break;
601 
602 		// port data (note: TMS7000 doesn't support it for port A)
603 		case 0x04: case 0x06: case 0x08: case 0x0a:
604 		{
605 			// note: in memory expansion modes, some port output pins are used for memory strobes.
606 			// this is currently ignored, since port writes will always be visible externally on peripheral expansion anyway.
607 			int port = offset / 2 - 2;
608 			m_port_out_cb[port](data & m_port_ddr[port]);
609 			m_port_latch[port] = data;
610 			break;
611 		}
612 
613 		// port direction (note: TMS7000 doesn't support it for port A)
614 		case 0x05: case 0x09: case 0x0b:
615 			// note: changing port direction does not change(refresh) the output pins
616 			m_port_ddr[offset / 2 - 2] = data;
617 			break;
618 
619 		default:
620 			logerror("'%s' (%04X): tms7000_pf_w @ $%04x = $%02x\n", tag(), m_pc, offset, data);
621 			break;
622 	}
623 }
624 
625 
626 //-------------------------------------------------
627 //  execute
628 //-------------------------------------------------
629 
execute_run()630 void tms7000_device::execute_run()
631 {
632 	check_interrupts();
633 
634 	do
635 	{
636 		debugger_instruction_hook(m_pc);
637 
638 		m_op = m_cache.read_byte(m_pc++);
639 		execute_one(m_op);
640 	} while (m_icount > 0);
641 }
642 
execute_one(uint8_t op)643 void tms7000_device::execute_one(uint8_t op)
644 {
645 	switch (op)
646 	{
647 		case 0x00: nop(); break;
648 		case 0x01: idle(); break;
649 		case 0x05: eint(); break;
650 		case 0x06: dint(); break;
651 		case 0x07: setc(); break;
652 		case 0x08: pop_st(); break;
653 		case 0x09: stsp(); break;
654 		case 0x0a: rets(); break;
655 		case 0x0b: reti(); break;
656 		case 0x0d: ldsp(); break;
657 		case 0x0e: push_st(); break;
658 
659 		case 0x12: am_r2a(&tms7000_device::op_mov); break;
660 		case 0x13: am_r2a(&tms7000_device::op_and); break;
661 		case 0x14: am_r2a(&tms7000_device::op_or); break;
662 		case 0x15: am_r2a(&tms7000_device::op_xor); break;
663 		case 0x16: am_r2a(&tms7000_device::op_btjo); break;
664 		case 0x17: am_r2a(&tms7000_device::op_btjz); break;
665 		case 0x18: am_r2a(&tms7000_device::op_add); break;
666 		case 0x19: am_r2a(&tms7000_device::op_adc); break;
667 		case 0x1a: am_r2a(&tms7000_device::op_sub); break;
668 		case 0x1b: am_r2a(&tms7000_device::op_sbb); break;
669 		case 0x1c: am_r2a(&tms7000_device::op_mpy); break;
670 		case 0x1d: am_r2a(&tms7000_device::op_cmp); break;
671 		case 0x1e: am_r2a(&tms7000_device::op_dac); break;
672 		case 0x1f: am_r2a(&tms7000_device::op_dsb); break;
673 
674 		case 0x22: am_i2a(&tms7000_device::op_mov); break;
675 		case 0x23: am_i2a(&tms7000_device::op_and); break;
676 		case 0x24: am_i2a(&tms7000_device::op_or); break;
677 		case 0x25: am_i2a(&tms7000_device::op_xor); break;
678 		case 0x26: am_i2a(&tms7000_device::op_btjo); break;
679 		case 0x27: am_i2a(&tms7000_device::op_btjz); break;
680 		case 0x28: am_i2a(&tms7000_device::op_add); break;
681 		case 0x29: am_i2a(&tms7000_device::op_adc); break;
682 		case 0x2a: am_i2a(&tms7000_device::op_sub); break;
683 		case 0x2b: am_i2a(&tms7000_device::op_sbb); break;
684 		case 0x2c: am_i2a(&tms7000_device::op_mpy); break;
685 		case 0x2d: am_i2a(&tms7000_device::op_cmp); break;
686 		case 0x2e: am_i2a(&tms7000_device::op_dac); break;
687 		case 0x2f: am_i2a(&tms7000_device::op_dsb); break;
688 
689 		case 0x32: am_r2b(&tms7000_device::op_mov); break;
690 		case 0x33: am_r2b(&tms7000_device::op_and); break;
691 		case 0x34: am_r2b(&tms7000_device::op_or); break;
692 		case 0x35: am_r2b(&tms7000_device::op_xor); break;
693 		case 0x36: am_r2b(&tms7000_device::op_btjo); break;
694 		case 0x37: am_r2b(&tms7000_device::op_btjz); break;
695 		case 0x38: am_r2b(&tms7000_device::op_add); break;
696 		case 0x39: am_r2b(&tms7000_device::op_adc); break;
697 		case 0x3a: am_r2b(&tms7000_device::op_sub); break;
698 		case 0x3b: am_r2b(&tms7000_device::op_sbb); break;
699 		case 0x3c: am_r2b(&tms7000_device::op_mpy); break;
700 		case 0x3d: am_r2b(&tms7000_device::op_cmp); break;
701 		case 0x3e: am_r2b(&tms7000_device::op_dac); break;
702 		case 0x3f: am_r2b(&tms7000_device::op_dsb); break;
703 
704 		case 0x42: am_r2r(&tms7000_device::op_mov); break;
705 		case 0x43: am_r2r(&tms7000_device::op_and); break;
706 		case 0x44: am_r2r(&tms7000_device::op_or); break;
707 		case 0x45: am_r2r(&tms7000_device::op_xor); break;
708 		case 0x46: am_r2r(&tms7000_device::op_btjo); break;
709 		case 0x47: am_r2r(&tms7000_device::op_btjz); break;
710 		case 0x48: am_r2r(&tms7000_device::op_add); break;
711 		case 0x49: am_r2r(&tms7000_device::op_adc); break;
712 		case 0x4a: am_r2r(&tms7000_device::op_sub); break;
713 		case 0x4b: am_r2r(&tms7000_device::op_sbb); break;
714 		case 0x4c: am_r2r(&tms7000_device::op_mpy); break;
715 		case 0x4d: am_r2r(&tms7000_device::op_cmp); break;
716 		case 0x4e: am_r2r(&tms7000_device::op_dac); break;
717 		case 0x4f: am_r2r(&tms7000_device::op_dsb); break;
718 
719 		case 0x52: am_i2b(&tms7000_device::op_mov); break;
720 		case 0x53: am_i2b(&tms7000_device::op_and); break;
721 		case 0x54: am_i2b(&tms7000_device::op_or); break;
722 		case 0x55: am_i2b(&tms7000_device::op_xor); break;
723 		case 0x56: am_i2b(&tms7000_device::op_btjo); break;
724 		case 0x57: am_i2b(&tms7000_device::op_btjz); break;
725 		case 0x58: am_i2b(&tms7000_device::op_add); break;
726 		case 0x59: am_i2b(&tms7000_device::op_adc); break;
727 		case 0x5a: am_i2b(&tms7000_device::op_sub); break;
728 		case 0x5b: am_i2b(&tms7000_device::op_sbb); break;
729 		case 0x5c: am_i2b(&tms7000_device::op_mpy); break;
730 		case 0x5d: am_i2b(&tms7000_device::op_cmp); break;
731 		case 0x5e: am_i2b(&tms7000_device::op_dac); break;
732 		case 0x5f: am_i2b(&tms7000_device::op_dsb); break;
733 
734 		case 0x62: am_b2a(&tms7000_device::op_mov); break;
735 		case 0x63: am_b2a(&tms7000_device::op_and); break;
736 		case 0x64: am_b2a(&tms7000_device::op_or); break;
737 		case 0x65: am_b2a(&tms7000_device::op_xor); break;
738 		case 0x66: am_b2a(&tms7000_device::op_btjo); break;
739 		case 0x67: am_b2a(&tms7000_device::op_btjz); break;
740 		case 0x68: am_b2a(&tms7000_device::op_add); break;
741 		case 0x69: am_b2a(&tms7000_device::op_adc); break;
742 		case 0x6a: am_b2a(&tms7000_device::op_sub); break;
743 		case 0x6b: am_b2a(&tms7000_device::op_sbb); break;
744 		case 0x6c: am_b2a(&tms7000_device::op_mpy); break;
745 		case 0x6d: am_b2a(&tms7000_device::op_cmp); break;
746 		case 0x6e: am_b2a(&tms7000_device::op_dac); break;
747 		case 0x6f: am_b2a(&tms7000_device::op_dsb); break;
748 
749 		case 0x72: am_i2r(&tms7000_device::op_mov); break;
750 		case 0x73: am_i2r(&tms7000_device::op_and); break;
751 		case 0x74: am_i2r(&tms7000_device::op_or); break;
752 		case 0x75: am_i2r(&tms7000_device::op_xor); break;
753 		case 0x76: am_i2r(&tms7000_device::op_btjo); break;
754 		case 0x77: am_i2r(&tms7000_device::op_btjz); break;
755 		case 0x78: am_i2r(&tms7000_device::op_add); break;
756 		case 0x79: am_i2r(&tms7000_device::op_adc); break;
757 		case 0x7a: am_i2r(&tms7000_device::op_sub); break;
758 		case 0x7b: am_i2r(&tms7000_device::op_sbb); break;
759 		case 0x7c: am_i2r(&tms7000_device::op_mpy); break;
760 		case 0x7d: am_i2r(&tms7000_device::op_cmp); break;
761 		case 0x7e: am_i2r(&tms7000_device::op_dac); break;
762 		case 0x7f: am_i2r(&tms7000_device::op_dsb); break;
763 
764 		case 0x80: am_p2a(&tms7000_device::op_mov); break;
765 		case 0x82: am_a2p(&tms7000_device::op_mov); break;
766 		case 0x83: am_a2p(&tms7000_device::op_and); break;
767 		case 0x84: am_a2p(&tms7000_device::op_or); break;
768 		case 0x85: am_a2p(&tms7000_device::op_xor); break;
769 		case 0x86: am_a2p(&tms7000_device::op_btjo); break;
770 		case 0x87: am_a2p(&tms7000_device::op_btjz); break;
771 		case 0x88: movd_dir(); break;
772 		case 0x8a: lda_dir(); break;
773 		case 0x8b: sta_dir(); break;
774 		case 0x8c: br_dir(); break;
775 		case 0x8d: cmpa_dir(); break;
776 		case 0x8e: call_dir(); break;
777 
778 		case 0x91: am_p2b(&tms7000_device::op_mov); break;
779 		case 0x92: am_b2p(&tms7000_device::op_mov); break;
780 		case 0x93: am_b2p(&tms7000_device::op_and); break;
781 		case 0x94: am_b2p(&tms7000_device::op_or); break;
782 		case 0x95: am_b2p(&tms7000_device::op_xor); break;
783 		case 0x96: am_b2p(&tms7000_device::op_btjo); break;
784 		case 0x97: am_b2p(&tms7000_device::op_btjz); break;
785 		case 0x98: movd_ind(); break;
786 		case 0x9a: lda_ind(); break;
787 		case 0x9b: sta_ind(); break;
788 		case 0x9c: br_ind(); break;
789 		case 0x9d: cmpa_ind(); break;
790 		case 0x9e: call_ind(); break;
791 
792 		case 0xa2: am_i2p(&tms7000_device::op_mov); break;
793 		case 0xa3: am_i2p(&tms7000_device::op_and); break;
794 		case 0xa4: am_i2p(&tms7000_device::op_or); break;
795 		case 0xa5: am_i2p(&tms7000_device::op_xor); break;
796 		case 0xa6: am_i2p(&tms7000_device::op_btjo); break;
797 		case 0xa7: am_i2p(&tms7000_device::op_btjz); break;
798 		case 0xa8: movd_inx(); break;
799 		case 0xaa: lda_inx(); break;
800 		case 0xab: sta_inx(); break;
801 		case 0xac: br_inx(); break;
802 		case 0xad: cmpa_inx(); break;
803 		case 0xae: call_inx(); break;
804 
805 		case 0xb0: am_a2a(&tms7000_device::op_mov); break; // aka clrc/tsta
806 		case 0xb1: am_b2a(&tms7000_device::op_mov); break; // undocumented
807 		case 0xb2: am_a(&tms7000_device::op_dec); break;
808 		case 0xb3: am_a(&tms7000_device::op_inc); break;
809 		case 0xb4: am_a(&tms7000_device::op_inv); break;
810 		case 0xb5: am_a(&tms7000_device::op_clr); break;
811 		case 0xb6: am_a(&tms7000_device::op_xchb); break;
812 		case 0xb7: am_a(&tms7000_device::op_swap); break;
813 		case 0xb8: push_a(); break;
814 		case 0xb9: pop_a(); break;
815 		case 0xba: am_a(&tms7000_device::op_djnz); break;
816 		case 0xbb: decd_a(); break;
817 		case 0xbc: am_a(&tms7000_device::op_rr); break;
818 		case 0xbd: am_a(&tms7000_device::op_rrc); break;
819 		case 0xbe: am_a(&tms7000_device::op_rl); break;
820 		case 0xbf: am_a(&tms7000_device::op_rlc); break;
821 
822 		case 0xc0: am_a2b(&tms7000_device::op_mov); break;
823 		case 0xc1: am_b2b(&tms7000_device::op_mov); break; // aka tstb
824 		case 0xc2: am_b(&tms7000_device::op_dec); break;
825 		case 0xc3: am_b(&tms7000_device::op_inc); break;
826 		case 0xc4: am_b(&tms7000_device::op_inv); break;
827 		case 0xc5: am_b(&tms7000_device::op_clr); break;
828 		case 0xc6: am_b(&tms7000_device::op_xchb); break; // result equivalent to tstb
829 		case 0xc7: am_b(&tms7000_device::op_swap); break;
830 		case 0xc8: push_b(); break;
831 		case 0xc9: pop_b(); break;
832 		case 0xca: am_b(&tms7000_device::op_djnz); break;
833 		case 0xcb: decd_b(); break;
834 		case 0xcc: am_b(&tms7000_device::op_rr); break;
835 		case 0xcd: am_b(&tms7000_device::op_rrc); break;
836 		case 0xce: am_b(&tms7000_device::op_rl); break;
837 		case 0xcf: am_b(&tms7000_device::op_rlc); break;
838 
839 		case 0xd0: am_a2r(&tms7000_device::op_mov); break;
840 		case 0xd1: am_b2r(&tms7000_device::op_mov); break;
841 		case 0xd2: am_r(&tms7000_device::op_dec); break;
842 		case 0xd3: am_r(&tms7000_device::op_inc); break;
843 		case 0xd4: am_r(&tms7000_device::op_inv); break;
844 		case 0xd5: am_r(&tms7000_device::op_clr); break;
845 		case 0xd6: am_r(&tms7000_device::op_xchb); break;
846 		case 0xd7: am_r(&tms7000_device::op_swap); break;
847 		case 0xd8: push_r(); break;
848 		case 0xd9: pop_r(); break;
849 		case 0xda: am_r(&tms7000_device::op_djnz); break;
850 		case 0xdb: decd_r(); break;
851 		case 0xdc: am_r(&tms7000_device::op_rr); break;
852 		case 0xdd: am_r(&tms7000_device::op_rrc); break;
853 		case 0xde: am_r(&tms7000_device::op_rl); break;
854 		case 0xdf: am_r(&tms7000_device::op_rlc); break;
855 
856 		case 0xe0: jmp(true); break;
857 		case 0xe1: jmp(m_sr & SR_N); break; // jn/jlt
858 		case 0xe2: jmp(m_sr & SR_Z); break; // jz/jeq
859 		case 0xe3: jmp(m_sr & SR_C); break; // jc/jhs
860 		case 0xe4: jmp(!(m_sr & (SR_Z | SR_N))); break; // jp/jgt
861 		case 0xe5: jmp(!(m_sr & SR_N)); break; // jpz/jge - note: error in TI official documentation
862 		case 0xe6: jmp(!(m_sr & SR_Z)); break; // jnz/jne
863 		case 0xe7: jmp(!(m_sr & SR_C)); break; // jnc/jl
864 
865 		case 0xe8: case 0xe9: case 0xea: case 0xeb: case 0xec: case 0xed: case 0xee: case 0xef:
866 		case 0xf0: case 0xf1: case 0xf2: case 0xf3: case 0xf4: case 0xf5: case 0xf6: case 0xf7:
867 		case 0xf8: case 0xf9: case 0xfa: case 0xfb: case 0xfc: case 0xfd: case 0xfe: case 0xff:
868 			trap(op << 1); break;
869 
870 		default: illegal(op); break;
871 	}
872 }
873 
execute_one(uint8_t op)874 void tms7020_exl_device::execute_one(uint8_t op)
875 {
876 	// TMS7020 Exelvision EXL 100 custom opcode(s)
877 	if (op == 0xd7)
878 		lvdp();
879 	else
880 		tms7000_device::execute_one(op);
881 }
882 
883 
884 //-------------------------------------------------
885 //  TMS70C46 specifics
886 //-------------------------------------------------
887 
device_start()888 void tms70c46_device::device_start()
889 {
890 	// init/zerofill
891 	m_control = 0;
892 
893 	// register for savestates
894 	save_item(NAME(m_control));
895 
896 	tms7000_device::device_start();
897 }
898 
device_reset()899 void tms70c46_device::device_reset()
900 {
901 	m_control = 0;
902 
903 	// reset port E
904 	m_port_out_cb[4](0xff);
905 
906 	tms7000_device::device_reset();
907 }
908 
control_r()909 uint8_t tms70c46_device::control_r()
910 {
911 	return m_control;
912 }
913 
control_w(uint8_t data)914 void tms70c46_device::control_w(uint8_t data)
915 {
916 	// d5: enable external databus
917 	if (~m_control & data & 0x20)
918 		m_port_out_cb[4](0xff); // put port E into high impedance
919 
920 	// d4: enable clock divider when accessing slow memory (not emulated)
921 	// known fast memory areas: internal ROM/RAM, system RAM
922 	// known slow memory areas: system ROM, cartridge ROM/RAM
923 
924 	// d0-d3(all bits?): clock divider when d4 is set and address bus is in slow memory area
925 	// needs to be measured, i just know that $30 is full speed, and $38 is about 4 times slower
926 	m_control = data;
927 }
928 
929 // DOCK-BUS: TODO..
930 // right now pretend that nothing is connected
931 // external pins are HD0-HD3(data), HSK(handshake), BAV(bus available)
932 
dockbus_status_r()933 uint8_t tms70c46_device::dockbus_status_r()
934 {
935 	// d0: slave _HSK
936 	// d1: slave _BAV
937 	// d2: unused?
938 	// d3: IRQ active
939 	return 0;
940 }
941 
dockbus_status_w(uint8_t data)942 void tms70c46_device::dockbus_status_w(uint8_t data)
943 {
944 	// d0: master _HSK (setting it low(write 1) also clears IRQ)
945 	// d1: master _BAV
946 	// other bits: unused?
947 }
948 
dockbus_data_r()949 uint8_t tms70c46_device::dockbus_data_r()
950 {
951 	return 0xff;
952 }
953 
dockbus_data_w(uint8_t data)954 void tms70c46_device::dockbus_data_w(uint8_t data)
955 {
956 }
957