diff --git a/.idea/modules.xml b/.idea/modules.xml
index 68b9739cd..fd2ee956b 100644
--- a/.idea/modules.xml
+++ b/.idea/modules.xml
@@ -14,6 +14,7 @@
+
\ No newline at end of file
diff --git a/codeGenCpu6502/simulator.iml b/codeGenCpu6502/simulator.iml
new file mode 100644
index 000000000..245d3429f
--- /dev/null
+++ b/codeGenCpu6502/simulator.iml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/codeGenExperimental6502/build.gradle b/codeGenExperimental6502/build.gradle
index b0eb3c62c..9e50c7c9e 100644
--- a/codeGenExperimental6502/build.gradle
+++ b/codeGenExperimental6502/build.gradle
@@ -26,6 +26,7 @@ compileTestKotlin {
dependencies {
implementation project(':compilerInterfaces')
implementation project(':compilerAst')
+ implementation project(':simulator')
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
// implementation "org.jetbrains.kotlin:kotlin-reflect"
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.14"
diff --git a/codeGenExperimental6502/codeGenExperimental6502.iml b/codeGenExperimental6502/codeGenExperimental6502.iml
index f04990594..44b4c2662 100644
--- a/codeGenExperimental6502/codeGenExperimental6502.iml
+++ b/codeGenExperimental6502/codeGenExperimental6502.iml
@@ -12,5 +12,6 @@
+
\ No newline at end of file
diff --git a/codeGenExperimental6502/src/prog8/codegen/experimental6502/AsmGen.kt b/codeGenExperimental6502/src/prog8/codegen/experimental6502/AsmGen.kt
index ef9f83100..4ddd624ce 100644
--- a/codeGenExperimental6502/src/prog8/codegen/experimental6502/AsmGen.kt
+++ b/codeGenExperimental6502/src/prog8/codegen/experimental6502/AsmGen.kt
@@ -1,8 +1,8 @@
package prog8.codegen.experimental6502
import prog8.ast.Program
-import prog8.ast.base.FatalAstException
import prog8.compilerinterface.*
+import prog8.sim.Simulator
class AsmGen(internal val program: Program,
internal val errors: IErrorReporter,
@@ -14,12 +14,14 @@ class AsmGen(internal val program: Program,
println("\n** experimental 65(c)02 code generator **\n")
symbolTable.print()
+ symbolTable.flat.forEach { println(it) }
// TODO temporary location to do this:
- val intermediateAst = IntermediateAstMaker.transform(program)
+ val intermediateAst = IntermediateAstMaker(program).transform()
intermediateAst.print()
- val entry = intermediateAst.entrypoint() ?: throw FatalAstException("no main.start() found")
- println(entry)
+
+ val sim = Simulator(intermediateAst, symbolTable)
+ sim.run()
println("..todo: create assembly code into ${options.outputDir.toAbsolutePath()}..")
return AssemblyProgram("dummy")
diff --git a/codeGenExperimental6502/src/prog8/codegen/experimental6502/IntermediateAstMaker.kt b/codeGenExperimental6502/src/prog8/codegen/experimental6502/IntermediateAstMaker.kt
index 8e7f94582..401635838 100644
--- a/codeGenExperimental6502/src/prog8/codegen/experimental6502/IntermediateAstMaker.kt
+++ b/codeGenExperimental6502/src/prog8/codegen/experimental6502/IntermediateAstMaker.kt
@@ -9,11 +9,10 @@ import prog8.ast.statements.*
import prog8.compilerinterface.intermediate.*
-object IntermediateAstMaker {
- fun transform(srcProgram: Program): PtProgram {
+class IntermediateAstMaker(val srcProgram: Program) {
+ fun transform(): PtProgram {
val program = PtProgram(
srcProgram.name,
- srcProgram.builtinFunctions,
srcProgram.memsizer,
srcProgram.encoding
)
@@ -28,7 +27,6 @@ object IntermediateAstMaker {
private fun transform(srcModule: Module): PtModule {
val module = PtModule(
srcModule.name,
- srcModule.source,
srcModule.loadAddress,
srcModule.isLibrary,
srcModule.position
@@ -115,8 +113,14 @@ object IntermediateAstMaker {
return target
}
- private fun transform(identifier: IdentifierReference): PtIdentifier =
- PtIdentifier(identifier.nameInSource, identifier.position)
+ private fun transform(identifier: IdentifierReference): PtIdentifier {
+ val target=identifier.targetStatement(srcProgram)!! as INamedStatement
+ val targetname = if(target.name in srcProgram.builtinFunctions.names)
+ listOf("", target.name)
+ else
+ target.scopedName
+ return PtIdentifier(identifier.nameInSource, targetname, identifier.position)
+ }
private fun transform(srcBlock: Block): PtBlock {
val block = PtBlock(srcBlock.name, srcBlock.address, srcBlock.isInLibrary, srcBlock.position)
@@ -240,9 +244,10 @@ object IntermediateAstMaker {
}
private fun transform(srcRepeat: RepeatLoop): PtRepeatLoop {
- val repeat = PtRepeatLoop(srcRepeat.iterations==null, srcRepeat.position)
- if(srcRepeat.iterations!=null)
- repeat.add(transformExpression(srcRepeat.iterations!!))
+ if(srcRepeat.iterations==null)
+ throw FatalAstException("repeat-forever loop should have been replaced with label+jump")
+ val repeat = PtRepeatLoop(srcRepeat.position)
+ repeat.add(transformExpression(srcRepeat.iterations!!))
for (statement in srcRepeat.body.statements) {
repeat.add(transformStatement(statement))
}
diff --git a/compiler/test/ast/TestIntermediateAst.kt b/compiler/test/ast/TestIntermediateAst.kt
index 91b645984..7b982506c 100644
--- a/compiler/test/ast/TestIntermediateAst.kt
+++ b/compiler/test/ast/TestIntermediateAst.kt
@@ -6,6 +6,7 @@ import io.kotest.matchers.ints.shouldBeGreaterThan
import io.kotest.matchers.shouldBe
import prog8.codegen.experimental6502.IntermediateAstMaker
import prog8.codegen.target.C64Target
+import prog8.compilerinterface.intermediate.PtVariable
import prog8tests.helpers.compileText
class TestIntermediateAst: FunSpec({
@@ -24,14 +25,18 @@ class TestIntermediateAst: FunSpec({
}
"""
val result = compileText(C64Target(), false, text, writeAssembly = false)!!
- val ast = IntermediateAstMaker.transform(result.program)
+ val ast = IntermediateAstMaker(result.program).transform()
ast.name shouldBe result.program.name
- ast.builtinFunctions.names shouldBe result.program.builtinFunctions.names
val entry = ast.entrypoint() ?: fail("no main.start() found")
entry.name shouldBe "start"
+ entry.scopedName shouldBe listOf("main", "start")
val blocks = ast.allBlocks().toList()
blocks.size shouldBeGreaterThan 1
blocks[0].name shouldBe "main"
+ blocks[0].scopedName shouldBe listOf("main")
+ val ccdecl = entry.children[0] as PtVariable
+ ccdecl.name shouldBe "cc"
+ ccdecl.scopedName shouldBe listOf("main", "start", "cc")
ast.print()
}
diff --git a/compilerAst/src/prog8/ast/statements/AstStatements.kt b/compilerAst/src/prog8/ast/statements/AstStatements.kt
index 13c81a4cc..cd8ac05cc 100644
--- a/compilerAst/src/prog8/ast/statements/AstStatements.kt
+++ b/compilerAst/src/prog8/ast/statements/AstStatements.kt
@@ -52,7 +52,7 @@ sealed class Statement : Node {
// this class is only created as temporary result from looking up the target for a builtin function call.
// this node is never actually part of the Ast.
-class BuiltinFunctionPlaceholder(val name: String, override val position: Position, override var parent: Node) : Statement() {
+class BuiltinFunctionPlaceholder(override val name: String, override val position: Position, override var parent: Node) : Statement(), INamedStatement {
override fun linkParents(parent: Node) {}
override fun accept(visitor: IAstVisitor) = throw FatalAstException("should not iterate over this node")
override fun accept(visitor: AstWalker, parent: Node) = throw FatalAstException("should not iterate over this node")
diff --git a/compilerInterfaces/src/prog8/compilerinterface/SymbolTable.kt b/compilerInterfaces/src/prog8/compilerinterface/SymbolTable.kt
index 8b5496eb8..aadf3bb9b 100644
--- a/compilerInterfaces/src/prog8/compilerinterface/SymbolTable.kt
+++ b/compilerInterfaces/src/prog8/compilerinterface/SymbolTable.kt
@@ -23,6 +23,20 @@ class SymbolTable : StNode("", StNodeType.GLOBAL, Position.DUMMY) {
children.values.forEach { flatten(it) }
result
}
+
+ val allVariables: Collection by lazy {
+ val vars = mutableListOf()
+ fun collect(node: StNode) {
+ for(child in node.children) {
+ if(child.value.type==StNodeType.STATICVAR)
+ vars.add(child.value as StStaticVariable)
+ else
+ collect(child.value)
+ }
+ }
+ collect(this)
+ vars
+ }
}
diff --git a/compilerInterfaces/src/prog8/compilerinterface/intermediate/AstBase.kt b/compilerInterfaces/src/prog8/compilerinterface/intermediate/AstBase.kt
index c36880663..92db71b16 100644
--- a/compilerInterfaces/src/prog8/compilerinterface/intermediate/AstBase.kt
+++ b/compilerInterfaces/src/prog8/compilerinterface/intermediate/AstBase.kt
@@ -1,15 +1,13 @@
package prog8.compilerinterface.intermediate
-import prog8.ast.IBuiltinFunctions
import prog8.ast.base.Position
import prog8.compilerinterface.IMemSizer
import prog8.compilerinterface.IStringEncoding
-import prog8.parser.SourceCode
// TODO : once the CodeGen doesn't need the old Ast anymore, get rid of the 'Pt' prefixes.
-abstract class PtNode(val position: Position, val children: MutableList = mutableListOf()) {
+sealed class PtNode(val position: Position, val children: MutableList = mutableListOf()) {
lateinit var parent: PtNode
@@ -35,14 +33,27 @@ abstract class PtNode(val position: Position, val children: MutableList
}
-class PtNodeGroup(): PtNode(Position.DUMMY) {
+class PtNodeGroup: PtNode(Position.DUMMY) {
override fun printProperties() {}
}
+abstract class PtNamedNode(val name: String, position: Position): PtNode(position) {
+ val scopedName: List by lazy {
+ if(this is PtModule)
+ emptyList()
+ else {
+ var namedParent: PtNode = this.parent
+ while(namedParent !is PtNamedNode)
+ namedParent = namedParent.parent
+ namedParent.scopedName + name
+ }
+ }
+}
+
+
class PtProgram(
val name: String,
- val builtinFunctions: IBuiltinFunctions,
val memsizer: IMemSizer,
val encoding: IStringEncoding
) : PtNode(Position.DUMMY) {
@@ -60,29 +71,29 @@ class PtProgram(
class PtModule(
- val name: String,
- val source: SourceCode,
+ name: String,
val loadAddress: UInt,
val library: Boolean,
position: Position
-) : PtNode(position) {
+) : PtNamedNode(name, position) {
override fun printProperties() {
print("$name addr=$loadAddress library=$library")
}
}
-class PtBlock(val name: String,
+class PtBlock(name: String,
val address: UInt?,
val library: Boolean,
- position: Position) : PtNode(position) {
+ position: Position
+) : PtNamedNode(name, position) {
override fun printProperties() {
print("$name addr=$address library=$library")
}
}
-class PtDirective(val name: String, position: Position) : PtNode(position) {
+class PtDirective(var name: String, position: Position) : PtNode(position) {
val args: List
get() = children.map { it as PtDirectiveArg }
@@ -108,7 +119,7 @@ class PtInlineAssembly(val assembly: String, position: Position) : PtNode(positi
}
-class PtLabel(val name: String, position: Position) : PtNode(position) {
+class PtLabel(name: String, position: Position) : PtNamedNode(name, position) {
override fun printProperties() {
print(name)
}
diff --git a/compilerInterfaces/src/prog8/compilerinterface/intermediate/AstExpressions.kt b/compilerInterfaces/src/prog8/compilerinterface/intermediate/AstExpressions.kt
index 0fa109b5d..b5e0f188e 100644
--- a/compilerInterfaces/src/prog8/compilerinterface/intermediate/AstExpressions.kt
+++ b/compilerInterfaces/src/prog8/compilerinterface/intermediate/AstExpressions.kt
@@ -6,8 +6,8 @@ import prog8.compilerinterface.Encoding
class PtAddressOf(position: Position) : PtNode(position) {
- val addr: PtNode
- get() = children.single()
+ val identifier: PtIdentifier
+ get() = children.single() as PtIdentifier
override fun printProperties() {}
}
@@ -51,9 +51,9 @@ class PtContainmentCheck(position: Position): PtNode(position) {
}
-class PtIdentifier(val name: List, position: Position) : PtNode(position) {
+class PtIdentifier(val ref: List, val targetName: List, position: Position) : PtNode(position) {
override fun printProperties() {
- print(name)
+ print("$ref --> $targetName")
}
}
diff --git a/compilerInterfaces/src/prog8/compilerinterface/intermediate/AstStatements.kt b/compilerInterfaces/src/prog8/compilerinterface/intermediate/AstStatements.kt
index 5de8e4b8c..706fb9796 100644
--- a/compilerInterfaces/src/prog8/compilerinterface/intermediate/AstStatements.kt
+++ b/compilerInterfaces/src/prog8/compilerinterface/intermediate/AstStatements.kt
@@ -11,14 +11,14 @@ import prog8.ast.toHex
class PtAsmSub(
- val name: String,
+ name: String,
val address: UInt?,
val clobbers: Set,
val paramRegisters: List,
val retvalRegisters: List,
val inline: Boolean,
position: Position
-) : PtNode(position) {
+) : PtNamedNode(name, position) {
override fun printProperties() {
print("$name inline=$inline")
}
@@ -26,19 +26,21 @@ class PtAsmSub(
class PtSub(
- val name: String,
+ name: String,
val parameters: List,
val returntypes: List,
val inline: Boolean,
position: Position
-) : PtNode(position) {
+) : PtNamedNode(name, position) {
override fun printProperties() {
print(name)
}
}
-class PtAssignment(val augmentable: Boolean, val origin: AssignmentOrigin, position: Position) : PtNode(position) {
+class PtAssignment(val augmentable: Boolean,
+ val origin: AssignmentOrigin, // TODO is this ever used in the codegen?
+ position: Position) : PtNode(position) {
val target: PtAssignTarget
get() = children[0] as PtAssignTarget
val value: PtNode
@@ -156,10 +158,11 @@ class PtPostIncrDecr(val operator: String, position: Position) : PtNode(position
}
-class PtRepeatLoop(val forever: Boolean, position: Position) : PtNode(position) {
- override fun printProperties() {
- print("forever=$forever")
- }
+class PtRepeatLoop(position: Position) : PtNode(position) {
+ val count: PtNode
+ get() = children.single()
+
+ override fun printProperties() {}
}
@@ -177,7 +180,7 @@ class PtReturn(position: Position) : PtNode(position) {
}
-class PtVariable(val name: String, val type: DataType, position: Position) : PtNode(position) {
+class PtVariable(name: String, val type: DataType, position: Position) : PtNamedNode(name, position) {
override fun printProperties() {
print("$type $name")
}
@@ -191,7 +194,7 @@ class PtConstant(val name: String, val type: DataType, val value: Double, positi
}
-class PtMemMapped(val name: String, val type: DataType, val address: UInt, position: Position) : PtNode(position) {
+class PtMemMapped(name: String, val type: DataType, val address: UInt, position: Position) : PtNamedNode(name, position) {
override fun printProperties() {
print("&$type $name = ${address.toHex()}")
}
diff --git a/examples/test.p8 b/examples/test.p8
index 866ab6561..377a16819 100644
--- a/examples/test.p8
+++ b/examples/test.p8
@@ -12,9 +12,6 @@ main {
sub start() {
sys.memset(sieve, 256, false) ; clear the sieve, to reset starting situation on subsequent runs
- ubyte qq = candidate_prime |> sin8u() |> cos8u()
- candidate_prime |> sin8u() |> txt.print_ub()
-
; calculate primes
txt.print("prime numbers up to 255:\n\n")
ubyte amount=0
diff --git a/settings.gradle b/settings.gradle
index 6cf8f044d..b5fa4ed1a 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -7,6 +7,7 @@ include(
':codeGenCpu6502',
':codeGenExperimental6502',
':compiler',
+ ':simulator',
':dbusCompilerService',
':httpCompilerService'
)
diff --git a/simulator/build.gradle b/simulator/build.gradle
new file mode 100644
index 000000000..235a754f9
--- /dev/null
+++ b/simulator/build.gradle
@@ -0,0 +1,73 @@
+plugins {
+ id 'java'
+ id 'application'
+ id "org.jetbrains.kotlin.jvm"
+ id "io.kotest" version "0.3.9"
+}
+
+java {
+ toolchain {
+ languageVersion = JavaLanguageVersion.of(javaVersion)
+ }
+}
+
+compileKotlin {
+ kotlinOptions {
+ jvmTarget = javaVersion
+ }
+}
+
+compileTestKotlin {
+ kotlinOptions {
+ jvmTarget = javaVersion
+ }
+}
+
+dependencies {
+ implementation project(':compilerInterfaces')
+ implementation project(':compilerAst')
+ implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
+ // implementation "org.jetbrains.kotlin:kotlin-reflect"
+ implementation 'org.jetbrains.kotlinx:kotlinx-cli:0.3.4'
+ implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.14"
+
+ testImplementation 'io.kotest:kotest-runner-junit5-jvm:5.1.0'
+}
+
+sourceSets {
+ main {
+ java {
+ srcDirs = ["${project.projectDir}/src"]
+ }
+ resources {
+ srcDirs = ["${project.projectDir}/res"]
+ }
+ }
+ test {
+ java {
+ srcDir "${project.projectDir}/test"
+ }
+ }
+}
+
+startScripts.enabled = true
+
+application {
+ mainClass = 'prog8.SimulatorMainKt'
+ applicationName = 'p8sim'
+}
+
+test {
+ // Enable JUnit 5 (Gradle 4.6+).
+ useJUnitPlatform()
+
+ // Always run tests, even when nothing changed.
+ dependsOn 'cleanTest'
+
+ // Show test results.
+ testLogging {
+ events "skipped", "failed"
+ }
+}
+
+build.finalizedBy installDist
\ No newline at end of file
diff --git a/simulator/simulator.iml b/simulator/simulator.iml
new file mode 100644
index 000000000..19508275d
--- /dev/null
+++ b/simulator/simulator.iml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/simulator/src/prog8/SimulatorMain.kt b/simulator/src/prog8/SimulatorMain.kt
new file mode 100644
index 000000000..9c9ce8337
--- /dev/null
+++ b/simulator/src/prog8/SimulatorMain.kt
@@ -0,0 +1,45 @@
+package prog8
+
+import prog8.ast.base.DataType
+import prog8.ast.base.Position
+import prog8.ast.statements.AssignmentOrigin
+import prog8.compilerinterface.Encoding
+import prog8.compilerinterface.SymbolTable
+import prog8.compilerinterface.intermediate.*
+import prog8.sim.MemSizer
+import prog8.sim.Simulator
+import prog8.sim.StringEncoding
+
+fun main(args: Array) {
+ println("\nProg8 simulator by Irmen de Jong (irmen@razorvine.net)")
+ println("This software is licensed under the GNU GPL 3.0, see https://www.gnu.org/licenses/gpl.html\n")
+ println("...todo: run from file...")
+
+ val program = PtProgram("test", MemSizer, StringEncoding)
+ val module = PtModule("test", 0u, false, Position.DUMMY)
+ val block = PtBlock("main", null, false, Position.DUMMY)
+ val sub = PtSub("start", emptyList(), emptyList(), false, Position.DUMMY)
+ block.add(sub)
+ module.add(block)
+ program.add(module)
+ val fcall = PtBuiltinFunctionCall("print", Position.DUMMY).also {
+ it.add(PtString("Hello, world! From the program.\n", Encoding.DEFAULT, Position.DUMMY))
+ }
+ sub.add(fcall)
+ val memwrite = PtAssignment(false, AssignmentOrigin.USERCODE, Position.DUMMY).also { assign ->
+ assign.add(PtAssignTarget(Position.DUMMY).also { tgt ->
+ tgt.add(PtMemoryByte(Position.DUMMY).also { mb ->
+ mb.add(PtNumber(DataType.UWORD, 1000.0, Position.DUMMY))
+ })
+ })
+ assign.add(PtNumber(DataType.UBYTE, 99.0, Position.DUMMY))
+ }
+ sub.add(memwrite)
+ sub.add(PtReturn(Position.DUMMY))
+ val symboltable = SymbolTable()
+
+ val sim = Simulator(program, symboltable)
+ println("memory at $1000=${sim.memory[1000u]}")
+ sim.run()
+ println("memory at $1000=${sim.memory[1000u]}")
+}
diff --git a/simulator/src/prog8/sim/BuiltinFunctions.kt b/simulator/src/prog8/sim/BuiltinFunctions.kt
new file mode 100644
index 000000000..f60f0c64b
--- /dev/null
+++ b/simulator/src/prog8/sim/BuiltinFunctions.kt
@@ -0,0 +1,27 @@
+package prog8.sim
+
+import prog8.ast.base.DataType
+import prog8.ast.statements.VarDecl
+import prog8.compilerinterface.Encoding
+import prog8.compilerinterface.IMemSizer
+import prog8.compilerinterface.IStringEncoding
+
+internal object MemSizer: IMemSizer {
+ override fun memorySize(dt: DataType): Int {
+ TODO("Not yet implemented")
+ }
+
+ override fun memorySize(decl: VarDecl): Int {
+ TODO("Not yet implemented")
+ }
+}
+
+internal object StringEncoding: IStringEncoding {
+ override fun encodeString(str: String, encoding: Encoding): List {
+ TODO("Not yet implemented")
+ }
+
+ override fun decodeString(bytes: List, encoding: Encoding): String {
+ TODO("Not yet implemented")
+ }
+}
\ No newline at end of file
diff --git a/simulator/src/prog8/sim/Evaluator.kt b/simulator/src/prog8/sim/Evaluator.kt
new file mode 100644
index 000000000..acdb53650
--- /dev/null
+++ b/simulator/src/prog8/sim/Evaluator.kt
@@ -0,0 +1,104 @@
+package prog8.sim
+
+import prog8.ast.base.DataType
+import prog8.compilerinterface.StNodeType
+import prog8.compilerinterface.StStaticVariable
+import prog8.compilerinterface.SymbolTable
+import prog8.compilerinterface.intermediate.*
+
+class Evaluator(
+ val symboltable: SymbolTable,
+ val memory: Memory,
+ val variables: Variables,
+ val simulator: Simulator
+) {
+
+ fun evaluateExpression(expr: PtNode): Pair {
+ return when(expr) {
+ is PtAddressOf -> evaluate(expr)
+ is PtArrayIndexer -> TODO()
+ is PtArrayLiteral -> throw IllegalArgumentException("arrayliteral $expr")
+ is PtBinaryExpression -> TODO()
+ is PtBuiltinFunctionCall -> TODO()
+ is PtConstant -> TODO()
+ is PtContainmentCheck -> TODO()
+ is PtFunctionCall -> evaluate(expr)
+ is PtIdentifier -> evaluate(expr)
+ is PtMemoryByte -> TODO()
+ is PtNumber -> Pair(expr.number, expr.type)
+ is PtPipe -> TODO()
+ is PtPrefix -> TODO()
+ is PtRange -> throw IllegalArgumentException("range $expr")
+ is PtString -> throw IllegalArgumentException("string $expr")
+ is PtTypeCast -> TODO()
+ else -> TODO("missing evaluator for $expr")
+ }
+ }
+
+ internal fun evaluate(fcall: PtFunctionCall): Pair {
+ val ref = fcall.target.ref
+ val args = fcall.args.children.map { evaluateExpression(it) }
+ return when(fcall.target.targetName) {
+ listOf("sys", "memset") -> {
+ memory.memset(args[0].first.toUInt(), args[1].first.toUInt(), args[2].first.toInt().toUByte())
+ Pair(0.0, DataType.UBYTE)
+ }
+ listOf("txt", "print") -> {
+ print(memory.getString(args.single().first.toUInt())) // strings are passed as a memory address
+ Pair(0.0, DataType.UBYTE)
+ }
+ else -> {
+ val sub = findPtNode(fcall.target.targetName, fcall)
+ passCallArgs(sub, args)
+ return simulator.executeSubroutine(sub)
+ }
+ }
+ }
+
+ private fun passCallArgs(sub: PtSub, args: List>) {
+ require(sub.parameters.size==args.size)
+ for ((param, arg) in sub.parameters.zip(args)) {
+ require(param.type==arg.second)
+ println("ARG ${param.name} = ${arg.first}") // TODO assign arg
+ }
+ }
+
+ private fun findPtNode(scopedName: List, scope: PtNode): PtSub {
+ var root = scope
+ while(root !is PtProgram) {
+ root=root.parent
+ }
+ val block = root.allBlocks().first {
+ it.name == scopedName.first()
+ }
+ var sub: PtNode = block
+ scopedName.drop(1).forEach { namepart->
+ val nodes = sub.children.filterIsInstance()
+ sub = nodes.first { it.name==namepart }
+ }
+ return sub as PtSub
+ }
+
+ private fun evaluate(ident: PtIdentifier): Pair {
+ val target = symboltable.flat.getValue(ident.targetName)
+ when(target.type) {
+ StNodeType.STATICVAR -> {
+ val variable = target as StStaticVariable
+ return Pair(variables.getValue(variable), target.dt)
+ }
+ StNodeType.CONSTANT -> throw IllegalArgumentException("constants should have been const folded")
+ else -> throw IllegalArgumentException("weird ref target")
+ }
+ }
+
+ private fun evaluate(addressOf: PtAddressOf): Pair {
+ val target = symboltable.flat.getValue(addressOf.identifier.targetName)
+ val alloc = variables[target]
+ return if(alloc==null) {
+ // TODO throw IllegalArgumentException("can't get address of ${target.scopedName}")
+ println("warning: returning dummy address for ${target.scopedName}")
+ Pair(4096.0, DataType.UWORD)
+ } else
+ Pair(alloc.toDouble(), DataType.UWORD)
+ }
+}
\ No newline at end of file
diff --git a/simulator/src/prog8/sim/InstructionPointer.kt b/simulator/src/prog8/sim/InstructionPointer.kt
new file mode 100644
index 000000000..64ed3100d
--- /dev/null
+++ b/simulator/src/prog8/sim/InstructionPointer.kt
@@ -0,0 +1,23 @@
+package prog8.sim
+
+import prog8.compilerinterface.intermediate.PtNode
+
+class InstructionPointer(var instructions: List, start: Int=0) {
+ var currentIdx = start
+ val current: PtNode
+ get() {
+ if(currentIdx=32768)
+ -(65536-word)
+ else
+ word
+ }
+
+ fun setSWord(address: UInt, value: Int) = setWord(address, value.toUInt())
+
+ fun getWord(address: UInt): UInt {
+ val lsb = mem[address.toInt()].toUByte()
+ val msb = mem[address.toInt()+1].toUByte()
+ return lsb + msb*256u
+ }
+
+ fun setWord(address: UInt, value: UInt) {
+ val lsb = value.toByte()
+ val msb = (value shr 8).toByte()
+ mem[address.toInt()] = lsb
+ mem[address.toInt()+1] = msb
+ }
+
+ fun clear() {
+ for(i in 0..65535)
+ mem[i]=0
+ }
+
+ fun setString(address: UInt, str: String, zeroTerminate: Boolean=true) {
+ var addr = address.toInt()
+ for (it in str.toByteArray(Charsets.ISO_8859_1)) {
+ mem[addr] = it
+ addr++
+ }
+ if(zeroTerminate)
+ mem[addr] = 0
+ }
+
+ fun getString(address: UInt): String {
+ var addr = address.toInt()
+ while(mem[addr] != 0.toByte()) addr++
+ return String(mem, address.toInt(), addr-address.toInt(), Charsets.ISO_8859_1)
+ }
+
+ fun memset(address: UInt, length: UInt, value: UByte) {
+ var addr=address.toInt()
+ val byteval = value.toByte()
+ repeat(length.toInt()) {
+ mem[addr] = byteval
+ addr++
+ }
+ }
+}
\ No newline at end of file
diff --git a/simulator/src/prog8/sim/Simulator.kt b/simulator/src/prog8/sim/Simulator.kt
new file mode 100644
index 000000000..61e5d26f9
--- /dev/null
+++ b/simulator/src/prog8/sim/Simulator.kt
@@ -0,0 +1,124 @@
+package prog8.sim
+
+import prog8.ast.base.DataType
+import prog8.ast.base.Position
+import prog8.compilerinterface.Encoding
+import prog8.compilerinterface.StStaticVariable
+import prog8.compilerinterface.SymbolTable
+import prog8.compilerinterface.intermediate.*
+import java.util.Stack
+
+
+class ExitProgram(val status: Int): Exception()
+
+
+class Simulator(val program: PtProgram, val symboltable: SymbolTable) {
+ val memory = Memory()
+ private val variables = Variables(symboltable, program.memsizer)
+ private val eval = Evaluator(symboltable, memory, variables, this)
+ private val callStack = Stack()
+ private var instructionPtr = InstructionPointer(listOf(PtReturn(Position.DUMMY)))
+
+ fun run() {
+ memory.clear()
+ val start = program.entrypoint() ?: throw NoSuchElementException("no main.start() found")
+ try {
+ executeSubroutine(start)
+ } catch (exit: ExitProgram) {
+ println("Program Exit! Status code: ${exit.status}")
+ }
+ }
+
+ internal fun executeSubroutine(sub: PtSub): Pair {
+ instructionPtr = InstructionPointer(sub.children)
+ callStack.push(instructionPtr)
+ while(true) {
+ val incr = executeStatement(instructionPtr.current)
+ if(callStack.empty())
+ throw ExitProgram(0)
+ if(incr)
+ instructionPtr++
+ }
+ }
+
+ internal fun executeStatement(node: PtNode): Boolean {
+ return when(node) {
+ is PtAsmSub -> true
+ is PtAssignment -> execute(node)
+ is PtBuiltinFunctionCall -> execute(node)
+ is PtConditionalBranch -> TODO()
+ is PtDirective -> execute(node)
+ is PtForLoop -> TODO()
+ is PtFunctionCall -> {
+ eval.evaluate(node) // throw away any result
+ true
+ }
+ is PtGosub -> TODO()
+ is PtIfElse -> TODO()
+ is PtInlineAssembly -> throw NotImplementedError("can't run inline assembly in simulator at this time")
+ is PtJump -> TODO()
+ is PtLabel -> true
+ is PtPipe -> execute(node)
+ is PtPostIncrDecr -> TODO()
+ is PtRepeatLoop -> execute(node)
+ is PtReturn -> execute(node)
+ is PtSub -> true // this simulator doesn't "fall through" into nested subroutines
+ is PtVariable -> true
+ is PtWhen -> TODO()
+ else -> TODO("missing code for node $node")
+ }
+ }
+
+ private fun execute(repeat: PtRepeatLoop): Boolean {
+ TODO("repeat $repeat. ${repeat.position}")
+ return true
+ }
+
+ private fun execute(pipe: PtPipe): Boolean {
+ TODO("pipe stmt $pipe")
+ }
+
+ private fun execute(ret: PtReturn): Boolean {
+ instructionPtr = callStack.pop()
+ return true
+ }
+
+ private fun execute(directive: PtDirective): Boolean {
+ // TODO handle directive
+ return true
+ }
+
+ private fun execute(assign: PtAssignment): Boolean {
+ val value = eval.evaluateExpression(assign.value)
+ val identifier = assign.target.identifier
+ val memoryAddr = assign.target.memory
+ val array = assign.target.array
+ if(identifier!=null) {
+ val targetvar = symboltable.flat.getValue(identifier.targetName) as StStaticVariable
+ variables.setValue(targetvar, value.first)
+ }
+ else if(memoryAddr!=null) {
+ val address = eval.evaluateExpression(memoryAddr.address)
+ require(address.second==DataType.UWORD)
+ require(value.second==DataType.UBYTE)
+ memory[address.first.toUInt()] = value.first.toInt().toUByte()
+ }
+ else if(array!=null)
+ TODO("assign $value to array $array")
+ else
+ throw IllegalArgumentException("missing assign target")
+ return true
+ }
+
+ private fun execute(fcall: PtBuiltinFunctionCall): Boolean {
+ when(fcall.name) {
+ "print" -> {
+ val string = fcall.children.single() as PtString
+ require(string.encoding==Encoding.DEFAULT)
+ print(string.value)
+ }
+ else -> TODO("missing builtin function ${fcall.name}")
+ }
+ return true
+ }
+}
\ No newline at end of file
diff --git a/simulator/src/prog8/sim/Variables.kt b/simulator/src/prog8/sim/Variables.kt
new file mode 100644
index 000000000..21f6d13fa
--- /dev/null
+++ b/simulator/src/prog8/sim/Variables.kt
@@ -0,0 +1,25 @@
+package prog8.sim
+
+import prog8.compilerinterface.IMemSizer
+import prog8.compilerinterface.StNode
+import prog8.compilerinterface.StStaticVariable
+import prog8.compilerinterface.SymbolTable
+
+class Variables(symboltable: SymbolTable, memsizer: IMemSizer) {
+
+ val allVars = symboltable.allVariables
+ val flatSymbolTable = symboltable.flat
+
+ operator fun get(target: StNode): UInt? {
+ return null
+ }
+
+ fun getValue(variable: StStaticVariable): Double {
+ println("warning: returning dummy value for staticvar ${variable.scopedName}")
+ return 0.0
+ }
+
+ fun setValue(variable: StStaticVariable, value: Double) {
+ println("warning: discarding value for staticvar ${variable.scopedName}")
+ }
+}
\ No newline at end of file
diff --git a/simulator/test/TestMemory.kt b/simulator/test/TestMemory.kt
new file mode 100644
index 000000000..23a659fc2
--- /dev/null
+++ b/simulator/test/TestMemory.kt
@@ -0,0 +1,67 @@
+package prog8simulatortests
+
+import io.kotest.core.spec.style.FunSpec
+import io.kotest.matchers.shouldBe
+import prog8.sim.Memory
+
+class TestMemory : FunSpec({
+
+ val mem = Memory()
+
+ test("unsigned byte") {
+ mem.clear()
+ mem[100u] shouldBe 0u
+ mem[100u] = 99u
+ mem[100u] shouldBe 99u
+ mem[100u] = 254u
+ mem[100u] shouldBe 254u
+ mem.getSByte(100u) shouldBe -2
+ }
+
+ test("signed byte") {
+ mem.clear()
+ mem.getSByte(100u) shouldBe 0
+ mem.setSByte(100u, 99)
+ mem.getSByte(100u) shouldBe 99
+ mem.setSByte(100u, -2)
+ mem.getSByte(100u) shouldBe -2
+ mem[100u] shouldBe 254u
+ }
+
+ test("unsigned word") {
+ mem.clear()
+ mem.getWord(100u) shouldBe 0u
+ mem.setWord(100u, 12345u)
+ mem.getWord(100u) shouldBe 12345u
+ mem.getSWord(100u) shouldBe 12345
+ mem.setWord(100u, 54321u)
+ mem.getWord(100u) shouldBe 54321u
+ mem.getSWord(100u) shouldBe -11215
+ mem[100u] shouldBe 0x31u
+ mem[101u] shouldBe 0xd4u
+ }
+
+ test("signed word") {
+ mem.clear()
+ mem.getSWord(100u) shouldBe 0
+ mem.setSWord(100u, 12345)
+ mem.getSWord(100u) shouldBe 12345
+ mem.getWord(100u) shouldBe 12345u
+ mem.setSWord(100u, -12345)
+ mem.getSWord(100u) shouldBe -12345
+ mem.getWord(100u) shouldBe 53191u
+ mem[100u] shouldBe 0xc7u
+ mem[101u] shouldBe 0xcfu
+ }
+
+ test("string") {
+ mem.clear()
+ mem[105u] = 1u
+ mem.setString(100u, "Hello")
+ mem[100u] shouldBe 'H'.code.toUByte()
+ mem[104u] shouldBe 'o'.code.toUByte()
+ mem[105u] shouldBe 0u
+
+ mem.getString(100u) shouldBe "Hello"
+ }
+})
\ No newline at end of file
diff --git a/simulator/test/TestSim.kt b/simulator/test/TestSim.kt
new file mode 100644
index 000000000..93429d40e
--- /dev/null
+++ b/simulator/test/TestSim.kt
@@ -0,0 +1,46 @@
+package prog8simulatortests
+
+import io.kotest.core.spec.style.FunSpec
+import io.kotest.matchers.shouldBe
+import prog8.ast.base.DataType
+import prog8.ast.base.Position
+import prog8.ast.statements.AssignmentOrigin
+import prog8.compilerinterface.Encoding
+import prog8.compilerinterface.SymbolTable
+import prog8.compilerinterface.intermediate.*
+import prog8.sim.MemSizer
+import prog8.sim.Simulator
+import prog8.sim.StringEncoding
+
+class TestSim : FunSpec({
+ test("simple program simulation") {
+ val program = PtProgram("test", MemSizer, StringEncoding)
+ val module = PtModule("test", 0u, false, Position.DUMMY)
+ val block = PtBlock("main", null, false, Position.DUMMY)
+ val sub = PtSub("start", emptyList(), emptyList(), false, Position.DUMMY)
+ block.add(sub)
+ module.add(block)
+ program.add(module)
+ val fcall = PtBuiltinFunctionCall("print", Position.DUMMY).also {
+ it.add(PtString("Hello, world! From the program.\n", Encoding.DEFAULT, Position.DUMMY))
+ }
+ sub.add(fcall)
+ val memwrite = PtAssignment(false, AssignmentOrigin.USERCODE, Position.DUMMY).also { assign ->
+ assign.add(PtAssignTarget(Position.DUMMY).also { tgt ->
+ tgt.add(PtMemoryByte(Position.DUMMY).also { mb ->
+ mb.add(PtNumber(DataType.UWORD, 1000.0, Position.DUMMY))
+ })
+ })
+ assign.add(PtNumber(DataType.UBYTE, 99.0, Position.DUMMY))
+ }
+ sub.add(memwrite)
+ sub.add(PtReturn(Position.DUMMY))
+
+ val symboltable = SymbolTable()
+
+ val sim = Simulator(program, symboltable)
+ sim.memory[1000u] shouldBe 0u
+ sim.run()
+ sim.memory[1000u] shouldBe 99u
+ }
+})
\ No newline at end of file