OpenCores
URL https://opencores.org/ocsvn/ion/ion/trunk

Subversion Repositories ion

[/] [ion/] [trunk/] [tools/] [slite/] [src/] [slite.c] - Rev 163

Go to most recent revision | Compare with Previous | Blame | View Log

/*------------------------------------------------------------------------------
* slite.c -- MIPS-I simulator based on Steve Rhoad's "mlite"
*
* This is a heavily modified version of Steve Rhoad's "mlite" simulator, which
* is part of his PLASMA project (original date: 1/31/01).
*
*-------------------------------------------------------------------------------
* Usage:
*     slite [options]
*
* See function 'usage' for a very brief explaination of the available options.
*
* Generally, upon startup the program will allocate some RAM for the simulated
* system and initialize it with the contents of one or more plain binary,
* big-endian object files. Then it will simulate a cpu reset and start the
* simulation, in interactive or batch mode.
*
* 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
* a simple way to validate the hardware for a given program. See the project
* readme files for details.
*
*-------------------------------------------------------------------------------
* This program simulates the CPU connected to a certain memory map (chosen from
* a set of predefined options) and to a UART.
* The UART is hardcoded at a fixed address and is not configurable in runtime.
* The simulated UART includes the real UART status bits, hardcoded to 'always
* ready' so that software and hardware simulations can be made identical with
* more ease (no need to simulate the actual cycle count of TX/RX, etc.).
*-------------------------------------------------------------------------------
* KNOWN BUGS:
*
*-------------------------------------------------------------------------------
* @date 2011-jan-16
*
*-------------------------------------------------------------------------------
* COPYRIGHT:    Software placed into the public domain by the author.
*               Software 'as is' without warranty.  Author liable for nothing.
*
* IMPORTANT: Assumes host is little endian and target is big endian.
*-----------------------------------------------------------------------------*/
 
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <ctype.h>
#include <assert.h>
 
/** CPU identification code (contents of register CP0[15], PRId */
#define R3000_ID (0x00000200)
 
/** Set to !=0 to disable file logging (much faster simulation) */
/* alternately you can just set an unreachable log trigger address */
#define FILE_LOGGING_DISABLED (0)
/** Define to enable cache simulation (unimplemented) */
//#define ENABLE_CACHE
/** Set to !=0 to display a fancier listing of register values */
#define FANCY_REGISTER_DISPLAY (1)
 
 
/*---- Definition of simulated system parameters -----------------------------*/
 
#define VECTOR_RESET (0xbfc00000)
#define VECTOR_TRAP  (0xbfc00180)
 
/** Definition of a memory block */
typedef struct s_block {
    uint32_t start;
    uint32_t size;
    uint32_t mask;
    uint32_t read_only;
    uint8_t  *mem;
    char     *area_name;
} t_block;
 
#define NUM_MEM_BLOCKS      (4)
#define NUM_MEM_MAPS        (4)
 
/** Definition of a memory map */
/* FIXME i/o addresses missing, hardcoded */
typedef struct s_map {
    t_block blocks[NUM_MEM_BLOCKS];
} t_map;
 
 
 
/*  Here's where we define the memory areas (blocks) of the system.
 
    The blocks should be defined in this order: BRAM, XRAM, FLASH
 
    BRAM is FPGA block ram initialized with bootstrap code
    XRAM is external SRAM
    FLASH is external flash
 
    Give any area a size of 0x0 to leave it unused.
 
    When a binary file is specified in the cmd line for one of these areas, it
    will be used to initialize it, checking bounds.
 
    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 how the actual hardware works.
    Make sure the blocks don't overlap or the scheme will fail.
*/
 
#define MAP_DEFAULT         (0)
#define MAP_UCLINUX_BRAM    (1)  /* debug only */
#define MAP_SMALL           (2)
#define MAP_UCLINUX         (3)
 
t_map memory_maps[NUM_MEM_MAPS] = {
    {/* Experimental memory map (default) */
        {/* Bootstrap BRAM, read only */
        {VECTOR_RESET,  0x00004800, 0xf8000000, 1, NULL, "Boot BRAM"},
        /* main external ram block  */
        {0x00000000,    0x00080000, 0xf8000000, 0, NULL, "XRAM0"},
        /* main external ram block  */
        {0x80000000,    0x00080000, 0xf8000000, 0, NULL, "XRAM1"},
        /* external flash block */
        {0xb0000000,    0x00040000, 0xf8000000, 0, NULL, "Flash"},
        }
    },
 
    {/* uClinux memory map with bootstrap BRAM, debug only, to be removed */
        {/* Bootstrap BRAM, read only */
        {VECTOR_RESET,  0x00001000, 0xf8000000, 1, NULL, "Boot BRAM"},
        /* main external ram block  */
        {0x80000000,    0x00200000, 0xf8000000, 0, NULL, "XRAM0"},
        {0x00000000,    0x00400000, 0xf8000000, 0, NULL, "XRAM1"},
        /* external flash block */
        {0xb0000000,    0x00100000, 0xf8000000, 0, NULL, "Flash"},
        }
    },
 
    {/* Experimental memory map with small XRAM */
        {/* Bootstrap BRAM, read only */
        {VECTOR_RESET,  0x00004800, 0xf8000000, 1, NULL, "Boot BRAM"},
        /* main external ram block  */
        {0x00000000,    0x00001000, 0xf8000000, 0, NULL, "XRAM0"},
        /* main external ram block  */
        {0x80000000,    0x00001000, 0xf8000000, 0, NULL, "XRAM1"},
        /* external flash block */
        {0xb0000000,    0x00040000, 0xf8000000, 0, NULL, "Flash"},
        }
    },
 
    {/* uClinux memory map with FLASH and XRAM */
        {/* Flash mapped at two different addresses is actually meant to be
            a single chip (note they have the same size). */
         /* E.g. put the bootloader at 0xbfc00000 and the romfs at 0xb0020000;
            chip offsets will be 0x0 and 0x20000. */
         /* Don't forget there's no address translation here. */
        {0xbfc00000,    0x00400000, 0xf8000000, 1, NULL, "Flash (bootloader)"},
        {0xb0000000,    0x00400000, 0xf8000000, 1, NULL, "Flash (romfs)"},
        /* main external ram block (kernal & user areas ) */
        {0x80000000,    0x00200000, 0xf8000000, 0, NULL, "XRAM (kernel)"},
        {0x00000000,    0x00400000, 0xf8000000, 0, NULL, "XRAM (user)"},
        }
    },
};
 
 
/*---- end of system parameters ----------------------------------------------*/
 
 
/** Values for the command line arguments */
typedef struct s_args {
    /** !=0 to trap on unimplemented opcodes, 0 to print warning and NOP */
    uint32_t trap_on_reserved;
    /** address to start execution from (by default, reset vector) */
    uint32_t start_addr;
    /** memory map to be used */
    uint32_t memory_map;
    /** implement unaligned load/stores (don't just trap them) */
    uint32_t do_unaligned;
    /** start simulation without showing monitor prompt and quit on
        end condition -- useful for batch runs */
    uint32_t no_prompt;
    /** breakpoint address (0xffffffff if unused) */
    uint32_t breakpoint;
    /** a code fetch from this address starts logging */
    uint32_t log_trigger_address;
    /** full name of log file */
    char *log_file_name;
    /** bin file to load to each area or null */
    char *bin_filename[NUM_MEM_BLOCKS];
    /** offset into area (in bytes) where bin wile will be loaded */
    uint32_t offset[NUM_MEM_BLOCKS];
} t_args;
/** Parse cmd line args globally accessible */
t_args cmd_line_args;
 
 
/*---- Endianess conversion macros -------------------------------------------*/
 
#define ntohs(A) ( ((A)>>8) | (((A)&0xff)<<8) )
#define htons(A) ntohs(A)
#define ntohl(A) ( ((A)>>24) | (((A)&0xff0000)>>8) | (((A)&0xff00)<<8) | ((A)<<24) )
#define htonl(A) ntohl(A)
 
