[UNTESTED] refactor how instructions are parsed
Signed-off-by: Amneesh Singh <natto@weirdnatto.in>
This commit is contained in:
@@ -6,6 +6,7 @@
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <unistd.h>
|
||||
#include <vector>
|
||||
|
||||
// NOLINTBEGIN
|
||||
@@ -83,7 +84,10 @@ main(int argc, const char* argv[]) {
|
||||
Memory memory(std::move(bios), std::move(rom));
|
||||
Bus bus(memory);
|
||||
Cpu cpu(bus);
|
||||
while (true) {
|
||||
cpu.step();
|
||||
sleep(1);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
1
include/cpu/instruction.hh
Symbolic link
1
include/cpu/instruction.hh
Symbolic link
@@ -0,0 +1 @@
|
||||
../../src/cpu/instruction.hh
|
@@ -1,5 +1,6 @@
|
||||
headers += files(
|
||||
'cpu.hh',
|
||||
'instruction.hh',
|
||||
'psr.hh',
|
||||
'utility.hh'
|
||||
)
|
@@ -1,79 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <fmt/ostream.h>
|
||||
#include <ostream>
|
||||
|
||||
static constexpr size_t ARM_INSTRUCTION_SIZE = 4;
|
||||
static constexpr size_t THUMB_INSTRUCTION_SIZE = 2;
|
||||
|
||||
enum class Mode {
|
||||
/* M[4:0] in PSR */
|
||||
User = 0b10000,
|
||||
Fiq = 0b10001,
|
||||
Irq = 0b10010,
|
||||
Supervisor = 0b10011,
|
||||
Abort = 0b10111,
|
||||
Undefined = 0b11011,
|
||||
System = 0b11111,
|
||||
};
|
||||
|
||||
enum class State {
|
||||
Arm = 0,
|
||||
Thumb = 1
|
||||
};
|
||||
|
||||
enum class Condition {
|
||||
EQ = 0b0000,
|
||||
NE = 0b0001,
|
||||
CS = 0b0010,
|
||||
CC = 0b0011,
|
||||
MI = 0b0100,
|
||||
PL = 0b0101,
|
||||
VS = 0b0110,
|
||||
VC = 0b0111,
|
||||
HI = 0b1000,
|
||||
LS = 0b1001,
|
||||
GE = 0b1010,
|
||||
LT = 0b1011,
|
||||
GT = 0b1100,
|
||||
LE = 0b1101,
|
||||
AL = 0b1110
|
||||
};
|
||||
|
||||
// https://fmt.dev/dev/api.html#std-ostream-support
|
||||
std::ostream&
|
||||
operator<<(std::ostream& os, const Condition cond);
|
||||
template<>
|
||||
struct fmt::formatter<Condition> : ostream_formatter {};
|
||||
|
||||
enum class OpCode {
|
||||
AND = 0b0000,
|
||||
EOR = 0b0001,
|
||||
SUB = 0b0010,
|
||||
RSB = 0b0011,
|
||||
ADD = 0b0100,
|
||||
ADC = 0b0101,
|
||||
SBC = 0b0110,
|
||||
RSC = 0b0111,
|
||||
TST = 0b1000,
|
||||
TEQ = 0b1001,
|
||||
CMP = 0b1010,
|
||||
CMN = 0b1011,
|
||||
ORR = 0b1100,
|
||||
MOV = 0b1101,
|
||||
BIC = 0b1110,
|
||||
MVN = 0b1111
|
||||
};
|
||||
|
||||
enum class ShiftType {
|
||||
LSL = 0b00,
|
||||
LSR = 0b01,
|
||||
ASR = 0b10,
|
||||
ROR = 0b11
|
||||
};
|
||||
|
||||
// https://fmt.dev/dev/api.html#std-ostream-support
|
||||
std::ostream&
|
||||
operator<<(std::ostream& os, const ShiftType cond);
|
||||
template<>
|
||||
struct fmt::formatter<ShiftType> : ostream_formatter {};
|
1
include/cpu/utility.hh
Symbolic link
1
include/cpu/utility.hh
Symbolic link
@@ -0,0 +1 @@
|
||||
../../src/cpu/utility.hh
|
265
src/cpu/cpu.cc
265
src/cpu/cpu.cc
@@ -1,4 +1,5 @@
|
||||
#include "cpu.hh"
|
||||
#include "util/bits.hh"
|
||||
#include "util/log.hh"
|
||||
#include "utility.hh"
|
||||
#include <algorithm>
|
||||
@@ -112,16 +113,276 @@ Cpu::chg_mode(const Mode to) {
|
||||
cpsr.set_mode(to);
|
||||
}
|
||||
|
||||
void
|
||||
Cpu::exec_arm(const ArmInstruction instruction) {
|
||||
auto cond = instruction.get_condition();
|
||||
auto data = instruction.get_data();
|
||||
|
||||
if (!cpsr.condition(cond)) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto pc_error = [](uint8_t r) {
|
||||
if (r == 15)
|
||||
log_error("Using PC (R15) as operand register");
|
||||
};
|
||||
|
||||
auto pc_warn = [](uint8_t r) {
|
||||
if (r == 15)
|
||||
log_warn("Using PC (R15) as operand register");
|
||||
};
|
||||
|
||||
std::visit(
|
||||
overloaded{
|
||||
[this, pc_warn](ArmInstruction::BranchAndExchange& data) {
|
||||
State state = static_cast<State>(data.rn & 1);
|
||||
|
||||
pc_warn(data.rn);
|
||||
|
||||
// set state
|
||||
cpsr.set_state(state);
|
||||
|
||||
// copy to PC
|
||||
pc = gpr[data.rn];
|
||||
|
||||
// ignore [1:0] bits for arm and 0 bit for thumb
|
||||
rst_nth_bit(pc, 0);
|
||||
|
||||
if (state == State::Arm)
|
||||
rst_nth_bit(pc, 1);
|
||||
},
|
||||
[this](ArmInstruction::Branch& data) {
|
||||
auto offset = data.offset;
|
||||
// lsh 2 and sign extend the 26 bit offset to 32 bits
|
||||
offset <<= 2;
|
||||
|
||||
if (get_nth_bit(offset, 25))
|
||||
offset |= 0xFC000000;
|
||||
|
||||
if (data.link)
|
||||
gpr[14] = pc - ARM_INSTRUCTION_SIZE;
|
||||
|
||||
pc += offset - ARM_INSTRUCTION_SIZE;
|
||||
},
|
||||
[this, pc_error](ArmInstruction::Multiply& data) {
|
||||
if (data.rd == data.rm)
|
||||
log_error("rd and rm are not distinct in {}",
|
||||
typeid(data).name());
|
||||
|
||||
pc_error(data.rd);
|
||||
pc_error(data.rd);
|
||||
pc_error(data.rd);
|
||||
|
||||
gpr[data.rd] =
|
||||
gpr[data.rm] * gpr[data.rs] + (data.acc ? gpr[data.rn] : 0);
|
||||
|
||||
if (data.set) {
|
||||
cpsr.set_z(!static_cast<bool>(gpr[data.rd]));
|
||||
cpsr.set_n(get_nth_bit(gpr[data.rd], 31));
|
||||
cpsr.set_c(0);
|
||||
}
|
||||
},
|
||||
[this, pc_error](ArmInstruction::MultiplyLong& data) {
|
||||
if (data.rdhi == data.rdlo || data.rdhi == data.rm ||
|
||||
data.rdlo == data.rm)
|
||||
log_error("rdhi, rdlo and rm are not distinct in {}",
|
||||
typeid(data).name());
|
||||
|
||||
pc_error(data.rdhi);
|
||||
pc_error(data.rdlo);
|
||||
pc_error(data.rm);
|
||||
pc_error(data.rs);
|
||||
if (data.uns) {
|
||||
uint64_t eval =
|
||||
static_cast<uint64_t>(gpr[data.rm]) *
|
||||
static_cast<uint64_t>(gpr[data.rs]) +
|
||||
(data.acc ? static_cast<uint64_t>(gpr[data.rdhi]) << 32 |
|
||||
static_cast<uint64_t>(gpr[data.rdlo])
|
||||
: 0);
|
||||
|
||||
gpr[data.rdlo] = get_bit_range(eval, 0, 31);
|
||||
gpr[data.rdhi] = get_bit_range(eval, 32, 63);
|
||||
|
||||
} else {
|
||||
int64_t eval =
|
||||
static_cast<int64_t>(gpr[data.rm]) *
|
||||
static_cast<int64_t>(gpr[data.rs]) +
|
||||
(data.acc ? static_cast<int64_t>(gpr[data.rdhi]) << 32 |
|
||||
static_cast<int64_t>(gpr[data.rdlo])
|
||||
: 0);
|
||||
|
||||
gpr[data.rdlo] = get_bit_range(eval, 0, 31);
|
||||
gpr[data.rdhi] = get_bit_range(eval, 32, 63);
|
||||
}
|
||||
|
||||
if (data.set) {
|
||||
cpsr.set_z(!(static_cast<bool>(gpr[data.rdhi]) ||
|
||||
static_cast<bool>(gpr[data.rdlo])));
|
||||
cpsr.set_n(get_nth_bit(gpr[data.rdhi], 31));
|
||||
cpsr.set_c(0);
|
||||
cpsr.set_v(0);
|
||||
}
|
||||
},
|
||||
[](ArmInstruction::Undefined) { log_warn("Undefined instruction"); },
|
||||
[this, pc_warn](ArmInstruction::SingleDataSwap& data) {
|
||||
pc_warn(data.rm);
|
||||
pc_warn(data.rn);
|
||||
pc_warn(data.rd);
|
||||
|
||||
if (data.byte) {
|
||||
gpr[data.rd] = bus->read_byte(gpr[data.rn]);
|
||||
bus->write_byte(gpr[data.rn], gpr[data.rm] & 0xFF);
|
||||
} else {
|
||||
gpr[data.rd] = bus->read_word(gpr[data.rn]);
|
||||
bus->write_word(gpr[data.rn], gpr[data.rm]);
|
||||
}
|
||||
},
|
||||
[this, pc_warn, pc_error](ArmInstruction::SingleDataTransfer& data) {
|
||||
uint32_t offset = 0;
|
||||
uint32_t address = gpr[data.rn];
|
||||
|
||||
if (!data.pre && data.write)
|
||||
log_warn("Write-back enabled with post-indexing in {}",
|
||||
typeid(data).name());
|
||||
|
||||
if (data.write)
|
||||
pc_warn(data.rn);
|
||||
|
||||
// evaluate the offset
|
||||
if (const uint16_t* immediate =
|
||||
std::get_if<uint16_t>(&data.offset)) {
|
||||
offset = *immediate;
|
||||
} else if (const Shift* shift = std::get_if<Shift>(&data.offset)) {
|
||||
uint8_t amount =
|
||||
(shift->data.immediate ? shift->data.operand
|
||||
: gpr[shift->data.operand] & 0xFF);
|
||||
|
||||
bool carry = cpsr.c();
|
||||
|
||||
if (!shift->data.immediate)
|
||||
pc_error(shift->data.operand);
|
||||
pc_error(shift->rm);
|
||||
|
||||
eval_shift(shift->data.type, gpr[shift->rm], amount, carry);
|
||||
|
||||
cpsr.set_c(carry);
|
||||
}
|
||||
|
||||
// PC is always two instructions ahead
|
||||
if (data.rn == 15)
|
||||
address -= 2 * ARM_INSTRUCTION_SIZE;
|
||||
|
||||
if (data.pre)
|
||||
address += (data.up ? offset : -offset);
|
||||
|
||||
// load
|
||||
if (data.load) {
|
||||
// byte
|
||||
if (data.byte)
|
||||
gpr[data.rd] = bus->read_byte(address);
|
||||
// word
|
||||
else
|
||||
gpr[data.rd] = bus->read_word(address);
|
||||
// store
|
||||
} else {
|
||||
// take PC into consideration
|
||||
if (data.rd == 15)
|
||||
address += ARM_INSTRUCTION_SIZE;
|
||||
|
||||
// byte
|
||||
if (data.byte)
|
||||
bus->write_byte(address, gpr[data.rd] & 0xFF);
|
||||
// word
|
||||
else
|
||||
bus->write_word(address, gpr[data.rd]);
|
||||
}
|
||||
|
||||
if (!data.pre)
|
||||
address += (data.up ? offset : -offset);
|
||||
|
||||
if (!data.pre || data.write)
|
||||
gpr[data.rn] = address;
|
||||
},
|
||||
[this, pc_warn, pc_error](ArmInstruction::HalfwordTransfer& data) {
|
||||
uint32_t address = gpr[data.rn];
|
||||
|
||||
if (!data.pre && data.write)
|
||||
log_error("Write-back enabled with post-indexing in {}",
|
||||
typeid(data).name());
|
||||
|
||||
if (data.sign && !data.load)
|
||||
log_error("Signed data found in {}", typeid(data).name());
|
||||
|
||||
if (data.write)
|
||||
pc_warn(data.rn);
|
||||
|
||||
// offset is register number (4 bits) when not an immediate
|
||||
if (!data.imm)
|
||||
pc_error(data.offset);
|
||||
|
||||
if (data.pre)
|
||||
address += (data.up ? data.offset : -data.offset);
|
||||
|
||||
// load
|
||||
if (data.load) {
|
||||
// signed
|
||||
if (data.sign) {
|
||||
// halfword
|
||||
if (data.half) {
|
||||
gpr[data.rd] = bus->read_halfword(address);
|
||||
|
||||
// sign extend the halfword
|
||||
if (get_nth_bit(gpr[data.rd], 15))
|
||||
gpr[data.rd] |= 0xFFFF0000;
|
||||
// byte
|
||||
} else {
|
||||
gpr[data.rd] = bus->read_byte(address);
|
||||
|
||||
// sign extend the byte
|
||||
if (get_nth_bit(gpr[data.rd], 7))
|
||||
gpr[data.rd] |= 0xFFFFFF00;
|
||||
}
|
||||
// unsigned halfword
|
||||
} else if (data.half) {
|
||||
gpr[data.rd] = bus->read_halfword(address);
|
||||
}
|
||||
// store
|
||||
} else {
|
||||
// take PC into consideration
|
||||
if (data.rd == 15)
|
||||
address += ARM_INSTRUCTION_SIZE;
|
||||
|
||||
// halfword
|
||||
if (data.half)
|
||||
bus->write_halfword(address, gpr[data.rd]);
|
||||
}
|
||||
|
||||
if (!data.pre)
|
||||
address += (data.up ? data.offset : -data.offset);
|
||||
|
||||
if (!data.pre || data.write)
|
||||
gpr[data.rn] = address;
|
||||
},
|
||||
[this](ArmInstruction::SoftwareInterrupt) {
|
||||
chg_mode(Mode::Supervisor);
|
||||
pc = 0x08;
|
||||
spsr = cpsr;
|
||||
},
|
||||
[](auto& data) { log_error("{} instruction", typeid(data).name()); } },
|
||||
data);
|
||||
}
|
||||
|
||||
void
|
||||
Cpu::step() {
|
||||
uint32_t cur_pc = pc - 2 * ARM_INSTRUCTION_SIZE;
|
||||
|
||||
if (cpsr.state() == State::Arm) {
|
||||
ArmInstruction instruction(bus->read_word(cur_pc));
|
||||
log_info("{:#034b}", bus->read_word(cur_pc));
|
||||
|
||||
std::string disassembled = exec_arm(bus->read_word(cur_pc));
|
||||
exec_arm(instruction);
|
||||
|
||||
log_info("{:#010X} : {}", cur_pc, disassembled);
|
||||
log_info("{:#010X} : {}", cur_pc, instruction.disassemble());
|
||||
|
||||
pc += ARM_INSTRUCTION_SIZE;
|
||||
}
|
||||
|
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "bus.hh"
|
||||
#include "instruction.hh"
|
||||
#include "psr.hh"
|
||||
|
||||
#include <cstdint>
|
||||
@@ -50,5 +51,5 @@ class Cpu {
|
||||
} spsr_banked; // banked saved program status registers
|
||||
|
||||
void chg_mode(const Mode to);
|
||||
std::string exec_arm(const uint32_t insn);
|
||||
void exec_arm(const ArmInstruction instruction);
|
||||
};
|
||||
|
@@ -1,342 +1,181 @@
|
||||
#include "cpu.hh"
|
||||
#include "cpu/utility.hh"
|
||||
#include "instruction.hh"
|
||||
#include "util/bits.hh"
|
||||
#include "util/log.hh"
|
||||
#include <cstdint>
|
||||
|
||||
using namespace logger;
|
||||
using std::array;
|
||||
using stringv = std::string_view;
|
||||
|
||||
std::string
|
||||
Cpu::exec_arm(const uint32_t insn) {
|
||||
Condition cond = static_cast<Condition>(get_bit_range(insn, 28, 31));
|
||||
std::string disassembled;
|
||||
|
||||
auto pc_error = [](uint8_t r, stringv syn) {
|
||||
if (r == 15)
|
||||
log_error("Using PC (R15) as operand in {}", syn);
|
||||
};
|
||||
|
||||
auto pc_undefined = [](uint8_t r, stringv syn) {
|
||||
if (r == 15)
|
||||
log_warn("Using PC (R15) as operand in {}", syn);
|
||||
};
|
||||
|
||||
ArmInstruction::ArmInstruction(uint32_t insn)
|
||||
: condition(static_cast<Condition>(get_bit_range(insn, 28, 31))) {
|
||||
// Branch and exhcange
|
||||
if ((insn & 0x0FFFFFF0) == 0x012FFF10) {
|
||||
static constexpr stringv syn = "BX";
|
||||
|
||||
uint8_t rn = insn & 0b1111;
|
||||
|
||||
pc_undefined(rn, syn);
|
||||
data = BranchAndExchange{ rn };
|
||||
|
||||
disassembled = fmt::format("{}{} R{:d}", syn, cond, rn);
|
||||
|
||||
if (cpsr.condition(cond)) {
|
||||
State state = static_cast<State>(rn & 1);
|
||||
|
||||
// set state
|
||||
cpsr.set_state(state);
|
||||
|
||||
// copy to PC
|
||||
pc = gpr[rn];
|
||||
|
||||
// ignore [1:0] bits for arm and 0 bit for thumb
|
||||
rst_nth_bit(pc, 0);
|
||||
if (state == State::Arm)
|
||||
rst_nth_bit(pc, 1);
|
||||
}
|
||||
// Branch
|
||||
} else if ((insn & 0x0E000000) == 0x0A000000) {
|
||||
static constexpr stringv syn = "B";
|
||||
|
||||
bool link = get_nth_bit(insn, 24);
|
||||
uint32_t offset = get_bit_range(insn, 0, 23);
|
||||
|
||||
disassembled =
|
||||
fmt::format("{}{}{} {:#08X}", syn, (link ? "L" : ""), cond, offset);
|
||||
|
||||
if (cpsr.condition(cond)) {
|
||||
// lsh 2 and sign extend the 26 bit offset to 32
|
||||
// bits
|
||||
offset <<= 2;
|
||||
if (get_nth_bit(offset, 25))
|
||||
offset |= 0xFC000000;
|
||||
|
||||
if (link)
|
||||
gpr[14] = pc - ARM_INSTRUCTION_SIZE;
|
||||
|
||||
pc += offset - ARM_INSTRUCTION_SIZE;
|
||||
}
|
||||
data = Branch{ .link = link, .offset = offset };
|
||||
|
||||
// Multiply
|
||||
} else if ((insn & 0x0FC000F0) == 0x00000090) {
|
||||
static constexpr array<stringv, 2> syn = { "MUL", "MLA" };
|
||||
|
||||
uint8_t rm = get_bit_range(insn, 0, 3);
|
||||
uint8_t rs = get_bit_range(insn, 8, 11);
|
||||
uint8_t rn = get_bit_range(insn, 12, 15);
|
||||
uint8_t rd = get_bit_range(insn, 16, 19);
|
||||
bool s = get_nth_bit(insn, 20);
|
||||
bool a = get_nth_bit(insn, 21);
|
||||
bool set = get_nth_bit(insn, 20);
|
||||
bool acc = get_nth_bit(insn, 21);
|
||||
|
||||
if (rd == rm)
|
||||
log_error("rd and rm are not distinct in {} : {:d}", syn[a], rd);
|
||||
|
||||
pc_error(rd, syn[a]);
|
||||
pc_error(rm, syn[a]);
|
||||
pc_error(rs, syn[a]);
|
||||
|
||||
if (a) {
|
||||
pc_error(rn, syn[a]);
|
||||
disassembled = fmt::format("{}{}{} R{:d},R{:d},R{:d},R{:d}",
|
||||
syn[a],
|
||||
cond,
|
||||
(s ? "S" : ""),
|
||||
rd,
|
||||
rm,
|
||||
rs,
|
||||
rn);
|
||||
} else {
|
||||
disassembled = fmt::format("{}{}{} R{:d},R{:d},R{:d}",
|
||||
syn[a],
|
||||
cond,
|
||||
(s ? "S" : ""),
|
||||
rd,
|
||||
rm,
|
||||
rs);
|
||||
}
|
||||
|
||||
if (cpsr.condition(cond)) {
|
||||
gpr[rd] = gpr[rm] * gpr[rs] + (a ? gpr[rn] : 0);
|
||||
|
||||
if (s) {
|
||||
cpsr.set_z(!static_cast<bool>(gpr[rd]));
|
||||
cpsr.set_n(get_nth_bit(gpr[rd], 31));
|
||||
cpsr.set_c(0);
|
||||
}
|
||||
}
|
||||
// Multiply long
|
||||
} else if ((insn & 0x0F8000F0) == 0x00800090) {
|
||||
static constexpr array<array<stringv, 2>, 2> syn = {
|
||||
{ { "SMULL", "SMLAL" }, { "UMULL", "UMLAL" } }
|
||||
data = Multiply{
|
||||
.rm = rm, .rs = rs, .rn = rn, .rd = rd, .set = set, .acc = acc
|
||||
};
|
||||
|
||||
// Multiply long
|
||||
} else if ((insn & 0x0F8000F0) == 0x00800090) {
|
||||
uint8_t rm = get_bit_range(insn, 0, 3);
|
||||
uint8_t rs = get_bit_range(insn, 8, 11);
|
||||
uint8_t rdlo = get_bit_range(insn, 12, 15);
|
||||
uint8_t rdhi = get_bit_range(insn, 16, 19);
|
||||
bool s = get_nth_bit(insn, 20);
|
||||
bool a = get_nth_bit(insn, 21);
|
||||
bool u = get_nth_bit(insn, 22);
|
||||
bool set = get_nth_bit(insn, 20);
|
||||
bool acc = get_nth_bit(insn, 21);
|
||||
bool uns = get_nth_bit(insn, 22);
|
||||
|
||||
if (rdhi == rdlo || rdhi == rm || rdlo == rm)
|
||||
log_error("rdhi, rdlo and rm are not distinct in {}", syn[u][a]);
|
||||
data = MultiplyLong{ .rm = rm,
|
||||
.rs = rs,
|
||||
.rdlo = rdlo,
|
||||
.rdhi = rdhi,
|
||||
.set = set,
|
||||
.acc = acc,
|
||||
.uns = uns };
|
||||
|
||||
pc_error(rdhi, syn[u][a]);
|
||||
pc_error(rdlo, syn[u][a]);
|
||||
pc_error(rm, syn[u][a]);
|
||||
pc_error(rs, syn[u][a]);
|
||||
|
||||
disassembled = fmt::format("{}{}{} R{:d},R{:d},R{:d},R{:d}",
|
||||
syn[u][a],
|
||||
cond,
|
||||
(s ? "S" : ""),
|
||||
rdlo,
|
||||
rdhi,
|
||||
rm,
|
||||
rs);
|
||||
|
||||
if (cpsr.condition(cond)) {
|
||||
if (u) {
|
||||
uint64_t eval = static_cast<uint64_t>(gpr[rm]) *
|
||||
static_cast<uint64_t>(gpr[rs]) +
|
||||
(a ? static_cast<uint64_t>(gpr[rdhi]) << 32 |
|
||||
static_cast<uint64_t>(gpr[rdlo])
|
||||
: 0);
|
||||
|
||||
gpr[rdlo] = get_bit_range(eval, 0, 31);
|
||||
gpr[rdhi] = get_bit_range(eval, 32, 63);
|
||||
|
||||
} else {
|
||||
int64_t eval = static_cast<int64_t>(gpr[rm]) *
|
||||
static_cast<int64_t>(gpr[rs]) +
|
||||
(a ? static_cast<int64_t>(gpr[rdhi]) << 32 |
|
||||
static_cast<int64_t>(gpr[rdlo])
|
||||
: 0);
|
||||
|
||||
gpr[rdlo] = get_bit_range(eval, 0, 31);
|
||||
gpr[rdhi] = get_bit_range(eval, 32, 63);
|
||||
}
|
||||
|
||||
if (s) {
|
||||
cpsr.set_z(!(static_cast<bool>(gpr[rdhi]) ||
|
||||
static_cast<bool>(gpr[rdlo])));
|
||||
cpsr.set_n(get_nth_bit(gpr[rdhi], 31));
|
||||
cpsr.set_c(0);
|
||||
cpsr.set_v(0);
|
||||
}
|
||||
}
|
||||
// Undefined
|
||||
} else if ((insn & 0x0E000010) == 0x06000010) {
|
||||
data = Undefined{};
|
||||
|
||||
// Single data swap
|
||||
} else if ((insn & 0x0FB00FF0) == 0x01000090) {
|
||||
static constexpr stringv syn = "SWP";
|
||||
|
||||
uint8_t rm = get_bit_range(insn, 0, 3);
|
||||
uint8_t rd = get_bit_range(insn, 12, 15);
|
||||
uint8_t rn = get_bit_range(insn, 16, 19);
|
||||
bool b = get_nth_bit(insn, 22);
|
||||
bool byte = get_nth_bit(insn, 22);
|
||||
|
||||
pc_undefined(rm, syn);
|
||||
pc_undefined(rn, syn);
|
||||
pc_undefined(rd, syn);
|
||||
data = SingleDataSwap{ .rm = rm, .rd = rd, .rn = rn, .byte = byte };
|
||||
|
||||
disassembled = fmt::format(
|
||||
"{}{}{} R{:d},R{:d},[R{:d}]", syn, cond, (b ? "B" : ""), rd, rm, rn);
|
||||
// Single data transfer
|
||||
} else if ((insn & 0x0C000000) == 0x04000000) {
|
||||
|
||||
if (cpsr.condition(cond)) {
|
||||
if (b) {
|
||||
gpr[rd] = bus->read_byte(gpr[rn]);
|
||||
bus->write_byte(gpr[rn], gpr[rm] & 0xFF);
|
||||
std::variant<uint16_t, Shift> offset;
|
||||
uint8_t rd = get_bit_range(insn, 12, 15);
|
||||
uint8_t rn = get_bit_range(insn, 16, 19);
|
||||
bool load = get_nth_bit(insn, 20);
|
||||
bool write = get_nth_bit(insn, 21);
|
||||
bool byte = get_nth_bit(insn, 22);
|
||||
bool up = get_nth_bit(insn, 23);
|
||||
bool pre = get_nth_bit(insn, 24);
|
||||
bool imm = get_nth_bit(insn, 25);
|
||||
|
||||
if (imm) {
|
||||
uint8_t rm = get_bit_range(insn, 0, 3);
|
||||
bool reg = get_nth_bit(insn, 4);
|
||||
ShiftType shift_type =
|
||||
static_cast<ShiftType>(get_bit_range(insn, 5, 6));
|
||||
uint8_t operand = get_bit_range(insn, (reg ? 8 : 7), 11);
|
||||
|
||||
offset = Shift{ .rm = rm,
|
||||
.data = ShiftData{ .type = shift_type,
|
||||
.immediate = !reg,
|
||||
.operand = operand } };
|
||||
} else {
|
||||
gpr[rd] = bus->read_word(gpr[rn]);
|
||||
bus->write_word(gpr[rn], gpr[rm]);
|
||||
}
|
||||
offset = static_cast<uint16_t>(get_bit_range(insn, 0, 11));
|
||||
}
|
||||
|
||||
data = SingleDataTransfer{ .offset = offset,
|
||||
.rd = rd,
|
||||
.rn = rn,
|
||||
.load = load,
|
||||
.write = write,
|
||||
.byte = byte,
|
||||
.up = up,
|
||||
.pre = pre };
|
||||
|
||||
// Halfword transfer
|
||||
// TODO: create abstraction to reuse for block data and single data
|
||||
// transfer
|
||||
} else if ((insn & 0x0E000090) == 0x00000090) {
|
||||
static constexpr array<stringv, 2> syn_ = { "STR", "LDR" };
|
||||
|
||||
uint8_t rm = get_bit_range(insn, 0, 3);
|
||||
uint8_t h = get_nth_bit(insn, 5);
|
||||
uint8_t s = get_nth_bit(insn, 6);
|
||||
uint8_t offset = get_bit_range(insn, 0, 3);
|
||||
bool half = get_nth_bit(insn, 5);
|
||||
bool sign = get_nth_bit(insn, 6);
|
||||
uint8_t rd = get_bit_range(insn, 12, 15);
|
||||
uint8_t rn = get_bit_range(insn, 16, 19);
|
||||
bool l = get_nth_bit(insn, 20);
|
||||
bool w = get_nth_bit(insn, 21);
|
||||
bool load = get_nth_bit(insn, 20);
|
||||
bool write = get_nth_bit(insn, 21);
|
||||
bool imm = get_nth_bit(insn, 22);
|
||||
bool u = get_nth_bit(insn, 23);
|
||||
bool p = get_nth_bit(insn, 24);
|
||||
uint32_t offset = 0;
|
||||
bool up = get_nth_bit(insn, 23);
|
||||
bool pre = get_nth_bit(insn, 24);
|
||||
|
||||
std::string syn =
|
||||
fmt::format("{}{}{}", syn_[l], (s ? "S" : ""), (h ? 'H' : 'B'));
|
||||
offset |= (imm ? get_bit_range(insn, 8, 11) << 2 : 0);
|
||||
|
||||
if (!p && w)
|
||||
log_error("Write-back enabled with post-indexing in {}", syn);
|
||||
data = HalfwordTransfer{ .offset = offset,
|
||||
.half = half,
|
||||
.sign = sign,
|
||||
.rd = rd,
|
||||
.rn = rn,
|
||||
.load = load,
|
||||
.write = write,
|
||||
.imm = imm,
|
||||
.up = up,
|
||||
.pre = pre };
|
||||
|
||||
if (s && !l)
|
||||
log_error("Signed data found in {}", syn);
|
||||
// Block data transfer
|
||||
} else if ((insn & 0x0E000000) == 0x08000000) {
|
||||
/*static constexpr array<stringv, 2> syn = { "STM", "LDM" };
|
||||
|
||||
if (w)
|
||||
pc_error(rn, syn);
|
||||
pc_error(rm, syn);
|
||||
uint16_t regs = get_bit_range(insn, 0, 15);
|
||||
uint8_t rn = get_bit_range(insn, 16, 19);
|
||||
bool load = get_nth_bit(insn, 20);
|
||||
bool write = get_nth_bit(insn, 21);
|
||||
bool s = get_nth_bit(insn, 22);
|
||||
bool up = get_nth_bit(insn, 23);
|
||||
bool pre = get_nth_bit(insn, 24);
|
||||
|
||||
// disassembly
|
||||
{
|
||||
offset = (imm ? get_bit_range(insn, 8, 11) << 4 | rm : gpr[rm]);
|
||||
std::string offset_str = fmt::format("{}{}{:d}",
|
||||
(u ? "" : "-"),
|
||||
(imm ? '#' : 'R'),
|
||||
(imm ? offset : rm));
|
||||
uint8_t lpu = load << 2 | pre << 1 | up;
|
||||
std::string addr_mode;
|
||||
|
||||
disassembled = fmt::format(
|
||||
"{}{}{}{} R{:d},{}",
|
||||
syn_[l],
|
||||
cond,
|
||||
(s ? "S" : ""),
|
||||
(h ? 'H' : 'B'),
|
||||
rd,
|
||||
(!offset ? fmt::format("[R{:d}]", rn)
|
||||
: p
|
||||
? fmt::format(",[R{:d},{}]{}", rn, offset_str, (w ? "!" : ""))
|
||||
: fmt::format(",[R{:d}],{}", rn, offset_str)));
|
||||
switch(lpu) {
|
||||
}
|
||||
}*/
|
||||
|
||||
if (cpsr.condition(cond)) {
|
||||
uint32_t address = gpr[rn];
|
||||
|
||||
if (p)
|
||||
address += (u ? offset : -offset);
|
||||
|
||||
// load
|
||||
if (l) {
|
||||
// signed
|
||||
if (s) {
|
||||
// halfword
|
||||
if (h) {
|
||||
gpr[rd] = bus->read_halfword(address);
|
||||
// sign extend the halfword
|
||||
if (get_nth_bit(gpr[rd], 15))
|
||||
gpr[rd] |= 0xFFFF0000;
|
||||
// byte
|
||||
} else {
|
||||
// sign extend the byte
|
||||
gpr[rd] = bus->read_byte(address);
|
||||
if (get_nth_bit(gpr[rd], 7))
|
||||
gpr[rd] |= 0xFFFFFF00;
|
||||
}
|
||||
// unsigned halfword
|
||||
} else if (h) {
|
||||
gpr[rd] = bus->read_halfword(address);
|
||||
}
|
||||
// store
|
||||
} else {
|
||||
// halfword
|
||||
if (h) {
|
||||
// take PC into consideration
|
||||
if (rd == 15)
|
||||
address += 12;
|
||||
bus->write_halfword(address, gpr[rd]);
|
||||
}
|
||||
}
|
||||
|
||||
if (!p)
|
||||
address += (u ? offset : -offset);
|
||||
|
||||
if (!p || w)
|
||||
gpr[rn] = address;
|
||||
}
|
||||
data = Undefined{};
|
||||
|
||||
// Software Interrupt
|
||||
// What to do here?
|
||||
} else if ((insn & 0x0F000000) == 0x0F000000) {
|
||||
static constexpr stringv syn = "SWI";
|
||||
|
||||
if (cpsr.condition(cond)) {
|
||||
chg_mode(Mode::Supervisor);
|
||||
pc = 0x08;
|
||||
spsr = cpsr;
|
||||
}
|
||||
|
||||
disassembled = fmt::format("{}{} 0", syn, cond);
|
||||
data = SoftwareInterrupt{};
|
||||
|
||||
// Coprocessor data transfer
|
||||
} else if ((insn & 0x0E000000) == 0x0C000000) {
|
||||
static constexpr array<stringv, 2> syn = { "STC", "LDC" };
|
||||
|
||||
[[maybe_unused]] uint8_t offset = get_bit_range(insn, 0, 7);
|
||||
uint8_t offset = get_bit_range(insn, 0, 7);
|
||||
uint8_t cpn = get_bit_range(insn, 8, 11);
|
||||
uint8_t crd = get_bit_range(insn, 12, 15);
|
||||
[[maybe_unused]] uint8_t rn = get_bit_range(insn, 16, 19);
|
||||
bool l = get_nth_bit(insn, 20);
|
||||
[[maybe_unused]] bool w = get_nth_bit(insn, 21);
|
||||
bool n = get_nth_bit(insn, 22);
|
||||
[[maybe_unused]] bool u = get_nth_bit(insn, 23);
|
||||
[[maybe_unused]] bool p = get_nth_bit(insn, 24);
|
||||
uint8_t rn = get_bit_range(insn, 16, 19);
|
||||
bool load = get_nth_bit(insn, 20);
|
||||
bool write = get_nth_bit(insn, 21);
|
||||
bool len = get_nth_bit(insn, 22);
|
||||
bool up = get_nth_bit(insn, 23);
|
||||
bool pre = get_nth_bit(insn, 24);
|
||||
|
||||
disassembled = fmt::format(
|
||||
"{}{}{} p{},c{},{}", syn[l], cond, (n ? "L" : ""), cpn, crd, "a.");
|
||||
|
||||
log_error("Unimplemented instruction: {}", syn[l]);
|
||||
data = CoprocessorDataTransfer{ .offset = offset,
|
||||
.cpn = cpn,
|
||||
.crd = crd,
|
||||
.rn = rn,
|
||||
.load = load,
|
||||
.write = write,
|
||||
.len = len,
|
||||
.up = up,
|
||||
.pre = pre };
|
||||
|
||||
// Coprocessor data operation
|
||||
} else if ((insn & 0x0F000010) == 0x0E000000) {
|
||||
static constexpr stringv syn = "CDP";
|
||||
|
||||
uint8_t crm = get_bit_range(insn, 0, 4);
|
||||
uint8_t cp = get_bit_range(insn, 5, 7);
|
||||
uint8_t cpn = get_bit_range(insn, 8, 11);
|
||||
@@ -344,42 +183,177 @@ Cpu::exec_arm(const uint32_t insn) {
|
||||
uint8_t crn = get_bit_range(insn, 16, 19);
|
||||
uint8_t cp_opc = get_bit_range(insn, 20, 23);
|
||||
|
||||
disassembled = fmt::format("{}{} p{},{},c{},c{},c{},{}",
|
||||
syn,
|
||||
cond,
|
||||
cpn,
|
||||
cp_opc,
|
||||
crd,
|
||||
crn,
|
||||
crm,
|
||||
cp);
|
||||
|
||||
log_error("Unimplemented instruction: {}", syn);
|
||||
data = CoprocessorDataOperation{ .crm = crm,
|
||||
.cp = cp,
|
||||
.cpn = cpn,
|
||||
.crd = crd,
|
||||
.crn = crn,
|
||||
.cp_opc = cp_opc };
|
||||
|
||||
// Coprocessor register transfer
|
||||
} else if ((insn & 0x0F000010) == 0x0E000010) {
|
||||
static constexpr array<stringv, 2> syn = { "MCR", "MRC" };
|
||||
|
||||
uint8_t crm = get_bit_range(insn, 0, 4);
|
||||
uint8_t cp = get_bit_range(insn, 5, 7);
|
||||
uint8_t cpn = get_bit_range(insn, 8, 11);
|
||||
uint8_t rd = get_bit_range(insn, 12, 15);
|
||||
uint8_t crn = get_bit_range(insn, 16, 19);
|
||||
bool l = get_nth_bit(insn, 20);
|
||||
bool load = get_nth_bit(insn, 20);
|
||||
uint8_t cp_opc = get_bit_range(insn, 21, 23);
|
||||
|
||||
disassembled = fmt::format("{}{} p{},{},c{},c{},c{},{}",
|
||||
syn[l],
|
||||
cond,
|
||||
cpn,
|
||||
cp_opc,
|
||||
rd,
|
||||
crn,
|
||||
crm,
|
||||
cp);
|
||||
|
||||
log_error("Unimplemented instruction: {}", syn[l]);
|
||||
data = CoprocessorRegisterTransfer{ .crm = crm,
|
||||
.cp = cp,
|
||||
.cpn = cpn,
|
||||
.rd = rd,
|
||||
.crn = crn,
|
||||
.load = load,
|
||||
.cp_opc = cp_opc };
|
||||
} else {
|
||||
data = Undefined{};
|
||||
}
|
||||
}
|
||||
|
||||
return disassembled;
|
||||
std::string
|
||||
ArmInstruction::disassemble() {
|
||||
static const std::string undefined = "UNDEFINED";
|
||||
// goddamn this is gore
|
||||
// TODO: make this less ugly
|
||||
return std::visit(
|
||||
overloaded{
|
||||
[this](BranchAndExchange& data) {
|
||||
return fmt::format("BX{} R{:d}", condition, data.rn);
|
||||
},
|
||||
[this](Branch& data) {
|
||||
return fmt::format(
|
||||
"B{}{} {:#08X}", (data.link ? "L" : ""), condition, data.offset);
|
||||
},
|
||||
[this](Multiply& data) {
|
||||
if (data.acc) {
|
||||
return fmt::format("MLA{}{} R{:d},R{:d},R{:d},R{:d}",
|
||||
condition,
|
||||
(data.set ? "S" : ""),
|
||||
data.rd,
|
||||
data.rm,
|
||||
data.rs,
|
||||
data.rn);
|
||||
} else {
|
||||
return fmt::format("MUL{}{} R{:d},R{:d},R{:d}",
|
||||
condition,
|
||||
(data.set ? "S" : ""),
|
||||
data.rd,
|
||||
data.rm,
|
||||
data.rs);
|
||||
}
|
||||
},
|
||||
[this](MultiplyLong& data) {
|
||||
return fmt::format("{}{}{}{} R{:d},R{:d},R{:d},R{:d}",
|
||||
(data.uns ? 'U' : 'S'),
|
||||
(data.acc ? "MLAL" : "MULL"),
|
||||
condition,
|
||||
(data.set ? "S" : ""),
|
||||
data.rdlo,
|
||||
data.rdhi,
|
||||
data.rm,
|
||||
data.rs);
|
||||
},
|
||||
[](Undefined) { return undefined; },
|
||||
[this](SingleDataSwap& data) {
|
||||
return fmt::format("SWP{}{} R{:d},R{:d},[R{:d}]",
|
||||
condition,
|
||||
(data.byte ? "B" : ""),
|
||||
data.rd,
|
||||
data.rm,
|
||||
data.rn);
|
||||
},
|
||||
[this](SingleDataTransfer& data) {
|
||||
std::string expression;
|
||||
std::string address;
|
||||
|
||||
if (const uint16_t* offset = std::get_if<uint16_t>(&data.offset)) {
|
||||
if (*offset == 0) {
|
||||
expression = "";
|
||||
} else {
|
||||
expression = fmt::format(",#{:d}", *offset);
|
||||
}
|
||||
} else if (const Shift* shift = std::get_if<Shift>(&data.offset)) {
|
||||
expression = fmt::format(",{}R{:d},{} {}{:d}",
|
||||
(data.up ? '+' : '-'),
|
||||
shift->rm,
|
||||
shift->data.type,
|
||||
(shift->data.immediate ? '#' : 'R'),
|
||||
shift->data.operand);
|
||||
}
|
||||
|
||||
return fmt::format(
|
||||
"{}{}{}{} R{:d},[R{:d}{}]{}",
|
||||
(data.load ? "LDR" : "STR"),
|
||||
condition,
|
||||
(data.byte ? "B" : ""),
|
||||
(!data.pre && data.write ? "T" : ""),
|
||||
data.rd,
|
||||
data.rn,
|
||||
(data.pre ? expression : ""),
|
||||
(data.pre ? (data.write ? "!" : "") : expression));
|
||||
},
|
||||
[this](HalfwordTransfer& data) {
|
||||
std::string expression;
|
||||
|
||||
if (data.imm) {
|
||||
if (data.offset == 0) {
|
||||
expression = "";
|
||||
} else {
|
||||
expression = fmt::format(",#{:d}", data.offset);
|
||||
}
|
||||
} else {
|
||||
expression =
|
||||
fmt::format(",{}R{:d}", (data.up ? '+' : '-'), data.offset);
|
||||
}
|
||||
|
||||
return fmt::format(
|
||||
"{}{}{}{} R{:d},[R{:d}{}]{}",
|
||||
(data.load ? "LDR" : "STR"),
|
||||
condition,
|
||||
(data.sign ? "S" : ""),
|
||||
(data.half ? 'H' : 'B'),
|
||||
data.rd,
|
||||
data.rn,
|
||||
(data.pre ? expression : ""),
|
||||
(data.pre ? (data.write ? "!" : "") : expression));
|
||||
},
|
||||
[this](SoftwareInterrupt) { return fmt::format("SWI{}", condition); },
|
||||
[this](CoprocessorDataTransfer& data) {
|
||||
std::string expression = fmt::format(",#{:d}", data.offset);
|
||||
return fmt::format(
|
||||
"{}{}{} p{:d},c{:d},[R{:d}{}]{}",
|
||||
(data.load ? "LDC" : "STC"),
|
||||
condition,
|
||||
(data.len ? "L" : ""),
|
||||
data.cpn,
|
||||
data.crd,
|
||||
data.rn,
|
||||
(data.pre ? expression : ""),
|
||||
(data.pre ? (data.write ? "!" : "") : expression));
|
||||
},
|
||||
[this](CoprocessorDataOperation& data) {
|
||||
return fmt::format("CDP{} p{},{},c{},c{},c{},{}",
|
||||
condition,
|
||||
data.cpn,
|
||||
data.cp_opc,
|
||||
data.crd,
|
||||
data.crn,
|
||||
data.crm,
|
||||
data.cp);
|
||||
},
|
||||
[this](CoprocessorRegisterTransfer& data) {
|
||||
return fmt::format("{}{} p{},{},c{},c{},c{},{}",
|
||||
(data.load ? "MRC" : "MCR"),
|
||||
condition,
|
||||
data.cpn,
|
||||
data.cp_opc,
|
||||
data.rd,
|
||||
data.crn,
|
||||
data.crm,
|
||||
data.cp);
|
||||
},
|
||||
[](auto) { return undefined; } },
|
||||
data);
|
||||
}
|
||||
|
132
src/cpu/instruction.hh
Normal file
132
src/cpu/instruction.hh
Normal file
@@ -0,0 +1,132 @@
|
||||
#include "cpu/utility.hh"
|
||||
#include <cstdint>
|
||||
#include <variant>
|
||||
|
||||
template<class... Ts>
|
||||
struct overloaded : Ts... {
|
||||
using Ts::operator()...;
|
||||
};
|
||||
template<class... Ts>
|
||||
overloaded(Ts...) -> overloaded<Ts...>;
|
||||
|
||||
class ArmInstruction {
|
||||
public:
|
||||
ArmInstruction() = delete;
|
||||
ArmInstruction(uint32_t insn);
|
||||
|
||||
auto get_condition() const { return condition; }
|
||||
auto get_data() const { return data; }
|
||||
|
||||
std::string disassemble();
|
||||
|
||||
struct BranchAndExchange {
|
||||
uint8_t rn;
|
||||
};
|
||||
|
||||
struct Branch {
|
||||
bool link;
|
||||
uint32_t offset;
|
||||
};
|
||||
|
||||
struct Multiply {
|
||||
uint8_t rm;
|
||||
uint8_t rs;
|
||||
uint8_t rn;
|
||||
uint8_t rd;
|
||||
bool set;
|
||||
bool acc;
|
||||
};
|
||||
|
||||
struct MultiplyLong {
|
||||
uint8_t rm;
|
||||
uint8_t rs;
|
||||
uint8_t rdlo;
|
||||
uint8_t rdhi;
|
||||
bool set;
|
||||
bool acc;
|
||||
bool uns;
|
||||
};
|
||||
|
||||
struct SingleDataSwap {
|
||||
uint8_t rm;
|
||||
uint8_t rd;
|
||||
uint8_t rn;
|
||||
bool byte;
|
||||
};
|
||||
|
||||
struct SingleDataTransfer {
|
||||
std::variant<uint16_t, Shift> offset;
|
||||
uint8_t rd;
|
||||
uint8_t rn;
|
||||
bool load;
|
||||
bool write;
|
||||
bool byte;
|
||||
bool up;
|
||||
bool pre;
|
||||
};
|
||||
|
||||
struct HalfwordTransfer {
|
||||
uint8_t offset;
|
||||
bool half;
|
||||
bool sign;
|
||||
uint8_t rd;
|
||||
uint8_t rn;
|
||||
bool load;
|
||||
bool write;
|
||||
bool byte;
|
||||
bool imm;
|
||||
bool up;
|
||||
bool pre;
|
||||
};
|
||||
|
||||
struct CoprocessorDataTransfer {
|
||||
uint8_t offset;
|
||||
uint8_t cpn;
|
||||
uint8_t crd;
|
||||
uint8_t rn;
|
||||
bool load;
|
||||
bool write;
|
||||
bool len;
|
||||
bool up;
|
||||
bool pre;
|
||||
};
|
||||
|
||||
struct CoprocessorDataOperation {
|
||||
uint8_t crm;
|
||||
uint8_t cp;
|
||||
uint8_t cpn;
|
||||
uint8_t crd;
|
||||
uint8_t crn;
|
||||
uint8_t cp_opc;
|
||||
};
|
||||
|
||||
struct CoprocessorRegisterTransfer {
|
||||
uint8_t crm;
|
||||
uint8_t cp;
|
||||
uint8_t cpn;
|
||||
uint8_t rd;
|
||||
uint8_t crn;
|
||||
bool load;
|
||||
uint8_t cp_opc;
|
||||
};
|
||||
|
||||
struct Undefined {};
|
||||
struct SoftwareInterrupt {};
|
||||
|
||||
using InstructionData = std::variant<BranchAndExchange,
|
||||
Branch,
|
||||
Multiply,
|
||||
MultiplyLong,
|
||||
SingleDataSwap,
|
||||
SingleDataTransfer,
|
||||
HalfwordTransfer,
|
||||
CoprocessorDataTransfer,
|
||||
CoprocessorDataOperation,
|
||||
CoprocessorRegisterTransfer,
|
||||
Undefined,
|
||||
SoftwareInterrupt>;
|
||||
|
||||
private:
|
||||
Condition condition;
|
||||
InstructionData data;
|
||||
};
|
@@ -1,4 +1,6 @@
|
||||
#include "utility.hh"
|
||||
#include "util/bits.hh"
|
||||
#include <bit>
|
||||
|
||||
std::ostream&
|
||||
operator<<(std::ostream& os, const Condition cond) {
|
||||
@@ -33,6 +35,47 @@ operator<<(std::ostream& os, const Condition cond) {
|
||||
return os;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
eval_shift(ShiftType shift_type, uint32_t value, uint8_t amount, bool& carry) {
|
||||
switch (shift_type) {
|
||||
case ShiftType::LSL:
|
||||
|
||||
if (amount > 0 && amount <= 32)
|
||||
carry = get_nth_bit(value, 32 - amount);
|
||||
else if (amount > 32)
|
||||
carry = 0;
|
||||
|
||||
return value << amount;
|
||||
case ShiftType::LSR:
|
||||
|
||||
if (amount > 0 && amount <= 32)
|
||||
carry = get_nth_bit(value, amount - 1);
|
||||
else if (amount > 32)
|
||||
carry = 0;
|
||||
else
|
||||
carry = get_nth_bit(value, 31);
|
||||
|
||||
return value >> amount;
|
||||
case ShiftType::ASR:
|
||||
if (amount > 0 && amount <= 32)
|
||||
carry = get_nth_bit(value, amount - 1);
|
||||
else
|
||||
carry = get_nth_bit(value, 31);
|
||||
|
||||
return static_cast<int32_t>(value) >> amount;
|
||||
case ShiftType::ROR:
|
||||
if (amount == 0) {
|
||||
bool old_carry = carry;
|
||||
|
||||
carry = get_nth_bit(value, 0);
|
||||
return (value >> 1) | (old_carry << 31);
|
||||
} else {
|
||||
carry = get_nth_bit(value, (amount % 32 + 31) % 32);
|
||||
return std::rotr(value, amount);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::ostream&
|
||||
operator<<(std::ostream& os, const ShiftType shift_type) {
|
||||
|
||||
|
@@ -72,6 +72,20 @@ enum class ShiftType {
|
||||
ROR = 0b11
|
||||
};
|
||||
|
||||
struct ShiftData {
|
||||
ShiftType type;
|
||||
bool immediate;
|
||||
uint8_t operand;
|
||||
};
|
||||
|
||||
struct Shift {
|
||||
uint8_t rm;
|
||||
ShiftData data;
|
||||
};
|
||||
|
||||
uint32_t
|
||||
eval_shift(ShiftType shift_type, uint32_t value, uint8_t amount, bool& carry);
|
||||
|
||||
// https://fmt.dev/dev/api.html#std-ostream-support
|
||||
std::ostream&
|
||||
operator<<(std::ostream& os, const ShiftType cond);
|
||||
|
@@ -4,7 +4,7 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
typedef struct {
|
||||
struct Header {
|
||||
enum class UniqueCode {
|
||||
Old, // old games
|
||||
New, // new games
|
||||
@@ -42,4 +42,4 @@ typedef struct {
|
||||
BootMode multiboot;
|
||||
uint32_t multiboot_entrypoint;
|
||||
uint8_t slave_id;
|
||||
} Header;
|
||||
};
|
||||
|
Reference in New Issue
Block a user