1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-09-29 00:56:21 +00:00
CLK/OSBindings/Mac/Clock SignalTests/ARMDecoderTests.mm

160 lines
4.7 KiB
Plaintext
Raw Normal View History

2024-02-21 20:43:24 +00:00
//
// ARMDecoderTests.m
// Clock Signal
//
// Created by Thomas Harte on 16/02/2024.
// Copyright 2024 Thomas Harte. All rights reserved.
//
#import <XCTest/XCTest.h>
#include "../../../InstructionSets/ARM/OperationMapper.hpp"
2024-02-26 03:01:51 +00:00
#include "../../../InstructionSets/ARM/Status.hpp"
2024-02-21 20:43:24 +00:00
using namespace InstructionSet::ARM;
namespace {
template <ShiftType type, bool set_carry>
void shift(uint32_t &source, uint32_t amount, uint32_t *carry = nullptr) {
2024-02-26 19:30:26 +00:00
switch(type) {
case ShiftType::LogicalLeft:
if(amount > 32) {
if constexpr (set_carry) *carry = 0;
source = 0;
2024-02-26 19:30:26 +00:00
} else if(amount == 32) {
if constexpr (set_carry) *carry = source & 1;
2024-02-26 19:30:26 +00:00
source = 0;
} else {
if constexpr (set_carry) *carry = source & (0x8000'0000 >> amount);
2024-02-26 19:30:26 +00:00
source <<= amount;
}
break;
default: break;
}
}
template <bool set_carry>
void shift(ShiftType type, uint32_t &source, uint32_t amount, uint32_t *carry) {
2024-02-26 19:30:26 +00:00
switch(type) {
case ShiftType::LogicalLeft:
shift<ShiftType::LogicalLeft, set_carry>(source, amount, carry);
break;
case ShiftType::LogicalRight:
shift<ShiftType::LogicalRight, set_carry>(source, amount, carry);
break;
case ShiftType::ArithmeticRight:
shift<ShiftType::ArithmeticRight, set_carry>(source, amount, carry);
break;
case ShiftType::RotateRight:
shift<ShiftType::RotateRight, set_carry>(source, amount, carry);
break;
2024-02-26 19:30:26 +00:00
}
}
2024-02-26 20:12:39 +00:00
struct Scheduler {
2024-02-26 19:30:26 +00:00
template <Flags f> void perform(Condition condition, DataProcessing fields) {
if(!status.test(condition)) {
return;
}
2024-02-26 20:12:39 +00:00
// TODO: how does register 15 fit into all of below? As an operand or as a target?
2024-02-26 19:30:26 +00:00
constexpr DataProcessingFlags flags(f);
auto &destination = registers_[fields.destination()];
const auto &operand1 = registers_[fields.operand1()];
uint32_t operand2;
uint32_t rotate_carry = 0;
// Populate carry from the shift only if it'll be used.
constexpr bool shift_sets_carry = is_logical(flags.operation()) && flags.set_condition_codes();
2024-02-26 20:10:00 +00:00
// Get operand 2.
2024-02-26 19:30:26 +00:00
if constexpr (flags.operand2_is_immediate()) {
operand2 = fields.immediate();
if(fields.rotate()) {
shift<ShiftType::RotateRight, shift_sets_carry>(operand2, fields.rotate(), &rotate_carry);
2024-02-26 19:30:26 +00:00
}
} else {
uint32_t shift_amount;
if(fields.shift_count_is_register()) {
shift_amount = registers_[fields.shift_register()];
} else {
shift_amount = fields.shift_amount();
}
operand2 = registers_[fields.operand2()];
shift<shift_sets_carry>(fields.shift_type(), operand2, shift_amount, &rotate_carry);
2024-02-26 19:30:26 +00:00
}
2024-02-26 20:10:00 +00:00
// Perform the data processing operation.
uint32_t conditions = 0;
2024-02-26 19:30:26 +00:00
switch(flags.operation()) {
2024-02-26 20:10:00 +00:00
// Logical operations.
case DataProcessingOperation::AND: conditions = destination = operand1 & operand2; break;
case DataProcessingOperation::EOR: conditions = destination = operand1 ^ operand2; break;
case DataProcessingOperation::ORR: conditions = destination = operand1 | operand2; break;
case DataProcessingOperation::BIC: conditions = destination = operand1 & ~operand2; break;
case DataProcessingOperation::MOV: conditions = destination = operand2; break;
case DataProcessingOperation::MVN: conditions = destination = ~operand2; break;
case DataProcessingOperation::TST: conditions = operand1 & operand2; break;
case DataProcessingOperation::TEQ: conditions = operand1 ^ operand2; break;
2024-02-26 19:30:26 +00:00
default: break; // ETC.
}
// Set N and Z in a unified way.
if constexpr (flags.set_condition_codes()) {
2024-02-26 20:10:00 +00:00
status.set_nz(conditions);
}
// Set C from the barrel shifter if applicable.
if constexpr (shift_sets_carry) {
status.set_c(rotate_carry);
}
2024-02-26 20:12:39 +00:00
// TODO: If register 15 was in use as a destination, write back and clean up.
2024-02-26 19:30:26 +00:00
}
template <Operation, Flags> void perform(Condition, Multiply) {}
template <Operation, Flags> void perform(Condition, SingleDataTransfer) {}
template <Operation, Flags> void perform(Condition, BlockDataTransfer) {}
2024-02-22 15:48:19 +00:00
template <Operation op> void perform(Condition condition, Branch branch) {
printf("Branch %sif %d; add %08x\n", op == Operation::BL ? "with link " : "", int(condition), branch.offset());
}
template <Operation, Flags> void perform(Condition, CoprocessorRegisterTransfer) {}
template <Flags> void perform(Condition, CoprocessorDataOperation) {}
template<Operation, Flags> void perform(Condition, CoprocessorDataTransfer) {}
void software_interrupt(Condition) {}
void unknown(uint32_t) {}
2024-02-26 19:30:26 +00:00
private:
Status status;
uint32_t registers_[16]; // TODO: register swaps with mode.
};
}
2024-02-21 20:43:24 +00:00
@interface ARMDecoderTests : XCTestCase
@end
@implementation ARMDecoderTests
- (void)testXYX {
Scheduler scheduler;
for(int c = 0; c < 65536; c++) {
InstructionSet::ARM::dispatch(c << 16, scheduler);
}
2024-02-22 15:48:19 +00:00
InstructionSet::ARM::dispatch(0xEAE06900, scheduler);
2024-02-21 20:43:24 +00:00
}
@end