Compare commits

...

6 Commits

Author SHA1 Message Date
Irmen de Jong
231b50dacb optimize certain word multiplication with bit shifting if it's a power of 2 2025-09-18 20:57:26 +02:00
Irmen de Jong
a71895cbe8 optimize pointer.field += 1 into pointer.field INC/DEC 2025-09-18 19:27:36 +02:00
Irmen de Jong
a8bede17b2 fix defer() with the arena allocator ("return values are evaluated before the defer is executed") 2025-09-17 23:59:32 +02:00
Irmen de Jong
f6c8e693a5 add offsetof() 2025-09-17 23:30:15 +02:00
Irmen de Jong
9461e4088c fixed the hashtable example and workarounds for misbehaving defer in allocators 2025-09-17 22:53:01 +02:00
Irmen de Jong
efd73fd10d implement some more in place pointer operators 2025-09-17 18:19:03 +02:00
19 changed files with 671 additions and 189 deletions

View File

@@ -103,6 +103,7 @@ val BuiltinFunctions: Map<String, FSignature> = mapOf(
"abs__float" to FSignature(true, BaseDataType.FLOAT, FParam("value", BaseDataType.FLOAT)), "abs__float" to FSignature(true, BaseDataType.FLOAT, FParam("value", BaseDataType.FLOAT)),
"len" to FSignature(true, BaseDataType.UWORD, FParam("values", *IterableDatatypes)), "len" to FSignature(true, BaseDataType.UWORD, FParam("values", *IterableDatatypes)),
"sizeof" to FSignature(true, BaseDataType.UBYTE, FParam("object", *(BaseDataType.entries - BaseDataType.STRUCT_INSTANCE).toTypedArray())), "sizeof" to FSignature(true, BaseDataType.UBYTE, FParam("object", *(BaseDataType.entries - BaseDataType.STRUCT_INSTANCE).toTypedArray())),
"offsetof" to FSignature(true, BaseDataType.UBYTE, FParam("field", BaseDataType.UBYTE)),
"sgn" to FSignature(true, BaseDataType.BYTE, FParam("value", *NumericDatatypes)), "sgn" to FSignature(true, BaseDataType.BYTE, FParam("value", *NumericDatatypes)),
"sqrt" to FSignature(true, null, FParam("value", *NumericDatatypes)), "sqrt" to FSignature(true, null, FParam("value", *NumericDatatypes)),
"sqrt__ubyte" to FSignature(true, BaseDataType.UBYTE, FParam("value", BaseDataType.UBYTE)), "sqrt__ubyte" to FSignature(true, BaseDataType.UBYTE, FParam("value", BaseDataType.UBYTE)),

View File

@@ -5,6 +5,7 @@ import prog8.code.ast.*
import prog8.code.core.* import prog8.code.core.*
import prog8.codegen.cpu6502.AsmGen6502Internal import prog8.codegen.cpu6502.AsmGen6502Internal
import prog8.codegen.cpu6502.VariableAllocator import prog8.codegen.cpu6502.VariableAllocator
import kotlin.math.log2
internal class AssignmentAsmGen( internal class AssignmentAsmGen(
@@ -1145,6 +1146,14 @@ internal class AssignmentAsmGen(
assignExpressionToRegister(expr.left, RegisterOrPair.A, expr.type.isSigned) assignExpressionToRegister(expr.left, RegisterOrPair.A, expr.type.isSigned)
if (value in asmgen.optimizedByteMultiplications) if (value in asmgen.optimizedByteMultiplications)
asmgen.out(" jsr prog8_math.mul_byte_${value}") asmgen.out(" jsr prog8_math.mul_byte_${value}")
else if(value in powersOfTwoInt) {
val shifts = log2(value.toDouble()).toInt()
if(shifts>=8) {
asmgen.out(" lda #0")
} else {
repeat(shifts) { asmgen.out(" asl a") }
}
}
else else
asmgen.out(" ldy #$value | jsr prog8_math.multiply_bytes") asmgen.out(" ldy #$value | jsr prog8_math.multiply_bytes")
assignRegisterByte(target, CpuRegister.A, false, true) assignRegisterByte(target, CpuRegister.A, false, true)
@@ -1155,7 +1164,26 @@ internal class AssignmentAsmGen(
assignExpressionToRegister(expr.left, RegisterOrPair.AY, expr.type.isSigned) assignExpressionToRegister(expr.left, RegisterOrPair.AY, expr.type.isSigned)
asmgen.out(" jsr prog8_math.mul_word_${value}") asmgen.out(" jsr prog8_math.mul_word_${value}")
} }
else if(value in powersOfTwoInt) {
val shifts = log2(value.toDouble()).toInt()
if(shifts>=16) {
assignExpressionToRegister(expr.left, RegisterOrPair.AY, expr.type.isSigned)
asmgen.out(" lda #0 | ldy #0")
} else {
if(target.kind==TargetStorageKind.VARIABLE && target.datatype.isWord) {
assignExpressionToVariable(expr.left, target.asmVarname, target.datatype)
repeat(shifts) { asmgen.out(" asl ${target.asmVarname} | rol ${target.asmVarname}+1") }
return true
} else {
assignExpressionToRegister(expr.left, RegisterOrPair.AY, expr.type.isSigned)
asmgen.out(" sty P8ZP_SCRATCH_REG")
repeat(shifts) { asmgen.out(" asl a | rol P8ZP_SCRATCH_REG") }
asmgen.out(" ldy P8ZP_SCRATCH_REG")
}
}
}
else { else {
assignExpressionToRegister(expr.left, RegisterOrPair.AY, expr.type.isSigned)
if(expr.definingBlock()!!.options.veraFxMuls){ if(expr.definingBlock()!!.options.veraFxMuls){
// cx16 verafx hardware mul // cx16 verafx hardware mul
asmgen.assignWordOperandsToAYAndVar(expr.right, expr.left, "cx16.r1") asmgen.assignWordOperandsToAYAndVar(expr.right, expr.left, "cx16.r1")

View File

@@ -257,17 +257,27 @@ internal class PointerAssignmentsGen(private val asmgen: AsmGen6502Internal, pri
internal fun inplaceModification(target: PtrTarget, operator: String, value: AsmAssignSource) { internal fun inplaceModification(target: PtrTarget, operator: String, value: AsmAssignSource) {
when (operator) { when (operator) {
"+" -> { "+" -> {
if(target.dt.isByte) inplaceByteAdd(target, value) if(target.dt.isByte && value.number?.number==1.0 || value.number?.number==2.0) {
else if(target.dt.isWord) inplaceWordAdd(target, value) val amount = value.number.number.toInt()
else if(target.dt.isFloat) inplaceFloatAddOrMul(target, "FADD", value) inplaceByteInc(target, amount)
} else {
if (target.dt.isByte) inplaceByteAdd(target, value)
else if (target.dt.isWord) inplaceWordAdd(target, value)
else if (target.dt.isFloat) inplaceFloatAddOrMul(target, "FADD", value)
else throw AssemblyError("weird dt ${target.dt} ${target.position}") else throw AssemblyError("weird dt ${target.dt} ${target.position}")
} }
}
"-" -> { "-" -> {
if(target.dt.isByte) inplaceByteSub(target, value) if(target.dt.isByte && value.number?.number==1.0 || value.number?.number==2.0) {
else if(target.dt.isWord) inplaceWordSub(target, value) val amount = value.number.number.toInt()
else if(target.dt.isFloat) inplaceFloatSubOrDiv(target, "FSUB", value) inplaceByteDec(target, amount)
} else {
if (target.dt.isByte) inplaceByteSub(target, value)
else if (target.dt.isWord) inplaceWordSub(target, value)
else if (target.dt.isFloat) inplaceFloatSubOrDiv(target, "FSUB", value)
else throw AssemblyError("weird dt ${target.position}") else throw AssemblyError("weird dt ${target.position}")
} }
}
"*" -> { "*" -> {
if(target.dt.isByte) TODO("inplaceByteMul(target, value) ${target.position}") if(target.dt.isByte) TODO("inplaceByteMul(target, value) ${target.position}")
else if(target.dt.isWord) inplaceWordMul(target, value) else if(target.dt.isWord) inplaceWordMul(target, value)
@@ -282,22 +292,26 @@ internal class PointerAssignmentsGen(private val asmgen: AsmGen6502Internal, pri
} }
"%" -> TODO("inplace ptr %") "%" -> TODO("inplace ptr %")
"<<" -> { "<<" -> {
if(target.dt.isByte) TODO("inplaceByteShiftLeft(target, value) ${target.position}") if(target.dt.isByte) inplaceByteShiftLeft(target, value)
else if(target.dt.isWord) inplaceWordShiftLeft(target, value) else if(target.dt.isWord) inplaceWordShiftLeft(target, value)
else throw AssemblyError("weird dt ${target.position}") else throw AssemblyError("weird dt ${target.position}")
} }
">>" -> { ">>" -> {
if(target.dt.isByte) TODO("inplaceByteShiftRight(target, value) ${target.position}") if(target.dt.isByte) inplaceByteShiftRight(target, value)
else if(target.dt.isWord) inplaceWordShiftRight(target, value) else if(target.dt.isWord) inplaceWordShiftRight(target, value)
else throw AssemblyError("weird dt ${target.position}") else throw AssemblyError("weird dt ${target.position}")
} }
"&", "and" -> { "&", "and" -> {
// byte targets are handled as direct memory access, not a pointer operation anymore however boolean targets are still to be handled here // 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 &") if(target.dt.isByteOrBool) inplaceByteAnd(target, value)
else if(target.dt.isWord) inplaceWordAnd(target, value)
else throw AssemblyError("weird dt ${target.dt} ${target.position}")
} }
"|", "or" -> { "|", "or" -> {
// byte targets are handled as direct memory access, not a pointer operation anymore however boolean targets are still to be handled here // 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 |") if(target.dt.isByteOrBool) inplaceByteOr(target, value)
else if(target.dt.isWord) inplaceWordOr(target, value)
else throw AssemblyError("weird dt ${target.dt} ${target.position}")
} }
"^", "xor" -> { "^", "xor" -> {
// byte targets are handled as direct memory access, not a pointer operation anymore however boolean targets are still to be handled here // byte targets are handled as direct memory access, not a pointer operation anymore however boolean targets are still to be handled here
@@ -305,12 +319,6 @@ internal class PointerAssignmentsGen(private val asmgen: AsmGen6502Internal, pri
else if(target.dt.isWord) inplaceWordXor(target, value) else if(target.dt.isWord) inplaceWordXor(target, value)
else throw AssemblyError("weird dt ${target.dt} ${target.position}") 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") else -> throw AssemblyError("invalid operator for in-place modification $operator")
} }
} }
@@ -693,7 +701,7 @@ internal class PointerAssignmentsGen(private val asmgen: AsmGen6502Internal, pri
val (zpPtrVar, offset) = deref(target.pointer) val (zpPtrVar, offset) = deref(target.pointer)
if(target.dt.isSigned) if(target.dt.isSigned)
TODO("signed word shift rigth ${target.position} $value") TODO("signed word shift right ${target.position} $value")
fun shift1unsigned() { fun shift1unsigned() {
asmgen.out(""" asmgen.out("""
@@ -720,21 +728,92 @@ internal class PointerAssignmentsGen(private val asmgen: AsmGen6502Internal, pri
} }
} }
SourceStorageKind.VARIABLE -> { SourceStorageKind.VARIABLE -> {
require(value.datatype.isWord) require(value.datatype.isByte)
val varname = value.asmVarname val varname = value.asmVarname
TODO("<< variable") asmgen.out(" ldx $varname")
asmgen.out("-")
shift1unsigned()
asmgen.out(" dex | bne -")
} }
SourceStorageKind.EXPRESSION -> { SourceStorageKind.EXPRESSION -> {
require(value.datatype.isWord) require(value.datatype.isByte)
asmgen.assignExpressionToRegister(value.expression!!, RegisterOrPair.AX) asmgen.assignExpressionToRegister(value.expression!!, RegisterOrPair.X)
TODO("<< expression") asmgen.out("-")
shift1unsigned()
asmgen.out(" dex | bne -")
} }
SourceStorageKind.REGISTER -> { SourceStorageKind.REGISTER -> {
require(value.datatype.isWord) require(value.datatype.isByte)
val register = value.register!! val register = value.register!!
asmgen.assignRegister(register, AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UWORD, null, target.position, variableAsmName = "P8ZP_SCRATCH_PTR")) asmgen.assignRegister(register, AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, DataType.UBYTE, null, target.position, register = RegisterOrPair.X))
require(register.isWord()) asmgen.out("-")
TODO("<< register") shift1unsigned()
asmgen.out(" dex | bne -")
}
else -> throw AssemblyError("weird source value $value")
}
}
private fun inplaceByteShiftRight(target: PtrTarget, value: AsmAssignSource) {
val (zpPtrVar, offset) = deref(target.pointer)
if(target.dt.isSigned)
TODO("signed byte shift right ${target.position} $value")
when(value.kind) {
SourceStorageKind.LITERALNUMBER -> {
val number = value.number!!.number.toInt()
if(number==1) {
asmgen.out("""
ldy #$offset
lda ($zpPtrVar),y
lsr a
sta ($zpPtrVar),y""")
} else if(number>1) {
asmgen.out("""
ldx #$number
ldy #$offset
lda ($zpPtrVar),y
- lsr a
dex
bne -
sta ($zpPtrVar),y""")
}
}
SourceStorageKind.VARIABLE -> {
require(value.datatype.isByte)
val varname = value.asmVarname
asmgen.out("""
ldx $varname
ldy #$offset
lda ($zpPtrVar),y
- lsr a
dex
bne -
sta ($zpPtrVar),y""")
}
SourceStorageKind.EXPRESSION -> {
require(value.datatype.isByte)
asmgen.assignExpressionToRegister(value.expression!!, RegisterOrPair.X)
asmgen.out("""
ldy #$offset
lda ($zpPtrVar),y
- lsr a
dex
bne -
sta ($zpPtrVar),y""")
}
SourceStorageKind.REGISTER -> {
require(value.datatype.isByte)
val register = value.register!!
asmgen.assignRegister(register, AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, DataType.UWORD, null, target.position, register = RegisterOrPair.X))
asmgen.out("""
ldy #$offset
lda ($zpPtrVar),y
- lsr a
dex
bne -
sta ($zpPtrVar),y""")
} }
else -> throw AssemblyError("weird source value $value") else -> throw AssemblyError("weird source value $value")
} }
@@ -768,21 +847,90 @@ internal class PointerAssignmentsGen(private val asmgen: AsmGen6502Internal, pri
} }
} }
SourceStorageKind.VARIABLE -> { SourceStorageKind.VARIABLE -> {
require(value.datatype.isWord) require(value.datatype.isByte)
val varname = value.asmVarname val varname = value.asmVarname
TODO("<< variable") asmgen.out(" ldx $varname")
asmgen.out("-")
shift1()
asmgen.out(" dex | bne -")
} }
SourceStorageKind.EXPRESSION -> { SourceStorageKind.EXPRESSION -> {
require(value.datatype.isWord) require(value.datatype.isByte)
asmgen.assignExpressionToRegister(value.expression!!, RegisterOrPair.AX) asmgen.assignExpressionToRegister(value.expression!!, RegisterOrPair.X)
TODO("<< expression") asmgen.out("-")
shift1()
asmgen.out(" dex | bne -")
} }
SourceStorageKind.REGISTER -> { SourceStorageKind.REGISTER -> {
require(value.datatype.isWord) require(value.datatype.isByte)
val register = value.register!! val register = value.register!!
asmgen.assignRegister(register, AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UWORD, null, target.position, variableAsmName = "P8ZP_SCRATCH_PTR")) asmgen.assignRegister(register, AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, DataType.UBYTE, null, target.position, register = RegisterOrPair.X))
require(register.isWord()) asmgen.out("-")
TODO("<< register") shift1()
asmgen.out(" dex | bne -")
}
else -> throw AssemblyError("weird source value $value")
}
}
private fun inplaceByteShiftLeft(target: PtrTarget, value: AsmAssignSource) {
val (zpPtrVar, offset) = deref(target.pointer)
when(value.kind) {
SourceStorageKind.LITERALNUMBER -> {
val number = value.number!!.number.toInt()
if(number==1) {
asmgen.out("""
ldy #$offset
lda ($zpPtrVar),y
asl a
sta ($zpPtrVar),y""")
} else if(number>1) {
asmgen.out("""
ldx #$number
ldy #$offset
lda ($zpPtrVar),y
- asl a
dex
bne -
sta ($zpPtrVar),y""")
}
}
SourceStorageKind.VARIABLE -> {
require(value.datatype.isByte)
val varname = value.asmVarname
asmgen.out("""
ldx $varname
ldy #$offset
lda ($zpPtrVar),y
- asl a
dex
bne -
sta ($zpPtrVar),y""")
}
SourceStorageKind.EXPRESSION -> {
require(value.datatype.isByte)
asmgen.assignExpressionToRegister(value.expression!!, RegisterOrPair.X)
asmgen.out("""
ldy #$offset
lda ($zpPtrVar),y
- asl a
dex
bne -
sta ($zpPtrVar),y""")
}
SourceStorageKind.REGISTER -> {
require(value.datatype.isByte)
val register = value.register!!
asmgen.assignRegister(register, AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, DataType.UBYTE, null, target.position, register = RegisterOrPair.X))
asmgen.out("-")
asmgen.out("""
ldy #$offset
lda ($zpPtrVar),y
- asl a
dex
bne -
sta ($zpPtrVar),y""")
} }
else -> throw AssemblyError("weird source value $value") else -> throw AssemblyError("weird source value $value")
} }
@@ -1046,6 +1194,46 @@ internal class PointerAssignmentsGen(private val asmgen: AsmGen6502Internal, pri
} }
} }
private fun inplaceByteInc(target: PtrTarget, amount: Int) {
require(amount==1 || amount==2)
val (zpPtrVar, offset) = deref(target.pointer)
if(offset==0.toUByte() && asmgen.isTargetCpu(CpuType.CPU65C02)) {
asmgen.out(" lda ($zpPtrVar)")
repeat(amount) {
asmgen.out(" inc a")
}
asmgen.out(" sta ($zpPtrVar)")
}
else {
asmgen.out("""
ldy #$offset
lda ($zpPtrVar),y
clc
adc #$amount
sta ($zpPtrVar),y""")
}
}
private fun inplaceByteDec(target: PtrTarget, amount: Int) {
require(amount==1 || amount==2)
val (zpPtrVar, offset) = deref(target.pointer)
if(offset==0.toUByte() && asmgen.isTargetCpu(CpuType.CPU65C02)) {
asmgen.out(" lda ($zpPtrVar)")
repeat(amount) {
asmgen.out(" dec a")
}
asmgen.out(" sta ($zpPtrVar)")
}
else {
asmgen.out("""
ldy #$offset
lda ($zpPtrVar),y
sec
sbc #$amount
sta ($zpPtrVar),y""")
}
}
private fun inplaceByteAdd(target: PtrTarget, value: AsmAssignSource) { private fun inplaceByteAdd(target: PtrTarget, value: AsmAssignSource) {
val (zpPtrVar, offset) = deref(target.pointer) val (zpPtrVar, offset) = deref(target.pointer)
when(value.kind) { when(value.kind) {
@@ -1230,46 +1418,220 @@ internal class PointerAssignmentsGen(private val asmgen: AsmGen6502Internal, pri
} }
} }
fun assignIndexedPointer(target: AsmAssignTarget, arrayVarName: String, index: PtExpression, arrayDt: DataType) { private fun inplaceByteOr(target: PtrTarget, value: AsmAssignSource) {
TODO("assign indexed pointer from array $arrayVarName at ${target.position}") val (zpPtrVar, offset) = deref(target.pointer)
val ptrZp = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UWORD, target.scope, target.position, variableAsmName="P8ZP_SCRATCH_PTR") when(value.kind) {
assignAddressOfIndexedPointer(ptrZp, arrayVarName, arrayDt, index) SourceStorageKind.LITERALNUMBER -> {
when { val number = value.number!!.number.toInt()
target.datatype.isByteOrBool -> { if(offset==0.toUByte() && asmgen.isTargetCpu(CpuType.CPU65C02))
asmgen.out(""" asmgen.out("""
ldy #0 lda ($zpPtrVar)
lda (P8ZP_SCRATCH_PTR),y""") ora #$number
asmgen.assignRegister(RegisterOrPair.A, target) sta ($zpPtrVar)""")
}
target.datatype.isWord || target.datatype.isPointer -> {
if(asmgen.isTargetCpu(CpuType.CPU65C02))
asmgen.out("""
ldy #1
lda (P8ZP_SCRATCH_PTR),y
tax
lda (P8ZP_SCRATCH_PTR)""")
else else
asmgen.out(""" asmgen.out("""
ldy #1 ldy #$offset
lda (P8ZP_SCRATCH_PTR),y lda ($zpPtrVar),y
tax ora #$number
dey sta ($zpPtrVar),y""")
lda (P8ZP_SCRATCH_PTR),y""")
asmgen.assignRegister(RegisterOrPair.AX, target)
} }
target.datatype.isLong -> { SourceStorageKind.VARIABLE -> {
TODO("assign long from pointer to $target ${target.position}") val varname = value.asmVarname
} if(offset==0.toUByte() && asmgen.isTargetCpu(CpuType.CPU65C02))
target.datatype.isFloat -> {
// TODO optimize the float copying to avoid having to go through FAC1
asmgen.out(""" asmgen.out("""
lda P8ZP_SCRATCH_PTR lda ($zpPtrVar)
ldy P8ZP_SCRATCH_PTR+1 ora $varname
jsr floats.MOVFM""") sta ($zpPtrVar)""")
asmgen.assignRegister(RegisterOrPair.FAC1, target) else
asmgen.out("""
ldy #$offset
lda ($zpPtrVar),y
ora $varname
sta ($zpPtrVar),y""")
} }
else -> throw AssemblyError("weird dt ${target.datatype}") SourceStorageKind.EXPRESSION -> {
asmgen.assignExpressionToRegister(value.expression!!, RegisterOrPair.A)
asmgen.out("""
ldy #$offset
ora ($zpPtrVar),y
sta ($zpPtrVar),y""")
} }
SourceStorageKind.REGISTER -> TODO("register | byte")
else -> throw AssemblyError("weird source value $value")
}
}
private fun inplaceWordOr(target: PtrTarget, value: AsmAssignSource) {
val (zpPtrVar, offset) = deref(target.pointer)
when(value.kind) {
SourceStorageKind.LITERALNUMBER -> {
val number = value.number!!.number.toInt()
asmgen.out("""
ldy #$offset
lda ($zpPtrVar),y
ora #<$number
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
ora #>$number
sta ($zpPtrVar),y""")
}
SourceStorageKind.VARIABLE -> {
require(value.datatype.isWord)
val varname = value.asmVarname
asmgen.out("""
ldy #$offset
lda ($zpPtrVar),y
ora $varname
sta ($zpPtrVar),y
lda ($zpPtrVar),y
ora $varname+1
sta ($zpPtrVar),y""")
}
SourceStorageKind.EXPRESSION -> {
require(value.datatype.isWord)
asmgen.assignExpressionToRegister(value.expression!!, RegisterOrPair.AX)
asmgen.out("""
ldy #$offset
ora ($zpPtrVar),y
sta ($zpPtrVar),y
iny
txa
ora ($zpPtrVar),y
sta ($zpPtrVar),y""")
}
SourceStorageKind.REGISTER -> TODO("register | word")
else -> throw AssemblyError("weird source value $value")
}
}
private fun inplaceByteAnd(target: PtrTarget, value: AsmAssignSource) {
val (zpPtrVar, offset) = deref(target.pointer)
when(value.kind) {
SourceStorageKind.LITERALNUMBER -> {
val number = value.number!!.number.toInt()
if(offset==0.toUByte() && asmgen.isTargetCpu(CpuType.CPU65C02))
asmgen.out("""
lda ($zpPtrVar)
and #$number
sta ($zpPtrVar)""")
else
asmgen.out("""
ldy #$offset
lda ($zpPtrVar),y
and #$number
sta ($zpPtrVar),y""")
}
SourceStorageKind.VARIABLE -> {
val varname = value.asmVarname
if(offset==0.toUByte() && asmgen.isTargetCpu(CpuType.CPU65C02))
asmgen.out("""
lda ($zpPtrVar)
and $varname
sta ($zpPtrVar)""")
else
asmgen.out("""
ldy #$offset
lda ($zpPtrVar),y
and $varname
sta ($zpPtrVar),y""")
}
SourceStorageKind.EXPRESSION -> {
asmgen.assignExpressionToRegister(value.expression!!, RegisterOrPair.A)
asmgen.out("""
ldy #$offset
and ($zpPtrVar),y
sta ($zpPtrVar),y""")
}
SourceStorageKind.REGISTER -> TODO("register & byte")
else -> throw AssemblyError("weird source value $value")
}
}
private fun inplaceWordAnd(target: PtrTarget, value: AsmAssignSource) {
val (zpPtrVar, offset) = deref(target.pointer)
when(value.kind) {
SourceStorageKind.LITERALNUMBER -> {
val number = value.number!!.number.toInt()
asmgen.out("""
ldy #$offset
lda ($zpPtrVar),y
and #<$number
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
and #>$number
sta ($zpPtrVar),y""")
}
SourceStorageKind.VARIABLE -> {
require(value.datatype.isWord)
val varname = value.asmVarname
asmgen.out("""
ldy #$offset
lda ($zpPtrVar),y
and $varname
sta ($zpPtrVar),y
lda ($zpPtrVar),y
and $varname+1
sta ($zpPtrVar),y""")
}
SourceStorageKind.EXPRESSION -> {
require(value.datatype.isWord)
asmgen.assignExpressionToRegister(value.expression!!, RegisterOrPair.AX)
asmgen.out("""
ldy #$offset
and ($zpPtrVar),y
sta ($zpPtrVar),y
iny
txa
and ($zpPtrVar),y
sta ($zpPtrVar),y""")
}
SourceStorageKind.REGISTER -> TODO("register & word")
else -> throw AssemblyError("weird source value $value")
}
}
fun assignIndexedPointer(target: AsmAssignTarget, arrayVarName: String, index: PtExpression, arrayDt: DataType) {
TODO("assign indexed pointer from array $arrayVarName at ${target.position}")
// val ptrZp = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UWORD, target.scope, target.position, variableAsmName="P8ZP_SCRATCH_PTR")
// assignAddressOfIndexedPointer(ptrZp, arrayVarName, arrayDt, index)
// when {
// target.datatype.isByteOrBool -> {
// asmgen.out("""
// ldy #0
// lda (P8ZP_SCRATCH_PTR),y""")
// asmgen.assignRegister(RegisterOrPair.A, target)
// }
// target.datatype.isWord || target.datatype.isPointer -> {
// if(asmgen.isTargetCpu(CpuType.CPU65C02))
// asmgen.out("""
// ldy #1
// lda (P8ZP_SCRATCH_PTR),y
// tax
// lda (P8ZP_SCRATCH_PTR)""")
// else
// asmgen.out("""
// ldy #1
// lda (P8ZP_SCRATCH_PTR),y
// tax
// dey
// lda (P8ZP_SCRATCH_PTR),y""")
// asmgen.assignRegister(RegisterOrPair.AX, target)
// }
// target.datatype.isLong -> {
// TODO("assign long from pointer to $target ${target.position}")
// }
// target.datatype.isFloat -> {
// // TODO optimize the float copying to avoid having to go through FAC1
// asmgen.out("""
// lda P8ZP_SCRATCH_PTR
// ldy P8ZP_SCRATCH_PTR+1
// jsr floats.MOVFM""")
// asmgen.assignRegister(RegisterOrPair.FAC1, target)
// }
// else -> throw AssemblyError("weird dt ${target.datatype}")
// }
} }
private fun saveOnStack(regs: RegisterOrPair) { private fun saveOnStack(regs: RegisterOrPair) {

View File

@@ -137,17 +137,28 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
val inplaceInstrs = mutableListOf<IRCodeChunkBase>() val inplaceInstrs = mutableListOf<IRCodeChunkBase>()
val (addressReg, fieldOffset) = codeGen.evaluatePointerAddressIntoReg(inplaceInstrs, pointerDeref) val (addressReg, fieldOffset) = codeGen.evaluatePointerAddressIntoReg(inplaceInstrs, pointerDeref)
val oldvalueReg = codeGen.registers.next(targetDt) val oldvalueReg = codeGen.registers.next(targetDt)
if((augAssign.operator=="+=" || augAssign.operator=="-=") && value.asConstInteger()==1 || value.asConstInteger()==2) {
// INC/DEC optimization instead of ADD/SUB
loadfield(inplaceInstrs, addressReg, fieldOffset, targetDt, oldvalueReg)
val instr = if(augAssign.operator=="+=") Opcode.INC else Opcode.DEC
repeat(value.asConstInteger()!!) {
addInstr(inplaceInstrs, IRInstruction(instr, targetDt, reg1 = oldvalueReg), null)
}
} else {
var operandTr = ExpressionCodeResult(emptyList(), IRDataType.BYTE, -1, -1) var operandTr = ExpressionCodeResult(emptyList(), IRDataType.BYTE, -1, -1)
if(augAssign.operator!="or=" && augAssign.operator!="and=") { if(augAssign.operator!="or=" && augAssign.operator!="and=") {
// for everything except the shortcircuit boolean operators, we can evaluate the value here unconditionally // for everything except the shortcircuit boolean operators, we can evaluate the value here unconditionally
operandTr = expressionEval.translateExpression(value) operandTr = expressionEval.translateExpression(value)
inplaceInstrs += operandTr.chunks // note: the instructions to load the value will be placed after the LOADFIELD instruction so that later optimizations about what modification is actually done, are easier
} }
if(targetDt== IRDataType.FLOAT) { if(targetDt== IRDataType.FLOAT) {
if(fieldOffset>0u)
addInstr(inplaceInstrs, IRInstruction(Opcode.LOADFIELD, targetDt, fpReg1 = oldvalueReg, reg1 = addressReg, immediate = fieldOffset.toInt()), null) loadfield(inplaceInstrs, addressReg, fieldOffset, targetDt, oldvalueReg)
else inplaceInstrs += operandTr.chunks
addInstr(inplaceInstrs, IRInstruction(Opcode.LOADI, targetDt, fpReg1 = oldvalueReg, reg1 = addressReg), null)
when(augAssign.operator) { when(augAssign.operator) {
"+=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.ADDR, targetDt, fpReg1 = oldvalueReg, fpReg2 = operandTr.resultFpReg), null) "+=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.ADDR, targetDt, fpReg1 = oldvalueReg, fpReg2 = operandTr.resultFpReg), null)
"-=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.SUBR, targetDt, fpReg1 = oldvalueReg, fpReg2 = operandTr.resultFpReg), null) "-=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.SUBR, targetDt, fpReg1 = oldvalueReg, fpReg2 = operandTr.resultFpReg), null)
@@ -157,11 +168,11 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
"+" -> { /* inplace + is a no-op */ } "+" -> { /* inplace + is a no-op */ }
else -> throw AssemblyError("invalid augmented assign operator for floats ${augAssign.operator}") else -> throw AssemblyError("invalid augmented assign operator for floats ${augAssign.operator}")
} }
} else { } else {
if(fieldOffset>0u)
addInstr(inplaceInstrs, IRInstruction(Opcode.LOADFIELD, targetDt, reg1 = oldvalueReg, reg2 = addressReg, immediate = fieldOffset.toInt()), null) loadfield(inplaceInstrs, addressReg, fieldOffset, targetDt, oldvalueReg)
else inplaceInstrs += operandTr.chunks
addInstr(inplaceInstrs, IRInstruction(Opcode.LOADI, targetDt, reg1 = oldvalueReg, reg2 = addressReg), null)
when(augAssign.operator) { when(augAssign.operator) {
"+=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.ADDR, targetDt, reg1 = oldvalueReg, reg2 = operandTr.resultReg), null) "+=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.ADDR, targetDt, reg1 = oldvalueReg, reg2 = operandTr.resultReg), null)
"-=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.SUBR, targetDt, reg1 = oldvalueReg, reg2 = operandTr.resultReg), null) "-=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.SUBR, targetDt, reg1 = oldvalueReg, reg2 = operandTr.resultReg), null)
@@ -196,6 +207,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
else -> throw AssemblyError("invalid augmented assign operator ${augAssign.operator}") else -> throw AssemblyError("invalid augmented assign operator ${augAssign.operator}")
} }
} }
}
codeGen.storeValueAtPointersLocation(inplaceInstrs, addressReg, fieldOffset, pointerDeref.type, false, oldvalueReg) codeGen.storeValueAtPointersLocation(inplaceInstrs, addressReg, fieldOffset, pointerDeref.type, false, oldvalueReg)
chunks = inplaceInstrs chunks = inplaceInstrs
@@ -222,6 +234,42 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
return chunks return chunks
} }
private fun loadfield(
inplaceInstrs: MutableList<IRCodeChunkBase>,
addressReg: Int,
fieldOffset: UByte,
targetDt: IRDataType,
oldvalueReg: Int
) {
if (targetDt == IRDataType.FLOAT) {
if (fieldOffset > 0u)
addInstr(
inplaceInstrs,
IRInstruction(Opcode.LOADFIELD, targetDt, fpReg1 = oldvalueReg, reg1 = addressReg, immediate = fieldOffset.toInt()),
null
)
else
addInstr(
inplaceInstrs,
IRInstruction(Opcode.LOADI, targetDt, fpReg1 = oldvalueReg, reg1 = addressReg),
null
)
} else {
if (fieldOffset > 0u)
addInstr(
inplaceInstrs,
IRInstruction(Opcode.LOADFIELD, targetDt, reg1 = oldvalueReg, reg2 = addressReg, immediate = fieldOffset.toInt()),
null
)
else
addInstr(
inplaceInstrs,
IRInstruction(Opcode.LOADI, targetDt, reg1 = oldvalueReg, reg2 = addressReg),
null
)
}
}
private fun fallbackAssign(origAssign: PtAugmentedAssign): IRCodeChunks { private fun fallbackAssign(origAssign: PtAugmentedAssign): IRCodeChunks {
val value: PtExpression val value: PtExpression
if(origAssign.operator in PrefixOperators) { if(origAssign.operator in PrefixOperators) {

View File

@@ -54,6 +54,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
"prog8_lib_square_word" -> funcSquare(call, IRDataType.WORD) "prog8_lib_square_word" -> funcSquare(call, IRDataType.WORD)
"prog8_lib_structalloc" -> funcStructAlloc(call) "prog8_lib_structalloc" -> funcStructAlloc(call)
"sizeof" -> throw AssemblyError("sizeof must have been replaced with a constant") "sizeof" -> throw AssemblyError("sizeof must have been replaced with a constant")
"offsetof" -> throw AssemblyError("offsetof must have been replaced with a constant")
else -> throw AssemblyError("missing builtinfunc for ${call.name}") else -> throw AssemblyError("missing builtinfunc for ${call.name}")
} }
} }

