[UNTESTED] refactor how instructions are parsed
Signed-off-by: Amneesh Singh <natto@weirdnatto.in>
This commit is contained in:
		
							
								
								
									
										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);
 | 
			
		||||
            } else {
 | 
			
		||||
                gpr[rd] = bus->read_word(gpr[rn]);
 | 
			
		||||
                bus->write_word(gpr[rn], gpr[rm]);
 | 
			
		||||
            }
 | 
			
		||||
        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 {
 | 
			
		||||
            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 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 load      = get_nth_bit(insn, 20);
 | 
			
		||||
        bool write     = get_nth_bit(insn, 21);
 | 
			
		||||
        bool imm       = get_nth_bit(insn, 22);
 | 
			
		||||
        bool up        = get_nth_bit(insn, 23);
 | 
			
		||||
        bool pre       = get_nth_bit(insn, 24);
 | 
			
		||||
 | 
			
		||||
        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 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 imm        = get_nth_bit(insn, 22);
 | 
			
		||||
        bool u          = get_nth_bit(insn, 23);
 | 
			
		||||
        bool p          = get_nth_bit(insn, 24);
 | 
			
		||||
        uint32_t offset = 0;
 | 
			
		||||
        offset |= (imm ? get_bit_range(insn, 8, 11) << 2 : 0);
 | 
			
		||||
 | 
			
		||||
        std::string syn =
 | 
			
		||||
          fmt::format("{}{}{}", syn_[l], (s ? "S" : ""), (h ? 'H' : 'B'));
 | 
			
		||||
        data = HalfwordTransfer{ .offset = offset,
 | 
			
		||||
                                 .half   = half,
 | 
			
		||||
                                 .sign   = sign,
 | 
			
		||||
                                 .rd     = rd,
 | 
			
		||||
                                 .rn     = rn,
 | 
			
		||||
                                 .load   = load,
 | 
			
		||||
                                 .write  = write,
 | 
			
		||||
                                 .imm    = imm,
 | 
			
		||||
                                 .up     = up,
 | 
			
		||||
                                 .pre    = pre };
 | 
			
		||||
 | 
			
		||||
        if (!p && w)
 | 
			
		||||
            log_error("Write-back enabled with post-indexing in {}", syn);
 | 
			
		||||
        // Block data transfer
 | 
			
		||||
    } else if ((insn & 0x0E000000) == 0x08000000) {
 | 
			
		||||
        /*static constexpr array<stringv, 2> syn = { "STM", "LDM" };
 | 
			
		||||
 | 
			
		||||
        if (s && !l)
 | 
			
		||||
            log_error("Signed data found in {}", syn);
 | 
			
		||||
 | 
			
		||||
        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)));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        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]);
 | 
			
		||||
                }
 | 
			
		||||
            switch(lpu) {
 | 
			
		||||
            }
 | 
			
		||||
            }*/
 | 
			
		||||
 | 
			
		||||
            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" };
 | 
			
		||||
        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);
 | 
			
		||||
        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);
 | 
			
		||||
 | 
			
		||||
        [[maybe_unused]] 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);
 | 
			
		||||
 | 
			
		||||
        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);
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user