mirror of
https://github.com/irmen/prog8.git
synced 2024-12-25 23:29:55 +00:00
more bool fixes and optimizations in codegen
This commit is contained in:
parent
c5c4c6f111
commit
7d8cdcbfea
@ -98,6 +98,8 @@ class PtIfElse(position: Position) : PtNode(position) {
|
||||
get() = children[1] as PtNodeGroup
|
||||
val elseScope: PtNodeGroup
|
||||
get() = children[2] as PtNodeGroup
|
||||
|
||||
fun hasElse(): Boolean = children.size==3 && elseScope.children.isNotEmpty()
|
||||
}
|
||||
|
||||
|
||||
|
@ -235,6 +235,7 @@ class AsmGen6502Internal (
|
||||
private val anyExprGen = AnyExprAsmGen(this)
|
||||
private val assignmentAsmGen = AssignmentAsmGen(program, this, anyExprGen, allocator)
|
||||
private val builtinFunctionsAsmGen = BuiltinFunctionsAsmGen(program, this, assignmentAsmGen)
|
||||
private val ifElseAsmgen = IfElseAsmGen(program, this)
|
||||
|
||||
fun compileToAssembly(): IAssemblyProgram? {
|
||||
|
||||
@ -594,7 +595,7 @@ class AsmGen6502Internal (
|
||||
}
|
||||
is PtLabel -> translate(stmt)
|
||||
is PtConditionalBranch -> translate(stmt)
|
||||
is PtIfElse -> translate(stmt)
|
||||
is PtIfElse -> ifElseAsmgen.translate(stmt)
|
||||
is PtForLoop -> forloopsAsmGen.translate(stmt)
|
||||
is PtRepeatLoop -> translate(stmt)
|
||||
is PtWhen -> translate(stmt)
|
||||
@ -767,26 +768,6 @@ class AsmGen6502Internal (
|
||||
}
|
||||
}
|
||||
|
||||
private fun translate(stmt: PtIfElse) {
|
||||
require(stmt.condition.type==DataType.BOOL)
|
||||
assignExpressionToRegister(stmt.condition, RegisterOrPair.A, false)
|
||||
val afterIfLabel = makeLabel("if")
|
||||
if(stmt.children.size>1) {
|
||||
// if and else blocks
|
||||
val elseLabel = makeLabel("else")
|
||||
out(" beq $elseLabel")
|
||||
translate(stmt.ifScope)
|
||||
jmp(afterIfLabel, false)
|
||||
out(elseLabel)
|
||||
translate(stmt.elseScope)
|
||||
} else {
|
||||
// no else block
|
||||
out(" beq $afterIfLabel")
|
||||
translate(stmt.ifScope)
|
||||
}
|
||||
out(afterIfLabel)
|
||||
}
|
||||
|
||||
private fun translate(stmt: PtRepeatLoop) {
|
||||
val endLabel = makeLabel("repeatend")
|
||||
loopEndLabels.push(endLabel)
|
||||
@ -880,7 +861,6 @@ $repeatLabel""")
|
||||
out(endLabel)
|
||||
}
|
||||
|
||||
|
||||
private fun repeatByteCount(count: Int, stmt: PtRepeatLoop) {
|
||||
require(count in 2..256) { "invalid repeat count ${stmt.position}" }
|
||||
val repeatLabel = makeLabel("repeat")
|
||||
@ -1041,7 +1021,7 @@ $repeatLabel""")
|
||||
}
|
||||
}
|
||||
|
||||
private fun getJumpTarget(jump: PtJump): Pair<String, Boolean> {
|
||||
internal fun getJumpTarget(jump: PtJump): Pair<String, Boolean> {
|
||||
val ident = jump.identifier
|
||||
val addr = jump.address
|
||||
return when {
|
||||
@ -1307,609 +1287,6 @@ $repeatLabel""")
|
||||
}
|
||||
}
|
||||
|
||||
private fun translateUwordGreaterOrEqualOrJumpElsewhere(left: PtExpression, right: PtExpression, leftConstVal: PtNumber?, rightConstVal: PtNumber?, jumpIfFalseLabel: String) {
|
||||
|
||||
fun code(msbCpyOperand: String, lsbCmpOperand: String) {
|
||||
out("""
|
||||
cpy $msbCpyOperand
|
||||
bcc $jumpIfFalseLabel
|
||||
bne +
|
||||
cmp $lsbCmpOperand
|
||||
bcc $jumpIfFalseLabel
|
||||
+""")
|
||||
}
|
||||
|
||||
if(rightConstVal!=null) {
|
||||
if(leftConstVal!=null) {
|
||||
if(rightConstVal<leftConstVal)
|
||||
jmp(jumpIfFalseLabel)
|
||||
return
|
||||
} else {
|
||||
if (left is PtIdentifier) {
|
||||
if(rightConstVal.number.toInt()!=0) {
|
||||
assignExpressionToRegister(left, RegisterOrPair.AY)
|
||||
code("#>${rightConstVal.number.toInt()}", "#<${rightConstVal.number.toInt()}")
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(wordJumpForSimpleRightOperands(left, right, ::code))
|
||||
return
|
||||
|
||||
assignWordOperandsToAYAndVar(right, left, "P8ZP_SCRATCH_W2")
|
||||
return out(" jsr prog8_lib.reg_lesseq_uw | beq $jumpIfFalseLabel")
|
||||
}
|
||||
|
||||
private fun translateWordGreaterOrEqualOrJumpElsewhere(left: PtExpression, right: PtExpression, leftConstVal: PtNumber?, rightConstVal: PtNumber?, jumpIfFalseLabel: String) {
|
||||
|
||||
fun code(msbCpyOperand: String, lsbCmpOperand: String) {
|
||||
out("""
|
||||
cmp $lsbCmpOperand
|
||||
tya
|
||||
sbc $msbCpyOperand
|
||||
bvc +
|
||||
eor #$80
|
||||
+ bmi $jumpIfFalseLabel""")
|
||||
}
|
||||
|
||||
if(rightConstVal!=null) {
|
||||
if(leftConstVal!=null) {
|
||||
if(rightConstVal<leftConstVal)
|
||||
jmp(jumpIfFalseLabel)
|
||||
return
|
||||
} else {
|
||||
if (left is PtIdentifier) {
|
||||
return if(rightConstVal.number.toInt()!=0) {
|
||||
assignExpressionToRegister(left, RegisterOrPair.AY)
|
||||
code("#>${rightConstVal.number.toInt()}", "#<${rightConstVal.number.toInt()}")
|
||||
}
|
||||
else {
|
||||
val name = asmVariableName(left)
|
||||
out(" lda $name+1 | bmi $jumpIfFalseLabel")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(wordJumpForSimpleRightOperands(left, right, ::code))
|
||||
return
|
||||
|
||||
assignWordOperandsToAYAndVar(left, right, "P8ZP_SCRATCH_W2")
|
||||
return out(" jsr prog8_lib.reg_lesseq_w | beq $jumpIfFalseLabel")
|
||||
}
|
||||
|
||||
private fun translateByteEqualsOrJumpElsewhere(left: PtExpression, right: PtExpression, leftConstVal: PtNumber?, rightConstVal: PtNumber?, jumpIfFalseLabel: String) {
|
||||
fun code(cmpOperand: String) {
|
||||
out(" cmp $cmpOperand | bne $jumpIfFalseLabel")
|
||||
}
|
||||
|
||||
if(rightConstVal!=null) {
|
||||
if(leftConstVal!=null) {
|
||||
if(rightConstVal!=leftConstVal)
|
||||
jmp(jumpIfFalseLabel)
|
||||
return
|
||||
} else {
|
||||
if (left is PtIdentifier) {
|
||||
assignExpressionToRegister(left, RegisterOrPair.A)
|
||||
return if(rightConstVal.number.toInt()!=0)
|
||||
code("#${rightConstVal.number.toInt()}")
|
||||
else
|
||||
out(" bne $jumpIfFalseLabel")
|
||||
}
|
||||
else if (left is PtMemoryByte) {
|
||||
translateDirectMemReadExpressionToRegA(left)
|
||||
return if(rightConstVal.number.toInt()!=0)
|
||||
code("#${rightConstVal.number.toInt()}")
|
||||
else
|
||||
out(" bne $jumpIfFalseLabel")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(byteJumpForSimpleRightOperand(left, right, ::code))
|
||||
return
|
||||
|
||||
if(left.isSimple()) {
|
||||
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
|
||||
assignExpressionToRegister(left, RegisterOrPair.A)
|
||||
} else if(right.isSimple()) {
|
||||
assignExpressionToVariable(left, "P8ZP_SCRATCH_B1", DataType.UBYTE)
|
||||
assignExpressionToRegister(right, RegisterOrPair.A)
|
||||
} else {
|
||||
pushCpuStack(DataType.UBYTE, left)
|
||||
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
|
||||
restoreRegisterStack(CpuRegister.A, false)
|
||||
}
|
||||
out(" cmp P8ZP_SCRATCH_B1 | bne $jumpIfFalseLabel")
|
||||
}
|
||||
|
||||
private fun translateByteNotEqualsOrJumpElsewhere(left: PtExpression, right: PtExpression, leftConstVal: PtNumber?, rightConstVal: PtNumber?, jumpIfFalseLabel: String) {
|
||||
|
||||
fun code(cmpOperand: String) {
|
||||
out(" cmp $cmpOperand | beq $jumpIfFalseLabel")
|
||||
}
|
||||
|
||||
if(rightConstVal!=null) {
|
||||
if(leftConstVal!=null) {
|
||||
if(rightConstVal==leftConstVal)
|
||||
jmp(jumpIfFalseLabel)
|
||||
return
|
||||
} else {
|
||||
if (left is PtIdentifier) {
|
||||
assignExpressionToRegister(left, RegisterOrPair.A)
|
||||
return if(rightConstVal.number.toInt()!=0)
|
||||
code("#${rightConstVal.number.toInt()}")
|
||||
else
|
||||
out(" beq $jumpIfFalseLabel")
|
||||
}
|
||||
else if (left is PtMemoryByte) {
|
||||
translateDirectMemReadExpressionToRegA(left)
|
||||
return if(rightConstVal.number.toInt()!=0)
|
||||
code("#${rightConstVal.number.toInt()}")
|
||||
else
|
||||
out(" beq $jumpIfFalseLabel")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(byteJumpForSimpleRightOperand(left, right, ::code))
|
||||
return
|
||||
|
||||
if(left.isSimple()) {
|
||||
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
|
||||
assignExpressionToRegister(left, RegisterOrPair.A)
|
||||
} else if(right.isSimple()) {
|
||||
assignExpressionToVariable(left, "P8ZP_SCRATCH_B1", DataType.UBYTE)
|
||||
assignExpressionToRegister(right, RegisterOrPair.A)
|
||||
} else {
|
||||
pushCpuStack(DataType.UBYTE, left)
|
||||
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
|
||||
restoreRegisterStack(CpuRegister.A, false)
|
||||
}
|
||||
return code("P8ZP_SCRATCH_B1")
|
||||
}
|
||||
|
||||
private fun translateWordEqualsOrJumpElsewhere(left: PtExpression, right: PtExpression, leftConstVal: PtNumber?, rightConstVal: PtNumber?, jumpIfFalseLabel: String) {
|
||||
if(rightConstVal!=null) {
|
||||
if(leftConstVal!=null) {
|
||||
if(rightConstVal!=leftConstVal)
|
||||
jmp(jumpIfFalseLabel)
|
||||
return
|
||||
} else {
|
||||
if (left is PtIdentifier) {
|
||||
val name = asmVariableName(left)
|
||||
if(rightConstVal.number!=0.0) {
|
||||
val rightNum = rightConstVal.number.toHex()
|
||||
out("""
|
||||
lda $name
|
||||
cmp #<$rightNum
|
||||
bne $jumpIfFalseLabel
|
||||
lda $name+1
|
||||
cmp #>$rightNum
|
||||
bne $jumpIfFalseLabel""")
|
||||
}
|
||||
else {
|
||||
out("""
|
||||
lda $name
|
||||
bne $jumpIfFalseLabel
|
||||
lda $name+1
|
||||
bne $jumpIfFalseLabel""")
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
when (right) {
|
||||
is PtBool -> TODO("word equals for bool operand")
|
||||
is PtNumber -> {
|
||||
assignExpressionToRegister(left, RegisterOrPair.AY)
|
||||
val number = right.number.toHex()
|
||||
out("""
|
||||
cmp #<$number
|
||||
bne $jumpIfFalseLabel
|
||||
cpy #>$number
|
||||
bne $jumpIfFalseLabel""")
|
||||
}
|
||||
is PtIdentifier -> {
|
||||
assignExpressionToRegister(left, RegisterOrPair.AY)
|
||||
out("""
|
||||
cmp ${asmVariableName(right)}
|
||||
bne $jumpIfFalseLabel
|
||||
cpy ${asmVariableName(right)}+1
|
||||
bne $jumpIfFalseLabel""")
|
||||
}
|
||||
is PtAddressOf -> {
|
||||
assignExpressionToRegister(left, RegisterOrPair.AY)
|
||||
val name = asmSymbolName(right.identifier)
|
||||
if(right.isFromArrayElement) {
|
||||
TODO("address-of array element $name at ${right.position}")
|
||||
// assignmentAsmGen.assignAddressOf(target, name, right.arrayIndexExpr)
|
||||
} else {
|
||||
out("""
|
||||
cmp #<$name
|
||||
bne $jumpIfFalseLabel
|
||||
cpy #>$name
|
||||
bne $jumpIfFalseLabel""")
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
if(left.isSimple()) {
|
||||
assignExpressionToVariable(right, "P8ZP_SCRATCH_W2", DataType.UWORD)
|
||||
assignExpressionToRegister(left, RegisterOrPair.AY)
|
||||
} else if(right.isSimple()) {
|
||||
assignExpressionToVariable(left, "P8ZP_SCRATCH_W2", DataType.UWORD)
|
||||
assignExpressionToRegister(right, RegisterOrPair.AY)
|
||||
} else {
|
||||
pushCpuStack(DataType.UWORD, left)
|
||||
assignExpressionToVariable(right, "P8ZP_SCRATCH_W2", DataType.UWORD)
|
||||
restoreRegisterStack(CpuRegister.Y, false)
|
||||
restoreRegisterStack(CpuRegister.A, false)
|
||||
}
|
||||
out("""
|
||||
cmp P8ZP_SCRATCH_W2
|
||||
bne $jumpIfFalseLabel
|
||||
cpy P8ZP_SCRATCH_W2+1
|
||||
bne $jumpIfFalseLabel""")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun translateWordNotEqualsOrJumpElsewhere(left: PtExpression, right: PtExpression, leftConstVal: PtNumber?, rightConstVal: PtNumber?, jumpIfFalseLabel: String) {
|
||||
|
||||
if(rightConstVal!=null) {
|
||||
if(leftConstVal!=null) {
|
||||
if(rightConstVal==leftConstVal)
|
||||
jmp(jumpIfFalseLabel)
|
||||
return
|
||||
} else {
|
||||
if (left is PtIdentifier) {
|
||||
val name = asmVariableName(left)
|
||||
if(rightConstVal.number.toInt()!=0) {
|
||||
val number = rightConstVal.number.toHex()
|
||||
out("""
|
||||
lda $name
|
||||
cmp #<$number
|
||||
bne +
|
||||
lda $name+1
|
||||
cmp #>$number
|
||||
beq $jumpIfFalseLabel
|
||||
+""")
|
||||
}
|
||||
else
|
||||
out("""
|
||||
lda $name
|
||||
bne +
|
||||
lda $name+1
|
||||
beq $jumpIfFalseLabel
|
||||
+""")
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
when (right) {
|
||||
is PtBool -> TODO("word not equals for bool operand")
|
||||
is PtNumber -> {
|
||||
assignExpressionToRegister(left, RegisterOrPair.AY)
|
||||
val number = right.number.toHex()
|
||||
out("""
|
||||
cmp #<$number
|
||||
bne +
|
||||
cpy #>$number
|
||||
beq $jumpIfFalseLabel
|
||||
+""")
|
||||
}
|
||||
is PtIdentifier -> {
|
||||
assignExpressionToRegister(left, RegisterOrPair.AY)
|
||||
out("""
|
||||
cmp ${asmVariableName(right)}
|
||||
bne +
|
||||
cpy ${asmVariableName(right)}+1
|
||||
beq $jumpIfFalseLabel
|
||||
+""")
|
||||
}
|
||||
is PtAddressOf -> {
|
||||
assignExpressionToRegister(left, RegisterOrPair.AY)
|
||||
val name = asmSymbolName(right.identifier)
|
||||
if(right.isFromArrayElement) {
|
||||
TODO("address-of array element $name at ${right.position}")
|
||||
} else {
|
||||
out("""
|
||||
cmp #<$name
|
||||
bne +
|
||||
cpy #>$name
|
||||
beq $jumpIfFalseLabel
|
||||
+""")
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
TODO("word not equals boolean in special case?")
|
||||
if(left.isSimple()) {
|
||||
assignExpressionToVariable(right, "P8ZP_SCRATCH_W2", DataType.UWORD)
|
||||
assignExpressionToRegister(left, RegisterOrPair.AY)
|
||||
} else if (right.isSimple()) {
|
||||
assignExpressionToVariable(left, "P8ZP_SCRATCH_W2", DataType.UWORD)
|
||||
assignExpressionToRegister(right, RegisterOrPair.AY)
|
||||
} else {
|
||||
pushCpuStack(DataType.UWORD, left)
|
||||
assignExpressionToVariable(right, "P8ZP_SCRATCH_W2", DataType.UWORD)
|
||||
restoreRegisterStack(CpuRegister.Y, false)
|
||||
restoreRegisterStack(CpuRegister.A, false)
|
||||
}
|
||||
out("""
|
||||
cmp P8ZP_SCRATCH_W2
|
||||
bne +
|
||||
cpy P8ZP_SCRATCH_W2+1
|
||||
beq $jumpIfFalseLabel
|
||||
+"""
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun translateFloatEqualsOrJumpElsewhere(left: PtExpression, right: PtExpression, leftConstVal: PtNumber?, rightConstVal: PtNumber?, jumpIfFalseLabel: String) {
|
||||
if(rightConstVal!=null) {
|
||||
if(leftConstVal!=null) {
|
||||
if(rightConstVal!=leftConstVal)
|
||||
jmp(jumpIfFalseLabel)
|
||||
return
|
||||
} else {
|
||||
if (left is PtIdentifier) {
|
||||
val name = asmVariableName(left)
|
||||
when(rightConstVal.number)
|
||||
{
|
||||
0.0 -> {
|
||||
out("""
|
||||
lda $name
|
||||
clc
|
||||
adc $name+1
|
||||
adc $name+2
|
||||
adc $name+3
|
||||
adc $name+4
|
||||
bne $jumpIfFalseLabel""")
|
||||
return
|
||||
}
|
||||
1.0 -> {
|
||||
out("""
|
||||
lda $name
|
||||
cmp #129
|
||||
bne $jumpIfFalseLabel
|
||||
lda $name+1
|
||||
clc
|
||||
adc $name+2
|
||||
adc $name+3
|
||||
adc $name+4
|
||||
bne $jumpIfFalseLabel""")
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(leftConstVal!=null && rightConstVal!=null) {
|
||||
throw AssemblyError("const-compare should have been optimized away")
|
||||
}
|
||||
else if(leftConstVal!=null && right is PtIdentifier) {
|
||||
throw AssemblyError("const-compare should have been optimized to have const as right operand")
|
||||
}
|
||||
else if(left is PtIdentifier && rightConstVal!=null) {
|
||||
val leftName = asmVariableName(left)
|
||||
val rightName = allocator.getFloatAsmConst(rightConstVal.number)
|
||||
out("""
|
||||
lda #<$leftName
|
||||
ldy #>$leftName
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
lda #<$rightName
|
||||
ldy #>$rightName
|
||||
jsr floats.vars_equal_f
|
||||
beq $jumpIfFalseLabel""")
|
||||
}
|
||||
else if(left is PtIdentifier && right is PtIdentifier) {
|
||||
val leftName = asmVariableName(left)
|
||||
val rightName = asmVariableName(right)
|
||||
out("""
|
||||
lda #<$leftName
|
||||
ldy #>$leftName
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
lda #<$rightName
|
||||
ldy #>$rightName
|
||||
jsr floats.vars_equal_f
|
||||
beq $jumpIfFalseLabel""")
|
||||
} else {
|
||||
val subroutine = left.definingSub()!!
|
||||
subroutineExtra(subroutine).usedFloatEvalResultVar1 = true
|
||||
assignExpressionToVariable(right, subroutineFloatEvalResultVar1, DataType.FLOAT)
|
||||
assignExpressionToRegister(left, RegisterOrPair.FAC1)
|
||||
out("""
|
||||
lda #<$subroutineFloatEvalResultVar1
|
||||
ldy #>$subroutineFloatEvalResultVar1
|
||||
jsr floats.var_fac1_notequal_f
|
||||
bne $jumpIfFalseLabel""")
|
||||
}
|
||||
}
|
||||
|
||||
private fun translateFloatNotEqualsOrJumpElsewhere(left: PtExpression, right: PtExpression, leftConstVal: PtNumber?, rightConstVal: PtNumber?, jumpIfFalseLabel: String) {
|
||||
if(rightConstVal!=null) {
|
||||
if(leftConstVal!=null) {
|
||||
if(rightConstVal==leftConstVal)
|
||||
jmp(jumpIfFalseLabel)
|
||||
return
|
||||
} else {
|
||||
if (left is PtIdentifier) {
|
||||
val name = asmVariableName(left)
|
||||
when(rightConstVal.number)
|
||||
{
|
||||
0.0 -> {
|
||||
out("""
|
||||
lda $name
|
||||
clc
|
||||
adc $name+1
|
||||
adc $name+2
|
||||
adc $name+3
|
||||
adc $name+4
|
||||
beq $jumpIfFalseLabel""")
|
||||
return
|
||||
}
|
||||
1.0 -> {
|
||||
out("""
|
||||
lda $name
|
||||
cmp #129
|
||||
bne +
|
||||
lda $name+1
|
||||
clc
|
||||
adc $name+2
|
||||
adc $name+3
|
||||
adc $name+4
|
||||
beq $jumpIfFalseLabel
|
||||
+""")
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(leftConstVal!=null && rightConstVal!=null) {
|
||||
throw AssemblyError("const-compare should have been optimized away")
|
||||
}
|
||||
else if(leftConstVal!=null && right is PtIdentifier) {
|
||||
throw AssemblyError("const-compare should have been optimized to have const as right operand")
|
||||
}
|
||||
else if(left is PtIdentifier && rightConstVal!=null) {
|
||||
val leftName = asmVariableName(left)
|
||||
val rightName = allocator.getFloatAsmConst(rightConstVal.number)
|
||||
out("""
|
||||
lda #<$leftName
|
||||
ldy #>$leftName
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
lda #<$rightName
|
||||
ldy #>$rightName
|
||||
jsr floats.vars_equal_f
|
||||
bne $jumpIfFalseLabel""")
|
||||
}
|
||||
else if(left is PtIdentifier && right is PtIdentifier) {
|
||||
val leftName = asmVariableName(left)
|
||||
val rightName = asmVariableName(right)
|
||||
out("""
|
||||
lda #<$leftName
|
||||
ldy #>$leftName
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
lda #<$rightName
|
||||
ldy #>$rightName
|
||||
jsr floats.vars_equal_f
|
||||
bne $jumpIfFalseLabel""")
|
||||
} else {
|
||||
val subroutine = left.definingSub()!!
|
||||
subroutineExtra(subroutine).usedFloatEvalResultVar1 = true
|
||||
assignExpressionToVariable(right, subroutineFloatEvalResultVar1, DataType.FLOAT)
|
||||
assignExpressionToRegister(left, RegisterOrPair.FAC1)
|
||||
out("""
|
||||
lda #<$subroutineFloatEvalResultVar1
|
||||
ldy #>$subroutineFloatEvalResultVar1
|
||||
jsr floats.var_fac1_notequal_f
|
||||
beq $jumpIfFalseLabel""")
|
||||
}
|
||||
}
|
||||
|
||||
private fun translateStringEqualsOrJumpElsewhere(left: PtIdentifier, right: PtIdentifier, jumpIfFalseLabel: String) {
|
||||
val leftNam = asmVariableName(left)
|
||||
val rightNam = asmVariableName(right)
|
||||
out("""
|
||||
lda #<$rightNam
|
||||
sta P8ZP_SCRATCH_W2
|
||||
lda #>$rightNam
|
||||
sta P8ZP_SCRATCH_W2+1
|
||||
lda #<$leftNam
|
||||
ldy #>$leftNam
|
||||
jsr prog8_lib.strcmp_mem
|
||||
bne $jumpIfFalseLabel""")
|
||||
}
|
||||
|
||||
private fun translateStringNotEqualsOrJumpElsewhere(left: PtIdentifier, right: PtIdentifier, jumpIfFalseLabel: String) {
|
||||
val leftNam = asmVariableName(left)
|
||||
val rightNam = asmVariableName(right)
|
||||
out("""
|
||||
lda #<$rightNam
|
||||
sta P8ZP_SCRATCH_W2
|
||||
lda #>$rightNam
|
||||
sta P8ZP_SCRATCH_W2+1
|
||||
lda #<$leftNam
|
||||
ldy #>$leftNam
|
||||
jsr prog8_lib.strcmp_mem
|
||||
beq $jumpIfFalseLabel""")
|
||||
}
|
||||
|
||||
private fun translateStringLessOrJumpElsewhere(left: PtIdentifier, right: PtIdentifier, jumpIfFalseLabel: String) {
|
||||
val leftNam = asmVariableName(left)
|
||||
val rightNam = asmVariableName(right)
|
||||
out("""
|
||||
lda #<$rightNam
|
||||
sta P8ZP_SCRATCH_W2
|
||||
lda #>$rightNam
|
||||
sta P8ZP_SCRATCH_W2+1
|
||||
lda #<$leftNam
|
||||
ldy #>$leftNam
|
||||
jsr prog8_lib.strcmp_mem
|
||||
bpl $jumpIfFalseLabel""")
|
||||
}
|
||||
|
||||
private fun translateStringGreaterOrJumpElsewhere(left: PtIdentifier, right: PtIdentifier, jumpIfFalseLabel: String) {
|
||||
val leftNam = asmVariableName(left)
|
||||
val rightNam = asmVariableName(right)
|
||||
out("""
|
||||
lda #<$rightNam
|
||||
sta P8ZP_SCRATCH_W2
|
||||
lda #>$rightNam
|
||||
sta P8ZP_SCRATCH_W2+1
|
||||
lda #<$leftNam
|
||||
ldy #>$leftNam
|
||||
jsr prog8_lib.strcmp_mem
|
||||
beq $jumpIfFalseLabel
|
||||
bmi $jumpIfFalseLabel""")
|
||||
}
|
||||
|
||||
private fun translateStringLessOrEqualOrJumpElsewhere(left: PtIdentifier, right: PtIdentifier, jumpIfFalseLabel: String) {
|
||||
val leftNam = asmVariableName(left)
|
||||
val rightNam = asmVariableName(right)
|
||||
out("""
|
||||
lda #<$rightNam
|
||||
sta P8ZP_SCRATCH_W2
|
||||
lda #>$rightNam
|
||||
sta P8ZP_SCRATCH_W2+1
|
||||
lda #<$leftNam
|
||||
ldy #>$leftNam
|
||||
jsr prog8_lib.strcmp_mem
|
||||
beq +
|
||||
bpl $jumpIfFalseLabel
|
||||
+""")
|
||||
}
|
||||
|
||||
private fun translateStringGreaterOrEqualOrJumpElsewhere(left: PtIdentifier, right: PtIdentifier, jumpIfFalseLabel: String) {
|
||||
val leftNam = asmVariableName(left)
|
||||
val rightNam = asmVariableName(right)
|
||||
out("""
|
||||
lda #<$rightNam
|
||||
sta P8ZP_SCRATCH_W2
|
||||
lda #>$rightNam
|
||||
sta P8ZP_SCRATCH_W2+1
|
||||
lda #<$leftNam
|
||||
ldy #>$leftNam
|
||||
jsr prog8_lib.strcmp_mem
|
||||
beq +
|
||||
bmi $jumpIfFalseLabel
|
||||
+""")
|
||||
}
|
||||
|
||||
internal fun translateDirectMemReadExpressionToRegA(expr: PtMemoryByte) {
|
||||
|
||||
fun assignViaExprEval() {
|
||||
@ -1940,100 +1317,6 @@ $repeatLabel""")
|
||||
}
|
||||
}
|
||||
|
||||
private fun wordJumpForSimpleLeftOperand(left: PtExpression, right: PtExpression, code: (String, String)->Unit): Boolean {
|
||||
when (left) {
|
||||
is PtBool -> TODO("word jump for bool operand")
|
||||
is PtNumber -> {
|
||||
assignExpressionToRegister(right, RegisterOrPair.AY)
|
||||
val number = left.number.toHex()
|
||||
code("#>$number", "#<$number")
|
||||
return true
|
||||
}
|
||||
is PtAddressOf -> {
|
||||
assignExpressionToRegister(right, RegisterOrPair.AY)
|
||||
val name = asmSymbolName(left.identifier)
|
||||
if(left.isFromArrayElement) {
|
||||
TODO("address-of array element $name at ${left.position}")
|
||||
} else {
|
||||
code("#>$name", "#<$name")
|
||||
return true
|
||||
}
|
||||
}
|
||||
is PtIdentifier -> {
|
||||
assignExpressionToRegister(right, RegisterOrPair.AY)
|
||||
val varname = asmVariableName(left)
|
||||
code("$varname+1", varname)
|
||||
return true
|
||||
}
|
||||
else -> return false
|
||||
}
|
||||
}
|
||||
|
||||
private fun byteJumpForSimpleRightOperand(left: PtExpression, right: PtExpression, code: (String)->Unit): Boolean {
|
||||
when (right) {
|
||||
is PtBool -> {
|
||||
assignExpressionToRegister(left, RegisterOrPair.A)
|
||||
code("#${right.asInt()} ; TODO can we get rid of this cmp when dealing with booleans?") // TODO can we get rid of the cmp when dealing with booleans?
|
||||
return true
|
||||
}
|
||||
is PtNumber -> {
|
||||
assignExpressionToRegister(left, RegisterOrPair.A)
|
||||
code("#${right.number.toHex()}")
|
||||
return true
|
||||
}
|
||||
|
||||
is PtIdentifier -> {
|
||||
assignExpressionToRegister(left, RegisterOrPair.A)
|
||||
code(asmVariableName(right))
|
||||
return true
|
||||
}
|
||||
|
||||
else -> {
|
||||
var memread = right as? PtMemoryByte
|
||||
if (memread == null && right is PtTypeCast)
|
||||
memread = right.value as? PtMemoryByte
|
||||
if (memread != null) {
|
||||
val address = memread.address as? PtNumber
|
||||
if (address != null) {
|
||||
assignExpressionToRegister(left, RegisterOrPair.A)
|
||||
code(address.number.toHex())
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun wordJumpForSimpleRightOperands(left: PtExpression, right: PtExpression, code: (String, String)->Unit): Boolean {
|
||||
when (right) {
|
||||
is PtBool -> TODO("word jump for bool operand")
|
||||
is PtNumber -> {
|
||||
assignExpressionToRegister(left, RegisterOrPair.AY)
|
||||
val number = right.number.toHex()
|
||||
code("#>$number", "#<$number")
|
||||
return true
|
||||
}
|
||||
is PtAddressOf -> {
|
||||
assignExpressionToRegister(left, RegisterOrPair.AY)
|
||||
val name = asmSymbolName(right.identifier)
|
||||
if(right.isFromArrayElement) {
|
||||
TODO("address-of array element $name at ${right.position}")
|
||||
} else {
|
||||
code("#>$name", "#<$name")
|
||||
return true
|
||||
}
|
||||
}
|
||||
is PtIdentifier -> {
|
||||
assignExpressionToRegister(left, RegisterOrPair.AY)
|
||||
val varname = asmVariableName(right)
|
||||
code("$varname+1", varname)
|
||||
return true
|
||||
}
|
||||
else -> return false
|
||||
}
|
||||
}
|
||||
|
||||
internal fun popCpuStack(asmsub: PtAsmSub, parameter: PtSubroutineParameter, reg: RegisterOrStatusflag) {
|
||||
val shouldKeepA = asmsub.parameters.any { it.first.registerOrPair==RegisterOrPair.AX || it.first.registerOrPair==RegisterOrPair.AY}
|
||||
if(reg.statusflag!=null) {
|
||||
|
@ -46,7 +46,7 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
|
||||
else if(sub is PtSub) {
|
||||
if(optimizeIntArgsViaRegisters(sub)) {
|
||||
if(sub.parameters.size==1) {
|
||||
val register = if (sub.parameters[0].type in ByteDatatypes) RegisterOrPair.A else RegisterOrPair.AY
|
||||
val register = if (sub.parameters[0].type in ByteDatatypesWithBoolean) RegisterOrPair.A else RegisterOrPair.AY
|
||||
argumentViaRegister(sub, IndexedValue(0, sub.parameters[0]), call.args[0], register)
|
||||
} else {
|
||||
// 2 byte params, second in Y, first in A
|
||||
@ -222,6 +222,8 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
|
||||
private fun isArgumentTypeCompatible(argType: DataType, paramType: DataType): Boolean {
|
||||
if(argType isAssignableTo paramType)
|
||||
return true
|
||||
if(argType==DataType.BOOL && paramType==DataType.BOOL)
|
||||
return true
|
||||
if(argType in ByteDatatypes && paramType in ByteDatatypes)
|
||||
return true
|
||||
if(argType in WordDatatypes && paramType in WordDatatypes)
|
||||
|
170
codeGenCpu6502/src/prog8/codegen/cpu6502/IfElseAsmGen.kt
Normal file
170
codeGenCpu6502/src/prog8/codegen/cpu6502/IfElseAsmGen.kt
Normal file
@ -0,0 +1,170 @@
|
||||
package prog8.codegen.cpu6502
|
||||
|
||||
import prog8.code.ast.*
|
||||
import prog8.code.core.*
|
||||
|
||||
internal class IfElseAsmGen(private val program: PtProgram, private val asmgen: AsmGen6502Internal) {
|
||||
|
||||
fun translate(stmt: PtIfElse) {
|
||||
require(stmt.condition.type== DataType.BOOL)
|
||||
|
||||
if(stmt.ifScope.children.singleOrNull() is PtJump) {
|
||||
translateIfWithOnlyJump(stmt)
|
||||
return
|
||||
}
|
||||
|
||||
val afterIfLabel = asmgen.makeLabel("afterif")
|
||||
|
||||
fun translateIfElseBodies(elseBranchInstr: String) {
|
||||
if(stmt.hasElse()) {
|
||||
// if and else blocks
|
||||
val elseLabel = asmgen.makeLabel("else")
|
||||
asmgen.out(" $elseBranchInstr $elseLabel")
|
||||
asmgen.translate(stmt.ifScope)
|
||||
asmgen.jmp(afterIfLabel, false)
|
||||
asmgen.out(elseLabel)
|
||||
asmgen.translate(stmt.elseScope)
|
||||
} else {
|
||||
// no else block
|
||||
asmgen.out(" $elseBranchInstr $afterIfLabel")
|
||||
asmgen.translate(stmt.ifScope)
|
||||
}
|
||||
asmgen.out(afterIfLabel)
|
||||
}
|
||||
|
||||
val compareCond = stmt.condition as? PtBinaryExpression
|
||||
if(compareCond!=null) {
|
||||
if((compareCond.right as? PtNumber)?.number==0.0 && compareCond.operator in arrayOf("==", "!=")) {
|
||||
if (compareCond.right.type in ByteDatatypesWithBoolean) {
|
||||
// equality comparison with 0 TODO temporary optimization
|
||||
val elseBranch = if (compareCond.operator == "==") "bne" else "beq"
|
||||
asmgen.assignExpressionToRegister(compareCond.left, RegisterOrPair.A, false)
|
||||
translateIfElseBodies(elseBranch)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
// TODO optimize if X comparison Y general case (also with 0 as a special case)
|
||||
}
|
||||
}
|
||||
|
||||
val prefixCond = stmt.condition as? PtPrefix
|
||||
if(prefixCond?.operator=="not") {
|
||||
asmgen.assignExpressionToRegister(prefixCond.value, RegisterOrPair.A, false)
|
||||
translateIfElseBodies("bne") // inverted condition, just swap the branches
|
||||
} else {
|
||||
asmgen.assignExpressionToRegister(stmt.condition, RegisterOrPair.A, false)
|
||||
translateIfElseBodies("beq")
|
||||
}
|
||||
}
|
||||
|
||||
private fun translateIfWithOnlyJump(stmt: PtIfElse) {
|
||||
val jump = stmt.ifScope.children.single() as PtJump
|
||||
val compareCond = stmt.condition as? PtBinaryExpression
|
||||
|
||||
fun doJumpAndElse(branchInstr: String, falseBranch: String, jump: PtJump) {
|
||||
val (asmLabel, indirect) = asmgen.getJumpTarget(jump)
|
||||
if(indirect) {
|
||||
asmgen.out("""
|
||||
$falseBranch +
|
||||
jmp ($asmLabel)
|
||||
+ """)
|
||||
} else {
|
||||
asmgen.out(" $branchInstr $asmLabel")
|
||||
}
|
||||
if(stmt.hasElse())
|
||||
asmgen.translate(stmt.elseScope)
|
||||
}
|
||||
|
||||
if(compareCond!=null) {
|
||||
if (compareCond.operator in arrayOf("==", "!=")) {
|
||||
val compareNumber = compareCond.right as? PtNumber
|
||||
when (compareCond.right.type) {
|
||||
in ByteDatatypesWithBoolean -> {
|
||||
asmgen.assignExpressionToRegister(compareCond.left, RegisterOrPair.A, false)
|
||||
if(compareNumber==null || compareNumber.number!=0.0)
|
||||
compareRegisterAwithByte(compareCond.right)
|
||||
if(compareCond.operator=="==") {
|
||||
// if X==something goto blah
|
||||
doJumpAndElse("beq", "bne", jump)
|
||||
return
|
||||
} else {
|
||||
// if X!=something goto blah
|
||||
doJumpAndElse("bne", "brq", jump)
|
||||
return
|
||||
}
|
||||
}
|
||||
in WordDatatypes -> {
|
||||
asmgen.assignExpressionToRegister(stmt.condition, RegisterOrPair.A, false)
|
||||
doJumpAndElse("bne", "beq", jump)
|
||||
return
|
||||
// TODO: optimize word
|
||||
// assignExpressionToRegister(compareCond.left, RegisterOrPair.AY, false)
|
||||
// compareRegisterAYwithWord(compareCond.operator, compareCond.right, jump)
|
||||
// if(compareCond.operator=="==") {
|
||||
// // if X==something goto blah
|
||||
// doJumpAndElse("beq", "bne", jump)
|
||||
// return
|
||||
// } else {
|
||||
// // if X!=something goto blah
|
||||
// doJumpAndElse("bne", "brq", jump)
|
||||
// return
|
||||
// }
|
||||
}
|
||||
DataType.FLOAT -> {
|
||||
TODO()
|
||||
}
|
||||
else -> {
|
||||
throw AssemblyError("weird dt")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
asmgen.assignExpressionToRegister(stmt.condition, RegisterOrPair.A, false)
|
||||
doJumpAndElse("bne", "beq", jump)
|
||||
}
|
||||
|
||||
private fun compareRegisterAwithByte(value: PtExpression) {
|
||||
fun cmpViaScratch() {
|
||||
if(!value.isSimple()) asmgen.out(" pha")
|
||||
asmgen.assignExpressionToVariable(value, "P8ZP_SCRATCH_REG", value.type)
|
||||
if(!value.isSimple()) asmgen.out(" pla")
|
||||
asmgen.out(" cmp P8ZP_SCRATCH_REG")
|
||||
}
|
||||
|
||||
// TODO how is if X==pointeervar[99] translated? can use cmp indirect indexed
|
||||
|
||||
when(value) {
|
||||
is PtArrayIndexer -> {
|
||||
val constIndex = value.index.asConstInteger()
|
||||
if(constIndex!=null) {
|
||||
val offset = constIndex * program.memsizer.memorySize(value.type)
|
||||
if(offset<256) {
|
||||
asmgen.out(" ldy #$offset | cmp ${asmgen.asmVariableName(value.variable.name)},y")
|
||||
return
|
||||
}
|
||||
}
|
||||
cmpViaScratch()
|
||||
}
|
||||
is PtMemoryByte -> {
|
||||
val constAddr = value.address.asConstInteger()
|
||||
if(constAddr!=null) {
|
||||
asmgen.out(" cmp ${constAddr.toHex()}")
|
||||
} else {
|
||||
cmpViaScratch()
|
||||
}
|
||||
}
|
||||
is PtIdentifier -> {
|
||||
asmgen.out(" cmp ${asmgen.asmVariableName(value.name)}")
|
||||
}
|
||||
is PtNumber -> {
|
||||
if(value.number!=0.0)
|
||||
asmgen.out(" cmp #${value.number.toInt()}")
|
||||
}
|
||||
else -> {
|
||||
cmpViaScratch()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -173,6 +173,7 @@ internal class ProgramAndVarsGen(
|
||||
for(num in 1..count) {
|
||||
val name = asmgen.buildTempVarName(dt, num)
|
||||
when (dt) {
|
||||
DataType.BOOL -> asmgen.out("$name .byte ?")
|
||||
DataType.BYTE -> asmgen.out("$name .char ?")
|
||||
DataType.UBYTE -> asmgen.out("$name .byte ?")
|
||||
DataType.WORD -> asmgen.out("$name .sint ?")
|
||||
|
@ -44,8 +44,8 @@ internal class AsmAssignTarget(val kind: TargetStorageKind,
|
||||
}
|
||||
|
||||
init {
|
||||
if(register!=null && datatype !in NumericDatatypes)
|
||||
throw AssemblyError("register must be integer or float type")
|
||||
if(register!=null && datatype !in NumericDatatypesWithBoolean)
|
||||
throw AssemblyError("must be numeric type")
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
@ -340,7 +340,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
}
|
||||
} else {
|
||||
// use a temporary variable
|
||||
val tempvar = if(value.type in ByteDatatypes) "P8ZP_SCRATCH_B1" else "P8ZP_SCRATCH_W1"
|
||||
val tempvar = if(value.type in ByteDatatypesWithBoolean) "P8ZP_SCRATCH_B1" else "P8ZP_SCRATCH_W1"
|
||||
assignExpressionToVariable(value.value, tempvar, value.type)
|
||||
when (value.operator) {
|
||||
"+" -> {}
|
||||
@ -394,16 +394,15 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
assignExpressionToRegister(assign.source.expression, RegisterOrPair.FAC1, true)
|
||||
assignFAC1float(assign.target)
|
||||
} else {
|
||||
// array[x] = -value ... use a tempvar then store that back into the array.
|
||||
val tempvar = asmgen.getTempVarName(assign.target.datatype)
|
||||
val assignToTempvar = AsmAssignment(assign.source,
|
||||
AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, assign.target.datatype, assign.target.scope, assign.target.position,
|
||||
variableAsmName=tempvar, origAstTarget = assign.target.origAstTarget), program.memsizer, assign.position)
|
||||
asmgen.translateNormalAssignment(assignToTempvar, scope)
|
||||
val register = if(assign.source.datatype in ByteDatatypesWithBoolean) RegisterOrPair.A else RegisterOrPair.AY
|
||||
val assignToRegister = AsmAssignment(assign.source,
|
||||
AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, assign.target.datatype, assign.target.scope, assign.target.position,
|
||||
register = register, origAstTarget = assign.target.origAstTarget), program.memsizer, assign.position)
|
||||
asmgen.translateNormalAssignment(assignToRegister, scope)
|
||||
val signed = assign.target.datatype in SignedDatatypes
|
||||
when(assign.target.datatype) {
|
||||
in ByteDatatypesWithBoolean -> assignVariableByte(assign.target, tempvar)
|
||||
in WordDatatypes -> assignVariableWord(assign.target, tempvar, assign.source.datatype)
|
||||
DataType.FLOAT -> assignVariableFloat(assign.target, tempvar)
|
||||
in ByteDatatypesWithBoolean -> assignRegisterByte(assign.target, CpuRegister.A, signed, false)
|
||||
in WordDatatypes -> assignRegisterpairWord(assign.target, RegisterOrPair.AY)
|
||||
else -> throw AssemblyError("weird dt")
|
||||
}
|
||||
}
|
||||
@ -1149,6 +1148,22 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
return true
|
||||
}
|
||||
}
|
||||
val rightArray = expr.right as? PtArrayIndexer
|
||||
if(rightArray!=null) {
|
||||
val constIndex = rightArray.index.asConstInteger()
|
||||
if(constIndex!=null) {
|
||||
assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
|
||||
val valueVarname = "${asmgen.asmSymbolName(rightArray.variable)} + ${constIndex*program.memsizer.memorySize(rightArray.type)}"
|
||||
when(expr.operator) {
|
||||
"&" -> asmgen.out(" and $valueVarname")
|
||||
"|" -> asmgen.out(" ora $valueVarname")
|
||||
"^" -> asmgen.out(" eor $valueVarname")
|
||||
else -> throw AssemblyError("invalid logical operator")
|
||||
}
|
||||
assignRegisterByte(target, CpuRegister.A, false, true)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
|
||||
if(directIntoY(expr.right)) {
|
||||
@ -1162,7 +1177,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
when (expr.operator) {
|
||||
"&" -> asmgen.out(" and P8ZP_SCRATCH_B1")
|
||||
"|" -> asmgen.out(" ora P8ZP_SCRATCH_B1")
|
||||
"^", "xor" -> asmgen.out(" eor P8ZP_SCRATCH_B1")
|
||||
"^" -> asmgen.out(" eor P8ZP_SCRATCH_B1")
|
||||
else -> throw AssemblyError("invalid bitwise operator")
|
||||
}
|
||||
assignRegisterByte(target, CpuRegister.A, false, true)
|
||||
@ -1183,7 +1198,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
when (expr.operator) {
|
||||
"&" -> asmgen.out(" and P8ZP_SCRATCH_W1 | tax | tya | and P8ZP_SCRATCH_W1+1 | tay | txa")
|
||||
"|" -> asmgen.out(" ora P8ZP_SCRATCH_W1 | tax | tya | ora P8ZP_SCRATCH_W1+1 | tay | txa")
|
||||
"^", "xor" -> asmgen.out(" eor P8ZP_SCRATCH_W1 | tax | tya | eor P8ZP_SCRATCH_W1+1 | tay | txa")
|
||||
"^" -> asmgen.out(" eor P8ZP_SCRATCH_W1 | tax | tya | eor P8ZP_SCRATCH_W1+1 | tay | txa")
|
||||
else -> throw AssemblyError("invalid bitwise operator")
|
||||
}
|
||||
assignRegisterpairWord(target, RegisterOrPair.AY)
|
||||
@ -1193,61 +1208,103 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
}
|
||||
|
||||
private fun optimizedLogicalExpr(expr: PtBinaryExpression, target: AsmAssignTarget): Boolean {
|
||||
if (expr.left.type in ByteDatatypesWithBoolean && expr.right.type in ByteDatatypesWithBoolean) {
|
||||
if (expr.right is PtBool || expr.right is PtNumber || expr.right is PtIdentifier) {
|
||||
assignLogicalAndOrWithSimpleRightOperandByte(target, expr.left, expr.operator, expr.right)
|
||||
return true
|
||||
}
|
||||
else if (expr.left is PtBool || expr.left is PtNumber || expr.left is PtIdentifier) {
|
||||
assignLogicalAndOrWithSimpleRightOperandByte(target, expr.right, expr.operator, expr.left)
|
||||
return true
|
||||
}
|
||||
|
||||
if(!expr.right.isSimple() && expr.operator!="xor") {
|
||||
// shortcircuit evaluation into A
|
||||
val shortcutLabel = asmgen.makeLabel("shortcut")
|
||||
when (expr.operator) {
|
||||
"and" -> {
|
||||
// short-circuit LEFT and RIGHT --> if LEFT then RIGHT else LEFT (== if !LEFT then LEFT else RIGHT)
|
||||
assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
|
||||
asmgen.out(" beq $shortcutLabel")
|
||||
assignExpressionToRegister(expr.right, RegisterOrPair.A, false)
|
||||
asmgen.out(shortcutLabel)
|
||||
}
|
||||
"or" -> {
|
||||
// short-circuit LEFT or RIGHT --> if LEFT then LEFT else RIGHT
|
||||
assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
|
||||
asmgen.out(" bne $shortcutLabel")
|
||||
assignExpressionToRegister(expr.right, RegisterOrPair.A, false)
|
||||
asmgen.out(shortcutLabel)
|
||||
}
|
||||
else -> throw AssemblyError("invalid logical operator")
|
||||
}
|
||||
} else {
|
||||
// normal evaluation into A, it is *likely* shorter and faster because of the simple operands.
|
||||
assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
|
||||
if(directIntoY(expr.right)) {
|
||||
assignExpressionToRegister(expr.right, RegisterOrPair.Y, false)
|
||||
fun swapOperands(): Boolean =
|
||||
if(expr.right is PtIdentifier || expr.right is PtMemoryByte)
|
||||
false
|
||||
else
|
||||
expr.left is PtIdentifier || expr.left is PtMemoryByte
|
||||
|
||||
fun assignResultIntoA(left: PtExpression, operator: String, right: PtExpression) {
|
||||
// non short-circuit evaluation it is *likely* shorter and faster because of the simple operands.
|
||||
|
||||
fun assignViaScratch() {
|
||||
if(directIntoY(right)) {
|
||||
assignExpressionToRegister(right, RegisterOrPair.Y, false)
|
||||
asmgen.out(" sty P8ZP_SCRATCH_B1")
|
||||
} else {
|
||||
asmgen.out(" pha")
|
||||
assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
|
||||
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
|
||||
asmgen.out(" pla")
|
||||
}
|
||||
when (expr.operator) {
|
||||
when (operator) {
|
||||
"and" -> asmgen.out(" and P8ZP_SCRATCH_B1")
|
||||
"or" -> asmgen.out(" ora P8ZP_SCRATCH_B1")
|
||||
"xor" -> asmgen.out(" eor P8ZP_SCRATCH_B1")
|
||||
else -> throw AssemblyError("invalid logical operator")
|
||||
}
|
||||
}
|
||||
assignRegisterByte(target, CpuRegister.A, false, true)
|
||||
return true
|
||||
|
||||
assignExpressionToRegister(left, RegisterOrPair.A, false)
|
||||
when(right) {
|
||||
is PtBool -> throw AssemblyError("bool literal in logical expr should have been optimized away")
|
||||
is PtIdentifier -> {
|
||||
val varname = asmgen.asmVariableName(right)
|
||||
when (operator) {
|
||||
"and" -> asmgen.out(" and $varname")
|
||||
"or" -> asmgen.out(" ora $varname")
|
||||
"xor" -> asmgen.out(" eor $varname")
|
||||
else -> throw AssemblyError("invalid logical operator")
|
||||
}
|
||||
}
|
||||
is PtMemoryByte -> {
|
||||
val constAddress = right.address.asConstInteger()
|
||||
if(constAddress!=null) {
|
||||
when (operator) {
|
||||
"and" -> asmgen.out(" and ${constAddress.toHex()}")
|
||||
"or" -> asmgen.out(" ora ${constAddress.toHex()}")
|
||||
"xor" -> asmgen.out(" eor ${constAddress.toHex()}")
|
||||
else -> throw AssemblyError("invalid logical operator")
|
||||
}
|
||||
}
|
||||
else assignViaScratch()
|
||||
}
|
||||
is PtArrayIndexer -> {
|
||||
val constIndex = right.index.asConstInteger()
|
||||
if(constIndex!=null) {
|
||||
val valueVarname = "${asmgen.asmSymbolName(right.variable)} + ${constIndex*program.memsizer.memorySize(right.type)}"
|
||||
when(operator) {
|
||||
"and" -> asmgen.out(" and $valueVarname")
|
||||
"or" -> asmgen.out(" ora $valueVarname")
|
||||
"xor" -> asmgen.out(" eor $valueVarname")
|
||||
else -> throw AssemblyError("invalid logical operator")
|
||||
}
|
||||
}
|
||||
else assignViaScratch()
|
||||
}
|
||||
else -> assignViaScratch()
|
||||
}
|
||||
}
|
||||
else if (expr.left.type in WordDatatypes && expr.right.type in WordDatatypes) {
|
||||
throw AssemblyError("logical and/or/xor on words should be done on typecast to bytes instead")
|
||||
|
||||
if(!expr.right.isSimple() && expr.operator!="xor") {
|
||||
// shortcircuit evaluation into A
|
||||
val shortcutLabel = asmgen.makeLabel("shortcut")
|
||||
when (expr.operator) {
|
||||
"and" -> {
|
||||
// short-circuit LEFT and RIGHT --> if LEFT then RIGHT else LEFT (== if !LEFT then LEFT else RIGHT)
|
||||
assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
|
||||
asmgen.out(" beq $shortcutLabel")
|
||||
assignExpressionToRegister(expr.right, RegisterOrPair.A, false)
|
||||
asmgen.out(shortcutLabel)
|
||||
}
|
||||
"or" -> {
|
||||
// short-circuit LEFT or RIGHT --> if LEFT then LEFT else RIGHT
|
||||
assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
|
||||
asmgen.out(" bne $shortcutLabel")
|
||||
assignExpressionToRegister(expr.right, RegisterOrPair.A, false)
|
||||
asmgen.out(shortcutLabel)
|
||||
}
|
||||
else -> throw AssemblyError("invalid logical operator")
|
||||
}
|
||||
} else if(swapOperands()) {
|
||||
// non short-circuit evaluation is *likely* shorter and faster because of the simple operands.
|
||||
assignResultIntoA(expr.right, expr.operator, expr.left)
|
||||
} else {
|
||||
assignResultIntoA(expr.left, expr.operator, expr.right)
|
||||
}
|
||||
return false
|
||||
|
||||
assignRegisterByte(target, CpuRegister.A, false, true)
|
||||
return true
|
||||
}
|
||||
|
||||
private fun assignBitwiseWithSimpleRightOperandByte(target: AsmAssignTarget, left: PtExpression, operator: String, right: PtExpression) {
|
||||
@ -1266,26 +1323,6 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
assignRegisterByte(target, CpuRegister.A, false, true)
|
||||
}
|
||||
|
||||
private fun assignLogicalAndOrWithSimpleRightOperandByte(target: AsmAssignTarget, left: PtExpression, operator: String, right: PtExpression) {
|
||||
// normal evaluation, not worth to shortcircuit the simple right operand
|
||||
assignExpressionToRegister(left, RegisterOrPair.A, false)
|
||||
if(directIntoY(right)) {
|
||||
assignExpressionToRegister(right, RegisterOrPair.Y, false)
|
||||
asmgen.out(" sty P8ZP_SCRATCH_B1")
|
||||
} else {
|
||||
asmgen.out(" pha")
|
||||
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
|
||||
asmgen.out(" pla")
|
||||
}
|
||||
when (operator) {
|
||||
"and" -> asmgen.out(" and P8ZP_SCRATCH_B1")
|
||||
"or" -> asmgen.out(" ora P8ZP_SCRATCH_B1")
|
||||
"xor" -> asmgen.out(" eor P8ZP_SCRATCH_B1")
|
||||
else -> throw AssemblyError("invalid logical operator")
|
||||
}
|
||||
assignRegisterByte(target, CpuRegister.A, false, true)
|
||||
}
|
||||
|
||||
private fun assignBitwiseWithSimpleRightOperandWord(target: AsmAssignTarget, left: PtExpression, operator: String, right: PtExpression) {
|
||||
assignExpressionToRegister(left, RegisterOrPair.AY, false)
|
||||
when(right) {
|
||||
@ -1555,7 +1592,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
}
|
||||
}
|
||||
|
||||
if(valueDt in ByteDatatypes) {
|
||||
if(valueDt in ByteDatatypesWithBoolean) {
|
||||
when(target.register) {
|
||||
RegisterOrPair.A,
|
||||
RegisterOrPair.X,
|
||||
@ -2763,7 +2800,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
if(indexVar!=null) {
|
||||
asmgen.out(" ldy ${asmgen.asmVariableName(indexVar)} | sta ${target.asmVarname},y")
|
||||
} else {
|
||||
require(target.array.index.type in ByteDatatypes)
|
||||
require(target.array.index.type in ByteDatatypesWithBoolean)
|
||||
asmgen.saveRegisterStack(register, false)
|
||||
asmgen.assignExpressionToRegister(target.array.index, RegisterOrPair.Y, false)
|
||||
asmgen.out(" pla | sta ${target.asmVarname},y")
|
||||
@ -3487,7 +3524,10 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
else -> throw AssemblyError("invalid reg dt for byte invert")
|
||||
}
|
||||
}
|
||||
TargetStorageKind.ARRAY -> assignPrefixedExpressionToArrayElt(makePrefixedExprFromArrayExprAssign("~", assign), scope)
|
||||
TargetStorageKind.ARRAY -> {
|
||||
val invertOperator = if(assign.target.datatype==DataType.BOOL) "not" else "~"
|
||||
assignPrefixedExpressionToArrayElt(makePrefixedExprFromArrayExprAssign(invertOperator, assign), scope)
|
||||
}
|
||||
}
|
||||
}
|
||||
DataType.UWORD -> {
|
||||
|
@ -63,9 +63,6 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
|
||||
fun regName(v: AsmAssignSource) = "cx16.${v.register!!.name.lowercase()}"
|
||||
|
||||
if(value.kind==SourceStorageKind.LITERALBOOLEAN)
|
||||
TODO("inplace modification literalboolean")
|
||||
|
||||
when (target.kind) {
|
||||
TargetStorageKind.VARIABLE -> {
|
||||
when (target.datatype) {
|
||||
@ -240,7 +237,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
// normal array
|
||||
val targetVarName = "${target.asmVarname} + ${index*program.memsizer.memorySize(target.datatype)}"
|
||||
when (target.datatype) {
|
||||
in ByteDatatypes -> {
|
||||
in ByteDatatypesWithBoolean -> {
|
||||
when(value.kind) {
|
||||
SourceStorageKind.LITERALBOOLEAN -> inplacemodificationByteVariableWithLiteralval(targetVarName, target.datatype, operator, value.boolean!!.asInt())
|
||||
SourceStorageKind.LITERALNUMBER -> inplacemodificationByteVariableWithLiteralval(targetVarName, target.datatype, operator, value.number!!.number.toInt())
|
||||
@ -309,7 +306,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
return
|
||||
|
||||
when (target.datatype) {
|
||||
in ByteDatatypes -> {
|
||||
in ByteDatatypesWithBoolean -> {
|
||||
if(value.kind==SourceStorageKind.EXPRESSION
|
||||
&& value.expression is PtTypeCast
|
||||
&& tryInplaceModifyWithRemovedRedundantCast(value.expression, target, operator))
|
||||
@ -1092,27 +1089,31 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
}
|
||||
|
||||
private fun inplacemodificationByteVariableWithValue(name: String, dt: DataType, operator: String, value: PtExpression) {
|
||||
val shortcutLabel = asmgen.makeLabel("shortcut")
|
||||
when (operator) {
|
||||
"and" -> {
|
||||
// short-circuit LEFT and RIGHT --> if LEFT then RIGHT else LEFT (== if !LEFT then LEFT else RIGHT)
|
||||
asmgen.out(" lda $name | beq $shortcutLabel")
|
||||
asmgen.assignExpressionToRegister(value, RegisterOrPair.A, dt in SignedDatatypes)
|
||||
asmgen.out("""
|
||||
and $name
|
||||
sta $name
|
||||
if(!value.isSimple()) {
|
||||
// attempt short-circuit (McCarthy) evaluation
|
||||
when (operator) {
|
||||
"and" -> {
|
||||
// short-circuit LEFT and RIGHT --> if LEFT then RIGHT else LEFT (== if !LEFT then LEFT else RIGHT)
|
||||
val shortcutLabel = asmgen.makeLabel("shortcut")
|
||||
asmgen.out(" lda $name | beq $shortcutLabel")
|
||||
asmgen.assignExpressionToRegister(value, RegisterOrPair.A, dt in SignedDatatypes)
|
||||
asmgen.out("""
|
||||
and $name
|
||||
sta $name
|
||||
$shortcutLabel:""")
|
||||
return
|
||||
}
|
||||
"or" -> {
|
||||
// short-circuit LEFT or RIGHT --> if LEFT then LEFT else RIGHT
|
||||
asmgen.out(" lda $name | bne $shortcutLabel")
|
||||
asmgen.assignExpressionToRegister(value, RegisterOrPair.A, dt in SignedDatatypes)
|
||||
asmgen.out("""
|
||||
ora $name
|
||||
sta $name
|
||||
return
|
||||
}
|
||||
"or" -> {
|
||||
// short-circuit LEFT or RIGHT --> if LEFT then LEFT else RIGHT
|
||||
val shortcutLabel = asmgen.makeLabel("shortcut")
|
||||
asmgen.out(" lda $name | bne $shortcutLabel")
|
||||
asmgen.assignExpressionToRegister(value, RegisterOrPair.A, dt in SignedDatatypes)
|
||||
asmgen.out("""
|
||||
ora $name
|
||||
sta $name
|
||||
$shortcutLabel:""")
|
||||
return
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -247,8 +247,8 @@ class IRCodeGen(
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
|
||||
val goto = branch.trueScope.children.firstOrNull() as? PtJump
|
||||
if(goto is PtJump && branch.falseScope.children.isEmpty()) {
|
||||
// special case the form: if_cc <condition> goto <place>
|
||||
if (goto is PtJump) {
|
||||
// special case the form: if_cc goto <place> (with optional else)
|
||||
val address = goto.address?.toInt()
|
||||
if(address!=null) {
|
||||
val branchIns = when(branch.condition) {
|
||||
@ -276,10 +276,11 @@ class IRCodeGen(
|
||||
}
|
||||
addInstr(result, branchIns, null)
|
||||
}
|
||||
if(branch.falseScope.children.isNotEmpty())
|
||||
result += translateNode(branch.falseScope)
|
||||
return result
|
||||
}
|
||||
|
||||
|
||||
val elseLabel = createLabelName()
|
||||
// note that the branch opcode used is the opposite as the branch condition, because the generated code jumps to the 'else' part
|
||||
val branchIns = when(branch.condition) {
|
||||
@ -1093,7 +1094,7 @@ class IRCodeGen(
|
||||
else -> throw AssemblyError("weird operator")
|
||||
}
|
||||
|
||||
if (ifElse.elseScope.children.isNotEmpty()) {
|
||||
if (ifElse.hasElse()) {
|
||||
// if and else parts
|
||||
val elseLabel = createLabelName()
|
||||
val afterIfLabel = createLabelName()
|
||||
@ -1126,12 +1127,11 @@ class IRCodeGen(
|
||||
|
||||
private fun ifWithElse_IntegerCond(ifElse: PtIfElse): List<IRCodeChunkBase> {
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
val hasElse = ifElse.elseScope.children.isNotEmpty()
|
||||
|
||||
fun translateSimple(condition: PtExpression, jumpFalseOpcode: Opcode) {
|
||||
val tr = expressionEval.translateExpression(condition)
|
||||
result += tr.chunks
|
||||
if(hasElse) {
|
||||
if(ifElse.hasElse()) {
|
||||
val elseLabel = createLabelName()
|
||||
val afterIfLabel = createLabelName()
|
||||
addInstr(result, IRInstruction(jumpFalseOpcode, labelSymbol = elseLabel), null)
|
||||
@ -1178,7 +1178,7 @@ class IRCodeGen(
|
||||
else -> throw AssemblyError("invalid comparison operator")
|
||||
}
|
||||
|
||||
if (hasElse) {
|
||||
if (ifElse.hasElse()) {
|
||||
// if and else parts
|
||||
val elseLabel = createLabelName()
|
||||
val afterIfLabel = createLabelName()
|
||||
@ -1252,7 +1252,7 @@ class IRCodeGen(
|
||||
else -> throw AssemblyError("invalid comparison operator")
|
||||
}
|
||||
|
||||
if (hasElse) {
|
||||
if (ifElse.hasElse()) {
|
||||
// if and else parts
|
||||
val elseLabel = createLabelName()
|
||||
val afterIfLabel = createLabelName()
|
||||
|
@ -112,10 +112,6 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
|
||||
if (!leftIDt.isKnown || !rightIDt.isKnown)
|
||||
throw FatalAstException("can't determine datatype of both expression operands $expr")
|
||||
|
||||
// ConstValue <associativeoperator> X --> X <associativeoperator> ConstValue
|
||||
if (leftVal != null && expr.operator in AssociativeOperators && rightVal == null && maySwapOperandOrder(expr))
|
||||
return listOf(IAstModification.SwapOperands(expr))
|
||||
|
||||
// NonBinaryExpression <associativeoperator> BinaryExpression --> BinaryExpression <associativeoperator> NonBinaryExpression
|
||||
if (expr.operator in AssociativeOperators && expr.left !is BinaryExpression && expr.right is BinaryExpression) {
|
||||
if(parent !is Assignment || !(expr.left isSameAs parent.target) && maySwapOperandOrder(expr))
|
||||
|
@ -103,25 +103,40 @@ class StatementOptimizer(private val program: Program,
|
||||
}
|
||||
}
|
||||
|
||||
// remove obvious dangling elses (else after a return)
|
||||
if(ifElse.elsepart.isNotEmpty() && ifElse.truepart.statements.singleOrNull() is Return) {
|
||||
val elsePart = AnonymousScope(ifElse.elsepart.statements, ifElse.elsepart.position)
|
||||
return listOf(
|
||||
IAstModification.ReplaceNode(ifElse.elsepart, AnonymousScope(mutableListOf(), ifElse.elsepart.position), ifElse),
|
||||
IAstModification.InsertAfter(ifElse, elsePart, parent as IStatementContainer)
|
||||
)
|
||||
}
|
||||
if(ifElse.elsepart.isNotEmpty()) {
|
||||
// remove obvious dangling elses (else after a return)
|
||||
if(ifElse.truepart.statements.singleOrNull() is Return) {
|
||||
val elsePart = AnonymousScope(ifElse.elsepart.statements, ifElse.elsepart.position)
|
||||
return listOf(
|
||||
IAstModification.ReplaceNode(ifElse.elsepart, AnonymousScope(mutableListOf(), ifElse.elsepart.position), ifElse),
|
||||
IAstModification.InsertAfter(ifElse, elsePart, parent as IStatementContainer)
|
||||
)
|
||||
}
|
||||
|
||||
// switch if/else around if the else is just a jump or branch
|
||||
if(ifElse.elsepart.isNotEmpty() && ifElse.elsepart.statements.size==1) {
|
||||
val jump = ifElse.elsepart.statements[0]
|
||||
if(jump is Jump) {
|
||||
val newTruePart = AnonymousScope(mutableListOf(jump), ifElse.elsepart.position)
|
||||
// switch if/else around if the else is just a jump or branch
|
||||
if(ifElse.elsepart.statements.size==1) {
|
||||
val jump = ifElse.elsepart.statements[0]
|
||||
if(jump is Jump) {
|
||||
val newTruePart = AnonymousScope(mutableListOf(jump), ifElse.elsepart.position)
|
||||
val newElsePart = AnonymousScope(ifElse.truepart.statements, ifElse.truepart.position)
|
||||
return listOf(
|
||||
IAstModification.ReplaceNode(ifElse.elsepart, newElsePart, ifElse),
|
||||
IAstModification.ReplaceNode(ifElse.truepart, newTruePart, ifElse),
|
||||
IAstModification.ReplaceNode(ifElse.condition, invertCondition(ifElse.condition, program), ifElse)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// switch if/else around if the condition is a not
|
||||
val prefixCond = ifElse.condition as? PrefixExpression
|
||||
if(prefixCond?.operator=="not") {
|
||||
errors.info("invert conditon and swap if/else blocks", ifElse.condition.position)
|
||||
val newTruePart = AnonymousScope(ifElse.elsepart.statements, ifElse.elsepart.position)
|
||||
val newElsePart = AnonymousScope(ifElse.truepart.statements, ifElse.truepart.position)
|
||||
return listOf(
|
||||
IAstModification.ReplaceNode(ifElse.elsepart, newElsePart, ifElse),
|
||||
IAstModification.ReplaceNode(ifElse.truepart, newTruePart, ifElse),
|
||||
IAstModification.ReplaceNode(ifElse.condition, invertCondition(ifElse.condition, program), ifElse)
|
||||
IAstModification.ReplaceNode(ifElse.condition, prefixCond.expression, ifElse)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -103,7 +103,9 @@ internal class BeforeAsmTypecastCleaner(val program: Program,
|
||||
val arg2 = bfcs.args[1]
|
||||
val dt1 = arg1.inferType(program).getOr(DataType.UNDEFINED)
|
||||
val dt2 = arg2.inferType(program).getOr(DataType.UNDEFINED)
|
||||
if(dt1 in ByteDatatypes) {
|
||||
if(dt1==DataType.BOOL && dt2==DataType.BOOL)
|
||||
return noModifications
|
||||
else if(dt1 in ByteDatatypes) {
|
||||
if(dt2 in ByteDatatypes)
|
||||
return noModifications
|
||||
val (replaced, cast) = arg1.typecastTo(if(dt1== DataType.UBYTE) DataType.UWORD else DataType.WORD, dt1, true)
|
||||
|
@ -1,9 +1,7 @@
|
||||
package prog8.compiler.astprocessing
|
||||
|
||||
import prog8.ast.*
|
||||
import prog8.ast.expressions.ArrayLiteral
|
||||
import prog8.ast.expressions.BinaryExpression
|
||||
import prog8.ast.expressions.IdentifierReference
|
||||
import prog8.ast.expressions.*
|
||||
import prog8.ast.statements.*
|
||||
import prog8.ast.walk.AstWalker
|
||||
import prog8.ast.walk.IAstModification
|
||||
@ -173,15 +171,18 @@ internal class StatementReorderer(
|
||||
}
|
||||
|
||||
override fun after(expr: BinaryExpression, parent: Node): Iterable<IAstModification> {
|
||||
// ConstValue <associativeoperator> X --> X <associativeoperator> ConstValue
|
||||
// simplething <associative> X -> X <associative> simplething
|
||||
// (this should be done by the ExpressionSimplifier when optimizing is enabled,
|
||||
// but the current assembly code generator for IF statements now also depends on it, so we do it here regardless of optimization.)
|
||||
if (expr.left.constValue(program) != null
|
||||
&& expr.operator in AssociativeOperators
|
||||
&& expr.right.constValue(program) == null
|
||||
&& maySwapOperandOrder(expr))
|
||||
return listOf(IAstModification.SwapOperands(expr))
|
||||
|
||||
if(expr.operator in AssociativeOperators) {
|
||||
if(expr.left is IdentifierReference || expr.left is NumericLiteral || expr.left is DirectMemoryRead || (expr.left as? ArrayIndexedExpression)?.indexer?.constIndex()!=null) {
|
||||
if(expr.right !is IdentifierReference && expr.right !is NumericLiteral && expr.right !is DirectMemoryRead) {
|
||||
if(maySwapOperandOrder(expr)) {
|
||||
return listOf(IAstModification.SwapOperands(expr))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return noModifications
|
||||
}
|
||||
|
||||
|
@ -4,27 +4,41 @@ TODO
|
||||
===== ====== =======
|
||||
VM 6502 what
|
||||
===== ====== =======
|
||||
ok . boolean const
|
||||
ok . boolean variables value, boolean subroutine param
|
||||
ok . static bool var (block scope) with initializer value (staticVariable2asm)
|
||||
ok . boolean arrays value, list and single value
|
||||
ok . return boolean value from sub
|
||||
ok . logical not, and, or, xor work correctly, also inplace
|
||||
ok . make sure that and,or,xor,not aren't getting replaced by the bitwise versions in the Ast
|
||||
ok . and, or, xor, not should work in expressions: print_ub((bb and true) as ubyte)
|
||||
ok . bitwise logical ops on bools give type error, including invert
|
||||
ok . arithmetic ops on bools give type error
|
||||
ok . logical ops on ints give type error
|
||||
ok . boolean values in ubyte array should give type error
|
||||
ok . type error for bool[3] derp = 99 and also for init value [1,0,1] and also for [true, false, 1, 0, 222]
|
||||
ok . while booleanvar==42 and do..until booleanvar==42 should give type error
|
||||
ok . while not <integervar> should give type error
|
||||
ok . while not <integer functioncall> should give type error
|
||||
ok . while not cx16.mouse_pos() should give condition type error
|
||||
ok . while boolean should produce identical code as while integer!=0
|
||||
ok ok boolean const
|
||||
ok ok boolean variables value, boolean subroutine param
|
||||
ok ok static bool var (block scope) with initializer value (staticVariable2asm)
|
||||
ok ok boolean arrays value, list and single value
|
||||
ok ok return boolean value from sub
|
||||
ok ok logical not, and, or, xor work correctly, also inplace
|
||||
ok ok make sure that and,or,xor,not aren't getting replaced by the bitwise versions in the Ast
|
||||
ok ok and, or, xor, not should work in expressions: print_ub((bb and true) as ubyte)
|
||||
ok ok swap operands around on evaluating staticboolvar xor ba[0]
|
||||
ok ok bitwise logical ops on bools give type error, including invert
|
||||
ok ok arithmetic ops on bools give type error
|
||||
ok ok logical ops on ints give type error
|
||||
ok ok boolean values in ubyte array should give type error
|
||||
ok ok type error for bool[3] derp = 99 and also for init value [1,0,1] and also for [true, false, 1, 0, 222]
|
||||
ok ok while booleanvar==42 and do..until booleanvar==42 should give type error
|
||||
ok ok while not <integervar> should give type error
|
||||
ok ok while not <integer functioncall> should give type error
|
||||
ok ok while not cx16.mouse_pos() should give condition type error
|
||||
ok ok efficient code for manipulating bools in an array (normal and agumented assigns)
|
||||
ok WIP efficient code for if with only a goto in it
|
||||
ok . efficient code for if byte comparisons against 0 (== and !=)
|
||||
ok . efficient code for if word comparisons against 0 (== and !=)
|
||||
ok . efficient code for if float comparisons against 0 (== and !=)
|
||||
ok . efficient code for if byte comparisons against a value
|
||||
ok . efficient code for if word comparisons against a value
|
||||
ok . efficient code for if float comparisons against a value
|
||||
ok . efficient code for assignment byte comparisons against 0 (== and !=)
|
||||
ok . efficient code for assignment word comparisons against 0 (== and !=)
|
||||
ok . efficient code for assignment float comparisons against 0 (== and !=)
|
||||
ok . efficient code for assignment byte comparisons against a value
|
||||
ok . efficient code for assignment word comparisons against a value
|
||||
ok . efficient code for assignment float comparisons against a value
|
||||
ok ok efficient code for if_cc conditional expressions
|
||||
ok FAIL while boolean should produce identical code as while integer!=0 and code should be efficient
|
||||
ok . while not boolvar -> can we get rid of the cmp? (6502 only?)
|
||||
ok . if someint==0 / ==1 should stil produce good asm same as what it used to be with if not someint/if someint
|
||||
ok . efficient code for manipulating bools in an array (normal and agumented assigns)
|
||||
ok . testmonogfx works
|
||||
ok . check program sizes vs. master branch
|
||||
===== ====== =======
|
||||
|
191
examples/test.p8
191
examples/test.p8
@ -1,4 +1,5 @@
|
||||
%import textio
|
||||
%import floats
|
||||
%zeropage basicsafe
|
||||
%option no_sysinit
|
||||
|
||||
@ -7,22 +8,29 @@ main {
|
||||
bool @shared staticbool2
|
||||
|
||||
sub start() {
|
||||
boolean_const_and_var(true)
|
||||
staticbool1 = boolean_arrays_and_return()
|
||||
txt.print_ub(staticbool1 as ubyte)
|
||||
txt.nl()
|
||||
and_or_xor_not()
|
||||
; boolean_const_and_var(true)
|
||||
; staticbool1 = boolean_arrays_and_return()
|
||||
; txt.print_ub(staticbool1 as ubyte)
|
||||
; txt.nl()
|
||||
; and_or_xor_not()
|
||||
; logical_operand_swap()
|
||||
; bitwise_on_bools_errors()
|
||||
; arith_on_bools_errors()
|
||||
; logical_on_ints_errors()
|
||||
; bools_in_intarray_errors()
|
||||
; ints_in_boolarray_errors()
|
||||
; while_until_int_errors()
|
||||
bools_in_array_assigns()
|
||||
bools_in_array_assigns_inplace()
|
||||
if_code()
|
||||
; bools_in_array_assigns()
|
||||
; bools_in_array_assigns_inplace()
|
||||
; while_bool_efficient()
|
||||
; efficient_compare_0()
|
||||
; efficient_compare_99()
|
||||
; efficient_assign_cmp_0()
|
||||
; efficient_assign_cmp_99()
|
||||
if_gotos()
|
||||
; if_code()
|
||||
;;sys.exit(1)
|
||||
while_equiv()
|
||||
; while_equiv()
|
||||
|
||||
; bool[3] barr
|
||||
; bool @shared bb
|
||||
@ -42,6 +50,108 @@ main {
|
||||
; bb = not bb
|
||||
}
|
||||
|
||||
sub while_bool_efficient() {
|
||||
while staticbool1 {
|
||||
cx16.r0++
|
||||
}
|
||||
while not staticbool1 {
|
||||
cx16.r0++
|
||||
}
|
||||
while cx16.r0L==0 {
|
||||
cx16.r0++
|
||||
}
|
||||
while cx16.r0L!=0 {
|
||||
cx16.r0++
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
sub efficient_assign_cmp_0() {
|
||||
ubyte @shared ub
|
||||
uword @shared uw
|
||||
float @shared fl
|
||||
bool @shared bb1, bb2
|
||||
|
||||
bb1 = ub==0
|
||||
bb2 = ub!=0
|
||||
bb1 = uw==0
|
||||
bb2 = uw!=0
|
||||
bb1 = fl==0.0
|
||||
bb2 = fl!=0.0
|
||||
}
|
||||
|
||||
|
||||
sub efficient_assign_cmp_99() {
|
||||
ubyte @shared ub
|
||||
uword @shared uw
|
||||
float @shared fl
|
||||
bool @shared bb1, bb2
|
||||
|
||||
bb1 = ub==99
|
||||
bb2 = ub!=99
|
||||
bb1 = uw==99
|
||||
bb2 = uw!=99
|
||||
bb1 = fl==99.0
|
||||
bb2 = fl!=99.0
|
||||
}
|
||||
|
||||
sub efficient_compare_0() {
|
||||
ubyte @shared ub
|
||||
uword @shared uw
|
||||
float @shared fl
|
||||
|
||||
if ub==0
|
||||
cx16.r0++
|
||||
if uw==0
|
||||
cx16.r0++
|
||||
if fl==0
|
||||
cx16.r0++
|
||||
if ub!=0
|
||||
cx16.r0++
|
||||
else
|
||||
cx16.r1++
|
||||
if uw!=0
|
||||
cx16.r0++
|
||||
else
|
||||
cx16.r1++
|
||||
if fl!=0
|
||||
cx16.r0++
|
||||
else
|
||||
cx16.r1++
|
||||
}
|
||||
|
||||
sub efficient_compare_99() {
|
||||
ubyte @shared ub
|
||||
uword @shared uw
|
||||
float @shared fl
|
||||
|
||||
if ub==99
|
||||
cx16.r0++
|
||||
if uw==99
|
||||
cx16.r0++
|
||||
if fl==99.99
|
||||
cx16.r0++
|
||||
if ub!=99
|
||||
cx16.r0++
|
||||
else
|
||||
cx16.r1++
|
||||
if uw!=99
|
||||
cx16.r0++
|
||||
else
|
||||
cx16.r1++
|
||||
if fl!=99.99
|
||||
cx16.r0++
|
||||
else
|
||||
cx16.r1++
|
||||
}
|
||||
|
||||
sub logical_operand_swap() {
|
||||
bool[] ba = [true, false, true]
|
||||
bool @shared bb = staticbool1 xor ba[0]
|
||||
ubyte @shared zz
|
||||
cx16.r0L = 99+zz
|
||||
}
|
||||
|
||||
sub boolean_const_and_var(bool barg) {
|
||||
const bool bconst1 = true
|
||||
const bool bconst2 = false
|
||||
@ -212,6 +322,20 @@ main {
|
||||
ba[2] = ba[0] and ba[1]
|
||||
ba[1] = ba[0] or ba[2]
|
||||
ba[1] = not ba[2]
|
||||
ba[1] = ba[0] xor ba[cx16.r0L]
|
||||
ba[2] = ba[0] and ba[cx16.r0L]
|
||||
ba[1] = ba[0] or ba[cx16.r0L]
|
||||
ba[1] = not ba[cx16.r0L]
|
||||
|
||||
ubyte[] uba = [11,22,33]
|
||||
uba[1] = uba[0] ^ uba[2]
|
||||
uba[2] = uba[0] & uba[1]
|
||||
uba[1] = uba[0] | uba[2]
|
||||
uba[1] = ~uba[2]
|
||||
uba[1] = uba[0] ^ uba[cx16.r0L]
|
||||
uba[2] = uba[0] & uba[cx16.r0L]
|
||||
uba[1] = uba[0] | uba[cx16.r0L]
|
||||
uba[1] = ~uba[cx16.r0L]
|
||||
}
|
||||
|
||||
sub bools_in_array_assigns_inplace() {
|
||||
@ -228,6 +352,55 @@ main {
|
||||
ba[1] = ba[1] and ba[2]
|
||||
ba[2] = ba[2] or ba[1]
|
||||
ba[2] = not ba[2]
|
||||
ba[2] = ba[2] xor ba[cx16.r0L]
|
||||
ba[1] = ba[1] and ba[cx16.r0L]
|
||||
ba[2] = ba[2] or ba[cx16.r0L]
|
||||
|
||||
ubyte[] uba = [11,22,33]
|
||||
uba[2] = uba[2] ^ uba[1]
|
||||
uba[1] = uba[1] & uba[2]
|
||||
uba[2] = uba[2] | uba[1]
|
||||
uba[2] = ~ uba[2]
|
||||
uba[2] = uba[2] ^ uba[cx16.r0L]
|
||||
uba[1] = uba[1] & uba[cx16.r0L]
|
||||
uba[2] = uba[2] | uba[cx16.r0L]
|
||||
}
|
||||
|
||||
sub if_gotos() {
|
||||
ubyte @shared ub
|
||||
|
||||
if_cc
|
||||
goto label
|
||||
if_cc
|
||||
goto label
|
||||
else
|
||||
cx16.r0++
|
||||
if_cs
|
||||
goto label
|
||||
else
|
||||
cx16.r0++
|
||||
|
||||
|
||||
if ub==0
|
||||
goto label
|
||||
if ub!=0
|
||||
goto label
|
||||
if not ub==99
|
||||
goto label
|
||||
|
||||
if ub==0
|
||||
goto label
|
||||
else
|
||||
cx16.r0++
|
||||
if ub!=0
|
||||
goto label
|
||||
else
|
||||
cx16.r0++
|
||||
if not ub==98
|
||||
goto label
|
||||
else
|
||||
cx16.r0++
|
||||
label:
|
||||
}
|
||||
|
||||
sub if_code() {
|
||||
|
Loading…
Reference in New Issue
Block a user