mirror of
https://github.com/irmen/prog8.git
synced 2025-08-07 21:25:18 +00:00
Compare commits
10 Commits
master
...
structs650
Author | SHA1 | Date | |
---|---|---|---|
|
5ada80779d | ||
|
e56f533e38 | ||
|
324fb7dbf7 | ||
|
44285b9b5d | ||
|
a68f477d61 | ||
|
9e10c15e2e | ||
|
6bd7752bac | ||
|
83ec437e8a | ||
|
4a1d05dd46 | ||
|
aa324e355a |
@@ -96,6 +96,7 @@ val BuiltinFunctions: Map<String, FSignature> = mapOf(
|
||||
"prog8_lib_stringcompare" to FSignature(true, BaseDataType.BYTE, FParam("str1", BaseDataType.STR), FParam("str2", BaseDataType.STR)),
|
||||
"prog8_lib_square_byte" to FSignature(true, BaseDataType.UBYTE, FParam("value", BaseDataType.BYTE, BaseDataType.UBYTE)),
|
||||
"prog8_lib_square_word" to FSignature(true, BaseDataType.UWORD, FParam("value", BaseDataType.WORD, BaseDataType.UWORD)),
|
||||
"prog8_lib_structalloc" to FSignature(true, BaseDataType.UWORD),
|
||||
"abs" to FSignature(true, null, FParam("value", *NumericDatatypes)),
|
||||
"abs__byte" to FSignature(true, BaseDataType.UBYTE, FParam("value", BaseDataType.BYTE)),
|
||||
"abs__word" to FSignature(true, BaseDataType.UWORD, FParam("value", BaseDataType.WORD)),
|
||||
|
@@ -350,6 +350,8 @@ class DataType private constructor(val base: BaseDataType, val sub: BaseDataType
|
||||
val isUnsigned = !base.isSigned
|
||||
val isArray = base.isArray
|
||||
val isPointer = base.isPointer
|
||||
val isPointerToByte = base.isPointer && sub?.isByteOrBool==true
|
||||
val isPointerToWord = base.isPointer && sub?.isWord==true
|
||||
val isStructInstance = base.isStructInstance
|
||||
val isPointerArray = base.isPointerArray
|
||||
val isBoolArray = base.isArray && !base.isPointerArray && sub == BaseDataType.BOOL
|
||||
@@ -422,6 +424,9 @@ enum class RegisterOrPair {
|
||||
}
|
||||
return listOf("cx16", name.lowercase()+suffix)
|
||||
}
|
||||
|
||||
fun isWord() = this==AX || this == AY || this==XY || this in Cx16VirtualRegisters
|
||||
|
||||
} // only used in parameter and return value specs in asm subroutines
|
||||
|
||||
enum class Statusflag {
|
||||
|
@@ -72,6 +72,7 @@ abstract class Zeropage(options: CompilationOptions): MemoryAllocator(options) {
|
||||
val size: Int =
|
||||
when {
|
||||
datatype.isIntegerOrBool -> options.compTarget.memorySize(datatype, null)
|
||||
datatype.isPointer -> options.compTarget.memorySize(datatype, null)
|
||||
datatype.isString || datatype.isArray -> {
|
||||
val memsize = options.compTarget.memorySize(datatype, numElements!!)
|
||||
if(position!=null)
|
||||
@@ -122,6 +123,7 @@ abstract class Zeropage(options: CompilationOptions): MemoryAllocator(options) {
|
||||
datatype.isNumericOrBool -> VarAllocation(address, datatype, size) // numerical variables in zeropage never have an initial value here because they are set in separate initializer assignments
|
||||
datatype.isString -> VarAllocation(address, datatype, size)
|
||||
datatype.isArray -> VarAllocation(address, datatype, size)
|
||||
datatype.isPointer -> VarAllocation(address, datatype, size)
|
||||
else -> throw AssemblyError("invalid dt")
|
||||
}
|
||||
}
|
||||
|
@@ -256,7 +256,8 @@ class AsmGen6502Internal (
|
||||
private val functioncallAsmGen = FunctionCallAsmGen(program, this)
|
||||
private val programGen = ProgramAndVarsGen(program, options, errors, symbolTable, functioncallAsmGen, this, allocator, zeropage)
|
||||
private val anyExprGen = AnyExprAsmGen(this)
|
||||
private val assignmentAsmGen = AssignmentAsmGen(program, this, anyExprGen, allocator)
|
||||
private val pointerGen = PointerAssignmentsGen(this, allocator)
|
||||
private val assignmentAsmGen = AssignmentAsmGen(program, this, pointerGen, anyExprGen, allocator)
|
||||
private val builtinFunctionsAsmGen = BuiltinFunctionsAsmGen(program, this, assignmentAsmGen)
|
||||
private val ifElseAsmgen = IfElseAsmGen(program, symbolTable, this, assignmentAsmGen, errors)
|
||||
private val ifExpressionAsmgen = IfExpressionAsmGen(this, assignmentAsmGen, errors)
|
||||
|
@@ -397,10 +397,16 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
private fun funcStructAlloc(fcall: PtBuiltinFunctionCall, discardResult: Boolean, resultRegister: RegisterOrPair?) {
|
||||
if(discardResult)
|
||||
throw AssemblyError("should not discard result of struct allocation at $fcall")
|
||||
if(fcall.args.isEmpty())
|
||||
TODO("struct alloc in BSS")
|
||||
else
|
||||
TODO("static struct alloc with values")
|
||||
val struct = fcall.type.subType!!
|
||||
// ... don't need to pay attention to args here because struct instance is put together elsewhere we just have to get a pointer to it
|
||||
val slabname = PtIdentifier("????TODO-STRUCTINSTANCENAME????", DataType.UWORD, fcall.position) // TODO STRUCTNAME
|
||||
val addressOf = PtAddressOf(fcall.type, true, fcall.position)
|
||||
addressOf.add(slabname)
|
||||
addressOf.parent = fcall
|
||||
val src = AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, fcall.type, expression = addressOf)
|
||||
val target = AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, false, fcall.position, null, asmgen)
|
||||
val assign = AsmAssignment(src, listOf(target), program.memsizer, fcall.position)
|
||||
asmgen.translateNormalAssignment(assign, fcall.definingISub())
|
||||
}
|
||||
|
||||
|
||||
|
@@ -333,7 +333,7 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
|
||||
} else {
|
||||
val scope = value.definingISub()
|
||||
val target: AsmAssignTarget =
|
||||
if(parameter.value.type.isByte && (register==RegisterOrPair.AX || register == RegisterOrPair.AY || register==RegisterOrPair.XY || register in Cx16VirtualRegisters))
|
||||
if(parameter.value.type.isByte && register.isWord())
|
||||
AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, parameter.value.type, scope, value.position, register = register)
|
||||
else {
|
||||
AsmAssignTarget.fromRegisters(register, parameter.value.type.isSigned, value.position, scope, asmgen)
|
||||
|
@@ -63,6 +63,11 @@ internal class IfElseAsmGen(private val program: PtProgram,
|
||||
}
|
||||
}
|
||||
|
||||
val deref = stmt.condition as? PtPointerDeref
|
||||
if(deref!=null) {
|
||||
TODO("ptr deref resulting in bool ${deref.position}")
|
||||
}
|
||||
|
||||
throw AssemblyError("weird non-boolean condition node type ${stmt.condition} at ${stmt.condition.position}")
|
||||
}
|
||||
|
||||
|
@@ -923,7 +923,7 @@ internal class ProgramAndVarsGen(
|
||||
else
|
||||
"-$$hexnum"
|
||||
}
|
||||
dt.isArray && dt.elementType().isUnsignedWord -> array.map {
|
||||
dt.isArray && (dt.elementType().isUnsignedWord || dt.elementType().isPointer) -> array.map {
|
||||
val number = it.number!!.toInt()
|
||||
"$" + number.toString(16).padStart(4, '0')
|
||||
}
|
||||
|
@@ -94,7 +94,7 @@ internal class VariableAllocator(private val symboltable: SymbolTable,
|
||||
if(errors.noErrors()) {
|
||||
val sortedList = varsDontCareWithoutAlignment.sortedByDescending { it.scopedNameString }
|
||||
for (variable in sortedList) {
|
||||
if(variable.dt.isIntegerOrBool) {
|
||||
if(variable.dt.isIntegerOrBool || variable.dt.isPointer) {
|
||||
if(zeropage.free.isEmpty()) {
|
||||
break
|
||||
} else {
|
||||
|
@@ -6,11 +6,12 @@ import prog8.codegen.cpu6502.AsmGen6502Internal
|
||||
|
||||
|
||||
internal enum class TargetStorageKind {
|
||||
VARIABLE,
|
||||
VARIABLE, // non-pointer variable
|
||||
ARRAY,
|
||||
MEMORY,
|
||||
REGISTER,
|
||||
VOID // assign nothing - used in multi-value assigns for void placeholders
|
||||
POINTER, // wherever the pointer variable points to
|
||||
VOID // assign nothing - used in multi-value assigns for void placeholders
|
||||
}
|
||||
|
||||
internal enum class SourceStorageKind {
|
||||
@@ -32,6 +33,7 @@ internal class AsmAssignTarget(val kind: TargetStorageKind,
|
||||
val array: PtArrayIndexer? = null,
|
||||
val memory: PtMemoryByte? = null,
|
||||
val register: RegisterOrPair? = null,
|
||||
val pointer: PtPointerDeref? = null,
|
||||
val origAstTarget: PtAssignTarget? = null
|
||||
)
|
||||
{
|
||||
@@ -84,6 +86,7 @@ internal class AsmAssignTarget(val kind: TargetStorageKind,
|
||||
}
|
||||
array != null -> return AsmAssignTarget(TargetStorageKind.ARRAY, asmgen, type, definingSub, target.position, array = array, origAstTarget = this)
|
||||
memory != null -> return AsmAssignTarget(TargetStorageKind.MEMORY, asmgen, type, definingSub, target.position, memory = memory, origAstTarget = this)
|
||||
pointerDeref != null -> return AsmAssignTarget(TargetStorageKind.POINTER, asmgen, type, definingSub, target.position, pointer = pointerDeref, origAstTarget = this)
|
||||
else -> throw AssemblyError("weird target")
|
||||
}
|
||||
}
|
||||
@@ -146,6 +149,9 @@ internal class AsmAssignTarget(val kind: TargetStorageKind,
|
||||
TargetStorageKind.MEMORY -> {
|
||||
left isSameAs memory!!
|
||||
}
|
||||
TargetStorageKind.POINTER -> {
|
||||
TODO("is pointer deref target same as expression? ${this.position}")
|
||||
}
|
||||
TargetStorageKind.REGISTER -> false
|
||||
TargetStorageKind.VOID -> false
|
||||
}
|
||||
|
@@ -10,10 +10,11 @@ import prog8.codegen.cpu6502.VariableAllocator
|
||||
internal class AssignmentAsmGen(
|
||||
private val program: PtProgram,
|
||||
private val asmgen: AsmGen6502Internal,
|
||||
private val pointergen: PointerAssignmentsGen,
|
||||
private val anyExprGen: AnyExprAsmGen,
|
||||
private val allocator: VariableAllocator
|
||||
) {
|
||||
private val augmentableAsmGen = AugmentableAssignmentAsmGen(program, this, asmgen, allocator)
|
||||
private val augmentableAsmGen = AugmentableAssignmentAsmGen(program, this, asmgen, pointergen, allocator)
|
||||
|
||||
fun translate(assignment: PtAssignment) {
|
||||
val target = AsmAssignTarget.fromAstAssignment(assignment.target, assignment.definingISub(), asmgen)
|
||||
@@ -214,13 +215,14 @@ internal class AssignmentAsmGen(
|
||||
}
|
||||
SourceStorageKind.LITERALNUMBER -> {
|
||||
// simple case: assign a constant number
|
||||
require(assign.target.datatype.isNumericOrBool)
|
||||
require(assign.target.datatype.isNumericOrBool || (assign.target.datatype.isPointer))
|
||||
val num = assign.source.number!!.number
|
||||
when (assign.target.datatype.base) {
|
||||
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.FLOAT -> assignConstantFloat(assign.target, num)
|
||||
BaseDataType.POINTER -> assignConstantWord(assign.target, num.toInt())
|
||||
else -> throw AssemblyError("weird numval type")
|
||||
}
|
||||
}
|
||||
@@ -243,7 +245,8 @@ internal class AssignmentAsmGen(
|
||||
}
|
||||
targetDt.isFloat -> assignVariableFloat(assign.target, variable)
|
||||
targetDt.isString -> assignVariableString(assign.target, variable)
|
||||
else -> throw AssemblyError("unsupported assignment target type ${assign.target.datatype}")
|
||||
targetDt.isPointer -> assignVariableWord(assign.target, variable, assign.source.datatype)
|
||||
else -> throw AssemblyError("unsupported assignment target type ${assign.target.datatype} ${assign.position}")
|
||||
}
|
||||
}
|
||||
SourceStorageKind.ARRAY -> {
|
||||
@@ -258,7 +261,7 @@ internal class AssignmentAsmGen(
|
||||
val constIndex = value.index.asConstInteger()
|
||||
|
||||
if(value.splitWords) {
|
||||
require(elementDt.isWord)
|
||||
require(elementDt.isWord || elementDt.isPointer)
|
||||
if(constIndex!=null) {
|
||||
asmgen.out(" lda ${arrayVarName}_lsb+$constIndex | ldy ${arrayVarName}_msb+$constIndex")
|
||||
assignRegisterpairWord(assign.target, RegisterOrPair.AY)
|
||||
@@ -480,6 +483,7 @@ internal class AssignmentAsmGen(
|
||||
}
|
||||
}
|
||||
is PtIfExpression -> asmgen.assignIfExpression(assign.target, value)
|
||||
is PtPointerDeref -> pointergen.assignPointerDerefExpression(assign.target, value)
|
||||
else -> throw AssemblyError("weird assignment value type $value")
|
||||
}
|
||||
}
|
||||
@@ -721,7 +725,7 @@ internal class AssignmentAsmGen(
|
||||
}
|
||||
assignRegisterByte(target, CpuRegister.A, false, false)
|
||||
}
|
||||
target.datatype.isWord -> assignRegisterpairWord(target, register)
|
||||
target.datatype.isWord || target.datatype.isPointer -> assignRegisterpairWord(target, register)
|
||||
else -> throw AssemblyError("expected byte or word")
|
||||
}
|
||||
}
|
||||
@@ -944,6 +948,7 @@ internal class AssignmentAsmGen(
|
||||
assignTrue.add(PtNumber.fromBoolean(true, assign.position))
|
||||
}
|
||||
}
|
||||
TargetStorageKind.POINTER -> TODO("optimized comparison for pointer-deref $expr.position")
|
||||
TargetStorageKind.REGISTER -> { /* handled earlier */ return true }
|
||||
TargetStorageKind.VOID -> { /* do nothing */ return true }
|
||||
}
|
||||
@@ -2475,7 +2480,7 @@ $endLabel""")
|
||||
else -> throw AssemblyError("weird type")
|
||||
}
|
||||
}
|
||||
sourceDt.isSignedWord -> {
|
||||
sourceDt.isSignedWord || sourceDt.isPointer -> {
|
||||
when(targetDt.base) {
|
||||
BaseDataType.BOOL -> {
|
||||
asmgen.out("""
|
||||
@@ -2488,7 +2493,7 @@ $endLabel""")
|
||||
BaseDataType.BYTE, BaseDataType.UBYTE -> {
|
||||
asmgen.out(" lda $sourceAsmVarName | sta $targetAsmVarName")
|
||||
}
|
||||
BaseDataType.UWORD -> {
|
||||
BaseDataType.UWORD, BaseDataType.POINTER -> {
|
||||
asmgen.out(" lda $sourceAsmVarName | sta $targetAsmVarName | lda $sourceAsmVarName+1 | sta $targetAsmVarName+1")
|
||||
}
|
||||
BaseDataType.FLOAT -> {
|
||||
@@ -2818,6 +2823,7 @@ $endLabel""")
|
||||
else -> throw AssemblyError("can't load address in a single 8-bit register")
|
||||
}
|
||||
}
|
||||
TargetStorageKind.POINTER -> pointergen.assignAddressOf(PtrTarget(target), sourceName, msb, arrayDt, arrayIndexExpr)
|
||||
TargetStorageKind.VOID -> { /* do nothing */ }
|
||||
}
|
||||
}
|
||||
@@ -2858,7 +2864,7 @@ $endLabel""")
|
||||
assignRegisterpairWord(target, RegisterOrPair.AY)
|
||||
return
|
||||
}
|
||||
require(sourceDt.isWord || sourceDt.isUnsignedByte || sourceDt.isBool) { "weird source dt for word variable" }
|
||||
require(sourceDt.isWord || sourceDt.isUnsignedByte || sourceDt.isBool || sourceDt.isPointer) { "weird source dt for word variable" }
|
||||
when(target.kind) {
|
||||
TargetStorageKind.VARIABLE -> {
|
||||
if(sourceDt.isUnsignedByte || sourceDt.isBool) {
|
||||
@@ -2961,6 +2967,7 @@ $endLabel""")
|
||||
}
|
||||
}
|
||||
}
|
||||
TargetStorageKind.POINTER -> pointergen.assignWordVar(PtrTarget(target), sourceName, sourceDt)
|
||||
TargetStorageKind.VOID -> { /* do nothing */ }
|
||||
}
|
||||
}
|
||||
@@ -2995,6 +3002,7 @@ $endLabel""")
|
||||
else if (target.register!! != RegisterOrPair.FAC1)
|
||||
throw AssemblyError("can't assign Fac1 float to another register")
|
||||
}
|
||||
TargetStorageKind.POINTER -> pointergen.assignFAC1(PtrTarget(target))
|
||||
TargetStorageKind.VOID -> { /* do nothing */ }
|
||||
}
|
||||
}
|
||||
@@ -3032,6 +3040,7 @@ $endLabel""")
|
||||
else -> throw AssemblyError("can only assign float to Fac1 or 2")
|
||||
}
|
||||
}
|
||||
TargetStorageKind.POINTER -> pointergen.assignFloatAY(PtrTarget(target))
|
||||
TargetStorageKind.VOID -> { /* do nothing */ }
|
||||
}
|
||||
}
|
||||
@@ -3069,6 +3078,7 @@ $endLabel""")
|
||||
else -> throw AssemblyError("can only assign float to Fac1 or 2")
|
||||
}
|
||||
}
|
||||
TargetStorageKind.POINTER -> pointergen.assignFloatVar(PtrTarget(target), sourceName)
|
||||
TargetStorageKind.VOID -> { /* do nothing */ }
|
||||
}
|
||||
}
|
||||
@@ -3113,6 +3123,7 @@ $endLabel""")
|
||||
else -> throw AssemblyError("weird register")
|
||||
}
|
||||
}
|
||||
TargetStorageKind.POINTER -> pointergen.assignByteVar(PtrTarget(target), sourceName)
|
||||
TargetStorageKind.VOID -> { /* do nothing */ }
|
||||
}
|
||||
}
|
||||
@@ -3493,6 +3504,7 @@ $endLabel""")
|
||||
}
|
||||
}
|
||||
}
|
||||
TargetStorageKind.POINTER -> pointergen.assignByteReg(PtrTarget(target), register, signed, extendWord)
|
||||
TargetStorageKind.VOID -> { /* do nothing */ }
|
||||
}
|
||||
}
|
||||
@@ -3529,7 +3541,7 @@ $endLabel""")
|
||||
}
|
||||
|
||||
internal fun assignRegisterpairWord(target: AsmAssignTarget, regs: RegisterOrPair) {
|
||||
require(target.datatype.isNumeric || target.datatype.isPassByRef) {
|
||||
require(target.datatype.isNumeric || target.datatype.isPassByRef || target.datatype.isPointer) {
|
||||
"assign target must be word type ${target.position}"
|
||||
}
|
||||
if(target.datatype.isFloat)
|
||||
@@ -3717,6 +3729,7 @@ $endLabel""")
|
||||
}
|
||||
}
|
||||
TargetStorageKind.MEMORY -> throw AssemblyError("can't store word into memory byte")
|
||||
TargetStorageKind.POINTER -> pointergen.assignWordRegister(PtrTarget(target), regs)
|
||||
TargetStorageKind.VOID -> { /* do nothing */ }
|
||||
}
|
||||
}
|
||||
@@ -3758,6 +3771,7 @@ $endLabel""")
|
||||
else -> throw AssemblyError("invalid register for word value")
|
||||
}
|
||||
}
|
||||
TargetStorageKind.POINTER -> pointergen.assignWord(PtrTarget(target), 0)
|
||||
TargetStorageKind.VOID -> { /* do nothing */ }
|
||||
}
|
||||
|
||||
@@ -3814,6 +3828,7 @@ $endLabel""")
|
||||
else -> throw AssemblyError("invalid register for word value")
|
||||
}
|
||||
}
|
||||
TargetStorageKind.POINTER -> pointergen.assignWord(PtrTarget(target), word)
|
||||
TargetStorageKind.VOID -> { /* do nothing */ }
|
||||
}
|
||||
}
|
||||
@@ -3856,6 +3871,7 @@ $endLabel""")
|
||||
}
|
||||
else -> throw AssemblyError("weird register")
|
||||
}
|
||||
TargetStorageKind.POINTER -> pointergen.assignByte(PtrTarget(target), 0)
|
||||
TargetStorageKind.VOID -> { /* do nothing */ }
|
||||
}
|
||||
|
||||
@@ -3901,6 +3917,7 @@ $endLabel""")
|
||||
}
|
||||
else -> throw AssemblyError("weird register")
|
||||
}
|
||||
TargetStorageKind.POINTER -> pointergen.assignByte(PtrTarget(target), byte)
|
||||
TargetStorageKind.VOID -> { /* do nothing */ }
|
||||
}
|
||||
}
|
||||
@@ -3944,6 +3961,7 @@ $endLabel""")
|
||||
else -> throw AssemblyError("can only assign float to Fac1 or 2")
|
||||
}
|
||||
}
|
||||
TargetStorageKind.POINTER -> pointergen.assignFloat(PtrTarget(target), 0.0)
|
||||
TargetStorageKind.VOID -> { /* do nothing */ }
|
||||
}
|
||||
} else {
|
||||
@@ -3982,6 +4000,7 @@ $endLabel""")
|
||||
else -> throw AssemblyError("can only assign float to Fac1 or 2")
|
||||
}
|
||||
}
|
||||
TargetStorageKind.POINTER -> pointergen.assignFloat(PtrTarget(target), float)
|
||||
TargetStorageKind.VOID -> { /* do nothing */ }
|
||||
}
|
||||
}
|
||||
@@ -4020,6 +4039,7 @@ $endLabel""")
|
||||
}
|
||||
else -> throw AssemblyError("weird register")
|
||||
}
|
||||
TargetStorageKind.POINTER -> pointergen.assignByteMemory(PtrTarget(target), address)
|
||||
TargetStorageKind.VOID -> { /* do nothing */ }
|
||||
}
|
||||
} else if (identifier != null) {
|
||||
@@ -4055,6 +4075,7 @@ $endLabel""")
|
||||
else -> throw AssemblyError("weird register")
|
||||
}
|
||||
}
|
||||
TargetStorageKind.POINTER -> pointergen.assignByteMemory(PtrTarget(target), identifier)
|
||||
TargetStorageKind.VOID -> { /* do nothing */ }
|
||||
}
|
||||
}
|
||||
@@ -4242,6 +4263,7 @@ $endLabel""")
|
||||
val invertOperator = if(assign.target.datatype.isBool) "not" else "~"
|
||||
assignPrefixedExpressionToArrayElt(makePrefixedExprFromArrayExprAssign(invertOperator, assign), scope)
|
||||
}
|
||||
TargetStorageKind.POINTER -> pointergen.inplaceByteInvert(PtrTarget(target))
|
||||
TargetStorageKind.VOID -> { /* do nothing */ }
|
||||
}
|
||||
}
|
||||
@@ -4266,6 +4288,7 @@ $endLabel""")
|
||||
}
|
||||
}
|
||||
TargetStorageKind.ARRAY -> assignPrefixedExpressionToArrayElt(makePrefixedExprFromArrayExprAssign("~", assign), scope)
|
||||
TargetStorageKind.POINTER -> pointergen.inplaceWordInvert(PtrTarget(target))
|
||||
else -> throw AssemblyError("weird target")
|
||||
}
|
||||
}
|
||||
@@ -4314,6 +4337,7 @@ $endLabel""")
|
||||
}
|
||||
TargetStorageKind.MEMORY -> throw AssemblyError("memory is ubyte, can't negate that")
|
||||
TargetStorageKind.ARRAY -> assignPrefixedExpressionToArrayElt(makePrefixedExprFromArrayExprAssign("-", assign), scope)
|
||||
TargetStorageKind.POINTER -> pointergen.inplaceByteNegate(PtrTarget(target), ignoreDatatype, scope)
|
||||
TargetStorageKind.VOID -> { /* do nothing */ }
|
||||
}
|
||||
}
|
||||
@@ -4373,6 +4397,7 @@ $endLabel""")
|
||||
}
|
||||
TargetStorageKind.MEMORY -> throw AssemblyError("memory is ubyte, can't negate that")
|
||||
TargetStorageKind.ARRAY -> assignPrefixedExpressionToArrayElt(makePrefixedExprFromArrayExprAssign("-", assign), scope)
|
||||
TargetStorageKind.POINTER -> pointergen.inplaceWordNegate(PtrTarget(target), ignoreDatatype, scope)
|
||||
TargetStorageKind.VOID -> { /* do nothing */ }
|
||||
}
|
||||
}
|
||||
|
@@ -9,6 +9,7 @@ import prog8.codegen.cpu6502.VariableAllocator
|
||||
internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
private val assignmentAsmGen: AssignmentAsmGen,
|
||||
private val asmgen: AsmGen6502Internal,
|
||||
private val ptrgen: PointerAssignmentsGen,
|
||||
private val allocator: VariableAllocator
|
||||
) {
|
||||
fun translate(assign: AsmAugmentedAssignment, scope: IPtSubroutine?) {
|
||||
@@ -514,6 +515,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
}
|
||||
}
|
||||
}
|
||||
TargetStorageKind.POINTER -> ptrgen.inplaceModification(PtrTarget(target), operator, value)
|
||||
TargetStorageKind.REGISTER -> throw AssemblyError("no asm gen for reg in-place modification")
|
||||
TargetStorageKind.VOID -> { /* do nothing */ }
|
||||
}
|
||||
|
@@ -0,0 +1,794 @@
|
||||
package prog8.codegen.cpu6502.assignment
|
||||
|
||||
import prog8.code.StStruct
|
||||
import prog8.code.ast.IPtSubroutine
|
||||
import prog8.code.ast.PtExpression
|
||||
import prog8.code.ast.PtIdentifier
|
||||
import prog8.code.ast.PtPointerDeref
|
||||
import prog8.code.core.*
|
||||
import prog8.codegen.cpu6502.AsmGen6502Internal
|
||||
import prog8.codegen.cpu6502.VariableAllocator
|
||||
|
||||
|
||||
internal class PtrTarget(target: AsmAssignTarget) {
|
||||
val dt = target.datatype
|
||||
val pointer = target.pointer!!
|
||||
val scope = target.scope
|
||||
val position = target.position
|
||||
}
|
||||
|
||||
|
||||
internal class PointerAssignmentsGen(private val asmgen: AsmGen6502Internal, private val allocator: VariableAllocator) {
|
||||
internal fun assignAddressOf(
|
||||
target: PtrTarget,
|
||||
sourceName: String,
|
||||
msb: Boolean,
|
||||
arrayDt: DataType?,
|
||||
arrayIndexExpr: PtExpression?
|
||||
) {
|
||||
TODO("assign address of to pointer deref ${target.position}")
|
||||
}
|
||||
|
||||
internal fun assignWordVar(target: PtrTarget, sourceName: String, sourceDt: DataType) {
|
||||
val zpPtrVar = deref(target.pointer)
|
||||
storeIndirectWordVar(sourceName, sourceDt, zpPtrVar)
|
||||
}
|
||||
|
||||
internal fun assignFAC1(target: PtrTarget) {
|
||||
TODO("assign FAC1 float to pointer deref ${target.position}")
|
||||
}
|
||||
|
||||
internal fun assignFloatAY(target: PtrTarget) {
|
||||
TODO("assign float from AY to pointer deref ${target.position}")
|
||||
}
|
||||
|
||||
internal fun assignFloatVar(target: PtrTarget, sourceName: String) {
|
||||
val zpPtrVar = deref(target.pointer)
|
||||
storeIndirectFloatVar(sourceName, zpPtrVar)
|
||||
}
|
||||
|
||||
internal fun assignByteVar(target: PtrTarget, sourceName: String) {
|
||||
val zpPtrVar = deref(target.pointer)
|
||||
storeIndirectByteVar(sourceName, zpPtrVar)
|
||||
}
|
||||
|
||||
internal fun assignByteReg(target: PtrTarget, register: CpuRegister, signed: Boolean, extendWord: Boolean) {
|
||||
TODO("assign register byte to pointer deref ${target.position}")
|
||||
}
|
||||
|
||||
internal fun assignWordRegister(target: PtrTarget, regs: RegisterOrPair) {
|
||||
TODO("assign register pair word to pointer deref ${target.position}")
|
||||
}
|
||||
|
||||
internal fun assignWord(target: PtrTarget, word: Int) {
|
||||
val zpPtrVar = deref(target.pointer)
|
||||
storeIndirectWord(word, zpPtrVar)
|
||||
}
|
||||
|
||||
internal fun assignByte(target: PtrTarget, byte: Int) {
|
||||
val zpPtrVar = deref(target.pointer)
|
||||
storeIndirectByte(byte, zpPtrVar)
|
||||
}
|
||||
|
||||
internal fun assignFloat(target: PtrTarget, float: Double) {
|
||||
val zpPtrVar = deref(target.pointer)
|
||||
storeIndirectFloat(float, zpPtrVar)
|
||||
}
|
||||
|
||||
internal fun assignByteMemory(target: PtrTarget, address: UInt) {
|
||||
TODO("assign memory byte to pointer deref ${target.position}")
|
||||
}
|
||||
|
||||
internal fun assignByteMemory(target: PtrTarget, identifier: PtIdentifier) {
|
||||
TODO("assign memory byte to pointer deref ${target.position}")
|
||||
}
|
||||
|
||||
internal fun inplaceByteInvert(target: PtrTarget) {
|
||||
TODO("inplace byte invert pointer deref ${target.position}")
|
||||
}
|
||||
|
||||
internal fun inplaceWordInvert(target: PtrTarget) {
|
||||
TODO("inplace word invert pointer deref ${target.position}")
|
||||
}
|
||||
|
||||
internal fun inplaceByteNegate(target: PtrTarget, ignoreDatatype: Boolean, scope: IPtSubroutine?) {
|
||||
TODO("inplace byte negate to pointer deref ${target.position}")
|
||||
}
|
||||
|
||||
internal fun inplaceWordNegate(target: PtrTarget, ignoreDatatype: Boolean, scope: IPtSubroutine?) {
|
||||
TODO("inplace word negate pointer deref ${target.position}")
|
||||
}
|
||||
|
||||
|
||||
|
||||
private fun deref(pointer: PtPointerDeref): String {
|
||||
// walk the pointer deref chain and leaves the final pointer value in a ZP var
|
||||
// this will often be the temp var P8ZP_SCRATCH_W1 but can also be the original pointer variable if it is already in zeropage
|
||||
if(pointer.chain.isEmpty()) {
|
||||
// TODO: do we have to look at derefLast ?
|
||||
|
||||
if(allocator.isZpVar(pointer.startpointer.name))
|
||||
return pointer.startpointer.name
|
||||
else {
|
||||
// have to copy it to temp zp var
|
||||
asmgen.assignExpressionToVariable(pointer.startpointer, "P8ZP_SCRATCH_W1", DataType.UWORD)
|
||||
return "P8ZP_SCRATCH_W1"
|
||||
}
|
||||
}
|
||||
|
||||
// walk pointer chain, calculate pointer address using P8ZP_SCRATCH_W1
|
||||
asmgen.assignExpressionToVariable(pointer.startpointer, "P8ZP_SCRATCH_W1", DataType.UWORD)
|
||||
|
||||
fun addFieldOffset(fieldoffset: UInt) {
|
||||
if(fieldoffset==0u)
|
||||
return
|
||||
require(fieldoffset<=0xffu)
|
||||
asmgen.out("""
|
||||
lda P8ZP_SCRATCH_W1
|
||||
clc
|
||||
adc #$fieldoffset
|
||||
sta P8ZP_SCRATCH_W1
|
||||
bcc +
|
||||
inc P8ZP_SCRATCH_W1+1
|
||||
+""")
|
||||
}
|
||||
|
||||
fun updatePointer() {
|
||||
asmgen.out("""
|
||||
ldy #0
|
||||
lda (P8ZP_SCRATCH_W1),y
|
||||
tax
|
||||
iny
|
||||
lda (P8ZP_SCRATCH_W1),y
|
||||
sta P8ZP_SCRATCH_W1+1
|
||||
stx P8ZP_SCRATCH_W1""")
|
||||
}
|
||||
|
||||
// traverse deref chain
|
||||
var struct: StStruct? = null
|
||||
if(pointer.startpointer.type.subType!=null)
|
||||
struct = pointer.startpointer.type.subType as StStruct
|
||||
for(deref in pointer.chain.dropLast(1)) {
|
||||
val fieldinfo = struct!!.getField(deref, asmgen.program.memsizer)
|
||||
val fieldoffset = fieldinfo.second
|
||||
struct = fieldinfo.first.subType as StStruct
|
||||
// get new pointer from field (P8ZP_SCRATCH_W1 += fieldoffset, read pointer from new location)
|
||||
addFieldOffset(fieldoffset)
|
||||
updatePointer()
|
||||
}
|
||||
|
||||
val field = pointer.chain.last()
|
||||
val fieldinfo = struct!!.getField(field, asmgen.program.memsizer)
|
||||
|
||||
addFieldOffset(fieldinfo.second)
|
||||
if(pointer.derefLast) {
|
||||
require(fieldinfo.first.isPointer)
|
||||
updatePointer()
|
||||
}
|
||||
return "P8ZP_SCRATCH_W1"
|
||||
}
|
||||
|
||||
internal fun assignPointerDerefExpression(target: AsmAssignTarget, value: PtPointerDeref) {
|
||||
val zpPtrVar = deref(value)
|
||||
if(value.type.isByteOrBool) {
|
||||
loadIndirectByte(zpPtrVar)
|
||||
asmgen.assignRegister(RegisterOrPair.A, target)
|
||||
}
|
||||
else if(value.type.isWord || value.type.isPointer) {
|
||||
loadIndirectWord(zpPtrVar)
|
||||
asmgen.assignRegister(RegisterOrPair.AY, target)
|
||||
}
|
||||
else if(value.type.isFloat) {
|
||||
loadIndirectFloat(zpPtrVar)
|
||||
asmgen.assignRegister(RegisterOrPair.FAC1, target)
|
||||
}
|
||||
else if(value.type.isLong)
|
||||
TODO("load long")
|
||||
else
|
||||
throw AssemblyError("weird dt ${value.type} in pointer deref assignment ${target.position}")
|
||||
}
|
||||
|
||||
private fun loadIndirectFloat(zpPtrVar: String) {
|
||||
// loads float pointed to by the ptrvar into FAC1
|
||||
asmgen.out("""
|
||||
lda $zpPtrVar
|
||||
ldy $zpPtrVar+1
|
||||
jsr floats.MOVFM
|
||||
""")
|
||||
}
|
||||
|
||||
private fun loadIndirectByte(zpPtrVar: String) {
|
||||
// loads byte pointed to by the ptrvar into A
|
||||
if(asmgen.isTargetCpu(CpuType.CPU65C02))
|
||||
asmgen.out(" lda ($zpPtrVar)")
|
||||
else
|
||||
asmgen.out(" ldy #0 | lda ($zpPtrVar),y")
|
||||
}
|
||||
|
||||
private fun loadIndirectWord(zpPtrVar: String) {
|
||||
// loads word pointed to by the ptr var into AY
|
||||
asmgen.out("""
|
||||
ldy #0
|
||||
lda ($zpPtrVar),y
|
||||
tax
|
||||
iny
|
||||
lda ($zpPtrVar),y
|
||||
tay
|
||||
txa""")
|
||||
}
|
||||
|
||||
private fun storeIndirectByte(byte: Int, zpPtrVar: String) {
|
||||
if(asmgen.isTargetCpu(CpuType.CPU65C02)) {
|
||||
asmgen.out(" lda #$byte | sta ($zpPtrVar)")
|
||||
} else {
|
||||
if (byte == 0) {
|
||||
asmgen.out(" lda #0 | tay | sta ($zpPtrVar),y")
|
||||
} else {
|
||||
asmgen.out(" lda #$byte | ldy #0 | sta ($zpPtrVar),y")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun storeIndirectByteVar(varname: String, zpPtrVar: String) {
|
||||
if(asmgen.isTargetCpu(CpuType.CPU65C02))
|
||||
asmgen.out(" lda $varname | sta ($zpPtrVar)")
|
||||
else
|
||||
asmgen.out(" lda $varname | ldy #0 | sta ($zpPtrVar),y")
|
||||
}
|
||||
|
||||
private fun storeIndirectWord(word: Int, zpPtrVar: String) {
|
||||
if(word==0) {
|
||||
asmgen.out("""
|
||||
lda #0
|
||||
tay
|
||||
sta ($zpPtrVar),y
|
||||
iny
|
||||
sta ($zpPtrVar),y""")
|
||||
} else {
|
||||
asmgen.out("""
|
||||
lda #<$word
|
||||
ldy #0
|
||||
sta ($zpPtrVar),y
|
||||
lda #>$word
|
||||
iny
|
||||
sta ($zpPtrVar),y""")
|
||||
}
|
||||
}
|
||||
|
||||
private fun storeIndirectWordVar(varname: String, sourceDt: DataType, zpPtrVar: String) {
|
||||
if(sourceDt.isByteOrBool) TODO("implement byte/bool to word pointer assignment")
|
||||
asmgen.out("""
|
||||
lda $varname
|
||||
ldy #0
|
||||
sta ($zpPtrVar),y
|
||||
lda $varname+1
|
||||
iny
|
||||
sta ($zpPtrVar),y""")
|
||||
}
|
||||
|
||||
private fun storeIndirectFloat(float: Double, zpPtrVar: String) {
|
||||
val floatConst = allocator.getFloatAsmConst(float)
|
||||
asmgen.out("""
|
||||
lda #<$floatConst
|
||||
ldy #>$floatConst
|
||||
sta P8ZP_SCRATCH_W2
|
||||
sty P8ZP_SCRATCH_W2+1
|
||||
lda $zpPtrVar
|
||||
ldy $zpPtrVar+1
|
||||
jsr floats.copy_float2""")
|
||||
}
|
||||
|
||||
private fun storeIndirectFloatVar(varname: String, zpPtrVar: String) {
|
||||
asmgen.out("""
|
||||
lda #<$varname
|
||||
ldy #>$varname+1
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
lda $zpPtrVar
|
||||
ldy $zpPtrVar+1
|
||||
jsr floats.copy_float""")
|
||||
}
|
||||
|
||||
fun inplaceModification(target: PtrTarget, operator: String, value: AsmAssignSource) {
|
||||
when (operator) {
|
||||
"+" -> {
|
||||
// byte targets are handled as direct memory access, not a pointer operation anymore
|
||||
if(target.dt.isWord) inplaceWordAdd(target, value)
|
||||
else if(target.dt.isFloat) inplaceFloatAddMul(target, "FADD", value)
|
||||
else throw AssemblyError("weird dt ${target.position}")
|
||||
}
|
||||
"-" -> {
|
||||
// byte targets are handled as direct memory access, not a pointer operation anymore
|
||||
if(target.dt.isWord) inplaceWordSub(target, value)
|
||||
else if(target.dt.isFloat) inplaceFloatSubDiv(target, "FSUB", value)
|
||||
else throw AssemblyError("weird dt ${target.position}")
|
||||
}
|
||||
"*" -> {
|
||||
// byte targets are handled as direct memory access, not a pointer operation anymore
|
||||
if(target.dt.isWord) inplaceWordMul(target, value)
|
||||
else if(target.dt.isFloat) inplaceFloatAddMul(target, "FMULT", value)
|
||||
else throw AssemblyError("weird dt ${target.position}")
|
||||
}
|
||||
"/" -> {
|
||||
if(target.dt.isWord) inplaceWordDiv(target, value)
|
||||
else if(target.dt.isFloat) inplaceFloatSubDiv(target, "FDIV", value)
|
||||
else throw AssemblyError("weird dt ${target.position}")
|
||||
}
|
||||
"%" -> TODO("inplace ptr %")
|
||||
"<<" -> {
|
||||
// byte targets are handled as direct memory access, not a pointer operation anymore
|
||||
if(target.dt.isWord) inplaceWordShiftLeft(target, value)
|
||||
else throw AssemblyError("weird dt ${target.position}")
|
||||
}
|
||||
">>" -> {
|
||||
// byte targets are handled as direct memory access, not a pointer operation anymore
|
||||
if(target.dt.isWord) inplaceWordShiftRight(target, value)
|
||||
else throw AssemblyError("weird dt ${target.position}")
|
||||
}
|
||||
"&", "and" -> {
|
||||
// byte targets are handled as direct memory access, not a pointer operation anymore however boolean targets are still to be handled here
|
||||
TODO("inplace ptr &")
|
||||
}
|
||||
"|", "or" -> {
|
||||
// byte targets are handled as direct memory access, not a pointer operation anymore however boolean targets are still to be handled here
|
||||
TODO("inplace ptr |")
|
||||
}
|
||||
"^", "xor" -> {
|
||||
// byte targets are handled as direct memory access, not a pointer operation anymore however boolean targets are still to be handled here
|
||||
if(target.dt.isByteOrBool) inplaceByteXor(target, value)
|
||||
else if(target.dt.isWord) inplaceWordXor(target, value)
|
||||
else throw AssemblyError("weird dt ${target.dt} ${target.position}")
|
||||
}
|
||||
"==" -> TODO("inplace ptr ==")
|
||||
"!=" -> TODO("inplace ptr !=")
|
||||
"<" -> TODO("inplace ptr <")
|
||||
"<=" -> TODO("inplace ptr <=")
|
||||
">" -> TODO("inplace ptr >")
|
||||
">=" -> TODO("inplace ptr >=")
|
||||
else -> throw AssemblyError("invalid operator for in-place modification $operator")
|
||||
}
|
||||
}
|
||||
|
||||
private fun inplaceWordShiftRight(target: PtrTarget, value: AsmAssignSource) {
|
||||
val ptrZpVar = deref(target.pointer)
|
||||
|
||||
if(target.dt.isSigned)
|
||||
TODO("signed word shift rigth ${target.position} $value")
|
||||
|
||||
fun shift1unsigned() {
|
||||
asmgen.out("""
|
||||
ldy #1
|
||||
lda ($ptrZpVar),y
|
||||
lsr a
|
||||
sta ($ptrZpVar),y
|
||||
dey
|
||||
lda ($ptrZpVar),y
|
||||
ror a
|
||||
sta ($ptrZpVar),y""")
|
||||
}
|
||||
|
||||
when(value.kind) {
|
||||
SourceStorageKind.LITERALNUMBER -> {
|
||||
val number = value.number!!.number.toInt()
|
||||
if(number==1) {
|
||||
shift1unsigned()
|
||||
} else if(number>1) {
|
||||
asmgen.out(" ldx #$number")
|
||||
asmgen.out("-")
|
||||
shift1unsigned()
|
||||
asmgen.out(" dex | bne -")
|
||||
}
|
||||
}
|
||||
SourceStorageKind.VARIABLE -> {
|
||||
require(value.datatype.isWord)
|
||||
val varname = value.asmVarname
|
||||
TODO("<< variable")
|
||||
}
|
||||
SourceStorageKind.EXPRESSION -> {
|
||||
require(value.datatype.isWord)
|
||||
asmgen.assignExpressionToRegister(value.expression!!, RegisterOrPair.AX, false)
|
||||
TODO("<< expression")
|
||||
}
|
||||
SourceStorageKind.REGISTER -> {
|
||||
require(value.datatype.isWord)
|
||||
val register = value.register!!
|
||||
asmgen.assignRegister(register, AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UWORD, null, target.position, variableAsmName = "P8ZP_SCRATCH_W1"))
|
||||
require(register.isWord())
|
||||
TODO("<< register")
|
||||
}
|
||||
else -> throw AssemblyError("weird source value ${value}")
|
||||
}
|
||||
}
|
||||
|
||||
private fun inplaceWordShiftLeft(target: PtrTarget, value: AsmAssignSource) {
|
||||
val ptrZpVar = deref(target.pointer)
|
||||
|
||||
fun shift1() {
|
||||
asmgen.out("""
|
||||
ldy #0
|
||||
lda ($ptrZpVar),y
|
||||
asl a
|
||||
sta ($ptrZpVar),y
|
||||
iny
|
||||
lda ($ptrZpVar),y
|
||||
rol a
|
||||
sta ($ptrZpVar),y""")
|
||||
}
|
||||
|
||||
when(value.kind) {
|
||||
SourceStorageKind.LITERALNUMBER -> {
|
||||
val number = value.number!!.number.toInt()
|
||||
if(number==1) {
|
||||
shift1()
|
||||
} else if(number>1) {
|
||||
asmgen.out(" ldx #$number")
|
||||
asmgen.out("-")
|
||||
shift1()
|
||||
asmgen.out(" dex | bne -")
|
||||
}
|
||||
}
|
||||
SourceStorageKind.VARIABLE -> {
|
||||
require(value.datatype.isWord)
|
||||
val varname = value.asmVarname
|
||||
TODO("<< variable")
|
||||
}
|
||||
SourceStorageKind.EXPRESSION -> {
|
||||
require(value.datatype.isWord)
|
||||
asmgen.assignExpressionToRegister(value.expression!!, RegisterOrPair.AX, false)
|
||||
TODO("<< expression")
|
||||
}
|
||||
SourceStorageKind.REGISTER -> {
|
||||
require(value.datatype.isWord)
|
||||
val register = value.register!!
|
||||
asmgen.assignRegister(register, AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UWORD, null, target.position, variableAsmName = "P8ZP_SCRATCH_W1"))
|
||||
require(register.isWord())
|
||||
TODO("<< register")
|
||||
}
|
||||
else -> throw AssemblyError("weird source value ${value}")
|
||||
}
|
||||
}
|
||||
|
||||
private fun inplaceWordAdd(target: PtrTarget, value: AsmAssignSource) {
|
||||
val ptrZpVar = deref(target.pointer)
|
||||
when(value.kind) {
|
||||
SourceStorageKind.LITERALNUMBER -> {
|
||||
val number = value.number!!.number.toInt()
|
||||
asmgen.out("""
|
||||
ldy #0
|
||||
lda ($ptrZpVar),y
|
||||
clc
|
||||
adc #<$number
|
||||
sta ($ptrZpVar),y
|
||||
iny
|
||||
lda ($ptrZpVar),y
|
||||
adc #>$number
|
||||
sta ($ptrZpVar),y""")
|
||||
}
|
||||
SourceStorageKind.VARIABLE -> {
|
||||
require(value.datatype.isWord)
|
||||
val varname = value.asmVarname
|
||||
asmgen.out("""
|
||||
ldy #0
|
||||
lda ($ptrZpVar),y
|
||||
clc
|
||||
adc $varname
|
||||
sta ($ptrZpVar),y
|
||||
iny
|
||||
lda ($ptrZpVar),y
|
||||
adc $varname+1
|
||||
sta ($ptrZpVar),y""")
|
||||
}
|
||||
SourceStorageKind.EXPRESSION -> {
|
||||
require(value.datatype.isWord)
|
||||
asmgen.assignExpressionToRegister(value.expression!!, RegisterOrPair.AX, false)
|
||||
asmgen.out("""
|
||||
ldy #0
|
||||
clc
|
||||
adc ($ptrZpVar),y
|
||||
sta ($ptrZpVar),y
|
||||
iny
|
||||
txa
|
||||
adc ($ptrZpVar),y
|
||||
sta ($ptrZpVar),y""")
|
||||
}
|
||||
SourceStorageKind.REGISTER -> {
|
||||
require(value.datatype.isWord)
|
||||
val register = value.register!!
|
||||
asmgen.assignRegister(register, AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UWORD, null, target.position, variableAsmName = "P8ZP_SCRATCH_W1"))
|
||||
require(register.isWord())
|
||||
asmgen.out("""
|
||||
ldy #0
|
||||
lda ($ptrZpVar),y
|
||||
clc
|
||||
adc P8ZP_SCRATCH_W1
|
||||
sta ($ptrZpVar),y
|
||||
iny
|
||||
lda ($ptrZpVar),y
|
||||
adc P8ZP_SCRATCH_W1+1
|
||||
sta ($ptrZpVar),y""")
|
||||
}
|
||||
else -> throw AssemblyError("weird source value ${value}")
|
||||
}
|
||||
}
|
||||
|
||||
private fun inplaceFloatAddMul(target: PtrTarget, floatoperation: String, value: AsmAssignSource) {
|
||||
require(floatoperation=="FADD" || floatoperation=="FMULT")
|
||||
val ptrZpVar = deref(target.pointer)
|
||||
asmgen.out("""
|
||||
lda $ptrZpVar
|
||||
ldy $ptrZpVar+1
|
||||
jsr floats.MOVFM""")
|
||||
when(value.kind) {
|
||||
SourceStorageKind.LITERALNUMBER -> {
|
||||
val floatConst = allocator.getFloatAsmConst(value.number!!.number)
|
||||
asmgen.out("""
|
||||
lda #<$floatConst
|
||||
ldy #>$floatConst
|
||||
jsr floats.$floatoperation
|
||||
ldx $ptrZpVar
|
||||
ldy $ptrZpVar+1
|
||||
jsr floats.MOVMF""")
|
||||
}
|
||||
SourceStorageKind.VARIABLE -> TODO("variable + * float")
|
||||
SourceStorageKind.EXPRESSION -> TODO("expression + * float")
|
||||
SourceStorageKind.REGISTER -> TODO("register + * float")
|
||||
else -> throw AssemblyError("weird source value ${value}")
|
||||
}
|
||||
}
|
||||
|
||||
private fun inplaceWordSub(target: PtrTarget, value: AsmAssignSource) {
|
||||
val ptrZpVar = deref(target.pointer)
|
||||
when(value.kind) {
|
||||
SourceStorageKind.LITERALNUMBER -> {
|
||||
val number = value.number!!.number.toInt()
|
||||
asmgen.out("""
|
||||
ldy #0
|
||||
lda ($ptrZpVar),y
|
||||
sec
|
||||
sbc #<$number
|
||||
sta ($ptrZpVar),y
|
||||
iny
|
||||
lda ($ptrZpVar),y
|
||||
sbc #>$number
|
||||
sta ($ptrZpVar),y""")
|
||||
}
|
||||
SourceStorageKind.VARIABLE -> {
|
||||
require(value.datatype.isWord)
|
||||
val varname = value.asmVarname
|
||||
asmgen.out("""
|
||||
ldy #0
|
||||
lda ($ptrZpVar),y
|
||||
sec
|
||||
sbc $varname
|
||||
sta ($ptrZpVar),y
|
||||
iny
|
||||
lda ($ptrZpVar),y
|
||||
sbc $varname+1
|
||||
sta ($ptrZpVar),y""")
|
||||
}
|
||||
SourceStorageKind.EXPRESSION -> {
|
||||
require(value.datatype.isWord)
|
||||
asmgen.assignExpressionToRegister(value.expression!!, RegisterOrPair.AX, false)
|
||||
asmgen.out("""
|
||||
ldy #0
|
||||
sec
|
||||
sbc ($ptrZpVar),y
|
||||
sta ($ptrZpVar),y
|
||||
iny
|
||||
txa
|
||||
sbc ($ptrZpVar),y
|
||||
sta ($ptrZpVar),y""")
|
||||
}
|
||||
SourceStorageKind.REGISTER -> TODO("register - word")
|
||||
else -> throw AssemblyError("weird source value ${value}")
|
||||
}
|
||||
}
|
||||
|
||||
private fun inplaceWordMul(target: PtrTarget, value: AsmAssignSource) {
|
||||
|
||||
val ptrZpVar = deref(target.pointer)
|
||||
|
||||
fun multiply() {
|
||||
// on entry here: number placed in routine argument variable
|
||||
loadIndirectWord(ptrZpVar)
|
||||
asmgen.out("""
|
||||
jsr prog8_math.multiply_words
|
||||
tax
|
||||
tya
|
||||
ldy #1
|
||||
sta ($ptrZpVar),y
|
||||
dey
|
||||
txa
|
||||
sta ($ptrZpVar),y""")
|
||||
}
|
||||
|
||||
when(value.kind) {
|
||||
SourceStorageKind.LITERALNUMBER -> {
|
||||
val number = value.number!!.number.toInt()
|
||||
if(number in powersOfTwoInt)
|
||||
throw AssemblyError("multiply by power of two should have been a shift $value.position")
|
||||
asmgen.out("""
|
||||
lda #<$number
|
||||
ldy #>$number
|
||||
sta prog8_math.multiply_words.multiplier
|
||||
sty prog8_math.multiply_words.multiplier+1""")
|
||||
multiply()
|
||||
}
|
||||
SourceStorageKind.VARIABLE -> {
|
||||
require(value.datatype.isWord)
|
||||
val varname = value.asmVarname
|
||||
asmgen.out("""
|
||||
lda $varname
|
||||
ldy $varname+1
|
||||
sta prog8_math.multiply_words.multiplier
|
||||
sty prog8_math.multiply_words.multiplier+1""")
|
||||
multiply()
|
||||
}
|
||||
SourceStorageKind.REGISTER -> {
|
||||
val register = value.register!!
|
||||
require(register.isWord())
|
||||
val multiplyArg = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UWORD, null, target.position, variableAsmName = " prog8_math.multiply_words.multiplier")
|
||||
asmgen.assignRegister(register, multiplyArg)
|
||||
multiply()
|
||||
}
|
||||
SourceStorageKind.EXPRESSION -> TODO("ptr * expr (word)")
|
||||
else -> throw AssemblyError("weird source value ${value}")
|
||||
}
|
||||
}
|
||||
|
||||
private fun inplaceWordDiv(target: PtrTarget, value: AsmAssignSource) {
|
||||
val ptrZpVar = deref(target.pointer)
|
||||
|
||||
fun divide(signed: Boolean) {
|
||||
// on entry here: number placed in P8ZP_SCRATCH_W1, divisor placed in AY
|
||||
if(signed) asmgen.out("jsr prog8_math.divmod_w_asm")
|
||||
else asmgen.out("jsr prog8_math.divmod_uw_asm")
|
||||
asmgen.out("""
|
||||
tax
|
||||
tya
|
||||
ldy #1
|
||||
sta ($ptrZpVar),y
|
||||
dey
|
||||
txa
|
||||
sta ($ptrZpVar),y""")
|
||||
}
|
||||
|
||||
when(value.kind) {
|
||||
SourceStorageKind.LITERALNUMBER -> {
|
||||
val number = value.number!!.number.toInt()
|
||||
if(number in powersOfTwoInt)
|
||||
throw AssemblyError("divide by power of two should have been a shift $value.position")
|
||||
loadIndirectWord(ptrZpVar)
|
||||
asmgen.out("""
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
lda #<$number
|
||||
ldy #>$number""")
|
||||
divide(target.dt.isSigned)
|
||||
}
|
||||
SourceStorageKind.VARIABLE -> {
|
||||
require(value.datatype.isWord)
|
||||
val varname = value.asmVarname
|
||||
TODO("inplace variable word divide")
|
||||
}
|
||||
SourceStorageKind.REGISTER -> {
|
||||
val register = value.register!!
|
||||
require(register.isWord())
|
||||
TODO("inplace register word divide")
|
||||
}
|
||||
SourceStorageKind.EXPRESSION -> TODO("ptr / expr (word)")
|
||||
else -> throw AssemblyError("weird source value ${value}")
|
||||
}
|
||||
}
|
||||
|
||||
private fun inplaceFloatSubDiv(target: PtrTarget, floatoperation: String, value: AsmAssignSource) {
|
||||
require(floatoperation=="FSUB" || floatoperation=="FDIV")
|
||||
val ptrZpVar = deref(target.pointer)
|
||||
when(value.kind) {
|
||||
SourceStorageKind.LITERALNUMBER -> {
|
||||
val floatConst = allocator.getFloatAsmConst(value.number!!.number)
|
||||
asmgen.out("""
|
||||
lda #<$floatConst
|
||||
ldy #>$floatConst
|
||||
jsr floats.MOVFM
|
||||
lda $ptrZpVar
|
||||
ldy $ptrZpVar+1
|
||||
jsr floats.$floatoperation
|
||||
ldx $ptrZpVar
|
||||
ldy $ptrZpVar+1
|
||||
jsr floats.MOVMF""")
|
||||
}
|
||||
SourceStorageKind.VARIABLE -> TODO("variable - / float")
|
||||
SourceStorageKind.EXPRESSION -> TODO("expression - / float")
|
||||
SourceStorageKind.REGISTER -> TODO("register - / float")
|
||||
else -> throw AssemblyError("weird source value ${value}")
|
||||
}
|
||||
}
|
||||
|
||||
private fun inplaceByteXor(target: PtrTarget, value: AsmAssignSource) {
|
||||
val ptrZpVar = deref(target.pointer)
|
||||
when(value.kind) {
|
||||
SourceStorageKind.LITERALNUMBER -> {
|
||||
val number = value.number!!.number.toInt()
|
||||
if(asmgen.isTargetCpu(CpuType.CPU65C02))
|
||||
asmgen.out("""
|
||||
lda ($ptrZpVar)
|
||||
eor #$number
|
||||
sta ($ptrZpVar)""")
|
||||
else
|
||||
asmgen.out("""
|
||||
ldy #0
|
||||
lda ($ptrZpVar),y
|
||||
eor #$number
|
||||
sta ($ptrZpVar),y""")
|
||||
}
|
||||
SourceStorageKind.VARIABLE -> {
|
||||
val varname = value.asmVarname
|
||||
if(asmgen.isTargetCpu(CpuType.CPU65C02))
|
||||
asmgen.out("""
|
||||
lda ($ptrZpVar)
|
||||
eor $varname
|
||||
sta ($ptrZpVar)""")
|
||||
else
|
||||
asmgen.out("""
|
||||
ldy #0
|
||||
lda ($ptrZpVar),y
|
||||
eor $varname
|
||||
sta ($ptrZpVar),y""")
|
||||
}
|
||||
SourceStorageKind.EXPRESSION -> {
|
||||
asmgen.assignExpressionToRegister(value.expression!!, RegisterOrPair.A, false)
|
||||
asmgen.out("""
|
||||
ldy #0
|
||||
eor ($ptrZpVar),y
|
||||
sta ($ptrZpVar),y""")
|
||||
}
|
||||
SourceStorageKind.REGISTER -> TODO("register ^ byte")
|
||||
else -> throw AssemblyError("weird source value ${value}")
|
||||
}
|
||||
}
|
||||
|
||||
private fun inplaceWordXor(target: PtrTarget, value: AsmAssignSource) {
|
||||
val ptrZpVar = deref(target.pointer)
|
||||
when(value.kind) {
|
||||
SourceStorageKind.LITERALNUMBER -> {
|
||||
val number = value.number!!.number.toInt()
|
||||
asmgen.out("""
|
||||
ldy #0
|
||||
lda ($ptrZpVar),y
|
||||
eor #<$number
|
||||
sta ($ptrZpVar),y
|
||||
iny
|
||||
lda ($ptrZpVar),y
|
||||
eor #>$number
|
||||
sta ($ptrZpVar),y""")
|
||||
}
|
||||
SourceStorageKind.VARIABLE -> {
|
||||
require(value.datatype.isWord)
|
||||
val varname = value.asmVarname
|
||||
asmgen.out("""
|
||||
ldy #0
|
||||
lda ($ptrZpVar),y
|
||||
eor $varname
|
||||
sta ($ptrZpVar),y
|
||||
lda ($ptrZpVar),y
|
||||
eor $varname+1
|
||||
sta ($ptrZpVar),y""")
|
||||
}
|
||||
SourceStorageKind.EXPRESSION -> {
|
||||
require(value.datatype.isWord)
|
||||
asmgen.assignExpressionToRegister(value.expression!!, RegisterOrPair.AX, false)
|
||||
asmgen.out("""
|
||||
ldy #0
|
||||
eor ($ptrZpVar),y
|
||||
sta ($ptrZpVar),y
|
||||
iny
|
||||
txa
|
||||
eor ($ptrZpVar),y
|
||||
sta ($ptrZpVar),y""")
|
||||
}
|
||||
SourceStorageKind.REGISTER -> TODO("register ^ word")
|
||||
else -> throw AssemblyError("weird source value ${value}")
|
||||
}
|
||||
}
|
||||
}
|
@@ -149,6 +149,29 @@ copy_float .proc
|
||||
rts
|
||||
.pend
|
||||
|
||||
copy_float2 .proc
|
||||
; -- copies the 5 bytes of the mflt value pointed to by P8ZP_SCRATCH_W2,
|
||||
; into the 5 bytes pointed to by A/Y. Clobbers A,Y.
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
ldy #0
|
||||
lda (P8ZP_SCRATCH_W2),y
|
||||
sta (P8ZP_SCRATCH_W1),y
|
||||
iny
|
||||
lda (P8ZP_SCRATCH_W2),y
|
||||
sta (P8ZP_SCRATCH_W1),y
|
||||
iny
|
||||
lda (P8ZP_SCRATCH_W2),y
|
||||
sta (P8ZP_SCRATCH_W1),y
|
||||
iny
|
||||
lda (P8ZP_SCRATCH_W2),y
|
||||
sta (P8ZP_SCRATCH_W1),y
|
||||
iny
|
||||
lda (P8ZP_SCRATCH_W2),y
|
||||
sta (P8ZP_SCRATCH_W1),y
|
||||
rts
|
||||
.pend
|
||||
|
||||
inc_var_f .proc
|
||||
; -- add 1 to float pointed to by A/Y
|
||||
; clobbers X
|
||||
|
@@ -2154,8 +2154,13 @@ internal class AstChecker(private val program: Program,
|
||||
}
|
||||
}
|
||||
}
|
||||
if (deref.inferType(program).isUnknown)
|
||||
errors.err("unable to determine type of dereferenced pointer expression", deref.position)
|
||||
if (deref.inferType(program).isUnknown) {
|
||||
val symbol = deref.definingScope.lookup(deref.chain.take(1))
|
||||
if(symbol==null)
|
||||
errors.err("undefined symbol: ${deref.chain[0]}", deref.position)
|
||||
else
|
||||
errors.err("unable to determine type of dereferenced pointer expression", deref.position)
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkLongType(expression: Expression) {
|
||||
|
@@ -15,29 +15,6 @@ internal fun postprocessSimplifiedAst(
|
||||
) {
|
||||
processDefers(program, st, errors)
|
||||
processSubtypesIntoStReferences(program, st)
|
||||
|
||||
if(option.compTarget.cpu!=CpuType.VIRTUAL)
|
||||
checkForPointerTypesOn6502(program, errors) // TODO remove this once the 6502 codegen can deal with pointer types and structs
|
||||
}
|
||||
|
||||
private fun checkForPointerTypesOn6502(program: PtProgram, errors: IErrorReporter) {
|
||||
fun check(node: PtNode) {
|
||||
when(node) {
|
||||
//is PtAddressOf -> if(node.type.isPointer) errors.err("cannot use pointer type yet on 6502 target $node", node.position)
|
||||
is PtAssignTarget -> if(!node.void && node.type.isPointer) errors.err("cannot use pointer type yet on 6502 target $node", node.position)
|
||||
//is PtBinaryExpression -> if(node.left.type.isPointer || node.right.type.isPointer) errors.err("cannot do pointer arithmetic yet on 6502 target $node", node.position)
|
||||
is PtIdentifier -> if(node.type.isPointer) errors.err("cannot use pointer type yet on 6502 target $node", node.position)
|
||||
is PtPointerDeref -> errors.err("cannot use pointer type yet on 6502 target $node", node.position)
|
||||
is PtPrefix -> if(node.type.isPointer) errors.err("cannot use pointer type yet on 6502 target $node", node.position)
|
||||
is PtTypeCast -> if(node.type.isPointer) errors.err("cannot use pointer type yet on 6502 target $node", node.position)
|
||||
is PtStructDecl -> errors.err("cannot use struct type yet on 6502 target $node", node.position)
|
||||
is PtSubroutineParameter -> if(node.type.isPointer) errors.err("cannot use pointer type yet on 6502 target $node", node.position)
|
||||
is PtVariable -> if(node.type.isPointer) errors.err("cannot use pointer type yet on 6502 target $node", node.position)
|
||||
is PtSubSignature -> if(node.returns.any{it.isPointer}) errors.err("cannot use pointer type yet on 6502 target $node", node.position)
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
walkAst(program) { node, _ -> check(node) }
|
||||
}
|
||||
|
||||
|
||||
|
@@ -16,6 +16,7 @@ import prog8.code.core.DataType
|
||||
import prog8.code.core.IMemSizer
|
||||
import prog8.code.core.ISubType
|
||||
import prog8.code.target.C64Target
|
||||
import prog8.code.target.Cx16Target
|
||||
import prog8.code.target.VMTarget
|
||||
import prog8.vm.VmRunner
|
||||
import prog8tests.helpers.ErrorReporterForTests
|
||||
@@ -29,7 +30,7 @@ class TestPointers: FunSpec( {
|
||||
|
||||
test("basic pointers") {
|
||||
val src="""
|
||||
%option enable_floats
|
||||
%import floats
|
||||
|
||||
main {
|
||||
^^bool g_bp
|
||||
@@ -173,12 +174,13 @@ other {
|
||||
}
|
||||
"""
|
||||
compileText(VMTarget(), false, src, outputDir) shouldNotBe null
|
||||
// TODO compileText(C64Target(), false, src, outputDir) shouldNotBe null
|
||||
compileText(C64Target(), false, src, outputDir) shouldNotBe null
|
||||
compileText(Cx16Target(), false, src, outputDir) shouldNotBe null
|
||||
}
|
||||
|
||||
test("struct pointers") {
|
||||
val src="""
|
||||
%option enable_floats
|
||||
%import floats
|
||||
|
||||
main {
|
||||
struct Node {
|
||||
@@ -218,7 +220,8 @@ main {
|
||||
}
|
||||
"""
|
||||
compileText(VMTarget(), false, src, outputDir) shouldNotBe null
|
||||
// TODO compileText(C64Target(), false, src, outputDir) shouldNotBe null
|
||||
compileText(C64Target(), false, src, outputDir) shouldNotBe null
|
||||
compileText(Cx16Target(), false, src, outputDir) shouldNotBe null
|
||||
}
|
||||
|
||||
test("pointer walking using simple dot notation should be equivalent to explicit dereference chain") {
|
||||
@@ -292,7 +295,7 @@ main {
|
||||
|
||||
test("word size pointer indexing on pointers") {
|
||||
val src="""
|
||||
%option enable_floats
|
||||
%import floats
|
||||
|
||||
main {
|
||||
|
||||
@@ -416,7 +419,8 @@ thing {
|
||||
}"""
|
||||
|
||||
compileText(VMTarget(), false, src, outputDir) shouldNotBe null
|
||||
// TODO compileText(C64Target(), false, src, outputDir) shouldNotBe null
|
||||
compileText(C64Target(), false, src, outputDir) shouldNotBe null
|
||||
compileText(Cx16Target(), false, src, outputDir) shouldNotBe null
|
||||
}
|
||||
|
||||
test("pointers in subroutine parameters") {
|
||||
@@ -455,7 +459,8 @@ thing {
|
||||
}"""
|
||||
|
||||
compileText(VMTarget(), false, src, outputDir) shouldNotBe null
|
||||
// TODO compileText(C64Target(), false, src, outputDir) shouldNotBe null
|
||||
compileText(C64Target(), false, src, outputDir) shouldNotBe null
|
||||
compileText(Cx16Target(), false, src, outputDir) shouldNotBe null
|
||||
}
|
||||
|
||||
test("str or ubyte array params or return type replaced by pointer to ubyte") {
|
||||
@@ -476,7 +481,6 @@ main {
|
||||
}
|
||||
}"""
|
||||
|
||||
// TODO check this for C64 target too
|
||||
val result = compileText(VMTarget(), false, src, outputDir, writeAssembly = false)!!
|
||||
val main = result.compilerAst.allBlocks.first {it.name=="main"}
|
||||
val test1 = main.statements[1] as Subroutine
|
||||
@@ -514,8 +518,10 @@ thing {
|
||||
|
||||
compileText(VMTarget(), false, src, outputDir) shouldNotBe null
|
||||
compileText(VMTarget(), true, src, outputDir) shouldNotBe null
|
||||
// TODO compileText(C64Target(), false, src, outputDir) shouldNotBe null
|
||||
// TODO compileText(C64Target(), true, src, outputDir) shouldNotBe null
|
||||
compileText(C64Target(), false, src, outputDir) shouldNotBe null
|
||||
compileText(C64Target(), true, src, outputDir) shouldNotBe null
|
||||
compileText(Cx16Target(), false, src, outputDir) shouldNotBe null
|
||||
compileText(Cx16Target(), true, src, outputDir) shouldNotBe null
|
||||
}
|
||||
|
||||
test("creating instances for unused vars should all be removed") {
|
||||
@@ -549,7 +555,8 @@ thing {
|
||||
start.children[0] shouldBe instanceOf<PtSubSignature>()
|
||||
start.children[1] shouldBe instanceOf<PtReturn>()
|
||||
}
|
||||
// TODO compileText(C64Target(), true, src, outputDir) shouldNotBe null
|
||||
compileText(C64Target(), true, src, outputDir) shouldNotBe null
|
||||
compileText(Cx16Target(), true, src, outputDir) shouldNotBe null
|
||||
}
|
||||
|
||||
test("creating instances should have correct number of args") {
|
||||
@@ -656,7 +663,7 @@ main {
|
||||
|
||||
test("untyped and typed address-of operators") {
|
||||
val src="""
|
||||
%option enable_floats
|
||||
%import floats
|
||||
|
||||
main {
|
||||
sub start() {
|
||||
@@ -666,7 +673,8 @@ main {
|
||||
}
|
||||
}"""
|
||||
|
||||
val result = compileText(VMTarget(), false, src, outputDir, writeAssembly = true)!!
|
||||
compileText(C64Target(), false, src, outputDir) shouldNotBe null
|
||||
val result = compileText(VMTarget(), false, src, outputDir)!!
|
||||
val st = result.codegenAst!!.entrypoint()!!.children
|
||||
st.size shouldBe 6
|
||||
val r0v = (st[3] as PtAssignment).value as PtBinaryExpression
|
||||
@@ -696,7 +704,7 @@ main {
|
||||
|
||||
test("address-of struct fields") {
|
||||
val src="""
|
||||
%option enable_floats
|
||||
%import floats
|
||||
%import textio
|
||||
|
||||
main {
|
||||
@@ -723,6 +731,8 @@ main {
|
||||
}
|
||||
}"""
|
||||
compileText(VMTarget(), false, src, outputDir) shouldNotBe null
|
||||
compileText(C64Target(), false, src, outputDir) shouldNotBe null
|
||||
compileText(Cx16Target(), false, src, outputDir) shouldNotBe null
|
||||
}
|
||||
|
||||
test("address-of pointer arithmetic on alias") {
|
||||
@@ -740,6 +750,7 @@ main {
|
||||
cx16.r4 = &curframe + index
|
||||
}
|
||||
}"""
|
||||
compileText(C64Target(), false, src, outputDir) shouldNotBe null
|
||||
val result = compileText(VMTarget(), false, src, outputDir)!!
|
||||
val st = result.compilerAst.entrypoint.statements
|
||||
st.size shouldBe 9
|
||||
@@ -776,6 +787,8 @@ main {
|
||||
}
|
||||
}"""
|
||||
compileText(VMTarget(), false, src, outputDir) shouldNotBe null
|
||||
compileText(C64Target(), false, src, outputDir) shouldNotBe null
|
||||
compileText(Cx16Target(), false, src, outputDir) shouldNotBe null
|
||||
}
|
||||
|
||||
test("uword as pointer versus pointer to uword difference") {
|
||||
@@ -790,6 +803,7 @@ main {
|
||||
}
|
||||
}"""
|
||||
|
||||
compileText(C64Target(), false, src, outputDir) shouldNotBe null
|
||||
val result = compileText(VMTarget(), false, src, outputDir)!!
|
||||
val st = result.codegenAst!!.entrypoint()!!.children
|
||||
st.size shouldBe 8
|
||||
@@ -863,7 +877,7 @@ main {
|
||||
|
||||
test("indexing pointers with index 0 is just a direct pointer dereference") {
|
||||
val src="""
|
||||
%option enable_floats
|
||||
%import floats
|
||||
main {
|
||||
struct List {
|
||||
^^uword s
|
||||
@@ -977,6 +991,8 @@ main {
|
||||
}
|
||||
}"""
|
||||
compileText(VMTarget(), true, src, outputDir) shouldNotBe null
|
||||
compileText(C64Target(), true, src, outputDir) shouldNotBe null
|
||||
compileText(Cx16Target(), true, src, outputDir) shouldNotBe null
|
||||
}
|
||||
|
||||
test("global struct var deref type") {
|
||||
@@ -998,6 +1014,8 @@ main {
|
||||
}"""
|
||||
|
||||
compileText(VMTarget(), true, src, outputDir) shouldNotBe null
|
||||
compileText(C64Target(), true, src, outputDir) shouldNotBe null
|
||||
compileText(Cx16Target(), true, src, outputDir) shouldNotBe null
|
||||
}
|
||||
|
||||
test("local struct var deref type") {
|
||||
@@ -1018,11 +1036,13 @@ main {
|
||||
}"""
|
||||
|
||||
compileText(VMTarget(), true, src, outputDir) shouldNotBe null
|
||||
compileText(C64Target(), true, src, outputDir) shouldNotBe null
|
||||
compileText(Cx16Target(), true, src, outputDir) shouldNotBe null
|
||||
}
|
||||
|
||||
test("assigning pointer dereferences via memcopy") {
|
||||
val src="""
|
||||
%option enable_floats
|
||||
%import floats
|
||||
|
||||
main {
|
||||
sub start() {
|
||||
@@ -1045,6 +1065,7 @@ main {
|
||||
}
|
||||
}"""
|
||||
|
||||
compileText(C64Target(), false, src, outputDir) shouldNotBe null
|
||||
val result = compileText(VMTarget(), false, src, outputDir)!!
|
||||
val st = result.compilerAst.entrypoint.statements
|
||||
st.size shouldBe 10
|
||||
@@ -1053,7 +1074,7 @@ main {
|
||||
|
||||
test("assigning pointer dereferences should be same type") {
|
||||
val src="""
|
||||
%option enable_floats
|
||||
%import floats
|
||||
|
||||
main {
|
||||
sub start() {
|
||||
@@ -1110,11 +1131,13 @@ other {
|
||||
}
|
||||
}"""
|
||||
compileText(VMTarget(), false, src, outputDir) shouldNotBe null
|
||||
compileText(C64Target(), false, src, outputDir) shouldNotBe null
|
||||
compileText(Cx16Target(), false, src, outputDir) shouldNotBe null
|
||||
}
|
||||
|
||||
test("a.b.c[i]^^ as expression where pointer is primitive type") {
|
||||
val src="""
|
||||
%option enable_floats
|
||||
%import floats
|
||||
|
||||
main {
|
||||
sub start() {
|
||||
@@ -1140,6 +1163,8 @@ other {
|
||||
}
|
||||
"""
|
||||
compileText(VMTarget(), false, src, outputDir) shouldNotBe null
|
||||
compileText(C64Target(), false, src, outputDir) shouldNotBe null
|
||||
compileText(Cx16Target(), false, src, outputDir) shouldNotBe null
|
||||
}
|
||||
|
||||
test("a.b.c[i]^^.value = X where pointer is struct gives good error message") {
|
||||
@@ -1222,7 +1247,7 @@ other {
|
||||
|
||||
test("a.b.c[i]^^ as assignment target where pointer is primitive type") {
|
||||
val src="""
|
||||
%option enable_floats
|
||||
%import floats
|
||||
|
||||
main {
|
||||
sub start() {
|
||||
@@ -1249,6 +1274,8 @@ other {
|
||||
}
|
||||
}"""
|
||||
compileText(VMTarget(), false, src, outputDir) shouldNotBe null
|
||||
compileText(C64Target(), false, src, outputDir) shouldNotBe null
|
||||
compileText(Cx16Target(), false, src, outputDir) shouldNotBe null
|
||||
}
|
||||
|
||||
test("passing arrays to subroutines via typed pointer parameters") {
|
||||
@@ -1289,11 +1316,13 @@ main {
|
||||
}
|
||||
}"""
|
||||
compileText(VMTarget(), false, src, outputDir) shouldNotBe null
|
||||
compileText(C64Target(), false, src, outputDir) shouldNotBe null
|
||||
compileText(Cx16Target(), false, src, outputDir) shouldNotBe null
|
||||
}
|
||||
|
||||
test("array of pointers as subroutine param are all passed as ^^ubyte because of split word arrays") {
|
||||
val src="""
|
||||
%option enable_floats
|
||||
%import floats
|
||||
|
||||
main {
|
||||
|
||||
@@ -1320,6 +1349,8 @@ main {
|
||||
}
|
||||
}"""
|
||||
compileText(VMTarget(), false, src, outputDir) shouldNotBe null
|
||||
compileText(C64Target(), false, src, outputDir) shouldNotBe null
|
||||
compileText(Cx16Target(), false, src, outputDir) shouldNotBe null
|
||||
}
|
||||
|
||||
test("hoist variable decl and initializer correctly in case of pointer type variable as well") {
|
||||
@@ -1343,6 +1374,7 @@ main {
|
||||
}
|
||||
}
|
||||
}"""
|
||||
compileText(C64Target(), false, src, outputDir) shouldNotBe null
|
||||
val result = compileText(VMTarget(), false, src, outputDir)!!
|
||||
val st = result.compilerAst.entrypoint.statements
|
||||
st.size shouldBe 6
|
||||
@@ -1370,6 +1402,8 @@ main {
|
||||
}"""
|
||||
|
||||
compileText(VMTarget(), false, src, outputDir) shouldNotBe null
|
||||
compileText(C64Target(), false, src, outputDir) shouldNotBe null
|
||||
compileText(Cx16Target(), false, src, outputDir) shouldNotBe null
|
||||
}
|
||||
|
||||
test("type error for invalid bool field initializer") {
|
||||
@@ -1453,6 +1487,8 @@ db {
|
||||
}"""
|
||||
|
||||
compileText(VMTarget(), false, src, outputDir) shouldNotBe null
|
||||
compileText(C64Target(), false, src, outputDir) shouldNotBe null
|
||||
compileText(Cx16Target(), false, src, outputDir) shouldNotBe null
|
||||
}
|
||||
|
||||
test("str can be used without explicit cast where ^^ubyte is expected") {
|
||||
@@ -1481,7 +1517,7 @@ main {
|
||||
|
||||
test("initializing arrays of pointers") {
|
||||
val src="""
|
||||
%option enable_floats
|
||||
%import floats
|
||||
|
||||
main {
|
||||
|
||||
@@ -1505,6 +1541,8 @@ main {
|
||||
}"""
|
||||
|
||||
compileText(VMTarget(), false, src, outputDir) shouldNotBe null
|
||||
compileText(C64Target(), false, src, outputDir) shouldNotBe null
|
||||
compileText(Cx16Target(), false, src, outputDir) shouldNotBe null
|
||||
}
|
||||
|
||||
test("array indexing a pointer and a pointer array both work") {
|
||||
@@ -1523,6 +1561,8 @@ main {
|
||||
}
|
||||
}"""
|
||||
compileText(VMTarget(), false, src, outputDir) shouldNotBe null
|
||||
compileText(C64Target(), false, src, outputDir) shouldNotBe null
|
||||
compileText(Cx16Target(), false, src, outputDir) shouldNotBe null
|
||||
}
|
||||
|
||||
test("passing nosplit array of structpointers to a subroutine in various forms should be param type ptr to struct") {
|
||||
@@ -1574,6 +1614,7 @@ main {
|
||||
}
|
||||
}"""
|
||||
val errors=ErrorReporterForTests(keepMessagesAfterReporting = true)
|
||||
compileText(C64Target(), false, src, outputDir, errors=errors) shouldNotBe null
|
||||
val result = compileText(VMTarget(), false, src, outputDir, errors=errors)
|
||||
errors.errors.size shouldBe 0
|
||||
errors.warnings.size shouldBe 0
|
||||
@@ -1639,6 +1680,8 @@ main {
|
||||
}
|
||||
}"""
|
||||
compileText(VMTarget(), false, src, outputDir) shouldNotBe null
|
||||
compileText(C64Target(), false, src, outputDir) shouldNotBe null
|
||||
compileText(Cx16Target(), false, src, outputDir) shouldNotBe null
|
||||
}
|
||||
|
||||
test("boolean field in if statement condition") {
|
||||
@@ -1658,6 +1701,8 @@ main {
|
||||
}"""
|
||||
|
||||
compileText(VMTarget(), false, src, outputDir) shouldNotBe null
|
||||
compileText(C64Target(), false, src, outputDir) shouldNotBe null
|
||||
compileText(Cx16Target(), false, src, outputDir) shouldNotBe null
|
||||
}
|
||||
|
||||
test("^^str is not valid") {
|
||||
@@ -1742,7 +1787,7 @@ other {
|
||||
|
||||
test("float ptr inplace operations") {
|
||||
val src="""
|
||||
%option enable_floats
|
||||
%import floats
|
||||
|
||||
main {
|
||||
^^float g_floats
|
||||
@@ -1795,8 +1840,8 @@ other {
|
||||
}
|
||||
}"""
|
||||
compileText(VMTarget(), false, src, outputDir) shouldNotBe null
|
||||
//compileText(C64Target(), false, src, outputDir) shouldNotBe null
|
||||
//compileText(Cx16Target(), false, src, outputDir) shouldNotBe null
|
||||
compileText(C64Target(), false, src, outputDir) shouldNotBe null
|
||||
compileText(Cx16Target(), false, src, outputDir) shouldNotBe null
|
||||
}
|
||||
|
||||
})
|
@@ -9,7 +9,7 @@ Structs and Pointers
|
||||
The 6502 cpu lacks some features (addressing modes, registers) to make pointers work efficiently.
|
||||
Also it requires that pointer variables have to be in zero page, or copied to a temporary zero page variable,
|
||||
before they can even be used as a pointer. This means that pointer operations in prog8 compile
|
||||
to rather inefficient assembly code most of the time, when compared to direct array access or regular variables.
|
||||
to rather large and inefficient assembly code most of the time, when compared to direct array access or regular variables.
|
||||
At least try to place heavily used pointer variables in zero page using ``@requirezp`` on their declaration,
|
||||
if zero page space allows.
|
||||
|
||||
|
@@ -2,164 +2,13 @@ TODO
|
||||
====
|
||||
|
||||
|
||||
STRUCTS and TYPED POINTERS
|
||||
--------------------------
|
||||
STRUCTS and TYPED POINTERS (6502 codegen specific)
|
||||
--------------------------------------------------
|
||||
|
||||
'DONE' means working in the 'virtual' compiler target... (no 6502 codegen has been touched yet)
|
||||
|
||||
- DONE: add ast type check for assignments to struct fields; node_ptr.nextnode = enemy_ptr should error
|
||||
- DONE: declare struct as a separate entity so you can then declare multiple variables (pointers) of the same struct type. Like usual.
|
||||
- DONE: struct is a 'packed' struct, fields are placed in order of declaration. This guarantees exact size and place of the fields
|
||||
- DONE: structs only supported as a reference type (uword pointer). This removes a lot of the problems related to introducing a variable length value type.
|
||||
- DONE: need to introduce typed pointer datatype in prog8 to allow this to make any sense. Syntax to declare a pointer type: ^^datatype (double hat to avoid parsing confusion with the eor operator)
|
||||
- DONE: initially only a pointer-to-struct should actually work, pointer-to-other-type is possible but that can come later.
|
||||
- DONE: a struct can contain only numeric type fields (byte,word,float) or str fields (translated into ^^ubyte) or other pointer fields. No nested structs, no arrays.
|
||||
- DONE: max 1 page of memory total size to allow regular register indexing
|
||||
- DONE: assigning ptrs of different types is only allowed via a cast as usual. For simple address (uword) assignments, no cast is needed (but allowed)
|
||||
- DONE: how to dereference a pointer? Pascal does it like this: ptr^ But this conflicts with the existing eor operator so we now use ptr^^^ (double hat)
|
||||
- DONE: dereferencing a pointer to struct could look like Pascal's ptr^.field as well, but the ^ is actually redundant here; compiler already knows it's a pointer type.
|
||||
Note that actually dereferencing a pointer to a struct as an explicit operation, conflicts with the third axiom on this list (structs only as reference types) so it can only be done for basic types?
|
||||
So... setting struct fields can simply be ``structvar.field = 42`` and reading them ``a = structvar.field``
|
||||
- DONE: you should be able to get the address of an individual field: ``&structpointer.field``
|
||||
- DONE: teach sizeof() how to calculate struct sizes (need unit test + doc)
|
||||
- DONE: sizeof(ptr^^) works
|
||||
- DONE: implicit cast of pointer to uword in conditional expressions
|
||||
- DONE: subroutine parameters and return values should be able to accept pointers as well now
|
||||
- DONE (for basic types only): allow array syntax on pointers too: ptr[2] means ptr+sizeof()*2, ptr[0] just means ptr^^ .
|
||||
- DONE (?) allow array syntax on pointers to structs too, but what type will ptr[2] have? And it will require ptr[2].field to work as well now. Actually that will be the only thing to work for now.
|
||||
- DONE: allow multi-field declarations in structs
|
||||
- DONE: static initialization of structs. It behaves like arrays; it won't reset to the original value when program is restarted, so beware.
|
||||
Syntax: ^^Node ptr = Node(1,2,3,4) statically allocates a Node with fields set to 1,2,3,4 and puts the address in ptr.
|
||||
Node() without arguments allocates a node in BSS variable space instead that gets zeroed out at startup.
|
||||
(Internally this gets translated into a structalloc(1,2,3,4) builtin function call that has a pointer to the struct as its return type)
|
||||
- DONE: pointer arrays are split-words only, enforce this (variable dt + initializer array dt)
|
||||
- DONE: make an error message for all pointer expressions (prefixed, binary) so we can start implementing the ones we need one by one.
|
||||
- DONE: start by making ptr.value++ work , and ptr.value = ptr.value+20, and ptr.value = cx16.r0L+20+ptr.value Likewise for subtraction. DON'T FORGET C POINTER SEMANTICS. Other operators are nonsensical for ptr arith
|
||||
- DONE: support @dirty on pointer vars -> uninitialized pointer placed in BSS_noclear segment
|
||||
- DONE: support comparison operators on pointers
|
||||
- DONE: implement augmented assignment on pointer dereference
|
||||
- DONE: pointer types in subroutine signatures (both normal and asm-subs, parameters and return values)
|
||||
- DONE: arrays of structs? No -> Just an array of uword pointers to said structs.
|
||||
- DONE: what about pointers to subroutines? should these be typed as well now? Probably not, just stick with UWORD untyped pointer to avoid needless complexity.
|
||||
- DONE: implement inplace logical and & or, with short-cirtuit, on dereferenced pointer
|
||||
- DONE: existing ARRAY type remains unchanged (it doesn't become a typed pointer) so we can keep doing register-indexed LDA array,Y addressing directly on them.
|
||||
- DONE: passing STR to a subroutine: parameter type becomes ^^UBYTE (rather than UWORD) (we still lose the bounds check)
|
||||
- DONE: passing ARRAY to a subroutine: parameter type becomes ^^ElementDt (rather than UWORD) (we still lose the bounds check)
|
||||
- DONE: @(ptr) complains that ptr is not uword when ptr is ^^ubyte (should be allowed)
|
||||
- DONE: pointer[0] should be replaced with @(pointer) if pointer is ^^ubyte, so these are now all identical: ptr[0], ptr^^, @(ptr) if ptr is ^^ubyte
|
||||
- DONE: STR should be asssignment compatible with UBYTE^^ but local scoped STR should still be accessed directly using LDA str,Y instead of through the pointer, like arrays.
|
||||
- DONE: disallow ^^str
|
||||
- DONE: allow return ubyte/uword when pointer type is expected as return value type
|
||||
- DONE: fix _msb/_lsb storage of the split-words pointer-arrays
|
||||
- DONE: what about static initialization of an array of struct pointers? -> impossible right now because the pointer values are not constants.
|
||||
- DONE: make typeForAddressOf() be even more specific about the typed pointers it returns for the address-of operator.
|
||||
- DONE: existing '&' address-of still returns untyped uword (for backward compatibility). New '&&' operator returns typed pointer.
|
||||
- DONE: allow list1^^ = list2^^ (value wise assignment of List structures) by replacing it with a sys.memcopy(list2, list1, sizeof(List)) call.
|
||||
- DONE: allow a.b.ptr[i].value (equiv to a.b.ptr[i]^^.value) expressions (assignment target doesn't parse yet, see below)
|
||||
- DONE: check passing arrays to typed ptr sub-parameters. NOTE: word array can only be a @nosplit array if the parameter type is ^^word, because the words need to be sequential in memory there
|
||||
- DONE: allow str assign to ^^ubyte without cast (take address)
|
||||
- DONE: added peekbool() and pokebool() and pokebowl() boolean peek and poke, the latter is equivalent to pokebool()
|
||||
- DONE: fixed support for (expression) array index dereferencing "array[2]^^" where array contains pointers to primitives: replace with peek()
|
||||
- DONE: fixed support for (assigntarget) array index dereferencing "array[2]^^" where array contains pointers to primitives: replace with poke()
|
||||
- DONE: replace str or ubyte[] param and returnvalue type into ^^ubyte rather than uword
|
||||
- DONE: allow sizeof(^^type) to return the size of a pointer
|
||||
|
||||
- TODO: allow initializing a pointer array with initialized structs: ^^Node[] nodes = [ Node(), Node(), Node() ] (fix missing variable error) (update sorting example)
|
||||
|
||||
- try to add support for array index dereferencing as assign target "array[2]^^.value = 99" where array is struct pointers (currently a 'no support' error)
|
||||
- try to add support for array index dereferencing as assign target "array[2].value = 99" where array is struct pointers (currently a parser error)
|
||||
- try to fix parse error l1^^.s[0] = 4242 (equivalent to l1.s[0]=4242 , which does parse correctly)
|
||||
- perhaps add ?. null-propagation operator (for expression and assignment)?
|
||||
- 6502 codegen: make all/most of the TestPointers unit tests also run on 6502 target
|
||||
- 6502 codegen: remove checks in checkForPointerTypesOn6502()
|
||||
- 6502 codegen should warn about writing to initialized struct instances when using romable code, like with arrays "can only be used as read-only in ROMable code"
|
||||
- 6502 asm symbol name prefixing should work for dereferences too.
|
||||
- 6502 statementreorderer: fix todo for str -> ^^ubyte instead of uword
|
||||
- update structpointers.rst docs with 6502 specific things?
|
||||
- scan through 6502 library modules to change untyped uword pointers to typed pointers
|
||||
- scan through 6502 examples to change untyped uword pointers to typed pointers
|
||||
- support for typed function pointers? (&routine could be typed by default as well then)
|
||||
- support @nosplit pointer arrays?
|
||||
- support pointer to pointer?
|
||||
- really fixing the pointer dereferencing issues (cursed hybrid beween IdentifierReference, PtrDereferece and PtrIndexedDereference) may require getting rid of scoped identifiers altogether and treat '.' as a "scope or pointer following operator"
|
||||
- (later, nasty parser problem:) support chaining pointer dereference on function calls that return a pointer. (type checking now fails on stuff like func().field and func().next.field)
|
||||
|
||||
|
||||
Future Things and Ideas
|
||||
^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
- %breakpoint after an assignment is parsed as part of the expression (x % breakpoint), that should not happen
|
||||
- when a complete block is removed because unused, suppress all info messages about everything in the block being removed
|
||||
- fix the line, cols in Position, sometimes they count from 0 sometimes from 1
|
||||
- is "checkAssignmentCompatible" redundant (gets called just 1 time!) when we also have "checkValueTypeAndRange" ?
|
||||
- enums?
|
||||
- romable: should we have a way to explicitly set the memory address for the BSS area (add a -varsaddress and -slabsaddress options?)
|
||||
- romable: fix remaining codegens (some for loops, see ForLoopsAsmGen)
|
||||
- Kotlin: can we use inline value classes in certain spots?
|
||||
- add float support to the configurable compiler targets
|
||||
- Improve the SublimeText syntax file for prog8, you can also install this for 'bat': https://github.com/sharkdp/bat?tab=readme-ov-file#adding-new-syntaxes--language-definitions
|
||||
- Change scoping rules for qualified symbols so that they don't always start from the root but behave like other programming languages (look in local scope first), maybe only when qualified symbol starts with '.' such as: .local.value = 33
|
||||
- something to reduce the need to use fully qualified names all the time. 'with' ? Or 'using <prefix>'?
|
||||
- Improve register load order in subroutine call args assignments:
|
||||
in certain situations (need examples!), the "wrong" order of evaluation of function call arguments is done which results
|
||||
in overwriting registers that already got their value, which requires a lot of stack juggling (especially on plain 6502 cpu!)
|
||||
Maybe this routine can be made more intelligent. See usesOtherRegistersWhileEvaluating() and argumentsViaRegisters().
|
||||
- Does it make codegen easier if everything is an expression? Start with the PtProgram ast classes, change statements to expressions that have (new) VOID data type
|
||||
- Can we support signed % (remainder) somehow?
|
||||
- Multidimensional arrays and chained indexing, purely as syntactic sugar over regular arrays. Probaby only useful once we have typed pointers. (addressed in 'struct' branch)
|
||||
- make a form of "manual generics" possible like: varsub routine(T arg)->T where T is expanded to a specific type
|
||||
(this is already done hardcoded for several of the builtin functions)
|
||||
- [much work:] more support for (64tass) SEGMENTS in the prog8 syntax itself?
|
||||
- ability to use a sub instead of only a var for @bank ? what for though? dynamic bank/overlay loading?
|
||||
- Zig-like try-based error handling where the V flag could indicate error condition? and/or BRK to jump into monitor on failure? (has to set BRK vector for that) But the V flag is also set on certain normal instructions
|
||||
|
||||
|
||||
IR/VM
|
||||
-----
|
||||
- possible to use LOADFIELD/STOREFIELD instructions more?
|
||||
- pointer dt's are all reduced to just an uword (in the irTypeString method) - is this okay or could it be beneficial to reintroduce the actual pointer type information? See commit 88b074c208450c58aa32469745afa03e4c5f564a
|
||||
- change the instruction format so an indirect register (a pointer) can be used more often, at least for the inplace assignment operators that operate on pointer
|
||||
- getting it in shape for code generation...: the IR file should be able to encode every detail about a prog8 program (the VM doesn't have to actually be able to run all of it though!)
|
||||
- fix call() return value handling (... what's wrong with it again?)
|
||||
- encode asmsub/extsub clobber info in the call , or maybe include these definitions in the p8ir file itself too. (return registers are already encoded in the CALL instruction)
|
||||
- proper code gen for the CALLI instruction and that it (optionally) returns a word value that needs to be assigned to a reg
|
||||
- implement fast code paths for TODO("inplace split....
|
||||
- implement more TODOs in AssignmentGen
|
||||
- sometimes source lines end up missing in the output p8ir, for example the first assignment is gone in:
|
||||
sub start() {
|
||||
cx16.r0L = cx16.r1 as ubyte
|
||||
cx16.r0sL = cx16.r1s as byte }
|
||||
- do something with the 'split' tag on split word arrays
|
||||
- add more optimizations in IRPeepholeOptimizer
|
||||
- apparently for SSA form, the IRCodeChunk is not a proper "basic block" yet because the last operation should be a branch or return, and no other branches
|
||||
- reduce register usage via linear-scan algorithm (based on live intervals) https://anoopsarkar.github.io/compilers-class/assets/lectures/opt3-regalloc-linearscan.pdf
|
||||
don't forget to take into account the data type of the register when it's going to be reused!
|
||||
- idea: (but LLVM IR simply keeps the variables, so not a good idea then?...): replace all scalar variables by an allocated register. Keep a table of the variable to register mapping (including the datatype)
|
||||
global initialization values are simply a list of LOAD instructions.
|
||||
Variables replaced include all subroutine parameters! So the only variables that remain as variables are arrays and strings.
|
||||
- the @split arrays are currently also split in _lsb/_msb arrays in the IR, and operations take multiple (byte) instructions that may lead to verbose and slow operation and machine code generation down the line.
|
||||
maybe another representation is needed once actual codegeneration is done from the IR...?
|
||||
- ExpressionCodeResult: get rid of the separation between single result register and multiple result registers? maybe not, this requires hundreds of lines to change
|
||||
|
||||
|
||||
Libraries
|
||||
---------
|
||||
- Add split-word array sorting routines to sorting module?
|
||||
- See if the raster interrupt handler on the C64 can be tweaked to be a more stable raster irq
|
||||
- pet32 target: make syslib more complete (missing kernal routines)?
|
||||
- need help with: PET disk routines (OPEN, SETLFS etc are not exposed as kernal calls)
|
||||
- c128 target: make syslib more complete (missing kernal routines)?
|
||||
|
||||
|
||||
Optimizations
|
||||
-------------
|
||||
|
||||
- Port benchmarks from https://thred.github.io/c-bench-64/ to prog8 and see how it stacks up.
|
||||
- Since fixing the missing zp-var initialization, programs grew in size again because STZ's reappered. Can we add more intelligent (and correct!) optimizations to remove those STZs that might be redundant again?
|
||||
- in Identifier: use typedarray of strings instead of listOf? Other places?
|
||||
- Compilation speed: try to join multiple modifications in 1 result in the AST processors instead of returning it straight away every time
|
||||
- Compare output of some Oscar64 samples to what prog8 does for the equivalent code (see https://github.com/drmortalwombat/OscarTutorials/tree/main and https://github.com/drmortalwombat/oscar64/tree/main/samples)
|
||||
- Optimize the IfExpression code generation to be more like regular if-else code. (both 6502 and IR) search for "TODO don't store condition as expression"
|
||||
- VariableAllocator: can we think of a smarter strategy for allocating variables into zeropage, rather than first-come-first-served?
|
||||
for instance, vars used inside loops first, then loopvars, then uwords used as pointers (or these first??), then the rest
|
||||
- various optimizers skip stuff if compTarget.name==VMTarget.NAME. Once 6502-codegen is done from IR code, those checks should probably be removed, or be made permanent
|
||||
|
Reference in New Issue
Block a user