about summary refs log tree commit diff stats
path: root/016index_addressing.cc
blob: 6e1f63e6c848688f4c64b009ed6111c7ae163759 (plain) (blame)
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
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
//: operating on memory at the address provided by some register plus optional scale and offset

:(code)
void test_add_r32_to_mem_at_r32_with_sib() {
  Reg[EBX].i = 0x10;
  Reg[EAX].i = 0x2000;
  run(
      "== code 0x1\n"
      // op     ModR/M  SIB   displacement  immediate
      "  01     1c      20                              \n"  // add EBX to *EAX
      // ModR/M in binary: 00 (indirect mode) 011 (src EBX) 100 (dest in SIB)
      // SIB in binary: 00 (scale 1) 100 (no index) 000 (base EAX)
      "== data 0x2000\n"
      "01 00 00 00\n"  // 0x00000001
  );
  CHECK_TRACE_CONTENTS(
      "run: add EBX to r/m32\n"
      "run: effective address is initially 0x00002000 (EAX)\n"
      "run: effective address is 0x00002000\n"
      "run: storing 0x00000011\n"
  );
}

:(before "End Mod 0 Special-cases(addr)")
case 4:  // exception: mod 0b00 rm 0b100 => incoming SIB (scale-index-base) byte
  addr = effective_address_from_sib(mod);
  break;
:(code)
uint32_t effective_address_from_sib(uint8_t mod) {
  const uint8_t sib = next();
  const uint8_t base = sib&0x7;
  uint32_t addr = 0;
  if (base != EBP || mod != 0) {
    addr = Reg[base].u;
    trace(Callstack_depth+1, "run") << "effective address is initially 0x" << HEXWORD << addr << " (" << rname(base) << ")" << end();
  }
  else {
    // base == EBP && mod == 0
    addr = next32();  // ignore base
    trace(Callstack_depth+1, "run") << "effective address is initially 0x" << HEXWORD << addr << " (disp32)" << end();
  }
  const uint8_t index = (sib>>3)&0x7;
  if (index == ESP) {
    // ignore index and scale
    trace(Callstack_depth+1, "run") << "effective address is 0x" << HEXWORD << addr << end();
  }
  else {
    const uint8_t scale = (1 << (sib>>6));
    addr += Reg[index].i*scale;  // treat index register as signed. Maybe base as well? But we'll always ensure it's non-negative.
    trace(Callstack_depth+1, "run") << "effective address is 0x" << HEXWORD << addr << " (after adding " << rname(index) << "*" << NUM(scale) << ")" << end();
  }
  return addr;
}

:(code)
void test_add_r32_to_mem_at_base_r32_index_r32() {
  Reg[EBX].i = 0x10;  // source
  Reg[EAX].i = 0x1ffe;  // dest base
  Reg[ECX].i = 0x2;  // dest index
  run(
      "== code 0x1\n"
      // op     ModR/M  SIB   displacement  immediate
      "  01     1c      08                              \n"  // add EBX to *(EAX+ECX)
      // ModR/M in binary: 00 (indirect mode) 011 (src EBX) 100 (dest in SIB)
      // SIB in binary: 00 (scale 1) 001 (index ECX) 000 (base EAX)
      "== data 0x2000\n"
      "01 00 00 00\n"  // 0x00000001
  );
  CHECK_TRACE_CONTENTS(
      "run: add EBX to r/m32\n"
      "run: effective address is initially 0x00001ffe (EAX)\n"
      "run: effective address is 0x00002000 (after adding ECX*1)\n"
      "run: storing 0x00000011\n"
  );
}

:(code)
void test_add_r32_to_mem_at_displacement_using_sib() {
  Reg[EBX].i = 0x10;  // source
  run(
      "== code 0x1\n"
      // op     ModR/M  SIB   displacement  immediate
      "  01     1c      25    00 20 00 00               \n"  // add EBX to *0x2000
      // ModR/M in binary: 00 (indirect mode) 011 (src EBX) 100 (dest in SIB)
      // SIB in binary: 00 (scale 1) 100 (no index) 101 (not EBP but disp32)
      "== data 0x2000\n"
      "01 00 00 00\n"  // 0x00000001
  );
  CHECK_TRACE_CONTENTS(
      "run: add EBX to r/m32\n"
      "run: effective address is initially 0x00002000 (disp32)\n"
      "run: effective address is 0x00002000\n"
      "run: storing 0x00000011\n"
  );
}

