02636d1ac0d7fc1b3d08fa92198ea62ebbda7359
[WebKit-https.git] / Source / JavaScriptCore / offlineasm / sh4.rb
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 "a0"
61             "r4"
62         when "a1"
63             "r5"
64         when "t0"
65             "r0"
66         when "t1"
67             "r1"
68         when "t2"
69             "r2"
70         when "t3"
71             "r10"
72         when "t4"
73             "r6"
74         when "cfr"
75             "r14"
76         when "sp"
77             "r15"
78         when "lr"
79             "pr"
80         else
81             raise "Bad register #{name} for SH4 at #{codeOriginString}"
82         end
83     end
84 end
85
86 class FPRegisterID
87     def sh4Operand
88         case name
89         when "ft0", "fr"
90             "dr0"
91         when "ft1"
92             "dr2"
93         when "ft2"
94             "dr4"
95         when "ft3"
96             "dr6"
97         when "ft4"
98             "dr8"
99         when "fa0"
100             "dr12"
101         else
102             raise "Bad register #{name} for SH4 at #{codeOriginString}"
103         end
104     end
105 end
106
107 class Immediate
108     def sh4Operand
109         raise "Invalid immediate #{value} at #{codeOriginString}" if value < -128 or value > 127
110         "##{value}"
111     end
112 end
113
114 class Address
115     def sh4Operand
116         raise "Bad offset #{offset.value} at #{codeOriginString}" if offset.value < 0 or offset.value > 60
117         if offset.value == 0
118             "@#{base.sh4Operand}"
119         else
120             "@(#{offset.value}, #{base.sh4Operand})"
121         end
122     end
123
124     def sh4OperandPostInc
125         raise "Bad offset #{offset.value} for post inc at #{codeOriginString}" unless offset.value == 0
126         "@#{base.sh4Operand}+"
127     end
128
129     def sh4OperandPreDec
130         raise "Bad offset #{offset.value} for pre dec at #{codeOriginString}" unless offset.value == 0
131         "@-#{base.sh4Operand}"
132     end
133 end
134
135 class BaseIndex
136     def sh4Operand
137         raise "Unconverted base index at #{codeOriginString}"
138     end
139 end
140
141 class AbsoluteAddress
142     def sh4Operand
143         raise "Unconverted absolute address at #{codeOriginString}"
144     end
145 end
146
147
148 #
149 # Lowering of shift ops for SH4. For example:
150 #
151 # rshifti foo, bar
152 #
153 # becomes:
154 #
155 # negi foo, tmp
156 # shld tmp, bar
157 #
158
159 def sh4LowerShiftOps(list)
160     newList = []
161     list.each {
162         | node |
163         if node.is_a? Instruction
164             case node.opcode
165             when "ulshifti", "ulshiftp", "urshifti", "urshiftp", "lshifti", "lshiftp", "rshifti", "rshiftp"
166                 if node.opcode[0,1] == "u"
167                     type = "l"
168                     direction = node.opcode[1,1]
169                 else
170                     type = "a"
171                     direction = node.opcode[0,1]
172                 end
173                 if node.operands[0].is_a? Immediate
174                     if node.operands[0].value == 0
175                         # There is nothing to do here.
176                     elsif node.operands[0].value == 1 or (type == "l" and [2, 8, 16].include? node.operands[0].value)
177                         newList << Instruction.new(node.codeOrigin, "sh#{type}#{direction}x", node.operands)
178                     else
179                         tmp = Tmp.new(node.codeOrigin, :gpr)
180                         if direction == "l"
181                             newList << Instruction.new(node.codeOrigin, "move", [node.operands[0], tmp])
182                         else
183                             newList << Instruction.new(node.codeOrigin, "move", [Immediate.new(node.operands[0].codeOrigin, -1 * node.operands[0].value), tmp])
184                         end
185                         newList << Instruction.new(node.codeOrigin, "sh#{type}d", [tmp, node.operands[1]])
186                     end
187                 else
188                     if direction == "l"
189                         newList << Instruction.new(node.codeOrigin, "sh#{type}d", node.operands)
190                     else
191                         tmp = Tmp.new(node.codeOrigin, :gpr)
192                         newList << Instruction.new(node.codeOrigin, "negi", [node.operands[0], tmp])
193                         newList << Instruction.new(node.codeOrigin, "sh#{type}d", [tmp, node.operands[1]])
194                     end
195                 end
196             else
197                 newList << node
198             end
199         else
200             newList << node
201         end
202     }
203     newList
204 end
205
206
207 #
208 # Lowering of simple branch ops for SH4. For example:
209 #
210 # baddis foo, bar, baz
211 #
212 # will become:
213 #
214 # addi foo, bar, tmp
215 # bs tmp, baz
216 #
217
218 def sh4LowerSimpleBranchOps(list)
219     newList = []
220     list.each {
221         | node |
222         if node.is_a? Instruction
223             annotation = node.annotation
224             case node.opcode
225             when /^b(addi|subi|ori|addp)/
226                 op = $1
227                 bc = $~.post_match
228                 branch = "b" + bc
229
230                 case op
231                 when "addi", "addp"
232                     op = "addi"
233                 when "subi"
234                     op = "subi"
235                 when "ori"
236                     op = "ori"
237                 end
238
239                 if bc == "s"
240                     tmp = Tmp.new(node.codeOrigin, :gpr)
241                     newList << Instruction.new(node.codeOrigin, op, [node.operands[0], node.operands[1], tmp])
242                     newList << Instruction.new(node.codeOrigin, "bs", [tmp, node.operands[2]])
243                 else
244                     newList << node
245                 end
246             else
247                 newList << node
248             end
249         else
250             newList << node
251         end
252     }
253     newList
254 end
255
256
257 #
258 # Lowering of double accesses for SH4. For example:
259 #
260 # loadd [foo, bar, 8], baz
261 #
262 # becomes:
263 #
264 # leap [foo, bar, 8], tmp
265 # loaddReversedAndIncrementAddress [tmp], baz
266 #
267
268 def sh4LowerDoubleAccesses(list)
269     newList = []
270     list.each {
271         | node |
272         if node.is_a? Instruction
273             case node.opcode
274             when "loadd"
275                 tmp = Tmp.new(codeOrigin, :gpr)
276                 addr = Address.new(codeOrigin, tmp, Immediate.new(codeOrigin, 0))
277                 newList << Instruction.new(codeOrigin, "leap", [node.operands[0], tmp])
278                 newList << Instruction.new(node.codeOrigin, "loaddReversedAndIncrementAddress", [addr, node.operands[1]], node.annotation)
279             when "stored"
280                 tmp = Tmp.new(codeOrigin, :gpr)
281                 addr = Address.new(codeOrigin, tmp, Immediate.new(codeOrigin, 0))
282                 newList << Instruction.new(codeOrigin, "leap", [node.operands[1].withOffset(8), tmp])
283                 newList << Instruction.new(node.codeOrigin, "storedReversedAndDecrementAddress", [node.operands[0], addr], node.annotation)
284             else
285                 newList << node
286             end
287         else
288             newList << node
289         end
290     }
291     newList
292 end
293
294
295 #
296 # Lowering of double specials for SH4.
297 #
298
299 def sh4LowerDoubleSpecials(list)
300     newList = []
301     list.each {
302         | node |
303         if node.is_a? Instruction
304             case node.opcode
305             when "bdnequn", "bdgtequn", "bdltun", "bdltequn", "bdgtun"
306                 # Handle floating point unordered opcodes.
307                 tmp1 = Tmp.new(codeOrigin, :gpr)
308                 tmp2 = Tmp.new(codeOrigin, :gpr)
309                 newList << Instruction.new(codeOrigin, "bdnan", [node.operands[0], node.operands[2], tmp1, tmp2])
310                 newList << Instruction.new(codeOrigin, "bdnan", [node.operands[1], node.operands[2], tmp1, tmp2])
311                 newList << Instruction.new(codeOrigin, node.opcode[0..-3], node.operands)
312             else
313                 newList << node
314             end
315         else
316             newList << node
317         end
318     }
319     newList
320 end
321
322
323 #
324 # Lowering of misplaced labels for SH4.
325 #
326
327 def sh4LowerMisplacedLabels(list)
328     newList = []
329     list.each {
330         | node |
331         if node.is_a? Instruction
332             case node.opcode
333             when "jmp"
334                 if node.operands[0].is_a? LabelReference
335                     tmp = Tmp.new(codeOrigin, :gpr)
336                     newList << Instruction.new(codeOrigin, "jmpf", [tmp, node.operands[0]])
337                 else
338                     newList << node
339                 end
340             when "call"
341                 if node.operands[0].is_a? LabelReference
342                     tmp1 = Tmp.new(codeOrigin, :gpr)
343                     tmp2 = Tmp.new(codeOrigin, :gpr)
344                     newList << Instruction.new(codeOrigin, "callf", [tmp1, tmp2, node.operands[0]])
345                 else
346                     newList << node
347                 end
348             else
349                 newList << node
350             end
351         else
352             newList << node
353         end
354     }
355     newList
356 end
357
358
359 class Sequence
360     def getModifiedListSH4
361         result = @list
362
363         # Verify that we will only see instructions and labels.
364         result.each {
365             | node |
366             unless node.is_a? Instruction or
367                     node.is_a? Label or
368                     node.is_a? LocalLabel or
369                     node.is_a? Skip
370                 raise "Unexpected #{node.inspect} at #{node.codeOrigin}"
371             end
372         }
373
374         result = sh4LowerShiftOps(result)
375         result = sh4LowerSimpleBranchOps(result)
376         result = riscLowerMalformedAddresses(result) {
377             | node, address |
378             if address.is_a? Address
379                 case node.opcode
380                 when "btbz", "btbnz", "cbeq", "bbeq", "bbneq", "bbb", "loadb"
381                     (0..15).include? address.offset.value and
382                         ((node.operands[0].is_a? RegisterID and node.operands[0].sh4Operand == "r0") or
383                          (node.operands[1].is_a? RegisterID and node.operands[1].sh4Operand == "r0"))
384                 when "loadh"
385                     (0..30).include? address.offset.value and
386                         ((node.operands[0].is_a? RegisterID and node.operands[0].sh4Operand == "r0") or
387                          (node.operands[1].is_a? RegisterID and node.operands[1].sh4Operand == "r0"))
388                 else
389                     (0..60).include? address.offset.value
390                 end
391             else
392                 false
393             end
394         }
395         result = sh4LowerDoubleAccesses(result)
396         result = sh4LowerDoubleSpecials(result)
397         result = riscLowerMisplacedImmediates(result, ["storeb", "storei", "storep", "muli", "mulp", "andi", "ori", "xori",
398             "cbeq", "cieq", "cpeq", "cineq", "cpneq", "cib", "baddio", "bsubio", "bmulio", "baddis",
399             "bbeq", "bbneq", "bbb", "bieq", "bpeq", "bineq", "bpneq", "bia", "bpa", "biaeq", "bpaeq", "bib", "bpb",
400             "bigteq", "bpgteq", "bilt", "bplt", "bigt", "bpgt", "bilteq", "bplteq", "btiz", "btpz", "btinz", "btpnz", "btbz", "btbnz"])
401         result = riscLowerMalformedImmediates(result, -128..127)
402         result = sh4LowerMisplacedLabels(result)
403         result = riscLowerMisplacedAddresses(result)
404
405         result = assignRegistersToTemporaries(result, :gpr, SH4_TMP_GPRS)
406         result = assignRegistersToTemporaries(result, :gpr, SH4_TMP_FPRS)
407
408         return result
409     end
410 end
411
412 def sh4Operands(operands)
413     operands.map{|v| v.sh4Operand}.join(", ")
414 end
415
416 def emitSH4LoadConstant(constant, operand)
417     if constant == 0x40000000
418         # FirstConstantRegisterIndex const is often used (0x40000000).
419         # It's more efficient to "build" the value with 3 opcodes without branch.
420         $asm.puts "mov #64, #{operand.sh4Operand}"
421         $asm.puts "shll16 #{operand.sh4Operand}"
422         $asm.puts "shll8 #{operand.sh4Operand}"
423     else
424         constlabel = LocalLabel.unique("loadconstant")
425         $asm.puts ".balign 4"
426         $asm.puts "mov.l @(8, PC), #{operand.sh4Operand}"
427         $asm.puts "bra #{LocalLabelReference.new(codeOrigin, constlabel).asmLabel}"
428         $asm.puts "nop"
429         $asm.puts "nop"
430         $asm.puts ".long #{constant}"
431         constlabel.lower("SH4")
432     end
433 end
434
435 def emitSH4Branch(sh4opcode, operand)
436     $asm.puts "#{sh4opcode} @#{operand.sh4Operand}"
437     $asm.puts "nop"
438 end
439
440 def emitSH4ShiftImm(val, operand, direction)
441     tmp = val
442     while tmp > 0
443         if tmp >= 16
444             $asm.puts "shl#{direction}16 #{operand.sh4Operand}"
445             tmp -= 16
446         elsif tmp >= 8
447             $asm.puts "shl#{direction}8 #{operand.sh4Operand}"
448             tmp -= 8
449         elsif tmp >= 2
450             $asm.puts "shl#{direction}2 #{operand.sh4Operand}"
451             tmp -= 2
452         else
453             $asm.puts "shl#{direction} #{operand.sh4Operand}"
454             tmp -= 1
455         end
456     end
457 end
458
459 def emitSH4BranchIfT(label, neg)
460     outlabel = LocalLabel.unique("branchIfT")
461     sh4opcode = neg ? "bt" : "bf"
462     $asm.puts "#{sh4opcode} #{LocalLabelReference.new(codeOrigin, outlabel).asmLabel}"
463     if label.is_a? LocalLabelReference
464         $asm.puts "bra #{label.asmLabel}"
465         $asm.puts "nop"
466     else
467         $asm.puts ".balign 4"
468         $asm.puts "mov.l @(8, PC), #{SH4_TMP_GPRS[0].sh4Operand}"
469         $asm.puts "jmp @#{SH4_TMP_GPRS[0].sh4Operand}"
470         $asm.puts "nop"
471         $asm.puts "nop"
472         $asm.puts ".long #{label.asmLabel}"
473     end
474     outlabel.lower("SH4")
475 end
476
477 def emitSH4IntCompare(cmpOpcode, operands)
478     $asm.puts "cmp/#{cmpOpcode} #{sh4Operands([operands[1], operands[0]])}"
479 end
480
481 def emitSH4CondBranch(cmpOpcode, neg, operands)
482     emitSH4IntCompare(cmpOpcode, operands)
483     emitSH4BranchIfT(operands[2], neg)
484 end
485
486 def emitSH4CompareSet(cmpOpcode, neg, operands)
487     emitSH4IntCompare(cmpOpcode, operands)
488     $asm.puts "movt #{operands[2].sh4Operand}"
489     if neg
490         $asm.puts "dt #{operands[2].sh4Operand}"
491     end
492 end
493
494 def emitSH4BranchIfNaN(operands)
495     raise "Invalid operands number (#{operands.size})" unless operands.size == 4
496     dblop = operands[0]
497     labelop = operands[1]
498     scrmask = operands[2]
499     scrint = operands[3]
500
501     # If we don't have "E = Emax + 1", it's not a NaN.
502     notNaNlabel = LocalLabel.unique("notnan")
503     $asm.puts "fcnvds #{dblop.sh4Operand}, fpul"
504     $asm.puts "sts fpul, #{scrint.sh4Operand}"
505     emitSH4LoadConstant(0x7f800000, scrmask)
506     $asm.puts "and #{sh4Operands([scrmask, scrint])}"
507     $asm.puts "cmp/eq #{sh4Operands([scrmask, scrint])}"
508     $asm.puts "bf #{LocalLabelReference.new(codeOrigin, notNaNlabel).asmLabel}"
509
510     # If we have "E = Emax + 1" and "f != 0", then it's a NaN.
511     $asm.puts "sts fpul, #{scrint.sh4Operand}"
512     emitSH4LoadConstant(0x003fffff, scrmask)
513     $asm.puts "tst #{sh4Operands([scrmask, scrint])}"
514     $asm.puts "bf #{labelop.asmLabel}"
515
516     notNaNlabel.lower("SH4")
517 end
518
519 def emitSH4DoubleCondBranch(cmpOpcode, neg, operands)
520     if cmpOpcode == "lt"
521         if (!neg)
522             outlabel = LocalLabel.unique("dcbout")
523             $asm.puts "fcmp/gt #{sh4Operands([operands[1], operands[0]])}"
524             $asm.puts "bt #{LocalLabelReference.new(codeOrigin, outlabel).asmLabel}"
525             $asm.puts "fcmp/eq #{sh4Operands([operands[1], operands[0]])}"
526             $asm.puts "bf #{operands[2].asmLabel}"
527             outlabel.lower("SH4")
528         else
529             $asm.puts "fcmp/gt #{sh4Operands([operands[1], operands[0]])}"
530             $asm.puts "bt #{operands[2].asmLabel}"
531             $asm.puts "fcmp/eq #{sh4Operands([operands[1], operands[0]])}"
532             $asm.puts "bt #{operands[2].asmLabel}"
533         end
534     else
535         $asm.puts "fcmp/#{cmpOpcode} #{sh4Operands([operands[1], operands[0]])}"
536         emitSH4BranchIfT(operands[2], neg)
537     end
538 end
539
540 class Instruction
541     def lowerSH4
542         $asm.comment codeOriginString
543         case opcode
544         when "addi", "addp"
545             if operands.size == 3
546                 if operands[0].sh4Operand == operands[2].sh4Operand
547                     $asm.puts "add #{sh4Operands([operands[1], operands[2]])}"
548                 elsif operands[1].sh4Operand == operands[2].sh4Operand
549                     $asm.puts "add #{sh4Operands([operands[0], operands[2]])}"
550                 else
551                     $asm.puts "mov #{sh4Operands([operands[0], operands[2]])}"
552                     $asm.puts "add #{sh4Operands([operands[1], operands[2]])}"
553                 end
554             else
555                 $asm.puts "add #{sh4Operands(operands)}"
556             end
557         when "subi"
558             raise "#{opcode} with #{operands.size} operands is not handled yet" unless operands.size == 2
559             if operands[0].is_a? Immediate
560                 $asm.puts "add #{sh4Operands([Immediate.new(codeOrigin, -1 * operands[0].value), operands[1]])}"
561             else
562                 $asm.puts "sub #{sh4Operands(operands)}"
563             end
564         when "muli", "mulp"
565             $asm.puts "mul.l #{sh4Operands(operands[0..1])}"
566             $asm.puts "sts macl, #{operands[-1].sh4Operand}"
567         when "negi"
568             if operands.size == 2
569                 $asm.puts "neg #{sh4Operands(operands)}"
570             else
571                 $asm.puts "neg #{sh4Operands([operands[0], operands[0]])}"
572             end
573         when "andi", "ori", "xori"
574             raise "#{opcode} with #{operands.size} operands is not handled yet" unless operands.size == 2
575             sh4opcode = opcode[0..-2]
576             $asm.puts "#{sh4opcode} #{sh4Operands(operands)}"
577         when "shllx", "shlrx"
578             raise "Unhandled parameters for opcode #{opcode}" unless operands[0].is_a? Immediate
579             if operands[0].value == 1
580                 $asm.puts "shl#{opcode[3,1]} #{operands[1].sh4Operand}"
581             else
582                 $asm.puts "shl#{opcode[3,1]}#{operands[0].value} #{operands[1].sh4Operand}"
583             end
584         when "shld", "shad"
585             $asm.puts "#{opcode} #{sh4Operands(operands)}"
586         when "loaddReversedAndIncrementAddress"
587             # As we are little endian, we don't use "fmov @Rm, DRn" here.
588             $asm.puts "fmov.s #{operands[0].sh4OperandPostInc}, #{operands[1].sh4SingleLo}"
589             $asm.puts "fmov.s #{operands[0].sh4OperandPostInc}, #{operands[1].sh4SingleHi}"
590         when "storedReversedAndDecrementAddress"
591             # As we are little endian, we don't use "fmov DRm, @Rn" here.
592             $asm.puts "fmov.s #{operands[0].sh4SingleHi}, #{operands[1].sh4OperandPreDec}"
593             $asm.puts "fmov.s #{operands[0].sh4SingleLo}, #{operands[1].sh4OperandPreDec}"
594         when "ci2d"
595             $asm.puts "lds #{operands[0].sh4Operand}, fpul"
596             $asm.puts "float fpul, #{operands[1].sh4Operand}"
597         when "fii2d"
598             $asm.puts "lds #{operands[0].sh4Operand}, fpul"
599             $asm.puts "fsts fpul, #{operands[2].sh4SingleLo}"
600             $asm.puts "lds #{operands[1].sh4Operand}, fpul"
601             $asm.puts "fsts fpul, #{operands[2].sh4SingleHi}"
602         when "fd2ii"
603             $asm.puts "flds #{operands[0].sh4SingleLo}, fpul"
604             $asm.puts "sts fpul, #{operands[1].sh4Operand}"
605             $asm.puts "flds #{operands[0].sh4SingleHi}, fpul"
606             $asm.puts "sts fpul, #{operands[2].sh4Operand}"
607         when "addd", "subd", "muld", "divd"
608             sh4opcode = opcode[0..-2]
609             $asm.puts "f#{sh4opcode} #{sh4Operands(operands)}"
610         when "bcd2i"
611             $asm.puts "ftrc #{operands[0].sh4Operand}, fpul"
612             $asm.puts "sts fpul, #{operands[1].sh4Operand}"
613             $asm.puts "float fpul, #{SH4_TMP_FPRS[0].sh4Operand}"
614             $asm.puts "fcmp/eq #{sh4Operands([operands[0], SH4_TMP_FPRS[0]])}"
615             $asm.puts "bf #{operands[2].asmLabel}"
616         when "bdnan"
617             emitSH4BranchIfNaN(operands)
618         when "bdneq"
619             emitSH4DoubleCondBranch("eq", true, operands)
620         when "bdgteq"
621             emitSH4DoubleCondBranch("lt", true, operands)
622         when "bdlt"
623             emitSH4DoubleCondBranch("lt", false, operands)
624         when "bdlteq"
625             emitSH4DoubleCondBranch("gt", true, operands)
626         when "bdgt"
627             emitSH4DoubleCondBranch("gt", false, operands)
628         when "baddio", "bsubio"
629             raise "#{opcode} with #{operands.size} operands is not handled yet" unless operands.size == 3
630             $asm.puts "#{opcode[1,3]}v #{sh4Operands([operands[0], operands[1]])}"
631             $asm.puts "bt #{operands[2].asmLabel}"
632         when "bmulio"
633             $asm.puts "dmuls.l #{sh4Operands([operands[0], operands[1]])}"
634             $asm.puts "sts mach, #{operands[-2].sh4Operand}"
635             $asm.puts "tst #{sh4Operands([operands[-2], operands[-2]])}"
636             $asm.puts "sts macl, #{operands[-2].sh4Operand}"
637             $asm.puts "bf #{operands[-1].asmLabel}"
638         when "btiz", "btpz", "btinz", "btpnz", "btbz", "btbnz"
639             if operands.size == 3
640                 $asm.puts "tst #{sh4Operands([operands[0], operands[1]])}"
641             else
642                 if operands[0].sh4Operand == "r0"
643                     $asm.puts "cmp/eq #0, r0"
644                 else
645                     $asm.puts "tst #{sh4Operands([operands[0], operands[0]])}"
646                 end
647             end
648             emitSH4BranchIfT(operands[-1], (opcode[-2,2] == "nz"))
649         when "cbeq"
650             emitSH4CompareSet("eq", false, operands)
651         when "cieq", "cpeq"
652             emitSH4CompareSet("eq", false, operands)
653         when "cineq", "cpneq"
654             emitSH4CompareSet("eq", true, operands)
655         when "cib"
656             emitSH4CompareSet("hs", true, operands)
657         when "bbeq"
658             emitSH4CondBranch("eq", false, operands)
659         when "bbneq"
660             emitSH4CondBranch("eq", true, operands)
661         when "bbb"
662             emitSH4CondBranch("hs", true, operands)
663         when "bieq", "bpeq"
664             emitSH4CondBranch("eq", false, operands)
665         when "bineq", "bpneq"
666             emitSH4CondBranch("eq", true, operands)
667         when "bia", "bpa"
668             emitSH4CondBranch("hi", false, operands)
669         when "biaeq", "bpaeq"
670             emitSH4CondBranch("hs", false, operands)
671         when "bib", "bpb"
672             emitSH4CondBranch("hs", true, operands)
673         when "bigteq", "bpgteq"
674             emitSH4CondBranch("ge", false, operands)
675         when "bilt", "bplt"
676             emitSH4CondBranch("ge", true, operands)
677         when "bigt", "bpgt"
678             emitSH4CondBranch("gt", false, operands)
679         when "bilteq", "bplteq"
680             emitSH4CondBranch("gt", true, operands)
681         when "bs"
682             $asm.puts "cmp/pz #{operands[0].sh4Operand}"
683             $asm.puts "bf #{operands[1].asmLabel}"
684         when "call"
685             if operands[0].is_a? LocalLabelReference
686                 $asm.puts "bsr #{operands[0].asmLabel}"
687                 $asm.puts "nop"
688             elsif operands[0].is_a? RegisterID or operands[0].is_a? SpecialRegister
689                 emitSH4Branch("jsr", operands[0])
690             else
691                 raise "Unhandled parameters for opcode #{opcode} at #{codeOriginString}"
692             end
693         when "callf"
694             $asm.puts ".balign 4"
695             $asm.puts "mov r0, #{operands[0].sh4Operand}"
696             $asm.puts "mova @(14, PC), r0"
697             $asm.puts "lds r0, pr"
698             $asm.puts "mov.l @(6, PC), #{operands[1].sh4Operand}"
699             $asm.puts "jmp @#{operands[1].sh4Operand}"
700             $asm.puts "mov #{operands[0].sh4Operand}, r0"
701             $asm.puts ".long #{operands[2].asmLabel}"
702         when "jmp"
703             if operands[0].is_a? LocalLabelReference
704                 $asm.puts "bra #{operands[0].asmLabel}"
705                 $asm.puts "nop"
706             elsif operands[0].is_a? RegisterID or operands[0].is_a? SpecialRegister
707                 emitSH4Branch("jmp", operands[0])
708             else
709                 raise "Unhandled parameters for opcode #{opcode} at #{codeOriginString}"
710             end
711         when "jmpf"
712             $asm.puts ".balign 4"
713             $asm.puts "mov.l @(8, PC), #{operands[0].sh4Operand}"
714             $asm.puts "jmp @#{operands[0].sh4Operand}"
715             $asm.puts "nop"
716             $asm.puts "nop"
717             $asm.puts ".long #{operands[1].asmLabel}"
718         when "ret"
719             $asm.puts "rts"
720             $asm.puts "nop"
721         when "loadb"
722             $asm.puts "mov.b #{sh4Operands(operands)}"
723             $asm.puts "extu.b #{sh4Operands([operands[1], operands[1]])}"
724         when "loadh"
725             $asm.puts "mov.w #{sh4Operands(operands)}"
726             $asm.puts "extu.w #{sh4Operands([operands[1], operands[1]])}"
727         when "loadi", "loadis", "loadp", "storei", "storep"
728             $asm.puts "mov.l #{sh4Operands(operands)}"
729         when "move"
730             if operands[0].is_a? Immediate and (operands[0].value < -128 or operands[0].value > 127)
731                 emitSH4LoadConstant(operands[0].value, operands[1])
732             elsif operands[0].is_a? LabelReference
733                 emitSH4LoadConstant(operands[0].asmLabel, operands[1])
734             else
735                 $asm.puts "mov #{sh4Operands(operands)}"
736             end
737         when "leap"
738             if operands[0].is_a? BaseIndex
739                 biop = operands[0]
740                 if biop.scale > 0
741                     $asm.puts "mov #{sh4Operands([biop.index, operands[1]])}"
742                     if biop.scaleShift > 0
743                         emitSH4ShiftImm(biop.scaleShift, operands[1], "l")
744                     end
745                     $asm.puts "add #{sh4Operands([biop.base, operands[1]])}"
746                 else
747                     $asm.puts "mov #{sh4Operands([biop.base, operands[1]])}"
748                 end
749                 if biop.offset.value != 0
750                     $asm.puts "add #{sh4Operands([biop.offset, operands[1]])}"
751                 end
752             elsif operands[0].is_a? Address
753                 if operands[0].base != operands[1]
754                     $asm.puts "mov #{sh4Operands([operands[0].base, operands[1]])}"
755                 end
756                 if operands[0].offset.value != 0
757                     $asm.puts "add #{sh4Operands([operands[0].offset, operands[1]])}"
758                 end
759             else
760                 raise "Unhandled parameters for opcode #{opcode} at #{codeOriginString}"
761             end
762         when "ldspr"
763             $asm.puts "lds #{sh4Operands(operands)}, pr"
764         when "stspr"
765             $asm.puts "sts pr, #{sh4Operands(operands)}"
766         when "break"
767             # This special opcode always generates an illegal instruction exception.
768             $asm.puts ".word 0xfffd"
769         else
770             raise "Unhandled opcode #{opcode} at #{codeOriginString}"
771         end
772     end
773 end
774