diff options
Diffstat (limited to 'src/ymh15/mips_cpu.cpp')
-rw-r--r-- | src/ymh15/mips_cpu.cpp | 325 |
1 files changed, 265 insertions, 60 deletions
diff --git a/src/ymh15/mips_cpu.cpp b/src/ymh15/mips_cpu.cpp index ea7cce1..02ca75a 100644 --- a/src/ymh15/mips_cpu.cpp +++ b/src/ymh15/mips_cpu.cpp @@ -4,32 +4,52 @@ struct mips_cpu_impl { mips_mem_h mem; uint32_t regs[32]; + // lo and hi registers + uint32_t lo, hi; + // program counter uint32_t pc; - uint32_t overflow; + // next pc used for branches and set the pc correctly + uint32_t next_pc; + // delay slot that makes branches work as expected + uint32_t delay_slot; + + // sets the debug level so that you can print more information + int debug_level; + FILE *debug_type; }; mips_cpu_h mips_cpu_create(mips_mem_h mem) { - mips_cpu_impl* new_mips_cpu = new mips_cpu_impl; - - new_mips_cpu->mem = mem; - new_mips_cpu->pc = 0; - new_mips_cpu->overflow = 0; + // creates new instance of a cpu explicitly + mips_cpu_impl* state = new mips_cpu_impl; + + // reset all the variables + state->mem = mem; + state->pc = 0; + state->next_pc = state->pc + 4; + state->delay_slot = 0; + state->debug_level = 0; + state->debug_type = NULL; + + // sets all registers to 0 for(int i = 0; i < 32; ++i) { - new_mips_cpu->regs[i] = 0; + state->regs[i] = 0; } - return new_mips_cpu; + return state; } mips_error mips_cpu_reset(mips_cpu_h state) { if(!state) { return mips_ErrorInvalidArgument; } - + + // resets both program counters state->pc = 0; - state->overflow = 0; + state->next_pc = state->pc + 4; + state->delay_slot = 0; + // and registers for(int i = 0; i < 32; ++i) { state->regs[i] = 0; } @@ -39,10 +59,11 @@ mips_error mips_cpu_reset(mips_cpu_h state) { mips_error mips_cpu_get_register(mips_cpu_h state, unsigned index, uint32_t *value) { - if(!state || !value) { + if(!state || !value || index > 31) { return mips_ErrorInvalidArgument; } - + + // get the state of a current register by using the provided pointer; *value = state->regs[index]; return mips_Success; @@ -50,10 +71,11 @@ mips_error mips_cpu_get_register(mips_cpu_h state, unsigned index, mips_error mips_cpu_set_register(mips_cpu_h state, unsigned index, uint32_t value) { - if(!state) { + if(!state || index > 31) { return mips_ErrorInvalidArgument; } - + + // set reg state state->regs[index] = value; return mips_Success; @@ -64,8 +86,10 @@ mips_error mips_cpu_set_pc(mips_cpu_h state, uint32_t pc) { if(!state) { return mips_ErrorInvalidArgument; } - + + // set the pc and next_pc to the right values state->pc = pc; + state->next_pc = state->pc+4; return mips_Success; } @@ -74,121 +98,302 @@ mips_error mips_cpu_get_pc(mips_cpu_h state, uint32_t *pc) { if(!state || !pc) { return mips_ErrorInvalidArgument; } - + + // get the value of the current pc *pc = state->pc; return mips_Success; } +// This function executes the code that is found in memory at the address +// of the program counter mips_error mips_cpu_step(mips_cpu_h state) { if(!state) { return mips_ErrorInvalidArgument; } - mips_error mips_err = read_instruction(state); + // gets the instruction from memory + uint32_t inst; + mips_mem_read(state->mem, state->pc, 4, (uint8_t*)&inst); + + // change the instruction back from big endian as it was read as little endian + inst = (inst<<24) | ((inst>>8)&0xff00) | ((inst<<8)&0xff0000) | (inst>>24); + + // it then executes the instruction by decoding it first and returns + // the state of the instruction + mips_error mips_err = exec_instruction(state, inst); + + + if(state->debug_level > 0) { + fprintf(state->debug_type, "pc: %d\tpc_next: %d\tinst: %#10x\tdelay_slot: %d\n", state->pc, state->next_pc, inst, state->delay_slot); + } + + // if the debug level is 2 or higher it will print the register state + if(state->debug_level > 1) { + fprintf(state->debug_type, "\nCPU register state:\n"); + for(unsigned i = 0; i < 32; ++i) { + fprintf(state->debug_type, "R%d:\t%#10x\n", i, + state->regs[i]); + } + fprintf(state->debug_type, "\n\n"); + } + + if(state->debug_level > 2) { + char c = getchar(); + } - state->pc += 4; + // it then updates the pc to next_pc which could have been changed + // by a branch, it only does this if it is not in a delay slot + if(!state->delay_slot) { + state->pc = state->next_pc; + state->next_pc += 4; + } else { + state->pc += 4; + --state->delay_slot; + } + // returns the operation error such as arithmetic overflow return mips_err; } mips_error mips_cpu_set_debug_level(mips_cpu_h state, unsigned level, FILE *dest) { - if(!state || !dest) { + if(!state) { return mips_ErrorInvalidArgument; } - - //TODO + + // just sets the cpu values for the debug level + state->debug_level = level; + state->debug_type = dest; return mips_Success; } void mips_cpu_free(mips_cpu_h state) { + // deletes the state pointer of mips_cpu_impl* type if(state) { delete state; } } -mips_error read_instruction(mips_cpu_h state) { - uint32_t inst; - mips_mem_read(state->mem, state->pc, 4, (uint8_t*)&inst); - - return exec_instruction(state, inst); -} +// The following functions are not present in the api as they are +// functions defined in a private header file mips_error exec_instruction(mips_cpu_h state, uint32_t inst) { + // creates var that holds all the information of the instruction uint32_t var[8]; + + // prints pc and instruction every clock cycle + for(int i = 0; i < 8; ++i) { var[i] = 0; } - + + // decodes the instruction + + // if first 6 bits are 0 then it is a R instruction if(((inst >> 26)&0x3f) == 0) { - // R Type + // R Type: set all necessary variables var[REG_S] = inst >> 21; var[REG_T] = (inst >> 16)&0x1f; var[REG_D] = (inst >> 11)&0x1f; var[SHIFT] = (inst >> 6)&0x1f; var[FUNC] = inst&0x3f; - + + // execute R instruction to perform operation return exec_R(state, var); - } else if(((inst >> 26)&0x3f) < 4) { - var[OPCODE] = inst >> 26; + } else if(((inst >> 26)&0x3f) < 4 && ((inst >> 26)&0x3f) != 1) { // as opcode is < 4 + var[OPCODE] = inst >> 26; // it has to be J type + var[MEM] = inst&0x3ffffff; + return exec_J(state, var); + } else { // otherwise it is an I type + var[OPCODE] = inst >> 26; var[REG_S] = (inst >> 21)&0x1f; var[REG_D] = (inst >> 16)&0x1f; var[IMM] = inst&0xffff; - - return exec_J(state, var); - } else { - var[OPCODE] = inst >> 26; - var[MEM] = inst&0x3ffffff; + if((var[IMM]&0x8000) == 0x8000) { + var[IMM] = (var[IMM]|0xffff0000); + } return exec_I(state, var); } - - return mips_ExceptionInvalidInstruction; } mips_error exec_R(mips_cpu_h state, uint32_t var[8]) { + // if the function code is between 0x1f and 0x24 then it is addition + // or subtraction if((var[FUNC]&0xf0) == 0x20 && (var[FUNC]&0xf) < 4) { - return add_sub(state, var, ((int32_t)-(var[FUNC]&0xf)/2)*2+1); + // calls add with -1 if it is a subtractin and 1 if it is addition + return add_sub(state, var, ((int32_t)-(var[FUNC]&0xf)/2)*2+1, 0); + } else if((var[FUNC]&0xf0) == 0x20 && (var[FUNC]&0xf) < 7) { - return bitwise(state, var); + // else if the number is between 0x23 and 0x27 which means it + // is a bitwise operation + return bitwise(state, var, 0); + + } else if(var[FUNC] == 8) { + // jr + return jump(state, var, 0); + } else if(var[FUNC] == 9) { + // jalr + return jump(state, var, 1); + } else { + // otherwise return that it was an invalid instruction + return mips_ExceptionInvalidInstruction; } - - return mips_ExceptionInvalidInstruction; } mips_error exec_J(mips_cpu_h state, uint32_t var[8]) { - //TODO - - return mips_ExceptionInvalidInstruction; + switch(var[OPCODE]) { + case 0: + case 2: // j + return jump(state, var, 0); + case 3: // jal + return jump(state, var, 1); + default: + return mips_ExceptionInvalidInstruction; + } } mips_error exec_I(mips_cpu_h state, uint32_t var[8]) { - //TODO - - return mips_ExceptionInvalidInstruction; + switch(var[OPCODE]) { + case 1: // bgez, bgezal, bltz, bltzal + return branch(state, var); + case 4: // beq + return branch(state, var); + case 5: // bne + return branch(state, var); + case 6: // blez + return branch(state, var); + case 7: // bgtz + return branch(state, var); + case 8: // addi + return add_sub(state, var, 1, 1); + case 9: // addiu + return add_sub(state, var, 1, 2); + case 12: // andi + return bitwise(state, var, 1); + case 13: // ori + return bitwise(state, var, 1); + case 14: // xori + return bitwise(state, var, 1); + case 32: // lb + return load(state, var); + case 36: // lbu + return load(state, var); + default: + return mips_ExceptionInvalidInstruction; + } } -mips_error add_sub(mips_cpu_h state, uint32_t var[8], int32_t add_sub) { +mips_error add_sub(mips_cpu_h state, uint32_t var[8], int32_t add_sub, + int32_t imm) { + // sets initial values of the registers as local variables int32_t reg_s, reg_t, reg_d; - reg_s = (int32_t)state->regs[var[REG_S]]; - reg_t = (int32_t)state->regs[var[REG_T]]; + + // set the locals to the correct value from the register + mips_cpu_get_register(state, var[REG_S], (uint32_t*)®_s); + if(imm > 0) { + reg_t = (int32_t)var[IMM]; + } else { + mips_cpu_get_register(state, var[REG_T], (uint32_t*)®_t); + } + + // perform the addition or subtraction reg_d = reg_s + add_sub*reg_t; - if((var[FUNC]&0x1) == 1 || !((reg_s > 0 && add_sub*reg_t > 0 && reg_d < 0) || (reg_s < 0 && add_sub*reg_t < 0 && reg_d > 0))) { - state->regs[var[REG_D]] = (uint32_t)reg_d; + + // check for overflow conditions and if it is an unsigned operation + if((var[FUNC]&0x1) == 1 || imm > 1 || !((reg_s > 0 && add_sub*reg_t > 0 && reg_d < 0) || (reg_s < 0 && add_sub*reg_t < 0 && reg_d > 0))) { + + // sets the destination register to the right answer + mips_cpu_set_register(state, var[REG_D], (uint32_t)reg_d); return mips_Success; } + return mips_ExceptionArithmeticOverflow; } -mips_error bitwise(mips_cpu_h state, uint32_t var[8]) { - if((var[FUNC]&0xf) == 4) { - state->regs[var[REG_D]] = state->regs[var[REG_S]] & state->regs[var[REG_T]]; - } else if((var[FUNC]&0xf) == 5) { - state->regs[var[REG_D]] = state->regs[var[REG_S]] | state->regs[var[REG_T]]; +mips_error bitwise(mips_cpu_h state, uint32_t var[8], unsigned imm) { + // selects the right bitwise operation to perform then sets the register to the right value + uint32_t reg_s, reg_t, reg_d; + + mips_cpu_get_register(state, var[REG_S], ®_s); + if(imm == 0) { + mips_cpu_get_register(state, var[REG_T], ®_t); } else { - state->regs[var[REG_D]] = state->regs[var[REG_S]] ^ state->regs[var[REG_T]]; + reg_t = var[IMM]; + } + + // performs correct bitwise operation and sets register; + if((var[FUNC]&0xf) == 4 || var[OPCODE] == 0xc) reg_d = reg_s & reg_t; + else if((var[FUNC]&0xf) == 5 || var[OPCODE] == 0xd) reg_d = reg_s | reg_t; + else reg_d = reg_s ^ reg_t; + + // set register to the answer + mips_cpu_set_register(state, var[REG_D], reg_d); + + return mips_Success; +} + +mips_error branch(mips_cpu_h state, uint32_t var[8]) { + if(var[OPCODE] == 1 && (var[REG_D] == 0x11 || var[REG_D] == 0x10)) { + state->regs[31] = state->pc+8; } + + if((state->regs[var[REG_S]] == state->regs[var[REG_D]] && var[OPCODE] == 4) || // bez + (state->regs[var[REG_S]] != state->regs[var[REG_D]] && var[OPCODE] == 5) || // bne + (((int32_t)state->regs[var[REG_S]] >= 0) && (var[OPCODE] == 1) && (var[REG_D]&1) == 1) || // bgez / bgezal + ((int32_t)state->regs[var[REG_S]] < 0 && var[OPCODE] == 1 && (var[REG_D]&1) == 0) || // bltz / bltzal + ((int32_t)state->regs[var[REG_S]] > 0 && var[OPCODE] == 7 && (var[REG_D]&1) == 0) || // bgtz + ((int32_t)state->regs[var[REG_S]] <= 0 && var[OPCODE] == 6 && (var[REG_D]&1) == 0)) { // blez + + state->next_pc += (var[IMM]<<2); + state->delay_slot += 1; + } + + return mips_Success; +} + +mips_error jump(mips_cpu_h state, uint32_t var[8], uint8_t link) { + uint8_t reg_d; + uint32_t jump_loc; + if(var[FUNC] == 9 || var[FUNC] == 8) { + jump_loc = state->regs[var[REG_S]]; + reg_d = var[REG_D]; + } else { + jump_loc = var[IMM]<<2; + reg_d = 31; + } + + if(link) { + state->regs[reg_d] = state->pc+8; + } + + state->next_pc = jump_loc; + state->delay_slot += 1; + + return mips_Success; +} + +mips_error load(mips_cpu_h state, uint32_t var[8]) { + uint32_t addr; + uint8_t mem_byte; + + addr = state->regs[var[REG_S]] + var[IMM]; + + mips_mem_read(state->mem, addr, 1, &mem_byte); + + if(var[OPCODE] == 0x24) { + state->regs[var[REG_D]] = (uint32_t)mem_byte; + } else if(var[OPCODE] == 0x20){ + state->regs[var[REG_D]] = (uint32_t)(int8_t)mem_byte; + } else { + return mips_ExceptionInvalidInstruction; + } + + return mips_Success; +} + +mips_error store(mips_cpu_h state, uint32_t var[8]) { return mips_Success; } |