/*---- OS-dependent support functions and definitions ------------------------*/
#ifndef WIN32
//Support for Linux
#define putch putchar
#include <termios.h>
#include <unistd.h>
 
void slite_sleep(unsigned int value){
    usleep(value * 1000);
}
 
int kbhit(void){
    struct termios oldt, newt;
    struct timeval tv;
    fd_set read_fd;
 
    tcgetattr(STDIN_FILENO, &oldt);
    newt = oldt;
    newt.c_lflag &= ~(ICANON | ECHO);
    tcsetattr(STDIN_FILENO, TCSANOW, &newt);
    tv.tv_sec=0;
    tv.tv_usec=0;
    FD_ZERO(&read_fd);
    FD_SET(0,&read_fd);
    if(select(1, &read_fd, NULL, NULL, &tv) == -1){
        return 0;
    }
    //tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
    if(FD_ISSET(0,&read_fd)){
        return 1;
    }
    return 0;
}
 
int getch(void){
    struct termios oldt, newt;
    int ch;
 
    tcgetattr(STDIN_FILENO, &oldt);
    newt = oldt;
    newt.c_lflag &= ~(ICANON | ECHO);
    tcsetattr(STDIN_FILENO, TCSANOW, &newt);
    ch = getchar();
    //tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
    return ch;
}
#else
//Support for Windows
#include <conio.h>
extern void __stdcall Sleep(unsigned long value);
 
void slite_sleep(unsigned int value){
    Sleep(value);
}
 
#endif
/*---- End of OS-dependent support functions and definitions -----------------*/
 
/*---- Hardware system parameters --------------------------------------------*/
 
/* Much of this is a remnant from Plasma's mlite and is  no longer used. */
/* FIXME Refactor HW system params */
 
#define DBG_REGS          (0x20010000)
 
#define UART_WRITE        0x20000000
#define UART_READ         0x20000000
#define IRQ_MASK          0x20000010
#define IRQ_STATUS        0x20000020
#define CONFIG_REG        0x20000070
#define MMU_PROCESS_ID    0x20000080
#define MMU_FAULT_ADDR    0x20000090
#define MMU_TLB           0x200000a0
 
#define IRQ_UART_READ_AVAILABLE  0x001
#define IRQ_UART_WRITE_AVAILABLE 0x002
#define IRQ_COUNTER18_NOT        0x004
#define IRQ_COUNTER18            0x008
#define IRQ_MMU                  0x200
 
/*----------------------------------------------------------------------------*/
 
/* These are flags that will be used to notify the main cycle function of any
   failed assertions in its subfunctions. */
#define ASRT_UNALIGNED_READ         (1<<0)
#define ASRT_UNALIGNED_WRITE        (1<<1)
 
char *assertion_messages[2] = {
   "Unaligned read",
   "Unaligned write"
};
 
 
/** Length of debugging jump target queue */
#define TRACE_BUFFER_SIZE (32)
 
typedef struct s_trace {
   unsigned int buf[TRACE_BUFFER_SIZE];   /**< queue of last jump targets */
   unsigned int next;                     /**< internal queue head pointer */
   FILE *log;                             /**< text log file or NULL */
   int log_triggered;                     /**< !=0 if log has been triggered */
   uint32_t log_trigger_address;          /**< */
   int pr[32];                            /**< last value of register bank */
   int hi, lo, epc, status;               /**< last value of internal regs */
   int disasm_ptr;                        /**< disassembly pointer */
} t_trace;
 
typedef struct s_state {
   unsigned failed_assertions;            /**< assertion bitmap */
   unsigned faulty_address;               /**< addr that failed assertion */
   uint32_t do_unaligned;                 /**< !=0 to enable unaligned L/S */
   uint32_t breakpoint;                   /**< BP address of 0xffffffff */
 
   int delay_slot;              /**< !=0 if prev. instruction was a branch */
 
   int r[32];
   int opcode;
   int pc, pc_next, epc;
   uint32_t op_addr;            /**< address of opcode being simulated */
   unsigned int hi;
   unsigned int lo;
   int status;
   unsigned cp0_cause;
   int userMode;                /**< DEPRECATED, to be removed */
   int processId;               /**< DEPRECATED, to be removed */
   int exceptionId;             /**< DEPRECATED, to be removed */
   int faultAddr;               /**< DEPRECATED, to be removed */
   int irqStatus;               /**< DEPRECATED, to be removed */
   int skip;
   t_trace t;
   t_block blocks[NUM_MEM_BLOCKS];
   int wakeup;
   int big_endian;
} t_state;
 
static char *reg_names[]={
    "zero","at","v0","v1","a0","a1","a2","a3",
    "t0","t1","t2","t3","t4","t5","t6","t7",
    "s0","s1","s2","s3","s4","s5","s6","s7",
    "t8","t9","k0","k1","gp","sp","s8","ra"
};
 
static char *opcode_string[]={
   "0SPECIAL","0REGIMM","1J","1JAL","2BEQ","2BNE","3BLEZ","3BGTZ",
   "5ADDI","5ADDIU","5SLTI","5SLTIU","5ANDI","5ORI","5XORI","6LUI",
   "cCOP0","cCOP1","cCOP2","cCOP3","2BEQL","2BNEL","3BLEZL","3BGTZL",
   "0?","0?","0?","0?","0?","0?","0?","0?",
   "8LB","8LH","8LWL","8LW","8LBU","8LHU","8LWR","0?",
   "8SB","8SH","8SWL","8SW","0?","0?","8SWR","0CACHE",
   "0LL","0LWC1","0LWC2","0LWC3","?","0LDC1","0LDC2","0LDC3"
   "0SC","0SWC1","0SWC2","0SWC3","?","0SDC1","0SDC2","0SDC3"
};
 
static char *special_string[]={
   "4SLL","0?","4SRL","4SRA","bSLLV","0?","bSRLV","bSRAV",
   "aJR","aJALR","0MOVZ","0MOVN","0SYSCALL","0BREAK","0?","0SYNC",
   "0MFHI","0MTHI","0MFLO","0MTLO","0?","0?","0?","0?",
   "0MULT","0MULTU","0DIV","0DIVU","0?","0?","0?","0?",
   "7ADD","7ADDU","7SUB","7SUBU","7AND","7OR","7XOR","7NOR",
   "0?","0?","7SLT","7SLTU","0?","0DADDU","0?","0?",
   "7TGE","7TGEU","7TLT","7TLTU","7TEQ","0?","7TNE","0?",
   "0?","0?","0?","0?","0?","0?","0?","0?"
};
 
static char *regimm_string[]={
   "9BLTZ","9BGEZ","9BLTZL","9BGEZL","0?","0?","0?","0?",
   "0TGEI","0TGEIU","0TLTI","0TLTIU","0TEQI","0?","0TNEI","0?",
   "9BLTZAL","9BEQZAL","9BLTZALL","9BGEZALL","0?","0?","0?","0?",
   "0?","0?","0?","0?","0?","0?","0?","0?"
};
 
static unsigned int HWMemory[8];
 
/*---- Local function prototypes ---------------------------------------------*/
 
/* Debug and logging */
void init_trace_buffer(t_state *s, t_args *args);
void close_trace_buffer(t_state *s);
void dump_trace_buffer(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_failed_assertions(t_state *s);
uint32_t log_enabled(t_state *s);
void trigger_log(t_state *s);
void print_opcode_fields(uint32_t opcode);
 
int32_t parse_cmd_line(uint32_t argc, char **argv, t_args *args);
void usage(void);
 
/* CPU model */
void free_cpu(t_state *s);
int init_cpu(t_state *s, t_args *args);
void reset_cpu(t_state *s);
void unimplemented(t_state *s, const char *txt);
void reverse_endianess(uint8_t *data, uint32_t bytes);
 
/* Hardware simulation */
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 start_load(t_state *s, uint32_t addr, int rt, int data);
 
 
/*---- Local functions -------------------------------------------------------*/
 
/** 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){
    /* if bit CP0.16==1, this is a D-Cache line invalidation access and
           the HW will not read any actual data, so skip the log (@note1) */
    if(log_enabled(s) && log!=0 && !(s->status & 0x00010000)){
        fprintf(s->t.log, "(%08X) [%08X] <**>=%08X RD\n",
              s->op_addr, full_address, word_value);
    }
}
 
