started with a simulator

This commit is contained in:
Irmen de Jong 2022-03-08 01:37:13 +01:00
parent 775d136b91
commit abcdd331db
25 changed files with 719 additions and 46 deletions

View File

@ -14,6 +14,7 @@
<module fileurl="file://$PROJECT_DIR$/examples/examples.iml" filepath="$PROJECT_DIR$/examples/examples.iml" />
<module fileurl="file://$PROJECT_DIR$/httpCompilerService/httpCompilerService.iml" filepath="$PROJECT_DIR$/httpCompilerService/httpCompilerService.iml" />
<module fileurl="file://$PROJECT_DIR$/parser/parser.iml" filepath="$PROJECT_DIR$/parser/parser.iml" />
<module fileurl="file://$PROJECT_DIR$/simulator/simulator.iml" filepath="$PROJECT_DIR$/simulator/simulator.iml" />
</modules>
</component>
</project>

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="KotlinJavaRuntime" level="project" />
</component>
</module>

View File

@ -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"

View File

@ -12,5 +12,6 @@
<orderEntry type="module" module-name="compilerAst" />
<orderEntry type="module" module-name="compilerInterfaces" />
<orderEntry type="library" name="michael.bull.kotlin.result.jvm" level="project" />
<orderEntry type="module" module-name="simulator" />
</component>
</module>

View File

@ -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")

View File

@ -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("<builtin>", 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))
}

View File

@ -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()
}

View File

@ -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")

View File

@ -23,6 +23,20 @@ class SymbolTable : StNode("", StNodeType.GLOBAL, Position.DUMMY) {
children.values.forEach { flatten(it) }
result
}
val allVariables: Collection<StStaticVariable> by lazy {
val vars = mutableListOf<StStaticVariable>()
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
}
}

View File

@ -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<PtNode> = mutableListOf()) {
sealed class PtNode(val position: Position, val children: MutableList<PtNode> = mutableListOf()) {
lateinit var parent: PtNode
@ -35,14 +33,27 @@ abstract class PtNode(val position: Position, val children: MutableList<PtNode>
}
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<String> 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<PtDirectiveArg>
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)
}

View File

@ -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<String>, position: Position) : PtNode(position) {
class PtIdentifier(val ref: List<String>, val targetName: List<String>, position: Position) : PtNode(position) {
override fun printProperties() {
print(name)
print("$ref --> $targetName")
}
}

View File

@ -11,14 +11,14 @@ import prog8.ast.toHex
class PtAsmSub(
val name: String,
name: String,
val address: UInt?,
val clobbers: Set<CpuRegister>,
val paramRegisters: List<RegisterOrStatusflag>,
val retvalRegisters: List<RegisterOrStatusflag>,
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<SubroutineParameter>,
val returntypes: List<DataType>,
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()}")
}

View File

@ -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

View File

@ -7,6 +7,7 @@ include(
':codeGenCpu6502',
':codeGenExperimental6502',
':compiler',
':simulator',
':dbusCompilerService',
':httpCompilerService'
)

73
simulator/build.gradle Normal file
View File

@ -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

18
simulator/simulator.iml Normal file
View File

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/test" isTestSource="true" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="KotlinJavaRuntime" level="project" />
<orderEntry type="module" module-name="compilerInterfaces" />
<orderEntry type="library" name="michael.bull.kotlin.result.jvm" level="project" />
<orderEntry type="library" name="io.kotest.runner.junit5.jvm" level="project" />
<orderEntry type="library" name="io.kotest.assertions.core.jvm" level="project" />
<orderEntry type="module" module-name="compilerAst" />
</component>
</module>

View File

@ -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<String>) {
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]}")
}

View File

@ -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<UByte> {
TODO("Not yet implemented")
}
override fun decodeString(bytes: List<UByte>, encoding: Encoding): String {
TODO("Not yet implemented")
}
}

View File

@ -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<Double, DataType> {
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<Double, DataType> {
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<Pair<Double, DataType>>) {
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<String>, 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<PtNamedNode>()
sub = nodes.first { it.name==namepart }
}
return sub as PtSub
}
private fun evaluate(ident: PtIdentifier): Pair<Double, DataType> {
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<Double, DataType> {
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)
}
}

View File

@ -0,0 +1,23 @@
package prog8.sim
import prog8.compilerinterface.intermediate.PtNode
class InstructionPointer(var instructions: List<PtNode>, start: Int=0) {
var currentIdx = start
val current: PtNode
get() {
if(currentIdx<instructions.size)
return instructions[currentIdx]
else
throw IllegalArgumentException("expected Return statement at end of statement list")
}
init {
require(instructions.isNotEmpty())
}
operator fun inc(): InstructionPointer {
currentIdx++
return this
}
}

View File

@ -0,0 +1,68 @@
package prog8.sim
class Memory {
val mem = ByteArray(65536)
operator fun get(addresss: UInt): UByte = getSByte(addresss).toUByte()
operator fun set(address: UInt, value: UByte) = setSByte(address, value.toByte())
fun getSByte(address: UInt): Byte = mem[address.toInt()]
fun setSByte(address: UInt, value: Byte) {
mem[address.toInt()] = value
}
fun getSWord(address: UInt): Int {
val word = getWord(address).toInt()
return if(word>=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++
}
}
}

View File

@ -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<InstructionPointer>()
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<Double, DataType> {
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
}
}

View File

@ -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}")
}
}

View File

@ -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"
}
})

46
simulator/test/TestSim.kt Normal file
View File

@ -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
}
})