implement long << >> expressions

This commit is contained in:
Irmen de Jong
2025-09-27 16:35:46 +02:00
parent b3bd2a6a09
commit e0107bacbd
10 changed files with 184 additions and 48 deletions

View File

@@ -400,7 +400,9 @@ enum class RegisterOrPair {
FAC2, FAC2,
// cx16 virtual registers: // cx16 virtual registers:
R0, R1, R2, R3, R4, R5, R6, R7, R0, R1, R2, R3, R4, R5, R6, R7,
R8, R9, R10, R11, R12, R13, R14, R15; R8, R9, R10, R11, R12, R13, R14, R15,
// combined virtual registers to store 32 bits longs:
R0R1_32, R2R3_32, R4R5_32, R6R7_32, R8R9_32, R10R11_32, R12R13_32, R14R15_32;
companion object { companion object {
val names by lazy { entries.map { it.toString()} } val names by lazy { entries.map { it.toString()} }
@@ -427,12 +429,13 @@ enum class RegisterOrPair {
BaseDataType.BYTE -> "sL" BaseDataType.BYTE -> "sL"
BaseDataType.WORD -> "s" BaseDataType.WORD -> "s"
BaseDataType.UWORD, null -> "" BaseDataType.UWORD, null -> ""
else -> throw IllegalArgumentException("invalid register param type") else -> throw IllegalArgumentException("invalid register param type for cx16 virtual reg")
} }
return listOf("cx16", name.lowercase()+suffix) return listOf("cx16", name.lowercase()+suffix)
} }
fun isWord() = this==AX || this == AY || this==XY || this in Cx16VirtualRegisters fun isWord() = this==AX || this == AY || this==XY || this in Cx16VirtualRegisters
fun isLong() = this in combinedLongRegisters
} // only used in parameter and return value specs in asm subroutines } // only used in parameter and return value specs in asm subroutines
@@ -469,6 +472,17 @@ val Cx16VirtualRegisters = arrayOf(
RegisterOrPair.R12, RegisterOrPair.R13, RegisterOrPair.R14, RegisterOrPair.R15 RegisterOrPair.R12, RegisterOrPair.R13, RegisterOrPair.R14, RegisterOrPair.R15
) )
val combinedLongRegisters = arrayOf(
RegisterOrPair.R0R1_32,
RegisterOrPair.R2R3_32,
RegisterOrPair.R4R5_32,
RegisterOrPair.R6R7_32,
RegisterOrPair.R8R9_32,
RegisterOrPair.R10R11_32,
RegisterOrPair.R12R13_32,
RegisterOrPair.R14R15_32
)
val CpuRegisters = arrayOf( val CpuRegisters = arrayOf(
RegisterOrPair.A, RegisterOrPair.X, RegisterOrPair.Y, RegisterOrPair.A, RegisterOrPair.X, RegisterOrPair.Y,
RegisterOrPair.AX, RegisterOrPair.AY, RegisterOrPair.XY RegisterOrPair.AX, RegisterOrPair.AY, RegisterOrPair.XY

View File

@@ -914,8 +914,7 @@ class AsmGen6502Internal (
when (target.kind) { when (target.kind) {
TargetStorageKind.VARIABLE -> { TargetStorageKind.VARIABLE -> {
out(" lda $valuesym | sta ${target.asmVarname}") out(" lda $valuesym | sta ${target.asmVarname}")
signExtendVariableLsb(target.asmVarname, value.value.type.base) signExtendLongVariable(target.asmVarname, value.value.type.base)
signExtendLongVariableMsw(target.asmVarname, value.value.type.base)
} }
TargetStorageKind.ARRAY -> TODO("assign long to array ${target.position}") TargetStorageKind.ARRAY -> TODO("assign long to array ${target.position}")
TargetStorageKind.MEMORY -> throw AssemblyError("memory is bytes not long ${target.position}") TargetStorageKind.MEMORY -> throw AssemblyError("memory is bytes not long ${target.position}")
@@ -931,11 +930,20 @@ class AsmGen6502Internal (
sta ${target.asmVarname} sta ${target.asmVarname}
lda $valuesym+1 lda $valuesym+1
sta ${target.asmVarname}+1""") sta ${target.asmVarname}+1""")
signExtendLongVariableMsw(target.asmVarname, value.value.type.base) signExtendLongVariable(target.asmVarname, value.value.type.base)
} }
TargetStorageKind.ARRAY -> TODO("assign long to array ${target.position}") TargetStorageKind.ARRAY -> TODO("assign long to array ${target.position}")
TargetStorageKind.MEMORY -> throw AssemblyError("memory is bytes not long ${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.REGISTER -> {
require(target.register in combinedLongRegisters)
val startreg = target.register.toString().take(2).lowercase()
out("""
lda $valuesym
sta cx16.$startreg
lda $valuesym+1
sta cx16.$startreg+1""")
signExtendLongVariable("cx16.$startreg", value.value.type.base)
}
TargetStorageKind.POINTER -> throw AssemblyError("can't assign long to pointer, pointers are 16 bits ${target.position}") TargetStorageKind.POINTER -> throw AssemblyError("can't assign long to pointer, pointers are 16 bits ${target.position}")
TargetStorageKind.VOID -> { /* do nothing */ } TargetStorageKind.VOID -> { /* do nothing */ }
} }
@@ -1384,7 +1392,7 @@ $repeatLabel""")
} }
} }
internal fun signExtendLongVariableMsw(asmvar: String, valueDt: BaseDataType) { internal fun signExtendLongVariable(asmvar: String, valueDt: BaseDataType) {
// sign extend signed word in a var to a full long in that variable // sign extend signed word in a var to a full long in that variable
when(valueDt) { when(valueDt) {
BaseDataType.UBYTE -> { BaseDataType.UBYTE -> {
@@ -1421,7 +1429,7 @@ $repeatLabel""")
+ sta $asmvar+2 + sta $asmvar+2
sta $asmvar+3""") sta $asmvar+3""")
} }
else -> throw AssemblyError("need byte type") else -> throw AssemblyError("need byte or word type")
} }
} }