/** Read memory, optionally logging */
int mem_read(t_state *s, int size, unsigned int address, int log){
    unsigned int value=0, word_value=0, i, ptr;
    unsigned int full_address = address;
 
    s->irqStatus |= IRQ_UART_WRITE_AVAILABLE;
    switch(address){
    case UART_READ:
        /* FIXME Take input from text file */
        while(!kbhit());
        HWMemory[0] = getch();
        //s->irqStatus &= ~IRQ_UART_READ_AVAILABLE; //clear bit
        printf("%c", HWMemory[0]);
        return (HWMemory[0] << 24) | 0x03;
    case IRQ_MASK:
       return HWMemory[1];
    case IRQ_MASK + 4:
       slite_sleep(10);
       return 0;
    case IRQ_STATUS:
       /*if(kbhit())
          s->irqStatus |= IRQ_UART_READ_AVAILABLE;
       return s->irqStatus;
       */
       /* FIXME Optionally simulate UART TX delay */
       word_value = 0x00000003; /* Ready to TX and RX */
       //log_read(s, full_address, word_value, size, log);
       return word_value;
    case MMU_PROCESS_ID:
       return s->processId;
    case MMU_FAULT_ADDR:
       return s->faultAddr;
    }
 
    /* point ptr to the byte in the block, or NULL is the address is unmapped */
    ptr = 0;
    for(i=0;i<NUM_MEM_BLOCKS;i++){
        if((address & s->blocks[i].mask) ==
           (s->blocks[i].start & s->blocks[i].mask)){
            ptr = (unsigned)(s->blocks[i].mem) +
                  ((address - s->blocks[i].start) % s->blocks[i].size);
            break;
        }
    }
    if(!ptr){
        /* address out of mapped blocks: log and return zero */
        /* if bit CP0.16==1, this is a D-Cache line invalidation access and
           the HW will not read any actual data, so skip the log (@note1) */
        if(log_enabled(s) && log!=0 && !(s->status & (1<<16))){
            fprintf(s->t.log, "(%08X) [%08X] <**>=%08X RD UNMAPPED\n",
                s->pc, full_address, 0);
        }
        return 0;
    }
 
    /* get the whole word */
    word_value = *(int*)(ptr&0xfffffffc);
    if(s->big_endian){
        word_value = ntohl(word_value);
    }
 
    switch(size){
    case 4:
        if(address & 3){
            printf("Unaligned access PC=0x%x address=0x%x\n",
                (int)s->pc, (int)address);
        }
        if((address & 3) != 0){
            /* unaligned word, log fault */
            s->failed_assertions |= ASRT_UNALIGNED_READ;
            s->faulty_address = address;
            address = address & 0xfffffffc;
        }
        value = *(int*)ptr;
        if(s->big_endian){
            value = ntohl(value);
        }
        break;
    case 2:
        if((address & 1) != 0){
            /* unaligned halfword, log fault */
            s->failed_assertions |= ASRT_UNALIGNED_READ;
            s->faulty_address = address;
            address = address & 0xfffffffe;
        }
        value = *(unsigned short*)ptr;
        if(s->big_endian){
            value = ntohs((unsigned short)value);
        }
        break;
    case 1:
        value = *(unsigned char*)ptr;
        break;
    default:
        /* This is a bug, display warning */
        printf("\n\n**** BUG: wrong memory read size at 0x%08x\n\n", s->pc);
    }
 
    //log_read(s, full_address, value, size, log);
    return(value);
}
 
/** Write memory */
void mem_write(t_state *s, int size, unsigned address, unsigned value, int log){
    unsigned int i, ptr, mask, dvalue, b0, b1, b2, b3;
 
    if(log_enabled(s)){
        b0 = value & 0x000000ff;
        b1 = value & 0x0000ff00;
        b2 = value & 0x00ff0000;
        b3 = value & 0xff000000;
 
        switch(size){
        case 4:  mask = 0x0f;
            dvalue = value;
            break;
        case 2:
            if((address&0x2)==0){
                mask = 0xc;
                dvalue = b1<<16 | b0<<16;
            }
            else{
               mask = 0x3;
               dvalue = b1 | b0;
            }
            break;
        case 1:
            switch(address%4){
            case 0 : mask = 0x8;
                dvalue = b0<<24;
                break;
            case 1 : mask = 0x4;
                dvalue = b0<<16;
                break;
            case 2 : mask = 0x2;
                dvalue = b0<<8;
                break;
            case 3 : mask = 0x1;
                dvalue = b0;
                break;
            }
            break;
        default:
            printf("BUG: mem write size invalid (%08x)\n", s->pc);
            exit(2);
        }
 
        fprintf(s->t.log, "(%08X) [%08X] |%02X|=%08X WR\n",
                //s->op_addr, address&0xfffffffc, mask, dvalue);
                s->op_addr, address, mask, dvalue);
    }
 
    if((address&0xffff0000)==DBG_REGS){
        printf("[%04x]=%08x\n", address & 0xffff, value);
    }
 
    switch(address){
    case UART_WRITE:
        putch(value);
        fflush(stdout);
        return;
    case IRQ_MASK:
        HWMemory[1] = value;
        return;
    case IRQ_STATUS:
        s->irqStatus = value;
        return;
    case CONFIG_REG:
        return;
    case MMU_PROCESS_ID:
        //printf("processId=%d\n", value);
        s->processId = value;
        return;
    }
 
    ptr = 0;
    for(i=0;i<NUM_MEM_BLOCKS;i++){
        if((address & s->blocks[i].mask) ==
                  (s->blocks[i].start & s->blocks[i].mask)){
            ptr = (unsigned)(s->blocks[i].mem) +
                            ((address - s->blocks[i].start) % s->blocks[i].size);
 
            if(s->blocks[i].read_only){
                if(log_enabled(s) && log!=0){
                    fprintf(s->t.log, "(%08X) [%08X] |%02X|=%08X WR READ ONLY\n",
                    s->op_addr, address, mask, dvalue);
                    return;
                }
            }
            break;
        }
    }
    if(!ptr){
        /* address out of mapped blocks: log and return zero */
        if(log_enabled(s) && log!=0){
            fprintf(s->t.log, "(%08X) [%08X] |%02X|=%08X WR UNMAPPED\n",
                s->op_addr, address, mask, dvalue);
        }
        return;
    }
 
    switch(size){
    case 4:
        if((address & 3) != 0){
            /* unaligned word, log fault */
            s->failed_assertions |= ASRT_UNALIGNED_WRITE;
            s->faulty_address = address;
            address = address & (~0x03);
        }
        if(s->big_endian){
            value = htonl(value);
        }
        *(int*)ptr = value;
        break;
    case 2:
        if((address & 1) != 0){
            /* unaligned halfword, log fault */
            s->failed_assertions |= ASRT_UNALIGNED_WRITE;
            s->faulty_address = address;
            address = address & (~0x01);
        }
        if(s->big_endian){
            value = htons((unsigned short)value);
        }
        *(short*)ptr = (unsigned short)value;
        break;
    case 1:
        *(char*)ptr = (unsigned char)value;
        break;
    default:
        /* This is a bug, display warning */
        printf("\n\n**** BUG: wrong memory write size at 0x%08x\n\n", s->pc);
    }
}
 
