mirror of
https://github.com/irmen/prog8.git
synced 2025-01-10 20:30:23 +00:00
started with a simulator
This commit is contained in:
parent
775d136b91
commit
abcdd331db
1
.idea/modules.xml
generated
1
.idea/modules.xml
generated
@ -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>
|
12
codeGenCpu6502/simulator.iml
Normal file
12
codeGenCpu6502/simulator.iml
Normal 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>
|
@ -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"
|
||||
|
@ -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>
|
@ -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")
|
||||
|
@ -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))
|
||||
}
|
||||
|
@ -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()
|
||||
}
|
||||
|
||||
|
@ -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")
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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()}")
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -7,6 +7,7 @@ include(
|
||||
':codeGenCpu6502',
|
||||
':codeGenExperimental6502',
|
||||
':compiler',
|
||||
':simulator',
|
||||
':dbusCompilerService',
|
||||
':httpCompilerService'
|
||||
)
|
||||
|
73
simulator/build.gradle
Normal file
73
simulator/build.gradle
Normal 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
18
simulator/simulator.iml
Normal 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>
|
45
simulator/src/prog8/SimulatorMain.kt
Normal file
45
simulator/src/prog8/SimulatorMain.kt
Normal 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]}")
|
||||
}
|
27
simulator/src/prog8/sim/BuiltinFunctions.kt
Normal file
27
simulator/src/prog8/sim/BuiltinFunctions.kt
Normal 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")
|
||||
}
|
||||
}
|
104
simulator/src/prog8/sim/Evaluator.kt
Normal file
104
simulator/src/prog8/sim/Evaluator.kt
Normal 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)
|
||||
}
|
||||
}
|
23
simulator/src/prog8/sim/InstructionPointer.kt
Normal file
23
simulator/src/prog8/sim/InstructionPointer.kt
Normal 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
|
||||
}
|
||||
}
|
68
simulator/src/prog8/sim/Memory.kt
Normal file
68
simulator/src/prog8/sim/Memory.kt
Normal 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++
|
||||
}
|
||||
}
|
||||
}
|
124
simulator/src/prog8/sim/Simulator.kt
Normal file
124
simulator/src/prog8/sim/Simulator.kt
Normal 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
|
||||
}
|
||||
}
|
25
simulator/src/prog8/sim/Variables.kt
Normal file
25
simulator/src/prog8/sim/Variables.kt
Normal 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}")
|
||||
}
|
||||
}
|
67
simulator/test/TestMemory.kt
Normal file
67
simulator/test/TestMemory.kt
Normal 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
46
simulator/test/TestSim.kt
Normal 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
|
||||
}
|
||||
})
|
Loading…
x
Reference in New Issue
Block a user