mirror of
https://github.com/irmen/prog8.git
synced 2025-09-26 16:16:39 +00:00
Compare commits
6 Commits
languageSe
...
v12.0-beta
Author | SHA1 | Date | |
---|---|---|---|
|
231b50dacb | ||
|
a71895cbe8 | ||
|
a8bede17b2 | ||
|
f6c8e693a5 | ||
|
9461e4088c | ||
|
efd73fd10d |
@@ -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)),
|
||||||
|
@@ -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")
|
||||||
|
@@ -257,16 +257,26 @@ 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 throw AssemblyError("weird dt ${target.dt} ${target.position}")
|
} 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}")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
"-" -> {
|
"-" -> {
|
||||||
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 throw AssemblyError("weird dt ${target.position}")
|
} 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}")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
"*" -> {
|
"*" -> {
|
||||||
if(target.dt.isByte) TODO("inplaceByteMul(target, value) ${target.position}")
|
if(target.dt.isByte) TODO("inplaceByteMul(target, value) ${target.position}")
|
||||||
@@ -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,48 +1418,222 @@ 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("""
|
|
||||||
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("""
|
asmgen.out("""
|
||||||
ldy #1
|
lda ($zpPtrVar)
|
||||||
lda (P8ZP_SCRATCH_PTR),y
|
ora #$number
|
||||||
tax
|
sta ($zpPtrVar)""")
|
||||||
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))
|
||||||
|
asmgen.out("""
|
||||||
|
lda ($zpPtrVar)
|
||||||
|
ora $varname
|
||||||
|
sta ($zpPtrVar)""")
|
||||||
|
else
|
||||||
|
asmgen.out("""
|
||||||
|
ldy #$offset
|
||||||
|
lda ($zpPtrVar),y
|
||||||
|
ora $varname
|
||||||
|
sta ($zpPtrVar),y""")
|
||||||
}
|
}
|
||||||
target.datatype.isFloat -> {
|
SourceStorageKind.EXPRESSION -> {
|
||||||
// TODO optimize the float copying to avoid having to go through FAC1
|
asmgen.assignExpressionToRegister(value.expression!!, RegisterOrPair.A)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda P8ZP_SCRATCH_PTR
|
ldy #$offset
|
||||||
ldy P8ZP_SCRATCH_PTR+1
|
ora ($zpPtrVar),y
|
||||||
jsr floats.MOVFM""")
|
sta ($zpPtrVar),y""")
|
||||||
asmgen.assignRegister(RegisterOrPair.FAC1, target)
|
|
||||||
}
|
}
|
||||||
else -> throw AssemblyError("weird dt ${target.datatype}")
|
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) {
|
||||||
when(regs) {
|
when(regs) {
|
||||||
RegisterOrPair.AX -> {
|
RegisterOrPair.AX -> {
|
||||||
|
@@ -137,63 +137,75 @@ 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)
|
||||||
var operandTr = ExpressionCodeResult(emptyList(), IRDataType.BYTE, -1, -1)
|
|
||||||
if(augAssign.operator!="or=" && augAssign.operator!="and=") {
|
if((augAssign.operator=="+=" || augAssign.operator=="-=") && value.asConstInteger()==1 || value.asConstInteger()==2) {
|
||||||
// for everything except the shortcircuit boolean operators, we can evaluate the value here unconditionally
|
// INC/DEC optimization instead of ADD/SUB
|
||||||
operandTr = expressionEval.translateExpression(value)
|
|
||||||
inplaceInstrs += operandTr.chunks
|
loadfield(inplaceInstrs, addressReg, fieldOffset, targetDt, oldvalueReg)
|
||||||
}
|
val instr = if(augAssign.operator=="+=") Opcode.INC else Opcode.DEC
|
||||||
if(targetDt== IRDataType.FLOAT) {
|
repeat(value.asConstInteger()!!) {
|
||||||
if(fieldOffset>0u)
|
addInstr(inplaceInstrs, IRInstruction(instr, targetDt, reg1 = oldvalueReg), null)
|
||||||
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)
|
|
||||||
when(augAssign.operator) {
|
|
||||||
"+=" -> 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.MULR, targetDt, fpReg1 = oldvalueReg, fpReg2 = operandTr.resultFpReg), null)
|
|
||||||
"/=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.DIVSR, targetDt, fpReg1 = oldvalueReg, fpReg2 = operandTr.resultFpReg), null)
|
|
||||||
"%=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.MODR, targetDt, fpReg1 = oldvalueReg, fpReg2 = operandTr.resultFpReg), null)
|
|
||||||
"+" -> { /* inplace + is a no-op */ }
|
|
||||||
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)
|
var operandTr = ExpressionCodeResult(emptyList(), IRDataType.BYTE, -1, -1)
|
||||||
else
|
if(augAssign.operator!="or=" && augAssign.operator!="and=") {
|
||||||
addInstr(inplaceInstrs, IRInstruction(Opcode.LOADI, targetDt, reg1 = oldvalueReg, reg2 = addressReg), null)
|
// for everything except the shortcircuit boolean operators, we can evaluate the value here unconditionally
|
||||||
when(augAssign.operator) {
|
operandTr = expressionEval.translateExpression(value)
|
||||||
"+=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.ADDR, targetDt, reg1 = oldvalueReg, reg2 = operandTr.resultReg), null)
|
// 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
|
||||||
"-=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.SUBR, targetDt, reg1 = oldvalueReg, reg2 = operandTr.resultReg), null)
|
}
|
||||||
"*=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.MULR, targetDt, reg1 = oldvalueReg, reg2 = operandTr.resultReg), null)
|
if(targetDt== IRDataType.FLOAT) {
|
||||||
"/=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.DIVR, targetDt, reg1 = oldvalueReg, reg2 = operandTr.resultReg), null)
|
|
||||||
"%=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.MODR, targetDt, reg1 = oldvalueReg, reg2 = operandTr.resultReg), null)
|
loadfield(inplaceInstrs, addressReg, fieldOffset, targetDt, oldvalueReg)
|
||||||
"|=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.ORR, targetDt, reg1 = oldvalueReg, reg2 = operandTr.resultReg), null)
|
inplaceInstrs += operandTr.chunks
|
||||||
"&=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.ANDR, targetDt, reg1 = oldvalueReg, reg2 = operandTr.resultReg), null)
|
when(augAssign.operator) {
|
||||||
"^=", "xor=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.XORR, targetDt, reg1 = oldvalueReg, reg2 = operandTr.resultReg), null)
|
"+=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.ADDR, targetDt, fpReg1 = oldvalueReg, fpReg2 = operandTr.resultFpReg), null)
|
||||||
"<<=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.LSLN, targetDt, reg1 = oldvalueReg, reg2 = operandTr.resultReg), null)
|
"-=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.SUBR, targetDt, fpReg1 = oldvalueReg, fpReg2 = operandTr.resultFpReg), null)
|
||||||
">>=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.LSRN, targetDt, reg1 = oldvalueReg, reg2 = operandTr.resultReg), null)
|
"*=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.MULR, targetDt, fpReg1 = oldvalueReg, fpReg2 = operandTr.resultFpReg), null)
|
||||||
"or=" -> {
|
"/=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.DIVSR, targetDt, fpReg1 = oldvalueReg, fpReg2 = operandTr.resultFpReg), null)
|
||||||
val shortcutLabel = codeGen.createLabelName()
|
"%=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.MODR, targetDt, fpReg1 = oldvalueReg, fpReg2 = operandTr.resultFpReg), null)
|
||||||
addInstr(inplaceInstrs, IRInstruction(Opcode.BSTNE, labelSymbol = shortcutLabel), null)
|
"+" -> { /* inplace + is a no-op */ }
|
||||||
val valueTr = expressionEval.translateExpression(value)
|
else -> throw AssemblyError("invalid augmented assign operator for floats ${augAssign.operator}")
|
||||||
inplaceInstrs += valueTr.chunks
|
|
||||||
addInstr(inplaceInstrs, IRInstruction(Opcode.ORR, targetDt, reg1=oldvalueReg, reg2=valueTr.resultReg), null)
|
|
||||||
inplaceInstrs += IRCodeChunk(shortcutLabel, null)
|
|
||||||
}
|
}
|
||||||
"and=" -> {
|
|
||||||
val shortcutLabel = codeGen.createLabelName()
|
} else {
|
||||||
addInstr(inplaceInstrs, IRInstruction(Opcode.BSTEQ, labelSymbol = shortcutLabel), null)
|
|
||||||
val valueTr = expressionEval.translateExpression(value)
|
loadfield(inplaceInstrs, addressReg, fieldOffset, targetDt, oldvalueReg)
|
||||||
inplaceInstrs += valueTr.chunks
|
inplaceInstrs += operandTr.chunks
|
||||||
addInstr(inplaceInstrs, IRInstruction(Opcode.ANDR, targetDt, reg1=oldvalueReg, reg2=valueTr.resultReg), null)
|
when(augAssign.operator) {
|
||||||
inplaceInstrs += IRCodeChunk(shortcutLabel, 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.MULR, targetDt, reg1 = oldvalueReg, reg2 = operandTr.resultReg), null)
|
||||||
|
"/=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.DIVR, targetDt, reg1 = oldvalueReg, reg2 = operandTr.resultReg), null)
|
||||||
|
"%=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.MODR, targetDt, reg1 = oldvalueReg, reg2 = operandTr.resultReg), null)
|
||||||
|
"|=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.ORR, targetDt, reg1 = oldvalueReg, reg2 = operandTr.resultReg), null)
|
||||||
|
"&=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.ANDR, targetDt, reg1 = oldvalueReg, reg2 = operandTr.resultReg), null)
|
||||||
|
"^=", "xor=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.XORR, targetDt, reg1 = oldvalueReg, reg2 = operandTr.resultReg), null)
|
||||||
|
"<<=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.LSLN, targetDt, reg1 = oldvalueReg, reg2 = operandTr.resultReg), null)
|
||||||
|
">>=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.LSRN, targetDt, reg1 = oldvalueReg, reg2 = operandTr.resultReg), null)
|
||||||
|
"or=" -> {
|
||||||
|
val shortcutLabel = codeGen.createLabelName()
|
||||||
|
addInstr(inplaceInstrs, IRInstruction(Opcode.BSTNE, labelSymbol = shortcutLabel), null)
|
||||||
|
val valueTr = expressionEval.translateExpression(value)
|
||||||
|
inplaceInstrs += valueTr.chunks
|
||||||
|
addInstr(inplaceInstrs, IRInstruction(Opcode.ORR, targetDt, reg1=oldvalueReg, reg2=valueTr.resultReg), null)
|
||||||
|
inplaceInstrs += IRCodeChunk(shortcutLabel, null)
|
||||||
|
}
|
||||||
|
"and=" -> {
|
||||||
|
val shortcutLabel = codeGen.createLabelName()
|
||||||
|
addInstr(inplaceInstrs, IRInstruction(Opcode.BSTEQ, labelSymbol = shortcutLabel), null)
|
||||||
|
val valueTr = expressionEval.translateExpression(value)
|
||||||
|
inplaceInstrs += valueTr.chunks
|
||||||
|
addInstr(inplaceInstrs, IRInstruction(Opcode.ANDR, targetDt, reg1=oldvalueReg, reg2=valueTr.resultReg), null)
|
||||||
|
inplaceInstrs += IRCodeChunk(shortcutLabel, null)
|
||||||
|
}
|
||||||
|
"-" -> addInstr(inplaceInstrs, IRInstruction(Opcode.NEG, targetDt, reg1 = oldvalueReg), null)
|
||||||
|
"~" -> addInstr(inplaceInstrs, IRInstruction(Opcode.INV, targetDt, reg1 = oldvalueReg), null)
|
||||||
|
"not" -> addInstr(inplaceInstrs, IRInstruction(Opcode.XOR, targetDt, reg1 = oldvalueReg, immediate = 1), null)
|
||||||
|
"+" -> { /* inplace + is a no-op */ }
|
||||||
|
else -> throw AssemblyError("invalid augmented assign operator ${augAssign.operator}")
|
||||||
}
|
}
|
||||||
"-" -> addInstr(inplaceInstrs, IRInstruction(Opcode.NEG, targetDt, reg1 = oldvalueReg), null)
|
|
||||||
"~" -> addInstr(inplaceInstrs, IRInstruction(Opcode.INV, targetDt, reg1 = oldvalueReg), null)
|
|
||||||
"not" -> addInstr(inplaceInstrs, IRInstruction(Opcode.XOR, targetDt, reg1 = oldvalueReg, immediate = 1), null)
|
|
||||||
"+" -> { /* inplace + is a no-op */ }
|
|
||||||
else -> throw AssemblyError("invalid augmented assign operator ${augAssign.operator}")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -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) {
|
||||||
|
@@ -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}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -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)
|
||||||
|
@@ -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 }
|
||||||
|
@@ -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"
|
||||||
|
@@ -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 {
|
||||||
|
@@ -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.
|
||||||
|
|
||||||
|
@@ -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
|
||||||
--------------------------------------
|
--------------------------------------
|
||||||
|
@@ -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!)
|
||||||
|
@@ -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 {
|
||||||
|
@@ -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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -14,7 +14,7 @@
|
|||||||
<keywords keywords="&;&&;&<;&>;->;@;^^;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="&;&&;&<;&>;->;@;^^;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" />
|
||||||
|
@@ -27,7 +27,7 @@
|
|||||||
<Keywords name="Keywords1">void const
str
byte ubyte bool
long word uword
float
zp shared split nosplit requirezp nozp struct</Keywords>
|
<Keywords name="Keywords1">void const
str
byte ubyte bool
long word uword
float
zp shared split nosplit requirezp nozp struct</Keywords>
|
||||||
<Keywords name="Keywords2">%address
%asm
%ir
%asmbinary
%asminclude
%align
%breakpoint
%encoding
%import
%jmptable
%memtop
%launcher
%option
%output
%zeropage
%zpreserved
%zpallowed</Keywords>
|
<Keywords name="Keywords2">%address
%asm
%ir
%asmbinary
%asminclude
%align
%breakpoint
%encoding
%import
%jmptable
%memtop
%launcher
%option
%output
%zeropage
%zpreserved
%zpallowed</Keywords>
|
||||||
<Keywords name="Keywords3">inline sub asmsub extsub
clobbers
asm
if
when else
if_cc if_cs if_eq if_mi if_neg if_nz if_pl if_pos if_vc if_vs if_z
for in step do while repeat unroll
break continue return goto</Keywords>
|
<Keywords name="Keywords3">inline sub asmsub extsub
clobbers
asm
if
when else
if_cc if_cs if_eq if_mi if_neg if_nz if_pl if_pos if_vc if_vs if_z
for in step do while repeat unroll
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
not and or xor
as to downto |></Keywords>
|
<Keywords name="Keywords5">true false
not and or xor
as to downto |></Keywords>
|
||||||
<Keywords name="Keywords6"></Keywords>
|
<Keywords name="Keywords6"></Keywords>
|
||||||
<Keywords name="Keywords7"></Keywords>
|
<Keywords name="Keywords7"></Keywords>
|
||||||
|
@@ -27,7 +27,7 @@
|
|||||||
<Keywords name="Keywords1">void const
str
byte ubyte bool
long word uword
float
zp shared split nosplit requirezp nozp struct</Keywords>
|
<Keywords name="Keywords1">void const
str
byte ubyte bool
long word uword
float
zp shared split nosplit requirezp nozp struct</Keywords>
|
||||||
<Keywords name="Keywords2">%address
%asm
%ir
%asmbinary
%asminclude
%align
%breakpoint
%encoding
%import
%jmptable
%memtop
%launcher
%option
%output
%zeropage
%zpreserved
%zpallowed</Keywords>
|
<Keywords name="Keywords2">%address
%asm
%ir
%asmbinary
%asminclude
%align
%breakpoint
%encoding
%import
%jmptable
%memtop
%launcher
%option
%output
%zeropage
%zpreserved
%zpallowed</Keywords>
|
||||||
<Keywords name="Keywords3">inline sub asmsub extsub
clobbers
asm
if
when else
if_cc if_cs if_eq if_mi if_neg if_nz if_pl if_pos if_vc if_vs if_z
for in step do while repeat unroll
break continue return goto</Keywords>
|
<Keywords name="Keywords3">inline sub asmsub extsub
clobbers
asm
if
when else
if_cc if_cs if_eq if_mi if_neg if_nz if_pl if_pos if_vc if_vs if_z
for in step do while repeat unroll
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
not and or xor
as to downto |></Keywords>
|
<Keywords name="Keywords5">true false
not and or xor
as to downto |></Keywords>
|
||||||
<Keywords name="Keywords6"></Keywords>
|
<Keywords name="Keywords6"></Keywords>
|
||||||
<Keywords name="Keywords7"></Keywords>
|
<Keywords name="Keywords7"></Keywords>
|
||||||
|
@@ -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)
|
||||||
|
@@ -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
|
||||||
|
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user