View File

@@ -133,6 +133,18 @@ internal class AsmAssignTarget(val kind: TargetStorageKind,
val dt = if(signed) DataType.WORD else DataType.UWORD val dt = if(signed) DataType.WORD else DataType.UWORD
AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, dt, scope, pos, register = registers) AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, dt, scope, pos, register = registers)
} }
RegisterOrPair.R0R1_32,
RegisterOrPair.R2R3_32,
RegisterOrPair.R4R5_32,
RegisterOrPair.R6R7_32,
RegisterOrPair.R8R9_32,
RegisterOrPair.R10R11_32,
RegisterOrPair.R12R13_32,
RegisterOrPair.R14R15_32 -> {
val dt = if(signed) DataType.LONG else TODO("unsigned long")
AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, dt, scope, pos, register = registers)
}
} }
} }

View File

@@ -656,7 +656,7 @@ internal class AssignmentAsmGen(
} }
returnDt== BaseDataType.LONG -> { returnDt== BaseDataType.LONG -> {
// longs are in R0:R1 (r0=lsw, r1=msw) // longs are in R0:R1 (r0=lsw, r1=msw)
assignRegisterLong(target, RegisterOrPair.R0, RegisterOrPair.R1) assignRegisterLong(target, RegisterOrPair.R0R1_32)
} }
returnDt==BaseDataType.FLOAT -> { returnDt==BaseDataType.FLOAT -> {
// float result from function sits in FAC1 // float result from function sits in FAC1
@@ -1244,7 +1244,7 @@ internal class AssignmentAsmGen(
asmgen.out(" jsr prog8_math.asl_byte_A") asmgen.out(" jsr prog8_math.asl_byte_A")
assignRegisterByte(target, CpuRegister.A, signed, true) assignRegisterByte(target, CpuRegister.A, signed, true)
return true return true
} else { } else if(dt.isWord) {
assignExpressionToRegister(expr.left, RegisterOrPair.AY, signed) assignExpressionToRegister(expr.left, RegisterOrPair.AY, signed)
asmgen.restoreRegisterStack(CpuRegister.X, true) asmgen.restoreRegisterStack(CpuRegister.X, true)
if(expr.operator==">>") if(expr.operator==">>")
@@ -1256,6 +1256,12 @@ internal class AssignmentAsmGen(
asmgen.out(" jsr prog8_math.asl_word_AY") asmgen.out(" jsr prog8_math.asl_word_AY")
assignRegisterpairWord(target, RegisterOrPair.AY) assignRegisterpairWord(target, RegisterOrPair.AY)
return true return true
} else if(dt.isLong) {
assignExpressionToRegister(expr.left, RegisterOrPair.R0R1_32, signed)
asmgen.out(" pla | sta P8ZP_SCRATCH_REG")
augmentableAsmGen.inplacemodificationLongWithVariable("cx16.r0", expr.operator, "P8ZP_SCRATCH_REG")
assignRegisterLong(target, RegisterOrPair.R0R1_32)
return true
} }
} }
else { else {
@@ -1375,6 +1381,11 @@ internal class AssignmentAsmGen(
return true return true
} }
} }
} else if(dt.isLong) {
assignExpressionToRegister(expr.left, RegisterOrPair.R0R1_32, signed)
augmentableAsmGen.inplacemodificationLongWithLiteralval("cx16.r0", expr.operator, shifts)
assignRegisterLong(target, RegisterOrPair.R0R1_32)
return true
} }
} }
return false return false
@@ -1787,7 +1798,11 @@ internal class AssignmentAsmGen(
} }
} }
else -> { else -> {
TODO("add/subtract long ${target.position} - use simple expressions and temporary variables for now") assignExpressionToRegister(expr.left, RegisterOrPair.R2R3_32, expr.left.type.isSigned)
assignExpressionToRegister(expr.right, RegisterOrPair.R0R1_32, expr.right.type.isSigned)
augmentableAsmGen.inplacemodificationLongWithVariable("cx16.r2", expr.operator, "cx16.r0")
assignRegisterLong(target, RegisterOrPair.R2R3_32)
return true
} }
} }
} }
@@ -2519,8 +2534,8 @@ $endLabel""")
} }
if(targetDt.isInteger && valueDt.isByteOrBool && valueDt.isAssignableTo(targetDt)) { if(targetDt.isInteger && valueDt.isByteOrBool && valueDt.isAssignableTo(targetDt)) {
require(targetDt.isWord) { require(targetDt.isWord || targetDt.isLong) {
"should be byte to word assignment ${origTypeCastExpression.position}" "should be byte to word or long assignment ${origTypeCastExpression.position}"
} }
when(target.kind) { when(target.kind) {
// TargetStorageKind.VARIABLE -> { // TargetStorageKind.VARIABLE -> {
@@ -2541,7 +2556,7 @@ $endLabel""")
return return
} }
TargetStorageKind.REGISTER -> { TargetStorageKind.REGISTER -> {
// byte to word, just assign to registers // byte to word or long, just assign to registers
assignExpressionToRegister(value, target.register!!, targetDt.isSigned) assignExpressionToRegister(value, target.register!!, targetDt.isSigned)
return return
} }
@@ -2686,7 +2701,7 @@ $endLabel""")
BaseDataType.LONG -> { BaseDataType.LONG -> {
asmgen.out(" lda $sourceAsmVarName | sta $targetAsmVarName") asmgen.out(" lda $sourceAsmVarName | sta $targetAsmVarName")
asmgen.signExtendVariableLsb(targetAsmVarName, BaseDataType.BYTE) asmgen.signExtendVariableLsb(targetAsmVarName, BaseDataType.BYTE)
asmgen.signExtendLongVariableMsw(targetAsmVarName, BaseDataType.WORD) asmgen.signExtendLongVariable(targetAsmVarName, BaseDataType.WORD)
} }
BaseDataType.FLOAT -> { BaseDataType.FLOAT -> {
asmgen.out(""" asmgen.out("""
@@ -2752,7 +2767,7 @@ $endLabel""")
sta $targetAsmVarName sta $targetAsmVarName
lda $sourceAsmVarName+1 lda $sourceAsmVarName+1
sta $targetAsmVarName+1""") sta $targetAsmVarName+1""")
asmgen.signExtendLongVariableMsw(targetAsmVarName, BaseDataType.WORD) asmgen.signExtendLongVariable(targetAsmVarName, BaseDataType.WORD)
} }
BaseDataType.FLOAT -> { BaseDataType.FLOAT -> {
asmgen.out(""" asmgen.out("""
@@ -3124,22 +3139,89 @@ $endLabel""")
} }
private fun assignVariableLong(target: AsmAssignTarget, varName: String, sourceDt: DataType) { private fun assignVariableLong(target: AsmAssignTarget, varName: String, sourceDt: DataType) {
require(sourceDt.isLong) require(sourceDt.isByte || sourceDt.isWord || sourceDt.isLong) {
"need byte/word/long as source value to assign to long variable $varName ${target.position}"
}
when(target.kind) { when(target.kind) {
TargetStorageKind.VARIABLE -> { TargetStorageKind.VARIABLE -> {
asmgen.out(""" when(sourceDt) {
lda $varName DataType.BYTE -> {
sta ${target.asmVarname} TODO("signed byte to long var")
lda $varName+1 }
sta ${target.asmVarname}+1 DataType.UBYTE -> {
lda $varName+2 TODO("ubyte to long var")
sta ${target.asmVarname}+2 }
lda $varName+3 DataType.WORD -> {
sta ${target.asmVarname}+3""") TODO("signed word to long var")
}
DataType.UWORD -> {
TODO("ubyte to long var")
}
DataType.LONG -> {
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""")
}
else -> throw AssemblyError("wrong dt ${target.position}")
}
} }
TargetStorageKind.ARRAY -> TODO("assign long to array ${target.position}") TargetStorageKind.ARRAY -> TODO("assign long to array ${target.position}")
TargetStorageKind.MEMORY -> throw AssemblyError("memory is bytes not long ${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.REGISTER -> {
require(target.register in combinedLongRegisters)
val regstart = target.register.toString().take(2).lowercase()
when(sourceDt) {
DataType.BYTE -> {
asmgen.out(" lda $varName | sta cx16.$regstart")
asmgen.signExtendLongVariable("cx16.$regstart", sourceDt.base)
}
DataType.UBYTE -> {
asmgen.out("""
lda $varName
sta cx16.$regstart
lda #0
sta cx16.$regstart+1
sta cx16.$regstart+2
sta cx16.$regstart+3""")
}
DataType.WORD -> {
asmgen.out("""
lda $varName
sta cx16.$regstart
lda $varName+1
sta cx16.$regstart+1""")
asmgen.signExtendLongVariable("cx16.$regstart", sourceDt.base)
}
DataType.UWORD -> {
asmgen.out("""
lda $varName
sta cx16.$regstart
lda $varName+1
sta cx16.$regstart+1
lda #0
sta cx16.$regstart+2
sta cx16.$regstart+3""")
}
DataType.LONG -> {
asmgen.out("""
lda $varName
sta cx16.$regstart
lda $varName+1
sta cx16.$regstart+1
lda $varName+2
sta cx16.$regstart+2
lda $varName+3
sta cx16.$regstart+3""")
}
else -> throw AssemblyError("wrong dt ${target.position}")
}
}
TargetStorageKind.POINTER -> throw AssemblyError("can't assign long to pointer, pointers are 16 bits ${target.position}") TargetStorageKind.POINTER -> throw AssemblyError("can't assign long to pointer, pointers are 16 bits ${target.position}")
TargetStorageKind.VOID -> { /* do nothing */ } TargetStorageKind.VOID -> { /* do nothing */ }
} }
@@ -3624,18 +3706,22 @@ $endLabel""")
} }
} }
internal fun assignRegisterLong(target: AsmAssignTarget, lsw: RegisterOrPair, msw: RegisterOrPair) { internal fun assignRegisterLong(target: AsmAssignTarget, pairedRegisters: RegisterOrPair) {
when(target.kind) { when(target.kind) {
TargetStorageKind.VARIABLE -> { TargetStorageKind.VARIABLE -> {
asmgen.out(""" if(pairedRegisters in combinedLongRegisters) {
lda cx16.r0L val startreg = pairedRegisters.name.take(2).lowercase()
sta ${target.asmVarname} asmgen.out("""
lda cx16.r0H lda cx16.$startreg
sta ${target.asmVarname}+1 sta ${target.asmVarname}
lda cx16.r1L lda cx16.$startreg+1
sta ${target.asmVarname}+2 sta ${target.asmVarname}+1
lda cx16.r1H lda cx16.$startreg+2
sta ${target.asmVarname}+3""") sta ${target.asmVarname}+2
lda cx16.$startreg+3
sta ${target.asmVarname}+3""")
}
else throw AssemblyError("only combined vreg allowed as long target ${target.position}")
} }
TargetStorageKind.ARRAY -> { TargetStorageKind.ARRAY -> {
TODO("assign 32 bits int in R0:R1 into array ${target.position}") TODO("assign 32 bits int in R0:R1 into array ${target.position}")

View File

@@ -553,7 +553,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
} }
} }
private fun inplacemodificationLongWithVariable(targetVar: String, operator: String, sourceVar: String) { internal fun inplacemodificationLongWithVariable(targetVar: String, operator: String, sourceVar: String) {
when(operator) { when(operator) {
"+" -> { "+" -> {
asmgen.out(""" asmgen.out("""
@@ -658,7 +658,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
} }
} }
private fun inplacemodificationLongWithLiteralval(variable: String, operator: String, value: Int) { internal fun inplacemodificationLongWithLiteralval(variable: String, operator: String, value: Int) {
fun inplaceLongShiftLeft() { fun inplaceLongShiftLeft() {
when { when {

View File

@@ -797,11 +797,11 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
} }
BaseDataType.UWORD -> { BaseDataType.UWORD -> {
actualResultReg2 = codeGen.registers.next(IRDataType.LONG) actualResultReg2 = codeGen.registers.next(IRDataType.LONG)
addInstr(result, IRInstruction(Opcode.EXTS, type = IRDataType.WORD, reg1 = actualResultReg2, reg2=tr.resultReg), null) addInstr(result, IRInstruction(Opcode.EXT, type = IRDataType.WORD, reg1 = actualResultReg2, reg2=tr.resultReg), null)
} }
BaseDataType.WORD -> { BaseDataType.WORD -> {
actualResultReg2 = codeGen.registers.next(IRDataType.LONG) actualResultReg2 = codeGen.registers.next(IRDataType.LONG)
addInstr(result, IRInstruction(Opcode.EXT, type = IRDataType.WORD, reg1 = actualResultReg2, reg2=tr.resultReg), null) addInstr(result, IRInstruction(Opcode.EXTS, type = IRDataType.WORD, reg1 = actualResultReg2, reg2=tr.resultReg), null)
} }
else -> throw AssemblyError("weird cast value type ${cast.position}") else -> throw AssemblyError("weird cast value type ${cast.position}")
} }

View File

@@ -35,11 +35,14 @@ class ConstantFoldingOptimizer(private val program: Program, private val errors:
override fun after(numLiteral: NumericLiteral, parent: Node): Iterable<IAstModification> { override fun after(numLiteral: NumericLiteral, parent: Node): Iterable<IAstModification> {
if(numLiteral.type==BaseDataType.LONG && parent !is Assignment && parent !is VarDecl) { if(numLiteral.type==BaseDataType.LONG) {
// see if LONG values may be reduced to something smaller // see if LONG values may be reduced to something smaller
val smaller = NumericLiteral.optimalInteger(numLiteral.number.toInt(), numLiteral.position) val smaller = NumericLiteral.optimalInteger(numLiteral.number.toInt(), numLiteral.position)
if(smaller.type!=BaseDataType.LONG) { if(smaller.type!=BaseDataType.LONG) {
return listOf(IAstModification.ReplaceNode(numLiteral, smaller, parent)) if(parent !is Assignment || !parent.target.inferType(program).isLong) {
// do NOT reduce the type if the target of the assignment is a long
return listOf(IAstModification.ReplaceNode(numLiteral, smaller, parent))
}
} }
} }

View File

@@ -1554,7 +1554,11 @@ internal class AstChecker(private val program: Program,
} else if(leftDt.isBool && rightDt.isByte || leftDt.isByte && rightDt.isBool) { } else if(leftDt.isBool && rightDt.isByte || leftDt.isByte && rightDt.isBool) {
// expression with one side BOOL other side (U)BYTE is allowed; bool==byte // expression with one side BOOL other side (U)BYTE is allowed; bool==byte
} else if((expr.operator == "<<" || expr.operator == ">>") } else if((expr.operator == "<<" || expr.operator == ">>")
&& (leftDt.isWord && rightDt.isByte || leftDt.isLong && rightDt.isByte || leftDt.isLong && rightDt.isWord)) { && (leftDt.isByte && rightDt.isByte ||
leftDt.isWord && rightDt.isByte ||
leftDt.isWord && rightDt.isWord ||
leftDt.isLong && rightDt.isByte ||
leftDt.isLong && rightDt.isWord)) {
// exception allowed: shifting a word by a byte, long by a word or byte // exception allowed: shifting a word by a byte, long by a word or byte
} else if((expr.operator in BitwiseOperators) && (leftDt.isInteger && rightDt.isInteger)) { } else if((expr.operator in BitwiseOperators) && (leftDt.isInteger && rightDt.isInteger)) {
// exception allowed: bitwise operations with any integers // exception allowed: bitwise operations with any integers

View File

@@ -4,8 +4,7 @@ TODO
LONG TYPE LONG TYPE
--------- ---------
- call convention: return long -> return it in R0+R1.... because AY is only 16 bits... - call convention: return long -> return it in R0+R1.... because AY is only 16 bits...
- call convention: long param -> passed as regular variable NOT via R0:R1 asmsubs don't have syntax for this so use explicit separate msw() and lsw() arguments... - call convention: long param -> passed as regular variable NOT via R0:R1? asmsubs don't have syntax for this so use explicit separate msw() and lsw() arguments... Or introduce new syntax for R0+R1 combo's?
- implement << >> expressions on longs
- implement the bitwise & | ^ operations as expressions on longs (all types args) - implement the bitwise & | ^ operations as expressions on longs (all types args)
- make sure == and != work with longs against byte and words as well signed and unsigned - make sure == and != work with longs against byte and words as well signed and unsigned
- how hard is it to also implement the other comparison operators on longs? - how hard is it to also implement the other comparison operators on longs?

View File

@@ -15,7 +15,16 @@ main {
txt.print_ulhex(lv1, false) txt.print_ulhex(lv1, false)
txt.nl() txt.nl()
txt.print_l(lv1<<3) ; TODO fix compiler error lv1 = 999999
txt.print_l(lv1<<3)
txt.nl()
cx16.r4sL = -55
lv1 = 999999
txt.print_l(lv1+cx16.r4sL)
txt.nl()
cx16.r4s = -5555
lv1 = 999999
txt.print_l(lv1+cx16.r4s)
txt.nl() txt.nl()
lv2 <<= cx16.r0L lv2 <<= cx16.r0L
@@ -51,6 +60,7 @@ main {
b2 = lv2 != cx16.r0s b2 = lv2 != cx16.r0s
b1 = lv1 != lv2 b1 = lv1 != lv2
} }
}
/* /*
sub start2() { sub start2() {
@@ -174,4 +184,4 @@ main {
} }
*/ */
} ;}