/*------------------------------------------------------------------------------
|
/*------------------------------------------------------------------------------
|
* slite.c -- MIPS-I simulator based on Steve Rhoad's "mlite"
|
* slite.c -- MIPS-I simulator based on Steve Rhoad's "mlite"
|
*
|
*
|
* This is a slightly modified version of Steve Rhoad's "mlite" simulator, which
|
* This is a slightly modified version of Steve Rhoad's "mlite" simulator, which
|
* is part of his PLASMA project (original date: 1/31/01).
|
* is part of his PLASMA project (original date: 1/31/01).
|
*
|
*
|
*-------------------------------------------------------------------------------
|
*-------------------------------------------------------------------------------
|
* Usage:
|
* Usage:
|
* slite <code file name> <data file name>
|
* slite <code file name> <data file name>
|
*
|
*
|
* The program will allocate a chunk of RAM (MEM_SIZE bytes) and map it to
|
* The program will allocate a chunk of RAM (MEM_SIZE bytes) and map it to
|
* address 0x00000000 of the simulated CPU.
|
* address 0x00000000 of the simulated CPU.
|
* Then it will read the 'code file' (as a big-endian plain binary) onto address
|
* Then it will read the 'code file' (as a big-endian plain binary) onto address
|
* 0x0 of the simulated CPU memory, and 'data file' on address 0x10000.
|
* 0x0 of the simulated CPU memory, and 'data file' on address 0x10000.
|
* Finally, will reset the CPU and enter the interactive debugger.
|
* Finally, will reset the CPU and enter the interactive debugger.
|
*
|
*
|
* (Note that the above is only necessary if the system does not have caches,
|
* (Note that the above is only necessary if the system does not have caches,
|
* because of the Harvard architecture. With caches in place, program loading
|
* because of the Harvard architecture. With caches in place, program loading
|
* would be antirely conventional).
|
* would be antirely conventional).
|
*
|
*
|
* A simulation log file will be dumped to file "sw_sim_log.txt". This log can be
|
* A simulation log file will be dumped to file "sw_sim_log.txt". This log can be
|
* used to compare with an equivalent log dumped by the hardware simulation, as
|
* used to compare with an equivalent log dumped by the hardware simulation, as
|
* a simple way to validate the hardware for a given program. See the project
|
* a simple way to validate the hardware for a given program. See the project
|
* readme files for details.
|
* readme files for details.
|
*
|
*
|
*-------------------------------------------------------------------------------
|
*-------------------------------------------------------------------------------
|
* KNOWN BUGS:
|
* KNOWN BUGS:
|
*
|
*
|
*-------------------------------------------------------------------------------
|
*-------------------------------------------------------------------------------
|
* @date 2011-jan-16
|
* @date 2011-jan-16
|
*
|
*
|
*-------------------------------------------------------------------------------
|
*-------------------------------------------------------------------------------
|
* COPYRIGHT: Software placed into the public domain by the author.
|
* COPYRIGHT: Software placed into the public domain by the author.
|
* Software 'as is' without warranty. Author liable for nothing.
|
* Software 'as is' without warranty. Author liable for nothing.
|
*
|
*
|
* IMPORTANT: Assumes host is little endian.
|
* IMPORTANT: Assumes host is little endian.
|
*-----------------------------------------------------------------------------*/
|
*-----------------------------------------------------------------------------*/
|
|
|
#include <stdio.h>
|
#include <stdio.h>
|
#include <stdlib.h>
|
#include <stdlib.h>
|
#include <stdint.h>
|
#include <stdint.h>
|
#include <string.h>
|
#include <string.h>
|
#include <ctype.h>
|
#include <ctype.h>
|
#include <assert.h>
|
#include <assert.h>
|
|
|
/** Set to !=0 to disable file logging (much faster simulation) */
|
/** Set to !=0 to disable file logging (much faster simulation) */
|
#define FILE_LOGGING_DISABLED (0)
|
#define FILE_LOGGING_DISABLED (0)
|
/** Define to enable cache simulation (unimplemented) */
|
/** Define to enable cache simulation (unimplemented) */
|
//#define ENABLE_CACHE
|
//#define ENABLE_CACHE
|
|
|
|
|
/*---- Definition of simulated system parameters -----------------------------*/
|
/*---- Definition of simulated system parameters -----------------------------*/
|
|
|
|
|
#define VECTOR_RESET (0x00000000)
|
#define VECTOR_RESET (0x00000000)
|
#define VECTOR_TRAP (0x0000003c)
|
#define VECTOR_TRAP (0x0000003c)
|
|
|
typedef struct s_block {
|
typedef struct s_block {
|
uint32_t start;
|
uint32_t start;
|
uint32_t size;
|
uint32_t size;
|
|
uint32_t mask;
|
uint8_t *mem;
|
uint8_t *mem;
|
char *name;
|
char *name;
|
} t_block;
|
} t_block;
|
|
|
|
|
/* Here's where we define the memory areas (blocks) of the system */
|
/* Here's where we define the memory areas (blocks) of the system.
|
/* (make sure they don't overlap or the simulated program will crash) */
|
Memory decoding is done in the order the blocks are defined; the address
|
|
is anded with field .mask and then compared to field .start. If they match
|
|
the address modulo the field .size is used to index the memory block, giving
|
|
a 'mirror' effect. All of this simulates the behavior of the actual hardware.
|
|
Make sure the blocks don't overlap or the scheme will fail.
|
|
*/
|
|
|
#define NUM_MEM_BLOCKS (2)
|
#define NUM_MEM_BLOCKS (2)
|
|
|
t_block default_blocks[NUM_MEM_BLOCKS] = {
|
t_block default_blocks[NUM_MEM_BLOCKS] = {
|
/* meant as bootstrap block, though it's read/write */
|
/* meant as bootstrap block, though it's read/write */
|
{VECTOR_RESET, 0x00010000, NULL, "ROM"},
|
{VECTOR_RESET, 0x00010000, 0xf0000000, NULL, "Boot"},
|
/* main ram block */
|
/* main ram block */
|
{0x80000000, 0x00010000, NULL, "Data"}
|
{0x80000000, 0x00001000, 0xf0000000, NULL, "Data"}
|
};
|
};
|
|
|
|
|
#define ntohs(A) ( ((A)>>8) | (((A)&0xff)<<8) )
|
#define ntohs(A) ( ((A)>>8) | (((A)&0xff)<<8) )
|
#define htons(A) ntohs(A)
|
#define htons(A) ntohs(A)
|
#define ntohl(A) ( ((A)>>24) | (((A)&0xff0000)>>8) | (((A)&0xff00)<<8) | ((A)<<24) )
|
#define ntohl(A) ( ((A)>>24) | (((A)&0xff0000)>>8) | (((A)&0xff00)<<8) | ((A)<<24) )
|
#define htonl(A) ntohl(A)
|
#define htonl(A) ntohl(A)
|
|
|
/*---- OS-dependent support functions and definitions ------------------------*/
|
/*---- OS-dependent support functions and definitions ------------------------*/
|
#ifndef WIN32
|
#ifndef WIN32
|
//Support for Linux
|
//Support for Linux
|
#define putch putchar
|
#define putch putchar
|
#include <termios.h>
|
#include <termios.h>
|
#include <unistd.h>
|
#include <unistd.h>
|
|
|
void slite_sleep(unsigned int value){
|
void slite_sleep(unsigned int value){
|
usleep(value * 1000);
|
usleep(value * 1000);
|
}
|
}
|
|
|
int kbhit(void){
|
int kbhit(void){
|
struct termios oldt, newt;
|
struct termios oldt, newt;
|
struct timeval tv;
|
struct timeval tv;
|
fd_set read_fd;
|
fd_set read_fd;
|
|
|
tcgetattr(STDIN_FILENO, &oldt);
|
tcgetattr(STDIN_FILENO, &oldt);
|
newt = oldt;
|
newt = oldt;
|
newt.c_lflag &= ~(ICANON | ECHO);
|
newt.c_lflag &= ~(ICANON | ECHO);
|
tcsetattr(STDIN_FILENO, TCSANOW, &newt);
|
tcsetattr(STDIN_FILENO, TCSANOW, &newt);
|
tv.tv_sec=0;
|
tv.tv_sec=0;
|
tv.tv_usec=0;
|
tv.tv_usec=0;
|
FD_ZERO(&read_fd);
|
FD_ZERO(&read_fd);
|
FD_SET(0,&read_fd);
|
FD_SET(0,&read_fd);
|
if(select(1, &read_fd, NULL, NULL, &tv) == -1){
|
if(select(1, &read_fd, NULL, NULL, &tv) == -1){
|
return 0;
|
return 0;
|
}
|
}
|
//tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
|
//tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
|
if(FD_ISSET(0,&read_fd)){
|
if(FD_ISSET(0,&read_fd)){
|
return 1;
|
return 1;
|
}
|
}
|
return 0;
|
return 0;
|
}
|
}
|
|
|
int getch(void){
|
int getch(void){
|
struct termios oldt, newt;
|
struct termios oldt, newt;
|
int ch;
|
int ch;
|
|
|
tcgetattr(STDIN_FILENO, &oldt);
|
tcgetattr(STDIN_FILENO, &oldt);
|
newt = oldt;
|
newt = oldt;
|
newt.c_lflag &= ~(ICANON | ECHO);
|
newt.c_lflag &= ~(ICANON | ECHO);
|
tcsetattr(STDIN_FILENO, TCSANOW, &newt);
|
tcsetattr(STDIN_FILENO, TCSANOW, &newt);
|
ch = getchar();
|
ch = getchar();
|
//tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
|
//tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
|
return ch;
|
return ch;
|
}
|
}
|
#else
|
#else
|
//Support for Windows
|
//Support for Windows
|
#include <conio.h>
|
#include <conio.h>
|
extern void __stdcall Sleep(unsigned long value);
|
extern void __stdcall Sleep(unsigned long value);
|
|
|
void slite_sleep(unsigned int value){
|
void slite_sleep(unsigned int value){
|
Sleep(value);
|
Sleep(value);
|
}
|
}
|
|
|
#endif
|
#endif
|
/*---- End of OS-dependent support functions and definitions -----------------*/
|
/*---- End of OS-dependent support functions and definitions -----------------*/
|
|
|
/*---- Hardware system parameters --------------------------------------------*/
|
/*---- Hardware system parameters --------------------------------------------*/
|
|
|
/* Much of this is a remnant from Plasma's mlite and is no longer used. */
|
/* Much of this is a remnant from Plasma's mlite and is no longer used. */
|
/* FIXME Refactor HW system params */
|
/* FIXME Refactor HW system params */
|
|
|
#define UART_WRITE 0x20000000
|
#define UART_WRITE 0x20000000
|
#define UART_READ 0x20000000
|
#define UART_READ 0x20000000
|
#define IRQ_MASK 0x20000010
|
#define IRQ_MASK 0x20000010
|
#define IRQ_STATUS 0x20000020
|
#define IRQ_STATUS 0x20000020
|
#define CONFIG_REG 0x20000070
|
#define CONFIG_REG 0x20000070
|
#define MMU_PROCESS_ID 0x20000080
|
#define MMU_PROCESS_ID 0x20000080
|
#define MMU_FAULT_ADDR 0x20000090
|
#define MMU_FAULT_ADDR 0x20000090
|
#define MMU_TLB 0x200000a0
|
#define MMU_TLB 0x200000a0
|
|
|
#define IRQ_UART_READ_AVAILABLE 0x001
|
#define IRQ_UART_READ_AVAILABLE 0x001
|
#define IRQ_UART_WRITE_AVAILABLE 0x002
|
#define IRQ_UART_WRITE_AVAILABLE 0x002
|
#define IRQ_COUNTER18_NOT 0x004
|
#define IRQ_COUNTER18_NOT 0x004
|
#define IRQ_COUNTER18 0x008
|
#define IRQ_COUNTER18 0x008
|
#define IRQ_MMU 0x200
|
#define IRQ_MMU 0x200
|
|
|
/*----------------------------------------------------------------------------*/
|
/*----------------------------------------------------------------------------*/
|
|
|
/* These are flags that will be used to notify the main cycle function of any
|
/* These are flags that will be used to notify the main cycle function of any
|
failed assertions in its subfunctions. */
|
failed assertions in its subfunctions. */
|
#define ASRT_UNALIGNED_READ (1<<0)
|
#define ASRT_UNALIGNED_READ (1<<0)
|
#define ASRT_UNALIGNED_WRITE (1<<1)
|
#define ASRT_UNALIGNED_WRITE (1<<1)
|
|
|
char *assertion_messages[2] = {
|
char *assertion_messages[2] = {
|
"Unaligned read",
|
"Unaligned read",
|
"Unaligned write"
|
"Unaligned write"
|
};
|
};
|
|
|
|
|
/** Length of debugging jump target queue */
|
/** Length of debugging jump target queue */
|
#define TRACE_BUFFER_SIZE (32)
|
#define TRACE_BUFFER_SIZE (32)
|
|
|
typedef struct s_trace {
|
typedef struct s_trace {
|
unsigned int buf[TRACE_BUFFER_SIZE]; /**< queue of last jump targets */
|
unsigned int buf[TRACE_BUFFER_SIZE]; /**< queue of last jump targets */
|
unsigned int next; /**< internal queue head pointer */
|
unsigned int next; /**< internal queue head pointer */
|
FILE *log; /**< text log file or NULL */
|
FILE *log; /**< text log file or NULL */
|
int pr[32]; /**< last value of register bank */
|
int pr[32]; /**< last value of register bank */
|
int hi, lo, epc; /**< last value of internal regs */
|
int hi, lo, epc; /**< last value of internal regs */
|
} t_trace;
|
} t_trace;
|
|
|
typedef struct s_state {
|
typedef struct s_state {
|
unsigned failed_assertions; /**< assertion bitmap */
|
unsigned failed_assertions; /**< assertion bitmap */
|
unsigned faulty_address; /**< addr that failed assertion */
|
unsigned faulty_address; /**< addr that failed assertion */
|
|
|
int delay_slot; /**< !=0 if prev. instruction was a branch */
|
int delay_slot; /**< !=0 if prev. instruction was a branch */
|
|
|
int r[32];
|
int r[32];
|
int opcode;
|
int opcode;
|
int pc, pc_next, epc;
|
int pc, pc_next, epc;
|
unsigned int hi;
|
unsigned int hi;
|
unsigned int lo;
|
unsigned int lo;
|
int status;
|
int status;
|
unsigned cp0_cause;
|
unsigned cp0_cause;
|
int userMode;
|
int userMode;
|
int processId;
|
int processId;
|
int exceptionId; /**< DEPRECATED, to be removed */
|
int exceptionId; /**< DEPRECATED, to be removed */
|
int faultAddr;
|
int faultAddr;
|
int irqStatus;
|
int irqStatus;
|
int skip;
|
int skip;
|
t_trace t;
|
t_trace t;
|
//unsigned char *mem;
|
//unsigned char *mem;
|
t_block blocks[NUM_MEM_BLOCKS];
|
t_block blocks[NUM_MEM_BLOCKS];
|
int wakeup;
|
int wakeup;
|
int big_endian;
|
int big_endian;
|
} t_state;
|
} t_state;
|
|
|
static char *opcode_string[]={
|
static char *opcode_string[]={
|
"SPECIAL","REGIMM","J","JAL","BEQ","BNE","BLEZ","BGTZ",
|
"SPECIAL","REGIMM","J","JAL","BEQ","BNE","BLEZ","BGTZ",
|
"ADDI","ADDIU","SLTI","SLTIU","ANDI","ORI","XORI","LUI",
|
"ADDI","ADDIU","SLTI","SLTIU","ANDI","ORI","XORI","LUI",
|
"COP0","COP1","COP2","COP3","BEQL","BNEL","BLEZL","BGTZL",
|
"COP0","COP1","COP2","COP3","BEQL","BNEL","BLEZL","BGTZL",
|
"?","?","?","?","?","?","?","?",
|
"?","?","?","?","?","?","?","?",
|
"LB","LH","LWL","LW","LBU","LHU","LWR","?",
|
"LB","LH","LWL","LW","LBU","LHU","LWR","?",
|
"SB","SH","SWL","SW","?","?","SWR","CACHE",
|
"SB","SH","SWL","SW","?","?","SWR","CACHE",
|
"LL","LWC1","LWC2","LWC3","?","LDC1","LDC2","LDC3"
|
"LL","LWC1","LWC2","LWC3","?","LDC1","LDC2","LDC3"
|
"SC","SWC1","SWC2","SWC3","?","SDC1","SDC2","SDC3"
|
"SC","SWC1","SWC2","SWC3","?","SDC1","SDC2","SDC3"
|
};
|
};
|
|
|
static char *special_string[]={
|
static char *special_string[]={
|
"SLL","?","SRL","SRA","SLLV","?","SRLV","SRAV",
|
"SLL","?","SRL","SRA","SLLV","?","SRLV","SRAV",
|
"JR","JALR","MOVZ","MOVN","SYSCALL","BREAK","?","SYNC",
|
"JR","JALR","MOVZ","MOVN","SYSCALL","BREAK","?","SYNC",
|
"MFHI","MTHI","MFLO","MTLO","?","?","?","?",
|
"MFHI","MTHI","MFLO","MTLO","?","?","?","?",
|
"MULT","MULTU","DIV","DIVU","?","?","?","?",
|
"MULT","MULTU","DIV","DIVU","?","?","?","?",
|
"ADD","ADDU","SUB","SUBU","AND","OR","XOR","NOR",
|
"ADD","ADDU","SUB","SUBU","AND","OR","XOR","NOR",
|
"?","?","SLT","SLTU","?","DADDU","?","?",
|
"?","?","SLT","SLTU","?","DADDU","?","?",
|
"TGE","TGEU","TLT","TLTU","TEQ","?","TNE","?",
|
"TGE","TGEU","TLT","TLTU","TEQ","?","TNE","?",
|
"?","?","?","?","?","?","?","?"
|
"?","?","?","?","?","?","?","?"
|
};
|
};
|
|
|
static char *regimm_string[]={
|
static char *regimm_string[]={
|
"BLTZ","BGEZ","BLTZL","BGEZL","?","?","?","?",
|
"BLTZ","BGEZ","BLTZL","BGEZL","?","?","?","?",
|
"TGEI","TGEIU","TLTI","TLTIU","TEQI","?","TNEI","?",
|
"TGEI","TGEIU","TLTI","TLTIU","TEQI","?","TNEI","?",
|
"BLTZAL","BEQZAL","BLTZALL","BGEZALL","?","?","?","?",
|
"BLTZAL","BEQZAL","BLTZALL","BGEZALL","?","?","?","?",
|
"?","?","?","?","?","?","?","?"
|
"?","?","?","?","?","?","?","?"
|
};
|
};
|
|
|
static unsigned int HWMemory[8];
|
static unsigned int HWMemory[8];
|
|
|
/*---- Local function prototypes ---------------------------------------------*/
|
/*---- Local function prototypes ---------------------------------------------*/
|
|
|
/* Debug and logging */
|
/* Debug and logging */
|
void init_trace_buffer(t_state *s, const char *log_file_name);
|
void init_trace_buffer(t_state *s, const char *log_file_name);
|
void close_trace_buffer(t_state *s);
|
void close_trace_buffer(t_state *s);
|
void dump_trace_buffer(t_state *s);
|
void dump_trace_buffer(t_state *s);
|
void log_cycle(t_state *s);
|
void log_cycle(t_state *s);
|
void log_read(t_state *s, int full_address, int word_value, int size, int log);
|
void log_read(t_state *s, int full_address, int word_value, int size, int log);
|
void log_failed_assertions(t_state *s);
|
void log_failed_assertions(t_state *s);
|
|
|
/* CPU model */
|
/* CPU model */
|
void free_cpu(t_state *s);
|
void free_cpu(t_state *s);
|
void init_cpu(t_state *s);
|
int init_cpu(t_state *s);
|
void reset_cpu(t_state *s);
|
void reset_cpu(t_state *s);
|
|
|
/* Hardware simulation */
|
/* Hardware simulation */
|
int mem_read(t_state *s, int size, unsigned int address, int log);
|
int mem_read(t_state *s, int size, unsigned int address, int log);
|
void mem_write(t_state *s, int size, unsigned address, unsigned value, int log);
|
void mem_write(t_state *s, int size, unsigned address, unsigned value, int log);
|
void start_load(t_state *s, int rt, int data);
|
void start_load(t_state *s, int rt, int data);
|
|
|
|
|
/*---- Local functions -------------------------------------------------------*/
|
/*---- Local functions -------------------------------------------------------*/
|
|
|
/** Log to file a memory read operation (not including target reg change) */
|
/** Log to file a memory read operation (not including target reg change) */
|
void log_read(t_state *s, int full_address, int word_value, int size, int log){
|
void log_read(t_state *s, int full_address, int word_value, int size, int log){
|
if(s->t.log!=NULL && log!=0){
|
if(s->t.log!=NULL && log!=0){
|
if(size==4){ size=0x04; }
|
if(size==4){ size=0x04; }
|
else if(size==2){ size=0x02; }
|
else if(size==2){ size=0x02; }
|
else { size=0x01; }
|
else { size=0x01; }
|
fprintf(s->t.log, "(%08X) [%08X] <**>=%08X RD\n",
|
fprintf(s->t.log, "(%08X) [%08X] <**>=%08X RD\n",
|
s->pc, full_address, /*size,*/ word_value);
|
s->pc, full_address, /*size,*/ word_value);
|
}
|
}
|
}
|
}
|
|
|
/** Read memory, optionally logging */
|
/** Read memory, optionally logging */
|
int mem_read(t_state *s, int size, unsigned int address, int log){
|
int mem_read(t_state *s, int size, unsigned int address, int log){
|
unsigned int value=0, word_value=0, i, ptr;
|
unsigned int value=0, word_value=0, i, ptr;
|
unsigned int full_address = address;
|
unsigned int full_address = address;
|
|
|
s->irqStatus |= IRQ_UART_WRITE_AVAILABLE;
|
s->irqStatus |= IRQ_UART_WRITE_AVAILABLE;
|
switch(address){
|
switch(address){
|
case UART_READ:
|
case UART_READ:
|
word_value = 0x00000001;
|
word_value = 0x00000001;
|
log_read(s, full_address, word_value, size, log);
|
log_read(s, full_address, word_value, size, log);
|
return word_value;
|
return word_value;
|
/* FIXME Take input from text file */
|
/* FIXME Take input from text file */
|
/*
|
/*
|
if(kbhit()){
|
if(kbhit()){
|
HWMemory[0] = getch();
|
HWMemory[0] = getch();
|
}
|
}
|
s->irqStatus &= ~IRQ_UART_READ_AVAILABLE; //clear bit
|
s->irqStatus &= ~IRQ_UART_READ_AVAILABLE; //clear bit
|
return HWMemory[0];
|
return HWMemory[0];
|
*/
|
*/
|
case IRQ_MASK:
|
case IRQ_MASK:
|
return HWMemory[1];
|
return HWMemory[1];
|
case IRQ_MASK + 4:
|
case IRQ_MASK + 4:
|
slite_sleep(10);
|
slite_sleep(10);
|
return 0;
|
return 0;
|
case IRQ_STATUS:
|
case IRQ_STATUS:
|
/*if(kbhit())
|
/*if(kbhit())
|
s->irqStatus |= IRQ_UART_READ_AVAILABLE;
|
s->irqStatus |= IRQ_UART_READ_AVAILABLE;
|
return s->irqStatus;
|
return s->irqStatus;
|
*/
|
*/
|
/* FIXME Optionally simulate UART TX delay */
|
/* FIXME Optionally simulate UART TX delay */
|
word_value = 0x00000003; /* Ready to TX and RX */
|
word_value = 0x00000003; /* Ready to TX and RX */
|
log_read(s, full_address, word_value, size, log);
|
log_read(s, full_address, word_value, size, log);
|
return word_value;
|
return word_value;
|
case MMU_PROCESS_ID:
|
case MMU_PROCESS_ID:
|
return s->processId;
|
return s->processId;
|
case MMU_FAULT_ADDR:
|
case MMU_FAULT_ADDR:
|
return s->faultAddr;
|
return s->faultAddr;
|
}
|
}
|
|
|
/* point ptr to the byte in the block, or NULL is the address is unmapped */
|
/* point ptr to the byte in the block, or NULL is the address is unmapped */
|
ptr = 0;
|
ptr = 0;
|
for(i=0;i<NUM_MEM_BLOCKS;i++){
|
for(i=0;i<NUM_MEM_BLOCKS;i++){
|
if(address >= s->blocks[i].start &&
|
if((address & s->blocks[i].mask) == s->blocks[i].start){
|
address < s->blocks[i].start + s->blocks[i].size){
|
ptr = (unsigned)(s->blocks[i].mem) +
|
ptr = (unsigned)(s->blocks[i].mem) + address - s->blocks[i].start;
|
((address - s->blocks[i].start) % s->blocks[i].size);
|
break;
|
break;
|
}
|
}
|
}
|
}
|
if(!ptr){
|
if(!ptr){
|
/* address out of mapped blocks: log and return zero */
|
/* address out of mapped blocks: log and return zero */
|
if(s->t.log!=NULL && log!=0){
|
if(s->t.log!=NULL && log!=0){
|
fprintf(s->t.log, "(%08X) [%08X] <**>=%08X RD UNMAPPED\n",
|
fprintf(s->t.log, "(%08X) [%08X] <**>=%08X RD UNMAPPED\n",
|
s->pc, full_address, 0);
|
s->pc, full_address, 0);
|
}
|
}
|
return 0;
|
return 0;
|
}
|
}
|
|
|
/* get the whole word */
|
/* get the whole word */
|
word_value = *(int*)(ptr&0xfffffffc);
|
word_value = *(int*)(ptr&0xfffffffc);
|
if(s->big_endian){
|
if(s->big_endian){
|
word_value = ntohl(word_value);
|
word_value = ntohl(word_value);
|
}
|
}
|
|
|
switch(size){
|
switch(size){
|
case 4:
|
case 4:
|
if(address & 3){
|
if(address & 3){
|
printf("Unaligned access PC=0x%x address=0x%x\n",
|
printf("Unaligned access PC=0x%x address=0x%x\n",
|
(int)s->pc, (int)address);
|
(int)s->pc, (int)address);
|
}
|
}
|
if((address & 3) != 0){
|
if((address & 3) != 0){
|
/* unaligned word, log fault */
|
/* unaligned word, log fault */
|
s->failed_assertions |= ASRT_UNALIGNED_READ;
|
s->failed_assertions |= ASRT_UNALIGNED_READ;
|
s->faulty_address = address;
|
s->faulty_address = address;
|
address = address & 0xfffffffc;
|
address = address & 0xfffffffc;
|
}
|
}
|
value = *(int*)ptr;
|
value = *(int*)ptr;
|
if(s->big_endian){
|
if(s->big_endian){
|
value = ntohl(value);
|
value = ntohl(value);
|
}
|
}
|
break;
|
break;
|
case 2:
|
case 2:
|
if((address & 1) != 0){
|
if((address & 1) != 0){
|
/* unaligned halfword, log fault */
|
/* unaligned halfword, log fault */
|
s->failed_assertions |= ASRT_UNALIGNED_READ;
|
s->failed_assertions |= ASRT_UNALIGNED_READ;
|
s->faulty_address = address;
|
s->faulty_address = address;
|
address = address & 0xfffffffe;
|
address = address & 0xfffffffe;
|
}
|
}
|
value = *(unsigned short*)ptr;
|
value = *(unsigned short*)ptr;
|
if(s->big_endian){
|
if(s->big_endian){
|
value = ntohs((unsigned short)value);
|
value = ntohs((unsigned short)value);
|
}
|
}
|
break;
|
break;
|
case 1:
|
case 1:
|
value = *(unsigned char*)ptr;
|
value = *(unsigned char*)ptr;
|
break;
|
break;
|
default:
|
default:
|
/* FIXME this is a bug, should quit */
|
/* FIXME this is a bug, should quit */
|
printf("ERROR");
|
printf("ERROR");
|
}
|
}
|
|
|
log_read(s, full_address, word_value, size, log);
|
log_read(s, full_address, word_value, size, log);
|
return(value);
|
return(value);
|
}
|
}
|
|
|
/** Write memory */
|
/** Write memory */
|
void mem_write(t_state *s, int size, unsigned address, unsigned value, int log){
|
void mem_write(t_state *s, int size, unsigned address, unsigned value, int log){
|
unsigned int i, ptr, mask, dvalue, b0, b1, b2, b3;
|
unsigned int i, ptr, mask, dvalue, b0, b1, b2, b3;
|
|
|
if(s->t.log!=NULL){
|
if(s->t.log!=NULL){
|
b0 = value & 0x000000ff;
|
b0 = value & 0x000000ff;
|
b1 = value & 0x0000ff00;
|
b1 = value & 0x0000ff00;
|
b2 = value & 0x00ff0000;
|
b2 = value & 0x00ff0000;
|
b3 = value & 0xff000000;
|
b3 = value & 0xff000000;
|
|
|
switch(size){
|
switch(size){
|
case 4: mask = 0x0f;
|
case 4: mask = 0x0f;
|
dvalue = value;
|
dvalue = value;
|
break;
|
break;
|
case 2:
|
case 2:
|
if((address&0x2)==0){
|
if((address&0x2)==0){
|
mask = 0xc;
|
mask = 0xc;
|
dvalue = b1<<16 | b0<<16;
|
dvalue = b1<<16 | b0<<16;
|
}
|
}
|
else{
|
else{
|
mask = 0x3;
|
mask = 0x3;
|
dvalue = b1 | b0;
|
dvalue = b1 | b0;
|
}
|
}
|
break;
|
break;
|
case 1:
|
case 1:
|
switch(address%4){
|
switch(address%4){
|
case 0 : mask = 0x8;
|
case 0 : mask = 0x8;
|
dvalue = b0<<24;
|
dvalue = b0<<24;
|
break;
|
break;
|
case 1 : mask = 0x4;
|
case 1 : mask = 0x4;
|
dvalue = b0<<16;
|
dvalue = b0<<16;
|
break;
|
break;
|
case 2 : mask = 0x2;
|
case 2 : mask = 0x2;
|
dvalue = b0<<8;
|
dvalue = b0<<8;
|
break;
|
break;
|
case 3 : mask = 0x1;
|
case 3 : mask = 0x1;
|
dvalue = b0;
|
dvalue = b0;
|
break;
|
break;
|
}
|
}
|
break;
|
break;
|
default:
|
default:
|
printf("BUG: mem write size invalid (%08x)\n", s->pc);
|
printf("BUG: mem write size invalid (%08x)\n", s->pc);
|
exit(2);
|
exit(2);
|
}
|
}
|
|
|
fprintf(s->t.log, "(%08X) [%08X] |%02X|=%08X WR\n",
|
fprintf(s->t.log, "(%08X) [%08X] |%02X|=%08X WR\n",
|
s->pc, address, mask, dvalue);
|
s->pc, address, mask, dvalue);
|
}
|
}
|
|
|
switch(address){
|
switch(address){
|
case UART_WRITE:
|
case UART_WRITE:
|
putch(value);
|
putch(value);
|
fflush(stdout);
|
fflush(stdout);
|
return;
|
return;
|
case IRQ_MASK:
|
case IRQ_MASK:
|
HWMemory[1] = value;
|
HWMemory[1] = value;
|
return;
|
return;
|
case IRQ_STATUS:
|
case IRQ_STATUS:
|
s->irqStatus = value;
|
s->irqStatus = value;
|
return;
|
return;
|
case CONFIG_REG:
|
case CONFIG_REG:
|
return;
|
return;
|
case MMU_PROCESS_ID:
|
case MMU_PROCESS_ID:
|
//printf("processId=%d\n", value);
|
//printf("processId=%d\n", value);
|
s->processId = value;
|
s->processId = value;
|
return;
|
return;
|
}
|
}
|
|
|
//ptr = (unsigned int)s->mem + (address % MEM_SIZE);
|
//ptr = (unsigned int)s->mem + (address % MEM_SIZE);
|
if(address >= 0x80000000){
|
if(address >= 0x80000000){
|
ptr = 1;
|
ptr = 1;
|
}
|
}
|
ptr = 0;
|
ptr = 0;
|
for(i=0;i<NUM_MEM_BLOCKS;i++){
|
for(i=0;i<NUM_MEM_BLOCKS;i++){
|
if(address >= s->blocks[i].start &&
|
if((address & s->blocks[i].mask) == s->blocks[i].start){
|
address < s->blocks[i].start + s->blocks[i].size){
|
ptr = (unsigned)(s->blocks[i].mem) +
|
ptr = (unsigned)(s->blocks[i].mem) + address - s->blocks[i].start;
|
((address - s->blocks[i].start) % s->blocks[i].size);
|
break;
|
break;
|
}
|
}
|
}
|
}
|
if(!ptr){
|
if(!ptr){
|
/* address out of mapped blocks: log and return zero */
|
/* address out of mapped blocks: log and return zero */
|
if(s->t.log!=NULL && log!=0){
|
if(s->t.log!=NULL && log!=0){
|
fprintf(s->t.log, "(%08X) [%08X] |%02X|=%08X WR UNMAPPED\n",
|
fprintf(s->t.log, "(%08X) [%08X] |%02X|=%08X WR UNMAPPED\n",
|
s->pc, address, mask, dvalue);
|
s->pc, address, mask, dvalue);
|
}
|
}
|
return;
|
return;
|
}
|
}
|
|
|
switch(size){
|
switch(size){
|
case 4:
|
case 4:
|
if((address & 3) != 0){
|
if((address & 3) != 0){
|
/* unaligned word, log fault */
|
/* unaligned word, log fault */
|
s->failed_assertions |= ASRT_UNALIGNED_WRITE;
|
s->failed_assertions |= ASRT_UNALIGNED_WRITE;
|
s->faulty_address = address;
|
s->faulty_address = address;
|
address = address & (~0x03);
|
address = address & (~0x03);
|
}
|
}
|
if(s->big_endian){
|
if(s->big_endian){
|
value = htonl(value);
|
value = htonl(value);
|
}
|
}
|
*(int*)ptr = value;
|
*(int*)ptr = value;
|
break;
|
break;
|
case 2:
|
case 2:
|
if((address & 1) != 0){
|
if((address & 1) != 0){
|
/* unaligned halfword, log fault */
|
/* unaligned halfword, log fault */
|
s->failed_assertions |= ASRT_UNALIGNED_WRITE;
|
s->failed_assertions |= ASRT_UNALIGNED_WRITE;
|
s->faulty_address = address;
|
s->faulty_address = address;
|
address = address & (~0x01);
|
address = address & (~0x01);
|
}
|
}
|
if(s->big_endian){
|
if(s->big_endian){
|
value = htons((unsigned short)value);
|
value = htons((unsigned short)value);
|
}
|
}
|
*(short*)ptr = (unsigned short)value;
|
*(short*)ptr = (unsigned short)value;
|
break;
|
break;
|
case 1:
|
case 1:
|
*(char*)ptr = (unsigned char)value;
|
*(char*)ptr = (unsigned char)value;
|
break;
|
break;
|
default:
|
default:
|
/* FIXME this is a bug, should quit */
|
/* FIXME this is a bug, should quit */
|
printf("ERROR");
|
printf("ERROR");
|
}
|
}
|
}
|
}
|
|
|
/*---- Optional MMU and cache implementation ---------------------------------*/
|
/*---- Optional MMU and cache implementation ---------------------------------*/
|
|
|
/*
|
/*
|
The actual core does not have a cache so all of the original Plasma mlite.c
|
The actual core does not have a cache so all of the original Plasma mlite.c
|
code for cache simulation has been removed.
|
code for cache simulation has been removed.
|
*/
|
*/
|
|
|
/*---- End optional cache implementation -------------------------------------*/
|
/*---- End optional cache implementation -------------------------------------*/
|
|
|
|
|
/** Simulates MIPS-I multiplier unsigned behavior*/
|
/** Simulates MIPS-I multiplier unsigned behavior*/
|
void mult_big(unsigned int a,
|
void mult_big(unsigned int a,
|
unsigned int b,
|
unsigned int b,
|
unsigned int *hi,
|
unsigned int *hi,
|
unsigned int *lo){
|
unsigned int *lo){
|
unsigned int ahi, alo, bhi, blo;
|
unsigned int ahi, alo, bhi, blo;
|
unsigned int c0, c1, c2;
|
unsigned int c0, c1, c2;
|
unsigned int c1_a, c1_b;
|
unsigned int c1_a, c1_b;
|
|
|
ahi = a >> 16;
|
ahi = a >> 16;
|
alo = a & 0xffff;
|
alo = a & 0xffff;
|
bhi = b >> 16;
|
bhi = b >> 16;
|
blo = b & 0xffff;
|
blo = b & 0xffff;
|
|
|
c0 = alo * blo;
|
c0 = alo * blo;
|
c1_a = ahi * blo;
|
c1_a = ahi * blo;
|
c1_b = alo * bhi;
|
c1_b = alo * bhi;
|
c2 = ahi * bhi;
|
c2 = ahi * bhi;
|
|
|
c2 += (c1_a >> 16) + (c1_b >> 16);
|
c2 += (c1_a >> 16) + (c1_b >> 16);
|
c1 = (c1_a & 0xffff) + (c1_b & 0xffff) + (c0 >> 16);
|
c1 = (c1_a & 0xffff) + (c1_b & 0xffff) + (c0 >> 16);
|
c2 += (c1 >> 16);
|
c2 += (c1 >> 16);
|
c0 = (c1 << 16) + (c0 & 0xffff);
|
c0 = (c1 << 16) + (c0 & 0xffff);
|
*hi = c2;
|
*hi = c2;
|
*lo = c0;
|
*lo = c0;
|
}
|
}
|
|
|
/** Simulates MIPS-I multiplier signed behavior*/
|
/** Simulates MIPS-I multiplier signed behavior*/
|
void mult_big_signed(int a,
|
void mult_big_signed(int a,
|
int b,
|
int b,
|
unsigned int *hi,
|
unsigned int *hi,
|
unsigned int *lo){
|
unsigned int *lo){
|
int64_t xa, xb, xr, temp;
|
int64_t xa, xb, xr, temp;
|
int32_t rh, rl;
|
int32_t rh, rl;
|
|
|
xa = a;
|
xa = a;
|
xb = b;
|
xb = b;
|
xr = xa * xb;
|
xr = xa * xb;
|
|
|
temp = (xr >> 32) & 0xffffffff;
|
temp = (xr >> 32) & 0xffffffff;
|
rh = temp;
|
rh = temp;
|
temp = (xr >> 0) & 0xffffffff;
|
temp = (xr >> 0) & 0xffffffff;
|
rl = temp;
|
rl = temp;
|
|
|
*hi = rh;
|
*hi = rh;
|
*lo = rl;
|
*lo = rl;
|
}
|
}
|
|
|
/** Load data from memory (used to simulate load delay slots) */
|
/** Load data from memory (used to simulate load delay slots) */
|
void start_load(t_state *s, int rt, int data){
|
void start_load(t_state *s, int rt, int data){
|
/* load delay slot not simulated */
|
/* load delay slot not simulated */
|
s->r[rt] = data;
|
s->r[rt] = data;
|
}
|
}
|
|
|
/** Execute one cycle of the CPU (including any interlock stall cycles) */
|
/** Execute one cycle of the CPU (including any interlock stall cycles) */
|
void cycle(t_state *s, int show_mode){
|
void cycle(t_state *s, int show_mode){
|
unsigned int opcode;
|
unsigned int opcode;
|
int delay_slot = 0; /* 1 of this instruction is a branch */
|
int delay_slot = 0; /* 1 of this instruction is a branch */
|
unsigned int op, rs, rt, rd, re, func, imm, target;
|
unsigned int op, rs, rt, rd, re, func, imm, target;
|
int trap_cause = 0;
|
int trap_cause = 0;
|
int imm_shift, branch=0, lbranch=2, skip2=0;
|
int imm_shift, branch=0, lbranch=2, skip2=0;
|
int *r=s->r;
|
int *r=s->r;
|
unsigned int *u=(unsigned int*)s->r;
|
unsigned int *u=(unsigned int*)s->r;
|
unsigned int ptr, epc, rSave;
|
unsigned int ptr, epc, rSave;
|
|
|
if(!show_mode){
|
if(!show_mode){
|
/* if we're NOT showing output to console, log state of CPU to file */
|
/* if we're NOT showing output to console, log state of CPU to file */
|
log_cycle(s);
|
log_cycle(s);
|
}
|
}
|
|
|
opcode = mem_read(s, 4, s->pc, 0);
|
opcode = mem_read(s, 4, s->pc, 0);
|
op = (opcode >> 26) & 0x3f;
|
op = (opcode >> 26) & 0x3f;
|
rs = (opcode >> 21) & 0x1f;
|
rs = (opcode >> 21) & 0x1f;
|
rt = (opcode >> 16) & 0x1f;
|
rt = (opcode >> 16) & 0x1f;
|
rd = (opcode >> 11) & 0x1f;
|
rd = (opcode >> 11) & 0x1f;
|
re = (opcode >> 6) & 0x1f;
|
re = (opcode >> 6) & 0x1f;
|
func = opcode & 0x3f;
|
func = opcode & 0x3f;
|
imm = opcode & 0xffff;
|
imm = opcode & 0xffff;
|
imm_shift = (((int)(short)imm) << 2) - 4;
|
imm_shift = (((int)(short)imm) << 2) - 4;
|
target = (opcode << 6) >> 4;
|
target = (opcode << 6) >> 4;
|
ptr = (short)imm + r[rs];
|
ptr = (short)imm + r[rs];
|
r[0] = 0;
|
r[0] = 0;
|
|
|
/* if we are priting state to console, do it now */
|
/* if we are priting state to console, do it now */
|
if(show_mode){
|
if(show_mode){
|
printf("%8.8x %8.8x ", s->pc, opcode);
|
printf("%8.8x %8.8x ", s->pc, opcode);
|
if(op == 0){
|
if(op == 0){
|
printf("%8s ", special_string[func]);
|
printf("%8s ", special_string[func]);
|
}
|
}
|
else if(op == 1){
|
else if(op == 1){
|
printf("%8s ", regimm_string[rt]);
|
printf("%8s ", regimm_string[rt]);
|
}
|
}
|
else{
|
else{
|
printf("%8s ", opcode_string[op]);
|
printf("%8s ", opcode_string[op]);
|
}
|
}
|
|
|
printf("$%2.2d $%2.2d $%2.2d $%2.2d ", rs, rt, rd, re);
|
printf("$%2.2d $%2.2d $%2.2d $%2.2d ", rs, rt, rd, re);
|
printf("%4.4x", imm);
|
printf("%4.4x", imm);
|
if(show_mode == 1){
|
if(show_mode == 1){
|
printf(" r[%2.2d]=%8.8x r[%2.2d]=%8.8x", rs, r[rs], rt, r[rt]);
|
printf(" r[%2.2d]=%8.8x r[%2.2d]=%8.8x", rs, r[rs], rt, r[rt]);
|
}
|
}
|
printf("\n");
|
printf("\n");
|
}
|
}
|
|
|
/* if we're just showing state to console, quit and don't run instruction */
|
/* if we're just showing state to console, quit and don't run instruction */
|
if(show_mode > 5){
|
if(show_mode > 5){
|
return;
|
return;
|
}
|
}
|
|
|
/* epc will point to the victim instruction, i.e. THIS instruction */
|
/* epc will point to the victim instruction, i.e. THIS instruction */
|
epc = s->pc;
|
epc = s->pc;
|
|
|
/* If we catch a jump instruction jumping to itself, assume we hit the
|
/* If we catch a jump instruction jumping to itself, assume we hit the
|
and of the program and quit. */
|
and of the program and quit. */
|
if(s->pc == s->pc_next+4){
|
if(s->pc == s->pc_next+4){
|
printf("\n\nEndless loop at 0x%08x\n\n", s->pc-4);
|
printf("\n\nEndless loop at 0x%08x\n\n", s->pc-4);
|
s->wakeup = 1;
|
s->wakeup = 1;
|
}
|
}
|
s->pc = s->pc_next;
|
s->pc = s->pc_next;
|
s->pc_next = s->pc_next + 4;
|
s->pc_next = s->pc_next + 4;
|
if(s->skip){
|
if(s->skip){
|
s->skip = 0;
|
s->skip = 0;
|
return;
|
return;
|
}
|
}
|
rSave = r[rt];
|
rSave = r[rt];
|
|
|
switch(op){
|
switch(op){
|
case 0x00:/*SPECIAL*/
|
case 0x00:/*SPECIAL*/
|
switch(func){
|
switch(func){
|
case 0x00:/*SLL*/ r[rd]=r[rt]<<re; break;
|
case 0x00:/*SLL*/ r[rd]=r[rt]<<re; break;
|
case 0x02:/*SRL*/ r[rd]=u[rt]>>re; break;
|
case 0x02:/*SRL*/ r[rd]=u[rt]>>re; break;
|
case 0x03:/*SRA*/ r[rd]=r[rt]>>re; break;
|
case 0x03:/*SRA*/ r[rd]=r[rt]>>re; break;
|
case 0x04:/*SLLV*/ r[rd]=r[rt]<<r[rs]; break;
|
case 0x04:/*SLLV*/ r[rd]=r[rt]<<r[rs]; break;
|
case 0x06:/*SRLV*/ r[rd]=u[rt]>>r[rs]; break;
|
case 0x06:/*SRLV*/ r[rd]=u[rt]>>r[rs]; break;
|
case 0x07:/*SRAV*/ r[rd]=r[rt]>>r[rs]; break;
|
case 0x07:/*SRAV*/ r[rd]=r[rt]>>r[rs]; break;
|
case 0x08:/*JR*/ delay_slot=1;
|
case 0x08:/*JR*/ delay_slot=1;
|
s->pc_next=r[rs]; break;
|
s->pc_next=r[rs]; break;
|
case 0x09:/*JALR*/ delay_slot=1;
|
case 0x09:/*JALR*/ delay_slot=1;
|
r[rd]=s->pc_next;
|
r[rd]=s->pc_next;
|
s->pc_next=r[rs]; break;
|
s->pc_next=r[rs]; break;
|
case 0x0a:/*MOVZ*/ if(!r[rt]) r[rd]=r[rs]; break; /*IV*/
|
case 0x0a:/*MOVZ*/ if(!r[rt]) r[rd]=r[rs]; break; /*IV*/
|
case 0x0b:/*MOVN*/ if(r[rt]) r[rd]=r[rs]; break; /*IV*/
|
case 0x0b:/*MOVN*/ if(r[rt]) r[rd]=r[rs]; break; /*IV*/
|
case 0x0c:/*SYSCALL*/ trap_cause = 8;
|
case 0x0c:/*SYSCALL*/ trap_cause = 8;
|
s->exceptionId=1; break;
|
s->exceptionId=1; break;
|
case 0x0d:/*BREAK*/ trap_cause = 9;
|
case 0x0d:/*BREAK*/ trap_cause = 9;
|
s->exceptionId=1; break;
|
s->exceptionId=1; break;
|
case 0x0f:/*SYNC*/ s->wakeup=1; break;
|
case 0x0f:/*SYNC*/ s->wakeup=1; break;
|
case 0x10:/*MFHI*/ r[rd]=s->hi; break;
|
case 0x10:/*MFHI*/ r[rd]=s->hi; break;
|
case 0x11:/*FTHI*/ s->hi=r[rs]; break;
|
case 0x11:/*FTHI*/ s->hi=r[rs]; break;
|
case 0x12:/*MFLO*/ r[rd]=s->lo; break;
|
case 0x12:/*MFLO*/ r[rd]=s->lo; break;
|
case 0x13:/*MTLO*/ s->lo=r[rs]; break;
|
case 0x13:/*MTLO*/ s->lo=r[rs]; break;
|
case 0x18:/*MULT*/ mult_big_signed(r[rs],r[rt],&s->hi,&s->lo); break;
|
case 0x18:/*MULT*/ mult_big_signed(r[rs],r[rt],&s->hi,&s->lo); break;
|
case 0x19:/*MULTU*/ mult_big(r[rs],r[rt],&s->hi,&s->lo); break;
|
case 0x19:/*MULTU*/ mult_big(r[rs],r[rt],&s->hi,&s->lo); break;
|
case 0x1a:/*DIV*/ s->lo=r[rs]/r[rt]; s->hi=r[rs]%r[rt]; break;
|
case 0x1a:/*DIV*/ s->lo=r[rs]/r[rt]; s->hi=r[rs]%r[rt]; break;
|
case 0x1b:/*DIVU*/ s->lo=u[rs]/u[rt]; s->hi=u[rs]%u[rt]; break;
|
case 0x1b:/*DIVU*/ s->lo=u[rs]/u[rt]; s->hi=u[rs]%u[rt]; break;
|
case 0x20:/*ADD*/ r[rd]=r[rs]+r[rt]; break;
|
case 0x20:/*ADD*/ r[rd]=r[rs]+r[rt]; break;
|
case 0x21:/*ADDU*/ r[rd]=r[rs]+r[rt]; break;
|
case 0x21:/*ADDU*/ r[rd]=r[rs]+r[rt]; break;
|
case 0x22:/*SUB*/ r[rd]=r[rs]-r[rt]; break;
|
case 0x22:/*SUB*/ r[rd]=r[rs]-r[rt]; break;
|
case 0x23:/*SUBU*/ r[rd]=r[rs]-r[rt]; break;
|
case 0x23:/*SUBU*/ r[rd]=r[rs]-r[rt]; break;
|
case 0x24:/*AND*/ r[rd]=r[rs]&r[rt]; break;
|
case 0x24:/*AND*/ r[rd]=r[rs]&r[rt]; break;
|
case 0x25:/*OR*/ r[rd]=r[rs]|r[rt]; break;
|
case 0x25:/*OR*/ r[rd]=r[rs]|r[rt]; break;
|
case 0x26:/*XOR*/ r[rd]=r[rs]^r[rt]; break;
|
case 0x26:/*XOR*/ r[rd]=r[rs]^r[rt]; break;
|
case 0x27:/*NOR*/ r[rd]=~(r[rs]|r[rt]); break;
|
case 0x27:/*NOR*/ r[rd]=~(r[rs]|r[rt]); break;
|
case 0x2a:/*SLT*/ r[rd]=r[rs]<r[rt]; break;
|
case 0x2a:/*SLT*/ r[rd]=r[rs]<r[rt]; break;
|
case 0x2b:/*SLTU*/ r[rd]=u[rs]<u[rt]; break;
|
case 0x2b:/*SLTU*/ r[rd]=u[rs]<u[rt]; break;
|
case 0x2d:/*DADDU*/r[rd]=r[rs]+u[rt]; break;
|
case 0x2d:/*DADDU*/r[rd]=r[rs]+u[rt]; break;
|
case 0x31:/*TGEU*/ break;
|
case 0x31:/*TGEU*/ break;
|
case 0x32:/*TLT*/ break;
|
case 0x32:/*TLT*/ break;
|
case 0x33:/*TLTU*/ break;
|
case 0x33:/*TLTU*/ break;
|
case 0x34:/*TEQ*/ break;
|
case 0x34:/*TEQ*/ break;
|
case 0x36:/*TNE*/ break;
|
case 0x36:/*TNE*/ break;
|
default: printf("ERROR0(*0x%x~0x%x)\n", s->pc, opcode);
|
default: printf("ERROR0(*0x%x~0x%x)\n", s->pc, opcode);
|
s->wakeup=1;
|
s->wakeup=1;
|
}
|
}
|
break;
|
break;
|
case 0x01:/*REGIMM*/
|
case 0x01:/*REGIMM*/
|
switch(rt){
|
switch(rt){
|
case 0x10:/*BLTZAL*/ r[31]=s->pc_next;
|
case 0x10:/*BLTZAL*/ r[31]=s->pc_next;
|
case 0x00:/*BLTZ*/ branch=r[rs]<0; break;
|
case 0x00:/*BLTZ*/ branch=r[rs]<0; break;
|
case 0x11:/*BGEZAL*/ r[31]=s->pc_next;
|
case 0x11:/*BGEZAL*/ r[31]=s->pc_next;
|
case 0x01:/*BGEZ*/ branch=r[rs]>=0; break;
|
case 0x01:/*BGEZ*/ branch=r[rs]>=0; break;
|
case 0x12:/*BLTZALL*/r[31]=s->pc_next;
|
case 0x12:/*BLTZALL*/r[31]=s->pc_next;
|
case 0x02:/*BLTZL*/ lbranch=r[rs]<0; break;
|
case 0x02:/*BLTZL*/ lbranch=r[rs]<0; break;
|
case 0x13:/*BGEZALL*/r[31]=s->pc_next;
|
case 0x13:/*BGEZALL*/r[31]=s->pc_next;
|
case 0x03:/*BGEZL*/ lbranch=r[rs]>=0; break;
|
case 0x03:/*BGEZL*/ lbranch=r[rs]>=0; break;
|
default: printf("ERROR1\n"); s->wakeup=1;
|
default: printf("ERROR1\n"); s->wakeup=1;
|
}
|
}
|
break;
|
break;
|
case 0x03:/*JAL*/ r[31]=s->pc_next;
|
case 0x03:/*JAL*/ r[31]=s->pc_next;
|
case 0x02:/*J*/ delay_slot=1;
|
case 0x02:/*J*/ delay_slot=1;
|
s->pc_next=(s->pc&0xf0000000)|target; break;
|
s->pc_next=(s->pc&0xf0000000)|target; break;
|
case 0x04:/*BEQ*/ branch=r[rs]==r[rt]; break;
|
case 0x04:/*BEQ*/ branch=r[rs]==r[rt]; break;
|
case 0x05:/*BNE*/ branch=r[rs]!=r[rt]; break;
|
case 0x05:/*BNE*/ branch=r[rs]!=r[rt]; break;
|
case 0x06:/*BLEZ*/ branch=r[rs]<=0; break;
|
case 0x06:/*BLEZ*/ branch=r[rs]<=0; break;
|
case 0x07:/*BGTZ*/ branch=r[rs]>0; break;
|
case 0x07:/*BGTZ*/ branch=r[rs]>0; break;
|
case 0x08:/*ADDI*/ r[rt]=r[rs]+(short)imm; break;
|
case 0x08:/*ADDI*/ r[rt]=r[rs]+(short)imm; break;
|
case 0x09:/*ADDIU*/ u[rt]=u[rs]+(short)imm; break;
|
case 0x09:/*ADDIU*/ u[rt]=u[rs]+(short)imm; break;
|
case 0x0a:/*SLTI*/ r[rt]=r[rs]<(short)imm; break;
|
case 0x0a:/*SLTI*/ r[rt]=r[rs]<(short)imm; break;
|
case 0x0b:/*SLTIU*/ u[rt]=u[rs]<(unsigned int)(short)imm; break;
|
case 0x0b:/*SLTIU*/ u[rt]=u[rs]<(unsigned int)(short)imm; break;
|
case 0x0c:/*ANDI*/ r[rt]=r[rs]&imm; break;
|
case 0x0c:/*ANDI*/ r[rt]=r[rs]&imm; break;
|
case 0x0d:/*ORI*/ r[rt]=r[rs]|imm; break;
|
case 0x0d:/*ORI*/ r[rt]=r[rs]|imm; break;
|
case 0x0e:/*XORI*/ r[rt]=r[rs]^imm; break;
|
case 0x0e:/*XORI*/ r[rt]=r[rs]^imm; break;
|
case 0x0f:/*LUI*/ r[rt]=(imm<<16); break;
|
case 0x0f:/*LUI*/ r[rt]=(imm<<16); break;
|
case 0x10:/*COP0*/
|
case 0x10:/*COP0*/
|
if((opcode & (1<<23)) == 0){ //move from CP0
|
if((opcode & (1<<23)) == 0){ //move from CP0
|
if(rd == 12){
|
if(rd == 12){
|
r[rt]=s->status;
|
r[rt]=s->status;
|
}
|
}
|
else if(rd == 13){
|
else if(rd == 13){
|
r[rt]=s->cp0_cause;
|
r[rt]=s->cp0_cause;
|
}
|
}
|
else{
|
else{
|
r[rt]=s->epc;
|
r[rt]=s->epc;
|
}
|
}
|
}
|
}
|
else{ //move to CP0
|
else{ //move to CP0
|
s->status=r[rt]&1;
|
s->status=r[rt]&1;
|
if(s->processId && (r[rt]&2)){
|
if(s->processId && (r[rt]&2)){
|
s->userMode|=r[rt]&2;
|
s->userMode|=r[rt]&2;
|
//printf("CpuStatus=%d %d %d\n", r[rt], s->status, s->userMode);
|
//printf("CpuStatus=%d %d %d\n", r[rt], s->status, s->userMode);
|
//s->wakeup = 1;
|
//s->wakeup = 1;
|
//printf("pc=0x%x\n", epc);
|
//printf("pc=0x%x\n", epc);
|
}
|
}
|
}
|
}
|
break;
|
break;
|
// case 0x11:/*COP1*/ break;
|
// case 0x11:/*COP1*/ break;
|
// case 0x12:/*COP2*/ break;
|
// case 0x12:/*COP2*/ break;
|
// case 0x13:/*COP3*/ break;
|
// case 0x13:/*COP3*/ break;
|
case 0x14:/*BEQL*/ lbranch=r[rs]==r[rt]; break;
|
case 0x14:/*BEQL*/ lbranch=r[rs]==r[rt]; break;
|
case 0x15:/*BNEL*/ lbranch=r[rs]!=r[rt]; break;
|
case 0x15:/*BNEL*/ lbranch=r[rs]!=r[rt]; break;
|
case 0x16:/*BLEZL*/ lbranch=r[rs]<=0; break;
|
case 0x16:/*BLEZL*/ lbranch=r[rs]<=0; break;
|
case 0x17:/*BGTZL*/ lbranch=r[rs]>0; break;
|
case 0x17:/*BGTZL*/ lbranch=r[rs]>0; break;
|
// case 0x1c:/*MAD*/ break; /*IV*/
|
// case 0x1c:/*MAD*/ break; /*IV*/
|
case 0x20:/*LB*/ //r[rt]=(signed char)mem_read(s,1,ptr,1); break;
|
case 0x20:/*LB*/ //r[rt]=(signed char)mem_read(s,1,ptr,1); break;
|
start_load(s, rt,(signed char)mem_read(s,1,ptr,1));
|
start_load(s, rt,(signed char)mem_read(s,1,ptr,1));
|
break;
|
break;
|
|
|
case 0x21:/*LH*/ //r[rt]=(signed short)mem_read(s,2,ptr,1); break;
|
case 0x21:/*LH*/ //r[rt]=(signed short)mem_read(s,2,ptr,1); break;
|
start_load(s, rt, (signed short)mem_read(s,2,ptr,1));
|
start_load(s, rt, (signed short)mem_read(s,2,ptr,1));
|
break;
|
break;
|
case 0x22:/*LWL*/ //target=8*(ptr&3);
|
case 0x22:/*LWL*/ //target=8*(ptr&3);
|
//r[rt]=(r[rt]&~(0xffffffff<<target))|
|
//r[rt]=(r[rt]&~(0xffffffff<<target))|
|
// (mem_read(s,4,ptr&~3)<<target); break;
|
// (mem_read(s,4,ptr&~3)<<target); break;
|
break;
|
break;
|
case 0x23:/*LW*/ //r[rt]=mem_read(s,4,ptr,1); break;
|
case 0x23:/*LW*/ //r[rt]=mem_read(s,4,ptr,1); break;
|
start_load(s, rt, mem_read(s,4,ptr,1));
|
start_load(s, rt, mem_read(s,4,ptr,1));
|
break;
|
break;
|
case 0x24:/*LBU*/ //r[rt]=(unsigned char)mem_read(s,1,ptr,1); break;
|
case 0x24:/*LBU*/ //r[rt]=(unsigned char)mem_read(s,1,ptr,1); break;
|
start_load(s, rt, (unsigned char)mem_read(s,1,ptr,1));
|
start_load(s, rt, (unsigned char)mem_read(s,1,ptr,1));
|
break;
|
break;
|
case 0x25:/*LHU*/ //r[rt]= (unsigned short)mem_read(s,2,ptr,1);
|
case 0x25:/*LHU*/ //r[rt]= (unsigned short)mem_read(s,2,ptr,1);
|
start_load(s, rt, (unsigned short)mem_read(s,2,ptr,1));
|
start_load(s, rt, (unsigned short)mem_read(s,2,ptr,1));
|
break;
|
break;
|
case 0x26:/*LWR*/ //target=32-8*(ptr&3);
|
case 0x26:/*LWR*/ //target=32-8*(ptr&3);
|
//r[rt]=(r[rt]&~((unsigned int)0xffffffff>>target))|
|
//r[rt]=(r[rt]&~((unsigned int)0xffffffff>>target))|
|
//((unsigned int)mem_read(s,4,ptr&~3)>>target);
|
//((unsigned int)mem_read(s,4,ptr&~3)>>target);
|
break;
|
break;
|
case 0x28:/*SB*/ mem_write(s,1,ptr,r[rt],1); break;
|
case 0x28:/*SB*/ mem_write(s,1,ptr,r[rt],1); break;
|
case 0x29:/*SH*/ mem_write(s,2,ptr,r[rt],1); break;
|
case 0x29:/*SH*/ mem_write(s,2,ptr,r[rt],1); break;
|
case 0x2a:/*SWL*/ //mem_write(s,1,ptr,r[rt]>>24);
|
case 0x2a:/*SWL*/ //mem_write(s,1,ptr,r[rt]>>24);
|
//mem_write(s,1,ptr+1,r[rt]>>16);
|
//mem_write(s,1,ptr+1,r[rt]>>16);
|
//mem_write(s,1,ptr+2,r[rt]>>8);
|
//mem_write(s,1,ptr+2,r[rt]>>8);
|
//mem_write(s,1,ptr+3,r[rt]); break;
|
//mem_write(s,1,ptr+3,r[rt]); break;
|
case 0x2b:/*SW*/ mem_write(s,4,ptr,r[rt],1); break;
|
case 0x2b:/*SW*/ mem_write(s,4,ptr,r[rt],1); break;
|
case 0x2e:/*SWR*/ break; //FIXME
|
case 0x2e:/*SWR*/ break; //FIXME
|
case 0x2f:/*CACHE*/ break;
|
case 0x2f:/*CACHE*/ break;
|
case 0x30:/*LL*/ //r[rt]=mem_read(s,4,ptr); break;
|
case 0x30:/*LL*/ //r[rt]=mem_read(s,4,ptr); break;
|
start_load(s, rt, mem_read(s,4,ptr,1));
|
start_load(s, rt, mem_read(s,4,ptr,1));
|
break;
|
break;
|
// case 0x31:/*LWC1*/ break;
|
// case 0x31:/*LWC1*/ break;
|
// case 0x32:/*LWC2*/ break;
|
// case 0x32:/*LWC2*/ break;
|
// case 0x33:/*LWC3*/ break;
|
// case 0x33:/*LWC3*/ break;
|
// case 0x35:/*LDC1*/ break;
|
// case 0x35:/*LDC1*/ break;
|
// case 0x36:/*LDC2*/ break;
|
// case 0x36:/*LDC2*/ break;
|
// case 0x37:/*LDC3*/ break;
|
// case 0x37:/*LDC3*/ break;
|
// case 0x38:/*SC*/ *(int*)ptr=r[rt]; r[rt]=1; break;
|
// case 0x38:/*SC*/ *(int*)ptr=r[rt]; r[rt]=1; break;
|
case 0x38:/*SC*/ mem_write(s,4,ptr,r[rt],1); r[rt]=1; break;
|
case 0x38:/*SC*/ mem_write(s,4,ptr,r[rt],1); r[rt]=1; break;
|
// case 0x39:/*SWC1*/ break;
|
// case 0x39:/*SWC1*/ break;
|
// case 0x3a:/*SWC2*/ break;
|
// case 0x3a:/*SWC2*/ break;
|
// case 0x3b:/*SWC3*/ break;
|
// case 0x3b:/*SWC3*/ break;
|
// case 0x3d:/*SDC1*/ break;
|
// case 0x3d:/*SDC1*/ break;
|
// case 0x3e:/*SDC2*/ break;
|
// case 0x3e:/*SDC2*/ break;
|
// case 0x3f:/*SDC3*/ break;
|
// case 0x3f:/*SDC3*/ break;
|
default:
|
default:
|
/* FIXME should trap unimplemented opcodes */
|
/* FIXME should trap unimplemented opcodes */
|
printf("ERROR2 address=0x%x opcode=0x%x\n", s->pc, opcode);
|
printf("ERROR2 address=0x%x opcode=0x%x\n", s->pc, opcode);
|
s->wakeup=1;
|
s->wakeup=1;
|
}
|
}
|
s->pc_next += (branch || lbranch == 1) ? imm_shift : 0;
|
s->pc_next += (branch || lbranch == 1) ? imm_shift : 0;
|
s->pc_next &= ~3;
|
s->pc_next &= ~3;
|
s->skip = (lbranch == 0) | skip2;
|
s->skip = (lbranch == 0) | skip2;
|
|
|
/* If there was trouble (failed assertions), log it */
|
/* If there was trouble (failed assertions), log it */
|
if(s->failed_assertions!=0){
|
if(s->failed_assertions!=0){
|
log_failed_assertions(s);
|
log_failed_assertions(s);
|
s->failed_assertions=0;
|
s->failed_assertions=0;
|
}
|
}
|
|
|
/* if there's a delayed load pending, do it now: load reg with memory data*/
|
/* if there's a delayed load pending, do it now: load reg with memory data*/
|
/* load delay slots not simulated */
|
/* load delay slots not simulated */
|
|
|
/* Handle exceptions */
|
/* Handle exceptions */
|
if(s->exceptionId){
|
if(s->exceptionId){
|
r[rt] = rSave;
|
r[rt] = rSave;
|
s->cp0_cause = (s->delay_slot & 0x1) << 31 | (trap_cause & 0x1f);
|
s->cp0_cause = (s->delay_slot & 0x1) << 31 | (trap_cause & 0x1f);
|
/* adjust epc if we (i.e. the victim instruction) are in a delay slot */
|
/* adjust epc if we (i.e. the victim instruction) are in a delay slot */
|
if(s->delay_slot){
|
if(s->delay_slot){
|
epc = epc - 4;
|
epc = epc - 4;
|
}
|
}
|
s->epc = epc;
|
s->epc = epc;
|
s->pc_next = 0x3c;
|
s->pc_next = 0x3c;
|
s->skip = 1;
|
s->skip = 1;
|
s->exceptionId = 0;
|
s->exceptionId = 0;
|
s->userMode = 0;
|
s->userMode = 0;
|
//s->wakeup = 1;
|
//s->wakeup = 1;
|
}
|
}
|
|
|
/* if this instruction was any kind of branch that actually jumped, then
|
/* if this instruction was any kind of branch that actually jumped, then
|
the next instruction will be in a delay slot. Remember it. */
|
the next instruction will be in a delay slot. Remember it. */
|
delay_slot = ((lbranch==1) || branch || delay_slot);
|
delay_slot = ((lbranch==1) || branch || delay_slot);
|
s->delay_slot = delay_slot;
|
s->delay_slot = delay_slot;
|
}
|
}
|
|
|
/** Dump CPU state to console */
|
/** Dump CPU state to console */
|
void show_state(t_state *s){
|
void show_state(t_state *s){
|
int i,j;
|
int i,j;
|
printf("pid=%d userMode=%d, epc=0x%x\n", s->processId, s->userMode, s->epc);
|
printf("pid=%d userMode=%d, epc=0x%x\n", s->processId, s->userMode, s->epc);
|
printf("hi=0x%08x lo=0x%08x\n", s->hi, s->lo);
|
printf("hi=0x%08x lo=0x%08x\n", s->hi, s->lo);
|
for(i = 0; i < 4; ++i){
|
for(i = 0; i < 4; ++i){
|
printf("%2.2d ", i * 8);
|
printf("%2.2d ", i * 8);
|
for(j = 0; j < 8; ++j){
|
for(j = 0; j < 8; ++j){
|
printf("%8.8x ", s->r[i*8+j]);
|
printf("%8.8x ", s->r[i*8+j]);
|
}
|
}
|
printf("\n");
|
printf("\n");
|
}
|
}
|
//printf("%8.8lx %8.8lx %8.8lx %8.8lx\n", s->pc, s->pc_next, s->hi, s->lo);
|
//printf("%8.8lx %8.8lx %8.8lx %8.8lx\n", s->pc, s->pc_next, s->hi, s->lo);
|
j = s->pc;
|
j = s->pc;
|
for(i = -4; i <= 8; ++i){
|
for(i = -4; i <= 8; ++i){
|
printf("%c", i==0 ? '*' : ' ');
|
printf("%c", i==0 ? '*' : ' ');
|
s->pc = j + i * 4;
|
s->pc = j + i * 4;
|
cycle(s, 10);
|
cycle(s, 10);
|
}
|
}
|
s->pc = j;
|
s->pc = j;
|
}
|
}
|
|
|
/** Show debug monitor prompt and execute user command */
|
/** Show debug monitor prompt and execute user command */
|
void do_debug(t_state *s){
|
void do_debug(t_state *s){
|
int ch;
|
int ch;
|
int i, j=0, watch=0, addr;
|
int i, j=0, watch=0, addr;
|
s->pc_next = s->pc + 4;
|
s->pc_next = s->pc + 4;
|
s->skip = 0;
|
s->skip = 0;
|
s->wakeup = 0;
|
s->wakeup = 0;
|
show_state(s);
|
show_state(s);
|
ch = ' ';
|
ch = ' ';
|
for(;;){
|
for(;;){
|
if(ch != 'n'){
|
if(ch != 'n'){
|
if(watch){
|
if(watch){
|
printf("0x%8.8x=0x%8.8x\n", watch, mem_read(s, 4, watch,0));
|
printf("0x%8.8x=0x%8.8x\n", watch, mem_read(s, 4, watch,0));
|
}
|
}
|
printf("1=Debug 2=t_trace 3=Step 4=BreakPt 5=Go 6=Memory ");
|
printf("1=Debug 2=t_trace 3=Step 4=BreakPt 5=Go 6=Memory ");
|
printf("7=Watch 8=Jump 9=Quit A=dump > ");
|
printf("7=Watch 8=Jump 9=Quit A=dump > ");
|
}
|
}
|
ch = getch();
|
ch = getch();
|
if(ch != 'n'){
|
if(ch != 'n'){
|
printf("\n");
|
printf("\n");
|
}
|
}
|
switch(ch){
|
switch(ch){
|
case 'a': case 'A':
|
case 'a': case 'A':
|
dump_trace_buffer(s); break;
|
dump_trace_buffer(s); break;
|
case '1': case 'd': case ' ':
|
case '1': case 'd': case ' ':
|
cycle(s, 0); show_state(s); break;
|
cycle(s, 0); show_state(s); break;
|
case 'n':
|
case 'n':
|
cycle(s, 1); break;
|
cycle(s, 1); break;
|
case '2': case 't':
|
case '2': case 't':
|
cycle(s, 0); printf("*"); cycle(s, 10); break;
|
cycle(s, 0); printf("*"); cycle(s, 10); break;
|
case '3': case 's':
|
case '3': case 's':
|
printf("Count> ");
|
printf("Count> ");
|
scanf("%d", &j);
|
scanf("%d", &j);
|
for(i = 0; i < j; ++i){
|
for(i = 0; i < j; ++i){
|
cycle(s, 1);
|
cycle(s, 1);
|
}
|
}
|
show_state(s);
|
show_state(s);
|
break;
|
break;
|
case '4': case 'b':
|
case '4': case 'b':
|
printf("Line> ");
|
printf("Line> ");
|
scanf("%x", &j);
|
scanf("%x", &j);
|
printf("break point=0x%x\n", j);
|
printf("break point=0x%x\n", j);
|
break;
|
break;
|
case '5': case 'g':
|
case '5': case 'g':
|
s->wakeup = 0;
|
s->wakeup = 0;
|
cycle(s, 0);
|
cycle(s, 0);
|
while(s->wakeup == 0){
|
while(s->wakeup == 0){
|
if(s->pc == j){
|
if(s->pc == j){
|
printf("\n\nStop: pc = 0x%08x\n\n", j);
|
printf("\n\nStop: pc = 0x%08x\n\n", j);
|
break;
|
break;
|
}
|
}
|
cycle(s, 0);
|
cycle(s, 0);
|
}
|
}
|
show_state(s);
|
show_state(s);
|
break;
|
break;
|
case 'G':
|
case 'G':
|
s->wakeup = 0;
|
s->wakeup = 0;
|
cycle(s, 1);
|
cycle(s, 1);
|
while(s->wakeup == 0){
|
while(s->wakeup == 0){
|
if(s->pc == j){
|
if(s->pc == j){
|
break;
|
break;
|
}
|
}
|
cycle(s, 1);
|
cycle(s, 1);
|
}
|
}
|
show_state(s);
|
show_state(s);
|
break;
|
break;
|
case '6': case 'm':
|
case '6': case 'm':
|
printf("Memory> ");
|
printf("Memory> ");
|
scanf("%x", &j);
|
scanf("%x", &j);
|
for(i = 0; i < 8; ++i){
|
for(i = 0; i < 8; ++i){
|
printf("%8.8x ", mem_read(s, 4, j+i*4, 0));
|
printf("%8.8x ", mem_read(s, 4, j+i*4, 0));
|
}
|
}
|
printf("\n");
|
printf("\n");
|
break;
|
break;
|
case '7': case 'w':
|
case '7': case 'w':
|
printf("Watch> ");
|
printf("Watch> ");
|
scanf("%x", &watch);
|
scanf("%x", &watch);
|
break;
|
break;
|
case '8': case 'j':
|
case '8': case 'j':
|
printf("Jump> ");
|
printf("Jump> ");
|
scanf("%x", &addr);
|
scanf("%x", &addr);
|
s->pc = addr;
|
s->pc = addr;
|
s->pc_next = addr + 4;
|
s->pc_next = addr + 4;
|
show_state(s);
|
show_state(s);
|
break;
|
break;
|
case '9': case 'q':
|
case '9': case 'q':
|
return;
|
return;
|
}
|
}
|
}
|
}
|
}
|
}
|
|
|
/** Read binary code and data files */
|
/** Read binary code and data files */
|
int read_program(t_state *s, uint32_t num_files, char **file_names){
|
int read_program(t_state *s, uint32_t num_files, char **file_names){
|
FILE *in;
|
FILE *in;
|
uint32_t bytes, i, j, files_read=0;
|
uint32_t bytes, i, files_read=0;
|
|
|
for(i=0;i<NUM_MEM_BLOCKS;i++){
|
for(i=0;i<NUM_MEM_BLOCKS;i++){
|
if(i<num_files){
|
if(i<num_files){
|
in = fopen(file_names[i], "rb");
|
in = fopen(file_names[i], "rb");
|
if(in == NULL){
|
if(in == NULL){
|
for(j=0;j<i;j++){
|
free_cpu(s);
|
free(s->blocks[j].mem);
|
|
}
|
|
printf("Can't open file %s, quitting!\n",file_names[i]);
|
printf("Can't open file %s, quitting!\n",file_names[i]);
|
getch();
|
getch();
|
return(2);
|
return(2);
|
}
|
}
|
|
|
s->blocks[i].mem = (unsigned char*)malloc(s->blocks[i].size);
|
|
|
|
if(s->blocks[i].mem == NULL){
|
|
for(j=0;j<i;j++){
|
|
free(s->blocks[j].mem);
|
|
}
|
|
printf("Can't allocate %d bytes, quitting!\n",
|
|
s->blocks[i].size);
|
|
getch();
|
|
return(2);
|
|
}
|
|
|
|
memset(s->blocks[i].mem, 0, s->blocks[i].size);
|
|
|
|
bytes = fread(s->blocks[i].mem, 1, s->blocks[i].size, in);
|
bytes = fread(s->blocks[i].mem, 1, s->blocks[i].size, in);
|
fclose(in);
|
fclose(in);
|
printf("%-16s [size= %6d, start= 0x%08x]\n",
|
printf("%-16s [size= %6d, start= 0x%08x]\n",
|
s->blocks[i].name,
|
s->blocks[i].name,
|
bytes,
|
bytes,
|
s->blocks[i].start);
|
s->blocks[i].start);
|
files_read++;
|
files_read++;
|
}
|
}
|
else{
|
else{
|
break;
|
break;
|
}
|
}
|
}
|
}
|
|
|
if(!files_read){
|
if(!files_read){
|
|
free_cpu(s);
|
printf("No binary object files read, quitting\n");
|
printf("No binary object files read, quitting\n");
|
}
|
return 0;
|
else{
|
|
for(i=files_read;i<NUM_MEM_BLOCKS;i++){
|
|
s->blocks[i].mem = (unsigned char*)malloc(s->blocks[i].size);
|
|
|
|
if(s->blocks[i].mem == NULL){
|
|
for(j=0;j<i;j++){
|
|
free(s->blocks[j].mem);
|
|
}
|
|
printf("Can't allocate %d bytes, quitting!\n",
|
|
s->blocks[i].size);
|
|
getch();
|
|
return(2);
|
|
}
|
|
}
|
|
}
|
}
|
|
|
return 0;
|
return files_read;
|
}
|
}
|
|
|
/*----------------------------------------------------------------------------*/
|
/*----------------------------------------------------------------------------*/
|
|
|
int main(int argc,char *argv[]){
|
int main(int argc,char *argv[]){
|
t_state state, *s=&state;
|
t_state state, *s=&state;
|
|
|
printf("MIPS-I emulator\n");
|
printf("MIPS-I emulator\n");
|
init_cpu(s);
|
if(!init_cpu(s)){
|
|
printf("Trouble allocating memory, quitting!\n");
|
|
getch();
|
|
return 1;
|
|
};
|
|
|
/* do a minimal check on args */
|
/* do a minimal check on args */
|
if(argc==3 || argc==2){
|
if(argc==3 || argc==2){
|
/* */
|
/* */
|
}
|
}
|
else{
|
else{
|
printf("Usage:");
|
printf("Usage:");
|
printf(" slite file.exe <bin code file> [<bin data file>]\n");
|
printf(" slite file.exe <bin code file> [<bin data file>]\n");
|
return 0;
|
return 0;
|
}
|
}
|
|
|
if(read_program(s, argc-1, &(argv[1]))){
|
if(!read_program(s, argc-1, &(argv[1]))){
|
return 2;
|
return 2;
|
}
|
}
|
|
|
init_trace_buffer(s, "sw_sim_log.txt");
|
init_trace_buffer(s, "sw_sim_log.txt");
|
|
|
/* NOTE: Original mlite supported loading little-endian code, which this
|
/* NOTE: Original mlite supported loading little-endian code, which this
|
program doesn't. The endianess-conversion code has been removed.
|
program doesn't. The endianess-conversion code has been removed.
|
*/
|
*/
|
|
|
/* Simulate a CPU reset */
|
/* Simulate a CPU reset */
|
reset_cpu(s);
|
reset_cpu(s);
|
|
|
/* Enter debug command interface; will only exit clean with user command */
|
/* Enter debug command interface; will only exit clean with user command */
|
do_debug(s);
|
do_debug(s);
|
|
|
/* Close and deallocate everything and quit */
|
/* Close and deallocate everything and quit */
|
close_trace_buffer(s);
|
close_trace_buffer(s);
|
free_cpu(s);
|
free_cpu(s);
|
return(0);
|
return(0);
|
}
|
}
|
|
|
/*----------------------------------------------------------------------------*/
|
/*----------------------------------------------------------------------------*/
|
|
|
|
|
void init_trace_buffer(t_state *s, const char *log_file_name){
|
void init_trace_buffer(t_state *s, const char *log_file_name){
|
int i;
|
int i;
|
|
|
#if FILE_LOGGING_DISABLED
|
#if FILE_LOGGING_DISABLED
|
s->t.log = NULL;
|
s->t.log = NULL;
|
return;
|
return;
|
#else
|
#else
|
for(i=0;i<TRACE_BUFFER_SIZE;i++){
|
for(i=0;i<TRACE_BUFFER_SIZE;i++){
|
s->t.buf[i]=0xffffffff;
|
s->t.buf[i]=0xffffffff;
|
}
|
}
|
s->t.next = 0;
|
s->t.next = 0;
|
|
|
/* if file logging is enabled, open log file */
|
/* if file logging is enabled, open log file */
|
if(log_file_name!=NULL){
|
if(log_file_name!=NULL){
|
s->t.log = fopen(log_file_name, "w");
|
s->t.log = fopen(log_file_name, "w");
|
if(s->t.log==NULL){
|
if(s->t.log==NULL){
|
printf("Error opening log file '%s', file logging disabled\n",
|
printf("Error opening log file '%s', file logging disabled\n",
|
log_file_name);
|
log_file_name);
|
}
|
}
|
}
|
}
|
else{
|
else{
|
s->t.log = NULL;
|
s->t.log = NULL;
|
}
|
}
|
#endif
|
#endif
|
}
|
}
|
|
|
/** Dumps last jump targets as a chunk of hex numbers (older is left top) */
|
/** Dumps last jump targets as a chunk of hex numbers (older is left top) */
|
void dump_trace_buffer(t_state *s){
|
void dump_trace_buffer(t_state *s){
|
int i, col;
|
int i, col;
|
|
|
for(i=0, col=0;i<TRACE_BUFFER_SIZE;i++, col++){
|
for(i=0, col=0;i<TRACE_BUFFER_SIZE;i++, col++){
|
printf("%08x ", s->t.buf[s->t.next + i]);
|
printf("%08x ", s->t.buf[s->t.next + i]);
|
if((col % 8)==7){
|
if((col % 8)==7){
|
printf("\n");
|
printf("\n");
|
}
|
}
|
}
|
}
|
}
|
}
|
|
|
/** Logs last cycle's activity (changes in state and/or loads/stores) */
|
/** Logs last cycle's activity (changes in state and/or loads/stores) */
|
void log_cycle(t_state *s){
|
void log_cycle(t_state *s){
|
static unsigned int last_pc = 0;
|
static unsigned int last_pc = 0;
|
int i;
|
int i;
|
|
|
/* store PC in trace buffer only if there was a jump */
|
/* store PC in trace buffer only if there was a jump */
|
if(s->pc != (last_pc+4)){
|
if(s->pc != (last_pc+4)){
|
s->t.buf[s->t.next] = s->pc;
|
s->t.buf[s->t.next] = s->pc;
|
s->t.next = (s->t.next + 1) % TRACE_BUFFER_SIZE;
|
s->t.next = (s->t.next + 1) % TRACE_BUFFER_SIZE;
|
}
|
}
|
last_pc = s->pc;
|
last_pc = s->pc;
|
|
|
/* if file logging is enabled, dump a trace log to file */
|
/* if file logging is enabled, dump a trace log to file */
|
if(s->t.log!=NULL){
|
if(s->t.log!=NULL){
|
for(i=0;i<32;i++){
|
for(i=0;i<32;i++){
|
if(s->t.pr[i] != s->r[i]){
|
if(s->t.pr[i] != s->r[i]){
|
fprintf(s->t.log, "(%08X) [%02X]=%08X\n", s->pc, i, s->r[i]);
|
fprintf(s->t.log, "(%08X) [%02X]=%08X\n", s->pc, i, s->r[i]);
|
}
|
}
|
s->t.pr[i] = s->r[i];
|
s->t.pr[i] = s->r[i];
|
}
|
}
|
if(s->lo != s->t.lo){
|
if(s->lo != s->t.lo){
|
fprintf(s->t.log, "(%08X) [LO]=%08X\n", s->pc, s->lo);
|
fprintf(s->t.log, "(%08X) [LO]=%08X\n", s->pc, s->lo);
|
}
|
}
|
s->t.lo = s->lo;
|
s->t.lo = s->lo;
|
|
|
if(s->hi != s->t.hi){
|
if(s->hi != s->t.hi){
|
fprintf(s->t.log, "(%08X) [HI]=%08X\n", s->pc, s->hi);
|
fprintf(s->t.log, "(%08X) [HI]=%08X\n", s->pc, s->hi);
|
}
|
}
|
s->t.hi = s->hi;
|
s->t.hi = s->hi;
|
|
|
if(s->epc != s->t.epc){
|
if(s->epc != s->t.epc){
|
fprintf(s->t.log, "(%08X) [EP]=%08X\n", s->pc, s->epc);
|
fprintf(s->t.log, "(%08X) [EP]=%08X\n", s->pc, s->epc);
|
}
|
}
|
s->t.epc = s->epc;
|
s->t.epc = s->epc;
|
}
|
}
|
}
|
}
|
|
|
/** Frees debug buffers and closes log file */
|
/** Frees debug buffers and closes log file */
|
void close_trace_buffer(t_state *s){
|
void close_trace_buffer(t_state *s){
|
if(s->t.log){
|
if(s->t.log){
|
fclose(s->t.log);
|
fclose(s->t.log);
|
}
|
}
|
}
|
}
|
|
|
/** Logs a message for each failed assertion, each in a line */
|
/** Logs a message for each failed assertion, each in a line */
|
void log_failed_assertions(t_state *s){
|
void log_failed_assertions(t_state *s){
|
unsigned bitmap = s->failed_assertions;
|
unsigned bitmap = s->failed_assertions;
|
int i = 0;
|
int i = 0;
|
|
|
/* This loop will crash the program if the message table is too short...*/
|
/* This loop will crash the program if the message table is too short...*/
|
if(s->t.log != NULL){
|
if(s->t.log != NULL){
|
for(i=0;i<32;i++){
|
for(i=0;i<32;i++){
|
if(bitmap & 0x1){
|
if(bitmap & 0x1){
|
fprintf(s->t.log, "ASSERTION FAILED: [%08x] %s\n",
|
fprintf(s->t.log, "ASSERTION FAILED: [%08x] %s\n",
|
s->faulty_address,
|
s->faulty_address,
|
assertion_messages[i]);
|
assertion_messages[i]);
|
}
|
}
|
bitmap = bitmap >> 1;
|
bitmap = bitmap >> 1;
|
}
|
}
|
}
|
}
|
}
|
}
|
|
|
void free_cpu(t_state *s){
|
void free_cpu(t_state *s){
|
int i;
|
int i;
|
|
|
for(i=0;i<NUM_MEM_BLOCKS;i++){
|
for(i=0;i<NUM_MEM_BLOCKS;i++){
|
free(s->blocks[i].mem);
|
free(s->blocks[i].mem);
|
s->blocks[i].mem = NULL;
|
s->blocks[i].mem = NULL;
|
|
|
}
|
}
|
}
|
}
|
|
|
void reset_cpu(t_state *s){
|
void reset_cpu(t_state *s){
|
s->pc = VECTOR_RESET; /* reset start vector */
|
s->pc = VECTOR_RESET; /* reset start vector */
|
s->delay_slot = 0;
|
s->delay_slot = 0;
|
s->failed_assertions = 0; /* no failed assertions pending */
|
s->failed_assertions = 0; /* no failed assertions pending */
|
}
|
}
|
|
|
void init_cpu(t_state *s){
|
int init_cpu(t_state *s){
|
int i;
|
int i, j;
|
|
|
memset(s, 0, sizeof(t_state));
|
memset(s, 0, sizeof(t_state));
|
s->big_endian = 1;
|
s->big_endian = 1;
|
for(i=0;i<NUM_MEM_BLOCKS;i++){
|
for(i=0;i<NUM_MEM_BLOCKS;i++){
|
s->blocks[i].start = default_blocks[i].start;
|
s->blocks[i].start = default_blocks[i].start;
|
s->blocks[i].size = default_blocks[i].size;
|
s->blocks[i].size = default_blocks[i].size;
|
s->blocks[i].name = default_blocks[i].name;
|
s->blocks[i].name = default_blocks[i].name;
|
s->blocks[i].mem = NULL;
|
s->blocks[i].mask = default_blocks[i].mask;
|
|
|
|
s->blocks[i].mem = (unsigned char*)malloc(s->blocks[i].size);
|
|
|
|
if(s->blocks[i].mem == NULL){
|
|
for(j=0;j<i;j++){
|
|
free(s->blocks[j].mem);
|
|
}
|
|
return 0;
|
|
}
|
|
memset(s->blocks[i].mem, 0, s->blocks[i].size);
|
}
|
}
|
|
return NUM_MEM_BLOCKS;
|
}
|
}
|
|
|