summaryrefslogtreecommitdiffhomepage
path: root/asm/segment.cpp
blob: d3050bb7a7c95f755da208a804ae52949584efda (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
#include "segment.h"

#include "operators.h"

#include <boost/endian/conversion.hpp>

#include <iostream>
#include <exception>

using namespace std::string_literals;

size_t Segment::getAddressOfLabel(const std::string& label)
{
 size_t address{0};
 auto i{begin()};
 while (i != end()) {
  Chunk& chunk{**i};
  address += chunk.size();
  if (typeid(chunk) == typeid(Label)) {
   if (dynamic_cast<Label&>(chunk).name() == label) {
    return address;
   }
  }
  i++;
 }

 throw std::runtime_error("Error: Label |"s + label + "| not found."s);
}

size_t Segment::getAddressOfIndex(size_t index)
{
 if (index >= size())
  throw std::runtime_error("Error: Index |"s + std::to_string(index) + "| not found."s);

 size_t address{0};
 for (size_t i = 0; i < index; i++) {
  address += (*this)[i]->size();
 }
 return address;
}

std::vector<uint8_t> Segment::getCode()
{
 std::vector<uint8_t> result;

 for (const auto& chunk: *this)
  result = result + chunk->getCode();

 return result;
}

void Segment::insertAddresses()
{
 // insert relative addresses
 for (size_t i = 0; i < this->size(); i++) {
  try {
   AddressFeature& af { dynamic_cast<AddressFeature&>(*((*this)[i]))};

   if (af.relativeAddressing == false)
    throw std::runtime_error("Error: Absolute Addressing not supported.");

   int64_t target_address = getAddressOfLabel(af.label); // throws if label not found
   int64_t start_address = getAddressOfIndex(i); // throws if out of range
   int64_t diff = target_address - start_address;
   uint64_t udiff = static_cast<uint64_t>(diff);
   if (af.addr_size == 1) {
    if (diff < -128 || diff > 127)
     throw std::runtime_error("Error: Address too far."s);
    af.machine_code[af.addr_offs] = static_cast<uint8_t>(udiff);
   } else if (af.addr_size == 4) {
    *(reinterpret_cast<uint32_t*>(af.machine_code.data() + af.addr_offs)) = boost::endian::native_to_little(static_cast<uint32_t>(udiff));
   } else
    throw std::runtime_error("Error: unexpected addr_size: "s + std::to_string(af.addr_size));
  } catch (const std::bad_cast& ex) {
   // ignore, expected for non-addressing chunks
  }
 }
}

void Segment::optimize()
{
 bool changed{false};

 for (size_t i = 0; i < this->size(); i++) {
  try {
   AddressFeature& af { dynamic_cast<AddressFeature&>(*((*this)[i]))};

   if (af.relativeAddressing == false)
    throw std::runtime_error("Error: Absolute Addressing not supported.");

   if (!af.alternative_code.empty()) {
    int64_t target_address = getAddressOfLabel(af.label); // throws if label not found
    int64_t start_address = getAddressOfIndex(i); // throws if out of range
    int64_t diff = target_address - start_address;
    if (af.addr_size == 4 && af.alternative_size == 1 && diff >= -128 && diff <= 127) {
     af.machine_code = af.alternative_code;
     af.addr_size = af.alternative_size;
     af.addr_offs = af.alternative_offs;
     af.alternative_code.clear();
     af.alternative_size = 0;
     af.alternative_offs = 0;
     changed = true;
    }
   }
  } catch (const std::bad_cast& ex) {
   // ignore, expected for non-addressing chunks
  }
 }

 if (changed)
  insertAddresses(); // renumber
}