//:

:(code)
void test_add_r32_to_mem_at_base_r32_index_r32_plus_disp8() {
  Reg[EBX].i = 0x10;  // source
  Reg[EAX].i = 0x1ff9;  // dest base
  Reg[ECX].i = 0x5;  // dest index
  run(
      "== code 0x1\n"
      // op     ModR/M  SIB   displacement  immediate
      "  01     5c      08    02                        \n"  // add EBX to *(EAX+ECX+2)
      // ModR/M in binary: 01 (indirect+disp8 mode) 011 (src EBX) 100 (dest in SIB)
      // SIB in binary: 00 (scale 1) 001 (index ECX) 000 (base EAX)
      "== data 0x2000\n"
      "01 00 00 00\n"  // 0x00000001
  );
  CHECK_TRACE_CONTENTS(
      "run: add EBX to r/m32\n"
      "run: effective address is initially 0x00001ff9 (EAX)\n"
      "run: effective address is 0x00001ffe (after adding ECX*1)\n"
      "run: effective address is 0x00002000 (after adding disp8)\n"
      "run: storing 0x00000011\n"
  );
}

:(before "End Mod 1 Special-cases(addr)")
case 4:  // exception: mod 0b01 rm 0b100 => incoming SIB (scale-index-base) byte
  addr = effective_address_from_sib(mod);
  break;

//:

:(code)
void test_add_r32_to_mem_at_base_r32_index_r32_plus_disp32() {
  Reg[EBX].i = 0x10;  // source
  Reg[EAX].i = 0x1ff9;  // dest base
  Reg[ECX].i = 0x5;  // dest index
  run(
      "== code 0x1\n"
      // op     ModR/M  SIB   displacement  immediate
      "  01     9c      08    02 00 00 00               \n"  // add EBX to *(EAX+ECX+2)
      // ModR/M in binary: 10 (indirect+disp32 mode) 011 (src EBX) 100 (dest in SIB)
      // SIB in binary: 00 (scale 1) 001 (index ECX) 000 (base EAX)
      "== data 0x2000\n"
      "01 00 00 00\n"  // 0x00000001
  );
  CHECK_TRACE_CONTENTS(
      "run: add EBX to r/m32\n"
      "run: effective address is initially 0x00001ff9 (EAX)\n"
      "run: effective address is 0x00001ffe (after adding ECX*1)\n"
      "run: effective address is 0x00002000 (after adding disp32)\n"
      "run: storing 0x00000011\n"
  );
}

:(before "End Mod 2 Special-cases(addr)")
case 4:  // exception: mod 0b10 rm 0b100 => incoming SIB (scale-index-base) byte
  addr = effective_address_from_sib(mod);
  break;
1f20e19e291f442ed9f576'>2cb36cd0 ^
7284d503 ^
64cf0a59 ^
d41955c1 ^

69e14325 ^
df8bb4c3 ^
f6d47435 ^
d41955c1 ^

31401373 ^
795f5244 ^
5eb49929 ^

317c0a34 ^
ec926027 ^

ac0e9db5 ^
2e8c5d39 ^
c442a5ad ^


31401373 ^
d8c6265d ^
9fdda88b ^
5f98a10c ^
8eff7919 ^
f6d47435 ^
f64f1ca5 ^










2a81a547 ^
f64f1ca5 ^

2a81a547 ^




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
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228

                                                                      
                          
          

   
       
                   
 
                             
 
                                 
          
   


                 
 
       

                 
 

           

                                          
                  


                                          
 




                                                                             
                                
                         
                    


                                                        
                       
                               

                           


                          
  
                              



                             
                       
                            
                         
                                           

       
                                    


                                                                                                                   
                            
                            
 


                                 

                                       













                                                                                                   
 












































                                                                                







                                                                                                                                        

                                                                  
                                            
                                                                                                                  

        
                                                                        
          

                                                                                                       


                                                                                                        


                                                                                    


                                             
                                           
   


                                                                                                                

                          
 

                                          
          

     
                                          

                                                           
                     
          
              
 
                                                   
 
                                                                        
 

                                              
                       
 
 

                                                                
                         
                                                         

 
                                  

                                                                           
                                                                
                                  


                                                                                                                                                               
                                     
                                                                        
                              
                                       
                         
 










                                                          
                               

        




                                                                                                                                   
//: So far the recipes we define can't run each other. Let's fix that.

