Compare commits
3 Commits
4c7073657f
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 93da83704f | |||
| 2d53a59399 | |||
| 7a893076fb |
6
LICENSE
6
LICENSE
@@ -1,4 +1,4 @@
|
||||
BSD 3-Clause License
|
||||
BSD 2-Clause License
|
||||
|
||||
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
|
||||
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"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
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)
|
||||
* `mos6502.c` - [MOS 6502](https://en.wikipedia.org/wiki/MOS_Technology_6502)
|
||||
|
||||
|
||||
2
chip8.c
2
chip8.c
@@ -345,7 +345,7 @@ void chip8_step(CHIP8 *c) {
|
||||
break;
|
||||
case 0x0A: {
|
||||
uint8_t key_pressed = 0;
|
||||
while (!key_pressed && WindowShouldClose()) {
|
||||
while (!key_pressed && !WindowShouldClose()) {
|
||||
for (size_t i = 0; i < 16; i++) {
|
||||
if (IsKeyDown(keyboard_map[i])) {
|
||||
key_pressed = 1;
|
||||
|
||||
532
riscv64.cc
532
riscv64.cc
@@ -1,5 +1,6 @@
|
||||
// https://docs.riscv.org/reference/isa/unpriv/rv-32-64g.html
|
||||
// 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
|
||||
#error "C++23 or later is required. Either get a newer compiler " \
|
||||
@@ -7,11 +8,11 @@
|
||||
"instead of <print> and remove this check."
|
||||
#endif
|
||||
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
#include <fstream>
|
||||
#include <gelf.h>
|
||||
#include <iostream>
|
||||
#include <libelf.h>
|
||||
#include <print>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/time.h>
|
||||
@@ -46,7 +47,13 @@ enum Op {
|
||||
ADDI,
|
||||
ADDIW,
|
||||
ADDW,
|
||||
AMOADD_D,
|
||||
AMOADD_W,
|
||||
AMOMAXU_D,
|
||||
AMOMAXU_W,
|
||||
AMOOR_W,
|
||||
AMOSWAP_D,
|
||||
AMOSWAP_W,
|
||||
AND,
|
||||
ANDI,
|
||||
AUIPC,
|
||||
@@ -67,7 +74,10 @@ enum Op {
|
||||
C_BEQZ,
|
||||
C_BNEZ,
|
||||
C_EBREAK,
|
||||
C_FLD,
|
||||
C_FLDSP,
|
||||
C_FSD,
|
||||
C_FSDSP,
|
||||
C_J,
|
||||
C_JALR,
|
||||
C_JR,
|
||||
@@ -76,6 +86,7 @@ enum Op {
|
||||
C_LI,
|
||||
C_LUI,
|
||||
C_LW,
|
||||
C_LWSP,
|
||||
C_MV,
|
||||
C_OR,
|
||||
C_SD,
|
||||
@@ -88,13 +99,33 @@ enum Op {
|
||||
C_SW,
|
||||
C_SWSP,
|
||||
C_XOR,
|
||||
CSRRS,
|
||||
CSRRSI,
|
||||
DIV,
|
||||
DIVU,
|
||||
DIVUW,
|
||||
DIVW,
|
||||
ECALL,
|
||||
FADD_D,
|
||||
FCLASS_D,
|
||||
FCVT_D_W,
|
||||
FCVT_D_WU,
|
||||
FENCE,
|
||||
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,
|
||||
JALR,
|
||||
LB,
|
||||
@@ -144,6 +175,8 @@ enum Op {
|
||||
SW,
|
||||
XOR,
|
||||
XORI,
|
||||
|
||||
NUM_OPS
|
||||
};
|
||||
|
||||
struct Ins {
|
||||
@@ -183,7 +216,9 @@ enum class Format {
|
||||
CR1,
|
||||
CL,
|
||||
R_ATOMIC,
|
||||
R_ATOMIC_LR
|
||||
R_ATOMIC_LR,
|
||||
CSR,
|
||||
CSRI
|
||||
};
|
||||
|
||||
struct OpDef {
|
||||
@@ -191,13 +226,19 @@ struct OpDef {
|
||||
Format format;
|
||||
};
|
||||
|
||||
static constexpr std::array<OpDef, 103> OP_TABLE = {{
|
||||
static constexpr auto OP_TABLE = std::to_array<OpDef>({
|
||||
{"???", Format::NONE},
|
||||
{"add", Format::R},
|
||||
{"addi", Format::I},
|
||||
{"addiw", Format::I},
|
||||
{"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.w", Format::R_ATOMIC},
|
||||
{"and", Format::R},
|
||||
{"andi", Format::I},
|
||||
{"auipc", Format::U},
|
||||
@@ -218,7 +259,10 @@ static constexpr std::array<OpDef, 103> OP_TABLE = {{
|
||||
{"c.beqz", Format::CB},
|
||||
{"c.bnez", Format::CB},
|
||||
{"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.jalr", Format::CR1},
|
||||
{"c.jr", Format::CR1},
|
||||
@@ -227,6 +271,7 @@ static constexpr std::array<OpDef, 103> OP_TABLE = {{
|
||||
{"c.li", Format::CI},
|
||||
{"c.lui", Format::CI},
|
||||
{"c.lw", Format::CL},
|
||||
{"c.lwsp", Format::CL},
|
||||
{"c.mv", Format::CR},
|
||||
{"c.or", Format::CR},
|
||||
{"c.sd", Format::S},
|
||||
@@ -239,13 +284,33 @@ static constexpr std::array<OpDef, 103> OP_TABLE = {{
|
||||
{"c.sw", Format::S},
|
||||
{"c.swsp", Format::CSS},
|
||||
{"c.xor", Format::CR},
|
||||
{"csrrs", Format::CSR},
|
||||
{"csrrsi", Format::CSRI},
|
||||
{"div", Format::R},
|
||||
{"divu", Format::R},
|
||||
{"divuw", Format::R},
|
||||
{"divw", Format::R},
|
||||
{"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.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},
|
||||
{"jalr", Format::I},
|
||||
{"lb", Format::I_LOAD},
|
||||
@@ -295,7 +360,9 @@ static constexpr std::array<OpDef, 103> OP_TABLE = {{
|
||||
{"sw", Format::S},
|
||||
{"xor", Format::R},
|
||||
{"xori", Format::I},
|
||||
}};
|
||||
});
|
||||
|
||||
static_assert(OP_TABLE.size() == NUM_OPS, "len(OP_TABLE) != len(Op::*)");
|
||||
|
||||
class RISCV64 {
|
||||
public:
|
||||
@@ -320,16 +387,22 @@ public:
|
||||
m_code_section = get_code_section(elf, ehdr);
|
||||
m_pc = m_code_section.offset;
|
||||
|
||||
u64 max_addr = 0;
|
||||
for (u64 i = 0; i < ehdr.e_phnum; i++) {
|
||||
GElf_Phdr phdr;
|
||||
gelf_getphdr(elf, i, &phdr);
|
||||
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,
|
||||
m_memory + phdr.p_vaddr);
|
||||
max_addr = std::max(max_addr, phdr.p_vaddr + phdr.p_memsz);
|
||||
}
|
||||
}
|
||||
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;
|
||||
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) {
|
||||
assert((u64)ins.op < OP_TABLE.size());
|
||||
|
||||
const OpDef &def = OP_TABLE[ins.op];
|
||||
|
||||
switch (def.format) {
|
||||
case Format::NONE:
|
||||
std::println("{}", def.mnemonic);
|
||||
break;
|
||||
case Format::R:
|
||||
std::println("{} {}, {}, {}", def.mnemonic, REGS[ins.rd], REGS[ins.rs1],
|
||||
REGS[ins.rs2]);
|
||||
@@ -421,23 +502,27 @@ public:
|
||||
std::println("{} {}, {}, ({})", def.mnemonic, REGS[ins.rd], REGS[ins.rs2],
|
||||
REGS[ins.rs1]);
|
||||
break;
|
||||
case Format::NONE:
|
||||
std::println("{}", def.mnemonic);
|
||||
case Format::CSR:
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
void dump() {
|
||||
std::print("REGS:");
|
||||
for (u64 i = 0; i < 32; i++) {
|
||||
std::print(" {}", m_regs[i]);
|
||||
std::print("{}=0x{:x} ", REGS[i], m_regs[i]);
|
||||
}
|
||||
std::println();
|
||||
}
|
||||
|
||||
void push_u64(u64 x) {
|
||||
void push_u64(u64 v) {
|
||||
m_regs[2] -= 8;
|
||||
*(u64 *)(m_memory + m_regs[2]) = x;
|
||||
mem_write<u64>(m_regs[2], v);
|
||||
}
|
||||
|
||||
void execute() {
|
||||
@@ -495,7 +580,7 @@ public:
|
||||
m_regs[i.rd] = m_regs[i.rs1] & i.imm;
|
||||
}; break;
|
||||
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;
|
||||
case Op::BEQ: {
|
||||
if (m_regs[i.rs1] == m_regs[i.rs2]) {
|
||||
@@ -569,36 +654,41 @@ public:
|
||||
continue;
|
||||
}
|
||||
}; break;
|
||||
case Op::C_EBREAK: {
|
||||
std::println(stderr, "EBREAK at pc=0x{:x}", m_pc);
|
||||
dump();
|
||||
exit(1);
|
||||
}; break;
|
||||
case Op::C_J: {
|
||||
m_pc += i.imm;
|
||||
continue;
|
||||
}; break;
|
||||
case Op::C_JALR: {
|
||||
m_regs[1] = m_pc + 2;
|
||||
m_pc = m_regs[i.rs1];
|
||||
m_pc = m_regs[i.rs1] & ~1ULL;
|
||||
continue;
|
||||
}; break;
|
||||
case Op::C_JR: {
|
||||
m_pc = m_regs[i.rs1];
|
||||
m_pc = m_regs[i.rs1] & ~1ULL;
|
||||
continue;
|
||||
}; break;
|
||||
case Op::C_LD: {
|
||||
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;
|
||||
case Op::C_LDSP: {
|
||||
u64 addr = sp + i.imm;
|
||||
m_regs[i.rd] = *(u64 *)(&m_memory[addr]);
|
||||
m_regs[i.rd] = mem_read<u64>(addr);
|
||||
}; break;
|
||||
case Op::C_LI: {
|
||||
m_regs[i.rd] = i.imm;
|
||||
}; break;
|
||||
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;
|
||||
case Op::C_LW: {
|
||||
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;
|
||||
case Op::C_MV: {
|
||||
m_regs[i.rd] = m_regs[i.rs2];
|
||||
@@ -608,7 +698,7 @@ public:
|
||||
}; break;
|
||||
case Op::C_SDSP: {
|
||||
u64 addr = sp + i.imm;
|
||||
*(u64 *)(&m_memory[addr]) = m_regs[i.rs2];
|
||||
mem_write<u64>(addr, m_regs[i.rs2]);
|
||||
}; break;
|
||||
case Op::C_SLLI: {
|
||||
m_regs[i.rd] = (i64)((u64)m_regs[i.rd] << i.imm);
|
||||
@@ -622,6 +712,20 @@ public:
|
||||
case Op::C_SUB: {
|
||||
m_regs[i.rd] -= m_regs[i.rs2];
|
||||
}; 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: {
|
||||
if (m_regs[i.rs2] == 0) {
|
||||
m_regs[i.rd] = -1;
|
||||
@@ -629,9 +733,16 @@ public:
|
||||
m_regs[i.rd] = m_regs[i.rs1] / m_regs[i.rs2];
|
||||
}
|
||||
}; 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: {
|
||||
if (m_regs[i.rs2] == 0) {
|
||||
m_regs[i.rd] = -1LL;
|
||||
if ((u32)m_regs[i.rs2] == 0) {
|
||||
m_regs[i.rd] = -1;
|
||||
} else {
|
||||
m_regs[i.rd] = (i32)((u32)m_regs[i.rs1] / (u32)m_regs[i.rs2]);
|
||||
}
|
||||
@@ -649,6 +760,7 @@ public:
|
||||
}; break;
|
||||
case Op::ECALL: {
|
||||
// https://jborza.com/post/2021-05-11-riscv-linux-syscalls/
|
||||
// ^ already got 2 syscalls wrong
|
||||
switch (m_regs[17]) {
|
||||
case 29: { // ioctl
|
||||
u32 fd = m_regs[10];
|
||||
@@ -666,13 +778,26 @@ public:
|
||||
}; 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
|
||||
u32 fd = m_regs[10];
|
||||
u64 buf = m_regs[11];
|
||||
u64 count = m_regs[12];
|
||||
|
||||
if (fd != 0) {
|
||||
std::println(stderr, "read syscall implemented only for stdin.");
|
||||
std::println(stderr, "read syscall implemented only for stdin");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
@@ -719,8 +844,8 @@ public:
|
||||
u64 total_written = 0;
|
||||
for (u64 i = 0; i < vlen; i++) {
|
||||
u64 iov_entry = vec + i * 16;
|
||||
u64 buf = *(u64 *)&m_memory[iov_entry];
|
||||
u64 len = *(u64 *)&m_memory[iov_entry + 8];
|
||||
u64 buf = mem_read<u64>(iov_entry);
|
||||
u64 len = mem_read<u64>(iov_entry + 8);
|
||||
|
||||
for (u64 j = 0; j < len; j++) {
|
||||
std::cout.put(m_memory[buf + j]);
|
||||
@@ -760,6 +885,39 @@ public:
|
||||
m_regs[10] = -errno;
|
||||
}
|
||||
}; 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:
|
||||
std::println(stderr, "Unimplemented syscall: {}", m_regs[17]);
|
||||
exit(1);
|
||||
@@ -783,46 +941,64 @@ public:
|
||||
m_regs[i.rd] = m_memory[m_regs[i.rs1] + i.imm];
|
||||
}; break;
|
||||
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;
|
||||
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;
|
||||
case Op::LUI: {
|
||||
m_regs[i.rd] = (i64)(i32)(i.imm << 12);
|
||||
m_regs[i.rd] = (i64)(i32)((u32)i.imm << 12);
|
||||
}; break;
|
||||
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;
|
||||
case Op::MUL: {
|
||||
m_regs[i.rd] = m_regs[i.rs1] * m_regs[i.rs2];
|
||||
}; break;
|
||||
case Op::MULH: {
|
||||
i64 a_hi = m_regs[i.rs1] >> 32;
|
||||
i64 b_hi = m_regs[i.rs2] >> 32;
|
||||
u64 a_lo = (u32)m_regs[i.rs1];
|
||||
u64 b_lo = (u32)m_regs[i.rs2];
|
||||
u64 p0 = a_lo * b_lo;
|
||||
i64 p1 = a_hi * b_lo;
|
||||
i64 p2 = b_hi * a_lo;
|
||||
i64 p3 = a_hi * b_hi;
|
||||
i64 carry = (p0 >> 32);
|
||||
i64 mid = p1 + p2 + carry;
|
||||
m_regs[i.rd] = p3 + (mid >> 32);
|
||||
i64 rs1 = m_regs[i.rs1];
|
||||
i64 rs2 = m_regs[i.rs2];
|
||||
u64 u = rs1;
|
||||
u64 v = rs2;
|
||||
u64 u0 = u & 0xffffffff;
|
||||
u64 u1 = u >> 32;
|
||||
u64 v0 = v & 0xffffffff;
|
||||
u64 v1 = v >> 32;
|
||||
u64 t = u0 * v0;
|
||||
u64 k = t >> 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;
|
||||
case Op::MULHU: {
|
||||
u64 a = m_regs[i.rs1];
|
||||
u64 b = m_regs[i.rs2];
|
||||
u64 a_lo = a & 0xffffffff;
|
||||
u64 a_hi = a >> 32;
|
||||
u64 b_lo = b & 0xffffffff;
|
||||
u64 b_hi = b >> 32;
|
||||
u64 lo_lo = a_lo * b_lo;
|
||||
u64 hi_lo = a_hi * b_lo;
|
||||
u64 lo_hi = a_lo * b_hi;
|
||||
u64 hi_hi = a_hi * b_hi;
|
||||
u64 mid = (lo_lo >> 32) + (hi_lo & 0xffffffff) + (lo_hi & 0xffffffff);
|
||||
m_regs[i.rd] = hi_hi + (hi_lo >> 32) + (lo_hi >> 32) + (mid >> 32);
|
||||
u64 a0 = a & 0xffffffff;
|
||||
u64 a1 = a >> 32;
|
||||
u64 b0 = b & 0xffffffff;
|
||||
u64 b1 = b >> 32;
|
||||
u64 t = a0 * b0;
|
||||
u64 k = t >> 32;
|
||||
t = a1 * b0 + k;
|
||||
u64 w1 = t & 0xffffffff;
|
||||
u64 w2 = t >> 32;
|
||||
t = a0 * b1 + w1;
|
||||
k = t >> 32;
|
||||
m_regs[i.rd] = a1 * b1 + w2 + k;
|
||||
}; break;
|
||||
case Op::MULW: {
|
||||
m_regs[i.rd] = (i32)(m_regs[i.rs1] * m_regs[i.rs2]);
|
||||
@@ -830,6 +1006,9 @@ public:
|
||||
case Op::OR: {
|
||||
m_regs[i.rd] = m_regs[i.rs1] | m_regs[i.rs2];
|
||||
}; break;
|
||||
case Op::ORI: {
|
||||
m_regs[i.rd] = m_regs[i.rs1] | (i64)i.imm;
|
||||
}; break;
|
||||
case Op::REM: {
|
||||
if (m_regs[i.rs2] == 0) {
|
||||
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]);
|
||||
}
|
||||
}; 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: {
|
||||
u64 addr = m_regs[i.rs1] + i.imm;
|
||||
m_memory[addr] = m_regs[i.rs2];
|
||||
@@ -858,11 +1049,14 @@ public:
|
||||
case Op::SD:
|
||||
case Op::C_SD: {
|
||||
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;
|
||||
case Op::SH: {
|
||||
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;
|
||||
case Op::SLLI: {
|
||||
m_regs[i.rd] = m_regs[i.rs1] << i.shamt;
|
||||
@@ -911,7 +1105,7 @@ public:
|
||||
case Op::C_SW:
|
||||
case Op::SW: {
|
||||
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;
|
||||
case Op::XOR: {
|
||||
m_regs[i.rd] = m_regs[i.rs1] ^ m_regs[i.rs2];
|
||||
@@ -935,6 +1129,9 @@ private:
|
||||
u64 m_pc;
|
||||
std::array<i64, 32> m_regs{};
|
||||
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) {
|
||||
u64 str_table_index;
|
||||
@@ -984,15 +1181,21 @@ private:
|
||||
switch (funct3) {
|
||||
case 0b000: {
|
||||
if (raw == 0) {
|
||||
std::println(stderr, "C: illegal instruction (all zeros)");
|
||||
exit(1);
|
||||
std::println(stderr, "Illegal instruction at 0x{:x} (all zeros)",
|
||||
m_pc);
|
||||
} else {
|
||||
i.rd = ((raw >> 2) & 0b111) + 8;
|
||||
i.rs1 = 2;
|
||||
i.imm = (((raw >> 11) & 0b11) << 4) | (((raw >> 7) & 0b1111) << 6) |
|
||||
(((raw >> 6) & 0b1) << 2) | (((raw >> 5) & 0b1) << 3);
|
||||
i.op = Op::C_ADDI4SPN;
|
||||
}
|
||||
|
||||
}; break;
|
||||
case 0b001: {
|
||||
i.rd = ((raw >> 2) & 0b111) + 8;
|
||||
i.rs1 = 2;
|
||||
i.imm = (((raw >> 11) & 0b11) << 4) | (((raw >> 7) & 0b1111) << 6) |
|
||||
(((raw >> 6) & 0b1) << 2) | (((raw >> 5) & 0b1) << 3);
|
||||
i.op = Op::C_ADDI4SPN;
|
||||
i.rs1 = ((raw >> 7) & 0b111) + 8;
|
||||
i.imm = (((raw >> 10) & 0b111) << 3) | (((raw >> 5) & 0b11) << 6);
|
||||
i.op = Op::C_FLD;
|
||||
}; break;
|
||||
case 0b010: {
|
||||
i.rd = ((raw >> 2) & 0b111) + 8;
|
||||
@@ -1007,6 +1210,12 @@ private:
|
||||
i.imm = (((raw >> 10) & 0b111) << 3) | (((raw >> 5) & 0b11) << 6);
|
||||
i.op = Op::C_LD;
|
||||
}; 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: {
|
||||
i.rs1 = ((raw >> 7) & 0b111) + 8;
|
||||
i.rs2 = ((raw >> 2) & 0b111) + 8;
|
||||
@@ -1176,6 +1385,12 @@ private:
|
||||
(((raw >> 2) & 0b111) << 6);
|
||||
i.op = Op::C_FLDSP;
|
||||
}; 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: {
|
||||
i.rs1 = 2;
|
||||
i.imm = (((raw >> 12) & 0b1) << 5) | (((raw >> 5) & 0b11) << 3) |
|
||||
@@ -1212,6 +1427,12 @@ private:
|
||||
}
|
||||
}
|
||||
}; 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: {
|
||||
i.rs2 = (raw >> 2) & 0b11111;
|
||||
i.rs1 = 2;
|
||||
@@ -1290,8 +1511,7 @@ private:
|
||||
if (funct6 == 0b000000) {
|
||||
i.op = Op::SLLI;
|
||||
} else {
|
||||
std::println(stderr,
|
||||
"I-type 1: funct3=001: unrecognized funct6: {:b}",
|
||||
std::println(stderr, "0010011: funct3=001: unrecognized funct6: {:b}",
|
||||
funct6);
|
||||
exit(1);
|
||||
}
|
||||
@@ -1310,8 +1530,7 @@ private:
|
||||
} else if (funct6 == 0b010000) {
|
||||
i.op = Op::SRAI;
|
||||
} else {
|
||||
std::println(stderr,
|
||||
"I-type 1: funct3=101: unrecognized funct6: {:b}",
|
||||
std::println(stderr, "0010011: funct3=101: unrecognized funct6: {:b}",
|
||||
funct6);
|
||||
exit(1);
|
||||
}
|
||||
@@ -1320,7 +1539,7 @@ private:
|
||||
} else if (funct3 == 0b111) {
|
||||
i.op = Op::ANDI;
|
||||
} else {
|
||||
std::println(stderr, "I-type 1: unrecognized funct3: {:03b}", funct3);
|
||||
std::println(stderr, "0010011: unrecognized funct3: {:03b}", funct3);
|
||||
exit(1);
|
||||
}
|
||||
}; break;
|
||||
@@ -1345,7 +1564,7 @@ private:
|
||||
} else if (funct3 == 0b110) {
|
||||
i.op = Op::LWU;
|
||||
} else {
|
||||
std::println(stderr, "I-type 2: unrecognized funct3: {:03b}", funct3);
|
||||
std::println(stderr, "0000011: unrecognized funct3: {:03b}", funct3);
|
||||
exit(1);
|
||||
}
|
||||
}; break;
|
||||
@@ -1359,18 +1578,26 @@ private:
|
||||
u8 funct3 = (raw >> 12) & 0b111;
|
||||
i.rd = (raw >> 7) & 0b11111;
|
||||
i.rs1 = (raw >> 15) & 0b11111;
|
||||
i.imm = ((i32)raw) >> 20;
|
||||
i.imm = (raw >> 20) & 0b111111111111;
|
||||
|
||||
if (funct3 == 0b000) {
|
||||
if (i.imm == 0b000000000000) {
|
||||
switch (funct3) {
|
||||
case 0b000: {
|
||||
if (i.imm == 0) {
|
||||
i.op = Op::ECALL;
|
||||
} else {
|
||||
std::println(stderr, "I-type 4: funct3=000 unrecognized imm: {:b}",
|
||||
std::println(stderr, "1110011: funct3=000: unrecognized imm: {:b}",
|
||||
i.imm);
|
||||
exit(1);
|
||||
}
|
||||
} else {
|
||||
std::println(stderr, "I-type 4: unrecognized funct3: {:03b}", funct3);
|
||||
}; break;
|
||||
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);
|
||||
}
|
||||
}; break;
|
||||
@@ -1496,12 +1723,24 @@ private:
|
||||
|
||||
if (funct3 == 0b010) {
|
||||
switch (funct7) {
|
||||
case 0b00000: {
|
||||
i.op = Op::AMOADD_W;
|
||||
}; break;
|
||||
case 0b00001: {
|
||||
i.op = Op::AMOSWAP_W;
|
||||
}; break;
|
||||
case 0b00010: {
|
||||
i.op = Op::LR_W;
|
||||
}; break;
|
||||
case 0b00011: {
|
||||
i.op = Op::SC_W;
|
||||
}; break;
|
||||
case 0b01000: {
|
||||
i.op = Op::AMOOR_W;
|
||||
}; break;
|
||||
case 0b11100: {
|
||||
i.op = Op::AMOMAXU_W;
|
||||
}; break;
|
||||
default: {
|
||||
std::println(stderr,
|
||||
"0101111: funct3=010: unrecognized funct7: {:05b}",
|
||||
@@ -1511,6 +1750,9 @@ private:
|
||||
}
|
||||
} else if (funct3 == 0b011) {
|
||||
switch (funct7) {
|
||||
case 0b00000: {
|
||||
i.op = Op::AMOADD_D;
|
||||
}; break;
|
||||
case 0b00001: {
|
||||
i.op = Op::AMOSWAP_D;
|
||||
}; break;
|
||||
@@ -1520,6 +1762,9 @@ private:
|
||||
case 0b00011: {
|
||||
i.op = Op::SC_D;
|
||||
}; break;
|
||||
case 0b11100: {
|
||||
i.op = Op::AMOMAXU_D;
|
||||
}; break;
|
||||
default: {
|
||||
std::println(stderr,
|
||||
"0101111: funct3=011: unrecognized funct7: {:05b}",
|
||||
@@ -1607,12 +1852,140 @@ private:
|
||||
}
|
||||
}; break;
|
||||
case 0b0000111: {
|
||||
std::println(stderr, "F extension not implemented yet.");
|
||||
exit(1);
|
||||
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);
|
||||
}; break;
|
||||
}
|
||||
}; break;
|
||||
case 0b1111000: {
|
||||
i.op = Op::FMV_W_X;
|
||||
}; break;
|
||||
default: {
|
||||
std::println(stderr, "1010011: unrecognized funct7: {:07b}", funct7);
|
||||
exit(1);
|
||||
}; break;
|
||||
}
|
||||
}; break;
|
||||
case 0b0100111: {
|
||||
std::println(stderr, "F extension not implemented yet.");
|
||||
exit(1);
|
||||
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: {
|
||||
u8 funct3 = (raw >> 12) & 0b111;
|
||||
@@ -1670,6 +2043,15 @@ private:
|
||||
|
||||
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[]) {
|
||||
|
||||
Reference in New Issue
Block a user