mirror of
https://github.com/irmen/prog8.git
synced 2024-08-23 10:29:40 +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(
|
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,
|
||||||
|
@ -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))
|
||||||
|
@ -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,
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
@ -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,
|
||||||
|
@ -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(" ")
|
||||||
|
@ -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")
|
||||||
}
|
}
|
||||||
|
@ -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")
|
||||||
|
@ -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")
|
|
||||||
|
// 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 -> {
|
DataType.STR, DataType.STR_S -> {
|
||||||
val rawStr = heap.get(v.second.heapId!!).str!!
|
val rawStr = heap.get(value.heapId!!).str!!
|
||||||
val bytes = encodeStr(rawStr, v.second.type).map { "$" + it.toString(16).padStart(2, '0') }
|
val bytes = encodeStr(rawStr, value.type).map { "$" + it.toString(16).padStart(2, '0') }
|
||||||
out("${v.first}\t; ${v.second.type} \"${escape(rawStr).replace("\u0000", "<NULL>")}\"")
|
out("$varname\t; ${value.type} \"${escape(rawStr).replace("\u0000", "<NULL>")}\"")
|
||||||
for (chunk in bytes.chunked(16))
|
for (chunk in bytes.chunked(16))
|
||||||
out(" .byte " + chunk.joinToString())
|
out(" .byte " + chunk.joinToString())
|
||||||
}
|
}
|
||||||
DataType.ARRAY_UB -> {
|
DataType.ARRAY_UB -> {
|
||||||
// unsigned integer byte arraysize
|
// unsigned integer byte arraysize
|
||||||
val data = makeArrayFillDataUnsigned(v.second)
|
val data = makeArrayFillDataUnsigned(value)
|
||||||
if (data.size <= 16)
|
if (data.size <= 16)
|
||||||
out("${v.first}\t.byte ${data.joinToString()}")
|
out("$varname\t.byte ${data.joinToString()}")
|
||||||
else {
|
else {
|
||||||
out(v.first)
|
out(varname)
|
||||||
for (chunk in data.chunked(16))
|
for (chunk in data.chunked(16))
|
||||||
out(" .byte " + chunk.joinToString())
|
out(" .byte " + chunk.joinToString())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DataType.ARRAY_B -> {
|
DataType.ARRAY_B -> {
|
||||||
// signed integer byte arraysize
|
// signed integer byte arraysize
|
||||||
val data = makeArrayFillDataSigned(v.second)
|
val data = makeArrayFillDataSigned(value)
|
||||||
if (data.size <= 16)
|
if (data.size <= 16)
|
||||||
out("${v.first}\t.char ${data.joinToString()}")
|
out("$varname\t.char ${data.joinToString()}")
|
||||||
else {
|
else {
|
||||||
out(v.first)
|
out(varname)
|
||||||
for (chunk in data.chunked(16))
|
for (chunk in data.chunked(16))
|
||||||
out(" .char " + chunk.joinToString())
|
out(" .char " + chunk.joinToString())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DataType.ARRAY_UW -> {
|
DataType.ARRAY_UW -> {
|
||||||
// unsigned word arraysize
|
// unsigned word arraysize
|
||||||
val data = makeArrayFillDataUnsigned(v.second)
|
val data = makeArrayFillDataUnsigned(value)
|
||||||
if (data.size <= 16)
|
if (data.size <= 16)
|
||||||
out("${v.first}\t.word ${data.joinToString()}")
|
out("$varname\t.word ${data.joinToString()}")
|
||||||
else {
|
else {
|
||||||
out(v.first)
|
out(varname)
|
||||||
for (chunk in data.chunked(16))
|
for (chunk in data.chunked(16))
|
||||||
out(" .word " + chunk.joinToString())
|
out(" .word " + chunk.joinToString())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DataType.ARRAY_W -> {
|
DataType.ARRAY_W -> {
|
||||||
// signed word arraysize
|
// signed word arraysize
|
||||||
val data = makeArrayFillDataSigned(v.second)
|
val data = makeArrayFillDataSigned(value)
|
||||||
if (data.size <= 16)
|
if (data.size <= 16)
|
||||||
out("${v.first}\t.sint ${data.joinToString()}")
|
out("$varname\t.sint ${data.joinToString()}")
|
||||||
else {
|
else {
|
||||||
out(v.first)
|
out(varname)
|
||||||
for (chunk in data.chunked(16))
|
for (chunk in data.chunked(16))
|
||||||
out(" .sint " + chunk.joinToString())
|
out(" .sint " + chunk.joinToString())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DataType.ARRAY_F -> {
|
DataType.ARRAY_F -> {
|
||||||
// float arraysize
|
// float arraysize
|
||||||
val array = heap.get(v.second.heapId!!).doubleArray!!
|
val array = heap.get(value.heapId!!).doubleArray!!
|
||||||
val floatFills = array.map { makeFloatFill(Mflpt5.fromNumber(it)) }
|
val floatFills = array.map { makeFloatFill(Mflpt5.fromNumber(it)) }
|
||||||
out(v.first)
|
out(varname)
|
||||||
for(f in array.zip(floatFills))
|
for(f in array.zip(floatFills))
|
||||||
out(" .byte ${f.second} ; float ${f.first}")
|
out(" .byte ${f.second} ; float ${f.first}")
|
||||||
}
|
}
|
||||||
DataType.STRUCT -> TODO("datatype struct")
|
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 -> {
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
@ -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
|
||||||
|
return try {
|
||||||
val heapId = expr.identifier.heapId(ctx.program.namespace)
|
val heapId = expr.identifier.heapId(ctx.program.namespace)
|
||||||
return RuntimeValue(DataType.UWORD, heapId)
|
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,12 +106,10 @@ 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 -> {
|
||||||
}
|
|
||||||
else {
|
|
||||||
val address = ctx.runtimeVars.getMemoryAddress(variable.definingScope(), variable.name)
|
val address = ctx.runtimeVars.getMemoryAddress(variable.definingScope(), variable.name)
|
||||||
return when(variable.datatype) {
|
return when(variable.datatype) {
|
||||||
DataType.UBYTE -> RuntimeValue(DataType.UBYTE, ctx.mem.getUByte(address))
|
DataType.UBYTE -> RuntimeValue(DataType.UBYTE, ctx.mem.getUByte(address))
|
||||||
@ -117,6 +122,7 @@ fun evaluate(expr: IExpression, ctx: EvalContext): RuntimeValue {
|
|||||||
else -> throw VmExecutionException("unexpected datatype $variable")
|
else -> throw VmExecutionException("unexpected datatype $variable")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} else
|
} else
|
||||||
throw VmExecutionException("weird identifier reference $variable")
|
throw VmExecutionException("weird identifier reference $variable")
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
@ -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(variable.heapId!=null) {
|
||||||
|
val heapId = variable.heapId
|
||||||
if (heapId < 0)
|
if (heapId < 0)
|
||||||
throw VmExecutionException("expected variable on heap")
|
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)
|
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)
|
||||||
|
@ -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::
|
||||||
|
@ -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
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user