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,
// cx16 virtual registers:
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 {
val names by lazy { entries.map { it.toString()} }
@@ -427,12 +429,13 @@ enum class RegisterOrPair {
BaseDataType.BYTE -> "sL"
BaseDataType.WORD -> "s"
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)
}
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
@@ -469,6 +472,17 @@ val Cx16VirtualRegisters = arrayOf(
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(
RegisterOrPair.A, RegisterOrPair.X, RegisterOrPair.Y,
RegisterOrPair.AX, RegisterOrPair.AY, RegisterOrPair.XY

View File

@@ -914,8 +914,7 @@ class AsmGen6502Internal (
when (target.kind) {
TargetStorageKind.VARIABLE -> {
out(" lda $valuesym | sta ${target.asmVarname}")
signExtendVariableLsb(target.asmVarname, value.value.type.base)
signExtendLongVariableMsw(target.asmVarname, value.value.type.base)
signExtendLongVariable(target.asmVarname, value.value.type.base)
}
TargetStorageKind.ARRAY -> TODO("assign long to array ${target.position}")
TargetStorageKind.MEMORY -> throw AssemblyError("memory is bytes not long ${target.position}")
@@ -931,11 +930,20 @@ class AsmGen6502Internal (
sta ${target.asmVarname}
lda $valuesym+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.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.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
when(valueDt) {
BaseDataType.UBYTE -> {
@@ -1421,7 +1429,7 @@ $repeatLabel""")
+ sta $asmvar+2
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
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 -> {
// longs are in R0:R1 (r0=lsw, r1=msw)
assignRegisterLong(target, RegisterOrPair.R0, RegisterOrPair.R1)
assignRegisterLong(target, RegisterOrPair.R0R1_32)
}
returnDt==BaseDataType.FLOAT -> {
// float result from function sits in FAC1
@@ -1244,7 +1244,7 @@ internal class AssignmentAsmGen(
asmgen.out(" jsr prog8_math.asl_byte_A")
assignRegisterByte(target, CpuRegister.A, signed, true)
return true
} else {
} else if(dt.isWord) {
assignExpressionToRegister(expr.left, RegisterOrPair.AY, signed)
asmgen.restoreRegisterStack(CpuRegister.X, true)
if(expr.operator==">>")
@@ -1256,6 +1256,12 @@ internal class AssignmentAsmGen(
asmgen.out(" jsr prog8_math.asl_word_AY")
assignRegisterpairWord(target, RegisterOrPair.AY)
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 {
@@ -1375,6 +1381,11 @@ internal class AssignmentAsmGen(
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
@@ -1787,7 +1798,11 @@ internal class AssignmentAsmGen(
}
}
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)) {
require(targetDt.isWord) {
"should be byte to word assignment ${origTypeCastExpression.position}"
require(targetDt.isWord || targetDt.isLong) {
"should be byte to word or long assignment ${origTypeCastExpression.position}"
}
when(target.kind) {
// TargetStorageKind.VARIABLE -> {
@@ -2541,7 +2556,7 @@ $endLabel""")
return
}
TargetStorageKind.REGISTER -> {
// byte to word, just assign to registers
// byte to word or long, just assign to registers
assignExpressionToRegister(value, target.register!!, targetDt.isSigned)
return
}
@@ -2686,7 +2701,7 @@ $endLabel""")
BaseDataType.LONG -> {
asmgen.out(" lda $sourceAsmVarName | sta $targetAsmVarName")
asmgen.signExtendVariableLsb(targetAsmVarName, BaseDataType.BYTE)
asmgen.signExtendLongVariableMsw(targetAsmVarName, BaseDataType.WORD)
asmgen.signExtendLongVariable(targetAsmVarName, BaseDataType.WORD)
}
BaseDataType.FLOAT -> {
asmgen.out("""
@@ -2752,7 +2767,7 @@ $endLabel""")
sta $targetAsmVarName
lda $sourceAsmVarName+1
sta $targetAsmVarName+1""")
asmgen.signExtendLongVariableMsw(targetAsmVarName, BaseDataType.WORD)
asmgen.signExtendLongVariable(targetAsmVarName, BaseDataType.WORD)
}
BaseDataType.FLOAT -> {
asmgen.out("""
@@ -3124,22 +3139,89 @@ $endLabel""")
}
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) {
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""")
when(sourceDt) {
DataType.BYTE -> {
TODO("signed byte to long var")
}
DataType.UBYTE -> {
TODO("ubyte to long var")
}
DataType.WORD -> {
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.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.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) {
TargetStorageKind.VARIABLE -> {
asmgen.out("""
lda cx16.r0L
sta ${target.asmVarname}
lda cx16.r0H
sta ${target.asmVarname}+1
lda cx16.r1L
sta ${target.asmVarname}+2
lda cx16.r1H
sta ${target.asmVarname}+3""")
if(pairedRegisters in combinedLongRegisters) {
val startreg = pairedRegisters.name.take(2).lowercase()
asmgen.out("""
lda cx16.$startreg
sta ${target.asmVarname}
lda cx16.$startreg+1
sta ${target.asmVarname}+1
lda cx16.$startreg+2
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 -> {
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) {
"+" -> {
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() {
when {

View File

@@ -797,11 +797,11 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
}
BaseDataType.UWORD -> {
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 -> {
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}")
}

View File

@@ -35,11 +35,14 @@ class ConstantFoldingOptimizer(private val program: Program, private val errors:
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
val smaller = NumericLiteral.optimalInteger(numLiteral.number.toInt(), numLiteral.position)
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) {
// expression with one side BOOL other side (U)BYTE is allowed; bool==byte
} 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
} else if((expr.operator in BitwiseOperators) && (leftDt.isInteger && rightDt.isInteger)) {
// exception allowed: bitwise operations with any integers

View File

@@ -4,8 +4,7 @@ TODO
LONG TYPE
---------
- 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...
- implement << >> expressions on longs
- 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 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
- 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.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()
lv2 <<= cx16.r0L
@@ -51,6 +60,7 @@ main {
b2 = lv2 != cx16.r0s
b1 = lv1 != lv2
}
}
/*
sub start2() {
@@ -174,4 +184,4 @@ main {
}
*/
}
;}