:(scenario calling_recipe)
def main [
  f
]
def f [
  3:num <- add 2, 2
]
+mem: storing 4 in location 3

:(scenario return_on_fallthrough)
def main [
  f
  1:num <- copy 0
  2:num <- copy 0
  3:num <- copy 0
]
def f [
  4:num <- copy 0
  5:num <- copy 0
]
+run: f
# running f
+run: {4: "number"} <- copy {0: "literal"}
+run: {5: "number"} <- copy {0: "literal"}
# back out to main
+run: {1: "number"} <- copy {0: "literal"}
+run: {2: "number"} <- copy {0: "literal"}
+run: {3: "number"} <- copy {0: "literal"}

:(before "struct routine {")
// Everytime a recipe runs another, we interrupt it and start running the new
// recipe. When that finishes, we continue this one where we left off.
// This requires maintaining a 'stack' of interrupted recipes or 'calls'.
struct call {
  recipe_ordinal running_recipe;
  int running_step_index;
  // End call Fields
  call(recipe_ordinal r) { clear(r, 0); }
  call(recipe_ordinal r, int index) { clear(r, index); }
  void clear(recipe_ordinal r, int index) {
    running_recipe = r;
    running_step_index = index;
    // End call Constructor
  }
  ~call() {
    // End call Destructor
  }
};
typedef list<call> call_stack;

:(replace{} "struct routine")
struct routine {
  call_stack calls;
  // End routine Fields
  routine(recipe_ordinal r);
  bool completed() const;
  const vector<instruction>& steps() const;
};
:(code)
routine::routine(recipe_ordinal r) {
  ++Callstack_depth;
  trace(Callstack_depth+1, "trace") << "new routine; incrementing callstack depth to " << Callstack_depth << end();
  assert(Callstack_depth < Max_depth);
  calls.push_front(call(r));
  // End routine Constructor
}

//:: now update routine's helpers

//: macro versions for a slight speedup

:(delete{} "int& current_step_index()")
:(delete{} "recipe_ordinal currently_running_recipe()")
:(delete{} "const string& current_recipe_name()")
:(delete{} "const recipe& current_recipe()")
:(delete{} "const instruction& current_instruction()")

:(before "End Includes")
#define current_call() Current_routine->calls.front()
#define current_step_index() current_call().running_step_index
#define currently_running_recipe() current_call().running_recipe
#define current_recipe() get(Recipe, currently_running_recipe())
#define current_recipe_name() current_recipe().name
#define to_instruction(call) get(Recipe, (call).running_recipe).steps.at((call).running_step_index)
#define current_instruction() to_instruction(current_call())

//: function versions for debugging

:(code)
//? :(before "End Globals")
//? bool Foo2 = false;
//? :(code)
//? call& current_call() {
//?   if (Foo2) cerr << __FUNCTION__ << '\n';
//?   return Current_routine->calls.front();
//? }
//? :(replace{} "int& current_step_index()")
//? int& current_step_index() {
//?   assert(!Current_routine->calls.empty());
//?   if (Foo2) cerr << __FUNCTION__ << '\n';
//?   return current_call().running_step_index;
//? }
//? :(replace{} "recipe_ordinal currently_running_recipe()")
//? recipe_ordinal currently_running_recipe() {
//?   assert(!Current_routine->calls.empty());
//?   if (Foo2) cerr << __FUNCTION__ << '\n';
//?   return current_call().running_recipe;
//? }
//? :(replace{} "const string& current_recipe_name()")
//? const string& current_recipe_name() {
//?   assert(!Current_routine->calls.empty());
//?   if (Foo2) cerr << __FUNCTION__ << '\n';
//?   return get(Recipe, current_call().running_recipe).name;
//? }
//? :(replace{} "const recipe& current_recipe()")
//? const recipe& current_recipe() {
//?   assert(!Current_routine->calls.empty());
//?   if (Foo2) cerr << __FUNCTION__ << '\n';
//?   return get(Recipe, current_call().running_recipe);
//? }
//? :(replace{} "const instruction& current_instruction()")
//? const instruction& current_instruction() {
//?   assert(!Current_routine->calls.empty());
//?   if (Foo2) cerr << __FUNCTION__ << '\n';
//?   return to_instruction(current_call());
//? }
//? :(code)
//? const instruction& to_instruction(const call& call) {
//?   return get(Recipe, call.running_recipe).steps.at(call.running_step_index);
//? }

