Merge branch 'pointer-index-optimize'

# Conflicts:
#	docs/source/todo.rst
This commit is contained in:
Irmen de Jong 2021-01-23 15:57:23 +01:00
commit f9fd426843
10 changed files with 264 additions and 893 deletions

View File

@ -558,44 +558,6 @@ internal class AsmGen(private val program: Program,
}
}
fun storeByteIntoPointer(pointervar: IdentifierReference, ldaInstructionArg: String?) {
val sourceName = asmVariableName(pointervar)
val vardecl = pointervar.targetVarDecl(program.namespace)!!
val scopedName = vardecl.makeScopedName(vardecl.name)
if (CompilationTarget.instance.machine.cpu == CpuType.CPU65c02) {
if (isZpVar(scopedName)) {
// pointervar is already in the zero page, no need to copy
if (ldaInstructionArg != null)
out(" lda $ldaInstructionArg")
out(" sta ($sourceName)")
} else {
out("""
ldy $sourceName
sty P8ZP_SCRATCH_W2
ldy $sourceName+1
sty P8ZP_SCRATCH_W2+1
${if (ldaInstructionArg == null) "" else "lda $ldaInstructionArg"}
sta (P8ZP_SCRATCH_W2)""")
}
} else {
if (isZpVar(scopedName)) {
// pointervar is already in the zero page, no need to copy
if (ldaInstructionArg != null)
out(" lda $ldaInstructionArg")
out(" ldy #0 | sta ($sourceName),y")
} else {
out("""
ldy $sourceName
sty P8ZP_SCRATCH_W2
ldy $sourceName+1
sty P8ZP_SCRATCH_W2+1
${if (ldaInstructionArg == null) "" else "lda $ldaInstructionArg"}
ldy #0
sta (P8ZP_SCRATCH_W2),y""")
}
}
}
private fun fixNameSymbols(name: String) = name.replace("<", "prog8_").replace(">", "") // take care of the autogenerated invalid (anon) label names
internal fun saveRegisterLocal(register: CpuRegister, scope: Subroutine) {
@ -1369,4 +1331,85 @@ $label nop""")
val vardecl = variable.targetVarDecl(program.namespace)!!
return vardecl.makeScopedName(vardecl.name) in allocatedZeropageVariables
}
internal fun pointerViaIndexRegisterPossible(pointerOffsetExpr: Expression): Pair<Expression, Expression>? {
if(pointerOffsetExpr is BinaryExpression && pointerOffsetExpr.operator=="+") {
val leftDt = pointerOffsetExpr.left.inferType(program)
val rightDt = pointerOffsetExpr.left.inferType(program)
if(leftDt.istype(DataType.UWORD) && rightDt.istype(DataType.UBYTE))
return Pair(pointerOffsetExpr.left, pointerOffsetExpr.right)
if(leftDt.istype(DataType.UBYTE) && rightDt.istype(DataType.UWORD))
return Pair(pointerOffsetExpr.right, pointerOffsetExpr.left)
if(leftDt.istype(DataType.UWORD) && rightDt.istype(DataType.UWORD)) {
// could be that the index was a constant numeric byte but converted to word, check that
val constIdx = pointerOffsetExpr.right.constValue(program)
if(constIdx!=null && constIdx.number.toInt()>=0 && constIdx.number.toInt()<=255) {
return Pair(pointerOffsetExpr.left, NumericLiteralValue(DataType.UBYTE, constIdx.number, constIdx.position))
}
// could be that the index was type casted into uword, check that
val rightTc = pointerOffsetExpr.right as? TypecastExpression
if(rightTc!=null && rightTc.expression.inferType(program).istype(DataType.UBYTE))
return Pair(pointerOffsetExpr.left, rightTc.expression)
val leftTc = pointerOffsetExpr.left as? TypecastExpression
if(leftTc!=null && leftTc.expression.inferType(program).istype(DataType.UBYTE))
return Pair(pointerOffsetExpr.right, leftTc.expression)
}
}
return null
}
internal fun tryOptimizedPointerAccessWithA(expr: BinaryExpression, write: Boolean): Boolean {
// optimize pointer,indexregister if possible
if(expr.operator=="+") {
val ptrAndIndex = pointerViaIndexRegisterPossible(expr)
if(ptrAndIndex!=null) {
val pointervar = ptrAndIndex.first as? IdentifierReference
if(write) {
when(ptrAndIndex.second) {
is NumericLiteralValue, is IdentifierReference -> {
if(pointervar!=null && isZpVar(pointervar)) {
assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.Y)
out(" sta (${asmSymbolName(pointervar)}),y")
} else {
// copy the pointer var to zp first
assignExpressionToVariable(ptrAndIndex.first, asmVariableName("P8ZP_SCRATCH_W2"), DataType.UWORD, null)
assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.Y)
out(" sta (P8ZP_SCRATCH_W2),y")
}
}
else -> {
// same as above but we need to save the A register
if(pointervar!=null && isZpVar(pointervar)) {
out(" pha")
assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.Y)
out(" pla")
out(" sta (${asmSymbolName(pointervar)}),y")
} else {
// copy the pointer var to zp first
assignExpressionToVariable(ptrAndIndex.first, asmVariableName("P8ZP_SCRATCH_W2"), DataType.UWORD, null)
out(" pha")
assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.Y)
out(" pla")
out(" sta (P8ZP_SCRATCH_W2),y")
}
}
}
} else {
if(pointervar!=null && isZpVar(pointervar)) {
assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.Y)
out(" lda (${asmSymbolName(pointervar)}),y")
} else {
// copy the pointer var to zp first
assignExpressionToVariable(ptrAndIndex.first, asmVariableName("P8ZP_SCRATCH_W2"), DataType.UWORD, null)
assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.Y)
out(" lda (P8ZP_SCRATCH_W2),y")
}
}
return true
}
}
return false
}
}

View File

@ -240,11 +240,23 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
val number = (what.addressExpression as NumericLiteralValue).number
asmgen.out(" ror ${number.toHex()}")
} else {
asmgen.assignExpressionToRegister(what.addressExpression, RegisterOrPair.AY)
asmgen.out("""
sta (+) + 1
sty (+) + 2
+ ror ${'$'}ffff ; modified""")
val ptrAndIndex = asmgen.pointerViaIndexRegisterPossible(what.addressExpression)
if(ptrAndIndex!=null) {
asmgen.assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.X)
asmgen.saveRegisterLocal(CpuRegister.X, (fcall as FunctionCallStatement).definingSubroutine()!!)
asmgen.assignExpressionToRegister(ptrAndIndex.first, RegisterOrPair.AY)
asmgen.restoreRegisterLocal(CpuRegister.X)
asmgen.out("""
sta (+) + 1
sty (+) + 2
+ ror ${'$'}ffff,x ; modified""")
} else {
asmgen.assignExpressionToRegister(what.addressExpression, RegisterOrPair.AY)
asmgen.out("""
sta (+) + 1
sty (+) + 2
+ ror ${'$'}ffff ; modified""")
}
}
}
is IdentifierReference -> {
@ -329,11 +341,23 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
val number = (what.addressExpression as NumericLiteralValue).number
asmgen.out(" rol ${number.toHex()}")
} else {
asmgen.assignExpressionToRegister(what.addressExpression, RegisterOrPair.AY)
asmgen.out("""
sta (+) + 1
sty (+) + 2
+ rol ${'$'}ffff ; modified""")
val ptrAndIndex = asmgen.pointerViaIndexRegisterPossible(what.addressExpression)
if(ptrAndIndex!=null) {
asmgen.assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.X)
asmgen.saveRegisterLocal(CpuRegister.X, (fcall as FunctionCallStatement).definingSubroutine()!!)
asmgen.assignExpressionToRegister(ptrAndIndex.first, RegisterOrPair.AY)
asmgen.restoreRegisterLocal(CpuRegister.X)
asmgen.out("""
sta (+) + 1
sty (+) + 2
+ rol ${'$'}ffff,x ; modified""")
} else {
asmgen.assignExpressionToRegister(what.addressExpression, RegisterOrPair.AY)
asmgen.out("""
sta (+) + 1
sty (+) + 2
+ rol ${'$'}ffff ; modified""")
}
}
}
is IdentifierReference -> {
@ -510,6 +534,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
// optimized simple case: swap two memory locations
if(first is DirectMemoryRead && second is DirectMemoryRead) {
// TODO optimize swap of two memread values with index, using the same pointer expression/variable, like swap(@(ptr+1), @(ptr+2))
val addr1 = (first.addressExpression as? NumericLiteralValue)?.number?.toHex()
val addr2 = (second.addressExpression as? NumericLiteralValue)?.number?.toHex()
val name1 = if(first.addressExpression is IdentifierReference) asmgen.asmVariableName(first.addressExpression as IdentifierReference) else null

View File

@ -1462,51 +1462,19 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
asmgen.assignExpressionToVariable(expr.addressExpression, asmgen.asmVariableName("P8ZP_SCRATCH_W2"), DataType.UWORD, null)
if (CompilationTarget.instance.machine.cpu == CpuType.CPU65c02) {
if (pushResultOnEstack) {
asmgen.out(" dex | lda (P8ZP_SCRATCH_W2) | sta P8ESTACK_LO+1,x")
asmgen.out(" lda (P8ZP_SCRATCH_W2) | dex | sta P8ESTACK_LO+1,x")
} else {
asmgen.out(" lda (P8ZP_SCRATCH_W2)")
}
} else {
if (pushResultOnEstack) {
asmgen.out(" dex | ldy #0 | lda (P8ZP_SCRATCH_W2),y | sta P8ESTACK_LO+1,x")
asmgen.out(" ldy #0 | lda (P8ZP_SCRATCH_W2),y | dex | sta P8ESTACK_LO+1,x")
} else {
asmgen.out(" ldy #0 | lda (P8ZP_SCRATCH_W2),y")
}
}
}
fun tryOptimizedPointerAccess(expr: BinaryExpression): Boolean {
// try to optimize simple cases like assignment from ptr+1, ptr+2...
if(expr.operator=="+") {
// we can assume const operand has been moved to the right.
val constOperand = expr.right.constValue(program)
if(constOperand!=null) {
val intIndex = constOperand.number.toInt()
if(intIndex in 1..255) {
val idref = expr.left as? IdentifierReference
if(idref!=null && asmgen.isZpVar(idref)) {
// pointer var is already in zp, we can indirect index immediately
if (pushResultOnEstack) {
asmgen.out(" dex | ldy #${intIndex} | lda (${asmgen.asmSymbolName(idref)}),y | sta P8ESTACK_LO+1,x")
} else {
asmgen.out(" ldy #${intIndex} | lda (${asmgen.asmSymbolName(idref)}),y")
}
} else {
// copy the pointer var to zp first
asmgen.assignExpressionToVariable(expr.left, asmgen.asmVariableName("P8ZP_SCRATCH_W2"), DataType.UWORD, null)
if (pushResultOnEstack) {
asmgen.out(" dex | ldy #${intIndex} | lda (P8ZP_SCRATCH_W2),y | sta P8ESTACK_LO+1,x")
} else {
asmgen.out(" ldy #${intIndex} | lda (P8ZP_SCRATCH_W2),y")
}
}
return true
}
}
}
return false
}
when(expr.addressExpression) {
is NumericLiteralValue -> {
val address = (expr.addressExpression as NumericLiteralValue).number.toInt()
@ -1521,8 +1489,12 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
asmgen.out(" sta P8ESTACK_LO,x | dex")
}
is BinaryExpression -> {
if(!tryOptimizedPointerAccess(expr.addressExpression as BinaryExpression))
if(asmgen.tryOptimizedPointerAccessWithA(expr.addressExpression as BinaryExpression, false)) {
if(pushResultOnEstack)
asmgen.out(" sta P8ESTACK_LO,x | dex")
} else {
assignViaExprEval()
}
}
else -> assignViaExprEval()
}

View File

@ -121,31 +121,6 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
assignRegisterByte(assign.target, CpuRegister.A)
}
fun tryOptimizedPointerAccess(expr: BinaryExpression): Boolean {
// try to optimize simple cases like assignment from ptr+1, ptr+2...
if(expr.operator=="+") {
// we can assume const operand has been moved to the right.
val constOperand = expr.right.constValue(program)
if(constOperand!=null) {
val intIndex = constOperand.number.toInt()
if(intIndex in 1..255) {
val idref = expr.left as? IdentifierReference
if(idref!=null && asmgen.isZpVar(idref)) {
// pointer var is already in zp, we can indirect index immediately
asmgen.out(" ldy #${intIndex} | lda (${asmgen.asmSymbolName(idref)}),y")
} else {
// copy the pointer var to zp first
assignExpressionToVariable(expr.left, asmgen.asmVariableName("P8ZP_SCRATCH_W2"), DataType.UWORD, assign.target.scope)
asmgen.out(" ldy #${intIndex} | lda (P8ZP_SCRATCH_W2),y")
}
assignRegisterByte(assign.target, CpuRegister.A)
return true
}
}
}
return false
}
val value = assign.source.memory!!
when (value.addressExpression) {
is NumericLiteralValue -> {
@ -156,8 +131,11 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
assignMemoryByte(assign.target, null, value.addressExpression as IdentifierReference)
}
is BinaryExpression -> {
if(!tryOptimizedPointerAccess(value.addressExpression as BinaryExpression))
if(asmgen.tryOptimizedPointerAccessWithA(value.addressExpression as BinaryExpression, false)) {
assignRegisterByte(assign.target, CpuRegister.A)
} else {
assignViaExprEval(value.addressExpression)
}
}
else -> assignViaExprEval(value.addressExpression)
}
@ -331,14 +309,37 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
}
is DirectMemoryRead -> {
if(targetDt in WordDatatypes) {
if (value.addressExpression is NumericLiteralValue) {
val address = (value.addressExpression as NumericLiteralValue).number.toInt()
assignMemoryByteIntoWord(target, address, null)
return
fun assignViaExprEval(addressExpression: Expression) {
asmgen.assignExpressionToVariable(addressExpression, asmgen.asmVariableName("P8ZP_SCRATCH_W2"), DataType.UWORD, null)
if (CompilationTarget.instance.machine.cpu == CpuType.CPU65c02)
asmgen.out(" lda (P8ZP_SCRATCH_W2)")
else
asmgen.out(" ldy #0 | lda (P8ZP_SCRATCH_W2),y")
assignRegisterByte(target, CpuRegister.A)
}
else if (value.addressExpression is IdentifierReference) {
assignMemoryByteIntoWord(target, null, value.addressExpression as IdentifierReference)
return
when (value.addressExpression) {
is NumericLiteralValue -> {
val address = (value.addressExpression as NumericLiteralValue).number.toInt()
assignMemoryByteIntoWord(target, address, null)
return
}
is IdentifierReference -> {
assignMemoryByteIntoWord(target, null, value.addressExpression as IdentifierReference)
return
}
is BinaryExpression -> {
if(asmgen.tryOptimizedPointerAccessWithA(value.addressExpression as BinaryExpression, false)) {
asmgen.out(" ldy #0")
assignRegisterpairWord(target, RegisterOrPair.AY)
} else {
assignViaExprEval(value.addressExpression)
}
}
else -> {
assignViaExprEval(value.addressExpression)
}
}
}
}
@ -749,8 +750,8 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
}
}
TargetStorageKind.MEMORY -> {
asmgen.out(" inx")
storeByteViaRegisterAInMemoryAddress("P8ESTACK_LO,x", target.memory!!)
asmgen.out(" inx | lda P8ESTACK_LO,x")
storeRegisterAInMemoryAddress(target.memory!!)
}
TargetStorageKind.ARRAY -> {
if(target.constArrayIndexValue!=null) {
@ -1178,7 +1179,8 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
""")
}
TargetStorageKind.MEMORY -> {
storeByteViaRegisterAInMemoryAddress(sourceName, target.memory!!)
asmgen.out(" lda $sourceName")
storeRegisterAInMemoryAddress(target.memory!!)
}
TargetStorageKind.ARRAY -> {
if (target.constArrayIndexValue!=null) {
@ -1338,7 +1340,12 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
asmgen.out(" st${register.name.toLowerCase()} ${target.asmVarname}")
}
TargetStorageKind.MEMORY -> {
storeRegisterInMemoryAddress(register, target.memory!!)
when(register) {
CpuRegister.A -> {}
CpuRegister.X -> asmgen.out(" txa")
CpuRegister.Y -> asmgen.out(" tya")
}
storeRegisterAInMemoryAddress(target.memory!!)
}
TargetStorageKind.ARRAY -> {
if (target.constArrayIndexValue!=null) {
@ -1608,7 +1615,8 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
asmgen.out(" stz ${target.asmVarname} ")
}
TargetStorageKind.MEMORY -> {
storeByteViaRegisterAInMemoryAddress("#${byte.toHex()}", target.memory!!)
asmgen.out(" lda #${byte.toHex()}")
storeRegisterAInMemoryAddress(target.memory!!)
}
TargetStorageKind.ARRAY -> {
if (target.constArrayIndexValue!=null) {
@ -1647,7 +1655,8 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
asmgen.out(" lda #${byte.toHex()} | sta ${target.asmVarname} ")
}
TargetStorageKind.MEMORY -> {
storeByteViaRegisterAInMemoryAddress("#${byte.toHex()}", target.memory!!)
asmgen.out(" lda #${byte.toHex()}")
storeRegisterAInMemoryAddress(target.memory!!)
}
TargetStorageKind.ARRAY -> {
if (target.constArrayIndexValue!=null) {
@ -1831,7 +1840,8 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
""")
}
TargetStorageKind.MEMORY -> {
storeByteViaRegisterAInMemoryAddress(address.toHex(), target.memory!!)
asmgen.out(" lda ${address.toHex()}")
storeRegisterAInMemoryAddress(target.memory!!)
}
TargetStorageKind.ARRAY -> {
throw AssemblyError("no asm gen for assign memory byte at $address to array ${target.asmVarname}")
@ -1869,7 +1879,8 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
}
TargetStorageKind.MEMORY -> {
val sourceName = asmgen.asmVariableName(identifier)
storeByteViaRegisterAInMemoryAddress(sourceName, target.memory!!)
asmgen.out(" lda $sourceName")
storeRegisterAInMemoryAddress(target.memory!!)
}
TargetStorageKind.ARRAY -> {
throw AssemblyError("no asm gen for assign memory byte $identifier to array ${target.asmVarname} ")
@ -1965,117 +1976,73 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
}
}
private fun storeByteViaRegisterAInMemoryAddress(ldaInstructionArg: String, memoryAddress: DirectMemoryWrite) {
private fun storeRegisterAInMemoryAddress(memoryAddress: DirectMemoryWrite) {
val addressExpr = memoryAddress.addressExpression
val addressLv = addressExpr as? NumericLiteralValue
fun storeViaExprEval() {
assignExpressionToVariable(addressExpr, asmgen.asmVariableName("P8ZP_SCRATCH_W2"), DataType.UWORD, null)
if (CompilationTarget.instance.machine.cpu == CpuType.CPU65c02)
asmgen.out(" lda $ldaInstructionArg | sta (P8ZP_SCRATCH_W2)")
else
asmgen.out(" lda $ldaInstructionArg | ldy #0 | sta (P8ZP_SCRATCH_W2),y")
}
fun tryOptimizedPointerAccess(expr: BinaryExpression): Boolean {
// try to optimize simple cases like storing value to ptr+1, ptr+2...
if(expr.operator=="+") {
// we can assume const operand has been moved to the right.
val constOperand = expr.right.constValue(program)
if(constOperand!=null) {
val intIndex = constOperand.number.toInt()
if(intIndex in 1..255) {
val idref = expr.left as? IdentifierReference
if(idref!=null && asmgen.isZpVar(idref)) {
// pointer var is already in zp, we can indirect index immediately
asmgen.out(" lda $ldaInstructionArg | ldy #${intIndex} | sta (${asmgen.asmSymbolName(idref)}),y")
} else {
// copy the pointer var to zp first
assignExpressionToVariable(expr.left, asmgen.asmVariableName("P8ZP_SCRATCH_W2"), DataType.UWORD, null)
asmgen.out(" lda $ldaInstructionArg | ldy #${intIndex} | sta (P8ZP_SCRATCH_W2),y")
}
return true
}
when(addressExpr) {
is NumericLiteralValue, is IdentifierReference -> {
assignExpressionToVariable(addressExpr, asmgen.asmVariableName("P8ZP_SCRATCH_W2"), DataType.UWORD, null)
if (CompilationTarget.instance.machine.cpu == CpuType.CPU65c02)
asmgen.out(" sta (P8ZP_SCRATCH_W2)")
else
asmgen.out(" ldy #0 | sta (P8ZP_SCRATCH_W2),y")
}
else -> {
// same as above but we need to save the A register
asmgen.out(" pha")
assignExpressionToVariable(addressExpr, asmgen.asmVariableName("P8ZP_SCRATCH_W2"), DataType.UWORD, null)
asmgen.out(" pla")
if (CompilationTarget.instance.machine.cpu == CpuType.CPU65c02)
asmgen.out(" sta (P8ZP_SCRATCH_W2)")
else
asmgen.out(" ldy #0 | sta (P8ZP_SCRATCH_W2),y")
}
}
}
fun storeAIntoPointerVar(pointervar: IdentifierReference) {
val sourceName = asmgen.asmVariableName(pointervar)
val vardecl = pointervar.targetVarDecl(program.namespace)!!
val scopedName = vardecl.makeScopedName(vardecl.name)
if (CompilationTarget.instance.machine.cpu == CpuType.CPU65c02) {
if (asmgen.isZpVar(scopedName)) {
// pointervar is already in the zero page, no need to copy
asmgen.out(" sta ($sourceName)")
} else {
asmgen.out("""
ldy $sourceName
sty P8ZP_SCRATCH_W2
ldy $sourceName+1
sty P8ZP_SCRATCH_W2+1
sta (P8ZP_SCRATCH_W2)""")
}
} else {
if (asmgen.isZpVar(scopedName)) {
// pointervar is already in the zero page, no need to copy
asmgen.out(" ldy #0 | sta ($sourceName),y")
} else {
asmgen.out("""
ldy $sourceName
sty P8ZP_SCRATCH_W2
ldy $sourceName+1
sty P8ZP_SCRATCH_W2+1
ldy #0
sta (P8ZP_SCRATCH_W2),y""")
}
}
return false
}
when {
addressLv != null -> {
asmgen.out(" lda $ldaInstructionArg | sta ${addressLv.number.toHex()}")
asmgen.out(" sta ${addressLv.number.toHex()}")
}
addressExpr is IdentifierReference -> {
asmgen.storeByteIntoPointer(addressExpr, ldaInstructionArg)
storeAIntoPointerVar(addressExpr)
}
addressExpr is BinaryExpression -> {
if(!tryOptimizedPointerAccess(addressExpr))
storeViaExprEval()
}
else -> storeViaExprEval()
}
}
private fun storeRegisterInMemoryAddress(register: CpuRegister, memoryAddress: DirectMemoryWrite) {
// this is optimized for register A.
val addressExpr = memoryAddress.addressExpression
val addressLv = addressExpr as? NumericLiteralValue
val registerName = register.name.toLowerCase()
fun storeViaExprEval() {
asmgen.saveRegisterStack(register, false)
assignExpressionToVariable(addressExpr, asmgen.asmVariableName("P8ZP_SCRATCH_W2"), DataType.UWORD, null)
asmgen.restoreRegisterStack(CpuRegister.A, false)
if (CompilationTarget.instance.machine.cpu == CpuType.CPU65c02)
asmgen.out(" sta (P8ZP_SCRATCH_W2)")
else
asmgen.out(" ldy #0 | sta (P8ZP_SCRATCH_W2),y")
}
fun tryOptimizedPointerAccess(expr: BinaryExpression): Boolean {
// try to optimize simple cases like storing value to ptr+1, ptr+2...
if(expr.operator=="+") {
// we can assume const operand has been moved to the right.
val constOperand = expr.right.constValue(program)
if(constOperand!=null) {
val intIndex = constOperand.number.toInt()
if(intIndex in 1..255) {
val idref = expr.left as? IdentifierReference
if(idref!=null && asmgen.isZpVar(idref)) {
// pointer var is already in zp, we can indirect index immediately
when(register) {
CpuRegister.A -> asmgen.out(" ldy #${intIndex} | sta (${asmgen.asmSymbolName(idref)}),y")
CpuRegister.X -> asmgen.out(" txa | ldy #${intIndex} | sta (${asmgen.asmSymbolName(idref)}),y")
CpuRegister.Y -> asmgen.out(" tya | ldy #${intIndex} | sta (${asmgen.asmSymbolName(idref)}),y")
}
} else {
// copy the pointer var to zp first
asmgen.saveRegisterStack(register, false)
assignExpressionToVariable(expr.left, asmgen.asmVariableName("P8ZP_SCRATCH_W2"), DataType.UWORD, null)
asmgen.restoreRegisterStack(CpuRegister.A, false)
asmgen.out(" ldy #${intIndex} | sta (P8ZP_SCRATCH_W2),y")
}
return true
}
}
}
return false
}
when {
addressLv != null -> {
asmgen.out(" st$registerName ${addressLv.number.toHex()}")
}
addressExpr is IdentifierReference -> {
when (register) {
CpuRegister.A -> {}
CpuRegister.X -> asmgen.out(" txa")
CpuRegister.Y -> asmgen.out(" tya")
}
asmgen.storeByteIntoPointer(addressExpr, null)
}
addressExpr is BinaryExpression -> {
if(!tryOptimizedPointerAccess(addressExpr))
if(!asmgen.tryOptimizedPointerAccessWithA(addressExpr, true))
storeViaExprEval()
}
else -> storeViaExprEval()

View File

@ -4,14 +4,20 @@ TODO
- why is fibonacci example (and others) generating larger code now? fix?
- fix textelite saving (and loading?)
- allow uwordpointer[index] syntax -> transform into @(uwordpointer+index) allow index to be >255!
- add any2(), all2(), max2(), min2(), reverse2(), sum2(), sort2() that take (array, startindex, length) arguments
- optimize for loop iterations better to allow proper inx, cpx #value, bne loop instructions (like repeat loop)
- why is there a beq _prog8_label_2_repeatend at the end of repeat loops? seems unused
- optimize swap of two memread values with index, using the same pointer expression/variable, like swap(@(ptr+1), @(ptr+2))
- can we get rid of the --longOptionName command line options and only keep the short versions? https://github.com/Kotlin/kotlinx-cli/issues/50
- add a f_seek() routine for the Cx16 that uses its seek dos api?
- add a compiler option to generate a symbol listing at the end
- optimizer: detect variables that are written but never read - mark those as unused too and remove them, such as uword unused = memory("unused222", 20) - also remove the memory slab allocation
- add a compiler option to not remove unused subroutines. this allows for building library programs
- hoist all variable declarations up to the subroutine scope *before* even the constant folding takes place (to avoid undefined symbol errors when referring to a variable from another nested scope in the subroutine)
- make it possible to use cpu opcodes such as 'nop' as variable names by prefixing all asm vars with something such as '_'
- option to load the built-in library files from a directory instead of the embedded ones (for easier library development/debugging)
- c64: use VIC banking to move up the graphics bitmap memory location. Move it to $e000 under the kernal rom?
- c64: make the graphics.BITMAP_ADDRESS configurable
- some support for recursive subroutines?
- via %option recursive?: allocate all params and local vars on estack, don't allow nested subroutines, can begin by first not allowing any local variables just fixing the parameters
- Or via a special recursive call operation that copies the current values of all local vars (including arguments) to the stack, replaces the arguments, jsr subroutine, and after returning copy the stack back to the local variables

View File

@ -1,496 +0,0 @@
from collections import Counter
from enum import IntEnum
class AddrMode(IntEnum):
Imp = 1,
Acc = 2,
Imm = 3,
Zp = 4,
ZpX = 5,
ZpY = 6,
Rel = 7,
Abs = 8,
AbsX = 9,
AbsY = 10,
Ind = 11,
IzX = 12,
IzY = 13,
Zpr = 14,
Izp = 15,
IaX = 16
AllInstructions = [
(0x00, "brk", AddrMode.Imp),
(0x01, "ora", AddrMode.IzX),
(0x02, "nop", AddrMode.Imm),
(0x03, "nop", AddrMode.Imp),
(0x04, "tsb", AddrMode.Zp),
(0x05, "ora", AddrMode.Zp),
(0x06, "asl", AddrMode.Zp),
(0x07, "rmb0", AddrMode.Zp),
(0x08, "php", AddrMode.Imp),
(0x09, "ora", AddrMode.Imm),
(0x0a, "asl", AddrMode.Acc),
(0x0b, "nop", AddrMode.Imp),
(0x0c, "tsb", AddrMode.Abs),
(0x0d, "ora", AddrMode.Abs),
(0x0e, "asl", AddrMode.Abs),
(0x0f, "bbr0", AddrMode.Zpr),
(0x10, "bpl", AddrMode.Rel),
(0x11, "ora", AddrMode.IzY),
(0x12, "ora", AddrMode.Izp),
(0x13, "nop", AddrMode.Imp),
(0x14, "trb", AddrMode.Zp),
(0x15, "ora", AddrMode.ZpX),
(0x16, "asl", AddrMode.ZpX),
(0x17, "rmb1", AddrMode.Zp),
(0x18, "clc", AddrMode.Imp),
(0x19, "ora", AddrMode.AbsY),
(0x1a, "inc", AddrMode.Acc),
(0x1b, "nop", AddrMode.Imp),
(0x1c, "trb", AddrMode.Abs),
(0x1d, "ora", AddrMode.AbsX),
(0x1e, "asl", AddrMode.AbsX),
(0x1f, "bbr1", AddrMode.Zpr),
(0x20, "jsr", AddrMode.Abs),
(0x21, "and", AddrMode.IzX),
(0x22, "nop", AddrMode.Imm),
(0x23, "nop", AddrMode.Imp),
(0x24, "bit", AddrMode.Zp),
(0x25, "and", AddrMode.Zp),
(0x26, "rol", AddrMode.Zp),
(0x27, "rmb2", AddrMode.Zp),
(0x28, "plp", AddrMode.Imp),
(0x29, "and", AddrMode.Imm),
(0x2a, "rol", AddrMode.Acc),
(0x2b, "nop", AddrMode.Imp),
(0x2c, "bit", AddrMode.Abs),
(0x2d, "and", AddrMode.Abs),
(0x2e, "rol", AddrMode.Abs),
(0x2f, "bbr2", AddrMode.Zpr),
(0x30, "bmi", AddrMode.Rel),
(0x31, "and", AddrMode.IzY),
(0x32, "and", AddrMode.Izp),
(0x33, "nop", AddrMode.Imp),
(0x34, "bit", AddrMode.ZpX),
(0x35, "and", AddrMode.ZpX),
(0x36, "rol", AddrMode.ZpX),
(0x37, "rmb3", AddrMode.Zp),
(0x38, "sec", AddrMode.Imp),
(0x39, "and", AddrMode.AbsY),
(0x3a, "dec", AddrMode.Acc),
(0x3b, "nop", AddrMode.Imp),
(0x3c, "bit", AddrMode.AbsX),
(0x3d, "and", AddrMode.AbsX),
(0x3e, "rol", AddrMode.AbsX),
(0x3f, "bbr3", AddrMode.Zpr),
(0x40, "rti", AddrMode.Imp),
(0x41, "eor", AddrMode.IzX),
(0x42, "nop", AddrMode.Imm),
(0x43, "nop", AddrMode.Imp),
(0x44, "nop", AddrMode.Zp),
(0x45, "eor", AddrMode.Zp),
(0x46, "lsr", AddrMode.Zp),
(0x47, "rmb4", AddrMode.Zp),
(0x48, "pha", AddrMode.Imp),
(0x49, "eor", AddrMode.Imm),
(0x4a, "lsr", AddrMode.Acc),
(0x4b, "nop", AddrMode.Imp),
(0x4c, "jmp", AddrMode.Abs),
(0x4d, "eor", AddrMode.Abs),
(0x4e, "lsr", AddrMode.Abs),
(0x4f, "bbr4", AddrMode.Zpr),
(0x50, "bvc", AddrMode.Rel),
(0x51, "eor", AddrMode.IzY),
(0x52, "eor", AddrMode.Izp),
(0x53, "nop", AddrMode.Imp),
(0x54, "nop", AddrMode.ZpX),
(0x55, "eor", AddrMode.ZpX),
(0x56, "lsr", AddrMode.ZpX),
(0x57, "rmb5", AddrMode.Zp),
(0x58, "cli", AddrMode.Imp),
(0x59, "eor", AddrMode.AbsY),
(0x5a, "phy", AddrMode.Imp),
(0x5b, "nop", AddrMode.Imp),
(0x5c, "nop", AddrMode.Abs),
(0x5d, "eor", AddrMode.AbsX),
(0x5e, "lsr", AddrMode.AbsX),
(0x5f, "bbr5", AddrMode.Zpr),
(0x60, "rts", AddrMode.Imp),
(0x61, "adc", AddrMode.IzX),
(0x62, "nop", AddrMode.Imm),
(0x63, "nop", AddrMode.Imp),
(0x64, "stz", AddrMode.Zp),
(0x65, "adc", AddrMode.Zp),
(0x66, "ror", AddrMode.Zp),
(0x67, "rmb6", AddrMode.Zp),
(0x68, "pla", AddrMode.Imp),
(0x69, "adc", AddrMode.Imm),
(0x6a, "ror", AddrMode.Acc),
(0x6b, "nop", AddrMode.Imp),
(0x6c, "jmp", AddrMode.Ind),
(0x6d, "adc", AddrMode.Abs),
(0x6e, "ror", AddrMode.Abs),
(0x6f, "bbr6", AddrMode.Zpr),
(0x70, "bvs", AddrMode.Rel),
(0x71, "adc", AddrMode.IzY),
(0x72, "adc", AddrMode.Izp),
(0x73, "nop", AddrMode.Imp),
(0x74, "stz", AddrMode.ZpX),
(0x75, "adc", AddrMode.ZpX),
(0x76, "ror", AddrMode.ZpX),
(0x77, "rmb7", AddrMode.Zp),
(0x78, "sei", AddrMode.Imp),
(0x79, "adc", AddrMode.AbsY),
(0x7a, "ply", AddrMode.Imp),
(0x7b, "nop", AddrMode.Imp),
(0x7c, "jmp", AddrMode.IaX),
(0x7d, "adc", AddrMode.AbsX),
(0x7e, "ror", AddrMode.AbsX),
(0x7f, "bbr7", AddrMode.Zpr),
(0x80, "bra", AddrMode.Rel),
(0x81, "sta", AddrMode.IzX),
(0x82, "nop", AddrMode.Imm),
(0x83, "nop", AddrMode.Imp),
(0x84, "sty", AddrMode.Zp),
(0x85, "sta", AddrMode.Zp),
(0x86, "stx", AddrMode.Zp),
(0x87, "smb0", AddrMode.Zp),
(0x88, "dey", AddrMode.Imp),
(0x89, "bit", AddrMode.Imm),
(0x8a, "txa", AddrMode.Imp),
(0x8b, "nop", AddrMode.Imp),
(0x8c, "sty", AddrMode.Abs),
(0x8d, "sta", AddrMode.Abs),
(0x8e, "stx", AddrMode.Abs),
(0x8f, "bbs0", AddrMode.Zpr),
(0x90, "bcc", AddrMode.Rel),
(0x91, "sta", AddrMode.IzY),
(0x92, "sta", AddrMode.Izp),
(0x93, "nop", AddrMode.Imp),
(0x94, "sty", AddrMode.ZpX),
(0x95, "sta", AddrMode.ZpX),
(0x96, "stx", AddrMode.ZpY),
(0x97, "smb1", AddrMode.Zp),
(0x98, "tya", AddrMode.Imp),
(0x99, "sta", AddrMode.AbsY),
(0x9a, "txs", AddrMode.Imp),
(0x9b, "nop", AddrMode.Imp),
(0x9c, "stz", AddrMode.Abs),
(0x9d, "sta", AddrMode.AbsX),
(0x9e, "stz", AddrMode.AbsX),
(0x9f, "bbs1", AddrMode.Zpr),
(0xa0, "ldy", AddrMode.Imm),
(0xa1, "lda", AddrMode.IzX),
(0xa2, "ldx", AddrMode.Imm),
(0xa3, "nop", AddrMode.Imp),
(0xa4, "ldy", AddrMode.Zp),
(0xa5, "lda", AddrMode.Zp),
(0xa6, "ldx", AddrMode.Zp),
(0xa7, "smb2", AddrMode.Zp),
(0xa8, "tay", AddrMode.Imp),
(0xa9, "lda", AddrMode.Imm),
(0xaa, "tax", AddrMode.Imp),
(0xab, "nop", AddrMode.Imp),
(0xac, "ldy", AddrMode.Abs),
(0xad, "lda", AddrMode.Abs),
(0xae, "ldx", AddrMode.Abs),
(0xaf, "bbs2", AddrMode.Zpr),
(0xb0, "bcs", AddrMode.Rel),
(0xb1, "lda", AddrMode.IzY),
(0xb2, "lda", AddrMode.Izp),
(0xb3, "nop", AddrMode.Imp),
(0xb4, "ldy", AddrMode.ZpX),
(0xb5, "lda", AddrMode.ZpX),
(0xb6, "ldx", AddrMode.ZpY),
(0xb7, "smb3", AddrMode.Zp),
(0xb8, "clv", AddrMode.Imp),
(0xb9, "lda", AddrMode.AbsY),
(0xba, "tsx", AddrMode.Imp),
(0xbb, "nop", AddrMode.Imp),
(0xbc, "ldy", AddrMode.AbsX),
(0xbd, "lda", AddrMode.AbsX),
(0xbe, "ldx", AddrMode.AbsY),
(0xbf, "bbs3", AddrMode.Zpr),
(0xc0, "cpy", AddrMode.Imm),
(0xc1, "cmp", AddrMode.IzX),
(0xc2, "nop", AddrMode.Imm),
(0xc3, "nop", AddrMode.Imp),
(0xc4, "cpy", AddrMode.Zp),
(0xc5, "cmp", AddrMode.Zp),
(0xc6, "dec", AddrMode.Zp),
(0xc7, "smb4", AddrMode.Zp),
(0xc8, "iny", AddrMode.Imp),
(0xc9, "cmp", AddrMode.Imm),
(0xca, "dex", AddrMode.Imp),
(0xcb, "wai", AddrMode.Imp),
(0xcc, "cpy", AddrMode.Abs),
(0xcd, "cmp", AddrMode.Abs),
(0xce, "dec", AddrMode.Abs),
(0xcf, "bbs4", AddrMode.Zpr),
(0xd0, "bne", AddrMode.Rel),
(0xd1, "cmp", AddrMode.IzY),
(0xd2, "cmp", AddrMode.Izp),
(0xd3, "nop", AddrMode.Imp),
(0xd4, "nop", AddrMode.ZpX),
(0xd5, "cmp", AddrMode.ZpX),
(0xd6, "dec", AddrMode.ZpX),
(0xd7, "smb5", AddrMode.Zp),
(0xd8, "cld", AddrMode.Imp),
(0xd9, "cmp", AddrMode.AbsY),
(0xda, "phx", AddrMode.Imp),
(0xdb, "stp", AddrMode.Imp),
(0xdc, "nop", AddrMode.Abs),
(0xdd, "cmp", AddrMode.AbsX),
(0xde, "dec", AddrMode.AbsX),
(0xdf, "bbs5", AddrMode.Zpr),
(0xe0, "cpx", AddrMode.Imm),
(0xe1, "sbc", AddrMode.IzX),
(0xe2, "nop", AddrMode.Imm),
(0xe3, "nop", AddrMode.Imp),
(0xe4, "cpx", AddrMode.Zp),
(0xe5, "sbc", AddrMode.Zp),
(0xe6, "inc", AddrMode.Zp),
(0xe7, "smb6", AddrMode.Zp),
(0xe8, "inx", AddrMode.Imp),
(0xe9, "sbc", AddrMode.Imm),
(0xea, "nop", AddrMode.Imp),
(0xeb, "nop", AddrMode.Imp),
(0xec, "cpx", AddrMode.Abs),
(0xed, "sbc", AddrMode.Abs),
(0xee, "inc", AddrMode.Abs),
(0xef, "bbs6", AddrMode.Zpr),
(0xf0, "beq", AddrMode.Rel),
(0xf1, "sbc", AddrMode.IzY),
(0xf2, "sbc", AddrMode.Izp),
(0xf3, "nop", AddrMode.Imp),
(0xf4, "nop", AddrMode.ZpX),
(0xf5, "sbc", AddrMode.ZpX),
(0xf6, "inc", AddrMode.ZpX),
(0xf7, "smb7", AddrMode.Zp),
(0xf8, "sed", AddrMode.Imp),
(0xf9, "sbc", AddrMode.AbsY),
(0xfa, "plx", AddrMode.Imp),
(0xfb, "nop", AddrMode.Imp),
(0xfc, "nop", AddrMode.AbsX),
(0xfd, "sbc", AddrMode.AbsX),
(0xfe, "inc", AddrMode.AbsX),
(0xff, "bbs7", AddrMode.Zpr)
]
# NOP is weird, it is all over the place.
# For the 'common' immediate NOP, keep only the $EA opcode (this was the original NOP on the 6502)
Instructions = [ins for ins in AllInstructions if ins[1] != "nop"] + [(0xea, "nop", AddrMode.Imp)]
InstructionsByName = {}
for ins in Instructions:
if ins[1] not in InstructionsByName:
InstructionsByName[ins[1]] = {ins[2]: ins[0]}
else:
InstructionsByName[ins[1]][ins[2]] = ins[0]
InstructionsByMode = {}
for ins in Instructions:
if ins[2] not in InstructionsByMode:
InstructionsByMode[ins[2]] = [(ins[1], ins[0])]
else:
InstructionsByMode[ins[2]].append((ins[1], ins[0]))
# build the name->modes table
print("; generated by opcodes.py")
print("; addressing modes:")
for mode in AddrMode:
print(";", mode.value, "=", mode.name)
print()
print("""
.enc "petscii" ;define an ascii to petscii encoding
.cdef " @", 32 ;characters
.cdef "AZ", $c1
.cdef "az", $41
.cdef "[[", $5b
.cdef "]]", $5d
.edef "<nothing>", [];replace with no bytes
""")
for instr in sorted(InstructionsByName.items()):
print("i_" + instr[0] + ":\n\t.byte ", end="")
if len(instr[1]) == 1:
# many instructions have just 1 addressing mode, save space for those
info = instr[1].popitem()
print("1,", info[0].value,",", info[1])
else:
print("0, ", end='')
mode_opcodes = []
for mode in AddrMode:
if mode in instr[1]:
mode_opcodes.append(instr[1][mode])
else:
mode_opcodes.append(0)
print(",".join(str(o) for o in mode_opcodes), end="")
print()
def determine_mnemonics():
mnemonics = list(sorted(set(ins[1] for ins in Instructions)))
# opcodes histogram (ordered by occurrence) (in kernal + basic roms of the c64):
opcode_occurrences = [
(32, 839), (133, 502), (165, 488), (0, 429), (208, 426), (169, 390), (76, 324), (240, 322), (2, 314), (160, 245),
(96, 228), (3, 201), (1, 191), (255, 186), (144, 182), (170, 175), (162, 169), (177, 165), (104, 159), (164, 158),
(132, 157), (201, 156), (72, 151), (141, 150), (200, 146), (173, 144), (166, 139), (176, 139), (16, 138),
(134, 138), (73, 127), (24, 119), (101, 113), (69, 109), (13, 107), (34, 104), (145, 103), (4, 102), (168, 101),
(221, 98), (230, 93), (48, 91), (189, 87), (41, 86), (6, 86), (9, 86), (8, 85), (79, 85), (138, 80), (10, 80),
(7, 79), (185, 77), (56, 75), (44, 75), (78, 74), (105, 73), (5, 73), (174, 73), (220, 71), (198, 69), (232, 69),
(36, 69), (202, 67), (152, 67), (95, 67), (100, 65), (102, 65), (247, 65), (188, 64), (136, 64), (84, 64),
(122, 62), (128, 61), (80, 61), (186, 60), (82, 59), (97, 58), (15, 57), (70, 57), (229, 56), (19, 55), (40, 54),
(183, 54), (65, 54), (233, 53), (180, 53), (12, 53), (171, 53), (197, 53), (83, 52), (248, 52), (112, 51),
(237, 51), (89, 50), (11, 50), (158, 50), (74, 49), (224, 48), (20, 47), (238, 47), (108, 46), (234, 46),
(251, 46), (254, 46), (184, 45), (14, 44), (163, 44), (226, 43), (211, 43), (88, 43), (98, 42), (17, 42),
(153, 42), (243, 41), (228, 41), (99, 41), (253, 41), (209, 41), (187, 39), (123, 39), (67, 39), (196, 38),
(68, 38), (35, 38), (172, 38), (175, 38), (161, 38), (85, 38), (191, 37), (113, 37), (182, 37), (151, 37),
(71, 36), (181, 35), (214, 35), (121, 35), (157, 35), (178, 35), (77, 35), (42, 34), (212, 33), (18, 33),
(127, 33), (241, 33), (21, 33), (249, 32), (23, 31), (245, 30), (142, 30), (55, 29), (140, 29), (46, 29),
(192, 29), (179, 29), (252, 29), (115, 29), (22, 29), (43, 28), (215, 28), (45, 28), (246, 28), (38, 28),
(86, 27), (225, 27), (25, 26), (239, 26), (58, 26), (167, 26), (147, 26), (217, 26), (149, 25), (30, 25),
(206, 25), (28, 24), (47, 24), (37, 24), (155, 24), (129, 23), (148, 23), (111, 23), (29, 23), (39, 23),
(51, 22), (193, 22), (236, 22), (120, 22), (64, 22), (204, 21), (210, 21), (244, 21), (52, 21), (66, 21),
(114, 20), (250, 20), (106, 20), (93, 19), (199, 19), (218, 19), (154, 19), (205, 19), (50, 19), (159, 19),
(194, 19), (49, 19), (190, 19), (103, 18), (216, 18), (213, 18), (107, 18), (131, 18), (63, 18), (94, 18),
(91, 17), (242, 17), (109, 17), (53, 16), (227, 16), (139, 16), (31, 16), (75, 16), (60, 16), (195, 15),
(231, 15), (62, 15), (59, 15), (87, 14), (207, 14), (27, 14), (90, 14), (110, 13), (223, 13), (57, 13),
(118, 12), (26, 12), (203, 12), (81, 12), (156, 12), (54, 12), (235, 12), (146, 11), (135, 11), (126, 11),
(150, 11), (130, 11), (143, 10), (61, 10), (219, 10), (124, 9), (222, 9), (125, 9), (119, 7), (137, 7),
(33, 7), (117, 5), (92, 4), (116, 3)
]
cnt = Counter()
for opcode, amount in opcode_occurrences:
cnt[AllInstructions[opcode][1]] += amount
cnt["nop"] = 13
cnt["tsb"] = 13
four_letter_mnemonics = list(sorted([ins[1] for ins in AllInstructions if len(ins[1])>3]))
for ins4 in four_letter_mnemonics:
del cnt[ins4]
cnt[ins4] = 1
mnem2 = [c[0] for c in cnt.most_common()]
if len(mnem2)!=len(mnemonics):
raise ValueError("mnem count mismatch")
return mnem2
mnemonics = determine_mnemonics()
def first_letters():
firstletters = {m[0]: 0 for m in mnemonics}
return firstletters.keys()
def second_letters(firstletter):
secondletters = {m[1]: 0 for m in mnemonics if m[0] == firstletter}
return secondletters.keys()
def third_letters(firstletter, secondletter):
thirdletters = {m[2]: 0 for m in mnemonics if m[0] == firstletter and m[1] == secondletter}
return thirdletters.keys()
def fourth_letters(firstletter, secondletter, thirdletter):
longmnem = [m for m in mnemonics if len(m) > 3]
fourthletters = {m[3]: 0 for m in longmnem if m[0] == firstletter and m[1] == secondletter and m[2] == thirdletter}
return fourthletters.keys()
def make_tree():
tree = {}
for first in first_letters():
tree[first] = {
secondletter: {
thirdletter: {
fourthletter: {}
for fourthletter in fourth_letters(first, secondletter, thirdletter)
}
for thirdletter in third_letters(first, secondletter)
}
for secondletter in second_letters(first)
}
return tree
tree = make_tree()
print("get_opcode_info .proc")
print("_mnem_fourth_letter = cx16.r4")
print("_mnem_fifth_letter = cx16.r5")
for first in tree:
print(" cmp #'%s'" % first)
print(" bne _not_%s" % first)
for second in tree[first]:
print(" cpx #'%s'" % second)
print(" bne _not_%s%s" % (first,second))
for third in tree[first][second]:
print(" cpy #'%s'" % third)
print(" bne _not_%s%s%s" % (first, second, third))
fourth = tree[first][second][third]
if fourth:
if "".join(fourth.keys()) != "01234567":
raise ValueError("fourth", fourth.keys())
print(" bra _check_%s%s%s" % (first, second, third))
else:
print(" lda _mnem_fourth_letter") # check that the fourth letter is not present
print(" bne _invalid")
print(" lda #<i_%s%s%s" % (first, second, third))
print(" ldy #>i_%s%s%s" % (first, second, third))
print(" rts")
print("_not_%s%s%s:" % (first, second, third))
print("_not_%s%s:" % (first, second))
print("_not_%s:" % first)
print("_invalid:")
print(" lda #0")
print(" ldy #0")
print(" rts")
# the 4-letter mnemonics are:
# smb[0-7]
# bbr[0-7]
# rmb[0-7]
# bbs[0-7]
for fourlettermnemonic in ["smb", "bbr", "rmb", "bbs"]:
print("_check_%s" % fourlettermnemonic)
print(" lda #<_tab_%s" % fourlettermnemonic)
print(" ldy #>_tab_%s" % fourlettermnemonic)
print(""" sta P8ZP_SCRATCH_W2
sty P8ZP_SCRATCH_W2+1
bra _check4""")
print("""_check4
lda _mnem_fourth_letter
cmp #'0'
bcc _invalid
cmp #'8'
bcs _invalid
lda _mnem_fifth_letter ; must have no fifth letter
bne _invalid
tay
lda (P8ZP_SCRATCH_W2),y
pha
iny
lda (P8ZP_SCRATCH_W2),y
tay
pla
rts""")
for fourlettermnemonic in ["smb", "bbr", "rmb", "bbs"]:
print("_tab_%s" % fourlettermnemonic)
for ii in "01234567":
print(" .word i_%s%s" % (fourlettermnemonic, ii))
print(" .pend")

View File

@ -79,7 +79,7 @@ main {
for x in 39 downto 0 {
; using a temp var here to enable expression optimization that can't be done on a 'problematic' ROM/RAM memory location
ubyte cc = xbuf[x] + ybuf[y]
@(screen) = cc
@(screen+x) = cc
; this is the fastest way to do this inner part:
; %asm {{
; ldy i
@ -90,8 +90,8 @@ main {
; ldy #0
; sta (screen),y
; }}
screen++
}
}
screen += 40
}
}

View File

@ -7,40 +7,35 @@ main {
sub start() {
str name = "abcdef"
uword screen=$0400
ubyte[256] xbuf = 1
ubyte[256] ybuf = 3
uword ptr = &name
ubyte cc
cc = @(ptr)
txt.chrout(cc)
txt.nl()
cc = @(ptr+1)
txt.chrout(cc)
txt.nl()
cc = @(ptr+2)
txt.chrout(cc)
txt.nl()
txt.nl()
ubyte ix = 0
ubyte cc = 0
cc=0
txt.chrout(@(ptr)+cc)
txt.chrout(@(ptr+1)+cc)
txt.chrout(@(ptr+2)+cc)
txt.nl()
repeat 20 {
cc++
}
@(ptr) = '1'
@(ptr+1) = '2'
@(ptr+2) = '3'
txt.print(name)
txt.nl()
@(screen) = 1
@(screen+1) = 2
swap(@(screen), @(screen+1))
cc=0
@(ptr+cc) = 'a'
@(ptr+cc+1) = 'b'
@(ptr+cc+2) = 'c'
txt.print(name)
txt.nl()
; cc = @(screen+2)
; cc++
; @(screen+2) = cc
; cc = @(screen+ix)
; cc++
; @(screen+ix) = cc
; for ii in 24 downto 0 {
; for i in 39 downto 0 {
; @(screen+i) = xbuf[i] + ybuf[ii]
; }
; screen+=40
; }
}
}

View File

@ -1,141 +0,0 @@
%import textio
%import floats
%zeropage basicsafe
; Note: this program is compatible with C64 and CX16.
main {
; this is only a parser/compiler test, there's no actual working program
sub start() {
txt.print("this is only a parser/compiler test\n")
return
str s1 = "hello"
str s2 = @"screencodes"
&str ms1 = $c000
byte[4] barray
ubyte[4] ubarray
word[4] warray
uword[4] uwarray
float[4] flarray
&byte[4] mbarray = $c000
&ubyte[4] mubarray = $c000
&word[4] mwarray = $c000
&uword[4] muwarray = $c000
&float[4] mflarray = $c000
ubyte a
byte bb
ubyte ub
word ww
uword uw
float fl
; read array
a=s1[2]
ub=s1[2]
bb=barray[2]
ub=ubarray[2]
ww=warray[2]
uw=uwarray[2]
fl=flarray[2]
a=ms1[2]
ub=ms1[2]
bb=mbarray[2]
ub=mubarray[2]
ww=mwarray[2]
uw=muwarray[2]
fl=mflarray[2]
a=s1[a]
ub=s1[a]
bb=barray[a]
ub=ubarray[a]
ww=warray[a]
uw=uwarray[a]
fl=flarray[a]
a=ms1[a]
ub=ms1[a]
bb=mbarray[a]
ub=mubarray[a]
ww=mwarray[a]
uw=muwarray[a]
fl=mflarray[a]
a=s1[bb]
ub=s1[bb]
bb=barray[bb]
ub=ubarray[bb]
ww=warray[bb]
uw=uwarray[bb]
fl=flarray[bb]
a=ms1[bb]
ub=ms1[bb]
bb=mbarray[bb]
ub=mubarray[bb]
ww=mwarray[bb]
uw=muwarray[bb]
fl=mflarray[bb]
; a=s1[bb*3]
; ub=s1[bb*3]
; bb=barray[bb*3]
; ub=ubarray[bb*3]
; ww=warray[bb*3]
; uw=uwarray[bb*3]
; fl=flarray[bb*3]
; a=ms1[bb*3]
; ub=ms1[bb*3]
; bb=mbarray[bb*3]
; ub=mubarray[bb*3]
; ww=mwarray[bb*3]
; uw=muwarray[bb*3]
; fl=mflarray[bb*3]
; write array
barray[2]++
barray[2]--
s1[2] = a
s1[2] = ub
barray[2] = bb
ubarray[2] = ub
warray[2] = ww
uwarray[2] = uw
flarray[2] = fl
ms1[2] = a
ms1[2] = ub
mbarray[2]++
mbarray[2] = bb
mbarray[2] = bb
mubarray[2] = ub
mwarray[2] = ww
muwarray[2] = uw
mflarray[2] = fl
s1[a] = ub
barray[a] = bb
ubarray[a] = ub
warray[a] = ww
uwarray[a] = uw
flarray[a] = fl
s1[bb] = ub
barray[bb] = bb
ubarray[bb] = ub
warray[bb] = ww
uwarray[bb] = uw
flarray[bb] = fl
; s1[bb*3] = ub
; barray[bb*3] = bb
; ubarray[bb*3] = ub
; warray[bb*3] = ww
; uwarray[bb*3] = uw
; flarray[bb*3] = fl
}
}

View File

@ -1,5 +1,5 @@
#!/usr/bin/env sh
rm -f *.jar *.asm *.prg *.vm.txt *.vice-mon-list
rm -f *.jar *.asm *.prg *.vm.txt *.vice-mon-list a.out
rm -rf build out