trunk/src/emu/cpu/tms9900/tms9900.c
| r26386 | r26387 | |
| 119 | 119 | ST_IM = 0x000f // Interrupt mask |
| 120 | 120 | }; |
| 121 | 121 | |
| 122 | | #define LOG logerror |
| 123 | | #define VERBOSE 1 |
| 122 | /* |
| 123 | The following defines can be set to 0 or 1 to disable or enable certain |
| 124 | output in the log. |
| 125 | */ |
| 126 | // Emulation setup |
| 127 | #define TRACE_SETUP 0 |
| 124 | 128 | |
| 129 | // Emulation details |
| 130 | #define TRACE_EMU 0 |
| 131 | |
| 132 | // Location and command |
| 133 | #define TRACE_EXEC 0 |
| 134 | |
| 135 | // Memory operation |
| 136 | #define TRACE_MEM 0 |
| 137 | |
| 138 | // Cycle count |
| 139 | #define TRACE_CYCLES 0 |
| 140 | |
| 141 | // Clock ticks |
| 142 | #define TRACE_CLOCK 0 |
| 143 | |
| 144 | // Wait states |
| 145 | #define TRACE_WAIT 0 |
| 146 | |
| 147 | // Interrupts |
| 148 | #define TRACE_INT 0 |
| 149 | |
| 150 | // CRU operation |
| 151 | #define TRACE_CRU 0 |
| 152 | |
| 153 | // Status register |
| 154 | #define TRACE_STATUS 0 |
| 155 | |
| 156 | // ALU details |
| 157 | #define TRACE_ALU 0 |
| 158 | |
| 159 | // Microinstruction level |
| 160 | #define TRACE_MICRO 0 |
| 161 | |
| 125 | 162 | /**************************************************************************** |
| 126 | 163 | Common constructor for TMS9900 and TMS9980A |
| 127 | 164 | The CRU mask is related to the bits, not to their addresses which are |
| r26386 | r26387 | |
| 192 | 229 | void tms99xx_device::device_stop() |
| 193 | 230 | { |
| 194 | 231 | int k = 0; |
| 195 | | if (VERBOSE>3) LOG("tms99xx: Deleting lookup tables\n"); |
| 232 | if (TRACE_SETUP) logerror("tms99xx: Deleting lookup tables\n"); |
| 196 | 233 | while (m_lotables[k]!=NULL) delete[] m_lotables[k++]; |
| 197 | 234 | } |
| 198 | 235 | |
| r26386 | r26387 | |
| 221 | 258 | */ |
| 222 | 259 | void tms99xx_device::device_reset() |
| 223 | 260 | { |
| 224 | | if (VERBOSE>3) LOG("tms99xx: Device reset\n"); |
| 261 | if (TRACE_EMU) logerror("tms99xx: Device reset by emulator\n"); |
| 225 | 262 | m_reset = true; |
| 226 | 263 | m_check_ready = false; |
| 227 | 264 | m_wait_state = false; |
| r26386 | r26387 | |
| 522 | 559 | { |
| 523 | 560 | ALU_NOP, |
| 524 | 561 | DATA_DERIVE, // Get argument |
| 525 | | ALU_XOP, // 0 Save the address of the source operand, set address = 0x0040 + xopNr*4 |
| 562 | ALU_XOP, // 0 Save the address of the source operand, set address = 0x0040 + xopNr*4, 6 cycles |
| 526 | 563 | MEMORY_READ, // Read the new WP |
| 527 | 564 | ALU_XOP, // 1 Save old WP, set new WP, get the source operand address |
| 528 | 565 | MEMORY_WRITE, // Write the address of the source operand into the new R11 |
| r26386 | r26387 | |
| 535 | 572 | ALU_XOP, // 5 Set the X bit in the ST |
| 536 | 573 | MEMORY_READ, // Read the new PC |
| 537 | 574 | ALU_XOP, // 6 Set the new PC |
| 538 | | ALU_NOP, |
| 539 | 575 | END |
| 540 | 576 | }; |
| 541 | 577 | |
| r26386 | r26387 | |
| 579 | 615 | ALU_NOP, |
| 580 | 616 | DATA_DERIVE, |
| 581 | 617 | ALU_B, |
| 618 | ALU_NOP, |
| 582 | 619 | MEMORY_WRITE, |
| 583 | | ALU_NOP, |
| 584 | 620 | END |
| 585 | 621 | }; |
| 586 | 622 | |
| r26386 | r26387 | |
| 589 | 625 | ALU_NOP, |
| 590 | 626 | DATA_DERIVE, // Get argument |
| 591 | 627 | ALU_BLWP, // 0 Save old WP, set new WP, save position |
| 628 | ALU_NOP, |
| 592 | 629 | MEMORY_WRITE, // write ST to R15 |
| 593 | 630 | ALU_BLWP, // 1 |
| 594 | 631 | MEMORY_WRITE, // write PC to R14 |
| r26386 | r26387 | |
| 607 | 644 | ALU_SOURCE, |
| 608 | 645 | ALU_NOP, |
| 609 | 646 | ALU_LDCR, |
| 647 | ALU_NOP, |
| 610 | 648 | MEMORY_READ, |
| 611 | 649 | ALU_LDCR, |
| 612 | 650 | CRU_OUTPUT, |
| r26386 | r26387 | |
| 619 | 657 | ALU_NOP, |
| 620 | 658 | DATA_DERIVE, |
| 621 | 659 | ALU_SOURCE, // Store address and value |
| 622 | | ALU_STCR, // 0 Set register_number = 12 |
| 660 | ALU_STCR, // 0 Set register_number = 12; 0 cycles (already done before) |
| 623 | 661 | MEMORY_READ, |
| 624 | 662 | ALU_STCR, // 1 Prepare CRU access |
| 663 | ALU_NOP, |
| 625 | 664 | CRU_INPUT, |
| 626 | 665 | ALU_STCR, // 2 Create result; Cycles = 5 + (8-#C-1) or + (16-#C) |
| 666 | ALU_NOP, |
| 667 | ALU_NOP, |
| 668 | ALU_NOP, |
| 627 | 669 | MEMORY_WRITE, |
| 628 | 670 | END |
| 629 | 671 | }; |
| r26386 | r26387 | |
| 631 | 673 | MICROPROGRAM(sbz_sbo_mp) |
| 632 | 674 | { |
| 633 | 675 | ALU_SBZ_SBO, |
| 676 | ALU_NOP, |
| 634 | 677 | MEMORY_READ, |
| 635 | 678 | ALU_SBZ_SBO, |
| 636 | 679 | CRU_OUTPUT, |
| r26386 | r26387 | |
| 650 | 693 | MICROPROGRAM(jmp_mp) |
| 651 | 694 | { |
| 652 | 695 | ALU_NOP, |
| 653 | | ALU_NOP, |
| 654 | 696 | ALU_JMP, |
| 655 | 697 | ALU_JMP, |
| 698 | ALU_NOP, |
| 656 | 699 | END |
| 657 | 700 | }; |
| 658 | 701 | |
| r26386 | r26387 | |
| 729 | 772 | |
| 730 | 773 | MICROPROGRAM(external_mp) |
| 731 | 774 | { |
| 775 | ALU_NOP, |
| 776 | ALU_NOP, |
| 732 | 777 | ALU_EXT, |
| 778 | ALU_NOP, |
| 779 | ALU_NOP, |
| 733 | 780 | END |
| 734 | 781 | }; |
| 735 | 782 | |
| 736 | | MICROPROGRAM(rtwp_mp) // Problem: This makes RTWP use 8 instead of 7 machine cycles. |
| 783 | MICROPROGRAM(rtwp_mp) |
| 737 | 784 | { |
| 785 | ALU_NOP, |
| 738 | 786 | ALU_RTWP, |
| 739 | 787 | MEMORY_READ, |
| 740 | | ALU_RTWP, |
| 788 | ALU_RTWP, // no cycles |
| 741 | 789 | MEMORY_READ, |
| 742 | | ALU_RTWP, |
| 790 | ALU_RTWP, // no cycles |
| 743 | 791 | MEMORY_READ, |
| 744 | 792 | ALU_RTWP, |
| 745 | 793 | END |
| r26386 | r26387 | |
| 1002 | 1050 | { |
| 1003 | 1051 | inst = &s_command[i]; |
| 1004 | 1052 | table = m_command_lookup_table; |
| 1005 | | if (VERBOSE>8) LOG("tms99xx: === opcode=%04x, len=%d\n", inst->opcode, format_mask_len[inst->format]); |
| 1053 | if (TRACE_SETUP) logerror("tms99xx: === opcode=%04x, len=%d\n", inst->opcode, format_mask_len[inst->format]); |
| 1006 | 1054 | bitcount = 4; |
| 1007 | 1055 | opcode = inst->opcode; |
| 1008 | 1056 | cmdindex = (opcode>>12) & 0x000f; |
| r26386 | r26387 | |
| 1012 | 1060 | // Descend |
| 1013 | 1061 | if (table[cmdindex].next_digit == NULL) |
| 1014 | 1062 | { |
| 1015 | | if (VERBOSE>8) LOG("tms99xx: create new table at bitcount=%d for index=%d\n", bitcount, cmdindex); |
| 1063 | if (TRACE_SETUP) logerror("tms99xx: create new table at bitcount=%d for index=%d\n", bitcount, cmdindex); |
| 1016 | 1064 | table[cmdindex].next_digit = new lookup_entry[16]; |
| 1017 | 1065 | m_lotables[k++] = table[cmdindex].next_digit; |
| 1018 | 1066 | for (int j=0; j < 16; j++) |
| r26386 | r26387 | |
| 1023 | 1071 | } |
| 1024 | 1072 | else |
| 1025 | 1073 | { |
| 1026 | | if (VERBOSE>8) LOG("tms99xx: found a table at bitcount=%d\n", bitcount); |
| 1074 | if (TRACE_SETUP) logerror("tms99xx: found a table at bitcount=%d\n", bitcount); |
| 1027 | 1075 | } |
| 1028 | 1076 | |
| 1029 | 1077 | table = table[cmdindex].next_digit; |
| r26386 | r26387 | |
| 1031 | 1079 | bitcount = bitcount+4; |
| 1032 | 1080 | opcode <<= 4; |
| 1033 | 1081 | cmdindex = (opcode>>12) & 0x000f; |
| 1034 | | if (VERBOSE>8) LOG("tms99xx: next index=%x\n", cmdindex); |
| 1082 | if (TRACE_SETUP) logerror("tms99xx: next index=%x\n", cmdindex); |
| 1035 | 1083 | } |
| 1036 | 1084 | |
| 1037 | | if (VERBOSE>8) LOG("tms99xx: bitcount=%d\n", bitcount); |
| 1085 | if (TRACE_SETUP) logerror("tms99xx: bitcount=%d\n", bitcount); |
| 1038 | 1086 | // We are at the target level |
| 1039 | 1087 | // Need to fill in the same entry for all values in the bitcount |
| 1040 | 1088 | // (if a command needs 10 bits we have to copy it four |
| 1041 | 1089 | // times for all combinations with 12 bits) |
| 1042 | 1090 | for (int j=0; j < (1<<(bitcount-format_mask_len[inst->format])); j++) |
| 1043 | 1091 | { |
| 1044 | | if (VERBOSE>8) LOG("tms99xx: opcode=%04x at position %d\n", inst->opcode, cmdindex+j); |
| 1092 | if (TRACE_SETUP) logerror("tms99xx: opcode=%04x at position %d\n", inst->opcode, cmdindex+j); |
| 1045 | 1093 | table[cmdindex+j].entry = inst; |
| 1046 | 1094 | } |
| 1047 | 1095 | |
| r26386 | r26387 | |
| 1049 | 1097 | } while (inst->opcode != 0xf000); |
| 1050 | 1098 | |
| 1051 | 1099 | m_lotables[k++] = NULL; |
| 1052 | | if (VERBOSE>8) LOG("tms99xx: Allocated %d tables\n", k); |
| 1100 | if (TRACE_SETUP) logerror("tms99xx: Allocated %d tables\n", k); |
| 1053 | 1101 | } |
| 1054 | 1102 | |
| 1055 | 1103 | /* |
| r26386 | r26387 | |
| 1092 | 1140 | { |
| 1093 | 1141 | if (m_reset) service_interrupt(); |
| 1094 | 1142 | |
| 1095 | | if (VERBOSE>6) LOG("tms99xx: calling execute_run for %d cycles\n", m_icount); |
| 1143 | if (TRACE_EMU) logerror("tms99xx: calling execute_run for %d cycles\n", m_icount); |
| 1096 | 1144 | do |
| 1097 | 1145 | { |
| 1098 | 1146 | // Only when last instruction has completed |
| r26386 | r26387 | |
| 1100 | 1148 | { |
| 1101 | 1149 | if (m_load_state) |
| 1102 | 1150 | { |
| 1103 | | if (VERBOSE>4) LOG("tms99xx: LOAD interrupt\n"); |
| 1151 | logerror("tms99xx: LOAD interrupt\n"); |
| 1104 | 1152 | m_irq_level = LOAD_INT; |
| 1105 | 1153 | m_irq_state = false; |
| 1106 | 1154 | service_interrupt(); |
| r26386 | r26387 | |
| 1118 | 1166 | |
| 1119 | 1167 | if (m_program == NULL && m_idle_state) |
| 1120 | 1168 | { |
| 1121 | | if (VERBOSE>5) LOG("tms99xx: idle state\n"); |
| 1169 | if (TRACE_WAIT) logerror("tms99xx: idle state\n"); |
| 1122 | 1170 | pulse_clock(1); |
| 1123 | 1171 | m_external_operation(IDLE_OP, 0); |
| 1124 | 1172 | m_external_operation(IDLE_OP, 1); |
| r26386 | r26387 | |
| 1134 | 1182 | m_program[MPC] != MEMORY_READ && m_program[MPC] != MEMORY_WRITE && |
| 1135 | 1183 | m_program[MPC] != REG_READ && m_program[MPC] != REG_WRITE))) |
| 1136 | 1184 | { |
| 1137 | | if (VERBOSE>2) LOG("tms99xx: hold state\n"); |
| 1185 | if (TRACE_WAIT) logerror("tms99xx: hold\n"); |
| 1138 | 1186 | if (!m_hold_acknowledged) acknowledge_hold(); |
| 1139 | 1187 | pulse_clock(1); |
| 1140 | 1188 | } |
| r26386 | r26387 | |
| 1145 | 1193 | { |
| 1146 | 1194 | // We are in a wait state |
| 1147 | 1195 | set_wait_state(true); |
| 1148 | | if (VERBOSE>2) LOG("tms99xx: wait state\n"); |
| 1196 | if (TRACE_WAIT) logerror("tms99xx: wait\n"); |
| 1149 | 1197 | // The clock output should be used to change the state of an outer |
| 1150 | 1198 | // device which operates the READY line |
| 1151 | 1199 | pulse_clock(1); |
| r26386 | r26387 | |
| 1160 | 1208 | { |
| 1161 | 1209 | m_op = m_program[MPC]; |
| 1162 | 1210 | } |
| 1163 | | if (VERBOSE>8) LOG("tms99xx: MPC = %d, m_op = %d\n", MPC, m_op); |
| 1211 | if (TRACE_MICRO) logerror("tms99xx: MPC = %d, m_op = %d\n", MPC, m_op); |
| 1164 | 1212 | // Call the operation of the microprogram |
| 1165 | 1213 | (this->*s_microoperation[m_op])(); |
| 1166 | 1214 | // If we have multiple passes (as in the TMS9980) |
| r26386 | r26387 | |
| 1176 | 1224 | } |
| 1177 | 1225 | } |
| 1178 | 1226 | } while (m_icount>0 && !m_reset); |
| 1179 | | if (VERBOSE>6) LOG("tms99xx: cycles expired; will return soon.\n"); |
| 1227 | if (TRACE_EMU) logerror("tms99xx: cycles expired; will return soon.\n"); |
| 1180 | 1228 | } |
| 1181 | 1229 | |
| 1182 | 1230 | /**************************************************************************/ |
| r26386 | r26387 | |
| 1196 | 1244 | { |
| 1197 | 1245 | m_load_state = (state==ASSERT_LINE); |
| 1198 | 1246 | m_irq_level = -1; |
| 1247 | m_reset = false; |
| 1199 | 1248 | } |
| 1200 | 1249 | else |
| 1201 | 1250 | { |
| r26386 | r26387 | |
| 1203 | 1252 | if (state==ASSERT_LINE) |
| 1204 | 1253 | { |
| 1205 | 1254 | m_irq_level = get_intlevel(state); |
| 1206 | | if (VERBOSE>6) LOG("tms99xx: interrupt line %d = %d, level=%d, ST=%04x\n", irqline, state, m_irq_level, ST); |
| 1255 | if (TRACE_INT) logerror("tms99xx: /INT asserted, level=%d, ST=%04x\n", m_irq_level, ST); |
| 1207 | 1256 | } |
| 1208 | 1257 | else |
| 1209 | 1258 | { |
| 1210 | | if (VERBOSE>6) LOG("tms99xx: cleared interrupt line %d\n", irqline); |
| 1259 | if (TRACE_INT) logerror("tms99xx: /INT cleared\n"); |
| 1211 | 1260 | } |
| 1212 | 1261 | } |
| 1213 | 1262 | } |
| r26386 | r26387 | |
| 1253 | 1302 | |
| 1254 | 1303 | m_reset = false; |
| 1255 | 1304 | } |
| 1256 | | if (VERBOSE>6) LOG("tms99xx: ********* triggered an interrupt on level %d\n", m_irq_level); |
| 1305 | if (TRACE_INT) |
| 1306 | { |
| 1307 | switch (m_irq_level) |
| 1308 | { |
| 1309 | case RESET_INT: logerror("tms99xx: **** triggered a RESET interrupt\n"); break; |
| 1310 | case LOAD_INT: logerror("tms99xx: **** triggered a LOAD (NMI) interrupt\n"); break; |
| 1311 | default: logerror("tms99xx: ** triggered an interrupt on level %d\n", m_irq_level); break; |
| 1312 | } |
| 1313 | } |
| 1257 | 1314 | |
| 1258 | 1315 | MPC = 0; |
| 1259 | 1316 | m_first_cycle = m_icount; |
| r26386 | r26387 | |
| 1270 | 1327 | m_ready = m_ready_bufd; // get the latched READY state |
| 1271 | 1328 | m_clock_out_line(CLEAR_LINE); |
| 1272 | 1329 | m_icount--; // This is the only location where we count down the cycles. |
| 1273 | | if (VERBOSE>7) |
| 1330 | if (TRACE_CLOCK) |
| 1274 | 1331 | { |
| 1275 | | if (m_check_ready) LOG("tms99xx: pulse_clock, READY=%d\n", m_ready? 1:0); |
| 1276 | | else LOG("tms99xx: pulse_clock\n"); |
| 1332 | if (m_check_ready) logerror("tms99xx: pulse_clock, READY=%d\n", m_ready? 1:0); |
| 1333 | else logerror("tms99xx: pulse_clock\n"); |
| 1277 | 1334 | } |
| 1278 | 1335 | } |
| 1279 | 1336 | } |
| r26386 | r26387 | |
| 1343 | 1400 | while (!complete) |
| 1344 | 1401 | { |
| 1345 | 1402 | index = (opcode >> 12) & 0x000f; |
| 1346 | | if (VERBOSE>8) LOG("tms99xx: Check next hex digit of instruction %x\n", index); |
| 1403 | if (TRACE_MICRO) logerror("tms99xx: Check next hex digit of instruction %x\n", index); |
| 1347 | 1404 | if (table[index].next_digit != NULL) |
| 1348 | 1405 | { |
| 1349 | 1406 | table = table[index].next_digit; |
| r26386 | r26387 | |
| 1355 | 1412 | if (decoded == NULL) |
| 1356 | 1413 | { |
| 1357 | 1414 | // not found |
| 1358 | | if (VERBOSE>0) LOG("tms99xx: Illegal opcode %04x\n", inst); |
| 1415 | logerror("tms99xx: Illegal opcode %04x\n", inst); |
| 1359 | 1416 | IR = 0; |
| 1360 | 1417 | // This will cause another instruction acquisition in the next machine cycle |
| 1361 | 1418 | // with an asserted IAQ line (can be used to indicate this illegal opcode detection). |
| r26386 | r26387 | |
| 1366 | 1423 | m_program = decoded->prog; |
| 1367 | 1424 | MPC = -1; |
| 1368 | 1425 | m_command = decoded->id; |
| 1369 | | if (VERBOSE>8) LOG("tms99xx: Command decoded as id %d, %s, base opcode %04x\n", m_command, opname[m_command], decoded->opcode); |
| 1426 | if (TRACE_MICRO) logerror("tms99xx: Command decoded as id %d, %s, base opcode %04x\n", m_command, opname[m_command], decoded->opcode); |
| 1370 | 1427 | // Byte operations are either format 1 with the byte flag set |
| 1371 | 1428 | // or format 4 (CRU multi bit operations) with 1-8 bits to transfer. |
| 1372 | 1429 | m_byteop = ((decoded->format==1 && ((IR & 0x1000)!=0)) |
| r26386 | r26387 | |
| 1394 | 1451 | if (m_mem_phase == 1) |
| 1395 | 1452 | { |
| 1396 | 1453 | decode(m_current_value); |
| 1397 | | if (VERBOSE>3) LOG("tms99xx: ===== Next operation %04x (%s) at %04x =====\n", IR, opname[m_command], PC); |
| 1454 | if (TRACE_EXEC) logerror("tms99xx: %04x: %04x (%s)\n", PC, IR, opname[m_command]); |
| 1398 | 1455 | debugger_instruction_hook(this, PC); |
| 1399 | 1456 | PC = (PC + 2) & 0xfffe & m_prgaddr_mask; |
| 1400 | 1457 | // IAQ will be cleared in the main loop |
| r26386 | r26387 | |
| 1418 | 1475 | m_check_ready = true; |
| 1419 | 1476 | m_mem_phase = 2; |
| 1420 | 1477 | m_pass = 2; |
| 1421 | | if (VERBOSE>7) LOG("tms99xx: set address bus %04x\n", m_address); |
| 1478 | if (TRACE_MEM) logerror("tms99xx: set address (r) %04x\n", m_address); |
| 1422 | 1479 | |
| 1423 | 1480 | pulse_clock(1); // Concludes the first cycle |
| 1424 | 1481 | // If READY has been found to be low, the CPU will now stay in the wait state loop |
| r26386 | r26387 | |
| 1430 | 1487 | pulse_clock(1); |
| 1431 | 1488 | m_dbin_line(CLEAR_LINE); |
| 1432 | 1489 | m_mem_phase = 1; // reset to phase 1 |
| 1433 | | if (VERBOSE>7) LOG("tms99xx: memory read %04x -> %04x\n", m_address, m_current_value); |
| 1490 | if (TRACE_MEM) logerror("tms99xx: mem r %04x -> %04x\n", m_address, m_current_value); |
| 1434 | 1491 | } |
| 1435 | 1492 | } |
| 1436 | 1493 | |
| r26386 | r26387 | |
| 1440 | 1497 | { |
| 1441 | 1498 | m_dbin_line(CLEAR_LINE); |
| 1442 | 1499 | // When writing, the data bus is asserted immediately after the address bus |
| 1443 | | if (VERBOSE>7) LOG("tms99xx: set address bus %04x\n", m_address); |
| 1500 | if (TRACE_MEM) logerror("tms99xx: set address (w) %04x\n", m_address); |
| 1444 | 1501 | m_prgspace->set_address(m_address & m_prgaddr_mask & 0xfffe); |
| 1445 | | if (VERBOSE>7) LOG("tms99xx: memory write %04x <- %04x\n", m_address, m_current_value); |
| 1502 | if (TRACE_MEM) logerror("tms99xx: mem w %04x <- %04x\n", m_address, m_current_value); |
| 1446 | 1503 | m_prgspace->write_word(m_address & m_prgaddr_mask & 0xfffe, m_current_value); |
| 1447 | 1504 | m_check_ready = true; |
| 1448 | 1505 | m_mem_phase = 2; |
| r26386 | r26387 | |
| 1560 | 1617 | // Write m_count bits from cru_address |
| 1561 | 1618 | for (int i=0; i < m_count; i++) |
| 1562 | 1619 | { |
| 1563 | | if (VERBOSE>5) LOG("tms99xx: CRU output operation, address %04x, value %d\n", location<<1, value & 0x01); |
| 1620 | if (TRACE_CRU) logerror("tms99xx: CRU output operation, address %04x, value %d\n", location<<1, value & 0x01); |
| 1564 | 1621 | m_cru->write_byte(location, (value & 0x01)); |
| 1565 | 1622 | value >>= 1; |
| 1566 | 1623 | location = (location + 1) & m_cruaddr_mask; |
| r26386 | r26387 | |
| 1580 | 1637 | void tms99xx_device::command_completed() |
| 1581 | 1638 | { |
| 1582 | 1639 | // Pseudo state at the end of the current instruction cycle sequence |
| 1583 | | if (VERBOSE>4) |
| 1640 | if (TRACE_CYCLES) |
| 1584 | 1641 | { |
| 1585 | | LOG("tms99xx: +++++ Instruction %04x (%s) completed", IR, opname[m_command]); |
| 1642 | logerror("tms99xx: ------"); |
| 1586 | 1643 | int cycles = m_first_cycle - m_icount; |
| 1587 | 1644 | // Avoid nonsense values due to expired and resumed main loop |
| 1588 | | if (cycles > 0 && cycles < 10000) LOG(", consumed %d cycles", cycles); |
| 1589 | | LOG(" +++++\n"); |
| 1645 | if (cycles > 0 && cycles < 10000) logerror(" %d cycles", cycles); |
| 1646 | logerror("\n"); |
| 1590 | 1647 | } |
| 1591 | 1648 | m_program = NULL; |
| 1592 | 1649 | } |
| r26386 | r26387 | |
| 1648 | 1705 | set_status_bit(ST_EQ, value1 == value2); |
| 1649 | 1706 | set_status_bit(ST_LH, value1 > value2); |
| 1650 | 1707 | set_status_bit(ST_AGT, (INT16)value1 > (INT16)value2); |
| 1651 | | if (VERBOSE>7) LOG("tms99xx: ST = %04x (val1=%04x, val2=%04x)\n", ST, value1, value2); |
| 1708 | if (TRACE_STATUS) logerror("tms99xx: ST = %04x (val1=%04x, val2=%04x)\n", ST, value1, value2); |
| 1652 | 1709 | } |
| 1653 | 1710 | |
| 1654 | 1711 | /************************************************************************** |
| r26386 | r26387 | |
| 1716 | 1773 | |
| 1717 | 1774 | void tms99xx_device::alu_imm() |
| 1718 | 1775 | { |
| 1719 | | if (VERBOSE>7) LOG("tms99xx: Immediate operand, reading from position %04x\n", PC); |
| 1720 | 1776 | m_value_copy = m_current_value; |
| 1721 | 1777 | m_address_copy = m_address; |
| 1722 | 1778 | m_address = PC; |
| r26386 | r26387 | |
| 1886 | 1942 | compare_and_set_lae(m_current_value, 0); |
| 1887 | 1943 | } |
| 1888 | 1944 | } |
| 1889 | | if (VERBOSE>7) LOG("tms99xx: ST = %04x\n", ST); |
| 1945 | if (TRACE_STATUS) logerror("tms99xx: ST = %04x\n", ST); |
| 1890 | 1946 | break; |
| 1891 | 1947 | } |
| 1892 | 1948 | |
| r26386 | r26387 | |
| 1905 | 1961 | m_address = ((IR >> 5) & 0x001e) + WP; |
| 1906 | 1962 | break; |
| 1907 | 1963 | case 1: // After reading the register (multiplier) |
| 1908 | | if (VERBOSE>7) LOG("tms99xx: Multiply %04x by %04x\n", m_current_value, m_source_value); |
| 1964 | if (TRACE_ALU) logerror("tms99xx: Multiply %04x by %04x\n", m_current_value, m_source_value); |
| 1909 | 1965 | result = (m_source_value & 0x0000ffff) * (m_current_value & 0x0000ffff); |
| 1910 | 1966 | m_current_value = (result >> 16) & 0xffff; |
| 1911 | 1967 | m_value_copy = result & 0xffff; |
| r26386 | r26387 | |
| 1954 | 2010 | // Create full word and perform division |
| 1955 | 2011 | uval32 = (m_value_copy << 16) | m_current_value; |
| 1956 | 2012 | |
| 1957 | | if (VERBOSE>7) LOG("tms99xx: Dividing %08x by %04x\n", uval32, m_source_value); |
| 2013 | if (TRACE_ALU) logerror("tms99xx: Dividing %08x by %04x\n", uval32, m_source_value); |
| 1958 | 2014 | m_current_value = uval32 / m_source_value; |
| 1959 | 2015 | m_value_copy = uval32 % m_source_value; |
| 1960 | 2016 | |
| 1961 | | if (VERBOSE>7) LOG("tms99xx: Quotient %04x, remainder %04x\n", m_current_value, m_value_copy); |
| 2017 | if (TRACE_ALU) logerror("tms99xx: Quotient %04x, remainder %04x\n", m_current_value, m_value_copy); |
| 1962 | 2018 | |
| 1963 | 2019 | m_address = m_address_copy; |
| 1964 | 2020 | |
| r26386 | r26387 | |
| 1984 | 2040 | // Prepare to write the remainder |
| 1985 | 2041 | m_current_value = m_value_copy; |
| 1986 | 2042 | m_address = m_address + 2; |
| 1987 | | if (VERBOSE>7) LOG("tms99xx: ST = %04x (div)\n", ST); |
| 2043 | if (TRACE_STATUS) logerror("tms99xx: ST = %04x (div)\n", ST); |
| 1988 | 2044 | break; |
| 1989 | 2045 | } |
| 1990 | 2046 | pulse_clock(2); |
| r26386 | r26387 | |
| 2002 | 2058 | // and calculate the vector location |
| 2003 | 2059 | // [0010 11xx xx tt SSSS] shift 6 right, then *4 => shift 4 right |
| 2004 | 2060 | m_address = 0x0040 + ((IR >> 4) & 0x003c); |
| 2061 | // Takes some additional cycles |
| 2062 | pulse_clock(4); |
| 2005 | 2063 | break; |
| 2006 | 2064 | case 1: |
| 2007 | 2065 | m_value_copy = WP; // save the old WP |
| r26386 | r26387 | |
| 2132 | 2190 | |
| 2133 | 2191 | void tms99xx_device::alu_x() |
| 2134 | 2192 | { |
| 2135 | | if (VERBOSE>7) LOG("tms99xx: Substituting current command by %04x\n", m_current_value); |
| 2193 | if (TRACE_ALU) logerror("tms99xx: Substituting current command by %04x\n", m_current_value); |
| 2136 | 2194 | decode(m_current_value); |
| 2137 | 2195 | pulse_clock(2); |
| 2138 | 2196 | } |
| r26386 | r26387 | |
| 2152 | 2210 | m_current_value = PC; |
| 2153 | 2211 | PC = m_address & m_prgaddr_mask & 0xfffe; |
| 2154 | 2212 | m_address = WP + 22; |
| 2155 | | if (VERBOSE>7) LOG("tms99xx: Set new PC = %04x\n", PC); |
| 2213 | if (TRACE_ALU) logerror("tms99xx: Set new PC = %04x\n", PC); |
| 2156 | 2214 | pulse_clock(2); |
| 2157 | 2215 | } |
| 2158 | 2216 | |
| r26386 | r26387 | |
| 2180 | 2238 | break; |
| 2181 | 2239 | case 4: |
| 2182 | 2240 | PC = m_current_value & m_prgaddr_mask & 0xfffe; |
| 2183 | | if (VERBOSE>5) LOG("tms9900: Context switch complete; WP=%04x, PC=%04x, ST=%04x\n", WP, PC, ST); |
| 2241 | if (TRACE_ALU) logerror("tms9900: Context switch complete; WP=%04x, PC=%04x, ST=%04x\n", WP, PC, ST); |
| 2184 | 2242 | break; |
| 2185 | 2243 | } |
| 2186 | 2244 | pulse_clock(2); |
| r26386 | r26387 | |
| 2222 | 2280 | } |
| 2223 | 2281 | m_cru_address = m_current_value; |
| 2224 | 2282 | m_value = value; |
| 2225 | | if (VERBOSE>6) LOG("tms99xx: Load CRU address %04x (%d bits), value = %04x\n", m_cru_address, m_count, m_value); |
| 2283 | if (TRACE_CRU) logerror("tms99xx: Load CRU address %04x (%d bits), value = %04x\n", m_cru_address, m_count, m_value); |
| 2226 | 2284 | } |
| 2227 | 2285 | m_state++; |
| 2228 | 2286 | pulse_clock(2); |
| r26386 | r26387 | |
| 2231 | 2289 | void tms99xx_device::alu_stcr() |
| 2232 | 2290 | { |
| 2233 | 2291 | UINT16 value; |
| 2234 | | |
| 2292 | int n = 2; |
| 2235 | 2293 | // For STCR transfers with more than 8 bits, the first CRU bit is |
| 2236 | 2294 | // always put into the least significant bit of the destination word. |
| 2237 | 2295 | // If the address is odd (e.g. 0x1001), it is treated as 0x1000, that is, |
| r26386 | r26387 | |
| 2243 | 2301 | { |
| 2244 | 2302 | case 0: // After getting the destination operand and saving the address/value |
| 2245 | 2303 | m_address = WP + 24; |
| 2304 | n = 0; |
| 2246 | 2305 | break; |
| 2247 | 2306 | case 1: // After getting R12 |
| 2248 | 2307 | m_cru_address = m_current_value; |
| r26386 | r26387 | |
| 2253 | 2312 | value = m_value & 0xffff; |
| 2254 | 2313 | if (m_count < 9) |
| 2255 | 2314 | { |
| 2256 | | if (VERBOSE>6) LOG("tms99xx: Store CRU at %04x (%d bits) in %04x, result = %02x\n", m_cru_address, m_count, m_source_address, value); |
| 2315 | if (TRACE_CRU) logerror("tms99xx: Store CRU at %04x (%d bits) in %04x, result = %02x\n", m_cru_address, m_count, m_source_address, value); |
| 2257 | 2316 | set_status_parity((UINT8)(value & 0xff)); |
| 2258 | 2317 | compare_and_set_lae(value<<8, 0); |
| 2259 | 2318 | if (m_source_even) |
| r26386 | r26387 | |
| 2265 | 2324 | } |
| 2266 | 2325 | else |
| 2267 | 2326 | { |
| 2268 | | if (VERBOSE>6) LOG("tms99xx: Store CRU at %04x (%d bits) in %04x, result = %04x\n", m_cru_address, m_count, m_source_address, value); |
| 2327 | if (TRACE_CRU) logerror("tms99xx: Store CRU at %04x (%d bits) in %04x, result = %04x\n", m_cru_address, m_count, m_source_address, value); |
| 2269 | 2328 | m_current_value = value; |
| 2270 | 2329 | compare_and_set_lae(value, 0); |
| 2271 | 2330 | pulse_clock(2*(5 + (16-m_count))); |
| r26386 | r26387 | |
| 2275 | 2334 | } |
| 2276 | 2335 | |
| 2277 | 2336 | m_state++; |
| 2278 | | pulse_clock(2); |
| 2337 | pulse_clock(n); |
| 2279 | 2338 | } |
| 2280 | 2339 | |
| 2281 | 2340 | void tms99xx_device::alu_sbz_sbo() |
| r26386 | r26387 | |
| 2311 | 2370 | break; |
| 2312 | 2371 | case 2: |
| 2313 | 2372 | set_status_bit(ST_EQ, m_value!=0); |
| 2314 | | if (VERBOSE>7) LOG("tms99xx: ST = %04x\n", ST); |
| 2373 | if (TRACE_STATUS) logerror("tms99xx: ST = %04x\n", ST); |
| 2315 | 2374 | break; |
| 2316 | 2375 | } |
| 2317 | 2376 | m_state++; |
| r26386 | r26387 | |
| 2367 | 2426 | cond = ((ST & ST_OP)!=0); |
| 2368 | 2427 | break; |
| 2369 | 2428 | } |
| 2370 | | pulse_clock(2); |
| 2371 | 2429 | if (!cond) |
| 2372 | 2430 | { |
| 2373 | | if (VERBOSE>7) LOG("tms99xx: Jump condition false\n"); |
| 2431 | if (TRACE_ALU) logerror("tms99xx: Jump condition false\n"); |
| 2374 | 2432 | MPC+=1; // skip next ALU call |
| 2375 | 2433 | } |
| 2376 | 2434 | else |
| 2377 | | if (VERBOSE>7) LOG("tms99xx: Jump condition true\n"); |
| 2435 | if (TRACE_ALU) logerror("tms99xx: Jump condition true\n"); |
| 2378 | 2436 | } |
| 2379 | 2437 | else |
| 2380 | 2438 | { |
| 2381 | 2439 | displacement = (IR & 0xff); |
| 2382 | 2440 | PC = (PC + (displacement<<1)) & m_prgaddr_mask & 0xfffe; |
| 2383 | | pulse_clock(2); |
| 2384 | 2441 | } |
| 2385 | 2442 | m_state++; |
| 2443 | pulse_clock(2); |
| 2386 | 2444 | } |
| 2387 | 2445 | |
| 2388 | 2446 | void tms99xx_device::alu_shift() |
| r26386 | r26387 | |
| 2413 | 2471 | } |
| 2414 | 2472 | else |
| 2415 | 2473 | { |
| 2416 | | if (VERBOSE>8) LOG("tms99xx: Shift operation gets count from R0\n"); |
| 2474 | if (TRACE_ALU) logerror("tms99xx: Shift operation gets count from R0\n"); |
| 2417 | 2475 | } |
| 2418 | 2476 | break; |
| 2419 | 2477 | case 2: |
| r26386 | r26387 | |
| 2455 | 2513 | set_status_bit(ST_OV, overflow); |
| 2456 | 2514 | compare_and_set_lae(m_current_value, 0); |
| 2457 | 2515 | m_address = m_address_saved; // Register address |
| 2458 | | if (VERBOSE>7) LOG("tms99xx: ST = %04x (val=%04x)\n", ST, m_current_value); |
| 2516 | if (TRACE_STATUS) logerror("tms99xx: ST = %04x (val=%04x)\n", ST, m_current_value); |
| 2459 | 2517 | break; |
| 2460 | 2518 | } |
| 2461 | 2519 | m_state++; |
| r26386 | r26387 | |
| 2507 | 2565 | void tms99xx_device::alu_limi() |
| 2508 | 2566 | { |
| 2509 | 2567 | ST = (ST & 0xfff0) | (m_current_value & 0x000f); |
| 2510 | | if (VERBOSE>7) LOG("tms99xx: ST = %04x\n", ST); |
| 2568 | if (TRACE_STATUS) logerror("tms99xx: ST = %04x\n", ST); |
| 2511 | 2569 | pulse_clock(2); |
| 2512 | 2570 | } |
| 2513 | 2571 | |
| r26386 | r26387 | |
| 2520 | 2578 | |
| 2521 | 2579 | void tms99xx_device::alu_external() |
| 2522 | 2580 | { |
| 2523 | | pulse_clock(10); |
| 2524 | | |
| 2525 | 2581 | // Call some possibly attached external device |
| 2526 | 2582 | // We pass the bit pattern of the address bus to the external function |
| 2527 | 2583 | |
| r26386 | r26387 | |
| 2535 | 2591 | m_idle_state = true; |
| 2536 | 2592 | |
| 2537 | 2593 | m_external_operation((IR >> 5) & 0x07, 1); |
| 2594 | pulse_clock(2); |
| 2538 | 2595 | } |
| 2539 | 2596 | |
| 2540 | 2597 | void tms99xx_device::alu_rtwp() |
| r26386 | r26387 | |
| 2543 | 2600 | { |
| 2544 | 2601 | case 0: |
| 2545 | 2602 | m_address = WP + 30; // R15 |
| 2603 | pulse_clock(2); |
| 2546 | 2604 | break; |
| 2547 | 2605 | case 1: |
| 2548 | 2606 | ST = m_current_value; |
| r26386 | r26387 | |
| 2554 | 2612 | break; |
| 2555 | 2613 | case 3: |
| 2556 | 2614 | WP = m_current_value & m_prgaddr_mask & 0xfffe; |
| 2615 | pulse_clock(2); |
| 2557 | 2616 | break; |
| 2558 | 2617 | } |
| 2559 | 2618 | m_state++; |
| 2560 | | pulse_clock(2); |
| 2561 | 2619 | } |
| 2562 | 2620 | |
| 2563 | 2621 | |
| 2564 | 2622 | void tms99xx_device::alu_int() |
| 2565 | 2623 | { |
| 2566 | | if (VERBOSE>7) LOG("tms99xx: INT state %d; irq_level %d\n", m_state, m_irq_level); |
| 2624 | if (TRACE_EMU) logerror("tms99xx: INT state %d; irq_level %d\n", m_state, m_irq_level); |
| 2567 | 2625 | switch (m_state) |
| 2568 | 2626 | { |
| 2569 | 2627 | case 0: |
| r26386 | r26387 | |
| 2598 | 2656 | break; |
| 2599 | 2657 | case 4: |
| 2600 | 2658 | m_address = (m_address_copy + 2) & 0xfffe & m_prgaddr_mask; |
| 2601 | | if (VERBOSE>7) LOG("tms99xx: read from %04x\n", m_address); |
| 2659 | if (TRACE_ALU) logerror("tms99xx: read from %04x\n", m_address); |
| 2602 | 2660 | break; |
| 2603 | 2661 | case 5: |
| 2604 | 2662 | PC = m_current_value & m_prgaddr_mask & 0xfffe; |