:(code)
void dump_callstack() {
  if (!Current_routine) return;
  if (Current_routine->calls.size() <= 1) return;
  for (call_stack::const_iterator p = ++Current_routine->calls.begin();  p != Current_routine->calls.end();  ++p)
    raise << "  called from " << get(Recipe, p->running_recipe).name << ": " << to_original_string(to_instruction(*p)) << '\n' << end();
}

:(after "Defined Recipe Checks")
// not a primitive; check that it's present in the book of recipes
if (!contains_key(Recipe, inst.operation)) {
  raise << maybe(get(Recipe, r).name) << "undefined operation in '" << to_original_string(inst) << "'\n" << end();
  break;
}
:(replace{} "default:" following "End Primitive Recipe Implementations")
default: {
  if (contains_key(Recipe, current_instruction().operation)) {  // error already raised in Checks above
    // not a primitive; look up the book of recipes
    ++Callstack_depth;
    trace(Callstack_depth+1, "trace") << "incrementing callstack depth to " << Callstack_depth << end();
    assert(Callstack_depth < Max_depth);
    const call& caller_frame = current_call();
    Current_routine->calls.push_front(call(to_instruction(caller_frame).operation));
    finish_call_housekeeping(to_instruction(caller_frame), ingredients);
    // not done with caller
    write_products = false;
    fall_through_to_next_instruction = false;
    // End Non-primitive Call(caller_frame)
  }
}
:(code)
void finish_call_housekeeping(const instruction& call_instruction, const vector<vector<double> >& ingredients) {
  // End Call Housekeeping
}

:(scenario calling_undefined_recipe_fails)
% Hide_errors = true;
def main [
  foo
]
+error: main: undefined operation in 'foo'

:(scenario calling_undefined_recipe_handles_missing_result)
% Hide_errors = true;
def main [
  x:num <- foo
]
+error: main: undefined operation in 'x:num <- foo'

//:: finally, we need to fix the termination conditions for the run loop

:(replace{} "bool routine::completed() const")
bool routine::completed() const {
  return calls.empty();
}

:(replace{} "const vector<instruction>& routine::steps() const")
const vector<instruction>& routine::steps() const {
  assert(!calls.empty());
  return get(Recipe, calls.front().running_recipe).steps;
}

:(after "Running One Instruction")
// when we reach the end of one call, we may reach the end of the one below
// it, and the one below that, and so on
while (current_step_index() >= SIZE(Current_routine->steps())) {
  // Falling Through End Of Recipe
  trace(Callstack_depth+1, "trace") << "fall-through: exiting " << current_recipe_name() << "; decrementing callstack depth from " << Callstack_depth << end();
  --Callstack_depth;
  assert(Callstack_depth >= 0);
  Current_routine->calls.pop_front();
  if (Current_routine->calls.empty()) goto stop_running_current_routine;
  // Complete Call Fallthrough
  // todo: fail if no products returned
  ++current_step_index();
}

:(before "End Primitive Recipe Declarations")
_DUMP_CALL_STACK,
:(before "End Primitive Recipe Numbers")
put(Recipe_ordinal, "$dump-call-stack", _DUMP_CALL_STACK);
:(before "End Primitive Recipe Checks")
case _DUMP_CALL_STACK: {
  break;
}
:(before "End Primitive Recipe Implementations")
case _DUMP_CALL_STACK: {
  dump(Current_routine->calls);
  break;
}
:(code)
void dump(const call_stack& calls) {
  for (call_stack::const_reverse_iterator p = calls.rbegin(); p != calls.rend(); ++p)
    cerr << get(Recipe, p->running_recipe).name << ":" << p->running_step_index << " -- " << to_string(to_instruction(*p)) << '\n';
}