/*
|
/*
|
* cpu.c -- execute instructions
|
* cpu.c -- execute instructions
|
*/
|
*/
|
|
|
|
|
#include "common.h"
|
#include "common.h"
|
#include "stdarg.h"
|
#include "stdarg.h"
|
#include "romlib.h"
|
#include "romlib.h"
|
#include "instr.h"
|
#include "instr.h"
|
#include "cpu.h"
|
#include "cpu.h"
|
#include "mmu.h"
|
#include "mmu.h"
|
#include "start.h"
|
#include "start.h"
|
|
|
|
|
#define RR(n) r[n]
|
#define RR(n) r[n]
|
#define WR(n,d) ((void) ((n) != 0 ? r[n] = (d) : (d)))
|
#define WR(n,d) ((void) ((n) != 0 ? r[n] = (d) : (d)))
|
|
|
#define BREAK (OP_TRAP << 26)
|
#define BREAK (OP_TRAP << 26)
|
|
|
|
|
/**************************************************************/
|
/**************************************************************/
|
|
|
|
|
static Word pc; /* program counter */
|
static Word pc; /* program counter */
|
static Word psw; /* processor status word */
|
static Word psw; /* processor status word */
|
static Word r[32]; /* general purpose registers */
|
static Word r[32]; /* general purpose registers */
|
|
|
static Bool breakSet; /* breakpoint set if true */
|
static Bool breakSet; /* breakpoint set if true */
|
static Word breakAddr; /* if breakSet, this is where */
|
static Word breakAddr; /* if breakSet, this is where */
|
|
|
|
|
/**************************************************************/
|
/**************************************************************/
|
|
|
|
|
Word cpuGetPC(void) {
|
Word cpuGetPC(void) {
|
return pc;
|
return pc;
|
}
|
}
|
|
|
|
|
void cpuSetPC(Word addr) {
|
void cpuSetPC(Word addr) {
|
pc = addr;
|
pc = addr;
|
}
|
}
|
|
|
|
|
Word cpuGetReg(int regnum) {
|
Word cpuGetReg(int regnum) {
|
return RR(regnum & 0x1F);
|
return RR(regnum & 0x1F);
|
}
|
}
|
|
|
|
|
void cpuSetReg(int regnum, Word value) {
|
void cpuSetReg(int regnum, Word value) {
|
WR(regnum & 0x1F, value);
|
WR(regnum & 0x1F, value);
|
}
|
}
|
|
|
|
|
Word cpuGetPSW(void) {
|
Word cpuGetPSW(void) {
|
return psw;
|
return psw;
|
}
|
}
|
|
|
|
|
void cpuSetPSW(Word value) {
|
void cpuSetPSW(Word value) {
|
psw = value;
|
psw = value;
|
}
|
}
|
|
|
|
|
Bool cpuTestBreak(void) {
|
Bool cpuTestBreak(void) {
|
return breakSet;
|
return breakSet;
|
}
|
}
|
|
|
|
|
Word cpuGetBreak(void) {
|
Word cpuGetBreak(void) {
|
return breakAddr;
|
return breakAddr;
|
}
|
}
|
|
|
|
|
void cpuSetBreak(Word addr) {
|
void cpuSetBreak(Word addr) {
|
breakAddr = addr;
|
breakAddr = addr;
|
breakSet = true;
|
breakSet = true;
|
}
|
}
|
|
|
|
|
void cpuResetBreak(void) {
|
void cpuResetBreak(void) {
|
breakSet = false;
|
breakSet = false;
|
}
|
}
|
|
|
|
|
/**************************************************************/
|
/**************************************************************/
|
|
|
|
|
static char *cause[32] = {
|
static char *cause[32] = {
|
/* 0 */ "serial line 0 xmt interrupt",
|
/* 0 */ "serial line 0 xmt interrupt",
|
/* 1 */ "serial line 0 rcv interrupt",
|
/* 1 */ "serial line 0 rcv interrupt",
|
/* 2 */ "serial line 1 xmt interrupt",
|
/* 2 */ "serial line 1 xmt interrupt",
|
/* 3 */ "serial line 1 rcv interrupt",
|
/* 3 */ "serial line 1 rcv interrupt",
|
/* 4 */ "keyboard interrupt",
|
/* 4 */ "keyboard interrupt",
|
/* 5 */ "unknown interrupt",
|
/* 5 */ "unknown interrupt",
|
/* 6 */ "unknown interrupt",
|
/* 6 */ "unknown interrupt",
|
/* 7 */ "unknown interrupt",
|
/* 7 */ "unknown interrupt",
|
/* 8 */ "disk interrupt",
|
/* 8 */ "disk interrupt",
|
/* 9 */ "unknown interrupt",
|
/* 9 */ "unknown interrupt",
|
/* 10 */ "unknown interrupt",
|
/* 10 */ "unknown interrupt",
|
/* 11 */ "unknown interrupt",
|
/* 11 */ "unknown interrupt",
|
/* 12 */ "unknown interrupt",
|
/* 12 */ "unknown interrupt",
|
/* 13 */ "unknown interrupt",
|
/* 13 */ "unknown interrupt",
|
/* 14 */ "timer 0 interrupt",
|
/* 14 */ "timer 0 interrupt",
|
/* 15 */ "timer 1 interrupt",
|
/* 15 */ "timer 1 interrupt",
|
/* 16 */ "bus timeout exception",
|
/* 16 */ "bus timeout exception",
|
/* 17 */ "illegal instruction exception",
|
/* 17 */ "illegal instruction exception",
|
/* 18 */ "privileged instruction exception",
|
/* 18 */ "privileged instruction exception",
|
/* 19 */ "divide instruction exception",
|
/* 19 */ "divide instruction exception",
|
/* 20 */ "trap instruction exception",
|
/* 20 */ "trap instruction exception",
|
/* 21 */ "TLB miss exception",
|
/* 21 */ "TLB miss exception",
|
/* 22 */ "TLB write exception",
|
/* 22 */ "TLB write exception",
|
/* 23 */ "TLB invalid exception",
|
/* 23 */ "TLB invalid exception",
|
/* 24 */ "illegal address exception",
|
/* 24 */ "illegal address exception",
|
/* 25 */ "privileged address exception",
|
/* 25 */ "privileged address exception",
|
/* 26 */ "unknown exception",
|
/* 26 */ "unknown exception",
|
/* 27 */ "unknown exception",
|
/* 27 */ "unknown exception",
|
/* 28 */ "unknown exception",
|
/* 28 */ "unknown exception",
|
/* 29 */ "unknown exception",
|
/* 29 */ "unknown exception",
|
/* 30 */ "unknown exception",
|
/* 30 */ "unknown exception",
|
/* 31 */ "unknown exception"
|
/* 31 */ "unknown exception"
|
};
|
};
|
|
|
|
|
char *exceptionToString(int exception) {
|
char *exceptionToString(int exception) {
|
if (exception < 0 ||
|
if (exception < 0 ||
|
exception >= sizeof(cause)/sizeof(cause[0])) {
|
exception >= sizeof(cause)/sizeof(cause[0])) {
|
return "<exception number out of bounds>";
|
return "<exception number out of bounds>";
|
}
|
}
|
return cause[exception];
|
return cause[exception];
|
}
|
}
|
|
|
|
|
/**************************************************************/
|
/**************************************************************/
|
|
|
|
|
static Byte stepType[64] = {
|
static Byte stepType[64] = {
|
/* 0 1 2 3 4 5 6 7 8 9 A B C D E F */
|
/* 0 1 2 3 4 5 6 7 8 9 A B C D E F */
|
/* 0x00 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
/* 0x00 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
/* 0x10 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
/* 0x10 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
/* 0x20 */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 4, 3, 4, 1, 0,
|
/* 0x20 */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 4, 3, 4, 1, 0,
|
/* 0x30 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
/* 0x30 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
};
|
};
|
|
|
|
|
static Bool evalCond(int cc, Word a, Word b) {
|
static Bool evalCond(int cc, Word a, Word b) {
|
switch (cc) {
|
switch (cc) {
|
case 0:
|
case 0:
|
/* equal */
|
/* equal */
|
if (a == b) {
|
if (a == b) {
|
return true;
|
return true;
|
}
|
}
|
break;
|
break;
|
case 1:
|
case 1:
|
/* not equal */
|
/* not equal */
|
if (a != b) {
|
if (a != b) {
|
return true;
|
return true;
|
}
|
}
|
break;
|
break;
|
case 2:
|
case 2:
|
/* less or equal (signed) */
|
/* less or equal (signed) */
|
if ((signed int) a <= (signed int) b) {
|
if ((signed int) a <= (signed int) b) {
|
return true;
|
return true;
|
}
|
}
|
break;
|
break;
|
case 3:
|
case 3:
|
/* less or equal (unsigned) */
|
/* less or equal (unsigned) */
|
if (a <= b) {
|
if (a <= b) {
|
return true;
|
return true;
|
}
|
}
|
break;
|
break;
|
case 4:
|
case 4:
|
/* less than (signed) */
|
/* less than (signed) */
|
if ((signed int) a < (signed int) b) {
|
if ((signed int) a < (signed int) b) {
|
return true;
|
return true;
|
}
|
}
|
break;
|
break;
|
case 5:
|
case 5:
|
/* less than (unsigned) */
|
/* less than (unsigned) */
|
if (a < b) {
|
if (a < b) {
|
return true;
|
return true;
|
}
|
}
|
break;
|
break;
|
case 6:
|
case 6:
|
/* greater or equal (signed) */
|
/* greater or equal (signed) */
|
if ((signed int) a >= (signed int) b) {
|
if ((signed int) a >= (signed int) b) {
|
return true;
|
return true;
|
}
|
}
|
break;
|
break;
|
case 7:
|
case 7:
|
/* greater or equal (unsigned) */
|
/* greater or equal (unsigned) */
|
if (a >= b) {
|
if (a >= b) {
|
return true;
|
return true;
|
}
|
}
|
break;
|
break;
|
case 8:
|
case 8:
|
/* greater than (signed) */
|
/* greater than (signed) */
|
if ((signed int) a > (signed int) b) {
|
if ((signed int) a > (signed int) b) {
|
return true;
|
return true;
|
}
|
}
|
break;
|
break;
|
case 9:
|
case 9:
|
/* greater than (unsigned) */
|
/* greater than (unsigned) */
|
if (a > b) {
|
if (a > b) {
|
return true;
|
return true;
|
}
|
}
|
break;
|
break;
|
default:
|
default:
|
/* this should never happen */
|
/* this should never happen */
|
printf("cannot compute condition code %d\n", cc);
|
printf("cannot compute condition code %d\n", cc);
|
break;
|
break;
|
}
|
}
|
return false;
|
return false;
|
}
|
}
|
|
|
|
|
void cpuStep(void) {
|
void cpuStep(void) {
|
Word instr;
|
Word instr;
|
int opcode;
|
int opcode;
|
int reg1, reg2;
|
int reg1, reg2;
|
Half immed;
|
Half immed;
|
Word offset;
|
Word offset;
|
Word nextAddr;
|
Word nextAddr;
|
Word nextInstr;
|
Word nextInstr;
|
int i;
|
int i;
|
MonitorState stepState;
|
MonitorState stepState;
|
MonitorState *origReturn;
|
MonitorState *origReturn;
|
|
|
instr = mmuReadWord(pc);
|
instr = mmuReadWord(pc);
|
opcode = (instr >> 26) & 0x3F;
|
opcode = (instr >> 26) & 0x3F;
|
reg1 = (instr >> 21) & 0x1F;
|
reg1 = (instr >> 21) & 0x1F;
|
reg2 = (instr >> 16) & 0x1F;
|
reg2 = (instr >> 16) & 0x1F;
|
immed = instr & 0x0000FFFF;
|
immed = instr & 0x0000FFFF;
|
offset = instr & 0x03FFFFFF;
|
offset = instr & 0x03FFFFFF;
|
switch (stepType[opcode]) {
|
switch (stepType[opcode]) {
|
case 1:
|
case 1:
|
/* next instruction follows current one immediately */
|
/* next instruction follows current one immediately */
|
nextAddr = pc + 4;
|
nextAddr = pc + 4;
|
break;
|
break;
|
case 2:
|
case 2:
|
/* next instruction conditionally reached by PC relative branch */
|
/* next instruction conditionally reached by PC relative branch */
|
nextAddr = pc + 4;
|
nextAddr = pc + 4;
|
if (evalCond(opcode - OP_BEQ, RR(reg1), RR(reg2))) {
|
if (evalCond(opcode - OP_BEQ, RR(reg1), RR(reg2))) {
|
nextAddr += SEXT16(immed) << 2;
|
nextAddr += SEXT16(immed) << 2;
|
}
|
}
|
break;
|
break;
|
case 3:
|
case 3:
|
/* next instruction reached by PC relative jump */
|
/* next instruction reached by PC relative jump */
|
nextAddr = pc + 4 + (SEXT26(offset) << 2);
|
nextAddr = pc + 4 + (SEXT26(offset) << 2);
|
break;
|
break;
|
case 4:
|
case 4:
|
/* next instruction reached by jump to register contents */
|
/* next instruction reached by jump to register contents */
|
nextAddr = RR(reg1) & 0xFFFFFFFC;
|
nextAddr = RR(reg1) & 0xFFFFFFFC;
|
break;
|
break;
|
default:
|
default:
|
printf("cannot single-step instruction with opcode 0x%02X\n",
|
printf("cannot single-step instruction with opcode 0x%02X\n",
|
opcode);
|
opcode);
|
return;
|
return;
|
}
|
}
|
nextInstr = mmuReadWord(nextAddr);
|
nextInstr = mmuReadWord(nextAddr);
|
mmuWriteWord(nextAddr, BREAK);
|
mmuWriteWord(nextAddr, BREAK);
|
for (i = 0; i < 32; i++) {
|
for (i = 0; i < 32; i++) {
|
userContext.reg[i] = RR(i);
|
userContext.reg[i] = RR(i);
|
}
|
}
|
userContext.reg[30] = pc;
|
userContext.reg[30] = pc;
|
userContext.psw = psw;
|
userContext.psw = psw;
|
userContext.tlbIndex = mmuGetIndex();
|
userContext.tlbIndex = mmuGetIndex();
|
userContext.tlbHi = mmuGetEntryHi();
|
userContext.tlbHi = mmuGetEntryHi();
|
userContext.tlbLo = mmuGetEntryLo();
|
userContext.tlbLo = mmuGetEntryLo();
|
userContext.badAddr = mmuGetBadAddr();
|
userContext.badAddr = mmuGetBadAddr();
|
userContext.badAccs = mmuGetBadAccs();
|
userContext.badAccs = mmuGetBadAccs();
|
if (saveState(&stepState)) {
|
if (saveState(&stepState)) {
|
origReturn = monitorReturn;
|
origReturn = monitorReturn;
|
monitorReturn = &stepState;
|
monitorReturn = &stepState;
|
resume();
|
resume();
|
}
|
}
|
monitorReturn = origReturn;
|
monitorReturn = origReturn;
|
for (i = 0; i < 32; i++) {
|
for (i = 0; i < 32; i++) {
|
WR(i, userContext.reg[i]);
|
WR(i, userContext.reg[i]);
|
}
|
}
|
pc = userContext.reg[30];
|
pc = userContext.reg[30];
|
psw = userContext.psw;
|
psw = userContext.psw;
|
mmuSetIndex(userContext.tlbIndex);
|
mmuSetIndex(userContext.tlbIndex);
|
mmuSetEntryHi(userContext.tlbHi);
|
mmuSetEntryHi(userContext.tlbHi);
|
mmuSetEntryLo(userContext.tlbLo);
|
mmuSetEntryLo(userContext.tlbLo);
|
mmuSetBadAddr(userContext.badAddr);
|
mmuSetBadAddr(userContext.badAddr);
|
mmuSetBadAccs(userContext.badAccs);
|
mmuSetBadAccs(userContext.badAccs);
|
mmuWriteWord(nextAddr, nextInstr);
|
mmuWriteWord(nextAddr, nextInstr);
|
if (nextAddr == pc) {
|
if (nextAddr == pc) {
|
return;
|
return;
|
}
|
}
|
if ((psw & PSW_PRIO_MASK) >> 16 == 21 &&
|
if ((psw & PSW_PRIO_MASK) >> 16 == 21 &&
|
(mmuGetEntryHi() & 0x80000000) == 0) {
|
(mmuGetEntryHi() & 0x80000000) == 0) {
|
/* TLB user miss */
|
/* TLB user miss */
|
if (umsrPtr == 0x00000000) {
|
|
printf("unexpected TLB user miss exception occurred\n");
|
printf("unexpected TLB user miss exception occurred\n");
|
return;
|
return;
|
}
|
|
pc = umsrPtr;
|
|
} else {
|
} else {
|
/* any other exception */
|
/* any other exception */
|
if (isrPtr == 0x00000000) {
|
|
printf("unexpected %s occurred\n",
|
printf("unexpected %s occurred\n",
|
exceptionToString((psw & PSW_PRIO_MASK) >> 16));
|
exceptionToString((psw & PSW_PRIO_MASK) >> 16));
|
return;
|
return;
|
}
|
}
|
pc = isrPtr;
|
|
}
|
|
}
|
}
|
|
|
|
|
void cpuRun(void) {
|
void cpuRun(void) {
|
Word instr;
|
Word instr;
|
int i;
|
int i;
|
MonitorState runState;
|
MonitorState runState;
|
MonitorState *origReturn;
|
MonitorState *origReturn;
|
|
|
if (breakSet && breakAddr == pc) {
|
if (breakSet && breakAddr == pc) {
|
/* single-step one instruction */
|
/* single-step one instruction */
|
cpuStep();
|
cpuStep();
|
}
|
}
|
while (1) {
|
while (1) {
|
if (breakSet) {
|
if (breakSet) {
|
instr = mmuReadWord(breakAddr);
|
instr = mmuReadWord(breakAddr);
|
mmuWriteWord(breakAddr, BREAK);
|
mmuWriteWord(breakAddr, BREAK);
|
}
|
}
|
for (i = 0; i < 32; i++) {
|
for (i = 0; i < 32; i++) {
|
userContext.reg[i] = RR(i);
|
userContext.reg[i] = RR(i);
|
}
|
}
|
userContext.reg[30] = pc;
|
userContext.reg[30] = pc;
|
userContext.psw = psw;
|
userContext.psw = psw;
|
userContext.tlbIndex = mmuGetIndex();
|
userContext.tlbIndex = mmuGetIndex();
|
userContext.tlbHi = mmuGetEntryHi();
|
userContext.tlbHi = mmuGetEntryHi();
|
userContext.tlbLo = mmuGetEntryLo();
|
userContext.tlbLo = mmuGetEntryLo();
|
userContext.badAddr = mmuGetBadAddr();
|
userContext.badAddr = mmuGetBadAddr();
|
userContext.badAccs = mmuGetBadAccs();
|
userContext.badAccs = mmuGetBadAccs();
|
if (saveState(&runState)) {
|
if (saveState(&runState)) {
|
origReturn = monitorReturn;
|
origReturn = monitorReturn;
|
monitorReturn = &runState;
|
monitorReturn = &runState;
|
resume();
|
resume();
|
}
|
}
|
monitorReturn = origReturn;
|
monitorReturn = origReturn;
|
for (i = 0; i < 32; i++) {
|
for (i = 0; i < 32; i++) {
|
WR(i, userContext.reg[i]);
|
WR(i, userContext.reg[i]);
|
}
|
}
|
pc = userContext.reg[30];
|
pc = userContext.reg[30];
|
psw = userContext.psw;
|
psw = userContext.psw;
|
mmuSetIndex(userContext.tlbIndex);
|
mmuSetIndex(userContext.tlbIndex);
|
mmuSetEntryHi(userContext.tlbHi);
|
mmuSetEntryHi(userContext.tlbHi);
|
mmuSetEntryLo(userContext.tlbLo);
|
mmuSetEntryLo(userContext.tlbLo);
|
mmuSetBadAddr(userContext.badAddr);
|
mmuSetBadAddr(userContext.badAddr);
|
mmuSetBadAccs(userContext.badAccs);
|
mmuSetBadAccs(userContext.badAccs);
|
if (breakSet) {
|
if (breakSet) {
|
mmuWriteWord(breakAddr, instr);
|
mmuWriteWord(breakAddr, instr);
|
}
|
}
|
if (breakSet && breakAddr == pc) {
|
if (breakSet && breakAddr == pc) {
|
return;
|
return;
|
}
|
}
|
if ((psw & PSW_PRIO_MASK) >> 16 == 21 &&
|
if ((psw & PSW_PRIO_MASK) >> 16 == 21 &&
|
(mmuGetEntryHi() & 0x80000000) == 0) {
|
(mmuGetEntryHi() & 0x80000000) == 0) {
|
/* TLB user miss */
|
/* TLB user miss */
|
if (umsrPtr == 0x00000000) {
|
|
printf("unexpected TLB user miss exception occurred\n");
|
printf("unexpected TLB user miss exception occurred\n");
|
return;
|
return;
|
}
|
|
pc = umsrPtr;
|
|
} else {
|
} else {
|
/* any other exception */
|
/* any other exception */
|
if (isrPtr == 0x00000000) {
|
|
printf("unexpected %s occurred\n",
|
printf("unexpected %s occurred\n",
|
exceptionToString((psw & PSW_PRIO_MASK) >> 16));
|
exceptionToString((psw & PSW_PRIO_MASK) >> 16));
|
return;
|
return;
|
}
|
}
|
pc = isrPtr;
|
|
}
|
|
}
|
}
|
}
|
}
|
|
|