about summary refs log tree commit diff stats
path: root/subx/026labels.cc
diff options
context:
space:
mode:
Diffstat (limited to 'subx/026labels.cc')
-rw-r--r--subx/026labels.cc77
1 files changed, 71 insertions, 6 deletions
diff --git a/subx/026labels.cc b/subx/026labels.cc
index cc11eb0b..db6091d6 100644
--- a/subx/026labels.cc
+++ b/subx/026labels.cc
@@ -19,7 +19,7 @@ void rewrite_labels(program& p) {
   trace(99, "transform") << "-- rewrite labels" << end();
   if (p.segments.empty()) return;
   segment& code = p.segments.at(0);
-  map<string, uint32_t> address;
+  map<string, int32_t> address;  // values are unsigned, but we're going to do subtractions on them so they need to fit in 31 bits
   compute_addresses_for_labels(code, address);
   if (trace_contains_errors()) return;
   drop_labels(code);
@@ -27,7 +27,7 @@ void rewrite_labels(program& p) {
   replace_labels_with_addresses(code, address);
 }
 
-void compute_addresses_for_labels(const segment& code, map<string, uint32_t> address) {
+void compute_addresses_for_labels(const segment& code, map<string, int32_t>& address) {
   int current_byte = 0;
   for (int i = 0;  i < SIZE(code.lines);  ++i) {
     const line& inst = code.lines.at(i);
@@ -64,7 +64,8 @@ void compute_addresses_for_labels(const segment& code, map<string, uint32_t> add
 void drop_labels(segment& code) {
   for (int i = 0;  i < SIZE(code.lines);  ++i) {
     line& inst = code.lines.at(i);
-    remove_if(inst.words.begin(), inst.words.end(), is_label);
+    vector<word>::iterator new_end = remove_if(inst.words.begin(), inst.words.end(), is_label);
+    inst.words.erase(new_end, inst.words.end());
   }
 }
 
@@ -72,7 +73,61 @@ bool is_label(const word& w) {
   return *w.data.rbegin() == ':';
 }
 
-void replace_labels_with_addresses(const segment& code, map<string, uint32_t> address) {
+void replace_labels_with_addresses(segment& code, const map<string, int32_t>& address) {
+  int32_t byte_next_instruction_starts_at = 0;
+  for (int i = 0;  i < SIZE(code.lines);  ++i) {
+    line& inst = code.lines.at(i);
+    byte_next_instruction_starts_at += num_bytes(inst);
+    line new_inst;
+    for (int j = 0;  j < SIZE(inst.words);  ++j) {
+      const word& curr = inst.words.at(j);
+      if (contains_key(address, curr.data)) {
+        int32_t offset = static_cast<int32_t>(get(address, curr.data)) - byte_next_instruction_starts_at;
+        if (has_metadata(curr, "disp8") || has_metadata(curr, "imm8")) {
+          if (offset > 0xff || offset < -0x7f)
+            raise << "'" << to_string(inst) << "': label too far away for distance " << std::hex << offset << " to fit in 8 bits\n" << end();
+          else
+            emit_hex_bytes(new_inst, offset, 1);
+        }
+        else if (has_metadata(curr, "disp16")) {
+          if (offset > 0xffff || offset < -0x7fff)
+            raise << "'" << to_string(inst) << "': label too far away for distance " << std::hex << offset << " to fit in 16 bits\n" << end();
+          else
+            emit_hex_bytes(new_inst, offset, 2);
+        }
+        else if (has_metadata(curr, "disp32") || has_metadata(curr, "imm32")) {
+          emit_hex_bytes(new_inst, offset, 4);
+        }
+      }
+      else {
+        new_inst.words.push_back(curr);
+      }
+    }
+    inst.words.swap(new_inst.words);
+    trace(99, "transform") << "instruction after transform: '" << data_to_string(inst) << "'" << end();
+  }
+}
+
+// Assumes all bitfields are packed.
+uint32_t num_bytes(const line& inst) {
+  uint32_t sum = 0;
+  for (int i = 0;  i < SIZE(inst.words);  ++i) {
+    const word& curr = inst.words.at(i);
+    if (has_metadata(curr, "disp32") || has_metadata(curr, "imm32"))  // only multi-byte operands
+      sum += 4;
+    else
+      sum++;
+  }
+  return sum;
+}
+
+string data_to_string(const line& inst) {
+  ostringstream out;
+  for (int i = 0;  i < SIZE(inst.words);  ++i) {
+    if (i > 0) out << ' ';
+    out << inst.words.at(i).data;
+  }
+  return out.str();
 }
 
 //: Label definitions must be the first word on a line. No jumping inside
@@ -86,11 +141,21 @@ void replace_labels_with_addresses(const segment& code, map<string, uint32_t> ad
           # instruction                     effective address                                           operand     displacement    immediate
           # op          subop               mod             rm32          base      index     scale     r32
           # 1-3 bytes   3 bits              2 bits          3 bits        3 bits    3 bits    2 bits    2 bits      0/1/2/4 bytes   0/1/2/4 bytes
+# address 1
 loop:
 loop2:
+# address 1 (labels take up no space)
             05                                                                                                                      0x0d0c0b0a/imm32  # add to EAX
+# address 6
+            eb                                                                                                      loop2/disp8
+# address 8
+            eb                                                                                                      loop3/disp8
+# address 10
 loop3:
-            f
 +transform: label 'loop' is at address 1
 +transform: label 'loop2' is at address 1
-+transform: label 'loop3' is at address 6
++transform: label 'loop3' is at address 10
+# first jump is to -7
++transform: instruction after transform: 'eb f9'
+# second jump is to 0 (fall through)
++transform: instruction after transform: 'eb 00'