tests: [WIP] add unit tests for some of the instructions
Signed-off-by: Amneesh Singh <natto@weirdnatto.in>
This commit is contained in:
		@@ -6,6 +6,7 @@
 | 
				
			|||||||
#include <fstream>
 | 
					#include <fstream>
 | 
				
			||||||
#include <iostream>
 | 
					#include <iostream>
 | 
				
			||||||
#include <memory>
 | 
					#include <memory>
 | 
				
			||||||
 | 
					#include <ostream>
 | 
				
			||||||
#include <unistd.h>
 | 
					#include <unistd.h>
 | 
				
			||||||
#include <vector>
 | 
					#include <vector>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -80,6 +81,9 @@ main(int argc, const char* argv[]) {
 | 
				
			|||||||
        return 1;
 | 
					        return 1;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    std::flush(std::cout);
 | 
				
			||||||
 | 
					    std::flush(std::cerr);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        Memory memory(std::move(bios), std::move(rom));
 | 
					        Memory memory(std::move(bios), std::move(rom));
 | 
				
			||||||
        Bus bus(memory);
 | 
					        Bus bus(memory);
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										11
									
								
								flake.nix
									
									
									
									
									
								
							
							
						
						
									
										11
									
								
								flake.nix
									
									
									
									
									
								
							@@ -8,17 +8,23 @@
 | 
				
			|||||||
      systems = [
 | 
					      systems = [
 | 
				
			||||||
        "x86_64-linux"
 | 
					        "x86_64-linux"
 | 
				
			||||||
        "aarch64-linux"
 | 
					        "aarch64-linux"
 | 
				
			||||||
 #       "i686-linux"
 | 
					 | 
				
			||||||
      ];
 | 
					      ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      eachSystem = with nixpkgs.lib; f: foldAttrs mergeAttrs { }
 | 
					      eachSystem = with nixpkgs.lib; f: foldAttrs mergeAttrs { }
 | 
				
			||||||
        (map (s: mapAttrs (_: v: { ${s} = v; }) (f s)) systems);
 | 
					        (map (s: mapAttrs (_: v: { ${s} = v; }) (f s)) systems);
 | 
				
			||||||
    in
 | 
					    in
 | 
				
			||||||
    eachSystem (system:
 | 
					    eachSystem (system:
 | 
				
			||||||
      let
 | 
					      let
 | 
				
			||||||
        pkgs = import nixpkgs { inherit system; };
 | 
					        pkgs = import nixpkgs { inherit system; };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # aliases
 | 
				
			||||||
        llvm = pkgs.llvmPackages_16;
 | 
					        llvm = pkgs.llvmPackages_16;
 | 
				
			||||||
        stdenv = llvm.libcxxStdenv;
 | 
					        stdenv = llvm.libcxxStdenv;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # packages
 | 
				
			||||||
 | 
					        catch2_v3 = pkgs.callPackage ./nix/catch2.nix { inherit stdenv; };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        #dependencies
 | 
				
			||||||
        nativeBuildInputs = with pkgs; [
 | 
					        nativeBuildInputs = with pkgs; [
 | 
				
			||||||
          meson
 | 
					          meson
 | 
				
			||||||
          ninja
 | 
					          ninja
 | 
				
			||||||
@@ -26,6 +32,7 @@
 | 
				
			|||||||
          # libraries
 | 
					          # libraries
 | 
				
			||||||
          pkg-config
 | 
					          pkg-config
 | 
				
			||||||
          fmt.dev
 | 
					          fmt.dev
 | 
				
			||||||
 | 
					          catch2_v3.dev
 | 
				
			||||||
        ];
 | 
					        ];
 | 
				
			||||||
      in
 | 
					      in
 | 
				
			||||||
      rec {
 | 
					      rec {
 | 
				
			||||||
@@ -51,6 +58,8 @@
 | 
				
			|||||||
          matar = pkgs.mkShell.override { inherit stdenv; } {
 | 
					          matar = pkgs.mkShell.override { inherit stdenv; } {
 | 
				
			||||||
            name = "matar";
 | 
					            name = "matar";
 | 
				
			||||||
            packages = nativeBuildInputs ++ (with pkgs; [
 | 
					            packages = nativeBuildInputs ++ (with pkgs; [
 | 
				
			||||||
 | 
					              llvm.libcxx
 | 
				
			||||||
 | 
					
 | 
				
			||||||
              # dev tools
 | 
					              # dev tools
 | 
				
			||||||
              clang-tools_16
 | 
					              clang-tools_16
 | 
				
			||||||
            ]);
 | 
					            ]);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -35,7 +35,7 @@ class Cpu {
 | 
				
			|||||||
    bool is_flushed;
 | 
					    bool is_flushed;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    void chg_mode(const Mode to);
 | 
					    void chg_mode(const Mode to);
 | 
				
			||||||
    void exec_arm(const ArmInstruction instruction);
 | 
					    void exec_arm(const arm::ArmInstruction instruction);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    struct {
 | 
					    struct {
 | 
				
			||||||
        std::array<uint32_t, GPR_COUNT - GPR_FIQ_FIRST - 1> fiq;
 | 
					        std::array<uint32_t, GPR_COUNT - GPR_FIQ_FIRST - 1> fiq;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -9,35 +9,26 @@ struct overloaded : Ts... {
 | 
				
			|||||||
template<class... Ts>
 | 
					template<class... Ts>
 | 
				
			||||||
overloaded(Ts...) -> overloaded<Ts...>;
 | 
					overloaded(Ts...) -> overloaded<Ts...>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class ArmInstruction {
 | 
					namespace arm {
 | 
				
			||||||
  public:
 | 
					struct BranchAndExchange {
 | 
				
			||||||
    ArmInstruction() = delete;
 | 
					 | 
				
			||||||
    ArmInstruction(uint32_t insn);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    auto get_condition() const { return condition; }
 | 
					 | 
				
			||||||
    auto get_data() const { return data; }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    std::string disassemble();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    struct BranchAndExchange {
 | 
					 | 
				
			||||||
    uint8_t rn;
 | 
					    uint8_t rn;
 | 
				
			||||||
    };
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    struct Branch {
 | 
					struct Branch {
 | 
				
			||||||
    bool link;
 | 
					    bool link;
 | 
				
			||||||
    uint32_t offset;
 | 
					    uint32_t offset;
 | 
				
			||||||
    };
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    struct Multiply {
 | 
					struct Multiply {
 | 
				
			||||||
    uint8_t rm;
 | 
					    uint8_t rm;
 | 
				
			||||||
    uint8_t rs;
 | 
					    uint8_t rs;
 | 
				
			||||||
    uint8_t rn;
 | 
					    uint8_t rn;
 | 
				
			||||||
    uint8_t rd;
 | 
					    uint8_t rd;
 | 
				
			||||||
    bool set;
 | 
					    bool set;
 | 
				
			||||||
    bool acc;
 | 
					    bool acc;
 | 
				
			||||||
    };
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    struct MultiplyLong {
 | 
					struct MultiplyLong {
 | 
				
			||||||
    uint8_t rm;
 | 
					    uint8_t rm;
 | 
				
			||||||
    uint8_t rs;
 | 
					    uint8_t rs;
 | 
				
			||||||
    uint8_t rdlo;
 | 
					    uint8_t rdlo;
 | 
				
			||||||
@@ -45,16 +36,16 @@ class ArmInstruction {
 | 
				
			|||||||
    bool set;
 | 
					    bool set;
 | 
				
			||||||
    bool acc;
 | 
					    bool acc;
 | 
				
			||||||
    bool uns;
 | 
					    bool uns;
 | 
				
			||||||
    };
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    struct SingleDataSwap {
 | 
					struct SingleDataSwap {
 | 
				
			||||||
    uint8_t rm;
 | 
					    uint8_t rm;
 | 
				
			||||||
    uint8_t rd;
 | 
					    uint8_t rd;
 | 
				
			||||||
    uint8_t rn;
 | 
					    uint8_t rn;
 | 
				
			||||||
    bool byte;
 | 
					    bool byte;
 | 
				
			||||||
    };
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    struct SingleDataTransfer {
 | 
					struct SingleDataTransfer {
 | 
				
			||||||
    std::variant<uint16_t, Shift> offset;
 | 
					    std::variant<uint16_t, Shift> offset;
 | 
				
			||||||
    uint8_t rd;
 | 
					    uint8_t rd;
 | 
				
			||||||
    uint8_t rn;
 | 
					    uint8_t rn;
 | 
				
			||||||
@@ -63,9 +54,9 @@ class ArmInstruction {
 | 
				
			|||||||
    bool byte;
 | 
					    bool byte;
 | 
				
			||||||
    bool up;
 | 
					    bool up;
 | 
				
			||||||
    bool pre;
 | 
					    bool pre;
 | 
				
			||||||
    };
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    struct HalfwordTransfer {
 | 
					struct HalfwordTransfer {
 | 
				
			||||||
    uint8_t offset;
 | 
					    uint8_t offset;
 | 
				
			||||||
    bool half;
 | 
					    bool half;
 | 
				
			||||||
    bool sign;
 | 
					    bool sign;
 | 
				
			||||||
@@ -77,9 +68,9 @@ class ArmInstruction {
 | 
				
			|||||||
    bool imm;
 | 
					    bool imm;
 | 
				
			||||||
    bool up;
 | 
					    bool up;
 | 
				
			||||||
    bool pre;
 | 
					    bool pre;
 | 
				
			||||||
    };
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    struct BlockDataTransfer {
 | 
					struct BlockDataTransfer {
 | 
				
			||||||
    uint16_t regs;
 | 
					    uint16_t regs;
 | 
				
			||||||
    uint8_t rn;
 | 
					    uint8_t rn;
 | 
				
			||||||
    bool load;
 | 
					    bool load;
 | 
				
			||||||
@@ -87,17 +78,17 @@ class ArmInstruction {
 | 
				
			|||||||
    bool s;
 | 
					    bool s;
 | 
				
			||||||
    bool up;
 | 
					    bool up;
 | 
				
			||||||
    bool pre;
 | 
					    bool pre;
 | 
				
			||||||
    };
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    struct DataProcessing {
 | 
					struct DataProcessing {
 | 
				
			||||||
    std::variant<Shift, uint32_t> operand;
 | 
					    std::variant<Shift, uint32_t> operand;
 | 
				
			||||||
    uint8_t rd;
 | 
					    uint8_t rd;
 | 
				
			||||||
    uint8_t rn;
 | 
					    uint8_t rn;
 | 
				
			||||||
    bool set;
 | 
					    bool set;
 | 
				
			||||||
    OpCode opcode;
 | 
					    OpCode opcode;
 | 
				
			||||||
    };
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    struct PsrTransfer {
 | 
					struct PsrTransfer {
 | 
				
			||||||
    enum class Type {
 | 
					    enum class Type {
 | 
				
			||||||
        Mrs,
 | 
					        Mrs,
 | 
				
			||||||
        Msr,
 | 
					        Msr,
 | 
				
			||||||
@@ -109,9 +100,9 @@ class ArmInstruction {
 | 
				
			|||||||
    Type type;
 | 
					    Type type;
 | 
				
			||||||
    // ignored outside MSR_flg
 | 
					    // ignored outside MSR_flg
 | 
				
			||||||
    bool imm;
 | 
					    bool imm;
 | 
				
			||||||
    };
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    struct CoprocessorDataTransfer {
 | 
					struct CoprocessorDataTransfer {
 | 
				
			||||||
    uint8_t offset;
 | 
					    uint8_t offset;
 | 
				
			||||||
    uint8_t cpn;
 | 
					    uint8_t cpn;
 | 
				
			||||||
    uint8_t crd;
 | 
					    uint8_t crd;
 | 
				
			||||||
@@ -121,18 +112,18 @@ class ArmInstruction {
 | 
				
			|||||||
    bool len;
 | 
					    bool len;
 | 
				
			||||||
    bool up;
 | 
					    bool up;
 | 
				
			||||||
    bool pre;
 | 
					    bool pre;
 | 
				
			||||||
    };
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    struct CoprocessorDataOperation {
 | 
					struct CoprocessorDataOperation {
 | 
				
			||||||
    uint8_t crm;
 | 
					    uint8_t crm;
 | 
				
			||||||
    uint8_t cp;
 | 
					    uint8_t cp;
 | 
				
			||||||
    uint8_t cpn;
 | 
					    uint8_t cpn;
 | 
				
			||||||
    uint8_t crd;
 | 
					    uint8_t crd;
 | 
				
			||||||
    uint8_t crn;
 | 
					    uint8_t crn;
 | 
				
			||||||
    uint8_t cp_opc;
 | 
					    uint8_t cp_opc;
 | 
				
			||||||
    };
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    struct CoprocessorRegisterTransfer {
 | 
					struct CoprocessorRegisterTransfer {
 | 
				
			||||||
    uint8_t crm;
 | 
					    uint8_t crm;
 | 
				
			||||||
    uint8_t cp;
 | 
					    uint8_t cp;
 | 
				
			||||||
    uint8_t cpn;
 | 
					    uint8_t cpn;
 | 
				
			||||||
@@ -140,12 +131,12 @@ class ArmInstruction {
 | 
				
			|||||||
    uint8_t crn;
 | 
					    uint8_t crn;
 | 
				
			||||||
    bool load;
 | 
					    bool load;
 | 
				
			||||||
    uint8_t cp_opc;
 | 
					    uint8_t cp_opc;
 | 
				
			||||||
    };
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    struct Undefined {};
 | 
					struct Undefined {};
 | 
				
			||||||
    struct SoftwareInterrupt {};
 | 
					struct SoftwareInterrupt {};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    using InstructionData = std::variant<BranchAndExchange,
 | 
					using InstructionData = std::variant<BranchAndExchange,
 | 
				
			||||||
                                     Branch,
 | 
					                                     Branch,
 | 
				
			||||||
                                     Multiply,
 | 
					                                     Multiply,
 | 
				
			||||||
                                     MultiplyLong,
 | 
					                                     MultiplyLong,
 | 
				
			||||||
@@ -161,7 +152,11 @@ class ArmInstruction {
 | 
				
			|||||||
                                     Undefined,
 | 
					                                     Undefined,
 | 
				
			||||||
                                     SoftwareInterrupt>;
 | 
					                                     SoftwareInterrupt>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private:
 | 
					struct ArmInstruction {
 | 
				
			||||||
    Condition condition;
 | 
					    Condition condition;
 | 
				
			||||||
    InstructionData data;
 | 
					    InstructionData data;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ArmInstruction(uint32_t insn);
 | 
				
			||||||
 | 
					    std::string disassemble();
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										26
									
								
								meson.build
									
									
									
									
									
								
							
							
						
						
									
										26
									
								
								meson.build
									
									
									
									
									
								
							@@ -3,10 +3,32 @@ project('matar', 'cpp',
 | 
				
			|||||||
  license : 'GPLv3',
 | 
					  license : 'GPLv3',
 | 
				
			||||||
  default_options : ['warning_level=3',
 | 
					  default_options : ['warning_level=3',
 | 
				
			||||||
                     'werror=true',
 | 
					                     'werror=true',
 | 
				
			||||||
                     'optimization=3',
 | 
					                     'optimization=3'])
 | 
				
			||||||
                     'cpp_std=c++20'])
 | 
					
 | 
				
			||||||
 | 
					compiler = meson.get_compiler('cpp')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if compiler.has_argument('-std=c++23')
 | 
				
			||||||
 | 
					  add_global_arguments('-std=c++23', language: 'cpp')
 | 
				
			||||||
 | 
					elif compiler.has_argument('-std=c++2b')
 | 
				
			||||||
 | 
					  add_global_arguments('-std=c++2b', language: 'cpp')
 | 
				
			||||||
 | 
					else
 | 
				
			||||||
 | 
					  error(compiler.get_id() + ' ' + compiler.version() + 'does not meet the compiler requirements')
 | 
				
			||||||
 | 
					endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					'''
 | 
				
			||||||
 | 
					TODO: use <print> and <format> instead of libfmt once LLVM 17 is out
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if compiler.has_argument('-fexperimental-library')
 | 
				
			||||||
 | 
					  add_global_arguments('-fexperimental-library', language: 'cpp')
 | 
				
			||||||
 | 
					else
 | 
				
			||||||
 | 
					  error(compiler.get_id() + ' ' + compiler.version() + 'does not support -fexperimental-library')
 | 
				
			||||||
 | 
					endif
 | 
				
			||||||
 | 
					'''
 | 
				
			||||||
 | 
					
 | 
				
			||||||
inc = include_directories('include')
 | 
					inc = include_directories('include')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
subdir('include')
 | 
					subdir('include')
 | 
				
			||||||
subdir('src')
 | 
					subdir('src')
 | 
				
			||||||
subdir('apps')
 | 
					subdir('apps')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					subdir('tests')
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										17
									
								
								nix/catch2.nix
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								nix/catch2.nix
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,17 @@
 | 
				
			|||||||
 | 
					{ stdenv, fetchFromGitHub, meson, ninja }:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					stdenv.mkDerivation rec {
 | 
				
			||||||
 | 
					  name = "catch2";
 | 
				
			||||||
 | 
					  version = "3.4.0";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  src = fetchFromGitHub {
 | 
				
			||||||
 | 
					    owner = "catchorg";
 | 
				
			||||||
 | 
					    repo = "Catch2";
 | 
				
			||||||
 | 
					    rev = "v${version}";
 | 
				
			||||||
 | 
					    sha256 = "sha256-DqGGfNjKPW9HFJrX9arFHyNYjB61uoL6NabZatTWrr0=";
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  nativeBuildInputs = [ meson ninja ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  outputs = [ "out" "dev" ];
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -116,27 +116,29 @@ Cpu::chg_mode(const Mode to) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void
 | 
					void
 | 
				
			||||||
Cpu::exec_arm(const ArmInstruction instruction) {
 | 
					Cpu::exec_arm(const arm::ArmInstruction instruction) {
 | 
				
			||||||
    auto cond = instruction.get_condition();
 | 
					    auto cond = instruction.condition;
 | 
				
			||||||
    auto data = instruction.get_data();
 | 
					    auto data = instruction.data;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (!cpsr.condition(cond)) {
 | 
					    if (!cpsr.condition(cond)) {
 | 
				
			||||||
        return;
 | 
					        return;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    auto pc_error = [](uint8_t r) {
 | 
					    auto pc_error = [](uint8_t r) {
 | 
				
			||||||
        if (r == 15)
 | 
					        if (r == PC_INDEX)
 | 
				
			||||||
            log_error("Using PC (R15) as operand register");
 | 
					            log_error("Using PC (R15) as operand register");
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    auto pc_warn = [](uint8_t r) {
 | 
					    auto pc_warn = [](uint8_t r) {
 | 
				
			||||||
        if (r == 15)
 | 
					        if (r == PC_INDEX)
 | 
				
			||||||
            log_warn("Using PC (R15) as operand register");
 | 
					            log_warn("Using PC (R15) as operand register");
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    using namespace arm;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    std::visit(
 | 
					    std::visit(
 | 
				
			||||||
      overloaded{
 | 
					      overloaded{
 | 
				
			||||||
        [this, pc_warn](ArmInstruction::BranchAndExchange& data) {
 | 
					        [this, pc_warn](BranchAndExchange& data) {
 | 
				
			||||||
            State state = static_cast<State>(data.rn & 1);
 | 
					            State state = static_cast<State>(data.rn & 1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            pc_warn(data.rn);
 | 
					            pc_warn(data.rn);
 | 
				
			||||||
@@ -156,7 +158,7 @@ Cpu::exec_arm(const ArmInstruction instruction) {
 | 
				
			|||||||
            // pc is affected so flush the pipeline
 | 
					            // pc is affected so flush the pipeline
 | 
				
			||||||
            is_flushed = true;
 | 
					            is_flushed = true;
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        [this](ArmInstruction::Branch& data) {
 | 
					        [this](Branch& data) {
 | 
				
			||||||
            if (data.link)
 | 
					            if (data.link)
 | 
				
			||||||
                gpr[14] = pc - ARM_INSTRUCTION_SIZE;
 | 
					                gpr[14] = pc - ARM_INSTRUCTION_SIZE;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -168,7 +170,7 @@ Cpu::exec_arm(const ArmInstruction instruction) {
 | 
				
			|||||||
            // pc is affected so flush the pipeline
 | 
					            // pc is affected so flush the pipeline
 | 
				
			||||||
            is_flushed = true;
 | 
					            is_flushed = true;
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        [this, pc_error](ArmInstruction::Multiply& data) {
 | 
					        [this, pc_error](Multiply& data) {
 | 
				
			||||||
            if (data.rd == data.rm)
 | 
					            if (data.rd == data.rm)
 | 
				
			||||||
                log_error("rd and rm are not distinct in {}",
 | 
					                log_error("rd and rm are not distinct in {}",
 | 
				
			||||||
                          typeid(data).name());
 | 
					                          typeid(data).name());
 | 
				
			||||||
@@ -186,7 +188,7 @@ Cpu::exec_arm(const ArmInstruction instruction) {
 | 
				
			|||||||
                cpsr.set_c(0);
 | 
					                cpsr.set_c(0);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        [this, pc_error](ArmInstruction::MultiplyLong& data) {
 | 
					        [this, pc_error](MultiplyLong& data) {
 | 
				
			||||||
            if (data.rdhi == data.rdlo || data.rdhi == data.rm ||
 | 
					            if (data.rdhi == data.rdlo || data.rdhi == data.rm ||
 | 
				
			||||||
                data.rdlo == data.rm)
 | 
					                data.rdlo == data.rm)
 | 
				
			||||||
                log_error("rdhi, rdlo and rm are not distinct in {}",
 | 
					                log_error("rdhi, rdlo and rm are not distinct in {}",
 | 
				
			||||||
@@ -226,8 +228,8 @@ Cpu::exec_arm(const ArmInstruction instruction) {
 | 
				
			|||||||
                cpsr.set_v(0);
 | 
					                cpsr.set_v(0);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        [](ArmInstruction::Undefined) { log_warn("Undefined instruction"); },
 | 
					        [](Undefined) { log_warn("Undefined instruction"); },
 | 
				
			||||||
        [this, pc_error](ArmInstruction::SingleDataSwap& data) {
 | 
					        [this, pc_error](SingleDataSwap& data) {
 | 
				
			||||||
            pc_error(data.rm);
 | 
					            pc_error(data.rm);
 | 
				
			||||||
            pc_error(data.rn);
 | 
					            pc_error(data.rn);
 | 
				
			||||||
            pc_error(data.rd);
 | 
					            pc_error(data.rd);
 | 
				
			||||||
@@ -240,7 +242,7 @@ Cpu::exec_arm(const ArmInstruction instruction) {
 | 
				
			|||||||
                bus->write_word(gpr[data.rn], gpr[data.rm]);
 | 
					                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 offset  = 0;
 | 
				
			||||||
            uint32_t address = gpr[data.rn];
 | 
					            uint32_t address = gpr[data.rn];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -316,7 +318,7 @@ Cpu::exec_arm(const ArmInstruction instruction) {
 | 
				
			|||||||
            if (data.rd == PC_INDEX && data.load)
 | 
					            if (data.rd == PC_INDEX && data.load)
 | 
				
			||||||
                is_flushed = true;
 | 
					                is_flushed = true;
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        [this, pc_warn, pc_error](ArmInstruction::HalfwordTransfer& data) {
 | 
					        [this, pc_warn, pc_error](HalfwordTransfer& data) {
 | 
				
			||||||
            uint32_t address = gpr[data.rn];
 | 
					            uint32_t address = gpr[data.rn];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (!data.pre && data.write)
 | 
					            if (!data.pre && data.write)
 | 
				
			||||||
@@ -345,15 +347,16 @@ Cpu::exec_arm(const ArmInstruction instruction) {
 | 
				
			|||||||
                        gpr[data.rd] = bus->read_halfword(address);
 | 
					                        gpr[data.rd] = bus->read_halfword(address);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                        // sign extend the halfword
 | 
					                        // sign extend the halfword
 | 
				
			||||||
                        if (get_bit(gpr[data.rd], PC_INDEX))
 | 
					                        gpr[data.rd] =
 | 
				
			||||||
                            gpr[data.rd] |= 0xFFFF0000;
 | 
					                          (static_cast<int32_t>(gpr[data.rd]) << 16) >> 16;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                        // byte
 | 
					                        // byte
 | 
				
			||||||
                    } else {
 | 
					                    } else {
 | 
				
			||||||
                        gpr[data.rd] = bus->read_byte(address);
 | 
					                        gpr[data.rd] = bus->read_byte(address);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                        // sign extend the byte
 | 
					                        // sign extend the byte
 | 
				
			||||||
                        if (get_bit(gpr[data.rd], 7))
 | 
					                        gpr[data.rd] =
 | 
				
			||||||
                            gpr[data.rd] |= 0xFFFFFF00;
 | 
					                          (static_cast<int32_t>(gpr[data.rd]) << 24) >> 24;
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                    // unsigned halfword
 | 
					                    // unsigned halfword
 | 
				
			||||||
                } else if (data.half) {
 | 
					                } else if (data.half) {
 | 
				
			||||||
@@ -379,7 +382,7 @@ Cpu::exec_arm(const ArmInstruction instruction) {
 | 
				
			|||||||
            if (data.rd == PC_INDEX && data.load)
 | 
					            if (data.rd == PC_INDEX && data.load)
 | 
				
			||||||
                is_flushed = true;
 | 
					                is_flushed = true;
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        [this, pc_error](ArmInstruction::BlockDataTransfer& data) {
 | 
					        [this, pc_error](BlockDataTransfer& data) {
 | 
				
			||||||
            uint32_t address  = gpr[data.rn];
 | 
					            uint32_t address  = gpr[data.rn];
 | 
				
			||||||
            Mode mode         = cpsr.mode();
 | 
					            Mode mode         = cpsr.mode();
 | 
				
			||||||
            uint8_t alignment = 4; // word
 | 
					            uint8_t alignment = 4; // word
 | 
				
			||||||
@@ -449,7 +452,7 @@ Cpu::exec_arm(const ArmInstruction instruction) {
 | 
				
			|||||||
            // load back the original mode registers
 | 
					            // load back the original mode registers
 | 
				
			||||||
            chg_mode(mode);
 | 
					            chg_mode(mode);
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        [this, pc_error](ArmInstruction::PsrTransfer& data) {
 | 
					        [this, pc_error](PsrTransfer& data) {
 | 
				
			||||||
            if (data.spsr && cpsr.mode() == Mode::User) {
 | 
					            if (data.spsr && cpsr.mode() == Mode::User) {
 | 
				
			||||||
                log_error("Accessing SPSR in User mode in {}",
 | 
					                log_error("Accessing SPSR in User mode in {}",
 | 
				
			||||||
                          typeid(data).name());
 | 
					                          typeid(data).name());
 | 
				
			||||||
@@ -458,18 +461,18 @@ Cpu::exec_arm(const ArmInstruction instruction) {
 | 
				
			|||||||
            Psr& psr = data.spsr ? spsr : cpsr;
 | 
					            Psr& psr = data.spsr ? spsr : cpsr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            switch (data.type) {
 | 
					            switch (data.type) {
 | 
				
			||||||
                case ArmInstruction::PsrTransfer::Type::Mrs:
 | 
					                case PsrTransfer::Type::Mrs:
 | 
				
			||||||
                    pc_error(data.operand);
 | 
					                    pc_error(data.operand);
 | 
				
			||||||
                    gpr[data.operand] = psr.raw();
 | 
					                    gpr[data.operand] = psr.raw();
 | 
				
			||||||
                    break;
 | 
					                    break;
 | 
				
			||||||
                case ArmInstruction::PsrTransfer::Type::Msr:
 | 
					                case PsrTransfer::Type::Msr:
 | 
				
			||||||
                    pc_error(data.operand);
 | 
					                    pc_error(data.operand);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    if (cpsr.mode() != Mode::User) {
 | 
					                    if (cpsr.mode() != Mode::User) {
 | 
				
			||||||
                        psr.set_all(gpr[data.operand]);
 | 
					                        psr.set_all(gpr[data.operand]);
 | 
				
			||||||
                        break;
 | 
					                        break;
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                case ArmInstruction::PsrTransfer::Type::Msr_flg:
 | 
					                case PsrTransfer::Type::Msr_flg:
 | 
				
			||||||
                    psr.set_n(get_bit(data.operand, 31));
 | 
					                    psr.set_n(get_bit(data.operand, 31));
 | 
				
			||||||
                    psr.set_z(get_bit(data.operand, 30));
 | 
					                    psr.set_z(get_bit(data.operand, 30));
 | 
				
			||||||
                    psr.set_c(get_bit(data.operand, 29));
 | 
					                    psr.set_c(get_bit(data.operand, 29));
 | 
				
			||||||
@@ -477,7 +480,7 @@ Cpu::exec_arm(const ArmInstruction instruction) {
 | 
				
			|||||||
                    break;
 | 
					                    break;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        [this, pc_error](ArmInstruction::DataProcessing& data) {
 | 
					        [this, pc_error](DataProcessing& data) {
 | 
				
			||||||
            uint32_t op_1 = gpr[data.rn];
 | 
					            uint32_t op_1 = gpr[data.rn];
 | 
				
			||||||
            uint32_t op_2 = 0;
 | 
					            uint32_t op_2 = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -671,7 +674,7 @@ Cpu::exec_arm(const ArmInstruction instruction) {
 | 
				
			|||||||
                    is_flushed = true;
 | 
					                    is_flushed = true;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        [this](ArmInstruction::SoftwareInterrupt) {
 | 
					        [this](SoftwareInterrupt) {
 | 
				
			||||||
            chg_mode(Mode::Supervisor);
 | 
					            chg_mode(Mode::Supervisor);
 | 
				
			||||||
            pc   = 0x08;
 | 
					            pc   = 0x08;
 | 
				
			||||||
            spsr = cpsr;
 | 
					            spsr = cpsr;
 | 
				
			||||||
@@ -690,7 +693,7 @@ Cpu::step() {
 | 
				
			|||||||
    if (cpsr.state() == State::Arm) {
 | 
					    if (cpsr.state() == State::Arm) {
 | 
				
			||||||
        debug(cur_pc);
 | 
					        debug(cur_pc);
 | 
				
			||||||
        uint32_t x = bus->read_word(cur_pc);
 | 
					        uint32_t x = bus->read_word(cur_pc);
 | 
				
			||||||
        ArmInstruction instruction(x);
 | 
					        arm::ArmInstruction instruction(x);
 | 
				
			||||||
        log_info("{:#034b}", x);
 | 
					        log_info("{:#034b}", x);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        exec_arm(instruction);
 | 
					        exec_arm(instruction);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -336,7 +336,8 @@ ArmInstruction::disassemble() {
 | 
				
			|||||||
                if (*offset == 0) {
 | 
					                if (*offset == 0) {
 | 
				
			||||||
                    expression = "";
 | 
					                    expression = "";
 | 
				
			||||||
                } else {
 | 
					                } else {
 | 
				
			||||||
                    expression = fmt::format(",#{:d}", *offset);
 | 
					                    expression =
 | 
				
			||||||
 | 
					                      fmt::format(",{}#{:d}", (data.up ? '+' : '-'), *offset);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            } else if (const Shift* shift = std::get_if<Shift>(&data.offset)) {
 | 
					            } else if (const Shift* shift = std::get_if<Shift>(&data.offset)) {
 | 
				
			||||||
                // Shifts are always immediate in single data transfer
 | 
					                // Shifts are always immediate in single data transfer
 | 
				
			||||||
@@ -365,7 +366,8 @@ ArmInstruction::disassemble() {
 | 
				
			|||||||
                if (data.offset == 0) {
 | 
					                if (data.offset == 0) {
 | 
				
			||||||
                    expression = "";
 | 
					                    expression = "";
 | 
				
			||||||
                } else {
 | 
					                } else {
 | 
				
			||||||
                    expression = fmt::format(",#{:d}", data.offset);
 | 
					                    expression = fmt::format(
 | 
				
			||||||
 | 
					                      ",{}#{:d}", (data.up ? '+' : '-'), data.offset);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            } else {
 | 
					            } else {
 | 
				
			||||||
                expression =
 | 
					                expression =
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										198
									
								
								tests/cpu/instruction.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										198
									
								
								tests/cpu/instruction.cc
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,198 @@
 | 
				
			|||||||
 | 
					#include "cpu/instruction.hh"
 | 
				
			||||||
 | 
					#include "cpu/utility.hh"
 | 
				
			||||||
 | 
					#include <catch2/catch_test_macros.hpp>
 | 
				
			||||||
 | 
					#include <iostream>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static constexpr auto TAG = "disassembler";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					using namespace arm;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					TEST_CASE("Branch and Exchange", TAG) {
 | 
				
			||||||
 | 
					    uint32_t raw = 0b11000001001011111111111100011010;
 | 
				
			||||||
 | 
					    ArmInstruction instruction(raw);
 | 
				
			||||||
 | 
					    BranchAndExchange* bx = nullptr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    REQUIRE((bx = std::get_if<BranchAndExchange>(&instruction.data)));
 | 
				
			||||||
 | 
					    REQUIRE(instruction.condition == Condition::GT);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    REQUIRE(bx->rn == 10);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    REQUIRE(instruction.disassemble() == "BXGT R10");
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					TEST_CASE("Branch", TAG) {
 | 
				
			||||||
 | 
					    uint32_t raw = 0b11101011100001010111111111000011;
 | 
				
			||||||
 | 
					    ArmInstruction instruction(raw);
 | 
				
			||||||
 | 
					    Branch* b = nullptr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    REQUIRE((b = std::get_if<Branch>(&instruction.data)));
 | 
				
			||||||
 | 
					    REQUIRE(instruction.condition == Condition::AL);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // last 24 bits = 8748995
 | 
				
			||||||
 | 
					    // (8748995 << 8) >> 6 sign extended = 0xFE15FF0C
 | 
				
			||||||
 | 
					    // Also +8 since PC is two instructions ahead
 | 
				
			||||||
 | 
					    REQUIRE(b->offset == 0xFE15FF14);
 | 
				
			||||||
 | 
					    REQUIRE(b->link == true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    REQUIRE(instruction.disassemble() == "BL 0xFE15FF14");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    b->link = false;
 | 
				
			||||||
 | 
					    REQUIRE(instruction.disassemble() == "B 0xFE15FF14");
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					TEST_CASE("Multiply", TAG) {
 | 
				
			||||||
 | 
					    uint32_t raw = 0b00000000001110101110111110010000;
 | 
				
			||||||
 | 
					    ArmInstruction instruction(raw);
 | 
				
			||||||
 | 
					    Multiply* mul = nullptr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    REQUIRE((mul = std::get_if<Multiply>(&instruction.data)));
 | 
				
			||||||
 | 
					    REQUIRE(instruction.condition == Condition::EQ);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    REQUIRE(mul->rm == 0);
 | 
				
			||||||
 | 
					    REQUIRE(mul->rs == 15);
 | 
				
			||||||
 | 
					    REQUIRE(mul->rn == 14);
 | 
				
			||||||
 | 
					    REQUIRE(mul->rd == 10);
 | 
				
			||||||
 | 
					    REQUIRE(mul->acc == true);
 | 
				
			||||||
 | 
					    REQUIRE(mul->set == true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    REQUIRE(instruction.disassemble() == "MLAEQS R10,R0,R15,R14");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    mul->acc = false;
 | 
				
			||||||
 | 
					    mul->set = false;
 | 
				
			||||||
 | 
					    REQUIRE(instruction.disassemble() == "MULEQ R10,R0,R15");
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					TEST_CASE("Multiply Long", TAG) {
 | 
				
			||||||
 | 
					    uint32_t raw = 0b00010000100111100111011010010010;
 | 
				
			||||||
 | 
					    ArmInstruction instruction(raw);
 | 
				
			||||||
 | 
					    MultiplyLong* mull = nullptr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    REQUIRE((mull = std::get_if<MultiplyLong>(&instruction.data)));
 | 
				
			||||||
 | 
					    REQUIRE(instruction.condition == Condition::NE);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    REQUIRE(mull->rm == 2);
 | 
				
			||||||
 | 
					    REQUIRE(mull->rs == 6);
 | 
				
			||||||
 | 
					    REQUIRE(mull->rdlo == 7);
 | 
				
			||||||
 | 
					    REQUIRE(mull->rdhi == 14);
 | 
				
			||||||
 | 
					    REQUIRE(mull->acc == false);
 | 
				
			||||||
 | 
					    REQUIRE(mull->set == true);
 | 
				
			||||||
 | 
					    REQUIRE(mull->uns == false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    REQUIRE(instruction.disassemble() == "SMULLNES R7,R14,R2,R6");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    mull->acc = true;
 | 
				
			||||||
 | 
					    REQUIRE(instruction.disassemble() == "SMLALNES R7,R14,R2,R6");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    mull->uns = true;
 | 
				
			||||||
 | 
					    mull->set = false;
 | 
				
			||||||
 | 
					    REQUIRE(instruction.disassemble() == "UMLALNE R7,R14,R2,R6");
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					TEST_CASE("Single Data Swap", TAG) {
 | 
				
			||||||
 | 
					    uint32_t raw = 0b10100001000010010101000010010110;
 | 
				
			||||||
 | 
					    ArmInstruction instruction(raw);
 | 
				
			||||||
 | 
					    SingleDataSwap* swp = nullptr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    REQUIRE((swp = std::get_if<SingleDataSwap>(&instruction.data)));
 | 
				
			||||||
 | 
					    REQUIRE(instruction.condition == Condition::GE);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    REQUIRE(swp->rm == 6);
 | 
				
			||||||
 | 
					    REQUIRE(swp->rd == 5);
 | 
				
			||||||
 | 
					    REQUIRE(swp->rn == 9);
 | 
				
			||||||
 | 
					    REQUIRE(swp->byte == false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    REQUIRE(instruction.disassemble() == "SWPGE R5,R6,[R9]");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    swp->byte = true;
 | 
				
			||||||
 | 
					    REQUIRE(instruction.disassemble() == "SWPGEB R5,R6,[R9]");
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					TEST_CASE("Single Data Transfer", TAG) {
 | 
				
			||||||
 | 
					    uint32_t raw = 0b11100111101000101010111100000110;
 | 
				
			||||||
 | 
					    ArmInstruction instruction(raw);
 | 
				
			||||||
 | 
					    SingleDataTransfer* ldr = nullptr;
 | 
				
			||||||
 | 
					    Shift* shift            = nullptr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    REQUIRE((ldr = std::get_if<SingleDataTransfer>(&instruction.data)));
 | 
				
			||||||
 | 
					    REQUIRE(instruction.condition == Condition::AL);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    REQUIRE((shift = std::get_if<Shift>(&ldr->offset)));
 | 
				
			||||||
 | 
					    REQUIRE(shift->rm == 6);
 | 
				
			||||||
 | 
					    REQUIRE(shift->data.immediate == true);
 | 
				
			||||||
 | 
					    REQUIRE(shift->data.type == ShiftType::LSL);
 | 
				
			||||||
 | 
					    REQUIRE(shift->data.operand == 30);
 | 
				
			||||||
 | 
					    REQUIRE(ldr->rd == 10);
 | 
				
			||||||
 | 
					    REQUIRE(ldr->rn == 2);
 | 
				
			||||||
 | 
					    REQUIRE(ldr->load == false);
 | 
				
			||||||
 | 
					    REQUIRE(ldr->write == true);
 | 
				
			||||||
 | 
					    REQUIRE(ldr->byte == false);
 | 
				
			||||||
 | 
					    REQUIRE(ldr->up == true);
 | 
				
			||||||
 | 
					    REQUIRE(ldr->pre == true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ldr->load        = true;
 | 
				
			||||||
 | 
					    ldr->byte        = true;
 | 
				
			||||||
 | 
					    ldr->write       = false;
 | 
				
			||||||
 | 
					    shift->data.type = ShiftType::ROR;
 | 
				
			||||||
 | 
					    REQUIRE(instruction.disassemble() == "LDRB R10,[R2,+R6,ROR #30]");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ldr->up  = false;
 | 
				
			||||||
 | 
					    ldr->pre = false;
 | 
				
			||||||
 | 
					    REQUIRE(instruction.disassemble() == "LDRB R10,[R2],-R6,ROR #30");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ldr->offset = static_cast<uint16_t>(9023);
 | 
				
			||||||
 | 
					    REQUIRE(instruction.disassemble() == "LDRB R10,[R2],-#9023");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ldr->pre = true;
 | 
				
			||||||
 | 
					    REQUIRE(instruction.disassemble() == "LDRB R10,[R2,-#9023]");
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					TEST_CASE("Halfword Transfer", TAG) {
 | 
				
			||||||
 | 
					    uint32_t raw = 0b00110001101011110010000010110110;
 | 
				
			||||||
 | 
					    ArmInstruction instruction(raw);
 | 
				
			||||||
 | 
					    HalfwordTransfer* ldr = nullptr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    REQUIRE((ldr = std::get_if<HalfwordTransfer>(&instruction.data)));
 | 
				
			||||||
 | 
					    REQUIRE(instruction.condition == Condition::CC);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // offset is not immediate
 | 
				
			||||||
 | 
					    REQUIRE(ldr->imm == 0);
 | 
				
			||||||
 | 
					    // hence this offset is a register number (rm)
 | 
				
			||||||
 | 
					    REQUIRE(ldr->offset == 6);
 | 
				
			||||||
 | 
					    REQUIRE(ldr->half == true);
 | 
				
			||||||
 | 
					    REQUIRE(ldr->sign == false);
 | 
				
			||||||
 | 
					    REQUIRE(ldr->rd == 2);
 | 
				
			||||||
 | 
					    REQUIRE(ldr->rn == 15);
 | 
				
			||||||
 | 
					    REQUIRE(ldr->load == false);
 | 
				
			||||||
 | 
					    REQUIRE(ldr->write == true);
 | 
				
			||||||
 | 
					    REQUIRE(ldr->up == true);
 | 
				
			||||||
 | 
					    REQUIRE(ldr->pre == true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    REQUIRE(instruction.disassemble() == "STRCCH R2,[R15,+R6]!");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ldr->pre  = false;
 | 
				
			||||||
 | 
					    ldr->load = true;
 | 
				
			||||||
 | 
					    ldr->sign = true;
 | 
				
			||||||
 | 
					    ldr->up   = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    REQUIRE(instruction.disassemble() == "LDRCCSH R2,[R15],-R6");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ldr->half = false;
 | 
				
			||||||
 | 
					    REQUIRE(instruction.disassemble() == "LDRCCSB R2,[R15],-R6");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // not a register anymore
 | 
				
			||||||
 | 
					    ldr->load   = false;
 | 
				
			||||||
 | 
					    ldr->imm    = 1;
 | 
				
			||||||
 | 
					    ldr->offset = 90;
 | 
				
			||||||
 | 
					    REQUIRE(instruction.disassemble() == "STRCCSB R2,[R15],-#90");
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					TEST_CASE("Undefined", TAG) {
 | 
				
			||||||
 | 
					    // notice how this is the same as single data transfer except the shift is
 | 
				
			||||||
 | 
					    // now a register based shift
 | 
				
			||||||
 | 
					    uint32_t raw = 0b11100111101000101010111100010110;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    REQUIRE(ArmInstruction(raw).disassemble() == "UNDEFINED");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    raw = 0b11100110000000000000000000010000;
 | 
				
			||||||
 | 
					    REQUIRE(ArmInstruction(raw).disassemble() == "UNDEFINED");
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										3
									
								
								tests/cpu/meson.build
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								tests/cpu/meson.build
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,3 @@
 | 
				
			|||||||
 | 
					tests_sources += files(
 | 
				
			||||||
 | 
					  'instruction.cc'
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
							
								
								
									
										19
									
								
								tests/meson.build
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								tests/meson.build
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,19 @@
 | 
				
			|||||||
 | 
					tests_deps = [
 | 
				
			||||||
 | 
					  lib
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					tests_sources = files()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					subdir('cpu')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					catch2 = dependency('catch2-with-main', version: '>=3.4.0', static: true)
 | 
				
			||||||
 | 
					catch2_tests = executable(
 | 
				
			||||||
 | 
					  meson.project_name() + '_tests',
 | 
				
			||||||
 | 
					  tests_sources,
 | 
				
			||||||
 | 
					  dependencies: catch2,
 | 
				
			||||||
 | 
					  link_with: tests_deps,
 | 
				
			||||||
 | 
					  include_directories: inc,
 | 
				
			||||||
 | 
					  build_by_default: false
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					test('catch2 tests', catch2_tests)
 | 
				
			||||||
		Reference in New Issue
	
	Block a user