Compare commits

..
10 Commits
51 changed files with 1741 additions and 307 deletions

No files matched your search

+1 -2
View File
@@ -221,12 +221,11 @@ class StMemorySlab(
name: String,
val size: UInt,
val align: UInt,
val allocatedAddress: UInt? = null, // this is used (for now) in the VM code generator. TODO remove this once no longer used
position: Position
):
StNode(name, StNodeType.MEMORYSLAB, position) {
override fun printProperties() {
print("$name size=$size align=$align address=$allocatedAddress")
print("$name size=$size align=$align")
}
}
@@ -29,6 +29,14 @@ class PtSub(
override fun printProperties() {
print(name)
}
init {
// params and return value should not be str
if(parameters.any{ it.type !in NumericDatatypes })
throw AssemblyError("non-numeric parameter")
if(returntype!=null && returntype !in NumericDatatypes)
throw AssemblyError("non-numeric returntype $returntype")
}
}
+2 -2
View File
@@ -106,7 +106,7 @@ sealed class SourceCode {
* [origin]: `library:/x/y/z.p8` for a given `pathString` of "x/y/z.p8"
*/
class Resource(pathString: String): SourceCode() {
private val normalized = "/" + Path.of(pathString).normalize().toMutableList().joinToString("/")
private val normalized = "/" + Path(pathString).normalize().toMutableList().joinToString("/")
override val isFromResources = true
override val isFromFilesystem = false
@@ -125,7 +125,7 @@ sealed class SourceCode {
}
val stream = object {}.javaClass.getResourceAsStream(normalized)
text = stream!!.reader().use { it.readText() }
name = Path.of(pathString).toFile().nameWithoutExtension
name = Path(pathString).toFile().nameWithoutExtension
}
}
@@ -32,14 +32,14 @@ class VirtualMachineDefinition: IMachineDefinition {
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path) {
println("\nStarting Virtual Machine...")
// to not have external module dependencies we launch the virtual machine via reflection
// to not have external module dependencies in our own module, we launch the virtual machine via reflection
val vm = Class.forName("prog8.vm.VmRunner").getDeclaredConstructor().newInstance() as IVirtualMachineRunner
val filename = programNameWithPath.name
if(filename.endsWith(".p8virt")) {
vm.runProgram(programNameWithPath.readText(), true)
vm.runProgram(programNameWithPath.readText())
} else if(File("$filename.p8virt").isFile) {
val source = File("$filename.p8virt").readText()
vm.runProgram(source, true)
vm.runProgram(source)
}
else
throw IllegalArgumentException("vm can only run .p8virt or .p8ir files")
@@ -53,5 +53,5 @@ class VirtualMachineDefinition: IMachineDefinition {
}
interface IVirtualMachineRunner {
fun runProgram(source: String, throttle: Boolean)
fun runProgram(source: String)
}
@@ -2900,7 +2900,7 @@ $repeatLabel lda $counterVar
fun addMemorySlab(name: String, size: UInt, align: UInt, position: Position): String {
val prefixedName = "prog8_memoryslab_$name"
symbolTable.add(StMemorySlab(prefixedName, size, align, null, position))
symbolTable.add(StMemorySlab(prefixedName, size, align, position))
return prefixedName
}
}
-1
View File
@@ -26,7 +26,6 @@ compileTestKotlin {
dependencies {
implementation project(':codeCore')
implementation project(':intermediate')
implementation project(':virtualmachine')
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
// implementation "org.jetbrains.kotlin:kotlin-reflect"
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.16"
@@ -12,7 +12,6 @@
<orderEntry type="library" name="KotlinJavaRuntime" level="project" />
<orderEntry type="module" module-name="codeCore" />
<orderEntry type="module" module-name="intermediate" />
<orderEntry type="module" module-name="virtualmachine" />
<orderEntry type="library" name="io.kotest.assertions.core.jvm" level="project" />
<orderEntry type="library" name="io.kotest.runner.junit5.jvm" level="project" />
</component>
@@ -6,7 +6,6 @@ import prog8.code.core.AssemblyError
import prog8.code.core.DataType
import prog8.code.core.Position
import prog8.intermediate.*
import prog8.vm.Syscall
internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGen: ExpressionGen) {
@@ -67,10 +66,10 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
val syscall =
when (array.dt) {
DataType.ARRAY_UB,
DataType.ARRAY_B -> Syscall.ANY_BYTE
DataType.ARRAY_B -> IMSyscall.ANY_BYTE
DataType.ARRAY_UW,
DataType.ARRAY_W -> Syscall.ANY_WORD
DataType.ARRAY_F -> Syscall.ANY_FLOAT
DataType.ARRAY_W -> IMSyscall.ANY_WORD
DataType.ARRAY_F -> IMSyscall.ANY_FLOAT
else -> throw IllegalArgumentException("weird type")
}
code += exprGen.translateExpression(call.args[0], 0, -1)
@@ -87,10 +86,10 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
val syscall =
when(array.dt) {
DataType.ARRAY_UB,
DataType.ARRAY_B -> Syscall.ALL_BYTE
DataType.ARRAY_B -> IMSyscall.ALL_BYTE
DataType.ARRAY_UW,
DataType.ARRAY_W -> Syscall.ALL_WORD
DataType.ARRAY_F -> Syscall.ALL_FLOAT
DataType.ARRAY_W -> IMSyscall.ALL_WORD
DataType.ARRAY_F -> IMSyscall.ALL_FLOAT
else -> throw IllegalArgumentException("weird type")
}
val code = IRCodeChunk(call.position)
@@ -187,37 +186,37 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
private fun funcReverse(call: PtBuiltinFunctionCall): IRCodeChunk {
val arrayName = call.args[0] as PtIdentifier
val array = codeGen.symbolTable.flat.getValue(arrayName.targetName) as StStaticVariable
val sortSyscall =
val syscall =
when(array.dt) {
DataType.ARRAY_UB, DataType.ARRAY_B, DataType.STR -> Syscall.REVERSE_BYTES
DataType.ARRAY_UW, DataType.ARRAY_W -> Syscall.REVERSE_WORDS
DataType.ARRAY_F -> Syscall.REVERSE_FLOATS
DataType.ARRAY_UB, DataType.ARRAY_B, DataType.STR -> IMSyscall.REVERSE_BYTES
DataType.ARRAY_UW, DataType.ARRAY_W -> IMSyscall.REVERSE_WORDS
DataType.ARRAY_F -> IMSyscall.REVERSE_FLOATS
else -> throw IllegalArgumentException("weird type to reverse")
}
val code = IRCodeChunk(call.position)
code += exprGen.translateExpression(call.args[0], 0, -1)
code += IRCodeInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=1, value=array.length)
code += IRCodeInstruction(Opcode.SYSCALL, value=sortSyscall.ordinal)
code += IRCodeInstruction(Opcode.SYSCALL, value=syscall.ordinal)
return code
}
private fun funcSort(call: PtBuiltinFunctionCall): IRCodeChunk {
val arrayName = call.args[0] as PtIdentifier
val array = codeGen.symbolTable.flat.getValue(arrayName.targetName) as StStaticVariable
val sortSyscall =
val syscall =
when(array.dt) {
DataType.ARRAY_UB -> Syscall.SORT_UBYTE
DataType.ARRAY_B -> Syscall.SORT_BYTE
DataType.ARRAY_UW -> Syscall.SORT_UWORD
DataType.ARRAY_W -> Syscall.SORT_WORD
DataType.STR -> Syscall.SORT_UBYTE
DataType.ARRAY_UB -> IMSyscall.SORT_UBYTE
DataType.ARRAY_B -> IMSyscall.SORT_BYTE
DataType.ARRAY_UW -> IMSyscall.SORT_UWORD
DataType.ARRAY_W -> IMSyscall.SORT_WORD
DataType.STR -> IMSyscall.SORT_UBYTE
DataType.ARRAY_F -> throw IllegalArgumentException("sorting a floating point array is not supported")
else -> throw IllegalArgumentException("weird type to sort")
}
val code = IRCodeChunk(call.position)
code += exprGen.translateExpression(call.args[0], 0, -1)
code += IRCodeInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=1, value=array.length)
code += IRCodeInstruction(Opcode.SYSCALL, value=sortSyscall.ordinal)
code += IRCodeInstruction(Opcode.SYSCALL, value=syscall.ordinal)
return code
}
@@ -24,9 +24,10 @@ class IRCodeGen(
internal val vmRegisters = RegisterPool()
fun generate(): IRProgram {
flattenLabelNames()
flattenNestedSubroutines()
val irProg = IRProgram(program.name, symbolTable, options, program.encoding)
val irProg = IRProgram(program.name, IRSymbolTable(symbolTable), options, program.encoding)
if(!options.dontReinitGlobals) {
// collect global variables initializers
@@ -101,9 +102,31 @@ class IRCodeGen(
}
}
private fun flattenLabelNames() {
val renameLabels = mutableListOf<Pair<PtNode, PtLabel>>()
fun flattenRecurse(node: PtNode) {
node.children.forEach {
if (it is PtLabel)
renameLabels += Pair(it.parent, it)
else
flattenRecurse(it)
}
}
flattenRecurse(program)
renameLabels.forEach { (parent, label) ->
val renamedLabel = PtLabel(label.scopedName.joinToString("."), label.position)
val idx = parent.children.indexOf(label)
parent.children.removeAt(idx)
parent.children.add(idx, renamedLabel)
}
}
private fun flattenNestedSubroutines() {
// this moves all nested subroutines up to the block scope.
// also changes the name to be the fully scoped one so it becomes unique at the top level.
// also changes the name to be the fully scoped one, so it becomes unique at the top level.
// also moves the start() entrypoint as first subroutine.
val flattenedSubs = mutableListOf<Pair<PtBlock, PtSub>>()
val flattenedAsmSubs = mutableListOf<Pair<PtBlock, PtAsmSub>>()
@@ -111,10 +134,9 @@ class IRCodeGen(
val removalsAsmSubs = mutableListOf<Pair<PtSub, PtAsmSub>>()
val renameSubs = mutableListOf<Pair<PtBlock, PtSub>>()
val renameAsmSubs = mutableListOf<Pair<PtBlock, PtAsmSub>>()
val renameLabels = mutableListOf<Pair<PtNode, PtLabel>>()
val entrypoint = program.entrypoint()
fun flattenNestedAsm(block: PtBlock, parentSub: PtSub, asmsub: PtAsmSub) {
fun flattenNestedAsmSub(block: PtBlock, parentSub: PtSub, asmsub: PtAsmSub) {
val flattened = PtAsmSub(asmsub.scopedName.joinToString("."),
asmsub.address,
asmsub.clobbers,
@@ -128,9 +150,9 @@ class IRCodeGen(
removalsAsmSubs += Pair(parentSub, asmsub)
}
fun flattenNested(block: PtBlock, parentSub: PtSub, sub: PtSub) {
sub.children.filterIsInstance<PtSub>().forEach { subsub->flattenNested(block, sub, subsub) }
sub.children.filterIsInstance<PtAsmSub>().forEach { asmsubsub->flattenNestedAsm(block, sub, asmsubsub) }
fun flattenNestedSub(block: PtBlock, parentSub: PtSub, sub: PtSub) {
sub.children.filterIsInstance<PtSub>().forEach { subsub->flattenNestedSub(block, sub, subsub) }
sub.children.filterIsInstance<PtAsmSub>().forEach { asmsubsub->flattenNestedAsmSub(block, sub, asmsubsub) }
val flattened = PtSub(sub.scopedName.joinToString("."),
sub.parameters,
sub.returntype,
@@ -145,24 +167,15 @@ class IRCodeGen(
block.children.forEach {
if(it is PtSub) {
// Only regular subroutines can have nested subroutines.
it.children.filterIsInstance<PtSub>().forEach { subsub->flattenNested(block, it, subsub)}
it.children.filterIsInstance<PtAsmSub>().forEach { asmsubsub->flattenNestedAsm(block, it, asmsubsub)}
it.children.filterIsInstance<PtLabel>().forEach { label->renameLabels.add(Pair(it, label))}
it.children.filterIsInstance<PtSub>().forEach { subsub->flattenNestedSub(block, it, subsub)}
it.children.filterIsInstance<PtAsmSub>().forEach { asmsubsub->flattenNestedAsmSub(block, it, asmsubsub)}
renameSubs += Pair(block, it)
}
if(it is PtAsmSub)
renameAsmSubs += Pair(block, it)
if(it is PtLabel)
renameLabels += Pair(block, it)
}
}
renameLabels.forEach { (parent, label) ->
val renamedLabel = PtLabel(label.scopedName.joinToString("."), label.position)
val idx = parent.children.indexOf(label)
parent.children.removeAt(idx)
parent.children.add(idx, renamedLabel)
}
removalsSubs.forEach { (parent, sub) -> parent.children.remove(sub) }
removalsAsmSubs.forEach { (parent, asmsub) -> parent.children.remove(asmsub) }
flattenedSubs.forEach { (block, sub) -> block.add(sub) }
@@ -902,15 +915,18 @@ class IRCodeGen(
}
}
val repeatLabel = createLabelName()
val skipRepeatLabel = createLabelName()
val code = IRCodeChunk(repeat.position)
val counterReg = vmRegisters.nextFree()
val vmDt = vmType(repeat.count.type)
code += expressionEval.translateExpression(repeat.count, counterReg, -1)
val repeatLabel = createLabelName()
code += IRCodeInstruction(Opcode.BZ, vmDt, reg1=counterReg, labelSymbol = skipRepeatLabel)
code += IRCodeLabel(repeatLabel)
code += translateNode(repeat.statements)
code += IRCodeInstruction(Opcode.DEC, vmDt, reg1=counterReg)
code += IRCodeInstruction(Opcode.BNZ, vmDt, reg1=counterReg, labelSymbol = repeatLabel)
code += IRCodeLabel(skipRepeatLabel)
return code
}
@@ -983,7 +999,8 @@ class IRCodeGen(
private fun translate(parameters: List<PtSubroutineParameter>) =
parameters.map {
val flattenedName = (it.definingSub()!!.scopedName + it.name)
symbolTable.flat.getValue(flattenedName) as StStaticVariable
val orig = symbolTable.flat.getValue(flattenedName) as StStaticVariable
IRSubroutine.IRParam(flattenedName.joinToString("."), orig.dt, orig)
}
private fun translate(alignment: PtBlock.BlockAlignment): IRBlock.BlockAlignment {
@@ -1023,7 +1040,7 @@ class IRCodeGen(
fun addMemorySlab(name: String, size: UInt, align: UInt, position: Position): String {
val scopedName = "prog8_memoryslab_$name"
symbolTable.add(StMemorySlab(scopedName, size, align, null, position))
symbolTable.add(StMemorySlab(scopedName, size, align, position))
return scopedName
}
}
@@ -1,6 +1,5 @@
import io.kotest.core.spec.style.FunSpec
import io.kotest.matchers.shouldBe
import prog8.code.SymbolTable
import prog8.code.core.*
import prog8.code.target.VMTarget
import prog8.codegen.intermediate.IRPeepholeOptimizer
@@ -15,7 +14,6 @@ class TestIRPeepholeOpt: FunSpec({
chunk += line
sub += chunk
block += sub
val st = SymbolTable()
val target = VMTarget()
val options = CompilationOptions(
OutputType.RAW,
@@ -27,7 +25,7 @@ class TestIRPeepholeOpt: FunSpec({
compTarget = target,
loadAddress = target.machine.PROGRAM_LOAD_ADDRESS
)
val prog = IRProgram("test", st, options, target)
val prog = IRProgram("test", IRSymbolTable(null), options, target)
prog.addBlock(block)
return prog
}
+1
View File
@@ -28,6 +28,7 @@ dependencies {
implementation project(':codeCore')
implementation project(':intermediate')
implementation project(':codeGenIntermediate')
implementation project(':virtualmachine')
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
// implementation "org.jetbrains.kotlin:kotlin-reflect"
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.16"
+1
View File
@@ -16,5 +16,6 @@
<orderEntry type="module" module-name="codeCore" />
<orderEntry type="module" module-name="intermediate" />
<orderEntry type="module" module-name="codeGenIntermediate" />
<orderEntry type="module" module-name="virtualmachine" />
</component>
</module>
@@ -4,6 +4,7 @@ import prog8.code.core.AssemblyError
import prog8.code.core.CompilationOptions
import prog8.code.core.IAssemblyProgram
import prog8.intermediate.*
import prog8.vm.Syscall
import java.io.BufferedWriter
import kotlin.io.path.bufferedWriter
import kotlin.io.path.div
@@ -19,7 +20,7 @@ internal class VmAssemblyProgram(override val name: String, private val irProgra
outfile.bufferedWriter().use { out ->
allocations.asVmMemory().forEach { (name, alloc) ->
out.write("var ${name.joinToString(".")} $alloc\n")
out.write("var ${name} $alloc\n")
}
out.write("------PROGRAM------\n")
@@ -84,7 +85,28 @@ private fun BufferedWriter.writeLine(line: IRCodeLine) {
write("; ${line.comment}\n")
}
is IRCodeInstruction -> {
write(line.ins.toString() + "\n")
if(line.ins.opcode==Opcode.SYSCALL) {
// convert IM Syscall to VM Syscall
val vmSyscall = when(line.ins.value!!) {
IMSyscall.SORT_UBYTE.ordinal -> Syscall.SORT_UBYTE
IMSyscall.SORT_BYTE.ordinal -> Syscall.SORT_BYTE
IMSyscall.SORT_UWORD.ordinal -> Syscall.SORT_UWORD
IMSyscall.SORT_WORD.ordinal -> Syscall.SORT_WORD
IMSyscall.ANY_BYTE.ordinal -> Syscall.ANY_BYTE
IMSyscall.ANY_WORD.ordinal -> Syscall.ANY_WORD
IMSyscall.ANY_FLOAT.ordinal -> Syscall.ANY_FLOAT
IMSyscall.ALL_BYTE.ordinal -> Syscall.ALL_BYTE
IMSyscall.ALL_WORD.ordinal -> Syscall.ALL_WORD
IMSyscall.ALL_FLOAT.ordinal -> Syscall.ALL_FLOAT
IMSyscall.REVERSE_BYTES.ordinal -> Syscall.REVERSE_BYTES
IMSyscall.REVERSE_WORDS.ordinal -> Syscall.REVERSE_WORDS
IMSyscall.REVERSE_FLOATS.ordinal -> Syscall.REVERSE_FLOATS
else -> throw IllegalArgumentException("invalid IM syscall number ${line.ins.value}")
}
val newIns = line.ins.copy(value = vmSyscall.ordinal)
write(newIns.toString() + "\n")
} else
write(line.ins.toString() + "\n")
}
is IRCodeInlineBinary -> {
write("!binary ")
@@ -9,7 +9,7 @@ import prog8.code.core.IErrorReporter
import prog8.codegen.intermediate.IRCodeGen
import prog8.intermediate.IRFileReader
import prog8.intermediate.IRFileWriter
import java.nio.file.Path
import kotlin.io.path.Path
class VmCodeGen(private val program: PtProgram,
private val symbolTable: SymbolTable,
@@ -33,7 +33,7 @@ class VmCodeGen(private val program: PtProgram,
companion object {
fun compileIR(listingFilename: String): IAssemblyProgram {
val irProgram = IRFileReader(Path.of(""), listingFilename).readFile()
val irProgram = IRFileReader(Path(""), listingFilename).readFile()
return VmAssemblyProgram(irProgram.name, irProgram)
}
}
@@ -1,12 +1,12 @@
package prog8.codegen.virtual
import prog8.code.SymbolTable
import prog8.code.core.*
import prog8.intermediate.IRSymbolTable
import prog8.intermediate.getTypeString
internal class VmVariableAllocator(val st: SymbolTable, val encoding: IStringEncoding, memsizer: IMemSizer) {
internal class VmVariableAllocator(val st: IRSymbolTable, val encoding: IStringEncoding, memsizer: IMemSizer) {
internal val allocations = mutableMapOf<List<String>, Int>()
internal val allocations = mutableMapOf<String, Int>()
private var freeMemoryStart: Int
val freeMem: Int
@@ -15,7 +15,7 @@ internal class VmVariableAllocator(val st: SymbolTable, val encoding: IStringEnc
init {
var nextLocation = 0
for (variable in st.allVariables) {
for (variable in st.allVariables()) {
val memsize =
when (variable.dt) {
DataType.STR -> variable.onetimeInitializationStringValue!!.first.length + 1 // include the zero byte
@@ -24,24 +24,24 @@ internal class VmVariableAllocator(val st: SymbolTable, val encoding: IStringEnc
else -> throw InternalCompilerException("weird dt")
}
allocations[variable.scopedName] = nextLocation
allocations[variable.name] = nextLocation
nextLocation += memsize
}
for(slab in st.allMemorySlabs) {
for(slab in st.allMemorySlabs()) {
// we ignore the alignment for the VM.
allocations[slab.scopedName] = nextLocation
allocations[slab.name] = nextLocation
nextLocation += slab.size.toInt()
}
freeMemoryStart = nextLocation
}
fun asVmMemory(): List<Pair<List<String>, String>> {
val mm = mutableListOf<Pair<List<String>, String>>()
fun asVmMemory(): List<Pair<String, String>> {
val mm = mutableListOf<Pair<String, String>>()
// normal variables
for (variable in st.allVariables) {
val location = allocations.getValue(variable.scopedName)
for (variable in st.allVariables()) {
val location = allocations.getValue(variable.name)
val value = when(variable.dt) {
DataType.FLOAT -> (variable.onetimeInitializationNumericValue ?: 0.0).toString()
in NumericDatatypes -> (variable.onetimeInitializationNumericValue ?: 0).toHex()
@@ -58,18 +58,23 @@ internal class VmVariableAllocator(val st: SymbolTable, val encoding: IStringEnc
}
in ArrayDatatypes -> {
if(variable.onetimeInitializationArrayValue!==null) {
variable.onetimeInitializationArrayValue!!.joinToString(",") { it.number!!.toHex() }
variable.onetimeInitializationArrayValue!!.joinToString(",") {
if(it.number!=null)
it.number!!.toHex()
else
"&${it.addressOf!!.joinToString(".")}"
}
} else {
(1..variable.length!!).joinToString(",") { "0" }
}
}
else -> throw InternalCompilerException("weird dt")
}
mm.add(Pair(variable.scopedName, "@$location ${getTypeString(variable)} $value"))
mm.add(Pair(variable.name, "@$location ${getTypeString(variable)} $value"))
}
// memory mapped variables
for (variable in st.allMemMappedVariables) {
for (variable in st.allMemMappedVariables()) {
val value = when(variable.dt) {
DataType.FLOAT -> "0.0"
in NumericDatatypes -> "0"
@@ -77,13 +82,13 @@ internal class VmVariableAllocator(val st: SymbolTable, val encoding: IStringEnc
in ArrayDatatypes -> (1..variable.length!!).joinToString(",") { "0" }
else -> throw InternalCompilerException("weird dt for mem mapped var")
}
mm.add(Pair(variable.scopedName, "@${variable.address} ${getTypeString(variable)} $value"))
mm.add(Pair(variable.name, "@${variable.address} ${getTypeString(variable)} $value"))
}
// memory slabs.
for(slab in st.allMemorySlabs) {
val address = allocations.getValue(slab.scopedName)
mm.add(Pair(slab.scopedName, "@$address ubyte[${slab.size}] 0"))
for(slab in st.allMemorySlabs()) {
val address = allocations.getValue(slab.name)
mm.add(Pair(slab.name, "@$address ubyte[${slab.size}] 0"))
}
return mm
+1
View File
@@ -33,6 +33,7 @@ dependencies {
implementation project(':codeGenCpu6502')
implementation project(':codeGenVirtual')
implementation project(':codeGenExperimental')
implementation project(':virtualmachine')
implementation 'org.antlr:antlr4-runtime:4.10.1'
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
// implementation "org.jetbrains.kotlin:kotlin-reflect"
+1
View File
@@ -22,5 +22,6 @@
<orderEntry type="module" module-name="codeGenCpu6502" />
<orderEntry type="module" module-name="codeGenExperimental" />
<orderEntry type="module" module-name="codeGenVirtual" />
<orderEntry type="module" module-name="virtualmachine" />
</component>
</module>
+23
View File
@@ -224,6 +224,29 @@ _done rts
}}
}
asmsub lowerchar(ubyte char @A) -> ubyte @A {
%asm {{
and #$7f
cmp #97
bcc +
cmp #123
bcs +
and #%11011111
+ rts
}}
}
asmsub upperchar(ubyte char @A) -> ubyte @A {
%asm {{
cmp #65
bcc +
cmp #91
bcs +
ora #%00100000
+ rts
}}
}
sub startswith(str st, str prefix) -> bool {
ubyte prefix_len = length(prefix)
ubyte str_len = length(st)
+12
View File
@@ -114,6 +114,18 @@ string {
}
}
sub lowerchar(ubyte char) -> ubyte {
if char >= 'A' and char <= 'Z'
char |= %00100000
return char
}
sub upperchar(ubyte char) -> ubyte {
if char >= 'a' and char <= 'z'
char &= %11011111
return char
}
sub startswith(str st, str prefix) -> bool {
ubyte prefix_len = length(prefix)
ubyte str_len = length(st)
+15
View File
@@ -120,4 +120,19 @@ sub input_chars (uword buffer) -> ubyte {
}}
}
sub plot (ubyte col, ubyte row) {
; use ANSI escape sequence to position the cursor
txt.chrout(27)
txt.chrout('[')
txt.print_ub(row)
txt.chrout(';')
txt.print_ub(col)
txt.chrout('H')
}
sub setchr (ubyte col, ubyte row, ubyte char) {
plot(col, row)
txt.chrout(char)
}
}
+1 -1
View File
@@ -1 +1 @@
8.5
8.6.1
+2 -1
View File
@@ -13,6 +13,7 @@ import java.io.File
import java.nio.file.*
import java.time.LocalDateTime
import kotlin.system.exitProcess
import kotlin.io.path.Path
fun main(args: Array<String>) {
@@ -142,7 +143,7 @@ private fun compileMain(args: Array<String>): Boolean {
for (importedFile in allImportedFiles) {
print(" ")
println(importedFile)
val watchDir = importedFile.parent ?: Path.of("")
val watchDir = importedFile.parent ?: Path("")
watchDir.register(watchservice, StandardWatchEventKinds.ENTRY_MODIFY)
}
println("[${LocalDateTime.now().withNano(0)}] Waiting for file changes.")
@@ -320,9 +320,12 @@ class IntermediateAstMaker(val program: Program) {
private fun transformSub(srcSub: Subroutine): PtSub {
val (vardecls, statements) = srcSub.statements.partition { it is VarDecl }
var returntype = srcSub.returntypes.singleOrNull()
if(returntype==DataType.STR)
returntype=DataType.UWORD // if a sub returns 'str', replace with uword. Intermediate AST and I.R. don't contain 'str' datatype anymore.
val sub = PtSub(srcSub.name,
srcSub.parameters.map { PtSubroutineParameter(it.name, it.type, it.position) },
srcSub.returntypes.singleOrNull(),
returntype,
srcSub.inline,
srcSub.position)
sub.parameters.forEach { it.parent=sub }
+30 -8
View File
@@ -3,18 +3,16 @@ package prog8tests
import io.kotest.core.spec.style.FunSpec
import io.kotest.matchers.shouldNotBe
import prog8.code.core.ICompilationTarget
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
import prog8tests.helpers.assumeDirectory
import prog8tests.helpers.cartesianProduct
import prog8tests.helpers.outputDir
import prog8tests.helpers.workingDir
import prog8tests.helpers.*
import prog8tests.helpers.compileText
import java.nio.file.Path
import kotlin.io.path.absolute
import kotlin.io.path.exists
import kotlin.io.path.readText
/**
@@ -47,8 +45,11 @@ private fun compileTheThing(filepath: Path, optimize: Boolean, target: ICompilat
private fun prepareTestFiles(source: String, optimize: Boolean, target: ICompilationTarget): Pair<String, Path> {
val searchIn = mutableListOf(examplesDir)
if (target is Cx16Target) {
searchIn.add(0, assumeDirectory(examplesDir, "cx16"))
when (target) {
is Cx16Target -> searchIn.add(0, assumeDirectory(examplesDir, "cx16"))
is VMTarget -> searchIn.add(0, assumeDirectory(examplesDir, "vm"))
is C128Target -> searchIn.add(0, assumeDirectory(examplesDir, "c128"))
is AtariTarget -> searchIn.add(0, assumeDirectory(examplesDir, "atari"))
}
val filepath = searchIn.asSequence()
.map { it.resolve("$source.p8") }
@@ -168,3 +169,24 @@ class TestCompilerOnExamplesBothC64andCx16: FunSpec({
}
}
})
class TestCompilerOnExamplesVirtual: FunSpec({
val onlyVirtual = listOf(
"bouncegfx",
"bsieve",
"pixelshader",
"sincos",
"textelite"
)
onlyVirtual.forEach {
val target = VMTarget()
val (displayName, filepath) = prepareTestFiles(it, false, target)
test(displayName) {
val src = filepath.readText()
compileText(target, false, src, writeAssembly = true, keepIR=false) shouldNotBe null
compileText(target, false, src, writeAssembly = true, keepIR=true) shouldNotBe null
}
}
})
+18
View File
@@ -0,0 +1,18 @@
package prog8tests
import io.kotest.core.spec.style.FunSpec
import prog8.code.target.VMTarget
import kotlin.io.path.deleteExisting
import kotlin.io.path.writeText
class TestLaunchEmu: FunSpec({
test("test launch virtualmachine via target") {
val target = VMTarget()
val tmpfile = kotlin.io.path.createTempFile(suffix=".p8virt")
tmpfile.writeText(";comment\n------PROGRAM------\n;comment\n")
target.machine.launchEmulator(0, tmpfile)
tmpfile.deleteExisting()
}
})
+3 -4
View File
@@ -14,7 +14,6 @@ import prog8.ast.statements.VarDecl
import prog8.code.core.DataType
import prog8.code.core.Position
import prog8.code.target.C64Target
import prog8.code.target.VMTarget
import prog8tests.helpers.ErrorReporterForTests
import prog8tests.helpers.compileText
@@ -31,7 +30,7 @@ class TestTypecasts: FunSpec({
}
}"""
val errors = ErrorReporterForTests()
val result = compileText(VMTarget(), false, text, writeAssembly = false, errors=errors)
val result = compileText(C64Target(), false, text, writeAssembly = false, errors=errors)
result shouldBe null
errors.errors.size shouldBe 1
errors.errors[0] shouldContain "type mismatch, was: FLOAT expected one of: [UBYTE, BYTE, UWORD, WORD]"
@@ -627,9 +626,9 @@ main {
ubyte @shared wordNr2 = (interlaced >= ${'$'}33) + (interlaced >= ${'$'}66) + (interlaced >= ${'$'}99) + (interlaced >= ${'$'}CC)
}
}"""
val result = compileText(VMTarget(), false, text, writeAssembly = true)!!
val result = compileText(C64Target(), false, text, writeAssembly = true)!!
val stmts = result.program.entrypoint.statements
stmts.size shouldBe 14
stmts.size shouldBeGreaterThan 10
}
test("word to byte casts") {
+5 -2
View File
@@ -22,6 +22,7 @@ internal fun compileFile(
outputDir: Path = prog8tests.helpers.outputDir,
errors: IErrorReporter? = null,
writeAssembly: Boolean = true,
keepIR: Boolean = true,
optFloatExpr: Boolean = true
) : CompilationResult? {
val filepath = fileDir.resolve(fileName)
@@ -36,7 +37,7 @@ internal fun compileFile(
quietAssembler = true,
asmListfile = false,
experimentalCodegen = false,
keepIR = false,
keepIR = keepIR,
platform.name,
evalStackBaseAddress = null,
symbolDefs = emptyMap(),
@@ -57,12 +58,14 @@ internal fun compileText(
sourceText: String,
errors: IErrorReporter? = null,
writeAssembly: Boolean = true,
keepIR: Boolean = true,
optFloatExpr: Boolean = true
) : CompilationResult? {
val filePath = outputDir.resolve("on_the_fly_test_" + sourceText.hashCode().toUInt().toString(16) + ".p8")
// we don't assumeNotExists(filePath) - should be ok to just overwrite it
filePath.toFile().writeText(sourceText)
return compileFile(platform, optimize, filePath.parent, filePath.name, errors=errors, writeAssembly=writeAssembly, optFloatExpr = optFloatExpr)
return compileFile(platform, optimize, filePath.parent, filePath.name,
errors=errors, writeAssembly=writeAssembly, optFloatExpr = optFloatExpr, keepIR=keepIR)
}
+159
View File
@@ -0,0 +1,159 @@
package prog8tests.vm
import io.kotest.core.spec.style.FunSpec
import io.kotest.matchers.shouldBe
import io.kotest.matchers.shouldNotBe
import prog8.code.target.Cx16Target
import prog8.code.target.VMTarget
import prog8.vm.VmRunner
import prog8tests.helpers.compileText
import kotlin.io.path.readText
class TestCompilerVirtual: FunSpec({
test("compile virtual: any all sort reverse builtin funcs") {
val src = """
main {
sub start() {
uword[] words = [1111,2222,0,4444,3333]
ubyte result = all(words)
result++
result = any(words)
result++
sort(words)
reverse(words)
}
}"""
val target = VMTarget()
val result = compileText(target, true, src, writeAssembly = true, keepIR=true)!!
val virtfile = result.compilationOptions.outputDir.resolve(result.program.name + ".p8virt")
VmRunner().runProgram(virtfile.readText())
val result2 = compileText(target, true, src, writeAssembly = true, keepIR=false)!!
val virtfile2 = result2.compilationOptions.outputDir.resolve(result2.program.name + ".p8virt")
VmRunner().runProgram(virtfile2.readText())
}
test("compile virtual: array with pointers") {
val src = """
main {
sub start() {
str localstr = "hello"
ubyte[] otherarray = [1,2,3]
uword[] words = [1111,2222,"three",&localstr,&otherarray]
uword @shared zz = &words
ubyte result = 2222 in words
zz = words[2]
zz++
zz = words[3]
}
}"""
val othertarget = Cx16Target()
compileText(othertarget, true, src, writeAssembly = true, keepIR=true) shouldNotBe null
val target = VMTarget()
val result = compileText(target, true, src, writeAssembly = true, keepIR=true)!!
val virtfile = result.compilationOptions.outputDir.resolve(result.program.name + ".p8virt")
VmRunner().runProgram(virtfile.readText())
val result2 = compileText(target, true, src, writeAssembly = true, keepIR=false)!!
val virtfile2 = result2.compilationOptions.outputDir.resolve(result2.program.name + ".p8virt")
VmRunner().runProgram(virtfile2.readText())
}
test("compile virtual: str args and return type") {
val src = """
main {
sub start() {
sub testsub(str s1) -> str {
return "result"
}
uword result = testsub("arg")
}
}"""
val target = VMTarget()
val result = compileText(target, true, src, writeAssembly = true, keepIR=true)!!
val virtfile = result.compilationOptions.outputDir.resolve(result.program.name + ".p8virt")
VmRunner().runProgram(virtfile.readText())
val result2 = compileText(target, true, src, writeAssembly = true, keepIR=false)!!
val virtfile2 = result2.compilationOptions.outputDir.resolve(result2.program.name + ".p8virt")
VmRunner().runProgram(virtfile2.readText())
}
test("compile virtual: nested labels") {
val src = """
main {
sub start() {
uword i
uword k
while k <= 10 {
k++
}
mylabel_outside:
for i in 0 to 10 {
mylabel_inside:
if i==100 {
goto mylabel_outside
goto mylabel_inside
}
while k <= 10 {
k++
}
do {
k--
} until k==0
for k in 0 to 5 {
i++
}
repeat 10 {
k++
}
}
}
}"""
val target = VMTarget()
val result = compileText(target, true, src, writeAssembly = true, keepIR=true)!!
val virtfile = result.compilationOptions.outputDir.resolve(result.program.name + ".p8virt")
VmRunner().runProgram(virtfile.readText())
val result2 = compileText(target, true, src, writeAssembly = true, keepIR=false)!!
val virtfile2 = result2.compilationOptions.outputDir.resolve(result2.program.name + ".p8virt")
VmRunner().runProgram(virtfile2.readText())
}
test("case sensitive symbols") {
val src = """
%zeropage basicsafe
main {
sub start() {
ubyte bytevar = 11 ; var at 0
ubyte byteVAR = 22 ; var at 1
ubyte ByteVar = 33 ; var at 2
ubyte @shared total = bytevar+byteVAR+ByteVar ; var at 3
goto skipLABEL
SkipLabel:
return
skipLABEL:
bytevar = 42
}
}"""
val othertarget = Cx16Target()
compileText(othertarget, true, src, writeAssembly = true, keepIR=true) shouldNotBe null
val target = VMTarget()
val result = compileText(target, true, src, writeAssembly = true, keepIR=true)!!
val virtfile = result.compilationOptions.outputDir.resolve(result.program.name + ".p8virt")
VmRunner().runAndTestProgram(virtfile.readText()) { vm ->
vm.memory.getUB(0) shouldBe 42u
vm.memory.getUB(3) shouldBe 66u
}
val result2 = compileText(target, true, src, writeAssembly = true, keepIR=false)!!
val virtfile2 = result2.compilationOptions.outputDir.resolve(result2.program.name + ".p8virt")
VmRunner().runAndTestProgram(virtfile2.readText()) { vm ->
vm.memory.getUB(0) shouldBe 42u
vm.memory.getUB(3) shouldBe 66u
}
}
})
@@ -8,7 +8,7 @@ import prog8.ast.expressions.*
import prog8.ast.statements.*
import prog8.code.core.*
import prog8.parser.Prog8ANTLRParser
import java.nio.file.Path
import kotlin.io.path.Path
import kotlin.io.path.isRegularFile
@@ -20,7 +20,7 @@ private data class NumericLiteralNode(val number: Double, val datatype: DataType
private fun ParserRuleContext.toPosition() : Position {
val pathString = start.inputStream.sourceName
val filename = if(SourceCode.isRegularFilesystemPath(pathString)) {
val path = Path.of(pathString)
val path = Path(pathString)
if(path.isRegularFile()) {
SourceCode.relative(path).toString()
} else {
+6
View File
@@ -191,6 +191,12 @@ Provides string manipulation routines.
``upper(string)``
Uppercases the petscii-string in place.
``lowerchar(char)``
Returns lowercased character.
``upperchar(char)``
Returns uppercased character.
``startswith(string, prefix) -> bool``
Returns true if string starts with prefix, otherwise false
+3 -3
View File
@@ -17,7 +17,7 @@ Future Things and Ideas
^^^^^^^^^^^^^^^^^^^^^^^
Compiler:
- vm/ir: all(), any(), reverse() and sort() still depend on a VM Syscall. Get rid of this. (maybe use a IR 'builtin' function?)
- vm: get rid of p8virt format and Assembler, run p8ir directly
- vm/ir: put variables and arrays in BSS section (unless -noreinit is specified)
- vm: Jumps go to a code block rather than a specific address(label) -> also helps future dead code elimination?
- vm: the above means that every label introduces a new code block. This eliminates the use of actual labels altogether.
@@ -51,8 +51,8 @@ Libraries:
- fix the problems in atari target, and flesh out its libraries.
- c64: make the graphics.BITMAP_ADDRESS configurable (VIC banking)
- optimize several inner loops in gfx2 even further?
- add modes 3 and perhaps even 2 to gfx2 (16 color and 4 color)?
- add a flood fill routine to gfx2?
- add modes 3 and perhaps even 2 to gfx2 (lores 16 color and 4 color)?
- add a flood fill (span fill/scanline fill) routine to gfx2?
Expressions:
+60 -106
View File
@@ -1,115 +1,69 @@
%import textio
%zeropage basicsafe
main {
sub start() {
%asm {{
loadcpu.b r1,_a
loadcpu.b r1,_x
loadcpu.b r1,_y
loadcpu.w r1,_ax
loadcpu.w r1,_ay
loadcpu.w r1,_xy
loadcpu.b r1,_pc
loadcpu.b r1,_pn
loadcpu.b r1,_pz
loadcpu.w r1,_r0
loadcpu.w r1,_r15
ubyte v1 = 1
uword v2 = 1
storezcpu.b _a
storezcpu.b _x
storezcpu.b _y
storezcpu.w _ax
storezcpu.w _ay
storezcpu.w _xy
storezcpu.b _pc
storezcpu.b _pn
storezcpu.b _pz
storezcpu.b _r0
storezcpu.w _r15
}}
ubyte counterb
uword counter
repeat v1-1 {
txt.print("!")
}
repeat v2-1 {
txt.print("?")
}
for counterb in 0 to v1 {
txt.print("y1")
}
for counter in 0 to v2 {
txt.print("y2")
}
repeat v1 {
txt.print("ok1")
}
repeat v2 {
txt.print("ok2")
}
repeat v1-1 {
txt.print("!")
}
repeat v2-1 {
txt.print("?")
}
while v1-1 {
txt.print("%")
}
while v2-1 {
txt.print("*")
}
for counterb in 0 to v1-1 {
txt.print("@")
}
for counter in 0 to v2-1 {
txt.print("y#")
}
repeat 0 {
txt.print("zero1")
}
repeat $0000 {
txt.print("zero2")
}
}
}
; uword @shared global1 = 1234
; str @shared globalstring = "irmen"
;
; %asm {{
; nop
; nop
; return
; }}
;
; romsub $ee33 = myromsub(ubyte arg1 @A) clobbers() -> ubyte @Y
; romsub $ee44 = myromsubmulti(ubyte arg1 @A) clobbers() -> ubyte @Y, ubyte @X, ubyte @Pc
;
; asmsub testasmsub(ubyte arg1 @A) clobbers(Y) -> uword @AX {
; %asm {{
; nop
; return
; }}
; }
;
; sub start() {
; void myromsubmulti(44)
; global1 = myromsub(44)
; sys.wait(1)
;
; cx16.r0 = 11111
; cx16.r0L = 22
; cx16.r0H = 33
;
; %asm {{
; nop
; jump main.start.a_label
; }}
;a_label:
;
; %asmbinary "LICENSE", 200, 513
;
;; TODO add proper memory SLAB support to IR+VM
; uword @shared slab1 = memory("slab 1", 2000, 0)
; uword @shared slab2 = memory("slab 1", 2000, 0)
; uword @shared slab3 = memory("other # slab", 2000, 64)
;
; &uword mapped = $c000
; &ubyte[20] mappedarray = $c100
;
; uword @shared zz
; zz = slab1+slab2+slab3
;
; uword @shared @zp qq = zz
; uword @shared @zp qq2 = &zz
;
; str @shared namestring = "irmen"
; uword[] @shared wordarray1 = [1111,2222,3333,4444]
; uword[4] @shared wordarray2 = 12345
; uword[4] @shared wordzeroarray
;
; qq=4242 ; TODO should generate symbol not allocated address????
; mapped = 99 ; TODO wrong VMASM code generated... should generate mapped memory address?????
; mappedarray[1]=99 ; TODO check code????
;
; qq=global1
; qq=other.global2
; nested(11,22)
; main.start.nested.nested2()
;
; sub nested(ubyte a1, ubyte a2) {
; qq++
; txt.print("zzz")
; nested2()
;
; sub nested2() {
; txt.print("zzz2")
; qq++
; }
; }
; }
;}
;
;other {
;
; uword global2 = 9999
;
;}
+1
View File
@@ -22,6 +22,7 @@ main {
txt.lowercase()
txt.print("\u000c\n --- TextElite v1.2 ---\n")
planet.set_seed(0, 0)
galaxy.travel_to(1, numforLave)
market.init(0) ; Lave's market is seeded with 0
ship.init()
+1 -3
View File
@@ -1,6 +1,3 @@
%import textio
%zeropage basicsafe
; NOTE: meant to test to virtual machine output target (use -target virtual)
main {
@@ -24,6 +21,7 @@ main {
repeat {
fade()
plot_particles()
sys.wait(1)
sys.waitvsync()
}
+40
View File
@@ -0,0 +1,40 @@
%import textio
; The "Byte Sieve" test. https://en.wikipedia.org/wiki/Byte_Sieve
main {
sub start() {
const ubyte ITERS = 10
uword count
uword i
uword prime
uword k
const uword SIZEPL = 8191
uword @zp flags_ptr = memory("flags", SIZEPL, $100)
txt.print("calculating...\n")
repeat ITERS {
sys.memset(flags_ptr, SIZEPL, 1)
count = 0
for i in 0 to SIZEPL-1 {
if @(flags_ptr+i) {
prime = i + i + 3
k = i + prime
while k <= SIZEPL-1 {
@(flags_ptr + k) = false
k += prime
}
txt.print_uw(prime)
txt.nl()
count++
}
}
}
txt.nl()
txt.print_uw(count)
txt.print(" primes\n")
}
}
+3 -3
View File
@@ -1,6 +1,3 @@
%import textio
%zeropage basicsafe
; NOTE: meant to test to virtual machine output target (use -target virtual)
main {
@@ -23,6 +20,9 @@ main {
yy++
}
shifter+=4
sys.wait(1)
sys.waitvsync()
}
}
}
+1 -3
View File
@@ -1,5 +1,4 @@
%import math
%import textio
; Draw sine and cosine graphs. The sine and cosine functions are table lookups
; where the tables are generated by 64tass list functions.
@@ -27,8 +26,7 @@ main {
sys.gfx_clear(0)
circles()
repeat {
}
sys.wait(120)
}
sub sincos255() {
+949
View File
@@ -0,0 +1,949 @@
%import textio
%import conv
%import string
; Prog8 adaptation of the Text-Elite galaxy system trading simulation engine.
; Original C-version obtained from: http://www.elitehomepage.org/text/index.htm
main {
const ubyte numforLave = 7 ; Lave is 7th generated planet in galaxy one
const ubyte numforZaonce = 129
const ubyte numforDiso = 147
const ubyte numforRiedquat = 46
sub start() {
txt.lowercase()
txt.print("\n--- TextElite v1.2 ---\n")
txt.print("VirtualMachine edition: no disk saving, bad market table layout!\n")
planet.set_seed(0, 0)
galaxy.travel_to(1, numforLave)
market.init(0) ; Lave's market is seeded with 0
ship.init()
planet.display(false, 0)
repeat {
str input = "????????"
txt.print("\nCash: ")
util.print_10s(ship.cash)
txt.print("\nCommand (?=help): ")
ubyte num_chars = txt.input_chars(input)
txt.nl()
if num_chars {
when input[0] {
'?' -> {
txt.print("\nCommands are:\n"+
"buy jump info map quit\n"+
"sell teleport market cash\n"+
"fuel galhyp local hold\n")
}
'q' -> break
'b' -> trader.do_buy()
's' -> trader.do_sell()
'f' -> trader.do_fuel()
'j' -> trader.do_jump()
't' -> trader.do_teleport()
'g' -> trader.do_next_galaxy()
'i' -> trader.do_info()
'm' -> {
if input[1]=='a' and input[2]=='p'
trader.do_map()
else
trader.do_show_market()
}
'l' -> trader.do_local()
'c' -> trader.do_cash()
'h' -> trader.do_hold()
}
}
}
}
}
trader {
str input = "??????????"
ubyte num_chars
sub do_jump() {
txt.print("\nJump to what system? ")
jump_to_system()
}
sub do_teleport() {
txt.print("\nCheat! Teleport to what system? ")
ubyte fuel = ship.fuel
ship.fuel = 255
jump_to_system()
ship.fuel = fuel
}
sub jump_to_system() {
void txt.input_chars(input)
ubyte current_planet = planet.number
ubyte x = planet.x
ubyte y = planet.y
if galaxy.search_closest_planet(input) {
ubyte distance = planet.distance(x, y)
if distance <= ship.fuel {
galaxy.init_market_for_planet()
ship.fuel -= distance
txt.print("\n\nHyperspace jump! Arrived at:\n")
planet.display(true,0 )
return
}
txt.print("\nInsufficient fuel\n")
} else {
txt.print(" Not found!\n")
}
galaxy.travel_to(galaxy.number, current_planet)
}
sub do_buy() {
txt.print("\nBuy what commodity? ")
str commodity = "???????????????"
void txt.input_chars(commodity)
ubyte ci = market.match(commodity)
if ci & 128 {
txt.print("Unknown\n")
} else {
txt.print("\nHow much? ")
void txt.input_chars(input)
ubyte amount = conv.str2ubyte(input)
if market.current_quantity[ci] < amount {
txt.print(" Insufficient supply!\n")
} else {
uword price = market.current_price[ci] * amount
txt.print(" Total price: ")
util.print_10s(price)
if price > ship.cash {
txt.print(" Not enough cash!\n")
} else {
ship.cash -= price
ship.cargohold[ci] += amount
market.current_quantity[ci] -= amount
}
}
}
}
sub do_sell() {
txt.print("\nSell what commodity? ")
str commodity = "???????????????"
void txt.input_chars(commodity)
ubyte ci = market.match(commodity)
if ci & 128 {
txt.print("Unknown\n")
} else {
txt.print("\nHow much? ")
void txt.input_chars(input)
ubyte amount = conv.str2ubyte(input)
if ship.cargohold[ci] < amount {
txt.print(" Insufficient supply!\n")
} else {
uword price = market.current_price[ci] * amount
txt.print(" Total price: ")
util.print_10s(price)
ship.cash += price
ship.cargohold[ci] -= amount
market.current_quantity[ci] += amount
}
}
}
sub do_fuel() {
txt.print("\nBuy fuel. Amount? ")
void txt.input_chars(input)
ubyte buy_fuel = 10*conv.str2ubyte(input)
ubyte max_fuel = ship.Max_fuel - ship.fuel
if buy_fuel > max_fuel
buy_fuel = max_fuel
uword price = buy_fuel as uword * ship.Fuel_cost
if price > ship.cash {
txt.print("Not enough cash!\n")
} else {
ship.cash -= price
ship.fuel += buy_fuel
}
}
sub do_cash() {
txt.print("\nCheat! Set cash amount: ")
void txt.input_chars(input)
ship.cash = conv.str2uword(input)
}
sub do_hold() {
txt.print("\nCheat! Set cargohold size: ")
void txt.input_chars(input)
ship.Max_cargo = conv.str2ubyte(input)
}
sub do_next_galaxy() {
txt.print("\n>>>>>>>> Galaxy Hyperjump!\n")
galaxy.travel_to(galaxy.number+1, planet.number)
planet.display(false, 0)
}
sub do_info() {
txt.print("\nSystem name (empty=current): ")
num_chars = txt.input_chars(input)
if num_chars {
ubyte current_planet = planet.number
ubyte x = planet.x
ubyte y = planet.y
if galaxy.search_closest_planet(input) {
ubyte distance = planet.distance(x, y)
planet.display(false, distance)
} else {
txt.print(" Not found!")
}
galaxy.travel_to(galaxy.number, current_planet)
} else {
planet.display(false, 0)
}
}
sub do_local() {
galaxy.local_area()
}
sub do_map() {
txt.print("\n(l)ocal or (g)alaxy starmap? ")
num_chars = txt.input_chars(input)
if num_chars {
galaxy.starmap(input[0]=='l')
}
}
sub do_show_market() {
market.display()
txt.print("\nFuel: ")
util.print_10s(ship.fuel)
txt.print(" Cargohold space: ")
txt.print_ub(ship.cargo_free())
txt.print("t\n")
}
}
ship {
const ubyte Max_fuel = 70
const ubyte Fuel_cost = 2
ubyte Max_cargo = 20
ubyte fuel = Max_fuel
uword cash = 1000 ; actually has to be 4 bytes for the ultra rich....
ubyte[17] cargohold = 0
sub init() {
sys.memset(cargohold, len(cargohold), 0)
}
sub cargo_free() -> ubyte {
ubyte ci
ubyte total = 0
for ci in 0 to len(cargohold)-1 {
if market.units[ci]==0 ; tonnes only
total += cargohold[ci]
}
return Max_cargo - total
}
}
market {
ubyte[17] baseprices = [$13, $14, $41, $28, $53, $C4, $EB, $9A, $75, $4E, $7C, $B0, $20, $61, $AB, $2D, $35]
byte[17] gradients = [-$02, -$01, -$03, -$05, -$05, $08, $1D, $0E, $06, $01, $0d, -$09, -$01, -$01, -$02, -$01, $0F]
ubyte[17] basequants = [$06, $0A, $02, $E2, $FB, $36, $08, $38, $28, $11, $1D, $DC, $35, $42, $37, $FA, $C0]
ubyte[17] maskbytes = [$01, $03, $07, $1F, $0F, $03, $78, $03, $07, $1F, $07, $3F, $03, $07, $1F, $0F, $07]
ubyte[17] units = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 0]
str[17] names = ["Food", "Textiles", "Radioactives", "Slaves", "Liquor/Wines", "Luxuries", "Narcotics", "Computers",
"Machinery", "Alloys", "Firearms", "Furs", "Minerals", "Gold", "Platinum", "Gem-Stones", "Alien Items"]
ubyte[17] current_quantity = 0
uword[17] current_price = 0
sub init(ubyte fluct) {
; Prices and availabilities are influenced by the planet's economy type
; (0-7) and a random "fluctuation" byte that was kept within the saved
; commander position to keep the market prices constant over gamesaves.
; Availabilities must be saved with the game since the player alters them
; by buying (and selling(?))
;
; Almost all operations are one byte only and overflow "errors" are
; extremely frequent and exploited.
;
; Trade Item prices are held internally in a single byte=true value/4.
; The decimal point in prices is introduced only when printing them.
; Internally, all prices are integers.
; The player's cash is held in four bytes.
ubyte ci
for ci in 0 to len(names)-1 {
word product
byte changing
product = planet.economy as word * gradients[ci]
changing = fluct & maskbytes[ci] as byte
ubyte q = (basequants[ci] as word + changing - product) as ubyte
if q & $80
q = 0 ; clip to positive 8-bit
current_quantity[ci] = q & $3f
q = (baseprices[ci] + changing + product) as ubyte
current_price[ci] = q * $0004
}
current_quantity[16] = 0 ; force nonavailability of Alien Items
}
sub display() {
ubyte ci
txt.nl()
planet.print_name_uppercase()
txt.print(" trade market:\n COMMODITY / PRICE / AVAIL / IN HOLD\n")
for ci in 0 to len(names)-1 {
util.print_right(13, names[ci])
txt.print(" ")
util.print_10s(current_price[ci])
txt.print(" ")
txt.print_ub(current_quantity[ci])
txt.chrout(' ')
when units[ci] {
0 -> txt.chrout('t')
1 -> txt.print("kg")
2 -> txt.chrout('g')
}
txt.print(" ")
txt.print_ub(ship.cargohold[ci])
txt.nl()
}
}
sub match(uword nameptr) -> ubyte {
ubyte ci
for ci in 0 to len(names)-1 {
if util.prefix_matches(nameptr, names[ci])
return ci
}
return 255
}
}
galaxy {
const uword GALSIZE = 256
const uword base0 = $5A4A ; seeds for the first galaxy
const uword base1 = $0248
const uword base2 = $B753
str pn_pairs = "..lexegezacebisousesarmaindirea.eratenberalavetiedorquanteisrion"
ubyte number
uword[3] seed
sub init(ubyte galaxynum) {
number = 1
planet.number = 255
seed[0] = base0
seed[1] = base1
seed[2] = base2
repeat galaxynum-1 {
nextgalaxy()
}
}
sub nextgalaxy() {
seed[0] = twist(seed[0])
seed[1] = twist(seed[1])
seed[2] = twist(seed[2])
number++
if number==9
number = 1
}
sub travel_to(ubyte galaxynum, ubyte system) {
init(galaxynum)
generate_next_planet() ; always at least planet 0 (separate to avoid repeat ubyte overflow)
repeat system {
generate_next_planet()
}
planet.name = make_current_planet_name()
init_market_for_planet()
}
sub init_market_for_planet() {
market.init(lsb(seed[0])+msb(seed[2]))
}
sub search_closest_planet(uword nameptr) -> ubyte {
ubyte x = planet.x
ubyte y = planet.y
ubyte current_planet_num = planet.number
init(number)
ubyte found = false
ubyte current_closest_pi
ubyte current_distance = 127
ubyte pi
for pi in 0 to 255 {
generate_next_planet()
planet.name = make_current_planet_name()
if util.prefix_matches(nameptr, planet.name) {
ubyte distance = planet.distance(x, y)
if distance < current_distance {
current_distance = distance
current_closest_pi = pi
found = true
}
}
}
if found
travel_to(number, current_closest_pi)
else
travel_to(number, current_planet_num)
return found
}
sub local_area() {
ubyte current_planet = planet.number
ubyte px = planet.x
ubyte py = planet.y
ubyte pn = 0
init(number)
txt.print("\nGalaxy #")
txt.print_ub(number)
txt.print(" - systems in vicinity:\n")
do {
generate_next_planet()
ubyte distance = planet.distance(px, py)
if distance <= ship.Max_fuel {
if distance <= ship.fuel
txt.chrout('*')
else
txt.chrout('-')
txt.spc()
planet.name = make_current_planet_name()
planet.display(true, distance)
}
pn++
} until pn==0
travel_to(number, current_planet)
}
sub starmap(bool local) {
ubyte current_planet = planet.number
ubyte px = planet.x
ubyte py = planet.y
uword current_name = &planet.name
ubyte pn = 0
uword scaling = 2
if local
scaling = 1
init(number)
txt.clear_screen()
txt.print("Galaxy #")
txt.print_ub(number)
if local
txt.print(" - local systems")
else
txt.print(" - galaxy")
txt.print(" starmap:\n")
ubyte max_distance = 255
if local
max_distance = ship.Max_fuel
do {
generate_next_planet()
ubyte distance = planet.distance(px, py)
if distance <= max_distance {
planet.name = make_current_planet_name()
planet.name[0] = string.upperchar(planet.name[0])
uword tx = planet.x
uword ty = planet.y
if local {
tx = tx + 24 - px
ty = ty + 24 - py
}
tx /= scaling
ty /= scaling*4
ubyte sx = lsb(tx)
ubyte sy = lsb(ty)
ubyte char = '*'
if planet.number==current_planet
char = '%'
if local or planet.number==current_planet {
txt.plot(2+sx-2, 2+sy+1)
txt.print(current_name)
if distance {
txt.plot(2+sx-2, 2+sy+2)
util.print_10s(distance)
txt.print(" LY")
}
}
txt.setchr(2+sx, 2+sy, char)
}
pn++
} until pn==0
if local
txt.plot(0, 20)
else
txt.plot(0, 33)
txt.nl()
travel_to(number, current_planet)
}
ubyte pn_pair1
ubyte pn_pair2
ubyte pn_pair3
ubyte pn_pair4
ubyte longname
sub generate_next_planet() {
determine_planet_properties()
longname = lsb(seed[0]) & 64
; Always four iterations of random number
pn_pair1 = (msb(seed[2]) & 31) * 2
tweakseed()
pn_pair2 = (msb(seed[2]) & 31) * 2
tweakseed()
pn_pair3 = (msb(seed[2]) & 31) * 2
tweakseed()
pn_pair4 = (msb(seed[2]) & 31) * 2
tweakseed()
}
sub make_current_planet_name() -> str {
ubyte ni = 0
str name = " " ; max 8
if pn_pairs[pn_pair1] != '.' {
name[ni] = pn_pairs[pn_pair1]
ni++
}
if pn_pairs[pn_pair1+1] != '.' {
name[ni] = pn_pairs[pn_pair1+1]
ni++
}
if pn_pairs[pn_pair2] != '.' {
name[ni] = pn_pairs[pn_pair2]
ni++
}
if pn_pairs[pn_pair2+1] != '.' {
name[ni] = pn_pairs[pn_pair2+1]
ni++
}
if pn_pairs[pn_pair3] != '.' {
name[ni] = pn_pairs[pn_pair3]
ni++
}
if pn_pairs[pn_pair3+1] != '.' {
name[ni] = pn_pairs[pn_pair3+1]
ni++
}
if longname {
if pn_pairs[pn_pair4] != '.' {
name[ni] = pn_pairs[pn_pair4]
ni++
}
if pn_pairs[pn_pair4+1] != '.' {
name[ni] = pn_pairs[pn_pair4+1]
ni++
}
}
name[ni] = 0
return name
}
sub determine_planet_properties() {
; create the planet's characteristics
planet.number++
planet.x = msb(seed[1])
planet.y = msb(seed[0])
planet.govtype = lsb(seed[1]) >> 3 & 7 ; bits 3,4 &5 of w1
planet.economy = msb(seed[0]) & 7 ; bits 8,9 &A of w0
if planet.govtype <= 1
planet.economy = (planet.economy | 2)
planet.techlevel = (msb(seed[1]) & 3) + (planet.economy ^ 7)
planet.techlevel += planet.govtype >> 1
if planet.govtype & 1
planet.techlevel++
planet.population = 4 * planet.techlevel + planet.economy
planet.population += planet.govtype + 1
planet.productivity = ((planet.economy ^ 7) + 3) * (planet.govtype + 4)
planet.productivity *= planet.population * 8
ubyte seed2_msb = msb(seed[2])
planet.radius = mkword((seed2_msb & 15) + 11, planet.x)
planet.species_is_alien = lsb(seed[2]) & 128 ; bit 7 of w2_lo
if planet.species_is_alien {
planet.species_size = (seed2_msb >> 2) & 7 ; bits 2-4 of w2_hi
planet.species_color = seed2_msb >> 5 ; bits 5-7 of w2_hi
planet.species_look = (seed2_msb ^ msb(seed[1])) & 7 ;bits 0-2 of (w0_hi EOR w1_hi)
planet.species_kind = (planet.species_look + (seed2_msb & 3)) & 7 ;Add bits 0-1 of w2_hi to A from previous step, and take bits 0-2 of the result
}
planet.goatsoup_seed[0] = lsb(seed[1])
planet.goatsoup_seed[1] = msb(seed[1])
planet.goatsoup_seed[2] = lsb(seed[2])
planet.goatsoup_seed[3] = seed2_msb
}
sub tweakseed() {
uword temp = seed[0] + seed[1] + seed[2]
seed[0] = seed[1]
seed[1] = seed[2]
seed[2] = temp
}
sub twist(uword x) -> uword {
ubyte xh = msb(x)
ubyte xl = lsb(x)
rol(xh)
rol(xl)
return mkword(xh, xl)
}
}
planet {
str[] species_sizes = ["Large", "Fierce", "Small"]
str[] species_colors = ["Green", "Red", "Yellow", "Blue", "Black", "Harmless"]
str[] species_looks = ["Slimy", "Bug-Eyed", "Horned", "Bony", "Fat", "Furry"]
str[] species_kinds = ["Rodents", "Frogs", "Lizards", "Lobsters", "Birds", "Humanoids", "Felines", "Insects"]
str[] govnames = ["Anarchy", "Feudal", "Multi-gov", "Dictatorship", "Communist", "Confederacy", "Democracy", "Corporate State"]
str[] econnames = ["Rich Industrial", "Average Industrial", "Poor Industrial", "Mainly Industrial",
"Mainly Agricultural", "Rich Agricultural", "Average Agricultural", "Poor Agricultural"]
str[] words81 = ["fabled", "notable", "well known", "famous", "noted"]
str[] words82 = ["very", "mildly", "most", "reasonably", ""]
str[] words83 = ["ancient", "\x95", "great", "vast", "pink"]
str[] words84 = ["\x9E \x9D plantations", "mountains", "\x9C", "\x94 forests", "oceans"]
str[] words85 = ["shyness", "silliness", "mating traditions", "loathing of \x86", "love for \x86"]
str[] words86 = ["food blenders", "tourists", "poetry", "discos", "\x8E"]
str[] words87 = ["talking tree", "crab", "bat", "lobst", "\xB2"]
str[] words88 = ["beset", "plagued", "ravaged", "cursed", "scourged"]
str[] words89 = ["\x96 civil war", "\x9B \x98 \x99s", "a \x9B disease", "\x96 earthquakes", "\x96 solar activity"]
str[] words8A = ["its \x83 \x84", "the \xB1 \x98 \x99", "its inhabitants' \x9A \x85", "\xA1", "its \x8D \x8E"]
str[] words8B = ["juice", "brandy", "water", "brew", "gargle blasters"]
str[] words8C = ["\xB2", "\xB1 \x99", "\xB1 \xB2", "\xB1 \x9B", "\x9B \xB2"]
str[] words8D = ["fabulous", "exotic", "hoopy", "unusual", "exciting"]
str[] words8E = ["cuisine", "night life", "casinos", "sit coms", " \xA1 "]
str[] words8F = ["\xB0", "The planet \xB0", "The world \xB0", "This planet", "This world"]
str[] words90 = ["n unremarkable", " boring", " dull", " tedious", " revolting"]
str[] words91 = ["planet", "world", "place", "little planet", "dump"]
str[] words92 = ["wasp", "moth", "grub", "ant", "\xB2"]
str[] words93 = ["poet", "arts graduate", "yak", "snail", "slug"]
str[] words94 = ["tropical", "dense", "rain", "impenetrable", "exuberant"]
str[] words95 = ["funny", "wierd", "unusual", "strange", "peculiar"]
str[] words96 = ["frequent", "occasional", "unpredictable", "dreadful", "deadly"]
str[] words97 = ["\x82 \x81 for \x8A", "\x82 \x81 for \x8A and \x8A", "\x88 by \x89", "\x82 \x81 for \x8A but \x88 by \x89", "a\x90 \x91"]
str[] words98 = ["\x9B", "mountain", "edible", "tree", "spotted"]
str[] words99 = ["\x9F", "\xA0", "\x87oid", "\x93", "\x92"]
str[] words9A = ["ancient", "exceptional", "eccentric", "ingrained", "\x95"]
str[] words9B = ["killer", "deadly", "evil", "lethal", "vicious"]
str[] words9C = ["parking meters", "dust clouds", "ice bergs", "rock formations", "volcanoes"]
str[] words9D = ["plant", "tulip", "banana", "corn", "\xB2weed"]
str[] words9E = ["\xB2", "\xB1 \xB2", "\xB1 \x9B", "inhabitant", "\xB1 \xB2"]
str[] words9F = ["shrew", "beast", "bison", "snake", "wolf"]
str[] wordsA0 = ["leopard", "cat", "monkey", "goat", "fish"]
str[] wordsA1 = ["\x8C \x8B", "\xB1 \x9F \xA2", "its \x8D \xA0 \xA2", "\xA3 \xA4", "\x8C \x8B"]
str[] wordsA2 = ["meat", "cutlet", "steak", "burgers", "soup"]
str[] wordsA3 = ["ice", "mud", "Zero-G", "vacuum", "\xB1 ultra"]
str[] wordsA4 = ["hockey", "cricket", "karate", "polo", "tennis"]
uword[] @shared wordlists = [
words81, words82, words83, words84, words85, words86, words87, words88,
words89, words8A, words8B, words8C, words8D, words8E, words8F, words90,
words91, words92, words93, words94, words95, words96, words97, words98,
words99, words9A, words9B, words9C, words9D, words9E, words9F, wordsA0,
wordsA1, wordsA2, wordsA3, wordsA4]
str pairs0 = "abouseitiletstonlonuthnoallexegezacebisousesarmaindirea.eratenbe"
ubyte[4] goatsoup_rnd = [0, 0, 0, 0]
ubyte[4] goatsoup_seed = [0, 0, 0, 0]
str name = " " ; 8 max
ubyte number ; starts at 0 in new galaxy, then increases by 1 for each generated planet
ubyte x
ubyte y
ubyte economy
ubyte govtype
ubyte techlevel
ubyte population
uword productivity
uword radius
ubyte species_is_alien ; otherwise "Human Colonials"
ubyte species_size
ubyte species_color
ubyte species_look
ubyte species_kind
sub set_seed(uword s1, uword s2) {
goatsoup_seed[0] = lsb(s1)
goatsoup_seed[1] = msb(s1)
goatsoup_seed[2] = lsb(s2)
goatsoup_seed[3] = msb(s2)
reset_rnd()
}
sub reset_rnd() {
goatsoup_rnd[0] = goatsoup_seed[0]
goatsoup_rnd[1] = goatsoup_seed[1]
goatsoup_rnd[2] = goatsoup_seed[2]
goatsoup_rnd[3] = goatsoup_seed[3]
}
sub random_name() -> str {
ubyte ii
str randname = " " ; 8 chars max
ubyte nx = 0
for ii in 0 to goatsoup_rnd_number() & 3 {
ubyte xx = goatsoup_rnd_number() & $3e
if pairs0[xx] != '.' {
randname[nx] = pairs0[xx]
nx++
}
xx++
if pairs0[xx] != '.' {
randname[nx] = pairs0[xx]
nx++
}
}
randname[nx] = 0
randname[0] = string.upperchar(randname[0])
return randname
}
sub goatsoup_rnd_number() -> ubyte {
ubyte xx = goatsoup_rnd[0] * 2
uword a = xx as uword + goatsoup_rnd[2]
if goatsoup_rnd[0] > 127
a ++
goatsoup_rnd[0] = lsb(a)
goatsoup_rnd[2] = xx
xx = goatsoup_rnd[1]
ubyte ac = xx + goatsoup_rnd[3] + msb(a)
goatsoup_rnd[1] = ac
goatsoup_rnd[3] = xx
return ac
}
sub distance(ubyte px, ubyte py) -> ubyte {
uword ax
uword ay
if px>x
ax=px-x
else
ax=x-px
if py>y
ay=py-y
else
ay=y-py
ay /= 2
ubyte d = sqrt16(ax*ax + ay*ay)
if d>63
return 255
return d*4
}
sub soup() -> str {
str planet_result = " " * 160
uword[6] source_stack
ubyte stack_ptr = 0
str start_source = "\x8F is \x97."
uword source_ptr = &start_source
uword result_ptr = &planet_result
reset_rnd()
recursive_soup()
return planet_result
sub recursive_soup() {
repeat {
ubyte c = @(source_ptr)
source_ptr++
if c == $00 {
@(result_ptr) = 0
return
}
else if c <= $80 {
@(result_ptr) = c
result_ptr++
}
else {
if c <= $a4 {
ubyte rnr = goatsoup_rnd_number()
ubyte wordNr = (rnr >= $33) + (rnr >= $66) + (rnr >= $99) + (rnr >= $CC)
source_stack[stack_ptr] = source_ptr
stack_ptr++
source_ptr = getword(c, wordNr)
recursive_soup() ; RECURSIVE CALL - ignore the warning message from the compiler; we don't use local variables or parameters so we're safe in this case
stack_ptr--
source_ptr = source_stack[stack_ptr]
} else {
if c == $b0 {
@(result_ptr) = string.upperchar(name[0])
result_ptr++
concat_string(&name + 1)
}
else if c == $b1 {
@(result_ptr) = string.upperchar(name[0])
result_ptr++
ubyte ni
for ni in 1 to len(name) {
ubyte cc = name[ni]
if cc in ['e', 'o', 0]
break
else {
@(result_ptr) = cc
result_ptr++
}
}
@(result_ptr) = 'i'
result_ptr++
@(result_ptr) = 'a'
result_ptr++
@(result_ptr) = 'n'
result_ptr++
}
else if c == $b2 {
concat_string(random_name())
}
else {
@(result_ptr) = c
result_ptr++
}
}
}
}
}
sub concat_string(uword str_ptr) {
repeat {
ubyte c = @(str_ptr)
if c==0
break
else {
@(result_ptr) = c
str_ptr++
result_ptr++
}
}
}
}
sub display(bool compressed, ubyte distance) {
if compressed {
print_name_uppercase()
if distance {
txt.print(" (")
util.print_10s(distance)
txt.print(" LY)")
}
txt.print(" Tech level:")
txt.print_ub(techlevel+1)
txt.print("\n ")
txt.print(econnames[economy])
txt.spc()
txt.print(govnames[govtype])
txt.nl()
} else {
txt.print("\n\nSystem: ")
print_name_uppercase()
txt.print("\nPosition: ")
txt.print_ub(x)
txt.chrout('\'')
txt.print_ub(y)
txt.spc()
txt.chrout('#')
txt.print_ub(number)
if distance {
txt.print("\nDistance: ")
util.print_10s(distance)
txt.print(" LY")
}
txt.print("\nEconomy: ")
txt.print(econnames[economy])
txt.print("\nGovernment: ")
txt.print(govnames[govtype])
txt.print("\nTech Level: ")
txt.print_ub(techlevel+1)
txt.print("\nTurnover: ")
txt.print_uw(productivity)
txt.print("\nRadius: ")
txt.print_uw(radius)
txt.print("\nPopulation: ")
txt.print_ub(population >> 3)
txt.print(" Billion\nSpecies: ")
if species_is_alien {
if species_size < len(species_sizes) {
txt.print(species_sizes[species_size])
txt.spc()
}
if species_color < len(species_colors) {
txt.print(species_colors[species_color])
txt.spc()
}
if species_look < len(species_looks) {
txt.print(species_looks[species_look])
txt.spc()
}
if species_kind < len(species_kinds) {
txt.print(species_kinds[species_kind])
}
} else {
txt.print("Human Colonials")
}
txt.nl()
txt.print(soup())
txt.nl()
}
}
sub print_name_uppercase() {
str uppername = "????????"
uppername = name
void string.upper(uppername)
txt.print(uppername)
}
sub getword(ubyte listnum, ubyte wordidx) -> uword {
uword list = wordlists[listnum-$81]
return peekw(list + wordidx*2)
}
}
util {
sub prefix_matches(uword prefixptr, uword stringptr) -> ubyte {
repeat {
ubyte pc = @(prefixptr)
ubyte sc = @(stringptr)
if pc == 0
return true
; to lowercase for case insensitive compare:
pc = string.lowerchar(pc)
sc = string.lowerchar(sc)
if pc != sc
return false
prefixptr++
stringptr++
}
}
sub print_right(ubyte width, uword s) {
repeat width - string.length(s) {
txt.spc()
}
txt.print(s)
}
sub print_10s(uword value) {
txt.print_uw(value/10)
txt.chrout('.')
txt.print_uw(value % 10)
}
}
@@ -13,8 +13,8 @@ import org.takes.rs.RsJson
import org.takes.tk.TkSlf4j
import prog8.compiler.CompilerArguments
import prog8.compiler.compileProgram
import java.nio.file.Path
import javax.json.Json
import kotlin.io.path.Path
class Jsonding: RsJson.Source {
@@ -31,7 +31,7 @@ class RequestParser : Take {
val names = form.names()
val a = form.param("a").single()
val args = CompilerArguments(
Path.of(a),
Path(a),
optimize = true,
optimizeFloatExpressions = false,
dontReinitGlobals = false,
@@ -0,0 +1,20 @@
package prog8.intermediate
// calls to builtin operations that are too complex to be implemented as an IR instruction
// these use the SYSCALL instruction instead.
enum class IMSyscall {
SORT_UBYTE,
SORT_BYTE,
SORT_UWORD,
SORT_WORD,
ANY_BYTE,
ANY_WORD,
ANY_FLOAT,
ALL_BYTE,
ALL_WORD,
ALL_FLOAT,
REVERSE_BYTES,
REVERSE_WORDS,
REVERSE_FLOATS
}
@@ -4,6 +4,7 @@ import prog8.code.*
import prog8.code.core.*
import prog8.code.target.*
import java.nio.file.Path
import kotlin.io.path.Path
import kotlin.io.path.bufferedReader
import kotlin.io.path.div
@@ -31,7 +32,7 @@ class IRFileReader(outputDir: Path, programName: String) {
val initGlobals = parseInitGlobals(lines)
val blocks = parseBlocksUntilProgramEnd(lines, variables)
val st = SymbolTable()
val st = IRSymbolTable(null)
variables.forEach { st.add(it) }
memorymapped.forEach { st.add(it) }
slabs.forEach { st.add(it) }
@@ -54,6 +55,7 @@ class IRFileReader(outputDir: Path, programName: String) {
var loadAddress = target.machine.PROGRAM_LOAD_ADDRESS
var dontReinitGlobals = false
var evalStackBaseAddress: UInt? = null
var outputDir = Path("")
if(line!="<OPTIONS>")
throw IRParseException("invalid OPTIONS")
while(true) {
@@ -82,6 +84,7 @@ class IRFileReader(outputDir: Path, programName: String) {
val (start, end) = value.split(',')
zpReserved.add(UIntRange(start.toUInt(), end.toUInt()))
}
"outputDir" -> outputDir = Path(value)
else -> throw IRParseException("illegal OPTION $name")
}
}
@@ -96,7 +99,8 @@ class IRFileReader(outputDir: Path, programName: String) {
target,
loadAddress,
dontReinitGlobals = dontReinitGlobals,
evalStackBaseAddress = evalStackBaseAddress
evalStackBaseAddress = evalStackBaseAddress,
outputDir = outputDir
)
}
@@ -130,7 +134,12 @@ class IRFileReader(outputDir: Path, programName: String) {
}
}
in ArrayDatatypes -> {
initArray = value.split(',').map { StArrayElement(it.toDouble(), null) }
initArray = value.split(',').map {
if(it.startsWith('&'))
StArrayElement(null, it.drop(1).split('.'))
else
StArrayElement(it.toDouble(), null)
}
}
DataType.STR -> throw IRParseException("STR should have been converted to byte array")
else -> throw IRParseException("weird dt")
@@ -173,7 +182,7 @@ class IRFileReader(outputDir: Path, programName: String) {
"uword" -> DataType.ARRAY_UW
"float" -> DataType.ARRAY_F
"bool" -> DataType.ARRAY_B
else -> throw IRParseException("invalid dt")
else -> throw IRParseException("invalid dt $type")
}
} else {
return when(type) {
@@ -183,7 +192,8 @@ class IRFileReader(outputDir: Path, programName: String) {
"uword" -> DataType.UWORD
"float" -> DataType.FLOAT
"bool" -> DataType.BOOL
else -> throw IRParseException("invalid dt")
// note: 'str' should not occur anymore in IR. Should be 'uword'
else -> throw IRParseException("invalid dt $type")
}
}
}
@@ -203,7 +213,7 @@ class IRFileReader(outputDir: Path, programName: String) {
// example: "SLAB slabname 4096 0"
val match = slabPattern.matchEntire(line) ?: throw IRParseException("invalid SLAB $line")
val (name, size, align) = match.destructured
slabs.add(StMemorySlab(name, size.toUInt(), align.toUInt(), null, Position.DUMMY))
slabs.add(StMemorySlab(name, size.toUInt(), align.toUInt(), Position.DUMMY))
}
return slabs
}
@@ -355,18 +365,19 @@ class IRFileReader(outputDir: Path, programName: String) {
return sub
}
private fun parseParameters(lines: Iterator<String>, variables: List<StStaticVariable>): List<StStaticVariable> {
private fun parseParameters(lines: Iterator<String>, variables: List<StStaticVariable>): List<IRSubroutine.IRParam> {
var line = lines.next()
if(line!="<PARAMS>")
throw IRParseException("missing PARAMS")
val params = mutableListOf<StStaticVariable>()
val params = mutableListOf<IRSubroutine.IRParam>()
while(true) {
line = lines.next()
if(line=="</PARAMS>")
return params
val (datatype, name) = line.split(' ')
val dt = parseDatatype(datatype, datatype.contains('['))
params.add(variables.single { it.dt==dt && it.name==name})
val orig = variables.single { it.dt==dt && it.name==name}
params.add(IRSubroutine.IRParam(name, dt, orig))
}
}
@@ -38,7 +38,7 @@ class IRFileWriter(private val irProgram: IRProgram) {
block.subroutines.forEach {
out.write("<SUB NAME=${it.name} RETURNTYPE=${it.returnType.toString().lowercase()} POS=${it.position}>\n")
out.write("<PARAMS>\n")
it.parameters.forEach { param -> out.write("${getTypeString(param)} ${param.scopedName.joinToString(".")}\n") }
it.parameters.forEach { param -> out.write("${getTypeString(param.dt)} ${param.name}\n") }
out.write("</PARAMS>\n")
it.chunks.forEach { chunk ->
if(chunk is IRInlineAsmChunk) {
@@ -93,13 +93,14 @@ class IRFileWriter(private val irProgram: IRProgram) {
out.write("loadAddress=${irProgram.options.loadAddress}\n")
out.write("dontReinitGlobals=${irProgram.options.dontReinitGlobals}\n")
out.write("evalStackBaseAddress=${irProgram.options.evalStackBaseAddress}\n")
out.write("outputDir=${irProgram.options.outputDir.toAbsolutePath()}\n")
// other options not yet useful here?
out.write("</OPTIONS>\n")
}
private fun writeVariableAllocations() {
out.write("\n<VARIABLES>\n")
for (variable in irProgram.st.allVariables) {
for (variable in irProgram.st.allVariables()) {
val typeStr = getTypeString(variable)
val value: String = when(variable.dt) {
DataType.FLOAT -> (variable.onetimeInitializationNumericValue ?: 0.0).toString()
@@ -117,7 +118,12 @@ class IRFileWriter(private val irProgram: IRProgram) {
}
in ArrayDatatypes -> {
if(variable.onetimeInitializationArrayValue!==null) {
variable.onetimeInitializationArrayValue!!.joinToString(",") { it.number!!.toInt().toString() }
variable.onetimeInitializationArrayValue!!.joinToString(",") {
if(it.number!=null)
it.number!!.toInt().toString()
else
"&${it.addressOf!!.joinToString(".")}"
}
} else {
(1..variable.length!!).joinToString(",") { "0" }
}
@@ -125,19 +131,19 @@ class IRFileWriter(private val irProgram: IRProgram) {
else -> throw InternalCompilerException("weird dt")
}
// TODO have uninitialized variables and arrays? (BSS SECTION)
out.write("$typeStr ${variable.scopedName.joinToString(".")}=$value zp=${variable.zpwish}\n")
out.write("$typeStr ${variable.name}=$value zp=${variable.zpwish}\n")
}
out.write("</VARIABLES>\n")
out.write("\n<MEMORYMAPPEDVARIABLES>\n")
for (variable in irProgram.st.allMemMappedVariables) {
for (variable in irProgram.st.allMemMappedVariables()) {
val typeStr = getTypeString(variable)
out.write("&$typeStr ${variable.scopedName.joinToString(".")}=${variable.address}\n")
out.write("&$typeStr ${variable.name}=${variable.address}\n")
}
out.write("</MEMORYMAPPEDVARIABLES>\n")
out.write("\n<MEMORYSLABS>\n")
irProgram.st.allMemorySlabs.forEach{ slab -> out.write("SLAB ${slab.name} ${slab.size} ${slab.align}\n") }
irProgram.st.allMemorySlabs().forEach{ slab -> out.write("SLAB ${slab.name} ${slab.size} ${slab.align}\n") }
out.write("</MEMORYSLABS>\n")
}
@@ -1,7 +1,6 @@
package prog8.intermediate
import prog8.code.StStaticVariable
import prog8.code.SymbolTable
import prog8.code.core.*
/*
@@ -47,7 +46,7 @@ PROGRAM:
*/
class IRProgram(val name: String,
val st: SymbolTable,
val st: IRSymbolTable,
val options: CompilationOptions,
val encoding: IStringEncoding) {
@@ -86,10 +85,12 @@ class IRBlock(
}
class IRSubroutine(val name: String,
val parameters: List<StStaticVariable>, // NOTE: these are the same objects as their occurrences as variables in the symbol table
val parameters: List<IRParam>,
val returnType: DataType?,
val position: Position) {
class IRParam(val name: String, val dt: DataType, val orig: StStaticVariable)
val chunks = mutableListOf<IRCodeChunkBase>()
init {
@@ -97,6 +98,12 @@ class IRSubroutine(val name: String,
throw IllegalArgumentException("subroutine name is not scoped: $name")
if(name.startsWith("main.main."))
throw IllegalArgumentException("subroutine name invalid main prefix: $name")
// params and return value should not be str
if(parameters.any{ it.dt !in NumericDatatypes })
throw IllegalArgumentException("non-numeric parameter")
if(returnType!=null && returnType !in NumericDatatypes)
throw IllegalArgumentException("non-numeric returntype $returnType")
}
operator fun plusAssign(chunk: IRCodeChunkBase) { chunks+= chunk }
@@ -109,8 +116,6 @@ class IRAsmSubroutine(val name: String,
val parameters: List<Pair<DataType, RegisterOrStatusflag>>,
val returns: List<Pair<DataType, RegisterOrStatusflag>>,
val assembly: String) {
val lines = mutableListOf<IRCodeLine>()
init {
if(!name.contains('.'))
throw IllegalArgumentException("subroutine name is not scoped: $name")
@@ -0,0 +1,111 @@
package prog8.intermediate
import prog8.code.*
// In the Intermediate Representation, all nesting has been removed.
// So the symbol table is just a big flat mapping of (scoped)name to node.
class IRSymbolTable(sourceSt: SymbolTable?) {
private val table = mutableMapOf<String, StNode>()
init {
if(sourceSt!=null) {
sourceSt.allVariables.forEach {
add(it)
}
sourceSt.allMemMappedVariables.forEach {
add(it)
}
sourceSt.allMemorySlabs.forEach {
add(it)
}
require(table.all { it.key==it.value.name })
allVariables().forEach {variable ->
variable.onetimeInitializationArrayValue?.let {
it.forEach { arrayElt ->
if(arrayElt.addressOf!=null) {
require(arrayElt.addressOf!!.size > 1) {
"pointer var in array should be properly scoped: ${arrayElt.addressOf!!} in ${variable.name}"
}
}
}
}
}
}
}
fun allVariables(): Sequence<StStaticVariable> =
table.asSequence().map { it.value }.filterIsInstance<StStaticVariable>()
fun allMemMappedVariables(): Sequence<StMemVar> =
table.asSequence().map { it.value }.filterIsInstance<StMemVar>()
fun allMemorySlabs(): Sequence<StMemorySlab> =
table.asSequence().map { it.value }.filterIsInstance<StMemorySlab>()
fun lookup(name: String) = table[name]
fun add(variable: StStaticVariable) {
val scopedName: String
val varToadd: StStaticVariable
if('.' in variable.name) {
scopedName = variable.name
varToadd = variable
} else {
fun fixupAddressOfInArray(array: List<StArrayElement>?): List<StArrayElement>? {
if(array==null)
return null
val newArray = mutableListOf<StArrayElement>()
array.forEach {
if(it.addressOf!=null) {
val target = variable.lookup(it.addressOf!!)!!
newArray.add(StArrayElement(null, target.scopedName))
} else {
newArray.add(it)
}
}
return newArray
}
scopedName = variable.scopedName.joinToString(".")
varToadd = StStaticVariable(scopedName, variable.dt,
variable.onetimeInitializationNumericValue,
variable.onetimeInitializationStringValue,
fixupAddressOfInArray(variable.onetimeInitializationArrayValue),
variable.length,
variable.zpwish,
variable.position
)
}
table[scopedName] = varToadd
}
fun add(variable: StMemVar) {
val scopedName: String
val varToadd: StMemVar
if('.' in variable.name) {
scopedName = variable.name
varToadd = variable
} else {
scopedName = variable.scopedName.joinToString(".")
varToadd = StMemVar(scopedName, variable.dt, variable.address, variable.length, variable.position)
}
table[scopedName] = varToadd
}
fun add(variable: StMemorySlab) {
val scopedName: String
val varToadd: StMemorySlab
if('.' in variable.name) {
scopedName = variable.name
varToadd = variable
} else {
scopedName = variable.scopedName.joinToString(".")
varToadd = StMemorySlab(scopedName, variable.size, variable.align, variable.position)
}
table[scopedName] = varToadd
}
}
+3 -3
View File
@@ -6,7 +6,7 @@ import prog8.code.core.DataType
import prog8.code.core.InternalCompilerException
public fun getTypeString(dt : DataType): String {
fun getTypeString(dt : DataType): String {
return when(dt) {
DataType.UBYTE -> "ubyte"
DataType.BYTE -> "byte"
@@ -22,7 +22,7 @@ public fun getTypeString(dt : DataType): String {
}
}
public fun getTypeString(memvar: StMemVar): String {
fun getTypeString(memvar: StMemVar): String {
return when(memvar.dt) {
DataType.UBYTE -> "ubyte"
DataType.BYTE -> "byte"
@@ -38,7 +38,7 @@ public fun getTypeString(memvar: StMemVar): String {
}
}
public fun getTypeString(variable : StStaticVariable): String {
fun getTypeString(variable : StStaticVariable): String {
return when(variable.dt) {
DataType.UBYTE -> "ubyte"
DataType.BYTE -> "byte"
+6 -10
View File
@@ -3,7 +3,6 @@ import io.kotest.matchers.ints.shouldBeGreaterThan
import io.kotest.matchers.shouldBe
import io.kotest.matchers.types.instanceOf
import prog8.code.StStaticVariable
import prog8.code.SymbolTable
import prog8.code.core.CbmPrgLauncherType
import prog8.code.core.CompilationOptions
import prog8.code.core.OutputType
@@ -12,17 +11,13 @@ import prog8.code.target.Cx16Target
import prog8.intermediate.IRFileReader
import prog8.intermediate.IRFileWriter
import prog8.intermediate.IRProgram
import java.nio.file.Path
import kotlin.io.path.deleteExisting
import kotlin.io.path.name
import kotlin.io.path.readLines
import kotlin.io.path.writeText
import prog8.intermediate.IRSymbolTable
import kotlin.io.path.*
class TestIRFileInOut: FunSpec({
test("test IR writer") {
val st = SymbolTable()
val target = Cx16Target()
val tempdir = Path.of(System.getProperty("java.io.tmpdir"))
val tempdir = Path(System.getProperty("java.io.tmpdir"))
val options = CompilationOptions(
OutputType.RAW,
CbmPrgLauncherType.NONE,
@@ -34,7 +29,7 @@ class TestIRFileInOut: FunSpec({
loadAddress = target.machine.PROGRAM_LOAD_ADDRESS,
outputDir = tempdir
)
val program = IRProgram("unittest-irwriter", st, options, target)
val program = IRProgram("unittest-irwriter", IRSymbolTable(null), options, target)
val writer = IRFileWriter(program)
writer.writeFile()
val generatedFile = tempdir.resolve("unittest-irwriter.p8ir")
@@ -100,7 +95,7 @@ return
</BLOCK>
</PROGRAM>
"""
val tempfile = kotlin.io.path.createTempFile(suffix = ".p8ir")
val tempfile = createTempFile(suffix = ".p8ir")
tempfile.writeText(source)
val filepart = tempfile.name.dropLast(5)
val reader = IRFileReader(tempfile.parent, filepart)
@@ -108,6 +103,7 @@ return
tempfile.deleteExisting()
program.name shouldBe "test-ir-reader"
program.blocks.size shouldBe 2
program.st.allVariables().count() shouldBe 1
program.st.lookup("sys.wait.jiffies") shouldBe instanceOf<StStaticVariable>()
}
})
-1
View File
@@ -2,7 +2,6 @@ import io.kotest.assertions.throwables.shouldThrow
import io.kotest.core.spec.style.FunSpec
import io.kotest.matchers.shouldBe
import io.kotest.matchers.shouldNotBe
import org.junit.jupiter.api.fail
import prog8.intermediate.*
+63 -44
View File
@@ -17,6 +17,8 @@ class Assembler {
fun initializeMemory(memsrc: String, memory: Memory) {
symbolAddresses.clear()
val arrayValuePlaceholders = mutableListOf<Pair<Int, String>>()
val instrPattern = Regex("""var (.+) @([0-9]+) ([a-z]+)(\[[0-9]+\])? (.+)""", RegexOption.IGNORE_CASE)
for(line in memsrc.lines()) {
if(line.isBlank() || line.startsWith(';'))
@@ -58,18 +60,33 @@ class Assembler {
}
}
"uword", "word" -> {
val array = values.split(',').map { parseValue(Opcode.LOADCPU, it.trim(), 0).toInt() }
require(array.size==numArrayElts || array.size==1)
if(numArrayElts>array.size) {
val value = array.single().toUShort()
repeat(numArrayElts) {
memory.setUW(address, value)
address += 2
}
if(arrayspec.isBlank()) {
// single value
val value = parseValue(Opcode.LOADCPU, values.trim(), 0).toInt()
memory.setUW(address, value.toUShort())
address += 2
} else {
for (value in array) {
memory.setUW(address, value.toUShort())
address += 2
// array initializer
val array = values.split(',').withIndex().map {(index, value) ->
val tv = value.trim()
if(tv.startsWith('&')) {
arrayValuePlaceholders += Pair(address+index*2, tv.drop(1))
9999 // will be replaced with correct value at the end.
} else
parseValue(Opcode.LOADCPU, tv, 0).toInt()
}
require(array.size==numArrayElts || array.size==1)
if(numArrayElts>array.size) {
val value = array.single().toUShort()
repeat(numArrayElts) {
memory.setUW(address, value)
address += 2
}
} else {
for (value in array) {
memory.setUW(address, value.toUShort())
address += 2
}
}
}
}
@@ -93,6 +110,12 @@ class Assembler {
}
}
}
// correct the addres-of values in array initializers
arrayValuePlaceholders.forEach { (address, symbol) ->
val addr = this.symbolAddresses.getValue(symbol)
memory.setUW(address, addr.toUShort())
}
}
fun assembleProgram(source: CharSequence): List<Instruction> {
@@ -110,7 +133,7 @@ class Assembler {
if(binarymatch!=null) {
val hex = binarymatch.groups[1]!!.value
val binary = hex.windowed(size=2, step=2).map {
it.toString().toByte(16)
it.toByte(16)
}.toByteArray()
program.add(Instruction(Opcode.BINARYDATA, binaryData = binary))
} else {
@@ -157,6 +180,18 @@ class Assembler {
var fpReg3: Int? = null
var value: Float? = null
var operand: String?
fun parseValueOrPlaceholder(operand: String, pc: Int, rest: String, restIndex: Int, opcode: Opcode): Float {
return if(operand.startsWith('_')) {
placeholders[pc] = rest.split(",")[restIndex].trim().drop(1)
0f
} else if(operand[0].isLetter()) {
placeholders[pc] = rest.split(",")[restIndex].trim()
0f
} else
parseValue(opcode, operand, pc)
}
if(operands.isNotEmpty() && operands[0].isNotEmpty()) {
operand = operands.removeFirst().trim()
if(operand[0]=='r')
@@ -164,13 +199,7 @@ class Assembler {
else if(operand[0]=='f' && operand[1]=='r')
fpReg1 = operand.substring(2).toInt()
else {
value = if(operand.startsWith('_')) {
// it's a label, keep the original case!
val labelname = rest.split(",").first().trim()
parseValue(opcode, labelname, program.size)
} else {
parseValue(opcode, operand, program.size)
}
value = parseValueOrPlaceholder(operand, program.size, rest, 0, opcode)
operands.clear()
}
if(operands.isNotEmpty()) {
@@ -180,7 +209,7 @@ class Assembler {
else if(operand[0]=='f' && operand[1]=='r')
fpReg2 = operand.substring(2).toInt()
else {
value = parseValue(opcode, operand, program.size)
value = parseValueOrPlaceholder(operand, program.size, rest, 1, opcode)
operands.clear()
}
if(operands.isNotEmpty()) {
@@ -190,13 +219,11 @@ class Assembler {
else if(operand[0]=='f' && operand[1]=='r')
fpReg3 = operand.substring(2).toInt()
else {
val symbol=rest.split(',').last()
value = parseValue(opcode, symbol, program.size)
value = parseValueOrPlaceholder(operand, program.size, rest, 2, opcode)
operands.clear()
}
if(operands.isNotEmpty()) {
val symbol=rest.split(',').last()
value = parseValue(opcode, symbol, program.size)
TODO("placeholder symbol? $operands rest=$rest'")
operands.clear()
}
}
@@ -296,26 +323,18 @@ class Assembler {
}
private fun parseValue(opcode: Opcode, value: String, pc: Int): Float {
if(value.startsWith("-")) {
return -parseValue(opcode, value.substring(1), pc)
}
if(value.startsWith('$'))
return value.substring(1).toInt(16).toFloat()
if(value.startsWith('%'))
return value.substring(1).toInt(2).toFloat()
if(value.startsWith("0x"))
return value.substring(2).toInt(16).toFloat()
if(value.startsWith('_')) {
if(opcode !in OpcodesForCpuRegisters)
placeholders[pc] = value.substring(1)
return 0f
}
if(value[0].isLetter()) {
if(opcode !in OpcodesForCpuRegisters)
placeholders[pc] = value
return 0f
}
return value.toFloat()
return if(value.startsWith("-"))
-parseValue(opcode, value.substring(1), pc)
else if(value.startsWith('$'))
value.substring(1).toInt(16).toFloat()
else if(value.startsWith('%'))
value.substring(1).toInt(2).toFloat()
else if(value.startsWith("0x"))
value.substring(2).toInt(16).toFloat()
else if(value.startsWith('_') || value[0].isLetter())
throw IllegalArgumentException("attempt to parse non-numeric value $value")
else
value.toFloat()
}
private fun convertType(typestr: String): VmDataType? {
+15 -4
View File
@@ -1,6 +1,7 @@
package prog8.vm
import prog8.code.core.AssemblyError
import java.lang.NumberFormatException
import kotlin.math.min
/*
@@ -253,13 +254,23 @@ object SysCalls {
}
Syscall.STR_TO_UWORD -> {
val stringAddr = vm.registers.getUW(0)
val string = vm.memory.getString(stringAddr.toInt())
vm.registers.setUW(0, string.toUShort())
val string = vm.memory.getString(stringAddr.toInt()).takeWhile { it.isDigit() }
val value = try {
string.toUShort()
} catch(_: NumberFormatException) {
0u
}
vm.registers.setUW(0, value)
}
Syscall.STR_TO_WORD -> {
val stringAddr = vm.registers.getUW(0)
val string = vm.memory.getString(stringAddr.toInt())
vm.registers.setSW(0, string.toShort())
val string = vm.memory.getString(stringAddr.toInt()).takeWhile { it.isDigit() }
val value = try {
string.toShort()
} catch(_: NumberFormatException) {
0
}
vm.registers.setSW(0, value)
}
Syscall.COMPARE_STRINGS -> {
val firstAddr = vm.registers.getUW(0)
+11 -6
View File
@@ -47,7 +47,7 @@ class VirtualMachine(val memory: Memory, program: List<Instruction>, val cx16vir
throw IllegalArgumentException("program cannot contain more than 65536 instructions")
}
fun run(throttle: Boolean = true) {
fun run() {
try {
var before = System.nanoTime()
var numIns = 0
@@ -55,9 +55,9 @@ class VirtualMachine(val memory: Memory, program: List<Instruction>, val cx16vir
step()
numIns++
if(throttle && stepCount and 32767 == 0) {
Thread.sleep(1) // avoid 100% cpu core usage
}
// if(stepCount and 32767 == 0) {
// Thread.sleep(1) // avoid 100% cpu core usage
// }
if(stepCount and 0xffffff == 0) {
val now = System.nanoTime()
@@ -2105,13 +2105,18 @@ class VirtualMachine(val memory: Memory, program: List<Instruction>, val cx16vir
// probably called via reflection
class VmRunner: IVirtualMachineRunner {
override fun runProgram(source: String, throttle: Boolean) {
override fun runProgram(source: String) {
runAndTestProgram(source) { /* no tests */ }
}
fun runAndTestProgram(source: String, test: (VirtualMachine) -> Unit) {
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, assembler.cx16virtualregBaseAdress)
vm.run(throttle = true)
vm.run()
test(vm)
}
}
+3 -3
View File
@@ -16,7 +16,7 @@ class TestVm: FunSpec( {
vm.valueStack.shouldBeEmpty()
vm.pc shouldBe 0
vm.stepCount shouldBe 0
vm.run(throttle = false)
vm.run()
vm.callStack.shouldBeEmpty()
vm.valueStack.shouldBeEmpty()
vm.pc shouldBe 0
@@ -37,7 +37,7 @@ class TestVm: FunSpec( {
vm.valueStack.shouldBeEmpty()
vm.pc shouldBe 0
vm.stepCount shouldBe 0
vm.run(throttle = false)
vm.run()
memory.getUW(1000) shouldBe 12345u
vm.callStack.shouldBeEmpty()
vm.valueStack.shouldBeEmpty()
@@ -47,6 +47,6 @@ class TestVm: FunSpec( {
test("vmrunner") {
val runner = VmRunner()
runner.runProgram(";comment\n------PROGRAM------\n;comment\n", false)
runner.runProgram(";comment\n------PROGRAM------\n;comment\n")
}
})