voodoo 1.0.2 → 1.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/bin/voodooc +85 -49
- data/lib/voodoo.rb +11 -6
- data/lib/voodoo/code_generator.rb +6 -6
- data/lib/voodoo/compiler.rb +2 -2
- data/lib/voodoo/config.rb +3 -3
- data/lib/voodoo/generators/amd64_nasm_generator.rb +196 -118
- data/lib/voodoo/generators/arm_gas_generator.rb +367 -334
- data/lib/voodoo/generators/common_code_generator.rb +467 -69
- data/lib/voodoo/generators/dummy_generator.rb +10 -0
- data/lib/voodoo/generators/i386_nasm_generator.rb +93 -60
- data/lib/voodoo/generators/mips_gas_generator.rb +275 -265
- data/lib/voodoo/generators/nasm_generator.rb +253 -251
- data/lib/voodoo/parser.rb +46 -22
- data/lib/voodoo/validator.rb +118 -60
- metadata +14 -14
- data/lib/voodoo/generators/arm_gas_generator.rb~ +0 -957
@@ -12,15 +12,16 @@ module Voodoo
|
|
12
12
|
#
|
13
13
|
# To use the functionality from this class, a subclass must define the
|
14
14
|
# following methods and constants:
|
15
|
-
# - #
|
16
|
-
# - #load_arg
|
17
|
-
# - #load_local
|
15
|
+
# - #begin_function
|
18
16
|
# - @CODE_ALIGNMENT
|
19
17
|
# - @DATA_ALIGNMENT
|
20
18
|
# - @FUNCTION_ALIGNMENT
|
21
|
-
# - @
|
19
|
+
# - @STACK_ALIGNMENT
|
20
|
+
# - @STACK_ALIGNMENT_BITS
|
21
|
+
# - @TEMPORARIES
|
22
22
|
# - @WORD_NAME
|
23
23
|
# - @WORDSIZE
|
24
|
+
# - @WORDSIZE_BITS
|
24
25
|
# - @AX, @BX, @CX, @DX, @BP, and @SP
|
25
26
|
class NasmGenerator < CommonCodeGenerator
|
26
27
|
def initialize params = {}
|
@@ -35,26 +36,44 @@ module Voodoo
|
|
35
36
|
|
36
37
|
# Export symbols from the current section
|
37
38
|
def export *symbols
|
38
|
-
|
39
|
+
case real_section_name(section)
|
40
|
+
when ".text"
|
41
|
+
symbols.each { |sym| emit "global #{sym}:function\n" }
|
42
|
+
else
|
43
|
+
@relocated_symbols.merge symbols
|
44
|
+
symbols.each { |sym| emit "global #{sym}:data #{sym}.end-#{sym}\n" }
|
45
|
+
end
|
39
46
|
end
|
40
47
|
|
41
48
|
# Continue execution at the given address
|
42
49
|
def goto value
|
43
|
-
|
44
|
-
|
45
|
-
|
50
|
+
with_temporary do |temporary|
|
51
|
+
value_ref = load_value value, temporary
|
52
|
+
emit "jmp #{value_ref}\n"
|
53
|
+
end
|
46
54
|
end
|
47
55
|
|
48
56
|
# Import labels into the current section
|
49
57
|
def import *symbols
|
58
|
+
if real_section_name(section) != ".text"
|
59
|
+
@relocated_symbols.merge symbols
|
60
|
+
end
|
50
61
|
emit "extern #{symbols.join ', '}\n"
|
51
62
|
end
|
52
63
|
|
53
|
-
#
|
54
|
-
def
|
64
|
+
# Emits a label.
|
65
|
+
def emit_label name
|
55
66
|
emit "#{name}:\n"
|
56
67
|
end
|
57
68
|
|
69
|
+
def emit_label_size name
|
70
|
+
emit ".end:\n"
|
71
|
+
end
|
72
|
+
|
73
|
+
def emit_label_type name, type
|
74
|
+
# nothing to do
|
75
|
+
end
|
76
|
+
|
58
77
|
#
|
59
78
|
# == Data definition
|
60
79
|
#
|
@@ -105,45 +124,21 @@ module Voodoo
|
|
105
124
|
# == Alignment
|
106
125
|
#
|
107
126
|
|
108
|
-
def
|
109
|
-
|
110
|
-
# Get default alignment
|
111
|
-
case @section
|
112
|
-
when :code
|
113
|
-
alignment = @CODE_ALIGNMENT
|
114
|
-
when :data
|
115
|
-
alignment = @DATA_ALIGNMENT
|
116
|
-
when :function
|
117
|
-
alignment = @FUNCTION_ALIGNMENT
|
118
|
-
else
|
119
|
-
# Use data alignment as default
|
120
|
-
alignment = @DATA_ALIGNMENT
|
121
|
-
end
|
122
|
-
end
|
123
|
-
emit "align #{alignment}\n" unless alignment == 0
|
127
|
+
def emit_align alignment
|
128
|
+
emit "align #{alignment}\n"
|
124
129
|
end
|
125
130
|
|
126
131
|
#
|
127
132
|
# == Functions
|
128
133
|
#
|
129
134
|
|
130
|
-
#
|
131
|
-
def begin_function *formals
|
132
|
-
emit "; function #{formals.join ' '}\n"
|
133
|
-
environment = Environment.new @environment
|
134
|
-
environment.add_args formals
|
135
|
-
@environment = environment
|
136
|
-
emit_function_prologue formals
|
137
|
-
end
|
138
|
-
|
139
|
-
# Emit function epilogue.
|
135
|
+
# Emits function epilogue.
|
140
136
|
def emit_function_epilogue formals = []
|
141
137
|
emit "leave\n"
|
142
138
|
end
|
143
139
|
|
144
|
-
#
|
140
|
+
# Ends a function body
|
145
141
|
def end_function
|
146
|
-
emit "; end function\n\n"
|
147
142
|
if @environment == @top_level
|
148
143
|
raise "Cannot end function when not in a function"
|
149
144
|
else
|
@@ -151,12 +146,11 @@ module Voodoo
|
|
151
146
|
end
|
152
147
|
end
|
153
148
|
|
154
|
-
#
|
149
|
+
# Returns from a function.
|
155
150
|
#
|
156
151
|
# _words_ may contain an expression to be evaluated. The result
|
157
152
|
# of the evaluation is returned from the function.
|
158
153
|
def ret *words
|
159
|
-
emit "; return #{words.join ' '}\n"
|
160
154
|
eval_expr(words) unless words.empty?
|
161
155
|
emit_function_epilogue
|
162
156
|
emit "ret\n"
|
@@ -168,7 +162,6 @@ module Voodoo
|
|
168
162
|
|
169
163
|
# Begins a new block.
|
170
164
|
def begin_block *code
|
171
|
-
emit "; begin block\n"
|
172
165
|
# If entering a block at top level,
|
173
166
|
# Save @BP, then set @BP to @SP
|
174
167
|
if @environment == @top_level
|
@@ -181,79 +174,116 @@ module Voodoo
|
|
181
174
|
|
182
175
|
# Ends the current block.
|
183
176
|
def end_block
|
184
|
-
emit "; end block\n"
|
185
|
-
|
186
|
-
# De-allocate block's variables
|
187
|
-
nvars = @environment.locals - @environment.parent.locals
|
188
|
-
emit "add #{@SP}, #{nvars * @WORDSIZE}\n" unless nvars == 0
|
189
|
-
|
190
177
|
# Restore old value of @environment
|
191
178
|
@environment = @environment.parent
|
192
179
|
|
193
180
|
# If returning to top level, restore old @BP
|
194
|
-
emit "
|
181
|
+
emit "leave\n" if @environment == @top_level
|
195
182
|
end
|
196
183
|
|
197
184
|
#
|
198
|
-
# ==
|
185
|
+
# == Memory Allocation
|
199
186
|
#
|
200
187
|
|
201
|
-
#
|
202
|
-
|
203
|
-
|
204
|
-
|
188
|
+
# Allocates n bytes on the stack and stores a pointer to the allocated
|
189
|
+
# memory in the specified register. The number of bytes is rounded up
|
190
|
+
# to the nearest multiple of @STACK_ALIGNMENT.
|
191
|
+
def auto_bytes n, register = @RETURN_REG
|
192
|
+
if n.kind_of? Integer
|
193
|
+
auto_bytes_immediate n, register
|
194
|
+
else
|
195
|
+
load_value_into_register n, register
|
196
|
+
auto_bytes_register register, register
|
197
|
+
end
|
205
198
|
end
|
206
199
|
|
207
|
-
#
|
208
|
-
#
|
209
|
-
|
200
|
+
# Implements auto_bytes where the number of bytes to allocate is given
|
201
|
+
# as an immediate value.
|
202
|
+
def auto_bytes_immediate nbytes, register
|
203
|
+
nbytes = ((nbytes + @STACK_ALIGNMENT - 1) >> @STACK_ALIGNMENT_BITS) <<
|
204
|
+
@STACK_ALIGNMENT_BITS
|
205
|
+
emit "sub #{@SP}, #{nbytes}\n"
|
206
|
+
emit "mov #{register}, #{@SP}\n" if register != @SP
|
207
|
+
end
|
210
208
|
|
211
|
-
#
|
212
|
-
|
213
|
-
|
209
|
+
# Implements auto_bytes where the number of bytes is supplied in a
|
210
|
+
# register.
|
211
|
+
def auto_bytes_register nbytes, register = @RETURN_REG
|
212
|
+
emit "add #{nbytes}, #{@STACK_ALIGNMENT - 1}\n"
|
213
|
+
emit "shr #{nbytes}, #{@STACK_ALIGNMENT_BITS}\n"
|
214
|
+
emit "shl #{nbytes}, #{@STACK_ALIGNMENT_BITS}\n"
|
215
|
+
emit "sub #{@SP}, #{nbytes}\n"
|
216
|
+
emit "mov #{register}, #{@SP}\n" if register != @SP
|
214
217
|
end
|
215
218
|
|
216
|
-
#
|
217
|
-
|
218
|
-
|
219
|
-
|
219
|
+
# Allocates n words on the stack and stores a pointer to the allocated
|
220
|
+
# memory in the specified register.
|
221
|
+
def auto_words n, register = @RETURN_REG
|
222
|
+
if n.kind_of? Integer
|
223
|
+
auto_bytes_immediate n * @WORDSIZE, register
|
224
|
+
else
|
225
|
+
load_value_into_register n, register
|
226
|
+
if @STACK_ALIGNMENT_BITS > @WORDSIZE_BITS
|
227
|
+
emit "add #{register}, " +
|
228
|
+
"#{(1 << @STACK_ALIGNMENT_BITS >> @WORDSIZE_BITS) - 1}\n"
|
229
|
+
emit "shr #{register}, #{@STACK_ALIGNMENT_BITS - @WORDSIZE_BITS}\n"
|
230
|
+
emit "shl #{register}, #{STACK_ALIGNMENT_BITS}\n"
|
231
|
+
else
|
232
|
+
emit "shl #{register}, #{@WORDSIZE_BITS}\n"
|
233
|
+
end
|
234
|
+
emit "sub #{@SP}, #{register}\n"
|
235
|
+
emit "mov #{register}, #{@SP}\n" if register != @SP
|
236
|
+
end
|
220
237
|
end
|
221
238
|
|
222
|
-
#
|
223
|
-
|
224
|
-
|
239
|
+
#
|
240
|
+
# == Variables
|
241
|
+
#
|
242
|
+
|
243
|
+
# Introduces a new local variable.
|
244
|
+
def let symbol, *words
|
245
|
+
loc = local_offset_or_register @environment.locals
|
246
|
+
@environment.add_local symbol, loc
|
247
|
+
set symbol, *words
|
225
248
|
end
|
226
249
|
|
227
|
-
#
|
228
|
-
|
229
|
-
|
250
|
+
#
|
251
|
+
# == Conditionals
|
252
|
+
#
|
253
|
+
|
254
|
+
# End a conditional body
|
255
|
+
def end_if
|
256
|
+
label = @if_labels.pop
|
257
|
+
emit "#{label}:\n"
|
230
258
|
end
|
231
259
|
|
260
|
+
#
|
261
|
+
# == Value Classification
|
262
|
+
#
|
263
|
+
|
232
264
|
# Tests if an operand is an immediate operand
|
233
265
|
def immediate_operand? operand
|
234
|
-
integer?(operand)
|
266
|
+
integer?(operand)
|
235
267
|
end
|
236
268
|
|
237
269
|
# Tests if an operand is a memory operand
|
238
270
|
def memory_operand? operand
|
239
|
-
operand[0] == ?[
|
240
|
-
end
|
241
|
-
|
242
|
-
# Test if a value is a symbol
|
243
|
-
def symbol? value
|
244
|
-
value.kind_of? Symbol
|
245
|
-
end
|
246
|
-
|
247
|
-
# Test if op is a symmetric operation (i.e. it will yield the
|
248
|
-
# same result if the order of its source operands is changed).
|
249
|
-
def symmetric_operation? op
|
250
|
-
[:add, :and, :mul, :or, :xor].member? op
|
271
|
+
operand.kind_of?(String) && operand[0] == ?[
|
251
272
|
end
|
252
273
|
|
253
274
|
#
|
254
275
|
# == Loading Values
|
255
276
|
#
|
256
277
|
|
278
|
+
# Loads a word into a register.
|
279
|
+
def emit_load_word register, base, offset = 0
|
280
|
+
if offset == 0
|
281
|
+
emit "mov #{register}, [#{base}]\n"
|
282
|
+
else
|
283
|
+
emit "mov #{register}, [#{base} + #{offset} * #{@WORDSIZE}]\n"
|
284
|
+
end
|
285
|
+
end
|
286
|
+
|
257
287
|
# Create a value reference to an address.
|
258
288
|
# Invoking this code may clobber @BX and/or @CX
|
259
289
|
def load_address base, offset, scale
|
@@ -302,8 +332,9 @@ module Voodoo
|
|
302
332
|
|
303
333
|
# Load the value at the given address.
|
304
334
|
# Invoking this code may clobber @BX.
|
305
|
-
def load_at address, reg
|
306
|
-
if integer?(address) || global?(address)
|
335
|
+
def load_at address, reg
|
336
|
+
if integer?(address) || (global?(address) &&
|
337
|
+
!@relocated_symbols.include?(address))
|
307
338
|
"[#{address}]"
|
308
339
|
else
|
309
340
|
load_value_into_register address, @BX
|
@@ -311,31 +342,41 @@ module Voodoo
|
|
311
342
|
end
|
312
343
|
end
|
313
344
|
|
314
|
-
#
|
315
|
-
|
316
|
-
def load_symbol symbol, reg = @SCRATCH_REG
|
345
|
+
# Loads the value associated with the given symbol.
|
346
|
+
def load_symbol symbol, reg
|
317
347
|
x = @environment[symbol]
|
318
|
-
if x
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
when :local
|
323
|
-
load_local x[1], reg
|
324
|
-
else
|
325
|
-
raise "Invalid variable type: #{x[0]}"
|
326
|
-
end
|
348
|
+
if x.kind_of? Symbol
|
349
|
+
x
|
350
|
+
elsif x.kind_of? Integer
|
351
|
+
offset_reference x
|
327
352
|
else
|
328
353
|
# Assume global
|
329
|
-
symbol
|
354
|
+
if @relocated_symbols.include? symbol
|
355
|
+
load_symbol_from_got symbol, reg
|
356
|
+
else
|
357
|
+
symbol
|
358
|
+
end
|
330
359
|
end
|
331
360
|
end
|
332
361
|
|
333
|
-
#
|
362
|
+
# Loads a value.
|
334
363
|
# Returns a string that can be used to refer to the loaded value.
|
335
|
-
def load_value value, reg
|
364
|
+
def load_value value, reg
|
365
|
+
if substitution? value
|
366
|
+
value = substitute_number value[1]
|
367
|
+
end
|
368
|
+
|
336
369
|
if integer? value
|
337
|
-
|
338
|
-
|
370
|
+
if @WORDSIZE > 4 && (value < -2147483648 || value > 2147483647)
|
371
|
+
# AMD64 can load immediate values that are outside the range
|
372
|
+
# that can be represented as a 32-bit signed integer, but
|
373
|
+
# only with a mov instruction that loads the value into a
|
374
|
+
# register.
|
375
|
+
emit "mov #{@WORD_NAME} #{reg}, #{value}\n"
|
376
|
+
reg
|
377
|
+
else
|
378
|
+
value
|
379
|
+
end
|
339
380
|
elsif symbol? value
|
340
381
|
load_symbol value, reg
|
341
382
|
elsif at_expr? value
|
@@ -345,20 +386,25 @@ module Voodoo
|
|
345
386
|
end
|
346
387
|
end
|
347
388
|
|
348
|
-
#
|
389
|
+
# Loads a value into a register.
|
349
390
|
def load_value_into_register value, register
|
350
|
-
|
351
|
-
|
352
|
-
load_code
|
353
|
-
else
|
354
|
-
"mov #{register}, #{load_code}\n"
|
355
|
-
end
|
391
|
+
value_ref = load_value value, register
|
392
|
+
set_register register, value_ref
|
356
393
|
end
|
357
394
|
|
358
395
|
#
|
359
396
|
# == Storing Values
|
360
397
|
#
|
361
398
|
|
399
|
+
# Stores the value of a register in memory.
|
400
|
+
def emit_store_word register, base, offset = 0
|
401
|
+
if offset == 0
|
402
|
+
emit "mov [#{base}], #{register}\n"
|
403
|
+
else
|
404
|
+
emit "mov [#{base} + #{offset} * #{@WORDSIZE}], #{register}\n"
|
405
|
+
end
|
406
|
+
end
|
407
|
+
|
362
408
|
# Evaluate the expr in words and store the result in target
|
363
409
|
def set target, *words
|
364
410
|
if integer? target
|
@@ -367,35 +413,10 @@ module Voodoo
|
|
367
413
|
raise "Cannot change value of global #{target}"
|
368
414
|
end
|
369
415
|
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
emit "; nothing to do; destination equals source\n"
|
374
|
-
else
|
375
|
-
target_ref = load_value target, @BX
|
376
|
-
if integer?(words[0])
|
377
|
-
if words[0].to_i == 0
|
378
|
-
# Set destination to 0
|
379
|
-
emit "xor #{@AX}, #{@AX}\n"
|
380
|
-
emit "mov #{target_ref}, #{@AX}\n"
|
381
|
-
else
|
382
|
-
# Load immediate
|
383
|
-
emit "mov #{@WORD_NAME} #{target_ref}, #{words[0]}\n"
|
384
|
-
end
|
385
|
-
else
|
386
|
-
# Copy source to destination
|
387
|
-
eval_expr words, @RETURN_REG
|
388
|
-
emit "mov #{target_ref}, #{@RETURN_REG}\n"
|
389
|
-
end
|
390
|
-
end
|
391
|
-
else
|
392
|
-
op = words[0]
|
393
|
-
|
394
|
-
if words.length == 3 && binop?(op)
|
395
|
-
# Binary operation
|
396
|
-
binop op, target, words[1], words[2]
|
416
|
+
if words.length != 1 || words[0] != target
|
417
|
+
if symbol?(target) && symbol?(@environment[target])
|
418
|
+
eval_expr words, @environment[target]
|
397
419
|
else
|
398
|
-
# Not a binary operation
|
399
420
|
eval_expr words, @RETURN_REG
|
400
421
|
target_ref = load_value target, @BX
|
401
422
|
emit "mov #{target_ref}, #{@RETURN_REG}\n"
|
@@ -405,12 +426,11 @@ module Voodoo
|
|
405
426
|
|
406
427
|
# Set the byte at _base_ + _offset_ to _value_
|
407
428
|
def set_byte base, offset, value
|
408
|
-
emit "; set-byte #{base} #{offset} #{value}\n"
|
409
429
|
if immediate_operand?(value)
|
410
430
|
value_ref = value
|
411
431
|
else
|
412
432
|
load_value_into_register value, @RETURN_REG
|
413
|
-
value_ref =
|
433
|
+
value_ref = :al
|
414
434
|
end
|
415
435
|
addr_ref = load_address base, offset, 1
|
416
436
|
emit "mov byte #{addr_ref}, #{value_ref}\n"
|
@@ -418,9 +438,8 @@ module Voodoo
|
|
418
438
|
|
419
439
|
# Set the word at _base_ + _offset_ * +@WORDSIZE+ to _value_
|
420
440
|
def set_word base, offset, value
|
421
|
-
emit "; set-word #{base} #{offset} #{value}\n"
|
422
441
|
if immediate_operand?(value)
|
423
|
-
value_ref = value
|
442
|
+
value_ref = load_value value, @RETURN_REG
|
424
443
|
else
|
425
444
|
load_value_into_register value, @RETURN_REG
|
426
445
|
value_ref = @RETURN_REG
|
@@ -429,69 +448,6 @@ module Voodoo
|
|
429
448
|
emit "mov #{@WORD_NAME} #{addr_ref}, #{value_ref}\n"
|
430
449
|
end
|
431
450
|
|
432
|
-
#
|
433
|
-
# == Binary Operations
|
434
|
-
#
|
435
|
-
|
436
|
-
# Emit code for a binary operation
|
437
|
-
def binop op, target, x, y
|
438
|
-
if target == x
|
439
|
-
binop2 op, target, y
|
440
|
-
elsif symmetric_operation?(op) && y == target
|
441
|
-
binop2 op, target, x
|
442
|
-
else
|
443
|
-
# Cases that are handled specially
|
444
|
-
return div(target, x, y) if op == :div
|
445
|
-
return mod(target, x, y) if op == :mod
|
446
|
-
return mul(target, x, y) if op == :mul
|
447
|
-
|
448
|
-
target_ref = load_value target, @BX
|
449
|
-
x_ref = load_value x, @DX
|
450
|
-
y_ref = load_value y, @CX
|
451
|
-
|
452
|
-
mnemonic = action_to_mnemonic op
|
453
|
-
if [:asr, :bsr, :rol, :ror, :shl, :shr].member? op
|
454
|
-
emit "mov cl, #{y_ref}\n" unless y_ref == @CX
|
455
|
-
y_ref = 'cl'
|
456
|
-
end
|
457
|
-
|
458
|
-
if memory_operand?(target_ref)
|
459
|
-
if memory_operand?(x_ref) || memory_operand?(y_ref)
|
460
|
-
emit "mov #{@AX}, #{x_ref}\n"
|
461
|
-
emit "#{mnemonic} #{@AX}, #{y_ref}\n"
|
462
|
-
emit "mov #{target_ref}, #{@AX}\n"
|
463
|
-
else
|
464
|
-
emit "mov #{@WORD_NAME} #{target_ref}, #{x_ref}\n"
|
465
|
-
emit "#{mnemonic} #{@WORD_NAME} #{target_ref}, #{y_ref}\n"
|
466
|
-
end
|
467
|
-
else
|
468
|
-
raise "Can't happen: target_ref is #{target_ref.inspect}"
|
469
|
-
end
|
470
|
-
end
|
471
|
-
end
|
472
|
-
|
473
|
-
# Emit code for a binary operation where the first operand
|
474
|
-
# is also the target
|
475
|
-
def binop2 op, target, y
|
476
|
-
# Cases that are handled specially
|
477
|
-
return div2(target, target, y) if op == :div
|
478
|
-
return mod2(target, y) if op == :mod
|
479
|
-
return mul2(target, y) if op == :mul
|
480
|
-
|
481
|
-
target_ref = load_value target, @AX
|
482
|
-
y_ref = load_value y, @CX
|
483
|
-
mnemonic = action_to_mnemonic op
|
484
|
-
if [:asr, :bsr, :rol, :ror, :shl, :shr].member? op
|
485
|
-
emit "mov cl, #{y_ref}\n" unless y_ref == @CX
|
486
|
-
emit "#{mnemonic} #{@WORD_NAME} #{target_ref}, cl\n"
|
487
|
-
elsif memory_operand?(target_ref) && memory_operand?(y_ref)
|
488
|
-
emit "mov #{@CX}, #{y_ref}\n"
|
489
|
-
emit "#{mnemonic} #{target_ref}, #{@CX}\n"
|
490
|
-
else
|
491
|
-
emit "#{mnemonic} #{@WORD_NAME} #{target_ref}, #{y_ref}\n"
|
492
|
-
end
|
493
|
-
end
|
494
|
-
|
495
451
|
# Divide x by y and store the quotient in target
|
496
452
|
def div target, x, y
|
497
453
|
eval_div x, y
|
@@ -535,15 +491,17 @@ module Voodoo
|
|
535
491
|
# Perform division.
|
536
492
|
# The quotient is stored in @AX, the remainder in @DX.
|
537
493
|
def eval_div x, y
|
538
|
-
|
539
|
-
|
540
|
-
|
541
|
-
|
542
|
-
|
543
|
-
|
544
|
-
|
545
|
-
|
546
|
-
|
494
|
+
with_temporary do |temporary|
|
495
|
+
x_ref = load_value_into_register x, @AX
|
496
|
+
y_ref = load_value y, temporary
|
497
|
+
emit "mov #{@DX}, #{@AX}\n"
|
498
|
+
emit "sar #{@DX}, #{@WORDSIZE * 8 - 1}\n"
|
499
|
+
if immediate_operand?(y_ref)
|
500
|
+
set_register @BX, y_ref
|
501
|
+
emit "idiv #{@BX}\n"
|
502
|
+
else
|
503
|
+
emit "idiv #{@WORD_NAME} #{y_ref}\n"
|
504
|
+
end
|
547
505
|
end
|
548
506
|
end
|
549
507
|
|
@@ -561,33 +519,45 @@ module Voodoo
|
|
561
519
|
op = words[0]
|
562
520
|
case op
|
563
521
|
when :asr, :bsr, :rol, :ror, :shl, :shr
|
564
|
-
|
522
|
+
if integer? words[2]
|
523
|
+
y_ref = words[2]
|
524
|
+
else
|
525
|
+
load_value_into_register words[2], @CX
|
526
|
+
y_ref = :cl
|
527
|
+
end
|
565
528
|
load_value_into_register words[1], register
|
566
|
-
emit "#{action_to_mnemonic op} #{register},
|
529
|
+
emit "#{action_to_mnemonic op} #{register}, #{y_ref}\n"
|
530
|
+
when :'auto-bytes'
|
531
|
+
auto_bytes words[1], register
|
532
|
+
when :'auto-words'
|
533
|
+
auto_words words[1], register
|
567
534
|
when :call
|
568
535
|
call *words[1..-1]
|
536
|
+
emit "mov #{register}, #{@RETURN_REG}\n" if register != @RETURN_REG
|
569
537
|
when :div
|
570
538
|
eval_div words[1], words[2]
|
571
539
|
set_register register, @AX
|
572
540
|
when :'get-byte'
|
573
|
-
# Clear register
|
574
|
-
set_register register, 0
|
575
541
|
# Get address reference
|
576
542
|
address_ref = load_address words[1], words[2], 1
|
577
543
|
# Load byte from address
|
578
544
|
case register
|
579
|
-
when
|
580
|
-
set_register
|
581
|
-
|
582
|
-
|
583
|
-
|
584
|
-
set_register
|
585
|
-
when
|
586
|
-
set_register
|
545
|
+
when :eax, :rax
|
546
|
+
set_register register, 0
|
547
|
+
set_register :al, address_ref
|
548
|
+
when :ebx, :rbx
|
549
|
+
set_register register, 0
|
550
|
+
set_register :bl, address_ref
|
551
|
+
when :ecx, :rcx
|
552
|
+
set_register register, 0
|
553
|
+
set_register :cl, address_ref
|
554
|
+
when :edx, :rdx
|
555
|
+
set_register register, 0
|
556
|
+
set_register :dl, address_ref
|
587
557
|
else
|
588
|
-
set_register @
|
589
|
-
set_register
|
590
|
-
set_register register, @
|
558
|
+
set_register @AX, 0
|
559
|
+
set_register :al, address_ref
|
560
|
+
set_register register, @AX
|
591
561
|
end
|
592
562
|
when :'get-word'
|
593
563
|
address_ref = load_address words[1], words[2], @WORDSIZE
|
@@ -604,7 +574,7 @@ module Voodoo
|
|
604
574
|
if binop?(op)
|
605
575
|
x_ref = load_value words[1], @DX
|
606
576
|
y_ref = load_value words[2], @BX
|
607
|
-
emit "mov #{register}, #{x_ref}\n"
|
577
|
+
emit "mov #{register}, #{x_ref}\n" unless register == x_ref
|
608
578
|
emit "#{op} #{register}, #{y_ref}\n"
|
609
579
|
else
|
610
580
|
raise "Not a magic word: #{words[0]}"
|
@@ -629,9 +599,11 @@ module Voodoo
|
|
629
599
|
end
|
630
600
|
elsif immediate_operand? y_ref
|
631
601
|
emit "imul #{register}, #{x_ref}, #{y_ref}\n"
|
632
|
-
|
633
|
-
emit "mov #{register}, #{x_ref}\n"
|
602
|
+
elsif y_ref != register
|
603
|
+
emit "mov #{register}, #{x_ref}\n" unless x_ref == register
|
634
604
|
emit "imul #{register}, #{y_ref}\n"
|
605
|
+
else
|
606
|
+
emit "imul #{register}, #{x_ref}\n"
|
635
607
|
end
|
636
608
|
end
|
637
609
|
|
@@ -642,13 +614,44 @@ module Voodoo
|
|
642
614
|
# Start a conditional using the specified branch instruction
|
643
615
|
# after the comparison.
|
644
616
|
def common_if branch, x, y = nil
|
645
|
-
|
646
|
-
|
617
|
+
# Inverses of branches. E.g.
|
618
|
+
# cmp x, y
|
619
|
+
# jle somewhere
|
620
|
+
# is the same as
|
621
|
+
# cmp y, x
|
622
|
+
# jgt somewhere
|
623
|
+
inverse_branch = {
|
624
|
+
:je => :je,
|
625
|
+
:jge => :jl,
|
626
|
+
:jg => :jle,
|
627
|
+
:jl => :jge,
|
628
|
+
:jle => :jg,
|
629
|
+
:jne => :jne,
|
630
|
+
}
|
631
|
+
|
632
|
+
y_ref = load_value y, @DX
|
633
|
+
x_ref = load_value x, @AX
|
634
|
+
if immediate_operand?(x_ref)
|
635
|
+
# Can't have an immediate value as the first operand.
|
636
|
+
if immediate_operand?(y_ref)
|
637
|
+
# Both are immediates. Put the first in a register.
|
638
|
+
emit "mov #{@AX}, #{x_ref}\n"
|
639
|
+
x_ref = @AX
|
640
|
+
else
|
641
|
+
# y isn't immediate; swap x and y.
|
642
|
+
x_ref, y_ref = [y_ref, x_ref]
|
643
|
+
branch = inverse_branch[branch]
|
644
|
+
end
|
645
|
+
elsif memory_operand?(x_ref) && memory_operand?(y_ref)
|
646
|
+
# Can't have two memory operands. Move the first into a register.
|
647
|
+
emit "mov #{@AX}, #{x_ref}\n"
|
648
|
+
x_ref = @AX
|
649
|
+
end
|
647
650
|
truelabel = @environment.gensym
|
648
651
|
falselabel = @environment.gensym
|
649
652
|
@if_labels.push falselabel
|
650
653
|
|
651
|
-
emit "cmp #{@
|
654
|
+
emit "cmp #{@WORD_NAME} #{x_ref}, #{y_ref}\n"
|
652
655
|
emit "#{branch} #{truelabel}\n"
|
653
656
|
emit "jmp #{falselabel}\n"
|
654
657
|
emit "#{truelabel}:\n"
|
@@ -662,7 +665,6 @@ module Voodoo
|
|
662
665
|
|
663
666
|
# Start the false path of a conditional.
|
664
667
|
def ifelse
|
665
|
-
emit "; else\n"
|
666
668
|
newlabel = @environment.gensym
|
667
669
|
emit "jmp #{newlabel}\n"
|
668
670
|
label = @if_labels.pop
|
@@ -672,38 +674,32 @@ module Voodoo
|
|
672
674
|
|
673
675
|
# Test if x is equal to y
|
674
676
|
def ifeq x, y
|
675
|
-
|
676
|
-
common_if 'je', x, y
|
677
|
+
common_if :je, x, y
|
677
678
|
end
|
678
679
|
|
679
680
|
# Test if x is greater than or equal to y
|
680
681
|
def ifge x, y
|
681
|
-
|
682
|
-
common_if 'jge', x, y
|
682
|
+
common_if :jge, x, y
|
683
683
|
end
|
684
684
|
|
685
685
|
# Test if x is strictly greater than y
|
686
686
|
def ifgt x, y
|
687
|
-
|
688
|
-
common_if 'jg', x, y
|
687
|
+
common_if :jg, x, y
|
689
688
|
end
|
690
689
|
|
691
690
|
# Test if x is less than or equal to y
|
692
691
|
def ifle x, y
|
693
|
-
|
694
|
-
common_if 'jle', x, y
|
692
|
+
common_if :jle, x, y
|
695
693
|
end
|
696
694
|
|
697
695
|
# Test if x is strictly less than y
|
698
696
|
def iflt x, y
|
699
|
-
|
700
|
-
common_if 'jl', x, y
|
697
|
+
common_if :jl, x, y
|
701
698
|
end
|
702
699
|
|
703
700
|
# Test if x different from y
|
704
701
|
def ifne x, y
|
705
|
-
|
706
|
-
common_if 'jne', x, y
|
702
|
+
common_if :jne, x, y
|
707
703
|
end
|
708
704
|
|
709
705
|
#
|
@@ -724,13 +720,19 @@ module Voodoo
|
|
724
720
|
|
725
721
|
# Emit a comment
|
726
722
|
def comment text
|
727
|
-
emit "
|
723
|
+
emit "; #{text}\n"
|
728
724
|
end
|
729
725
|
|
730
|
-
#
|
731
|
-
|
732
|
-
|
733
|
-
|
726
|
+
# Returns a memory reference for the address at the given offset
|
727
|
+
# from the frame pointer.
|
728
|
+
def offset_reference offset
|
729
|
+
if offset > 0
|
730
|
+
"[#{@BP} + #{offset}]"
|
731
|
+
elsif offset < 0
|
732
|
+
"[#{@BP} - #{-offset}]"
|
733
|
+
else
|
734
|
+
"[#{@BP}]"
|
735
|
+
end
|
734
736
|
end
|
735
737
|
|
736
738
|
# Set a register to a value.
|