[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 <fstream>
 | 
				
			||||||
#include <iostream>
 | 
					#include <iostream>
 | 
				
			||||||
#include <memory>
 | 
					#include <memory>
 | 
				
			||||||
 | 
					#include <unistd.h>
 | 
				
			||||||
#include <vector>
 | 
					#include <vector>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// NOLINTBEGIN
 | 
					// NOLINTBEGIN
 | 
				
			||||||
@@ -83,7 +84,10 @@ main(int argc, const char* argv[]) {
 | 
				
			|||||||
        Memory memory(std::move(bios), std::move(rom));
 | 
					        Memory memory(std::move(bios), std::move(rom));
 | 
				
			||||||
        Bus bus(memory);
 | 
					        Bus bus(memory);
 | 
				
			||||||
        Cpu cpu(bus);
 | 
					        Cpu cpu(bus);
 | 
				
			||||||
 | 
					        while (true) {
 | 
				
			||||||
            cpu.step();
 | 
					            cpu.step();
 | 
				
			||||||
 | 
					            sleep(1);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return 0;
 | 
					    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(
 | 
					headers += files(
 | 
				
			||||||
  'cpu.hh',
 | 
					  'cpu.hh',
 | 
				
			||||||
 | 
					  'instruction.hh',
 | 
				
			||||||
  'psr.hh',
 | 
					  'psr.hh',
 | 
				
			||||||
  'utility.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 "cpu.hh"
 | 
				
			||||||
 | 
					#include "util/bits.hh"
 | 
				
			||||||
#include "util/log.hh"
 | 
					#include "util/log.hh"
 | 
				
			||||||
#include "utility.hh"
 | 
					#include "utility.hh"
 | 
				
			||||||
#include <algorithm>
 | 
					#include <algorithm>
 | 
				
			||||||
@@ -112,16 +113,276 @@ Cpu::chg_mode(const Mode to) {
 | 
				
			|||||||
    cpsr.set_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
 | 
					void
 | 
				
			||||||
Cpu::step() {
 | 
					Cpu::step() {
 | 
				
			||||||
    uint32_t cur_pc = pc - 2 * ARM_INSTRUCTION_SIZE;
 | 
					    uint32_t cur_pc = pc - 2 * ARM_INSTRUCTION_SIZE;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (cpsr.state() == State::Arm) {
 | 
					    if (cpsr.state() == State::Arm) {
 | 
				
			||||||
 | 
					        ArmInstruction instruction(bus->read_word(cur_pc));
 | 
				
			||||||
        log_info("{:#034b}", 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;
 | 
					        pc += ARM_INSTRUCTION_SIZE;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,6 +1,7 @@
 | 
				
			|||||||
#pragma once
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "bus.hh"
 | 
					#include "bus.hh"
 | 
				
			||||||
 | 
					#include "instruction.hh"
 | 
				
			||||||
#include "psr.hh"
 | 
					#include "psr.hh"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <cstdint>
 | 
					#include <cstdint>
 | 
				
			||||||
@@ -50,5 +51,5 @@ class Cpu {
 | 
				
			|||||||
    } spsr_banked; // banked saved program status registers
 | 
					    } spsr_banked; // banked saved program status registers
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    void chg_mode(const Mode to);
 | 
					    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 "instruction.hh"
 | 
				
			||||||
#include "cpu/utility.hh"
 | 
					 | 
				
			||||||
#include "util/bits.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
 | 
					    // Branch and exhcange
 | 
				
			||||||
    if ((insn & 0x0FFFFFF0) == 0x012FFF10) {
 | 
					    if ((insn & 0x0FFFFFF0) == 0x012FFF10) {
 | 
				
			||||||
        static constexpr stringv syn = "BX";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        uint8_t rn = insn & 0b1111;
 | 
					        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
 | 
					        // Branch
 | 
				
			||||||
    } else if ((insn & 0x0E000000) == 0x0A000000) {
 | 
					    } else if ((insn & 0x0E000000) == 0x0A000000) {
 | 
				
			||||||
        static constexpr stringv syn = "B";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        bool link       = get_nth_bit(insn, 24);
 | 
					        bool link       = get_nth_bit(insn, 24);
 | 
				
			||||||
        uint32_t offset = get_bit_range(insn, 0, 23);
 | 
					        uint32_t offset = get_bit_range(insn, 0, 23);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        disassembled =
 | 
					        data = Branch{ .link = link, .offset = offset };
 | 
				
			||||||
          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;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Multiply
 | 
					        // Multiply
 | 
				
			||||||
    } else if ((insn & 0x0FC000F0) == 0x00000090) {
 | 
					    } else if ((insn & 0x0FC000F0) == 0x00000090) {
 | 
				
			||||||
        static constexpr array<stringv, 2> syn = { "MUL", "MLA" };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        uint8_t rm = get_bit_range(insn, 0, 3);
 | 
					        uint8_t rm = get_bit_range(insn, 0, 3);
 | 
				
			||||||
        uint8_t rs = get_bit_range(insn, 8, 11);
 | 
					        uint8_t rs = get_bit_range(insn, 8, 11);
 | 
				
			||||||
        uint8_t rn = get_bit_range(insn, 12, 15);
 | 
					        uint8_t rn = get_bit_range(insn, 12, 15);
 | 
				
			||||||
        uint8_t rd = get_bit_range(insn, 16, 19);
 | 
					        uint8_t rd = get_bit_range(insn, 16, 19);
 | 
				
			||||||
        bool s     = get_nth_bit(insn, 20);
 | 
					        bool set   = get_nth_bit(insn, 20);
 | 
				
			||||||
        bool a     = get_nth_bit(insn, 21);
 | 
					        bool acc   = get_nth_bit(insn, 21);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (rd == rm)
 | 
					        data = Multiply{
 | 
				
			||||||
            log_error("rd and rm are not distinct in {} : {:d}", syn[a], rd);
 | 
					            .rm = rm, .rs = rs, .rn = rn, .rd = rd, .set = set, .acc = acc
 | 
				
			||||||
 | 
					 | 
				
			||||||
        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" } }
 | 
					 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Multiply long
 | 
				
			||||||
 | 
					    } else if ((insn & 0x0F8000F0) == 0x00800090) {
 | 
				
			||||||
        uint8_t rm   = get_bit_range(insn, 0, 3);
 | 
					        uint8_t rm   = get_bit_range(insn, 0, 3);
 | 
				
			||||||
        uint8_t rs   = get_bit_range(insn, 8, 11);
 | 
					        uint8_t rs   = get_bit_range(insn, 8, 11);
 | 
				
			||||||
        uint8_t rdlo = get_bit_range(insn, 12, 15);
 | 
					        uint8_t rdlo = get_bit_range(insn, 12, 15);
 | 
				
			||||||
        uint8_t rdhi = get_bit_range(insn, 16, 19);
 | 
					        uint8_t rdhi = get_bit_range(insn, 16, 19);
 | 
				
			||||||
        bool s       = get_nth_bit(insn, 20);
 | 
					        bool set     = get_nth_bit(insn, 20);
 | 
				
			||||||
        bool a       = get_nth_bit(insn, 21);
 | 
					        bool acc     = get_nth_bit(insn, 21);
 | 
				
			||||||
        bool u       = get_nth_bit(insn, 22);
 | 
					        bool uns     = get_nth_bit(insn, 22);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (rdhi == rdlo || rdhi == rm || rdlo == rm)
 | 
					        data = MultiplyLong{ .rm   = rm,
 | 
				
			||||||
            log_error("rdhi, rdlo and rm are not distinct in {}", syn[u][a]);
 | 
					                             .rs   = rs,
 | 
				
			||||||
 | 
					                             .rdlo = rdlo,
 | 
				
			||||||
 | 
					                             .rdhi = rdhi,
 | 
				
			||||||
 | 
					                             .set  = set,
 | 
				
			||||||
 | 
					                             .acc  = acc,
 | 
				
			||||||
 | 
					                             .uns  = uns };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        pc_error(rdhi, syn[u][a]);
 | 
					        // Undefined
 | 
				
			||||||
        pc_error(rdlo, syn[u][a]);
 | 
					    } else if ((insn & 0x0E000010) == 0x06000010) {
 | 
				
			||||||
        pc_error(rm, syn[u][a]);
 | 
					        data = Undefined{};
 | 
				
			||||||
        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);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Single data swap
 | 
					        // Single data swap
 | 
				
			||||||
    } else if ((insn & 0x0FB00FF0) == 0x01000090) {
 | 
					    } else if ((insn & 0x0FB00FF0) == 0x01000090) {
 | 
				
			||||||
        static constexpr stringv syn = "SWP";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        uint8_t rm = get_bit_range(insn, 0, 3);
 | 
					        uint8_t rm = get_bit_range(insn, 0, 3);
 | 
				
			||||||
        uint8_t rd = get_bit_range(insn, 12, 15);
 | 
					        uint8_t rd = get_bit_range(insn, 12, 15);
 | 
				
			||||||
        uint8_t rn = get_bit_range(insn, 16, 19);
 | 
					        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);
 | 
					        data = SingleDataSwap{ .rm = rm, .rd = rd, .rn = rn, .byte = byte };
 | 
				
			||||||
        pc_undefined(rn, syn);
 | 
					 | 
				
			||||||
        pc_undefined(rd, syn);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        disassembled = fmt::format(
 | 
					        // Single data transfer
 | 
				
			||||||
          "{}{}{} R{:d},R{:d},[R{:d}]", syn, cond, (b ? "B" : ""), rd, rm, rn);
 | 
					    } else if ((insn & 0x0C000000) == 0x04000000) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (cpsr.condition(cond)) {
 | 
					        std::variant<uint16_t, Shift> offset;
 | 
				
			||||||
            if (b) {
 | 
					        uint8_t rd = get_bit_range(insn, 12, 15);
 | 
				
			||||||
                gpr[rd] = bus->read_byte(gpr[rn]);
 | 
					        uint8_t rn = get_bit_range(insn, 16, 19);
 | 
				
			||||||
                bus->write_byte(gpr[rn], gpr[rm] & 0xFF);
 | 
					        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 {
 | 
					        } else {
 | 
				
			||||||
                gpr[rd] = bus->read_word(gpr[rn]);
 | 
					            offset = static_cast<uint16_t>(get_bit_range(insn, 0, 11));
 | 
				
			||||||
                bus->write_word(gpr[rn], gpr[rm]);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        data = SingleDataTransfer{ .offset = offset,
 | 
				
			||||||
 | 
					                                   .rd     = rd,
 | 
				
			||||||
 | 
					                                   .rn     = rn,
 | 
				
			||||||
 | 
					                                   .load   = load,
 | 
				
			||||||
 | 
					                                   .write  = write,
 | 
				
			||||||
 | 
					                                   .byte   = byte,
 | 
				
			||||||
 | 
					                                   .up     = up,
 | 
				
			||||||
 | 
					                                   .pre    = pre };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Halfword transfer
 | 
					        // Halfword transfer
 | 
				
			||||||
        // TODO: create abstraction to reuse for block data and single data
 | 
					 | 
				
			||||||
        // transfer
 | 
					 | 
				
			||||||
    } else if ((insn & 0x0E000090) == 0x00000090) {
 | 
					    } 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);
 | 
				
			||||||
        uint8_t rm      = get_bit_range(insn, 0, 3);
 | 
					        bool sign      = get_nth_bit(insn, 6);
 | 
				
			||||||
        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 rd     = get_bit_range(insn, 12, 15);
 | 
				
			||||||
        uint8_t rn     = get_bit_range(insn, 16, 19);
 | 
					        uint8_t rn     = get_bit_range(insn, 16, 19);
 | 
				
			||||||
        bool l          = get_nth_bit(insn, 20);
 | 
					        bool load      = get_nth_bit(insn, 20);
 | 
				
			||||||
        bool w          = get_nth_bit(insn, 21);
 | 
					        bool write     = get_nth_bit(insn, 21);
 | 
				
			||||||
        bool imm       = get_nth_bit(insn, 22);
 | 
					        bool imm       = get_nth_bit(insn, 22);
 | 
				
			||||||
        bool u          = get_nth_bit(insn, 23);
 | 
					        bool up        = get_nth_bit(insn, 23);
 | 
				
			||||||
        bool p          = get_nth_bit(insn, 24);
 | 
					        bool pre       = get_nth_bit(insn, 24);
 | 
				
			||||||
        uint32_t offset = 0;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        std::string syn =
 | 
					        offset |= (imm ? get_bit_range(insn, 8, 11) << 2 : 0);
 | 
				
			||||||
          fmt::format("{}{}{}", syn_[l], (s ? "S" : ""), (h ? 'H' : 'B'));
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (!p && w)
 | 
					        data = HalfwordTransfer{ .offset = offset,
 | 
				
			||||||
            log_error("Write-back enabled with post-indexing in {}", syn);
 | 
					                                 .half   = half,
 | 
				
			||||||
 | 
					                                 .sign   = sign,
 | 
				
			||||||
 | 
					                                 .rd     = rd,
 | 
				
			||||||
 | 
					                                 .rn     = rn,
 | 
				
			||||||
 | 
					                                 .load   = load,
 | 
				
			||||||
 | 
					                                 .write  = write,
 | 
				
			||||||
 | 
					                                 .imm    = imm,
 | 
				
			||||||
 | 
					                                 .up     = up,
 | 
				
			||||||
 | 
					                                 .pre    = pre };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (s && !l)
 | 
					        // Block data transfer
 | 
				
			||||||
            log_error("Signed data found in {}", syn);
 | 
					    } else if ((insn & 0x0E000000) == 0x08000000) {
 | 
				
			||||||
 | 
					        /*static constexpr array<stringv, 2> syn = { "STM", "LDM" };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (w)
 | 
					        uint16_t regs = get_bit_range(insn, 0, 15);
 | 
				
			||||||
            pc_error(rn, syn);
 | 
					        uint8_t rn    = get_bit_range(insn, 16, 19);
 | 
				
			||||||
        pc_error(rm, syn);
 | 
					        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]);
 | 
					            uint8_t lpu = load << 2 | pre << 1 | up;
 | 
				
			||||||
            std::string offset_str = fmt::format("{}{}{:d}",
 | 
					            std::string addr_mode;
 | 
				
			||||||
                                                 (u ? "" : "-"),
 | 
					 | 
				
			||||||
                                                 (imm ? '#' : 'R'),
 | 
					 | 
				
			||||||
                                                 (imm ? offset : rm));
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
            disassembled = fmt::format(
 | 
					            switch(lpu) {
 | 
				
			||||||
              "{}{}{}{} 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)) {
 | 
					        data = Undefined{};
 | 
				
			||||||
            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;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Software Interrupt
 | 
					        // Software Interrupt
 | 
				
			||||||
        // What to do here?
 | 
					        // What to do here?
 | 
				
			||||||
    } else if ((insn & 0x0F000000) == 0x0F000000) {
 | 
					    } else if ((insn & 0x0F000000) == 0x0F000000) {
 | 
				
			||||||
        static constexpr stringv syn = "SWI";
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (cpsr.condition(cond)) {
 | 
					        data = SoftwareInterrupt{};
 | 
				
			||||||
            chg_mode(Mode::Supervisor);
 | 
					 | 
				
			||||||
            pc   = 0x08;
 | 
					 | 
				
			||||||
            spsr = cpsr;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        disassembled = fmt::format("{}{} 0", syn, cond);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Coprocessor data transfer
 | 
					        // Coprocessor data transfer
 | 
				
			||||||
    } else if ((insn & 0x0E000000) == 0x0C000000) {
 | 
					    } else if ((insn & 0x0E000000) == 0x0C000000) {
 | 
				
			||||||
        static constexpr array<stringv, 2> syn = { "STC", "LDC" };
 | 
					        uint8_t offset = get_bit_range(insn, 0, 7);
 | 
				
			||||||
 | 
					 | 
				
			||||||
        [[maybe_unused]] uint8_t offset = get_bit_range(insn, 0, 7);
 | 
					 | 
				
			||||||
        uint8_t cpn    = get_bit_range(insn, 8, 11);
 | 
					        uint8_t cpn    = get_bit_range(insn, 8, 11);
 | 
				
			||||||
        uint8_t crd    = get_bit_range(insn, 12, 15);
 | 
					        uint8_t crd    = get_bit_range(insn, 12, 15);
 | 
				
			||||||
        [[maybe_unused]] uint8_t rn     = get_bit_range(insn, 16, 19);
 | 
					        uint8_t rn     = get_bit_range(insn, 16, 19);
 | 
				
			||||||
        bool l                          = get_nth_bit(insn, 20);
 | 
					        bool load      = get_nth_bit(insn, 20);
 | 
				
			||||||
        [[maybe_unused]] bool w         = get_nth_bit(insn, 21);
 | 
					        bool write     = get_nth_bit(insn, 21);
 | 
				
			||||||
        bool n                          = get_nth_bit(insn, 22);
 | 
					        bool len       = get_nth_bit(insn, 22);
 | 
				
			||||||
        [[maybe_unused]] bool u         = get_nth_bit(insn, 23);
 | 
					        bool up        = get_nth_bit(insn, 23);
 | 
				
			||||||
        [[maybe_unused]] bool p         = get_nth_bit(insn, 24);
 | 
					        bool pre       = get_nth_bit(insn, 24);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        disassembled = fmt::format(
 | 
					        data = CoprocessorDataTransfer{ .offset = offset,
 | 
				
			||||||
          "{}{}{} p{},c{},{}", syn[l], cond, (n ? "L" : ""), cpn, crd, "a.");
 | 
					                                        .cpn    = cpn,
 | 
				
			||||||
 | 
					                                        .crd    = crd,
 | 
				
			||||||
        log_error("Unimplemented instruction: {}", syn[l]);
 | 
					                                        .rn     = rn,
 | 
				
			||||||
 | 
					                                        .load   = load,
 | 
				
			||||||
 | 
					                                        .write  = write,
 | 
				
			||||||
 | 
					                                        .len    = len,
 | 
				
			||||||
 | 
					                                        .up     = up,
 | 
				
			||||||
 | 
					                                        .pre    = pre };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Coprocessor data operation
 | 
					        // Coprocessor data operation
 | 
				
			||||||
    } else if ((insn & 0x0F000010) == 0x0E000000) {
 | 
					    } else if ((insn & 0x0F000010) == 0x0E000000) {
 | 
				
			||||||
        static constexpr stringv syn = "CDP";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        uint8_t crm    = get_bit_range(insn, 0, 4);
 | 
					        uint8_t crm    = get_bit_range(insn, 0, 4);
 | 
				
			||||||
        uint8_t cp     = get_bit_range(insn, 5, 7);
 | 
					        uint8_t cp     = get_bit_range(insn, 5, 7);
 | 
				
			||||||
        uint8_t cpn    = get_bit_range(insn, 8, 11);
 | 
					        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 crn    = get_bit_range(insn, 16, 19);
 | 
				
			||||||
        uint8_t cp_opc = get_bit_range(insn, 20, 23);
 | 
					        uint8_t cp_opc = get_bit_range(insn, 20, 23);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        disassembled = fmt::format("{}{} p{},{},c{},c{},c{},{}",
 | 
					        data = CoprocessorDataOperation{ .crm    = crm,
 | 
				
			||||||
                                   syn,
 | 
					                                         .cp     = cp,
 | 
				
			||||||
                                   cond,
 | 
					                                         .cpn    = cpn,
 | 
				
			||||||
                                   cpn,
 | 
					                                         .crd    = crd,
 | 
				
			||||||
                                   cp_opc,
 | 
					                                         .crn    = crn,
 | 
				
			||||||
                                   crd,
 | 
					                                         .cp_opc = cp_opc };
 | 
				
			||||||
                                   crn,
 | 
					 | 
				
			||||||
                                   crm,
 | 
					 | 
				
			||||||
                                   cp);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        log_error("Unimplemented instruction: {}", syn);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Coprocessor register transfer
 | 
					        // Coprocessor register transfer
 | 
				
			||||||
    } else if ((insn & 0x0F000010) == 0x0E000010) {
 | 
					    } else if ((insn & 0x0F000010) == 0x0E000010) {
 | 
				
			||||||
        static constexpr array<stringv, 2> syn = { "MCR", "MRC" };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        uint8_t crm    = get_bit_range(insn, 0, 4);
 | 
					        uint8_t crm    = get_bit_range(insn, 0, 4);
 | 
				
			||||||
        uint8_t cp     = get_bit_range(insn, 5, 7);
 | 
					        uint8_t cp     = get_bit_range(insn, 5, 7);
 | 
				
			||||||
        uint8_t cpn    = get_bit_range(insn, 8, 11);
 | 
					        uint8_t cpn    = get_bit_range(insn, 8, 11);
 | 
				
			||||||
        uint8_t rd     = get_bit_range(insn, 12, 15);
 | 
					        uint8_t rd     = get_bit_range(insn, 12, 15);
 | 
				
			||||||
        uint8_t crn    = get_bit_range(insn, 16, 19);
 | 
					        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);
 | 
					        uint8_t cp_opc = get_bit_range(insn, 21, 23);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        disassembled = fmt::format("{}{} p{},{},c{},c{},c{},{}",
 | 
					        data = CoprocessorRegisterTransfer{ .crm    = crm,
 | 
				
			||||||
                                   syn[l],
 | 
					                                            .cp     = cp,
 | 
				
			||||||
                                   cond,
 | 
					                                            .cpn    = cpn,
 | 
				
			||||||
                                   cpn,
 | 
					                                            .rd     = rd,
 | 
				
			||||||
                                   cp_opc,
 | 
					                                            .crn    = crn,
 | 
				
			||||||
                                   rd,
 | 
					                                            .load   = load,
 | 
				
			||||||
                                   crn,
 | 
					                                            .cp_opc = cp_opc };
 | 
				
			||||||
                                   crm,
 | 
					    } else {
 | 
				
			||||||
                                   cp);
 | 
					        data = Undefined{};
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
        log_error("Unimplemented instruction: {}", syn[l]);
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    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 "utility.hh"
 | 
				
			||||||
 | 
					#include "util/bits.hh"
 | 
				
			||||||
 | 
					#include <bit>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
std::ostream&
 | 
					std::ostream&
 | 
				
			||||||
operator<<(std::ostream& os, const Condition cond) {
 | 
					operator<<(std::ostream& os, const Condition cond) {
 | 
				
			||||||
@@ -33,6 +35,47 @@ operator<<(std::ostream& os, const Condition cond) {
 | 
				
			|||||||
    return os;
 | 
					    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&
 | 
					std::ostream&
 | 
				
			||||||
operator<<(std::ostream& os, const ShiftType shift_type) {
 | 
					operator<<(std::ostream& os, const ShiftType shift_type) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -72,6 +72,20 @@ enum class ShiftType {
 | 
				
			|||||||
    ROR = 0b11
 | 
					    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
 | 
					// https://fmt.dev/dev/api.html#std-ostream-support
 | 
				
			||||||
std::ostream&
 | 
					std::ostream&
 | 
				
			||||||
operator<<(std::ostream& os, const ShiftType cond);
 | 
					operator<<(std::ostream& os, const ShiftType cond);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,7 +4,7 @@
 | 
				
			|||||||
#include <string>
 | 
					#include <string>
 | 
				
			||||||
#include <vector>
 | 
					#include <vector>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
typedef struct {
 | 
					struct Header {
 | 
				
			||||||
    enum class UniqueCode {
 | 
					    enum class UniqueCode {
 | 
				
			||||||
        Old,        // old games
 | 
					        Old,        // old games
 | 
				
			||||||
        New,        // new games
 | 
					        New,        // new games
 | 
				
			||||||
@@ -42,4 +42,4 @@ typedef struct {
 | 
				
			|||||||
    BootMode multiboot;
 | 
					    BootMode multiboot;
 | 
				
			||||||
    uint32_t multiboot_entrypoint;
 | 
					    uint32_t multiboot_entrypoint;
 | 
				
			||||||
    uint8_t slave_id;
 | 
					    uint8_t slave_id;
 | 
				
			||||||
} Header;
 | 
					};
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user