/*-- unaligned store and load instructions -----------------------------------*/
/*
 These are meant to be left unimplemented and trapped. These functions simulate
 the unaligned r/w instructions until proper trap handlers are written.
*/
 
void mem_swl(t_state *s, uint32_t address, uint32_t value, uint32_t log){
    uint32_t data, offset;
 
    if(!s->do_unaligned) return unimplemented(s, "SWL");
 
    offset = (address & 0x03);
    address = (address & (~0x03));
    data = value;
 
    while(offset<4){
        mem_write(s,1,address+offset,(data>>24) & 0xff,0);
        data = data << 8;
        offset++;
    }
}
 
void mem_swr(t_state *s, uint32_t address, uint32_t value, uint32_t log){
    uint32_t data, offset;
 
    if(!s->do_unaligned) return unimplemented(s, "SWR");
 
    offset = (address & 0x03);
    address = (address & (~0x03));
    data = value;
 
    while(offset>=0){
        mem_write(s,1,address+offset,data & 0xff,0);
        data = data >> 8;
        offset--;
    }
}
 
void mem_lwr(t_state *s, uint32_t address, uint32_t reg_index, uint32_t log){
    uint32_t offset, data;
    uint32_t disp[4] = {24,         16,         8,          0};
    uint32_t mask[4] = {0x000000ff, 0x0000ffff, 0x00ffffff, 0xffffffff};
 
    if(!s->do_unaligned) return unimplemented(s, "LWR");
 
    offset = (address & 0x03);
    address = (address & (~0x03));
 
    data = mem_read(s, 4, address, 0);
    data = (data >> disp[offset]) & mask[offset];
 
    s->r[reg_index] = (s->r[reg_index] & (~mask[offset])) | data;
}
 
void mem_lwl(t_state *s, uint32_t address, uint32_t reg_index, uint32_t log){
    uint32_t offset, data;
    uint32_t disp[4] = {0,          8,          16,         24};
    uint32_t mask[4] = {0xffffffff, 0xffffff00, 0xffff0000, 0xff000000};
 
    if(!s->do_unaligned) return unimplemented(s, "LWL");
 
    offset = (address & 0x03);
    address = (address & (~0x03));
 
    data = mem_read(s, 4, address, 0);
    data = (data << disp[offset]) & mask[offset];
 
    s->r[reg_index] = (s->r[reg_index] & (~mask[offset])) | data;
}
 
 
/*---- Optional MMU and cache implementation ---------------------------------*/
 
/*
   The actual core does not have a cache so all of the original Plasma mlite.c
   code for cache simulation has been removed.
*/
 
/*---- End optional cache implementation -------------------------------------*/
 
 
/** Simulates MIPS-I multiplier unsigned behavior*/
void mult_big(unsigned int a,
              unsigned int b,
              unsigned int *hi,
              unsigned int *lo){
    unsigned int ahi, alo, bhi, blo;
    unsigned int c0, c1, c2;
    unsigned int c1_a, c1_b;
 
    ahi = a >> 16;
    alo = a & 0xffff;
    bhi = b >> 16;
    blo = b & 0xffff;
 
    c0 = alo * blo;
    c1_a = ahi * blo;
    c1_b = alo * bhi;
    c2 = ahi * bhi;
 
    c2 += (c1_a >> 16) + (c1_b >> 16);
    c1 = (c1_a & 0xffff) + (c1_b & 0xffff) + (c0 >> 16);
    c2 += (c1 >> 16);
    c0 = (c1 << 16) + (c0 & 0xffff);
    *hi = c2;
    *lo = c0;
}
 
/** Simulates MIPS-I multiplier signed behavior*/
void mult_big_signed(int a,
                     int b,
                     unsigned int *hi,
                     unsigned int *lo){
    int64_t xa, xb, xr, temp;
    int32_t rh, rl;
 
    xa = a;
    xb = b;
    xr = xa * xb;
 
    temp = (xr >> 32) & 0xffffffff;
    rh = temp;
    temp = (xr >> 0) & 0xffffffff;
    rl = temp;
 
    *hi = rh;
    *lo = rl;
}
 
/** Load data from memory (used to simulate load delay slots) */
void start_load(t_state *s, uint32_t addr, int rt, int data){
    /* load delay slot not simulated */
    log_read(s, addr, data, 1, 1);
    s->r[rt] = data;
}
 
