1 // license:BSD-3-Clause
2 // copyright-holders:hap, Tim Lindner
3 
4 // TMS7000 opcode handlers
5 
6 #include "emu.h"
7 #include "tms7000.h"
8 
9 // flag helpers
10 #define GET_C()     (m_sr >> 7 & 1)
11 #define SET_C(x)    m_sr = (m_sr & 0x7f) | ((x) >> 1 & 0x80)
12 #define SET_NZ(x)   m_sr = (m_sr & 0x9f) | ((x) >> 1 & 0x40) | (((x) & 0xff) ? 0 : 0x20)
13 #define SET_CNZ(x)  m_sr = (m_sr & 0x1f) | ((x) >> 1 & 0xc0) | (((x) & 0xff) ? 0 : 0x20)
14 
15 
16 // addressing modes (not all opcodes have a write cycle)
17 #define WB_NO -1
18 #define AM_WB(write_func, address, param1, param2) \
19 	int result = (this->*op)(param1, param2); \
20 	if (result > WB_NO) write_func(address, result)
21 
am_a(op_func op)22 void tms7000_device::am_a(op_func op)
23 {
24 	m_icount -= 5;
25 	AM_WB(write_r8, 0, read_r8(0), 0);
26 }
27 
am_b(op_func op)28 void tms7000_device::am_b(op_func op)
29 {
30 	m_icount -= 5;
31 	AM_WB(write_r8, 1, read_r8(1), 0);
32 }
33 
am_r(op_func op)34 void tms7000_device::am_r(op_func op)
35 {
36 	m_icount -= 7;
37 	uint8_t r = imm8();
38 	AM_WB(write_r8, r, read_r8(r), 0);
39 }
40 
am_a2a(op_func op)41 void tms7000_device::am_a2a(op_func op)
42 {
43 	m_icount -= 6;
44 	AM_WB(write_r8, 0, read_r8(0), read_r8(0));
45 }
46 
am_a2b(op_func op)47 void tms7000_device::am_a2b(op_func op)
48 {
49 	m_icount -= 6;
50 	AM_WB(write_r8, 1, read_r8(1), read_r8(0));
51 }
52 
am_a2p(op_func op)53 void tms7000_device::am_a2p(op_func op)
54 {
55 	m_icount -= 10;
56 	uint8_t r = imm8();
57 	AM_WB(write_p, r, read_p(r), read_r8(0));
58 }
59 
am_a2r(op_func op)60 void tms7000_device::am_a2r(op_func op)
61 {
62 	m_icount -= 8;
63 	uint8_t r = imm8();
64 	AM_WB(write_r8, r, read_r8(r), read_r8(0));
65 }
66 
am_b2a(op_func op)67 void tms7000_device::am_b2a(op_func op)
68 {
69 	m_icount -= 5;
70 	AM_WB(write_r8, 0, read_r8(0), read_r8(1));
71 }
72 
am_b2b(op_func op)73 void tms7000_device::am_b2b(op_func op)
74 {
75 	m_icount -= 6;
76 	AM_WB(write_r8, 1, read_r8(1), read_r8(1));
77 }
78 
am_b2r(op_func op)79 void tms7000_device::am_b2r(op_func op)
80 {
81 	m_icount -= 7;
82 	uint8_t r = imm8();
83 	AM_WB(write_r8, r, read_r8(r), read_r8(1));
84 }
85 
am_b2p(op_func op)86 void tms7000_device::am_b2p(op_func op)
87 {
88 	m_icount -= 9;
89 	uint8_t r = imm8();
90 	AM_WB(write_p, r, read_p(r), read_r8(1));
91 }
92 
am_r2a(op_func op)93 void tms7000_device::am_r2a(op_func op)
94 {
95 	m_icount -= 8;
96 	AM_WB(write_r8, 0, read_r8(0), read_r8(imm8()));
97 }
98 
am_r2b(op_func op)99 void tms7000_device::am_r2b(op_func op)
100 {
101 	m_icount -= 8;
102 	AM_WB(write_r8, 1, read_r8(1), read_r8(imm8()));
103 }
104 
am_r2r(op_func op)105 void tms7000_device::am_r2r(op_func op)
106 {
107 	m_icount -= 10;
108 	uint8_t param2 = read_r8(imm8());
109 	uint8_t r = imm8();
110 	AM_WB(write_r8, r, read_r8(r), param2);
111 }
112 
am_i2a(op_func op)113 void tms7000_device::am_i2a(op_func op)
114 {
115 	m_icount -= 7;
116 	AM_WB(write_r8, 0, read_r8(0), imm8());
117 }
118 
am_i2b(op_func op)119 void tms7000_device::am_i2b(op_func op)
120 {
121 	m_icount -= 7;
122 	AM_WB(write_r8, 1, read_r8(1), imm8());
123 }
124 
am_i2r(op_func op)125 void tms7000_device::am_i2r(op_func op)
126 {
127 	m_icount -= 9;
128 	uint8_t param2 = imm8();
129 	uint8_t r = imm8();
130 	AM_WB(write_r8, r, read_r8(r), param2);
131 }
132 
am_i2p(op_func op)133 void tms7000_device::am_i2p(op_func op)
134 {
135 	m_icount -= 11;
136 	uint8_t param2 = imm8();
137 	uint8_t r = imm8();
138 	AM_WB(write_p, r, read_p(r), param2);
139 }
140 
am_p2a(op_func op)141 void tms7000_device::am_p2a(op_func op)
142 {
143 	m_icount -= 9;
144 	AM_WB(write_r8, 0, read_r8(0), read_p(imm8()));
145 }
146 
am_p2b(op_func op)147 void tms7000_device::am_p2b(op_func op)
148 {
149 	m_icount -= 8;
150 	AM_WB(write_r8, 1, read_r8(1), read_p(imm8()));
151 }
152 
153 
154 
155 // common opcodes
156 // 1 param
op_clr(uint8_t param1,uint8_t param2)157 int tms7000_device::op_clr(uint8_t param1, uint8_t param2)
158 {
159 	uint8_t t = 0;
160 	SET_CNZ(t);
161 	return t;
162 }
163 
op_dec(uint8_t param1,uint8_t param2)164 int tms7000_device::op_dec(uint8_t param1, uint8_t param2)
165 {
166 	uint16_t t = param1 - 1;
167 	SET_NZ(t);
168 	SET_C(~t);
169 	return t;
170 }
171 
op_inc(uint8_t param1,uint8_t param2)172 int tms7000_device::op_inc(uint8_t param1, uint8_t param2)
173 {
174 	uint16_t t = param1 + 1;
175 	SET_CNZ(t);
176 	return t;
177 }
178 
op_inv(uint8_t param1,uint8_t param2)179 int tms7000_device::op_inv(uint8_t param1, uint8_t param2)
180 {
181 	uint8_t t = ~param1;
182 	SET_CNZ(t);
183 	return t;
184 }
185 
op_rl(uint8_t param1,uint8_t param2)186 int tms7000_device::op_rl(uint8_t param1, uint8_t param2)
187 {
188 	uint16_t t = param1 << 1 | param1 >> 7;
189 	SET_CNZ(t);
190 	return t;
191 }
192 
op_rlc(uint8_t param1,uint8_t param2)193 int tms7000_device::op_rlc(uint8_t param1, uint8_t param2)
194 {
195 	uint16_t t = param1 << 1 | GET_C();
196 	SET_CNZ(t);
197 	return t;
198 }
199 
op_rr(uint8_t param1,uint8_t param2)200 int tms7000_device::op_rr(uint8_t param1, uint8_t param2)
201 {
202 	uint16_t t = param1 >> 1 | param1 << 8 | (param1 << 7 & 0x80);
203 	SET_CNZ(t);
204 	return t;
205 }
206 
op_rrc(uint8_t param1,uint8_t param2)207 int tms7000_device::op_rrc(uint8_t param1, uint8_t param2)
208 {
209 	uint16_t t = param1 >> 1 | param1 << 8 | GET_C() << 7;
210 	SET_CNZ(t);
211 	return t;
212 }
213 
op_swap(uint8_t param1,uint8_t param2)214 int tms7000_device::op_swap(uint8_t param1, uint8_t param2)
215 {
216 	m_icount -= 3;
217 	uint16_t t = param1 >> 4 | param1 << 4;
218 	SET_CNZ(t);
219 	return t;
220 }
221 
op_xchb(uint8_t param1,uint8_t param2)222 int tms7000_device::op_xchb(uint8_t param1, uint8_t param2)
223 {
224 	m_icount -= 1;
225 	uint8_t t = read_r8(1);
226 	SET_CNZ(t);
227 	write_r8(1, param1);
228 	return t;
229 }
230 
231 // 2 params
op_adc(uint8_t param1,uint8_t param2)232 int tms7000_device::op_adc(uint8_t param1, uint8_t param2)
233 {
234 	uint16_t t = param1 + param2 + GET_C();
235 	SET_CNZ(t);
236 	return t;
237 }
238 
op_add(uint8_t param1,uint8_t param2)239 int tms7000_device::op_add(uint8_t param1, uint8_t param2)
240 {
241 	uint16_t t = param1 + param2;
242 	SET_CNZ(t);
243 	return t;
244 }
245 
op_and(uint8_t param1,uint8_t param2)246 int tms7000_device::op_and(uint8_t param1, uint8_t param2)
247 {
248 	uint8_t t = param1 & param2;
249 	SET_CNZ(t);
250 	return t;
251 }
252 
op_cmp(uint8_t param1,uint8_t param2)253 int tms7000_device::op_cmp(uint8_t param1, uint8_t param2)
254 {
255 	uint16_t t = param1 - param2;
256 	SET_NZ(t);
257 	SET_C(~t);
258 	return WB_NO;
259 }
260 
op_mpy(uint8_t param1,uint8_t param2)261 int tms7000_device::op_mpy(uint8_t param1, uint8_t param2)
262 {
263 	m_icount -= 39;
264 	uint16_t t = param1 * param2;
265 	SET_CNZ(t >> 8);
266 	write_mem16(0, t); // always writes result to regs A-B
267 	return WB_NO;
268 }
269 
op_mov(uint8_t param1,uint8_t param2)270 int tms7000_device::op_mov(uint8_t param1, uint8_t param2)
271 {
272 	uint8_t t = param2;
273 	SET_CNZ(t);
274 	return t;
275 }
276 
op_or(uint8_t param1,uint8_t param2)277 int tms7000_device::op_or(uint8_t param1, uint8_t param2)
278 {
279 	uint8_t t = param1 | param2;
280 	SET_CNZ(t);
281 	return t;
282 }
283 
op_sbb(uint8_t param1,uint8_t param2)284 int tms7000_device::op_sbb(uint8_t param1, uint8_t param2)
285 {
286 	uint16_t t = param1 - param2 - (!GET_C());
287 	SET_NZ(t);
288 	SET_C(~t);
289 	return t;
290 }
291 
op_sub(uint8_t param1,uint8_t param2)292 int tms7000_device::op_sub(uint8_t param1, uint8_t param2)
293 {
294 	uint16_t t = param1 - param2;
295 	SET_NZ(t);
296 	SET_C(~t);
297 	return t;
298 }
299 
op_xor(uint8_t param1,uint8_t param2)300 int tms7000_device::op_xor(uint8_t param1, uint8_t param2)
301 {
302 	uint8_t t = param1 ^ param2;
303 	SET_CNZ(t);
304 	return t;
305 }
306 
307 // BCD arthrimetic handling
308 static const uint8_t lut_bcd_out[6] = { 0x00, 0x06, 0x00, 0x66, 0x60, 0x66 };
309 
op_dac(uint8_t param1,uint8_t param2)310 int tms7000_device::op_dac(uint8_t param1, uint8_t param2)
311 {
312 	m_icount -= 2;
313 	int c = GET_C();
314 
315 	uint8_t h1 = param1 >> 4 & 0xf;
316 	uint8_t l1 = param1 >> 0 & 0xf;
317 	uint8_t h2 = param2 >> 4 & 0xf;
318 	uint8_t l2 = param2 >> 0 & 0xf;
319 
320 	// compute bcd constant
321 	uint8_t d = ((l1 + l2 + c) < 10) ? 0 : 1;
322 	if ((h1 + h2) == 9)
323 		d |= 2;
324 	else if ((h1 + h2) > 9)
325 		d |= 4;
326 
327 	uint8_t t = param1 + param2 + c + lut_bcd_out[d];
328 	SET_CNZ(t);
329 	if (d > 2)
330 		m_sr |= SR_C;
331 
332 	return t;
333 }
334 
op_dsb(uint8_t param1,uint8_t param2)335 int tms7000_device::op_dsb(uint8_t param1, uint8_t param2)
336 {
337 	m_icount -= 2;
338 	int c = !GET_C();
339 
340 	uint8_t h1 = param1 >> 4 & 0xf;
341 	uint8_t l1 = param1 >> 0 & 0xf;
342 	uint8_t h2 = param2 >> 4 & 0xf;
343 	uint8_t l2 = param2 >> 0 & 0xf;
344 
345 	// compute bcd constant
346 	uint8_t d = ((l1 - c) >= l2) ? 0 : 1;
347 	if (h1 == h2)
348 		d |= 2;
349 	else if (h1 < h2)
350 		d |= 4;
351 
352 	uint8_t t = param1 - param2 - c - lut_bcd_out[d];
353 	SET_CNZ(t);
354 	if (d <= 2)
355 		m_sr |= SR_C;
356 
357 	return t;
358 }
359 
360 // branches
shortbranch(bool check)361 void tms7000_device::shortbranch(bool check)
362 {
363 	m_icount -= 2;
364 	int8_t d = (int8_t)imm8();
365 
366 	if (check)
367 	{
368 		m_pc += d;
369 		m_icount -= 2;
370 	}
371 }
372 
jmp(bool check)373 void tms7000_device::jmp(bool check)
374 {
375 	m_icount -= 3;
376 	shortbranch(check);
377 }
378 
op_djnz(uint8_t param1,uint8_t param2)379 int tms7000_device::op_djnz(uint8_t param1, uint8_t param2)
380 {
381 	uint16_t t = param1 - 1;
382 	shortbranch(t != 0);
383 	return t;
384 }
385 
op_btjo(uint8_t param1,uint8_t param2)386 int tms7000_device::op_btjo(uint8_t param1, uint8_t param2)
387 {
388 	uint8_t t = param1 & param2;
389 	SET_CNZ(t);
390 	shortbranch(t != 0);
391 	return WB_NO;
392 }
393 
op_btjz(uint8_t param1,uint8_t param2)394 int tms7000_device::op_btjz(uint8_t param1, uint8_t param2)
395 {
396 	uint8_t t = ~param1 & param2;
397 	SET_CNZ(t);
398 	shortbranch(t != 0);
399 	return WB_NO;
400 }
401 
402 
403 
404 // other opcodes
405 // dec double
decd_a()406 void tms7000_device::decd_a()
407 {
408 	m_icount -= 9;
409 	uint32_t t = read_r16(0) - 1;
410 	write_r16(0, t);
411 	SET_NZ(t >> 8);
412 	SET_C(~(t >> 8));
413 }
414 
decd_b()415 void tms7000_device::decd_b()
416 {
417 	m_icount -= 9;
418 	uint32_t t = read_r16(1) - 1;
419 	write_r16(1, t);
420 	SET_NZ(t >> 8);
421 	SET_C(~(t >> 8));
422 }
423 
decd_r()424 void tms7000_device::decd_r()
425 {
426 	m_icount -= 11;
427 	uint8_t r = imm8();
428 	uint32_t t = read_r16(r) - 1;
429 	write_r16(r, t);
430 	SET_NZ(t >> 8);
431 	SET_C(~(t >> 8));
432 }
433 
434 // cmpa extended
cmpa_dir()435 void tms7000_device::cmpa_dir()
436 {
437 	m_icount -= 12;
438 	uint16_t t = read_r8(0) - read_mem8(imm16());
439 	SET_NZ(t);
440 	SET_C(~t);
441 }
442 
cmpa_inx()443 void tms7000_device::cmpa_inx()
444 {
445 	m_icount -= 14;
446 	uint16_t t = read_r8(0) - read_mem8(imm16() + read_r8(1));
447 	SET_NZ(t);
448 	SET_C(~t);
449 }
450 
cmpa_ind()451 void tms7000_device::cmpa_ind()
452 {
453 	m_icount -= 11;
454 	uint16_t t = read_r8(0) - read_mem8(read_r16(imm8()));
455 	SET_NZ(t);
456 	SET_C(~t);
457 }
458 
459 // lda extended
lda_dir()460 void tms7000_device::lda_dir()
461 {
462 	m_icount -= 11;
463 	uint8_t t = read_mem8(imm16());
464 	write_r8(0, t);
465 	SET_CNZ(t);
466 }
467 
lda_inx()468 void tms7000_device::lda_inx()
469 {
470 	m_icount -= 13;
471 	uint8_t t = read_mem8(imm16() + read_r8(1));
472 	write_r8(0, t);
473 	SET_CNZ(t);
474 }
475 
lda_ind()476 void tms7000_device::lda_ind()
477 {
478 	m_icount -= 10;
479 	uint8_t t = read_mem8(read_r16(imm8()));
480 	write_r8(0, t);
481 	SET_CNZ(t);
482 }
483 
484 // sta extended
sta_dir()485 void tms7000_device::sta_dir()
486 {
487 	m_icount -= 11;
488 	uint8_t t = read_r8(0);
489 	write_mem8(imm16(), t);
490 	SET_CNZ(t);
491 }
492 
sta_inx()493 void tms7000_device::sta_inx()
494 {
495 	m_icount -= 13;
496 	uint8_t t = read_r8(0);
497 	write_mem8(imm16() + read_r8(1), t);
498 	SET_CNZ(t);
499 }
500 
sta_ind()501 void tms7000_device::sta_ind()
502 {
503 	m_icount -= 10;
504 	uint8_t t = read_r8(0);
505 	write_mem8(read_r16(imm8()), t);
506 	SET_CNZ(t);
507 }
508 
509 // mov double
movd_dir()510 void tms7000_device::movd_dir()
511 {
512 	m_icount -= 15;
513 	uint16_t t = imm16();
514 	write_r16(imm8(), t);
515 	SET_CNZ(t >> 8);
516 }
517 
movd_inx()518 void tms7000_device::movd_inx()
519 {
520 	m_icount -= 17;
521 	uint16_t t = imm16() + read_r8(1);
522 	write_r16(imm8(), t);
523 	SET_CNZ(t >> 8);
524 }
525 
movd_ind()526 void tms7000_device::movd_ind()
527 {
528 	m_icount -= 14;
529 	uint16_t t = read_r16(imm8());
530 	write_r16(imm8(), t);
531 	SET_CNZ(t >> 8);
532 }
533 
534 // long branch
br_dir()535 void tms7000_device::br_dir()
536 {
537 	m_icount -= 10;
538 	m_pc = imm16();
539 }
540 
br_inx()541 void tms7000_device::br_inx()
542 {
543 	m_icount -= 12;
544 	m_pc = imm16() + read_r8(1);
545 }
546 
br_ind()547 void tms7000_device::br_ind()
548 {
549 	m_icount -= 9;
550 	m_pc = read_r16(imm8());
551 }
552 
553 // call/return
call_dir()554 void tms7000_device::call_dir()
555 {
556 	m_icount -= 14;
557 	uint16_t t = imm16();
558 	push16(m_pc);
559 	m_pc = t;
560 }
561 
call_inx()562 void tms7000_device::call_inx()
563 {
564 	m_icount -= 16;
565 	uint16_t t = imm16() + read_r8(1);
566 	push16(m_pc);
567 	m_pc = t;
568 }
569 
call_ind()570 void tms7000_device::call_ind()
571 {
572 	m_icount -= 13;
573 	uint16_t t = read_r16(imm8());
574 	push16(m_pc);
575 	m_pc = t;
576 }
577 
trap(uint8_t address)578 void tms7000_device::trap(uint8_t address)
579 {
580 	m_icount -= 14;
581 	push16(m_pc);
582 	m_pc = read_mem16(0xff00 | address);
583 }
584 
reti()585 void tms7000_device::reti()
586 {
587 	m_icount -= 9;
588 	m_pc = pull16();
589 	m_sr = pull8() & 0xf0;
590 	check_interrupts();
591 }
592 
rets()593 void tms7000_device::rets()
594 {
595 	m_icount -= 7;
596 	m_pc = pull16();
597 }
598 
599 // pop
pop_a()600 void tms7000_device::pop_a()
601 {
602 	m_icount -= 6;
603 	uint8_t t = pull8();
604 	write_r8(0, t);
605 	SET_CNZ(t);
606 }
607 
pop_b()608 void tms7000_device::pop_b()
609 {
610 	m_icount -= 6;
611 	uint8_t t = pull8();
612 	write_r8(1, t);
613 	SET_CNZ(t);
614 }
615 
pop_r()616 void tms7000_device::pop_r()
617 {
618 	m_icount -= 8;
619 	uint8_t t = pull8();
620 	write_r8(imm8(), t);
621 	SET_CNZ(t);
622 }
623 
pop_st()624 void tms7000_device::pop_st()
625 {
626 	m_icount -= 6;
627 	m_sr = pull8() & 0xf0;
628 	check_interrupts();
629 }
630 
631 // push
push_a()632 void tms7000_device::push_a()
633 {
634 	m_icount -= 6;
635 	uint8_t t = read_r8(0);
636 	push8(t);
637 	SET_CNZ(t);
638 }
639 
push_b()640 void tms7000_device::push_b()
641 {
642 	m_icount -= 6;
643 	uint8_t t = read_r8(1);
644 	push8(t);
645 	SET_CNZ(t);
646 }
647 
push_r()648 void tms7000_device::push_r()
649 {
650 	m_icount -= 8;
651 	uint8_t t = read_r8(imm8());
652 	push8(t);
653 	SET_CNZ(t);
654 }
655 
push_st()656 void tms7000_device::push_st()
657 {
658 	m_icount -= 6;
659 	push8(m_sr);
660 }
661 
662 // other
nop()663 void tms7000_device::nop()
664 {
665 	m_icount -= 5;
666 }
667 
idle()668 void tms7000_device::idle()
669 {
670 	m_icount -= 6;
671 	m_pc--;
672 	m_idle_state = true;
673 }
674 
dint()675 void tms7000_device::dint()
676 {
677 	m_icount -= 5;
678 	m_sr &= ~(SR_N | SR_Z | SR_C | SR_I);
679 }
680 
eint()681 void tms7000_device::eint()
682 {
683 	m_icount -= 5;
684 	m_sr |= (SR_N | SR_Z | SR_C | SR_I);
685 	check_interrupts();
686 }
687 
ldsp()688 void tms7000_device::ldsp()
689 {
690 	m_icount -= 5;
691 	m_sp = read_r8(1);
692 }
693 
stsp()694 void tms7000_device::stsp()
695 {
696 	m_icount -= 6;
697 	write_r8(1, m_sp);
698 }
699 
setc()700 void tms7000_device::setc()
701 {
702 	m_icount -= 5;
703 	m_sr = (m_sr & ~SR_N) | SR_C | SR_Z;
704 }
705 
706 // not standard
lvdp()707 void tms7020_exl_device::lvdp()
708 {
709 	/* on EXL100, opcode D7 ?? (SWAP R) was changed to LVDP, mostly equivalent to:
710 	* MOVP P40,xx
711 	* MOVP P36,A
712 	*/
713 	m_icount -= 10; // TODO: check real timing
714 	imm8(); // always 0x28? discarded?
715 	read_p(0x28);
716 	uint8_t t = read_p(0x24);
717 	write_r8(0, t);
718 	SET_CNZ(t);
719 }
720 
721 // illegal opcode handling
illegal(uint8_t op)722 void tms7000_device::illegal(uint8_t op)
723 {
724 	m_icount -= 5; // guessed
725 	logerror("%s: illegal opcode $%02X @ $%04x\n", tag(), op, m_pc);
726 }
727