require 'risc'
class Node
def sh4SingleHi
doubleOperand = sh4Operand
raise "Bogus register name #{doubleOperand}" unless doubleOperand =~ /^dr/
"fr" + ($~.post_match.to_i).to_s
end
def sh4SingleLo
doubleOperand = sh4Operand
raise "Bogus register name #{doubleOperand}" unless doubleOperand =~ /^dr/
"fr" + ($~.post_match.to_i + 1).to_s
end
end
class SpecialRegister < NoChildren
def sh4Operand
@name
end
def dump
@name
end
def register?
true
end
end
SH4_TMP_GPRS = [ SpecialRegister.new("r10"), SpecialRegister.new("r11"), SpecialRegister.new("r13") ]
SH4_TMP_FPRS = [ SpecialRegister.new("dr12") ]
class RegisterID
def sh4Operand
case name
when "a0"
"r4"
when "a1"
"r5"
when "r0", "t0"
"r0"
when "r1", "t1"
"r1"
when "a2", "t2"
"r6"
when "a3", "t3"
"r7"
when "t4"
"r2"
when "t5"
"r3"
when "cfr"
"r14"
when "sp"
"r15"
when "lr"
"pr"
else
raise "Bad register #{name} for SH4 at #{codeOriginString}"
end
end
end
class FPRegisterID
def sh4Operand
case name
when "ft0", "fr"
"dr0"
when "ft1"
"dr2"
when "ft2", "fa0"
"dr4"
when "ft3", "fa1"
"dr6"
when "ft4"
"dr8"
when "ft5"
"dr10"
else
raise "Bad register #{name} for SH4 at #{codeOriginString}"
end
end
end
class Immediate
def sh4Operand
raise "Invalid immediate #{value} at #{codeOriginString}" if value < -128 or value > 127
"##{value}"
end
end
class Address
def sh4Operand
raise "Bad offset #{offset.value} at #{codeOriginString}" if offset.value < 0 or offset.value > 60
if offset.value == 0
"@#{base.sh4Operand}"
else
"@(#{offset.value}, #{base.sh4Operand})"
end
end
def sh4OperandPostInc
raise "Bad offset #{offset.value} for post inc at #{codeOriginString}" unless offset.value == 0
"@#{base.sh4Operand}+"
end
def sh4OperandPreDec
raise "Bad offset #{offset.value} for pre dec at #{codeOriginString}" unless offset.value == 0
"@-#{base.sh4Operand}"
end
end
class BaseIndex
def sh4Operand
raise "Unconverted base index at #{codeOriginString}"
end
end
class AbsoluteAddress
def sh4Operand
raise "Unconverted absolute address at #{codeOriginString}"
end
end
class LabelReference
def sh4Operand
value
end
end
class SubImmediates < Node
def sh4Operand
"#{@left.sh4Operand} - #{@right.sh4Operand}"
end
end
class ConstPool < Node
attr_reader :size
attr_reader :entries
def initialize(codeOrigin, entries, size)
super(codeOrigin)
raise "Invalid size #{size} for ConstPool" unless size == 16 or size == 32
@size = size
@entries = entries
end
def dump
"#{size}: #{entries}"
end
def address?
false
end
def label?
false
end
def immediate?
false
end
def register?
false
end
def lowerSH4
if size == 16
$asm.puts ".balign 2"
else
$asm.puts ".balign 4"
end
entries.map {
|e|
e.label.lower("SH4")
if e.size == 16
$asm.puts ".word #{e.value}"
else
$asm.puts ".long #{e.value}"
end
}
end
end
class ConstPoolEntry < Node
attr_reader :size
attr_reader :value
attr_reader :label
attr_reader :labelref
def initialize(codeOrigin, value, size)
super(codeOrigin)
raise "Invalid size #{size} for ConstPoolEntry" unless size == 16 or size == 32
@size = size
@value = value
@label = LocalLabel.unique("constpool#{size}")
@labelref = LocalLabelReference.new(codeOrigin, label)
end
def dump
"#{value} (#{size} @ #{label})"
end
def ==(other)
other.is_a? ConstPoolEntry and other.value == @value
end
def address?
false
end
def label?
false
end
def immediate?
false
end
def register?
false
end
end
def sh4LowerShiftOps(list)
newList = []
list.each {
| node |
if node.is_a? Instruction
case node.opcode
when "ulshifti", "ulshiftp", "urshifti", "urshiftp", "lshifti", "lshiftp", "rshifti", "rshiftp"
if node.opcode[0, 1] == "u"
type = "l"
direction = node.opcode[1, 1]
else
type = "a"
direction = node.opcode[0, 1]
end
if node.operands[0].is_a? Immediate
maskedImm = Immediate.new(node.operands[0].codeOrigin, node.operands[0].value & 31)
if maskedImm.value == 0
elsif maskedImm.value == 1 or (type == "l" and [2, 8, 16].include? maskedImm.value)
newList << Instruction.new(node.codeOrigin, "sh#{type}#{direction}x", [maskedImm, node.operands[1]])
else
tmp = Tmp.new(node.codeOrigin, :gpr)
if direction == "l"
newList << Instruction.new(node.codeOrigin, "move", [maskedImm, tmp])
else
newList << Instruction.new(node.codeOrigin, "move", [Immediate.new(node.operands[0].codeOrigin, -1 * maskedImm.value), tmp])
end
newList << Instruction.new(node.codeOrigin, "sh#{type}d", [tmp, node.operands[1]])
end
else
tmp = Tmp.new(node.codeOrigin, :gpr)
newList << Instruction.new(node.codeOrigin, "move", [Immediate.new(node.operands[0].codeOrigin, 31), tmp])
newList << Instruction.new(node.codeOrigin, "andi", [node.operands[0], tmp])
if direction == "r"
newList << Instruction.new(node.codeOrigin, "negi", [tmp, tmp])
end
newList << Instruction.new(node.codeOrigin, "sh#{type}d", [tmp, node.operands[1]])
end
else
newList << node
end
else
newList << node
end
}
newList
end
def sh4LowerSimpleBranchOps(list)
newList = []
list.each {
| node |
if node.is_a? Instruction
annotation = node.annotation
case node.opcode
when /^b(addi|subi|ori|addp)/
op = $1
bc = $~.post_match
case op
when "addi", "addp"
op = "addi"
when "subi", "subp"
op = "subi"
when "ori", "orp"
op = "ori"
end
if bc == "s"
raise "Invalid operands number (#{node.operands.size})" unless node.operands.size == 3
if node.operands[1].is_a? RegisterID or node.operands[1].is_a? SpecialRegister
newList << Instruction.new(node.codeOrigin, op, node.operands[0..1])
newList << Instruction.new(node.codeOrigin, "bs", node.operands[1..2])
else
tmpVal = Tmp.new(node.codeOrigin, :gpr)
tmpPtr = Tmp.new(node.codeOrigin, :gpr)
addr = Address.new(node.codeOrigin, tmpPtr, Immediate.new(node.codeOrigin, 0))
newList << Instruction.new(node.codeOrigin, "leap", [node.operands[1], tmpPtr])
newList << Instruction.new(node.codeOrigin, "loadi", [addr, tmpVal])
newList << Instruction.new(node.codeOrigin, op, [node.operands[0], tmpVal])
newList << Instruction.new(node.codeOrigin, "storei", [tmpVal, addr])
newList << Instruction.new(node.codeOrigin, "bs", [tmpVal, node.operands[2]])
end
elsif bc == "nz"
raise "Invalid operands number (#{node.operands.size})" unless node.operands.size == 3
newList << Instruction.new(node.codeOrigin, op, node.operands[0..1])
newList << Instruction.new(node.codeOrigin, "btinz", node.operands[1..2])
else
newList << node
end
when "bmulio", "bmulpo"
raise "Invalid operands number (#{node.operands.size})" unless node.operands.size == 3
tmp1 = Tmp.new(node.codeOrigin, :gpr)
tmp2 = Tmp.new(node.codeOrigin, :gpr)
newList << Instruction.new(node.codeOrigin, node.opcode, [tmp1, tmp2].concat(node.operands))
else
newList << node
end
else
newList << node
end
}
newList
end
def sh4LowerDoubleAccesses(list)
newList = []
list.each {
| node |
if node.is_a? Instruction
case node.opcode
when "loadd"
tmp = Tmp.new(codeOrigin, :gpr)
addr = Address.new(codeOrigin, tmp, Immediate.new(codeOrigin, 0))
newList << Instruction.new(codeOrigin, "leap", [node.operands[0], tmp])
newList << Instruction.new(node.codeOrigin, "loaddReversedAndIncrementAddress", [addr, node.operands[1]], node.annotation)
when "stored"
tmp = Tmp.new(codeOrigin, :gpr)
addr = Address.new(codeOrigin, tmp, Immediate.new(codeOrigin, 0))
newList << Instruction.new(codeOrigin, "leap", [node.operands[1].withOffset(8), tmp])
newList << Instruction.new(node.codeOrigin, "storedReversedAndDecrementAddress", [node.operands[0], addr], node.annotation)
else
newList << node
end
else
newList << node
end
}
newList
end
def sh4LowerDoubleSpecials(list)
newList = []
list.each {
| node |
if node.is_a? Instruction
case node.opcode
when "bdltun", "bdgtun"
newList << Instruction.new(codeOrigin, "bdnan", [node.operands[0], node.operands[2]])
newList << Instruction.new(codeOrigin, "bdnan", [node.operands[1], node.operands[2]])
newList << Instruction.new(codeOrigin, node.opcode[0..-3], node.operands)
when "bdnequn", "bdgtequn", "bdltequn"
newList << Instruction.new(codeOrigin, node.opcode[0..-3], node.operands)
when "bdneq", "bdgteq", "bdlteq"
outlabel = LocalLabel.unique("out_#{node.opcode}")
outref = LocalLabelReference.new(codeOrigin, outlabel)
newList << Instruction.new(codeOrigin, "bdnan", [node.operands[0], outref])
newList << Instruction.new(codeOrigin, "bdnan", [node.operands[1], outref])
newList << Instruction.new(codeOrigin, node.opcode, node.operands)
newList << outlabel
else
newList << node
end
else
newList << node
end
}
newList
end
def sh4LowerMisplacedLabels(list)
newList = []
list.each {
| node |
if node.is_a? Instruction
operands = node.operands
newOperands = []
operands.each {
| operand |
if operand.is_a? LabelReference and node.opcode != "mova"
tmp = Tmp.new(operand.codeOrigin, :gpr)
newList << Instruction.new(operand.codeOrigin, "move", [operand, tmp])
newOperands << tmp
else
newOperands << operand
end
}
newList << Instruction.new(node.codeOrigin, node.opcode, newOperands, node.annotation)
else
newList << node
end
}
newList
end
def sh4LowerMisplacedSpecialRegisters(list)
newList = []
list.each {
| node |
if node.is_a? Instruction
case node.opcode
when "move"
if node.operands[0].is_a? RegisterID and node.operands[0].sh4Operand == "pr"
newList << Instruction.new(codeOrigin, "stspr", [node.operands[1]])
elsif node.operands[1].is_a? RegisterID and node.operands[1].sh4Operand == "pr"
newList << Instruction.new(codeOrigin, "ldspr", [node.operands[0]])
else
newList << node
end
when "loadi", "loadis", "loadp"
if node.operands[1].is_a? RegisterID and node.operands[1].sh4Operand == "pr"
tmp = Tmp.new(codeOrigin, :gpr)
newList << Instruction.new(codeOrigin, node.opcode, [node.operands[0], tmp])
newList << Instruction.new(codeOrigin, "ldspr", [tmp])
else
newList << node
end
when "storei", "storep"
if node.operands[0].is_a? RegisterID and node.operands[0].sh4Operand == "pr"
tmp = Tmp.new(codeOrigin, :gpr)
newList << Instruction.new(codeOrigin, "stspr", [tmp])
newList << Instruction.new(codeOrigin, node.opcode, [tmp, node.operands[1]])
else
newList << node
end
else
newList << node
end
else
newList << node
end
}
newList
end
def sh4LowerConstPool(list)
newList = []
currentPool16 = []
currentPool32 = []
list.each {
| node |
if node.is_a? Instruction
case node.opcode
when "jmp", "ret", "flushcp"
if node.opcode == "flushcp"
outlabel = LocalLabel.unique("flushcp")
newList << Instruction.new(codeOrigin, "jmp", [LocalLabelReference.new(codeOrigin, outlabel)])
else
newList << node
end
if not currentPool16.empty?
newList << ConstPool.new(codeOrigin, currentPool16, 16)
currentPool16 = []
end
if not currentPool32.empty?
newList << ConstPool.new(codeOrigin, currentPool32, 32)
currentPool32 = []
end
if node.opcode == "flushcp"
newList << outlabel
end
when "move"
if node.operands[0].is_a? Immediate and not (-128..127).include? node.operands[0].value
poolEntry = nil
if (-32768..32767).include? node.operands[0].value
currentPool16.each { |e|
if e.value == node.operands[0].value
poolEntry = e
end
}
if !poolEntry
poolEntry = ConstPoolEntry.new(codeOrigin, node.operands[0].value, 16)
currentPool16 << poolEntry
end
else
currentPool32.each { |e|
if e.value == node.operands[0].value
poolEntry = e
end
}
if !poolEntry
poolEntry = ConstPoolEntry.new(codeOrigin, node.operands[0].value, 32)
currentPool32 << poolEntry
end
end
newList << Instruction.new(codeOrigin, "move", [poolEntry, node.operands[1]])
elsif node.operands[0].is_a? LabelReference
poolEntry = nil
currentPool32.each { |e|
if e.value == node.operands[0].asmLabel
poolEntry = e
end
}
if !poolEntry
poolEntry = ConstPoolEntry.new(codeOrigin, node.operands[0].asmLabel, 32)
currentPool32 << poolEntry
end
newList << Instruction.new(codeOrigin, "move", [poolEntry, node.operands[1]])
elsif node.operands[0].is_a? SubImmediates
poolEntry = ConstPoolEntry.new(codeOrigin, node.operands[0].sh4Operand, 32)
currentPool32 << poolEntry
newList << Instruction.new(codeOrigin, "move", [poolEntry, node.operands[1]])
else
newList << node
end
else
newList << node
end
else
newList << node
end
}
if not currentPool16.empty?
newList << ConstPool.new(codeOrigin, currentPool16, 16)
end
if not currentPool32.empty?
newList << ConstPool.new(codeOrigin, currentPool32, 32)
end
newList
end
def sh4LowerArgumentSetup(list)
a0 = RegisterID.forName(codeOrigin, "a0")
a1 = RegisterID.forName(codeOrigin, "a1")
a2 = RegisterID.forName(codeOrigin, "a2")
a3 = RegisterID.forName(codeOrigin, "a3")
newList = []
list.each {
| node |
if node.is_a? Instruction
case node.opcode
when "setargs"
if node.operands.size == 2
if node.operands[1].sh4Operand != a0.sh4Operand
newList << Instruction.new(codeOrigin, "move", [node.operands[0], a0])
newList << Instruction.new(codeOrigin, "move", [node.operands[1], a1])
elsif node.operands[0].sh4Operand != a1.sh4Operand
newList << Instruction.new(codeOrigin, "move", [node.operands[1], a1])
newList << Instruction.new(codeOrigin, "move", [node.operands[0], a0])
else
newList << Instruction.new(codeOrigin, "xori", [a0, a1])
newList << Instruction.new(codeOrigin, "xori", [a1, a0])
newList << Instruction.new(codeOrigin, "xori", [a0, a1])
end
elsif node.operands.size == 4
if (node.operands[0].sh4Operand == a1.sh4Operand) or
(node.operands[0].sh4Operand == a2.sh4Operand) or
(node.operands[0].sh4Operand == a3.sh4Operand) or
(node.operands[1].sh4Operand == a0.sh4Operand) or
(node.operands[1].sh4Operand == a2.sh4Operand) or
(node.operands[1].sh4Operand == a3.sh4Operand) or
(node.operands[2].sh4Operand == a0.sh4Operand) or
(node.operands[2].sh4Operand == a1.sh4Operand) or
(node.operands[2].sh4Operand == a3.sh4Operand) or
(node.operands[3].sh4Operand == a0.sh4Operand) or
(node.operands[3].sh4Operand == a1.sh4Operand) or
(node.operands[3].sh4Operand == a2.sh4Operand)
raise "Potential argument register trampling detected."
end
newList << Instruction.new(codeOrigin, "move", [node.operands[0], a0])
newList << Instruction.new(codeOrigin, "move", [node.operands[1], a1])
newList << Instruction.new(codeOrigin, "move", [node.operands[2], a2])
newList << Instruction.new(codeOrigin, "move", [node.operands[3], a3])
else
raise "Invalid operands number (#{node.operands.size}) for setargs"
end
else
newList << node
end
else
newList << node
end
}
newList
end
class Sequence
def getModifiedListSH4
result = @list
result.each {
| node |
unless node.is_a? Instruction or
node.is_a? Label or
node.is_a? LocalLabel or
node.is_a? Skip
raise "Unexpected #{node.inspect} at #{node.codeOrigin}"
end
}
result = sh4LowerShiftOps(result)
result = sh4LowerSimpleBranchOps(result)
result = riscLowerMalformedAddresses(result) {
| node, address |
if address.is_a? Address
case node.opcode
when "btbz", "btbnz", "cbeq", "bbeq", "bbneq", "bbb", "loadb", "storeb"
(0..15).include? address.offset.value and
((node.operands[0].is_a? RegisterID and node.operands[0].sh4Operand == "r0") or
(node.operands[1].is_a? RegisterID and node.operands[1].sh4Operand == "r0"))
when "loadh"
(0..30).include? address.offset.value and
((node.operands[0].is_a? RegisterID and node.operands[0].sh4Operand == "r0") or
(node.operands[1].is_a? RegisterID and node.operands[1].sh4Operand == "r0"))
else
(0..60).include? address.offset.value
end
else
false
end
}
result = sh4LowerDoubleAccesses(result)
result = sh4LowerDoubleSpecials(result)
result = riscLowerMisplacedImmediates(result, ["storeb", "storei", "storep", "muli", "mulp", "andi", "ori", "xori",
"cbeq", "cieq", "cpeq", "cineq", "cpneq", "cib", "baddio", "bsubio", "bmulio", "baddis",
"bbeq", "bbneq", "bbb", "bieq", "bpeq", "bineq", "bpneq", "bia", "bpa", "biaeq", "bpaeq", "bib", "bpb",
"bigteq", "bpgteq", "bilt", "bplt", "bigt", "bpgt", "bilteq", "bplteq", "btiz", "btpz", "btinz", "btpnz", "btbz", "btbnz"])
result = riscLowerMalformedImmediates(result, -128..127)
result = riscLowerMisplacedAddresses(result)
result = sh4LowerMisplacedLabels(result)
result = sh4LowerMisplacedSpecialRegisters(result)
result = assignRegistersToTemporaries(result, :gpr, SH4_TMP_GPRS)
result = assignRegistersToTemporaries(result, :gpr, SH4_TMP_FPRS)
result = sh4LowerConstPool(result)
result = sh4LowerArgumentSetup(result)
return result
end
end
def sh4Operands(operands)
operands.map{|v| v.sh4Operand}.join(", ")
end
def emitSH4Branch(sh4opcode, operand)
raise "Invalid operand #{operand}" unless operand.is_a? RegisterID or operand.is_a? SpecialRegister
$asm.puts "#{sh4opcode} @#{operand.sh4Operand}"
$asm.puts "nop"
end
def emitSH4ShiftImm(val, operand, direction)
tmp = val
while tmp > 0
if tmp >= 16
$asm.puts "shl#{direction}16 #{operand.sh4Operand}"
tmp -= 16
elsif tmp >= 8
$asm.puts "shl#{direction}8 #{operand.sh4Operand}"
tmp -= 8
elsif tmp >= 2
$asm.puts "shl#{direction}2 #{operand.sh4Operand}"
tmp -= 2
else
$asm.puts "shl#{direction} #{operand.sh4Operand}"
tmp -= 1
end
end
end
def emitSH4BranchIfT(dest, neg)
outlabel = LocalLabel.unique("branchIfT")
sh4opcode = neg ? "bt" : "bf"
$asm.puts "#{sh4opcode} #{LocalLabelReference.new(codeOrigin, outlabel).asmLabel}"
if dest.is_a? LocalLabelReference
$asm.puts "bra #{dest.asmLabel}"
$asm.puts "nop"
else
emitSH4Branch("jmp", dest)
end
outlabel.lower("SH4")
end
def emitSH4IntCompare(cmpOpcode, operands)
$asm.puts "cmp/#{cmpOpcode} #{sh4Operands([operands[1], operands[0]])}"
end
def emitSH4CondBranch(cmpOpcode, neg, operands)
emitSH4IntCompare(cmpOpcode, operands)
emitSH4BranchIfT(operands[2], neg)
end
def emitSH4CompareSet(cmpOpcode, neg, operands)
emitSH4IntCompare(cmpOpcode, operands)
if !neg
$asm.puts "movt #{operands[2].sh4Operand}"
else
outlabel = LocalLabel.unique("compareSet")
$asm.puts "mov #0, #{operands[2].sh4Operand}"
$asm.puts "bt #{LocalLabelReference.new(codeOrigin, outlabel).asmLabel}"
$asm.puts "mov #1, #{operands[2].sh4Operand}"
outlabel.lower("SH4")
end
end
def emitSH4BranchIfNaN(operands)
raise "Invalid operands number (#{operands.size})" unless operands.size == 2
$asm.puts "fcmp/eq #{sh4Operands([operands[0], operands[0]])}"
$asm.puts "bf #{operands[1].asmLabel}"
end
def emitSH4DoubleCondBranch(cmpOpcode, neg, operands)
if cmpOpcode == "lt"
$asm.puts "fcmp/gt #{sh4Operands([operands[0], operands[1]])}"
else
$asm.puts "fcmp/#{cmpOpcode} #{sh4Operands([operands[1], operands[0]])}"
end
emitSH4BranchIfT(operands[2], neg)
end
class Instruction
def lowerSH4
$asm.comment codeOriginString
case opcode
when "addi", "addp"
if operands.size == 3
if operands[0].sh4Operand == operands[2].sh4Operand
$asm.puts "add #{sh4Operands([operands[1], operands[2]])}"
elsif operands[1].sh4Operand == operands[2].sh4Operand
$asm.puts "add #{sh4Operands([operands[0], operands[2]])}"
else
$asm.puts "mov #{sh4Operands([operands[0], operands[2]])}"
$asm.puts "add #{sh4Operands([operands[1], operands[2]])}"
end
else
$asm.puts "add #{sh4Operands(operands)}"
end
when "subi", "subp"
if operands.size == 3
if operands[1].is_a? Immediate
$asm.puts "mov #{sh4Operands([Immediate.new(codeOrigin, -1 * operands[1].value), operands[2]])}"
$asm.puts "add #{sh4Operands([operands[0], operands[2]])}"
elsif operands[1].sh4Operand == operands[2].sh4Operand
$asm.puts "neg #{sh4Operands([operands[2], operands[2]])}"
$asm.puts "add #{sh4Operands([operands[0], operands[2]])}"
else
$asm.puts "mov #{sh4Operands([operands[0], operands[2]])}"
$asm.puts "sub #{sh4Operands([operands[1], operands[2]])}"
end
else
if operands[0].is_a? Immediate
$asm.puts "add #{sh4Operands([Immediate.new(codeOrigin, -1 * operands[0].value), operands[1]])}"
else
$asm.puts "sub #{sh4Operands(operands)}"
end
end
when "muli", "mulp"
$asm.puts "mul.l #{sh4Operands(operands[0..1])}"
$asm.puts "sts macl, #{operands[-1].sh4Operand}"
when "negi", "negp"
if operands.size == 2
$asm.puts "neg #{sh4Operands(operands)}"
else
$asm.puts "neg #{sh4Operands([operands[0], operands[0]])}"
end
when "andi", "andp", "ori", "orp", "xori", "xorp"
raise "#{opcode} with #{operands.size} operands is not handled yet" unless operands.size == 2
sh4opcode = opcode[0..-2]
$asm.puts "#{sh4opcode} #{sh4Operands(operands)}"
when "shllx", "shlrx"
raise "Unhandled parameters for opcode #{opcode}" unless operands[0].is_a? Immediate
if operands[0].value == 1
$asm.puts "shl#{opcode[3, 1]} #{operands[1].sh4Operand}"
else
$asm.puts "shl#{opcode[3, 1]}#{operands[0].value} #{operands[1].sh4Operand}"
end
when "shalx", "sharx"
raise "Unhandled parameters for opcode #{opcode}" unless operands[0].is_a? Immediate and operands[0].value == 1
$asm.puts "sha#{opcode[3, 1]} #{operands[1].sh4Operand}"
when "shld", "shad"
$asm.puts "#{opcode} #{sh4Operands(operands)}"
when "loaddReversedAndIncrementAddress"
$asm.puts "fmov.s #{operands[0].sh4OperandPostInc}, #{operands[1].sh4SingleLo}"
$asm.puts "fmov.s #{operands[0].sh4OperandPostInc}, #{operands[1].sh4SingleHi}"
when "storedReversedAndDecrementAddress"
$asm.puts "fmov.s #{operands[0].sh4SingleHi}, #{operands[1].sh4OperandPreDec}"
$asm.puts "fmov.s #{operands[0].sh4SingleLo}, #{operands[1].sh4OperandPreDec}"
when "ci2d"
$asm.puts "lds #{operands[0].sh4Operand}, fpul"
$asm.puts "float fpul, #{operands[1].sh4Operand}"
when "fii2d"
$asm.puts "lds #{operands[0].sh4Operand}, fpul"
$asm.puts "fsts fpul, #{operands[2].sh4SingleLo}"
$asm.puts "lds #{operands[1].sh4Operand}, fpul"
$asm.puts "fsts fpul, #{operands[2].sh4SingleHi}"
when "fd2ii"
$asm.puts "flds #{operands[0].sh4SingleLo}, fpul"
$asm.puts "sts fpul, #{operands[1].sh4Operand}"
$asm.puts "flds #{operands[0].sh4SingleHi}, fpul"
$asm.puts "sts fpul, #{operands[2].sh4Operand}"
when "addd", "subd", "muld", "divd"
sh4opcode = opcode[0..-2]
$asm.puts "f#{sh4opcode} #{sh4Operands(operands)}"
when "bcd2i"
$asm.puts "ftrc #{operands[0].sh4Operand}, fpul"
$asm.puts "sts fpul, #{operands[1].sh4Operand}"
$asm.puts "float fpul, #{SH4_TMP_FPRS[0].sh4Operand}"
$asm.puts "fcmp/eq #{sh4Operands([operands[0], SH4_TMP_FPRS[0]])}"
$asm.puts "bf #{operands[2].asmLabel}"
$asm.puts "tst #{sh4Operands([operands[1], operands[1]])}"
$asm.puts "bt #{operands[2].asmLabel}"
when "bdnan"
emitSH4BranchIfNaN(operands)
when "bdneq"
emitSH4DoubleCondBranch("eq", true, operands)
when "bdgteq"
emitSH4DoubleCondBranch("lt", true, operands)
when "bdlt"
emitSH4DoubleCondBranch("lt", false, operands)
when "bdlteq"
emitSH4DoubleCondBranch("gt", true, operands)
when "bdgt"
emitSH4DoubleCondBranch("gt", false, operands)
when "baddio", "baddpo", "bsubio", "bsubpo"
raise "#{opcode} with #{operands.size} operands is not handled yet" unless operands.size == 3
$asm.puts "#{opcode[1, 3]}v #{sh4Operands([operands[0], operands[1]])}"
$asm.puts "bt #{operands[2].asmLabel}"
when "bmulio", "bmulpo"
raise "Invalid operands number (#{operands.size})" unless operands.size == 5
$asm.puts "dmuls.l #{sh4Operands([operands[2], operands[3]])}"
$asm.puts "sts macl, #{operands[3].sh4Operand}"
$asm.puts "cmp/pz #{operands[3].sh4Operand}"
$asm.puts "movt #{operands[1].sh4Operand}"
$asm.puts "add #-1, #{operands[1].sh4Operand}"
$asm.puts "sts mach, #{operands[0].sh4Operand}"
$asm.puts "cmp/eq #{sh4Operands([operands[0], operands[1]])}"
$asm.puts "bf #{operands[4].asmLabel}"
when "btiz", "btpz", "btbz", "btinz", "btpnz", "btbnz"
if operands.size == 3
$asm.puts "tst #{sh4Operands([operands[0], operands[1]])}"
else
if operands[0].sh4Operand == "r0"
$asm.puts "cmp/eq #0, r0"
else
$asm.puts "tst #{sh4Operands([operands[0], operands[0]])}"
end
end
emitSH4BranchIfT(operands[-1], (opcode[-2, 2] == "nz"))
when "cieq", "cpeq", "cbeq"
emitSH4CompareSet("eq", false, operands)
when "cineq", "cpneq", "cbneq"
emitSH4CompareSet("eq", true, operands)
when "cib", "cpb", "cbb"
emitSH4CompareSet("hs", true, operands)
when "bieq", "bpeq", "bbeq"
emitSH4CondBranch("eq", false, operands)
when "bineq", "bpneq", "bbneq"
emitSH4CondBranch("eq", true, operands)
when "bib", "bpb", "bbb"
emitSH4CondBranch("hs", true, operands)
when "bia", "bpa", "bba"
emitSH4CondBranch("hi", false, operands)
when "bibeq", "bpbeq"
emitSH4CondBranch("hi", true, operands)
when "biaeq", "bpaeq"
emitSH4CondBranch("hs", false, operands)
when "bigteq", "bpgteq", "bbgteq"
emitSH4CondBranch("ge", false, operands)
when "bilt", "bplt", "bblt"
emitSH4CondBranch("ge", true, operands)
when "bigt", "bpgt", "bbgt"
emitSH4CondBranch("gt", false, operands)
when "bilteq", "bplteq", "bblteq"
emitSH4CondBranch("gt", true, operands)
when "bs"
$asm.puts "cmp/pz #{operands[0].sh4Operand}"
$asm.puts "bf #{operands[1].asmLabel}"
when "call"
if operands[0].is_a? LocalLabelReference
$asm.puts "bsr #{operands[0].asmLabel}"
$asm.puts "nop"
elsif operands[0].is_a? RegisterID or operands[0].is_a? SpecialRegister
emitSH4Branch("jsr", operands[0])
else
raise "Unhandled parameters for opcode #{opcode} at #{codeOriginString}"
end
when "jmp"
if operands[0].is_a? LocalLabelReference
$asm.puts "bra #{operands[0].asmLabel}"
$asm.puts "nop"
elsif operands[0].is_a? RegisterID or operands[0].is_a? SpecialRegister
emitSH4Branch("jmp", operands[0])
else
raise "Unhandled parameters for opcode #{opcode} at #{codeOriginString}"
end
when "ret"
$asm.puts "rts"
$asm.puts "nop"
when "loadb"
$asm.puts "mov.b #{sh4Operands(operands)}"
$asm.puts "extu.b #{sh4Operands([operands[1], operands[1]])}"
when "storeb"
$asm.puts "mov.b #{sh4Operands(operands)}"
when "loadh"
$asm.puts "mov.w #{sh4Operands(operands)}"
$asm.puts "extu.w #{sh4Operands([operands[1], operands[1]])}"
when "loadi", "loadis", "loadp", "storei", "storep"
$asm.puts "mov.l #{sh4Operands(operands)}"
when "alignformova"
$asm.puts ".balign 4" when "mova"
$asm.puts "mova #{sh4Operands(operands)}"
when "move"
if operands[0].is_a? ConstPoolEntry
if operands[0].size == 16
$asm.puts "mov.w #{operands[0].labelref.asmLabel}, #{operands[1].sh4Operand}"
else
$asm.puts "mov.l #{operands[0].labelref.asmLabel}, #{operands[1].sh4Operand}"
end
elsif operands[0].sh4Operand != operands[1].sh4Operand
$asm.puts "mov #{sh4Operands(operands)}"
end
when "leap"
if operands[0].is_a? BaseIndex
biop = operands[0]
$asm.puts "mov #{sh4Operands([biop.index, operands[1]])}"
if biop.scaleShift > 0
emitSH4ShiftImm(biop.scaleShift, operands[1], "l")
end
$asm.puts "add #{sh4Operands([biop.base, operands[1]])}"
if biop.offset.value != 0
$asm.puts "add #{sh4Operands([biop.offset, operands[1]])}"
end
elsif operands[0].is_a? Address
if operands[0].base != operands[1]
$asm.puts "mov #{sh4Operands([operands[0].base, operands[1]])}"
end
if operands[0].offset.value != 0
$asm.puts "add #{sh4Operands([operands[0].offset, operands[1]])}"
end
else
raise "Unhandled parameters for opcode #{opcode} at #{codeOriginString}"
end
when "ldspr"
$asm.puts "lds #{sh4Operands(operands)}, pr"
when "stspr"
$asm.puts "sts pr, #{sh4Operands(operands)}"
when "memfence"
$asm.puts "synco"
when "pop"
if operands[0].sh4Operand == "pr"
$asm.puts "lds.l @r15+, #{sh4Operands(operands)}"
else
$asm.puts "mov.l @r15+, #{sh4Operands(operands)}"
end
when "push"
if operands[0].sh4Operand == "pr"
$asm.puts "sts.l #{sh4Operands(operands)}, @-r15"
else
$asm.puts "mov.l #{sh4Operands(operands)}, @-r15"
end
when "break"
$asm.puts ".word 0xfffd"
else
lowerDefault
end
end
end