tests: [WIP] add unit tests for some of the instructions
Signed-off-by: Amneesh Singh <natto@weirdnatto.in>
This commit is contained in:
		@@ -116,27 +116,29 @@ Cpu::chg_mode(const Mode to) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
Cpu::exec_arm(const ArmInstruction instruction) {
 | 
			
		||||
    auto cond = instruction.get_condition();
 | 
			
		||||
    auto data = instruction.get_data();
 | 
			
		||||
Cpu::exec_arm(const arm::ArmInstruction instruction) {
 | 
			
		||||
    auto cond = instruction.condition;
 | 
			
		||||
    auto data = instruction.data;
 | 
			
		||||
 | 
			
		||||
    if (!cpsr.condition(cond)) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    auto pc_error = [](uint8_t r) {
 | 
			
		||||
        if (r == 15)
 | 
			
		||||
        if (r == PC_INDEX)
 | 
			
		||||
            log_error("Using PC (R15) as operand register");
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    auto pc_warn = [](uint8_t r) {
 | 
			
		||||
        if (r == 15)
 | 
			
		||||
        if (r == PC_INDEX)
 | 
			
		||||
            log_warn("Using PC (R15) as operand register");
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    using namespace arm;
 | 
			
		||||
 | 
			
		||||
    std::visit(
 | 
			
		||||
      overloaded{
 | 
			
		||||
        [this, pc_warn](ArmInstruction::BranchAndExchange& data) {
 | 
			
		||||
        [this, pc_warn](BranchAndExchange& data) {
 | 
			
		||||
            State state = static_cast<State>(data.rn & 1);
 | 
			
		||||
 | 
			
		||||
            pc_warn(data.rn);
 | 
			
		||||
@@ -156,7 +158,7 @@ Cpu::exec_arm(const ArmInstruction instruction) {
 | 
			
		||||
            // pc is affected so flush the pipeline
 | 
			
		||||
            is_flushed = true;
 | 
			
		||||
        },
 | 
			
		||||
        [this](ArmInstruction::Branch& data) {
 | 
			
		||||
        [this](Branch& data) {
 | 
			
		||||
            if (data.link)
 | 
			
		||||
                gpr[14] = pc - ARM_INSTRUCTION_SIZE;
 | 
			
		||||
 | 
			
		||||
@@ -168,7 +170,7 @@ Cpu::exec_arm(const ArmInstruction instruction) {
 | 
			
		||||
            // pc is affected so flush the pipeline
 | 
			
		||||
            is_flushed = true;
 | 
			
		||||
        },
 | 
			
		||||
        [this, pc_error](ArmInstruction::Multiply& data) {
 | 
			
		||||
        [this, pc_error](Multiply& data) {
 | 
			
		||||
            if (data.rd == data.rm)
 | 
			
		||||
                log_error("rd and rm are not distinct in {}",
 | 
			
		||||
                          typeid(data).name());
 | 
			
		||||
@@ -186,7 +188,7 @@ Cpu::exec_arm(const ArmInstruction instruction) {
 | 
			
		||||
                cpsr.set_c(0);
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        [this, pc_error](ArmInstruction::MultiplyLong& data) {
 | 
			
		||||
        [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 {}",
 | 
			
		||||
@@ -226,8 +228,8 @@ Cpu::exec_arm(const ArmInstruction instruction) {
 | 
			
		||||
                cpsr.set_v(0);
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        [](ArmInstruction::Undefined) { log_warn("Undefined instruction"); },
 | 
			
		||||
        [this, pc_error](ArmInstruction::SingleDataSwap& data) {
 | 
			
		||||
        [](Undefined) { log_warn("Undefined instruction"); },
 | 
			
		||||
        [this, pc_error](SingleDataSwap& data) {
 | 
			
		||||
            pc_error(data.rm);
 | 
			
		||||
            pc_error(data.rn);
 | 
			
		||||
            pc_error(data.rd);
 | 
			
		||||
@@ -240,7 +242,7 @@ Cpu::exec_arm(const ArmInstruction instruction) {
 | 
			
		||||
                bus->write_word(gpr[data.rn], gpr[data.rm]);
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        [this, pc_warn, pc_error](ArmInstruction::SingleDataTransfer& data) {
 | 
			
		||||
        [this, pc_warn, pc_error](SingleDataTransfer& data) {
 | 
			
		||||
            uint32_t offset  = 0;
 | 
			
		||||
            uint32_t address = gpr[data.rn];
 | 
			
		||||
 | 
			
		||||
@@ -316,7 +318,7 @@ Cpu::exec_arm(const ArmInstruction instruction) {
 | 
			
		||||
            if (data.rd == PC_INDEX && data.load)
 | 
			
		||||
                is_flushed = true;
 | 
			
		||||
        },
 | 
			
		||||
        [this, pc_warn, pc_error](ArmInstruction::HalfwordTransfer& data) {
 | 
			
		||||
        [this, pc_warn, pc_error](HalfwordTransfer& data) {
 | 
			
		||||
            uint32_t address = gpr[data.rn];
 | 
			
		||||
 | 
			
		||||
            if (!data.pre && data.write)
 | 
			
		||||
@@ -345,15 +347,16 @@ Cpu::exec_arm(const ArmInstruction instruction) {
 | 
			
		||||
                        gpr[data.rd] = bus->read_halfword(address);
 | 
			
		||||
 | 
			
		||||
                        // sign extend the halfword
 | 
			
		||||
                        if (get_bit(gpr[data.rd], PC_INDEX))
 | 
			
		||||
                            gpr[data.rd] |= 0xFFFF0000;
 | 
			
		||||
                        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
 | 
			
		||||
                        if (get_bit(gpr[data.rd], 7))
 | 
			
		||||
                            gpr[data.rd] |= 0xFFFFFF00;
 | 
			
		||||
                        gpr[data.rd] =
 | 
			
		||||
                          (static_cast<int32_t>(gpr[data.rd]) << 24) >> 24;
 | 
			
		||||
                    }
 | 
			
		||||
                    // unsigned halfword
 | 
			
		||||
                } else if (data.half) {
 | 
			
		||||
@@ -379,7 +382,7 @@ Cpu::exec_arm(const ArmInstruction instruction) {
 | 
			
		||||
            if (data.rd == PC_INDEX && data.load)
 | 
			
		||||
                is_flushed = true;
 | 
			
		||||
        },
 | 
			
		||||
        [this, pc_error](ArmInstruction::BlockDataTransfer& data) {
 | 
			
		||||
        [this, pc_error](BlockDataTransfer& data) {
 | 
			
		||||
            uint32_t address  = gpr[data.rn];
 | 
			
		||||
            Mode mode         = cpsr.mode();
 | 
			
		||||
            uint8_t alignment = 4; // word
 | 
			
		||||
@@ -449,7 +452,7 @@ Cpu::exec_arm(const ArmInstruction instruction) {
 | 
			
		||||
            // load back the original mode registers
 | 
			
		||||
            chg_mode(mode);
 | 
			
		||||
        },
 | 
			
		||||
        [this, pc_error](ArmInstruction::PsrTransfer& data) {
 | 
			
		||||
        [this, pc_error](PsrTransfer& data) {
 | 
			
		||||
            if (data.spsr && cpsr.mode() == Mode::User) {
 | 
			
		||||
                log_error("Accessing SPSR in User mode in {}",
 | 
			
		||||
                          typeid(data).name());
 | 
			
		||||
@@ -458,18 +461,18 @@ Cpu::exec_arm(const ArmInstruction instruction) {
 | 
			
		||||
            Psr& psr = data.spsr ? spsr : cpsr;
 | 
			
		||||
 | 
			
		||||
            switch (data.type) {
 | 
			
		||||
                case ArmInstruction::PsrTransfer::Type::Mrs:
 | 
			
		||||
                case PsrTransfer::Type::Mrs:
 | 
			
		||||
                    pc_error(data.operand);
 | 
			
		||||
                    gpr[data.operand] = psr.raw();
 | 
			
		||||
                    break;
 | 
			
		||||
                case ArmInstruction::PsrTransfer::Type::Msr:
 | 
			
		||||
                case PsrTransfer::Type::Msr:
 | 
			
		||||
                    pc_error(data.operand);
 | 
			
		||||
 | 
			
		||||
                    if (cpsr.mode() != Mode::User) {
 | 
			
		||||
                        psr.set_all(gpr[data.operand]);
 | 
			
		||||
                        break;
 | 
			
		||||
                    }
 | 
			
		||||
                case ArmInstruction::PsrTransfer::Type::Msr_flg:
 | 
			
		||||
                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));
 | 
			
		||||
@@ -477,7 +480,7 @@ Cpu::exec_arm(const ArmInstruction instruction) {
 | 
			
		||||
                    break;
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        [this, pc_error](ArmInstruction::DataProcessing& data) {
 | 
			
		||||
        [this, pc_error](DataProcessing& data) {
 | 
			
		||||
            uint32_t op_1 = gpr[data.rn];
 | 
			
		||||
            uint32_t op_2 = 0;
 | 
			
		||||
 | 
			
		||||
@@ -671,7 +674,7 @@ Cpu::exec_arm(const ArmInstruction instruction) {
 | 
			
		||||
                    is_flushed = true;
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        [this](ArmInstruction::SoftwareInterrupt) {
 | 
			
		||||
        [this](SoftwareInterrupt) {
 | 
			
		||||
            chg_mode(Mode::Supervisor);
 | 
			
		||||
            pc   = 0x08;
 | 
			
		||||
            spsr = cpsr;
 | 
			
		||||
@@ -690,7 +693,7 @@ Cpu::step() {
 | 
			
		||||
    if (cpsr.state() == State::Arm) {
 | 
			
		||||
        debug(cur_pc);
 | 
			
		||||
        uint32_t x = bus->read_word(cur_pc);
 | 
			
		||||
        ArmInstruction instruction(x);
 | 
			
		||||
        arm::ArmInstruction instruction(x);
 | 
			
		||||
        log_info("{:#034b}", x);
 | 
			
		||||
 | 
			
		||||
        exec_arm(instruction);
 | 
			
		||||
 
 | 
			
		||||
@@ -336,7 +336,8 @@ ArmInstruction::disassemble() {
 | 
			
		||||
                if (*offset == 0) {
 | 
			
		||||
                    expression = "";
 | 
			
		||||
                } else {
 | 
			
		||||
                    expression = fmt::format(",#{:d}", *offset);
 | 
			
		||||
                    expression =
 | 
			
		||||
                      fmt::format(",{}#{:d}", (data.up ? '+' : '-'), *offset);
 | 
			
		||||
                }
 | 
			
		||||
            } else if (const Shift* shift = std::get_if<Shift>(&data.offset)) {
 | 
			
		||||
                // Shifts are always immediate in single data transfer
 | 
			
		||||
@@ -365,7 +366,8 @@ ArmInstruction::disassemble() {
 | 
			
		||||
                if (data.offset == 0) {
 | 
			
		||||
                    expression = "";
 | 
			
		||||
                } else {
 | 
			
		||||
                    expression = fmt::format(",#{:d}", data.offset);
 | 
			
		||||
                    expression = fmt::format(
 | 
			
		||||
                      ",{}#{:d}", (data.up ? '+' : '-'), data.offset);
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                expression =
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user