mirror of
https://github.com/irmen/prog8.git
synced 2026-10-24 03:02:20 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dcc9a71455 | ||
|
|
1a56743bb1 | ||
|
|
387a4b7c35 | ||
|
|
1d65d63bd9 | ||
|
|
dda19c29fe | ||
|
|
ca41669f4f | ||
|
|
0e1886e6bd | ||
|
|
c26e116f0e | ||
|
|
5c9c7f2c5e | ||
|
|
ca2fb6cef3 |
No files matched your search
@@ -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")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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>
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 @@
|
||||
8.5
|
||||
8.6.1
|
||||
@@ -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 }
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -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()
|
||||
}
|
||||
})
|
||||
@@ -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") {
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
@@ -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
|
||||
;
|
||||
;}
|
||||
@@ -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,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()
|
||||
}
|
||||
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
}
|
||||
@@ -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,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() {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
|
||||
@@ -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>()
|
||||
}
|
||||
})
|
||||
@@ -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.*
|
||||
|
||||
|
||||
|
||||
@@ -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? {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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")
|
||||
}
|
||||
})
|
||||
Reference in new issue
Block a user