mirror of
https://github.com/irmen/prog8.git
synced 2025-03-27 21:33:58 +00:00
added start of virtual machine compilation target
This commit is contained in:
parent
a99e77093f
commit
7d2bf892b1
.idea
codeAst
codeCore
codeGenCpu6502/src/prog8/codegen/cpu6502
codeGenExperimental/src/prog8/codegen/experimental
codeGenVirtual
compiler
examples
settings.gradlevirtualmachine
2
.idea/modules.xml
generated
2
.idea/modules.xml
generated
@ -6,6 +6,7 @@
|
||||
<module fileurl="file://$PROJECT_DIR$/codeCore/codeCore.iml" filepath="$PROJECT_DIR$/codeCore/codeCore.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/codeGenCpu6502/codeGenCpu6502.iml" filepath="$PROJECT_DIR$/codeGenCpu6502/codeGenCpu6502.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/codeGenExperimental/codeGenExperimental.iml" filepath="$PROJECT_DIR$/codeGenExperimental/codeGenExperimental.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/codeGenVirtual/codeGenVirtual.iml" filepath="$PROJECT_DIR$/codeGenVirtual/codeGenVirtual.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/codeOptimizers/codeOptimizers.iml" filepath="$PROJECT_DIR$/codeOptimizers/codeOptimizers.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/compiler/compiler.iml" filepath="$PROJECT_DIR$/compiler/compiler.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/compilerAst/compilerAst.iml" filepath="$PROJECT_DIR$/compilerAst/compilerAst.iml" />
|
||||
@ -14,6 +15,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$/virtualmachine/virtualmachine.iml" filepath="$PROJECT_DIR$/virtualmachine/virtualmachine.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
@ -4,6 +4,7 @@
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
|
@ -24,6 +24,7 @@ compileTestKotlin {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation project(':virtualmachine')
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
|
||||
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.14"
|
||||
}
|
||||
|
@ -4,10 +4,12 @@
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<orderEntry type="library" name="KotlinJavaRuntime" level="project" />
|
||||
<orderEntry type="library" name="michael.bull.kotlin.result.jvm" level="project" />
|
||||
<orderEntry type="module" module-name="virtualmachine" />
|
||||
</component>
|
||||
</module>
|
@ -10,7 +10,8 @@ interface IMachineFloat {
|
||||
|
||||
enum class CpuType {
|
||||
CPU6502,
|
||||
CPU65c02
|
||||
CPU65c02,
|
||||
VIRTUAL
|
||||
}
|
||||
|
||||
interface IMachineDefinition {
|
||||
|
@ -18,7 +18,7 @@ class AtariTarget: ICompilationTarget, IStringEncoding by Encoder, IMemSizer {
|
||||
return when(dt) {
|
||||
in ByteDatatypes -> 1
|
||||
in WordDatatypes, in PassByReferenceDatatypes -> 2
|
||||
DataType.FLOAT -> 6
|
||||
DataType.FLOAT -> machine.FLOAT_MEM_SIZE
|
||||
else -> Int.MIN_VALUE
|
||||
}
|
||||
}
|
||||
|
24
codeCore/src/prog8/code/target/VMTarget.kt
Normal file
24
codeCore/src/prog8/code/target/VMTarget.kt
Normal file
@ -0,0 +1,24 @@
|
||||
package prog8.code.target
|
||||
|
||||
import prog8.code.core.*
|
||||
import prog8.code.target.virtual.VirtualMachineDefinition
|
||||
|
||||
class VMTarget: ICompilationTarget, IStringEncoding by Encoder, IMemSizer {
|
||||
override val name = NAME
|
||||
override val machine = VirtualMachineDefinition()
|
||||
override val supportedEncodings = setOf(Encoding.ISO)
|
||||
override val defaultEncoding = Encoding.ISO
|
||||
|
||||
companion object {
|
||||
const val NAME = "virtual"
|
||||
}
|
||||
|
||||
override fun memorySize(dt: DataType): Int {
|
||||
return when(dt) {
|
||||
in ByteDatatypes -> 1
|
||||
in WordDatatypes, in PassByReferenceDatatypes -> 2
|
||||
DataType.FLOAT -> machine.FLOAT_MEM_SIZE
|
||||
else -> Int.MIN_VALUE
|
||||
}
|
||||
}
|
||||
}
|
@ -23,7 +23,7 @@ class AtariMachineDefinition: IMachineDefinition {
|
||||
override fun getFloat(num: Number) = TODO("float from number")
|
||||
|
||||
override fun importLibs(compilerOptions: CompilationOptions, compilationTargetName: String): List<String> {
|
||||
return if (compilerOptions.launcher == CbmPrgLauncherType.BASIC || compilerOptions.output == OutputType.PRG)
|
||||
return if (compilerOptions.output == OutputType.XEX)
|
||||
listOf("syslib")
|
||||
else
|
||||
emptyList()
|
||||
|
@ -0,0 +1,51 @@
|
||||
package prog8.code.target.virtual
|
||||
|
||||
import prog8.code.core.CompilationOptions
|
||||
import prog8.code.core.CpuType
|
||||
import prog8.code.core.IMachineDefinition
|
||||
import prog8.code.core.Zeropage
|
||||
import prog8.vm.Assembler
|
||||
import prog8.vm.Memory
|
||||
import prog8.vm.VirtualMachine
|
||||
import java.io.File
|
||||
import java.nio.file.Path
|
||||
|
||||
|
||||
class VirtualMachineDefinition: IMachineDefinition {
|
||||
|
||||
override val cpu = CpuType.VIRTUAL
|
||||
|
||||
override val FLOAT_MAX_POSITIVE = Float.MAX_VALUE.toDouble()
|
||||
override val FLOAT_MAX_NEGATIVE = -Float.MAX_VALUE.toDouble()
|
||||
override val FLOAT_MEM_SIZE = 4
|
||||
override val PROGRAM_LOAD_ADDRESS = 0u // not actually used
|
||||
|
||||
override val ESTACK_LO = 0u // not actually used
|
||||
override val ESTACK_HI = 0u // not actually used
|
||||
|
||||
override lateinit var zeropage: Zeropage // not actually used
|
||||
|
||||
override fun getFloat(num: Number) = TODO("float from number")
|
||||
|
||||
override fun importLibs(compilerOptions: CompilationOptions, compilationTargetName: String): List<String> {
|
||||
return listOf("syslib")
|
||||
}
|
||||
|
||||
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path) {
|
||||
println("\nStarting Virtual Machine...")
|
||||
val source = File("$programNameWithPath.p8virt").readText()
|
||||
val (memsrc, programsrc) = source.split("------PROGRAM------".toRegex(), 2)
|
||||
val memory = Memory()
|
||||
val assembler = Assembler()
|
||||
assembler.initializeMemory(memsrc, memory)
|
||||
val program = assembler.assembleProgram(programsrc)
|
||||
val vm = VirtualMachine(memory, program)
|
||||
vm.run()
|
||||
}
|
||||
|
||||
override fun isIOAddress(address: UInt): Boolean = false
|
||||
|
||||
override fun initializeZeropage(compilerOptions: CompilationOptions) {}
|
||||
|
||||
override val opcodeNames = emptySet<String>()
|
||||
}
|
@ -22,9 +22,10 @@ internal const val subroutineFloatEvalResultVar2 = "prog8_float_eval_result2"
|
||||
|
||||
|
||||
class AsmGen(internal val program: Program,
|
||||
internal val errors: IErrorReporter,
|
||||
internal val symbolTable: SymbolTable,
|
||||
internal val options: CompilationOptions): IAssemblyGenerator {
|
||||
internal val options: CompilationOptions,
|
||||
internal val errors: IErrorReporter
|
||||
): IAssemblyGenerator {
|
||||
|
||||
internal val optimizedByteMultiplications = setOf(3,5,6,7,9,10,11,12,13,14,15,20,25,40,50,80,100)
|
||||
internal val optimizedWordMultiplications = setOf(3,5,6,7,9,10,12,15,20,25,40,50,80,100,320,640)
|
||||
|
@ -19,9 +19,9 @@ import prog8.code.core.IErrorReporter
|
||||
|
||||
|
||||
class AsmGen(internal val program: PtProgram,
|
||||
internal val errors: IErrorReporter,
|
||||
internal val symbolTable: SymbolTable,
|
||||
internal val options: CompilationOptions
|
||||
internal val options: CompilationOptions,
|
||||
internal val errors: IErrorReporter
|
||||
): IAssemblyGenerator {
|
||||
|
||||
override fun compileToAssembly(): IAssemblyProgram? {
|
||||
|
47
codeGenVirtual/build.gradle
Normal file
47
codeGenVirtual/build.gradle
Normal file
@ -0,0 +1,47 @@
|
||||
|
||||
plugins {
|
||||
id 'java'
|
||||
id 'application'
|
||||
id "org.jetbrains.kotlin.jvm"
|
||||
}
|
||||
|
||||
java {
|
||||
toolchain {
|
||||
languageVersion = JavaLanguageVersion.of(javaVersion)
|
||||
}
|
||||
}
|
||||
|
||||
compileKotlin {
|
||||
kotlinOptions {
|
||||
jvmTarget = javaVersion
|
||||
}
|
||||
}
|
||||
|
||||
compileTestKotlin {
|
||||
kotlinOptions {
|
||||
jvmTarget = javaVersion
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation project(':virtualmachine')
|
||||
implementation project(':codeAst')
|
||||
implementation project(':codeCore')
|
||||
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"
|
||||
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
main {
|
||||
java {
|
||||
srcDirs = ["${project.projectDir}/src"]
|
||||
}
|
||||
resources {
|
||||
srcDirs = ["${project.projectDir}/res"]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// note: there are no unit tests in this module!
|
16
codeGenVirtual/codeGenVirtual.iml
Normal file
16
codeGenVirtual/codeGenVirtual.iml
Normal file
@ -0,0 +1,16 @@
|
||||
<?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" />
|
||||
<orderEntry type="library" name="michael.bull.kotlin.result.jvm" level="project" />
|
||||
<orderEntry type="module" module-name="codeAst" />
|
||||
<orderEntry type="module" module-name="codeCore" />
|
||||
<orderEntry type="module" module-name="virtualmachine" />
|
||||
</component>
|
||||
</module>
|
55
codeGenVirtual/src/prog8/codegen/virtual/AssemblyProgram.kt
Normal file
55
codeGenVirtual/src/prog8/codegen/virtual/AssemblyProgram.kt
Normal file
@ -0,0 +1,55 @@
|
||||
package prog8.codegen.virtual
|
||||
|
||||
import prog8.code.core.CompilationOptions
|
||||
import prog8.code.core.IAssemblyProgram
|
||||
import kotlin.io.path.bufferedWriter
|
||||
import kotlin.io.path.div
|
||||
|
||||
|
||||
internal class AssemblyProgram(override val name: String) : IAssemblyProgram
|
||||
{
|
||||
override fun assemble(options: CompilationOptions): Boolean {
|
||||
val outfile = options.outputDir / ("$name.p8virt")
|
||||
println("write code to ${outfile}")
|
||||
outfile.bufferedWriter().use {
|
||||
it.write(memsrc)
|
||||
it.write("------PROGRAM------\n")
|
||||
it.write(src)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
val memsrc = """
|
||||
$4000 strz "Hello from program! "derp" bye.\n"
|
||||
$2000 ubyte 65,66,67,68,0
|
||||
$2100 uword $1111,$2222,$3333,$4444
|
||||
"""
|
||||
val src = """
|
||||
; enable lores gfx screen
|
||||
load r0, 0
|
||||
syscall 8
|
||||
load.w r10, 320
|
||||
load.w r11, 240
|
||||
load.b r12, 0
|
||||
|
||||
_forever:
|
||||
load.w r1, 0
|
||||
_yloop:
|
||||
load.w r0, 0
|
||||
_xloop:
|
||||
mul.b r2,r0,r1
|
||||
add.b r2,r2,r12
|
||||
syscall 10
|
||||
addi.w r0,r0,1
|
||||
blt.w r0, r10, _xloop
|
||||
addi.w r1,r1,1
|
||||
blt.w r1, r11,_yloop
|
||||
addi.b r12,r12,1
|
||||
jump _forever
|
||||
|
||||
load.w r0, 2000
|
||||
syscall 7
|
||||
load.w r0,0
|
||||
return"""
|
||||
|
||||
}
|
20
codeGenVirtual/src/prog8/codegen/virtual/CodeGen.kt
Normal file
20
codeGenVirtual/src/prog8/codegen/virtual/CodeGen.kt
Normal file
@ -0,0 +1,20 @@
|
||||
package prog8.codegen.virtual
|
||||
|
||||
import prog8.code.SymbolTable
|
||||
import prog8.code.ast.PtProgram
|
||||
import prog8.code.core.CompilationOptions
|
||||
import prog8.code.core.IAssemblyGenerator
|
||||
import prog8.code.core.IAssemblyProgram
|
||||
import prog8.code.core.IErrorReporter
|
||||
|
||||
class CodeGen(internal val program: PtProgram,
|
||||
internal val symbolTable: SymbolTable,
|
||||
internal val options: CompilationOptions,
|
||||
internal val errors: IErrorReporter
|
||||
): IAssemblyGenerator {
|
||||
|
||||
override fun compileToAssembly(): IAssemblyProgram? {
|
||||
|
||||
return AssemblyProgram(program.name)
|
||||
}
|
||||
}
|
@ -32,6 +32,7 @@ dependencies {
|
||||
implementation project(':codeOptimizers')
|
||||
implementation project(':compilerAst')
|
||||
implementation project(':codeGenCpu6502')
|
||||
implementation project(':codeGenVirtual')
|
||||
implementation project(':codeGenExperimental')
|
||||
implementation 'org.antlr:antlr4-runtime:4.9.3'
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
|
||||
|
@ -22,5 +22,6 @@
|
||||
<orderEntry type="module" module-name="codeOptimizers" />
|
||||
<orderEntry type="module" module-name="codeGenCpu6502" />
|
||||
<orderEntry type="module" module-name="codeGenExperimental" />
|
||||
<orderEntry type="module" module-name="codeGenVirtual" />
|
||||
</component>
|
||||
</module>
|
151
compiler/res/prog8lib/virtual/syslib.p8
Normal file
151
compiler/res/prog8lib/virtual/syslib.p8
Normal file
@ -0,0 +1,151 @@
|
||||
; Prog8 definitions for the Virtual Machine
|
||||
;
|
||||
; Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
|
||||
;
|
||||
|
||||
sys {
|
||||
; ------- lowlevel system routines --------
|
||||
|
||||
const ubyte target = 255 ; compilation target specifier. 64 = C64, 128 = C128, 16 = CommanderX16, 8 = atari800XL, 255 = virtual
|
||||
|
||||
|
||||
sub reset_system() {
|
||||
; Soft-reset the system back to initial power-on Basic prompt.
|
||||
; TODO
|
||||
}
|
||||
|
||||
sub wait(uword jiffies) {
|
||||
; --- wait approximately the given number of jiffies (1/60th seconds)
|
||||
; TODO
|
||||
}
|
||||
|
||||
sub waitvsync() {
|
||||
; --- busy wait till the next vsync has occurred (approximately), without depending on custom irq handling.
|
||||
; TODO
|
||||
}
|
||||
|
||||
sub memcopy(uword source, uword target, uword count) {
|
||||
; TODO
|
||||
}
|
||||
|
||||
sub memset(uword mem, uword numbytes, ubyte value) {
|
||||
; TODO
|
||||
}
|
||||
|
||||
sub memsetw(uword mem, uword numwords, uword value) {
|
||||
; TODO
|
||||
}
|
||||
|
||||
sub exit(ubyte returnvalue) {
|
||||
; -- immediately exit the program with a return code in the A register
|
||||
; TODO
|
||||
}
|
||||
}
|
||||
|
||||
cx16 {
|
||||
|
||||
; the sixteen virtual 16-bit registers that the CX16 has defined in the zeropage
|
||||
; they are simulated on the VirtualMachine as well but their location in memory is different
|
||||
; the sixteen virtual 16-bit registers in both normal unsigned mode and signed mode (s)
|
||||
&uword r0 = $0002
|
||||
&uword r1 = $0004
|
||||
&uword r2 = $0006
|
||||
&uword r3 = $0008
|
||||
&uword r4 = $000a
|
||||
&uword r5 = $000c
|
||||
&uword r6 = $000e
|
||||
&uword r7 = $0010
|
||||
&uword r8 = $0012
|
||||
&uword r9 = $0014
|
||||
&uword r10 = $0016
|
||||
&uword r11 = $0018
|
||||
&uword r12 = $001a
|
||||
&uword r13 = $001c
|
||||
&uword r14 = $001e
|
||||
&uword r15 = $0020
|
||||
|
||||
&word r0s = $0002
|
||||
&word r1s = $0004
|
||||
&word r2s = $0006
|
||||
&word r3s = $0008
|
||||
&word r4s = $000a
|
||||
&word r5s = $000c
|
||||
&word r6s = $000e
|
||||
&word r7s = $0010
|
||||
&word r8s = $0012
|
||||
&word r9s = $0014
|
||||
&word r10s = $0016
|
||||
&word r11s = $0018
|
||||
&word r12s = $001a
|
||||
&word r13s = $001c
|
||||
&word r14s = $001e
|
||||
&word r15s = $0020
|
||||
|
||||
&ubyte r0L = $0002
|
||||
&ubyte r1L = $0004
|
||||
&ubyte r2L = $0006
|
||||
&ubyte r3L = $0008
|
||||
&ubyte r4L = $000a
|
||||
&ubyte r5L = $000c
|
||||
&ubyte r6L = $000e
|
||||
&ubyte r7L = $0010
|
||||
&ubyte r8L = $0012
|
||||
&ubyte r9L = $0014
|
||||
&ubyte r10L = $0016
|
||||
&ubyte r11L = $0018
|
||||
&ubyte r12L = $001a
|
||||
&ubyte r13L = $001c
|
||||
&ubyte r14L = $001e
|
||||
&ubyte r15L = $0020
|
||||
|
||||
&ubyte r0H = $0003
|
||||
&ubyte r1H = $0005
|
||||
&ubyte r2H = $0007
|
||||
&ubyte r3H = $0009
|
||||
&ubyte r4H = $000b
|
||||
&ubyte r5H = $000d
|
||||
&ubyte r6H = $000f
|
||||
&ubyte r7H = $0011
|
||||
&ubyte r8H = $0013
|
||||
&ubyte r9H = $0015
|
||||
&ubyte r10H = $0017
|
||||
&ubyte r11H = $0019
|
||||
&ubyte r12H = $001b
|
||||
&ubyte r13H = $001d
|
||||
&ubyte r14H = $001f
|
||||
&ubyte r15H = $0021
|
||||
|
||||
&byte r0sL = $0002
|
||||
&byte r1sL = $0004
|
||||
&byte r2sL = $0006
|
||||
&byte r3sL = $0008
|
||||
&byte r4sL = $000a
|
||||
&byte r5sL = $000c
|
||||
&byte r6sL = $000e
|
||||
&byte r7sL = $0010
|
||||
&byte r8sL = $0012
|
||||
&byte r9sL = $0014
|
||||
&byte r10sL = $0016
|
||||
&byte r11sL = $0018
|
||||
&byte r12sL = $001a
|
||||
&byte r13sL = $001c
|
||||
&byte r14sL = $001e
|
||||
&byte r15sL = $0020
|
||||
|
||||
&byte r0sH = $0003
|
||||
&byte r1sH = $0005
|
||||
&byte r2sH = $0007
|
||||
&byte r3sH = $0009
|
||||
&byte r4sH = $000b
|
||||
&byte r5sH = $000d
|
||||
&byte r6sH = $000f
|
||||
&byte r7sH = $0011
|
||||
&byte r8sH = $0013
|
||||
&byte r9sH = $0015
|
||||
&byte r10sH = $0017
|
||||
&byte r11sH = $0019
|
||||
&byte r12sH = $001b
|
||||
&byte r13sH = $001d
|
||||
&byte r14sH = $001f
|
||||
&byte r15sH = $0021
|
||||
}
|
107
compiler/res/prog8lib/virtual/textio.p8
Normal file
107
compiler/res/prog8lib/virtual/textio.p8
Normal file
@ -0,0 +1,107 @@
|
||||
; Prog8 definitions for the Text I/O and Screen routines for the Virtual Machine
|
||||
;
|
||||
; Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
|
||||
|
||||
%import syslib
|
||||
|
||||
|
||||
txt {
|
||||
|
||||
const ubyte DEFAULT_WIDTH = 40
|
||||
const ubyte DEFAULT_HEIGHT = 24
|
||||
|
||||
|
||||
sub clear_screen() {
|
||||
txt.chrout(125)
|
||||
}
|
||||
|
||||
sub nl() {
|
||||
txt.chrout('\n')
|
||||
}
|
||||
|
||||
sub spc() {
|
||||
txt.chrout(' ')
|
||||
}
|
||||
|
||||
sub fill_screen (ubyte char) {
|
||||
; ---- fill the character screen with the given fill character.
|
||||
; TODO
|
||||
}
|
||||
|
||||
sub clear_screenchars (ubyte char) {
|
||||
; ---- clear the character screen with the given fill character (leaves colors)
|
||||
; (assumes screen matrix is at the default address)
|
||||
; TODO
|
||||
}
|
||||
|
||||
sub chrout(ubyte char) {
|
||||
; TODO
|
||||
}
|
||||
|
||||
sub print (str text) {
|
||||
; ---- print null terminated string from A/Y
|
||||
; note: the compiler contains an optimization that will replace
|
||||
; a call to this subroutine with a string argument of just one char,
|
||||
; by just one call to CHROUT of that single char.
|
||||
; TODO
|
||||
}
|
||||
|
||||
sub print_ub0 (ubyte value) {
|
||||
; ---- print the ubyte in A in decimal form, with left padding 0s (3 positions total)
|
||||
; TODO
|
||||
}
|
||||
|
||||
sub print_ub (ubyte value) {
|
||||
; ---- print the ubyte in A in decimal form, without left padding 0s
|
||||
; TODO
|
||||
}
|
||||
|
||||
sub print_b (byte value) {
|
||||
; ---- print the byte in A in decimal form, without left padding 0s
|
||||
; TODO
|
||||
}
|
||||
|
||||
sub print_ubhex (ubyte value, ubyte prefix) {
|
||||
; ---- print the ubyte in A in hex form (if Carry is set, a radix prefix '$' is printed as well)
|
||||
; TODO
|
||||
}
|
||||
|
||||
sub print_ubbin (ubyte value, ubyte prefix) {
|
||||
; ---- print the ubyte in A in binary form (if Carry is set, a radix prefix '%' is printed as well)
|
||||
; TODO
|
||||
}
|
||||
|
||||
sub print_uwbin (uword value, ubyte prefix) {
|
||||
; ---- print the uword in A/Y in binary form (if Carry is set, a radix prefix '%' is printed as well)
|
||||
; TODO
|
||||
}
|
||||
|
||||
sub print_uwhex (uword value, ubyte prefix) {
|
||||
; ---- print the uword in A/Y in hexadecimal form (4 digits)
|
||||
; (if Carry is set, a radix prefix '$' is printed as well)
|
||||
; TODO
|
||||
}
|
||||
|
||||
sub print_uw0 (uword value) {
|
||||
; ---- print the uword in A/Y in decimal form, with left padding 0s (5 positions total)
|
||||
; TODO
|
||||
}
|
||||
|
||||
sub print_uw (uword value) {
|
||||
; ---- print the uword in A/Y in decimal form, without left padding 0s
|
||||
; TODO
|
||||
}
|
||||
|
||||
sub print_w (word value) {
|
||||
; ---- print the (signed) word in A/Y in decimal form, without left padding 0's
|
||||
; TODO
|
||||
}
|
||||
|
||||
sub input_chars (uword buffer) -> ubyte {
|
||||
; ---- Input a string (max. 80 chars) from the keyboard. Returns length in Y. (string is terminated with a 0 byte as well)
|
||||
; It assumes the keyboard is selected as I/O channel!
|
||||
; TODO
|
||||
return 0
|
||||
}
|
||||
|
||||
}
|
@ -3,10 +3,7 @@ package prog8
|
||||
import kotlinx.cli.*
|
||||
import prog8.ast.base.AstException
|
||||
import prog8.code.core.CbmPrgLauncherType
|
||||
import prog8.code.target.AtariTarget
|
||||
import prog8.code.target.C128Target
|
||||
import prog8.code.target.C64Target
|
||||
import prog8.code.target.Cx16Target
|
||||
import prog8.code.target.*
|
||||
import prog8.compiler.CompilationResult
|
||||
import prog8.compiler.CompilerArguments
|
||||
import prog8.compiler.compileProgram
|
||||
@ -46,8 +43,8 @@ private fun compileMain(args: Array<String>): Boolean {
|
||||
val quietAssembler by cli.option(ArgType.Boolean, fullName = "quietasm", description = "don't print assembler output results")
|
||||
val asmListfile by cli.option(ArgType.Boolean, fullName = "asmlist", description = "make the assembler produce a listing file as well")
|
||||
val experimentalCodegen by cli.option(ArgType.Boolean, fullName = "expericodegen", description = "use experimental codegen")
|
||||
val compilationTarget by cli.option(ArgType.String, fullName = "target", description = "target output of the compiler (one of '${C64Target.NAME}', '${C128Target.NAME}', '${Cx16Target.NAME}', '${AtariTarget.NAME}')").default(
|
||||
C64Target.NAME)
|
||||
val compilationTarget by cli.option(ArgType.String, fullName = "target", description = "target output of the compiler (one of '${C64Target.NAME}', '${C128Target.NAME}', '${Cx16Target.NAME}', '${AtariTarget.NAME}', '${VMTarget.NAME}')")
|
||||
.default(C64Target.NAME)
|
||||
val sourceDirs by cli.option(ArgType.String, fullName="srcdirs", description = "list of extra paths, separated with ${File.pathSeparator}, to search in for imported modules").multiple().delimiter(File.pathSeparator)
|
||||
val moduleFiles by cli.argument(ArgType.String, fullName = "modules", description = "main module file(s) to compile").multiple(999)
|
||||
|
||||
@ -74,7 +71,7 @@ private fun compileMain(args: Array<String>): Boolean {
|
||||
if(srcdirs.firstOrNull()!=".")
|
||||
srcdirs.add(0, ".")
|
||||
|
||||
if (compilationTarget !in setOf(C64Target.NAME, C128Target.NAME, Cx16Target.NAME, AtariTarget.NAME)) {
|
||||
if (compilationTarget !in setOf(C64Target.NAME, C128Target.NAME, Cx16Target.NAME, AtariTarget.NAME, VMTarget.NAME)) {
|
||||
System.err.println("Invalid compilation target: $compilationTarget")
|
||||
return false
|
||||
}
|
||||
|
@ -13,17 +13,13 @@ import prog8.ast.statements.VarDecl
|
||||
import prog8.ast.walk.IAstVisitor
|
||||
import prog8.code.SymbolTable
|
||||
import prog8.code.core.*
|
||||
import prog8.code.target.AtariTarget
|
||||
import prog8.code.target.C128Target
|
||||
import prog8.code.target.C64Target
|
||||
import prog8.code.target.Cx16Target
|
||||
import prog8.code.target.*
|
||||
import prog8.compiler.astprocessing.*
|
||||
import prog8.optimizer.*
|
||||
import prog8.parser.ParseError
|
||||
import java.nio.file.Path
|
||||
import kotlin.io.path.Path
|
||||
import kotlin.io.path.nameWithoutExtension
|
||||
import kotlin.math.exp
|
||||
import kotlin.math.round
|
||||
import kotlin.system.measureTimeMillis
|
||||
|
||||
@ -59,6 +55,7 @@ fun compileProgram(args: CompilerArguments): CompilationResult? {
|
||||
C128Target.NAME -> C128Target()
|
||||
Cx16Target.NAME -> Cx16Target()
|
||||
AtariTarget.NAME -> AtariTarget()
|
||||
VMTarget.NAME -> VMTarget()
|
||||
else -> throw IllegalArgumentException("invalid compilation target")
|
||||
}
|
||||
|
||||
@ -444,13 +441,18 @@ internal fun asmGeneratorFor(program: Program,
|
||||
if(options.experimentalCodegen) {
|
||||
if (options.compTarget.machine.cpu in arrayOf(CpuType.CPU6502, CpuType.CPU65c02)) {
|
||||
|
||||
// TODO for now, only use the new Intermediary Ast for this experimental codegen:
|
||||
// TODO for now, use the new Intermediary Ast for this experimental codegen:
|
||||
val intermediateAst = IntermediateAstMaker(program).transform()
|
||||
return prog8.codegen.experimental.AsmGen(intermediateAst, errors, symbolTable, options)
|
||||
return prog8.codegen.experimental.AsmGen(intermediateAst, symbolTable, options, errors)
|
||||
}
|
||||
} else {
|
||||
if (options.compTarget.machine.cpu in arrayOf(CpuType.CPU6502, CpuType.CPU65c02))
|
||||
return prog8.codegen.cpu6502.AsmGen(program, errors, symbolTable, options)
|
||||
return prog8.codegen.cpu6502.AsmGen(program, symbolTable, options, errors)
|
||||
if (options.compTarget.name == VMTarget.NAME) {
|
||||
// TODO for now, use the new Intermediary Ast for this codegen:
|
||||
val intermediateAst = IntermediateAstMaker(program).transform()
|
||||
return prog8.codegen.virtual.CodeGen(intermediateAst, symbolTable, options, errors)
|
||||
}
|
||||
}
|
||||
|
||||
throw NotImplementedError("no asm generator for cpu ${options.compTarget.machine.cpu}")
|
||||
|
@ -75,7 +75,7 @@ class TestAsmGenSymbols: StringSpec({
|
||||
val options = CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FULL, emptyList(), false, true, C64Target(), 999u)
|
||||
options.compTarget.machine.zeropage = C64Zeropage(options)
|
||||
val st = SymbolTableMaker().makeFrom(program)
|
||||
return AsmGen(program, errors, st, options)
|
||||
return AsmGen(program, st, options, errors)
|
||||
}
|
||||
|
||||
"symbol and variable names from strings" {
|
||||
|
@ -77,7 +77,7 @@ internal fun generateAssembly(
|
||||
val errors = ErrorReporterForTests()
|
||||
determineProgramLoadAddress(program, coptions, errors)
|
||||
errors.report()
|
||||
val asmgen = AsmGen(program, errors, st, coptions)
|
||||
val asmgen = AsmGen(program, st, coptions, errors)
|
||||
errors.report()
|
||||
return asmgen.compileToAssembly()
|
||||
}
|
||||
|
@ -1,14 +1,6 @@
|
||||
%import textio
|
||||
%import test_stack
|
||||
%zeropage basicsafe
|
||||
%zpreserved 50,80
|
||||
%zpreserved 150,155
|
||||
%option align_word
|
||||
|
||||
|
||||
main {
|
||||
%option align_word
|
||||
|
||||
ubyte[256] sieve
|
||||
ubyte candidate_prime = 2 ; is increased in the loop
|
||||
|
||||
|
@ -4,6 +4,8 @@ include(
|
||||
':codeAst',
|
||||
':compilerAst',
|
||||
':codeOptimizers',
|
||||
':virtualmachine',
|
||||
':codeGenVirtual',
|
||||
':codeGenCpu6502',
|
||||
':codeGenExperimental',
|
||||
':compiler',
|
||||
|
42
virtualmachine/build.gradle
Normal file
42
virtualmachine/build.gradle
Normal file
@ -0,0 +1,42 @@
|
||||
|
||||
plugins {
|
||||
id 'java'
|
||||
id 'application'
|
||||
id "org.jetbrains.kotlin.jvm"
|
||||
}
|
||||
|
||||
java {
|
||||
toolchain {
|
||||
languageVersion = JavaLanguageVersion.of(javaVersion)
|
||||
}
|
||||
}
|
||||
|
||||
compileKotlin {
|
||||
kotlinOptions {
|
||||
jvmTarget = javaVersion
|
||||
}
|
||||
}
|
||||
|
||||
compileTestKotlin {
|
||||
kotlinOptions {
|
||||
jvmTarget = javaVersion
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
|
||||
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.14"
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
main {
|
||||
java {
|
||||
srcDirs = ["${project.projectDir}/src"]
|
||||
}
|
||||
resources {
|
||||
srcDirs = ["${project.projectDir}/res"]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// note: there are no unit tests in this module!
|
214
virtualmachine/src/prog8/vm/Assembler.kt
Normal file
214
virtualmachine/src/prog8/vm/Assembler.kt
Normal file
@ -0,0 +1,214 @@
|
||||
package prog8.vm
|
||||
|
||||
|
||||
class Assembler {
|
||||
private val labels = mutableMapOf<String, Int>()
|
||||
private val placeholders = mutableMapOf<Int, String>()
|
||||
|
||||
init {
|
||||
require(instructionFormats.size== Opcode.values().size)
|
||||
}
|
||||
|
||||
fun initializeMemory(memsrc: String, memory: Memory) {
|
||||
val instrPattern = Regex("""(.+?)\s+([a-z]+)\s+(.+)""", RegexOption.IGNORE_CASE)
|
||||
for(line in memsrc.lines()) {
|
||||
if(line.isBlank())
|
||||
continue
|
||||
val match = instrPattern.matchEntire(line.trim())
|
||||
if(match==null)
|
||||
throw IllegalArgumentException("invalid line $line")
|
||||
else {
|
||||
val (_, addr, what, values) = match.groupValues
|
||||
var address = parseValue(addr, 0)
|
||||
when(what) {
|
||||
"str" -> {
|
||||
val string = unescape(values.trim('"'))
|
||||
memory.setString(address, string, false)
|
||||
}
|
||||
"strz" -> {
|
||||
val string = unescape(values.trim('"'))
|
||||
memory.setString(address, string, true)
|
||||
}
|
||||
"ubyte" -> {
|
||||
val array = values.split(',').map { parseValue(it.trim(), 0) }
|
||||
for (value in array) {
|
||||
memory.setB(address, value.toUByte())
|
||||
address++
|
||||
}
|
||||
}
|
||||
"uword" -> {
|
||||
val array = values.split(',').map { parseValue(it.trim(), 0) }
|
||||
for (value in array) {
|
||||
memory.setW(address, value.toUShort())
|
||||
address++
|
||||
}
|
||||
}
|
||||
else -> throw IllegalArgumentException("mem instr $what")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun assembleProgram(source: CharSequence): List<Instruction> {
|
||||
labels.clear()
|
||||
placeholders.clear()
|
||||
val program = mutableListOf<Instruction>()
|
||||
val instructionPattern = Regex("""([a-z]+)(\.b|\.w)?(.*)""", RegexOption.IGNORE_CASE)
|
||||
val labelPattern = Regex("""_([a-z0-9]+):""")
|
||||
for (line in source.lines()) {
|
||||
if(line.isBlank() || line.startsWith(';'))
|
||||
continue
|
||||
val match = instructionPattern.matchEntire(line.trim())
|
||||
if(match==null) {
|
||||
val labelmatch = labelPattern.matchEntire(line.trim())
|
||||
if(labelmatch==null)
|
||||
throw IllegalArgumentException("invalid line $line at line ${program.size+1}")
|
||||
else {
|
||||
val label = labelmatch.groupValues[1]
|
||||
if(label in labels)
|
||||
throw IllegalArgumentException("label redefined $label")
|
||||
labels[label] = program.size
|
||||
}
|
||||
} else {
|
||||
val (_, instr, typestr, rest) = match.groupValues
|
||||
val opcode = Opcode.valueOf(instr.uppercase())
|
||||
var type: DataType? = convertType(typestr)
|
||||
val operands = rest.lowercase().split(",").toMutableList()
|
||||
var reg1: Int? = null
|
||||
var reg2: Int? = null
|
||||
var reg3: Int? = null
|
||||
var value: Int? = null
|
||||
var operand: String?
|
||||
if(operands.isNotEmpty() && operands[0].isNotEmpty()) {
|
||||
operand = operands.removeFirst().trim()
|
||||
if(operand[0]=='r')
|
||||
reg1 = operand.substring(1).toInt()
|
||||
else {
|
||||
value = parseValue(operand, program.size)
|
||||
operands.clear()
|
||||
}
|
||||
if(operands.isNotEmpty()) {
|
||||
operand = operands.removeFirst().trim()
|
||||
if(operand[0]=='r')
|
||||
reg2 = operand.substring(1).toInt()
|
||||
else {
|
||||
value = parseValue(operand, program.size)
|
||||
operands.clear()
|
||||
}
|
||||
if(operands.isNotEmpty()) {
|
||||
operand = operands.removeFirst().trim()
|
||||
if(operand[0]=='r')
|
||||
reg3 = operand.substring(1).toInt()
|
||||
else {
|
||||
value = parseValue(operand, program.size)
|
||||
operands.clear()
|
||||
}
|
||||
if(operands.isNotEmpty()) {
|
||||
operand = operands.removeFirst().trim()
|
||||
value = parseValue(operand, program.size)
|
||||
operands.clear()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
val format = instructionFormats.getValue(opcode)
|
||||
if(type==null && format.datatypes.isNotEmpty())
|
||||
type= DataType.BYTE
|
||||
if(type!=null && type !in format.datatypes)
|
||||
throw IllegalArgumentException("invalid type code for $line")
|
||||
if(format.reg1 && reg1==null)
|
||||
throw IllegalArgumentException("needs reg1 for $line")
|
||||
if(format.reg2 && reg2==null)
|
||||
throw IllegalArgumentException("needs reg2 for $line")
|
||||
if(format.reg3 && reg3==null)
|
||||
throw IllegalArgumentException("needs reg3 for $line")
|
||||
if(format.value && value==null)
|
||||
throw IllegalArgumentException("needs value for $line")
|
||||
if(!format.reg1 && reg1!=null)
|
||||
throw IllegalArgumentException("invalid reg1 for $line")
|
||||
if(!format.reg2 && reg2!=null)
|
||||
throw IllegalArgumentException("invalid reg2 for $line")
|
||||
if(!format.reg3 && reg3!=null)
|
||||
throw IllegalArgumentException("invalid reg3 for $line")
|
||||
if(!format.value && value!=null)
|
||||
throw IllegalArgumentException("invalid value for $line")
|
||||
program.add(Instruction(opcode, type, reg1, reg2, reg3, value))
|
||||
}
|
||||
}
|
||||
|
||||
pass2replaceLabels(program)
|
||||
return program
|
||||
}
|
||||
|
||||
private fun pass2replaceLabels(program: MutableList<Instruction>) {
|
||||
for((line, label) in placeholders) {
|
||||
val replacement = labels.getValue(label)
|
||||
program[line] = program[line].copy(value = replacement)
|
||||
}
|
||||
}
|
||||
|
||||
private fun parseValue(value: String, pc: Int): Int {
|
||||
if(value.startsWith('$'))
|
||||
return value.substring(1).toInt(16)
|
||||
if(value.startsWith('%'))
|
||||
return value.substring(1).toInt(2)
|
||||
if(value.startsWith("0x"))
|
||||
return value.substring(2).toInt(16)
|
||||
if(value.startsWith('_')) {
|
||||
placeholders[pc] = value.substring(1)
|
||||
return 0
|
||||
}
|
||||
return value.toInt()
|
||||
}
|
||||
|
||||
private fun convertType(typestr: String): DataType? {
|
||||
return when(typestr.lowercase()) {
|
||||
"" -> null
|
||||
".b" -> DataType.BYTE
|
||||
".w" -> DataType.WORD
|
||||
else -> throw IllegalArgumentException("invalid type $typestr")
|
||||
}
|
||||
}
|
||||
|
||||
private fun unescape(str: String): String {
|
||||
val result = mutableListOf<Char>()
|
||||
val iter = str.iterator()
|
||||
while(iter.hasNext()) {
|
||||
val c = iter.nextChar()
|
||||
if(c=='\\') {
|
||||
val ec = iter.nextChar()
|
||||
result.add(when(ec) {
|
||||
'\\' -> '\\'
|
||||
'n' -> '\n'
|
||||
'r' -> '\r'
|
||||
'"' -> '"'
|
||||
'\'' -> '\''
|
||||
'u' -> {
|
||||
try {
|
||||
"${iter.nextChar()}${iter.nextChar()}${iter.nextChar()}${iter.nextChar()}".toInt(16).toChar()
|
||||
} catch (sb: StringIndexOutOfBoundsException) {
|
||||
throw IllegalArgumentException("invalid \\u escape sequence")
|
||||
} catch (nf: NumberFormatException) {
|
||||
throw IllegalArgumentException("invalid \\u escape sequence")
|
||||
}
|
||||
}
|
||||
'x' -> {
|
||||
// special hack 0x8000..0x80ff will be outputted verbatim without encoding
|
||||
try {
|
||||
val hex = ("" + iter.nextChar() + iter.nextChar()).toInt(16)
|
||||
(0x8000 + hex).toChar()
|
||||
} catch (sb: StringIndexOutOfBoundsException) {
|
||||
throw IllegalArgumentException("invalid \\x escape sequence")
|
||||
} catch (nf: NumberFormatException) {
|
||||
throw IllegalArgumentException("invalid \\x escape sequence")
|
||||
}
|
||||
}
|
||||
else -> throw IllegalArgumentException("invalid escape char in string: \\$ec")
|
||||
})
|
||||
} else {
|
||||
result.add(c)
|
||||
}
|
||||
}
|
||||
return result.joinToString("")
|
||||
}
|
||||
}
|
86
virtualmachine/src/prog8/vm/GraphicsWindow.kt
Normal file
86
virtualmachine/src/prog8/vm/GraphicsWindow.kt
Normal file
@ -0,0 +1,86 @@
|
||||
package prog8.vm
|
||||
|
||||
import java.awt.*
|
||||
import java.awt.image.BufferedImage
|
||||
import javax.swing.JFrame
|
||||
import javax.swing.JPanel
|
||||
import javax.swing.Timer
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
|
||||
|
||||
class GraphicsWindow(val pixelWidth: Int, val pixelHeight: Int, val pixelScaling: Int): JFrame("vm-graphics $pixelWidth × $pixelHeight"), AutoCloseable {
|
||||
private lateinit var repaintTimer: Timer
|
||||
|
||||
fun start() {
|
||||
val refreshRate = optimalRefreshRate(this)
|
||||
repaintTimer = Timer(1000/refreshRate) {
|
||||
repaint()
|
||||
}
|
||||
repaintTimer.initialDelay = 0
|
||||
repaintTimer.start()
|
||||
}
|
||||
|
||||
override fun close() {
|
||||
repaintTimer.stop()
|
||||
dispose()
|
||||
}
|
||||
|
||||
val image: BufferedImage
|
||||
|
||||
init {
|
||||
contentPane.layout = BorderLayout()
|
||||
background = Color.BLACK
|
||||
contentPane.background = Color.BLACK
|
||||
isResizable = false
|
||||
isLocationByPlatform = true
|
||||
defaultCloseOperation = JFrame.EXIT_ON_CLOSE
|
||||
image = graphicsConfiguration.createCompatibleImage(pixelWidth, pixelHeight, Transparency.OPAQUE)
|
||||
contentPane.add(BitmapScreenPanel(image, pixelScaling), BorderLayout.CENTER)
|
||||
pack()
|
||||
requestFocusInWindow()
|
||||
isVisible = true
|
||||
}
|
||||
|
||||
private fun optimalRefreshRate(frame: JFrame): Int {
|
||||
var rate = frame.graphicsConfiguration.device.displayMode.refreshRate
|
||||
if(rate==0)
|
||||
rate = GraphicsEnvironment.getLocalGraphicsEnvironment().screenDevices.map { it.displayMode.refreshRate }.first { it>0 }
|
||||
return max(30, min(250, rate))
|
||||
}
|
||||
|
||||
fun clear(color: Int) {
|
||||
val g2d = image.graphics as Graphics2D
|
||||
g2d.background = Color(color, color, color)
|
||||
g2d.clearRect(0,0, pixelWidth*pixelScaling, pixelHeight*pixelScaling)
|
||||
g2d.dispose()
|
||||
}
|
||||
|
||||
fun plot(x: Int, y: Int, color: Int) {
|
||||
if(x<0 || x>=pixelWidth)
|
||||
throw IllegalArgumentException("plot x outside of screen: $x")
|
||||
if(y<0 || y>=pixelHeight)
|
||||
throw IllegalArgumentException("plot y outside of screen: $y")
|
||||
image.setRGB(x, y, Color(color, color, color, 255).rgb)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
internal class BitmapScreenPanel(private val drawImage: BufferedImage, val pixelScaling: Int) : JPanel() {
|
||||
init {
|
||||
val size = Dimension(drawImage.width * pixelScaling, drawImage.height * pixelScaling)
|
||||
minimumSize = size
|
||||
maximumSize = size
|
||||
preferredSize = size
|
||||
isFocusable = true
|
||||
isDoubleBuffered = false
|
||||
requestFocusInWindow()
|
||||
}
|
||||
|
||||
override fun paint(graphics: Graphics) {
|
||||
val g2d = graphics as Graphics2D
|
||||
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR)
|
||||
g2d.drawImage(drawImage, 0, 0, size.width, size.height, null)
|
||||
Toolkit.getDefaultToolkit().sync()
|
||||
}
|
||||
}
|
250
virtualmachine/src/prog8/vm/Instructions.kt
Normal file
250
virtualmachine/src/prog8/vm/Instructions.kt
Normal file
@ -0,0 +1,250 @@
|
||||
package prog8.vm
|
||||
|
||||
/*
|
||||
|
||||
65536 virtual registers, maximum 16 bits wide.
|
||||
65536 bytes of memory.
|
||||
So a memory pointer is also limited to 16 bits.
|
||||
|
||||
|
||||
TODO: also make a 3-address-code instructionset?
|
||||
|
||||
|
||||
Instruction serialization format possibility:
|
||||
|
||||
OPCODE: 1 byte
|
||||
TYPECODE: 1 byte
|
||||
REGISTER 1: 2 bytes
|
||||
REGISTER 2: 2 bytes
|
||||
REG3/MEMORY/VALUE: 2 bytes
|
||||
|
||||
Instructions with Type come in variants b/w/f (omitting it in the instruction means '8' if instruction otherwise has a T)
|
||||
Currently NO support for 24 or 32 bits, and FLOATING POINT is not implemented yet either.
|
||||
|
||||
*only* LOAD AND STORE instructions have a possible memory operand, all other instructions use only registers or immediate value.
|
||||
|
||||
|
||||
LOAD/STORE -- all have type b/w/f. (note: float not yet implemented)
|
||||
|
||||
|
||||
load reg1, value - load immediate value into register
|
||||
loadm reg1, value - load reg1 with value in memory address given by value
|
||||
loadi reg1, reg2 - load reg1 with value in memory indirect, memory pointed to by reg2
|
||||
loadx reg1, reg2, value - load reg1 with value in memory address given by value, indexed by value in reg2
|
||||
loadr reg1, reg2 - load reg1 with value in register reg2
|
||||
swapreg reg1, reg2 - swap values in reg1 and reg2
|
||||
|
||||
storem reg1, value - store reg1 in memory address given by value
|
||||
storei reg1, reg2 - store reg1 in memory indirect, memory pointed to by reg2
|
||||
storex reg1, reg2, value - store reg1 in memory address given by value, indexed by value in reg2
|
||||
storez value - store zero in memory given by value
|
||||
storezi reg1 - store zero in memory pointed to by reg
|
||||
storezx reg1, value - store zero in memory given by value, indexed by value in reg
|
||||
|
||||
|
||||
FLOW CONTROL
|
||||
|
||||
Subroutine call convention:
|
||||
Subroutine parameters set in Reg 0, 1, 2... before gosub.
|
||||
Return value in Reg 0 before return.
|
||||
|
||||
jump location - continue running at instruction number given by location
|
||||
jumpi reg1 - continue running at instruction number given by reg1
|
||||
gosub location - save current instruction location+1, continue execution at location
|
||||
gosubi reg1 - gosub to subroutine at instruction number given by reg1
|
||||
syscall value - do a systemcall identified by call number
|
||||
return - restore last saved instruction location and continue at that instruction
|
||||
|
||||
branch instructions have b/w/f types (f not implemented)
|
||||
bz reg1, value - branch if reg1 is zero
|
||||
bnz reg1, value - branch if reg1 is not zero
|
||||
beq reg1, reg2, value - jump to location in program given by value, if reg1 == reg2
|
||||
bne reg1, reg2, value - jump to location in program given by value, if reg1 != reg2
|
||||
blt reg1, reg2, value - jump to location in program given by value, if reg1 < reg2 (unsigned)
|
||||
blts reg1, reg2, value - jump to location in program given by value, if reg1 < reg2 (signed)
|
||||
bgt reg1, reg2, value - jump to location in program given by value, if reg1 > reg2 (unsigned)
|
||||
bgts reg1, reg2, value - jump to location in program given by value, if reg1 > reg2 (signed)
|
||||
ble reg1, reg2, value - jump to location in program given by value, if reg1 <= reg2 (unsigned)
|
||||
bles reg1, reg2, value - jump to location in program given by value, if reg1 <= reg2 (signed)
|
||||
bge reg1, reg2, value - jump to location in program given by value, if reg1 >= reg2 (unsigned)
|
||||
bges reg1, reg2, value - jump to location in program given by value, if reg1 >= reg2 (signed)
|
||||
|
||||
|
||||
ARITHMETIC - all have a type of b/w/f. (note: float not yet implemented)
|
||||
(note: for calculations, all types -result, and both operands- are identical)
|
||||
|
||||
neg reg1, reg2 - reg1 = sign negation of reg2
|
||||
add reg1, reg2, reg3 - reg1 = reg2+reg3 (unsigned + signed)
|
||||
addi reg1, reg2, value - reg1 = reg2+value (unsigned + signed)
|
||||
sub reg1, reg2, reg3 - reg1 = reg2-reg3 (unsigned + signed)
|
||||
subi reg1, reg2, value - reg1 = reg2-value (unsigned + signed)
|
||||
ext reg1, reg2 - reg1 = unsigned extension of reg2 (which in practice just means clearing the MSB / MSW) (latter not yet implemented as we don't have longs yet)
|
||||
exts reg1, reg2 - reg1 = signed extension of reg2 (byte to word, or word to long) (note: latter ext.w, not yet implemented as we don't have longs yet)
|
||||
mul reg1, reg2, reg3 - unsigned multiply reg1=reg2*reg3 note: byte*byte->byte, no type extension to word!
|
||||
div reg1, reg2, reg3 - unsigned division reg1=reg2/reg3 note: division by zero yields max signed int $ff/$ffff
|
||||
|
||||
|
||||
LOGICAL/BITWISE - all have a type of b/w. but never f
|
||||
inv reg1, reg2 - reg1 = bitwise invert of reg2
|
||||
and reg1, reg2, reg3 - reg1 = reg2 bitwise and reg3
|
||||
or reg1, reg2, reg3 - reg1 = reg2 bitwise or reg3
|
||||
xor reg1, reg2, reg3 - reg1 = reg2 bitwise xor reg3
|
||||
lsr reg1, reg2 - reg1 = shift reg2 right by 1 bit
|
||||
lsl reg1, reg2 - reg1 = shift reg2 left by 1 bit
|
||||
ror reg1, reg2 - reg1 = rotate reg2 right by 1 bit, not using carry
|
||||
rol reg1, reg2 - reg1 = rotate reg2 left by 1 bit, not using carry
|
||||
|
||||
Not sure if the variants using the carry bit should be added, for the ror/rol instructions.
|
||||
These do map directly on 6502 and 68k instructions though.
|
||||
|
||||
|
||||
MISC
|
||||
|
||||
nop - do nothing
|
||||
breakpoint - trigger a breakpoint
|
||||
copy reg1, reg2, length - copy memory from ptrs in reg1 to reg3, length bytes
|
||||
copyz reg1, reg2 - copy memory from ptrs in reg1 to reg3, stop after first 0-byte
|
||||
swap [b, w] reg1, reg2 - reg1 = swapped lsb and msb from register reg2 (16 bits) or lsw and msw (32 bits)
|
||||
|
||||
|
||||
*/
|
||||
|
||||
enum class Opcode {
|
||||
NOP,
|
||||
LOAD,
|
||||
LOADM,
|
||||
LOADI,
|
||||
LOADX,
|
||||
LOADR,
|
||||
SWAPREG,
|
||||
STOREM,
|
||||
STOREI,
|
||||
STOREX,
|
||||
STOREZ,
|
||||
STOREZI,
|
||||
STOREZX,
|
||||
|
||||
JUMP,
|
||||
JUMPI,
|
||||
GOSUB,
|
||||
GOSUBI,
|
||||
SYSCALL,
|
||||
RETURN,
|
||||
BZ,
|
||||
BNZ,
|
||||
BEQ,
|
||||
BNE,
|
||||
BLT,
|
||||
BLTS,
|
||||
BGT,
|
||||
BGTS,
|
||||
BLE,
|
||||
BLES,
|
||||
BGE,
|
||||
BGES,
|
||||
|
||||
NEG,
|
||||
ADD,
|
||||
ADDI,
|
||||
SUB,
|
||||
SUBI,
|
||||
MUL,
|
||||
DIV,
|
||||
EXT,
|
||||
EXTS,
|
||||
|
||||
INV,
|
||||
AND,
|
||||
OR,
|
||||
XOR,
|
||||
LSR,
|
||||
LSL,
|
||||
ROR,
|
||||
ROL,
|
||||
|
||||
COPY,
|
||||
COPYZ,
|
||||
SWAP,
|
||||
BREAKPOINT
|
||||
}
|
||||
|
||||
enum class DataType {
|
||||
BYTE,
|
||||
WORD
|
||||
// TODO add LONG? FLOAT?
|
||||
}
|
||||
|
||||
data class Instruction(
|
||||
val opcode: Opcode,
|
||||
val type: DataType?=null,
|
||||
val reg1: Int?=null, // 0-$ffff
|
||||
val reg2: Int?=null, // 0-$ffff
|
||||
val reg3: Int?=null, // 0-$ffff
|
||||
val value: Int?=null, // 0-$ffff
|
||||
)
|
||||
|
||||
data class InstructionFormat(val datatypes: Set<DataType>, val reg1: Boolean, val reg2: Boolean, val reg3: Boolean, val value: Boolean)
|
||||
|
||||
private val NN = emptySet<DataType>()
|
||||
private val BW = setOf(DataType.BYTE, DataType.WORD)
|
||||
|
||||
@Suppress("BooleanLiteralArgument")
|
||||
val instructionFormats = mutableMapOf(
|
||||
// opcode to types, reg1, reg2, reg3, value
|
||||
Opcode.NOP to InstructionFormat(NN, false, false, false, false),
|
||||
Opcode.LOAD to InstructionFormat(BW, true, false, false, true ),
|
||||
Opcode.LOADM to InstructionFormat(BW, true, false, false, true ),
|
||||
Opcode.LOADI to InstructionFormat(BW, true, true, false, false),
|
||||
Opcode.LOADX to InstructionFormat(BW, true, true, false, true ),
|
||||
Opcode.LOADR to InstructionFormat(BW, true, true, false, false),
|
||||
Opcode.SWAPREG to InstructionFormat(BW, true, true, false, false),
|
||||
Opcode.STOREM to InstructionFormat(BW, true, false, false, true ),
|
||||
Opcode.STOREI to InstructionFormat(BW, true, true, false, false),
|
||||
Opcode.STOREX to InstructionFormat(BW, true, true, false, true ),
|
||||
Opcode.STOREZ to InstructionFormat(BW, false, false, false, true ),
|
||||
Opcode.STOREZI to InstructionFormat(BW, true, false, false, false),
|
||||
Opcode.STOREZX to InstructionFormat(BW, true, false, false, true ),
|
||||
|
||||
Opcode.JUMP to InstructionFormat(NN, false, false, false, true ),
|
||||
Opcode.JUMPI to InstructionFormat(NN, true, false, false, false),
|
||||
Opcode.GOSUB to InstructionFormat(NN, false, false, false, true ),
|
||||
Opcode.GOSUBI to InstructionFormat(NN, true, false, false, false),
|
||||
Opcode.SYSCALL to InstructionFormat(NN, false, false, false, true ),
|
||||
Opcode.RETURN to InstructionFormat(NN, false, false, false, false),
|
||||
Opcode.BZ to InstructionFormat(BW, true, false, false, true ),
|
||||
Opcode.BNZ to InstructionFormat(BW, true, false, false, true ),
|
||||
Opcode.BEQ to InstructionFormat(BW, true, true, false, true ),
|
||||
Opcode.BNE to InstructionFormat(BW, true, true, false, true ),
|
||||
Opcode.BLT to InstructionFormat(BW, true, true, false, true ),
|
||||
Opcode.BLTS to InstructionFormat(BW, true, true, false, true ),
|
||||
Opcode.BGT to InstructionFormat(BW, true, true, false, true ),
|
||||
Opcode.BGTS to InstructionFormat(BW, true, true, false, true ),
|
||||
Opcode.BLE to InstructionFormat(BW, true, true, false, true ),
|
||||
Opcode.BLES to InstructionFormat(BW, true, true, false, true ),
|
||||
Opcode.BGE to InstructionFormat(BW, true, true, false, true ),
|
||||
Opcode.BGES to InstructionFormat(BW, true, true, false, true ),
|
||||
|
||||
Opcode.NEG to InstructionFormat(BW, true, true, false, false),
|
||||
Opcode.ADD to InstructionFormat(BW, true, true, true, false),
|
||||
Opcode.ADDI to InstructionFormat(BW, true, true, false, true ),
|
||||
Opcode.SUB to InstructionFormat(BW, true, true, true, false),
|
||||
Opcode.SUBI to InstructionFormat(BW, true, true, false, true ),
|
||||
Opcode.MUL to InstructionFormat(BW, true, true, true, false),
|
||||
Opcode.DIV to InstructionFormat(BW, true, true, true, false),
|
||||
Opcode.EXT to InstructionFormat(BW, true, true, false, false),
|
||||
Opcode.EXTS to InstructionFormat(BW, true, true, false, false),
|
||||
|
||||
Opcode.INV to InstructionFormat(BW, true, true, false, false),
|
||||
Opcode.AND to InstructionFormat(BW, true, true, true, false),
|
||||
Opcode.OR to InstructionFormat(BW, true, true, true, false),
|
||||
Opcode.XOR to InstructionFormat(BW, true, true, true, false),
|
||||
Opcode.LSR to InstructionFormat(BW, true, true, false, false),
|
||||
Opcode.LSL to InstructionFormat(BW, true, true, false, false),
|
||||
Opcode.ROR to InstructionFormat(BW, true, true, false, false),
|
||||
Opcode.ROL to InstructionFormat(BW, true, true, false, false),
|
||||
|
||||
Opcode.COPY to InstructionFormat(NN, true, true, false, true ),
|
||||
Opcode.COPYZ to InstructionFormat(NN, true, true, false, false),
|
||||
Opcode.SWAP to InstructionFormat(BW, true, true, false, false),
|
||||
Opcode.BREAKPOINT to InstructionFormat(NN, false, false, false, false)
|
||||
)
|
43
virtualmachine/src/prog8/vm/Main.kt
Normal file
43
virtualmachine/src/prog8/vm/Main.kt
Normal file
@ -0,0 +1,43 @@
|
||||
package prog8.vm
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
val memsrc = """
|
||||
$4000 strz "Hello from program! "derp" bye.\n"
|
||||
$2000 ubyte 65,66,67,68,0
|
||||
$2100 uword $1111,$2222,$3333,$4444
|
||||
"""
|
||||
val src = """
|
||||
; enable lores gfx screen
|
||||
load r0, 0
|
||||
syscall 8
|
||||
load.w r10, 320
|
||||
load.w r11, 240
|
||||
load.b r12, 0
|
||||
|
||||
_forever:
|
||||
load.w r1, 0
|
||||
_yloop:
|
||||
load.w r0, 0
|
||||
_xloop:
|
||||
mul.b r2,r0,r1
|
||||
add.b r2,r2,r12
|
||||
syscall 10
|
||||
addi.w r0,r0,1
|
||||
blt.w r0, r10, _xloop
|
||||
addi.w r1,r1,1
|
||||
blt.w r1, r11,_yloop
|
||||
addi.b r12,r12,1
|
||||
jump _forever
|
||||
|
||||
load.w r0, 2000
|
||||
syscall 7
|
||||
load.w r0,0
|
||||
return"""
|
||||
val memory = Memory()
|
||||
val assembler = Assembler()
|
||||
assembler.initializeMemory(memsrc, memory)
|
||||
val program = assembler.assembleProgram(src)
|
||||
|
||||
val vm = VirtualMachine(memory, program)
|
||||
vm.run()
|
||||
}
|
65
virtualmachine/src/prog8/vm/Memory.kt
Normal file
65
virtualmachine/src/prog8/vm/Memory.kt
Normal file
@ -0,0 +1,65 @@
|
||||
package prog8.vm
|
||||
|
||||
/**
|
||||
* 64 Kb of random access memory.
|
||||
*/
|
||||
class Memory {
|
||||
private val mem = Array<UByte>(64 * 1024) { 0u }
|
||||
|
||||
fun reset() {
|
||||
mem.fill(0u)
|
||||
}
|
||||
|
||||
fun getB(address: Int): UByte {
|
||||
return mem[address]
|
||||
}
|
||||
|
||||
fun setB(address: Int, value: UByte) {
|
||||
mem[address] = value
|
||||
}
|
||||
|
||||
fun getW(address: Int): UShort {
|
||||
return (256u*mem[address] + mem[address+1]).toUShort()
|
||||
}
|
||||
|
||||
fun setW(address: Int, value: UShort) {
|
||||
mem[address] = (value.toInt() ushr 8).toUByte()
|
||||
mem[address+1] = value.toUByte()
|
||||
}
|
||||
|
||||
// for now, no LONG 32-bits and no FLOAT support.
|
||||
// fun getL(address: Int): UInt {
|
||||
// return mem[address+3] + 256u*mem[address+2] + 65536u*mem[address+1] + 16777216u*mem[address]
|
||||
// }
|
||||
//
|
||||
// fun setL(address: Int, value: UInt) {
|
||||
// val v = value.toInt()
|
||||
// mem[address] = (v ushr 24).toUByte()
|
||||
// mem[address+1] = (v ushr 16).toUByte()
|
||||
// mem[address+2] = (v ushr 8).toUByte()
|
||||
// mem[address+3] = v.toUByte()
|
||||
// }
|
||||
|
||||
fun setString(address: Int, string: String, zeroTerminated: Boolean) {
|
||||
var addr=address
|
||||
for (c in string) {
|
||||
mem[addr] = c.code.toUByte()
|
||||
addr++
|
||||
}
|
||||
if(zeroTerminated)
|
||||
mem[addr] = 0u
|
||||
}
|
||||
|
||||
fun getString(address: Int): String {
|
||||
val sb = StringBuilder()
|
||||
var addr = address
|
||||
while(true) {
|
||||
val char = mem[addr].toInt()
|
||||
if(char==0)
|
||||
break
|
||||
sb.append(Char(char))
|
||||
addr++
|
||||
}
|
||||
return sb.toString()
|
||||
}
|
||||
}
|
24
virtualmachine/src/prog8/vm/Registers.kt
Normal file
24
virtualmachine/src/prog8/vm/Registers.kt
Normal file
@ -0,0 +1,24 @@
|
||||
package prog8.vm
|
||||
|
||||
/**
|
||||
* 65536 virtual registers of 16 bits wide.
|
||||
*/
|
||||
class Registers {
|
||||
private val registers = Array<UShort>(65536) { 0u }
|
||||
|
||||
fun reset() {
|
||||
registers.fill(0u)
|
||||
}
|
||||
|
||||
fun setB(reg: Int, value: UByte) {
|
||||
registers[reg] = registers[reg] and 0xff00u or value.toUShort()
|
||||
}
|
||||
|
||||
fun setW(reg: Int, value: UShort) {
|
||||
registers[reg] = value
|
||||
}
|
||||
|
||||
fun getB(reg: Int) = registers[reg].toUByte()
|
||||
|
||||
fun getW(reg: Int) = registers[reg]
|
||||
}
|
81
virtualmachine/src/prog8/vm/SysCalls.kt
Normal file
81
virtualmachine/src/prog8/vm/SysCalls.kt
Normal file
@ -0,0 +1,81 @@
|
||||
package prog8.vm
|
||||
|
||||
import kotlin.math.min
|
||||
|
||||
/*
|
||||
SYSCALLS:
|
||||
|
||||
0 = reset ; resets system
|
||||
1 = exit ; stops program and returns statuscode from r0.w
|
||||
2 = print_c ; print single character
|
||||
3 = print_s ; print 0-terminated string from memory
|
||||
4 = print_u8 ; print unsigned int byte
|
||||
5 = print_u16 ; print unsigned int word
|
||||
6 = input ; reads a line of text entered by the user, r0.w = memory buffer, r1.b = maxlength (0-255, 0=unlimited). Zero-terminates the string. Returns length in r65535.w
|
||||
7 = sleep ; sleep amount of milliseconds
|
||||
8 = gfx_enable ; enable graphics window r0.b = 0 -> lores 320x240, r0.b = 1 -> hires 640x480
|
||||
9 = gfx_clear ; clear graphics window with shade in r0.b
|
||||
10 = gfx_plot ; plot pixel in graphics window, r0.w/r1.w contain X and Y coordinates, r2.b contains brightness
|
||||
*/
|
||||
|
||||
enum class Syscall {
|
||||
RESET,
|
||||
EXIT,
|
||||
PRINT_C,
|
||||
PRINT_S,
|
||||
PRINT_U8,
|
||||
PRINT_U16,
|
||||
INPUT,
|
||||
SLEEP,
|
||||
GFX_ENABLE,
|
||||
GFX_CLEAR,
|
||||
GFX_PLOT
|
||||
}
|
||||
|
||||
object SysCalls {
|
||||
fun call(call: Syscall, vm: VirtualMachine) {
|
||||
when(call) {
|
||||
Syscall.RESET -> {
|
||||
vm.reset()
|
||||
}
|
||||
Syscall.EXIT ->{
|
||||
vm.exit()
|
||||
}
|
||||
Syscall.PRINT_C -> {
|
||||
val char = vm.registers.getB(0).toInt()
|
||||
print(Char(char))
|
||||
}
|
||||
Syscall.PRINT_S -> {
|
||||
var addr = vm.registers.getW(0).toInt()
|
||||
while(true) {
|
||||
val char = vm.memory.getB(addr).toInt()
|
||||
if(char==0)
|
||||
break
|
||||
print(Char(char))
|
||||
addr++
|
||||
}
|
||||
}
|
||||
Syscall.PRINT_U8 -> {
|
||||
print(vm.registers.getB(0))
|
||||
}
|
||||
Syscall.PRINT_U16 -> {
|
||||
print(vm.registers.getW(0))
|
||||
}
|
||||
Syscall.INPUT -> {
|
||||
var input = readln()
|
||||
val maxlen = vm.registers.getB(1).toInt()
|
||||
if(maxlen>0)
|
||||
input = input.substring(0, min(input.length, maxlen))
|
||||
vm.memory.setString(vm.registers.getW(0).toInt(), input, true)
|
||||
vm.registers.setW(65535, input.length.toUShort())
|
||||
}
|
||||
Syscall.SLEEP -> {
|
||||
val duration = vm.registers.getW(0).toLong()
|
||||
Thread.sleep(duration)
|
||||
}
|
||||
Syscall.GFX_ENABLE -> vm.gfx_enable()
|
||||
Syscall.GFX_CLEAR -> vm.gfx_clear()
|
||||
Syscall.GFX_PLOT -> vm.gfx_plot()
|
||||
}
|
||||
}
|
||||
}
|
647
virtualmachine/src/prog8/vm/VirtualMachine.kt
Normal file
647
virtualmachine/src/prog8/vm/VirtualMachine.kt
Normal file
@ -0,0 +1,647 @@
|
||||
package prog8.vm
|
||||
|
||||
import java.util.*
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
|
||||
class ProgramExitException(val status: Int): Exception()
|
||||
|
||||
|
||||
class BreakpointException(val pc: Int): Exception()
|
||||
|
||||
|
||||
@Suppress("FunctionName")
|
||||
class VirtualMachine(val memory: Memory, program: List<Instruction>) {
|
||||
val registers = Registers()
|
||||
val program: Array<Instruction> = program.toTypedArray()
|
||||
val callStack = Stack<Int>()
|
||||
var pc = 0
|
||||
var stepCount = 0
|
||||
|
||||
init {
|
||||
if(program.size>65536)
|
||||
throw IllegalArgumentException("program cannot contain more than 65536 instructions")
|
||||
}
|
||||
|
||||
fun run() {
|
||||
try {
|
||||
var before = System.nanoTime()
|
||||
var numIns = 0
|
||||
while(true) {
|
||||
step()
|
||||
numIns++
|
||||
|
||||
if(stepCount and 32767 == 0) {
|
||||
Thread.sleep(0, 10) // avoid 100% cpu core usage
|
||||
}
|
||||
|
||||
if(stepCount and 0xffffff == 0) {
|
||||
val now = System.nanoTime()
|
||||
val duration = now-before
|
||||
before = now
|
||||
val insPerSecond = numIns*1000.0/duration
|
||||
println("${insPerSecond.roundToInt()} MIPS")
|
||||
numIns = 0
|
||||
}
|
||||
}
|
||||
} catch (hx: ProgramExitException) {
|
||||
println("\nProgram exit! Statuscode=${hx.status} #steps=${stepCount}")
|
||||
gfx_close()
|
||||
}
|
||||
}
|
||||
|
||||
fun reset() {
|
||||
registers.reset()
|
||||
memory.reset()
|
||||
pc = 0
|
||||
stepCount = 0
|
||||
callStack.clear()
|
||||
}
|
||||
|
||||
fun exit() {
|
||||
throw ProgramExitException(registers.getW(0).toInt())
|
||||
}
|
||||
|
||||
fun step(count: Int=1) {
|
||||
var left=count
|
||||
while(left>0) {
|
||||
stepCount++
|
||||
dispatch()
|
||||
left--
|
||||
}
|
||||
}
|
||||
|
||||
private fun dispatch() {
|
||||
if(pc >= program.size)
|
||||
exit()
|
||||
val ins = program[pc]
|
||||
when(ins.opcode) {
|
||||
Opcode.NOP -> { pc++ }
|
||||
Opcode.LOAD -> InsLOAD(ins)
|
||||
Opcode.LOADM -> InsLOADM(ins)
|
||||
Opcode.LOADX -> InsLOADX(ins)
|
||||
Opcode.LOADI -> InsLOADI(ins)
|
||||
Opcode.LOADR -> InsLOADR(ins)
|
||||
Opcode.SWAPREG -> InsSWAPREG(ins)
|
||||
Opcode.STOREM -> InsSTOREM(ins)
|
||||
Opcode.STOREX -> InsSTOREX(ins)
|
||||
Opcode.STOREI -> InsSTOREI(ins)
|
||||
Opcode.STOREZ -> InsSTOREZ(ins)
|
||||
Opcode.STOREZX -> InsSTOREZX(ins)
|
||||
Opcode.STOREZI -> InsSTOREZI(ins)
|
||||
Opcode.JUMP -> InsJUMP(ins)
|
||||
Opcode.JUMPI -> InsJUMPI(ins)
|
||||
Opcode.GOSUB -> InsGOSUB(ins)
|
||||
Opcode.GOSUBI -> InsGOSUBI(ins)
|
||||
Opcode.SYSCALL -> InsSYSCALL(ins)
|
||||
Opcode.RETURN -> InsRETURN()
|
||||
Opcode.BZ -> InsBZ(ins)
|
||||
Opcode.BNZ -> InsBNZ(ins)
|
||||
Opcode.BEQ -> InsBEQ(ins)
|
||||
Opcode.BNE -> InsBNE(ins)
|
||||
Opcode.BLT -> InsBLTU(ins)
|
||||
Opcode.BLTS -> InsBLTS(ins)
|
||||
Opcode.BGT -> InsBGTU(ins)
|
||||
Opcode.BGTS -> InsBGTS(ins)
|
||||
Opcode.BLE -> InsBLEU(ins)
|
||||
Opcode.BLES -> InsBLES(ins)
|
||||
Opcode.BGE -> InsBGEU(ins)
|
||||
Opcode.BGES -> InsBGES(ins)
|
||||
Opcode.NEG -> InsNEG(ins)
|
||||
Opcode.ADD -> InsADD(ins)
|
||||
Opcode.ADDI -> InsADDI(ins)
|
||||
Opcode.SUB -> InsSUB(ins)
|
||||
Opcode.SUBI -> InsSUBI(ins)
|
||||
Opcode.MUL -> InsMul(ins)
|
||||
Opcode.DIV -> InsDiv(ins)
|
||||
Opcode.EXT -> InsEXT(ins)
|
||||
Opcode.EXTS -> InsEXTS(ins)
|
||||
Opcode.INV -> InsINV(ins)
|
||||
Opcode.AND -> InsAND(ins)
|
||||
Opcode.OR -> InsOR(ins)
|
||||
Opcode.XOR -> InsXOR(ins)
|
||||
Opcode.LSR -> InsLSR(ins)
|
||||
Opcode.LSL -> InsLSL(ins)
|
||||
Opcode.ROR -> InsROR(ins)
|
||||
Opcode.ROL -> InsROL(ins)
|
||||
Opcode.SWAP -> InsSWAP(ins)
|
||||
Opcode.COPY -> InsCOPY(ins)
|
||||
Opcode.COPYZ -> InsCOPYZ(ins)
|
||||
Opcode.BREAKPOINT -> InsBREAKPOINT()
|
||||
else -> throw IllegalArgumentException("invalid opcode ${ins.opcode}")
|
||||
}
|
||||
}
|
||||
|
||||
private fun InsSYSCALL(ins: Instruction) {
|
||||
val call = Syscall.values()[ins.value!!]
|
||||
SysCalls.call(call, this)
|
||||
pc++
|
||||
}
|
||||
|
||||
private fun InsBREAKPOINT() {
|
||||
pc++
|
||||
throw BreakpointException(pc)
|
||||
}
|
||||
|
||||
private fun InsLOAD(i: Instruction) {
|
||||
when(i.type!!) {
|
||||
DataType.BYTE -> registers.setB(i.reg1!!, i.value!!.toUByte())
|
||||
DataType.WORD -> registers.setW(i.reg1!!, i.value!!.toUShort())
|
||||
}
|
||||
pc++
|
||||
}
|
||||
|
||||
private fun InsLOADM(i: Instruction) {
|
||||
when(i.type!!) {
|
||||
DataType.BYTE -> registers.setB(i.reg1!!, memory.getB(i.value!!))
|
||||
DataType.WORD -> registers.setW(i.reg1!!, memory.getW(i.value!!))
|
||||
}
|
||||
pc++
|
||||
}
|
||||
|
||||
private fun InsLOADI(i: Instruction) {
|
||||
when(i.type!!) {
|
||||
DataType.BYTE -> registers.setB(i.reg1!!, memory.getB(registers.getW(i.reg2!!).toInt()))
|
||||
DataType.WORD -> registers.setW(i.reg1!!, memory.getW(registers.getW(i.reg2!!).toInt()))
|
||||
}
|
||||
pc++
|
||||
}
|
||||
private fun InsLOADX(i: Instruction) {
|
||||
when (i.type!!) {
|
||||
DataType.BYTE -> registers.setB(i.reg1!!, memory.getB(i.value!! + registers.getW(i.reg2!!).toInt()))
|
||||
DataType.WORD -> registers.setW(i.reg1!!, memory.getW(i.value!! + registers.getW(i.reg2!!).toInt()))
|
||||
}
|
||||
pc++
|
||||
}
|
||||
|
||||
private fun InsLOADR(i: Instruction) {
|
||||
when(i.type!!) {
|
||||
DataType.BYTE -> registers.setB(i.reg1!!, registers.getB(i.reg2!!))
|
||||
DataType.WORD -> registers.setW(i.reg1!!, registers.getW(i.reg2!!))
|
||||
}
|
||||
pc++
|
||||
}
|
||||
|
||||
private fun InsSWAPREG(i: Instruction) {
|
||||
when(i.type!!) {
|
||||
DataType.BYTE -> {
|
||||
val oldR2 = registers.getB(i.reg2!!)
|
||||
registers.setB(i.reg2, registers.getB(i.reg1!!))
|
||||
registers.setB(i.reg1, oldR2)
|
||||
}
|
||||
DataType.WORD -> {
|
||||
val oldR2 = registers.getW(i.reg2!!)
|
||||
registers.setW(i.reg2, registers.getW(i.reg1!!))
|
||||
registers.setW(i.reg1, oldR2)
|
||||
}
|
||||
}
|
||||
pc++
|
||||
}
|
||||
|
||||
private fun InsSTOREM(i: Instruction) {
|
||||
when(i.type!!) {
|
||||
DataType.BYTE -> memory.setB(i.value!!, registers.getB(i.reg1!!))
|
||||
DataType.WORD -> memory.setW(i.value!!, registers.getW(i.reg1!!))
|
||||
}
|
||||
pc++
|
||||
}
|
||||
|
||||
private fun InsSTOREI(i: Instruction) {
|
||||
when (i.type!!) {
|
||||
DataType.BYTE -> memory.setB(registers.getW(i.reg2!!).toInt(), registers.getB(i.reg1!!))
|
||||
DataType.WORD -> memory.setW(registers.getW(i.reg2!!).toInt(), registers.getW(i.reg1!!))
|
||||
}
|
||||
pc++
|
||||
}
|
||||
|
||||
private fun InsSTOREX(i: Instruction) {
|
||||
when (i.type!!) {
|
||||
DataType.BYTE -> memory.setB(registers.getW(i.reg2!!).toInt() + i.value!!, registers.getB(i.reg1!!))
|
||||
DataType.WORD -> memory.setW(registers.getW(i.reg2!!).toInt() + i.value!!, registers.getW(i.reg1!!))
|
||||
}
|
||||
pc++
|
||||
}
|
||||
|
||||
private fun InsSTOREZ(i: Instruction) {
|
||||
when(i.type!!) {
|
||||
DataType.BYTE -> memory.setB(i.value!!, 0u)
|
||||
DataType.WORD -> memory.setW(i.value!!, 0u)
|
||||
}
|
||||
}
|
||||
|
||||
private fun InsSTOREZI(i: Instruction) {
|
||||
when (i.type!!) {
|
||||
DataType.BYTE -> memory.setB(registers.getW(i.reg2!!).toInt(), 0u)
|
||||
DataType.WORD -> memory.setW(registers.getW(i.reg2!!).toInt(), 0u)
|
||||
}
|
||||
pc++
|
||||
}
|
||||
private fun InsSTOREZX(i: Instruction) {
|
||||
when (i.type!!) {
|
||||
DataType.BYTE -> memory.setB(registers.getW(i.reg2!!).toInt() + i.value!!, 0u)
|
||||
DataType.WORD -> memory.setW(registers.getW(i.reg2!!).toInt() + i.value!!, 0u)
|
||||
}
|
||||
pc++
|
||||
}
|
||||
|
||||
private fun InsJUMP(i: Instruction) {
|
||||
pc = i.value!!
|
||||
}
|
||||
|
||||
private fun InsJUMPI(i: Instruction) {
|
||||
pc = registers.getW(i.reg1!!).toInt()
|
||||
}
|
||||
|
||||
private fun InsGOSUB(i: Instruction) {
|
||||
callStack.push(pc+1)
|
||||
pc = i.value!!
|
||||
}
|
||||
|
||||
private fun InsGOSUBI(i: Instruction) {
|
||||
callStack.push(pc+1)
|
||||
pc = registers.getW(i.reg1!!).toInt()
|
||||
}
|
||||
|
||||
private fun InsRETURN() {
|
||||
if(callStack.isEmpty())
|
||||
exit()
|
||||
else
|
||||
pc = callStack.pop()
|
||||
}
|
||||
|
||||
private fun InsBZ(i: Instruction) {
|
||||
when(i.type!!) {
|
||||
DataType.BYTE -> {
|
||||
if(registers.getB(i.reg1!!)==0.toUByte())
|
||||
pc = i.value!!
|
||||
else
|
||||
pc++
|
||||
}
|
||||
DataType.WORD -> {
|
||||
if(registers.getW(i.reg1!!)==0.toUShort())
|
||||
pc = i.value!!
|
||||
else
|
||||
pc++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun InsBNZ(i: Instruction) {
|
||||
when(i.type!!) {
|
||||
DataType.BYTE -> {
|
||||
if(registers.getB(i.reg1!!)!=0.toUByte())
|
||||
pc = i.value!!
|
||||
else
|
||||
pc++
|
||||
}
|
||||
DataType.WORD -> {
|
||||
if(registers.getW(i.reg1!!)!=0.toUShort())
|
||||
pc = i.value!!
|
||||
else
|
||||
pc++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun InsBEQ(i: Instruction) {
|
||||
val (left: Int, right: Int) = getBranchOperands(i)
|
||||
if(left==right)
|
||||
pc = i.value!!
|
||||
else
|
||||
pc++
|
||||
}
|
||||
|
||||
private fun getBranchOperands(i: Instruction): Pair<Int, Int> {
|
||||
return when(i.type) {
|
||||
DataType.BYTE -> Pair(registers.getB(i.reg1!!).toInt(), registers.getB(i.reg2!!).toInt())
|
||||
DataType.WORD -> Pair(registers.getW(i.reg1!!).toInt(), registers.getW(i.reg2!!).toInt())
|
||||
null -> throw IllegalArgumentException("need type for branch instruction")
|
||||
}
|
||||
}
|
||||
|
||||
private fun getBranchOperandsU(i: Instruction): Pair<UInt, UInt> {
|
||||
return when(i.type) {
|
||||
DataType.BYTE -> Pair(registers.getB(i.reg1!!).toUInt(), registers.getB(i.reg2!!).toUInt())
|
||||
DataType.WORD -> Pair(registers.getW(i.reg1!!).toUInt(), registers.getW(i.reg2!!).toUInt())
|
||||
null -> throw IllegalArgumentException("need type for branch instruction")
|
||||
}
|
||||
}
|
||||
|
||||
private fun getLogicalOperandsU(i: Instruction): Pair<UInt, UInt> {
|
||||
return when(i.type) {
|
||||
DataType.BYTE -> Pair(registers.getB(i.reg2!!).toUInt(), registers.getB(i.reg3!!).toUInt())
|
||||
DataType.WORD -> Pair(registers.getW(i.reg2!!).toUInt(), registers.getW(i.reg3!!).toUInt())
|
||||
null -> throw IllegalArgumentException("need type for logical instruction")
|
||||
}
|
||||
}
|
||||
|
||||
private fun InsBNE(i: Instruction) {
|
||||
val (left: Int, right: Int) = getBranchOperands(i)
|
||||
if(left!=right)
|
||||
pc = i.value!!
|
||||
else
|
||||
pc++
|
||||
}
|
||||
|
||||
private fun InsBLTU(i: Instruction) {
|
||||
val (left: UInt, right: UInt) = getBranchOperandsU(i)
|
||||
if(left<right)
|
||||
pc = i.value!!
|
||||
else
|
||||
pc++
|
||||
}
|
||||
|
||||
private fun InsBLTS(i: Instruction) {
|
||||
val (left: Int, right: Int) = getBranchOperands(i)
|
||||
if(left<right)
|
||||
pc = i.value!!
|
||||
else
|
||||
pc++
|
||||
}
|
||||
|
||||
private fun InsBGTU(i: Instruction) {
|
||||
val (left: UInt, right: UInt) = getBranchOperandsU(i)
|
||||
if(left>right)
|
||||
pc = i.value!!
|
||||
else
|
||||
pc++
|
||||
|
||||
}
|
||||
|
||||
private fun InsBGTS(i: Instruction) {
|
||||
val (left: Int, right: Int) = getBranchOperands(i)
|
||||
if(left>right)
|
||||
pc = i.value!!
|
||||
else
|
||||
pc++
|
||||
}
|
||||
|
||||
private fun InsBLEU(i: Instruction) {
|
||||
val (left: UInt, right: UInt) = getBranchOperandsU(i)
|
||||
if(left<=right)
|
||||
pc = i.value!!
|
||||
else
|
||||
pc++
|
||||
|
||||
}
|
||||
|
||||
private fun InsBLES(i: Instruction) {
|
||||
val (left: Int, right: Int) = getBranchOperands(i)
|
||||
if(left<=right)
|
||||
pc = i.value!!
|
||||
else
|
||||
pc++
|
||||
}
|
||||
|
||||
private fun InsBGEU(i: Instruction) {
|
||||
val (left: UInt, right: UInt) = getBranchOperandsU(i)
|
||||
if(left>=right)
|
||||
pc = i.value!!
|
||||
else
|
||||
pc++
|
||||
|
||||
}
|
||||
|
||||
private fun InsBGES(i: Instruction) {
|
||||
val (left: Int, right: Int) = getBranchOperands(i)
|
||||
if(left>=right)
|
||||
pc = i.value!!
|
||||
else
|
||||
pc++
|
||||
}
|
||||
|
||||
private fun InsNEG(i: Instruction) {
|
||||
when(i.type!!) {
|
||||
DataType.BYTE -> registers.setB(i.reg1!!, (-registers.getB(i.reg2!!).toInt()).toUByte())
|
||||
DataType.WORD -> registers.setW(i.reg1!!, (-registers.getW(i.reg2!!).toInt()).toUShort())
|
||||
}
|
||||
pc++
|
||||
}
|
||||
|
||||
private fun InsADD(i: Instruction) {
|
||||
when(i.type!!) {
|
||||
DataType.BYTE -> arithByte("+", i.reg1!!, i.reg2!!, i.reg3!!, null)
|
||||
DataType.WORD -> arithWord("+", i.reg1!!, i.reg2!!, i.reg3!!, null)
|
||||
}
|
||||
pc++
|
||||
}
|
||||
|
||||
private fun InsMul(i: Instruction) {
|
||||
when(i.type!!) {
|
||||
DataType.BYTE -> arithByte("*", i.reg1!!, i.reg2!!, i.reg3!!, null)
|
||||
DataType.WORD -> arithWord("*", i.reg1!!, i.reg2!!, i.reg3!!, null)
|
||||
}
|
||||
pc++
|
||||
}
|
||||
|
||||
private fun InsDiv(i: Instruction) {
|
||||
when(i.type!!) {
|
||||
DataType.BYTE -> arithByte("/", i.reg1!!, i.reg2!!, i.reg3!!, null)
|
||||
DataType.WORD -> arithWord("/", i.reg1!!, i.reg2!!, i.reg3!!, null)
|
||||
}
|
||||
pc++
|
||||
}
|
||||
|
||||
private fun arithByte(operator: String, reg1: Int, reg2: Int, reg3: Int?, value: UByte?) {
|
||||
val left = registers.getB(reg2)
|
||||
val right = value ?: registers.getB(reg3!!)
|
||||
val result = when(operator) {
|
||||
"+" -> left + right
|
||||
"-" -> left - right
|
||||
"*" -> left * right
|
||||
"/" -> {
|
||||
if(right==0.toUByte()) 0xffu
|
||||
else left / right
|
||||
}
|
||||
else -> TODO("operator $operator")
|
||||
}
|
||||
registers.setB(reg1, result.toUByte())
|
||||
}
|
||||
|
||||
private fun arithWord(operator: String, reg1: Int, reg2: Int, reg3: Int?, value: UShort?) {
|
||||
val left = registers.getW(reg2)
|
||||
val right = value ?: registers.getW(reg3!!)
|
||||
val result = when(operator) {
|
||||
"+" -> left + right
|
||||
"-" -> left - right
|
||||
"*" -> left * right
|
||||
"/" -> {
|
||||
if(right==0.toUShort()) 0xffffu
|
||||
else left / right
|
||||
}
|
||||
else -> TODO("operator $operator")
|
||||
}
|
||||
registers.setW(reg1, result.toUShort())
|
||||
}
|
||||
|
||||
private fun InsADDI(i: Instruction) {
|
||||
when(i.type!!) {
|
||||
DataType.BYTE -> arithByte("+", i.reg1!!, i.reg2!!, null, i.value!!.toUByte())
|
||||
DataType.WORD -> arithWord("+", i.reg1!!, i.reg2!!, null, i.value!!.toUShort())
|
||||
}
|
||||
pc++
|
||||
}
|
||||
|
||||
private fun InsSUB(i: Instruction) {
|
||||
when(i.type!!) {
|
||||
DataType.BYTE -> arithByte("-", i.reg1!!, i.reg2!!, i.reg3!!, null)
|
||||
DataType.WORD -> arithWord("-", i.reg1!!, i.reg2!!, i.reg3!!, null)
|
||||
}
|
||||
pc++
|
||||
}
|
||||
|
||||
private fun InsSUBI(i: Instruction) {
|
||||
when(i.type!!) {
|
||||
DataType.BYTE -> arithByte("-", i.reg1!!, i.reg2!!, null, i.value!!.toUByte())
|
||||
DataType.WORD -> arithWord("-", i.reg1!!, i.reg2!!, null, i.value!!.toUShort())
|
||||
}
|
||||
pc++
|
||||
}
|
||||
|
||||
private fun InsEXT(i: Instruction) {
|
||||
when(i.type!!){
|
||||
DataType.BYTE -> registers.setW(i.reg1!!, registers.getB(i.reg2!!).toUShort())
|
||||
DataType.WORD -> TODO("ext.w requires 32 bits registers")
|
||||
}
|
||||
pc++
|
||||
}
|
||||
|
||||
private fun InsEXTS(i: Instruction) {
|
||||
when(i.type!!){
|
||||
DataType.BYTE -> registers.setW(i.reg1!!, (registers.getB(i.reg2!!).toByte()).toUShort())
|
||||
DataType.WORD -> TODO("ext.w requires 32 bits registers")
|
||||
}
|
||||
pc++
|
||||
}
|
||||
|
||||
private fun InsINV(i: Instruction) {
|
||||
when(i.type!!) {
|
||||
DataType.BYTE -> registers.setB(i.reg1!!, registers.getB(i.reg2!!).inv())
|
||||
DataType.WORD -> registers.setW(i.reg1!!, registers.getW(i.reg2!!).inv())
|
||||
}
|
||||
pc++
|
||||
}
|
||||
|
||||
private fun InsAND(i: Instruction) {
|
||||
val (left: UInt, right: UInt) = getLogicalOperandsU(i)
|
||||
when(i.type!!) {
|
||||
DataType.BYTE -> registers.setB(i.reg1!!, (left and right).toUByte())
|
||||
DataType.WORD -> registers.setW(i.reg1!!, (left and right).toUShort())
|
||||
}
|
||||
pc++
|
||||
}
|
||||
|
||||
private fun InsOR(i: Instruction) {
|
||||
val (left: UInt, right: UInt) = getLogicalOperandsU(i)
|
||||
when(i.type!!) {
|
||||
DataType.BYTE -> registers.setB(i.reg1!!, (left or right).toUByte())
|
||||
DataType.WORD -> registers.setW(i.reg1!!, (left or right).toUShort())
|
||||
}
|
||||
pc++
|
||||
}
|
||||
|
||||
private fun InsXOR(i: Instruction) {
|
||||
val (left: UInt, right: UInt) = getLogicalOperandsU(i)
|
||||
when(i.type!!) {
|
||||
DataType.BYTE -> registers.setB(i.reg1!!, (left xor right).toUByte())
|
||||
DataType.WORD -> registers.setW(i.reg1!!, (left xor right).toUShort())
|
||||
}
|
||||
pc++
|
||||
}
|
||||
|
||||
private fun InsLSR(i: Instruction) {
|
||||
when(i.type!!) {
|
||||
DataType.BYTE -> registers.setB(i.reg1!!, (registers.getB(i.reg2!!).toInt() ushr 1).toUByte())
|
||||
DataType.WORD -> registers.setW(i.reg1!!, (registers.getW(i.reg2!!).toInt() ushr 1).toUShort())
|
||||
}
|
||||
pc++
|
||||
}
|
||||
|
||||
private fun InsLSL(i: Instruction) {
|
||||
when(i.type!!) {
|
||||
DataType.BYTE -> registers.setB(i.reg1!!, (registers.getB(i.reg2!!).toInt() shl 1).toUByte())
|
||||
DataType.WORD -> registers.setW(i.reg1!!, (registers.getW(i.reg2!!).toInt() shl 1).toUShort())
|
||||
}
|
||||
pc++
|
||||
}
|
||||
|
||||
private fun InsROR(i: Instruction) {
|
||||
when(i.type!!) {
|
||||
DataType.BYTE -> registers.setB(i.reg1!!, (registers.getB(i.reg2!!).toInt().rotateRight(1).toUByte()))
|
||||
DataType.WORD -> registers.setW(i.reg1!!, (registers.getW(i.reg2!!).toInt().rotateRight(1).toUShort()))
|
||||
}
|
||||
pc++
|
||||
}
|
||||
|
||||
private fun InsROL(i: Instruction) {
|
||||
when(i.type!!) {
|
||||
DataType.BYTE -> registers.setB(i.reg1!!, (registers.getB(i.reg2!!).toInt().rotateLeft(1).toUByte()))
|
||||
DataType.WORD -> registers.setW(i.reg1!!, (registers.getW(i.reg2!!).toInt().rotateLeft(1).toUShort()))
|
||||
}
|
||||
pc++
|
||||
}
|
||||
|
||||
private fun InsCOPY(i: Instruction) = doCopy(i.reg1!!, i.reg2!!, i.reg3!!, false)
|
||||
|
||||
private fun InsCOPYZ(i: Instruction) = doCopy(i.reg1!!, i.reg2!!, null, true)
|
||||
|
||||
private fun doCopy(reg1: Int, reg2: Int, length: Int?, untilzero: Boolean) {
|
||||
var from = registers.getW(reg1).toInt()
|
||||
var to = registers.getW(reg2).toInt()
|
||||
if(untilzero) {
|
||||
while(true) {
|
||||
val char = memory.getB(from).toInt()
|
||||
memory.setB(to, char.toUByte())
|
||||
if(char==0)
|
||||
break
|
||||
from++
|
||||
to++
|
||||
}
|
||||
} else {
|
||||
var len = length!!
|
||||
while(len>0) {
|
||||
val char = memory.getB(from).toInt()
|
||||
memory.setB(to, char.toUByte())
|
||||
from++
|
||||
to++
|
||||
len--
|
||||
}
|
||||
}
|
||||
pc++
|
||||
}
|
||||
|
||||
private fun InsSWAP(i: Instruction) {
|
||||
when(i.type!!) {
|
||||
DataType.BYTE -> {
|
||||
val value = registers.getW(i.reg2!!)
|
||||
val newValue = value.toUByte()*256u + (value.toInt() ushr 8).toUInt()
|
||||
registers.setW(i.reg1!!, newValue.toUShort())
|
||||
}
|
||||
DataType.WORD -> TODO("swap.w requires 32-bits registers")
|
||||
}
|
||||
pc++
|
||||
}
|
||||
|
||||
private var window: GraphicsWindow? = null
|
||||
|
||||
fun gfx_enable() {
|
||||
window = when(registers.getB(0).toInt()) {
|
||||
0 -> GraphicsWindow(320, 240, 3)
|
||||
1 -> GraphicsWindow(640, 480, 2)
|
||||
else -> throw IllegalArgumentException("invalid screen mode")
|
||||
}
|
||||
window!!.start()
|
||||
}
|
||||
|
||||
fun gfx_clear() {
|
||||
window?.clear(registers.getB(0).toInt())
|
||||
}
|
||||
|
||||
fun gfx_plot() {
|
||||
window?.plot(registers.getW(0).toInt(), registers.getW(1).toInt(), registers.getB(2).toInt())
|
||||
}
|
||||
|
||||
fun gfx_close() {
|
||||
window?.close()
|
||||
}
|
||||
}
|
13
virtualmachine/virtualmachine.iml
Normal file
13
virtualmachine/virtualmachine.iml
Normal file
@ -0,0 +1,13 @@
|
||||
<?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" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<orderEntry type="library" name="KotlinJavaRuntime" level="project" />
|
||||
</component>
|
||||
</module>
|
Loading…
x
Reference in New Issue
Block a user