prevent struct member vars from shuffling around, can take address of struct now

This commit is contained in:
Irmen de Jong 2019-07-13 00:27:03 +02:00
parent a089c48378
commit 1f5420010d
16 changed files with 235 additions and 162 deletions

View File

@ -66,7 +66,7 @@ private fun prog8Parser.StatementContext.toAst() : IStatement {
return VarDecl( return VarDecl(
VarDeclType.VAR, VarDeclType.VAR,
vd.datatype()?.toAst() ?: DataType.STRUCT, vd.datatype()?.toAst() ?: DataType.STRUCT,
vd.ZEROPAGE() != null, if(vd.ZEROPAGE() != null) ZeropageWish.PREFER_ZEROPAGE else ZeropageWish.DONTCARE,
vd.arrayindex()?.toAst(), vd.arrayindex()?.toAst(),
vd.varname.text, vd.varname.text,
vd.structname?.text, vd.structname?.text,
@ -83,7 +83,7 @@ private fun prog8Parser.StatementContext.toAst() : IStatement {
return VarDecl( return VarDecl(
VarDeclType.CONST, VarDeclType.CONST,
vd.datatype()?.toAst() ?: DataType.STRUCT, vd.datatype()?.toAst() ?: DataType.STRUCT,
vd.ZEROPAGE() != null, if(vd.ZEROPAGE() != null) ZeropageWish.PREFER_ZEROPAGE else ZeropageWish.DONTCARE,
vd.arrayindex()?.toAst(), vd.arrayindex()?.toAst(),
vd.varname.text, vd.varname.text,
vd.structname?.text, vd.structname?.text,
@ -100,7 +100,7 @@ private fun prog8Parser.StatementContext.toAst() : IStatement {
return VarDecl( return VarDecl(
VarDeclType.MEMORY, VarDeclType.MEMORY,
vd.datatype()?.toAst() ?: DataType.STRUCT, vd.datatype()?.toAst() ?: DataType.STRUCT,
vd.ZEROPAGE() != null, if(vd.ZEROPAGE() != null) ZeropageWish.PREFER_ZEROPAGE else ZeropageWish.DONTCARE,
vd.arrayindex()?.toAst(), vd.arrayindex()?.toAst(),
vd.varname.text, vd.varname.text,
vd.structname?.text, vd.structname?.text,
@ -519,7 +519,7 @@ private fun prog8Parser.BranchconditionContext.toAst() = BranchCondition.valueOf
private fun prog8Parser.ForloopContext.toAst(): ForLoop { private fun prog8Parser.ForloopContext.toAst(): ForLoop {
val loopregister = register()?.toAst() val loopregister = register()?.toAst()
val datatype = datatype()?.toAst() val datatype = datatype()?.toAst()
val zeropage = ZEROPAGE()!=null val zeropage = if(ZEROPAGE() != null) ZeropageWish.PREFER_ZEROPAGE else ZeropageWish.DONTCARE
val loopvar = identifier()?.toAst() val loopvar = identifier()?.toAst()
val iterable = expression()!!.toAst() val iterable = expression()!!.toAst()
val scope = val scope =
@ -573,7 +573,7 @@ private fun prog8Parser.VardeclContext.toAst(): VarDecl {
return VarDecl( return VarDecl(
VarDeclType.VAR, VarDeclType.VAR,
datatype()?.toAst() ?: DataType.STRUCT, datatype()?.toAst() ?: DataType.STRUCT,
ZEROPAGE() != null, if(ZEROPAGE() != null) ZeropageWish.PREFER_ZEROPAGE else ZeropageWish.DONTCARE,
arrayindex()?.toAst(), arrayindex()?.toAst(),
varname.text, varname.text,
structname?.text, structname?.text,

View File

@ -450,7 +450,7 @@ internal class AstChecker(private val program: Program,
if(variable==null) if(variable==null)
checkResult.add(ExpressionError("pointer-of operand must be the name of a heap variable", addressOf.position)) checkResult.add(ExpressionError("pointer-of operand must be the name of a heap variable", addressOf.position))
else { else {
if(variable.datatype !in ArrayDatatypes && variable.datatype !in StringDatatypes) if(variable.datatype !in ArrayDatatypes && variable.datatype !in StringDatatypes && variable.datatype!=DataType.STRUCT)
checkResult.add(ExpressionError("pointer-of operand must be the name of a string or array heap variable", addressOf.position)) checkResult.add(ExpressionError("pointer-of operand must be the name of a string or array heap variable", addressOf.position))
} }
if(addressOf.scopedname==null) if(addressOf.scopedname==null)
@ -500,12 +500,14 @@ internal class AstChecker(private val program: Program,
when(decl.type) { when(decl.type) {
VarDeclType.VAR, VarDeclType.CONST -> { VarDeclType.VAR, VarDeclType.CONST -> {
if(decl.struct!=null || decl.datatype==DataType.STRUCT) { if(decl.datatype==DataType.STRUCT) {
if(decl.datatype!=DataType.STRUCT)
throw FatalAstException("struct vardecl should be of data type struct $decl")
if(decl.struct==null) if(decl.struct==null)
throw FatalAstException("struct vardecl should be linked to its struct $decl") throw FatalAstException("struct vardecl should be linked to its struct $decl")
if(decl.zeropage) if(decl.zeropage==ZeropageWish.PREFER_ZEROPAGE || decl.zeropage==ZeropageWish.REQUIRE_ZEROPAGE)
err("struct can not be in zeropage")
}
if(decl.struct!=null) {
if(decl.zeropage==ZeropageWish.PREFER_ZEROPAGE || decl.zeropage==ZeropageWish.REQUIRE_ZEROPAGE)
err("struct can not be in zeropage") err("struct can not be in zeropage")
} }
if (decl.value == null) { if (decl.value == null) {
@ -1284,7 +1286,7 @@ internal class AstChecker(private val program: Program,
if(decl==null) if(decl==null)
checkResult.add(SyntaxError("struct can only contain variable declarations", structDecl.position)) checkResult.add(SyntaxError("struct can only contain variable declarations", structDecl.position))
else { else {
if(decl.zeropage) if(decl.zeropage==ZeropageWish.REQUIRE_ZEROPAGE || decl.zeropage==ZeropageWish.PREFER_ZEROPAGE)
checkResult.add(SyntaxError("struct can not contain zeropage members", decl.position)) checkResult.add(SyntaxError("struct can not contain zeropage members", decl.position))
if(decl.datatype !in NumericDatatypes) if(decl.datatype !in NumericDatatypes)
checkResult.add(SyntaxError("structs can only contain numerical types", decl.position)) checkResult.add(SyntaxError("structs can only contain numerical types", decl.position))

View File

@ -71,10 +71,10 @@ internal class AstIdentifiersChecker(private val namespace: INameScope) : IAstMo
VarDecl( VarDecl(
VarDeclType.VAR, VarDeclType.VAR,
member.datatype, member.datatype,
false, ZeropageWish.NOT_IN_ZEROPAGE,
member.arraysize, member.arraysize,
mangledStructMemberName(decl.name, member.name), mangledStructMemberName(decl.name, member.name),
null, decl.struct!!.name,
initvalue, initvalue,
member.isArray, member.isArray,
true, true,
@ -131,7 +131,7 @@ internal class AstIdentifiersChecker(private val namespace: INameScope) : IAstMo
subroutine.parameters subroutine.parameters
.filter { it.name !in namesInSub } .filter { it.name !in namesInSub }
.forEach { .forEach {
val vardecl = VarDecl(VarDeclType.VAR, it.type, false, null, it.name, null, null, val vardecl = VarDecl(VarDeclType.VAR, it.type, ZeropageWish.DONTCARE, null, it.name, null, null,
isArray = false, hiddenButDoNotRemove = true, position = subroutine.position) isArray = false, hiddenButDoNotRemove = true, position = subroutine.position)
vardecl.linkParents(subroutine) vardecl.linkParents(subroutine)
subroutine.statements.add(0, vardecl) subroutine.statements.add(0, vardecl)
@ -183,7 +183,7 @@ internal class AstIdentifiersChecker(private val namespace: INameScope) : IAstMo
val existing = if(forLoop.body.containsNoCodeNorVars()) null else forLoop.body.lookup(listOf(ForLoop.iteratorLoopcounterVarname), forLoop.body.statements.first()) val existing = if(forLoop.body.containsNoCodeNorVars()) null else forLoop.body.lookup(listOf(ForLoop.iteratorLoopcounterVarname), forLoop.body.statements.first())
if(existing==null) { if(existing==null) {
// create loop iteration counter variable (without value, to avoid an assignment) // create loop iteration counter variable (without value, to avoid an assignment)
val vardecl = VarDecl(VarDeclType.VAR, DataType.UBYTE, true, null, ForLoop.iteratorLoopcounterVarname, null, null, val vardecl = VarDecl(VarDeclType.VAR, DataType.UBYTE, ZeropageWish.PREFER_ZEROPAGE, null, ForLoop.iteratorLoopcounterVarname, null, null,
isArray = false, hiddenButDoNotRemove = true, position = forLoop.loopVar.position) isArray = false, hiddenButDoNotRemove = true, position = forLoop.loopVar.position)
vardecl.linkParents(forLoop.body) vardecl.linkParents(forLoop.body)
forLoop.body.statements.add(0, vardecl) forLoop.body.statements.add(0, vardecl)
@ -230,7 +230,7 @@ internal class AstIdentifiersChecker(private val namespace: INameScope) : IAstMo
val declaredType = if(literalValue.isArray) ArrayElementTypes.getValue(literalValue.type) else literalValue.type val declaredType = if(literalValue.isArray) ArrayElementTypes.getValue(literalValue.type) else literalValue.type
val variable = VarDecl(VarDeclType.VAR, val variable = VarDecl(VarDeclType.VAR,
declaredType, declaredType,
false, ZeropageWish.NOT_IN_ZEROPAGE,
null, null,
"$autoHeapValuePrefix${literalValue.heapId}", "$autoHeapValuePrefix${literalValue.heapId}",
null, null,

View File

@ -128,7 +128,7 @@ internal class VarInitValueAndAddressOfCreator(private val namespace: INameScope
pointerExpr.linkParents(arglist[argparam.first.index].parent) pointerExpr.linkParents(arglist[argparam.first.index].parent)
arglist[argparam.first.index] = pointerExpr arglist[argparam.first.index] = pointerExpr
// add a vardecl so that the autovar can be resolved in later lookups // add a vardecl so that the autovar can be resolved in later lookups
val variable = VarDecl(VarDeclType.VAR, strvalue.type, false, null, autoVarName, null, strvalue, val variable = VarDecl(VarDeclType.VAR, strvalue.type, ZeropageWish.NOT_IN_ZEROPAGE, null, autoVarName, null, strvalue,
isArray = false, hiddenButDoNotRemove = false, position = strvalue.position) isArray = false, hiddenButDoNotRemove = false, position = strvalue.position)
addVarDecl(strvalue.definingScope(), variable) addVarDecl(strvalue.definingScope(), variable)
} }

View File

@ -135,9 +135,17 @@ class Break(override val position: Position) : IStatement {
override fun accept(visitor: IAstVisitor) = visitor.visit(this) override fun accept(visitor: IAstVisitor) = visitor.visit(this)
} }
enum class ZeropageWish {
REQUIRE_ZEROPAGE,
PREFER_ZEROPAGE,
DONTCARE,
NOT_IN_ZEROPAGE
}
class VarDecl(val type: VarDeclType, class VarDecl(val type: VarDeclType,
private val declaredDatatype: DataType, private val declaredDatatype: DataType,
val zeropage: Boolean, val zeropage: ZeropageWish,
var arraysize: ArrayIndex?, var arraysize: ArrayIndex?,
val name: String, val name: String,
private val structName: String?, private val structName: String?,
@ -617,7 +625,7 @@ class BranchStatement(var condition: BranchCondition,
class ForLoop(val loopRegister: Register?, class ForLoop(val loopRegister: Register?,
val decltype: DataType?, val decltype: DataType?,
val zeropage: Boolean, val zeropage: ZeropageWish,
val loopVar: IdentifierReference?, val loopVar: IdentifierReference?,
var iterable: IExpression, var iterable: IExpression,
var body: AnonymousScope, var body: AnonymousScope,

View File

@ -116,7 +116,7 @@ class AstToSourceCode(val output: (text: String) -> Unit): IAstVisitor {
if(decl.isArray) if(decl.isArray)
output("]") output("]")
if(decl.zeropage) if(decl.zeropage == ZeropageWish.REQUIRE_ZEROPAGE || decl.zeropage==ZeropageWish.PREFER_ZEROPAGE)
output(" @zp") output(" @zp")
output(" ${decl.name} ") output(" ${decl.name} ")
if(decl.value!=null) { if(decl.value!=null) {
@ -305,7 +305,7 @@ class AstToSourceCode(val output: (text: String) -> Unit): IAstVisitor {
output("for ") output("for ")
if(forLoop.decltype!=null) { if(forLoop.decltype!=null) {
output(datatypeString(forLoop.decltype)) output(datatypeString(forLoop.decltype))
if (forLoop.zeropage) if (forLoop.zeropage==ZeropageWish.REQUIRE_ZEROPAGE || forLoop.zeropage==ZeropageWish.PREFER_ZEROPAGE)
output(" @zp ") output(" @zp ")
else else
output(" ") output(" ")

View File

@ -1488,6 +1488,22 @@ internal class Compiler(private val program: Program) {
} }
} }
private fun pushStructAddress(value: IExpression) {
when (value) {
is LiteralValue -> throw CompilerException("can only push address of struct that is a variable on the heap")
is IdentifierReference -> {
// notice that the mangled name of the first struct member is the start address of this struct var
val vardecl = value.targetVarDecl(program.namespace)!!
val firstStructMember = (vardecl.struct!!.statements.first() as VarDecl).name
val firstVarName = listOf(vardecl.name, firstStructMember)
// find the flattened var that belongs to this first struct member
val firstVar = value.definingScope().lookup(firstVarName, value) as VarDecl
prog.instr(Opcode.PUSH_ADDR_HEAPVAR, callLabel = firstVar.scopedname) // TODO
}
else -> throw CompilerException("can only take address of a the float as constant literal or variable")
}
}
private fun popValueIntoTarget(assignTarget: AssignTarget, datatype: DataType) { private fun popValueIntoTarget(assignTarget: AssignTarget, datatype: DataType) {
when { when {
assignTarget.identifier != null -> { assignTarget.identifier != null -> {
@ -2066,6 +2082,9 @@ internal class Compiler(private val program: Program) {
else if(target.datatype== DataType.FLOAT) { else if(target.datatype== DataType.FLOAT) {
pushFloatAddress(addrof.identifier) pushFloatAddress(addrof.identifier)
} }
else if(target.datatype == DataType.STRUCT) {
pushStructAddress(addrof.identifier)
}
else else
throw CompilerException("cannot take memory pointer $addrof") throw CompilerException("cannot take memory pointer $addrof")
} }

View File

@ -6,6 +6,7 @@ import prog8.ast.base.printWarning
import prog8.ast.expressions.LiteralValue import prog8.ast.expressions.LiteralValue
import prog8.ast.statements.StructDecl import prog8.ast.statements.StructDecl
import prog8.ast.statements.VarDecl import prog8.ast.statements.VarDecl
import prog8.ast.statements.ZeropageWish
import prog8.vm.RuntimeValue import prog8.vm.RuntimeValue
import prog8.compiler.CompilerException import prog8.compiler.CompilerException
import prog8.compiler.HeapValues import prog8.compiler.HeapValues
@ -17,10 +18,12 @@ import java.nio.file.Path
class IntermediateProgram(val name: String, var loadAddress: Int, val heap: HeapValues, val source: Path) { class IntermediateProgram(val name: String, var loadAddress: Int, val heap: HeapValues, val source: Path) {
data class VariableParameters (val zp: ZeropageWish, val memberOfStruct: StructDecl?)
class ProgramBlock(val name: String, class ProgramBlock(val name: String,
var address: Int?, var address: Int?,
val instructions: MutableList<Instruction> = mutableListOf(), val instructions: MutableList<Instruction> = mutableListOf(),
val variables: MutableMap<String, RuntimeValue> = mutableMapOf(), // names are fully scoped val variables: MutableList<Triple<String, RuntimeValue, VariableParameters>> = mutableListOf(), // names are fully scoped
val memoryPointers: MutableMap<String, Pair<Int, DataType>> = mutableMapOf(), val memoryPointers: MutableMap<String, Pair<Int, DataType>> = mutableMapOf(),
val labels: MutableMap<String, Instruction> = mutableMapOf(), // names are fully scoped val labels: MutableMap<String, Instruction> = mutableMapOf(), // names are fully scoped
val force_output: Boolean) val force_output: Boolean)
@ -29,7 +32,7 @@ class IntermediateProgram(val name: String, var loadAddress: Int, val heap: Heap
get() { return variables.size } get() { return variables.size }
val numInstructions: Int val numInstructions: Int
get() { return instructions.filter { it.opcode!= Opcode.LINE }.size } get() { return instructions.filter { it.opcode!= Opcode.LINE }.size }
val variablesMarkedForZeropage: MutableSet<String> = mutableSetOf() val variablesMarkedForZeropage: MutableSet<String> = mutableSetOf() // TODO maybe this can be removed now we have ValueParameters
} }
val allocatedZeropageVariables = mutableMapOf<String, Pair<Int, DataType>>() val allocatedZeropageVariables = mutableMapOf<String, Pair<Int, DataType>>()
@ -46,14 +49,16 @@ class IntermediateProgram(val name: String, var loadAddress: Int, val heap: Heap
// allocates all @zp marked variables on the zeropage (for all blocks, as long as there is space in the ZP) // allocates all @zp marked variables on the zeropage (for all blocks, as long as there is space in the ZP)
var notAllocated = 0 var notAllocated = 0
for(block in blocks) { for(block in blocks) {
val zpVariables = block.variables.filter { it.key in block.variablesMarkedForZeropage } val zpVariables = block.variables.filter { it.first in block.variablesMarkedForZeropage }
if (zpVariables.isNotEmpty()) { if (zpVariables.isNotEmpty()) {
for (variable in zpVariables) { for ((varname, value, varparams) in zpVariables) {
if(varparams.zp==ZeropageWish.NOT_IN_ZEROPAGE || varparams.memberOfStruct!=null)
throw CompilerException("zp conflict")
try { try {
val address = zeropage.allocate(variable.key, variable.value.type, null) val address = zeropage.allocate(varname, value.type, null)
allocatedZeropageVariables[variable.key] = Pair(address, variable.value.type) allocatedZeropageVariables[varname] = Pair(address, value.type)
} catch (x: ZeropageDepletedError) { } catch (x: ZeropageDepletedError) {
printWarning(x.toString() + " variable ${variable.key} type ${variable.value.type}") printWarning(x.toString() + " variable $varname type ${value.type}")
notAllocated++ notAllocated++
} }
} }
@ -399,6 +404,7 @@ class IntermediateProgram(val name: String, var loadAddress: Int, val heap: Heap
if(decl.parent is StructDecl) if(decl.parent is StructDecl)
return return
val valueparams = VariableParameters(decl.zeropage, decl.struct)
val value = when(decl.datatype) { val value = when(decl.datatype) {
in NumericDatatypes -> { in NumericDatatypes -> {
RuntimeValue(decl.datatype, (decl.value as LiteralValue).asNumericValue!!) RuntimeValue(decl.datatype, (decl.value as LiteralValue).asNumericValue!!)
@ -421,9 +427,11 @@ class IntermediateProgram(val name: String, var loadAddress: Int, val heap: Heap
} }
else -> throw CompilerException("weird datatype") else -> throw CompilerException("weird datatype")
} }
currentBlock.variables[scopedname] = value currentBlock.variables.add(Triple(scopedname, value, valueparams))
if(decl.zeropage) if(decl.zeropage==ZeropageWish.PREFER_ZEROPAGE)
currentBlock.variablesMarkedForZeropage.add(scopedname) currentBlock.variablesMarkedForZeropage.add(scopedname)
else if(decl.zeropage==ZeropageWish.REQUIRE_ZEROPAGE)
TODO("REQUIRE_ZEROPAGE not yet implemented")
} }
VarDeclType.MEMORY -> { VarDeclType.MEMORY -> {
// note that constants are all folded away, but assembly code may still refer to them // note that constants are all folded away, but assembly code may still refer to them
@ -513,9 +521,11 @@ class IntermediateProgram(val name: String, var loadAddress: Int, val heap: Heap
out.println("\n%block ${blk.name} ${blk.address?.toString(16) ?: ""}") out.println("\n%block ${blk.name} ${blk.address?.toString(16) ?: ""}")
out.println("%variables") out.println("%variables")
for (variable in blk.variables) { for ((vname, value, parameters) in blk.variables) {
val valuestr = variable.value.toString() if(parameters.zp==ZeropageWish.REQUIRE_ZEROPAGE)
out.println("${variable.key} ${variable.value.type.name.toLowerCase()} $valuestr") throw CompilerException("zp conflict")
val valuestr = value.toString()
out.println("$vname ${value.type.name.toLowerCase()} $valuestr")
} }
out.println("%end_variables") out.println("%end_variables")
out.println("%memorypointers") out.println("%memorypointers")

View File

@ -6,6 +6,7 @@ package prog8.compiler.target.c64
import prog8.ast.antlr.escape import prog8.ast.antlr.escape
import prog8.ast.base.DataType import prog8.ast.base.DataType
import prog8.ast.base.initvarsSubName import prog8.ast.base.initvarsSubName
import prog8.ast.statements.ZeropageWish
import prog8.vm.RuntimeValue import prog8.vm.RuntimeValue
import prog8.compiler.* import prog8.compiler.*
import prog8.compiler.intermediate.* import prog8.compiler.intermediate.*
@ -44,7 +45,7 @@ class AsmGen(private val options: CompilationOptions, private val program: Inter
// Convert invalid label names (such as "<anon-1>") to something that's allowed. // Convert invalid label names (such as "<anon-1>") to something that's allowed.
val newblocks = mutableListOf<IntermediateProgram.ProgramBlock>() val newblocks = mutableListOf<IntermediateProgram.ProgramBlock>()
for(block in program.blocks) { for(block in program.blocks) {
val newvars = block.variables.map { symname(it.key, block) to it.value }.toMap().toMutableMap() val newvars = block.variables.map { Triple(symname(it.first, block), it.second, it.third) }.toMutableList()
val newvarsZeropaged = block.variablesMarkedForZeropage.map{symname(it, block)}.toMutableSet() val newvarsZeropaged = block.variablesMarkedForZeropage.map{symname(it, block)}.toMutableSet()
val newlabels = block.labels.map { symname(it.key, block) to it.value}.toMap().toMutableMap() val newlabels = block.labels.map { symname(it.key, block) to it.value}.toMap().toMutableMap()
val newinstructions = block.instructions.asSequence().map { val newinstructions = block.instructions.asSequence().map {
@ -236,18 +237,20 @@ class AsmGen(private val options: CompilationOptions, private val program: Inter
} }
// deal with zeropage variables // deal with zeropage variables
for(variable in blk.variables) { for((varname, value, parameters) in blk.variables) {
val sym = symname(blk.name+"."+variable.key, null) val sym = symname(blk.name+"."+varname, null)
val zpVar = program.allocatedZeropageVariables[sym] val zpVar = program.allocatedZeropageVariables[sym]
if(zpVar==null) { if(zpVar==null) {
// This var is not on the ZP yet. Attempt to move it there (if it's not a float, those take up too much space) // This var is not on the ZP yet. Attempt to move it there (if it's not a float, those take up too much space)
if(variable.value.type in zeropage.allowedDatatypes && variable.value.type != DataType.FLOAT) { if(parameters.zp != ZeropageWish.NOT_IN_ZEROPAGE &&
value.type in zeropage.allowedDatatypes
&& value.type != DataType.FLOAT) {
try { try {
val address = zeropage.allocate(sym, variable.value.type, null) val address = zeropage.allocate(sym, value.type, null)
out("${variable.key} = $address\t; auto zp ${variable.value.type}") out("$varname = $address\t; auto zp ${value.type}")
// make sure we add the var to the set of zpvars for this block // make sure we add the var to the set of zpvars for this block
blk.variablesMarkedForZeropage.add(variable.key) blk.variablesMarkedForZeropage.add(varname)
program.allocatedZeropageVariables[sym] = Pair(address, variable.value.type) program.allocatedZeropageVariables[sym] = Pair(address, value.type)
} catch (x: ZeropageDepletedError) { } catch (x: ZeropageDepletedError) {
// leave it as it is. // leave it as it is.
} }
@ -255,7 +258,7 @@ class AsmGen(private val options: CompilationOptions, private val program: Inter
} }
else { else {
// it was already allocated on the zp // it was already allocated on the zp
out("${variable.key} = ${zpVar.first}\t; zp ${zpVar.second}") out("$varname = ${zpVar.first}\t; zp ${zpVar.second}")
} }
} }
@ -289,79 +292,95 @@ class AsmGen(private val options: CompilationOptions, private val program: Inter
} }
private fun vardecls2asm(block: IntermediateProgram.ProgramBlock) { private fun vardecls2asm(block: IntermediateProgram.ProgramBlock) {
// these are the non-zeropage variables val uniqueNames = block.variables.map { it.first }.toSet()
val sortedVars = block.variables.filter{it.key !in block.variablesMarkedForZeropage}.toList().sortedBy { it.second.type } if (uniqueNames.size != block.variables.size)
for (v in sortedVars) { throw AssemblyError("not all variables have unique names")
when (v.second.type) {
DataType.UBYTE -> out("${v.first}\t.byte 0") // these are the non-zeropage variables.
DataType.BYTE -> out("${v.first}\t.char 0") // first get all the flattened struct members, they MUST remain in order
DataType.UWORD -> out("${v.first}\t.word 0") val (structMembers, normalVars) = block.variables.partition { it.third.memberOfStruct!=null }
DataType.WORD -> out("${v.first}\t.sint 0") structMembers.forEach { vardecl2asm(it.first, it.second, it.third) }
DataType.FLOAT -> out("${v.first}\t.byte 0,0,0,0,0 ; float")
DataType.STR, DataType.STR_S -> { // leave outsort the other variables by type
val rawStr = heap.get(v.second.heapId!!).str!! val sortedVars = normalVars.sortedBy { it.second.type }
val bytes = encodeStr(rawStr, v.second.type).map { "$" + it.toString(16).padStart(2, '0') } for ((varname, value, parameters) in sortedVars) {
out("${v.first}\t; ${v.second.type} \"${escape(rawStr).replace("\u0000", "<NULL>")}\"") if(varname in block.variablesMarkedForZeropage)
for (chunk in bytes.chunked(16)) continue // skip the ones that belong in the zero page
out(" .byte " + chunk.joinToString()) vardecl2asm(varname, value, parameters)
}
DataType.ARRAY_UB -> {
// unsigned integer byte arraysize
val data = makeArrayFillDataUnsigned(v.second)
if (data.size <= 16)
out("${v.first}\t.byte ${data.joinToString()}")
else {
out(v.first)
for (chunk in data.chunked(16))
out(" .byte " + chunk.joinToString())
}
}
DataType.ARRAY_B -> {
// signed integer byte arraysize
val data = makeArrayFillDataSigned(v.second)
if (data.size <= 16)
out("${v.first}\t.char ${data.joinToString()}")
else {
out(v.first)
for (chunk in data.chunked(16))
out(" .char " + chunk.joinToString())
}
}
DataType.ARRAY_UW -> {
// unsigned word arraysize
val data = makeArrayFillDataUnsigned(v.second)
if (data.size <= 16)
out("${v.first}\t.word ${data.joinToString()}")
else {
out(v.first)
for (chunk in data.chunked(16))
out(" .word " + chunk.joinToString())
}
}
DataType.ARRAY_W -> {
// signed word arraysize
val data = makeArrayFillDataSigned(v.second)
if (data.size <= 16)
out("${v.first}\t.sint ${data.joinToString()}")
else {
out(v.first)
for (chunk in data.chunked(16))
out(" .sint " + chunk.joinToString())
}
}
DataType.ARRAY_F -> {
// float arraysize
val array = heap.get(v.second.heapId!!).doubleArray!!
val floatFills = array.map { makeFloatFill(Mflpt5.fromNumber(it)) }
out(v.first)
for(f in array.zip(floatFills))
out(" .byte ${f.second} ; float ${f.first}")
}
DataType.STRUCT -> TODO("datatype struct")
}
} }
} }
private fun vardecl2asm(varname: String, value: RuntimeValue, parameters: IntermediateProgram.VariableParameters) {
when (value.type) {
DataType.UBYTE -> out("$varname\t.byte 0")
DataType.BYTE -> out("$varname\t.char 0")
DataType.UWORD -> out("$varname\t.word 0")
DataType.WORD -> out("$varname\t.sint 0")
DataType.FLOAT -> out("$varname\t.byte 0,0,0,0,0 ; float")
DataType.STR, DataType.STR_S -> {
val rawStr = heap.get(value.heapId!!).str!!
val bytes = encodeStr(rawStr, value.type).map { "$" + it.toString(16).padStart(2, '0') }
out("$varname\t; ${value.type} \"${escape(rawStr).replace("\u0000", "<NULL>")}\"")
for (chunk in bytes.chunked(16))
out(" .byte " + chunk.joinToString())
}
DataType.ARRAY_UB -> {
// unsigned integer byte arraysize
val data = makeArrayFillDataUnsigned(value)
if (data.size <= 16)
out("$varname\t.byte ${data.joinToString()}")
else {
out(varname)
for (chunk in data.chunked(16))
out(" .byte " + chunk.joinToString())
}
}
DataType.ARRAY_B -> {
// signed integer byte arraysize
val data = makeArrayFillDataSigned(value)
if (data.size <= 16)
out("$varname\t.char ${data.joinToString()}")
else {
out(varname)
for (chunk in data.chunked(16))
out(" .char " + chunk.joinToString())
}
}
DataType.ARRAY_UW -> {
// unsigned word arraysize
val data = makeArrayFillDataUnsigned(value)
if (data.size <= 16)
out("$varname\t.word ${data.joinToString()}")
else {
out(varname)
for (chunk in data.chunked(16))
out(" .word " + chunk.joinToString())
}
}
DataType.ARRAY_W -> {
// signed word arraysize
val data = makeArrayFillDataSigned(value)
if (data.size <= 16)
out("$varname\t.sint ${data.joinToString()}")
else {
out(varname)
for (chunk in data.chunked(16))
out(" .sint " + chunk.joinToString())
}
}
DataType.ARRAY_F -> {
// float arraysize
val array = heap.get(value.heapId!!).doubleArray!!
val floatFills = array.map { makeFloatFill(Mflpt5.fromNumber(it)) }
out(varname)
for(f in array.zip(floatFills))
out(" .byte ${f.second} ; float ${f.first}")
}
DataType.STRUCT -> throw AssemblyError("vars of type STRUCT should have been removed because flattened")
}
}
private fun encodeStr(str: String, dt: DataType): List<Short> { private fun encodeStr(str: String, dt: DataType): List<Short> {
return when(dt) { return when(dt) {
DataType.STR -> { DataType.STR -> {

View File

@ -650,7 +650,7 @@ internal class FlattenAnonymousScopesAndRemoveNops: IAstVisitor {
nop.parent = namescope as Node nop.parent = namescope as Node
namescope.statements[idx] = nop namescope.statements[idx] = nop
namescope.statements.addAll(idx, scope.statements) namescope.statements.addAll(idx, scope.statements)
scope.statements.forEach { it.parent = namescope as Node } scope.statements.forEach { it.parent = namescope }
visit(nop) visit(nop)
} }
} }

View File

@ -2,6 +2,8 @@ package prog8.vm
import prog8.ast.base.* import prog8.ast.base.*
import prog8.ast.expressions.LiteralValue import prog8.ast.expressions.LiteralValue
import prog8.ast.statements.StructDecl
import prog8.ast.statements.ZeropageWish
import prog8.compiler.HeapValues import prog8.compiler.HeapValues
import prog8.compiler.target.c64.Petscii import prog8.compiler.target.c64.Petscii
import kotlin.math.abs import kotlin.math.abs
@ -13,7 +15,8 @@ import kotlin.math.pow
* this runtime value can be used to *execute* the parsed Ast (or another intermediary form) * this runtime value can be used to *execute* the parsed Ast (or another intermediary form)
* It contains a value of a variable during run time of the program and provides arithmetic operations on the value. * It contains a value of a variable during run time of the program and provides arithmetic operations on the value.
*/ */
open class RuntimeValue(val type: DataType, num: Number?=null, val str: String?=null, val array: Array<Number>?=null, val heapId: Int?=null) { open class RuntimeValue(val type: DataType, num: Number?=null, val str: String?=null,
val array: Array<Number>?=null, val heapId: Int?=null) {
val byteval: Short? val byteval: Short?
val wordval: Int? val wordval: Int?
@ -45,8 +48,7 @@ open class RuntimeValue(val type: DataType, num: Number?=null, val str: String?=
if(elt.value.integer!=null) if(elt.value.integer!=null)
resultArray.add(elt.value.integer!!) resultArray.add(elt.value.integer!!)
else { else {
println("ADDRESSOF ${elt.value}") TODO("ADDRESSOF ${elt.value}")
resultArray.add(0x8000)
} }
} }
RuntimeValue(value.type, array = resultArray.toTypedArray(), heapId = heapId) RuntimeValue(value.type, array = resultArray.toTypedArray(), heapId = heapId)

View File

@ -3,6 +3,7 @@ package prog8.vm.astvm
import prog8.ast.* import prog8.ast.*
import prog8.ast.base.ArrayElementTypes import prog8.ast.base.ArrayElementTypes
import prog8.ast.base.DataType import prog8.ast.base.DataType
import prog8.ast.base.FatalAstException
import prog8.ast.base.VarDeclType import prog8.ast.base.VarDeclType
import prog8.ast.expressions.* import prog8.ast.expressions.*
import prog8.ast.statements.BuiltinFunctionStatementPlaceholder import prog8.ast.statements.BuiltinFunctionStatementPlaceholder
@ -87,8 +88,14 @@ fun evaluate(expr: IExpression, ctx: EvalContext): RuntimeValue {
} }
is AddressOf -> { is AddressOf -> {
// we support: address of heap var -> the heap id // we support: address of heap var -> the heap id
val heapId = expr.identifier.heapId(ctx.program.namespace) return try {
return RuntimeValue(DataType.UWORD, heapId) val heapId = expr.identifier.heapId(ctx.program.namespace)
RuntimeValue(DataType.UWORD, heapId)
} catch( f: FatalAstException) {
// fallback: use the hash of the name, so we have at least *a* value...
val address = expr.identifier.hashCode() and 65535
RuntimeValue(DataType.UWORD, address)
}
} }
is DirectMemoryRead -> { is DirectMemoryRead -> {
val address = evaluate(expr.addressExpression, ctx).wordval!! val address = evaluate(expr.addressExpression, ctx).wordval!!
@ -99,22 +106,21 @@ fun evaluate(expr: IExpression, ctx: EvalContext): RuntimeValue {
val scope = expr.definingScope() val scope = expr.definingScope()
val variable = scope.lookup(expr.nameInSource, expr) val variable = scope.lookup(expr.nameInSource, expr)
if(variable is VarDecl) { if(variable is VarDecl) {
if(variable.type==VarDeclType.VAR) when {
return ctx.runtimeVars.get(variable.definingScope(), variable.name) variable.type==VarDeclType.VAR -> return ctx.runtimeVars.get(variable.definingScope(), variable.name)
else if(variable.datatype==DataType.STRUCT) { variable.datatype==DataType.STRUCT -> throw VmExecutionException("cannot process structs by-value. at ${expr.position}")
throw VmExecutionException("cannot process structs by-value. at ${expr.position}") else -> {
} val address = ctx.runtimeVars.getMemoryAddress(variable.definingScope(), variable.name)
else { return when(variable.datatype) {
val address = ctx.runtimeVars.getMemoryAddress(variable.definingScope(), variable.name) DataType.UBYTE -> RuntimeValue(DataType.UBYTE, ctx.mem.getUByte(address))
return when(variable.datatype) { DataType.BYTE -> RuntimeValue(DataType.BYTE, ctx.mem.getSByte(address))
DataType.UBYTE -> RuntimeValue(DataType.UBYTE, ctx.mem.getUByte(address)) DataType.UWORD -> RuntimeValue(DataType.UWORD, ctx.mem.getUWord(address))
DataType.BYTE -> RuntimeValue(DataType.BYTE, ctx.mem.getSByte(address)) DataType.WORD -> RuntimeValue(DataType.WORD, ctx.mem.getSWord(address))
DataType.UWORD -> RuntimeValue(DataType.UWORD, ctx.mem.getUWord(address)) DataType.FLOAT -> RuntimeValue(DataType.FLOAT, ctx.mem.getFloat(address))
DataType.WORD -> RuntimeValue(DataType.WORD, ctx.mem.getSWord(address)) DataType.STR -> RuntimeValue(DataType.STR, str = ctx.mem.getString(address))
DataType.FLOAT -> RuntimeValue(DataType.FLOAT, ctx.mem.getFloat(address)) DataType.STR_S -> RuntimeValue(DataType.STR_S, str = ctx.mem.getScreencodeString(address))
DataType.STR -> RuntimeValue(DataType.STR, str = ctx.mem.getString(address)) else -> throw VmExecutionException("unexpected datatype $variable")
DataType.STR_S -> RuntimeValue(DataType.STR_S, str = ctx.mem.getScreencodeString(address)) }
else -> throw VmExecutionException("unexpected datatype $variable")
} }
} }
} else } else

View File

@ -6,6 +6,7 @@ import prog8.ast.expressions.LiteralValue
import prog8.ast.processing.IAstModifyingVisitor import prog8.ast.processing.IAstModifyingVisitor
import prog8.ast.statements.StructDecl import prog8.ast.statements.StructDecl
import prog8.ast.statements.VarDecl import prog8.ast.statements.VarDecl
import prog8.ast.statements.ZeropageWish
import prog8.compiler.HeapValues import prog8.compiler.HeapValues
import prog8.vm.RuntimeValue import prog8.vm.RuntimeValue
@ -18,11 +19,11 @@ class VariablesCreator(private val runtimeVariables: RuntimeVariables, private v
runtimeVariables.define(program.namespace, Register.Y.name, RuntimeValue(DataType.UBYTE, 0)) runtimeVariables.define(program.namespace, Register.Y.name, RuntimeValue(DataType.UBYTE, 0))
val globalpos = Position("<<global>>", 0, 0, 0) val globalpos = Position("<<global>>", 0, 0, 0)
val vdA = VarDecl(VarDeclType.VAR, DataType.UBYTE, false, null, Register.A.name, null, val vdA = VarDecl(VarDeclType.VAR, DataType.UBYTE, ZeropageWish.DONTCARE, null, Register.A.name, null,
LiteralValue.optimalInteger(0, globalpos), isArray = false, hiddenButDoNotRemove = true, position = globalpos) LiteralValue.optimalInteger(0, globalpos), isArray = false, hiddenButDoNotRemove = true, position = globalpos)
val vdX = VarDecl(VarDeclType.VAR, DataType.UBYTE, false, null, Register.X.name, null, val vdX = VarDecl(VarDeclType.VAR, DataType.UBYTE, ZeropageWish.DONTCARE, null, Register.X.name, null,
LiteralValue.optimalInteger(255, globalpos), isArray = false, hiddenButDoNotRemove = true, position = globalpos) LiteralValue.optimalInteger(255, globalpos), isArray = false, hiddenButDoNotRemove = true, position = globalpos)
val vdY = VarDecl(VarDeclType.VAR, DataType.UBYTE, false, null, Register.Y.name, null, val vdY = VarDecl(VarDeclType.VAR, DataType.UBYTE, ZeropageWish.DONTCARE, null, Register.Y.name, null,
LiteralValue.optimalInteger(0, globalpos), isArray = false, hiddenButDoNotRemove = true, position = globalpos) LiteralValue.optimalInteger(0, globalpos), isArray = false, hiddenButDoNotRemove = true, position = globalpos)
vdA.linkParents(program.namespace) vdA.linkParents(program.namespace)
vdX.linkParents(program.namespace) vdX.linkParents(program.namespace)

View File

@ -1873,10 +1873,17 @@ class StackVm(private var traceOutputFile: String?) {
Opcode.INLINE_ASSEMBLY -> throw VmExecutionException("stackVm doesn't support executing inline assembly code $ins") Opcode.INLINE_ASSEMBLY -> throw VmExecutionException("stackVm doesn't support executing inline assembly code $ins")
Opcode.INCLUDE_FILE -> throw VmExecutionException("stackVm doesn't support including a file $ins") Opcode.INCLUDE_FILE -> throw VmExecutionException("stackVm doesn't support including a file $ins")
Opcode.PUSH_ADDR_HEAPVAR -> { Opcode.PUSH_ADDR_HEAPVAR -> {
val heapId = variables.getValue(ins.callLabel!!).heapId!! val variable = variables.getValue(ins.callLabel!!)
if(heapId<0) if(variable.heapId!=null) {
throw VmExecutionException("expected variable on heap") val heapId = variable.heapId
evalstack.push(RuntimeValue(DataType.UWORD, heapId)) // push the "address" of the string or array variable (this is taken care of properly in the assembly code generator) if (heapId < 0)
throw VmExecutionException("expected variable on heap")
evalstack.push(RuntimeValue(DataType.UWORD, heapId)) // push the "address" of the string or array variable (this is taken care of properly in the assembly code generator)
} else {
// hack: return hash of the name, so we have at least *a* value...
val addr = ins.callLabel.hashCode() and 65535
evalstack.push(RuntimeValue(DataType.UWORD, addr))
}
} }
Opcode.CAST_UB_TO_B -> typecast(DataType.UBYTE, DataType.BYTE) Opcode.CAST_UB_TO_B -> typecast(DataType.UBYTE, DataType.BYTE)
Opcode.CAST_W_TO_B -> typecast(DataType.WORD, DataType.BYTE) Opcode.CAST_W_TO_B -> typecast(DataType.WORD, DataType.BYTE)

View File

@ -284,9 +284,10 @@ use a scoped name to refer to them: ``structvariable.membername``.
Structs are a bit limited in Prog8: you can only use numerical variables Structs are a bit limited in Prog8: you can only use numerical variables
as member of a struct, so strings and arrays and other structs can not be part of a struct. as member of a struct, so strings and arrays and other structs can not be part of a struct.
Also, it is not possible to use a struct itself inside an array. Also, it is not possible to use a struct itself inside an array.
Structs are mainly syntactic sugar for repeated groups of vardecls Structs are mainly syntactic sugar for repeated groups of vardecls
and assignments that belong together. and assignments that belong together. However, *they are layed out
in sequence in memory as the members are defined* which may be useful
if you want to pass pointers around
To create a variable of a struct type you need to define the struct itself, To create a variable of a struct type you need to define the struct itself,
and then create a variable with it:: and then create a variable with it::

View File

@ -3,28 +3,26 @@
~ main { ~ main {
Color blocklevelcolor
sub start() {
Color subcol
A=msb(subcol.red)
for ubyte i in 10 to 20 {
;A=subcol.red
;A=blocklevelcolor.green
;subcol.blue = Y
;blocklevelcolor.green=Y
A=msb(subcol.red)
}
return
}
struct Color { struct Color {
ubyte red uword red
ubyte green ubyte green
ubyte blue ubyte blue
} }
sub start() {
Color col_one
Color col_two
Color col_three
col_one.red= 111
col_two.blue= 222
c64scr.print_uwhex(1, &col_one)
c64scr.print_uwhex(1, &col_two)
c64scr.print_uwhex(1, &col_three)
return
}
} }