/*
|
/*
|
* cpu.c -- CPU simulation
|
* cpu.c -- CPU simulation
|
*/
|
*/
|
|
|
|
|
#include <stdio.h>
|
#include <stdio.h>
|
#include <stdlib.h>
|
#include <stdlib.h>
|
#include <string.h>
|
#include <string.h>
|
#include <setjmp.h>
|
#include <setjmp.h>
|
|
|
#include "common.h"
|
#include "common.h"
|
#include "console.h"
|
#include "console.h"
|
#include "error.h"
|
#include "error.h"
|
#include "except.h"
|
#include "except.h"
|
#include "instr.h"
|
#include "instr.h"
|
#include "cpu.h"
|
#include "cpu.h"
|
#include "mmu.h"
|
#include "mmu.h"
|
#include "timer.h"
|
#include "timer.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 V (psw & PSW_V)
|
#define V (psw & PSW_V)
|
#define UM (psw & PSW_UM)
|
#define UM (psw & PSW_UM)
|
#define PUM (psw & PSW_PUM)
|
#define PUM (psw & PSW_PUM)
|
#define OUM (psw & PSW_OUM)
|
#define OUM (psw & PSW_OUM)
|
#define IE (psw & PSW_IE)
|
#define IE (psw & PSW_IE)
|
#define PIE (psw & PSW_PIE)
|
#define PIE (psw & PSW_PIE)
|
#define OIE (psw & PSW_OIE)
|
#define OIE (psw & PSW_OIE)
|
|
|
|
|
/**************************************************************/
|
/**************************************************************/
|
|
|
|
|
static Bool debugIRQ = false; /* set to true if debugging IRQs */
|
static Bool debugIRQ = false; /* set to true if debugging IRQs */
|
|
|
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 int instrCount; /* counts instrs for timer tick */
|
|
static unsigned irqPending; /* one bit for each pending IRQ */
|
static unsigned irqPending; /* one bit for each pending IRQ */
|
|
|
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 */
|
|
|
static Word total; /* counts total number of instrs */
|
static Word total; /* counts total number of instrs */
|
|
|
static Bool run; /* CPU runs continuously if true */
|
static Bool run; /* CPU runs continuously if true */
|
|
|
static Word startAddr; /* start of ROM (or start of RAM, */
|
static Word startAddr; /* start of ROM (or start of RAM, */
|
/* in case a program was loaded) */
|
/* in case a program was loaded) */
|
|
|
|
|
/**************************************************************/
|
/**************************************************************/
|
|
|
|
|
static void handleRealTimeTasks(void) {
|
|
/* handle 'real-time' tasks */
|
|
if (++instrCount == INSTRS_PER_MSEC) {
|
|
instrCount = 0;
|
|
timerTick();
|
|
}
|
|
}
|
|
|
|
|
|
static void handleInterrupts(void) {
|
static void handleInterrupts(void) {
|
unsigned irqMask;
|
unsigned irqMask;
|
unsigned irqSeen;
|
unsigned irqSeen;
|
int priority;
|
int priority;
|
|
|
/* handle exceptions and interrupts */
|
/* handle exceptions and interrupts */
|
if (irqPending == 0) {
|
if (irqPending == 0) {
|
/* no exception or interrupt pending */
|
/* no exception or interrupt pending */
|
return;
|
return;
|
}
|
}
|
/* at least one exception or interrupt is pending */
|
/* at least one exception or interrupt is pending */
|
irqMask = ~PSW_IRQ_MASK | (psw & PSW_IRQ_MASK);
|
irqMask = ~PSW_IRQ_MASK | (psw & PSW_IRQ_MASK);
|
if (debugIRQ) {
|
if (debugIRQ) {
|
cPrintf("**** IRQ = 0x%08X ****\n", irqPending);
|
cPrintf("**** IRQ = 0x%08X ****\n", irqPending);
|
cPrintf("**** MASK = 0x%08X ****\n", irqMask);
|
cPrintf("**** MASK = 0x%08X ****\n", irqMask);
|
}
|
}
|
irqSeen = irqPending & irqMask;
|
irqSeen = irqPending & irqMask;
|
if (irqSeen == 0) {
|
if (irqSeen == 0) {
|
/* none that gets through */
|
/* none that gets through */
|
return;
|
return;
|
}
|
}
|
/* determine the one with the highest priority */
|
/* determine the one with the highest priority */
|
for (priority = 31; priority >= 0; priority--) {
|
for (priority = 31; priority >= 0; priority--) {
|
if ((irqSeen & ((unsigned) 1 << priority)) != 0) {
|
if ((irqSeen & ((unsigned) 1 << priority)) != 0) {
|
/* highest priority among visible ones found */
|
/* highest priority among visible ones found */
|
break;
|
break;
|
}
|
}
|
}
|
}
|
/* acknowledge exception, or interrupt if enabled */
|
/* acknowledge exception, or interrupt if enabled */
|
if (priority >= 16 || IE != 0) {
|
if (priority >= 16 || IE != 0) {
|
if (priority >= 16) {
|
if (priority >= 16) {
|
/* clear corresponding bit in irqPending vector */
|
/* clear corresponding bit in irqPending vector */
|
/* only done for exceptions, since interrupts are level-sensitive */
|
/* only done for exceptions, since interrupts are level-sensitive */
|
irqPending &= ~((unsigned) 1 << priority);
|
irqPending &= ~((unsigned) 1 << priority);
|
}
|
}
|
/* copy and reset interrupt enable bit in PSW */
|
/* copy and reset interrupt enable bit in PSW */
|
if (PIE != 0) {
|
if (PIE != 0) {
|
psw |= PSW_OIE;
|
psw |= PSW_OIE;
|
} else {
|
} else {
|
psw &= ~PSW_OIE;
|
psw &= ~PSW_OIE;
|
}
|
}
|
if (IE != 0) {
|
if (IE != 0) {
|
psw |= PSW_PIE;
|
psw |= PSW_PIE;
|
} else {
|
} else {
|
psw &= ~PSW_PIE;
|
psw &= ~PSW_PIE;
|
}
|
}
|
psw &= ~PSW_IE;
|
psw &= ~PSW_IE;
|
/* copy and reset user mode enable bit in PSW */
|
/* copy and reset user mode enable bit in PSW */
|
if (PUM != 0) {
|
if (PUM != 0) {
|
psw |= PSW_OUM;
|
psw |= PSW_OUM;
|
} else {
|
} else {
|
psw &= ~PSW_OUM;
|
psw &= ~PSW_OUM;
|
}
|
}
|
if (UM != 0) {
|
if (UM != 0) {
|
psw |= PSW_PUM;
|
psw |= PSW_PUM;
|
} else {
|
} else {
|
psw &= ~PSW_PUM;
|
psw &= ~PSW_PUM;
|
}
|
}
|
psw &= ~PSW_UM;
|
psw &= ~PSW_UM;
|
/* reflect priority in PSW */
|
/* reflect priority in PSW */
|
psw &= ~PSW_PRIO_MASK;
|
psw &= ~PSW_PRIO_MASK;
|
psw |= priority << PSW_PRIO_SHFT;
|
psw |= priority << PSW_PRIO_SHFT;
|
/* save interrupt return address and start service routine */
|
/* save interrupt return address and start service routine */
|
WR(30, pc);
|
WR(30, pc);
|
if (V == 0) {
|
if (V == 0) {
|
/* exceptions and interrupts are vectored to ROM */
|
/* exceptions and interrupts are vectored to ROM */
|
pc = 0xC0000000 | ROM_BASE;
|
pc = 0xC0000000 | ROM_BASE;
|
} else {
|
} else {
|
/* exceptions and interrupts are vectored to RAM */
|
/* exceptions and interrupts are vectored to RAM */
|
pc = 0xC0000000;
|
pc = 0xC0000000;
|
}
|
}
|
if (priority == EXC_TLB_MISS &&
|
if (priority == EXC_TLB_MISS &&
|
(mmuGetBadAddr() & 0x80000000) == 0) {
|
(mmuGetBadAddr() & 0x80000000) == 0) {
|
/* user TLB miss exception */
|
/* user TLB miss exception */
|
pc |= 0x00000008;
|
pc |= 0x00000008;
|
} else {
|
} else {
|
/* any other exception or interrupt */
|
/* any other exception or interrupt */
|
pc |= 0x00000004;
|
pc |= 0x00000004;
|
}
|
}
|
}
|
}
|
}
|
}
|
|
|
|
|
static void execNextInstruction(void) {
|
static void execNextInstruction(void) {
|
Word instr;
|
Word instr;
|
Word next;
|
Word next;
|
int op, reg1, reg2, reg3;
|
int op, reg1, reg2, reg3;
|
Half immed;
|
Half immed;
|
Word offset;
|
Word offset;
|
int scnt;
|
int scnt;
|
Word smsk;
|
Word smsk;
|
Word aux;
|
Word aux;
|
|
|
/* count the instruction */
|
/* count the instruction */
|
total++;
|
total++;
|
/* fetch the instruction */
|
/* fetch the instruction */
|
instr = mmuReadWord(pc, UM);
|
instr = mmuReadWord(pc, UM);
|
/* decode the instruction */
|
/* decode the instruction */
|
op = (instr >> 26) & 0x3F;
|
op = (instr >> 26) & 0x3F;
|
reg1 = (instr >> 21) & 0x1F;
|
reg1 = (instr >> 21) & 0x1F;
|
reg2 = (instr >> 16) & 0x1F;
|
reg2 = (instr >> 16) & 0x1F;
|
reg3 = (instr >> 11) & 0x1F;
|
reg3 = (instr >> 11) & 0x1F;
|
immed = instr & 0x0000FFFF;
|
immed = instr & 0x0000FFFF;
|
offset = instr & 0x03FFFFFF;
|
offset = instr & 0x03FFFFFF;
|
next = pc + 4;
|
next = pc + 4;
|
/* execute the instruction */
|
/* execute the instruction */
|
switch (op) {
|
switch (op) {
|
case OP_ADD:
|
case OP_ADD:
|
WR(reg3, (signed int) RR(reg1) + (signed int) RR(reg2));
|
WR(reg3, (signed int) RR(reg1) + (signed int) RR(reg2));
|
break;
|
break;
|
case OP_ADDI:
|
case OP_ADDI:
|
WR(reg2, (signed int) RR(reg1) + (signed int) SEXT16(immed));
|
WR(reg2, (signed int) RR(reg1) + (signed int) SEXT16(immed));
|
break;
|
break;
|
case OP_SUB:
|
case OP_SUB:
|
WR(reg3, (signed int) RR(reg1) - (signed int) RR(reg2));
|
WR(reg3, (signed int) RR(reg1) - (signed int) RR(reg2));
|
break;
|
break;
|
case OP_SUBI:
|
case OP_SUBI:
|
WR(reg2, (signed int) RR(reg1) - (signed int) SEXT16(immed));
|
WR(reg2, (signed int) RR(reg1) - (signed int) SEXT16(immed));
|
break;
|
break;
|
case OP_MUL:
|
case OP_MUL:
|
WR(reg3, (signed int) RR(reg1) * (signed int) RR(reg2));
|
WR(reg3, (signed int) RR(reg1) * (signed int) RR(reg2));
|
break;
|
break;
|
case OP_MULI:
|
case OP_MULI:
|
WR(reg2, (signed int) RR(reg1) * (signed int) SEXT16(immed));
|
WR(reg2, (signed int) RR(reg1) * (signed int) SEXT16(immed));
|
break;
|
break;
|
case OP_MULU:
|
case OP_MULU:
|
WR(reg3, RR(reg1) * RR(reg2));
|
WR(reg3, RR(reg1) * RR(reg2));
|
break;
|
break;
|
case OP_MULUI:
|
case OP_MULUI:
|
WR(reg2, RR(reg1) * ZEXT16(immed));
|
WR(reg2, RR(reg1) * ZEXT16(immed));
|
break;
|
break;
|
case OP_DIV:
|
case OP_DIV:
|
if (RR(reg2) == 0) {
|
if (RR(reg2) == 0) {
|
throwException(EXC_DIVIDE);
|
throwException(EXC_DIVIDE);
|
}
|
}
|
WR(reg3, (signed int) RR(reg1) / (signed int) RR(reg2));
|
WR(reg3, (signed int) RR(reg1) / (signed int) RR(reg2));
|
break;
|
break;
|
case OP_DIVI:
|
case OP_DIVI:
|
if (SEXT16(immed) == 0) {
|
if (SEXT16(immed) == 0) {
|
throwException(EXC_DIVIDE);
|
throwException(EXC_DIVIDE);
|
}
|
}
|
WR(reg2, (signed int) RR(reg1) / (signed int) SEXT16(immed));
|
WR(reg2, (signed int) RR(reg1) / (signed int) SEXT16(immed));
|
break;
|
break;
|
case OP_DIVU:
|
case OP_DIVU:
|
if (RR(reg2) == 0) {
|
if (RR(reg2) == 0) {
|
throwException(EXC_DIVIDE);
|
throwException(EXC_DIVIDE);
|
}
|
}
|
WR(reg3, RR(reg1) / RR(reg2));
|
WR(reg3, RR(reg1) / RR(reg2));
|
break;
|
break;
|
case OP_DIVUI:
|
case OP_DIVUI:
|
if (SEXT16(immed) == 0) {
|
if (SEXT16(immed) == 0) {
|
throwException(EXC_DIVIDE);
|
throwException(EXC_DIVIDE);
|
}
|
}
|
WR(reg2, RR(reg1) / ZEXT16(immed));
|
WR(reg2, RR(reg1) / ZEXT16(immed));
|
break;
|
break;
|
case OP_REM:
|
case OP_REM:
|
if (RR(reg2) == 0) {
|
if (RR(reg2) == 0) {
|
throwException(EXC_DIVIDE);
|
throwException(EXC_DIVIDE);
|
}
|
}
|
WR(reg3, (signed int) RR(reg1) % (signed int) RR(reg2));
|
WR(reg3, (signed int) RR(reg1) % (signed int) RR(reg2));
|
break;
|
break;
|
case OP_REMI:
|
case OP_REMI:
|
if (SEXT16(immed) == 0) {
|
if (SEXT16(immed) == 0) {
|
throwException(EXC_DIVIDE);
|
throwException(EXC_DIVIDE);
|
}
|
}
|
WR(reg2, (signed int) RR(reg1) % (signed int) SEXT16(immed));
|
WR(reg2, (signed int) RR(reg1) % (signed int) SEXT16(immed));
|
break;
|
break;
|
case OP_REMU:
|
case OP_REMU:
|
if (RR(reg2) == 0) {
|
if (RR(reg2) == 0) {
|
throwException(EXC_DIVIDE);
|
throwException(EXC_DIVIDE);
|
}
|
}
|
WR(reg3, RR(reg1) % RR(reg2));
|
WR(reg3, RR(reg1) % RR(reg2));
|
break;
|
break;
|
case OP_REMUI:
|
case OP_REMUI:
|
if (SEXT16(immed) == 0) {
|
if (SEXT16(immed) == 0) {
|
throwException(EXC_DIVIDE);
|
throwException(EXC_DIVIDE);
|
}
|
}
|
WR(reg2, RR(reg1) % ZEXT16(immed));
|
WR(reg2, RR(reg1) % ZEXT16(immed));
|
break;
|
break;
|
case OP_AND:
|
case OP_AND:
|
WR(reg3, RR(reg1) & RR(reg2));
|
WR(reg3, RR(reg1) & RR(reg2));
|
break;
|
break;
|
case OP_ANDI:
|
case OP_ANDI:
|
WR(reg2, RR(reg1) & ZEXT16(immed));
|
WR(reg2, RR(reg1) & ZEXT16(immed));
|
break;
|
break;
|
case OP_OR:
|
case OP_OR:
|
WR(reg3, RR(reg1) | RR(reg2));
|
WR(reg3, RR(reg1) | RR(reg2));
|
break;
|
break;
|
case OP_ORI:
|
case OP_ORI:
|
WR(reg2, RR(reg1) | ZEXT16(immed));
|
WR(reg2, RR(reg1) | ZEXT16(immed));
|
break;
|
break;
|
case OP_XOR:
|
case OP_XOR:
|
WR(reg3, RR(reg1) ^ RR(reg2));
|
WR(reg3, RR(reg1) ^ RR(reg2));
|
break;
|
break;
|
case OP_XORI:
|
case OP_XORI:
|
WR(reg2, RR(reg1) ^ ZEXT16(immed));
|
WR(reg2, RR(reg1) ^ ZEXT16(immed));
|
break;
|
break;
|
case OP_XNOR:
|
case OP_XNOR:
|
WR(reg3, ~(RR(reg1) ^ RR(reg2)));
|
WR(reg3, ~(RR(reg1) ^ RR(reg2)));
|
break;
|
break;
|
case OP_XNORI:
|
case OP_XNORI:
|
WR(reg2, ~(RR(reg1) ^ ZEXT16(immed)));
|
WR(reg2, ~(RR(reg1) ^ ZEXT16(immed)));
|
break;
|
break;
|
case OP_SLL:
|
case OP_SLL:
|
scnt = RR(reg2) & 0x1F;
|
scnt = RR(reg2) & 0x1F;
|
WR(reg3, RR(reg1) << scnt);
|
WR(reg3, RR(reg1) << scnt);
|
break;
|
break;
|
case OP_SLLI:
|
case OP_SLLI:
|
scnt = immed & 0x1F;
|
scnt = immed & 0x1F;
|
WR(reg2, RR(reg1) << scnt);
|
WR(reg2, RR(reg1) << scnt);
|
break;
|
break;
|
case OP_SLR:
|
case OP_SLR:
|
scnt = RR(reg2) & 0x1F;
|
scnt = RR(reg2) & 0x1F;
|
WR(reg3, RR(reg1) >> scnt);
|
WR(reg3, RR(reg1) >> scnt);
|
break;
|
break;
|
case OP_SLRI:
|
case OP_SLRI:
|
scnt = immed & 0x1F;
|
scnt = immed & 0x1F;
|
WR(reg2, RR(reg1) >> scnt);
|
WR(reg2, RR(reg1) >> scnt);
|
break;
|
break;
|
case OP_SAR:
|
case OP_SAR:
|
scnt = RR(reg2) & 0x1F;
|
scnt = RR(reg2) & 0x1F;
|
smsk = (RR(reg1) & 0x80000000 ? ~(((Word) 0xFFFFFFFF) >> scnt) : 0);
|
smsk = (RR(reg1) & 0x80000000 ? ~(((Word) 0xFFFFFFFF) >> scnt) : 0);
|
WR(reg3, smsk | (RR(reg1) >> scnt));
|
WR(reg3, smsk | (RR(reg1) >> scnt));
|
break;
|
break;
|
case OP_SARI:
|
case OP_SARI:
|
scnt = immed & 0x1F;
|
scnt = immed & 0x1F;
|
smsk = (RR(reg1) & 0x80000000 ? ~(((Word) 0xFFFFFFFF) >> scnt) : 0);
|
smsk = (RR(reg1) & 0x80000000 ? ~(((Word) 0xFFFFFFFF) >> scnt) : 0);
|
WR(reg2, smsk | (RR(reg1) >> scnt));
|
WR(reg2, smsk | (RR(reg1) >> scnt));
|
break;
|
break;
|
case OP_LDHI:
|
case OP_LDHI:
|
WR(reg2, ZEXT16(immed) << 16);
|
WR(reg2, ZEXT16(immed) << 16);
|
break;
|
break;
|
case OP_BEQ:
|
case OP_BEQ:
|
if (RR(reg1) == RR(reg2)) {
|
if (RR(reg1) == RR(reg2)) {
|
next += SEXT16(immed) << 2;
|
next += SEXT16(immed) << 2;
|
}
|
}
|
break;
|
break;
|
case OP_BNE:
|
case OP_BNE:
|
if (RR(reg1) != RR(reg2)) {
|
if (RR(reg1) != RR(reg2)) {
|
next += SEXT16(immed) << 2;
|
next += SEXT16(immed) << 2;
|
}
|
}
|
break;
|
break;
|
case OP_BLE:
|
case OP_BLE:
|
if ((signed int) RR(reg1) <= (signed int) RR(reg2)) {
|
if ((signed int) RR(reg1) <= (signed int) RR(reg2)) {
|
next += SEXT16(immed) << 2;
|
next += SEXT16(immed) << 2;
|
}
|
}
|
break;
|
break;
|
case OP_BLEU:
|
case OP_BLEU:
|
if (RR(reg1) <= RR(reg2)) {
|
if (RR(reg1) <= RR(reg2)) {
|
next += SEXT16(immed) << 2;
|
next += SEXT16(immed) << 2;
|
}
|
}
|
break;
|
break;
|
case OP_BLT:
|
case OP_BLT:
|
if ((signed int) RR(reg1) < (signed int) RR(reg2)) {
|
if ((signed int) RR(reg1) < (signed int) RR(reg2)) {
|
next += SEXT16(immed) << 2;
|
next += SEXT16(immed) << 2;
|
}
|
}
|
break;
|
break;
|
case OP_BLTU:
|
case OP_BLTU:
|
if (RR(reg1) < RR(reg2)) {
|
if (RR(reg1) < RR(reg2)) {
|
next += SEXT16(immed) << 2;
|
next += SEXT16(immed) << 2;
|
}
|
}
|
break;
|
break;
|
case OP_BGE:
|
case OP_BGE:
|
if ((signed int) RR(reg1) >= (signed int) RR(reg2)) {
|
if ((signed int) RR(reg1) >= (signed int) RR(reg2)) {
|
next += SEXT16(immed) << 2;
|
next += SEXT16(immed) << 2;
|
}
|
}
|
break;
|
break;
|
case OP_BGEU:
|
case OP_BGEU:
|
if (RR(reg1) >= RR(reg2)) {
|
if (RR(reg1) >= RR(reg2)) {
|
next += SEXT16(immed) << 2;
|
next += SEXT16(immed) << 2;
|
}
|
}
|
break;
|
break;
|
case OP_BGT:
|
case OP_BGT:
|
if ((signed int) RR(reg1) > (signed int) RR(reg2)) {
|
if ((signed int) RR(reg1) > (signed int) RR(reg2)) {
|
next += SEXT16(immed) << 2;
|
next += SEXT16(immed) << 2;
|
}
|
}
|
break;
|
break;
|
case OP_BGTU:
|
case OP_BGTU:
|
if (RR(reg1) > RR(reg2)) {
|
if (RR(reg1) > RR(reg2)) {
|
next += SEXT16(immed) << 2;
|
next += SEXT16(immed) << 2;
|
}
|
}
|
break;
|
break;
|
case OP_J:
|
case OP_J:
|
next += SEXT26(offset) << 2;
|
next += SEXT26(offset) << 2;
|
break;
|
break;
|
case OP_JR:
|
case OP_JR:
|
next = RR(reg1);
|
next = RR(reg1);
|
break;
|
break;
|
case OP_JAL:
|
case OP_JAL:
|
WR(31, next);
|
WR(31, next);
|
next += SEXT26(offset) << 2;
|
next += SEXT26(offset) << 2;
|
break;
|
break;
|
case OP_JALR:
|
case OP_JALR:
|
aux = RR(reg1);
|
aux = RR(reg1);
|
WR(31, next);
|
WR(31, next);
|
next = aux;
|
next = aux;
|
break;
|
break;
|
case OP_TRAP:
|
case OP_TRAP:
|
throwException(EXC_TRAP);
|
throwException(EXC_TRAP);
|
break;
|
break;
|
case OP_RFX:
|
case OP_RFX:
|
if (UM != 0) {
|
if (UM != 0) {
|
throwException(EXC_PRV_INSTRCT);
|
throwException(EXC_PRV_INSTRCT);
|
}
|
}
|
if (PIE != 0) {
|
if (PIE != 0) {
|
psw |= PSW_IE;
|
psw |= PSW_IE;
|
} else {
|
} else {
|
psw &= ~PSW_IE;
|
psw &= ~PSW_IE;
|
}
|
}
|
if (OIE != 0) {
|
if (OIE != 0) {
|
psw |= PSW_PIE;
|
psw |= PSW_PIE;
|
} else {
|
} else {
|
psw &= ~PSW_PIE;
|
psw &= ~PSW_PIE;
|
}
|
}
|
if (PUM != 0) {
|
if (PUM != 0) {
|
psw |= PSW_UM;
|
psw |= PSW_UM;
|
} else {
|
} else {
|
psw &= ~PSW_UM;
|
psw &= ~PSW_UM;
|
}
|
}
|
if (OUM != 0) {
|
if (OUM != 0) {
|
psw |= PSW_PUM;
|
psw |= PSW_PUM;
|
} else {
|
} else {
|
psw &= ~PSW_PUM;
|
psw &= ~PSW_PUM;
|
}
|
}
|
next = RR(30);
|
next = RR(30);
|
break;
|
break;
|
case OP_LDW:
|
case OP_LDW:
|
WR(reg2, mmuReadWord(RR(reg1) + SEXT16(immed), UM));
|
WR(reg2, mmuReadWord(RR(reg1) + SEXT16(immed), UM));
|
break;
|
break;
|
case OP_LDH:
|
case OP_LDH:
|
WR(reg2, (signed int) (signed short)
|
WR(reg2, (signed int) (signed short)
|
mmuReadHalf(RR(reg1) + SEXT16(immed), UM));
|
mmuReadHalf(RR(reg1) + SEXT16(immed), UM));
|
break;
|
break;
|
case OP_LDHU:
|
case OP_LDHU:
|
WR(reg2, mmuReadHalf(RR(reg1) + SEXT16(immed), UM));
|
WR(reg2, mmuReadHalf(RR(reg1) + SEXT16(immed), UM));
|
break;
|
break;
|
case OP_LDB:
|
case OP_LDB:
|
WR(reg2, (signed int) (signed char)
|
WR(reg2, (signed int) (signed char)
|
mmuReadByte(RR(reg1) + SEXT16(immed), UM));
|
mmuReadByte(RR(reg1) + SEXT16(immed), UM));
|
break;
|
break;
|
case OP_LDBU:
|
case OP_LDBU:
|
WR(reg2, mmuReadByte(RR(reg1) + SEXT16(immed), UM));
|
WR(reg2, mmuReadByte(RR(reg1) + SEXT16(immed), UM));
|
break;
|
break;
|
case OP_STW:
|
case OP_STW:
|
mmuWriteWord(RR(reg1) + SEXT16(immed), RR(reg2), UM);
|
mmuWriteWord(RR(reg1) + SEXT16(immed), RR(reg2), UM);
|
break;
|
break;
|
case OP_STH:
|
case OP_STH:
|
mmuWriteHalf(RR(reg1) + SEXT16(immed), RR(reg2), UM);
|
mmuWriteHalf(RR(reg1) + SEXT16(immed), RR(reg2), UM);
|
break;
|
break;
|
case OP_STB:
|
case OP_STB:
|
mmuWriteByte(RR(reg1) + SEXT16(immed), RR(reg2), UM);
|
mmuWriteByte(RR(reg1) + SEXT16(immed), RR(reg2), UM);
|
break;
|
break;
|
case OP_MVFS:
|
case OP_MVFS:
|
switch (immed) {
|
switch (immed) {
|
case 0:
|
case 0:
|
WR(reg2, psw);
|
WR(reg2, psw);
|
break;
|
break;
|
case 1:
|
case 1:
|
WR(reg2, mmuGetIndex());
|
WR(reg2, mmuGetIndex());
|
break;
|
break;
|
case 2:
|
case 2:
|
WR(reg2, mmuGetEntryHi());
|
WR(reg2, mmuGetEntryHi());
|
break;
|
break;
|
case 3:
|
case 3:
|
WR(reg2, mmuGetEntryLo());
|
WR(reg2, mmuGetEntryLo());
|
break;
|
break;
|
case 4:
|
case 4:
|
WR(reg2, mmuGetBadAddr());
|
WR(reg2, mmuGetBadAddr());
|
break;
|
break;
|
default:
|
default:
|
throwException(EXC_ILL_INSTRCT);
|
throwException(EXC_ILL_INSTRCT);
|
break;
|
break;
|
}
|
}
|
break;
|
break;
|
case OP_MVTS:
|
case OP_MVTS:
|
if (UM != 0) {
|
if (UM != 0) {
|
throwException(EXC_PRV_INSTRCT);
|
throwException(EXC_PRV_INSTRCT);
|
}
|
}
|
switch (immed) {
|
switch (immed) {
|
case 0:
|
case 0:
|
psw = RR(reg2);
|
psw = RR(reg2);
|
break;
|
break;
|
case 1:
|
case 1:
|
mmuSetIndex(RR(reg2));
|
mmuSetIndex(RR(reg2));
|
break;
|
break;
|
case 2:
|
case 2:
|
mmuSetEntryHi(RR(reg2));
|
mmuSetEntryHi(RR(reg2));
|
break;
|
break;
|
case 3:
|
case 3:
|
mmuSetEntryLo(RR(reg2));
|
mmuSetEntryLo(RR(reg2));
|
break;
|
break;
|
case 4:
|
case 4:
|
mmuSetBadAddr(RR(reg2));
|
mmuSetBadAddr(RR(reg2));
|
break;
|
break;
|
default:
|
default:
|
throwException(EXC_ILL_INSTRCT);
|
throwException(EXC_ILL_INSTRCT);
|
break;
|
break;
|
}
|
}
|
break;
|
break;
|
case OP_TBS:
|
case OP_TBS:
|
if (UM != 0) {
|
if (UM != 0) {
|
throwException(EXC_PRV_INSTRCT);
|
throwException(EXC_PRV_INSTRCT);
|
}
|
}
|
mmuTbs();
|
mmuTbs();
|
break;
|
break;
|
case OP_TBWR:
|
case OP_TBWR:
|
if (UM != 0) {
|
if (UM != 0) {
|
throwException(EXC_PRV_INSTRCT);
|
throwException(EXC_PRV_INSTRCT);
|
}
|
}
|
mmuTbwr();
|
mmuTbwr();
|
break;
|
break;
|
case OP_TBRI:
|
case OP_TBRI:
|
if (UM != 0) {
|
if (UM != 0) {
|
throwException(EXC_PRV_INSTRCT);
|
throwException(EXC_PRV_INSTRCT);
|
}
|
}
|
mmuTbri();
|
mmuTbri();
|
break;
|
break;
|
case OP_TBWI:
|
case OP_TBWI:
|
if (UM != 0) {
|
if (UM != 0) {
|
throwException(EXC_PRV_INSTRCT);
|
throwException(EXC_PRV_INSTRCT);
|
}
|
}
|
mmuTbwi();
|
mmuTbwi();
|
break;
|
break;
|
default:
|
default:
|
throwException(EXC_ILL_INSTRCT);
|
throwException(EXC_ILL_INSTRCT);
|
break;
|
break;
|
}
|
}
|
/* update PC */
|
/* update PC */
|
pc = next;
|
pc = next;
|
}
|
}
|
|
|
|
|
/**************************************************************/
|
/**************************************************************/
|
|
|
|
|
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;
|
}
|
}
|
|
|
|
|
Word cpuGetIRQ(void) {
|
Word cpuGetIRQ(void) {
|
return irqPending;
|
return irqPending;
|
}
|
}
|
|
|
|
|
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;
|
}
|
}
|
|
|
|
|
Word cpuGetTotal(void) {
|
Word cpuGetTotal(void) {
|
return total;
|
return total;
|
}
|
}
|
|
|
|
|
void cpuStep(void) {
|
void cpuStep(void) {
|
jmp_buf myEnvironment;
|
jmp_buf myEnvironment;
|
int exception;
|
int exception;
|
|
|
exception = setjmp(myEnvironment);
|
exception = setjmp(myEnvironment);
|
if (exception == 0) {
|
if (exception == 0) {
|
/* initialization */
|
/* initialization */
|
pushEnvironment(&myEnvironment);
|
pushEnvironment(&myEnvironment);
|
handleRealTimeTasks();
|
timerTick();
|
execNextInstruction();
|
execNextInstruction();
|
handleInterrupts();
|
handleInterrupts();
|
} else {
|
} else {
|
/* an exception was thrown */
|
/* an exception was thrown */
|
cpuSetInterrupt(exception);
|
cpuSetInterrupt(exception);
|
handleInterrupts();
|
handleInterrupts();
|
}
|
}
|
popEnvironment();
|
popEnvironment();
|
}
|
}
|
|
|
|
|
void cpuRun(void) {
|
void cpuRun(void) {
|
jmp_buf myEnvironment;
|
jmp_buf myEnvironment;
|
int exception;
|
int exception;
|
|
|
run = true;
|
run = true;
|
exception = setjmp(myEnvironment);
|
exception = setjmp(myEnvironment);
|
if (exception == 0) {
|
if (exception == 0) {
|
/* initialization */
|
/* initialization */
|
pushEnvironment(&myEnvironment);
|
pushEnvironment(&myEnvironment);
|
} else {
|
} else {
|
/* an exception was thrown */
|
/* an exception was thrown */
|
cpuSetInterrupt(exception);
|
cpuSetInterrupt(exception);
|
handleInterrupts();
|
handleInterrupts();
|
if (breakSet && pc == breakAddr) {
|
if (breakSet && pc == breakAddr) {
|
run = false;
|
run = false;
|
}
|
}
|
}
|
}
|
while (run) {
|
while (run) {
|
handleRealTimeTasks();
|
timerTick();
|
execNextInstruction();
|
execNextInstruction();
|
handleInterrupts();
|
handleInterrupts();
|
if (breakSet && pc == breakAddr) {
|
if (breakSet && pc == breakAddr) {
|
run = false;
|
run = false;
|
}
|
}
|
}
|
}
|
popEnvironment();
|
popEnvironment();
|
}
|
}
|
|
|
|
|
void cpuHalt(void) {
|
void cpuHalt(void) {
|
run = false;
|
run = false;
|
}
|
}
|
|
|
|
|
void cpuSetInterrupt(int priority) {
|
void cpuSetInterrupt(int priority) {
|
irqPending |= ((unsigned) 1 << priority);
|
irqPending |= ((unsigned) 1 << priority);
|
}
|
}
|
|
|
|
|
void cpuResetInterrupt(int priority) {
|
void cpuResetInterrupt(int priority) {
|
irqPending &= ~((unsigned) 1 << priority);
|
irqPending &= ~((unsigned) 1 << priority);
|
}
|
}
|
|
|
|
|
void cpuReset(void) {
|
void cpuReset(void) {
|
int i;
|
int i;
|
|
|
cPrintf("Resetting CPU...\n");
|
cPrintf("Resetting CPU...\n");
|
/* most registers are in a random state */
|
/* most registers are in a random state */
|
for (i = 1; i < 32; i++) {
|
for (i = 1; i < 32; i++) {
|
r[i] = rand();
|
r[i] = rand();
|
}
|
}
|
/* but not all */
|
/* but not all */
|
pc = startAddr;
|
pc = startAddr;
|
r[0] = 0;
|
r[0] = 0;
|
psw = 0;
|
psw = 0;
|
/* reset simulator control variables */
|
/* reset simulator control variables */
|
instrCount = 0;
|
|
irqPending = 0;
|
irqPending = 0;
|
total = 0;
|
total = 0;
|
}
|
}
|
|
|
|
|
void cpuInit(Word initialPC) {
|
void cpuInit(Word initialPC) {
|
startAddr = initialPC;
|
startAddr = initialPC;
|
cpuReset();
|
cpuReset();
|
}
|
}
|
|
|
|
|
void cpuExit(void) {
|
void cpuExit(void) {
|
}
|
}
|
|
|