voodoo 0.5.0
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/ast.rb +47 -0
- data/bin/voodooc +146 -0
- data/lib/voodoo.rb +74 -0
- data/lib/voodoo/code_generator.rb +74 -0
- data/lib/voodoo/compiler.rb +45 -0
- data/lib/voodoo/config.rb +43 -0
- data/lib/voodoo/generators/amd64_elf_generator.rb +28 -0
- data/lib/voodoo/generators/amd64_nasm_generator.rb +288 -0
- data/lib/voodoo/generators/command_postprocessor.rb +30 -0
- data/lib/voodoo/generators/common_code_generator.rb +238 -0
- data/lib/voodoo/generators/gas_generator.rb +91 -0
- data/lib/voodoo/generators/generator_api1.rb +95 -0
- data/lib/voodoo/generators/i386_elf_generator.rb +62 -0
- data/lib/voodoo/generators/i386_nasm_generator.rb +177 -0
- data/lib/voodoo/generators/mips_gas_generator.rb +148 -0
- data/lib/voodoo/generators/nasm_elf_generator.rb +55 -0
- data/lib/voodoo/generators/nasm_generator.rb +679 -0
- data/lib/voodoo/parser.rb +283 -0
- metadata +82 -0
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'voodoo/config'
|
2
|
+
require 'voodoo/generators/command_postprocessor'
|
3
|
+
|
4
|
+
module Voodoo
|
5
|
+
# Class that generates ELF object files by invoking nasm on
|
6
|
+
# the output of a code generator that generates NASM assembly.
|
7
|
+
class NasmELFGenerator
|
8
|
+
def initialize nasmgenerator, nasm_extra_args
|
9
|
+
@nasmgenerator = nasmgenerator
|
10
|
+
@nasm_extra_args = nasm_extra_args
|
11
|
+
end
|
12
|
+
|
13
|
+
include CommandPostProcessor
|
14
|
+
|
15
|
+
def output_file_name input_name
|
16
|
+
input_name.sub(/\.voo$/, '') + '.o'
|
17
|
+
end
|
18
|
+
|
19
|
+
# Writes the generated code to the given IO handle
|
20
|
+
def write io
|
21
|
+
# Create temporary file to write NASM code to
|
22
|
+
if io.respond_to? :path
|
23
|
+
base = File.basename(io.path).sub(/([^.]*).*/, "\\1")
|
24
|
+
else
|
25
|
+
base = self.class.name
|
26
|
+
end
|
27
|
+
|
28
|
+
Tempfile.open(base + '.asm') do |nasmfile|
|
29
|
+
Tempfile.open(base + '.o') do |elffile|
|
30
|
+
elffile.close
|
31
|
+
# Write NASM code to nsamfile
|
32
|
+
@nasmgenerator.write nasmfile
|
33
|
+
nasmfile.close
|
34
|
+
# Find out the name of the nasm executable
|
35
|
+
if ENV.has_key? 'NASM'
|
36
|
+
nasm = ENV['NASM']
|
37
|
+
elsif ENV.has_key? 'YASM'
|
38
|
+
nasm = ENV['YASM']
|
39
|
+
else
|
40
|
+
nasm = Voodoo::Config.nasm_command
|
41
|
+
end
|
42
|
+
# Invoke nasm on nasmfile
|
43
|
+
command = "#{nasm} #{@nasm_extra_args}" +
|
44
|
+
" -o #{shell_encode elffile.path}" +
|
45
|
+
" #{shell_encode nasmfile.path}"
|
46
|
+
if system(command)
|
47
|
+
write_file_to_io elffile.path, io
|
48
|
+
end
|
49
|
+
elffile.unlink
|
50
|
+
end
|
51
|
+
nasmfile.unlink
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,679 @@
|
|
1
|
+
require 'voodoo/generators/common_code_generator'
|
2
|
+
|
3
|
+
module Voodoo
|
4
|
+
# = NASM Code Generator
|
5
|
+
#
|
6
|
+
# The NASM code generator is a common base class for generators that output
|
7
|
+
# assembly code for use with the {Netwide Assembler}[http://www.nasm.us/].
|
8
|
+
#
|
9
|
+
# This class is used by both the I386NasmGenerator and the
|
10
|
+
# AMD64NasmGenerator, and contains the functionality that is common to
|
11
|
+
# both.
|
12
|
+
#
|
13
|
+
# To use the functionality from this class, a subclass must define the
|
14
|
+
# following methods and constants:
|
15
|
+
# - #emit_function_prologue
|
16
|
+
# - #load_arg
|
17
|
+
# - #load_local
|
18
|
+
# - @CODE_ALIGNMENT
|
19
|
+
# - @DATA_ALIGNMENT
|
20
|
+
# - @FUNCTION_ALIGNMENT
|
21
|
+
# - @SCRATCH_REG
|
22
|
+
# - @WORD_NAME
|
23
|
+
# - @WORDSIZE
|
24
|
+
# - @AX, @BX, @CX, and @DX
|
25
|
+
class NasmGenerator < CommonCodeGenerator
|
26
|
+
def initialize params = {}
|
27
|
+
super params
|
28
|
+
@if_labels = []
|
29
|
+
end
|
30
|
+
|
31
|
+
#
|
32
|
+
# == Information About the Generator
|
33
|
+
#
|
34
|
+
|
35
|
+
# Given an input file name, returns the canonical output file name
|
36
|
+
# for this code generator.
|
37
|
+
def output_file_name input_name
|
38
|
+
input_name.sub(/\.voo$/, '') + '.asm'
|
39
|
+
end
|
40
|
+
|
41
|
+
# Returns the number of bits per word for this code generator.
|
42
|
+
def wordsize
|
43
|
+
@WORDSIZE * 8
|
44
|
+
end
|
45
|
+
|
46
|
+
#
|
47
|
+
# == Labels
|
48
|
+
#
|
49
|
+
|
50
|
+
# Export symbols from the current section
|
51
|
+
def export *symbols
|
52
|
+
emit "global #{symbols.join ', '}\n"
|
53
|
+
end
|
54
|
+
|
55
|
+
# Continue execution at the given address
|
56
|
+
def goto value
|
57
|
+
emit "; goto #{value}\n"
|
58
|
+
value_ref = load_value value, @SCRATCH_REG
|
59
|
+
emit "jmp #{value_ref}\n"
|
60
|
+
end
|
61
|
+
|
62
|
+
# Import labels into the current section
|
63
|
+
def import *symbols
|
64
|
+
emit "extern #{symbols.join ', '}\n"
|
65
|
+
end
|
66
|
+
|
67
|
+
# Define a label in the current section
|
68
|
+
def label name
|
69
|
+
emit "#{name}:\n"
|
70
|
+
end
|
71
|
+
|
72
|
+
#
|
73
|
+
# == Data definition
|
74
|
+
#
|
75
|
+
|
76
|
+
# Define a byte with the given value
|
77
|
+
def byte value
|
78
|
+
emit "db #{value}\n"
|
79
|
+
end
|
80
|
+
|
81
|
+
# Define a dword with the given value
|
82
|
+
def dword value
|
83
|
+
emit "dd #{value}\n"
|
84
|
+
end
|
85
|
+
|
86
|
+
# Define a qword with the given value
|
87
|
+
def qword value
|
88
|
+
emit "dq #{value}\n"
|
89
|
+
end
|
90
|
+
|
91
|
+
# Define a string with the given value
|
92
|
+
def string value
|
93
|
+
code = ''
|
94
|
+
in_quote = false
|
95
|
+
value.each_byte do |b|
|
96
|
+
if b >= 32 && b < 128
|
97
|
+
if in_quote
|
98
|
+
code << b.chr
|
99
|
+
else
|
100
|
+
code << ',' unless code.empty?
|
101
|
+
code << "'" + b.chr
|
102
|
+
in_quote = true
|
103
|
+
end
|
104
|
+
else
|
105
|
+
if in_quote
|
106
|
+
code << "',#{b}"
|
107
|
+
in_quote = false
|
108
|
+
else
|
109
|
+
code << ',' unless code.empty?
|
110
|
+
code << "#{b}"
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
code << "'" if in_quote
|
115
|
+
emit "db #{code}\n"
|
116
|
+
end
|
117
|
+
|
118
|
+
#
|
119
|
+
# == Alignment
|
120
|
+
#
|
121
|
+
|
122
|
+
def align alignment = nil
|
123
|
+
unless alignment
|
124
|
+
# Get default alignment
|
125
|
+
case @section
|
126
|
+
when :code
|
127
|
+
alignment = @CODE_ALIGNMENT
|
128
|
+
when :data
|
129
|
+
alignment = @DATA_ALIGNMENT
|
130
|
+
when :function
|
131
|
+
alignment = @FUNCTION_ALIGNMENT
|
132
|
+
else
|
133
|
+
# Use data alignment as default
|
134
|
+
alignment = @DATA_ALIGNMENT
|
135
|
+
end
|
136
|
+
end
|
137
|
+
emit "align #{alignment}\n" unless alignment == 0
|
138
|
+
end
|
139
|
+
|
140
|
+
#
|
141
|
+
# == Functions
|
142
|
+
#
|
143
|
+
|
144
|
+
# Emit function preamble and declare _formals_ as function arguments
|
145
|
+
def begin_function *formals
|
146
|
+
emit "; function #{formals.join ' '}\n"
|
147
|
+
environment = Environment.new @environment
|
148
|
+
environment.add_args formals
|
149
|
+
@environment = environment
|
150
|
+
emit_function_prologue formals
|
151
|
+
end
|
152
|
+
|
153
|
+
# Emit function epilogue.
|
154
|
+
def emit_function_epilogue formals = []
|
155
|
+
emit "leave\n"
|
156
|
+
end
|
157
|
+
|
158
|
+
# End a function body
|
159
|
+
def end_function
|
160
|
+
emit "; end function\n\n"
|
161
|
+
if @environment == @top_level
|
162
|
+
raise "Cannot end function when not in a function"
|
163
|
+
else
|
164
|
+
@environment = @top_level
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
# Return a from a function.
|
169
|
+
#
|
170
|
+
# _words_ may contain an expression to be evaluated. The result
|
171
|
+
# of the evaluation is returned from the function.
|
172
|
+
def ret *words
|
173
|
+
emit "; return #{words.join ' '}\n"
|
174
|
+
eval_expr words
|
175
|
+
emit_function_epilogue
|
176
|
+
emit "ret\n"
|
177
|
+
end
|
178
|
+
|
179
|
+
#
|
180
|
+
# == Conditionals
|
181
|
+
#
|
182
|
+
|
183
|
+
# End a conditional body
|
184
|
+
def end_if
|
185
|
+
label = @if_labels.pop
|
186
|
+
emit "#{label}:\n"
|
187
|
+
end
|
188
|
+
|
189
|
+
#
|
190
|
+
# == Value Classification
|
191
|
+
#
|
192
|
+
|
193
|
+
# Test if op is a binary operation
|
194
|
+
def binop? op
|
195
|
+
[:div, :mod, :sub].member?(op) || symmetric_operation?(op)
|
196
|
+
end
|
197
|
+
|
198
|
+
# Test if a value is an integer
|
199
|
+
def integer? value
|
200
|
+
value.kind_of? Integer
|
201
|
+
end
|
202
|
+
|
203
|
+
# Test if a symbol refers to a global
|
204
|
+
def global? symbol
|
205
|
+
symbol?(symbol) && @environment[symbol] == nil
|
206
|
+
end
|
207
|
+
|
208
|
+
# Tests if an operand is an immediate operand
|
209
|
+
def immediate_operand? operand
|
210
|
+
integer?(operand) || global?(operand)
|
211
|
+
end
|
212
|
+
|
213
|
+
# Tests if an operand is a memory operand
|
214
|
+
def memory_operand? operand
|
215
|
+
operand[0] == ?[
|
216
|
+
end
|
217
|
+
|
218
|
+
# Test if a value is a symbol
|
219
|
+
def symbol? value
|
220
|
+
value.kind_of? Symbol
|
221
|
+
end
|
222
|
+
|
223
|
+
# Test if op is a symmetric operation (i.e. it will yield the
|
224
|
+
# same result if the order of its source operands is changed).
|
225
|
+
def symmetric_operation? op
|
226
|
+
[:add, :and, :mul, :or, :xor].member? op
|
227
|
+
end
|
228
|
+
|
229
|
+
#
|
230
|
+
# == Loading Values
|
231
|
+
#
|
232
|
+
|
233
|
+
# Create a value reference to an address.
|
234
|
+
# Invoking this code may clobber @BX and/or @CX
|
235
|
+
def load_address base, offset, scale
|
236
|
+
base_ref = load_value base, @BX
|
237
|
+
offset_ref = load_value offset, @CX
|
238
|
+
|
239
|
+
if offset_ref == 0
|
240
|
+
if integer? base_ref
|
241
|
+
# Only an integer base
|
242
|
+
"[#{base_ref}]"
|
243
|
+
else
|
244
|
+
# Some complex base; load in @BX
|
245
|
+
emit "mov #{@BX}, #{base_ref}\n"
|
246
|
+
"[#{@BX}]"
|
247
|
+
end
|
248
|
+
elsif base_ref == 0
|
249
|
+
if integer? offset_ref
|
250
|
+
# Only a scaled offset
|
251
|
+
"[#{offset_ref.to_i * scale}]"
|
252
|
+
else
|
253
|
+
# Some complex offset; load in @CX
|
254
|
+
emit "mov #{@CX}, #{offset_ref}\n"
|
255
|
+
"[#{@CX} * #{scale}]"
|
256
|
+
end
|
257
|
+
elsif integer? base_ref
|
258
|
+
if integer? offset_ref
|
259
|
+
# All integers, combine them together
|
260
|
+
"[#{base_ref.to_i + (offset_ref.to_i * scale)}]"
|
261
|
+
else
|
262
|
+
# Complex offset; use @CX
|
263
|
+
emit "mov #{@CX}, #{offset_ref}\n"
|
264
|
+
"[#{base_ref} + #{@CX} * #{scale}]"
|
265
|
+
end
|
266
|
+
elsif integer? offset_ref
|
267
|
+
# Complex base, integer offset; use @BX
|
268
|
+
emit "mov #{@BX}, #{base_ref}\n"
|
269
|
+
"[#{@BX} + #{offset_ref.to_i * scale}]"
|
270
|
+
else
|
271
|
+
# Both base and offset are complex
|
272
|
+
# Use both @BX and @CX
|
273
|
+
emit "mov #{@BX}, #{base_ref}\n"
|
274
|
+
emit "mov #{@CX}, #{offset_ref}\n"
|
275
|
+
"[#{@BX} + #{@CX} * #{scale}]"
|
276
|
+
end
|
277
|
+
end
|
278
|
+
|
279
|
+
# Load the value associated with the given symbol.
|
280
|
+
# Returns a string that can be used to refer to the loaded value.
|
281
|
+
def load_symbol symbol, reg = @SCRATCH_REG
|
282
|
+
x = @environment[symbol]
|
283
|
+
if x
|
284
|
+
case x[0]
|
285
|
+
when :arg
|
286
|
+
load_arg x[1], reg
|
287
|
+
when :local
|
288
|
+
load_local x[1], reg
|
289
|
+
else
|
290
|
+
raise "Invalid variable type: #{x[0]}"
|
291
|
+
end
|
292
|
+
else
|
293
|
+
# Assume global
|
294
|
+
symbol.to_s
|
295
|
+
end
|
296
|
+
end
|
297
|
+
|
298
|
+
# Load a value.
|
299
|
+
# Returns a string that can be used to refer to the loaded value.
|
300
|
+
def load_value value, reg = @SCRATCH_REG
|
301
|
+
if integer? value
|
302
|
+
# Integers can be used as is
|
303
|
+
value
|
304
|
+
elsif symbol? value
|
305
|
+
load_symbol value, reg
|
306
|
+
end
|
307
|
+
end
|
308
|
+
|
309
|
+
#
|
310
|
+
# == Storing Values
|
311
|
+
#
|
312
|
+
|
313
|
+
# Evaluate the expr in words and store the result in target
|
314
|
+
def set target, *words
|
315
|
+
if integer? target
|
316
|
+
raise "Cannot change value of integer #{target}"
|
317
|
+
elsif global?(target)
|
318
|
+
raise "Cannot change value of global #{target}"
|
319
|
+
end
|
320
|
+
|
321
|
+
emit "; set #{target} #{words.join ' '}\n"
|
322
|
+
if words.length == 1
|
323
|
+
if words[0] == target
|
324
|
+
emit "; nothing to do; destination equals source\n"
|
325
|
+
else
|
326
|
+
target_ref = load_value target, @BX
|
327
|
+
if integer?(words[0])
|
328
|
+
if words[0].to_i == 0
|
329
|
+
# Set destination to 0
|
330
|
+
emit "xor #{@AX}, #{@AX}\n"
|
331
|
+
emit "mov #{target_ref}, #{@AX}\n"
|
332
|
+
else
|
333
|
+
# Load immediate
|
334
|
+
emit "mov #{@WORD_NAME} #{target_ref}, #{words[0]}\n"
|
335
|
+
end
|
336
|
+
else
|
337
|
+
# Copy source to destination
|
338
|
+
eval_expr words, @RETURN_REG
|
339
|
+
emit "mov #{target_ref}, #{@RETURN_REG}\n"
|
340
|
+
end
|
341
|
+
end
|
342
|
+
else
|
343
|
+
op = words[0]
|
344
|
+
|
345
|
+
if words.length == 3 && binop?(op)
|
346
|
+
# Binary operation
|
347
|
+
binop op, target, words[1], words[2]
|
348
|
+
else
|
349
|
+
# Not a binary operation
|
350
|
+
eval_expr words, @RETURN_REG
|
351
|
+
target_ref = load_value target, @BX
|
352
|
+
emit "mov #{target_ref}, #{@RETURN_REG}\n"
|
353
|
+
end
|
354
|
+
end
|
355
|
+
end
|
356
|
+
|
357
|
+
# Set the byte at _base_ + _offset_ to _value_
|
358
|
+
def set_byte base, offset, value
|
359
|
+
emit "; set-byte #{base} #{offset} #{value}\n"
|
360
|
+
value_ref = load_value value, @RETURN_REG
|
361
|
+
addr_ref = load_address base, offset, 1
|
362
|
+
emit "mov byte #{addr_ref}, #{value_ref}\n"
|
363
|
+
end
|
364
|
+
|
365
|
+
# Set the word at _base_ + _offset_ * +@WORDSIZE+ to _value_
|
366
|
+
def set_word base, offset, value
|
367
|
+
emit "; set-word #{base} #{offset} #{value}\n"
|
368
|
+
value_ref = load_value value, @RETURN_REG
|
369
|
+
addr_ref = load_address base, offset, @WORDSIZE
|
370
|
+
emit "mov dword #{addr_ref}, #{value_ref}\n"
|
371
|
+
end
|
372
|
+
|
373
|
+
#
|
374
|
+
# == Binary Operations
|
375
|
+
#
|
376
|
+
|
377
|
+
# Emit code for a binary operation
|
378
|
+
def binop op, target, x, y
|
379
|
+
if target == x
|
380
|
+
binop2 op, target, y
|
381
|
+
elsif symmetric_operation?(op) && y == target
|
382
|
+
binop2 op, target, x
|
383
|
+
else
|
384
|
+
# Cases that are handled specially
|
385
|
+
return div(target, x, y) if op == :div
|
386
|
+
return mod(target, x, y) if op == :mod
|
387
|
+
return mul(target, x, y) if op == :mul
|
388
|
+
|
389
|
+
target_ref = load_value target, @AX
|
390
|
+
x_ref = load_value x, @DX
|
391
|
+
y_ref = load_value y, @BX
|
392
|
+
|
393
|
+
if memory_operand?(target_ref)
|
394
|
+
if memory_operand?(x_ref) || memory_operand?(y_ref)
|
395
|
+
emit "mov #{@CX}, #{x_ref}\n"
|
396
|
+
emit "#{op} #{@CX}, #{y_ref}\n"
|
397
|
+
emit "mov #{target_ref}, #{@CX}\n"
|
398
|
+
else
|
399
|
+
emit "mov #{@WORD_NAME} #{target_ref}, #{x_ref}\n"
|
400
|
+
emit "#{op} #{@WORD_NAME} #{target_ref}, #{y_ref}\n"
|
401
|
+
end
|
402
|
+
else
|
403
|
+
raise "Can't happen: target_ref is #{target_ref.inspect}"
|
404
|
+
end
|
405
|
+
end
|
406
|
+
end
|
407
|
+
|
408
|
+
# Emit code for a binary operation where the first operand
|
409
|
+
# is also the target
|
410
|
+
def binop2 op, target, y
|
411
|
+
# Cases that are handled specially
|
412
|
+
return div2(target, target, y) if op == :div
|
413
|
+
return mod2(target, y) if op == :mod
|
414
|
+
return mul2(target, y) if op == :mul
|
415
|
+
|
416
|
+
target_ref = load_value target, @BX
|
417
|
+
y_ref = load_value y, @DX
|
418
|
+
if memory_operand?(target_ref) && memory_operand?(y_ref)
|
419
|
+
emit "mov #{@AX}, #{y_ref}\n"
|
420
|
+
emit "#{op} #{target_ref}, #{@AX}\n"
|
421
|
+
else
|
422
|
+
emit "#{op} #{@WORD_NAME} #{target_ref}, #{y_ref}\n"
|
423
|
+
end
|
424
|
+
end
|
425
|
+
|
426
|
+
# Divide x by y and store the quotient in target
|
427
|
+
def div target, x, y
|
428
|
+
eval_div x, y
|
429
|
+
target_ref = load_value target, @BX
|
430
|
+
emit "mov #{target_ref}, #{@AX}\n"
|
431
|
+
end
|
432
|
+
|
433
|
+
# Divide target by x and store the quotient in target
|
434
|
+
def div2 target, x
|
435
|
+
div target, target, x
|
436
|
+
end
|
437
|
+
|
438
|
+
# Divide x by y and store the remainder in target
|
439
|
+
def mod target, x, y
|
440
|
+
eval_div x, y
|
441
|
+
target_ref = load_value target, @BX
|
442
|
+
emit "mov #{target_ref}, #{@DX}\n"
|
443
|
+
end
|
444
|
+
|
445
|
+
# Divide target by x and store the remainder in target
|
446
|
+
def mod2 target, x
|
447
|
+
mod target, target, x
|
448
|
+
end
|
449
|
+
|
450
|
+
# Multiply x by y and store the result in target
|
451
|
+
def mul target, x, y
|
452
|
+
eval_mul x, y
|
453
|
+
target_ref = load_value target, @BX
|
454
|
+
emit "mov #{target_ref}, #{@RETURN_REG}\n"
|
455
|
+
end
|
456
|
+
|
457
|
+
# Multiply target by x and store the result in target
|
458
|
+
def mul2 target, x
|
459
|
+
mul target, target, x
|
460
|
+
end
|
461
|
+
|
462
|
+
#
|
463
|
+
# == Expressions
|
464
|
+
#
|
465
|
+
|
466
|
+
# Perform division.
|
467
|
+
# The quotient is stored in @AX, the remainder in @DX.
|
468
|
+
def eval_div x, y
|
469
|
+
x_ref = load_value_into_register x, @AX
|
470
|
+
y_ref = load_value y, @SCRATCH_REG
|
471
|
+
set_register @DX, 0
|
472
|
+
if immediate_operand?(y_ref)
|
473
|
+
set_register @BX, y_ref
|
474
|
+
emit "idiv #{@BX}\n"
|
475
|
+
else
|
476
|
+
emit "idiv #{@WORD_NAME} #{y_ref}\n"
|
477
|
+
end
|
478
|
+
end
|
479
|
+
|
480
|
+
# Evaluate an expression.
|
481
|
+
# The result is stored in _register_ (@RETURN_REG by default).
|
482
|
+
def eval_expr words, register = @RETURN_REG
|
483
|
+
if words.length == 1
|
484
|
+
if words[0] == 0
|
485
|
+
emit "xor #{register}, #{register}\n"
|
486
|
+
else
|
487
|
+
load_value_into_register words[0], register
|
488
|
+
end
|
489
|
+
else
|
490
|
+
op = words[0]
|
491
|
+
case op
|
492
|
+
when :call
|
493
|
+
call *words[1..-1]
|
494
|
+
when :div
|
495
|
+
eval_div words[1], words[2]
|
496
|
+
set_register register, @AX
|
497
|
+
when :'get-byte'
|
498
|
+
# Clear register
|
499
|
+
set_register register, 0
|
500
|
+
# Get address reference
|
501
|
+
address_ref = load_address words[1], words[2], 1
|
502
|
+
# Load byte from address
|
503
|
+
case register
|
504
|
+
when 'eax', 'rax'
|
505
|
+
set_register 'al', address_ref
|
506
|
+
when 'ebx', 'rbx'
|
507
|
+
set_register 'bl', address_ref
|
508
|
+
when 'ecx', 'rcx'
|
509
|
+
set_register 'cl', address_ref
|
510
|
+
when 'edx', 'rdx'
|
511
|
+
set_register 'dl', address_ref
|
512
|
+
else
|
513
|
+
set_register @BX, 0
|
514
|
+
set_register 'bl', address_ref
|
515
|
+
set_register register, @BX
|
516
|
+
end
|
517
|
+
when :'get-word'
|
518
|
+
address_ref = load_address words[1], words[2], @WORDSIZE
|
519
|
+
set_register register, address_ref
|
520
|
+
when :mod
|
521
|
+
eval_div words[1], words[2]
|
522
|
+
set_register register, @DX
|
523
|
+
when :mul
|
524
|
+
eval_mul words[1], words[2], register
|
525
|
+
when :not
|
526
|
+
load_value_into_register words[1], register
|
527
|
+
emit "not #{register}\n"
|
528
|
+
else
|
529
|
+
if binop?(op)
|
530
|
+
x_ref = load_value words[1], @DX
|
531
|
+
y_ref = load_value words[2], @BX
|
532
|
+
emit "mov #{register}, #{x_ref}\n"
|
533
|
+
emit "#{op} #{register}, #{y_ref}\n"
|
534
|
+
else
|
535
|
+
raise "Not a magic word: #{words[0]}"
|
536
|
+
end
|
537
|
+
end
|
538
|
+
end
|
539
|
+
end
|
540
|
+
|
541
|
+
# Multiply x by y.
|
542
|
+
# The result is stored in @AX by default, but
|
543
|
+
# a different register can be specified by passing
|
544
|
+
# a third argument.
|
545
|
+
def eval_mul x, y, register = @AX
|
546
|
+
x_ref = load_value x, @DX
|
547
|
+
y_ref = load_value y, @BX
|
548
|
+
|
549
|
+
if immediate_operand? x_ref
|
550
|
+
if immediate_operand? y_ref
|
551
|
+
set_register register, x_ref * y_ref
|
552
|
+
else
|
553
|
+
emit "imul #{register}, #{y_ref}, #{x_ref}\n"
|
554
|
+
end
|
555
|
+
elsif immediate_operand? y_ref
|
556
|
+
emit "imul #{register}, #{x_ref}, #{y_ref}\n"
|
557
|
+
else
|
558
|
+
emit "mov #{register}, #{x_ref}\n"
|
559
|
+
emit "imul #{register}, #{y_ref}\n"
|
560
|
+
end
|
561
|
+
end
|
562
|
+
|
563
|
+
#
|
564
|
+
# == Conditionals
|
565
|
+
#
|
566
|
+
|
567
|
+
# Start a conditional using the specified branch instruction
|
568
|
+
# after the comparison.
|
569
|
+
def common_if branch, x, y = nil
|
570
|
+
load_value_into_register y, @DX if y
|
571
|
+
load_value_into_register x, @AX
|
572
|
+
truelabel = @environment.gensym
|
573
|
+
falselabel = @environment.gensym
|
574
|
+
@if_labels.push falselabel
|
575
|
+
|
576
|
+
emit "cmp #{@AX}, #{@DX}\n"
|
577
|
+
emit "#{branch} #{truelabel}\n"
|
578
|
+
emit "jmp #{falselabel}\n"
|
579
|
+
emit "#{truelabel}:\n"
|
580
|
+
end
|
581
|
+
|
582
|
+
# End a conditional.
|
583
|
+
def end_if
|
584
|
+
label = @if_labels.pop
|
585
|
+
emit "#{label}:\n"
|
586
|
+
end
|
587
|
+
|
588
|
+
# Start the false path of a conditional.
|
589
|
+
def ifelse
|
590
|
+
emit "; else\n"
|
591
|
+
newlabel = @environment.gensym
|
592
|
+
emit "jmp #{newlabel}\n"
|
593
|
+
label = @if_labels.pop
|
594
|
+
emit "#{label}:\n"
|
595
|
+
@if_labels.push newlabel
|
596
|
+
end
|
597
|
+
|
598
|
+
# Test if x is equal to y
|
599
|
+
def ifeq x, y
|
600
|
+
emit "; ifeq #{x} #{y}\n"
|
601
|
+
common_if 'je', x, y
|
602
|
+
end
|
603
|
+
|
604
|
+
# Test if x is greater than or equal to y
|
605
|
+
def ifge x, y
|
606
|
+
emit "; ifge #{x} #{y}\n"
|
607
|
+
common_if 'jge', x, y
|
608
|
+
end
|
609
|
+
|
610
|
+
# Test if x is strictly greater than y
|
611
|
+
def ifgt x, y
|
612
|
+
emit "; ifgt #{x} #{y}\n"
|
613
|
+
common_if 'jg', x, y
|
614
|
+
end
|
615
|
+
|
616
|
+
# Test if x is less than or equal to y
|
617
|
+
def ifle x, y
|
618
|
+
emit "; ifle #{x} #{y}\n"
|
619
|
+
common_if 'jle', x, y
|
620
|
+
end
|
621
|
+
|
622
|
+
# Test if x is strictly less than y
|
623
|
+
def iflt x, y
|
624
|
+
emit "; iflt #{x} #{y}\n"
|
625
|
+
common_if 'jl', x, y
|
626
|
+
end
|
627
|
+
|
628
|
+
# Test if x different from y
|
629
|
+
def ifne x, y
|
630
|
+
emit "; ifne #{x} #{y}\n"
|
631
|
+
common_if 'jne', x, y
|
632
|
+
end
|
633
|
+
|
634
|
+
#
|
635
|
+
# == Miscellaneous
|
636
|
+
#
|
637
|
+
|
638
|
+
# Emit a comment
|
639
|
+
def comment text
|
640
|
+
emit ";#{text}\n"
|
641
|
+
end
|
642
|
+
|
643
|
+
# Load a value into a register
|
644
|
+
def load_value_into_register value, register
|
645
|
+
value_ref = load_value value, register
|
646
|
+
set_register register, value_ref
|
647
|
+
end
|
648
|
+
|
649
|
+
# Set a register to a value.
|
650
|
+
# The value must be a valid operand to the mov instruction.
|
651
|
+
def set_register register, value_ref
|
652
|
+
case value_ref
|
653
|
+
when register
|
654
|
+
# Nothing to do
|
655
|
+
when 0
|
656
|
+
emit "xor #{register}, #{register}\n"
|
657
|
+
else
|
658
|
+
emit "mov #{register}, #{value_ref}\n"
|
659
|
+
end
|
660
|
+
end
|
661
|
+
|
662
|
+
#
|
663
|
+
# == Output
|
664
|
+
#
|
665
|
+
|
666
|
+
# Write generated code to the given IO object.
|
667
|
+
def write io
|
668
|
+
io.puts "bits #{@WORDSIZE * 8}\n\n"
|
669
|
+
@sections.each do |section,code|
|
670
|
+
unless code.empty?
|
671
|
+
io.puts "section #{section.to_s}"
|
672
|
+
io.puts code
|
673
|
+
io.puts
|
674
|
+
end
|
675
|
+
end
|
676
|
+
end
|
677
|
+
|
678
|
+
end
|
679
|
+
end
|