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