diff --git a/README.txt b/README.txt index 13db62b..d4af1d1 100644 --- a/README.txt +++ b/README.txt @@ -4,6 +4,13 @@ So don't use it. Example programs: - return.bin: returns -1094647826 (puts it to register 10) +- return2.bin: returns a different value with more steps + +Emulator exit codes: +- <0 = OK +- 0 = never happens +- 1 - invalid opcode +- 2 - illegal instruction argument (like funct3) To compile stuff: 0. Get the toolchain obviously diff --git a/programs/return2.bin b/programs/return2.bin new file mode 100644 index 0000000..685fc27 Binary files /dev/null and b/programs/return2.bin differ diff --git a/src/cpu.c b/src/cpu.c index d1bd313..f8714a1 100644 --- a/src/cpu.c +++ b/src/cpu.c @@ -33,9 +33,9 @@ int cpu_cycle(CPU *cpu) { return -1; } - execute_instruction_on_cpu(cpu, instruction); + int result = execute_instruction_on_cpu(cpu, instruction); cpu->programCounter += 4; - return 0; + return result; } \ No newline at end of file diff --git a/src/instruction_executor.c b/src/instruction_executor.c index eb71330..f9538c7 100644 --- a/src/instruction_executor.c +++ b/src/instruction_executor.c @@ -1,5 +1,8 @@ #include "cpu.h" #include +#include + +#define VERBOSE_LOGGING 0 static void printBinary(int num) { if (num > 1) { @@ -8,7 +11,7 @@ static void printBinary(int num) { printf("%d", num % 2); } -int execute_instruction_on_cpu(CPU *cpu, uint32_t instruction) { +int execute_instruction_on_cpu(CPU *cpu, uint32_t instruction) { // TODO consider making instruction int AddressSpace *addressSpace = cpu->addressSpace; uint32_t *registers = cpu->registers; registers[0] = 0; // x0 is always 0 @@ -16,60 +19,191 @@ int execute_instruction_on_cpu(CPU *cpu, uint32_t instruction) { uint8_t opcode = instruction & 0x7F; uint8_t rd = instruction >> 7 & 0x1F; - printf("\nPC: %d\n", cpu->programCounter); - - if (opcode != 0) { + if (VERBOSE_LOGGING == 1) { + printf("\n\nPC: %d\n", cpu->programCounter); printf("Instruction: "); printBinary(instruction); printf("\nOpcode: 0x%X (", opcode); printBinary(opcode); printf(")\n"); printf("Dest. reg.: x%d\n", rd); + } else { + // TODO perhaps no break between instructions? Would look better but there would be padding to fix + printf("\n\nPC: %d ", cpu->programCounter); } switch (opcode) { + case 0b1100011: { // BRANCH instruction (B type) + uint8_t funct3 = instruction >> 12 & 0x7; + uint8_t rs1 = instruction >> 15 & 0x1F; + uint8_t rs2 = instruction >> 20 & 0x1F; + + int32_t imm = ((int32_t)instruction >> 31) << 12; // imm[12] and sign extension + imm |= ((instruction >> 7) & 0x1) << 11; // imm[11] + imm |= ((instruction >> 25) & 0x3f) << 5; // imm[10:5] + imm |= ((instruction >> 8) & 0xf) << 1; // imm[4:1] + // imm[0] is implicitly 0 + + bool branch = false; + + if (funct3 == 0b000) { // BEQ + printf("BEQ: x%d %d == x%d %d?", rs1, registers[rs1], rs2, registers[rs2]); + branch = registers[rs1] == registers[rs2]; + } else if (funct3 == 0b001) { // BNE + printf("BNE: x%d %d != x%d %d?", rs1, registers[rs1], rs2, registers[rs2]); + branch = registers[rs1] != registers[rs2]; + } else if (funct3 == 0b100) { // BLT + printf("BLT: x%d %d < x%d %d?", rs1, registers[rs1], rs2, registers[rs2]); + branch = (int32_t)registers[rs1] < (int32_t)registers[rs2]; + } else if (funct3 == 0b101) { // BGE + printf("BGE: x%d %d > x%d %d?", rs1, registers[rs1], rs2, registers[rs2]); + branch = (int32_t)registers[rs1] > (int32_t)registers[rs2]; + } else if (funct3 == 0b110) { // BLTU + printf("BLTU: x%d %u < x%d %u?", rs1, registers[rs1], rs2, registers[rs2]); + branch = registers[rs1] < registers[rs2]; + } else if (funct3 == 0b111) { // BGEU + printf("BGEU: x%d %u > x%d %u?", rs1, registers[rs1], rs2, registers[rs2]); + branch = registers[rs1] > registers[rs2]; + } else { + // TODO illegal instruction + } + + if (branch) { + printf(" Yes, branching"); + cpu->programCounter += imm - 4; // program counter is always incremented after executing instruction so -4 + } else { + printf(" No, not branching"); + } + break; + } case 0b0010011: { // OP-IMM for Integer Register-Immediate Instructions (I type) uint8_t funct3 = instruction >> 12 & 0x7; uint8_t rs1 = instruction >> 15 & 0x1F; - uint32_t imm = (int32_t)instruction >> 20; + int32_t imm = (int32_t)instruction >> 20; switch (funct3) { case 0b000: // ADDI + printf("ADDI - x%u %d + %d = %d -> x%u", rs1, registers[rs1], imm, registers[rs1] + imm, rd); registers[rd] = registers[rs1] + imm; break; default: - // TODO other and illegal instruction + // TODO proper error handling + fprintf(stderr, "Unrecognized OP-IMM funct3!"); + return 2; break; } /* code */ break; } case 0b0110111: { // LUI load upper immediate (U type) - uint32_t imm = instruction & 0xFFFFF000; + int32_t imm = instruction & 0xFFFFF000; // no need for cast because no sign extension registers[rd] = imm; + + printf("LUI - 0x%X (%d) -> x%u", imm, imm, rd); break; } case 0b1100111: { // JALR for unconditional jump and link register (I type) uint8_t funct3 = instruction >> 12 & 0x7; + uint8_t rs1 = instruction >> 15 & 0x1F; + int32_t imm = (int32_t)instruction >> 20; if (funct3 != 0) { - // TODO illegal instruction + // TODO proper error handling + fprintf(stderr, "Invalid JALR funct3! Should always be 0"); + return 2; } - uint8_t rs1 = instruction >> 15 & 0x1F; - uint32_t imm = (int32_t)instruction >> 20; - int result = registers[rs1] + imm; result &= ~1; // set LSB to 0 registers[rd] = cpu->programCounter + 4; cpu->programCounter = result - 4; // program counter is always incremented after executing instruction + printf("JALR - x%u %u + %u = %u", rs1, result - imm, imm, result); + break; } - default: - // TODO illegal instruction + case 0b0100011: { // STORE instruction (S type) + uint8_t funct3 = instruction >> 12 & 0x7; + uint8_t rs1 = instruction >> 15 & 0x1F; + uint8_t rs2 = instruction >> 20 & 0x1F; + int32_t imm = ((int32_t)instruction >> 20 & 0xffffffe0) | rd; + uint32_t addr = registers[rs1] + imm; + + if (funct3 == 0b000) { // SB + // SB stores an 8-bit value from the low bits of register rs2 to memory. + uint8_t val = registers[rs2] & 0xFF; + write_address_space(addressSpace, addr, 1, &val); + + printf("SB: x%d %u -> %u", rs2, val, addr); + } else if (funct3 == 0b001) { // SH + // SH stores a 16-bit value from the low bits of register rs2 to memory. + uint8_t val = registers[rs2] & 0xFFFF; + write_address_space(addressSpace, addr, 2, &val); + + printf("SH: x%d %u -> %u", rs2, val, addr); + } else if (funct3 == 0b010) { // SW + // SW stores a 32-bit value from the low bits of register rs2 to memory. + write_address_space(addressSpace, addr, 4, registers + rs2); + + printf("SW: x%d %u -> %u", rs2, registers[rs2], addr); + } else { + // TODO illegal funct3 + } break; + } + case 0b0000011: { // LOAD instruction (I type) + uint8_t funct3 = instruction >> 12 & 0x7; + uint8_t rs1 = instruction >> 15 & 0x1F; + int32_t imm = (int32_t)instruction >> 20; // sign extends automatically + uint32_t addr = registers[rs1] + imm; + + int32_t val; + + if (funct3 == 0b000) { // LB + // LB loads an 8-bit value from memory, then sign-extends to 32-bits. + read_address_space(addressSpace, addr, 1, &val); + val >>= 24; + + printf("LB (8bit)"); + } else if (funct3 == 0b001) { // LH + // LH loads a 16-bit value from memory, then sign-extends to 32-bits. + read_address_space(addressSpace, addr, 2, &val); + val >>= 16; + + printf("LH (16bit)"); + } else if (funct3 == 0b010) { // LW + // LW instruction loads a 32-bit value from memory into rd. + read_address_space(addressSpace, addr, 4, &val); // TODO we could assign directly + + printf("LW (32bit)"); + } else if (funct3 == 0b100) { // LBU + // LBU loads an 8-bit value from memory but then zero extends to 32-bits. + read_address_space(addressSpace, addr, 1, &val); + val = (uint32_t)val >> 24; // TODO optimize that perhaps + + printf("LBU (8bit)"); + } else if (funct3 == 0b101) { // LHU + // LHU loads a 16-bit value from memory but then zero extends to 32-bits. + read_address_space(addressSpace, addr, 2, &val); + val = (uint32_t)val >> 16; // TODO optimize that perhaps + + printf("LHU (16bit)"); + } else { + // TODO illegal funct3 + } + + printf(" - 0x%X -> x%u: 0x%X", addr, rd, val); + + registers[rd] = val; + break; + } + default: { + // TODO illegal instruction + fprintf(stderr, "Unrecognized opcode!"); + return 1; + break; + } } // TODO diff --git a/src/main.c b/src/main.c index 7cefdc1..54411d3 100644 --- a/src/main.c +++ b/src/main.c @@ -12,7 +12,7 @@ int main(int argc, char *argv[]) { return 1; } - AddressSpace *addressSpace = create_address_space(1024, 1024); + AddressSpace *addressSpace = create_address_space(256, 256); printf("Address space: %dB ROM, %dB RAM\n", addressSpace->romSize, addressSpace->ramSize); if (load_to_rom(argv[1], addressSpace) != 0) { @@ -23,14 +23,20 @@ int main(int argc, char *argv[]) { CPU cpu = create_cpu(addressSpace); cpu.registers[1] = addressSpace->romSize + addressSpace->ramSize; // make jumping to x1 end the program + printf("\n----- Start of program -----"); + int code; - while ((code = cpu_cycle(&cpu)) != -1) { + uint32_t cycles = 0; + while ((code = cpu_cycle(&cpu)) == 0) { + cycles++; //sleep(1); } + printf("\n\n----- End of program -----\n"); - printf("\n Emulator exited with code \033[1m%d\033[0m\n", code); - printf(" Program exited with code \033[1m%d\033[0m\n\n", cpu.registers[10]); + printf("\n Emulator exit code: \033[1m%d\033[0m\n", code); + printf(" Program exit code: \033[1m%d\033[0m (x10)\n", cpu.registers[10]); + printf(" Cycles: \033[1m%u\033[0m\n\n", cycles); int cols[8] = {0}; int total = 0;