View File

@@ -5,6 +5,7 @@ import prog8.ast.FatalAstException
import prog8.ast.Program import prog8.ast.Program
import prog8.ast.SyntaxError import prog8.ast.SyntaxError
import prog8.ast.expressions.* import prog8.ast.expressions.*
import prog8.ast.statements.StructDecl
import prog8.ast.statements.VarDecl import prog8.ast.statements.VarDecl
import prog8.code.core.* import prog8.code.core.*
import kotlin.math.* import kotlin.math.*
@@ -15,6 +16,7 @@ internal val constEvaluatorsForBuiltinFuncs: Map<String, ConstExpressionCaller>
"abs" to ::builtinAbs, "abs" to ::builtinAbs,
"len" to ::builtinLen, "len" to ::builtinLen,
"sizeof" to ::builtinSizeof, "sizeof" to ::builtinSizeof,
"offsetof" to ::builtinOffsetof,
"sgn" to ::builtinSgn, "sgn" to ::builtinSgn,
"sqrt__ubyte" to { a, p, prg -> oneIntArgOutputInt(a, p, prg, false) { sqrt(it.toDouble()) } }, "sqrt__ubyte" to { a, p, prg -> oneIntArgOutputInt(a, p, prg, false) { sqrt(it.toDouble()) } },
"sqrt__uword" to { a, p, prg -> oneIntArgOutputInt(a, p, prg, false) { sqrt(it.toDouble()) } }, "sqrt__uword" to { a, p, prg -> oneIntArgOutputInt(a, p, prg, false) { sqrt(it.toDouble()) } },
@@ -90,6 +92,25 @@ private fun builtinAbs(args: List<Expression>, position: Position, program: Prog
else throw SyntaxError("abs requires one integer argument", position) else throw SyntaxError("abs requires one integer argument", position)
} }
private fun builtinOffsetof(args: List<Expression>, position: Position, program: Program): NumericLiteral {
// 1 arg, "Struct.field"
if(args.size!=1)
throw SyntaxError("offsetof requires one argument", position)
val identifier = (args[0] as? IdentifierReference)?.nameInSource
if(identifier==null || identifier.size<2)
throw CannotEvaluateException("offsetof","argument should be an identifier of the form Struct.field")
val structname = identifier.dropLast(1)
val fieldname = identifier.last()
val struct = args[0].definingScope.lookup(structname) as? StructDecl
if(struct==null)
throw SyntaxError("cannot find struct '$structname'", args[0].position)
val offset = struct.offsetof(fieldname, program.memsizer)
if(offset==null)
throw SyntaxError("no such field '${identifier.joinToString(".")}'", args[0].position)
return NumericLiteral.optimalInteger(offset.toInt(), position)
}
private fun builtinSizeof(args: List<Expression>, position: Position, program: Program): NumericLiteral { private fun builtinSizeof(args: List<Expression>, position: Position, program: Program): NumericLiteral {
// 1 arg, type = anything, result type = ubyte or uword // 1 arg, type = anything, result type = ubyte or uword
if(args.size!=1) if(args.size!=1)

View File

@@ -185,7 +185,7 @@ private fun integrateDefers(subdefers: Map<PtSub, List<PtDefer>>, program: PtPro
is PtNumber, is PtNumber,
is PtRange, is PtRange,
is PtString -> true is PtString -> true
is PtIdentifier -> true // actually PtIdentifier IS "complex" this time (it's a variable that might change) but it's kinda annoying to give a warning message for this very common case // note that PtIdentifier als is "complex" this time (it's a variable that might change)
else -> false else -> false
} }
@@ -202,7 +202,6 @@ private fun integrateDefers(subdefers: Map<PtSub, List<PtDefer>>, program: PtPro
} }
// complex return value, need to store it before calling the defer block // complex return value, need to store it before calling the defer block
errors.warn("using defer with nontrivial return value(s) incurs stack overhead", ret.children.first { !notComplex(it as PtExpression)}.position)
val pushAndPopCalls = ret.children.map { makePushPopFunctionCalls(it as PtExpression) } val pushAndPopCalls = ret.children.map { makePushPopFunctionCalls(it as PtExpression) }
val pushCalls = pushAndPopCalls.map { it.first }.reversed() // push in reverse order val pushCalls = pushAndPopCalls.map { it.first }.reversed() // push in reverse order
val popCalls = pushAndPopCalls.map { it.second } val popCalls = pushAndPopCalls.map { it.second }

