tests: add execution tests
all but data processing Signed-off-by: Amneesh Singh <natto@weirdnatto.in>
This commit is contained in:
		@@ -1,7 +1,7 @@
 | 
			
		||||
#include "bus.hh"
 | 
			
		||||
#include <memory>
 | 
			
		||||
 | 
			
		||||
Bus::Bus(Memory& memory)
 | 
			
		||||
Bus::Bus(const Memory& memory)
 | 
			
		||||
  : memory(std::make_shared<Memory>(memory)) {}
 | 
			
		||||
 | 
			
		||||
uint8_t
 | 
			
		||||
@@ -31,5 +31,5 @@ Bus::read_word(size_t address) {
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
Bus::write_word(size_t address, uint32_t word) {
 | 
			
		||||
    memory->write_halfword(address, word);
 | 
			
		||||
    memory->write_word(address, word);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										590
									
								
								src/cpu/arm/exec.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										590
									
								
								src/cpu/arm/exec.cc
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,590 @@
 | 
			
		||||
#include "cpu/cpu-impl.hh"
 | 
			
		||||
#include "util/bits.hh"
 | 
			
		||||
#include "util/log.hh"
 | 
			
		||||
 | 
			
		||||
using namespace logger;
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
CpuImpl::exec_arm(const arm::Instruction instruction) {
 | 
			
		||||
    auto cond = instruction.condition;
 | 
			
		||||
    auto data = instruction.data;
 | 
			
		||||
 | 
			
		||||
    if (!cpsr.condition(cond)) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    auto pc_error = [](uint8_t r) {
 | 
			
		||||
        if (r == PC_INDEX)
 | 
			
		||||
            log_error("Using PC (R15) as operand register");
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    auto pc_warn = [](uint8_t r) {
 | 
			
		||||
        if (r == PC_INDEX)
 | 
			
		||||
            log_warn("Using PC (R15) as operand register");
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    using namespace arm;
 | 
			
		||||
 | 
			
		||||
    std::visit(
 | 
			
		||||
      overloaded{
 | 
			
		||||
        [this, pc_warn](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_bit(pc, 0);
 | 
			
		||||
 | 
			
		||||
            if (state == State::Arm)
 | 
			
		||||
                rst_bit(pc, 1);
 | 
			
		||||
 | 
			
		||||
            // pc is affected so flush the pipeline
 | 
			
		||||
            is_flushed = true;
 | 
			
		||||
        },
 | 
			
		||||
        [this](Branch& data) {
 | 
			
		||||
            if (data.link)
 | 
			
		||||
                gpr[14] = pc - INSTRUCTION_SIZE;
 | 
			
		||||
 | 
			
		||||
            // data.offset accounts for two instructions ahead when
 | 
			
		||||
            // disassembling, so need to adjust
 | 
			
		||||
            pc = static_cast<int32_t>(pc) - 2 * INSTRUCTION_SIZE + data.offset;
 | 
			
		||||
 | 
			
		||||
            // pc is affected so flush the pipeline
 | 
			
		||||
            is_flushed = true;
 | 
			
		||||
        },
 | 
			
		||||
        [this, pc_error](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(gpr[data.rd] == 0);
 | 
			
		||||
                cpsr.set_n(get_bit(gpr[data.rd], 31));
 | 
			
		||||
                cpsr.set_c(0);
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        [this, pc_error](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) {
 | 
			
		||||
                auto cast = [](uint32_t x) -> uint64_t {
 | 
			
		||||
                    return static_cast<uint64_t>(x);
 | 
			
		||||
                };
 | 
			
		||||
 | 
			
		||||
                uint64_t eval = cast(gpr[data.rm]) * cast(gpr[data.rs]) +
 | 
			
		||||
                                (data.acc ? (cast(gpr[data.rdhi]) << 32) |
 | 
			
		||||
                                              cast(gpr[data.rdlo])
 | 
			
		||||
                                          : 0);
 | 
			
		||||
 | 
			
		||||
                gpr[data.rdlo] = bit_range(eval, 0, 31);
 | 
			
		||||
                gpr[data.rdhi] = bit_range(eval, 32, 63);
 | 
			
		||||
 | 
			
		||||
            } else {
 | 
			
		||||
                auto cast = [](uint32_t x) -> int64_t {
 | 
			
		||||
                    return static_cast<int64_t>(static_cast<int32_t>(x));
 | 
			
		||||
                };
 | 
			
		||||
 | 
			
		||||
                int64_t eval = cast(gpr[data.rm]) * cast(gpr[data.rs]) +
 | 
			
		||||
                               (data.acc ? (cast(gpr[data.rdhi]) << 32) |
 | 
			
		||||
                                             cast(gpr[data.rdlo])
 | 
			
		||||
                                         : 0);
 | 
			
		||||
 | 
			
		||||
                gpr[data.rdlo] = bit_range(eval, 0, 31);
 | 
			
		||||
                gpr[data.rdhi] = bit_range(eval, 32, 63);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (data.set) {
 | 
			
		||||
                cpsr.set_z(gpr[data.rdhi] == 0 && gpr[data.rdlo] == 0);
 | 
			
		||||
                cpsr.set_n(get_bit(gpr[data.rdhi], 31));
 | 
			
		||||
                cpsr.set_c(0);
 | 
			
		||||
                cpsr.set_v(0);
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        [](Undefined) { log_warn("Undefined instruction"); },
 | 
			
		||||
        [this, pc_error](SingleDataSwap& data) {
 | 
			
		||||
            pc_error(data.rm);
 | 
			
		||||
            pc_error(data.rn);
 | 
			
		||||
            pc_error(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](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.rn == PC_INDEX && data.write)
 | 
			
		||||
                log_warn("Write-back enabled with base register as PC {}",
 | 
			
		||||
                         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);
 | 
			
		||||
 | 
			
		||||
                offset =
 | 
			
		||||
                  eval_shift(shift->data.type, gpr[shift->rm], amount, carry);
 | 
			
		||||
 | 
			
		||||
                cpsr.set_c(carry);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // PC is always two instructions ahead
 | 
			
		||||
            if (data.rn == PC_INDEX)
 | 
			
		||||
                address -= 2 * 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 == PC_INDEX)
 | 
			
		||||
                    address += 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;
 | 
			
		||||
 | 
			
		||||
            if (data.rd == PC_INDEX && data.load)
 | 
			
		||||
                is_flushed = true;
 | 
			
		||||
        },
 | 
			
		||||
        [this, pc_warn, pc_error](HalfwordTransfer& data) {
 | 
			
		||||
            uint32_t address = gpr[data.rn];
 | 
			
		||||
            uint32_t offset  = 0;
 | 
			
		||||
 | 
			
		||||
            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);
 | 
			
		||||
                offset = gpr[data.offset];
 | 
			
		||||
            } else {
 | 
			
		||||
                offset = data.offset;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // PC is always two instructions ahead
 | 
			
		||||
            if (data.rn == PC_INDEX)
 | 
			
		||||
                address -= 2 * INSTRUCTION_SIZE;
 | 
			
		||||
 | 
			
		||||
            if (data.pre)
 | 
			
		||||
                address += (data.up ? offset : -offset);
 | 
			
		||||
 | 
			
		||||
            // load
 | 
			
		||||
            if (data.load) {
 | 
			
		||||
                // signed
 | 
			
		||||
                if (data.sign) {
 | 
			
		||||
                    // halfword
 | 
			
		||||
                    if (data.half) {
 | 
			
		||||
                        gpr[data.rd] = bus->read_halfword(address);
 | 
			
		||||
 | 
			
		||||
                        // sign extend the halfword
 | 
			
		||||
                        gpr[data.rd] =
 | 
			
		||||
                          (static_cast<int32_t>(gpr[data.rd]) << 16) >> 16;
 | 
			
		||||
 | 
			
		||||
                        // byte
 | 
			
		||||
                    } else {
 | 
			
		||||
                        gpr[data.rd] = bus->read_byte(address);
 | 
			
		||||
 | 
			
		||||
                        // sign extend the byte
 | 
			
		||||
                        gpr[data.rd] =
 | 
			
		||||
                          (static_cast<int32_t>(gpr[data.rd]) << 24) >> 24;
 | 
			
		||||
                    }
 | 
			
		||||
                    // unsigned halfword
 | 
			
		||||
                } else if (data.half) {
 | 
			
		||||
                    gpr[data.rd] = bus->read_halfword(address);
 | 
			
		||||
                }
 | 
			
		||||
                // store
 | 
			
		||||
            } else {
 | 
			
		||||
                // take PC into consideration
 | 
			
		||||
                if (data.rd == PC_INDEX)
 | 
			
		||||
                    address += INSTRUCTION_SIZE;
 | 
			
		||||
 | 
			
		||||
                // halfword
 | 
			
		||||
                if (data.half)
 | 
			
		||||
                    bus->write_halfword(address, gpr[data.rd]);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (!data.pre)
 | 
			
		||||
                address += (data.up ? offset : -offset);
 | 
			
		||||
 | 
			
		||||
            if (!data.pre || data.write)
 | 
			
		||||
                gpr[data.rn] = address;
 | 
			
		||||
 | 
			
		||||
            if (data.rd == PC_INDEX && data.load)
 | 
			
		||||
                is_flushed = true;
 | 
			
		||||
        },
 | 
			
		||||
        [this, pc_error](BlockDataTransfer& data) {
 | 
			
		||||
            uint32_t address  = gpr[data.rn];
 | 
			
		||||
            Mode mode         = cpsr.mode();
 | 
			
		||||
            uint8_t alignment = 4; // word
 | 
			
		||||
            uint8_t i         = 0;
 | 
			
		||||
            uint8_t n_regs    = std::popcount(data.regs);
 | 
			
		||||
 | 
			
		||||
            pc_error(data.rn);
 | 
			
		||||
 | 
			
		||||
            if (cpsr.mode() == Mode::User && data.s) {
 | 
			
		||||
                log_error("Bit S is set outside priviliged modes in {}",
 | 
			
		||||
                          typeid(data).name());
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // we just change modes to load user registers
 | 
			
		||||
            if ((!get_bit(data.regs, PC_INDEX) && data.s) ||
 | 
			
		||||
                (!data.load && data.s)) {
 | 
			
		||||
                chg_mode(Mode::User);
 | 
			
		||||
 | 
			
		||||
                if (data.write) {
 | 
			
		||||
                    log_error("Write-back enable for user bank registers in {}",
 | 
			
		||||
                              typeid(data).name());
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // account for decrement
 | 
			
		||||
            if (!data.up)
 | 
			
		||||
                address -= (n_regs - 1) * alignment;
 | 
			
		||||
 | 
			
		||||
            if (data.pre)
 | 
			
		||||
                address += (data.up ? alignment : -alignment);
 | 
			
		||||
 | 
			
		||||
            if (data.load) {
 | 
			
		||||
                if (get_bit(data.regs, PC_INDEX) && data.s && data.load) {
 | 
			
		||||
                    // current mode's spsr is already loaded when it was
 | 
			
		||||
                    // switched
 | 
			
		||||
                    spsr = cpsr;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                for (i = 0; i < GPR_COUNT; i++) {
 | 
			
		||||
                    if (get_bit(data.regs, i)) {
 | 
			
		||||
                        gpr[i] = bus->read_word(address);
 | 
			
		||||
                        address += alignment;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                for (i = 0; i < GPR_COUNT; i++) {
 | 
			
		||||
                    if (get_bit(data.regs, i)) {
 | 
			
		||||
                        bus->write_word(address, gpr[i]);
 | 
			
		||||
                        address += alignment;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (!data.pre)
 | 
			
		||||
                address += (data.up ? alignment : -alignment);
 | 
			
		||||
 | 
			
		||||
            // reset back to original address + offset if incremented earlier
 | 
			
		||||
            if (data.up)
 | 
			
		||||
                address -= n_regs * alignment;
 | 
			
		||||
            else
 | 
			
		||||
                address -= alignment;
 | 
			
		||||
 | 
			
		||||
            if (!data.pre || data.write)
 | 
			
		||||
                gpr[data.rn] = address;
 | 
			
		||||
 | 
			
		||||
            if (data.load && get_bit(data.regs, PC_INDEX))
 | 
			
		||||
                is_flushed = true;
 | 
			
		||||
 | 
			
		||||
            // load back the original mode registers
 | 
			
		||||
            chg_mode(mode);
 | 
			
		||||
        },
 | 
			
		||||
        [this, pc_error](PsrTransfer& data) {
 | 
			
		||||
            if (data.spsr && cpsr.mode() == Mode::User) {
 | 
			
		||||
                log_error("Accessing SPSR in User mode in {}",
 | 
			
		||||
                          typeid(data).name());
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            Psr& psr = data.spsr ? spsr : cpsr;
 | 
			
		||||
 | 
			
		||||
            switch (data.type) {
 | 
			
		||||
                case PsrTransfer::Type::Mrs:
 | 
			
		||||
                    pc_error(data.operand);
 | 
			
		||||
                    gpr[data.operand] = psr.raw();
 | 
			
		||||
                    break;
 | 
			
		||||
                case PsrTransfer::Type::Msr:
 | 
			
		||||
                    pc_error(data.operand);
 | 
			
		||||
 | 
			
		||||
                    if (cpsr.mode() != Mode::User) {
 | 
			
		||||
                        psr.set_all(gpr[data.operand]);
 | 
			
		||||
                    }
 | 
			
		||||
                    break;
 | 
			
		||||
                case PsrTransfer::Type::Msr_flg:
 | 
			
		||||
                    uint32_t operand =
 | 
			
		||||
                      (data.imm ? data.operand : gpr[data.operand]);
 | 
			
		||||
                    psr.set_n(get_bit(operand, 31));
 | 
			
		||||
                    psr.set_z(get_bit(operand, 30));
 | 
			
		||||
                    psr.set_c(get_bit(operand, 29));
 | 
			
		||||
                    psr.set_v(get_bit(operand, 28));
 | 
			
		||||
                    break;
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        [this, pc_error](DataProcessing& data) {
 | 
			
		||||
            uint32_t op_1 = gpr[data.rn];
 | 
			
		||||
            uint32_t op_2 = 0;
 | 
			
		||||
 | 
			
		||||
            uint32_t result = 0;
 | 
			
		||||
 | 
			
		||||
            bool overflow = cpsr.v();
 | 
			
		||||
            bool carry    = cpsr.c();
 | 
			
		||||
            bool negative = cpsr.n();
 | 
			
		||||
            bool zero     = cpsr.z();
 | 
			
		||||
 | 
			
		||||
            if (const uint32_t* immediate =
 | 
			
		||||
                  std::get_if<uint32_t>(&data.operand)) {
 | 
			
		||||
                op_2 = *immediate;
 | 
			
		||||
            } else if (const Shift* shift = std::get_if<Shift>(&data.operand)) {
 | 
			
		||||
                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);
 | 
			
		||||
 | 
			
		||||
                op_2 =
 | 
			
		||||
                  eval_shift(shift->data.type, gpr[shift->rm], amount, carry);
 | 
			
		||||
 | 
			
		||||
                cpsr.set_c(carry);
 | 
			
		||||
 | 
			
		||||
                // PC is 12 bytes ahead when shifting
 | 
			
		||||
                if (data.rn == PC_INDEX)
 | 
			
		||||
                    op_1 += INSTRUCTION_SIZE;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            switch (data.opcode) {
 | 
			
		||||
                case OpCode::AND: {
 | 
			
		||||
                    result = op_1 & op_2;
 | 
			
		||||
 | 
			
		||||
                    negative = get_bit(result, 31);
 | 
			
		||||
                } break;
 | 
			
		||||
                case OpCode::EOR: {
 | 
			
		||||
                    result = op_1 ^ op_2;
 | 
			
		||||
 | 
			
		||||
                    negative = get_bit(result, 31);
 | 
			
		||||
                } break;
 | 
			
		||||
                case OpCode::SUB: {
 | 
			
		||||
                    bool s1  = get_bit(op_1, 31);
 | 
			
		||||
                    bool s2  = get_bit(op_2, 31);
 | 
			
		||||
                    result   = op_1 - op_2;
 | 
			
		||||
                    negative = get_bit(result, 31);
 | 
			
		||||
                    carry    = op_1 < op_2;
 | 
			
		||||
                    overflow = s1 != s2 && s2 == negative;
 | 
			
		||||
                } break;
 | 
			
		||||
                case OpCode::RSB: {
 | 
			
		||||
                    bool s1 = get_bit(op_1, 31);
 | 
			
		||||
                    bool s2 = get_bit(op_2, 31);
 | 
			
		||||
                    result  = op_2 - op_1;
 | 
			
		||||
 | 
			
		||||
                    negative = get_bit(result, 31);
 | 
			
		||||
                    carry    = op_2 < op_1;
 | 
			
		||||
                    overflow = s1 != s2 && s1 == negative;
 | 
			
		||||
                } break;
 | 
			
		||||
                case OpCode::ADD: {
 | 
			
		||||
                    bool s1 = get_bit(op_1, 31);
 | 
			
		||||
                    bool s2 = get_bit(op_2, 31);
 | 
			
		||||
 | 
			
		||||
                    // result_ is 33 bits
 | 
			
		||||
                    uint64_t result_ = op_2 + op_1;
 | 
			
		||||
                    result           = result_ & 0xFFFFFFFF;
 | 
			
		||||
 | 
			
		||||
                    negative = get_bit(result, 31);
 | 
			
		||||
                    carry    = get_bit(result_, 32);
 | 
			
		||||
                    overflow = s1 == s2 && s1 != negative;
 | 
			
		||||
                } break;
 | 
			
		||||
                case OpCode::ADC: {
 | 
			
		||||
                    bool s1 = get_bit(op_1, 31);
 | 
			
		||||
                    bool s2 = get_bit(op_2, 31);
 | 
			
		||||
 | 
			
		||||
                    uint64_t result_ = op_2 + op_1 + carry;
 | 
			
		||||
                    result           = result_ & 0xFFFFFFFF;
 | 
			
		||||
 | 
			
		||||
                    negative = get_bit(result, 31);
 | 
			
		||||
                    carry    = get_bit(result_, 32);
 | 
			
		||||
                    overflow = s1 == s2 && s1 != negative;
 | 
			
		||||
                } break;
 | 
			
		||||
                case OpCode::SBC: {
 | 
			
		||||
                    bool s1 = get_bit(op_1, 31);
 | 
			
		||||
                    bool s2 = get_bit(op_2, 31);
 | 
			
		||||
 | 
			
		||||
                    uint64_t result_ = op_1 - op_2 + carry - 1;
 | 
			
		||||
                    result           = result_ & 0xFFFFFFFF;
 | 
			
		||||
 | 
			
		||||
                    negative = get_bit(result, 31);
 | 
			
		||||
                    carry    = get_bit(result_, 32);
 | 
			
		||||
                    overflow = s1 != s2 && s2 == negative;
 | 
			
		||||
                } break;
 | 
			
		||||
                case OpCode::RSC: {
 | 
			
		||||
                    bool s1 = get_bit(op_1, 31);
 | 
			
		||||
                    bool s2 = get_bit(op_2, 31);
 | 
			
		||||
 | 
			
		||||
                    uint64_t result_ = op_1 - op_2 + carry - 1;
 | 
			
		||||
                    result           = result_ & 0xFFFFFFFF;
 | 
			
		||||
 | 
			
		||||
                    negative = get_bit(result, 31);
 | 
			
		||||
                    carry    = get_bit(result_, 32);
 | 
			
		||||
                    overflow = s1 != s2 && s1 == negative;
 | 
			
		||||
                } break;
 | 
			
		||||
                case OpCode::TST: {
 | 
			
		||||
                    result = op_1 & op_2;
 | 
			
		||||
 | 
			
		||||
                    negative = get_bit(result, 31);
 | 
			
		||||
                } break;
 | 
			
		||||
                case OpCode::TEQ: {
 | 
			
		||||
                    result = op_1 ^ op_2;
 | 
			
		||||
 | 
			
		||||
                    negative = get_bit(result, 31);
 | 
			
		||||
                } break;
 | 
			
		||||
                case OpCode::CMP: {
 | 
			
		||||
                    bool s1 = get_bit(op_1, 31);
 | 
			
		||||
                    bool s2 = get_bit(op_2, 31);
 | 
			
		||||
 | 
			
		||||
                    result = op_1 - op_2;
 | 
			
		||||
 | 
			
		||||
                    negative = get_bit(result, 31);
 | 
			
		||||
                    carry    = op_1 < op_2;
 | 
			
		||||
                    overflow = s1 != s2 && s2 == negative;
 | 
			
		||||
                } break;
 | 
			
		||||
                case OpCode::CMN: {
 | 
			
		||||
                    bool s1 = get_bit(op_1, 31);
 | 
			
		||||
                    bool s2 = get_bit(op_2, 31);
 | 
			
		||||
 | 
			
		||||
                    uint64_t result_ = op_2 + op_1;
 | 
			
		||||
                    result           = result_ & 0xFFFFFFFF;
 | 
			
		||||
 | 
			
		||||
                    negative = get_bit(result, 31);
 | 
			
		||||
                    carry    = get_bit(result_, 32);
 | 
			
		||||
                    overflow = s1 == s2 && s1 != negative;
 | 
			
		||||
                } break;
 | 
			
		||||
                case OpCode::ORR: {
 | 
			
		||||
                    result = op_1 | op_2;
 | 
			
		||||
 | 
			
		||||
                    negative = get_bit(result, 31);
 | 
			
		||||
                } break;
 | 
			
		||||
                case OpCode::MOV: {
 | 
			
		||||
                    result = op_2;
 | 
			
		||||
 | 
			
		||||
                    negative = get_bit(result, 31);
 | 
			
		||||
                } break;
 | 
			
		||||
                case OpCode::BIC: {
 | 
			
		||||
                    result = op_1 & ~op_2;
 | 
			
		||||
 | 
			
		||||
                    negative = get_bit(result, 31);
 | 
			
		||||
                } break;
 | 
			
		||||
                case OpCode::MVN: {
 | 
			
		||||
                    result = ~op_2;
 | 
			
		||||
 | 
			
		||||
                    negative = get_bit(result, 31);
 | 
			
		||||
                } break;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            zero = result == 0;
 | 
			
		||||
 | 
			
		||||
            debug(carry);
 | 
			
		||||
            debug(overflow);
 | 
			
		||||
            debug(zero);
 | 
			
		||||
            debug(negative);
 | 
			
		||||
 | 
			
		||||
            auto set_conditions = [this, carry, overflow, negative, zero]() {
 | 
			
		||||
                cpsr.set_c(carry);
 | 
			
		||||
                cpsr.set_v(overflow);
 | 
			
		||||
                cpsr.set_n(negative);
 | 
			
		||||
                cpsr.set_z(zero);
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            if (data.set) {
 | 
			
		||||
                if (data.rd == PC_INDEX) {
 | 
			
		||||
                    if (cpsr.mode() == Mode::User)
 | 
			
		||||
                        log_error("Running {} in User mode",
 | 
			
		||||
                                  typeid(data).name());
 | 
			
		||||
                } else {
 | 
			
		||||
                    set_conditions();
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (data.opcode == OpCode::TST || data.opcode == OpCode::TEQ ||
 | 
			
		||||
                data.opcode == OpCode::CMP || data.opcode == OpCode::CMN) {
 | 
			
		||||
                set_conditions();
 | 
			
		||||
            } else {
 | 
			
		||||
                gpr[data.rd] = result;
 | 
			
		||||
                if (data.rd == PC_INDEX || data.opcode == OpCode::MVN)
 | 
			
		||||
                    is_flushed = true;
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        [this](SoftwareInterrupt) {
 | 
			
		||||
            chg_mode(Mode::Supervisor);
 | 
			
		||||
            pc   = 0x08;
 | 
			
		||||
            spsr = cpsr;
 | 
			
		||||
        },
 | 
			
		||||
        [](auto& data) {
 | 
			
		||||
            log_error("Unimplemented {} instruction", typeid(data).name());
 | 
			
		||||
        } },
 | 
			
		||||
      data);
 | 
			
		||||
}
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
#include "cpu/instruction.hh"
 | 
			
		||||
#include "instruction.hh"
 | 
			
		||||
#include "cpu/utility.hh"
 | 
			
		||||
#include "util/bits.hh"
 | 
			
		||||
#include <iterator>
 | 
			
		||||
@@ -21,7 +21,7 @@ Instruction::Instruction(uint32_t insn)
 | 
			
		||||
        // lsh 2 and sign extend the 26 bit offset to 32 bits
 | 
			
		||||
        offset = (static_cast<int32_t>(offset) << 8) >> 6;
 | 
			
		||||
 | 
			
		||||
        offset += 2 * ARM_INSTRUCTION_SIZE;
 | 
			
		||||
        offset += 2 * INSTRUCTION_SIZE;
 | 
			
		||||
 | 
			
		||||
        data = Branch{ .link = link, .offset = offset };
 | 
			
		||||
 | 
			
		||||
@@ -46,7 +46,7 @@ Instruction::Instruction(uint32_t insn)
 | 
			
		||||
        uint8_t rdhi = bit_range(insn, 16, 19);
 | 
			
		||||
        bool set     = get_bit(insn, 20);
 | 
			
		||||
        bool acc     = get_bit(insn, 21);
 | 
			
		||||
        bool uns     = get_bit(insn, 22);
 | 
			
		||||
        bool uns     = !get_bit(insn, 22);
 | 
			
		||||
 | 
			
		||||
        data = MultiplyLong{ .rm   = rm,
 | 
			
		||||
                             .rs   = rs,
 | 
			
		||||
@@ -166,13 +166,13 @@ Instruction::Instruction(uint32_t insn)
 | 
			
		||||
        } else if ((opcode == OpCode::TEQ || opcode == OpCode::CMN) && !set) {
 | 
			
		||||
            uint32_t operand = 0;
 | 
			
		||||
 | 
			
		||||
            if (!imm) {
 | 
			
		||||
                operand = bit_range(insn, 0, 3);
 | 
			
		||||
            } else {
 | 
			
		||||
            if (imm) {
 | 
			
		||||
                uint32_t immediate = bit_range(insn, 0, 7);
 | 
			
		||||
                uint8_t rotate     = bit_range(insn, 8, 11);
 | 
			
		||||
 | 
			
		||||
                operand = std::rotr(immediate, rotate * 2);
 | 
			
		||||
            } else {
 | 
			
		||||
                operand = bit_range(insn, 0, 3);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            data = PsrTransfer{ .operand = operand,
 | 
			
		||||
@@ -184,7 +184,7 @@ Instruction::Instruction(uint32_t insn)
 | 
			
		||||
        } else {
 | 
			
		||||
            std::variant<Shift, uint32_t> operand;
 | 
			
		||||
 | 
			
		||||
            if (!imm) {
 | 
			
		||||
            if (imm) {
 | 
			
		||||
                uint32_t immediate = bit_range(insn, 0, 7);
 | 
			
		||||
                uint8_t rotate     = bit_range(insn, 8, 11);
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										167
									
								
								src/cpu/arm/instruction.hh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										167
									
								
								src/cpu/arm/instruction.hh
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,167 @@
 | 
			
		||||
#include "cpu/utility.hh"
 | 
			
		||||
#include <cstdint>
 | 
			
		||||
#include <variant>
 | 
			
		||||
 | 
			
		||||
template<class... Ts>
 | 
			
		||||
struct overloaded : Ts... {
 | 
			
		||||
    using Ts::operator()...;
 | 
			
		||||
};
 | 
			
		||||
template<class... Ts>
 | 
			
		||||
overloaded(Ts...) -> overloaded<Ts...>;
 | 
			
		||||
 | 
			
		||||
namespace arm {
 | 
			
		||||
static constexpr size_t INSTRUCTION_SIZE = 4;
 | 
			
		||||
 | 
			
		||||
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 imm;
 | 
			
		||||
    bool up;
 | 
			
		||||
    bool pre;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct BlockDataTransfer {
 | 
			
		||||
    uint16_t regs;
 | 
			
		||||
    uint8_t rn;
 | 
			
		||||
    bool load;
 | 
			
		||||
    bool write;
 | 
			
		||||
    bool s;
 | 
			
		||||
    bool up;
 | 
			
		||||
    bool pre;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct DataProcessing {
 | 
			
		||||
    std::variant<Shift, uint32_t> operand;
 | 
			
		||||
    uint8_t rd;
 | 
			
		||||
    uint8_t rn;
 | 
			
		||||
    bool set;
 | 
			
		||||
    OpCode opcode;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct PsrTransfer {
 | 
			
		||||
    enum class Type {
 | 
			
		||||
        Mrs,
 | 
			
		||||
        Msr,
 | 
			
		||||
        Msr_flg
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    uint32_t operand;
 | 
			
		||||
    bool spsr;
 | 
			
		||||
    Type type;
 | 
			
		||||
    // ignored outside MSR_flg
 | 
			
		||||
    bool imm;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
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,
 | 
			
		||||
                                     BlockDataTransfer,
 | 
			
		||||
                                     DataProcessing,
 | 
			
		||||
                                     PsrTransfer,
 | 
			
		||||
                                     CoprocessorDataTransfer,
 | 
			
		||||
                                     CoprocessorDataOperation,
 | 
			
		||||
                                     CoprocessorRegisterTransfer,
 | 
			
		||||
                                     Undefined,
 | 
			
		||||
                                     SoftwareInterrupt>;
 | 
			
		||||
 | 
			
		||||
struct Instruction {
 | 
			
		||||
    Condition condition;
 | 
			
		||||
    InstructionData data;
 | 
			
		||||
 | 
			
		||||
    Instruction(uint32_t insn);
 | 
			
		||||
    Instruction(Condition condition, InstructionData data) noexcept
 | 
			
		||||
      : condition(condition)
 | 
			
		||||
      , data(data){};
 | 
			
		||||
 | 
			
		||||
    std::string disassemble();
 | 
			
		||||
};
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										4
									
								
								src/cpu/arm/meson.build
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								src/cpu/arm/meson.build
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,4 @@
 | 
			
		||||
lib_sources += files(
 | 
			
		||||
  'instruction.cc',
 | 
			
		||||
  'exec.cc'
 | 
			
		||||
)
 | 
			
		||||
							
								
								
									
										144
									
								
								src/cpu/cpu-impl.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										144
									
								
								src/cpu/cpu-impl.cc
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,144 @@
 | 
			
		||||
#include "cpu-impl.hh"
 | 
			
		||||
#include "util/bits.hh"
 | 
			
		||||
#include "util/log.hh"
 | 
			
		||||
#include "utility.hh"
 | 
			
		||||
#include <algorithm>
 | 
			
		||||
#include <cstdio>
 | 
			
		||||
 | 
			
		||||
using namespace logger;
 | 
			
		||||
 | 
			
		||||
CpuImpl::CpuImpl(const Bus& bus) noexcept
 | 
			
		||||
  : bus(std::make_shared<Bus>(bus))
 | 
			
		||||
  , gpr({ 0 })
 | 
			
		||||
  , cpsr(0)
 | 
			
		||||
  , spsr(0)
 | 
			
		||||
  , is_flushed(false)
 | 
			
		||||
  , gpr_banked({ { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 } })
 | 
			
		||||
  , spsr_banked({ 0, 0, 0, 0, 0 }) {
 | 
			
		||||
    cpsr.set_mode(Mode::Supervisor);
 | 
			
		||||
    cpsr.set_irq_disabled(true);
 | 
			
		||||
    cpsr.set_fiq_disabled(true);
 | 
			
		||||
    cpsr.set_state(State::Arm);
 | 
			
		||||
    log_info("CPU successfully initialised");
 | 
			
		||||
 | 
			
		||||
    // PC always points to two instructions ahead
 | 
			
		||||
    // PC - 2 is the instruction being executed
 | 
			
		||||
    pc += 2 * arm::INSTRUCTION_SIZE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* change modes */
 | 
			
		||||
void
 | 
			
		||||
CpuImpl::chg_mode(const Mode to) {
 | 
			
		||||
    Mode from = cpsr.mode();
 | 
			
		||||
 | 
			
		||||
    if (from == to)
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
/* TODO: replace visible registers with view once I understand how to
 | 
			
		||||
 * concatenate views */
 | 
			
		||||
#define STORE_BANKED(mode, MODE)                                               \
 | 
			
		||||
    std::copy(gpr.begin() + GPR_##MODE##_FIRST,                                \
 | 
			
		||||
              gpr.begin() + gpr.size() - 1,                                    \
 | 
			
		||||
              gpr_banked.mode.begin())
 | 
			
		||||
 | 
			
		||||
    switch (from) {
 | 
			
		||||
        case Mode::Fiq:
 | 
			
		||||
            STORE_BANKED(fiq, FIQ);
 | 
			
		||||
            spsr_banked.fiq = spsr;
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case Mode::Supervisor:
 | 
			
		||||
            STORE_BANKED(svc, SVC);
 | 
			
		||||
            spsr_banked.svc = spsr;
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case Mode::Abort:
 | 
			
		||||
            STORE_BANKED(abt, ABT);
 | 
			
		||||
            spsr_banked.abt = spsr;
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case Mode::Irq:
 | 
			
		||||
            STORE_BANKED(irq, IRQ);
 | 
			
		||||
            spsr_banked.irq = spsr;
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case Mode::Undefined:
 | 
			
		||||
            STORE_BANKED(und, UND);
 | 
			
		||||
            spsr_banked.und = spsr;
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case Mode::User:
 | 
			
		||||
        case Mode::System:
 | 
			
		||||
            STORE_BANKED(old, SYS_USR);
 | 
			
		||||
            break;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
#define RESTORE_BANKED(mode, MODE)                                             \
 | 
			
		||||
    std::copy(gpr_banked.mode.begin(),                                         \
 | 
			
		||||
              gpr_banked.mode.end(),                                           \
 | 
			
		||||
              gpr.begin() + GPR_##MODE##_FIRST)
 | 
			
		||||
 | 
			
		||||
    switch (to) {
 | 
			
		||||
        case Mode::Fiq:
 | 
			
		||||
            RESTORE_BANKED(fiq, FIQ);
 | 
			
		||||
            spsr = spsr_banked.fiq;
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case Mode::Supervisor:
 | 
			
		||||
            RESTORE_BANKED(svc, SVC);
 | 
			
		||||
            spsr = spsr_banked.svc;
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case Mode::Abort:
 | 
			
		||||
            RESTORE_BANKED(abt, ABT);
 | 
			
		||||
            spsr = spsr_banked.abt;
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case Mode::Irq:
 | 
			
		||||
            RESTORE_BANKED(irq, IRQ);
 | 
			
		||||
            spsr = spsr_banked.irq;
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case Mode::Undefined:
 | 
			
		||||
            RESTORE_BANKED(und, UND);
 | 
			
		||||
            spsr = spsr_banked.und;
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case Mode::User:
 | 
			
		||||
        case Mode::System:
 | 
			
		||||
            STORE_BANKED(old, SYS_USR);
 | 
			
		||||
            break;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
#undef RESTORE_BANKED
 | 
			
		||||
 | 
			
		||||
    cpsr.set_mode(to);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
CpuImpl::step() {
 | 
			
		||||
    // Current instruction is two instructions behind PC
 | 
			
		||||
    uint32_t cur_pc = pc - 2 * arm::INSTRUCTION_SIZE;
 | 
			
		||||
 | 
			
		||||
    if (cpsr.state() == State::Arm) {
 | 
			
		||||
        debug(cur_pc);
 | 
			
		||||
        uint32_t x = bus->read_word(cur_pc);
 | 
			
		||||
        arm::Instruction instruction(x);
 | 
			
		||||
        log_info("{:#034b}", x);
 | 
			
		||||
 | 
			
		||||
        exec_arm(instruction);
 | 
			
		||||
 | 
			
		||||
        log_info("0x{:08X} : {}", cur_pc, instruction.disassemble());
 | 
			
		||||
 | 
			
		||||
        if (is_flushed) {
 | 
			
		||||
            // if flushed, do not increment the PC, instead set it to two
 | 
			
		||||
            // instructions ahead to account for flushed "fetch" and "decode"
 | 
			
		||||
            // instructions
 | 
			
		||||
            pc += 2 * arm::INSTRUCTION_SIZE;
 | 
			
		||||
            is_flushed = false;
 | 
			
		||||
        } else {
 | 
			
		||||
            // if not flushed continue like normal
 | 
			
		||||
            pc += arm::INSTRUCTION_SIZE;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										57
									
								
								src/cpu/cpu-impl.hh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								src/cpu/cpu-impl.hh
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,57 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "bus.hh"
 | 
			
		||||
#include "cpu/arm/instruction.hh"
 | 
			
		||||
#include "cpu/psr.hh"
 | 
			
		||||
 | 
			
		||||
#include <cstdint>
 | 
			
		||||
 | 
			
		||||
class CpuImpl {
 | 
			
		||||
  public:
 | 
			
		||||
    CpuImpl(const Bus& bus) noexcept;
 | 
			
		||||
 | 
			
		||||
    void step();
 | 
			
		||||
    void chg_mode(const Mode to);
 | 
			
		||||
    void exec_arm(const arm::Instruction instruction);
 | 
			
		||||
 | 
			
		||||
    static constexpr uint8_t GPR_COUNT = 16;
 | 
			
		||||
 | 
			
		||||
    static constexpr uint8_t GPR_FIQ_FIRST     = 8;
 | 
			
		||||
    static constexpr uint8_t GPR_SVC_FIRST     = 13;
 | 
			
		||||
    static constexpr uint8_t GPR_ABT_FIRST     = 13;
 | 
			
		||||
    static constexpr uint8_t GPR_IRQ_FIRST     = 13;
 | 
			
		||||
    static constexpr uint8_t GPR_UND_FIRST     = 13;
 | 
			
		||||
    static constexpr uint8_t GPR_SYS_USR_FIRST = 8;
 | 
			
		||||
 | 
			
		||||
    std::shared_ptr<Bus> bus;
 | 
			
		||||
    std::array<uint32_t, GPR_COUNT> gpr; // general purpose registers
 | 
			
		||||
 | 
			
		||||
    Psr cpsr; // current program status register
 | 
			
		||||
    Psr spsr; // status program status register
 | 
			
		||||
 | 
			
		||||
    static constexpr uint8_t PC_INDEX = 15;
 | 
			
		||||
    static_assert(PC_INDEX < GPR_COUNT);
 | 
			
		||||
 | 
			
		||||
    uint32_t& pc = gpr[PC_INDEX];
 | 
			
		||||
 | 
			
		||||
    bool is_flushed;
 | 
			
		||||
 | 
			
		||||
    struct {
 | 
			
		||||
        std::array<uint32_t, GPR_COUNT - GPR_FIQ_FIRST - 1> fiq;
 | 
			
		||||
        std::array<uint32_t, GPR_COUNT - GPR_SVC_FIRST - 1> svc;
 | 
			
		||||
        std::array<uint32_t, GPR_COUNT - GPR_ABT_FIRST - 1> abt;
 | 
			
		||||
        std::array<uint32_t, GPR_COUNT - GPR_IRQ_FIRST - 1> irq;
 | 
			
		||||
        std::array<uint32_t, GPR_COUNT - GPR_UND_FIRST - 1> und;
 | 
			
		||||
 | 
			
		||||
        // visible registers before the mode switch
 | 
			
		||||
        std::array<uint32_t, GPR_COUNT - GPR_SYS_USR_FIRST> old;
 | 
			
		||||
    } gpr_banked; // banked general purpose registers
 | 
			
		||||
 | 
			
		||||
    struct {
 | 
			
		||||
        Psr fiq;
 | 
			
		||||
        Psr svc;
 | 
			
		||||
        Psr abt;
 | 
			
		||||
        Psr irq;
 | 
			
		||||
        Psr und;
 | 
			
		||||
    } spsr_banked; // banked saved program status registers
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										714
									
								
								src/cpu/cpu.cc
									
									
									
									
									
								
							
							
						
						
									
										714
									
								
								src/cpu/cpu.cc
									
									
									
									
									
								
							@@ -1,714 +1,12 @@
 | 
			
		||||
#include "cpu/cpu.hh"
 | 
			
		||||
#include "cpu/utility.hh"
 | 
			
		||||
#include "util/bits.hh"
 | 
			
		||||
#include "util/log.hh"
 | 
			
		||||
#include <algorithm>
 | 
			
		||||
#include <cstdio>
 | 
			
		||||
#include "cpu-impl.hh"
 | 
			
		||||
 | 
			
		||||
using namespace logger;
 | 
			
		||||
Cpu::Cpu(const Bus& bus) noexcept
 | 
			
		||||
  : impl(std::make_unique<CpuImpl>(bus)){};
 | 
			
		||||
 | 
			
		||||
Cpu::Cpu(Bus& bus)
 | 
			
		||||
  : bus(std::make_shared<Bus>(bus))
 | 
			
		||||
  , gpr({ 0 })
 | 
			
		||||
  , cpsr(0)
 | 
			
		||||
  , spsr(0)
 | 
			
		||||
  , is_flushed(false)
 | 
			
		||||
  , gpr_banked({ { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 } })
 | 
			
		||||
  , spsr_banked({ 0, 0, 0, 0, 0 }) {
 | 
			
		||||
    cpsr.set_mode(Mode::Supervisor);
 | 
			
		||||
    cpsr.set_irq_disabled(true);
 | 
			
		||||
    cpsr.set_fiq_disabled(true);
 | 
			
		||||
    cpsr.set_state(State::Arm);
 | 
			
		||||
    log_info("CPU successfully initialised");
 | 
			
		||||
 | 
			
		||||
    // PC always points to two instructions ahead
 | 
			
		||||
    // PC - 2 is the instruction being executed
 | 
			
		||||
    pc += 2 * ARM_INSTRUCTION_SIZE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* change modes */
 | 
			
		||||
void
 | 
			
		||||
Cpu::chg_mode(const Mode to) {
 | 
			
		||||
    Mode from = cpsr.mode();
 | 
			
		||||
 | 
			
		||||
    if (from == to)
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
/* TODO: replace visible registers with view once I understand how to
 | 
			
		||||
 * concatenate views */
 | 
			
		||||
#define STORE_BANKED(mode, MODE)                                               \
 | 
			
		||||
    std::copy(gpr.begin() + GPR_##MODE##_FIRST,                                \
 | 
			
		||||
              gpr.begin() + gpr.size() - 1,                                    \
 | 
			
		||||
              gpr_banked.mode.begin())
 | 
			
		||||
 | 
			
		||||
    switch (from) {
 | 
			
		||||
        case Mode::Fiq:
 | 
			
		||||
            STORE_BANKED(fiq, FIQ);
 | 
			
		||||
            spsr_banked.fiq = spsr;
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case Mode::Supervisor:
 | 
			
		||||
            STORE_BANKED(svc, SVC);
 | 
			
		||||
            spsr_banked.svc = spsr;
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case Mode::Abort:
 | 
			
		||||
            STORE_BANKED(abt, ABT);
 | 
			
		||||
            spsr_banked.abt = spsr;
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case Mode::Irq:
 | 
			
		||||
            STORE_BANKED(irq, IRQ);
 | 
			
		||||
            spsr_banked.irq = spsr;
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case Mode::Undefined:
 | 
			
		||||
            STORE_BANKED(und, UND);
 | 
			
		||||
            spsr_banked.und = spsr;
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case Mode::User:
 | 
			
		||||
        case Mode::System:
 | 
			
		||||
            STORE_BANKED(old, SYS_USR);
 | 
			
		||||
            break;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
#define RESTORE_BANKED(mode, MODE)                                             \
 | 
			
		||||
    std::copy(gpr_banked.mode.begin(),                                         \
 | 
			
		||||
              gpr_banked.mode.end(),                                           \
 | 
			
		||||
              gpr.begin() + GPR_##MODE##_FIRST)
 | 
			
		||||
 | 
			
		||||
    switch (to) {
 | 
			
		||||
        case Mode::Fiq:
 | 
			
		||||
            RESTORE_BANKED(fiq, FIQ);
 | 
			
		||||
            spsr = spsr_banked.fiq;
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case Mode::Supervisor:
 | 
			
		||||
            RESTORE_BANKED(svc, SVC);
 | 
			
		||||
            spsr = spsr_banked.svc;
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case Mode::Abort:
 | 
			
		||||
            RESTORE_BANKED(abt, ABT);
 | 
			
		||||
            spsr = spsr_banked.abt;
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case Mode::Irq:
 | 
			
		||||
            RESTORE_BANKED(irq, IRQ);
 | 
			
		||||
            spsr = spsr_banked.irq;
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case Mode::Undefined:
 | 
			
		||||
            RESTORE_BANKED(und, UND);
 | 
			
		||||
            spsr = spsr_banked.und;
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case Mode::User:
 | 
			
		||||
        case Mode::System:
 | 
			
		||||
            STORE_BANKED(old, SYS_USR);
 | 
			
		||||
            break;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
#undef RESTORE_BANKED
 | 
			
		||||
 | 
			
		||||
    cpsr.set_mode(to);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
Cpu::exec_arm(const arm::Instruction instruction) {
 | 
			
		||||
    auto cond = instruction.condition;
 | 
			
		||||
    auto data = instruction.data;
 | 
			
		||||
 | 
			
		||||
    if (!cpsr.condition(cond)) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    auto pc_error = [](uint8_t r) {
 | 
			
		||||
        if (r == PC_INDEX)
 | 
			
		||||
            log_error("Using PC (R15) as operand register");
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    auto pc_warn = [](uint8_t r) {
 | 
			
		||||
        if (r == PC_INDEX)
 | 
			
		||||
            log_warn("Using PC (R15) as operand register");
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    using namespace arm;
 | 
			
		||||
 | 
			
		||||
    std::visit(
 | 
			
		||||
      overloaded{
 | 
			
		||||
        [this, pc_warn](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_bit(pc, 0);
 | 
			
		||||
 | 
			
		||||
            if (state == State::Arm)
 | 
			
		||||
                rst_bit(pc, 1);
 | 
			
		||||
 | 
			
		||||
            // pc is affected so flush the pipeline
 | 
			
		||||
            is_flushed = true;
 | 
			
		||||
        },
 | 
			
		||||
        [this](Branch& data) {
 | 
			
		||||
            if (data.link)
 | 
			
		||||
                gpr[14] = pc - ARM_INSTRUCTION_SIZE;
 | 
			
		||||
 | 
			
		||||
            // data.offset accounts for two instructions ahead when
 | 
			
		||||
            // disassembling, so need to adjust
 | 
			
		||||
            pc =
 | 
			
		||||
              static_cast<int32_t>(pc) - 2 * ARM_INSTRUCTION_SIZE + data.offset;
 | 
			
		||||
 | 
			
		||||
            // pc is affected so flush the pipeline
 | 
			
		||||
            is_flushed = true;
 | 
			
		||||
        },
 | 
			
		||||
        [this, pc_error](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(gpr[data.rd] == 0);
 | 
			
		||||
                cpsr.set_n(get_bit(gpr[data.rd], 31));
 | 
			
		||||
                cpsr.set_c(0);
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        [this, pc_error](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] = bit_range(eval, 0, 31);
 | 
			
		||||
                gpr[data.rdhi] = 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] = bit_range(eval, 0, 31);
 | 
			
		||||
                gpr[data.rdhi] = bit_range(eval, 32, 63);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (data.set) {
 | 
			
		||||
                cpsr.set_z(gpr[data.rdhi] == 0 && gpr[data.rdlo] == 0);
 | 
			
		||||
                cpsr.set_n(get_bit(gpr[data.rdhi], 31));
 | 
			
		||||
                cpsr.set_c(0);
 | 
			
		||||
                cpsr.set_v(0);
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        [](Undefined) { log_warn("Undefined instruction"); },
 | 
			
		||||
        [this, pc_error](SingleDataSwap& data) {
 | 
			
		||||
            pc_error(data.rm);
 | 
			
		||||
            pc_error(data.rn);
 | 
			
		||||
            pc_error(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](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.rn == PC_INDEX && data.write)
 | 
			
		||||
                log_warn("Write-back enabled with base register as PC {}",
 | 
			
		||||
                         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);
 | 
			
		||||
 | 
			
		||||
                offset =
 | 
			
		||||
                  eval_shift(shift->data.type, gpr[shift->rm], amount, carry);
 | 
			
		||||
 | 
			
		||||
                cpsr.set_c(carry);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // PC is always two instructions ahead
 | 
			
		||||
            if (data.rn == PC_INDEX)
 | 
			
		||||
                address -= 2 * ARM_INSTRUCTION_SIZE;
 | 
			
		||||
 | 
			
		||||
            if (data.pre)
 | 
			
		||||
                address += (data.up ? offset : -offset);
 | 
			
		||||
 | 
			
		||||
            debug(address);
 | 
			
		||||
 | 
			
		||||
            // 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 == PC_INDEX)
 | 
			
		||||
                    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;
 | 
			
		||||
 | 
			
		||||
            if (data.rd == PC_INDEX && data.load)
 | 
			
		||||
                is_flushed = true;
 | 
			
		||||
        },
 | 
			
		||||
        [this, pc_warn, pc_error](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
 | 
			
		||||
                        gpr[data.rd] =
 | 
			
		||||
                          (static_cast<int32_t>(gpr[data.rd]) << 16) >> 16;
 | 
			
		||||
 | 
			
		||||
                        // byte
 | 
			
		||||
                    } else {
 | 
			
		||||
                        gpr[data.rd] = bus->read_byte(address);
 | 
			
		||||
 | 
			
		||||
                        // sign extend the byte
 | 
			
		||||
                        gpr[data.rd] =
 | 
			
		||||
                          (static_cast<int32_t>(gpr[data.rd]) << 24) >> 24;
 | 
			
		||||
                    }
 | 
			
		||||
                    // unsigned halfword
 | 
			
		||||
                } else if (data.half) {
 | 
			
		||||
                    gpr[data.rd] = bus->read_halfword(address);
 | 
			
		||||
                }
 | 
			
		||||
                // store
 | 
			
		||||
            } else {
 | 
			
		||||
                // take PC into consideration
 | 
			
		||||
                if (data.rd == PC_INDEX)
 | 
			
		||||
                    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;
 | 
			
		||||
 | 
			
		||||
            if (data.rd == PC_INDEX && data.load)
 | 
			
		||||
                is_flushed = true;
 | 
			
		||||
        },
 | 
			
		||||
        [this, pc_error](BlockDataTransfer& data) {
 | 
			
		||||
            uint32_t address  = gpr[data.rn];
 | 
			
		||||
            Mode mode         = cpsr.mode();
 | 
			
		||||
            uint8_t alignment = 4; // word
 | 
			
		||||
            uint8_t i         = 0;
 | 
			
		||||
            uint8_t n_regs    = std::popcount(data.regs);
 | 
			
		||||
 | 
			
		||||
            pc_error(data.rn);
 | 
			
		||||
 | 
			
		||||
            if (cpsr.mode() == Mode::User && data.s) {
 | 
			
		||||
                log_error("Bit S is set outside priviliged modes in {}",
 | 
			
		||||
                          typeid(data).name());
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // we just change modes to load user registers
 | 
			
		||||
            if ((!get_bit(data.regs, PC_INDEX) && data.s) ||
 | 
			
		||||
                (!data.load && data.s)) {
 | 
			
		||||
                chg_mode(Mode::User);
 | 
			
		||||
 | 
			
		||||
                if (data.write) {
 | 
			
		||||
                    log_error("Write-back enable for user bank registers in {}",
 | 
			
		||||
                              typeid(data).name());
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // account for decrement
 | 
			
		||||
            if (!data.up)
 | 
			
		||||
                address -= (n_regs - 1) * alignment;
 | 
			
		||||
 | 
			
		||||
            if (data.pre)
 | 
			
		||||
                address += (data.up ? alignment : -alignment);
 | 
			
		||||
 | 
			
		||||
            if (data.load) {
 | 
			
		||||
                if (get_bit(data.regs, PC_INDEX) && data.s && data.load) {
 | 
			
		||||
                    // current mode's spsr is already loaded when it was
 | 
			
		||||
                    // switched
 | 
			
		||||
                    spsr = cpsr;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                for (i = 0; i < GPR_COUNT; i++) {
 | 
			
		||||
                    if (get_bit(data.regs, i)) {
 | 
			
		||||
                        gpr[i] = bus->read_word(address);
 | 
			
		||||
                        address += alignment;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                for (i = 0; i < GPR_COUNT; i++) {
 | 
			
		||||
                    if (get_bit(data.regs, i)) {
 | 
			
		||||
                        bus->write_word(address, gpr[i]);
 | 
			
		||||
                        address += alignment;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (!data.pre)
 | 
			
		||||
                address += (data.up ? alignment : -alignment);
 | 
			
		||||
 | 
			
		||||
            // reset back to original address + offset if incremented earlier
 | 
			
		||||
            if (data.up)
 | 
			
		||||
                address -= n_regs * alignment;
 | 
			
		||||
 | 
			
		||||
            if (!data.pre || data.write)
 | 
			
		||||
                gpr[data.rn] = address;
 | 
			
		||||
 | 
			
		||||
            if (data.load && get_bit(data.regs, PC_INDEX))
 | 
			
		||||
                is_flushed = true;
 | 
			
		||||
 | 
			
		||||
            // load back the original mode registers
 | 
			
		||||
            chg_mode(mode);
 | 
			
		||||
        },
 | 
			
		||||
        [this, pc_error](PsrTransfer& data) {
 | 
			
		||||
            if (data.spsr && cpsr.mode() == Mode::User) {
 | 
			
		||||
                log_error("Accessing SPSR in User mode in {}",
 | 
			
		||||
                          typeid(data).name());
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            Psr& psr = data.spsr ? spsr : cpsr;
 | 
			
		||||
 | 
			
		||||
            switch (data.type) {
 | 
			
		||||
                case PsrTransfer::Type::Mrs:
 | 
			
		||||
                    pc_error(data.operand);
 | 
			
		||||
                    gpr[data.operand] = psr.raw();
 | 
			
		||||
                    break;
 | 
			
		||||
                case PsrTransfer::Type::Msr:
 | 
			
		||||
                    pc_error(data.operand);
 | 
			
		||||
 | 
			
		||||
                    if (cpsr.mode() != Mode::User) {
 | 
			
		||||
                        psr.set_all(gpr[data.operand]);
 | 
			
		||||
                    }
 | 
			
		||||
                    break;
 | 
			
		||||
                case PsrTransfer::Type::Msr_flg:
 | 
			
		||||
                    psr.set_n(get_bit(data.operand, 31));
 | 
			
		||||
                    psr.set_z(get_bit(data.operand, 30));
 | 
			
		||||
                    psr.set_c(get_bit(data.operand, 29));
 | 
			
		||||
                    psr.set_v(get_bit(data.operand, 28));
 | 
			
		||||
                    break;
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        [this, pc_error](DataProcessing& data) {
 | 
			
		||||
            uint32_t op_1 = gpr[data.rn];
 | 
			
		||||
            uint32_t op_2 = 0;
 | 
			
		||||
 | 
			
		||||
            uint32_t result = 0;
 | 
			
		||||
 | 
			
		||||
            bool overflow = cpsr.v();
 | 
			
		||||
            bool carry    = cpsr.c();
 | 
			
		||||
            bool negative = cpsr.n();
 | 
			
		||||
            bool zero     = cpsr.z();
 | 
			
		||||
 | 
			
		||||
            if (const uint32_t* immediate =
 | 
			
		||||
                  std::get_if<uint32_t>(&data.operand)) {
 | 
			
		||||
                op_2 = *immediate;
 | 
			
		||||
            } else if (const Shift* shift = std::get_if<Shift>(&data.operand)) {
 | 
			
		||||
                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);
 | 
			
		||||
 | 
			
		||||
                op_2 =
 | 
			
		||||
                  eval_shift(shift->data.type, gpr[shift->rm], amount, carry);
 | 
			
		||||
 | 
			
		||||
                cpsr.set_c(carry);
 | 
			
		||||
 | 
			
		||||
                // PC is 12 bytes ahead when shifting
 | 
			
		||||
                if (data.rn == PC_INDEX)
 | 
			
		||||
                    op_1 += ARM_INSTRUCTION_SIZE;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            switch (data.opcode) {
 | 
			
		||||
                case OpCode::AND: {
 | 
			
		||||
                    result = op_1 & op_2;
 | 
			
		||||
 | 
			
		||||
                    negative = get_bit(result, 31);
 | 
			
		||||
                } break;
 | 
			
		||||
                case OpCode::EOR: {
 | 
			
		||||
                    result = op_1 ^ op_2;
 | 
			
		||||
 | 
			
		||||
                    negative = get_bit(result, 31);
 | 
			
		||||
                } break;
 | 
			
		||||
                case OpCode::SUB: {
 | 
			
		||||
                    bool s1  = get_bit(op_1, 31);
 | 
			
		||||
                    bool s2  = get_bit(op_2, 31);
 | 
			
		||||
                    result   = op_1 - op_2;
 | 
			
		||||
                    negative = get_bit(result, 31);
 | 
			
		||||
                    carry    = op_1 < op_2;
 | 
			
		||||
                    overflow = s1 != s2 && s2 == negative;
 | 
			
		||||
                } break;
 | 
			
		||||
                case OpCode::RSB: {
 | 
			
		||||
                    bool s1 = get_bit(op_1, 31);
 | 
			
		||||
                    bool s2 = get_bit(op_2, 31);
 | 
			
		||||
                    result  = op_2 - op_1;
 | 
			
		||||
 | 
			
		||||
                    negative = get_bit(result, 31);
 | 
			
		||||
                    carry    = op_2 < op_1;
 | 
			
		||||
                    overflow = s1 != s2 && s1 == negative;
 | 
			
		||||
                } break;
 | 
			
		||||
                case OpCode::ADD: {
 | 
			
		||||
                    bool s1 = get_bit(op_1, 31);
 | 
			
		||||
                    bool s2 = get_bit(op_2, 31);
 | 
			
		||||
 | 
			
		||||
                    // result_ is 33 bits
 | 
			
		||||
                    uint64_t result_ = op_2 + op_1;
 | 
			
		||||
                    result           = result_ & 0xFFFFFFFF;
 | 
			
		||||
 | 
			
		||||
                    negative = get_bit(result, 31);
 | 
			
		||||
                    carry    = get_bit(result_, 32);
 | 
			
		||||
                    overflow = s1 == s2 && s1 != negative;
 | 
			
		||||
                } break;
 | 
			
		||||
                case OpCode::ADC: {
 | 
			
		||||
                    bool s1 = get_bit(op_1, 31);
 | 
			
		||||
                    bool s2 = get_bit(op_2, 31);
 | 
			
		||||
 | 
			
		||||
                    uint64_t result_ = op_2 + op_1 + carry;
 | 
			
		||||
                    result           = result_ & 0xFFFFFFFF;
 | 
			
		||||
 | 
			
		||||
                    negative = get_bit(result, 31);
 | 
			
		||||
                    carry    = get_bit(result_, 32);
 | 
			
		||||
                    overflow = s1 == s2 && s1 != negative;
 | 
			
		||||
                } break;
 | 
			
		||||
                case OpCode::SBC: {
 | 
			
		||||
                    bool s1 = get_bit(op_1, 31);
 | 
			
		||||
                    bool s2 = get_bit(op_2, 31);
 | 
			
		||||
 | 
			
		||||
                    uint64_t result_ = op_1 - op_2 + carry - 1;
 | 
			
		||||
                    result           = result_ & 0xFFFFFFFF;
 | 
			
		||||
 | 
			
		||||
                    negative = get_bit(result, 31);
 | 
			
		||||
                    carry    = get_bit(result_, 32);
 | 
			
		||||
                    overflow = s1 != s2 && s2 == negative;
 | 
			
		||||
                } break;
 | 
			
		||||
                case OpCode::RSC: {
 | 
			
		||||
                    bool s1 = get_bit(op_1, 31);
 | 
			
		||||
                    bool s2 = get_bit(op_2, 31);
 | 
			
		||||
 | 
			
		||||
                    uint64_t result_ = op_1 - op_2 + carry - 1;
 | 
			
		||||
                    result           = result_ & 0xFFFFFFFF;
 | 
			
		||||
 | 
			
		||||
                    negative = get_bit(result, 31);
 | 
			
		||||
                    carry    = get_bit(result_, 32);
 | 
			
		||||
                    overflow = s1 != s2 && s1 == negative;
 | 
			
		||||
                } break;
 | 
			
		||||
                case OpCode::TST: {
 | 
			
		||||
                    result = op_1 & op_2;
 | 
			
		||||
 | 
			
		||||
                    negative = get_bit(result, 31);
 | 
			
		||||
                } break;
 | 
			
		||||
                case OpCode::TEQ: {
 | 
			
		||||
                    result = op_1 ^ op_2;
 | 
			
		||||
 | 
			
		||||
                    negative = get_bit(result, 31);
 | 
			
		||||
                } break;
 | 
			
		||||
                case OpCode::CMP: {
 | 
			
		||||
                    bool s1 = get_bit(op_1, 31);
 | 
			
		||||
                    bool s2 = get_bit(op_2, 31);
 | 
			
		||||
 | 
			
		||||
                    result = op_1 - op_2;
 | 
			
		||||
 | 
			
		||||
                    negative = get_bit(result, 31);
 | 
			
		||||
                    carry    = op_1 < op_2;
 | 
			
		||||
                    overflow = s1 != s2 && s2 == negative;
 | 
			
		||||
                } break;
 | 
			
		||||
                case OpCode::CMN: {
 | 
			
		||||
                    bool s1 = get_bit(op_1, 31);
 | 
			
		||||
                    bool s2 = get_bit(op_2, 31);
 | 
			
		||||
 | 
			
		||||
                    uint64_t result_ = op_2 + op_1;
 | 
			
		||||
                    result           = result_ & 0xFFFFFFFF;
 | 
			
		||||
 | 
			
		||||
                    negative = get_bit(result, 31);
 | 
			
		||||
                    carry    = get_bit(result_, 32);
 | 
			
		||||
                    overflow = s1 == s2 && s1 != negative;
 | 
			
		||||
                } break;
 | 
			
		||||
                case OpCode::ORR: {
 | 
			
		||||
                    result = op_1 | op_2;
 | 
			
		||||
 | 
			
		||||
                    negative = get_bit(result, 31);
 | 
			
		||||
                } break;
 | 
			
		||||
                case OpCode::MOV: {
 | 
			
		||||
                    result = op_2;
 | 
			
		||||
 | 
			
		||||
                    negative = get_bit(result, 31);
 | 
			
		||||
                } break;
 | 
			
		||||
                case OpCode::BIC: {
 | 
			
		||||
                    result = op_1 & ~op_2;
 | 
			
		||||
 | 
			
		||||
                    negative = get_bit(result, 31);
 | 
			
		||||
                } break;
 | 
			
		||||
                case OpCode::MVN: {
 | 
			
		||||
                    result = ~op_2;
 | 
			
		||||
 | 
			
		||||
                    negative = get_bit(result, 31);
 | 
			
		||||
                } break;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            zero = result == 0;
 | 
			
		||||
 | 
			
		||||
            debug(carry);
 | 
			
		||||
            debug(overflow);
 | 
			
		||||
            debug(zero);
 | 
			
		||||
            debug(negative);
 | 
			
		||||
 | 
			
		||||
            auto set_conditions = [this, carry, overflow, negative, zero]() {
 | 
			
		||||
                cpsr.set_c(carry);
 | 
			
		||||
                cpsr.set_v(overflow);
 | 
			
		||||
                cpsr.set_n(negative);
 | 
			
		||||
                cpsr.set_z(zero);
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            if (data.set) {
 | 
			
		||||
                if (data.rd == 15) {
 | 
			
		||||
                    if (cpsr.mode() == Mode::User)
 | 
			
		||||
                        log_error("Running {} in User mode",
 | 
			
		||||
                                  typeid(data).name());
 | 
			
		||||
                } else {
 | 
			
		||||
                    set_conditions();
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (data.opcode == OpCode::TST || data.opcode == OpCode::TEQ ||
 | 
			
		||||
                data.opcode == OpCode::CMP || data.opcode == OpCode::CMN) {
 | 
			
		||||
                set_conditions();
 | 
			
		||||
            } else {
 | 
			
		||||
                gpr[data.rd] = result;
 | 
			
		||||
                if (data.rd == 15 || data.opcode == OpCode::MVN)
 | 
			
		||||
                    is_flushed = true;
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        [this](SoftwareInterrupt) {
 | 
			
		||||
            chg_mode(Mode::Supervisor);
 | 
			
		||||
            pc   = 0x08;
 | 
			
		||||
            spsr = cpsr;
 | 
			
		||||
        },
 | 
			
		||||
        [](auto& data) {
 | 
			
		||||
            log_error("Unimplemented {} instruction", typeid(data).name());
 | 
			
		||||
        } },
 | 
			
		||||
      data);
 | 
			
		||||
}
 | 
			
		||||
Cpu::~Cpu() = default;
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
Cpu::step() {
 | 
			
		||||
    // Current instruction is two instructions behind PC
 | 
			
		||||
    uint32_t cur_pc = pc - 2 * ARM_INSTRUCTION_SIZE;
 | 
			
		||||
 | 
			
		||||
    if (cpsr.state() == State::Arm) {
 | 
			
		||||
        debug(cur_pc);
 | 
			
		||||
        uint32_t x = bus->read_word(cur_pc);
 | 
			
		||||
        arm::Instruction instruction(x);
 | 
			
		||||
        log_info("{:#034b}", x);
 | 
			
		||||
 | 
			
		||||
        exec_arm(instruction);
 | 
			
		||||
 | 
			
		||||
        log_info("0x{:08X} : {}", cur_pc, instruction.disassemble());
 | 
			
		||||
 | 
			
		||||
        if (is_flushed) {
 | 
			
		||||
            // if flushed, do not increment the PC, instead set it to two
 | 
			
		||||
            // instructions ahead to account for flushed "fetch" and "decode"
 | 
			
		||||
            // instructions
 | 
			
		||||
            pc += 2 * ARM_INSTRUCTION_SIZE;
 | 
			
		||||
            is_flushed = false;
 | 
			
		||||
        } else {
 | 
			
		||||
            // if not flushed continue like normal
 | 
			
		||||
            pc += ARM_INSTRUCTION_SIZE;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
    impl->step();
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,8 @@
 | 
			
		||||
lib_sources += files(
 | 
			
		||||
  'cpu-impl.cc',
 | 
			
		||||
  'cpu.cc',
 | 
			
		||||
  'instruction.cc',
 | 
			
		||||
  'psr.cc',
 | 
			
		||||
  'utility.cc'
 | 
			
		||||
)
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
subdir('arm')
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
#include "cpu/psr.hh"
 | 
			
		||||
#include "psr.hh"
 | 
			
		||||
#include "util/bits.hh"
 | 
			
		||||
#include "util/log.hh"
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										55
									
								
								src/cpu/psr.hh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								src/cpu/psr.hh
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,55 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "utility.hh"
 | 
			
		||||
#include <cstdint>
 | 
			
		||||
 | 
			
		||||
class Psr {
 | 
			
		||||
  public:
 | 
			
		||||
    // clear the reserved bits i.e, [8:27]
 | 
			
		||||
    Psr(uint32_t raw);
 | 
			
		||||
 | 
			
		||||
    uint32_t raw() const;
 | 
			
		||||
    void set_all(uint32_t raw);
 | 
			
		||||
 | 
			
		||||
    // Mode : [4:0]
 | 
			
		||||
    Mode mode() const;
 | 
			
		||||
    void set_mode(Mode mode);
 | 
			
		||||
 | 
			
		||||
    // State : [5]
 | 
			
		||||
    State state() const;
 | 
			
		||||
    void set_state(State state);
 | 
			
		||||
 | 
			
		||||
#define GET_SET_NTH_BIT_FUNCTIONS(name)                                        \
 | 
			
		||||
    bool name() const;                                                         \
 | 
			
		||||
    void set_##name(bool val);
 | 
			
		||||
 | 
			
		||||
    // FIQ disable : [6]
 | 
			
		||||
    GET_SET_NTH_BIT_FUNCTIONS(fiq_disabled)
 | 
			
		||||
 | 
			
		||||
    // IRQ disable : [7]
 | 
			
		||||
    GET_SET_NTH_BIT_FUNCTIONS(irq_disabled)
 | 
			
		||||
 | 
			
		||||
    // Reserved bits : [27:8]
 | 
			
		||||
 | 
			
		||||
    // Overflow flag : [28]
 | 
			
		||||
    GET_SET_NTH_BIT_FUNCTIONS(v)
 | 
			
		||||
 | 
			
		||||
    // Carry flag : [29]
 | 
			
		||||
    GET_SET_NTH_BIT_FUNCTIONS(c)
 | 
			
		||||
 | 
			
		||||
    // Zero flag : [30]
 | 
			
		||||
    GET_SET_NTH_BIT_FUNCTIONS(z)
 | 
			
		||||
 | 
			
		||||
    // Negative flag : [30]
 | 
			
		||||
    GET_SET_NTH_BIT_FUNCTIONS(n)
 | 
			
		||||
 | 
			
		||||
#undef GET_SET_NTH_BIT_FUNCTIONS
 | 
			
		||||
 | 
			
		||||
    bool condition(Condition cond) const;
 | 
			
		||||
 | 
			
		||||
  private:
 | 
			
		||||
    static constexpr uint32_t PSR_CLEAR_RESERVED = 0xF00000FF;
 | 
			
		||||
    static constexpr uint32_t PSR_CLEAR_MODE     = 0xFFFFFFE0;
 | 
			
		||||
 | 
			
		||||
    uint32_t psr;
 | 
			
		||||
};
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
#include "cpu/utility.hh"
 | 
			
		||||
#include "utility.hh"
 | 
			
		||||
#include "util/bits.hh"
 | 
			
		||||
#include <bit>
 | 
			
		||||
 | 
			
		||||
@@ -102,13 +102,11 @@ eval_shift(ShiftType shift_type, uint32_t value, uint8_t amount, bool& carry) {
 | 
			
		||||
            break;
 | 
			
		||||
        case ShiftType::ROR:
 | 
			
		||||
            if (amount == 0) {
 | 
			
		||||
                bool old_carry = carry;
 | 
			
		||||
 | 
			
		||||
                eval  = (value >> 1) | (carry << 31);
 | 
			
		||||
                carry = get_bit(value, 0);
 | 
			
		||||
                eval  = (value >> 1) | (old_carry << 31);
 | 
			
		||||
            } else {
 | 
			
		||||
                carry = get_bit(value, (amount % 32 + 31) % 32);
 | 
			
		||||
                eval  = std::rotr(value, amount);
 | 
			
		||||
                carry = get_bit(value, (amount % 32 + 31) % 32);
 | 
			
		||||
            }
 | 
			
		||||
            break;
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										98
									
								
								src/cpu/utility.hh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										98
									
								
								src/cpu/utility.hh
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,98 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <fmt/ostream.h>
 | 
			
		||||
#include <ostream>
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// https://fmt.dev/dev/api.html#std-ostream-support
 | 
			
		||||
std::ostream&
 | 
			
		||||
operator<<(std::ostream& os, const OpCode cond);
 | 
			
		||||
template<>
 | 
			
		||||
struct fmt::formatter<OpCode> : ostream_formatter {};
 | 
			
		||||
 | 
			
		||||
enum class ShiftType {
 | 
			
		||||
    LSL = 0b00,
 | 
			
		||||
    LSR = 0b01,
 | 
			
		||||
    ASR = 0b10,
 | 
			
		||||
    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);
 | 
			
		||||
template<>
 | 
			
		||||
struct fmt::formatter<ShiftType> : ostream_formatter {};
 | 
			
		||||
@@ -4,11 +4,12 @@
 | 
			
		||||
#include "util/log.hh"
 | 
			
		||||
#include "util/utils.hh"
 | 
			
		||||
#include <bitset>
 | 
			
		||||
#include <stdexcept>
 | 
			
		||||
 | 
			
		||||
using namespace logger;
 | 
			
		||||
 | 
			
		||||
Memory::Memory(std::array<uint8_t, BIOS_SIZE>&& bios,
 | 
			
		||||
               std::vector<uint8_t>&& rom) noexcept
 | 
			
		||||
               std::vector<uint8_t>&& rom)
 | 
			
		||||
  : bios(std::move(bios))
 | 
			
		||||
  , board_wram({ 0 })
 | 
			
		||||
  , chip_wram({ 0 })
 | 
			
		||||
@@ -116,18 +117,24 @@ Memory::read_word(size_t address) const {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
Memory::write_word(size_t address, uint32_t halfword) {
 | 
			
		||||
Memory::write_word(size_t address, uint32_t word) {
 | 
			
		||||
    if (address & 0b11)
 | 
			
		||||
        log_warn("Writing to a non aligned word address");
 | 
			
		||||
 | 
			
		||||
    write(address, halfword & 0xFF);
 | 
			
		||||
    write(address + 1, halfword >> 8 & 0xFF);
 | 
			
		||||
    write(address + 2, halfword >> 16 & 0xFF);
 | 
			
		||||
    write(address + 3, halfword >> 24 & 0xFF);
 | 
			
		||||
    write(address, word & 0xFF);
 | 
			
		||||
    write(address + 1, word >> 8 & 0xFF);
 | 
			
		||||
    write(address + 2, word >> 16 & 0xFF);
 | 
			
		||||
    write(address + 3, word >> 24 & 0xFF);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
Memory::parse_header() {
 | 
			
		||||
 | 
			
		||||
    if (rom.size() < header.HEADER_SIZE) {
 | 
			
		||||
        throw std::out_of_range(
 | 
			
		||||
          "ROM is not large enough to even have a header");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // entrypoint
 | 
			
		||||
    header.entrypoint =
 | 
			
		||||
      rom[0x00] | rom[0x01] << 8 | rom[0x02] << 16 | rom[0x03] << 24;
 | 
			
		||||
 
 | 
			
		||||
@@ -3,8 +3,6 @@
 | 
			
		||||
#include <array>
 | 
			
		||||
#include <bit>
 | 
			
		||||
#include <fmt/core.h>
 | 
			
		||||
#include <iomanip>
 | 
			
		||||
#include <sstream>
 | 
			
		||||
#include <string>
 | 
			
		||||
 | 
			
		||||
// Why I wrote this myself? I do not know
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user