]>
Commit | Line | Data |
---|---|---|
1 | # Copyright (C) 2013 Apple Inc. All rights reserved. | |
2 | # Copyright (C) 2013 Cisco Systems, Inc. All rights reserved. | |
3 | # | |
4 | # Redistribution and use in source and binary forms, with or without | |
5 | # modification, are permitted provided that the following conditions | |
6 | # are met: | |
7 | # 1. Redistributions of source code must retain the above copyright | |
8 | # notice, this list of conditions and the following disclaimer. | |
9 | # 2. Redistributions in binary form must reproduce the above copyright | |
10 | # notice, this list of conditions and the following disclaimer in the | |
11 | # documentation and/or other materials provided with the distribution. | |
12 | # | |
13 | # THIS SOFTWARE IS PROVIDED BY CISCO SYSTEMS, INC. ``AS IS'' AND ANY | |
14 | # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
15 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | |
16 | # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL CISCO SYSTEMS, INC. OR ITS | |
17 | # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | |
18 | # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | |
19 | # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | |
20 | # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY | |
21 | # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
22 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
23 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
24 | ||
25 | require 'risc' | |
26 | ||
27 | class Node | |
28 | def sh4SingleHi | |
29 | doubleOperand = sh4Operand | |
30 | raise "Bogus register name #{doubleOperand}" unless doubleOperand =~ /^dr/ | |
31 | "fr" + ($~.post_match.to_i).to_s | |
32 | end | |
33 | def sh4SingleLo | |
34 | doubleOperand = sh4Operand | |
35 | raise "Bogus register name #{doubleOperand}" unless doubleOperand =~ /^dr/ | |
36 | "fr" + ($~.post_match.to_i + 1).to_s | |
37 | end | |
38 | end | |
39 | ||
40 | class SpecialRegister < NoChildren | |
41 | def sh4Operand | |
42 | @name | |
43 | end | |
44 | ||
45 | def dump | |
46 | @name | |
47 | end | |
48 | ||
49 | def register? | |
50 | true | |
51 | end | |
52 | end | |
53 | ||
54 | SH4_TMP_GPRS = [ SpecialRegister.new("r3"), SpecialRegister.new("r11"), SpecialRegister.new("r13") ] | |
55 | SH4_TMP_FPRS = [ SpecialRegister.new("dr10") ] | |
56 | ||
57 | class RegisterID | |
58 | def sh4Operand | |
59 | case name | |
60 | when "t0" | |
61 | "r0" | |
62 | when "t1" | |
63 | "r1" | |
64 | when "t2" | |
65 | "r2" | |
66 | when "t3" | |
67 | "r10" | |
68 | when "t4", "a0" | |
69 | "r4" | |
70 | when "t5", "a1" | |
71 | "r5" | |
72 | when "t6", "a2" | |
73 | "r6" | |
74 | when "t7", "a3" | |
75 | "r7" | |
76 | when "t8" | |
77 | "r8" | |
78 | when "t9" | |
79 | "r9" | |
80 | when "cfr" | |
81 | "r14" | |
82 | when "sp" | |
83 | "r15" | |
84 | when "lr" | |
85 | "pr" | |
86 | else | |
87 | raise "Bad register #{name} for SH4 at #{codeOriginString}" | |
88 | end | |
89 | end | |
90 | end | |
91 | ||
92 | class FPRegisterID | |
93 | def sh4Operand | |
94 | case name | |
95 | when "ft0", "fr" | |
96 | "dr0" | |
97 | when "ft1" | |
98 | "dr2" | |
99 | when "ft2" | |
100 | "dr4" | |
101 | when "ft3" | |
102 | "dr6" | |
103 | when "ft4" | |
104 | "dr8" | |
105 | when "fa0" | |
106 | "dr12" | |
107 | else | |
108 | raise "Bad register #{name} for SH4 at #{codeOriginString}" | |
109 | end | |
110 | end | |
111 | end | |
112 | ||
113 | class Immediate | |
114 | def sh4Operand | |
115 | raise "Invalid immediate #{value} at #{codeOriginString}" if value < -128 or value > 127 | |
116 | "##{value}" | |
117 | end | |
118 | end | |
119 | ||
120 | class Address | |
121 | def sh4Operand | |
122 | raise "Bad offset #{offset.value} at #{codeOriginString}" if offset.value < 0 or offset.value > 60 | |
123 | if offset.value == 0 | |
124 | "@#{base.sh4Operand}" | |
125 | else | |
126 | "@(#{offset.value}, #{base.sh4Operand})" | |
127 | end | |
128 | end | |
129 | ||
130 | def sh4OperandPostInc | |
131 | raise "Bad offset #{offset.value} for post inc at #{codeOriginString}" unless offset.value == 0 | |
132 | "@#{base.sh4Operand}+" | |
133 | end | |
134 | ||
135 | def sh4OperandPreDec | |
136 | raise "Bad offset #{offset.value} for pre dec at #{codeOriginString}" unless offset.value == 0 | |
137 | "@-#{base.sh4Operand}" | |
138 | end | |
139 | end | |
140 | ||
141 | class BaseIndex | |
142 | def sh4Operand | |
143 | raise "Unconverted base index at #{codeOriginString}" | |
144 | end | |
145 | end | |
146 | ||
147 | class AbsoluteAddress | |
148 | def sh4Operand | |
149 | raise "Unconverted absolute address at #{codeOriginString}" | |
150 | end | |
151 | end | |
152 | ||
153 | class LabelReference | |
154 | def sh4Operand | |
155 | value | |
156 | end | |
157 | end | |
158 | ||
159 | class SubImmediates < Node | |
160 | def sh4Operand | |
161 | "#{@left.sh4Operand} - #{@right.sh4Operand}" | |
162 | end | |
163 | end | |
164 | ||
165 | class ConstPool < Node | |
166 | attr_reader :size | |
167 | attr_reader :entries | |
168 | ||
169 | def initialize(codeOrigin, entries, size) | |
170 | super(codeOrigin) | |
171 | raise "Invalid size #{size} for ConstPool" unless size == 16 or size == 32 | |
172 | @size = size | |
173 | @entries = entries | |
174 | end | |
175 | ||
176 | def dump | |
177 | "#{size}: #{entries}" | |
178 | end | |
179 | ||
180 | def address? | |
181 | false | |
182 | end | |
183 | ||
184 | def label? | |
185 | false | |
186 | end | |
187 | ||
188 | def immediate? | |
189 | false | |
190 | end | |
191 | ||
192 | def register? | |
193 | false | |
194 | end | |
195 | ||
196 | def lowerSH4 | |
197 | if size == 16 | |
198 | $asm.puts ".balign 2" | |
199 | else | |
200 | $asm.puts ".balign 4" | |
201 | end | |
202 | entries.map { | |
203 | |e| | |
204 | e.label.lower("SH4") | |
205 | if e.size == 16 | |
206 | $asm.puts ".word #{e.value}" | |
207 | else | |
208 | $asm.puts ".long #{e.value}" | |
209 | end | |
210 | } | |
211 | end | |
212 | end | |
213 | ||
214 | class ConstPoolEntry < Node | |
215 | attr_reader :size | |
216 | attr_reader :value | |
217 | attr_reader :label | |
218 | attr_reader :labelref | |
219 | ||
220 | def initialize(codeOrigin, value, size) | |
221 | super(codeOrigin) | |
222 | raise "Invalid size #{size} for ConstPoolEntry" unless size == 16 or size == 32 | |
223 | @size = size | |
224 | @value = value | |
225 | @label = LocalLabel.unique("constpool#{size}") | |
226 | @labelref = LocalLabelReference.new(codeOrigin, label) | |
227 | end | |
228 | ||
229 | def dump | |
230 | "#{value} (#{size} @ #{label})" | |
231 | end | |
232 | ||
233 | def ==(other) | |
234 | other.is_a? ConstPoolEntry and other.value == @value | |
235 | end | |
236 | ||
237 | def address? | |
238 | false | |
239 | end | |
240 | ||
241 | def label? | |
242 | false | |
243 | end | |
244 | ||
245 | def immediate? | |
246 | false | |
247 | end | |
248 | ||
249 | def register? | |
250 | false | |
251 | end | |
252 | end | |
253 | ||
254 | ||
255 | # | |
256 | # Lowering of shift ops for SH4. For example: | |
257 | # | |
258 | # rshifti foo, bar | |
259 | # | |
260 | # becomes: | |
261 | # | |
262 | # negi foo, tmp | |
263 | # shad tmp, bar | |
264 | # | |
265 | ||
266 | def sh4LowerShiftOps(list) | |
267 | newList = [] | |
268 | list.each { | |
269 | | node | | |
270 | if node.is_a? Instruction | |
271 | case node.opcode | |
272 | when "ulshifti", "ulshiftp", "urshifti", "urshiftp", "lshifti", "lshiftp", "rshifti", "rshiftp" | |
273 | if node.opcode[0, 1] == "u" | |
274 | type = "l" | |
275 | direction = node.opcode[1, 1] | |
276 | else | |
277 | type = "a" | |
278 | direction = node.opcode[0, 1] | |
279 | end | |
280 | if node.operands[0].is_a? Immediate | |
281 | maskedImm = Immediate.new(node.operands[0].codeOrigin, node.operands[0].value & 31) | |
282 | if maskedImm.value == 0 | |
283 | # There is nothing to do here. | |
284 | elsif maskedImm.value == 1 or (type == "l" and [2, 8, 16].include? maskedImm.value) | |
285 | newList << Instruction.new(node.codeOrigin, "sh#{type}#{direction}x", [maskedImm, node.operands[1]]) | |
286 | else | |
287 | tmp = Tmp.new(node.codeOrigin, :gpr) | |
288 | if direction == "l" | |
289 | newList << Instruction.new(node.codeOrigin, "move", [maskedImm, tmp]) | |
290 | else | |
291 | newList << Instruction.new(node.codeOrigin, "move", [Immediate.new(node.operands[0].codeOrigin, -1 * maskedImm.value), tmp]) | |
292 | end | |
293 | newList << Instruction.new(node.codeOrigin, "sh#{type}d", [tmp, node.operands[1]]) | |
294 | end | |
295 | else | |
296 | tmp = Tmp.new(node.codeOrigin, :gpr) | |
297 | newList << Instruction.new(node.codeOrigin, "move", [Immediate.new(node.operands[0].codeOrigin, 31), tmp]) | |
298 | newList << Instruction.new(node.codeOrigin, "andi", [node.operands[0], tmp]) | |
299 | if direction == "r" | |
300 | newList << Instruction.new(node.codeOrigin, "negi", [tmp, tmp]) | |
301 | end | |
302 | newList << Instruction.new(node.codeOrigin, "sh#{type}d", [tmp, node.operands[1]]) | |
303 | end | |
304 | else | |
305 | newList << node | |
306 | end | |
307 | else | |
308 | newList << node | |
309 | end | |
310 | } | |
311 | newList | |
312 | end | |
313 | ||
314 | ||
315 | # | |
316 | # Lowering of simple branch ops for SH4. For example: | |
317 | # | |
318 | # baddis foo, bar, baz | |
319 | # | |
320 | # will become: | |
321 | # | |
322 | # addi foo, bar | |
323 | # bs bar, baz | |
324 | # | |
325 | ||
326 | def sh4LowerSimpleBranchOps(list) | |
327 | newList = [] | |
328 | list.each { | |
329 | | node | | |
330 | if node.is_a? Instruction | |
331 | annotation = node.annotation | |
332 | case node.opcode | |
333 | when /^b(addi|subi|ori|addp)/ | |
334 | op = $1 | |
335 | bc = $~.post_match | |
336 | ||
337 | case op | |
338 | when "addi", "addp" | |
339 | op = "addi" | |
340 | when "subi", "subp" | |
341 | op = "subi" | |
342 | when "ori", "orp" | |
343 | op = "ori" | |
344 | end | |
345 | ||
346 | if bc == "s" | |
347 | raise "Invalid operands number (#{node.operands.size})" unless node.operands.size == 3 | |
348 | if node.operands[1].is_a? RegisterID or node.operands[1].is_a? SpecialRegister | |
349 | newList << Instruction.new(node.codeOrigin, op, node.operands[0..1]) | |
350 | newList << Instruction.new(node.codeOrigin, "bs", node.operands[1..2]) | |
351 | else | |
352 | tmpVal = Tmp.new(node.codeOrigin, :gpr) | |
353 | tmpPtr = Tmp.new(node.codeOrigin, :gpr) | |
354 | addr = Address.new(node.codeOrigin, tmpPtr, Immediate.new(node.codeOrigin, 0)) | |
355 | newList << Instruction.new(node.codeOrigin, "leap", [node.operands[1], tmpPtr]) | |
356 | newList << Instruction.new(node.codeOrigin, "loadi", [addr, tmpVal]) | |
357 | newList << Instruction.new(node.codeOrigin, op, [node.operands[0], tmpVal]) | |
358 | newList << Instruction.new(node.codeOrigin, "storei", [tmpVal, addr]) | |
359 | newList << Instruction.new(node.codeOrigin, "bs", [tmpVal, node.operands[2]]) | |
360 | end | |
361 | elsif bc == "nz" | |
362 | raise "Invalid operands number (#{node.operands.size})" unless node.operands.size == 3 | |
363 | newList << Instruction.new(node.codeOrigin, op, node.operands[0..1]) | |
364 | newList << Instruction.new(node.codeOrigin, "btinz", node.operands[1..2]) | |
365 | else | |
366 | newList << node | |
367 | end | |
368 | when "bmulio", "bmulpo" | |
369 | raise "Invalid operands number (#{node.operands.size})" unless node.operands.size == 3 | |
370 | tmp1 = Tmp.new(node.codeOrigin, :gpr) | |
371 | tmp2 = Tmp.new(node.codeOrigin, :gpr) | |
372 | newList << Instruction.new(node.codeOrigin, node.opcode, [tmp1, tmp2].concat(node.operands)) | |
373 | else | |
374 | newList << node | |
375 | end | |
376 | else | |
377 | newList << node | |
378 | end | |
379 | } | |
380 | newList | |
381 | end | |
382 | ||
383 | ||
384 | # | |
385 | # Lowering of double accesses for SH4. For example: | |
386 | # | |
387 | # loadd [foo, bar, 8], baz | |
388 | # | |
389 | # becomes: | |
390 | # | |
391 | # leap [foo, bar, 8], tmp | |
392 | # loaddReversedAndIncrementAddress [tmp], baz | |
393 | # | |
394 | ||
395 | def sh4LowerDoubleAccesses(list) | |
396 | newList = [] | |
397 | list.each { | |
398 | | node | | |
399 | if node.is_a? Instruction | |
400 | case node.opcode | |
401 | when "loadd" | |
402 | tmp = Tmp.new(codeOrigin, :gpr) | |
403 | addr = Address.new(codeOrigin, tmp, Immediate.new(codeOrigin, 0)) | |
404 | newList << Instruction.new(codeOrigin, "leap", [node.operands[0], tmp]) | |
405 | newList << Instruction.new(node.codeOrigin, "loaddReversedAndIncrementAddress", [addr, node.operands[1]], node.annotation) | |
406 | when "stored" | |
407 | tmp = Tmp.new(codeOrigin, :gpr) | |
408 | addr = Address.new(codeOrigin, tmp, Immediate.new(codeOrigin, 0)) | |
409 | newList << Instruction.new(codeOrigin, "leap", [node.operands[1].withOffset(8), tmp]) | |
410 | newList << Instruction.new(node.codeOrigin, "storedReversedAndDecrementAddress", [node.operands[0], addr], node.annotation) | |
411 | else | |
412 | newList << node | |
413 | end | |
414 | else | |
415 | newList << node | |
416 | end | |
417 | } | |
418 | newList | |
419 | end | |
420 | ||
421 | ||
422 | # | |
423 | # Lowering of double specials for SH4. | |
424 | # | |
425 | ||
426 | def sh4LowerDoubleSpecials(list) | |
427 | newList = [] | |
428 | list.each { | |
429 | | node | | |
430 | if node.is_a? Instruction | |
431 | case node.opcode | |
432 | when "bdltun", "bdgtun" | |
433 | # Handle specific floating point unordered opcodes. | |
434 | newList << Instruction.new(codeOrigin, "bdnan", [node.operands[0], node.operands[2]]) | |
435 | newList << Instruction.new(codeOrigin, "bdnan", [node.operands[1], node.operands[2]]) | |
436 | newList << Instruction.new(codeOrigin, node.opcode[0..-3], node.operands) | |
437 | when "bdnequn", "bdgtequn", "bdltequn" | |
438 | newList << Instruction.new(codeOrigin, node.opcode[0..-3], node.operands) | |
439 | when "bdneq", "bdgteq", "bdlteq" | |
440 | # Handle specific floating point ordered opcodes. | |
441 | outlabel = LocalLabel.unique("out_#{node.opcode}") | |
442 | outref = LocalLabelReference.new(codeOrigin, outlabel) | |
443 | newList << Instruction.new(codeOrigin, "bdnan", [node.operands[0], outref]) | |
444 | newList << Instruction.new(codeOrigin, "bdnan", [node.operands[1], outref]) | |
445 | newList << Instruction.new(codeOrigin, node.opcode, node.operands) | |
446 | newList << outlabel | |
447 | else | |
448 | newList << node | |
449 | end | |
450 | else | |
451 | newList << node | |
452 | end | |
453 | } | |
454 | newList | |
455 | end | |
456 | ||
457 | ||
458 | # | |
459 | # Lowering of misplaced labels for SH4. | |
460 | # | |
461 | ||
462 | def sh4LowerMisplacedLabels(list) | |
463 | newList = [] | |
464 | list.each { | |
465 | | node | | |
466 | if node.is_a? Instruction | |
467 | operands = node.operands | |
468 | newOperands = [] | |
469 | operands.each { | |
470 | | operand | | |
471 | if operand.is_a? LabelReference and node.opcode != "mova" | |
472 | tmp = Tmp.new(operand.codeOrigin, :gpr) | |
473 | newList << Instruction.new(operand.codeOrigin, "move", [operand, tmp]) | |
474 | newOperands << tmp | |
475 | else | |
476 | newOperands << operand | |
477 | end | |
478 | } | |
479 | newList << Instruction.new(node.codeOrigin, node.opcode, newOperands, node.annotation) | |
480 | else | |
481 | newList << node | |
482 | end | |
483 | } | |
484 | newList | |
485 | end | |
486 | ||
487 | ||
488 | # | |
489 | # Lowering of misplaced special registers for SH4. For example: | |
490 | # | |
491 | # storep pr, foo | |
492 | # | |
493 | # becomes: | |
494 | # | |
495 | # stspr tmp | |
496 | # storep tmp, foo | |
497 | # | |
498 | ||
499 | def sh4LowerMisplacedSpecialRegisters(list) | |
500 | newList = [] | |
501 | list.each { | |
502 | | node | | |
503 | if node.is_a? Instruction | |
504 | case node.opcode | |
505 | when "move" | |
506 | if node.operands[0].is_a? RegisterID and node.operands[0].sh4Operand == "pr" | |
507 | newList << Instruction.new(codeOrigin, "stspr", [node.operands[1]]) | |
508 | elsif node.operands[1].is_a? RegisterID and node.operands[1].sh4Operand == "pr" | |
509 | newList << Instruction.new(codeOrigin, "ldspr", [node.operands[0]]) | |
510 | else | |
511 | newList << node | |
512 | end | |
513 | when "loadi", "loadis", "loadp" | |
514 | if node.operands[1].is_a? RegisterID and node.operands[1].sh4Operand == "pr" | |
515 | tmp = Tmp.new(codeOrigin, :gpr) | |
516 | newList << Instruction.new(codeOrigin, node.opcode, [node.operands[0], tmp]) | |
517 | newList << Instruction.new(codeOrigin, "ldspr", [tmp]) | |
518 | else | |
519 | newList << node | |
520 | end | |
521 | when "storei", "storep" | |
522 | if node.operands[0].is_a? RegisterID and node.operands[0].sh4Operand == "pr" | |
523 | tmp = Tmp.new(codeOrigin, :gpr) | |
524 | newList << Instruction.new(codeOrigin, "stspr", [tmp]) | |
525 | newList << Instruction.new(codeOrigin, node.opcode, [tmp, node.operands[1]]) | |
526 | else | |
527 | newList << node | |
528 | end | |
529 | else | |
530 | newList << node | |
531 | end | |
532 | else | |
533 | newList << node | |
534 | end | |
535 | } | |
536 | newList | |
537 | end | |
538 | ||
539 | ||
540 | # | |
541 | # Group immediate values outside -128..127 range into constant pools for SH4. | |
542 | # These constant pools will be placed behind non-return opcodes jmp and ret, for example: | |
543 | # | |
544 | # move 1024, foo | |
545 | # ... | |
546 | # ret | |
547 | # | |
548 | # becomes: | |
549 | # | |
550 | # move [label], foo | |
551 | # ... | |
552 | # ret | |
553 | # label: 1024 | |
554 | # | |
555 | ||
556 | def sh4LowerConstPool(list) | |
557 | newList = [] | |
558 | currentPool16 = [] | |
559 | currentPool32 = [] | |
560 | list.each { | |
561 | | node | | |
562 | if node.is_a? Instruction | |
563 | case node.opcode | |
564 | when "jmp", "ret", "flushcp" | |
565 | if node.opcode == "flushcp" | |
566 | outlabel = LocalLabel.unique("flushcp") | |
567 | newList << Instruction.new(codeOrigin, "jmp", [LocalLabelReference.new(codeOrigin, outlabel)]) | |
568 | else | |
569 | newList << node | |
570 | end | |
571 | if not currentPool16.empty? | |
572 | newList << ConstPool.new(codeOrigin, currentPool16, 16) | |
573 | currentPool16 = [] | |
574 | end | |
575 | if not currentPool32.empty? | |
576 | newList << ConstPool.new(codeOrigin, currentPool32, 32) | |
577 | currentPool32 = [] | |
578 | end | |
579 | if node.opcode == "flushcp" | |
580 | newList << outlabel | |
581 | end | |
582 | when "move" | |
583 | if node.operands[0].is_a? Immediate and not (-128..127).include? node.operands[0].value | |
584 | poolEntry = nil | |
585 | if (-32768..32767).include? node.operands[0].value | |
586 | currentPool16.each { |e| | |
587 | if e.value == node.operands[0].value | |
588 | poolEntry = e | |
589 | end | |
590 | } | |
591 | if !poolEntry | |
592 | poolEntry = ConstPoolEntry.new(codeOrigin, node.operands[0].value, 16) | |
593 | currentPool16 << poolEntry | |
594 | end | |
595 | else | |
596 | currentPool32.each { |e| | |
597 | if e.value == node.operands[0].value | |
598 | poolEntry = e | |
599 | end | |
600 | } | |
601 | if !poolEntry | |
602 | poolEntry = ConstPoolEntry.new(codeOrigin, node.operands[0].value, 32) | |
603 | currentPool32 << poolEntry | |
604 | end | |
605 | end | |
606 | newList << Instruction.new(codeOrigin, "move", [poolEntry, node.operands[1]]) | |
607 | elsif node.operands[0].is_a? LabelReference | |
608 | poolEntry = nil | |
609 | currentPool32.each { |e| | |
610 | if e.value == node.operands[0].asmLabel | |
611 | poolEntry = e | |
612 | end | |
613 | } | |
614 | if !poolEntry | |
615 | poolEntry = ConstPoolEntry.new(codeOrigin, node.operands[0].asmLabel, 32) | |
616 | currentPool32 << poolEntry | |
617 | end | |
618 | newList << Instruction.new(codeOrigin, "move", [poolEntry, node.operands[1]]) | |
619 | elsif node.operands[0].is_a? SubImmediates | |
620 | poolEntry = ConstPoolEntry.new(codeOrigin, node.operands[0].sh4Operand, 32) | |
621 | currentPool32 << poolEntry | |
622 | newList << Instruction.new(codeOrigin, "move", [poolEntry, node.operands[1]]) | |
623 | else | |
624 | newList << node | |
625 | end | |
626 | else | |
627 | newList << node | |
628 | end | |
629 | else | |
630 | newList << node | |
631 | end | |
632 | } | |
633 | if not currentPool16.empty? | |
634 | newList << ConstPool.new(codeOrigin, currentPool16, 16) | |
635 | end | |
636 | if not currentPool32.empty? | |
637 | newList << ConstPool.new(codeOrigin, currentPool32, 32) | |
638 | end | |
639 | newList | |
640 | end | |
641 | ||
642 | ||
643 | # | |
644 | # Lowering of argument setup for SH4. | |
645 | # This phase avoids argument register trampling. For example, if a0 == t4: | |
646 | # | |
647 | # setargs t1, t4 | |
648 | # | |
649 | # becomes: | |
650 | # | |
651 | # move t4, a1 | |
652 | # move t1, a0 | |
653 | # | |
654 | ||
655 | def sh4LowerArgumentSetup(list) | |
656 | a0 = RegisterID.forName(codeOrigin, "a0") | |
657 | a1 = RegisterID.forName(codeOrigin, "a1") | |
658 | a2 = RegisterID.forName(codeOrigin, "a2") | |
659 | a3 = RegisterID.forName(codeOrigin, "a3") | |
660 | newList = [] | |
661 | list.each { | |
662 | | node | | |
663 | if node.is_a? Instruction | |
664 | case node.opcode | |
665 | when "setargs" | |
666 | if node.operands.size == 2 | |
667 | if node.operands[1].sh4Operand != a0.sh4Operand | |
668 | newList << Instruction.new(codeOrigin, "move", [node.operands[0], a0]) | |
669 | newList << Instruction.new(codeOrigin, "move", [node.operands[1], a1]) | |
670 | elsif node.operands[0].sh4Operand != a1.sh4Operand | |
671 | newList << Instruction.new(codeOrigin, "move", [node.operands[1], a1]) | |
672 | newList << Instruction.new(codeOrigin, "move", [node.operands[0], a0]) | |
673 | else | |
674 | # As (operands[0] == a1) and (operands[1] == a0), we just need to swap a0 and a1. | |
675 | newList << Instruction.new(codeOrigin, "xori", [a0, a1]) | |
676 | newList << Instruction.new(codeOrigin, "xori", [a1, a0]) | |
677 | newList << Instruction.new(codeOrigin, "xori", [a0, a1]) | |
678 | end | |
679 | elsif node.operands.size == 4 | |
680 | # FIXME: We just raise an error if something is likely to go wrong for now. | |
681 | # It would be better to implement a recovering algorithm. | |
682 | if (node.operands[0].sh4Operand == a1.sh4Operand) or | |
683 | (node.operands[0].sh4Operand == a2.sh4Operand) or | |
684 | (node.operands[0].sh4Operand == a3.sh4Operand) or | |
685 | (node.operands[1].sh4Operand == a0.sh4Operand) or | |
686 | (node.operands[1].sh4Operand == a2.sh4Operand) or | |
687 | (node.operands[1].sh4Operand == a3.sh4Operand) or | |
688 | (node.operands[2].sh4Operand == a0.sh4Operand) or | |
689 | (node.operands[2].sh4Operand == a1.sh4Operand) or | |
690 | (node.operands[2].sh4Operand == a3.sh4Operand) or | |
691 | (node.operands[3].sh4Operand == a0.sh4Operand) or | |
692 | (node.operands[3].sh4Operand == a1.sh4Operand) or | |
693 | (node.operands[3].sh4Operand == a2.sh4Operand) | |
694 | raise "Potential argument register trampling detected." | |
695 | end | |
696 | ||
697 | newList << Instruction.new(codeOrigin, "move", [node.operands[0], a0]) | |
698 | newList << Instruction.new(codeOrigin, "move", [node.operands[1], a1]) | |
699 | newList << Instruction.new(codeOrigin, "move", [node.operands[2], a2]) | |
700 | newList << Instruction.new(codeOrigin, "move", [node.operands[3], a3]) | |
701 | else | |
702 | raise "Invalid operands number (#{node.operands.size}) for setargs" | |
703 | end | |
704 | else | |
705 | newList << node | |
706 | end | |
707 | else | |
708 | newList << node | |
709 | end | |
710 | } | |
711 | newList | |
712 | end | |
713 | ||
714 | ||
715 | class Sequence | |
716 | def getModifiedListSH4 | |
717 | result = @list | |
718 | ||
719 | # Verify that we will only see instructions and labels. | |
720 | result.each { | |
721 | | node | | |
722 | unless node.is_a? Instruction or | |
723 | node.is_a? Label or | |
724 | node.is_a? LocalLabel or | |
725 | node.is_a? Skip | |
726 | raise "Unexpected #{node.inspect} at #{node.codeOrigin}" | |
727 | end | |
728 | } | |
729 | ||
730 | result = sh4LowerShiftOps(result) | |
731 | result = sh4LowerSimpleBranchOps(result) | |
732 | result = riscLowerMalformedAddresses(result) { | |
733 | | node, address | | |
734 | if address.is_a? Address | |
735 | case node.opcode | |
736 | when "btbz", "btbnz", "cbeq", "bbeq", "bbneq", "bbb", "loadb", "storeb" | |
737 | (0..15).include? address.offset.value and | |
738 | ((node.operands[0].is_a? RegisterID and node.operands[0].sh4Operand == "r0") or | |
739 | (node.operands[1].is_a? RegisterID and node.operands[1].sh4Operand == "r0")) | |
740 | when "loadh" | |
741 | (0..30).include? address.offset.value and | |
742 | ((node.operands[0].is_a? RegisterID and node.operands[0].sh4Operand == "r0") or | |
743 | (node.operands[1].is_a? RegisterID and node.operands[1].sh4Operand == "r0")) | |
744 | else | |
745 | (0..60).include? address.offset.value | |
746 | end | |
747 | else | |
748 | false | |
749 | end | |
750 | } | |
751 | result = sh4LowerDoubleAccesses(result) | |
752 | result = sh4LowerDoubleSpecials(result) | |
753 | result = riscLowerMisplacedImmediates(result, ["storeb", "storei", "storep", "muli", "mulp", "andi", "ori", "xori", | |
754 | "cbeq", "cieq", "cpeq", "cineq", "cpneq", "cib", "baddio", "bsubio", "bmulio", "baddis", | |
755 | "bbeq", "bbneq", "bbb", "bieq", "bpeq", "bineq", "bpneq", "bia", "bpa", "biaeq", "bpaeq", "bib", "bpb", | |
756 | "bigteq", "bpgteq", "bilt", "bplt", "bigt", "bpgt", "bilteq", "bplteq", "btiz", "btpz", "btinz", "btpnz", "btbz", "btbnz"]) | |
757 | result = riscLowerMalformedImmediates(result, -128..127) | |
758 | result = riscLowerMisplacedAddresses(result) | |
759 | result = sh4LowerMisplacedLabels(result) | |
760 | result = sh4LowerMisplacedSpecialRegisters(result) | |
761 | ||
762 | result = assignRegistersToTemporaries(result, :gpr, SH4_TMP_GPRS) | |
763 | result = assignRegistersToTemporaries(result, :gpr, SH4_TMP_FPRS) | |
764 | ||
765 | result = sh4LowerConstPool(result) | |
766 | result = sh4LowerArgumentSetup(result) | |
767 | ||
768 | return result | |
769 | end | |
770 | end | |
771 | ||
772 | def sh4Operands(operands) | |
773 | operands.map{|v| v.sh4Operand}.join(", ") | |
774 | end | |
775 | ||
776 | def emitSH4Branch(sh4opcode, operand) | |
777 | raise "Invalid operand #{operand}" unless operand.is_a? RegisterID or operand.is_a? SpecialRegister | |
778 | $asm.puts "#{sh4opcode} @#{operand.sh4Operand}" | |
779 | $asm.puts "nop" | |
780 | end | |
781 | ||
782 | def emitSH4ShiftImm(val, operand, direction) | |
783 | tmp = val | |
784 | while tmp > 0 | |
785 | if tmp >= 16 | |
786 | $asm.puts "shl#{direction}16 #{operand.sh4Operand}" | |
787 | tmp -= 16 | |
788 | elsif tmp >= 8 | |
789 | $asm.puts "shl#{direction}8 #{operand.sh4Operand}" | |
790 | tmp -= 8 | |
791 | elsif tmp >= 2 | |
792 | $asm.puts "shl#{direction}2 #{operand.sh4Operand}" | |
793 | tmp -= 2 | |
794 | else | |
795 | $asm.puts "shl#{direction} #{operand.sh4Operand}" | |
796 | tmp -= 1 | |
797 | end | |
798 | end | |
799 | end | |
800 | ||
801 | def emitSH4BranchIfT(dest, neg) | |
802 | outlabel = LocalLabel.unique("branchIfT") | |
803 | sh4opcode = neg ? "bt" : "bf" | |
804 | $asm.puts "#{sh4opcode} #{LocalLabelReference.new(codeOrigin, outlabel).asmLabel}" | |
805 | if dest.is_a? LocalLabelReference | |
806 | $asm.puts "bra #{dest.asmLabel}" | |
807 | $asm.puts "nop" | |
808 | else | |
809 | emitSH4Branch("jmp", dest) | |
810 | end | |
811 | outlabel.lower("SH4") | |
812 | end | |
813 | ||
814 | def emitSH4IntCompare(cmpOpcode, operands) | |
815 | $asm.puts "cmp/#{cmpOpcode} #{sh4Operands([operands[1], operands[0]])}" | |
816 | end | |
817 | ||
818 | def emitSH4CondBranch(cmpOpcode, neg, operands) | |
819 | emitSH4IntCompare(cmpOpcode, operands) | |
820 | emitSH4BranchIfT(operands[2], neg) | |
821 | end | |
822 | ||
823 | def emitSH4CompareSet(cmpOpcode, neg, operands) | |
824 | emitSH4IntCompare(cmpOpcode, operands) | |
825 | if !neg | |
826 | $asm.puts "movt #{operands[2].sh4Operand}" | |
827 | else | |
828 | outlabel = LocalLabel.unique("compareSet") | |
829 | $asm.puts "mov #0, #{operands[2].sh4Operand}" | |
830 | $asm.puts "bt #{LocalLabelReference.new(codeOrigin, outlabel).asmLabel}" | |
831 | $asm.puts "mov #1, #{operands[2].sh4Operand}" | |
832 | outlabel.lower("SH4") | |
833 | end | |
834 | end | |
835 | ||
836 | def emitSH4BranchIfNaN(operands) | |
837 | raise "Invalid operands number (#{operands.size})" unless operands.size == 2 | |
838 | $asm.puts "fcmp/eq #{sh4Operands([operands[0], operands[0]])}" | |
839 | $asm.puts "bf #{operands[1].asmLabel}" | |
840 | end | |
841 | ||
842 | def emitSH4DoubleCondBranch(cmpOpcode, neg, operands) | |
843 | if cmpOpcode == "lt" | |
844 | $asm.puts "fcmp/gt #{sh4Operands([operands[0], operands[1]])}" | |
845 | else | |
846 | $asm.puts "fcmp/#{cmpOpcode} #{sh4Operands([operands[1], operands[0]])}" | |
847 | end | |
848 | emitSH4BranchIfT(operands[2], neg) | |
849 | end | |
850 | ||
851 | class Instruction | |
852 | def lowerSH4 | |
853 | $asm.comment codeOriginString | |
854 | case opcode | |
855 | when "addi", "addp" | |
856 | if operands.size == 3 | |
857 | if operands[0].sh4Operand == operands[2].sh4Operand | |
858 | $asm.puts "add #{sh4Operands([operands[1], operands[2]])}" | |
859 | elsif operands[1].sh4Operand == operands[2].sh4Operand | |
860 | $asm.puts "add #{sh4Operands([operands[0], operands[2]])}" | |
861 | else | |
862 | $asm.puts "mov #{sh4Operands([operands[0], operands[2]])}" | |
863 | $asm.puts "add #{sh4Operands([operands[1], operands[2]])}" | |
864 | end | |
865 | else | |
866 | $asm.puts "add #{sh4Operands(operands)}" | |
867 | end | |
868 | when "subi", "subp" | |
869 | if operands.size == 3 | |
870 | if operands[1].is_a? Immediate | |
871 | $asm.puts "mov #{sh4Operands([Immediate.new(codeOrigin, -1 * operands[1].value), operands[2]])}" | |
872 | $asm.puts "add #{sh4Operands([operands[0], operands[2]])}" | |
873 | elsif operands[1].sh4Operand == operands[2].sh4Operand | |
874 | $asm.puts "neg #{sh4Operands([operands[2], operands[2]])}" | |
875 | $asm.puts "add #{sh4Operands([operands[0], operands[2]])}" | |
876 | else | |
877 | $asm.puts "mov #{sh4Operands([operands[0], operands[2]])}" | |
878 | $asm.puts "sub #{sh4Operands([operands[1], operands[2]])}" | |
879 | end | |
880 | else | |
881 | if operands[0].is_a? Immediate | |
882 | $asm.puts "add #{sh4Operands([Immediate.new(codeOrigin, -1 * operands[0].value), operands[1]])}" | |
883 | else | |
884 | $asm.puts "sub #{sh4Operands(operands)}" | |
885 | end | |
886 | end | |
887 | when "muli", "mulp" | |
888 | $asm.puts "mul.l #{sh4Operands(operands[0..1])}" | |
889 | $asm.puts "sts macl, #{operands[-1].sh4Operand}" | |
890 | when "negi", "negp" | |
891 | if operands.size == 2 | |
892 | $asm.puts "neg #{sh4Operands(operands)}" | |
893 | else | |
894 | $asm.puts "neg #{sh4Operands([operands[0], operands[0]])}" | |
895 | end | |
896 | when "andi", "andp", "ori", "orp", "xori", "xorp" | |
897 | raise "#{opcode} with #{operands.size} operands is not handled yet" unless operands.size == 2 | |
898 | sh4opcode = opcode[0..-2] | |
899 | $asm.puts "#{sh4opcode} #{sh4Operands(operands)}" | |
900 | when "shllx", "shlrx" | |
901 | raise "Unhandled parameters for opcode #{opcode}" unless operands[0].is_a? Immediate | |
902 | if operands[0].value == 1 | |
903 | $asm.puts "shl#{opcode[3, 1]} #{operands[1].sh4Operand}" | |
904 | else | |
905 | $asm.puts "shl#{opcode[3, 1]}#{operands[0].value} #{operands[1].sh4Operand}" | |
906 | end | |
907 | when "shalx", "sharx" | |
908 | raise "Unhandled parameters for opcode #{opcode}" unless operands[0].is_a? Immediate and operands[0].value == 1 | |
909 | $asm.puts "sha#{opcode[3, 1]} #{operands[1].sh4Operand}" | |
910 | when "shld", "shad" | |
911 | $asm.puts "#{opcode} #{sh4Operands(operands)}" | |
912 | when "loaddReversedAndIncrementAddress" | |
913 | # As we are little endian, we don't use "fmov @Rm, DRn" here. | |
914 | $asm.puts "fmov.s #{operands[0].sh4OperandPostInc}, #{operands[1].sh4SingleLo}" | |
915 | $asm.puts "fmov.s #{operands[0].sh4OperandPostInc}, #{operands[1].sh4SingleHi}" | |
916 | when "storedReversedAndDecrementAddress" | |
917 | # As we are little endian, we don't use "fmov DRm, @Rn" here. | |
918 | $asm.puts "fmov.s #{operands[0].sh4SingleHi}, #{operands[1].sh4OperandPreDec}" | |
919 | $asm.puts "fmov.s #{operands[0].sh4SingleLo}, #{operands[1].sh4OperandPreDec}" | |
920 | when "ci2d" | |
921 | $asm.puts "lds #{operands[0].sh4Operand}, fpul" | |
922 | $asm.puts "float fpul, #{operands[1].sh4Operand}" | |
923 | when "fii2d" | |
924 | $asm.puts "lds #{operands[0].sh4Operand}, fpul" | |
925 | $asm.puts "fsts fpul, #{operands[2].sh4SingleLo}" | |
926 | $asm.puts "lds #{operands[1].sh4Operand}, fpul" | |
927 | $asm.puts "fsts fpul, #{operands[2].sh4SingleHi}" | |
928 | when "fd2ii" | |
929 | $asm.puts "flds #{operands[0].sh4SingleLo}, fpul" | |
930 | $asm.puts "sts fpul, #{operands[1].sh4Operand}" | |
931 | $asm.puts "flds #{operands[0].sh4SingleHi}, fpul" | |
932 | $asm.puts "sts fpul, #{operands[2].sh4Operand}" | |
933 | when "addd", "subd", "muld", "divd" | |
934 | sh4opcode = opcode[0..-2] | |
935 | $asm.puts "f#{sh4opcode} #{sh4Operands(operands)}" | |
936 | when "bcd2i" | |
937 | $asm.puts "ftrc #{operands[0].sh4Operand}, fpul" | |
938 | $asm.puts "sts fpul, #{operands[1].sh4Operand}" | |
939 | $asm.puts "float fpul, #{SH4_TMP_FPRS[0].sh4Operand}" | |
940 | $asm.puts "fcmp/eq #{sh4Operands([operands[0], SH4_TMP_FPRS[0]])}" | |
941 | $asm.puts "bf #{operands[2].asmLabel}" | |
942 | $asm.puts "tst #{sh4Operands([operands[1], operands[1]])}" | |
943 | $asm.puts "bt #{operands[2].asmLabel}" | |
944 | when "bdnan" | |
945 | emitSH4BranchIfNaN(operands) | |
946 | when "bdneq" | |
947 | emitSH4DoubleCondBranch("eq", true, operands) | |
948 | when "bdgteq" | |
949 | emitSH4DoubleCondBranch("lt", true, operands) | |
950 | when "bdlt" | |
951 | emitSH4DoubleCondBranch("lt", false, operands) | |
952 | when "bdlteq" | |
953 | emitSH4DoubleCondBranch("gt", true, operands) | |
954 | when "bdgt" | |
955 | emitSH4DoubleCondBranch("gt", false, operands) | |
956 | when "baddio", "baddpo", "bsubio", "bsubpo" | |
957 | raise "#{opcode} with #{operands.size} operands is not handled yet" unless operands.size == 3 | |
958 | $asm.puts "#{opcode[1, 3]}v #{sh4Operands([operands[0], operands[1]])}" | |
959 | $asm.puts "bt #{operands[2].asmLabel}" | |
960 | when "bmulio", "bmulpo" | |
961 | raise "Invalid operands number (#{operands.size})" unless operands.size == 5 | |
962 | $asm.puts "dmuls.l #{sh4Operands([operands[2], operands[3]])}" | |
963 | $asm.puts "sts macl, #{operands[3].sh4Operand}" | |
964 | $asm.puts "cmp/pz #{operands[3].sh4Operand}" | |
965 | $asm.puts "movt #{operands[1].sh4Operand}" | |
966 | $asm.puts "add #-1, #{operands[1].sh4Operand}" | |
967 | $asm.puts "sts mach, #{operands[0].sh4Operand}" | |
968 | $asm.puts "cmp/eq #{sh4Operands([operands[0], operands[1]])}" | |
969 | $asm.puts "bf #{operands[4].asmLabel}" | |
970 | when "btiz", "btpz", "btbz", "btinz", "btpnz", "btbnz" | |
971 | if operands.size == 3 | |
972 | $asm.puts "tst #{sh4Operands([operands[0], operands[1]])}" | |
973 | else | |
974 | if operands[0].sh4Operand == "r0" | |
975 | $asm.puts "cmp/eq #0, r0" | |
976 | else | |
977 | $asm.puts "tst #{sh4Operands([operands[0], operands[0]])}" | |
978 | end | |
979 | end | |
980 | emitSH4BranchIfT(operands[-1], (opcode[-2, 2] == "nz")) | |
981 | when "cieq", "cpeq", "cbeq" | |
982 | emitSH4CompareSet("eq", false, operands) | |
983 | when "cineq", "cpneq", "cbneq" | |
984 | emitSH4CompareSet("eq", true, operands) | |
985 | when "cib", "cpb", "cbb" | |
986 | emitSH4CompareSet("hs", true, operands) | |
987 | when "bieq", "bpeq", "bbeq" | |
988 | emitSH4CondBranch("eq", false, operands) | |
989 | when "bineq", "bpneq", "bbneq" | |
990 | emitSH4CondBranch("eq", true, operands) | |
991 | when "bib", "bpb", "bbb" | |
992 | emitSH4CondBranch("hs", true, operands) | |
993 | when "bia", "bpa", "bba" | |
994 | emitSH4CondBranch("hi", false, operands) | |
995 | when "bibeq", "bpbeq" | |
996 | emitSH4CondBranch("hi", true, operands) | |
997 | when "biaeq", "bpaeq" | |
998 | emitSH4CondBranch("hs", false, operands) | |
999 | when "bigteq", "bpgteq", "bbgteq" | |
1000 | emitSH4CondBranch("ge", false, operands) | |
1001 | when "bilt", "bplt", "bblt" | |
1002 | emitSH4CondBranch("ge", true, operands) | |
1003 | when "bigt", "bpgt", "bbgt" | |
1004 | emitSH4CondBranch("gt", false, operands) | |
1005 | when "bilteq", "bplteq", "bblteq" | |
1006 | emitSH4CondBranch("gt", true, operands) | |
1007 | when "bs" | |
1008 | $asm.puts "cmp/pz #{operands[0].sh4Operand}" | |
1009 | $asm.puts "bf #{operands[1].asmLabel}" | |
1010 | when "call" | |
1011 | if operands[0].is_a? LocalLabelReference | |
1012 | $asm.puts "bsr #{operands[0].asmLabel}" | |
1013 | $asm.puts "nop" | |
1014 | elsif operands[0].is_a? RegisterID or operands[0].is_a? SpecialRegister | |
1015 | emitSH4Branch("jsr", operands[0]) | |
1016 | else | |
1017 | raise "Unhandled parameters for opcode #{opcode} at #{codeOriginString}" | |
1018 | end | |
1019 | when "jmp" | |
1020 | if operands[0].is_a? LocalLabelReference | |
1021 | $asm.puts "bra #{operands[0].asmLabel}" | |
1022 | $asm.puts "nop" | |
1023 | elsif operands[0].is_a? RegisterID or operands[0].is_a? SpecialRegister | |
1024 | emitSH4Branch("jmp", operands[0]) | |
1025 | else | |
1026 | raise "Unhandled parameters for opcode #{opcode} at #{codeOriginString}" | |
1027 | end | |
1028 | when "ret" | |
1029 | $asm.puts "rts" | |
1030 | $asm.puts "nop" | |
1031 | when "loadb" | |
1032 | $asm.puts "mov.b #{sh4Operands(operands)}" | |
1033 | $asm.puts "extu.b #{sh4Operands([operands[1], operands[1]])}" | |
1034 | when "storeb" | |
1035 | $asm.puts "mov.b #{sh4Operands(operands)}" | |
1036 | when "loadh" | |
1037 | $asm.puts "mov.w #{sh4Operands(operands)}" | |
1038 | $asm.puts "extu.w #{sh4Operands([operands[1], operands[1]])}" | |
1039 | when "loadi", "loadis", "loadp", "storei", "storep" | |
1040 | $asm.puts "mov.l #{sh4Operands(operands)}" | |
1041 | when "alignformova" | |
1042 | $asm.puts ".balign 4" # As balign directive is in a code section, fill value is 'nop' instruction. | |
1043 | when "mova" | |
1044 | $asm.puts "mova #{sh4Operands(operands)}" | |
1045 | when "move" | |
1046 | if operands[0].is_a? ConstPoolEntry | |
1047 | if operands[0].size == 16 | |
1048 | $asm.puts "mov.w #{operands[0].labelref.asmLabel}, #{operands[1].sh4Operand}" | |
1049 | else | |
1050 | $asm.puts "mov.l #{operands[0].labelref.asmLabel}, #{operands[1].sh4Operand}" | |
1051 | end | |
1052 | elsif operands[0].sh4Operand != operands[1].sh4Operand | |
1053 | $asm.puts "mov #{sh4Operands(operands)}" | |
1054 | end | |
1055 | when "leap" | |
1056 | if operands[0].is_a? BaseIndex | |
1057 | biop = operands[0] | |
1058 | $asm.puts "mov #{sh4Operands([biop.index, operands[1]])}" | |
1059 | if biop.scaleShift > 0 | |
1060 | emitSH4ShiftImm(biop.scaleShift, operands[1], "l") | |
1061 | end | |
1062 | $asm.puts "add #{sh4Operands([biop.base, operands[1]])}" | |
1063 | if biop.offset.value != 0 | |
1064 | $asm.puts "add #{sh4Operands([biop.offset, operands[1]])}" | |
1065 | end | |
1066 | elsif operands[0].is_a? Address | |
1067 | if operands[0].base != operands[1] | |
1068 | $asm.puts "mov #{sh4Operands([operands[0].base, operands[1]])}" | |
1069 | end | |
1070 | if operands[0].offset.value != 0 | |
1071 | $asm.puts "add #{sh4Operands([operands[0].offset, operands[1]])}" | |
1072 | end | |
1073 | else | |
1074 | raise "Unhandled parameters for opcode #{opcode} at #{codeOriginString}" | |
1075 | end | |
1076 | when "ldspr" | |
1077 | $asm.puts "lds #{sh4Operands(operands)}, pr" | |
1078 | when "stspr" | |
1079 | $asm.puts "sts pr, #{sh4Operands(operands)}" | |
1080 | when "memfence" | |
1081 | $asm.puts "synco" | |
1082 | when "pop" | |
1083 | if operands[0].sh4Operand == "pr" | |
1084 | $asm.puts "lds.l @r15+, #{sh4Operands(operands)}" | |
1085 | else | |
1086 | $asm.puts "mov.l @r15+, #{sh4Operands(operands)}" | |
1087 | end | |
1088 | when "push" | |
1089 | if operands[0].sh4Operand == "pr" | |
1090 | $asm.puts "sts.l #{sh4Operands(operands)}, @-r15" | |
1091 | else | |
1092 | $asm.puts "mov.l #{sh4Operands(operands)}, @-r15" | |
1093 | end | |
1094 | when "break" | |
1095 | # This special opcode always generates an illegal instruction exception. | |
1096 | $asm.puts ".word 0xfffd" | |
1097 | else | |
1098 | lowerDefault | |
1099 | end | |
1100 | end | |
1101 | end | |
1102 |