Compare commits

..

3 Commits

Author SHA1 Message Date
93da83704f fix chip8 key press 2026-05-15 20:31:09 +02:00
2d53a59399 brk, mmap, lseek 2026-03-10 16:41:05 +01:00
7a893076fb disassemble printf 2026-03-10 14:26:07 +01:00
4 changed files with 460 additions and 82 deletions

View File

@@ -1,4 +1,4 @@
BSD 3-Clause License BSD 2-Clause License
Copyright (c) 2025-2026, Antoni Piasecki Copyright (c) 2025-2026, Antoni Piasecki
@@ -12,10 +12,6 @@ modification, are permitted provided that the following conditions are met:
this list of conditions and the following disclaimer in the documentation this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution. and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE

View File

@@ -2,7 +2,7 @@
Emulators written in C and C++ Emulators written in C and C++
* `riscv64.cc` - [RV64IM instruction set](https://en.wikipedia.org/wiki/RISC-V) (more extensions coming soon-ish) * `riscv64.cc` - [RV64IMAC instruction set](https://en.wikipedia.org/wiki/RISC-V) (more extensions coming soon-ish)
* `chip8.c` - [CHIP8](https://en.wikipedia.org/wiki/CHIP-8) * `chip8.c` - [CHIP8](https://en.wikipedia.org/wiki/CHIP-8)
* `mos6502.c` - [MOS 6502](https://en.wikipedia.org/wiki/MOS_Technology_6502) * `mos6502.c` - [MOS 6502](https://en.wikipedia.org/wiki/MOS_Technology_6502)

View File

@@ -345,7 +345,7 @@ void chip8_step(CHIP8 *c) {
break; break;
case 0x0A: { case 0x0A: {
uint8_t key_pressed = 0; uint8_t key_pressed = 0;
while (!key_pressed && WindowShouldClose()) { while (!key_pressed && !WindowShouldClose()) {
for (size_t i = 0; i < 16; i++) { for (size_t i = 0; i < 16; i++) {
if (IsKeyDown(keyboard_map[i])) { if (IsKeyDown(keyboard_map[i])) {
key_pressed = 1; key_pressed = 1;

View File

@@ -1,5 +1,6 @@
// https://docs.riscv.org/reference/isa/unpriv/rv-32-64g.html // https://docs.riscv.org/reference/isa/unpriv/rv-32-64g.html
// https://riscv.org/wp-content/uploads/2024/12/riscv-calling.pdf // https://riscv.org/wp-content/uploads/2024/12/riscv-calling.pdf
// TODO: convert C instructions to normal ones at parse time?
#if !defined(__cplusplus) || __cplusplus < 202302L #if !defined(__cplusplus) || __cplusplus < 202302L
#error "C++23 or later is required. Either get a newer compiler " \ #error "C++23 or later is required. Either get a newer compiler " \
@@ -7,11 +8,11 @@
"instead of <print> and remove this check." "instead of <print> and remove this check."
#endif #endif
#include <cassert>
#include <cstring> #include <cstring>
#include <fstream> #include <fstream>
#include <gelf.h> #include <gelf.h>
#include <iostream> #include <iostream>
#include <libelf.h>
#include <print> #include <print>
#include <sys/mman.h> #include <sys/mman.h>
#include <sys/time.h> #include <sys/time.h>
@@ -46,7 +47,13 @@ enum Op {
ADDI, ADDI,
ADDIW, ADDIW,
ADDW, ADDW,
AMOADD_D,
AMOADD_W,
AMOMAXU_D,
AMOMAXU_W,
AMOOR_W,
AMOSWAP_D, AMOSWAP_D,
AMOSWAP_W,
AND, AND,
ANDI, ANDI,
AUIPC, AUIPC,
@@ -67,7 +74,10 @@ enum Op {
C_BEQZ, C_BEQZ,
C_BNEZ, C_BNEZ,
C_EBREAK, C_EBREAK,
C_FLD,
C_FLDSP, C_FLDSP,
C_FSD,
C_FSDSP,
C_J, C_J,
C_JALR, C_JALR,
C_JR, C_JR,
@@ -76,6 +86,7 @@ enum Op {
C_LI, C_LI,
C_LUI, C_LUI,
C_LW, C_LW,
C_LWSP,
C_MV, C_MV,
C_OR, C_OR,
C_SD, C_SD,
@@ -88,13 +99,33 @@ enum Op {
C_SW, C_SW,
C_SWSP, C_SWSP,
C_XOR, C_XOR,
CSRRS,
CSRRSI,
DIV, DIV,
DIVU, DIVU,
DIVUW, DIVUW,
DIVW, DIVW,
ECALL, ECALL,
FADD_D,
FCLASS_D,
FCVT_D_W,
FCVT_D_WU,
FENCE, FENCE,
FENCE_TSO, FENCE_TSO,
FLD,
FLW,
FMUL_D,
FMV_D_X,
FMV_W_X,
FMV_X_D,
FSD,
FSGNJ_D,
FSGNJN_D,
FSGNJX_D,
FSGNJ_S,
FSGNJN_S,
FSGNJX_S,
FSW,
JAL, JAL,
JALR, JALR,
LB, LB,
@@ -144,6 +175,8 @@ enum Op {
SW, SW,
XOR, XOR,
XORI, XORI,
NUM_OPS
}; };
struct Ins { struct Ins {
@@ -183,7 +216,9 @@ enum class Format {
CR1, CR1,
CL, CL,
R_ATOMIC, R_ATOMIC,
R_ATOMIC_LR R_ATOMIC_LR,
CSR,
CSRI
}; };
struct OpDef { struct OpDef {
@@ -191,13 +226,19 @@ struct OpDef {
Format format; Format format;
}; };
static constexpr std::array<OpDef, 103> OP_TABLE = {{ static constexpr auto OP_TABLE = std::to_array<OpDef>({
{"???", Format::NONE}, {"???", Format::NONE},
{"add", Format::R}, {"add", Format::R},
{"addi", Format::I}, {"addi", Format::I},
{"addiw", Format::I}, {"addiw", Format::I},
{"addw", Format::R}, {"addw", Format::R},
{"amoadd.d", Format::R_ATOMIC},
{"amoadd.w", Format::R_ATOMIC},
{"amomaxu.d", Format::R_ATOMIC},
{"amomaxu.w", Format::R_ATOMIC},
{"amoor.w", Format::R_ATOMIC},
{"amoswap.d", Format::R_ATOMIC}, {"amoswap.d", Format::R_ATOMIC},
{"amoswap.w", Format::R_ATOMIC},
{"and", Format::R}, {"and", Format::R},
{"andi", Format::I}, {"andi", Format::I},
{"auipc", Format::U}, {"auipc", Format::U},
@@ -218,7 +259,10 @@ static constexpr std::array<OpDef, 103> OP_TABLE = {{
{"c.beqz", Format::CB}, {"c.beqz", Format::CB},
{"c.bnez", Format::CB}, {"c.bnez", Format::CB},
{"c.ebreak", Format::NONE}, {"c.ebreak", Format::NONE},
{"c.fldsp", Format::CSS}, {"c.fld", Format::CL},
{"c.fldsp", Format::CI},
{"c.fsd", Format::S},
{"c.fsdsp", Format::CSS},
{"c.j", Format::CJ}, {"c.j", Format::CJ},
{"c.jalr", Format::CR1}, {"c.jalr", Format::CR1},
{"c.jr", Format::CR1}, {"c.jr", Format::CR1},
@@ -227,6 +271,7 @@ static constexpr std::array<OpDef, 103> OP_TABLE = {{
{"c.li", Format::CI}, {"c.li", Format::CI},
{"c.lui", Format::CI}, {"c.lui", Format::CI},
{"c.lw", Format::CL}, {"c.lw", Format::CL},
{"c.lwsp", Format::CL},
{"c.mv", Format::CR}, {"c.mv", Format::CR},
{"c.or", Format::CR}, {"c.or", Format::CR},
{"c.sd", Format::S}, {"c.sd", Format::S},
@@ -239,13 +284,33 @@ static constexpr std::array<OpDef, 103> OP_TABLE = {{
{"c.sw", Format::S}, {"c.sw", Format::S},
{"c.swsp", Format::CSS}, {"c.swsp", Format::CSS},
{"c.xor", Format::CR}, {"c.xor", Format::CR},
{"csrrs", Format::CSR},
{"csrrsi", Format::CSRI},
{"div", Format::R}, {"div", Format::R},
{"divu", Format::R}, {"divu", Format::R},
{"divuw", Format::R}, {"divuw", Format::R},
{"divw", Format::R}, {"divw", Format::R},
{"ecall", Format::NONE}, {"ecall", Format::NONE},
{"fadd.d", Format::R},
{"fclass.d", Format::R},
{"fcvt.d.w", Format::R},
{"fcvt.d.wu", Format::R},
{"fence", Format::NONE}, {"fence", Format::NONE},
{"fence.tso", Format::NONE}, {"fence.tso", Format::NONE},
{"fld", Format::I},
{"flw", Format::I},
{"fmul.d", Format::R},
{"fmv.d.x", Format::R},
{"fmv.x.d", Format::R},
{"fmv.w.x", Format::R},
{"fsw", Format::S},
{"fsd", Format::S},
{"fsgnj.d", Format::R},
{"fsgnjn.d", Format::R},
{"fsgnjx.d", Format::R},
{"fsgnj.s", Format::R},
{"fsgnjn.s", Format::R},
{"fsgnjx.s", Format::R},
{"jal", Format::J}, {"jal", Format::J},
{"jalr", Format::I}, {"jalr", Format::I},
{"lb", Format::I_LOAD}, {"lb", Format::I_LOAD},
@@ -295,7 +360,9 @@ static constexpr std::array<OpDef, 103> OP_TABLE = {{
{"sw", Format::S}, {"sw", Format::S},
{"xor", Format::R}, {"xor", Format::R},
{"xori", Format::I}, {"xori", Format::I},
}}; });
static_assert(OP_TABLE.size() == NUM_OPS, "len(OP_TABLE) != len(Op::*)");
class RISCV64 { class RISCV64 {
public: public:
@@ -320,16 +387,22 @@ public:
m_code_section = get_code_section(elf, ehdr); m_code_section = get_code_section(elf, ehdr);
m_pc = m_code_section.offset; m_pc = m_code_section.offset;
u64 max_addr = 0;
for (u64 i = 0; i < ehdr.e_phnum; i++) { for (u64 i = 0; i < ehdr.e_phnum; i++) {
GElf_Phdr phdr; GElf_Phdr phdr;
gelf_getphdr(elf, i, &phdr); gelf_getphdr(elf, i, &phdr);
if (phdr.p_type == PT_LOAD) { if (phdr.p_type == PT_LOAD) {
// TODO: probably should disassemble all of those instead of just .text?
std::copy_n(exe_bytes.data() + phdr.p_offset, phdr.p_filesz, std::copy_n(exe_bytes.data() + phdr.p_offset, phdr.p_filesz,
m_memory + phdr.p_vaddr); m_memory + phdr.p_vaddr);
max_addr = std::max(max_addr, phdr.p_vaddr + phdr.p_memsz);
} }
} }
elf_end(elf); elf_end(elf);
m_brk = m_brk_base = (max_addr + 4095ULL) & ~4095ULL; // page align
m_next_mmap_addr = m_brk_base + 128 * 1024 * 1024;
u64 num_ins = m_code_section.size / 2; u64 num_ins = m_code_section.size / 2;
m_decoded.resize(num_ins); m_decoded.resize(num_ins);
@@ -358,10 +431,18 @@ public:
} }
} }
// TODO: the registers in floating-point instructions should use the f0-f31
// registers but that would require rewriting a lot of stuff and its not gonna
// be a problem in execution so we'll live with that for now
void disassemble_ins(Ins ins) { void disassemble_ins(Ins ins) {
assert((u64)ins.op < OP_TABLE.size());
const OpDef &def = OP_TABLE[ins.op]; const OpDef &def = OP_TABLE[ins.op];
switch (def.format) { switch (def.format) {
case Format::NONE:
std::println("{}", def.mnemonic);
break;
case Format::R: case Format::R:
std::println("{} {}, {}, {}", def.mnemonic, REGS[ins.rd], REGS[ins.rs1], std::println("{} {}, {}, {}", def.mnemonic, REGS[ins.rd], REGS[ins.rs1],
REGS[ins.rs2]); REGS[ins.rs2]);
@@ -421,23 +502,27 @@ public:
std::println("{} {}, {}, ({})", def.mnemonic, REGS[ins.rd], REGS[ins.rs2], std::println("{} {}, {}, ({})", def.mnemonic, REGS[ins.rd], REGS[ins.rs2],
REGS[ins.rs1]); REGS[ins.rs1]);
break; break;
case Format::NONE: case Format::CSR:
std::println("{}", def.mnemonic); std::println("{} {}, {}, {}", def.mnemonic, REGS[ins.rd], ins.imm,
REGS[ins.rs1]);
break;
case Format::CSRI:
std::println("{} {}, {}, {}", def.mnemonic, REGS[ins.rd], ins.imm,
ins.rs1);
break; break;
} }
} }
void dump() { void dump() {
std::print("REGS:");
for (u64 i = 0; i < 32; i++) { for (u64 i = 0; i < 32; i++) {
std::print(" {}", m_regs[i]); std::print("{}=0x{:x} ", REGS[i], m_regs[i]);
} }
std::println(); std::println();
} }
void push_u64(u64 x) { void push_u64(u64 v) {
m_regs[2] -= 8; m_regs[2] -= 8;
*(u64 *)(m_memory + m_regs[2]) = x; mem_write<u64>(m_regs[2], v);
} }
void execute() { void execute() {
@@ -495,7 +580,7 @@ public:
m_regs[i.rd] = m_regs[i.rs1] & i.imm; m_regs[i.rd] = m_regs[i.rs1] & i.imm;
}; break; }; break;
case Op::AUIPC: { case Op::AUIPC: {
m_regs[i.rd] = m_pc + ((i64)i.imm << 12); m_regs[i.rd] = m_pc + (i64)(i32)((u32)i.imm << 12);
}; break; }; break;
case Op::BEQ: { case Op::BEQ: {
if (m_regs[i.rs1] == m_regs[i.rs2]) { if (m_regs[i.rs1] == m_regs[i.rs2]) {
@@ -569,36 +654,41 @@ public:
continue; continue;
} }
}; break; }; break;
case Op::C_EBREAK: {
std::println(stderr, "EBREAK at pc=0x{:x}", m_pc);
dump();
exit(1);
}; break;
case Op::C_J: { case Op::C_J: {
m_pc += i.imm; m_pc += i.imm;
continue; continue;
}; break; }; break;
case Op::C_JALR: { case Op::C_JALR: {
m_regs[1] = m_pc + 2; m_regs[1] = m_pc + 2;
m_pc = m_regs[i.rs1]; m_pc = m_regs[i.rs1] & ~1ULL;
continue; continue;
}; break; }; break;
case Op::C_JR: { case Op::C_JR: {
m_pc = m_regs[i.rs1]; m_pc = m_regs[i.rs1] & ~1ULL;
continue; continue;
}; break; }; break;
case Op::C_LD: { case Op::C_LD: {
u64 addr = m_regs[i.rs1] + i.imm; u64 addr = m_regs[i.rs1] + i.imm;
m_regs[i.rd] = *(u64 *)&m_memory[addr]; m_regs[i.rd] = mem_read<u64>(addr);
}; break; }; break;
case Op::C_LDSP: { case Op::C_LDSP: {
u64 addr = sp + i.imm; u64 addr = sp + i.imm;
m_regs[i.rd] = *(u64 *)(&m_memory[addr]); m_regs[i.rd] = mem_read<u64>(addr);
}; break; }; break;
case Op::C_LI: { case Op::C_LI: {
m_regs[i.rd] = i.imm; m_regs[i.rd] = i.imm;
}; break; }; break;
case Op::C_LUI: { case Op::C_LUI: {
m_regs[i.rd] = (int64_t)(uint64_t)((int64_t)i.imm) << 12; m_regs[i.rd] = (i64)(i32)((u32)i.imm << 12);
}; break; }; break;
case Op::C_LW: { case Op::C_LW: {
u64 addr = m_regs[i.rs1] + i.imm; u64 addr = m_regs[i.rs1] + i.imm;
m_regs[i.rd] = *(u32 *)&m_memory[addr]; m_regs[i.rd] = (i64)mem_read<i32>(addr);
}; break; }; break;
case Op::C_MV: { case Op::C_MV: {
m_regs[i.rd] = m_regs[i.rs2]; m_regs[i.rd] = m_regs[i.rs2];
@@ -608,7 +698,7 @@ public:
}; break; }; break;
case Op::C_SDSP: { case Op::C_SDSP: {
u64 addr = sp + i.imm; u64 addr = sp + i.imm;
*(u64 *)(&m_memory[addr]) = m_regs[i.rs2]; mem_write<u64>(addr, m_regs[i.rs2]);
}; break; }; break;
case Op::C_SLLI: { case Op::C_SLLI: {
m_regs[i.rd] = (i64)((u64)m_regs[i.rd] << i.imm); m_regs[i.rd] = (i64)((u64)m_regs[i.rd] << i.imm);
@@ -622,6 +712,20 @@ public:
case Op::C_SUB: { case Op::C_SUB: {
m_regs[i.rd] -= m_regs[i.rs2]; m_regs[i.rd] -= m_regs[i.rs2];
}; break; }; break;
case Op::C_SUBW: {
m_regs[i.rd] = (i32)(m_regs[i.rd] - m_regs[i.rs2]);
}; break;
case Op::C_SWSP: {
u64 addr = (u64)sp + (u64)i.imm;
mem_write<u32>(addr, m_regs[i.rs2]);
}; break;
case Op::C_LWSP: {
u64 addr = (u64)sp + (u64)i.imm;
m_regs[i.rd] = (i32)mem_read<u32>(addr);
}; break;
case Op::C_XOR: {
m_regs[i.rd] ^= m_regs[i.rs2];
}; break;
case Op::DIV: { case Op::DIV: {
if (m_regs[i.rs2] == 0) { if (m_regs[i.rs2] == 0) {
m_regs[i.rd] = -1; m_regs[i.rd] = -1;
@@ -629,9 +733,16 @@ public:
m_regs[i.rd] = m_regs[i.rs1] / m_regs[i.rs2]; m_regs[i.rd] = m_regs[i.rs1] / m_regs[i.rs2];
} }
}; break; }; break;
case Op::DIVU: {
if ((u64)m_regs[i.rs2] == 0) {
m_regs[i.rd] = -1;
} else {
m_regs[i.rd] = (i64)((u64)m_regs[i.rs1] / (u64)m_regs[i.rs2]);
}
}; break;
case Op::DIVUW: { case Op::DIVUW: {
if (m_regs[i.rs2] == 0) { if ((u32)m_regs[i.rs2] == 0) {
m_regs[i.rd] = -1LL; m_regs[i.rd] = -1;
} else { } else {
m_regs[i.rd] = (i32)((u32)m_regs[i.rs1] / (u32)m_regs[i.rs2]); m_regs[i.rd] = (i32)((u32)m_regs[i.rs1] / (u32)m_regs[i.rs2]);
} }
@@ -649,6 +760,7 @@ public:
}; break; }; break;
case Op::ECALL: { case Op::ECALL: {
// https://jborza.com/post/2021-05-11-riscv-linux-syscalls/ // https://jborza.com/post/2021-05-11-riscv-linux-syscalls/
// ^ already got 2 syscalls wrong
switch (m_regs[17]) { switch (m_regs[17]) {
case 29: { // ioctl case 29: { // ioctl
u32 fd = m_regs[10]; u32 fd = m_regs[10];
@@ -666,13 +778,26 @@ public:
}; break; }; break;
} }
}; break; }; break;
case 62: { // lseek
u32 fd = m_regs[10];
// i64 offset = m_regs[11];
// u32 whence = m_regs[12];
if (fd != 0) {
std::println(stderr, "lseek syscall implemented only for stdin");
exit(1);
}
// TODO
m_regs[10] = -ESPIPE;
}; break;
case 63: { // read case 63: { // read
u32 fd = m_regs[10]; u32 fd = m_regs[10];
u64 buf = m_regs[11]; u64 buf = m_regs[11];
u64 count = m_regs[12]; u64 count = m_regs[12];
if (fd != 0) { if (fd != 0) {
std::println(stderr, "read syscall implemented only for stdin."); std::println(stderr, "read syscall implemented only for stdin");
exit(1); exit(1);
} }
@@ -719,8 +844,8 @@ public:
u64 total_written = 0; u64 total_written = 0;
for (u64 i = 0; i < vlen; i++) { for (u64 i = 0; i < vlen; i++) {
u64 iov_entry = vec + i * 16; u64 iov_entry = vec + i * 16;
u64 buf = *(u64 *)&m_memory[iov_entry]; u64 buf = mem_read<u64>(iov_entry);
u64 len = *(u64 *)&m_memory[iov_entry + 8]; u64 len = mem_read<u64>(iov_entry + 8);
for (u64 j = 0; j < len; j++) { for (u64 j = 0; j < len; j++) {
std::cout.put(m_memory[buf + j]); std::cout.put(m_memory[buf + j]);
@@ -760,6 +885,39 @@ public:
m_regs[10] = -errno; m_regs[10] = -errno;
} }
}; break; }; break;
case 214: { // brk
u64 brk = m_regs[10];
if (brk >= m_brk_base) {
m_brk = brk;
}
m_regs[10] = (i64)m_brk;
}; break;
case 222: { // mmap
u64 addr = m_regs[10];
u64 length = m_regs[11];
// i32 prot = m_regs[12];
i32 flags = m_regs[13];
// i32 fd = m_regs[14];
// i64 offset = m_regs[15];
if (!(flags & MAP_PRIVATE) || !(flags & MAP_ANONYMOUS)) {
std::println(
stderr,
"mmap implemented only for flags=(MAP_PRIVATE|MAP_ANONYMOUS)",
flags);
exit(1);
}
if (!(flags & MAP_FIXED)) {
length = (length + 4095) & ~4095;
addr = m_next_mmap_addr;
m_next_mmap_addr += length;
}
std::memset(m_memory + addr, 0, length);
m_regs[10] = addr;
}; break;
default: default:
std::println(stderr, "Unimplemented syscall: {}", m_regs[17]); std::println(stderr, "Unimplemented syscall: {}", m_regs[17]);
exit(1); exit(1);
@@ -783,46 +941,64 @@ public:
m_regs[i.rd] = m_memory[m_regs[i.rs1] + i.imm]; m_regs[i.rd] = m_memory[m_regs[i.rs1] + i.imm];
}; break; }; break;
case Op::LD: { case Op::LD: {
m_regs[i.rd] = *(u64 *)&m_memory[m_regs[i.rs1] + i.imm]; m_regs[i.rd] = mem_read<u64>(m_regs[i.rs1] + i.imm);
}; break; }; break;
case Op::LH: { case Op::LH: {
m_regs[i.rd] = *(i16 *)&m_memory[m_regs[i.rs1] + i.imm]; m_regs[i.rd] = mem_read<i16>(m_regs[i.rs1] + i.imm);
}; break;
case Op::LHU: {
m_regs[i.rd] = mem_read<u16>(m_regs[i.rs1] + i.imm);
}; break; }; break;
case Op::LUI: { case Op::LUI: {
m_regs[i.rd] = (i64)(i32)(i.imm << 12); m_regs[i.rd] = (i64)(i32)((u32)i.imm << 12);
}; break; }; break;
case Op::LW: { case Op::LW: {
m_regs[i.rd] = *(i32 *)&m_memory[m_regs[i.rs1] + i.imm]; m_regs[i.rd] = mem_read<i32>(m_regs[i.rs1] + i.imm);
}; break;
case Op::LWU: {
m_regs[i.rd] = mem_read<u32>(m_regs[i.rs1] + i.imm);
}; break; }; break;
case Op::MUL: { case Op::MUL: {
m_regs[i.rd] = m_regs[i.rs1] * m_regs[i.rs2]; m_regs[i.rd] = m_regs[i.rs1] * m_regs[i.rs2];
}; break; }; break;
case Op::MULH: { case Op::MULH: {
i64 a_hi = m_regs[i.rs1] >> 32; i64 rs1 = m_regs[i.rs1];
i64 b_hi = m_regs[i.rs2] >> 32; i64 rs2 = m_regs[i.rs2];
u64 a_lo = (u32)m_regs[i.rs1]; u64 u = rs1;
u64 b_lo = (u32)m_regs[i.rs2]; u64 v = rs2;
u64 p0 = a_lo * b_lo; u64 u0 = u & 0xffffffff;
i64 p1 = a_hi * b_lo; u64 u1 = u >> 32;
i64 p2 = b_hi * a_lo; u64 v0 = v & 0xffffffff;
i64 p3 = a_hi * b_hi; u64 v1 = v >> 32;
i64 carry = (p0 >> 32); u64 t = u0 * v0;
i64 mid = p1 + p2 + carry; u64 k = t >> 32;
m_regs[i.rd] = p3 + (mid >> 32); t = u1 * v0 + k;
u64 w1 = t & 0xffffffff;
u64 w2 = t >> 32;
t = u0 * v1 + w1;
k = t >> 32;
u64 res = u1 * v1 + w2 + k;
if (rs1 < 0)
res -= u64(rs2);
if (rs2 < 0)
res -= u64(rs1);
m_regs[i.rd] = (i64)res;
}; break; }; break;
case Op::MULHU: { case Op::MULHU: {
u64 a = m_regs[i.rs1]; u64 a = m_regs[i.rs1];
u64 b = m_regs[i.rs2]; u64 b = m_regs[i.rs2];
u64 a_lo = a & 0xffffffff; u64 a0 = a & 0xffffffff;
u64 a_hi = a >> 32; u64 a1 = a >> 32;
u64 b_lo = b & 0xffffffff; u64 b0 = b & 0xffffffff;
u64 b_hi = b >> 32; u64 b1 = b >> 32;
u64 lo_lo = a_lo * b_lo; u64 t = a0 * b0;
u64 hi_lo = a_hi * b_lo; u64 k = t >> 32;
u64 lo_hi = a_lo * b_hi; t = a1 * b0 + k;
u64 hi_hi = a_hi * b_hi; u64 w1 = t & 0xffffffff;
u64 mid = (lo_lo >> 32) + (hi_lo & 0xffffffff) + (lo_hi & 0xffffffff); u64 w2 = t >> 32;
m_regs[i.rd] = hi_hi + (hi_lo >> 32) + (lo_hi >> 32) + (mid >> 32); t = a0 * b1 + w1;
k = t >> 32;
m_regs[i.rd] = a1 * b1 + w2 + k;
}; break; }; break;
case Op::MULW: { case Op::MULW: {
m_regs[i.rd] = (i32)(m_regs[i.rs1] * m_regs[i.rs2]); m_regs[i.rd] = (i32)(m_regs[i.rs1] * m_regs[i.rs2]);
@@ -830,6 +1006,9 @@ public:
case Op::OR: { case Op::OR: {
m_regs[i.rd] = m_regs[i.rs1] | m_regs[i.rs2]; m_regs[i.rd] = m_regs[i.rs1] | m_regs[i.rs2];
}; break; }; break;
case Op::ORI: {
m_regs[i.rd] = m_regs[i.rs1] | (i64)i.imm;
}; break;
case Op::REM: { case Op::REM: {
if (m_regs[i.rs2] == 0) { if (m_regs[i.rs2] == 0) {
m_regs[i.rd] = m_regs[i.rs1]; m_regs[i.rd] = m_regs[i.rs1];
@@ -851,6 +1030,18 @@ public:
m_regs[i.rd] = (i32)((u32)m_regs[i.rs1] % (u32)m_regs[i.rs2]); m_regs[i.rd] = (i32)((u32)m_regs[i.rs1] % (u32)m_regs[i.rs2]);
} }
}; break; }; break;
case Op::REMW: {
i32 a = (i32)m_regs[i.rs1];
i32 b = (i32)m_regs[i.rs2];
if (b == 0) {
m_regs[i.rd] = (i64)a;
} else if (a == INT32_MIN && b == -1) {
m_regs[i.rd] = 0;
} else {
m_regs[i.rd] = (i64)(a % b);
}
}; break;
case Op::SB: { case Op::SB: {
u64 addr = m_regs[i.rs1] + i.imm; u64 addr = m_regs[i.rs1] + i.imm;
m_memory[addr] = m_regs[i.rs2]; m_memory[addr] = m_regs[i.rs2];
@@ -858,11 +1049,14 @@ public:
case Op::SD: case Op::SD:
case Op::C_SD: { case Op::C_SD: {
u64 addr = m_regs[i.rs1] + i.imm; u64 addr = m_regs[i.rs1] + i.imm;
*(u64 *)(&m_memory[addr]) = m_regs[i.rs2]; mem_write<u64>(addr, m_regs[i.rs2]);
}; break; }; break;
case Op::SH: { case Op::SH: {
u64 addr = m_regs[i.rs1] + i.imm; u64 addr = m_regs[i.rs1] + i.imm;
*(u16 *)(&m_memory[addr]) = m_regs[i.rs2]; mem_write<u16>(addr, m_regs[i.rs2]);
}; break;
case Op::SLL: {
m_regs[i.rd] = (u64)m_regs[i.rs1] << ((u64)m_regs[i.rs2] & 0b111111);
}; break; }; break;
case Op::SLLI: { case Op::SLLI: {
m_regs[i.rd] = m_regs[i.rs1] << i.shamt; m_regs[i.rd] = m_regs[i.rs1] << i.shamt;
@@ -911,7 +1105,7 @@ public:
case Op::C_SW: case Op::C_SW:
case Op::SW: { case Op::SW: {
u64 addr = m_regs[i.rs1] + i.imm; u64 addr = m_regs[i.rs1] + i.imm;
*(u32 *)(&m_memory[addr]) = m_regs[i.rs2]; mem_write<u32>(addr, m_regs[i.rs2]);
}; break; }; break;
case Op::XOR: { case Op::XOR: {
m_regs[i.rd] = m_regs[i.rs1] ^ m_regs[i.rs2]; m_regs[i.rd] = m_regs[i.rs1] ^ m_regs[i.rs2];
@@ -935,6 +1129,9 @@ private:
u64 m_pc; u64 m_pc;
std::array<i64, 32> m_regs{}; std::array<i64, 32> m_regs{};
Section m_code_section; Section m_code_section;
u64 m_brk;
u64 m_brk_base;
u64 m_next_mmap_addr;
static Section get_code_section(Elf *elf, GElf_Ehdr ehdr) { static Section get_code_section(Elf *elf, GElf_Ehdr ehdr) {
u64 str_table_index; u64 str_table_index;
@@ -984,15 +1181,21 @@ private:
switch (funct3) { switch (funct3) {
case 0b000: { case 0b000: {
if (raw == 0) { if (raw == 0) {
std::println(stderr, "C: illegal instruction (all zeros)"); std::println(stderr, "Illegal instruction at 0x{:x} (all zeros)",
exit(1); m_pc);
} } else {
i.rd = ((raw >> 2) & 0b111) + 8; i.rd = ((raw >> 2) & 0b111) + 8;
i.rs1 = 2; i.rs1 = 2;
i.imm = (((raw >> 11) & 0b11) << 4) | (((raw >> 7) & 0b1111) << 6) | i.imm = (((raw >> 11) & 0b11) << 4) | (((raw >> 7) & 0b1111) << 6) |
(((raw >> 6) & 0b1) << 2) | (((raw >> 5) & 0b1) << 3); (((raw >> 6) & 0b1) << 2) | (((raw >> 5) & 0b1) << 3);
i.op = Op::C_ADDI4SPN; i.op = Op::C_ADDI4SPN;
}
}; break;
case 0b001: {
i.rd = ((raw >> 2) & 0b111) + 8;
i.rs1 = ((raw >> 7) & 0b111) + 8;
i.imm = (((raw >> 10) & 0b111) << 3) | (((raw >> 5) & 0b11) << 6);
i.op = Op::C_FLD;
}; break; }; break;
case 0b010: { case 0b010: {
i.rd = ((raw >> 2) & 0b111) + 8; i.rd = ((raw >> 2) & 0b111) + 8;
@@ -1007,6 +1210,12 @@ private:
i.imm = (((raw >> 10) & 0b111) << 3) | (((raw >> 5) & 0b11) << 6); i.imm = (((raw >> 10) & 0b111) << 3) | (((raw >> 5) & 0b11) << 6);
i.op = Op::C_LD; i.op = Op::C_LD;
}; break; }; break;
case 0b101: {
i.rs2 = ((raw >> 2) & 0b111) + 8;
i.rs1 = ((raw >> 7) & 0b111) + 8;
i.imm = (((raw >> 10) & 0b111) << 3) | (((raw >> 5) & 0b11) << 6);
i.op = Op::C_FSD;
}; break;
case 0b110: { case 0b110: {
i.rs1 = ((raw >> 7) & 0b111) + 8; i.rs1 = ((raw >> 7) & 0b111) + 8;
i.rs2 = ((raw >> 2) & 0b111) + 8; i.rs2 = ((raw >> 2) & 0b111) + 8;
@@ -1176,6 +1385,12 @@ private:
(((raw >> 2) & 0b111) << 6); (((raw >> 2) & 0b111) << 6);
i.op = Op::C_FLDSP; i.op = Op::C_FLDSP;
}; break; }; break;
case 0b010: {
i.rs1 = 2;
i.imm = (((raw >> 2) & 0b11) << 6) | (((raw >> 12) & 0b1) << 5) |
(((raw >> 4) & 0b111) << 2);
i.op = Op::C_LWSP;
}; break;
case 0b011: { case 0b011: {
i.rs1 = 2; i.rs1 = 2;
i.imm = (((raw >> 12) & 0b1) << 5) | (((raw >> 5) & 0b11) << 3) | i.imm = (((raw >> 12) & 0b1) << 5) | (((raw >> 5) & 0b11) << 3) |
@@ -1212,6 +1427,12 @@ private:
} }
} }
}; break; }; break;
case 0b101: {
i.rs2 = (raw >> 2) & 0b11111;
i.rs1 = 2;
i.imm = (((raw >> 10) & 0b111) << 3) | (((raw >> 7) & 0b111) << 6);
i.op = Op::C_FSDSP;
}; break;
case 0b110: { case 0b110: {
i.rs2 = (raw >> 2) & 0b11111; i.rs2 = (raw >> 2) & 0b11111;
i.rs1 = 2; i.rs1 = 2;
@@ -1290,8 +1511,7 @@ private:
if (funct6 == 0b000000) { if (funct6 == 0b000000) {
i.op = Op::SLLI; i.op = Op::SLLI;
} else { } else {
std::println(stderr, std::println(stderr, "0010011: funct3=001: unrecognized funct6: {:b}",
"I-type 1: funct3=001: unrecognized funct6: {:b}",
funct6); funct6);
exit(1); exit(1);
} }
@@ -1310,8 +1530,7 @@ private:
} else if (funct6 == 0b010000) { } else if (funct6 == 0b010000) {
i.op = Op::SRAI; i.op = Op::SRAI;
} else { } else {
std::println(stderr, std::println(stderr, "0010011: funct3=101: unrecognized funct6: {:b}",
"I-type 1: funct3=101: unrecognized funct6: {:b}",
funct6); funct6);
exit(1); exit(1);
} }
@@ -1320,7 +1539,7 @@ private:
} else if (funct3 == 0b111) { } else if (funct3 == 0b111) {
i.op = Op::ANDI; i.op = Op::ANDI;
} else { } else {
std::println(stderr, "I-type 1: unrecognized funct3: {:03b}", funct3); std::println(stderr, "0010011: unrecognized funct3: {:03b}", funct3);
exit(1); exit(1);
} }
}; break; }; break;
@@ -1345,7 +1564,7 @@ private:
} else if (funct3 == 0b110) { } else if (funct3 == 0b110) {
i.op = Op::LWU; i.op = Op::LWU;
} else { } else {
std::println(stderr, "I-type 2: unrecognized funct3: {:03b}", funct3); std::println(stderr, "0000011: unrecognized funct3: {:03b}", funct3);
exit(1); exit(1);
} }
}; break; }; break;
@@ -1359,18 +1578,26 @@ private:
u8 funct3 = (raw >> 12) & 0b111; u8 funct3 = (raw >> 12) & 0b111;
i.rd = (raw >> 7) & 0b11111; i.rd = (raw >> 7) & 0b11111;
i.rs1 = (raw >> 15) & 0b11111; i.rs1 = (raw >> 15) & 0b11111;
i.imm = ((i32)raw) >> 20; i.imm = (raw >> 20) & 0b111111111111;
if (funct3 == 0b000) { switch (funct3) {
if (i.imm == 0b000000000000) { case 0b000: {
if (i.imm == 0) {
i.op = Op::ECALL; i.op = Op::ECALL;
} else { } else {
std::println(stderr, "I-type 4: funct3=000 unrecognized imm: {:b}", std::println(stderr, "1110011: funct3=000: unrecognized imm: {:b}",
i.imm); i.imm);
exit(1); exit(1);
} }
} else { }; break;
std::println(stderr, "I-type 4: unrecognized funct3: {:03b}", funct3); case 0b010: {
i.op = Op::CSRRS;
}; break;
case 0b110: {
i.op = Op::CSRRSI;
}; break;
default:
std::println(stderr, "1110011: unrecognized funct3: {:03b}", funct3);
exit(1); exit(1);
} }
}; break; }; break;
@@ -1496,12 +1723,24 @@ private:
if (funct3 == 0b010) { if (funct3 == 0b010) {
switch (funct7) { switch (funct7) {
case 0b00000: {
i.op = Op::AMOADD_W;
}; break;
case 0b00001: {
i.op = Op::AMOSWAP_W;
}; break;
case 0b00010: { case 0b00010: {
i.op = Op::LR_W; i.op = Op::LR_W;
}; break; }; break;
case 0b00011: { case 0b00011: {
i.op = Op::SC_W; i.op = Op::SC_W;
}; break; }; break;
case 0b01000: {
i.op = Op::AMOOR_W;
}; break;
case 0b11100: {
i.op = Op::AMOMAXU_W;
}; break;
default: { default: {
std::println(stderr, std::println(stderr,
"0101111: funct3=010: unrecognized funct7: {:05b}", "0101111: funct3=010: unrecognized funct7: {:05b}",
@@ -1511,6 +1750,9 @@ private:
} }
} else if (funct3 == 0b011) { } else if (funct3 == 0b011) {
switch (funct7) { switch (funct7) {
case 0b00000: {
i.op = Op::AMOADD_D;
}; break;
case 0b00001: { case 0b00001: {
i.op = Op::AMOSWAP_D; i.op = Op::AMOSWAP_D;
}; break; }; break;
@@ -1520,6 +1762,9 @@ private:
case 0b00011: { case 0b00011: {
i.op = Op::SC_D; i.op = Op::SC_D;
}; break; }; break;
case 0b11100: {
i.op = Op::AMOMAXU_D;
}; break;
default: { default: {
std::println(stderr, std::println(stderr,
"0101111: funct3=011: unrecognized funct7: {:05b}", "0101111: funct3=011: unrecognized funct7: {:05b}",
@@ -1607,13 +1852,141 @@ private:
} }
}; break; }; break;
case 0b0000111: { case 0b0000111: {
std::println(stderr, "F extension not implemented yet."); u8 funct3 = (raw >> 12) & 0b111;
switch (funct3) {
case 0b010: {
i.rd = (raw >> 7) & 0b11111;
i.rs1 = (raw >> 15) & 0b11111;
i.imm = (i32)raw >> 20;
i.op = Op::FLW;
}; break;
case 0b011: {
i.rd = (raw >> 7) & 0b11111;
i.rs1 = (raw >> 15) & 0b11111;
i.imm = (i32)raw >> 20;
i.op = Op::FLD;
}; break;
default:
std::println(stderr, "0000111: unrecognized funct3: {:03b}", funct3);
exit(1);
}
}; break;
case 0b1010011: {
u8 funct3 = (raw >> 12) & 0b111;
u8 funct7 = (raw >> 25) & 0b1111111;
i.rs1 = (raw >> 15) & 0b11111;
i.rs2 = (raw >> 20) & 0b11111;
switch (funct7) {
case 0b0000001: {
i.op = Op::FADD_D;
}; break;
case 0b0010000: {
switch (funct3) {
case 0b000:
i.op = Op::FSGNJ_S;
break;
case 0b001:
i.op = Op::FSGNJN_S;
break;
case 0b010:
i.op = Op::FSGNJX_S;
break;
default:
std::println(stderr,
"1010011: funct7=0010000: unrecognized funct3: {:03b}",
funct3);
exit(1);
}
}; break;
case 0b1111001: {
i.rs1 = (raw >> 15) & 0b11111;
i.op = Op::FMV_D_X;
}; break;
case 0b1101001: {
if (i.rs2 == 0b00000) {
i.op = Op::FCVT_D_W;
} else if (i.rs2 == 0b00001) {
i.op = Op::FCVT_D_WU;
} else {
std::println(
stderr, "1010011: funct7=1101001: unrecognized rs2: {:b}", i.rs2);
exit(1);
}
}; break;
case 0b0001001: {
i.op = Op::FMUL_D;
}; break;
case 0b0010001: {
switch (funct3) {
case 0b000:
i.op = Op::FSGNJ_D;
break;
case 0b001:
i.op = Op::FSGNJN_D;
break;
case 0b010:
i.op = Op::FSGNJX_D;
break;
default:
std::println(stderr,
"1010011: funct7=0010001: unrecognized funct3: {:03b}",
funct3);
exit(1);
}
}; break;
case 0b1110001: {
switch (funct3) {
case 0b000: {
i.op = Op::FMV_X_D;
}; break;
case 0b001: {
i.op = Op::FCLASS_D;
}; break;
default: {
std::println(stderr,
"1010011: funct7=1110001: unrecognized funct3: {:03b}",
funct3);
exit(1); exit(1);
}; break; }; break;
case 0b0100111: { }
std::println(stderr, "F extension not implemented yet."); }; break;
case 0b1111000: {
i.op = Op::FMV_W_X;
}; break;
default: {
std::println(stderr, "1010011: unrecognized funct7: {:07b}", funct7);
exit(1); exit(1);
}; break; }; break;
}
}; break;
case 0b0100111: {
u8 funct3 = (raw >> 12) & 0b111;
switch (funct3) {
case 0b010: {
i.rs1 = (raw >> 15) & 0b11111;
i.rs2 = (raw >> 20) & 0b11111;
i32 imm_hi = (i32)raw >> 25;
i32 imm_lo = (raw >> 7) & 0b11111;
i.imm = (imm_hi << 5) | imm_lo;
i.op = Op::FSW;
}; break;
case 0b011: {
i.rs1 = (raw >> 15) & 0b11111;
i.rs2 = (raw >> 20) & 0b11111;
i32 imm_hi = (i32)raw >> 25;
i32 imm_lo = (raw >> 7) & 0b11111;
i.imm = (imm_hi << 5) | imm_lo;
i.op = Op::FSD;
}; break;
default: {
std::println(stderr, "0100111: unrecognized funct3: {:03b}", funct3);
exit(1);
}; break;
}
}; break;
case 0b0100011: { case 0b0100011: {
u8 funct3 = (raw >> 12) & 0b111; u8 funct3 = (raw >> 12) & 0b111;
i.rs1 = (raw >> 15) & 0b11111; i.rs1 = (raw >> 15) & 0b11111;
@@ -1670,6 +2043,15 @@ private:
return i; return i;
} }
template <typename T> T mem_read(u64 addr) {
T v;
std::memcpy(&v, m_memory + addr, sizeof(T));
return v;
}
template <typename T> void mem_write(u64 addr, T v) {
std::memcpy(m_memory + addr, &v, sizeof(T));
}
}; };
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {