mirror of
https://github.com/irmen/prog8.git
synced 2024-09-07 19:54:26 +00:00
prevent struct member vars from shuffling around, can take address of struct now
This commit is contained in:
parent
a089c48378
commit
1f5420010d
@ -66,7 +66,7 @@ private fun prog8Parser.StatementContext.toAst() : IStatement {
|
||||
return VarDecl(
|
||||
VarDeclType.VAR,
|
||||
vd.datatype()?.toAst() ?: DataType.STRUCT,
|
||||
vd.ZEROPAGE() != null,
|
||||
if(vd.ZEROPAGE() != null) ZeropageWish.PREFER_ZEROPAGE else ZeropageWish.DONTCARE,
|
||||
vd.arrayindex()?.toAst(),
|
||||
vd.varname.text,
|
||||
vd.structname?.text,
|
||||
@ -83,7 +83,7 @@ private fun prog8Parser.StatementContext.toAst() : IStatement {
|
||||
return VarDecl(
|
||||
VarDeclType.CONST,
|
||||
vd.datatype()?.toAst() ?: DataType.STRUCT,
|
||||
vd.ZEROPAGE() != null,
|
||||
if(vd.ZEROPAGE() != null) ZeropageWish.PREFER_ZEROPAGE else ZeropageWish.DONTCARE,
|
||||
vd.arrayindex()?.toAst(),
|
||||
vd.varname.text,
|
||||
vd.structname?.text,
|
||||
@ -100,7 +100,7 @@ private fun prog8Parser.StatementContext.toAst() : IStatement {
|
||||
return VarDecl(
|
||||
VarDeclType.MEMORY,
|
||||
vd.datatype()?.toAst() ?: DataType.STRUCT,
|
||||
vd.ZEROPAGE() != null,
|
||||
if(vd.ZEROPAGE() != null) ZeropageWish.PREFER_ZEROPAGE else ZeropageWish.DONTCARE,
|
||||
vd.arrayindex()?.toAst(),
|
||||
vd.varname.text,
|
||||
vd.structname?.text,
|
||||
@ -519,7 +519,7 @@ private fun prog8Parser.BranchconditionContext.toAst() = BranchCondition.valueOf
|
||||
private fun prog8Parser.ForloopContext.toAst(): ForLoop {
|
||||
val loopregister = register()?.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 iterable = expression()!!.toAst()
|
||||
val scope =
|
||||
@ -573,7 +573,7 @@ private fun prog8Parser.VardeclContext.toAst(): VarDecl {
|
||||
return VarDecl(
|
||||
VarDeclType.VAR,
|
||||
datatype()?.toAst() ?: DataType.STRUCT,
|
||||
ZEROPAGE() != null,
|
||||
if(ZEROPAGE() != null) ZeropageWish.PREFER_ZEROPAGE else ZeropageWish.DONTCARE,
|
||||
arrayindex()?.toAst(),
|
||||
varname.text,
|
||||
structname?.text,
|
||||
|
@ -450,7 +450,7 @@ internal class AstChecker(private val program: Program,
|
||||
if(variable==null)
|
||||
checkResult.add(ExpressionError("pointer-of operand must be the name of a heap variable", addressOf.position))
|
||||
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))
|
||||
}
|
||||
if(addressOf.scopedname==null)
|
||||
@ -500,12 +500,14 @@ internal class AstChecker(private val program: Program,
|
||||
|
||||
when(decl.type) {
|
||||
VarDeclType.VAR, VarDeclType.CONST -> {
|
||||
if(decl.struct!=null || decl.datatype==DataType.STRUCT) {
|
||||
if(decl.datatype!=DataType.STRUCT)
|
||||
throw FatalAstException("struct vardecl should be of data type struct $decl")
|
||||
if(decl.datatype==DataType.STRUCT) {
|
||||
if(decl.struct==null)
|
||||
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")
|
||||
}
|
||||
if (decl.value == null) {
|
||||
@ -1284,7 +1286,7 @@ internal class AstChecker(private val program: Program,
|
||||
if(decl==null)
|
||||
checkResult.add(SyntaxError("struct can only contain variable declarations", structDecl.position))
|
||||
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))
|
||||
if(decl.datatype !in NumericDatatypes)
|
||||
checkResult.add(SyntaxError("structs can only contain numerical types", decl.position))
|
||||
|
@ -71,10 +71,10 @@ internal class AstIdentifiersChecker(private val namespace: INameScope) : IAstMo
|
||||
VarDecl(
|
||||
VarDeclType.VAR,
|
||||
member.datatype,
|
||||
false,
|
||||
ZeropageWish.NOT_IN_ZEROPAGE,
|
||||
member.arraysize,
|
||||
mangledStructMemberName(decl.name, member.name),
|
||||
null,
|
||||
decl.struct!!.name,
|
||||
initvalue,
|
||||
member.isArray,
|
||||
true,
|
||||
@ -131,7 +131,7 @@ internal class AstIdentifiersChecker(private val namespace: INameScope) : IAstMo
|
||||
subroutine.parameters
|
||||
.filter { it.name !in namesInSub }
|
||||
.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)
|
||||
vardecl.linkParents(subroutine)
|
||||
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())
|
||||
if(existing==null) {
|
||||
// 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)
|
||||
vardecl.linkParents(forLoop.body)
|
||||
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 variable = VarDecl(VarDeclType.VAR,
|
||||
declaredType,
|
||||
false,
|
||||
ZeropageWish.NOT_IN_ZEROPAGE,
|
||||
null,
|
||||
"$autoHeapValuePrefix${literalValue.heapId}",
|
||||
null,
|
||||
|
@ -128,7 +128,7 @@ internal class VarInitValueAndAddressOfCreator(private val namespace: INameScope
|
||||
pointerExpr.linkParents(arglist[argparam.first.index].parent)
|
||||
arglist[argparam.first.index] = pointerExpr
|
||||
// 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)
|
||||
addVarDecl(strvalue.definingScope(), variable)
|
||||
}
|
||||
|
@ -135,9 +135,17 @@ class Break(override val position: Position) : IStatement {
|
||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||
}
|
||||
|
||||
|
||||
enum class ZeropageWish {
|
||||
REQUIRE_ZEROPAGE,
|
||||
PREFER_ZEROPAGE,
|
||||
DONTCARE,
|
||||
NOT_IN_ZEROPAGE
|
||||
}
|
||||
|
||||
class VarDecl(val type: VarDeclType,
|
||||
private val declaredDatatype: DataType,
|
||||
val zeropage: Boolean,
|
||||
val zeropage: ZeropageWish,
|
||||
var arraysize: ArrayIndex?,
|
||||
val name: String,
|
||||
private val structName: String?,
|
||||
@ -617,7 +625,7 @@ class BranchStatement(var condition: BranchCondition,
|
||||
|
||||
class ForLoop(val loopRegister: Register?,
|
||||
val decltype: DataType?,
|
||||
val zeropage: Boolean,
|
||||
val zeropage: ZeropageWish,
|
||||
val loopVar: IdentifierReference?,
|
||||
var iterable: IExpression,
|
||||
var body: AnonymousScope,
|
||||
|
@ -116,7 +116,7 @@ class AstToSourceCode(val output: (text: String) -> Unit): IAstVisitor {
|
||||
if(decl.isArray)
|
||||
output("]")
|
||||
|
||||
if(decl.zeropage)
|
||||
if(decl.zeropage == ZeropageWish.REQUIRE_ZEROPAGE || decl.zeropage==ZeropageWish.PREFER_ZEROPAGE)
|
||||
output(" @zp")
|
||||
output(" ${decl.name} ")
|
||||
if(decl.value!=null) {
|
||||
@ -305,7 +305,7 @@ class AstToSourceCode(val output: (text: String) -> Unit): IAstVisitor {
|
||||
output("for ")
|
||||
if(forLoop.decltype!=null) {
|
||||
output(datatypeString(forLoop.decltype))
|
||||
if (forLoop.zeropage)
|
||||
if (forLoop.zeropage==ZeropageWish.REQUIRE_ZEROPAGE || forLoop.zeropage==ZeropageWish.PREFER_ZEROPAGE)
|
||||
output(" @zp ")
|
||||
else
|
||||
output(" ")
|
||||
|
@ -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) {
|
||||
when {
|
||||
assignTarget.identifier != null -> {
|
||||
@ -2066,6 +2082,9 @@ internal class Compiler(private val program: Program) {
|
||||
else if(target.datatype== DataType.FLOAT) {
|
||||
pushFloatAddress(addrof.identifier)
|
||||
}
|
||||
else if(target.datatype == DataType.STRUCT) {
|
||||
pushStructAddress(addrof.identifier)
|
||||
}
|
||||
else
|
||||
throw CompilerException("cannot take memory pointer $addrof")
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ import prog8.ast.base.printWarning
|
||||
import prog8.ast.expressions.LiteralValue
|
||||
import prog8.ast.statements.StructDecl
|
||||
import prog8.ast.statements.VarDecl
|
||||
import prog8.ast.statements.ZeropageWish
|
||||
import prog8.vm.RuntimeValue
|
||||
import prog8.compiler.CompilerException
|
||||
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) {
|
||||
|
||||
data class VariableParameters (val zp: ZeropageWish, val memberOfStruct: StructDecl?)
|
||||
|
||||
class ProgramBlock(val name: String,
|
||||
var address: Int?,
|
||||
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 labels: MutableMap<String, Instruction> = mutableMapOf(), // names are fully scoped
|
||||
val force_output: Boolean)
|
||||
@ -29,7 +32,7 @@ class IntermediateProgram(val name: String, var loadAddress: Int, val heap: Heap
|
||||
get() { return variables.size }
|
||||
val numInstructions: Int
|
||||
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>>()
|
||||
@ -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)
|
||||
var notAllocated = 0
|
||||
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()) {
|
||||
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 {
|
||||
val address = zeropage.allocate(variable.key, variable.value.type, null)
|
||||
allocatedZeropageVariables[variable.key] = Pair(address, variable.value.type)
|
||||
val address = zeropage.allocate(varname, value.type, null)
|
||||
allocatedZeropageVariables[varname] = Pair(address, value.type)
|
||||
} catch (x: ZeropageDepletedError) {
|
||||
printWarning(x.toString() + " variable ${variable.key} type ${variable.value.type}")
|
||||
printWarning(x.toString() + " variable $varname type ${value.type}")
|
||||
notAllocated++
|
||||
}
|
||||
}
|
||||
@ -399,6 +404,7 @@ class IntermediateProgram(val name: String, var loadAddress: Int, val heap: Heap
|
||||
if(decl.parent is StructDecl)
|
||||
return
|
||||
|
||||
val valueparams = VariableParameters(decl.zeropage, decl.struct)
|
||||
val value = when(decl.datatype) {
|
||||
in NumericDatatypes -> {
|
||||
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")
|
||||
}
|
||||
currentBlock.variables[scopedname] = value
|
||||
if(decl.zeropage)
|
||||
currentBlock.variables.add(Triple(scopedname, value, valueparams))
|
||||
if(decl.zeropage==ZeropageWish.PREFER_ZEROPAGE)
|
||||
currentBlock.variablesMarkedForZeropage.add(scopedname)
|
||||
else if(decl.zeropage==ZeropageWish.REQUIRE_ZEROPAGE)
|
||||
TODO("REQUIRE_ZEROPAGE not yet implemented")
|
||||
}
|
||||
VarDeclType.MEMORY -> {
|
||||
// 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("%variables")
|
||||
for (variable in blk.variables) {
|
||||
val valuestr = variable.value.toString()
|
||||
out.println("${variable.key} ${variable.value.type.name.toLowerCase()} $valuestr")
|
||||
for ((vname, value, parameters) in blk.variables) {
|
||||
if(parameters.zp==ZeropageWish.REQUIRE_ZEROPAGE)
|
||||
throw CompilerException("zp conflict")
|
||||
val valuestr = value.toString()
|
||||
out.println("$vname ${value.type.name.toLowerCase()} $valuestr")
|
||||
}
|
||||
out.println("%end_variables")
|
||||
out.println("%memorypointers")
|
||||
|
@ -6,6 +6,7 @@ package prog8.compiler.target.c64
|
||||
import prog8.ast.antlr.escape
|
||||
import prog8.ast.base.DataType
|
||||
import prog8.ast.base.initvarsSubName
|
||||
import prog8.ast.statements.ZeropageWish
|
||||
import prog8.vm.RuntimeValue
|
||||
import prog8.compiler.*
|
||||
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.
|
||||
val newblocks = mutableListOf<IntermediateProgram.ProgramBlock>()
|
||||
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 newlabels = block.labels.map { symname(it.key, block) to it.value}.toMap().toMutableMap()
|
||||
val newinstructions = block.instructions.asSequence().map {
|
||||
@ -236,18 +237,20 @@ class AsmGen(private val options: CompilationOptions, private val program: Inter
|
||||
}
|
||||
|
||||
// deal with zeropage variables
|
||||
for(variable in blk.variables) {
|
||||
val sym = symname(blk.name+"."+variable.key, null)
|
||||
for((varname, value, parameters) in blk.variables) {
|
||||
val sym = symname(blk.name+"."+varname, null)
|
||||
val zpVar = program.allocatedZeropageVariables[sym]
|
||||
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)
|
||||
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 {
|
||||
val address = zeropage.allocate(sym, variable.value.type, null)
|
||||
out("${variable.key} = $address\t; auto zp ${variable.value.type}")
|
||||
val address = zeropage.allocate(sym, value.type, null)
|
||||
out("$varname = $address\t; auto zp ${value.type}")
|
||||
// make sure we add the var to the set of zpvars for this block
|
||||
blk.variablesMarkedForZeropage.add(variable.key)
|
||||
program.allocatedZeropageVariables[sym] = Pair(address, variable.value.type)
|
||||
blk.variablesMarkedForZeropage.add(varname)
|
||||
program.allocatedZeropageVariables[sym] = Pair(address, value.type)
|
||||
} catch (x: ZeropageDepletedError) {
|
||||
// leave it as it is.
|
||||
}
|
||||
@ -255,7 +258,7 @@ class AsmGen(private val options: CompilationOptions, private val program: Inter
|
||||
}
|
||||
else {
|
||||
// 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) {
|
||||
// these are the non-zeropage variables
|
||||
val sortedVars = block.variables.filter{it.key !in block.variablesMarkedForZeropage}.toList().sortedBy { it.second.type }
|
||||
for (v in sortedVars) {
|
||||
when (v.second.type) {
|
||||
DataType.UBYTE -> out("${v.first}\t.byte 0")
|
||||
DataType.BYTE -> out("${v.first}\t.char 0")
|
||||
DataType.UWORD -> out("${v.first}\t.word 0")
|
||||
DataType.WORD -> out("${v.first}\t.sint 0")
|
||||
DataType.FLOAT -> out("${v.first}\t.byte 0,0,0,0,0 ; float")
|
||||
DataType.STR, DataType.STR_S -> {
|
||||
val rawStr = heap.get(v.second.heapId!!).str!!
|
||||
val bytes = encodeStr(rawStr, v.second.type).map { "$" + it.toString(16).padStart(2, '0') }
|
||||
out("${v.first}\t; ${v.second.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(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")
|
||||
}
|
||||
val uniqueNames = block.variables.map { it.first }.toSet()
|
||||
if (uniqueNames.size != block.variables.size)
|
||||
throw AssemblyError("not all variables have unique names")
|
||||
|
||||
// these are the non-zeropage variables.
|
||||
// first get all the flattened struct members, they MUST remain in order
|
||||
val (structMembers, normalVars) = block.variables.partition { it.third.memberOfStruct!=null }
|
||||
structMembers.forEach { vardecl2asm(it.first, it.second, it.third) }
|
||||
|
||||
// leave outsort the other variables by type
|
||||
val sortedVars = normalVars.sortedBy { it.second.type }
|
||||
for ((varname, value, parameters) in sortedVars) {
|
||||
if(varname in block.variablesMarkedForZeropage)
|
||||
continue // skip the ones that belong in the zero page
|
||||
vardecl2asm(varname, value, parameters)
|
||||
}
|
||||
}
|
||||
|
||||
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> {
|
||||
return when(dt) {
|
||||
DataType.STR -> {
|
||||
|
@ -650,7 +650,7 @@ internal class FlattenAnonymousScopesAndRemoveNops: IAstVisitor {
|
||||
nop.parent = namescope as Node
|
||||
namescope.statements[idx] = nop
|
||||
namescope.statements.addAll(idx, scope.statements)
|
||||
scope.statements.forEach { it.parent = namescope as Node }
|
||||
scope.statements.forEach { it.parent = namescope }
|
||||
visit(nop)
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,8 @@ package prog8.vm
|
||||
|
||||
import prog8.ast.base.*
|
||||
import prog8.ast.expressions.LiteralValue
|
||||
import prog8.ast.statements.StructDecl
|
||||
import prog8.ast.statements.ZeropageWish
|
||||
import prog8.compiler.HeapValues
|
||||
import prog8.compiler.target.c64.Petscii
|
||||
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)
|
||||
* 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 wordval: Int?
|
||||
@ -45,8 +48,7 @@ open class RuntimeValue(val type: DataType, num: Number?=null, val str: String?=
|
||||
if(elt.value.integer!=null)
|
||||
resultArray.add(elt.value.integer!!)
|
||||
else {
|
||||
println("ADDRESSOF ${elt.value}")
|
||||
resultArray.add(0x8000)
|
||||
TODO("ADDRESSOF ${elt.value}")
|
||||
}
|
||||
}
|
||||
RuntimeValue(value.type, array = resultArray.toTypedArray(), heapId = heapId)
|
||||
|
@ -3,6 +3,7 @@ package prog8.vm.astvm
|
||||
import prog8.ast.*
|
||||
import prog8.ast.base.ArrayElementTypes
|
||||
import prog8.ast.base.DataType
|
||||
import prog8.ast.base.FatalAstException
|
||||
import prog8.ast.base.VarDeclType
|
||||
import prog8.ast.expressions.*
|
||||
import prog8.ast.statements.BuiltinFunctionStatementPlaceholder
|
||||
@ -87,8 +88,14 @@ fun evaluate(expr: IExpression, ctx: EvalContext): RuntimeValue {
|
||||
}
|
||||
is AddressOf -> {
|
||||
// we support: address of heap var -> the heap id
|
||||
val heapId = expr.identifier.heapId(ctx.program.namespace)
|
||||
return RuntimeValue(DataType.UWORD, heapId)
|
||||
return try {
|
||||
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 -> {
|
||||
val address = evaluate(expr.addressExpression, ctx).wordval!!
|
||||
@ -99,22 +106,21 @@ fun evaluate(expr: IExpression, ctx: EvalContext): RuntimeValue {
|
||||
val scope = expr.definingScope()
|
||||
val variable = scope.lookup(expr.nameInSource, expr)
|
||||
if(variable is VarDecl) {
|
||||
if(variable.type==VarDeclType.VAR)
|
||||
return ctx.runtimeVars.get(variable.definingScope(), variable.name)
|
||||
else if(variable.datatype==DataType.STRUCT) {
|
||||
throw VmExecutionException("cannot process structs by-value. at ${expr.position}")
|
||||
}
|
||||
else {
|
||||
val address = ctx.runtimeVars.getMemoryAddress(variable.definingScope(), variable.name)
|
||||
return when(variable.datatype) {
|
||||
DataType.UBYTE -> RuntimeValue(DataType.UBYTE, ctx.mem.getUByte(address))
|
||||
DataType.BYTE -> RuntimeValue(DataType.BYTE, ctx.mem.getSByte(address))
|
||||
DataType.UWORD -> RuntimeValue(DataType.UWORD, ctx.mem.getUWord(address))
|
||||
DataType.WORD -> RuntimeValue(DataType.WORD, ctx.mem.getSWord(address))
|
||||
DataType.FLOAT -> RuntimeValue(DataType.FLOAT, ctx.mem.getFloat(address))
|
||||
DataType.STR -> RuntimeValue(DataType.STR, str = ctx.mem.getString(address))
|
||||
DataType.STR_S -> RuntimeValue(DataType.STR_S, str = ctx.mem.getScreencodeString(address))
|
||||
else -> throw VmExecutionException("unexpected datatype $variable")
|
||||
when {
|
||||
variable.type==VarDeclType.VAR -> return ctx.runtimeVars.get(variable.definingScope(), variable.name)
|
||||
variable.datatype==DataType.STRUCT -> throw VmExecutionException("cannot process structs by-value. at ${expr.position}")
|
||||
else -> {
|
||||
val address = ctx.runtimeVars.getMemoryAddress(variable.definingScope(), variable.name)
|
||||
return when(variable.datatype) {
|
||||
DataType.UBYTE -> RuntimeValue(DataType.UBYTE, ctx.mem.getUByte(address))
|
||||
DataType.BYTE -> RuntimeValue(DataType.BYTE, ctx.mem.getSByte(address))
|
||||
DataType.UWORD -> RuntimeValue(DataType.UWORD, ctx.mem.getUWord(address))
|
||||
DataType.WORD -> RuntimeValue(DataType.WORD, ctx.mem.getSWord(address))
|
||||
DataType.FLOAT -> RuntimeValue(DataType.FLOAT, ctx.mem.getFloat(address))
|
||||
DataType.STR -> RuntimeValue(DataType.STR, str = ctx.mem.getString(address))
|
||||
DataType.STR_S -> RuntimeValue(DataType.STR_S, str = ctx.mem.getScreencodeString(address))
|
||||
else -> throw VmExecutionException("unexpected datatype $variable")
|
||||
}
|
||||
}
|
||||
}
|
||||
} else
|
||||
|
@ -6,6 +6,7 @@ import prog8.ast.expressions.LiteralValue
|
||||
import prog8.ast.processing.IAstModifyingVisitor
|
||||
import prog8.ast.statements.StructDecl
|
||||
import prog8.ast.statements.VarDecl
|
||||
import prog8.ast.statements.ZeropageWish
|
||||
import prog8.compiler.HeapValues
|
||||
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))
|
||||
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
vdA.linkParents(program.namespace)
|
||||
vdX.linkParents(program.namespace)
|
||||
|
@ -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.INCLUDE_FILE -> throw VmExecutionException("stackVm doesn't support including a file $ins")
|
||||
Opcode.PUSH_ADDR_HEAPVAR -> {
|
||||
val heapId = variables.getValue(ins.callLabel!!).heapId!!
|
||||
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)
|
||||
val variable = variables.getValue(ins.callLabel!!)
|
||||
if(variable.heapId!=null) {
|
||||
val heapId = variable.heapId
|
||||
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_W_TO_B -> typecast(DataType.WORD, DataType.BYTE)
|
||||
|
@ -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
|
||||
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.
|
||||
|
||||
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,
|
||||
and then create a variable with it::
|
||||
|
@ -3,28 +3,26 @@
|
||||
|
||||
~ 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 {
|
||||
ubyte red
|
||||
uword red
|
||||
ubyte green
|
||||
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
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user