View File

@@ -223,6 +223,7 @@ class TestCompilerOnExamplesBothC64andCx16: FunSpec({
"textelite", "textelite",
"pointers/animalgame", "pointers/animalgame",
"pointers/binarytree", "pointers/binarytree",
"pointers/hashtable",
"pointers/sortedlist", "pointers/sortedlist",
"pointers/sorting" "pointers/sorting"
), ),
@@ -253,6 +254,7 @@ class TestCompilerOnExamplesVirtual: FunSpec({
"sincos", "sincos",
"pointers/animalgame", "pointers/animalgame",
"pointers/binarytree", "pointers/binarytree",
"pointers/hashtable",
"pointers/sortedlist", "pointers/sortedlist",
"pointers/fountain-virtual", "pointers/fountain-virtual",
"pointers/sorting" "pointers/sorting"

View File

@@ -414,6 +414,15 @@ class StructDecl(override val name: String, val fields: Array<Pair<DataType, Str
override fun getFieldType(name: String): DataType? = fields.firstOrNull { it.second==name }?.first override fun getFieldType(name: String): DataType? = fields.firstOrNull { it.second==name }?.first
override val scopedNameString by lazy { scopedName.joinToString(".") } override val scopedNameString by lazy { scopedName.joinToString(".") }
fun offsetof(fieldname: String, sizer: IMemSizer): UByte? {
fields.fold(0) { offset, field ->
if (field.second == fieldname)
return offset.toUByte()
offset + sizer.memorySize(field.first, 1)
}
return null
}
} }
class StructFieldRef(val pointer: IdentifierReference, val struct: StructDecl, val type: DataType, override val name: String, override val position: Position): Statement(), INamedStatement { class StructFieldRef(val pointer: IdentifierReference, val struct: StructDecl, val type: DataType, override val name: String, override val position: Position): Statement(), INamedStatement {

View File

@@ -118,6 +118,11 @@ mkword (msb, lsb)
Don't get confused by how the system actually stores this 16-bit word value in memory (which is Don't get confused by how the system actually stores this 16-bit word value in memory (which is
in little-endian format, so lsb first then msb) in little-endian format, so lsb first then msb)
offsetof (Struct.field)
The offset in bytes of the given field in the struct. The first field will always have offset 0.
Usually you just reference the fields directly but in some cases it might be useful to know how many
bytes from the start of the structure a field is located at.
peek (address) peek (address)
same as @(address) - reads the byte at the given address in memory. same as @(address) - reads the byte at the given address in memory.

View File

@@ -1303,6 +1303,11 @@ It's possible to write a defer for a block of statements, but the advice is to k
If a piece of inlined assembly somehow causes the routine to exit, the compiler cannot detect this, If a piece of inlined assembly somehow causes the routine to exit, the compiler cannot detect this,
and defers won't be handled in such cases. and defers won't be handled in such cases.
.. attention::
Using defer always has a slight code overhead.
If you are returning non-constant values in a routine that uses defer, the compiler even has to insert some additional
code that uses the cpu stack to save some temporary values.
Library routines and builtin functions Library routines and builtin functions
-------------------------------------- --------------------------------------

View File

@@ -58,21 +58,15 @@ and for example the below code omits line 5::
STRUCTS and TYPED POINTERS STRUCTS and TYPED POINTERS
-------------------------- --------------------------
- fix the pointers/hashtable.p8 example - the type of struct initializer arrays should not be uword[] but ^^struct[] ?
- fix code size regressions (if any left)
- optimize deref in PointerAssignmentsGen: optimize 'forceTemporary' to only use a temporary when the offset is >0
- update structpointers.rst docs with 6502 specific things?
- implement the remaining TODO's in PointerAssignmentsGen. - implement the remaining TODO's in PointerAssignmentsGen.
- optimize deref in PointerAssignmentsGen: optimize 'forceTemporary' to only use a temporary when the offset is >0
- optimize addUnsignedByteOrWordToAY in PointerAssignmentsGen a bit more
- optimize the float copying in assignIndexedPointer() (also word?) - optimize the float copying in assignIndexedPointer() (also word?)
- implement even more struct instance assignments (via memcopy) in CodeDesugarer (see the TODO) (add to documentation as well, paragraph 'Structs') - implement even more struct instance assignments (via memcopy) in CodeDesugarer (see the TODO) (add to documentation as well, paragraph 'Structs')
- try to optimize pointer arithmetic used in peek/poke a bit more so the routines in sorting module can use typed pointers without increasing code size, see test.p8 in commit d394dc1e
- should @(wordpointer) be equivalent to wordpointer^^ (that would require a LOT of code rewrite that now knows that @() is strictly byte based) ?
or do an implicit cast @(wpointer as ubyte^^) ? And/or add a warning about that?
- optimize addUnsignedByteOrWordToAY in PointerAssignmentsGen a bit more
- support for typed function pointers? (&routine could be typed by default as well then)
- support @nosplit pointer arrays? - support @nosplit pointer arrays?
- support pointer to pointer? - support pointer to pointer?
- the type of struct initializer arrays should not be uword[] but ^^struct[] - support for typed function pointers? (&routine could be typed by default as well then)
- 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" - 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) - (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)
@@ -90,6 +84,7 @@ Future Things and Ideas
7 } 7 }
8 modifications.forEach { it.perform() } 8 modifications.forEach { it.perform() }
9 } 9 }
- improve ANTLR grammar with better error handling (according to Qwen AI)
- allow memory() to occur in array initializer - allow memory() to occur in array initializer
- %breakpoint after an assignment is parsed as part of the expression (x % breakpoint), that should not happen - %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 - when a complete block is removed because unused, suppress all info messages about everything in the block being removed
@@ -120,6 +115,26 @@ Future Things and Ideas
IR/VM IR/VM
----- -----
- is it possible to use LOADFIELD/STOREFIELD instructions more? - is it possible to use LOADFIELD/STOREFIELD instructions more?
- make multiple classes of registers and maybe also categorize by life time , to prepare for better register allocation in the future
SYSCALL_ARGS, // Reserved for syscall arguments (r99000-99099, r99100-99199)
FUNCTION_PARAMS, // For passing function parameters
FUNCTION_RETURNS, // For function return values
TEMPORARY, // Short-lived temporary values
LOCAL_VARIABLES, // Local variables within functions
GLOBAL_VARIABLES, // Global/static variables
HARDWARE_MAPPED, // Mapped to CPU hardware registers
LOOP_INDICES, // Used as loop counters
ADDRESS_CALCULATION // Used for pointer arithmetic
Categorizing registers by lifetime can significantly improve allocation:
- Short-lived: Temporary registers used in expressions
- Medium-lived: Local variables within a function
Registers could be categorized by how frequently they're accessed:
- Hot Registers: Frequently accessed (should be allocated to faster physical registers)
- Warm Registers: Moderately accessed
- Cold Registers: Rarely accessed (can be spilled to memory if needed)
We already have type-based pools
- byte, word, float registers
- 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 - 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 - 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!) - 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!)

