From b7f27abaf4cb6e14c7ba4d2bb519d1346da54f7e Mon Sep 17 00:00:00 2001 From: Toni Date: Sat, 28 Feb 2026 19:27:17 +0100 Subject: [PATCH] implement more instructions --- build.sh | 8 +- riscv64.c | 340 +++++++++++++++++++++++++++++++++++++++--------------- 2 files changed, 254 insertions(+), 94 deletions(-) diff --git a/build.sh b/build.sh index c45d778..9a99dad 100755 --- a/build.sh +++ b/build.sh @@ -1,13 +1,15 @@ #!/bin/bash +WARNINGS="-Wall -Wextra -Wpedantic -Wno-unused-variable -Wno-gnu-binary-literal" + echo "building mos6502..." -cc -Wall -Wextra -Wpedantic -std=c99 -o mos6502 mos6502.c +cc $WARNINGS -std=c99 -o mos6502 mos6502.c if command -v pkg-config >/dev/null; then RAYLIB_FLAGS=$(pkg-config --cflags --libs raylib 2>/dev/null) if [ $? -eq 0 ]; then echo "building chip8..." - cc -Wall -Wextra -Wpedantic -std=c99 -o chip8 chip8.c $RAYLIB_FLAGS + cc $WARNINGS -std=c99 -o chip8 chip8.c $RAYLIB_FLAGS else echo "raylib not found - skipping chip8..." fi @@ -15,7 +17,7 @@ if command -v pkg-config >/dev/null; then LIBELF_FLAGS=$(pkg-config --cflags --libs libelf 2>/dev/null) if [ $? -eq 0 ]; then echo "building riscv64..." - cc -Wall -Wextra -Wpedantic -std=gnu11 -Wno-gnu-binary-literal -Wno-unused-variable -o riscv64 riscv64.c $LIBELF_FLAGS + cc $WARNINGS -std=gnu11 -o riscv64 riscv64.c $LIBELF_FLAGS else echo "libelf not found - skipping riscv64..." fi diff --git a/riscv64.c b/riscv64.c index ebd8574..01ddece 100644 --- a/riscv64.c +++ b/riscv64.c @@ -13,11 +13,49 @@ uint8_t rs1 = (ins >> 15) & 0b11111; \ int32_t imm = ((int32_t)ins) >> 20 +#define PARSE_R_INS(ins) \ + uint8_t rd = (ins >> 7) & 0b11111; \ + uint8_t funct3 = (ins >> 12) & 0b111; \ + uint8_t rs1 = (ins >> 15) & 0b11111; \ + uint8_t rs2 = (ins >> 20) & 0b11111; \ + int32_t funct7 = ((int32_t)ins) >> 25 + +#define PARSE_B_INS(ins) \ + uint8_t funct3 = (ins >> 12) & 0b111; \ + uint8_t rs1 = (ins >> 15) & 0b11111; \ + uint8_t rs2 = (ins >> 20) & 0b11111; \ + int32_t imm_12 = (ins >> 31) & 0b1; \ + int32_t imm_10_5 = (ins >> 25) & 0b111111; \ + int32_t imm_4_1 = (ins >> 8) & 0b1111; \ + int32_t imm_11 = (ins >> 7) & 0b1; \ + int32_t imm = \ + (imm_12 << 12) | (imm_11 << 11) | (imm_10_5 << 5) | (imm_4_1 << 1); \ + imm = (imm << 19) >> 19 + +#define PARSE_S_INS(ins) \ + uint8_t funct3 = (ins >> 12) & 0b111; \ + uint8_t rs1 = (ins >> 15) & 0b11111; \ + uint8_t rs2 = (ins >> 20) & 0b11111; \ + int32_t imm_11_5 = (ins >> 25) & 0b1111111; \ + int32_t imm_4_0 = (ins >> 7) & 0b11111; \ + int32_t imm = (imm_11_5 << 5) | imm_4_0; \ + imm = (imm << 20) >> 20 + +#define PARSE_J_INS(ins) \ + uint8_t rd = (ins >> 7) & 0b11111; \ + int32_t imm_20 = (ins >> 31) & 0b1; \ + int32_t imm_10_1 = (ins >> 21) & 0b1111111111; \ + int32_t imm_11 = (ins >> 20) & 0b1; \ + int32_t imm_19_12 = (ins >> 12) & 0b11111111; \ + int32_t imm = \ + (imm_20 << 20) | (imm_19_12 << 12) | (imm_11 << 11) | (imm_10_1 << 1); \ + imm = (imm << 11) >> 11 + #define PARSE_U_INS(ins) \ uint8_t rd = (ins >> 7) & 0b11111; \ - int32_t imm = ((int32_t)ins) >> 20 + int32_t imm = ins >> 12 -static const char *REGS[32] = { +static const char *const REGS[32] = { "zero", "ra", "sp", "gp", "tp", "t0", "t1", "t2", "fp", "s1", "a0", "a1", "a2", "a3", "a4", "a5", "a6", "a7", "s2", "s3", "s4", "s5", "s6", "s7", "s8", "s9", "s10", "s11", "t3", "t4", "t5", "t6"}; @@ -30,7 +68,7 @@ typedef struct { typedef struct { uint8_t *memory; size_t pc; - uint64_t regs[32]; + int64_t regs[32]; Section code_section; } RISCV64; @@ -75,7 +113,7 @@ RISCV64 riscv64_load(uint8_t *exe_bytes, size_t exe_size) { }; } -void riscv64_dump(RISCV64 *r) { +void riscv64_dump(const RISCV64 *r) { printf("REGS:"); for (size_t i = 0; i < 32; i++) { printf(" %lu", r->regs[i]); @@ -83,91 +121,121 @@ void riscv64_dump(RISCV64 *r) { printf("\n"); } -void riscv64_disassemble(RISCV64 *r) { - r->pc = r->code_section.offset; +void riscv64_disassemble_one(const RISCV64 *r) { + uint32_t ins; + memcpy(&ins, r->memory + r->pc, 4); - while (r->pc < r->code_section.offset + r->code_section.size) { - uint32_t ins; - memcpy(&ins, r->memory + r->pc, 4); + uint16_t opcode = ins & 0b1111111; - uint16_t opcode = ins & 0b1111111; + // https://stackoverflow.com/questions/62939410/how-can-i-find-out-the-instruction-format-of-a-risc-v-instruction + switch (opcode) { + case 0b1100011: { + PARSE_B_INS(ins); - // https://stackoverflow.com/questions/62939410/how-can-i-find-out-the-instruction-format-of-a-risc-v-instruction - switch (opcode) { - case 0b1100011: - printf("B-type\n"); + if (funct3 == 0b100) { + printf("blt %s, %s, %d\n", REGS[rs1], REGS[rs2], imm); + } else if (funct3 == 0b101) { + printf("bge %s, %s, %d\n", REGS[rs1], REGS[rs2], imm); break; - case 0b0010011: { - PARSE_I_INS(ins); - - switch (funct3) { - case 0b000: - printf("addi %s, %s, %d\n", REGS[rd], REGS[rs1], imm); - break; - default: - fprintf(stderr, "I-type 1: unrecognized funct3: %03b\n", funct3); - exit(1); - } - }; break; - case 0b0000011: - printf("I-type 2\n"); - break; - case 0b1100111: - printf("I-type 3\n"); - break; - case 0b1110011: { - PARSE_I_INS(ins); - - switch (funct3) { - case 0b000: - switch (imm) { - case 0: - printf("ecall\n"); - break; - default: - fprintf(stderr, "I-type 4: funct3=000 unrecognized imm: %b\n", imm); - exit(1); - }; - break; - default: - fprintf(stderr, "I-type 4: unrecognized funct3: %03b\n", funct3); - exit(1); - } - }; break; - case 0b1101111: - printf("J-type\n"); - break; - case 0b0110011: - printf("R-type 1\n"); - break; - case 0b0101111: - printf("R-type 2\n"); - break; - case 0b0111011: - printf("R-type 3\n"); - break; - case 0b0100011: - printf("S-type\n"); - break; - case 0b0110111: - printf("U-type 1\n"); - break; - case 0b0010111: { - PARSE_U_INS(ins); - printf("auipc %s, %d\n", REGS[rd], imm); - }; break; - default: - fprintf(stderr, "Unrecognized opcode: %07b\n", opcode); + } else { + fprintf(stderr, "B-type: unrecognized funct3: %03b\n", funct3); exit(1); } + }; break; + case 0b0010011: { + PARSE_I_INS(ins); - r->pc += 4; + if (funct3 == 0b000) { + printf("addi %s, %s, %d\n", REGS[rd], REGS[rs1], imm); + } else { + fprintf(stderr, "I-type 1: unrecognized funct3: %03b\n", funct3); + exit(1); + } + }; break; + case 0b0000011: { + PARSE_I_INS(ins); + + if (funct3 == 0b000) { + printf("lb %s, %d(%s)\n", REGS[rd], imm, REGS[rs1]); + } else { + fprintf(stderr, "I-type 2: unrecognized funct3: %03b\n", funct3); + exit(1); + } + }; break; + case 0b1100111: { + PARSE_I_INS(ins); + printf("jalr %s, %s, %d\n", REGS[rd], REGS[rs1], imm); + }; break; + case 0b1110011: { + PARSE_I_INS(ins); + + if (funct3 == 0b000) { + if (imm == 0) { + printf("ecall\n"); + } else { + fprintf(stderr, "I-type 4: funct3=000 unrecognized imm: %b\n", imm); + exit(1); + } + break; + } else { + fprintf(stderr, "I-type 4: unrecognized funct3: %03b\n", funct3); + exit(1); + } + }; break; + case 0b1101111: { + PARSE_J_INS(ins); + printf("jal %s, %d\n", REGS[rd], imm); + }; break; + case 0b0110011: { + PARSE_R_INS(ins); + if (funct3 == 0b000) { + if (funct7 == 0) { + printf("add %s, %s, %s\n", REGS[rd], REGS[rs1], REGS[rs2]); + } else { + fprintf(stderr, "R-type 1: funct3=0b000: unrecognized funct7: %b\n", + funct7); + exit(1); + } + } else { + fprintf(stderr, "R-type 1: unrecognized funct3: %03b\n", funct3); + exit(1); + } + }; break; + case 0b0101111: + fprintf(stderr, "R-type 2: unimplemented\n"); + exit(1); + case 0b0111011: + fprintf(stderr, "R-type 3: unimplemented\n"); + exit(1); + case 0b0100011: { + PARSE_S_INS(ins); + + if (funct3 == 0b000) { + printf("sb %s, %d(%s)\n", REGS[rs2], imm, REGS[rs1]); + } else { + fprintf(stderr, "S-type: unrecognized funct3: %03b\n", funct3); + exit(1); + } + }; break; + case 0b0110111: + fprintf(stderr, "U-type 1: unimplemented\n"); + exit(1); + break; + case 0b0010111: { + PARSE_U_INS(ins); + printf("auipc %s, %d\n", REGS[rd], imm); + }; break; + default: + fprintf(stderr, "Unrecognized opcode: %07b\n", opcode); + exit(1); } } void riscv64_execute(RISCV64 *r) { r->pc = r->code_section.offset; + r->regs[2] = (20 * 1024 * 1024) - 1; // set the stack pointer + while (r->pc < r->code_section.offset + r->code_section.size) { r->regs[0] = 0; // clear the zero register @@ -178,30 +246,81 @@ void riscv64_execute(RISCV64 *r) { // https://stackoverflow.com/questions/62939410/how-can-i-find-out-the-instruction-format-of-a-risc-v-instruction switch (opcode) { + case 0b1100011: { + PARSE_B_INS(ins); + + if (funct3 == 0b101) { // bge + if (r->regs[rs1] >= r->regs[rs2]) { + r->pc += imm; + continue; + } + } else if (funct3 == 0b100) { // blt + if (r->regs[rs1] < r->regs[rs2]) { + r->pc += imm; + continue; + } + } else { + fprintf(stderr, "B-type: unrecognized funct3: %03b\n", funct3); + exit(1); + } + }; break; case 0b0010011: { PARSE_I_INS(ins); - switch (funct3) { - case 0b000: + if (funct3 == 0b000) { // addi r->regs[rd] = r->regs[rs1] + imm; - break; - default: + } else { fprintf(stderr, "I-type 1: unrecognized funct3: %03b\n", funct3); exit(1); } }; break; + case 0b0000011: { + PARSE_I_INS(ins); + + if (funct3 == 0b000) { // lb + r->regs[rd] = r->memory[r->regs[rs1] + imm]; + } else { + fprintf(stderr, "I-type 2: unrecognized funct3: %03b\n", funct3); + exit(1); + } + }; break; + case 0b1100111: { // jalr + PARSE_I_INS(ins); + uint32_t target = (r->regs[rs1] + imm) & ~1; + r->regs[rd] = r->pc + 4; + r->pc = target; + continue; + }; break; case 0b1110011: { PARSE_I_INS(ins); - switch (funct3) { - case 0b000: - switch (imm) { - case 0: + if (funct3 == 0b000) { + if (imm == 0) { // ecall // https://jborza.com/post/2021-05-11-riscv-linux-syscalls/ switch (r->regs[17]) { + case 63: { // read + if (r->regs[10] != 0) { + fprintf(stderr, "read syscall implemented only for stdin.\n"); + exit(1); + } + + size_t start = r->regs[11]; + size_t count = r->regs[12]; + size_t bytes_read = 0; + + for (size_t i = 0; i < count; i++) { + int c = getchar(); + if (c == EOF) + break; + r->memory[start + i] = (uint8_t)c; + bytes_read++; + } + + r->regs[10] = bytes_read; + }; break; case 64: { // write if (r->regs[10] != 1) { - fprintf(stderr, "write syscall is implemented only for stdout\n"); + fprintf(stderr, "write syscall implemented only for stdout.\n"); exit(1); } @@ -213,7 +332,7 @@ void riscv64_execute(RISCV64 *r) { } }; break; case 93: { // exit - printf("Program exited with code %lu.\n", r->regs[11]); + printf("Program exited with code %lu.\n", r->regs[10]); return; }; break; default: @@ -221,19 +340,52 @@ void riscv64_execute(RISCV64 *r) { exit(1); } break; - default: + } else { fprintf(stderr, "I-type 4: funct3=000 unrecognized imm: %b\n", imm); exit(1); }; break; - default: + } else { fprintf(stderr, "I-type 4: unrecognized funct3: %03b\n", funct3); exit(1); } }; break; - case 0b0010111: { + case 0b1101111: { // jal + PARSE_J_INS(ins); + r->regs[rd] = r->pc + 4; + r->pc += imm; + continue; + }; break; + case 0b0110011: { + PARSE_R_INS(ins); + if (funct3 == 0b000) { + if (funct7 == 0) { // add + r->regs[rd] = r->regs[rs1] + r->regs[rs2]; + break; + } else { + fprintf(stderr, "R-type 1: funct3=0b000: unrecognized funct7: %b\n", + funct7); + exit(1); + } + } else { + fprintf(stderr, "R-type 1: unrecognized funct3: %03b\n", funct3); + exit(1); + } + }; break; + case 0b0100011: { + PARSE_S_INS(ins); + + if (funct3 == 0b000) { // sb + size_t addr = r->regs[rs1] + imm; + r->memory[addr] = r->regs[rs2]; + } else { + fprintf(stderr, "S-type: unrecognized funct3: %03b\n", funct3); + exit(1); + } + }; break; + case 0b0010111: { // auipc PARSE_U_INS(ins); - r->regs[rd] = r->pc + (imm << 12); + r->regs[rd] = r->pc + (imm >> 12); }; break; default: fprintf(stderr, "Unrecognized opcode: %07b\n", opcode); @@ -272,6 +424,12 @@ int main(int argc, char *argv[]) { fclose(file); RISCV64 r = riscv64_load(exe_bytes, exe_size); - riscv64_disassemble(&r); + + for (r.pc = r.code_section.offset; + r.pc < r.code_section.offset + r.code_section.size; r.pc += 4) { + riscv64_disassemble_one(&r); + } + printf("END DISASSEMBLY\n"); + riscv64_execute(&r); }