swap() is now a statement instead of a builtin function call

This commit is contained in:
Irmen de Jong
2026-02-13 01:16:12 +01:00
parent 74ca190ce0
commit 2613d45cd5
20 changed files with 709 additions and 846 deletions
@@ -116,11 +116,6 @@ val BuiltinFunctions: Map<String, FSignature> = mapOf(
"sqrt__uword" to FSignature(true, BaseDataType.UBYTE, FParam("value", BaseDataType.UWORD)),
"sqrt__long" to FSignature(true, BaseDataType.UWORD, FParam("value", BaseDataType.LONG)),
"sqrt__float" to FSignature(true, BaseDataType.FLOAT, FParam("value", BaseDataType.FLOAT)),
"swap" to FSignature(false, null, FParam("var1", *NumericDatatypes + BaseDataType.BOOL), FParam("var2", *NumericDatatypes + BaseDataType.BOOL)),
"swap__byte" to FSignature(false, null, FParam("var1", BaseDataType.BYTE, BaseDataType.UBYTE, BaseDataType.BOOL), FParam("var2",BaseDataType.BYTE, BaseDataType.UBYTE, BaseDataType.BOOL)),
"swap__word" to FSignature(false, null, FParam("var1", BaseDataType.WORD, BaseDataType.UWORD), FParam("var2",BaseDataType.WORD, BaseDataType.UWORD)),
"swap__long" to FSignature(false, null, FParam("var1", BaseDataType.LONG), FParam("var2",BaseDataType.LONG)),
"swap__float" to FSignature(false, null, FParam("var1", BaseDataType.FLOAT), FParam("var2",BaseDataType.FLOAT)),
"divmod" to FSignature(false, null, FParam("dividend", BaseDataType.UBYTE, BaseDataType.UWORD), FParam("divisor", BaseDataType.UBYTE, BaseDataType.UWORD), FParam("quotient", BaseDataType.UBYTE, BaseDataType.UWORD), FParam("remainder", BaseDataType.UBYTE, BaseDataType.UWORD)),
"divmod__ubyte" to FSignature(false, null, FParam("dividend", BaseDataType.UBYTE), FParam("divisor", BaseDataType.UBYTE), FParam("quotient", BaseDataType.UBYTE), FParam("remainder", BaseDataType.UBYTE)),
"divmod__uword" to FSignature(false, null, FParam("dividend", BaseDataType.UWORD), FParam("divisor", BaseDataType.UWORD), FParam("quotient", BaseDataType.UWORD), FParam("remainder", BaseDataType.UWORD)),
@@ -174,8 +169,7 @@ val BuiltinFunctions: Map<String, FSignature> = mapOf(
val InplaceModifyingBuiltinFunctions = setOf(
"setlsb", "setmsb",
"rol", "ror", "rol2", "ror2",
"divmod", "divmod__ubyte", "divmod__uword",
"swap", "swap__byte", "swap__word", "swap__long", "swap__float"
"divmod", "divmod__ubyte", "divmod__uword"
)
val SimpleBuiltinFunctions = setOf(
@@ -716,6 +716,7 @@ class AsmGen6502Internal (
is PtDefer -> throw AssemblyError("defer should have been transformed")
is PtNodeGroup -> stmt.children.forEach { translate(it) }
is PtJmpTable -> translate(stmt)
is PtSwap -> translate(stmt)
is PtNop, is PtStructDecl, is PtSubSignature -> {}
else -> throw AssemblyError("missing asm translation for $stmt")
}
@@ -1199,6 +1200,483 @@ $repeatLabel""")
out(" ; end jumptable")
}
private fun translate(swap: PtSwap) {
// TODO only supports a select few combinations of swappable arguments
fun swapByte() {
if(swap.target1.identifier!=null && swap.target2.identifier!=null) {
val varname1 = asmVariableName(swap.target1.identifier!!)
val varname2 = asmVariableName(swap.target2.identifier!!)
out("""
lda $varname1
ldy $varname2
sta $varname2
sty $varname1""")
}
else if(swap.target1.memory!=null && swap.target2.memory!=null) {
var var1ZpPtrVar = ""
var var2ZpPtrVar = ""
val v1 = swap.target1.memory!!
val v2 = swap.target2.memory!!
if(v1.address is PtIdentifier && v2.address is PtIdentifier) {
var1ZpPtrVar = asmVariableName(v1.address as PtIdentifier)
var2ZpPtrVar = asmVariableName(v2.address as PtIdentifier)
if(!isZpVar(v1.address as PtIdentifier)) {
out(" lda $var1ZpPtrVar | ldy $var1ZpPtrVar+1 | sta P8ZP_SCRATCH_W1 | sty P8ZP_SCRATCH_W1+1")
var1ZpPtrVar = "P8ZP_SCRATCH_W1"
}
if(!isZpVar(v2.address as PtIdentifier)) {
out(" lda $var2ZpPtrVar | ldy $var2ZpPtrVar+1 | sta P8ZP_SCRATCH_W2 | sty P8ZP_SCRATCH_W2+1")
var2ZpPtrVar = "P8ZP_SCRATCH_W2"
}
out("""
ldy #0
lda ($var1ZpPtrVar),y
pha
lda ($var2ZpPtrVar),y
sta ($var1ZpPtrVar),y
pla
sta ($var2ZpPtrVar),y""")
} else {
if (v1.address is PtNumber) {
out(" lda ${v1.address.asConstInteger()!!.toHex()} | pha")
} else if (v1.address is PtIdentifier) {
var1ZpPtrVar = loadByteFromPointerIntoA(v1.address as PtIdentifier)
out(" pha")
} else {
TODO("swap bytes not supported for this expression. Use a simpler expression, or even just a temporary variable and assignments for now. ${v1.position}")
}
if (v2.address is PtNumber) {
out(" lda ${v2.address.asConstInteger()!!.toHex()}")
} else if (v2.address is PtIdentifier) {
var2ZpPtrVar = loadByteFromPointerIntoA(
v2.address as PtIdentifier,
tempZpPtrVar = "P8ZP_SCRATCH_W1"
)
} else {
TODO("swap bytes not supported for this expression. Use a simpler expression, or even just a temporary variable and assignments for now. ${v2.position}")
}
if (v1.address is PtNumber) {
out(" sta ${v1.address.asConstInteger()!!.toHex()}")
} else if (v1.address is PtIdentifier) {
storeIndirectByteReg(CpuRegister.A, var1ZpPtrVar, 0u, false, false)
}
if (v2.address is PtNumber) {
out(" pla | sta ${v2.address.asConstInteger()!!.toHex()}")
} else if (v2.address is PtIdentifier) {
out(" pla")
storeIndirectByteReg(CpuRegister.A, var2ZpPtrVar, 0u, false, false)
}
}
}
else if(swap.target1.pointerDeref!=null || swap.target2.pointerDeref!=null) {
throw AssemblyError("swap bytes pointer dereference should have been replaced by swap memorybytes ${swap.position}")
}
else if(swap.target1.array?.variable?.type?.isPointer==true || swap.target2.array?.variable?.type?.isPointer==true) {
TODO("swap bytes expressions not supported yet for these expressions. Use a simpler expression, or even just a temporary variable and assignments for now. ${swap.position}")
}
else if(swap.target1.array?.pointerderef==null && swap.target1.array?.index?.isSimple()==true && swap.target2.array?.pointerderef==null && swap.target2.array?.index?.isSimple()==true) {
loadScaledArrayIndexIntoRegister(swap.target1.array!!, CpuRegister.X)
loadScaledArrayIndexIntoRegister(swap.target2.array!!, CpuRegister.Y)
val varname1 = asmVariableName(swap.target1.array!!.variable!!.name)
val varname2 = asmVariableName(swap.target2.array!!.variable!!.name)
out("""
lda $varname1,x
pha
lda $varname2,y
sta $varname1,x
pla
sta $varname2,y""")
}
else if(swap.target1.array!=null && swap.target2.array!=null && swap.target1.array!!.pointerderef==null && swap.target2.array!!.pointerderef==null) {
val offset1 = simpleOffsetIndexer(swap.target1.array!!)
val offset2 = simpleOffsetIndexer(swap.target2.array!!)
if(offset1==null || offset2==null)
TODO("swap bytes not supported yet for nontrivial indexed[] expressions. Use a simpler index expression, or even just temporary variable and assignments for now. ${swap.position}")
else {
val arrayname1 = asmVariableName(swap.target1.array!!.variable!!.name)
val arrayname2 = asmVariableName(swap.target1.array!!.variable!!.name)
val offsetname1 = asmVariableName(offset1.first)
val offsetname2 = asmVariableName(offset2.first)
val op1 = offset1.second
val op2 = offset2.second
out("""
ldx $offsetname1
ldy $offsetname2
lda $arrayname1 $op1 ${offset1.third},x
pha
lda $arrayname2 $op2 ${offset2.third},y
sta $arrayname1 $op1 ${offset1.third},x
pla
sta $arrayname2 $op2 ${offset2.third},y""")
}
}
else {
TODO("swap bytes not supported yet for these expressions. Use a simpler expression, or even just temporary variable and assignments for now. ${swap.position}")
}
}
fun swapWord() {
if(swap.target1.identifier!=null && swap.target2.identifier!=null) {
val varname1 = asmVariableName(swap.target1.identifier!!)
val varname2 = asmVariableName(swap.target2.identifier!!)
out("""
lda $varname1
ldy $varname2
sta $varname2
sty $varname1
lda $varname1+1
ldy $varname2+1
sta $varname2+1
sty $varname1+1""")
}
else if(swap.target1.pointerDeref!=null && swap.target2.pointerDeref!=null) {
val v1 = swap.target1.pointerDeref!!
val v2 = swap.target1.pointerDeref!!
if(v1.derefLast && v1.chain.isEmpty() && v2.derefLast && v2.chain.isEmpty() && isZpVar(v1.startpointer) && isZpVar(v2.startpointer)) {
// optimized case where v1 and v2 are both already zeropage pointer variables
val name1 = asmVariableName(v1.startpointer)
val name2 = asmVariableName(v2.startpointer)
out("""
ldy #0
lda ($name1),y
pha
lda ($name2),y
sta ($name1),y
pla
sta ($name2),y
iny
lda ($name1),y
pha
lda ($name2),y
sta ($name1),y
pla
sta ($name2),y""")
} else {
val (zpVar, offset) = pointerGen.deref(v1, true)
require(offset == 0.toUByte())
out(" lda $zpVar | ldy $zpVar+1 | sta P8ZP_SCRATCH_W1 | sty P8ZP_SCRATCH_W1+1")
val (zpVar2, offset2) = pointerGen.deref(v2, true)
require(offset2 == 0.toUByte())
out(" lda $zpVar2 | ldy $zpVar2+1 | jsr prog8_lib.swap_words")
}
}
else if(swap.target1.array?.variable?.type?.isPointer==true || swap.target2.array?.variable?.type?.isPointer==true) {
TODO("swap words expressions not supported yet for these expressions. Use a simpler expression, or even just a temporary variable and assignments for now. ${swap.position}")
}
else if(swap.target1.array?.pointerderef==null && swap.target1.array?.index?.isSimple()==true && swap.target2.array?.pointerderef==null && swap.target2.array?.index?.isSimple()==true) {
val v1 = swap.target1.array!!
val v2 = swap.target2.array!!
loadScaledArrayIndexIntoRegister(v1, CpuRegister.X)
loadScaledArrayIndexIntoRegister(v2, CpuRegister.Y)
val varname1 = asmVariableName(v1.variable!!.name)
val varname2 = asmVariableName(v2.variable!!.name)
if(v1.splitWords && v2.splitWords) {
// both of them are split-words arrays
out("""
lda ${varname1}_lsb,x
pha
lda ${varname2}_lsb,y
sta ${varname1}_lsb,x
pla
sta ${varname2}_lsb,y
lda ${varname1}_msb,x
pha
lda ${varname2}_msb,y
sta ${varname1}_msb,x
pla
sta ${varname2}_msb,y""")
} else if(v1.splitWords || v2.splitWords) {
TODO("swap words expressions not supported yet for 1 @split and 1 normal word array. Make them both the same, or use a temporary variable and assignments for now. ${swap.position}")
} else {
out("""
lda $varname1,x
pha
lda $varname2,y
sta $varname1,x
pla
sta $varname2,y
lda $varname1+1,x
pha
lda $varname2+1,y
sta $varname1+1,x
pla
sta $varname2+1,y""")
}
}
else if(swap.target1.array!=null && swap.target2.array!=null && swap.target1.array!!.pointerderef==null && swap.target2.array!!.pointerderef==null) {
val v1 = swap.target1.array!!
val v2 = swap.target2.array!!
val offset1 = simpleOffsetIndexer(v1)
val offset2 = simpleOffsetIndexer(v2)
if(offset1==null || offset2==null)
TODO("swap words not supported yet for nontrivial indexed[] expressions. Use a simpler index expression, or even just temporary variable and assignments for now. ${swap.position}")
else {
val arrayname1 = asmVariableName(v1.variable!!.name)
val arrayname2 = asmVariableName(v2.variable!!.name)
val offsetname1 = asmVariableName(offset1.first)
val offsetname2 = asmVariableName(offset2.first)
val op1 = offset1.second
val op2 = offset2.second
if(v1.splitWords && v2.splitWords) {
out("""
ldx $offsetname1
ldy $offsetname2
lda ${arrayname1}_lsb $op1 ${offset1.third},x
pha
lda ${arrayname2}_lsb $op2 ${offset2.third},y
sta ${arrayname1}_lsb $op1 ${offset1.third},x
pla
sta ${arrayname2}_lsb $op2 ${offset2.third},y
lda ${arrayname1}_msb $op1 ${offset1.third},x
pha
lda ${arrayname2}_msb $op2 ${offset2.third},y
sta ${arrayname1}_msb $op1 ${offset1.third},x
pla
sta ${arrayname2}_msb $op2 ${offset2.third},y""")
}
else {
out("""
lda $offsetname1
asl a
tax
lda $offsetname2
asl a
tay
lda $arrayname1 $op1 ${offset1.third * 2},x
pha
lda $arrayname2 $op2 ${offset2.third * 2},y
sta $arrayname1 $op1 ${offset1.third * 2},x
pla
sta $arrayname2 $op2 ${offset2.third * 2},y
lda $arrayname1+1 $op1 ${offset1.third * 2},x
pha
lda $arrayname2+1 $op2 ${offset2.third * 2},y
sta $arrayname1+1 $op1 ${offset1.third * 2},x
pla
sta $arrayname2+1 $op2 ${offset2.third * 2},y""")
}
}
}
else {
TODO("swap words expressions not supported yet for these expressions. Use a simpler expression, or even just a temporary variable and assignments for now. ${swap.position}")
}
}
fun swapLong() {
if(swap.target1.identifier!=null && swap.target2.identifier!=null) {
val varname1 = asmVariableName(swap.target1.identifier!!)
val varname2 = asmVariableName(swap.target2.identifier!!)
out("""
lda $varname1
ldy $varname2
sta $varname2
sty $varname1
lda $varname1+1
ldy $varname2+1
sta $varname2+1
sty $varname1+1
lda $varname1+2
ldy $varname2+2
sta $varname2+2
sty $varname1+2
lda $varname1+3
ldy $varname2+3
sta $varname2+3
sty $varname1+3""")
}
else if(swap.target1.pointerDeref!=null && swap.target2.pointerDeref!=null) {
val (zpVar, offset) = pointerGen.deref(swap.target1.pointerDeref!!, true)
require(offset == 0.toUByte())
out(" lda $zpVar | ldy $zpVar+1 | sta P8ZP_SCRATCH_W1 | sty P8ZP_SCRATCH_W1+1")
val (zpVar2, offset2) = pointerGen.deref(swap.target2.pointerDeref!!, true)
require(offset2 == 0.toUByte())
out(" lda $zpVar2 | ldy $zpVar2+1 | jsr prog8_lib.swap_longs")
}
else if(swap.target1.array?.variable?.type?.isPointer==true || swap.target2.array?.variable?.type?.isPointer==true) {
TODO("swap longs expressions not supported yet for these expressions. Use a simpler expression, or even just a temporary variable and assignments for now. ${swap.position}")
}
else if(swap.target1.array?.pointerderef==null && swap.target1.array?.index?.isSimple()==true && swap.target2.array?.pointerderef==null && swap.target2.array?.index?.isSimple()==true) {
val v1 = swap.target1.array!!
val v2 = swap.target2.array!!
loadScaledArrayIndexIntoRegister(v1, CpuRegister.X)
loadScaledArrayIndexIntoRegister(v2, CpuRegister.Y)
val varname1 = asmVariableName(v1.variable!!.name)
val varname2 = asmVariableName(v2.variable!!.name)
out("""
lda #4
sta P8ZP_SCRATCH_REG
- lda $varname1,x
pha
lda $varname2,y
sta $varname1,x
pla
sta $varname2,y
inx
iny
dec P8ZP_SCRATCH_REG
bne -""")
}
else if(swap.target1.array!=null && swap.target2.array!=null && swap.target1.array!!.pointerderef==null && swap.target2.array!!.pointerderef==null) {
val v1 = swap.target1.array!!
val v2 = swap.target2.array!!
val offset1 = simpleOffsetIndexer(v1)
val offset2 = simpleOffsetIndexer(v2)
if(offset1==null || offset2==null)
TODO("swap longs not supported yet for nontrivial indexed[] expressions. Use a simpler expression, or even just temporary variable and assignments for now. ${swap.position}")
else {
val arrayname1 = asmVariableName(v1.variable!!.name)
val arrayname2 = asmVariableName(v2.variable!!.name)
val offsetname1 = asmVariableName(offset1.first)
val offsetname2 = asmVariableName(offset2.first)
val op1 = offset1.second
val op2 = offset2.second
out("""
lda $offsetname1
asl a
asl a
tax
lda $offsetname2
asl a
asl a
tay
lda #4
sta P8ZP_SCRATCH_REG
- lda $arrayname1 $op1 ${offset1.third * 4},x
pha
lda $arrayname2 $op2 ${offset2.third * 4},y
sta $arrayname1 $op1 ${offset1.third * 4},x
pla
sta $arrayname2 $op2 ${offset2.third * 4},y
inx
iny
dec P8ZP_SCRATCH_REG
bne -""")
}
}
else {
TODO("swap longs expressions not supported yet for these expressions. Use a simpler expression, or even just a temporary variable and assignments for now. ${swap.position}")
}
}
fun swapFloat() {
if(swap.target1.identifier!=null && swap.target2.identifier!=null) {
val varname1 = asmVariableName(swap.target1.identifier!!)
val varname2 = asmVariableName(swap.target2.identifier!!)
out("""
lda #<$varname1
ldy #>$varname1
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
lda #<$varname2
ldy #>$varname2
jsr floats.swap_floats""")
}
else if(swap.target1.pointerDeref!=null || swap.target2.pointerDeref!=null) {
val (zpVar, offset) = pointerGen.deref(swap.target1.pointerDeref!!, true)
require(offset == 0.toUByte())
out(" lda $zpVar | ldy $zpVar+1 | sta P8ZP_SCRATCH_W1 | sty P8ZP_SCRATCH_W1+1")
val (zpVar2, offset2) = pointerGen.deref(swap.target2.pointerDeref!!, true)
require(offset2 == 0.toUByte())
out(" lda $zpVar2 | ldy $zpVar2+1 | jsr floats.swap_floats")
}
else if(swap.target1.array?.variable?.type?.isPointer==true || swap.target2.array?.variable?.type?.isPointer==true) {
TODO("swap floats expressions not supported yet for these expressions. Use a simpler expression, or even just a temporary variable and assignments for now. ${swap.position}")
}
else if(swap.target1.array?.pointerderef==null && swap.target1.array?.index?.isSimple()==true && swap.target2.array?.pointerderef==null && swap.target2.array?.index?.isSimple()==true) {
val v1 = swap.target1.array!!
val v2 = swap.target2.array!!
loadScaledArrayIndexIntoRegister(v1, CpuRegister.X)
loadScaledArrayIndexIntoRegister(v2, CpuRegister.Y)
val varname1 = asmVariableName(v1.variable!!.name)
val varname2 = asmVariableName(v2.variable!!.name)
out("""
lda #5
sta P8ZP_SCRATCH_REG
- lda $varname1,x
pha
lda $varname2,y
sta $varname1,x
pla
sta $varname2,y
inx
iny
dec P8ZP_SCRATCH_REG
bne -""")
}
else if(swap.target1.array!=null && swap.target2.array!=null && swap.target1.array!!.pointerderef==null && swap.target2.array!!.pointerderef==null) {
val v1 = swap.target1.array!!
val v2 = swap.target2.array!!
val offset1 = simpleOffsetIndexer(v1)
val offset2 = simpleOffsetIndexer(v2)
if(offset1==null || offset2==null)
TODO("swap floats not supported yet for nontrivial indexed[] expressions. Use a simpler expression, or even just temporary variable and assignments for now. ${swap.position}")
else {
require(options.compTarget.FLOAT_MEM_SIZE==5)
val arrayname1 = asmVariableName(v1.variable!!.name)
val arrayname2 = asmVariableName(v2.variable!!.name)
val offsetname1 = asmVariableName(offset1.first)
val offsetname2 = asmVariableName(offset2.first)
val op1 = offset1.second
val op2 = offset2.second
out("""
lda $offsetname1
asl a
asl a
clc
adc $offsetname1
tax
lda $offsetname2
asl a
asl a
clc
adc $offsetname2
tay
lda #5
sta P8ZP_SCRATCH_REG
- lda $arrayname1 $op1 ${offset1.third * 5},x
pha
lda $arrayname2 $op2 ${offset2.third * 5},y
sta $arrayname1 $op1 ${offset1.third * 5},x
pla
sta $arrayname2 $op2 ${offset2.third * 5},y
inx
iny
dec P8ZP_SCRATCH_REG
bne -""")
}
}
else {
TODO("swap floats expressions not supported yet for these expressions. Use a simpler expression, or even just a temporary variable and assignments for now. ${swap.position}")
}
}
val dt = swap.target1.type
when {
dt.isByteOrBool -> swapByte()
dt.isWord || dt.isPointer -> swapWord()
dt.isLong -> swapLong()
dt.isFloat -> swapFloat()
else -> throw AssemblyError("weird type $dt")
}
}
private fun simpleOffsetIndexer(indexer: PtArrayIndexer): Triple<PtIdentifier, Char, Int>? {
val expr = indexer.index as? PtBinaryExpression
if(expr!=null && expr.operator in "+-") {
val identifier = expr.left as? PtIdentifier ?: return null
val offset = expr.right as? PtNumber ?: return null
return Triple(identifier, expr.operator.first(), offset.number.toInt())
}
val identifier = indexer.index as? PtIdentifier
return if(identifier!=null)
Triple(identifier,'+',0)
else
null
}
private fun translate(stmt: PtConditionalBranch) {
if(stmt.trueScope.children.isEmpty() && stmt.falseScope.children.isNotEmpty())
throw AssemblyError("only else part contains code, shoud have been switched already")
@@ -40,7 +40,6 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
"min__byte", "min__ubyte", "min__word", "min__uword", "min__long" -> funcMin(fcall, resultRegister)
"max__byte", "max__ubyte", "max__word", "max__uword", "max__long" -> funcMax(fcall, resultRegister)
"abs__byte", "abs__word", "abs__long", "abs__float" -> funcAbs(fcall, resultRegister, sscope)
"swap__byte", "swap__word", "swap__long", "swap__float" -> funcSwap(fcall)
"sgn" -> funcSgn(fcall, resultRegister, sscope)
"sqrt__ubyte", "sqrt__uword", "sqrt__long", "sqrt__float" -> funcSqrt(fcall, resultRegister, sscope)
"divmod__ubyte" -> funcDivmod(fcall)
@@ -997,488 +996,6 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, false, fcall.position, scope, asmgen), CpuRegister.A, true, true)
}
private fun funcSwap(fcall: PtBuiltinFunctionCall) {
val (v1,v2) = fcall.args
fun swapByte() {
when (v1) {
is PtIdentifier if v2 is PtIdentifier -> {
val varname1 = asmgen.asmVariableName(v1)
val varname2 = asmgen.asmVariableName(v2)
asmgen.out("""
lda $varname1
ldy $varname2
sta $varname2
sty $varname1""")
}
is PtMemoryByte if v2 is PtMemoryByte -> {
var var1ZpPtrVar = ""
var var2ZpPtrVar = ""
if(v1.address is PtIdentifier && v2.address is PtIdentifier) {
var1ZpPtrVar = asmgen.asmVariableName(v1.address as PtIdentifier)
var2ZpPtrVar = asmgen.asmVariableName(v2.address as PtIdentifier)
if(!asmgen.isZpVar(v1.address as PtIdentifier)) {
asmgen.out(" lda $var1ZpPtrVar | ldy $var1ZpPtrVar+1 | sta P8ZP_SCRATCH_W1 | sty P8ZP_SCRATCH_W1+1")
var1ZpPtrVar = "P8ZP_SCRATCH_W1"
}
if(!asmgen.isZpVar(v2.address as PtIdentifier)) {
asmgen.out(" lda $var2ZpPtrVar | ldy $var2ZpPtrVar+1 | sta P8ZP_SCRATCH_W2 | sty P8ZP_SCRATCH_W2+1")
var2ZpPtrVar = "P8ZP_SCRATCH_W2"
}
asmgen.out("""
ldy #0
lda ($var1ZpPtrVar),y
pha
lda ($var2ZpPtrVar),y
sta ($var1ZpPtrVar),y
pla
sta ($var2ZpPtrVar),y""")
} else {
if (v1.address is PtNumber) {
asmgen.out(" lda ${v1.address.asConstInteger()!!.toHex()} | pha")
} else if (v1.address is PtIdentifier) {
var1ZpPtrVar = asmgen.loadByteFromPointerIntoA(v1.address as PtIdentifier)
asmgen.out(" pha")
} else {
TODO("swap bytes not supported for this expression. Use a simpler expression, or even just a temporary variable and assignments for now. ${v1.position}")
}
if (v2.address is PtNumber) {
asmgen.out(" lda ${v2.address.asConstInteger()!!.toHex()}")
} else if (v2.address is PtIdentifier) {
var2ZpPtrVar = asmgen.loadByteFromPointerIntoA(
v2.address as PtIdentifier,
tempZpPtrVar = "P8ZP_SCRATCH_W1"
)
} else {
TODO("swap bytes not supported for this expression. Use a simpler expression, or even just a temporary variable and assignments for now. ${v2.position}")
}
if (v1.address is PtNumber) {
asmgen.out(" sta ${v1.address.asConstInteger()!!.toHex()}")
} else if (v1.address is PtIdentifier) {
asmgen.storeIndirectByteReg(CpuRegister.A, var1ZpPtrVar, 0u, false, false)
}
if (v2.address is PtNumber) {
asmgen.out(" pla | sta ${v2.address.asConstInteger()!!.toHex()}")
} else if (v2.address is PtIdentifier) {
asmgen.out(" pla")
asmgen.storeIndirectByteReg(CpuRegister.A, var2ZpPtrVar, 0u, false, false)
}
}
}
is PtPointerDeref if v2 is PtPointerDeref -> {
TODO("swap bytes pointer dereference should not occur instead memorybyte? ${fcall.position}")
}
is PtArrayIndexer if v2 is PtArrayIndexer && (v1.variable?.type?.isPointer==true || v2.variable?.type?.isPointer==true) -> {
TODO("swap bytes expressions not supported yet for these expressions. Use a simpler expression, or even just a temporary variable and assignments for now. ${fcall.position}")
}
is PtArrayIndexer if v2 is PtArrayIndexer && v1.pointerderef==null && v1.index.isSimple() && v2.pointerderef==null && v2.index.isSimple() -> {
asmgen.loadScaledArrayIndexIntoRegister(v1, CpuRegister.X)
asmgen.loadScaledArrayIndexIntoRegister(v2, CpuRegister.Y)
val varname1 = asmgen.asmVariableName(v1.variable!!.name)
val varname2 = asmgen.asmVariableName(v2.variable!!.name)
asmgen.out("""
lda $varname1,x
pha
lda $varname2,y
sta $varname1,x
pla
sta $varname2,y""")
}
is PtArrayIndexer if v2 is PtArrayIndexer && v1.pointerderef==null && v2.pointerderef==null -> {
val offset1 = simpleOffsetIndexer(v1)
val offset2 = simpleOffsetIndexer(v2)
if(offset1==null || offset2==null)
TODO("swap bytes not supported yet for nontrivial indexed[] expressions. Use a simpler index expression, or even just temporary variable and assignments for now. ${fcall.position}")
else {
val arrayname1 = asmgen.asmVariableName(v1.variable!!.name)
val arrayname2 = asmgen.asmVariableName(v2.variable!!.name)
val offsetname1 = asmgen.asmVariableName(offset1.first)
val offsetname2 = asmgen.asmVariableName(offset2.first)
val op1 = offset1.second
val op2 = offset2.second
asmgen.out("""
ldx $offsetname1
ldy $offsetname2
lda $arrayname1 $op1 ${offset1.third},x
pha
lda $arrayname2 $op2 ${offset2.third},y
sta $arrayname1 $op1 ${offset1.third},x
pla
sta $arrayname2 $op2 ${offset2.third},y""")
}
}
else -> {
TODO("swap bytes not supported yet for these expressions. Use a simpler expression, or even just temporary variable and assignments for now. ${fcall.position}")
}
}
}
fun swapWord() {
when (v1) {
is PtIdentifier if v2 is PtIdentifier -> {
val varname1 = asmgen.asmVariableName(v1)
val varname2 = asmgen.asmVariableName(v2)
asmgen.out("""
lda $varname1
ldy $varname2
sta $varname2
sty $varname1
lda $varname1+1
ldy $varname2+1
sta $varname2+1
sty $varname1+1""")
}
is PtPointerDeref if v2 is PtPointerDeref -> {
if(v1.derefLast && v1.chain.isEmpty() && v2.derefLast && v2.chain.isEmpty() && asmgen.isZpVar(v1.startpointer) && asmgen.isZpVar(v2.startpointer)) {
// optimized case where v1 and v2 are both already zeropage pointer variables
val name1 = asmgen.asmVariableName(v1.startpointer)
val name2 = asmgen.asmVariableName(v2.startpointer)
asmgen.out("""
ldy #0
lda ($name1),y
pha
lda ($name2),y
sta ($name1),y
pla
sta ($name2),y
iny
lda ($name1),y
pha
lda ($name2),y
sta ($name1),y
pla
sta ($name2),y""")
} else {
val (zpVar, offset) = ptrgen.deref(v1, true)
require(offset == 0.toUByte())
asmgen.out(" lda $zpVar | ldy $zpVar+1 | sta P8ZP_SCRATCH_W1 | sty P8ZP_SCRATCH_W1+1")
val (zpVar2, offset2) = ptrgen.deref(v2, true)
require(offset2 == 0.toUByte())
asmgen.out(" lda $zpVar2 | ldy $zpVar2+1 | jsr prog8_lib.swap_words")
}
}
is PtArrayIndexer if v2 is PtArrayIndexer && (v1.variable?.type?.isPointer==true || v2.variable?.type?.isPointer==true) -> {
TODO("swap words expressions not supported yet for these expressions. Use a simpler expression, or even just a temporary variable and assignments for now. ${fcall.position}")
}
is PtArrayIndexer if v2 is PtArrayIndexer && v1.pointerderef==null && v1.index.isSimple() && v2.pointerderef==null && v2.index.isSimple() -> {
asmgen.loadScaledArrayIndexIntoRegister(v1, CpuRegister.X)
asmgen.loadScaledArrayIndexIntoRegister(v2, CpuRegister.Y)
val varname1 = asmgen.asmVariableName(v1.variable!!.name)
val varname2 = asmgen.asmVariableName(v2.variable!!.name)
if(v1.splitWords && v2.splitWords) {
// both of them are split-words arrays
asmgen.out("""
lda ${varname1}_lsb,x
pha
lda ${varname2}_lsb,y
sta ${varname1}_lsb,x
pla
sta ${varname2}_lsb,y
lda ${varname1}_msb,x
pha
lda ${varname2}_msb,y
sta ${varname1}_msb,x
pla
sta ${varname2}_msb,y""")
} else if(v1.splitWords || v2.splitWords) {
TODO("swap words expressions not supported yet for 1 @split and 1 normal word array. Make them both the same, or use a temporary variable and assignments for now. ${fcall.position}")
} else {
asmgen.out("""
lda $varname1,x
pha
lda $varname2,y
sta $varname1,x
pla
sta $varname2,y
lda $varname1+1,x
pha
lda $varname2+1,y
sta $varname1+1,x
pla
sta $varname2+1,y""")
}
}
is PtArrayIndexer if v2 is PtArrayIndexer && v1.pointerderef==null && v2.pointerderef==null -> {
val offset1 = simpleOffsetIndexer(v1)
val offset2 = simpleOffsetIndexer(v2)
if(offset1==null || offset2==null)
TODO("swap words not supported yet for nontrivial indexed[] expressions. Use a simpler index expression, or even just temporary variable and assignments for now. ${fcall.position}")
else {
val arrayname1 = asmgen.asmVariableName(v1.variable!!.name)
val arrayname2 = asmgen.asmVariableName(v2.variable!!.name)
val offsetname1 = asmgen.asmVariableName(offset1.first)
val offsetname2 = asmgen.asmVariableName(offset2.first)
val op1 = offset1.second
val op2 = offset2.second
if(v1.splitWords && v2.splitWords) {
asmgen.out("""
ldx $offsetname1
ldy $offsetname2
lda ${arrayname1}_lsb $op1 ${offset1.third},x
pha
lda ${arrayname2}_lsb $op2 ${offset2.third},y
sta ${arrayname1}_lsb $op1 ${offset1.third},x
pla
sta ${arrayname2}_lsb $op2 ${offset2.third},y
lda ${arrayname1}_msb $op1 ${offset1.third},x
pha
lda ${arrayname2}_msb $op2 ${offset2.third},y
sta ${arrayname1}_msb $op1 ${offset1.third},x
pla
sta ${arrayname2}_msb $op2 ${offset2.third},y""")
}
else {
asmgen.out("""
lda $offsetname1
asl a
tax
lda $offsetname2
asl a
tay
lda $arrayname1 $op1 ${offset1.third * 2},x
pha
lda $arrayname2 $op2 ${offset2.third * 2},y
sta $arrayname1 $op1 ${offset1.third * 2},x
pla
sta $arrayname2 $op2 ${offset2.third * 2},y
lda $arrayname1+1 $op1 ${offset1.third * 2},x
pha
lda $arrayname2+1 $op2 ${offset2.third * 2},y
sta $arrayname1+1 $op1 ${offset1.third * 2},x
pla
sta $arrayname2+1 $op2 ${offset2.third * 2},y""")
}
}
}
else -> {
TODO("swap words expressions not supported yet for these expressions. Use a simpler expression, or even just a temporary variable and assignments for now. ${fcall.position}")
}
}
}
fun swapLong() {
when (v1) {
is PtIdentifier if v2 is PtIdentifier -> {
val varname1 = asmgen.asmVariableName(v1)
val varname2 = asmgen.asmVariableName(v2)
asmgen.out("""
lda $varname1
ldy $varname2
sta $varname2
sty $varname1
lda $varname1+1
ldy $varname2+1
sta $varname2+1
sty $varname1+1
lda $varname1+2
ldy $varname2+2
sta $varname2+2
sty $varname1+2
lda $varname1+3
ldy $varname2+3
sta $varname2+3
sty $varname1+3""")
}
is PtPointerDeref if v2 is PtPointerDeref -> {
val (zpVar, offset) = ptrgen.deref(v1, true)
require(offset == 0.toUByte())
asmgen.out(" lda $zpVar | ldy $zpVar+1 | sta P8ZP_SCRATCH_W1 | sty P8ZP_SCRATCH_W1+1")
val (zpVar2, offset2) = ptrgen.deref(v2, true)
require(offset2 == 0.toUByte())
asmgen.out(" lda $zpVar2 | ldy $zpVar2+1 | jsr prog8_lib.swap_longs")
}
is PtArrayIndexer if v2 is PtArrayIndexer && (v1.variable?.type?.isPointer==true || v2.variable?.type?.isPointer==true) -> {
TODO("swap longs expressions not supported yet for these expressions. Use a simpler expression, or even just a temporary variable and assignments for now. ${fcall.position}")
}
is PtArrayIndexer if v2 is PtArrayIndexer && v1.pointerderef==null && v1.index.isSimple() && v2.pointerderef==null && v2.index.isSimple() -> {
asmgen.loadScaledArrayIndexIntoRegister(v1, CpuRegister.X)
asmgen.loadScaledArrayIndexIntoRegister(v2, CpuRegister.Y)
val varname1 = asmgen.asmVariableName(v1.variable!!.name)
val varname2 = asmgen.asmVariableName(v2.variable!!.name)
asmgen.out("""
lda #4
sta P8ZP_SCRATCH_REG
- lda $varname1,x
pha
lda $varname2,y
sta $varname1,x
pla
sta $varname2,y
inx
iny
dec P8ZP_SCRATCH_REG
bne -""")
}
is PtArrayIndexer if v2 is PtArrayIndexer && v1.pointerderef==null && v2.pointerderef==null -> {
val offset1 = simpleOffsetIndexer(v1)
val offset2 = simpleOffsetIndexer(v2)
if(offset1==null || offset2==null)
TODO("swap longs not supported yet for nontrivial indexed[] expressions. Use a simpler expression, or even just temporary variable and assignments for now. ${fcall.position}")
else {
val arrayname1 = asmgen.asmVariableName(v1.variable!!.name)
val arrayname2 = asmgen.asmVariableName(v2.variable!!.name)
val offsetname1 = asmgen.asmVariableName(offset1.first)
val offsetname2 = asmgen.asmVariableName(offset2.first)
val op1 = offset1.second
val op2 = offset2.second
asmgen.out("""
lda $offsetname1
asl a
asl a
tax
lda $offsetname2
asl a
asl a
tay
lda #4
sta P8ZP_SCRATCH_REG
- lda $arrayname1 $op1 ${offset1.third * 4},x
pha
lda $arrayname2 $op2 ${offset2.third * 4},y
sta $arrayname1 $op1 ${offset1.third * 4},x
pla
sta $arrayname2 $op2 ${offset2.third * 4},y
inx
iny
dec P8ZP_SCRATCH_REG
bne -""")
}
}
else -> {
TODO("swap longs expressions not supported yet for these expressions. Use a simpler expression, or even just a temporary variable and assignments for now. ${fcall.position}")
}
}
}
fun swapFloat() {
when (v1) {
is PtIdentifier if v2 is PtIdentifier -> {
val varname1 = asmgen.asmVariableName(v1)
val varname2 = asmgen.asmVariableName(v2)
asmgen.out("""
lda #<$varname1
ldy #>$varname1
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
lda #<$varname2
ldy #>$varname2
jsr floats.swap_floats""")
}
is PtPointerDeref if v2 is PtPointerDeref -> {
val (zpVar, offset) = ptrgen.deref(v1, true)
require(offset == 0.toUByte())
asmgen.out(" lda $zpVar | ldy $zpVar+1 | sta P8ZP_SCRATCH_W1 | sty P8ZP_SCRATCH_W1+1")
val (zpVar2, offset2) = ptrgen.deref(v2, true)
require(offset2 == 0.toUByte())
asmgen.out(" lda $zpVar2 | ldy $zpVar2+1 | jsr floats.swap_floats")
}
is PtArrayIndexer if v2 is PtArrayIndexer && (v1.variable?.type?.isPointer==true || v2.variable?.type?.isPointer==true) -> {
TODO("swap floats expressions not supported yet for these expressions. Use a simpler expression, or even just a temporary variable and assignments for now. ${fcall.position}")
}
is PtArrayIndexer if v2 is PtArrayIndexer && v1.pointerderef==null && v1.index.isSimple() && v2.pointerderef==null && v2.index.isSimple() -> {
asmgen.loadScaledArrayIndexIntoRegister(v1, CpuRegister.X)
asmgen.loadScaledArrayIndexIntoRegister(v2, CpuRegister.Y)
val varname1 = asmgen.asmVariableName(v1.variable!!.name)
val varname2 = asmgen.asmVariableName(v2.variable!!.name)
asmgen.out("""
lda #5
sta P8ZP_SCRATCH_REG
- lda $varname1,x
pha
lda $varname2,y
sta $varname1,x
pla
sta $varname2,y
inx
iny
dec P8ZP_SCRATCH_REG
bne -""")
}
is PtArrayIndexer if v2 is PtArrayIndexer && v1.pointerderef==null && v2.pointerderef==null -> {
val offset1 = simpleOffsetIndexer(v1)
val offset2 = simpleOffsetIndexer(v2)
if(offset1==null || offset2==null)
TODO("swap floats not supported yet for nontrivial indexed[] expressions. Use a simpler expression, or even just temporary variable and assignments for now. ${fcall.position}")
else {
require(asmgen.options.compTarget.FLOAT_MEM_SIZE==5)
val arrayname1 = asmgen.asmVariableName(v1.variable!!.name)
val arrayname2 = asmgen.asmVariableName(v2.variable!!.name)
val offsetname1 = asmgen.asmVariableName(offset1.first)
val offsetname2 = asmgen.asmVariableName(offset2.first)
val op1 = offset1.second
val op2 = offset2.second
asmgen.out("""
lda $offsetname1
asl a
asl a
clc
adc $offsetname1
tax
lda $offsetname2
asl a
asl a
clc
adc $offsetname2
tay
lda #5
sta P8ZP_SCRATCH_REG
- lda $arrayname1 $op1 ${offset1.third * 5},x
pha
lda $arrayname2 $op2 ${offset2.third * 5},y
sta $arrayname1 $op1 ${offset1.third * 5},x
pla
sta $arrayname2 $op2 ${offset2.third * 5},y
inx
iny
dec P8ZP_SCRATCH_REG
bne -""")
}
}
else -> {
TODO("swap floats expressions not supported yet for these expressions. Use a simpler expression, or even just a temporary variable and assignments for now. ${fcall.position}")
}
}
}
val dt = v1.type
when {
dt.isByteOrBool -> swapByte()
dt.isWord || dt.isPointer -> swapWord()
dt.isLong -> swapLong()
dt.isFloat -> swapFloat()
else -> throw AssemblyError("weird type $dt")
}
}
private fun simpleOffsetIndexer(indexer: PtArrayIndexer): Triple<PtIdentifier, Char, Int>? {
val expr = indexer.index as? PtBinaryExpression
if(expr!=null && expr.operator in "+-") {
val identifier = expr.left as? PtIdentifier ?: return null
val offset = expr.right as? PtNumber ?: return null
return Triple(identifier, expr.operator.first(), offset.number.toInt())
}
val identifier = indexer.index as? PtIdentifier
return if(identifier!=null)
Triple(identifier,'+',0)
else
null
}
private fun funcAbs(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?, scope: IPtSubroutine?) {
translateArguments(fcall, null, scope)
val dt = fcall.args.single().type.base
@@ -62,10 +62,6 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
"prog8_lib_copyfloat" -> funcCopyFromPointer1ToPointer2(call, IRDataType.FLOAT)
"sizeof" -> throw AssemblyError("sizeof must have been replaced with a constant")
"offsetof" -> throw AssemblyError("offsetof must have been replaced with a constant")
"swap__byte" -> funcSwap(call)
"swap__word" -> funcSwap(call)
"swap__long" -> funcSwap(call)
"swap__float" -> funcSwap(call)
else -> throw AssemblyError("missing builtinfunc for ${call.name}")
}
}
@@ -184,8 +180,8 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
// DIVMOD result convention: on value stack, division and remainder on top.
addInstr(result, IRInstruction(Opcode.POP, type, reg1=remainderReg), null)
addInstr(result, IRInstruction(Opcode.POP, type, reg1=divisionReg), null)
result += assignRegisterTo(call.args[2], divisionReg)
result += assignRegisterTo(call.args[3], remainderReg)
result += codeGen.assignRegisterTo(call.args[2], divisionReg)
result += codeGen.assignRegisterTo(call.args[3], remainderReg)
return ExpressionCodeResult(result, type, -1, -1)
}
@@ -463,28 +459,6 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
return ExpressionCodeResult(result, type, leftTr.resultReg, -1)
}
private fun funcSwap(call: PtBuiltinFunctionCall): ExpressionCodeResult {
require(call.args[0].type == call.args[1].type)
val result = mutableListOf<IRCodeChunkBase>()
val dt = irType(call.args[0].type)
val t1 = exprGen.translateExpression(call.args[0])
val t2 = exprGen.translateExpression(call.args[1])
if(dt==IRDataType.FLOAT) {
addToResult(result, t1, -1, t1.resultFpReg)
addToResult(result, t2, -1, t2.resultFpReg)
addInstr(result, IRInstruction(Opcode.SWAP, dt, fpReg1=t1.resultFpReg, fpReg2=t2.resultFpReg), null)
result += assignFpRegisterTo(call.args[0], t1.resultFpReg)
result += assignFpRegisterTo( call.args[1], t2.resultFpReg)
} else {
addToResult(result, t1, t1.resultReg, -1)
addToResult(result, t2, t2.resultReg, -1)
addInstr(result, IRInstruction(Opcode.SWAP, dt, reg1=t1.resultReg, reg2=t2.resultReg), null)
result += assignRegisterTo(call.args[0], t1.resultReg)
result += assignRegisterTo( call.args[1], t2.resultReg)
}
return ExpressionCodeResult(result, IRDataType.BYTE, -1, -1)
}
private fun funcPoke(call: PtBuiltinFunctionCall, dt: IRDataType): ExpressionCodeResult {
val result = mutableListOf<IRCodeChunkBase>()
@@ -754,7 +728,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
addInstr(result, IRInstruction(opcode, vmDt, reg1 = tr.resultReg), null)
if(saveCarry)
addInstr(result, IRInstruction(Opcode.PUSHST), null) // save Carry
result += assignRegisterTo(arg, tr.resultReg)
result += codeGen.assignRegisterTo(arg, tr.resultReg)
if(saveCarry)
addInstr(result, IRInstruction(Opcode.POPST), null)
return ExpressionCodeResult(result, vmDt, -1, -1)
@@ -893,28 +867,4 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
}
return ExpressionCodeResult(result, IRDataType.WORD, -1, -1)
}
private fun assignRegisterTo(target: PtExpression, register: Int): IRCodeChunks {
val assignment = PtAssignment(target.position)
val assignTarget = PtAssignTarget(false, target.position)
assignTarget.children.add(target)
assignment.children.add(assignTarget)
assignment.children.add(PtIrRegister(register, target.type, target.position))
val result = mutableListOf<IRCodeChunkBase>()
result += codeGen.translateNode(assignment)
return result
}
private fun assignFpRegisterTo(target: PtExpression, fpRegister: Int): IRCodeChunks {
require(target.type.isFloat)
val assignment = PtAssignment(target.position)
val assignTarget = PtAssignTarget(false, target.position)
assignTarget.children.add(target)
assignment.children.add(assignTarget)
assignment.children.add(PtIrRegister(fpRegister, DataType.FLOAT, target.position))
val result = mutableListOf<IRCodeChunkBase>()
result += codeGen.translateNode(assignment)
return result
}
}
@@ -252,6 +252,7 @@ class IRCodeGen(
listOf(chunk)
}
is PtConditionalBranch -> translate(node)
is PtSwap -> translate(node)
is PtInlineAssembly -> listOf(IRInlineAsmChunk(null, node.assembly, node.isIR, null))
is PtIncludeBinary -> listOf(IRInlineBinaryChunk(null, readBinaryData(node), null))
is PtAddressOf,
@@ -1923,6 +1924,50 @@ class IRCodeGen(
return irBlock
}
private fun translate(swap: PtSwap): IRCodeChunks {
require(swap.target1.type == swap.target2.type)
val result = mutableListOf<IRCodeChunkBase>()
val target1: PtExpression = swap.target1.children.single() as PtExpression
val target2: PtExpression = swap.target2.children.single() as PtExpression
val t1 = expressionEval.translateExpression(target1)
val t2 = expressionEval.translateExpression(target2)
if(swap.target1.type.isFloat) {
addToResult(result, t1, -1, t1.resultFpReg)
addToResult(result, t2, -1, t2.resultFpReg)
result += assignFpRegisterTo(target1, t2.resultFpReg)
result += assignFpRegisterTo(target2, t1.resultFpReg)
} else {
addToResult(result, t1, t1.resultReg, -1)
addToResult(result, t2, t2.resultReg, -1)
result += assignRegisterTo(target1, t2.resultReg)
result += assignRegisterTo(target2, t1.resultReg)
}
return result
}
private fun assignFpRegisterTo(target: PtExpression, fpRegister: Int): IRCodeChunks {
require(target.type.isFloat)
val assignment = PtAssignment(target.position)
val assignTarget = PtAssignTarget(false, target.position)
assignTarget.children.add(target)
assignment.children.add(assignTarget)
assignment.children.add(PtIrRegister(fpRegister, DataType.FLOAT, target.position))
val result = mutableListOf<IRCodeChunkBase>()
result += translateNode(assignment)
return result
}
internal fun assignRegisterTo(target: PtExpression, register: Int): IRCodeChunks {
val assignment = PtAssignment(target.position)
val assignTarget = PtAssignTarget(false, target.position)
assignTarget.children.add(target)
assignment.children.add(assignTarget)
assignment.children.add(PtIrRegister(register, target.type, target.position))
val result = mutableListOf<IRCodeChunkBase>()
result += translateNode(assignment)
return result
}
private fun translateParameters(parameters: List<PtNode>): List<IRSubroutine.IRParam> {
val result = mutableListOf<IRSubroutine.IRParam>()
parameters.forEach {
@@ -303,25 +303,6 @@ class VarConstantValueTypeAdjuster(
functionCallStatement))
}
}
else if(func==listOf("swap")) {
val t1 = functionCallStatement.args[0].inferType(program)
if(t1.isKnown) {
val dt = t1.getOrElse { throw InternalCompilerException("invalid dt") }
val replaceFunc = when {
dt.isByteOrBool -> "swap__byte"
dt.isWord || dt.isPointer -> "swap__word"
dt.isLong -> "swap__long"
dt.isFloat -> "swap__float"
else -> {
errors.err("expected numeric arguments", functionCallStatement.args[0].position)
return noModifications
}
}
return listOf(IAstModification.SetExpression({functionCallStatement.target = it as IdentifierReference},
IdentifierReference(listOf(replaceFunc), functionCallStatement.target.position),
functionCallStatement))
}
}
return noModifications
}
}
@@ -77,6 +77,7 @@ class SimplifiedAstMaker(private val program: Program, private val errors: IErro
is VarDecl -> transform(statement)
is StructDecl -> transform(statement)
is When -> transform(statement)
is Swap -> transform(statement)
is WhileLoop -> throw FatalAstException("while loops must have been converted to jumps")
is OnGoto -> throw FatalAstException("ongoto must have been converted to array and separate call/goto")
is StructFieldRef -> throw FatalAstException("should not occur as part of the actual AST")
@@ -107,9 +108,16 @@ class SimplifiedAstMaker(private val program: Program, private val errors: IErro
}
}
private fun transform(swap: Swap): PtSwap {
val ptswap = PtSwap(swap.position)
ptswap.add(transform(swap.t1))
ptswap.add(transform(swap.t2))
return ptswap
}
private fun transform(deref: PtrDereference): PtPointerDeref {
val type = deref.inferType(program).getOrElse {
throw FatalAstException("unknown dt")
throw FatalAstException("unknown dt ${deref.position}")
}
val targetVar = deref.definingScope.lookup(deref.chain) as? VarDecl
@@ -22,6 +22,7 @@ internal class StatementReorderer(
// - sorts the choices in when statement.
// - insert AddressOf (&) expression where required (string params to a UWORD function param etc.).
// - consolidates multiple consecutive additions , subtractions, multiplications.
// - swap byte pointer deref -> swap directmemory
private val directivesToMove = setOf("%output", "%launcher", "%zeropage", "%zpreserved", "%zpallowed", "%address", "%memtop", "%option", "%encoding")
@@ -430,4 +431,26 @@ internal class StatementReorderer(
}
return noModifications
}
override fun after(swap: Swap, parent: Node): Iterable<IAstModification> {
val mods = mutableListOf<IAstModification>()
val dt = swap.t1.inferType(program).getOrUndef()
if(dt.isByteOrBool) {
// replace swap(ptr^^, ...) by swap(@(ptr), ...) when ptr is byte/ubyte/bool
// regular ptr^^ -> @(ptr) replacement only checks for unsigned byte type.
val deref1 = swap.t1.pointerDereference
val deref2 = swap.t2.pointerDereference
if(deref1!=null) {
val identifier = IdentifierReference(deref1.chain, deref1.position)
val memwrite = DirectMemoryWrite(identifier, deref1.position)
mods.add(IAstModification.ReplaceNode(deref1, memwrite, swap.t1))
}
if(deref2!=null) {
val identifier = IdentifierReference(deref2.chain, deref2.position)
val memwrite = DirectMemoryWrite(identifier, deref2.position)
mods.add(IAstModification.ReplaceNode(deref2, memwrite, swap.t2))
}
}
return mods
}
}
@@ -591,4 +591,12 @@ class AstToSourceTextConverter(val output: (text: String) -> Unit, val program:
output(" : ")
outputListMembers(initializer.args.toTypedArray())
}
override fun visit(swap: Swap) {
output("swap (")
swap.t1.accept(this)
output(", ")
swap.t2.accept(this)
output(")")
}
}
@@ -692,10 +692,16 @@ class Antlr2KotlinVisitor(val source: SourceCode): AbstractParseTreeVisitor<Node
return dt to identifiers.map { getname(it) }
}
override fun visitSwap(ctx: SwapContext): Swap {
val (t1, t2) = ctx.assign_target().map { it.accept(this) as AssignTarget }
return Swap(t1, t2, ctx.toPosition())
}
override fun visitModule_element(ctx: Module_elementContext): Node = visitChildren(ctx)
override fun visitBlock_statement(ctx: Block_statementContext): Statement = visitChildren(ctx) as Statement
override fun visitStatement(ctx: StatementContext): Statement = visitChildren(ctx) as Statement
override fun visitVariabledeclaration(ctx: VariabledeclarationContext): VarDecl = visitChildren(ctx) as VarDecl
override fun visitLiteralvalue(ctx: LiteralvalueContext): Expression = visitChildren(ctx) as Expression
@@ -451,6 +451,26 @@ class StructFieldRef(val pointer: IdentifierReference, val struct: StructDecl, v
}
class Swap(var t1: AssignTarget, var t2: AssignTarget, override val position: Position) : Statement() {
override lateinit var parent: Node
override fun linkParents(parent: Node) {
this.parent = parent
t1.linkParents(this)
t2.linkParents(this)
}
override fun copy(): Swap = Swap(t1.copy(), t2.copy(), position)
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
override fun referencesIdentifier(nameInSource: List<String>): Boolean = t1.referencesIdentifier(nameInSource) || t2.referencesIdentifier(nameInSource)
override fun replaceChildNode(node: Node, replacement: Node) {
if(node===t1) t1 = replacement as AssignTarget
else if(node===t2) t2 = replacement as AssignTarget
else throw FatalAstException("invalid replace in $this at ${node.position}: $node -> $replacement")
}
}
class ArrayIndex(var indexExpr: Expression, override val position: Position) : Node {
override lateinit var parent: Node
@@ -144,6 +144,7 @@ abstract class AstWalker {
open fun before(whenStmt: When, parent: Node): Iterable<IAstModification> = noModifications
open fun before(whileLoop: WhileLoop, parent: Node): Iterable<IAstModification> = noModifications
open fun before(ongoto: OnGoto, parent: Node): Iterable<IAstModification> = noModifications
open fun before(swap: Swap, parent: Node): Iterable<IAstModification> = noModifications
open fun after(addressOf: AddressOf, parent: Node): Iterable<IAstModification> = noModifications
open fun after(array: ArrayLiteral, parent: Node): Iterable<IAstModification> = noModifications
@@ -196,6 +197,7 @@ abstract class AstWalker {
open fun after(whenStmt: When, parent: Node): Iterable<IAstModification> = noModifications
open fun after(whileLoop: WhileLoop, parent: Node): Iterable<IAstModification> = noModifications
open fun after(ongoto: OnGoto, parent: Node): Iterable<IAstModification> = noModifications
open fun after(swap: Swap, parent: Node): Iterable<IAstModification> = noModifications
protected val modifications = mutableListOf<Triple<IAstModification, Node, Node>>()
@@ -563,5 +565,12 @@ abstract class AstWalker {
deref.chain.forEach { it.second?.accept(this) }
track(after(deref, parent), deref, parent)
}
fun visit(swap: Swap, parent: Node) {
track(before(swap, parent), swap, parent)
swap.t1.accept(this, swap)
swap.t2.accept(this, swap)
track(after(swap, parent), swap, parent)
}
}
@@ -50,6 +50,11 @@ interface IAstVisitor {
fun visit(field: StructFieldRef) {
}
fun visit(swap: Swap) {
swap.t1.accept(this)
swap.t2.accept(this)
}
fun visit(subroutine: Subroutine) {
subroutine.asmAddress?.varbank?.accept(this)
subroutine.statements.forEach { it.accept(this) }
-2
View File
@@ -15,7 +15,6 @@ Weird Heisenbug
Future Things and Ideas
^^^^^^^^^^^^^^^^^^^^^^^
- make swap() a special syntax node with assigntarget arg types instead of a builtin function call (see swap-statement branch)
- make builtin functions capable of returning multiple values, then make divmod() return the 2 results rather than accepting 2 extra variables as arguments
- then also introduce lmh(longvalue) -or whatever sensible name- builtin function that returns the low, mid, hi (bank) bytes of a long.
- and rewrite the divmod (and others?) function to just return the 2 results instead of taking target variables as arguments.
@@ -76,7 +75,6 @@ IR/VM
-----
- getting it in shape for code generation: the IR file should be able to encode every detail about a prog8 program (the VM doesn't have to actually be able to run all of it though!)
- encode asmsub/extsub clobber info in the call, or maybe include these definitions in the p8ir file itself too. (return registers are already encoded in the CALL instruction)
- SWAP opcode needs more addressing modes to avoid code bloat? for instance to swap 2 array elements by index
- extend the index register datatype in the LOADX, STOREX, STOREZX instructions from byte to word (0-255 to 0-65535) (this not compatible with 8 bit 6502, but the 68000 can use that , well, up to 32767)
- or just get rid of LOADX/STOREX/STOREZX, just use add + loadi / storei (because we have to translate that sequence anyway)?
- if instruction has both integer and float registers, the sequence of the registers is sometimes weird in the .p8ir file (float regs always at the end even when otherwise the target -integer- register is the first one in the list, for example.)
+87 -247
View File
@@ -1,297 +1,137 @@
%import textio
%import conv
%import strings
%import floats
%import math
%zeropage basicsafe
main {
; Test the routine
sub testsgn() {
byte @shared b1 = -100
word @shared w1 = -1000
long @shared l1 = -1000000
cx16.r0sL = sgn(b1)
txt.print_b(cx16.r0sL)
txt.spc()
cx16.r1sL = sgn(w1)
txt.print_b(cx16.r1sL)
txt.spc()
cx16.r2sL = sgn(l1)
txt.print_b(cx16.r2sL)
txt.nl()
b1 = 100
w1 = 1000
l1 = 1000000
cx16.r0sL = sgn(b1)
txt.print_b(cx16.r0sL)
txt.spc()
cx16.r1sL = sgn(w1)
txt.print_b(cx16.r1sL)
txt.spc()
cx16.r2sL = sgn(l1)
txt.print_b(cx16.r2sL)
txt.nl()
}
sub start() {
testsgn()
bool bb1 = true
bool bb2 = false
ubyte ub1 = 11
ubyte ub2 = 22
uword uw1 = 1111
uword uw2 = 2222
long l1 = 1111111
long l2 = 2222222
float f1 = 1.111
float f2 = 2.222
float f3 = 1.111
float f4 = 2.222
^^long ptr1 = 1111
^^long ptr2 = 2222
byte[2] bytes = [-11,-22]
word[2] words = [-1111,-2222]
word[2] @nosplit words2 = [-1111,-2222]
long[2] longs = [-1111111,-2222222]
float[2] floata = [-1.111,-2.222]
cx16.r6 = 1111
cx16.r7 = 2222
cx16.r4L = 11
cx16.r4H = 22
long l3 = 1111111
long l4 = 2222222
^^long lptr1 = &l3
^^long lptr2 = &l4
^^uword uwptr1 = &cx16.r6
^^uword uwptr2 = &cx16.r7
^^byte bptr1 = &cx16.r4L
^^byte bptr2 = &cx16.r4H
^^float fptr1 = &f3
^^float fptr2 = &f4
long @shared lv
uword @shared wv
ubyte @shared bv
swap(bb1, bb2)
swap(ub1, ub2)
swap(uw1, uw2)
swap(l1, l2)
swap(f1, f2)
swap(ptr1, ptr2)
swap(bptr1^^, bptr2^^)
swap(uwptr1^^, uwptr2^^)
swap(lptr1^^, lptr2^^)
swap(fptr1^^, fptr2^^)
txt.print_l(0) ; value
cx16.r9L = 0
cx16.r10L = 1
swap(bytes[cx16.r9L], bytes[cx16.r10L])
swap(words[cx16.r9L], words[cx16.r10L])
swap(words2[cx16.r9L], words2[cx16.r10L])
swap(floata[cx16.r9L],floata[cx16.r10L])
swap(longs[cx16.r9L],longs[cx16.r10L])
txt.print_b(bytes[0])
txt.spc()
txt.print_uw(0) ; expected result
txt.spc()
txt.print_uw(prog8_lib.sqrt_long(0)) ; calculated result
txt.spc()
lv = 0
txt.print_uw(sqrt(lv)) ; calculated result
txt.print_b(bytes[1])
txt.nl()
txt.print_l(55555555) ; value
txt.print_bool(bb1)
txt.spc()
txt.print_uw(7453) ; expected result
txt.spc()
txt.print_uw(prog8_lib.sqrt_long(55555555)) ; calculated result
txt.spc()
lv = 55555555
txt.print_uw(sqrt(lv)) ; calculated result
txt.print_bool(bb2)
txt.nl()
; Additional test cases for square root
txt.print_l(1) ; value
txt.print_ub(ub1)
txt.spc()
txt.print_uw(1) ; expected result
txt.spc()
txt.print_uw(prog8_lib.sqrt_long(1)) ; calculated result
txt.spc()
lv = 1
txt.print_uw(sqrt(lv)) ; calculated result
txt.print_ub(ub2)
txt.nl()
txt.print_l(4) ; value
txt.print_ub(cx16.r4L)
txt.spc()
txt.print_uw(2) ; expected result
txt.spc()
txt.print_uw(prog8_lib.sqrt_long(4)) ; calculated result
txt.spc()
lv = 4
txt.print_uw(sqrt(lv)) ; calculated result
txt.print_ub(cx16.r4H)
txt.nl()
txt.print_l(9) ; value
txt.print_uw(uw1)
txt.spc()
txt.print_uw(3) ; expected result
txt.spc()
txt.print_uw(prog8_lib.sqrt_long(9)) ; calculated result
txt.spc()
lv = 9
txt.print_uw(sqrt(lv)) ; calculated result
txt.print_uw(uw2)
txt.nl()
txt.print_l(16) ; value
txt.print_uw(ptr1)
txt.spc()
txt.print_uw(4) ; expected result
txt.spc()
txt.print_uw(prog8_lib.sqrt_long(16)) ; calculated result
txt.spc()
lv = 16
txt.print_uw(sqrt(lv)) ; calculated result
txt.print_uw(ptr2)
txt.nl()
txt.print_l(25) ; value
txt.print_uw(cx16.r6)
txt.spc()
txt.print_uw(5) ; expected result
txt.spc()
txt.print_uw(prog8_lib.sqrt_long(25)) ; calculated result
txt.spc()
lv = 25
txt.print_uw(sqrt(lv)) ; calculated result
txt.print_uw(cx16.r7)
txt.nl()
txt.print_l(36) ; value
txt.print_w(words[0])
txt.spc()
txt.print_uw(6) ; expected result
txt.spc()
txt.print_uw(prog8_lib.sqrt_long(36)) ; calculated result
txt.spc()
lv = 36
txt.print_uw(sqrt(lv)) ; calculated result
txt.print_w(words[1])
txt.nl()
txt.print_l(49) ; value
txt.print_w(words2[0])
txt.spc()
txt.print_uw(7) ; expected result
txt.spc()
txt.print_uw(prog8_lib.sqrt_long(49)) ; calculated result
txt.spc()
lv = 49
txt.print_uw(sqrt(lv)) ; calculated result
txt.print_w(words2[1])
txt.nl()
txt.print_l(64) ; value
txt.print_l(longs[0])
txt.spc()
txt.print_uw(8) ; expected result
txt.spc()
txt.print_uw(prog8_lib.sqrt_long(64)) ; calculated result
txt.spc()
lv = 64
txt.print_uw(sqrt(lv)) ; calculated result
txt.print_l(longs[1])
txt.nl()
txt.print_l(81) ; value
txt.print_f(floata[0])
txt.spc()
txt.print_uw(9) ; expected result
txt.spc()
txt.print_uw(prog8_lib.sqrt_long(81)) ; calculated result
txt.spc()
lv = 81
txt.print_uw(sqrt(lv)) ; calculated result
txt.print_f(floata[1])
txt.nl()
txt.print_l(100) ; value
txt.print_l(l1)
txt.spc()
txt.print_uw(10) ; expected result
txt.print_l(l2)
txt.nl()
txt.print_l(l3)
txt.spc()
txt.print_uw(prog8_lib.sqrt_long(100)) ; calculated result
txt.spc()
lv = 100
txt.print_uw(sqrt(lv)) ; calculated result
txt.print_l(l4)
txt.nl()
txt.print_l(121) ; value
txt.print_f(f1)
txt.spc()
txt.print_uw(11) ; expected result
txt.spc()
txt.print_uw(prog8_lib.sqrt_long(121)) ; calculated result
txt.spc()
lv = 121
txt.print_uw(sqrt(lv)) ; calculated result
txt.print_f(f2)
txt.nl()
txt.print_l(144) ; value
txt.print_f(f3)
txt.spc()
txt.print_uw(12) ; expected result
txt.spc()
txt.print_uw(prog8_lib.sqrt_long(144)) ; calculated result
txt.spc()
lv = 144
txt.print_uw(sqrt(lv)) ; calculated result
txt.print_f(f4)
txt.nl()
txt.print_l(255) ; value
txt.spc()
txt.print_uw(15) ; expected result
txt.spc()
txt.print_uw(prog8_lib.sqrt_long(255)) ; calculated result
txt.spc()
lv = 255
txt.print_uw(sqrt(lv)) ; calculated result
txt.nl()
txt.print_l(256) ; value
txt.spc()
txt.print_uw(16) ; expected result
txt.spc()
txt.print_uw(prog8_lib.sqrt_long(256)) ; calculated result
txt.spc()
lv = 256
txt.print_uw(sqrt(lv)) ; calculated result
txt.nl()
txt.print_l(1024) ; value
txt.spc()
txt.print_uw(32) ; expected result
txt.spc()
txt.print_uw(prog8_lib.sqrt_long(1024)) ; calculated result
txt.spc()
lv = 1024
txt.print_uw(sqrt(lv)) ; calculated result
txt.nl()
txt.print_l(65536) ; value
txt.spc()
txt.print_uw(256) ; expected result
txt.spc()
txt.print_uw(prog8_lib.sqrt_long(65536)) ; calculated result
txt.spc()
lv = 65536
txt.print_uw(sqrt(lv)) ; calculated result
txt.nl()
txt.print_l(1000000) ; value
txt.spc()
txt.print_uw(1000) ; expected result
txt.spc()
txt.print_uw(prog8_lib.sqrt_long(1000000)) ; calculated result
txt.spc()
lv = 1000000
txt.print_uw(sqrt(lv)) ; calculated result
txt.nl()
txt.print_l(16777216) ; value
txt.spc()
txt.print_uw(4096) ; expected result
txt.spc()
txt.print_uw(prog8_lib.sqrt_long(16777216)) ; calculated result
txt.spc()
lv = 16777216
txt.print_uw(sqrt(lv)) ; calculated result
txt.nl()
txt.print_l(2147483647) ; value
txt.spc()
txt.print_uw(46340) ; expected result
txt.spc()
txt.print_uw(prog8_lib.sqrt_long(2147483647)) ; calculated result
txt.spc()
lv = 2147483647
txt.print_uw(sqrt(lv)) ; calculated result
txt.nl()
txt.print_l(4294967295) ; value
txt.spc()
txt.print_uw(65535) ; expected result
txt.spc()
txt.print_uw(prog8_lib.sqrt_long(4294967295)) ; calculated result
txt.nl()
; test_b()
; test_w()
; test_long()
;
; sub test_b() {
; bv = 99
; txt.print_ub(min(bv, 200))
; txt.spc()
; bv = min(bv, 200)
; txt.print_ub(bv)
; txt.nl()
; }
;
; sub test_w() {
; wv = 22211
; txt.print_uw(min(wv, 55555))
; txt.spc()
; wv = min(wv, 55555)
; txt.print_uw(wv)
; txt.nl()
; }
;
; sub test_long() {
; lv = 55551111
; txt.print_uw(min(lv, 88888888))
; txt.spc()
; lv = min(lv, 88888888)
; txt.print_uw(lv)
; txt.nl()
; }
}
}
@@ -258,7 +258,6 @@ push [b, w, f] reg1 - push value in reg1 on the stack
pop [b, w, f] reg1 - pop value from stack into reg1
pushst - push status register bits to stack
popst - pop status register bits from stack
swap reg1, reg2 - exchange values in registers 1 and 2 (all datatypes)
*/
enum class Opcode {
@@ -431,7 +430,6 @@ enum class Opcode {
MSIGW,
BSIGB,
CONCAT,
SWAP,
BREAKPOINT,
ALIGN
}
@@ -822,7 +820,6 @@ val instructionFormats = mutableMapOf(
Opcode.PUSHST to InstructionFormat.from("N"),
Opcode.POPST to InstructionFormat.from("N"),
Opcode.CONCAT to InstructionFormat.from("BW,<>r1,<r2,<r3"),
Opcode.SWAP to InstructionFormat.from("BWL,<>r1,<>r2 | F,<>fr1,<>fr2"),
Opcode.CLC to InstructionFormat.from("N"),
Opcode.SEC to InstructionFormat.from("N"),
Opcode.CLI to InstructionFormat.from("N"),
+3
View File
@@ -121,8 +121,11 @@ statement :
| labeldef
| defer
| alias
| swap
;
swap: 'swap' '(' assign_target ',' assign_target ')' ;
variabledeclaration :
varinitializer
@@ -198,6 +198,9 @@ fun printAst(root: PtNode, skipLibraries: Boolean, output: (text: String) -> Uni
val deref = if(node.derefLast) "^^" else ""
"deref $chain $deref ${type(node.type)}"
}
is PtSwap -> {
"swap"
}
}
}
@@ -344,3 +344,12 @@ class PtDefer(position: Position): PtNode(position)
class PtJmpTable(position: Position) : PtNode(position) // contains only PtIdentifier nodes
class PtSwap(position: Position): PtNode(position) {
val target1: PtAssignTarget
get() = children[0] as PtAssignTarget
val target2: PtAssignTarget
get() = children[1] as PtAssignTarget
}
@@ -315,7 +315,6 @@ class VirtualMachine(irProgram: IRProgram) {
Opcode.MSIGW -> InsMSIGW(ins)
Opcode.BSIGB -> InsBSIGB(ins)
Opcode.CONCAT -> InsCONCAT(ins)
Opcode.SWAP -> InsSWAP(ins)
Opcode.PUSH -> InsPUSH(ins)
Opcode.POP -> InsPOP(ins)
Opcode.PUSHST -> InsPUSHST()
@@ -2620,36 +2619,6 @@ class VirtualMachine(irProgram: IRProgram) {
nextPc()
}
private fun InsSWAP(i: IRInstruction) {
when(i.type!!) {
IRDataType.BYTE -> {
val value1 = registers.getUB(i.reg1!!)
val value2 = registers.getUB(i.reg2!!)
registers.setUB(i.reg1!!, value2)
registers.setUB(i.reg2!!, value1)
}
IRDataType.WORD -> {
val value1 = registers.getUW(i.reg1!!)
val value2 = registers.getUW(i.reg2!!)
registers.setUW(i.reg1!!, value2)
registers.setUW(i.reg2!!, value1)
}
IRDataType.LONG -> {
val value1 = registers.getSL(i.reg1!!)
val value2 = registers.getSL(i.reg2!!)
registers.setSL(i.reg1!!, value2)
registers.setSL(i.reg2!!, value1)
}
IRDataType.FLOAT -> {
val value1 = registers.getFloat(i.fpReg1!!)
val value2 = registers.getFloat(i.fpReg2!!)
registers.setFloat(i.fpReg1!!, value2)
registers.setFloat(i.fpReg2!!, value1)
}
}
nextPc()
}
private fun InsCONCAT(i: IRInstruction) {
when(i.type!!) {
IRDataType.BYTE -> {