/** Execute one cycle of the CPU (including any interlock stall cycles) */
void cycle(t_state *s, int show_mode){
    unsigned int opcode;
    int delay_slot = 0; /* 1 of this instruction is a branch */
    unsigned int op, rs, rt, rd, re, func, imm, target;
    int trap_cause = 0;
    int imm_shift, branch=0, lbranch=2, skip2=0;
    int *r=s->r;
    unsigned int *u=(unsigned int*)s->r;
    unsigned int ptr, epc, rSave;
    char format;
    uint32_t aux;
    uint32_t target_offset16;
    uint32_t target_long;
 
    /* fetch and decode instruction */
    opcode = mem_read(s, 4, s->pc, 0);
    op = (opcode >> 26) & 0x3f;
    rs = (opcode >> 21) & 0x1f;
    rt = (opcode >> 16) & 0x1f;
    rd = (opcode >> 11) & 0x1f;
    re = (opcode >> 6) & 0x1f;
    func = opcode & 0x3f;
    imm = opcode & 0xffff;
    imm_shift = (((int)(short)imm) << 2) - 4;
    target = (opcode << 6) >> 4;
    ptr = (short)imm + r[rs];
    r[0] = 0;
    target_offset16 = opcode & 0xffff;
    if(target_offset16 & 0x8000){
        target_offset16 |= 0xffff0000;
    }
    target_long = (opcode & 0x03ffffff)<<2;
    target_long |= (s->pc & 0xf0000000);
 
    /* Trigger log if we fetch from trigger address */
    if(s->pc == s->t.log_trigger_address){
        trigger_log(s);
    }
 
    /* if we are priting state to console, do it now */
    if(show_mode){
        printf("%8.8x %8.8x ", s->pc, opcode);
        if(op == 0){
            printf("  %-6s ", &(special_string[func][1]));
            format = special_string[func][0];
        }
        else if(op == 1){
            printf("  %-6s ", &(regimm_string[rt][1]));
            format = regimm_string[rt][0];
        }
        else{
            format = opcode_string[op][0];
            if(format!='c'){
                printf("  %-6s ", &(opcode_string[op][1]));
            }
            else{
                aux = op&0x03;
                switch(rs){
                    case 16:
                        /* FIXME partial decoding */
                        printf("  RFE      "); format = ' '; break;
                    case 4:
                        printf("  MTC%1d   ", aux); break;
                    case 0:
                        printf("  MFC%1d   ", aux); break;
                    default:
                        printf("  ???      "); break;
                        format = '?';
                }
            }
        }
 
        switch(format){
            case '1':
                printf("0x%08x", target_long);
                break;
            case '2':
                printf("%s,%s,0x%08x",
                       reg_names[rt], reg_names[rs],
                       (target_offset16*4)+s->pc+4);
                break;
            case '3':
                printf("%s,0x%08x", reg_names[rt], (target_offset16*4)+s->pc+4);
                break;
            case '4':
                printf("%s,%s,%d", reg_names[rd], reg_names[rt], re);
                break;
            case '5':
                printf("%s,%s,0x%04x",
                       reg_names[rt], reg_names[rs],
                       target_offset16&0xffff);
                break;
            case '6':
                printf("%s,0x%04x",
                       reg_names[rt],
                       target_offset16&0xffff);
                break;
            case '7':
                printf("%s,%s,%s", reg_names[rd], reg_names[rs], reg_names[rt]);
                break;
            case '8':
                printf("%s,%d(%s)", reg_names[rt],
                       (target_offset16), reg_names[rs]);
                break;
            case '9':
                printf("%s,0x%08x", reg_names[rt], (target_offset16*4)+s->pc+4);
                break;
            case 'a':
                printf("%s", reg_names[rs]);
                break;
            case 'b':
                printf("%s,%s,%s", reg_names[rd], reg_names[rt], reg_names[rs]);
                break;
            case 'c':
                printf("%s,$%d", reg_names[rt], rd);
                break;
            case '0':
                printf("$%2.2d $%2.2d $%2.2d $%2.2d ", rs, rt, rd, re);
                printf("%4.4x", imm);
                break;
            default:;
        }
 
 
        if(show_mode == 1){
            printf(" r[%2.2d]=%8.8x r[%2.2d]=%8.8x", rs, r[rs], rt, r[rt]);
        }
        printf("\n");
    }
 
    /* if we're just showing state to console, quit and don't run instruction */
    if(show_mode > 5){
        return;
    }
 
    /* epc will point to the victim instruction, i.e. THIS instruction */
    epc = s->pc;
 
    /* If we catch a jump instruction jumping to itself, assume we hit the
       and of the program and quit. */
    if(s->pc == s->pc_next+4){
        printf("\n\nEndless loop at 0x%08x\n\n", s->pc-4);
        s->wakeup = 1;
    }
    s->op_addr = s->pc;
    s->pc = s->pc_next;
    s->pc_next = s->pc_next + 4;
    if(s->skip){
        s->skip = 0;
        return;
    }
    rSave = r[rt];
 
    switch(op){
    case 0x00:/*SPECIAL*/
        switch(func){
        case 0x00:/*SLL*/  r[rd]=r[rt]<<re;          break;
        case 0x02:/*SRL*/  r[rd]=u[rt]>>re;          break;
        case 0x03:/*SRA*/  r[rd]=r[rt]>>re;          break;
        case 0x04:/*SLLV*/ r[rd]=r[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 0x08:/*JR*/   delay_slot=1;
                           s->pc_next=r[rs];         break;
        case 0x09:/*JALR*/ delay_slot=1;
                           r[rd]=s->pc_next;
                           s->pc_next=r[rs]; break;
        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 0x0c:/*SYSCALL*/ trap_cause = 8;
                              s->exceptionId=1;
                              /*
                              FIXME enable when running uClinux
                              printf("SYSCALL (%08x)\n", s->pc);
                              */
                              break;
        case 0x0d:/*BREAK*/   trap_cause = 9;
                              s->exceptionId=1;
                              /*
                              FIXME enable when running uClinux
                              printf("BREAK (%08x)\n", s->pc);
                              */
                              break;
        case 0x0f:/*SYNC*/ s->wakeup=1;              break;
        case 0x10:/*MFHI*/ r[rd]=s->hi;              break;
        case 0x11:/*FTHI*/ s->hi=r[rs];              break;
        case 0x12:/*MFLO*/ r[rd]=s->lo;              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 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 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 0x21:/*ADDU*/ 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 0x24:/*AND*/  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 0x27:/*NOR*/  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 0x2d:/*DADDU*/r[rd]=r[rs]+u[rt];        break;
        case 0x31:/*TGEU*/ break;
        case 0x32:/*TLT*/  break;
        case 0x33:/*TLTU*/ break;
        case 0x34:/*TEQ*/  break;
        case 0x36:/*TNE*/  break;
        default: printf("ERROR0(*0x%x~0x%x)\n", s->pc, opcode);
            /* FIXME should trap unknown opcode */
            s->wakeup=1;
        }
        break;
    case 0x01:/*REGIMM*/
        switch(rt){
            case 0x10:/*BLTZAL*/ r[31]=s->pc_next;
            case 0x00:/*BLTZ*/   branch=r[rs]<0;    break;
            case 0x11:/*BGEZAL*/ r[31]=s->pc_next;
            case 0x01:/*BGEZ*/   branch=r[rs]>=0;   break;
            case 0x12:/*BLTZALL*/r[31]=s->pc_next;
            case 0x02:/*BLTZL*/  lbranch=r[rs]<0;   break;
            case 0x13:/*BGEZALL*/r[31]=s->pc_next;
            case 0x03:/*BGEZL*/  lbranch=r[rs]>=0;  break;
            default: printf("ERROR1\n"); s->wakeup=1;
        }
        break;
    case 0x03:/*JAL*/    r[31]=s->pc_next;
    case 0x02:/*J*/      delay_slot=1;
                       s->pc_next=(s->pc&0xf0000000)|target; break;
    case 0x04:/*BEQ*/    branch=r[rs]==r[rt];     break;
    case 0x05:/*BNE*/    branch=r[rs]!=r[rt];     break;
    case 0x06:/*BLEZ*/   branch=r[rs]<=0;         break;
    case 0x07:/*BGTZ*/   branch=r[rs]>0;          break;
    case 0x08:/*ADDI*/   r[rt]=r[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 0x0b:/*SLTIU*/  u[rt]=u[rs]<(unsigned int)(short)imm; break;
    case 0x0c:/*ANDI*/   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 0x0f:/*LUI*/    r[rt]=(imm<<16);         break;
    case 0x10:/*COP0*/
        if(s->status & 0x02){ /* kernel mode? */
            if((opcode & (1<<23)) == 0){  //move from CP0 (mfc0)
                //printf("mfc0: [SR]=0x%08x @ [0x%08x]\n", s->status, epc);
                switch(rd){
                    case 12: r[rt]=s->status & 0x0000003f; break;
                    case 13: r[rt]=s->cp0_cause; break;
                    case 14: r[rt]=s->epc; break;
                    case 15: r[rt]=R3000_ID; break;
                    default:
                        break;
                }
            }
            else{                         //move to CP0 (mtc0)
                /* FIXME check CF= reg address */
                if(rd==12){
                    s->status=r[rt] & 0x0003003f; /* mask W/O bits */
                    //printf("mtc0: [SR]=0x%08x @ [0x%08x]\n", s->status, epc);
                }
                else{
                    /* Move to unimplemented COP0 register: display warning */
                    /* FIXME should log ignored move */
                    printf("mtc0 [%2d]=0x%08x @ [0x%08x] IGNORED\n",
                           rd, r[rt], epc);
                }
                /* FIXME remove remnants of Plasma MMU simulation */
                /*
                if(s->processId && (r[rt]&2)){
                    s->userMode|=r[rt]&2;
                    //printf("CpuStatus=%d %d %d\n", r[rt], s->status, s->userMode);
                    //s->wakeup = 1;
                    //printf("pc=0x%x\n", epc);
                }
                */
            }
        }
        else{
            /* tried to execute mtc* or mfc* in user mode: trap */
            printf("COP0 UNAVAILABLE address=0x%08x opcode=0x%x -- ",
                   epc, opcode);
            print_opcode_fields(opcode);
            printf("\n");
 
            trap_cause = 11; /* unavailable coprocessor */
            s->exceptionId=1;
        }
        break;
    case 0x11:/*COP1*/  unimplemented(s,"COP1");
                        break;
//      case 0x12:/*COP2*/ break;
//      case 0x13:/*COP3*/ break;
    case 0x14:/*BEQL*/  lbranch=r[rs]==r[rt];    break;
    case 0x15:/*BNEL*/  lbranch=r[rs]!=r[rt];    break;
    case 0x16:/*BLEZL*/ lbranch=r[rs]<=0;        break;
    case 0x17:/*BGTZL*/ lbranch=r[rs]>0;         break;
//      case 0x1c:/*MAD*/  break;   /*IV*/
    case 0x20:/*LB*/    //r[rt]=(signed char)mem_read(s,1,ptr,1);  break;
                        start_load(s, ptr, rt,(signed char)mem_read(s,1,ptr,1));
                        break;
 
    case 0x21:/*LH*/    //r[rt]=(signed short)mem_read(s,2,ptr,1); break;
                        start_load(s, ptr, rt, (signed short)mem_read(s,2,ptr,1));
                        break;
    case 0x22:/*LWL*/   mem_lwl(s, ptr, rt, 1);
                        //printf("LWL\n");
                        break;
    case 0x23:/*LW*/    //r[rt]=mem_read(s,4,ptr,1);   break;
                        start_load(s, ptr, rt, mem_read(s,4,ptr,1));
                        break;
    case 0x24:/*LBU*/   //r[rt]=(unsigned char)mem_read(s,1,ptr,1); break;
                        start_load(s, ptr, rt, (unsigned char)mem_read(s,1,ptr,1));
                        break;
    case 0x25:/*LHU*/   //r[rt]= (unsigned short)mem_read(s,2,ptr,1);
                        start_load(s, ptr, rt, (unsigned short)mem_read(s,2,ptr,1));
                        break;
    case 0x26:/*LWR*/   mem_lwr(s, ptr, rt, 1);
                        //printf("LWR\n");
                        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 0x2a:/*SWL*/   mem_swl(s, ptr, r[rt], 1);
                        //printf("SWL\n");
                        break;
    case 0x2b:/*SW*/    mem_write(s,4,ptr,r[rt],1);  break;
    case 0x2e:/*SWR*/   mem_swr(s, ptr, r[rt], 1);
                        //printf("SWR\n");
                        break;
    case 0x2f:/*CACHE*/ unimplemented(s,"CACHE");
                        break;
    case 0x30:/*LL*/    //unimplemented(s,"LL");
                        start_load(s, ptr, rt, mem_read(s,4,ptr,1));
                        break;
//      case 0x31:/*LWC1*/ break;
//      case 0x32:/*LWC2*/ break;
//      case 0x33:/*LWC3*/ break;
//      case 0x35:/*LDC1*/ break;
//      case 0x36:/*LDC2*/ break;
//      case 0x37:/*LDC3*/ 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 0x39:/*SWC1*/ break;
//      case 0x3a:/*SWC2*/ break;
//      case 0x3b:/*SWC3*/ break;
//      case 0x3d:/*SDC1*/ break;
//      case 0x3e:/*SDC2*/ break;
//      case 0x3f:/*SDC3*/ break;
    default:  /* unimplemented opcode */
        if(cmd_line_args.trap_on_reserved){
            trap_cause = 10; /* reserved instruction */
            s->exceptionId=1;
        }
        else{
            printf("RESERVED OPCODE address=0x%08x opcode=0x%x -- ",
                   epc, opcode);
            print_opcode_fields(opcode);
            printf("\n");
        }
    }
 
    /* adjust next PC if this was a a jump instruction */
    s->pc_next += (branch || lbranch == 1) ? imm_shift : 0;
    s->pc_next &= ~3;
    s->skip = (lbranch == 0) | skip2;
 
    /* If there was trouble (failed assertions), log it */
    if(s->failed_assertions!=0){
        log_failed_assertions(s);
        s->failed_assertions=0;
    }
 
    /* if there's a delayed load pending, do it now: load reg with memory data*/
    /* load delay slots not simulated */
 
    /* Handle exceptions */
   if(s->exceptionId){
        r[rt] = rSave;
        /* set cause field ... */
        s->cp0_cause = (s->delay_slot & 0x1) << 31 | (trap_cause & 0x1f) << 2;
        /* ...save previous KU/IE flags in SR... */
        s->status = (s->status & 0xffffffc3) | ((s->status & 0x0f) << 2);
        /* ...and raise KU(EXL) kernel mode flag */
        s->status |= 0x02;
        /* adjust epc if we (i.e. the victim instruction) are in a delay slot */
        if(s->delay_slot){
            epc = epc - 4;
        }
        s->epc = epc;
        s->pc_next = VECTOR_TRAP;
        s->skip = 1;
        s->exceptionId = 0;
        s->userMode = 0;
        //s->wakeup = 1;
    }
 
    /* if we're NOT showing output to console, log state of CPU to file */
    if(!show_mode){
        log_cycle(s);
    }
 
 
 
    /* if this instruction was any kind of branch that actually jumped, then
       the next instruction will be in a delay slot. Remember it. */
    delay_slot = ((lbranch==1) || branch || delay_slot);
    s->delay_slot = delay_slot;
}
 
/** Print opcode fields for easier debugging */
void print_opcode_fields(uint32_t opcode){
    uint32_t field;
 
    field = (opcode >> 26)&0x3f;
    printf("%02x:", field);
    field = (opcode >> 21)&0x1f;
    printf("%02x:", field);
    field = (opcode >> 16)&0x1f;
    printf("%02x:", field);
    field = (opcode >> 11)&0x1f;
    printf("%02x:", field);
    field = (opcode >>  6)&0x1f;
    printf("%02x:", field);
    field = (opcode >>  0)&0x3f;
    printf("%02x",  field);
}
 
 
/** Dump CPU state to console */
void show_state(t_state *s){
    int i,j;
    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);
 
    /* print register values */
    #if FANCY_REGISTER_DISPLAY
    printf(" v = [%08x %08x]  ", s->r[2], s->r[3]);
    printf("           a = [");
    for(i=4;i<8;i++){
        printf("%08x ", s->r[i]);
    }
    printf("]\n");
    printf(" s = [");
    for(i=16;i<24;i++){
        printf("%08x ", s->r[i]);
    }
    printf("]\n");
    printf(" t = [");
    for(i=8;i<16;i++){
        printf("%08x ", s->r[i]);
    }
    printf("-\n");
    printf("      %08x %08x]  ", s->r[24], s->r[25]);
    printf("                          ");
    printf("  k = [ %08x %08x ]\n", s->r[26], s->r[27]);
    printf(" gp = %08x     sp = %08x    ", s->r[28], s->r[29]);
    printf(" fp = %08x     ra = %08x ", s->r[30], s->r[31]);
    printf("\n\n");
    #else
    for(i = 0; i < 4; ++i){
        printf("%2.2d ", i * 8);
        for(j = 0; j < 8; ++j){
            printf("%8.8x ", s->r[i*8+j]);
        }
        printf("\n");
    }
    #endif
 
    j = s->pc; /* save pc value (it's altered by the 'cycle' function) */
    for(i = -4; i <= 8; ++i){
        printf("%c", i==0 ? '*' : ' ');
        s->pc = j + i * 4;
        cycle(s, 10);
    }
    s->t.disasm_ptr = s->pc; /* executing code updates the disasm pointer */
    s->pc = j; /* restore pc value */
}
 
/** Show debug monitor prompt and execute user command */
void do_debug(t_state *s, uint32_t no_prompt){
    int ch;
    int i, j=0, watch=0, addr;
    j = s->breakpoint;
    s->pc_next = s->pc + 4;
    s->skip = 0;
    s->wakeup = 0;
 
    printf("Starting simulation.\n");
 
    if(no_prompt){
        ch = '5'; /* 'go' command */
        printf("\n\n");
    }
    else{
        show_state(s);
        ch = ' ';
    }
 
    for(;;){
        if(ch != 'n' && !no_prompt){
            if(watch){
                printf("0x%8.8x=0x%8.8x\n", watch, mem_read(s, 4, watch,0));
            }
            printf("1=Debug   2=Trace   3=Step    4=BreakPt 5=Go      ");
            printf("6=Memory  7=Watch   8=Jump\n");
            printf("9=Quit    A=Dump    L=LogTrg  C=Disasm  ");
            printf("> ");
        }
        if(ch==' ') ch = getch();
        if(ch != 'n'){
            printf("\n");
        }
        switch(ch){
        case 'a': case 'A':
            dump_trace_buffer(s); break;
        case '1': case 'd': case ' ':
            cycle(s, 0); show_state(s); break;
        case 'n':
            cycle(s, 1); break;
        case '2': case 't':
            cycle(s, 0); printf("*"); cycle(s, 10); break;
        case '3': case 's':
            printf("Count> ");
            scanf("%d", &j);
            for(i = 0; i < j; ++i){
                cycle(s, 1);
            }
            show_state(s);
            break;
        case '4': case 'b':
            printf("Line> ");
            scanf("%x", &j);
            printf("break point=0x%x\n", j);
            break;
        case '5': case 'g':
            s->wakeup = 0;
            cycle(s, 0);
            while(s->wakeup == 0){
                if(s->pc == j){
                    printf("\n\nStop: pc = 0x%08x\n\n", j);
                    break;
                }
                cycle(s, 0);
            }
            if(no_prompt) return;
            show_state(s);
            break;
        case 'G':
            s->wakeup = 0;
            cycle(s, 1);
            while(s->wakeup == 0){
                if(s->pc == j){
                    break;
                }
                cycle(s, 1);
            }
            show_state(s);
            break;
        case '6': case 'm':
            printf("Memory> ");
            scanf("%x", &j);
            for(i = 0; i < 8; ++i){
                printf("%8.8x ", mem_read(s, 4, j+i*4, 0));
            }
            printf("\n");
            break;
        case '7': case 'w':
            printf("Watch> ");
            scanf("%x", &watch);
            break;
        case '8': case 'j':
            printf("Jump> ");
            scanf("%x", &addr);
            s->pc = addr;
            s->pc_next = addr + 4;
            show_state(s);
            break;
        case '9': case 'q':
            return;
        case 'l':
            printf("Address> ");
            scanf("%x", &(s->t.log_trigger_address));
            printf("Log trigger address=0x%x\n", s->t.log_trigger_address);
            break;
        case 'c': case 'C':
            j = s->pc;
            for(i = 1; i <= 16; ++i){
                printf("%c", i==0 ? '*' : ' ');
                s->pc = s->t.disasm_ptr + i * 4;
                cycle(s, 10);
            }
            s->t.disasm_ptr = s->pc;
            s->pc = j;
        }
        ch = ' ';
    }
}
 
/** Read binary code and data files */
int read_binary_files(t_state *s, t_args *args){
    FILE *in;
    uint8_t *target;
    uint32_t bytes=0, i, files_read=0;
 
    for(i=0;i<NUM_MEM_BLOCKS;i++){
        bytes = 0;
        if(args->bin_filename[i]!=NULL){
 
            in = fopen(args->bin_filename[i], "rb");
            if(in == NULL){
                free_cpu(s);
                printf("Can't open file %s, quitting!\n",args->bin_filename[i]);
                return(0);
            }
 
            /* FIXME load offset 0x2000 for linux kernel hardcoded! */
            //bytes = fread((s->blocks[i].mem + 0x2000), 1, s->blocks[i].size, in);
            target = (uint8_t *)(s->blocks[i].mem + args->offset[i]);
            while(!feof(in) &&
                  ((bytes+1024+args->offset[i]) < (s->blocks[i].size))){
                bytes += fread(&(target[bytes]), 1, 1024, in);
                if(errno!=0){
                    printf("ERROR: file load failed with code %d ('%s')\n",
                        errno, strerror(errno));
                    free_cpu(s);
                    return 0;
                }
            }
 
            fclose(in);
 
            /* Now reverse the endianness of the data we just read, if it's
             necessary. */
             /* FIXME add cmd line param, etc. */
            //reverse_endianess(target, bytes);
 
            files_read++;
        }
        printf("%-16s [size= %6dKB, start= 0x%08x] loaded %d bytes.\n",
                s->blocks[i].area_name,
                s->blocks[i].size/1024,
                s->blocks[i].start,
                bytes);
    }
 
    if(!files_read){
        free_cpu(s);
        printf("No binary object files read, quitting\n");
        return 0;
    }
 
    return files_read;
}
 
void reverse_endianess(uint8_t *data, uint32_t bytes){
    uint8_t w[4];
    uint32_t i, j;
 
    for(i=0;i<bytes;i=i+4){
        for(j=0;j<4;j++){
            w[3-j] = data[i+j];
        }
        for(j=0;j<4;j++){
            data[i+j] = w[j];
        }
    }
}
 
 
/*----------------------------------------------------------------------------*/
 
int main(int argc,char *argv[]){
    t_state state, *s=&state;
 
    /* Parse command line and pass any relevant arguments to CPU record */
    if(parse_cmd_line(argc,argv, &cmd_line_args)==0){
        return 0;
    }
 
    printf("MIPS-I emulator (" __DATE__ ")\n\n");
    if(!init_cpu(s, &cmd_line_args)){
        printf("Trouble allocating memory, quitting!\n");
        return 1;
    };
 
    /* Read binary object files into memory*/
    if(!read_binary_files(s, &cmd_line_args)){
        return 2;
    }
    printf("\n\n");
 
    init_trace_buffer(s, &cmd_line_args);
 
    /* NOTE: Original mlite supported loading little-endian code, which this
      program doesn't. The endianess-conversion code has been removed.
    */
 
    /* Simulate a CPU reset */
    reset_cpu(s);
 
    /* Simulate the work of the uClinux bootloader */
    if(cmd_line_args.memory_map == MAP_UCLINUX){
        /* FIXME this is a stub, flesh it out */
        s->pc = 0x80002400;
    }
 
    /* Enter debug command interface; will only exit clean with user command */
    do_debug(s, cmd_line_args.no_prompt);
 
    /* Close and deallocate everything and quit */
    close_trace_buffer(s);
    free_cpu(s);
    return(0);
}
 
/*----------------------------------------------------------------------------*/
 
 
void init_trace_buffer(t_state *s, t_args *args){
    int i;
 
    /* setup misc info related to the monitor interface */
    s->t.disasm_ptr = VECTOR_RESET;
 
#if FILE_LOGGING_DISABLED
    s->t.log = NULL;
    s->t.log_triggered = 0;
    return;
#else
    /* clear trace buffer */
    for(i=0;i<TRACE_BUFFER_SIZE;i++){
        s->t.buf[i]=0xffffffff;
    }
    s->t.next = 0;
 
    /* if file logging is enabled, open log file */
    if(args->log_file_name!=NULL){
        s->t.log = fopen(args->log_file_name, "w");
        if(s->t.log==NULL){
            printf("Error opening log file '%s', file logging disabled\n",
                    args->log_file_name);
        }
    }
    else{
        s->t.log = NULL;
    }
 
    /* Setup log trigger */
    s->t.log_triggered = 0;
    s->t.log_trigger_address = args->log_trigger_address;
#endif
}
 
/** Dumps last jump targets as a chunk of hex numbers (older is left top) */
void dump_trace_buffer(t_state *s){
    int i, col;
 
    for(i=0, col=0;i<TRACE_BUFFER_SIZE;i++, col++){
        printf("%08x ", s->t.buf[s->t.next + i]);
        if((col % 8)==7){
            printf("\n");
        }
    }
}
 
/** Logs last cycle's activity (changes in state and/or loads/stores) */
void log_cycle(t_state *s){
    static unsigned int last_pc = 0;
    int i;
    uint32_t log_pc;
 
    /* store PC in trace buffer only if there was a jump */
    if(s->pc != (last_pc+4)){
        s->t.buf[s->t.next] = s->pc;
        s->t.next = (s->t.next + 1) % TRACE_BUFFER_SIZE;
    }
    last_pc = s->pc;
 
    /* if file logging is enabled, dump a trace log to file */
    if(log_enabled(s)){
        log_pc = s->op_addr;
 
        /* skip register zero which does not change */
        for(i=1;i<32;i++){
            if(s->t.pr[i] != s->r[i]){
                fprintf(s->t.log, "(%08X) [%02X]=%08X\n", log_pc, i, s->r[i]);
            }
            s->t.pr[i] = s->r[i];
        }
        if(s->lo != s->t.lo){
            //fprintf(s->t.log, "(%08X) [LO]=%08X\n", log_pc, s->lo);
        }
        s->t.lo = s->lo;
 
        if(s->hi != s->t.hi){
            //fprintf(s->t.log, "(%08X) [HI]=%08X\n", log_pc, s->hi);
        }
        s->t.hi = s->hi;
 
        /* Catch changes in EPC by direct write (mtc0) and by exception */
        if(s->epc != s->t.epc){
            fprintf(s->t.log, "(%08X) [EP]=%08X\n", log_pc, s->epc);
        }
        s->t.epc = s->epc;
 
        if(s->status != s->t. status){
            fprintf(s->t.log, "(%08X) [SR]=%08X\n", log_pc, s->status);
        }
        s->t.status = s->status;
    }
}
 
/** Frees debug buffers and closes log file */
void close_trace_buffer(t_state *s){
    if(s->t.log){
        fclose(s->t.log);
    }
}
 
/** Logs a message for each failed assertion, each in a line */
void log_failed_assertions(t_state *s){
    unsigned bitmap = s->failed_assertions;
    int i = 0;
 
    /* This loop will crash the program if the message table is too short...*/
    if(s->t.log != NULL){
        for(i=0;i<32;i++){
            if(bitmap & 0x1){
                fprintf(s->t.log, "ASSERTION FAILED: [%08x] %s\n",
                        s->faulty_address,
                        assertion_messages[i]);
            }
            bitmap = bitmap >> 1;
        }
    }
}
 
uint32_t log_enabled(t_state *s){
    return ((s->t.log != NULL) && (s->t.log_triggered!=0));
}
 
void trigger_log(t_state *s){
    uint32_t i;
 
    s->t.log_triggered = 1;
 
    for(i=0;i<32;i++){
        s->t.pr[i] = s->r[i];
    }
 
    s->t.lo = s->lo;
    s->t.hi = s->hi;
    s->t.epc = s->epc;
}
 
void free_cpu(t_state *s){
    int i;
 
    for(i=0;i<NUM_MEM_BLOCKS;i++){
        free(s->blocks[i].mem);
        s->blocks[i].mem = NULL;
    }
}
 
void reset_cpu(t_state *s){
    s->pc = cmd_line_args.start_addr; /* reset start vector or cmd line address */
    s->delay_slot = 0;
    s->failed_assertions = 0; /* no failed assertions pending */
    s->status = 0x02; /* kernel mode, interrupts disabled */
    /* init trace struct to prevent spurious logs */
    s->t.status = s->status;
}
 
void unimplemented(t_state *s, const char *txt){
    /* FIXME unimplemented opcode trap */
    printf("UNIMPLEMENTED: %s\n", txt);
}
 
int init_cpu(t_state *s, t_args *args){
    int i, j;
    uint32_t k = args->memory_map;
 
    memset(s, 0, sizeof(t_state));
    s->big_endian = 1;
 
    s->do_unaligned = args->do_unaligned;
    s->breakpoint = args->breakpoint;
 
    /* Initialize memory map */
    for(i=0;i<NUM_MEM_BLOCKS;i++){
        s->blocks[i].start =        memory_maps[k].blocks[i].start;
        s->blocks[i].size =         memory_maps[k].blocks[i].size;
        s->blocks[i].area_name =    memory_maps[k].blocks[i].area_name;
        s->blocks[i].mask =         memory_maps[k].blocks[i].mask;
        s->blocks[i].read_only =    memory_maps[k].blocks[i].read_only;
 
        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;
}
 
int32_t parse_cmd_line(uint32_t argc, char **argv, t_args *args){
    uint32_t i;
 
    /* fill cmd line args with default values */
    args->memory_map = MAP_DEFAULT;
    args->trap_on_reserved = 1;
    args->start_addr = VECTOR_RESET;
    args->do_unaligned = 0;
    args->no_prompt = 0;
    args->breakpoint = 0xffffffff;
    args->log_file_name = "sw_sim_log.txt";
    args->log_trigger_address = VECTOR_RESET;
    for(i=0;i<NUM_MEM_BLOCKS;i++){
        args->bin_filename[i] = NULL;
        args->offset[i] = 0;
    }
 
    /* parse actual cmd line args */
    for(i=1;i<argc;i++){
        if(strcmp(argv[i],"--plasma")==0){
            /* plasma simulation not supported, error*/
            printf("Error: program compiled for compatibility to MIPS-I\n");
            return 0;
        }
        else if(strcmp(argv[i],"--uclinux")==0){
            args->memory_map = MAP_UCLINUX;
            /* FIXME selecting uClinux enables unaligned L/S emulation */
            args->do_unaligned = 1;
        }
        else if(strcmp(argv[i],"--small")==0){
            args->memory_map = MAP_SMALL;
        }
        else if(strcmp(argv[i],"--unaligned")==0){
            args->do_unaligned = 1;
        }
        else if(strcmp(argv[i],"--noprompt")==0){
            args->no_prompt = 1;
        }
        else if(strcmp(argv[i],"--notrap10")==0){
            args->trap_on_reserved = 0;
        }
        else if(strncmp(argv[i],"--bram=", strlen("--bram="))==0){
            args->bin_filename[0] = &(argv[i][strlen("--bram=")]);
        }
        else if(strncmp(argv[i],"--flash=", strlen("--flash="))==0){
            args->bin_filename[3] = &(argv[i][strlen("--flash=")]);
        }
        else if(strncmp(argv[i],"--xram=", strlen("--xram="))==0){
            args->bin_filename[1] = &(argv[i][strlen("--xram=")]);
        }
        else if(strncmp(argv[i],"--start=", strlen("--start="))==0){
            sscanf(&(argv[i][strlen("--start=")]), "%x", &(args->start_addr));
        }
        else if(strncmp(argv[i],"--kernel=", strlen("--kernel="))==0){
            args->bin_filename[1] = &(argv[i][strlen("--kernel=")]);
            /* FIXME uClinux kernel 'offset' hardcoded */
            args->offset[1] = 0x2000;
        }
        else if(strncmp(argv[i],"--trigger=", strlen("--trigger="))==0){
            sscanf(&(argv[i][strlen("--trigger=")]), "%x", &(args->log_trigger_address));
        }
        else if(strncmp(argv[i],"--break=", strlen("--break="))==0){
            sscanf(&(argv[i][strlen("--break=")]), "%x", &(args->breakpoint));
        }
        else if(strncmp(argv[i],"--breakpoint=", strlen("--breakpoint="))==0){
            sscanf(&(argv[i][strlen("--breakpoint=")]), "%x", &(args->breakpoint));
        }
        else if((strcmp(argv[i],"--help")==0)||(strcmp(argv[i],"-h")==0)){
            usage();
            return 0;
        }
        else{
            printf("unknown argument '%s'\n\n",argv[i]);
            usage();
            return 0;
        }
    }
 
    return 1;
}
 
void usage(void){
    printf("Usage:");
    printf("    slite file.exe [arguments]\n");
    printf("Arguments:\n");
    printf("--bram=<file name>      : BRAM initialization file\n");
    printf("--xram=<file name>      : XRAM initialization file\n");
    printf("--kernel=<file name>    : XRAM initialization file for uClinux kernel\n");
    printf("                          (loads at block offset 0x2000)\n");
    printf("--flash=<file name>     : FLASH initialization file\n");
    printf("--trigger=<hex number>  : Log trigger address\n");
    printf("--break=<hex number>    : Breakpoint address\n");
    printf("--start=<hex number>    : Start here instead of at reset vector\n");
    printf("--notrap10              : Reserverd opcodes are NOPs and don't trap\n");
    printf("--plasma                : Simulate Plasma instead of MIPS-I\n");
    printf("--uclinux               : Use memory map tailored to uClinux\n");
    printf("--unaligned             : Implement unaligned load/store instructions\n");
    printf("--noprompt              : Run in batch mode\n");
    printf("--stop_at_zero          : Stop simulation when fetching from address 0x0\n");
    printf("--help, -h              : Show this usage text\n");
}
 
 

Go to most recent revision | Compare with Previous | Blame | View Log

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.