summaryrefslogtreecommitdiffhomepage
path: root/asm/arm/instruction.h
blob: 4262ee983904d2506be7b4cd5f311b48ff0f8fba (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
#pragma once

#include <memory>
#include <stdexcept>
#include <string>
#include <vector>

using namespace std::string_literals;

using code_sequence = std::vector<uint8_t>;

template<typename from_t>
code_sequence to_code_sequence(from_t v);

template<>
code_sequence to_code_sequence<uint16_t>(uint16_t v) {
 return code_sequence{static_cast<uint8_t>(v & 0xFF), static_cast<uint8_t>(v >> 8)};
}

template<>
code_sequence to_code_sequence<uint32_t>(uint32_t v) {
 return code_sequence{static_cast<uint8_t>(v & 0xFF), static_cast<uint8_t>(v >> 8), static_cast<uint8_t>(v >> 16),static_cast<uint8_t>(v >> 24) };
}

uint32_t high_bits(uint8_t number)
{
 return ~((static_cast<uint32_t>(1) << (32 - number)) - 1);
}

// Identify operator with leading bits
class Pattern
{
public:
 Pattern(uint32_t bits, uint32_t mask): _bits{bits}, _mask{mask} {}

 template<typename T>
 T encode()
 {
  return static_cast<T>(_bits);
 }


private:
 uint32_t _bits;
 uint32_t _mask;
};

class Operand
{
protected:
 Operand(uint8_t pos): _pos(pos){}
private:
 uint8_t _pos;
};

class Register: public Operand
{
public:
 Register(uint8_t size, uint8_t pos): Operand{pos} {}
 Register(uint32_t mask): Operand{0} { throw std::runtime_error("Unimplemented"); }
 Register(const std::string& name): Operand{0} { throw std::runtime_error("Unimplemented"); }
};

class SameRegister: public Operand
{
public:
 SameRegister(uint8_t index): Operand{index} {};
};

class Immediate: public Operand
{
public:
 Immediate(uint8_t size, uint8_t pos): Operand{pos}, _bits{size} {}
private:
 uint8_t _bits;
};

using Operands = std::vector<std::shared_ptr<Operand>>;

// Pattern [, Operand, ...]
class Instruction
{
public:
 Instruction(const std::string& mnemonic, uint8_t size, Pattern pattern, Operands operands): _mnemonic(mnemonic), _size(size), _pattern(pattern), _operands(operands) {}
 code_sequence encode(const std::vector<std::string>& arguments)
 {
  if (_size == 2) { // 16 bit thumb insn
   uint16_t result{ _pattern.encode<uint16_t>()};
   return to_code_sequence(result);
  } else if (_size == 4) { // 32 bit thumb insn
   uint32_t result{ _pattern.encode<uint32_t>()};
   return to_code_sequence(result);
  } else {
   throw std::runtime_error("Unsupported instruction size "s + std::to_string(_size));
  }
 }

private:
 std::string _mnemonic;
 uint8_t _size;
 Pattern _pattern;
 Operands _operands;
};

namespace {
 // factory functions
 std::shared_ptr<Operand> imm(uint8_t size, uint8_t pos){ return std::make_shared<Immediate>(size, pos); }
 std::shared_ptr<Operand> reg(uint8_t size, uint8_t pos) { return std::make_shared<Register>(size, pos); }
 std::shared_ptr<Operand> reg(uint32_t mask) { return std::make_shared<Register>(mask); }
 std::shared_ptr<Operand> reg(const std::string& name) { return std::make_shared<Register>(name); }
 std::shared_ptr<Operand> same_reg(uint8_t index) { return std::make_shared<SameRegister>(index); }

 std::shared_ptr<Operand> optional(std::shared_ptr<Operand> op) { return op; }

 // TODO: consistency checks:
 // * all bits defined
 // * unambiguous
 std::vector<Instruction> insns{
  {"adcs", 2, Pattern(0x4140, high_bits(10)), Operands{optional(same_reg(1)), reg(3, 0), reg(3, 3)}}, // T1
  {"adds", 2, Pattern(0x1C00, high_bits(8)), Operands{reg(3, 0), reg(3, 3), imm(3, 6)}}, // T1
  {"adds", 2, Pattern(0x3000, high_bits(5)), Operands{optional(same_reg(1)), reg(3, 8), imm(8, 0)}}, // T2
  {"adds", 2, Pattern(0x1800, high_bits(7)), Operands{reg(3, 0), reg(3, 3), reg(6, 6)}}, // T1
  {"add", 2, Pattern(0x4400, high_bits(8)), Operands{reg(0b10000111), reg(4, 3)}}, // T2
  {"add", 2, Pattern(0xA800, high_bits(5)), Operands{reg(3, 8), imm(8, 0)}}, // T1
  {"add", 2, Pattern(0xB000, high_bits(9)), Operands{optional(reg("sp")), reg("sp"), imm(7, 0)}}, // T2
  {"add", 2, Pattern(0x4468, 0xFF78), Operands{same_reg(2), reg("sp"), reg(0b10000111)}}, // T1
  {"add", 2, Pattern(0x4485, 0xFF87), Operands{reg("sp"), reg(4, 3)}}, // T2

  {"lsls", 2, Pattern(0x0000, high_bits(5)), Operands{reg(3, 0), reg(3, 3), imm(5, 6)}}
 };
};