View File

@@ -3,16 +3,13 @@
; for efficient data storage and retrieval ; for efficient data storage and retrieval
; TODO fix this AI generated code
%import math
%import strings %import strings
%import textio %import textio
%zeropage basicsafe %zeropage basicsafe
main { main {
sub start() { sub start() {
txt.lowercase()
txt.print("Doubly Linked List with Hash Table Cache Demo\n") txt.print("Doubly Linked List with Hash Table Cache Demo\n")
txt.print("============================================\n\n") txt.print("============================================\n\n")
@@ -66,6 +63,8 @@ main {
cache.add("A", "First", 20) cache.add("A", "First", 20)
cache.add("B", "Second", 21) cache.add("B", "Second", 21)
cache.add("C", "Third", 22) cache.add("C", "Third", 22)
cache.add("D", "Fourth", 23)
cache.add("E", "Fifth", 24)
txt.print("Added entries with potential hash collisions.\n") txt.print("Added entries with potential hash collisions.\n")
txt.print("Forward traversal:\n") txt.print("Forward traversal:\n")
@@ -91,29 +90,14 @@ cache {
sub init() { sub init() {
; Initialize hash table buckets to null ; Initialize hash table buckets to null
ubyte i sys.memsetw(hash_table, HASH_TABLE_SIZE, 0)
for i in 0 to HASH_TABLE_SIZE-1
hash_table[i] = 0
}
sub hash(str key) -> ubyte {
; Simple hash function
ubyte hash_value = 0
ubyte i = 0
while key[i] != 0 {
hash_value = hash_value * 31 + key[i]
i++
}
return hash_value % HASH_TABLE_SIZE
} }
sub add(str name, str job, ubyte age) { sub add(str name, str job, ubyte age) {
; Create new entry ; Create new entry
^^Entry new_entry = arena.alloc(sizeof(Entry)) ^^Entry new_entry = arena.alloc(sizeof(Entry))
ubyte name_len = strings.length(name) ^^ubyte name_copy = arena.alloc(strings.length(name) + 1)
ubyte job_len = strings.length(job) ^^ubyte job_copy = arena.alloc(strings.length(job) + 1)
^^ubyte name_copy = arena.alloc(name_len + 1)
^^ubyte job_copy = arena.alloc(job_len + 1)
void strings.copy(name, name_copy) void strings.copy(name, name_copy)
void strings.copy(job, job_copy) void strings.copy(job, job_copy)
@@ -137,7 +121,7 @@ cache {
} }
; Add to hash table ; Add to hash table
ubyte bucket = hash(name) ubyte bucket = strings.hash(name) % HASH_TABLE_SIZE
new_entry.hash_next = hash_table[bucket] new_entry.hash_next = hash_table[bucket]
hash_table[bucket] = new_entry hash_table[bucket] = new_entry
@@ -149,7 +133,7 @@ cache {
sub find(str name) -> ^^Entry { sub find(str name) -> ^^Entry {
; Find entry using hash table for O(1) average case ; Find entry using hash table for O(1) average case
ubyte bucket = hash(name) ubyte bucket = strings.hash(name) % HASH_TABLE_SIZE
^^Entry current = hash_table[bucket] ^^Entry current = hash_table[bucket]
while current != 0 { while current != 0 {
@@ -179,7 +163,7 @@ cache {
tail = to_remove.prev ; Was the tail tail = to_remove.prev ; Was the tail
; Remove from hash table ; Remove from hash table
ubyte bucket = hash(name) ubyte bucket = strings.hash(name) % HASH_TABLE_SIZE
if hash_table[bucket] == to_remove { if hash_table[bucket] == to_remove {
hash_table[bucket] = to_remove.hash_next hash_table[bucket] = to_remove.hash_next
} else { } else {

View File

@@ -1,32 +1,34 @@
%option enable_floats
main { main {
struct Node {
ubyte id
str name
uword array
bool flag
float perc
}
struct Foobar {
bool thing
}
sub start() { sub start() {
^^Node[] @shared nodeswithtype = [ ^^uword @shared ptr
^^Node: [1,"one", 1000, true, 1.111],
^^Node: [],
]
^^Node derp2 = ^^Foobar: [] add1()
add2()
sub1()
sub2()
^^Node[] @shared nodeswithout = [ sub add1() {
[2,"two", 2000, false, 2.222], ptr += 5
[1,2,3,true,5], cx16.r0 = ptr + 5
[] cx16.r0 = peekw(ptr + 5)
] }
^^Node @shared nptrwithtype = ^^Node : [1, "one", 1000, false, 3.333] sub add2() {
^^Node @shared nptrwithouttype = [1, "one", 1000, false, 3.333] ptr += cx16.r0L
cx16.r0 = ptr + cx16.r0L
cx16.r0 = peekw(ptr + cx16.r0L)
}
sub sub1() {
ptr -= 5
cx16.r0 = ptr - 5
cx16.r0 = peekw(ptr - 5)
}
sub sub2() {
ptr -= cx16.r0L
cx16.r0 = ptr - cx16.r0L
cx16.r0 = peekw(ptr - cx16.r0L)
}
} }
} }

View File

@@ -14,7 +14,7 @@
<keywords keywords="&amp;;&amp;&amp;;&amp;&lt;;&amp;&gt;;-&gt;;@;^^;alias;and;as;asmsub;break;clobbers;continue;do;downto;else;extsub;false;for;goto;if;if_cc;if_cs;if_eq;if_mi;if_ne;if_neg;if_nz;if_pl;if_pos;if_vc;if_vs;if_z;in;inline;not;on;or;repeat;return;step;sub;to;true;unroll;until;when;while;xor;~" ignore_case="false" /> <keywords keywords="&amp;;&amp;&amp;;&amp;&lt;;&amp;&gt;;-&gt;;@;^^;alias;and;as;asmsub;break;clobbers;continue;do;downto;else;extsub;false;for;goto;if;if_cc;if_cs;if_eq;if_mi;if_ne;if_neg;if_nz;if_pl;if_pos;if_vc;if_vs;if_z;in;inline;not;on;or;repeat;return;step;sub;to;true;unroll;until;when;while;xor;~" ignore_case="false" />
<keywords2 keywords="%address;%align;%asm;%asmbinary;%asminclude;%breakpoint;%encoding;%import;%ir;%jmptable;%launcher;%memtop;%option;%output;%zeropage;%zpallowed;%zpreserved;@align64;@alignpage;@alignword;@bank;@dirty;@nosplit;@nozp;@requirezp;@shared;@split;@zp;atascii:;c64os:;cp437:;default:;iso16:;iso5:;iso:;kata:;petscii:;sc:" /> <keywords2 keywords="%address;%align;%asm;%asmbinary;%asminclude;%breakpoint;%encoding;%import;%ir;%jmptable;%launcher;%memtop;%option;%output;%zeropage;%zpallowed;%zpreserved;@align64;@alignpage;@alignword;@bank;@dirty;@nosplit;@nozp;@requirezp;@shared;@split;@zp;atascii:;c64os:;cp437:;default:;iso16:;iso5:;iso:;kata:;petscii:;sc:" />
<keywords3 keywords="^^bool;^^byte;^^float;^^long;^^str;^^ubyte;^^uword;^^word;bool;byte;const;float;long;str;struct;ubyte;uword;void;word" /> <keywords3 keywords="^^bool;^^byte;^^float;^^long;^^str;^^ubyte;^^uword;^^word;bool;byte;const;float;long;str;struct;ubyte;uword;void;word" />
<keywords4 keywords="abs;bmx;call;callfar;callfar2;cbm;clamp;cmp;conv;cx16;defer;diskio;divmod;floats;len;lsb;lsw;math;max;memory;min;mkword;msb;msw;peek;peekbool;peekf;peekw;poke;pokebool;pokef;pokew;psg;rol;rol2;ror;ror2;rrestore;rrestorex;rsave;rsavex;setlsb;setmsb;sgn;sizeof;sqrt;strings;sys;txt;verafx" /> <keywords4 keywords="abs;bmx;call;callfar;callfar2;cbm;clamp;cmp;conv;cx16;defer;diskio;divmod;floats;len;lsb;lsw;math;max;memory;min;mkword;msb;msw;offsetof;peek;peekbool;peekf;peekw;poke;pokebool;pokef;pokew;psg;rol;rol2;ror;ror2;rrestore;rrestorex;rsave;rsavex;setlsb;setmsb;sgn;sizeof;sqrt;strings;sys;txt;verafx" />
</highlighting> </highlighting>
<extensionMap> <extensionMap>
<mapping ext="p8" /> <mapping ext="p8" />

View File

@@ -27,7 +27,7 @@
<Keywords name="Keywords1">void const&#x000D;&#x000A;str&#x000D;&#x000A;byte ubyte bool&#x000D;&#x000A;long word uword&#x000D;&#x000A;float&#x000D;&#x000A;zp shared split nosplit requirezp nozp struct</Keywords> <Keywords name="Keywords1">void const&#x000D;&#x000A;str&#x000D;&#x000A;byte ubyte bool&#x000D;&#x000A;long word uword&#x000D;&#x000A;float&#x000D;&#x000A;zp shared split nosplit requirezp nozp struct</Keywords>
<Keywords name="Keywords2">%address&#x000D;&#x000A;%asm&#x000D;&#x000A;%ir&#x000D;&#x000A;%asmbinary&#x000D;&#x000A;%asminclude&#x000D;&#x000A;%align&#x000D;&#x000A;%breakpoint&#x000D;&#x000A;%encoding&#x000D;&#x000A;%import&#x000D;&#x000A;%jmptable&#x000D;&#x000A;%memtop&#x000D;&#x000A;%launcher&#x000D;&#x000A;%option&#x000D;&#x000A;%output&#x000D;&#x000A;%zeropage&#x000D;&#x000A;%zpreserved&#x000D;&#x000A;%zpallowed</Keywords> <Keywords name="Keywords2">%address&#x000D;&#x000A;%asm&#x000D;&#x000A;%ir&#x000D;&#x000A;%asmbinary&#x000D;&#x000A;%asminclude&#x000D;&#x000A;%align&#x000D;&#x000A;%breakpoint&#x000D;&#x000A;%encoding&#x000D;&#x000A;%import&#x000D;&#x000A;%jmptable&#x000D;&#x000A;%memtop&#x000D;&#x000A;%launcher&#x000D;&#x000A;%option&#x000D;&#x000A;%output&#x000D;&#x000A;%zeropage&#x000D;&#x000A;%zpreserved&#x000D;&#x000A;%zpallowed</Keywords>
<Keywords name="Keywords3">inline sub asmsub extsub&#x000D;&#x000A;clobbers&#x000D;&#x000A;asm&#x000D;&#x000A;if&#x000D;&#x000A;when else&#x000D;&#x000A;if_cc if_cs if_eq if_mi if_neg if_nz if_pl if_pos if_vc if_vs if_z&#x000D;&#x000A;for in step do while repeat unroll&#x000D;&#x000A;break continue return goto</Keywords> <Keywords name="Keywords3">inline sub asmsub extsub&#x000D;&#x000A;clobbers&#x000D;&#x000A;asm&#x000D;&#x000A;if&#x000D;&#x000A;when else&#x000D;&#x000A;if_cc if_cs if_eq if_mi if_neg if_nz if_pl if_pos if_vc if_vs if_z&#x000D;&#x000A;for in step do while repeat unroll&#x000D;&#x000A;break continue return goto</Keywords>
<Keywords name="Keywords4">alias abs call callfar callfar2 clamp cmp defer divmod len lsb lsw lsl lsr memory mkword min max msb msw peek peekw peekf peekbool poke pokew pokef pokebool rsave rsavex rrestore rrestorex rnd rndw rol rol2 ror ror2 setlsb setmsb sgn sizeof sqrtw</Keywords> <Keywords name="Keywords4">alias abs call callfar callfar2 clamp cmp defer divmod len lsb lsw lsl lsr memory mkword min max msb msw peek peekw peekf peekbool poke pokew pokef pokebool rsave rsavex rrestore rrestorex rnd rndw rol rol2 ror ror2 setlsb setmsb sgn sizeof offsetof sqrtw</Keywords>
<Keywords name="Keywords5">true false&#x000D;&#x000A;not and or xor&#x000D;&#x000A;as to downto |&gt;</Keywords> <Keywords name="Keywords5">true false&#x000D;&#x000A;not and or xor&#x000D;&#x000A;as to downto |&gt;</Keywords>
<Keywords name="Keywords6"></Keywords> <Keywords name="Keywords6"></Keywords>
<Keywords name="Keywords7"></Keywords> <Keywords name="Keywords7"></Keywords>

View File

@@ -27,7 +27,7 @@
<Keywords name="Keywords1">void const&#x000D;&#x000A;str&#x000D;&#x000A;byte ubyte bool&#x000D;&#x000A;long word uword&#x000D;&#x000A;float&#x000D;&#x000A;zp shared split nosplit requirezp nozp struct</Keywords> <Keywords name="Keywords1">void const&#x000D;&#x000A;str&#x000D;&#x000A;byte ubyte bool&#x000D;&#x000A;long word uword&#x000D;&#x000A;float&#x000D;&#x000A;zp shared split nosplit requirezp nozp struct</Keywords>
<Keywords name="Keywords2">%address&#x000D;&#x000A;%asm&#x000D;&#x000A;%ir&#x000D;&#x000A;%asmbinary&#x000D;&#x000A;%asminclude&#x000D;&#x000A;%align&#x000D;&#x000A;%breakpoint&#x000D;&#x000A;%encoding&#x000D;&#x000A;%import&#x000D;&#x000A;%jmptable&#x000D;&#x000A;%memtop&#x000D;&#x000A;%launcher&#x000D;&#x000A;%option&#x000D;&#x000A;%output&#x000D;&#x000A;%zeropage&#x000D;&#x000A;%zpreserved&#x000D;&#x000A;%zpallowed</Keywords> <Keywords name="Keywords2">%address&#x000D;&#x000A;%asm&#x000D;&#x000A;%ir&#x000D;&#x000A;%asmbinary&#x000D;&#x000A;%asminclude&#x000D;&#x000A;%align&#x000D;&#x000A;%breakpoint&#x000D;&#x000A;%encoding&#x000D;&#x000A;%import&#x000D;&#x000A;%jmptable&#x000D;&#x000A;%memtop&#x000D;&#x000A;%launcher&#x000D;&#x000A;%option&#x000D;&#x000A;%output&#x000D;&#x000A;%zeropage&#x000D;&#x000A;%zpreserved&#x000D;&#x000A;%zpallowed</Keywords>
<Keywords name="Keywords3">inline sub asmsub extsub&#x000D;&#x000A;clobbers&#x000D;&#x000A;asm&#x000D;&#x000A;if&#x000D;&#x000A;when else&#x000D;&#x000A;if_cc if_cs if_eq if_mi if_neg if_nz if_pl if_pos if_vc if_vs if_z&#x000D;&#x000A;for in step do while repeat unroll&#x000D;&#x000A;break continue return goto</Keywords> <Keywords name="Keywords3">inline sub asmsub extsub&#x000D;&#x000A;clobbers&#x000D;&#x000A;asm&#x000D;&#x000A;if&#x000D;&#x000A;when else&#x000D;&#x000A;if_cc if_cs if_eq if_mi if_neg if_nz if_pl if_pos if_vc if_vs if_z&#x000D;&#x000A;for in step do while repeat unroll&#x000D;&#x000A;break continue return goto</Keywords>
<Keywords name="Keywords4">alias abs call callfar callfar2 clamp cmp defer divmod len lsb lsw lsl lsr memory mkword min max msb msw peek peekw peekf peekbool poke pokew pokef pokebool rsave rsavex rrestore rrestorex rnd rndw rol rol2 ror ror2 setlsb setmsb sgn sizeof sqrtw</Keywords> <Keywords name="Keywords4">alias abs call callfar callfar2 clamp cmp defer divmod len lsb lsw lsl lsr memory mkword min max msb msw peek peekw peekf peekbool poke pokew pokef pokebool rsave rsavex rrestore rrestorex rnd rndw rol rol2 ror ror2 setlsb setmsb sgn sizeof offsetof sqrtw</Keywords>
<Keywords name="Keywords5">true false&#x000D;&#x000A;not and or xor&#x000D;&#x000A;as to downto |&gt;</Keywords> <Keywords name="Keywords5">true false&#x000D;&#x000A;not and or xor&#x000D;&#x000A;as to downto |&gt;</Keywords>
<Keywords name="Keywords6"></Keywords> <Keywords name="Keywords6"></Keywords>
<Keywords name="Keywords7"></Keywords> <Keywords name="Keywords7"></Keywords>

View File

@@ -156,7 +156,7 @@ contexts:
- match: (\b(const)\b) - match: (\b(const)\b)
scope: storage.modifier.prog8 scope: storage.modifier.prog8
support: support:
- match: (\b(abs|atan|ceil|cos|cos8u|cos8|cos16u|cos16|deg|floor|ln|log2|rad|round|sin|sgn|sin8u|sin8|sin16u|sin16|sqrt16|sqrt|tan|any|all|len|max|min|reverse|sum|sort|memcopy|memset|memsetw|leftstr|rightstr|strlen|strcmp|strncmp|substr|exit|lsb|msb|lsw|msw|mkword|rnd|rndw|rndf|rol|rol2|ror|ror2|rsave|rrestore|read_flags|sizeof|set_carry|clear_carry|set_irqd|clear_irqd|swap)\b) - match: (\b(abs|atan|ceil|cos|cos8u|cos8|cos16u|cos16|deg|floor|ln|log2|rad|round|sin|sgn|sin8u|sin8|sin16u|sin16|sqrt16|sqrt|tan|any|all|len|max|min|reverse|sum|sort|memcopy|memset|memsetw|leftstr|rightstr|strlen|strcmp|strncmp|substr|exit|lsb|msb|lsw|msw|mkword|rnd|rndw|rndf|rol|rol2|ror|ror2|rsave|rrestore|read_flags|sizeof|offsetof|set_carry|clear_carry|set_irqd|clear_irqd|swap)\b)
scope: support.function.prog8 scope: support.function.prog8
variable: variable:
- match: (\b\w+\b) - match: (\b\w+\b)

View File

@@ -14,7 +14,7 @@ syn keyword prog8BuiltInFunc len
" Miscellaneous functions " Miscellaneous functions
syn keyword prog8BuiltInFunc cmp divmod lsb msb lsw msw mkword min max peek peekw peekf peekbool poke pokew pokef pokebool rsave rsavex rrestore rrestorex syn keyword prog8BuiltInFunc cmp divmod lsb msb lsw msw mkword min max peek peekw peekf peekbool poke pokew pokef pokebool rsave rsavex rrestore rrestorex
syn keyword prog8BuiltInFunc rol rol2 ror ror2 sizeof setlsb setmsb syn keyword prog8BuiltInFunc rol rol2 ror ror2 sizeof offsetof setlsb setmsb
syn keyword prog8BuiltInFunc memory call callfar callfar2 clamp defer alias syn keyword prog8BuiltInFunc memory call callfar callfar2 clamp defer alias