work on longs

This commit is contained in:
Irmen de Jong
2025-09-19 06:41:16 +02:00
parent a2b9d78cf3
commit d66dc664de
17 changed files with 483 additions and 64 deletions

View File

@@ -21,7 +21,7 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
// we consider them NOT to be optimized into (possibly different) CPU registers.
// Just load them in whatever the register spec says.
return when (params.size) {
1 -> params[0].register == null && (params[0].type.isIntegerOrBool || params[0].type.isPointer)
1 -> params[0].register == null && (params[0].type.isWordOrByteOrBool || params[0].type.isPointer)
2 -> params[0].type.isByteOrBool && params[1].type.isByteOrBool && params[0].register == null && params[1].register == null
else -> false
}

View File

@@ -46,6 +46,7 @@ internal class IfElseAsmGen(private val program: PtProgram,
return when {
rightDt.isByteOrBool -> translateIfByte(stmt, jumpAfterIf)
rightDt.isWord || rightDt.isPointer -> translateIfWord(stmt, compareCond, jumpAfterIf)
rightDt.isLong -> translateIfLong(stmt, compareCond, jumpAfterIf)
rightDt.isFloat -> translateIfFloat(stmt, compareCond, jumpAfterIf)
else -> throw AssemblyError("weird dt")
}
@@ -575,6 +576,32 @@ internal class IfElseAsmGen(private val program: PtProgram,
}
}
private fun translateIfLong(stmt: PtIfElse, condition: PtBinaryExpression, jumpAfterIf: PtJump?) {
val constValue = condition.right.asConstInteger()
if(constValue==0) {
// optimized comparisons with zero
return when (condition.operator) {
"==" -> longEqualsZero(condition.left, false, jumpAfterIf, stmt)
"!=" -> longEqualsZero(condition.left, true, jumpAfterIf, stmt)
"<" -> longLessZero(condition.left, false, jumpAfterIf, stmt)
"<=" -> longLessZero(condition.left, true, jumpAfterIf, stmt)
">" -> longGreaterZero(condition.left, false, jumpAfterIf, stmt)
">=" -> longGreaterZero(condition.left, true, jumpAfterIf, stmt)
else -> throw AssemblyError("expected comparison operator")
}
}
return when (condition.operator) {
"==" -> longEqualsValue(condition.left, condition.right, false, jumpAfterIf, stmt)
"!=" -> longEqualsValue(condition.left, condition.right, true, jumpAfterIf, stmt)
"<" -> TODO("long < 0")
"<=" -> TODO("long <= 0")
">" -> TODO("long > 0")
">=" -> TODO("long >= 0")
else -> throw AssemblyError("expected comparison operator")
}
}
private fun translateIfWord(stmt: PtIfElse, condition: PtBinaryExpression, jumpAfterIf: PtJump?) {
val signed = condition.left.type.isSigned
val constValue = condition.right.asConstInteger()
@@ -606,7 +633,7 @@ internal class IfElseAsmGen(private val program: PtProgram,
private fun wordGreaterEqualsZero(value: PtExpression, signed: Boolean, jump: PtJump?, stmt: PtIfElse) {
// special case for word>=0
if(signed) {
loadAndCmp0MSB(value)
loadAndCmp0MSB(value, false)
if (jump != null)
translateJumpElseBodies("bpl", "bmi", jump, stmt.elseScope)
else
@@ -619,7 +646,7 @@ internal class IfElseAsmGen(private val program: PtProgram,
private fun wordLessZero(value: PtExpression, signed: Boolean, jump: PtJump?, stmt: PtIfElse) {
// special case for word<0
if(signed) {
loadAndCmp0MSB(value)
loadAndCmp0MSB(value, false)
if (jump != null)
translateJumpElseBodies("bmi", "bpl", jump, stmt.elseScope)
else
@@ -894,23 +921,31 @@ _jump jmp (${target.asmLabel})
}
private fun loadAndCmp0MSB(value: PtExpression) {
private fun loadAndCmp0MSB(value: PtExpression, long: Boolean) {
when(value) {
is PtArrayIndexer -> {
if(value.variable==null)
TODO("support for ptr indexing ${value.position}")
val varname = asmgen.asmVariableName(value.variable!!)
asmgen.loadScaledArrayIndexIntoRegister(value, CpuRegister.Y)
if(value.splitWords)
if(value.splitWords) {
require(!long)
asmgen.out(" lda ${varname}_msb,y")
}
else if(long)
asmgen.out(" lda $varname+3,y")
else
asmgen.out(" lda $varname+1,y")
}
is PtIdentifier -> {
val varname = asmgen.asmVariableName(value)
asmgen.out(" lda $varname+1")
if(long)
asmgen.out(" lda $varname+3")
else
asmgen.out(" lda $varname+1")
}
is PtAddressOf -> {
require(!long)
if(value.isFromArrayElement) {
asmgen.assignExpressionToRegister(value, RegisterOrPair.AY, true)
asmgen.out(" cpy #0")
@@ -923,6 +958,7 @@ _jump jmp (${target.asmLabel})
}
}
else -> {
require(!long)
asmgen.assignExpressionToRegister(value, RegisterOrPair.AY, true)
asmgen.out(" cpy #0")
}
@@ -1226,6 +1262,34 @@ _jump jmp (${target.asmLabel})
}
}
private fun longEqualsZero(value: PtExpression, notEquals: Boolean, jump: PtJump?, stmt: PtIfElse) {
TODO("long == 0")
}
private fun longLessZero(value: PtExpression, lessEquals: Boolean, jump: PtJump?, stmt: PtIfElse) {
if(lessEquals) {
TODO("long <= 0")
} else {
loadAndCmp0MSB(value, true)
if (jump != null)
translateJumpElseBodies("bmi", "bpl", jump, stmt.elseScope)
else
translateIfElseBodies("bpl", stmt)
}
}
private fun longGreaterZero(value: PtExpression, lessEquals: Boolean, jump: PtJump?, stmt: PtIfElse) {
if(lessEquals) {
TODO("long >= 0")
} else {
loadAndCmp0MSB(value, true)
if (jump != null)
translateJumpElseBodies("bpl", "bmi", jump, stmt.elseScope)
else
translateIfElseBodies("bmi", stmt)
}
}
private fun wordEqualsZero(value: PtExpression, notEquals: Boolean, signed: Boolean, jump: PtJump?, stmt: PtIfElse) {
// special case for (u)word == 0
@@ -1303,6 +1367,16 @@ _jump jmp (${target.asmLabel})
}
}
private fun longEqualsValue(
left: PtExpression,
right: PtExpression,
notEquals: Boolean,
jump: PtJump?,
stmt: PtIfElse
) {
TODO("long == value")
}
private fun wordEqualsValue(
left: PtExpression,
right: PtExpression,

View File

@@ -377,6 +377,7 @@ internal class ProgramAndVarsGen(
dt.isSignedByte -> ".char"
dt.isUnsignedWord || dt.isPointer -> ".word"
dt.isSignedWord -> ".sint"
dt.isLong -> ".dint"
dt.isFloat -> ".byte"
else -> {
throw AssemblyError("weird dt")
@@ -418,14 +419,7 @@ internal class ProgramAndVarsGen(
structtype.fields.withIndex().forEach { (index, field) ->
val dt = field.first
val varname = "f${index}"
val type = when {
dt.isBool || dt.isUnsignedByte -> ".byte"
dt.isSignedByte -> ".char"
dt.isUnsignedWord || dt.isPointer -> ".word"
dt.isSignedWord -> ".sint"
dt.isFloat -> ".byte" // TODO check that float bytes are passed as an array parameter
else -> throw AssemblyError("weird dt")
}
val type = asmTypeString(dt)
asmgen.out("p8v_${field.second} $type \\$varname") // note: struct field symbol prefixing done here because that is a lot simpler than fixing up all expressions in the AST
}
asmgen.out(" .endstruct\n")
@@ -781,6 +775,7 @@ internal class ProgramAndVarsGen(
dt.isSignedByte -> asmgen.out("${variable.name}\t.char ?")
dt.isUnsignedWord -> asmgen.out("${variable.name}\t.word ?")
dt.isSignedWord -> asmgen.out("${variable.name}\t.sint ?")
dt.isLong -> asmgen.out("${variable.name}\t.dint ?")
dt.isFloat -> asmgen.out("${variable.name}\t.fill ${compTarget.FLOAT_MEM_SIZE}")
dt.isSplitWordArray -> {
alignVar(variable.align)
@@ -829,6 +824,7 @@ internal class ProgramAndVarsGen(
// dt.isSignedByte -> asmgen.out("${variable.name}\t.char $initialValue")
// dt.isUnsignedWord -> asmgen.out("${variable.name}\t.word ${initialValue.toHex()}")
// dt.isSignedWord -> asmgen.out("${variable.name}\t.sint $initialValue")
// dt.isLong -> asmgen.out("${variable.name}\t.dint $initialValue")
// dt.isFloat -> {
// if(initialValue==0) {
// asmgen.out("${variable.name}\t.byte 0,0,0,0,0 ; float")

View File

@@ -222,6 +222,7 @@ internal class AssignmentAsmGen(
BaseDataType.BOOL -> assignConstantByte(assign.target, if(num==0.0) 0 else 1)
BaseDataType.UBYTE, BaseDataType.BYTE -> assignConstantByte(assign.target, num.toInt())
BaseDataType.UWORD, BaseDataType.WORD -> assignConstantWord(assign.target, num.toInt())
BaseDataType.LONG -> assignConstantLong(assign.target, num.toInt())
BaseDataType.FLOAT -> assignConstantFloat(assign.target, num)
BaseDataType.POINTER -> assignConstantWord(assign.target, num.toInt())
else -> throw AssemblyError("weird numval type")
@@ -244,6 +245,7 @@ internal class AssignmentAsmGen(
else
assignVariableWord(assign.target, variable, assign.source.datatype)
}
targetDt.isLong -> assignVariableLong(assign.target, variable, assign.source.datatype)
targetDt.isFloat -> assignVariableFloat(assign.target, variable)
targetDt.isString -> assignVariableString(assign.target, variable)
targetDt.isPointer -> assignVariableWord(assign.target, variable, assign.source.datatype)
@@ -536,7 +538,7 @@ internal class AssignmentAsmGen(
private fun assignPrefixExpr(assign: AsmAssignment, value: PtPrefix, scope: IPtSubroutine?) {
if(assign.target.array==null) {
if(assign.source.datatype isAssignableTo assign.target.datatype || (assign.source.datatype.isBool && assign.target.datatype.isByte)) {
if(assign.source.datatype.isIntegerOrBool) {
if(assign.source.datatype.isWordOrByteOrBool) {
val signed = assign.source.datatype.isSigned
if(assign.source.datatype.isByteOrBool) {
assignExpressionToRegister(value.value, RegisterOrPair.A, signed)
@@ -2417,8 +2419,8 @@ $endLabel""")
}
}
if(targetDt.isInteger && valueDt.isIntegerOrBool && valueDt.isAssignableTo(targetDt)) {
require(targetDt.isWord && valueDt.isByteOrBool) {
if(targetDt.isInteger && valueDt.isByteOrBool && valueDt.isAssignableTo(targetDt)) {
require(targetDt.isWord) {
"should be byte to word assignment ${origTypeCastExpression.position}"
}
when(target.kind) {
@@ -3003,6 +3005,28 @@ $endLabel""")
}
}
private fun assignVariableLong(target: AsmAssignTarget, varName: String, sourceDt: DataType) {
require(sourceDt.isLong)
when(target.kind) {
TargetStorageKind.VARIABLE -> {
asmgen.out("""
lda $varName
sta ${target.asmVarname}
lda $varName+1
sta ${target.asmVarname}+1
lda $varName+2
sta ${target.asmVarname}+2
lda $varName+3
sta ${target.asmVarname}+3""")
}
TargetStorageKind.ARRAY -> TODO("assign long to array ${target.position}")
TargetStorageKind.MEMORY -> throw AssemblyError("memory is bytes not long ${target.position}")
TargetStorageKind.REGISTER -> TODO("32 bits register assign? (we have no 32 bits registers right now) ${target.position}")
TargetStorageKind.POINTER -> throw AssemblyError("can't assign long to pointer, pointers are 16 bits ${target.position}")
TargetStorageKind.VOID -> { /* do nothing */ }
}
}
private fun assignVariableWord(target: AsmAssignTarget, varName: String, sourceDt: DataType) {
if(sourceDt.isSignedByte) {
// need to sign extend
@@ -3929,6 +3953,49 @@ $endLabel""")
}
}
private fun assignConstantLong(target: AsmAssignTarget, long: Int) {
if(long==0 && asmgen.isTargetCpu(CpuType.CPU65C02)) {
// optimize setting zero value for this processor
when(target.kind) {
TargetStorageKind.VARIABLE -> {
asmgen.out("""
stz ${target.asmVarname}
stz ${target.asmVarname}+1
stz ${target.asmVarname}+2
stz ${target.asmVarname}+3""")
}
TargetStorageKind.ARRAY -> TODO("assign long zero to array ${target.position}")
TargetStorageKind.MEMORY -> throw AssemblyError("memory is bytes not long ${target.position}")
TargetStorageKind.REGISTER -> TODO("32 bits register assign? (we have no 32 bits registers right now) ${target.position}")
TargetStorageKind.POINTER -> throw AssemblyError("can't assign long to pointer, pointers are 16 bits ${target.position}")
TargetStorageKind.VOID -> { /* do nothing */ }
}
return
}
when(target.kind) {
TargetStorageKind.VARIABLE -> {
fun store(hexbyte: String, offset: Int) {
if(asmgen.isTargetCpu(CpuType.CPU65C02) && hexbyte=="00") {
asmgen.out(" stz ${target.asmVarname}+$offset")
} else {
asmgen.out(" lda #$$hexbyte | sta ${target.asmVarname}+$offset")
}
}
val hex = long.toUInt().toString(16).padStart(8, '0')
store(hex.substring(6,8), 0)
store(hex.substring(4,6), 1)
store(hex.substring(2,4), 2)
store(hex.substring(0,2), 3)
}
TargetStorageKind.ARRAY -> TODO("assign long $long to array ${target.position}")
TargetStorageKind.MEMORY -> throw AssemblyError("memory is bytes not long ${target.position}")
TargetStorageKind.REGISTER -> TODO("32 bits register assign? (we have no 32 bits registers right now) ${target.position}")
TargetStorageKind.POINTER -> throw AssemblyError("can't assign long to pointer, pointers are 16 bits ${target.position}")
TargetStorageKind.VOID -> { /* do nothing */ }
}
}
private fun assignConstantWord(target: AsmAssignTarget, word: Int) {
if(word==0 && asmgen.isTargetCpu(CpuType.CPU65C02)) {
// optimize setting zero value for this processor
@@ -4627,6 +4694,31 @@ $endLabel""")
TargetStorageKind.VOID -> { /* do nothing */ }
}
}
datatype.isLong -> {
when(target.kind) {
TargetStorageKind.VARIABLE -> {
asmgen.out("""
lda #0
sec
sbc ${target.asmVarname}
sta ${target.asmVarname}
lda #0
sbc ${target.asmVarname}+1
sta ${target.asmVarname}+1
lda #0
sbc ${target.asmVarname}+2
sta ${target.asmVarname}+2
lda #0
sbc ${target.asmVarname}+3
sta ${target.asmVarname}+3""")
}
TargetStorageKind.ARRAY -> TODO(" - long array ${target.position}")
TargetStorageKind.MEMORY -> throw AssemblyError("memory is bytes not long ${target.position}")
TargetStorageKind.REGISTER -> TODO("32 bits register assign? (we have no 32 bits registers right now) ${target.position}")
TargetStorageKind.POINTER -> throw AssemblyError("can't assign long to pointer, pointers are 16 bits ${target.position}")
TargetStorageKind.VOID -> { /* do nothing */ }
}
}
datatype.isFloat -> {
when (target.kind) {
TargetStorageKind.REGISTER -> {

View File

@@ -106,6 +106,17 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
}
}
}
target.datatype.isLong -> {
when(value.kind) {
SourceStorageKind.LITERALBOOLEAN -> inplacemodificationLongWithLiteralval(target.asmVarname, operator, value.boolean!!.asInt())
SourceStorageKind.LITERALNUMBER -> inplacemodificationLongWithLiteralval(target.asmVarname, operator, value.number!!.number.toInt())
SourceStorageKind.VARIABLE -> inplacemodificationLongWithVariable(target.asmVarname, operator, value.asmVarname)
SourceStorageKind.EXPRESSION -> TODO("inplace modify long with expression ${target.position}")
SourceStorageKind.REGISTER -> TODO("32 bits register inplace modification? ${target.position}")
SourceStorageKind.ARRAY -> TODO("inplace modify long with array ${target.position}")
SourceStorageKind.MEMORY -> TODO("memread into long ${target.position}")
}
}
target.datatype.isFloat -> {
when(value.kind) {
SourceStorageKind.LITERALBOOLEAN -> inplacemodificationFloatWithLiteralval(target.asmVarname, operator, value.boolean!!.asInt().toDouble())
@@ -523,6 +534,132 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
}
}
private fun inplacemodificationLongWithVariable(targetVar: String, operator: String, sourceVar: String) {
when(operator) {
"+" -> {
asmgen.out("""
clc
lda $targetVar
adc $sourceVar
sta $targetVar
lda $targetVar+1
adc $sourceVar+1
sta $targetVar+1
lda $targetVar+2
adc $sourceVar+2
sta $targetVar+2
lda $targetVar+3
adc $sourceVar+3
sta $targetVar+3""")
}
"-" -> {
asmgen.out("""
sec
lda $targetVar
sbc $sourceVar
sta $targetVar
lda $targetVar+1
sbc $sourceVar+1
sta $targetVar+1
lda $targetVar+2
sbc $sourceVar+2
sta $targetVar+2
lda $targetVar+3
sbc $sourceVar+3
sta $targetVar+3""")
}
else -> {
TODO("Not yet implemented")
}
}
}
private fun inplacemodificationLongWithLiteralval(variable: String, operator: String, value: Int) {
when(operator) {
"+" -> {
when(value) {
0 -> {}
1 -> {
asmgen.out("""
inc $variable
bne +
inc $variable+1
bne +
inc $variable+2
bne +
inc $variable+3
+""")
}
else -> {
if(value in 1..255) {
TODO("optimized inplace long += $value")
} else if(value in 1..65535) {
TODO("optimized inplace long += $value")
} else {
val hex = value.toUInt().toString(16).padStart(8, '0')
asmgen.out("""
clc
lda $variable
adc #$${hex.substring(6,8)}
sta $variable
lda $variable+1
adc #$${hex.substring(4, 6)}
sta $variable+1
lda $variable+2
adc #$${hex.substring(2, 4)}
sta $variable+2
lda $variable+3
adc #$${hex.take(2)}
sta $variable+3""")
}
}
}
}
"-" -> {
when(value) {
0 -> {}
1 -> {
asmgen.out("""
lda $variable
bne +
dec $variable+1
bne +
dec $variable+2
bne +
dec $variable+3
+ dec $variable""")
}
else -> {
if(value in 1..255) {
TODO("optimized inplace long += $value")
} else if(value in 1..65535) {
TODO("optimized inplace long += $value")
} else {
val hex = value.toUInt().toString(16).padStart(8, '0')
asmgen.out("""
sec
lda $variable
sbc #$${hex.substring(6,8)}
sta $variable
lda $variable+1
sbc #$${hex.substring(4, 6)}
sta $variable+1
lda $variable+2
sbc #$${hex.substring(2, 4)}
sta $variable+2
lda $variable+3
sbc #$${hex.take(2)}
sta $variable+3""")
}
}
}
}
else -> {
TODO("inplace long $operator $value")
}
}
}
private fun tryIndexedIncDec(array: PtArrayIndexer, operator: String): Boolean {
val arrayVar = array.variable
if(arrayVar==null) {

View File

@@ -12,12 +12,14 @@ internal object DummyMemsizer : IMemSizer {
return when(dt.sub) {
BaseDataType.BOOL, BaseDataType.BYTE, BaseDataType.UBYTE -> numElements
BaseDataType.UWORD, BaseDataType.WORD -> numElements*2
BaseDataType.LONG -> numElements*4
BaseDataType.FLOAT -> numElements*5
else -> throw IllegalArgumentException("invalid sub type")
}
}
return when {
dt.isByteOrBool -> 1 * (numElements ?: 1)
dt.isLong -> 4 * (numElements ?: 1)
dt.isFloat -> 5 * (numElements ?: 1)
else -> 2 * (numElements ?: 1)
}