Emu is a MIPS emulator. In this project, we complete the print_instruction and execute_instruction functions.
To run Emu, use the following command:
$ make dcc emu.c ram.c registers.c execute_instruction.c print_instruction.c -o emu
$ ./emu ...This function is contained in print_instruction.c. It is given a MIPS instruction as a 32-bit unsigned integer value, and needs to print out the assembler for the instruction.
For example, print_instruction(0x00851820) should print
add $3, $4, $5
emu takes several command-line arguments. The -p option indicates the rest of the command is hexadecimal integers describing instructions that print_instruction will be called on —, for example:
$ make dcc emu.c ram.c registers.c execute_instruction.c print_instruction.c -o emu
$ ./emu -p 0x00851820
[00400024] 00851820 add $3, $4, $5The assembler instruction should be quoted since the $ character has special meaning in Shell.
emu also has a -P option, which takes a file of assembler statements, converts them to integers, and calls print_instruction for each integer, for example:
$ ./emu -P print10.s
[00400024] 34080001 ori $8, $0, 1
[00400028] 2901000B slti $1, $8, 11
[0040002C] 10200009 beq $1, $0, 9
[00400030] 00082020 add $4, $0, $8
[00400034] 34020001 ori $2, $0, 1
[00400038] 0000000C syscall
[0040003C] 3404000A ori $4, $0, 10
[00400040] 3402000B ori $2, $0, 11
[00400044] 0000000C syscall
[00400048] 21080001 addi $8, $8, 1
[0040004C] 0401FFF7 bgez $0, -9
[00400050] 03E00008 jr $31This function is contained in execute_instruction.c. It is given a MIPS instruction as a 32-bit unsigned integer value
You implement instructions by appropriately calling the functions get_register, set_register, get_byte and set_byte. execute_instruction must also update the program counter.
emu has a -e option, will also accept assembler statements, convert them to integers, and calls execute_instruction for each integer, then print the value of registers. For example:
$ ./emu -e ‘add $4, $14, $12’
R0 [$zero] = 00000000
R1 [$at] = 00000000
R2 [$v0] = 00000000
R3 [$v1] = 00000000
R4 [$a0] = 0000000A
R5 [$a1] = 00000000
R6 [$a2] = 00000000
R7 [$a3] = 00000000
R8 [$t0] = 00000000
R9 [$t1] = 00000001
R10 [$t2] = 00000002
R11 [$t3] = 00000003
R12 [$t4] = 00000004
R13 [$t5] = 00000005
R14 [$t6] = 00000006
R15 [$t7] = 00000007
R16 [$s0] = 00000000
R17 [$s1] = 00000000
R18 [$s2] = 00000000
R19 [$s3] = 00000000
R20 [$s4] = 00000000
R21 [$s5] = 00000000
R22 [$s6] = 00000000
R23 [$s7] = 00000000
R24 [$t8] = 00000000
R25 [$t9] = 00000000
R26 [$k0] = 00000000
R27 [$k1] = 00000000
R28 [$gp] = 10008000
R29 [$sp] = 7FFFF8E4
R30 [$fp] = 00000000
R31 [$ra] = 00400018emu also has a -E option, which like -P takes a file of assembler statements, converts them to integers, and calls execute_instruction multiple times to execute them. For example:
$ ./emu -E sum_100_squares.s 338350emu can also be run interactively:
$ ./emu add_memory.s
PC = [00400024] 34080011 ori $8, $0, 17 emu > h In interactive mode, available commands are:
sstep (execute one instruction)rexecute all remaining instructionsqquiththis help messagePprint ProgramRprint RegistersDprint Data segmentSprint Stack segmentTprint Text segment
Entering nothing will re-send the previous command.
emu > R
R0 [$zero] = 00000000
R1 [$at] = 00000000
R2 [$v0] = 00000000
R3 [$v1] = 00000000
R4 [$a0] = 00000000
R5 [$a1] = 00000000
R6 [$a2] = 00000000
R7 [$a3] = 00000000
R8 [$t0] = 00000000
R9 [$t1] = 00000001
R10 [$t2] = 00000002
R11 [$t3] = 00000003
R12 [$t4] = 00000004
R13 [$t5] = 00000005
R14 [$t6] = 00000006
R15 [$t7] = 00000007
R16 [$s0] = 00000000
R17 [$s1] = 00000000
R18 [$s2] = 00000000
R19 [$s3] = 00000000
R20 [$s4] = 00000000
R21 [$s5] = 00000000
R22 [$s6] = 00000000
R23 [$s7] = 00000000
R24 [$t8] = 00000000
R25 [$t9] = 00000000
R26 [$k0] = 00000000
R27 [$k1] = 00000000
R28 [$gp] = 10008000
R29 [$sp] = 7FFFF8E4
R30 [$fp] = 00000000
R31 [$ra] = 00400018
PC = [00400024] 34080011 ori $8, $0, 17 emu > s
PC = [00400028] 3C011001 lui $1, 4097 emu > s
PC = [0040002C] AC280000 sw $8, 0($1) emu > D
[10000000..1000FFFC] 00000000 [10010000] 00000011 [10010004] 00000019 [10010008] 0000002A You only need to implement the following subset of instructions and system calls; emu will only be tested on these:
add $d, $s, $tadd d = s + t 000000ssssstttttddddd00000100000sub $d, $s, $tsubtract d = s – t 000000ssssstttttddddd00000100010mul $d, $s, $tmultiply to low d = s * t 011100ssssstttttddddd00000000010and $d, $s, $tand d = s & t 000000ssssstttttddddd00000100100or $d, $s, $tor d = s l t 000000ssssstttttddddd00000100101xor $d, $s, $txor d = s ^ t 000000ssssstttttddddd00000100110sllv $d, $t, $sshift left d = t << s 000000ssssstttttddddd00000000100srlv $d, $t, $sshift right d = t >> s 000000ssssstttttddddd00000000110slt $d, $s, $tset on less than d = (s < t) 000000ssssstttttddddd00000101010addi $t, $s, Iadd immediate t = s + I 001000ssssstttttIIIIIIIIIIIIIIIIandi $t, $s, Iand with immediate t = s & I 001100ssssstttttIIIIIIIIIIIIIIIIori $t, $s, Ior with immediate t = s l I 001101ssssstttttIIIIIIIIIIIIIIIIxori $t, $s, Ixor with immediate t = s ^ I 001110ssssstttttIIIIIIIIIIIIIIIIsll $d, $t, Ishift left immediate d = t << I 00000000000tttttdddddIIIII000000srl $d, $t, Ishift right immediate d = t >> I 00000000000tttttdddddIIIII000010slti $t, $s, Iset on less than immediate t = (s < I) 001010ssssstttttIIIIIIIIIIIIIIIIlui $t, Iload upper immediate t = I << 16 00111100000tttttIIIIIIIIIIIIIIIIlb $t, O($b)load byte t = *(int8*)(b + O) 100000bbbbbtttttOOOOOOOOOOOOOOOOlh $t, O($b)load half word t = *(int16*)(b + O) 100001bbbbbtttttOOOOOOOOOOOOOOOOlw $t, O($b)load word t = *(int32*)(b + O) 100011bbbbbtttttOOOOOOOOOOOOOOOOsb $t, O($b)store byte *(uint8*)(b + O) = (t & 0xff) 101000bbbbbtttttOOOOOOOOOOOOOOOOsh $t, O($b)store half *(uint16*)(b + O) = (t & 0xffff) 101001bbbbbtttttOOOOOOOOOOOOOOOOsw $t, O($b)store word *(uint32*)(b + O) = t 101011bbbbbtttttOOOOOOOOOOOOOOOObeq $s, $t, Ibranch on equal if (s == t) PC += I<<2; else PC += 4; 000100ssssstttttIIIIIIIIIIIIIIIIbne $s, $t, Ibranch on not equal if (s != t) PC += I<<2; else PC += 4; 000101ssssstttttIIIIIIIIIIIIIIIIblez $s, Ibranch less than or equal than zero if (s <= 0) PC += I<<2; else PC += 4; 000110sssss00000IIIIIIIIIIIIIIIIbgtz $s, Ibranch greater than zero if (s > 0) PC += I<<2; else PC += 4; 000111sssss00000IIIIIIIIIIIIIIIIbltz $s, Ibranch on less than zero if (s < 0) PC += I<<2; else PC += 4; 000001sssss00000IIIIIIIIIIIIIIIIbgez $s, Ibranch on greater than or equal to zero if (s >= 0) PC += I<<2; else PC += 4; 000001sssss00001IIIIIIIIIIIIIIIIj Xjump PC = (PC & 0xF0000000) | (X << 2) 000010XXXXXXXXXXXXXXXXXXXXXXXXXXjal Xjump and link $ra = PC + 4; PC = (PC & 0xF0000000) | (X << 2) 000011XXXXXXXXXXXXXXXXXXXXXXXXXXjr $sjump register PC = s 000000sssss000000000000000001000syscallsystem call determined by $v0 00000000000000000000000000001100
The instruction 'Bit Pattern' uniquely identifies each instruction:
0: Literal bit zero1: Literal bit oneX: Immediate, print as hex with'0x'prefix I: Immediate, print as dec b: Base register field (number is theNin$N)O: Offset immediate, print as dec [lowercase letter]:- Register field (number is the
Nin$N)
Arithmetic instruction should assume registers contain a signed 32-bit number. Arithmetical instruction should not attempt to stop overflows as a result of their operation.
Every instruction has a PC += 4 after the operation, except for instruction that directly change the program counter, such as branches or jumps.
You only need to implement this subset of system calls.