mirror of
https://github.com/irmen/prog8.git
synced 2026-10-22 20:02:27 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9f09784b55 | ||
|
|
e7a3a89bfb | ||
|
|
7ea7e63f44 | ||
|
|
1d2ce2cbeb | ||
|
|
06cf2e0bd7 | ||
|
|
9d219ae4b9 | ||
|
|
71f5a6c50e | ||
|
|
90b2be2bf4 | ||
|
|
db1aa8fcbd | ||
|
|
11c000f764 | ||
|
|
4d6dcbd173 | ||
|
|
0da117efd2 | ||
|
|
533c368e32 | ||
|
|
8883513b0e | ||
|
|
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")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -173,8 +173,7 @@ class PtPrefix(val operator: String, type: DataType, position: Position): PtExpr
|
||||
|
||||
init {
|
||||
// note: the "not" operator may no longer occur in the ast; not x should have been replaced with x==0
|
||||
if(operator !in setOf("+", "-", "~"))
|
||||
throw IllegalArgumentException("invalid prefix operator: $operator")
|
||||
require(operator in setOf("+", "-", "~")) { "invalid prefix operator: $operator" }
|
||||
}
|
||||
|
||||
override fun printProperties() {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,12 +4,11 @@ import prog8.code.core.CompilationOptions
|
||||
import prog8.code.core.CpuType
|
||||
import prog8.code.core.IMachineDefinition
|
||||
import prog8.code.core.Zeropage
|
||||
import java.io.File
|
||||
import java.nio.file.Path
|
||||
import kotlin.io.path.isReadable
|
||||
import kotlin.io.path.name
|
||||
import kotlin.io.path.readText
|
||||
|
||||
|
||||
class VirtualMachineDefinition: IMachineDefinition {
|
||||
|
||||
override val cpu = CpuType.VIRTUAL
|
||||
@@ -32,17 +31,18 @@ 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)
|
||||
} else if(File("$filename.p8virt").isFile) {
|
||||
val source = File("$filename.p8virt").readText()
|
||||
vm.runProgram(source, true)
|
||||
if(programNameWithPath.isReadable()) {
|
||||
vm.runProgram(programNameWithPath.readText())
|
||||
} else {
|
||||
val withExt = programNameWithPath.resolveSibling("$filename.p8ir")
|
||||
if(withExt.isReadable())
|
||||
vm.runProgram(withExt.readText())
|
||||
else
|
||||
throw NoSuchFileException(withExt.toFile(), reason="not a .p8ir file")
|
||||
}
|
||||
else
|
||||
throw IllegalArgumentException("vm can only run .p8virt or .p8ir files")
|
||||
}
|
||||
|
||||
override fun isIOAddress(address: UInt): Boolean = false
|
||||
@@ -53,5 +53,5 @@ class VirtualMachineDefinition: IMachineDefinition {
|
||||
}
|
||||
|
||||
interface IVirtualMachineRunner {
|
||||
fun runProgram(source: String, throttle: Boolean)
|
||||
fun runProgram(irSource: CharSequence)
|
||||
}
|
||||
@@ -8,7 +8,6 @@ import prog8.ast.Program
|
||||
import prog8.ast.base.FatalAstException
|
||||
import prog8.ast.expressions.*
|
||||
import prog8.ast.statements.*
|
||||
import prog8.code.StMemorySlab
|
||||
import prog8.code.SymbolTable
|
||||
import prog8.code.core.*
|
||||
import prog8.codegen.cpu6502.assignment.*
|
||||
@@ -42,7 +41,7 @@ class AsmGen(internal val program: Program,
|
||||
private val expressionsAsmGen = ExpressionsAsmGen(program, this, allocator)
|
||||
private val programGen = ProgramAndVarsGen(program, options, errors, symbolTable, functioncallAsmGen, this, allocator, zeropage)
|
||||
private val assignmentAsmGen = AssignmentAsmGen(program, this, allocator)
|
||||
private val builtinFunctionsAsmGen = BuiltinFunctionsAsmGen(program, this, assignmentAsmGen, allocator)
|
||||
private val builtinFunctionsAsmGen = BuiltinFunctionsAsmGen(program, this, assignmentAsmGen)
|
||||
|
||||
override fun compileToAssembly(): IAssemblyProgram? {
|
||||
|
||||
@@ -78,7 +77,7 @@ class AsmGen(internal val program: Program,
|
||||
}
|
||||
|
||||
internal fun out(str: String, splitlines: Boolean = true) {
|
||||
val fragment = (if(splitlines && " | " in str) str.replace("|", "\n") else str).trim('\n')
|
||||
val fragment = (if(splitlines && " | " in str) str.replace("|", "\n") else str).trim('\r', '\n')
|
||||
if (splitlines) {
|
||||
for (line in fragment.splitToSequence('\n')) {
|
||||
val trimmed = if (line.startsWith(' ')) "\t" + line.trim() else line
|
||||
@@ -839,7 +838,7 @@ $repeatLabel lda $counterVar
|
||||
if(stmt.definingModule.source is SourceCode.Generated)
|
||||
throw AssemblyError("%asminclude inside non-library/non-filesystem module not yet supported")
|
||||
loadAsmIncludeFile(includedName, stmt.definingModule.source).fold(
|
||||
success = { assemblyLines.add(it.trimEnd().trimStart('\n')) },
|
||||
success = { assemblyLines.add(it.trimEnd().trimStart('\r', '\n')) },
|
||||
failure = { errors.err(it.toString(), stmt.position) }
|
||||
)
|
||||
}
|
||||
@@ -910,7 +909,7 @@ $repeatLabel lda $counterVar
|
||||
}
|
||||
|
||||
private fun translate(asm: InlineAssembly) {
|
||||
val assembly = asm.assembly.trimEnd().trimStart('\n')
|
||||
val assembly = asm.assembly.trimEnd().trimStart('\r', '\n')
|
||||
assemblyLines.add(assembly)
|
||||
}
|
||||
|
||||
@@ -2897,12 +2896,6 @@ $repeatLabel lda $counterVar
|
||||
else
|
||||
extra
|
||||
}
|
||||
|
||||
fun addMemorySlab(name: String, size: UInt, align: UInt, position: Position): String {
|
||||
val prefixedName = "prog8_memoryslab_$name"
|
||||
symbolTable.add(StMemorySlab(prefixedName, size, align, null, position))
|
||||
return prefixedName
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -15,8 +15,7 @@ import prog8.compiler.FSignature
|
||||
|
||||
internal class BuiltinFunctionsAsmGen(private val program: Program,
|
||||
private val asmgen: AsmGen,
|
||||
private val assignAsmGen: AssignmentAsmGen,
|
||||
private val allocations: VariableAllocator) {
|
||||
private val assignAsmGen: AssignmentAsmGen) {
|
||||
|
||||
internal fun translateFunctioncallExpression(fcall: BuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?) {
|
||||
val func = BuiltinFunctions.getValue(fcall.target.nameInSource.single())
|
||||
@@ -309,10 +308,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
|
||||
throw AssemblyError("should not discard result of memory allocation at $fcall")
|
||||
val name = (fcall.args[0] as StringLiteral).value
|
||||
require(name.all { it.isLetterOrDigit() || it=='_' }) {"memory name should be a valid symbol name"}
|
||||
val size = (fcall.args[1] as NumericLiteral).number.toUInt()
|
||||
val align = (fcall.args[2] as NumericLiteral).number.toUInt()
|
||||
val prefixedName = asmgen.addMemorySlab(name, size, align, fcall.position)
|
||||
val slabname = IdentifierReference(listOf("prog8_slabs", prefixedName), fcall.position)
|
||||
val slabname = IdentifierReference(listOf("prog8_slabs", "prog8_memoryslab_$name"), fcall.position)
|
||||
slabname.linkParents(fcall)
|
||||
val src = AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, DataType.UWORD, expression = AddressOf(slabname, fcall.position))
|
||||
val target =
|
||||
|
||||
@@ -22,7 +22,7 @@ class CodeGen(private val program: PtProgram,
|
||||
val irProgram = irCodeGen.generate()
|
||||
|
||||
// this stub only writes the IR program to disk but doesn't generate anything else.
|
||||
IRFileWriter(irProgram).writeFile()
|
||||
IRFileWriter(irProgram, null).write()
|
||||
|
||||
println("** experimental codegen stub: no assembly generated **")
|
||||
return null
|
||||
|
||||
@@ -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,7 @@ import prog8.code.core.DataType
|
||||
import prog8.code.core.Position
|
||||
import prog8.code.core.SignedDatatypes
|
||||
import prog8.intermediate.IRCodeChunk
|
||||
import prog8.intermediate.IRCodeInstruction
|
||||
import prog8.intermediate.IRInstruction
|
||||
import prog8.intermediate.Opcode
|
||||
import prog8.intermediate.VmDataType
|
||||
|
||||
@@ -62,8 +62,8 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
||||
else {
|
||||
// read and write a (i/o) memory location to itself.
|
||||
val tempReg = codeGen.vmRegisters.nextFree()
|
||||
code += IRCodeInstruction(Opcode.LOADM, vmDt, reg1 = tempReg, value = address)
|
||||
code += IRCodeInstruction(Opcode.STOREM, vmDt, reg1 = tempReg, value = address)
|
||||
code += IRInstruction(Opcode.LOADM, vmDt, reg1 = tempReg, value = address)
|
||||
code += IRInstruction(Opcode.STOREM, vmDt, reg1 = tempReg, value = address)
|
||||
code
|
||||
}
|
||||
}
|
||||
@@ -85,8 +85,8 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
||||
is PtBinaryExpression -> return inplaceBinexpr(value.operator, value.right, vmDt, value.type in SignedDatatypes, null, symbol, origAssign)
|
||||
is PtMemoryByte -> {
|
||||
val tempReg = codeGen.vmRegisters.nextFree()
|
||||
code += IRCodeInstruction(Opcode.LOADM, vmDt, reg1 = tempReg, labelSymbol = symbol)
|
||||
code += IRCodeInstruction(Opcode.STOREM, vmDt, reg1 = tempReg, labelSymbol = symbol)
|
||||
code += IRInstruction(Opcode.LOADM, vmDt, reg1 = tempReg, labelSymbol = symbol)
|
||||
code += IRInstruction(Opcode.STOREM, vmDt, reg1 = tempReg, labelSymbol = symbol)
|
||||
return code
|
||||
}
|
||||
else -> return fallbackAssign(origAssign)
|
||||
@@ -145,18 +145,18 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
||||
"+" -> { }
|
||||
"-" -> {
|
||||
code += if(knownAddress!=null)
|
||||
IRCodeInstruction(Opcode.NEGM, vmDt, value = knownAddress)
|
||||
IRInstruction(Opcode.NEGM, vmDt, value = knownAddress)
|
||||
else
|
||||
IRCodeInstruction(Opcode.NEGM, vmDt, labelSymbol = addressSymbol)
|
||||
IRInstruction(Opcode.NEGM, vmDt, labelSymbol = addressSymbol)
|
||||
}
|
||||
"~" -> {
|
||||
val regMask = codeGen.vmRegisters.nextFree()
|
||||
val mask = if(vmDt==VmDataType.BYTE) 0x00ff else 0xffff
|
||||
code += IRCodeInstruction(Opcode.LOAD, vmDt, reg1=regMask, value = mask)
|
||||
code += IRInstruction(Opcode.LOAD, vmDt, reg1=regMask, value = mask)
|
||||
code += if(knownAddress!=null)
|
||||
IRCodeInstruction(Opcode.XORM, vmDt, reg1=regMask, value = knownAddress)
|
||||
IRInstruction(Opcode.XORM, vmDt, reg1=regMask, value = knownAddress)
|
||||
else
|
||||
IRCodeInstruction(Opcode.XORM, vmDt, reg1=regMask, labelSymbol = addressSymbol)
|
||||
IRInstruction(Opcode.XORM, vmDt, reg1=regMask, labelSymbol = addressSymbol)
|
||||
}
|
||||
else -> throw AssemblyError("weird prefix operator")
|
||||
}
|
||||
@@ -192,12 +192,12 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
||||
if(ident!=null) {
|
||||
val symbol = ident.targetName.joinToString(".")
|
||||
code += if(zero) {
|
||||
IRCodeInstruction(Opcode.STOREZM, vmDt, labelSymbol = symbol)
|
||||
IRInstruction(Opcode.STOREZM, vmDt, labelSymbol = symbol)
|
||||
} else {
|
||||
if (vmDt == VmDataType.FLOAT)
|
||||
IRCodeInstruction(Opcode.STOREM, vmDt, fpReg1 = resultFpRegister, labelSymbol = symbol)
|
||||
IRInstruction(Opcode.STOREM, vmDt, fpReg1 = resultFpRegister, labelSymbol = symbol)
|
||||
else
|
||||
IRCodeInstruction(Opcode.STOREM, vmDt, reg1 = resultRegister, labelSymbol = symbol)
|
||||
IRInstruction(Opcode.STOREM, vmDt, reg1 = resultRegister, labelSymbol = symbol)
|
||||
}
|
||||
}
|
||||
else if(array!=null) {
|
||||
@@ -215,9 +215,9 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
||||
if(zero) {
|
||||
// there's no STOREZIX instruction
|
||||
resultRegister = codeGen.vmRegisters.nextFree()
|
||||
code += IRCodeInstruction(Opcode.LOAD, vmDt, reg1=resultRegister, value=0)
|
||||
code += IRInstruction(Opcode.LOAD, vmDt, reg1=resultRegister, value=0)
|
||||
}
|
||||
code += IRCodeInstruction(Opcode.STOREIX, vmDt, reg1=resultRegister, reg2=idxReg, labelSymbol = variable)
|
||||
code += IRInstruction(Opcode.STOREIX, vmDt, reg1=resultRegister, reg2=idxReg, labelSymbol = variable)
|
||||
return code
|
||||
}
|
||||
|
||||
@@ -225,30 +225,30 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
||||
if(zero) {
|
||||
if(fixedIndex!=null) {
|
||||
val offset = fixedIndex*itemsize
|
||||
code += IRCodeInstruction(Opcode.STOREZM, vmDt, labelSymbol = "$variable+$offset")
|
||||
code += IRInstruction(Opcode.STOREZM, vmDt, labelSymbol = "$variable+$offset")
|
||||
} else {
|
||||
val indexReg = codeGen.vmRegisters.nextFree()
|
||||
code += loadIndexReg(array, itemsize, indexReg, array.position)
|
||||
code += IRCodeInstruction(Opcode.STOREZX, vmDt, reg1=indexReg, labelSymbol = variable)
|
||||
code += IRInstruction(Opcode.STOREZX, vmDt, reg1=indexReg, labelSymbol = variable)
|
||||
}
|
||||
} else {
|
||||
if(vmDt== VmDataType.FLOAT) {
|
||||
if(fixedIndex!=null) {
|
||||
val offset = fixedIndex*itemsize
|
||||
code += IRCodeInstruction(Opcode.STOREM, vmDt, fpReg1 = resultFpRegister, labelSymbol = "$variable+$offset")
|
||||
code += IRInstruction(Opcode.STOREM, vmDt, fpReg1 = resultFpRegister, labelSymbol = "$variable+$offset")
|
||||
} else {
|
||||
val indexReg = codeGen.vmRegisters.nextFree()
|
||||
code += loadIndexReg(array, itemsize, indexReg, array.position)
|
||||
code += IRCodeInstruction(Opcode.STOREX, vmDt, reg1 = resultRegister, reg2=indexReg, labelSymbol = variable)
|
||||
code += IRInstruction(Opcode.STOREX, vmDt, reg1 = resultRegister, reg2=indexReg, labelSymbol = variable)
|
||||
}
|
||||
} else {
|
||||
if(fixedIndex!=null) {
|
||||
val offset = fixedIndex*itemsize
|
||||
code += IRCodeInstruction(Opcode.STOREM, vmDt, reg1 = resultRegister, labelSymbol = "$variable+$offset")
|
||||
code += IRInstruction(Opcode.STOREM, vmDt, reg1 = resultRegister, labelSymbol = "$variable+$offset")
|
||||
} else {
|
||||
val indexReg = codeGen.vmRegisters.nextFree()
|
||||
code += loadIndexReg(array, itemsize, indexReg, array.position)
|
||||
code += IRCodeInstruction(Opcode.STOREX, vmDt, reg1 = resultRegister, reg2=indexReg, labelSymbol = variable)
|
||||
code += IRInstruction(Opcode.STOREX, vmDt, reg1 = resultRegister, reg2=indexReg, labelSymbol = variable)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -257,19 +257,19 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
||||
require(vmDt== VmDataType.BYTE)
|
||||
if(zero) {
|
||||
if(memory.address is PtNumber) {
|
||||
code += IRCodeInstruction(Opcode.STOREZM, vmDt, value=(memory.address as PtNumber).number.toInt())
|
||||
code += IRInstruction(Opcode.STOREZM, vmDt, value=(memory.address as PtNumber).number.toInt())
|
||||
} else {
|
||||
val addressReg = codeGen.vmRegisters.nextFree()
|
||||
code += expressionEval.translateExpression(memory.address, addressReg, -1)
|
||||
code += IRCodeInstruction(Opcode.STOREZI, vmDt, reg1=addressReg)
|
||||
code += IRInstruction(Opcode.STOREZI, vmDt, reg1=addressReg)
|
||||
}
|
||||
} else {
|
||||
if(memory.address is PtNumber) {
|
||||
code += IRCodeInstruction(Opcode.STOREM, vmDt, reg1=resultRegister, value=(memory.address as PtNumber).number.toInt())
|
||||
code += IRInstruction(Opcode.STOREM, vmDt, reg1=resultRegister, value=(memory.address as PtNumber).number.toInt())
|
||||
} else {
|
||||
val addressReg = codeGen.vmRegisters.nextFree()
|
||||
code += expressionEval.translateExpression(memory.address, addressReg, -1)
|
||||
code += IRCodeInstruction(Opcode.STOREI, vmDt, reg1=resultRegister, reg2=addressReg)
|
||||
code += IRInstruction(Opcode.STOREI, vmDt, reg1=resultRegister, reg2=addressReg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
@@ -56,7 +55,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
val rightRegister = codeGen.vmRegisters.nextFree()
|
||||
code += exprGen.translateExpression(call.args[0], leftRegister, -1)
|
||||
code += exprGen.translateExpression(call.args[1], rightRegister, -1)
|
||||
code += IRCodeInstruction(Opcode.CMP, codeGen.vmType(call.args[0].type), reg1=leftRegister, reg2=rightRegister)
|
||||
code += IRInstruction(Opcode.CMP, codeGen.vmType(call.args[0].type), reg1=leftRegister, reg2=rightRegister)
|
||||
return code
|
||||
}
|
||||
|
||||
@@ -67,17 +66,17 @@ 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)
|
||||
code += IRCodeInstruction(Opcode.LOAD, VmDataType.BYTE, reg1 = 1, value = array.length)
|
||||
code += IRCodeInstruction(Opcode.SYSCALL, value = syscall.ordinal)
|
||||
code += IRInstruction(Opcode.LOAD, VmDataType.BYTE, reg1 = 1, value = array.length)
|
||||
code += IRInstruction(Opcode.SYSCALL, value=syscall.ordinal)
|
||||
if (resultRegister != 0)
|
||||
code += IRCodeInstruction(Opcode.LOADR, VmDataType.BYTE, reg1 = resultRegister, reg2 = 0)
|
||||
code += IRInstruction(Opcode.LOADR, VmDataType.BYTE, reg1 = resultRegister, reg2 = 0)
|
||||
return code
|
||||
}
|
||||
|
||||
@@ -87,18 +86,18 @@ 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)
|
||||
code += exprGen.translateExpression(call.args[0], 0, -1)
|
||||
code += IRCodeInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=1, value=array.length)
|
||||
code += IRCodeInstruction(Opcode.SYSCALL, value=syscall.ordinal)
|
||||
code += IRInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=1, value=array.length)
|
||||
code += IRInstruction(Opcode.SYSCALL, value=syscall.ordinal)
|
||||
if(resultRegister!=0)
|
||||
code += IRCodeInstruction(Opcode.LOADR, VmDataType.BYTE, reg1=resultRegister, reg2=0)
|
||||
code += IRInstruction(Opcode.LOADR, VmDataType.BYTE, reg1=resultRegister, reg2=0)
|
||||
return code
|
||||
}
|
||||
|
||||
@@ -109,25 +108,25 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
code += exprGen.translateExpression(call.args[0], resultRegister, -1)
|
||||
when (sourceDt) {
|
||||
DataType.UBYTE -> {
|
||||
code += IRCodeInstruction(Opcode.EXT, VmDataType.BYTE, reg1=resultRegister)
|
||||
code += IRInstruction(Opcode.EXT, VmDataType.BYTE, reg1=resultRegister)
|
||||
}
|
||||
DataType.BYTE -> {
|
||||
val notNegativeLabel = codeGen.createLabelName()
|
||||
val compareReg = codeGen.vmRegisters.nextFree()
|
||||
code += IRCodeInstruction(Opcode.LOADR, VmDataType.BYTE, reg1=compareReg, reg2=resultRegister)
|
||||
code += IRCodeInstruction(Opcode.AND, VmDataType.BYTE, reg1=compareReg, value=0x80)
|
||||
code += IRCodeInstruction(Opcode.BZ, VmDataType.BYTE, reg1=compareReg, labelSymbol = notNegativeLabel)
|
||||
code += IRCodeInstruction(Opcode.NEG, VmDataType.BYTE, reg1=resultRegister)
|
||||
code += IRCodeInstruction(Opcode.EXT, VmDataType.BYTE, reg1=resultRegister)
|
||||
code += IRInstruction(Opcode.LOADR, VmDataType.BYTE, reg1=compareReg, reg2=resultRegister)
|
||||
code += IRInstruction(Opcode.AND, VmDataType.BYTE, reg1=compareReg, value=0x80)
|
||||
code += IRInstruction(Opcode.BZ, VmDataType.BYTE, reg1=compareReg, labelSymbol = notNegativeLabel)
|
||||
code += IRInstruction(Opcode.NEG, VmDataType.BYTE, reg1=resultRegister)
|
||||
code += IRInstruction(Opcode.EXT, VmDataType.BYTE, reg1=resultRegister)
|
||||
code += IRCodeLabel(notNegativeLabel)
|
||||
}
|
||||
DataType.WORD -> {
|
||||
val notNegativeLabel = codeGen.createLabelName()
|
||||
val compareReg = codeGen.vmRegisters.nextFree()
|
||||
code += IRCodeInstruction(Opcode.LOADR, VmDataType.WORD, reg1=compareReg, reg2=resultRegister)
|
||||
code += IRCodeInstruction(Opcode.AND, VmDataType.WORD, reg1=compareReg, value=0x8000)
|
||||
code += IRCodeInstruction(Opcode.BZ, VmDataType.WORD, reg1=compareReg, labelSymbol = notNegativeLabel)
|
||||
code += IRCodeInstruction(Opcode.NEG, VmDataType.WORD, reg1=resultRegister)
|
||||
code += IRInstruction(Opcode.LOADR, VmDataType.WORD, reg1=compareReg, reg2=resultRegister)
|
||||
code += IRInstruction(Opcode.AND, VmDataType.WORD, reg1=compareReg, value=0x8000)
|
||||
code += IRInstruction(Opcode.BZ, VmDataType.WORD, reg1=compareReg, labelSymbol = notNegativeLabel)
|
||||
code += IRInstruction(Opcode.NEG, VmDataType.WORD, reg1=resultRegister)
|
||||
code += IRCodeLabel(notNegativeLabel)
|
||||
}
|
||||
else -> throw AssemblyError("weird type")
|
||||
@@ -140,7 +139,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
val code = IRCodeChunk(call.position)
|
||||
val reg = codeGen.vmRegisters.nextFree()
|
||||
code += exprGen.translateExpression(call.args.single(), reg, -1)
|
||||
code += IRCodeInstruction(Opcode.SGN, codeGen.vmType(call.type), reg1=resultRegister, reg2=reg)
|
||||
code += IRInstruction(Opcode.SGN, codeGen.vmType(call.type), reg1=resultRegister, reg2=reg)
|
||||
return code
|
||||
}
|
||||
|
||||
@@ -148,14 +147,14 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
val code = IRCodeChunk(call.position)
|
||||
val reg = codeGen.vmRegisters.nextFree()
|
||||
code += exprGen.translateExpression(call.args.single(), reg, -1)
|
||||
code += IRCodeInstruction(Opcode.SQRT, VmDataType.WORD, reg1=resultRegister, reg2=reg)
|
||||
code += IRInstruction(Opcode.SQRT, VmDataType.WORD, reg1=resultRegister, reg2=reg)
|
||||
return code
|
||||
}
|
||||
|
||||
private fun funcPop(call: PtBuiltinFunctionCall): IRCodeChunk {
|
||||
val code = IRCodeChunk(call.position)
|
||||
val reg = codeGen.vmRegisters.nextFree()
|
||||
code += IRCodeInstruction(Opcode.POP, VmDataType.BYTE, reg1=reg)
|
||||
code += IRInstruction(Opcode.POP, VmDataType.BYTE, reg1=reg)
|
||||
code += assignRegisterTo(call.args.single(), reg)
|
||||
return code
|
||||
}
|
||||
@@ -163,7 +162,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
private fun funcPopw(call: PtBuiltinFunctionCall): IRCodeChunk {
|
||||
val code = IRCodeChunk(call.position)
|
||||
val reg = codeGen.vmRegisters.nextFree()
|
||||
code += IRCodeInstruction(Opcode.POP, VmDataType.WORD, reg1=reg)
|
||||
code += IRInstruction(Opcode.POP, VmDataType.WORD, reg1=reg)
|
||||
code += assignRegisterTo(call.args.single(), reg)
|
||||
return code
|
||||
}
|
||||
@@ -172,7 +171,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
val code = IRCodeChunk(call.position)
|
||||
val reg = codeGen.vmRegisters.nextFree()
|
||||
code += exprGen.translateExpression(call.args.single(), reg, -1)
|
||||
code += IRCodeInstruction(Opcode.PUSH, VmDataType.BYTE, reg1=reg)
|
||||
code += IRInstruction(Opcode.PUSH, VmDataType.BYTE, reg1=reg)
|
||||
return code
|
||||
}
|
||||
|
||||
@@ -180,44 +179,44 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
val code = IRCodeChunk(call.position)
|
||||
val reg = codeGen.vmRegisters.nextFree()
|
||||
code += exprGen.translateExpression(call.args.single(), reg, -1)
|
||||
code += IRCodeInstruction(Opcode.PUSH, VmDataType.WORD, reg1=reg)
|
||||
code += IRInstruction(Opcode.PUSH, VmDataType.WORD, reg1=reg)
|
||||
return code
|
||||
}
|
||||
|
||||
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 += IRInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=1, value=array.length)
|
||||
code += IRInstruction(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 += IRInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=1, value=array.length)
|
||||
code += IRInstruction(Opcode.SYSCALL, value=syscall.ordinal)
|
||||
return code
|
||||
}
|
||||
|
||||
@@ -226,7 +225,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
val code = IRCodeChunk(call.position)
|
||||
code += exprGen.translateExpression(call.args[0], msbReg, -1)
|
||||
code += exprGen.translateExpression(call.args[1], resultRegister, -1)
|
||||
code += IRCodeInstruction(Opcode.CONCAT, VmDataType.BYTE, reg1=resultRegister, reg2=msbReg)
|
||||
code += IRInstruction(Opcode.CONCAT, VmDataType.BYTE, reg1=resultRegister, reg2=msbReg)
|
||||
return code
|
||||
}
|
||||
|
||||
@@ -235,23 +234,23 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
if(codeGen.isZero(call.args[1])) {
|
||||
if (call.args[0] is PtNumber) {
|
||||
val address = (call.args[0] as PtNumber).number.toInt()
|
||||
code += IRCodeInstruction(Opcode.STOREZM, VmDataType.WORD, value = address)
|
||||
code += IRInstruction(Opcode.STOREZM, VmDataType.WORD, value = address)
|
||||
} else {
|
||||
val addressReg = codeGen.vmRegisters.nextFree()
|
||||
code += exprGen.translateExpression(call.args[0], addressReg, -1)
|
||||
code += IRCodeInstruction(Opcode.STOREZI, VmDataType.WORD, reg2 = addressReg)
|
||||
code += IRInstruction(Opcode.STOREZI, VmDataType.WORD, reg2 = addressReg)
|
||||
}
|
||||
} else {
|
||||
val valueReg = codeGen.vmRegisters.nextFree()
|
||||
if (call.args[0] is PtNumber) {
|
||||
val address = (call.args[0] as PtNumber).number.toInt()
|
||||
code += exprGen.translateExpression(call.args[1], valueReg, -1)
|
||||
code += IRCodeInstruction(Opcode.STOREM, VmDataType.WORD, reg1 = valueReg, value = address)
|
||||
code += IRInstruction(Opcode.STOREM, VmDataType.WORD, reg1 = valueReg, value = address)
|
||||
} else {
|
||||
val addressReg = codeGen.vmRegisters.nextFree()
|
||||
code += exprGen.translateExpression(call.args[0], addressReg, -1)
|
||||
code += exprGen.translateExpression(call.args[1], valueReg, -1)
|
||||
code += IRCodeInstruction(Opcode.STOREI, VmDataType.WORD, reg1 = valueReg, reg2 = addressReg)
|
||||
code += IRInstruction(Opcode.STOREI, VmDataType.WORD, reg1 = valueReg, reg2 = addressReg)
|
||||
}
|
||||
}
|
||||
return code
|
||||
@@ -262,23 +261,23 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
if(codeGen.isZero(call.args[1])) {
|
||||
if (call.args[0] is PtNumber) {
|
||||
val address = (call.args[0] as PtNumber).number.toInt()
|
||||
code += IRCodeInstruction(Opcode.STOREZM, VmDataType.BYTE, value = address)
|
||||
code += IRInstruction(Opcode.STOREZM, VmDataType.BYTE, value = address)
|
||||
} else {
|
||||
val addressReg = codeGen.vmRegisters.nextFree()
|
||||
code += exprGen.translateExpression(call.args[0], addressReg, -1)
|
||||
code += IRCodeInstruction(Opcode.STOREZI, VmDataType.BYTE, reg2 = addressReg)
|
||||
code += IRInstruction(Opcode.STOREZI, VmDataType.BYTE, reg2 = addressReg)
|
||||
}
|
||||
} else {
|
||||
val valueReg = codeGen.vmRegisters.nextFree()
|
||||
if (call.args[0] is PtNumber) {
|
||||
val address = (call.args[0] as PtNumber).number.toInt()
|
||||
code += exprGen.translateExpression(call.args[1], valueReg, -1)
|
||||
code += IRCodeInstruction(Opcode.STOREM, VmDataType.BYTE, reg1 = valueReg, value = address)
|
||||
code += IRInstruction(Opcode.STOREM, VmDataType.BYTE, reg1 = valueReg, value = address)
|
||||
} else {
|
||||
val addressReg = codeGen.vmRegisters.nextFree()
|
||||
code += exprGen.translateExpression(call.args[0], addressReg, -1)
|
||||
code += exprGen.translateExpression(call.args[1], valueReg, -1)
|
||||
code += IRCodeInstruction(Opcode.STOREI, VmDataType.BYTE, reg1 = valueReg, reg2 = addressReg)
|
||||
code += IRInstruction(Opcode.STOREI, VmDataType.BYTE, reg1 = valueReg, reg2 = addressReg)
|
||||
}
|
||||
}
|
||||
return code
|
||||
@@ -288,11 +287,11 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
val code = IRCodeChunk(call.position)
|
||||
if(call.args[0] is PtNumber) {
|
||||
val address = (call.args[0] as PtNumber).number.toInt()
|
||||
code += IRCodeInstruction(Opcode.LOADM, VmDataType.WORD, reg1 = resultRegister, value = address)
|
||||
code += IRInstruction(Opcode.LOADM, VmDataType.WORD, reg1 = resultRegister, value = address)
|
||||
} else {
|
||||
val addressReg = codeGen.vmRegisters.nextFree()
|
||||
code += exprGen.translateExpression(call.args.single(), addressReg, -1)
|
||||
code += IRCodeInstruction(Opcode.LOADI, VmDataType.WORD, reg1 = resultRegister, reg2 = addressReg)
|
||||
code += IRInstruction(Opcode.LOADI, VmDataType.WORD, reg1 = resultRegister, reg2 = addressReg)
|
||||
}
|
||||
return code
|
||||
}
|
||||
@@ -301,34 +300,31 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
val code = IRCodeChunk(call.position)
|
||||
if(call.args[0] is PtNumber) {
|
||||
val address = (call.args[0] as PtNumber).number.toInt()
|
||||
code += IRCodeInstruction(Opcode.LOADM, VmDataType.BYTE, reg1 = resultRegister, value = address)
|
||||
code += IRInstruction(Opcode.LOADM, VmDataType.BYTE, reg1 = resultRegister, value = address)
|
||||
} else {
|
||||
val addressReg = codeGen.vmRegisters.nextFree()
|
||||
code += exprGen.translateExpression(call.args.single(), addressReg, -1)
|
||||
code += IRCodeInstruction(Opcode.LOADI, VmDataType.BYTE, reg1 = resultRegister, reg2 = addressReg)
|
||||
code += IRInstruction(Opcode.LOADI, VmDataType.BYTE, reg1 = resultRegister, reg2 = addressReg)
|
||||
}
|
||||
return code
|
||||
}
|
||||
|
||||
private fun funcRnd(resultRegister: Int, position: Position): IRCodeChunk {
|
||||
val code = IRCodeChunk(position)
|
||||
code += IRCodeInstruction(Opcode.RND, VmDataType.BYTE, reg1=resultRegister)
|
||||
code += IRInstruction(Opcode.RND, VmDataType.BYTE, reg1=resultRegister)
|
||||
return code
|
||||
}
|
||||
|
||||
private fun funcRndw(resultRegister: Int, position: Position): IRCodeChunk {
|
||||
val code = IRCodeChunk(position)
|
||||
code += IRCodeInstruction(Opcode.RND, VmDataType.WORD, reg1=resultRegister)
|
||||
code += IRInstruction(Opcode.RND, VmDataType.WORD, reg1=resultRegister)
|
||||
return code
|
||||
}
|
||||
|
||||
private fun funcMemory(call: PtBuiltinFunctionCall, resultRegister: Int): IRCodeChunk {
|
||||
val name = (call.args[0] as PtString).value
|
||||
val size = (call.args[1] as PtNumber).number.toUInt()
|
||||
val align = (call.args[2] as PtNumber).number.toUInt()
|
||||
val label = codeGen.addMemorySlab(name, size, align, call.position)
|
||||
val code = IRCodeChunk(call.position)
|
||||
code += IRCodeInstruction(Opcode.LOAD, VmDataType.WORD, reg1=resultRegister, labelSymbol = label)
|
||||
code += IRInstruction(Opcode.LOAD, VmDataType.WORD, reg1=resultRegister, labelSymbol = "prog8_slabs.prog8_memoryslab_$name")
|
||||
return code
|
||||
}
|
||||
|
||||
@@ -342,7 +338,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
private fun funcMsb(call: PtBuiltinFunctionCall, resultRegister: Int): IRCodeChunk {
|
||||
val code = IRCodeChunk(call.position)
|
||||
code += exprGen.translateExpression(call.args.single(), resultRegister, -1)
|
||||
code += IRCodeInstruction(Opcode.MSIG, VmDataType.BYTE, reg1 = resultRegister, reg2=resultRegister)
|
||||
code += IRInstruction(Opcode.MSIG, VmDataType.BYTE, reg1 = resultRegister, reg2=resultRegister)
|
||||
// note: if a word result is needed, the upper byte is cleared by the typecast that follows. No need to do it here.
|
||||
return code
|
||||
}
|
||||
@@ -351,7 +347,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
val vmDt = codeGen.vmType(call.args[0].type)
|
||||
val code = IRCodeChunk(call.position)
|
||||
code += exprGen.translateExpression(call.args[0], resultRegister, -1)
|
||||
code += IRCodeInstruction(opcode, vmDt, reg1=resultRegister)
|
||||
code += IRInstruction(opcode, vmDt, reg1=resultRegister)
|
||||
code += assignRegisterTo(call.args[0], resultRegister)
|
||||
return code
|
||||
}
|
||||
|
||||
@@ -18,43 +18,43 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
is PtMachineRegister -> {
|
||||
if(resultRegister!=expr.register) {
|
||||
val vmDt = codeGen.vmType(expr.type)
|
||||
code += IRCodeInstruction(Opcode.LOADR, vmDt, reg1=resultRegister, reg2=expr.register)
|
||||
code += IRInstruction(Opcode.LOADR, vmDt, reg1=resultRegister, reg2=expr.register)
|
||||
}
|
||||
}
|
||||
is PtNumber -> {
|
||||
val vmDt = codeGen.vmType(expr.type)
|
||||
code += if(vmDt==VmDataType.FLOAT)
|
||||
IRCodeInstruction(Opcode.LOAD, vmDt, fpReg1 = resultFpRegister, fpValue = expr.number.toFloat())
|
||||
IRInstruction(Opcode.LOAD, vmDt, fpReg1 = resultFpRegister, fpValue = expr.number.toFloat())
|
||||
else
|
||||
IRCodeInstruction(Opcode.LOAD, vmDt, reg1=resultRegister, value=expr.number.toInt())
|
||||
IRInstruction(Opcode.LOAD, vmDt, reg1=resultRegister, value=expr.number.toInt())
|
||||
}
|
||||
is PtIdentifier -> {
|
||||
val vmDt = codeGen.vmType(expr.type)
|
||||
val symbol = expr.targetName.joinToString(".")
|
||||
code += if (expr.type in PassByValueDatatypes) {
|
||||
if(vmDt==VmDataType.FLOAT)
|
||||
IRCodeInstruction(Opcode.LOADM, vmDt, fpReg1 = resultFpRegister, labelSymbol = symbol)
|
||||
IRInstruction(Opcode.LOADM, vmDt, fpReg1 = resultFpRegister, labelSymbol = symbol)
|
||||
else
|
||||
IRCodeInstruction(Opcode.LOADM, vmDt, reg1 = resultRegister, labelSymbol = symbol)
|
||||
IRInstruction(Opcode.LOADM, vmDt, reg1 = resultRegister, labelSymbol = symbol)
|
||||
} else {
|
||||
// for strings and arrays etc., load the *address* of the value instead
|
||||
IRCodeInstruction(Opcode.LOAD, vmDt, reg1 = resultRegister, labelSymbol = symbol)
|
||||
IRInstruction(Opcode.LOAD, vmDt, reg1 = resultRegister, labelSymbol = symbol)
|
||||
}
|
||||
}
|
||||
is PtAddressOf -> {
|
||||
val vmDt = codeGen.vmType(expr.type)
|
||||
val symbol = expr.identifier.targetName.joinToString(".")
|
||||
// note: LOAD <symbol> gets you the address of the symbol, whereas LOADM <symbol> would get you the value stored at that location
|
||||
code += IRCodeInstruction(Opcode.LOAD, vmDt, reg1=resultRegister, labelSymbol = symbol)
|
||||
code += IRInstruction(Opcode.LOAD, vmDt, reg1=resultRegister, labelSymbol = symbol)
|
||||
}
|
||||
is PtMemoryByte -> {
|
||||
if(expr.address is PtNumber) {
|
||||
val address = (expr.address as PtNumber).number.toInt()
|
||||
code += IRCodeInstruction(Opcode.LOADM, VmDataType.BYTE, reg1=resultRegister, value = address)
|
||||
code += IRInstruction(Opcode.LOADM, VmDataType.BYTE, reg1=resultRegister, value = address)
|
||||
} else {
|
||||
val addressRegister = codeGen.vmRegisters.nextFree()
|
||||
code += translateExpression(expr.address, addressRegister, -1)
|
||||
code += IRCodeInstruction(Opcode.LOADI, VmDataType.BYTE, reg1=resultRegister, reg2=addressRegister)
|
||||
code += IRInstruction(Opcode.LOADI, VmDataType.BYTE, reg1=resultRegister, reg2=addressRegister)
|
||||
}
|
||||
}
|
||||
is PtTypeCast -> code += translate(expr, resultRegister, resultFpRegister)
|
||||
@@ -117,24 +117,24 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
if(arrayIx.index.type!=DataType.UBYTE)
|
||||
throw AssemblyError("non-array var indexing requires bytes index")
|
||||
code += translateExpression(arrayIx.index, idxReg, -1)
|
||||
code += IRCodeInstruction(Opcode.LOADIX, vmDt, reg1=resultRegister, reg2=idxReg, labelSymbol = arrayVarSymbol)
|
||||
code += IRInstruction(Opcode.LOADIX, vmDt, reg1=resultRegister, reg2=idxReg, labelSymbol = arrayVarSymbol)
|
||||
return code
|
||||
}
|
||||
|
||||
if(arrayIx.index is PtNumber) {
|
||||
val memOffset = ((arrayIx.index as PtNumber).number.toInt() * eltSize).toString()
|
||||
if(vmDt==VmDataType.FLOAT)
|
||||
code += IRCodeInstruction(Opcode.LOADM, VmDataType.FLOAT, fpReg1=resultFpRegister, labelSymbol = "$arrayVarSymbol+$memOffset")
|
||||
code += IRInstruction(Opcode.LOADM, VmDataType.FLOAT, fpReg1=resultFpRegister, labelSymbol = "$arrayVarSymbol+$memOffset")
|
||||
else
|
||||
code += IRCodeInstruction(Opcode.LOADM, vmDt, reg1=resultRegister, labelSymbol = "$arrayVarSymbol+$memOffset")
|
||||
code += IRInstruction(Opcode.LOADM, vmDt, reg1=resultRegister, labelSymbol = "$arrayVarSymbol+$memOffset")
|
||||
} else {
|
||||
code += translateExpression(arrayIx.index, idxReg, -1)
|
||||
if(eltSize>1)
|
||||
code += codeGen.multiplyByConst(VmDataType.BYTE, idxReg, eltSize, arrayIx.position)
|
||||
if(vmDt==VmDataType.FLOAT)
|
||||
code += IRCodeInstruction(Opcode.LOADX, VmDataType.FLOAT, fpReg1 = resultFpRegister, reg1=idxReg, labelSymbol = arrayVarSymbol)
|
||||
code += IRInstruction(Opcode.LOADX, VmDataType.FLOAT, fpReg1 = resultFpRegister, reg1=idxReg, labelSymbol = arrayVarSymbol)
|
||||
else
|
||||
code += IRCodeInstruction(Opcode.LOADX, vmDt, reg1=resultRegister, reg2=idxReg, labelSymbol = arrayVarSymbol)
|
||||
code += IRInstruction(Opcode.LOADX, vmDt, reg1=resultRegister, reg2=idxReg, labelSymbol = arrayVarSymbol)
|
||||
}
|
||||
return code
|
||||
}
|
||||
@@ -146,11 +146,11 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
when(expr.operator) {
|
||||
"+" -> { }
|
||||
"-" -> {
|
||||
code += IRCodeInstruction(Opcode.NEG, vmDt, reg1=resultRegister)
|
||||
code += IRInstruction(Opcode.NEG, vmDt, reg1=resultRegister)
|
||||
}
|
||||
"~" -> {
|
||||
val mask = if(vmDt==VmDataType.BYTE) 0x00ff else 0xffff
|
||||
code += IRCodeInstruction(Opcode.XOR, vmDt, reg1=resultRegister, value=mask)
|
||||
code += IRInstruction(Opcode.XOR, vmDt, reg1=resultRegister, value=mask)
|
||||
}
|
||||
else -> throw AssemblyError("weird prefix operator")
|
||||
}
|
||||
@@ -173,14 +173,14 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
DataType.UBYTE -> {
|
||||
when(cast.value.type) {
|
||||
DataType.BYTE, DataType.UWORD, DataType.WORD -> { /* just keep the LSB as it is */ }
|
||||
DataType.FLOAT -> code += IRCodeInstruction(Opcode.FTOUB, VmDataType.FLOAT, reg1=actualResultReg, fpReg1 = actualResultFpReg)
|
||||
DataType.FLOAT -> code += IRInstruction(Opcode.FTOUB, VmDataType.FLOAT, reg1=actualResultReg, fpReg1 = actualResultFpReg)
|
||||
else -> throw AssemblyError("weird cast value type")
|
||||
}
|
||||
}
|
||||
DataType.BYTE -> {
|
||||
when(cast.value.type) {
|
||||
DataType.UBYTE, DataType.UWORD, DataType.WORD -> { /* just keep the LSB as it is */ }
|
||||
DataType.FLOAT -> code += IRCodeInstruction(Opcode.FTOSB, VmDataType.FLOAT, reg1=actualResultReg, fpReg1 = actualResultFpReg)
|
||||
DataType.FLOAT -> code += IRInstruction(Opcode.FTOSB, VmDataType.FLOAT, reg1=actualResultReg, fpReg1 = actualResultFpReg)
|
||||
else -> throw AssemblyError("weird cast value type")
|
||||
}
|
||||
}
|
||||
@@ -188,15 +188,15 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
when(cast.value.type) {
|
||||
DataType.BYTE -> {
|
||||
// byte -> uword: sign extend
|
||||
code += IRCodeInstruction(Opcode.EXTS, type = VmDataType.BYTE, reg1 = actualResultReg)
|
||||
code += IRInstruction(Opcode.EXTS, type = VmDataType.BYTE, reg1 = actualResultReg)
|
||||
}
|
||||
DataType.UBYTE -> {
|
||||
// ubyte -> uword: sign extend
|
||||
code += IRCodeInstruction(Opcode.EXT, type = VmDataType.BYTE, reg1 = actualResultReg)
|
||||
code += IRInstruction(Opcode.EXT, type = VmDataType.BYTE, reg1 = actualResultReg)
|
||||
}
|
||||
DataType.WORD -> { }
|
||||
DataType.FLOAT -> {
|
||||
code += IRCodeInstruction(Opcode.FTOUW, VmDataType.FLOAT, reg1=actualResultReg, fpReg1 = actualResultFpReg)
|
||||
code += IRInstruction(Opcode.FTOUW, VmDataType.FLOAT, reg1=actualResultReg, fpReg1 = actualResultFpReg)
|
||||
}
|
||||
else -> throw AssemblyError("weird cast value type")
|
||||
}
|
||||
@@ -205,15 +205,15 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
when(cast.value.type) {
|
||||
DataType.BYTE -> {
|
||||
// byte -> word: sign extend
|
||||
code += IRCodeInstruction(Opcode.EXTS, type = VmDataType.BYTE, reg1 = actualResultReg)
|
||||
code += IRInstruction(Opcode.EXTS, type = VmDataType.BYTE, reg1 = actualResultReg)
|
||||
}
|
||||
DataType.UBYTE -> {
|
||||
// byte -> word: sign extend
|
||||
code += IRCodeInstruction(Opcode.EXT, type = VmDataType.BYTE, reg1 = actualResultReg)
|
||||
code += IRInstruction(Opcode.EXT, type = VmDataType.BYTE, reg1 = actualResultReg)
|
||||
}
|
||||
DataType.UWORD -> { }
|
||||
DataType.FLOAT -> {
|
||||
code += IRCodeInstruction(Opcode.FTOSW, VmDataType.FLOAT, reg1=actualResultReg, fpReg1 = actualResultFpReg)
|
||||
code += IRInstruction(Opcode.FTOSW, VmDataType.FLOAT, reg1=actualResultReg, fpReg1 = actualResultFpReg)
|
||||
}
|
||||
else -> throw AssemblyError("weird cast value type")
|
||||
}
|
||||
@@ -221,16 +221,16 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
DataType.FLOAT -> {
|
||||
code += when(cast.value.type) {
|
||||
DataType.UBYTE -> {
|
||||
IRCodeInstruction(Opcode.FFROMUB, VmDataType.FLOAT, reg1=actualResultReg, fpReg1 = actualResultFpReg)
|
||||
IRInstruction(Opcode.FFROMUB, VmDataType.FLOAT, reg1=actualResultReg, fpReg1 = actualResultFpReg)
|
||||
}
|
||||
DataType.BYTE -> {
|
||||
IRCodeInstruction(Opcode.FFROMSB, VmDataType.FLOAT, reg1=actualResultReg, fpReg1 = actualResultFpReg)
|
||||
IRInstruction(Opcode.FFROMSB, VmDataType.FLOAT, reg1=actualResultReg, fpReg1 = actualResultFpReg)
|
||||
}
|
||||
DataType.UWORD -> {
|
||||
IRCodeInstruction(Opcode.FFROMUW, VmDataType.FLOAT, reg1=actualResultReg, fpReg1 = actualResultFpReg)
|
||||
IRInstruction(Opcode.FFROMUW, VmDataType.FLOAT, reg1=actualResultReg, fpReg1 = actualResultFpReg)
|
||||
}
|
||||
DataType.WORD -> {
|
||||
IRCodeInstruction(Opcode.FFROMSW, VmDataType.FLOAT, reg1=actualResultReg, fpReg1 = actualResultFpReg)
|
||||
IRInstruction(Opcode.FFROMSW, VmDataType.FLOAT, reg1=actualResultReg, fpReg1 = actualResultFpReg)
|
||||
}
|
||||
else -> throw AssemblyError("weird cast value type")
|
||||
}
|
||||
@@ -278,14 +278,14 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
val zeroRegister = codeGen.vmRegisters.nextFree()
|
||||
code += translateExpression(binExpr.left, -1, leftFpReg)
|
||||
code += translateExpression(binExpr.right, -1, rightFpReg)
|
||||
code += IRCodeInstruction(Opcode.FCOMP, VmDataType.FLOAT, reg1=resultRegister, fpReg1 = leftFpReg, fpReg2 = rightFpReg)
|
||||
code += IRCodeInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=zeroRegister, value=0)
|
||||
code += IRInstruction(Opcode.FCOMP, VmDataType.FLOAT, reg1=resultRegister, fpReg1 = leftFpReg, fpReg2 = rightFpReg)
|
||||
code += IRInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=zeroRegister, value=0)
|
||||
val ins = if (signed) {
|
||||
if (greaterEquals) Opcode.SGES else Opcode.SGTS
|
||||
} else {
|
||||
if (greaterEquals) Opcode.SGE else Opcode.SGT
|
||||
}
|
||||
code += IRCodeInstruction(ins, VmDataType.BYTE, reg1 = resultRegister, reg2 = zeroRegister)
|
||||
code += IRInstruction(ins, VmDataType.BYTE, reg1 = resultRegister, reg2 = zeroRegister)
|
||||
} else {
|
||||
if(binExpr.left.type==DataType.STR && binExpr.right.type==DataType.STR) {
|
||||
val comparisonCall = PtFunctionCall(listOf("prog8_lib", "string_compare"), false, DataType.BYTE, Position.DUMMY)
|
||||
@@ -293,11 +293,11 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
comparisonCall.children.add(binExpr.right)
|
||||
code += translate(comparisonCall, resultRegister, -1)
|
||||
val zeroRegister = codeGen.vmRegisters.nextFree()
|
||||
code += IRCodeInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=zeroRegister, value=0)
|
||||
code += IRInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=zeroRegister, value=0)
|
||||
code += if(greaterEquals)
|
||||
IRCodeInstruction(Opcode.SGES, VmDataType.BYTE, reg1=resultRegister, reg2=zeroRegister)
|
||||
IRInstruction(Opcode.SGES, VmDataType.BYTE, reg1=resultRegister, reg2=zeroRegister)
|
||||
else
|
||||
IRCodeInstruction(Opcode.SGTS, VmDataType.BYTE, reg1=resultRegister, reg2=zeroRegister)
|
||||
IRInstruction(Opcode.SGTS, VmDataType.BYTE, reg1=resultRegister, reg2=zeroRegister)
|
||||
} else {
|
||||
val rightResultReg = codeGen.vmRegisters.nextFree()
|
||||
code += translateExpression(binExpr.left, resultRegister, -1)
|
||||
@@ -307,7 +307,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
} else {
|
||||
if (greaterEquals) Opcode.SGE else Opcode.SGT
|
||||
}
|
||||
code += IRCodeInstruction(ins, vmDt, reg1 = resultRegister, reg2 = rightResultReg)
|
||||
code += IRInstruction(ins, vmDt, reg1 = resultRegister, reg2 = rightResultReg)
|
||||
}
|
||||
}
|
||||
return code
|
||||
@@ -327,14 +327,14 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
val zeroRegister = codeGen.vmRegisters.nextFree()
|
||||
code += translateExpression(binExpr.left, -1, leftFpReg)
|
||||
code += translateExpression(binExpr.right, -1, rightFpReg)
|
||||
code += IRCodeInstruction(Opcode.FCOMP, VmDataType.FLOAT, reg1=resultRegister, fpReg1 = leftFpReg, fpReg2 = rightFpReg)
|
||||
code += IRCodeInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=zeroRegister, value=0)
|
||||
code += IRInstruction(Opcode.FCOMP, VmDataType.FLOAT, reg1=resultRegister, fpReg1 = leftFpReg, fpReg2 = rightFpReg)
|
||||
code += IRInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=zeroRegister, value=0)
|
||||
val ins = if (signed) {
|
||||
if (lessEquals) Opcode.SLES else Opcode.SLTS
|
||||
} else {
|
||||
if (lessEquals) Opcode.SLE else Opcode.SLT
|
||||
}
|
||||
code += IRCodeInstruction(ins, VmDataType.BYTE, reg1 = resultRegister, reg2 = zeroRegister)
|
||||
code += IRInstruction(ins, VmDataType.BYTE, reg1 = resultRegister, reg2 = zeroRegister)
|
||||
} else {
|
||||
if(binExpr.left.type==DataType.STR && binExpr.right.type==DataType.STR) {
|
||||
val comparisonCall = PtFunctionCall(listOf("prog8_lib", "string_compare"), false, DataType.BYTE, Position.DUMMY)
|
||||
@@ -342,11 +342,11 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
comparisonCall.children.add(binExpr.right)
|
||||
code += translate(comparisonCall, resultRegister, -1)
|
||||
val zeroRegister = codeGen.vmRegisters.nextFree()
|
||||
code += IRCodeInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=zeroRegister, value=0)
|
||||
code += IRInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=zeroRegister, value=0)
|
||||
code += if(lessEquals)
|
||||
IRCodeInstruction(Opcode.SLES, VmDataType.BYTE, reg1=resultRegister, reg2=zeroRegister)
|
||||
IRInstruction(Opcode.SLES, VmDataType.BYTE, reg1=resultRegister, reg2=zeroRegister)
|
||||
else
|
||||
IRCodeInstruction(Opcode.SLTS, VmDataType.BYTE, reg1=resultRegister, reg2=zeroRegister)
|
||||
IRInstruction(Opcode.SLTS, VmDataType.BYTE, reg1=resultRegister, reg2=zeroRegister)
|
||||
} else {
|
||||
val rightResultReg = codeGen.vmRegisters.nextFree()
|
||||
code += translateExpression(binExpr.left, resultRegister, -1)
|
||||
@@ -356,7 +356,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
} else {
|
||||
if (lessEquals) Opcode.SLE else Opcode.SLT
|
||||
}
|
||||
code += IRCodeInstruction(ins, vmDt, reg1 = resultRegister, reg2 = rightResultReg)
|
||||
code += IRInstruction(ins, vmDt, reg1 = resultRegister, reg2 = rightResultReg)
|
||||
}
|
||||
}
|
||||
return code
|
||||
@@ -370,14 +370,14 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
code += translateExpression(binExpr.left, -1, leftFpReg)
|
||||
code += translateExpression(binExpr.right, -1, rightFpReg)
|
||||
if (notEquals) {
|
||||
code += IRCodeInstruction(Opcode.FCOMP, VmDataType.FLOAT, reg1=resultRegister, fpReg1 = leftFpReg, fpReg2 = rightFpReg)
|
||||
code += IRInstruction(Opcode.FCOMP, VmDataType.FLOAT, reg1=resultRegister, fpReg1 = leftFpReg, fpReg2 = rightFpReg)
|
||||
} else {
|
||||
val label = codeGen.createLabelName()
|
||||
val valueReg = codeGen.vmRegisters.nextFree()
|
||||
code += IRCodeInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=resultRegister, value=1)
|
||||
code += IRCodeInstruction(Opcode.FCOMP, VmDataType.FLOAT, reg1=valueReg, fpReg1 = leftFpReg, fpReg2 = rightFpReg)
|
||||
code += IRCodeInstruction(Opcode.BZ, VmDataType.BYTE, reg1=valueReg, labelSymbol = label)
|
||||
code += IRCodeInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=resultRegister, value=0)
|
||||
code += IRInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=resultRegister, value=1)
|
||||
code += IRInstruction(Opcode.FCOMP, VmDataType.FLOAT, reg1=valueReg, fpReg1 = leftFpReg, fpReg2 = rightFpReg)
|
||||
code += IRInstruction(Opcode.BZ, VmDataType.BYTE, reg1=valueReg, labelSymbol = label)
|
||||
code += IRInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=resultRegister, value=0)
|
||||
code += IRCodeLabel(label)
|
||||
}
|
||||
} else {
|
||||
@@ -387,14 +387,14 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
comparisonCall.children.add(binExpr.right)
|
||||
code += translate(comparisonCall, resultRegister, -1)
|
||||
if(!notEquals)
|
||||
code += IRCodeInstruction(Opcode.INV, vmDt, reg1=resultRegister)
|
||||
code += IRCodeInstruction(Opcode.AND, vmDt, reg1=resultRegister, value=1)
|
||||
code += IRInstruction(Opcode.INV, vmDt, reg1=resultRegister)
|
||||
code += IRInstruction(Opcode.AND, vmDt, reg1=resultRegister, value=1)
|
||||
} else {
|
||||
val rightResultReg = codeGen.vmRegisters.nextFree()
|
||||
code += translateExpression(binExpr.left, resultRegister, -1)
|
||||
code += translateExpression(binExpr.right, rightResultReg, -1)
|
||||
val opcode = if (notEquals) Opcode.SNE else Opcode.SEQ
|
||||
code += IRCodeInstruction(opcode, vmDt, reg1 = resultRegister, reg2 = rightResultReg)
|
||||
code += IRInstruction(opcode, vmDt, reg1 = resultRegister, reg2 = rightResultReg)
|
||||
}
|
||||
}
|
||||
return code
|
||||
@@ -405,13 +405,13 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
if(codeGen.isOne(binExpr.right)) {
|
||||
code += translateExpression(binExpr.left, resultRegister, -1)
|
||||
val opc = if (signed) Opcode.ASR else Opcode.LSR
|
||||
code += IRCodeInstruction(opc, vmDt, reg1 = resultRegister)
|
||||
code += IRInstruction(opc, vmDt, reg1 = resultRegister)
|
||||
} else {
|
||||
val rightResultReg = codeGen.vmRegisters.nextFree()
|
||||
code += translateExpression(binExpr.left, resultRegister, -1)
|
||||
code += translateExpression(binExpr.right, rightResultReg, -1)
|
||||
val opc = if (signed) Opcode.ASRN else Opcode.LSRN
|
||||
code += IRCodeInstruction(opc, vmDt, reg1 = resultRegister, reg2 = rightResultReg)
|
||||
code += IRInstruction(opc, vmDt, reg1 = resultRegister, reg2 = rightResultReg)
|
||||
}
|
||||
return code
|
||||
}
|
||||
@@ -421,17 +421,17 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
if(codeGen.isOne(operand)) {
|
||||
val opc = if (signed) Opcode.ASRM else Opcode.LSRM
|
||||
code += if(knownAddress!=null)
|
||||
IRCodeInstruction(opc, vmDt, value=knownAddress)
|
||||
IRInstruction(opc, vmDt, value=knownAddress)
|
||||
else
|
||||
IRCodeInstruction(opc, vmDt, labelSymbol = symbol)
|
||||
IRInstruction(opc, vmDt, labelSymbol = symbol)
|
||||
} else {
|
||||
val operandReg = codeGen.vmRegisters.nextFree()
|
||||
code += translateExpression(operand, operandReg, -1)
|
||||
val opc = if (signed) Opcode.ASRNM else Opcode.LSRNM
|
||||
code += if(knownAddress!=null)
|
||||
IRCodeInstruction(opc, vmDt, reg1 = operandReg, value=knownAddress)
|
||||
IRInstruction(opc, vmDt, reg1 = operandReg, value=knownAddress)
|
||||
else
|
||||
IRCodeInstruction(opc, vmDt, reg1 = operandReg, labelSymbol = symbol)
|
||||
IRInstruction(opc, vmDt, reg1 = operandReg, labelSymbol = symbol)
|
||||
}
|
||||
return code
|
||||
}
|
||||
@@ -440,12 +440,12 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
val code = IRCodeChunk(binExpr.position)
|
||||
if(codeGen.isOne(binExpr.right)){
|
||||
code += translateExpression(binExpr.left, resultRegister, -1)
|
||||
code += IRCodeInstruction(Opcode.LSL, vmDt, reg1=resultRegister)
|
||||
code += IRInstruction(Opcode.LSL, vmDt, reg1=resultRegister)
|
||||
} else {
|
||||
val rightResultReg = codeGen.vmRegisters.nextFree()
|
||||
code += translateExpression(binExpr.left, resultRegister, -1)
|
||||
code += translateExpression(binExpr.right, rightResultReg, -1)
|
||||
code += IRCodeInstruction(Opcode.LSLN, vmDt, reg1=resultRegister, rightResultReg)
|
||||
code += IRInstruction(Opcode.LSLN, vmDt, reg1=resultRegister, rightResultReg)
|
||||
}
|
||||
return code
|
||||
}
|
||||
@@ -454,16 +454,16 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
val code = IRCodeChunk(operand.position)
|
||||
if(codeGen.isOne(operand)){
|
||||
code += if(knownAddress!=null)
|
||||
IRCodeInstruction(Opcode.LSLM, vmDt, value=knownAddress)
|
||||
IRInstruction(Opcode.LSLM, vmDt, value=knownAddress)
|
||||
else
|
||||
IRCodeInstruction(Opcode.LSLM, vmDt, labelSymbol = symbol)
|
||||
IRInstruction(Opcode.LSLM, vmDt, labelSymbol = symbol)
|
||||
} else {
|
||||
val operandReg = codeGen.vmRegisters.nextFree()
|
||||
code += translateExpression(operand, operandReg, -1)
|
||||
code += if(knownAddress!=null)
|
||||
IRCodeInstruction(Opcode.LSLNM, vmDt, reg1=operandReg, value=knownAddress)
|
||||
IRInstruction(Opcode.LSLNM, vmDt, reg1=operandReg, value=knownAddress)
|
||||
else
|
||||
IRCodeInstruction(Opcode.LSLNM, vmDt, reg1=operandReg, labelSymbol = symbol)
|
||||
IRInstruction(Opcode.LSLNM, vmDt, reg1=operandReg, labelSymbol = symbol)
|
||||
}
|
||||
return code
|
||||
}
|
||||
@@ -472,12 +472,12 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
val code = IRCodeChunk(binExpr.position)
|
||||
if(binExpr.right is PtNumber) {
|
||||
code += translateExpression(binExpr.left, resultRegister, -1)
|
||||
code += IRCodeInstruction(Opcode.XOR, vmDt, reg1 = resultRegister, value=(binExpr.right as PtNumber).number.toInt())
|
||||
code += IRInstruction(Opcode.XOR, vmDt, reg1 = resultRegister, value=(binExpr.right as PtNumber).number.toInt())
|
||||
} else {
|
||||
val rightResultReg = codeGen.vmRegisters.nextFree()
|
||||
code += translateExpression(binExpr.left, resultRegister, -1)
|
||||
code += translateExpression(binExpr.right, rightResultReg, -1)
|
||||
code += IRCodeInstruction(Opcode.XORR, vmDt, reg1 = resultRegister, reg2 = rightResultReg)
|
||||
code += IRInstruction(Opcode.XORR, vmDt, reg1 = resultRegister, reg2 = rightResultReg)
|
||||
}
|
||||
return code
|
||||
}
|
||||
@@ -487,9 +487,9 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
val operandReg = codeGen.vmRegisters.nextFree()
|
||||
code += translateExpression(operand, operandReg, -1)
|
||||
code += if(knownAddress!=null)
|
||||
IRCodeInstruction(Opcode.XORM, vmDt, reg1=operandReg, value = knownAddress)
|
||||
IRInstruction(Opcode.XORM, vmDt, reg1=operandReg, value = knownAddress)
|
||||
else
|
||||
IRCodeInstruction(Opcode.XORM, vmDt, reg1=operandReg, labelSymbol = symbol)
|
||||
IRInstruction(Opcode.XORM, vmDt, reg1=operandReg, labelSymbol = symbol)
|
||||
return code
|
||||
}
|
||||
|
||||
@@ -497,12 +497,12 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
val code = IRCodeChunk(binExpr.position)
|
||||
if(binExpr.right is PtNumber) {
|
||||
code += translateExpression(binExpr.left, resultRegister, -1)
|
||||
code += IRCodeInstruction(Opcode.AND, vmDt, reg1 = resultRegister, value=(binExpr.right as PtNumber).number.toInt())
|
||||
code += IRInstruction(Opcode.AND, vmDt, reg1 = resultRegister, value=(binExpr.right as PtNumber).number.toInt())
|
||||
} else {
|
||||
val rightResultReg = codeGen.vmRegisters.nextFree()
|
||||
code += translateExpression(binExpr.left, resultRegister, -1)
|
||||
code += translateExpression(binExpr.right, rightResultReg, -1)
|
||||
code += IRCodeInstruction(Opcode.ANDR, vmDt, reg1 = resultRegister, reg2 = rightResultReg)
|
||||
code += IRInstruction(Opcode.ANDR, vmDt, reg1 = resultRegister, reg2 = rightResultReg)
|
||||
}
|
||||
return code
|
||||
}
|
||||
@@ -512,9 +512,9 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
val operandReg = codeGen.vmRegisters.nextFree()
|
||||
code += translateExpression(operand, operandReg, -1)
|
||||
code += if(knownAddress!=null)
|
||||
IRCodeInstruction(Opcode.ANDM, vmDt, reg1=operandReg, value=knownAddress)
|
||||
IRInstruction(Opcode.ANDM, vmDt, reg1=operandReg, value=knownAddress)
|
||||
else
|
||||
IRCodeInstruction(Opcode.ANDM, vmDt, reg1=operandReg, labelSymbol = symbol)
|
||||
IRInstruction(Opcode.ANDM, vmDt, reg1=operandReg, labelSymbol = symbol)
|
||||
return code
|
||||
}
|
||||
|
||||
@@ -522,12 +522,12 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
val code = IRCodeChunk(binExpr.position)
|
||||
if(binExpr.right is PtNumber) {
|
||||
code += translateExpression(binExpr.left, resultRegister, -1)
|
||||
code += IRCodeInstruction(Opcode.OR, vmDt, reg1 = resultRegister, value=(binExpr.right as PtNumber).number.toInt())
|
||||
code += IRInstruction(Opcode.OR, vmDt, reg1 = resultRegister, value=(binExpr.right as PtNumber).number.toInt())
|
||||
} else {
|
||||
val rightResultReg = codeGen.vmRegisters.nextFree()
|
||||
code += translateExpression(binExpr.left, resultRegister, -1)
|
||||
code += translateExpression(binExpr.right, rightResultReg, -1)
|
||||
code += IRCodeInstruction(Opcode.ORR, vmDt, reg1 = resultRegister, reg2 = rightResultReg)
|
||||
code += IRInstruction(Opcode.ORR, vmDt, reg1 = resultRegister, reg2 = rightResultReg)
|
||||
}
|
||||
return code
|
||||
}
|
||||
@@ -537,24 +537,23 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
val operandReg = codeGen.vmRegisters.nextFree()
|
||||
code += translateExpression(operand, operandReg, -1)
|
||||
code += if(knownAddress!=null)
|
||||
IRCodeInstruction(Opcode.ORM, vmDt, reg1=operandReg, value = knownAddress)
|
||||
IRInstruction(Opcode.ORM, vmDt, reg1=operandReg, value = knownAddress)
|
||||
else
|
||||
IRCodeInstruction(Opcode.ORM, vmDt, reg1=operandReg, labelSymbol = symbol)
|
||||
IRInstruction(Opcode.ORM, vmDt, reg1=operandReg, labelSymbol = symbol)
|
||||
return code
|
||||
}
|
||||
|
||||
private fun operatorModulo(binExpr: PtBinaryExpression, vmDt: VmDataType, resultRegister: Int): IRCodeChunk {
|
||||
if(vmDt==VmDataType.FLOAT)
|
||||
throw IllegalArgumentException("floating-point modulo not supported")
|
||||
require(vmDt!=VmDataType.FLOAT) {"floating-point modulo not supported"}
|
||||
val code = IRCodeChunk(binExpr.position)
|
||||
val rightResultReg = codeGen.vmRegisters.nextFree()
|
||||
if(binExpr.right is PtNumber) {
|
||||
code += translateExpression(binExpr.left, resultRegister, -1)
|
||||
code += IRCodeInstruction(Opcode.MOD, vmDt, reg1 = resultRegister, value=(binExpr.right as PtNumber).number.toInt())
|
||||
code += IRInstruction(Opcode.MOD, vmDt, reg1 = resultRegister, value=(binExpr.right as PtNumber).number.toInt())
|
||||
} else {
|
||||
code += translateExpression(binExpr.left, resultRegister, -1)
|
||||
code += translateExpression(binExpr.right, rightResultReg, -1)
|
||||
code += IRCodeInstruction(Opcode.MODR, vmDt, reg1 = resultRegister, reg2 = rightResultReg)
|
||||
code += IRInstruction(Opcode.MODR, vmDt, reg1 = resultRegister, reg2 = rightResultReg)
|
||||
}
|
||||
return code
|
||||
}
|
||||
@@ -576,9 +575,9 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
code += translateExpression(binExpr.left, -1, resultFpRegister)
|
||||
code += translateExpression(binExpr.right, -1, rightResultFpReg)
|
||||
code += if(signed)
|
||||
IRCodeInstruction(Opcode.DIVSR, vmDt, fpReg1 = resultFpRegister, fpReg2=rightResultFpReg)
|
||||
IRInstruction(Opcode.DIVSR, vmDt, fpReg1 = resultFpRegister, fpReg2=rightResultFpReg)
|
||||
else
|
||||
IRCodeInstruction(Opcode.DIVR, vmDt, fpReg1 = resultFpRegister, fpReg2=rightResultFpReg)
|
||||
IRInstruction(Opcode.DIVR, vmDt, fpReg1 = resultFpRegister, fpReg2=rightResultFpReg)
|
||||
}
|
||||
} else {
|
||||
if(constFactorRight!=null && constFactorRight.type!=DataType.FLOAT) {
|
||||
@@ -590,16 +589,16 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
if(binExpr.right is PtNumber) {
|
||||
code += translateExpression(binExpr.left, resultRegister, -1)
|
||||
code += if (signed)
|
||||
IRCodeInstruction(Opcode.DIVS, vmDt, reg1 = resultRegister, value=(binExpr.right as PtNumber).number.toInt())
|
||||
IRInstruction(Opcode.DIVS, vmDt, reg1 = resultRegister, value=(binExpr.right as PtNumber).number.toInt())
|
||||
else
|
||||
IRCodeInstruction(Opcode.DIV, vmDt, reg1 = resultRegister, value=(binExpr.right as PtNumber).number.toInt())
|
||||
IRInstruction(Opcode.DIV, vmDt, reg1 = resultRegister, value=(binExpr.right as PtNumber).number.toInt())
|
||||
} else {
|
||||
code += translateExpression(binExpr.left, resultRegister, -1)
|
||||
code += translateExpression(binExpr.right, rightResultReg, -1)
|
||||
code += if (signed)
|
||||
IRCodeInstruction(Opcode.DIVSR, vmDt, reg1 = resultRegister, reg2 = rightResultReg)
|
||||
IRInstruction(Opcode.DIVSR, vmDt, reg1 = resultRegister, reg2 = rightResultReg)
|
||||
else
|
||||
IRCodeInstruction(Opcode.DIVR, vmDt, reg1 = resultRegister, reg2 = rightResultReg)
|
||||
IRInstruction(Opcode.DIVR, vmDt, reg1 = resultRegister, reg2 = rightResultReg)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -618,15 +617,15 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
code += translateExpression(operand, -1, operandFpReg)
|
||||
code += if(signed) {
|
||||
if(knownAddress!=null)
|
||||
IRCodeInstruction(Opcode.DIVSM, vmDt, fpReg1 = operandFpReg, value = knownAddress)
|
||||
IRInstruction(Opcode.DIVSM, vmDt, fpReg1 = operandFpReg, value = knownAddress)
|
||||
else
|
||||
IRCodeInstruction(Opcode.DIVSM, vmDt, fpReg1 = operandFpReg, labelSymbol = symbol)
|
||||
IRInstruction(Opcode.DIVSM, vmDt, fpReg1 = operandFpReg, labelSymbol = symbol)
|
||||
}
|
||||
else {
|
||||
if(knownAddress!=null)
|
||||
IRCodeInstruction(Opcode.DIVM, vmDt, fpReg1 = operandFpReg, value = knownAddress)
|
||||
IRInstruction(Opcode.DIVM, vmDt, fpReg1 = operandFpReg, value = knownAddress)
|
||||
else
|
||||
IRCodeInstruction(Opcode.DIVM, vmDt, fpReg1 = operandFpReg, labelSymbol = symbol)
|
||||
IRInstruction(Opcode.DIVM, vmDt, fpReg1 = operandFpReg, labelSymbol = symbol)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -638,15 +637,15 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
code += translateExpression(operand, operandReg, -1)
|
||||
code += if(signed) {
|
||||
if(knownAddress!=null)
|
||||
IRCodeInstruction(Opcode.DIVSM, vmDt, reg1 = operandReg, value = knownAddress)
|
||||
IRInstruction(Opcode.DIVSM, vmDt, reg1 = operandReg, value = knownAddress)
|
||||
else
|
||||
IRCodeInstruction(Opcode.DIVSM, vmDt, reg1 = operandReg, labelSymbol = symbol)
|
||||
IRInstruction(Opcode.DIVSM, vmDt, reg1 = operandReg, labelSymbol = symbol)
|
||||
}
|
||||
else {
|
||||
if(knownAddress!=null)
|
||||
IRCodeInstruction(Opcode.DIVM, vmDt, reg1 = operandReg, value = knownAddress)
|
||||
IRInstruction(Opcode.DIVM, vmDt, reg1 = operandReg, value = knownAddress)
|
||||
else
|
||||
IRCodeInstruction(Opcode.DIVM, vmDt, reg1 = operandReg, labelSymbol = symbol)
|
||||
IRInstruction(Opcode.DIVM, vmDt, reg1 = operandReg, labelSymbol = symbol)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -670,7 +669,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
val rightResultFpReg = codeGen.vmRegisters.nextFreeFloat()
|
||||
code += translateExpression(binExpr.left, -1, resultFpRegister)
|
||||
code += translateExpression(binExpr.right, -1, rightResultFpReg)
|
||||
code += IRCodeInstruction(Opcode.MULR, vmDt, fpReg1 = resultFpRegister, fpReg2 = rightResultFpReg)
|
||||
code += IRInstruction(Opcode.MULR, vmDt, fpReg1 = resultFpRegister, fpReg2 = rightResultFpReg)
|
||||
}
|
||||
} else {
|
||||
if(constFactorLeft!=null && constFactorLeft.type!=DataType.FLOAT) {
|
||||
@@ -685,7 +684,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
val rightResultReg = codeGen.vmRegisters.nextFree()
|
||||
code += translateExpression(binExpr.left, resultRegister, -1)
|
||||
code += translateExpression(binExpr.right, rightResultReg, -1)
|
||||
code += IRCodeInstruction(Opcode.MULR, vmDt, reg1 = resultRegister, reg2 = rightResultReg)
|
||||
code += IRInstruction(Opcode.MULR, vmDt, reg1 = resultRegister, reg2 = rightResultReg)
|
||||
}
|
||||
}
|
||||
return code
|
||||
@@ -702,9 +701,9 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
val operandFpReg = codeGen.vmRegisters.nextFreeFloat()
|
||||
code += translateExpression(operand, -1, operandFpReg)
|
||||
code += if(knownAddress!=null)
|
||||
IRCodeInstruction(Opcode.MULM, vmDt, fpReg1 = operandFpReg, value = knownAddress)
|
||||
IRInstruction(Opcode.MULM, vmDt, fpReg1 = operandFpReg, value = knownAddress)
|
||||
else
|
||||
IRCodeInstruction(Opcode.MULM, vmDt, fpReg1 = operandFpReg, labelSymbol = symbol)
|
||||
IRInstruction(Opcode.MULM, vmDt, fpReg1 = operandFpReg, labelSymbol = symbol)
|
||||
}
|
||||
} else {
|
||||
if(constFactorRight!=null && constFactorRight.type!=DataType.FLOAT) {
|
||||
@@ -714,9 +713,9 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
val operandReg = codeGen.vmRegisters.nextFree()
|
||||
code += translateExpression(operand, operandReg, -1)
|
||||
code += if(knownAddress!=null)
|
||||
IRCodeInstruction(Opcode.MULM, vmDt, reg1=operandReg, value = knownAddress)
|
||||
IRInstruction(Opcode.MULM, vmDt, reg1=operandReg, value = knownAddress)
|
||||
else
|
||||
IRCodeInstruction(Opcode.MULM, vmDt, reg1=operandReg, labelSymbol = symbol)
|
||||
IRInstruction(Opcode.MULM, vmDt, reg1=operandReg, labelSymbol = symbol)
|
||||
}
|
||||
}
|
||||
return code
|
||||
@@ -727,33 +726,33 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
if(vmDt==VmDataType.FLOAT) {
|
||||
if((binExpr.right as? PtNumber)?.number==1.0) {
|
||||
code += translateExpression(binExpr.left, -1, resultFpRegister)
|
||||
code += IRCodeInstruction(Opcode.DEC, vmDt, fpReg1 = resultFpRegister)
|
||||
code += IRInstruction(Opcode.DEC, vmDt, fpReg1 = resultFpRegister)
|
||||
}
|
||||
else {
|
||||
if(binExpr.right is PtNumber) {
|
||||
code += translateExpression(binExpr.left, -1, resultFpRegister)
|
||||
code += IRCodeInstruction(Opcode.SUB, vmDt, fpReg1 = resultFpRegister, fpValue = (binExpr.right as PtNumber).number.toFloat())
|
||||
code += IRInstruction(Opcode.SUB, vmDt, fpReg1 = resultFpRegister, fpValue = (binExpr.right as PtNumber).number.toFloat())
|
||||
} else {
|
||||
val rightResultFpReg = codeGen.vmRegisters.nextFreeFloat()
|
||||
code += translateExpression(binExpr.left, -1, resultFpRegister)
|
||||
code += translateExpression(binExpr.right, -1, rightResultFpReg)
|
||||
code += IRCodeInstruction(Opcode.SUBR, vmDt, fpReg1 = resultFpRegister, fpReg2 = rightResultFpReg)
|
||||
code += IRInstruction(Opcode.SUBR, vmDt, fpReg1 = resultFpRegister, fpReg2 = rightResultFpReg)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if((binExpr.right as? PtNumber)?.number==1.0) {
|
||||
code += translateExpression(binExpr.left, resultRegister, -1)
|
||||
code += IRCodeInstruction(Opcode.DEC, vmDt, reg1=resultRegister)
|
||||
code += IRInstruction(Opcode.DEC, vmDt, reg1=resultRegister)
|
||||
}
|
||||
else {
|
||||
if(binExpr.right is PtNumber) {
|
||||
code += translateExpression(binExpr.left, resultRegister, -1)
|
||||
code += IRCodeInstruction(Opcode.SUB, vmDt, reg1 = resultRegister, value = (binExpr.right as PtNumber).number.toInt())
|
||||
code += IRInstruction(Opcode.SUB, vmDt, reg1 = resultRegister, value = (binExpr.right as PtNumber).number.toInt())
|
||||
} else {
|
||||
val rightResultReg = codeGen.vmRegisters.nextFree()
|
||||
code += translateExpression(binExpr.left, resultRegister, -1)
|
||||
code += translateExpression(binExpr.right, rightResultReg, -1)
|
||||
code += IRCodeInstruction(Opcode.SUBR, vmDt, reg1 = resultRegister, reg2 = rightResultReg)
|
||||
code += IRInstruction(Opcode.SUBR, vmDt, reg1 = resultRegister, reg2 = rightResultReg)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -765,32 +764,32 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
if(vmDt==VmDataType.FLOAT) {
|
||||
if((operand as? PtNumber)?.number==1.0) {
|
||||
code += if(knownAddress!=null)
|
||||
IRCodeInstruction(Opcode.DECM, vmDt, value=knownAddress)
|
||||
IRInstruction(Opcode.DECM, vmDt, value=knownAddress)
|
||||
else
|
||||
IRCodeInstruction(Opcode.DECM, vmDt, labelSymbol = symbol)
|
||||
IRInstruction(Opcode.DECM, vmDt, labelSymbol = symbol)
|
||||
}
|
||||
else {
|
||||
val operandFpReg = codeGen.vmRegisters.nextFreeFloat()
|
||||
code += translateExpression(operand, -1, operandFpReg)
|
||||
code += if(knownAddress!=null)
|
||||
IRCodeInstruction(Opcode.SUBM, vmDt, fpReg1=operandFpReg, value=knownAddress)
|
||||
IRInstruction(Opcode.SUBM, vmDt, fpReg1=operandFpReg, value=knownAddress)
|
||||
else
|
||||
IRCodeInstruction(Opcode.SUBM, vmDt, fpReg1=operandFpReg, labelSymbol = symbol)
|
||||
IRInstruction(Opcode.SUBM, vmDt, fpReg1=operandFpReg, labelSymbol = symbol)
|
||||
}
|
||||
} else {
|
||||
if((operand as? PtNumber)?.number==1.0) {
|
||||
code += if(knownAddress!=null)
|
||||
IRCodeInstruction(Opcode.DECM, vmDt, value=knownAddress)
|
||||
IRInstruction(Opcode.DECM, vmDt, value=knownAddress)
|
||||
else
|
||||
IRCodeInstruction(Opcode.DECM, vmDt, labelSymbol = symbol)
|
||||
IRInstruction(Opcode.DECM, vmDt, labelSymbol = symbol)
|
||||
}
|
||||
else {
|
||||
val operandReg = codeGen.vmRegisters.nextFree()
|
||||
code += translateExpression(operand, operandReg, -1)
|
||||
code += if(knownAddress!=null)
|
||||
IRCodeInstruction(Opcode.SUBM, vmDt, reg1=operandReg, value = knownAddress)
|
||||
IRInstruction(Opcode.SUBM, vmDt, reg1=operandReg, value = knownAddress)
|
||||
else
|
||||
IRCodeInstruction(Opcode.SUBM, vmDt, reg1=operandReg, labelSymbol = symbol)
|
||||
IRInstruction(Opcode.SUBM, vmDt, reg1=operandReg, labelSymbol = symbol)
|
||||
}
|
||||
}
|
||||
return code
|
||||
@@ -801,41 +800,41 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
if(vmDt==VmDataType.FLOAT) {
|
||||
if((binExpr.left as? PtNumber)?.number==1.0) {
|
||||
code += translateExpression(binExpr.right, -1, resultFpRegister)
|
||||
code += IRCodeInstruction(Opcode.INC, vmDt, fpReg1=resultFpRegister)
|
||||
code += IRInstruction(Opcode.INC, vmDt, fpReg1=resultFpRegister)
|
||||
}
|
||||
else if((binExpr.right as? PtNumber)?.number==1.0) {
|
||||
code += translateExpression(binExpr.left, -1, resultFpRegister)
|
||||
code += IRCodeInstruction(Opcode.INC, vmDt, fpReg1=resultFpRegister)
|
||||
code += IRInstruction(Opcode.INC, vmDt, fpReg1=resultFpRegister)
|
||||
}
|
||||
else {
|
||||
if(binExpr.right is PtNumber) {
|
||||
code += translateExpression(binExpr.left, -1, resultFpRegister)
|
||||
code += IRCodeInstruction(Opcode.ADD, vmDt, fpReg1 = resultFpRegister, fpValue = (binExpr.right as PtNumber).number.toFloat())
|
||||
code += IRInstruction(Opcode.ADD, vmDt, fpReg1 = resultFpRegister, fpValue = (binExpr.right as PtNumber).number.toFloat())
|
||||
} else {
|
||||
val rightResultFpReg = codeGen.vmRegisters.nextFreeFloat()
|
||||
code += translateExpression(binExpr.left, -1, resultFpRegister)
|
||||
code += translateExpression(binExpr.right, -1, rightResultFpReg)
|
||||
code += IRCodeInstruction(Opcode.ADDR, vmDt, fpReg1 = resultFpRegister, fpReg2 = rightResultFpReg)
|
||||
code += IRInstruction(Opcode.ADDR, vmDt, fpReg1 = resultFpRegister, fpReg2 = rightResultFpReg)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if((binExpr.left as? PtNumber)?.number==1.0) {
|
||||
code += translateExpression(binExpr.right, resultRegister, -1)
|
||||
code += IRCodeInstruction(Opcode.INC, vmDt, reg1=resultRegister)
|
||||
code += IRInstruction(Opcode.INC, vmDt, reg1=resultRegister)
|
||||
}
|
||||
else if((binExpr.right as? PtNumber)?.number==1.0) {
|
||||
code += translateExpression(binExpr.left, resultRegister, -1)
|
||||
code += IRCodeInstruction(Opcode.INC, vmDt, reg1=resultRegister)
|
||||
code += IRInstruction(Opcode.INC, vmDt, reg1=resultRegister)
|
||||
}
|
||||
else {
|
||||
if(binExpr.right is PtNumber) {
|
||||
code += translateExpression(binExpr.left, resultRegister, -1)
|
||||
code += IRCodeInstruction(Opcode.ADD, vmDt, reg1 = resultRegister, value=(binExpr.right as PtNumber).number.toInt())
|
||||
code += IRInstruction(Opcode.ADD, vmDt, reg1 = resultRegister, value=(binExpr.right as PtNumber).number.toInt())
|
||||
} else {
|
||||
val rightResultReg = codeGen.vmRegisters.nextFree()
|
||||
code += translateExpression(binExpr.left, resultRegister, -1)
|
||||
code += translateExpression(binExpr.right, rightResultReg, -1)
|
||||
code += IRCodeInstruction(Opcode.ADDR, vmDt, reg1 = resultRegister, reg2 = rightResultReg)
|
||||
code += IRInstruction(Opcode.ADDR, vmDt, reg1 = resultRegister, reg2 = rightResultReg)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -847,32 +846,32 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
if(vmDt==VmDataType.FLOAT) {
|
||||
if((operand as? PtNumber)?.number==1.0) {
|
||||
code += if(knownAddress!=null)
|
||||
IRCodeInstruction(Opcode.INCM, vmDt, value = knownAddress)
|
||||
IRInstruction(Opcode.INCM, vmDt, value = knownAddress)
|
||||
else
|
||||
IRCodeInstruction(Opcode.INCM, vmDt, labelSymbol = symbol)
|
||||
IRInstruction(Opcode.INCM, vmDt, labelSymbol = symbol)
|
||||
}
|
||||
else {
|
||||
val operandFpReg = codeGen.vmRegisters.nextFreeFloat()
|
||||
code += translateExpression(operand, -1, operandFpReg)
|
||||
code += if(knownAddress!=null)
|
||||
IRCodeInstruction(Opcode.ADDM, vmDt, fpReg1=operandFpReg, value = knownAddress)
|
||||
IRInstruction(Opcode.ADDM, vmDt, fpReg1=operandFpReg, value = knownAddress)
|
||||
else
|
||||
IRCodeInstruction(Opcode.ADDM, vmDt, fpReg1=operandFpReg, labelSymbol = symbol)
|
||||
IRInstruction(Opcode.ADDM, vmDt, fpReg1=operandFpReg, labelSymbol = symbol)
|
||||
}
|
||||
} else {
|
||||
if((operand as? PtNumber)?.number==1.0) {
|
||||
code += if(knownAddress!=null)
|
||||
IRCodeInstruction(Opcode.INCM, vmDt, value = knownAddress)
|
||||
IRInstruction(Opcode.INCM, vmDt, value = knownAddress)
|
||||
else
|
||||
IRCodeInstruction(Opcode.INCM, vmDt, labelSymbol = symbol)
|
||||
IRInstruction(Opcode.INCM, vmDt, labelSymbol = symbol)
|
||||
}
|
||||
else {
|
||||
val operandReg = codeGen.vmRegisters.nextFree()
|
||||
code += translateExpression(operand, operandReg, -1)
|
||||
code += if(knownAddress!=null)
|
||||
IRCodeInstruction(Opcode.ADDM, vmDt, reg1=operandReg, value=knownAddress)
|
||||
IRInstruction(Opcode.ADDM, vmDt, reg1=operandReg, value=knownAddress)
|
||||
else
|
||||
IRCodeInstruction(Opcode.ADDM, vmDt, reg1=operandReg, labelSymbol = symbol)
|
||||
IRInstruction(Opcode.ADDM, vmDt, reg1=operandReg, labelSymbol = symbol)
|
||||
}
|
||||
}
|
||||
return code
|
||||
@@ -886,29 +885,29 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
val paramDt = codeGen.vmType(parameter.type)
|
||||
val symbol = (fcall.functionName + parameter.name).joinToString(".")
|
||||
if(codeGen.isZero(arg)) {
|
||||
code += IRCodeInstruction(Opcode.STOREZM, paramDt, labelSymbol = symbol)
|
||||
code += IRInstruction(Opcode.STOREZM, paramDt, labelSymbol = symbol)
|
||||
} else {
|
||||
if (paramDt == VmDataType.FLOAT) {
|
||||
val argFpReg = codeGen.vmRegisters.nextFreeFloat()
|
||||
code += translateExpression(arg, -1, argFpReg)
|
||||
code += IRCodeInstruction(Opcode.STOREM, paramDt, fpReg1 = argFpReg, labelSymbol = symbol)
|
||||
code += IRInstruction(Opcode.STOREM, paramDt, fpReg1 = argFpReg, labelSymbol = symbol)
|
||||
} else {
|
||||
val argReg = codeGen.vmRegisters.nextFree()
|
||||
code += translateExpression(arg, argReg, -1)
|
||||
code += IRCodeInstruction(Opcode.STOREM, paramDt, reg1 = argReg, labelSymbol = symbol)
|
||||
code += IRInstruction(Opcode.STOREM, paramDt, reg1 = argReg, labelSymbol = symbol)
|
||||
}
|
||||
}
|
||||
}
|
||||
code += IRCodeInstruction(Opcode.CALL, labelSymbol=fcall.functionName.joinToString("."))
|
||||
code += IRInstruction(Opcode.CALL, labelSymbol=fcall.functionName.joinToString("."))
|
||||
if(fcall.type==DataType.FLOAT) {
|
||||
if (!fcall.void && resultFpRegister != 0) {
|
||||
// Call convention: result value is in fr0, so put it in the required register instead.
|
||||
code += IRCodeInstruction(Opcode.LOADR, VmDataType.FLOAT, fpReg1 = resultFpRegister, fpReg2 = 0)
|
||||
code += IRInstruction(Opcode.LOADR, VmDataType.FLOAT, fpReg1 = resultFpRegister, fpReg2 = 0)
|
||||
}
|
||||
} else {
|
||||
if (!fcall.void && resultRegister != 0) {
|
||||
// Call convention: result value is in r0, so put it in the required register instead.
|
||||
code += IRCodeInstruction(Opcode.LOADR, codeGen.vmType(fcall.type), reg1 = resultRegister, reg2 = 0)
|
||||
code += IRInstruction(Opcode.LOADR, codeGen.vmType(fcall.type), reg1 = resultRegister, reg2 = 0)
|
||||
}
|
||||
}
|
||||
return code
|
||||
@@ -919,16 +918,16 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
val paramDt = codeGen.vmType(parameter.type)
|
||||
val paramRegStr = if(parameter.register.registerOrPair!=null) parameter.register.registerOrPair.toString() else parameter.register.statusflag.toString()
|
||||
if(codeGen.isZero(arg)) {
|
||||
code += IRCodeInstruction(Opcode.STOREZCPU, paramDt, labelSymbol = paramRegStr)
|
||||
code += IRInstruction(Opcode.STOREZCPU, paramDt, labelSymbol = paramRegStr)
|
||||
} else {
|
||||
if (paramDt == VmDataType.FLOAT)
|
||||
throw AssemblyError("doesn't support float register argument in asm romsub")
|
||||
val argReg = codeGen.vmRegisters.nextFree()
|
||||
code += translateExpression(arg, argReg, -1)
|
||||
code += IRCodeInstruction(Opcode.STORECPU, paramDt, reg1 = argReg, labelSymbol = paramRegStr)
|
||||
code += IRInstruction(Opcode.STORECPU, paramDt, reg1 = argReg, labelSymbol = paramRegStr)
|
||||
}
|
||||
}
|
||||
code += IRCodeInstruction(Opcode.CALL, value=callTarget.address.toInt())
|
||||
code += IRInstruction(Opcode.CALL, value=callTarget.address.toInt())
|
||||
if(!fcall.void) {
|
||||
if(callTarget.returns.size!=1)
|
||||
throw AssemblyError("expect precisely 1 return value")
|
||||
@@ -936,7 +935,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
throw AssemblyError("doesn't support float register result in asm romsub")
|
||||
val returns = callTarget.returns.single()
|
||||
val regStr = if(returns.registerOrPair!=null) returns.registerOrPair.toString() else returns.statusflag.toString()
|
||||
code += IRCodeInstruction(Opcode.LOADCPU, codeGen.vmType(fcall.type), reg1=resultRegister, labelSymbol = regStr)
|
||||
code += IRInstruction(Opcode.LOADCPU, codeGen.vmType(fcall.type), reg1=resultRegister, labelSymbol = regStr)
|
||||
}
|
||||
return code
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package prog8.codegen.intermediate
|
||||
|
||||
import prog8.code.StMemVar
|
||||
import prog8.code.StMemorySlab
|
||||
import prog8.code.StStaticVariable
|
||||
import prog8.code.SymbolTable
|
||||
import prog8.code.ast.*
|
||||
@@ -24,9 +23,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
|
||||
@@ -63,8 +63,8 @@ class IRCodeGen(
|
||||
val replacements = mutableListOf<Triple<IRCodeChunkBase, Int, UInt>>()
|
||||
irProg.blocks.asSequence().flatMap { it.subroutines }.flatMap { it.chunks }.forEach { chunk ->
|
||||
chunk.lines.withIndex().forEach {
|
||||
(lineIndex, line)-> if(line is IRCodeInstruction) {
|
||||
val symbolExpr = line.ins.labelSymbol?.single()
|
||||
(lineIndex, line) -> if(line is IRInstruction) {
|
||||
val symbolExpr = line.labelSymbol
|
||||
if(symbolExpr!=null) {
|
||||
val symbol: String
|
||||
val index: UInt
|
||||
@@ -86,14 +86,14 @@ class IRCodeGen(
|
||||
}
|
||||
|
||||
replacements.forEach {
|
||||
val old = it.first.lines[it.second] as IRCodeInstruction
|
||||
it.first.lines[it.second] = IRCodeInstruction(
|
||||
old.ins.opcode,
|
||||
old.ins.type,
|
||||
old.ins.reg1,
|
||||
old.ins.reg2,
|
||||
old.ins.fpReg1,
|
||||
old.ins.fpReg2,
|
||||
val old = it.first.lines[it.second] as IRInstruction
|
||||
it.first.lines[it.second] = IRInstruction(
|
||||
old.opcode,
|
||||
old.type,
|
||||
old.reg1,
|
||||
old.reg2,
|
||||
old.fpReg1,
|
||||
old.fpReg2,
|
||||
it.third.toInt(),
|
||||
null,
|
||||
null
|
||||
@@ -101,9 +101,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 +133,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 +149,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 +166,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) }
|
||||
@@ -221,7 +233,7 @@ class IRCodeGen(
|
||||
}
|
||||
is PtBreakpoint -> {
|
||||
val chunk = IRCodeChunk(node.position)
|
||||
chunk += IRCodeInstruction(Opcode.BREAKPOINT)
|
||||
chunk += IRInstruction(Opcode.BREAKPOINT)
|
||||
return chunk
|
||||
}
|
||||
is PtConditionalBranch -> translate(node)
|
||||
@@ -231,7 +243,7 @@ class IRCodeGen(
|
||||
val data = node.file.readBytes()
|
||||
.drop(node.offset?.toInt() ?: 0)
|
||||
.take(node.length?.toInt() ?: Int.MAX_VALUE)
|
||||
chunk += IRCodeInlineBinary(data.toByteArray())
|
||||
chunk += IRCodeInlineBinary(data.map { it.toUByte() })
|
||||
return chunk
|
||||
}
|
||||
is PtAddressOf,
|
||||
@@ -262,19 +274,19 @@ class IRCodeGen(
|
||||
val elseLabel = createLabelName()
|
||||
// note that the branch opcode used is the opposite as the branch condition, because the generated code jumps to the 'else' part
|
||||
code += when(branch.condition) {
|
||||
BranchCondition.CS -> IRCodeInstruction(Opcode.BSTCC, labelSymbol = elseLabel)
|
||||
BranchCondition.CC -> IRCodeInstruction(Opcode.BSTCS, labelSymbol = elseLabel)
|
||||
BranchCondition.EQ, BranchCondition.Z -> IRCodeInstruction(Opcode.BSTNE, labelSymbol = elseLabel)
|
||||
BranchCondition.NE, BranchCondition.NZ -> IRCodeInstruction(Opcode.BSTEQ, labelSymbol = elseLabel)
|
||||
BranchCondition.MI, BranchCondition.NEG -> IRCodeInstruction(Opcode.BSTPOS, labelSymbol = elseLabel)
|
||||
BranchCondition.PL, BranchCondition.POS -> IRCodeInstruction(Opcode.BSTNEG, labelSymbol = elseLabel)
|
||||
BranchCondition.CS -> IRInstruction(Opcode.BSTCC, labelSymbol = elseLabel)
|
||||
BranchCondition.CC -> IRInstruction(Opcode.BSTCS, labelSymbol = elseLabel)
|
||||
BranchCondition.EQ, BranchCondition.Z -> IRInstruction(Opcode.BSTNE, labelSymbol = elseLabel)
|
||||
BranchCondition.NE, BranchCondition.NZ -> IRInstruction(Opcode.BSTEQ, labelSymbol = elseLabel)
|
||||
BranchCondition.MI, BranchCondition.NEG -> IRInstruction(Opcode.BSTPOS, labelSymbol = elseLabel)
|
||||
BranchCondition.PL, BranchCondition.POS -> IRInstruction(Opcode.BSTNEG, labelSymbol = elseLabel)
|
||||
BranchCondition.VC,
|
||||
BranchCondition.VS -> throw AssemblyError("conditional branch ${branch.condition} not supported in vm target due to lack of cpu V flag ${branch.position}")
|
||||
}
|
||||
code += translateNode(branch.trueScope)
|
||||
if(branch.falseScope.children.isNotEmpty()) {
|
||||
val endLabel = createLabelName()
|
||||
code += IRCodeInstruction(Opcode.JUMP, labelSymbol = endLabel)
|
||||
code += IRInstruction(Opcode.JUMP, labelSymbol = endLabel)
|
||||
code += IRCodeLabel(elseLabel)
|
||||
code += translateNode(branch.falseScope)
|
||||
code += IRCodeLabel(endLabel)
|
||||
@@ -301,22 +313,22 @@ class IRCodeGen(
|
||||
val skipLabel = createLabelName()
|
||||
val values = choice.values.children.map {it as PtNumber}
|
||||
if(values.size==1) {
|
||||
code += IRCodeInstruction(Opcode.LOAD, valueDt, reg1=choiceReg, value=values[0].number.toInt())
|
||||
code += IRCodeInstruction(Opcode.BNE, valueDt, reg1=valueReg, reg2=choiceReg, labelSymbol = skipLabel)
|
||||
code += IRInstruction(Opcode.LOAD, valueDt, reg1=choiceReg, value=values[0].number.toInt())
|
||||
code += IRInstruction(Opcode.BNE, valueDt, reg1=valueReg, reg2=choiceReg, labelSymbol = skipLabel)
|
||||
code += translateNode(choice.statements)
|
||||
if(choice.statements.children.last() !is PtReturn)
|
||||
code += IRCodeInstruction(Opcode.JUMP, labelSymbol = endLabel)
|
||||
code += IRInstruction(Opcode.JUMP, labelSymbol = endLabel)
|
||||
} else {
|
||||
val matchLabel = createLabelName()
|
||||
for (value in values) {
|
||||
code += IRCodeInstruction(Opcode.LOAD, valueDt, reg1=choiceReg, value=value.number.toInt())
|
||||
code += IRCodeInstruction(Opcode.BEQ, valueDt, reg1=valueReg, reg2=choiceReg, labelSymbol = matchLabel)
|
||||
code += IRInstruction(Opcode.LOAD, valueDt, reg1=choiceReg, value=value.number.toInt())
|
||||
code += IRInstruction(Opcode.BEQ, valueDt, reg1=valueReg, reg2=choiceReg, labelSymbol = matchLabel)
|
||||
}
|
||||
code += IRCodeInstruction(Opcode.JUMP, labelSymbol = skipLabel)
|
||||
code += IRInstruction(Opcode.JUMP, labelSymbol = skipLabel)
|
||||
code += IRCodeLabel(matchLabel)
|
||||
code += translateNode(choice.statements)
|
||||
if(choice.statements.children.last() !is PtReturn)
|
||||
code += IRCodeInstruction(Opcode.JUMP, labelSymbol = endLabel)
|
||||
code += IRInstruction(Opcode.JUMP, labelSymbol = endLabel)
|
||||
}
|
||||
code += IRCodeLabel(skipLabel)
|
||||
}
|
||||
@@ -346,14 +358,14 @@ class IRCodeGen(
|
||||
val endLabel = createLabelName()
|
||||
if(iterableVar.dt==DataType.STR) {
|
||||
// iterate over a zero-terminated string
|
||||
code += IRCodeInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=indexReg, value=0)
|
||||
code += IRInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=indexReg, value=0)
|
||||
code += IRCodeLabel(loopLabel)
|
||||
code += IRCodeInstruction(Opcode.LOADX, VmDataType.BYTE, reg1=tmpReg, reg2=indexReg, labelSymbol = symbol)
|
||||
code += IRCodeInstruction(Opcode.BZ, VmDataType.BYTE, reg1=tmpReg, labelSymbol = endLabel)
|
||||
code += IRCodeInstruction(Opcode.STOREM, VmDataType.BYTE, reg1=tmpReg, labelSymbol = loopvarSymbol)
|
||||
code += IRInstruction(Opcode.LOADX, VmDataType.BYTE, reg1=tmpReg, reg2=indexReg, labelSymbol = symbol)
|
||||
code += IRInstruction(Opcode.BZ, VmDataType.BYTE, reg1=tmpReg, labelSymbol = endLabel)
|
||||
code += IRInstruction(Opcode.STOREM, VmDataType.BYTE, reg1=tmpReg, labelSymbol = loopvarSymbol)
|
||||
code += translateNode(forLoop.statements)
|
||||
code += IRCodeInstruction(Opcode.INC, VmDataType.BYTE, reg1=indexReg)
|
||||
code += IRCodeInstruction(Opcode.JUMP, labelSymbol = loopLabel)
|
||||
code += IRInstruction(Opcode.INC, VmDataType.BYTE, reg1=indexReg)
|
||||
code += IRInstruction(Opcode.JUMP, labelSymbol = loopLabel)
|
||||
code += IRCodeLabel(endLabel)
|
||||
} else {
|
||||
// iterate over array
|
||||
@@ -362,22 +374,22 @@ class IRCodeGen(
|
||||
val lengthBytes = iterableVar.length!! * elementSize
|
||||
if(lengthBytes<256) {
|
||||
val lengthReg = vmRegisters.nextFree()
|
||||
code += IRCodeInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=indexReg, value=0)
|
||||
code += IRCodeInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=lengthReg, value=lengthBytes)
|
||||
code += IRInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=indexReg, value=0)
|
||||
code += IRInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=lengthReg, value=lengthBytes)
|
||||
code += IRCodeLabel(loopLabel)
|
||||
code += IRCodeInstruction(Opcode.LOADX, vmType(elementDt), reg1=tmpReg, reg2=indexReg, labelSymbol=symbol)
|
||||
code += IRCodeInstruction(Opcode.STOREM, vmType(elementDt), reg1=tmpReg, labelSymbol = loopvarSymbol)
|
||||
code += IRInstruction(Opcode.LOADX, vmType(elementDt), reg1=tmpReg, reg2=indexReg, labelSymbol=symbol)
|
||||
code += IRInstruction(Opcode.STOREM, vmType(elementDt), reg1=tmpReg, labelSymbol = loopvarSymbol)
|
||||
code += translateNode(forLoop.statements)
|
||||
code += addConstReg(VmDataType.BYTE, indexReg, elementSize, iterable.position)
|
||||
code += IRCodeInstruction(Opcode.BNE, VmDataType.BYTE, reg1=indexReg, reg2=lengthReg, labelSymbol = loopLabel)
|
||||
code += IRInstruction(Opcode.BNE, VmDataType.BYTE, reg1=indexReg, reg2=lengthReg, labelSymbol = loopLabel)
|
||||
} else if(lengthBytes==256) {
|
||||
code += IRCodeInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=indexReg, value=0)
|
||||
code += IRInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=indexReg, value=0)
|
||||
code += IRCodeLabel(loopLabel)
|
||||
code += IRCodeInstruction(Opcode.LOADX, vmType(elementDt), reg1=tmpReg, reg2=indexReg, labelSymbol=symbol)
|
||||
code += IRCodeInstruction(Opcode.STOREM, vmType(elementDt), reg1=tmpReg, labelSymbol = loopvarSymbol)
|
||||
code += IRInstruction(Opcode.LOADX, vmType(elementDt), reg1=tmpReg, reg2=indexReg, labelSymbol=symbol)
|
||||
code += IRInstruction(Opcode.STOREM, vmType(elementDt), reg1=tmpReg, labelSymbol = loopvarSymbol)
|
||||
code += translateNode(forLoop.statements)
|
||||
code += addConstReg(VmDataType.BYTE, indexReg, elementSize, iterable.position)
|
||||
code += IRCodeInstruction(Opcode.BNZ, VmDataType.BYTE, reg1=indexReg, labelSymbol = loopLabel)
|
||||
code += IRInstruction(Opcode.BNZ, VmDataType.BYTE, reg1=indexReg, labelSymbol = loopLabel)
|
||||
} else {
|
||||
throw AssemblyError("iterator length should never exceed 256")
|
||||
}
|
||||
@@ -402,13 +414,13 @@ class IRCodeGen(
|
||||
|
||||
code += expressionEval.translateExpression(iterable.to, endvalueReg, -1)
|
||||
code += expressionEval.translateExpression(iterable.from, indexReg, -1)
|
||||
code += IRCodeInstruction(Opcode.STOREM, loopvarDt, reg1=indexReg, labelSymbol=loopvarSymbol)
|
||||
code += IRInstruction(Opcode.STOREM, loopvarDt, reg1=indexReg, labelSymbol=loopvarSymbol)
|
||||
code += IRCodeLabel(loopLabel)
|
||||
code += translateNode(forLoop.statements)
|
||||
code += addConstMem(loopvarDt, null, loopvarSymbol, step, iterable.position)
|
||||
code += IRCodeInstruction(Opcode.LOADM, loopvarDt, reg1 = indexReg, labelSymbol = loopvarSymbol)
|
||||
code += IRInstruction(Opcode.LOADM, loopvarDt, reg1 = indexReg, labelSymbol = loopvarSymbol)
|
||||
val branchOpcode = if(loopvar.dt in SignedDatatypes) Opcode.BLES else Opcode.BLE
|
||||
code += IRCodeInstruction(branchOpcode, loopvarDt, reg1=indexReg, reg2=endvalueReg, labelSymbol=loopLabel)
|
||||
code += IRInstruction(branchOpcode, loopvarDt, reg1=indexReg, reg2=endvalueReg, labelSymbol=loopLabel)
|
||||
return code
|
||||
}
|
||||
|
||||
@@ -430,20 +442,20 @@ class IRCodeGen(
|
||||
val endvalueReg: Int
|
||||
if(rangeEndWrapped!=0) {
|
||||
endvalueReg = vmRegisters.nextFree()
|
||||
code += IRCodeInstruction(Opcode.LOAD, loopvarDt, reg1 = endvalueReg, value = rangeEndWrapped)
|
||||
code += IRInstruction(Opcode.LOAD, loopvarDt, reg1 = endvalueReg, value = rangeEndWrapped)
|
||||
} else {
|
||||
endvalueReg = -1 // not used
|
||||
}
|
||||
code += IRCodeInstruction(Opcode.LOAD, loopvarDt, reg1=indexReg, value=rangeStart)
|
||||
code += IRCodeInstruction(Opcode.STOREM, loopvarDt, reg1=indexReg, labelSymbol=loopvarSymbol)
|
||||
code += IRInstruction(Opcode.LOAD, loopvarDt, reg1=indexReg, value=rangeStart)
|
||||
code += IRInstruction(Opcode.STOREM, loopvarDt, reg1=indexReg, labelSymbol=loopvarSymbol)
|
||||
code += IRCodeLabel(loopLabel)
|
||||
code += translateNode(forLoop.statements)
|
||||
code += addConstMem(loopvarDt, null, loopvarSymbol, step, iterable.position)
|
||||
code += IRCodeInstruction(Opcode.LOADM, loopvarDt, reg1 = indexReg, labelSymbol = loopvarSymbol)
|
||||
code += IRInstruction(Opcode.LOADM, loopvarDt, reg1 = indexReg, labelSymbol = loopvarSymbol)
|
||||
code += if(rangeEndWrapped==0) {
|
||||
IRCodeInstruction(Opcode.BNZ, loopvarDt, reg1 = indexReg, labelSymbol = loopLabel)
|
||||
IRInstruction(Opcode.BNZ, loopvarDt, reg1 = indexReg, labelSymbol = loopLabel)
|
||||
} else {
|
||||
IRCodeInstruction(Opcode.BNE, loopvarDt, reg1 = indexReg, reg2 = endvalueReg, labelSymbol = loopLabel)
|
||||
IRInstruction(Opcode.BNE, loopvarDt, reg1 = indexReg, reg2 = endvalueReg, labelSymbol = loopLabel)
|
||||
}
|
||||
return code
|
||||
}
|
||||
@@ -453,24 +465,24 @@ class IRCodeGen(
|
||||
when(value) {
|
||||
0 -> { /* do nothing */ }
|
||||
1 -> {
|
||||
code += IRCodeInstruction(Opcode.INC, dt, reg1=reg)
|
||||
code += IRInstruction(Opcode.INC, dt, reg1=reg)
|
||||
}
|
||||
2 -> {
|
||||
code += IRCodeInstruction(Opcode.INC, dt, reg1=reg)
|
||||
code += IRCodeInstruction(Opcode.INC, dt, reg1=reg)
|
||||
code += IRInstruction(Opcode.INC, dt, reg1=reg)
|
||||
code += IRInstruction(Opcode.INC, dt, reg1=reg)
|
||||
}
|
||||
-1 -> {
|
||||
code += IRCodeInstruction(Opcode.DEC, dt, reg1=reg)
|
||||
code += IRInstruction(Opcode.DEC, dt, reg1=reg)
|
||||
}
|
||||
-2 -> {
|
||||
code += IRCodeInstruction(Opcode.DEC, dt, reg1=reg)
|
||||
code += IRCodeInstruction(Opcode.DEC, dt, reg1=reg)
|
||||
code += IRInstruction(Opcode.DEC, dt, reg1=reg)
|
||||
code += IRInstruction(Opcode.DEC, dt, reg1=reg)
|
||||
}
|
||||
else -> {
|
||||
code += if(value>0) {
|
||||
IRCodeInstruction(Opcode.ADD, dt, reg1 = reg, value=value)
|
||||
IRInstruction(Opcode.ADD, dt, reg1 = reg, value=value)
|
||||
} else {
|
||||
IRCodeInstruction(Opcode.SUB, dt, reg1 = reg, value=-value)
|
||||
IRInstruction(Opcode.SUB, dt, reg1 = reg, value=-value)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -483,49 +495,49 @@ class IRCodeGen(
|
||||
0 -> { /* do nothing */ }
|
||||
1 -> {
|
||||
code += if(knownAddress!=null)
|
||||
IRCodeInstruction(Opcode.INCM, dt, value=knownAddress.toInt())
|
||||
IRInstruction(Opcode.INCM, dt, value=knownAddress.toInt())
|
||||
else
|
||||
IRCodeInstruction(Opcode.INCM, dt, labelSymbol = symbol)
|
||||
IRInstruction(Opcode.INCM, dt, labelSymbol = symbol)
|
||||
}
|
||||
2 -> {
|
||||
if(knownAddress!=null) {
|
||||
code += IRCodeInstruction(Opcode.INCM, dt, value = knownAddress.toInt())
|
||||
code += IRCodeInstruction(Opcode.INCM, dt, value = knownAddress.toInt())
|
||||
code += IRInstruction(Opcode.INCM, dt, value = knownAddress.toInt())
|
||||
code += IRInstruction(Opcode.INCM, dt, value = knownAddress.toInt())
|
||||
} else {
|
||||
code += IRCodeInstruction(Opcode.INCM, dt, labelSymbol = symbol)
|
||||
code += IRCodeInstruction(Opcode.INCM, dt, labelSymbol = symbol)
|
||||
code += IRInstruction(Opcode.INCM, dt, labelSymbol = symbol)
|
||||
code += IRInstruction(Opcode.INCM, dt, labelSymbol = symbol)
|
||||
}
|
||||
}
|
||||
-1 -> {
|
||||
code += if(knownAddress!=null)
|
||||
IRCodeInstruction(Opcode.DECM, dt, value=knownAddress.toInt())
|
||||
IRInstruction(Opcode.DECM, dt, value=knownAddress.toInt())
|
||||
else
|
||||
IRCodeInstruction(Opcode.DECM, dt, labelSymbol = symbol)
|
||||
IRInstruction(Opcode.DECM, dt, labelSymbol = symbol)
|
||||
}
|
||||
-2 -> {
|
||||
if(knownAddress!=null) {
|
||||
code += IRCodeInstruction(Opcode.DECM, dt, value = knownAddress.toInt())
|
||||
code += IRCodeInstruction(Opcode.DECM, dt, value = knownAddress.toInt())
|
||||
code += IRInstruction(Opcode.DECM, dt, value = knownAddress.toInt())
|
||||
code += IRInstruction(Opcode.DECM, dt, value = knownAddress.toInt())
|
||||
} else {
|
||||
code += IRCodeInstruction(Opcode.DECM, dt, labelSymbol = symbol)
|
||||
code += IRCodeInstruction(Opcode.DECM, dt, labelSymbol = symbol)
|
||||
code += IRInstruction(Opcode.DECM, dt, labelSymbol = symbol)
|
||||
code += IRInstruction(Opcode.DECM, dt, labelSymbol = symbol)
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
val valueReg = vmRegisters.nextFree()
|
||||
if(value>0) {
|
||||
code += IRCodeInstruction(Opcode.LOAD, dt, reg1=valueReg, value=value)
|
||||
code += IRInstruction(Opcode.LOAD, dt, reg1=valueReg, value=value)
|
||||
code += if(knownAddress!=null)
|
||||
IRCodeInstruction(Opcode.ADDM, dt, reg1=valueReg, value=knownAddress.toInt())
|
||||
IRInstruction(Opcode.ADDM, dt, reg1=valueReg, value=knownAddress.toInt())
|
||||
else
|
||||
IRCodeInstruction(Opcode.ADDM, dt, reg1=valueReg, labelSymbol = symbol)
|
||||
IRInstruction(Opcode.ADDM, dt, reg1=valueReg, labelSymbol = symbol)
|
||||
}
|
||||
else {
|
||||
code += IRCodeInstruction(Opcode.LOAD, dt, reg1=valueReg, value=-value)
|
||||
code += IRInstruction(Opcode.LOAD, dt, reg1=valueReg, value=-value)
|
||||
code += if(knownAddress!=null)
|
||||
IRCodeInstruction(Opcode.SUBM, dt, reg1=valueReg, value=knownAddress.toInt())
|
||||
IRInstruction(Opcode.SUBM, dt, reg1=valueReg, value=knownAddress.toInt())
|
||||
else
|
||||
IRCodeInstruction(Opcode.SUBM, dt, reg1=valueReg, labelSymbol = symbol)
|
||||
IRInstruction(Opcode.SUBM, dt, reg1=valueReg, labelSymbol = symbol)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -537,9 +549,9 @@ class IRCodeGen(
|
||||
if(factor==1f)
|
||||
return code
|
||||
code += if(factor==0f) {
|
||||
IRCodeInstruction(Opcode.LOAD, VmDataType.FLOAT, fpReg1 = fpReg, fpValue = 0f)
|
||||
IRInstruction(Opcode.LOAD, VmDataType.FLOAT, fpReg1 = fpReg, fpValue = 0f)
|
||||
} else {
|
||||
IRCodeInstruction(Opcode.MUL, VmDataType.FLOAT, fpReg1 = fpReg, fpValue=factor)
|
||||
IRInstruction(Opcode.MUL, VmDataType.FLOAT, fpReg1 = fpReg, fpValue=factor)
|
||||
}
|
||||
return code
|
||||
}
|
||||
@@ -550,16 +562,16 @@ class IRCodeGen(
|
||||
return code
|
||||
if(factor==0f) {
|
||||
code += if(knownAddress!=null)
|
||||
IRCodeInstruction(Opcode.STOREZM, VmDataType.FLOAT, value = knownAddress)
|
||||
IRInstruction(Opcode.STOREZM, VmDataType.FLOAT, value = knownAddress)
|
||||
else
|
||||
IRCodeInstruction(Opcode.STOREZM, VmDataType.FLOAT, labelSymbol = symbol)
|
||||
IRInstruction(Opcode.STOREZM, VmDataType.FLOAT, labelSymbol = symbol)
|
||||
} else {
|
||||
val factorReg = vmRegisters.nextFreeFloat()
|
||||
code += IRCodeInstruction(Opcode.LOAD, VmDataType.FLOAT, fpReg1=factorReg, fpValue = factor)
|
||||
code += IRInstruction(Opcode.LOAD, VmDataType.FLOAT, fpReg1=factorReg, fpValue = factor)
|
||||
code += if(knownAddress!=null)
|
||||
IRCodeInstruction(Opcode.MULM, VmDataType.FLOAT, fpReg1 = factorReg, value = knownAddress)
|
||||
IRInstruction(Opcode.MULM, VmDataType.FLOAT, fpReg1 = factorReg, value = knownAddress)
|
||||
else
|
||||
IRCodeInstruction(Opcode.MULM, VmDataType.FLOAT, fpReg1 = factorReg, labelSymbol = symbol)
|
||||
IRInstruction(Opcode.MULM, VmDataType.FLOAT, fpReg1 = factorReg, labelSymbol = symbol)
|
||||
}
|
||||
return code
|
||||
}
|
||||
@@ -573,18 +585,18 @@ class IRCodeGen(
|
||||
val pow2 = powersOfTwo.indexOf(factor)
|
||||
if(pow2==1) {
|
||||
// just shift 1 bit
|
||||
code += IRCodeInstruction(Opcode.LSL, dt, reg1=reg)
|
||||
code += IRInstruction(Opcode.LSL, dt, reg1=reg)
|
||||
}
|
||||
else if(pow2>=1) {
|
||||
// just shift multiple bits
|
||||
val pow2reg = vmRegisters.nextFree()
|
||||
code += IRCodeInstruction(Opcode.LOAD, dt, reg1=pow2reg, value=pow2)
|
||||
code += IRCodeInstruction(Opcode.LSLN, dt, reg1=reg, reg2=pow2reg)
|
||||
code += IRInstruction(Opcode.LOAD, dt, reg1=pow2reg, value=pow2)
|
||||
code += IRInstruction(Opcode.LSLN, dt, reg1=reg, reg2=pow2reg)
|
||||
} else {
|
||||
code += if (factor == 0) {
|
||||
IRCodeInstruction(Opcode.LOAD, dt, reg1=reg, value=0)
|
||||
IRInstruction(Opcode.LOAD, dt, reg1=reg, value=0)
|
||||
} else {
|
||||
IRCodeInstruction(Opcode.MUL, dt, reg1=reg, value=factor)
|
||||
IRInstruction(Opcode.MUL, dt, reg1=reg, value=factor)
|
||||
}
|
||||
}
|
||||
return code
|
||||
@@ -598,32 +610,32 @@ class IRCodeGen(
|
||||
if(pow2==1) {
|
||||
// just shift 1 bit
|
||||
code += if(knownAddress!=null)
|
||||
IRCodeInstruction(Opcode.LSLM, dt, value = knownAddress)
|
||||
IRInstruction(Opcode.LSLM, dt, value = knownAddress)
|
||||
else
|
||||
IRCodeInstruction(Opcode.LSLM, dt, labelSymbol = symbol)
|
||||
IRInstruction(Opcode.LSLM, dt, labelSymbol = symbol)
|
||||
}
|
||||
else if(pow2>=1) {
|
||||
// just shift multiple bits
|
||||
val pow2reg = vmRegisters.nextFree()
|
||||
code += IRCodeInstruction(Opcode.LOAD, dt, reg1=pow2reg, value=pow2)
|
||||
code += IRInstruction(Opcode.LOAD, dt, reg1=pow2reg, value=pow2)
|
||||
code += if(knownAddress!=null)
|
||||
IRCodeInstruction(Opcode.LSLNM, dt, reg1=pow2reg, value=knownAddress)
|
||||
IRInstruction(Opcode.LSLNM, dt, reg1=pow2reg, value=knownAddress)
|
||||
else
|
||||
IRCodeInstruction(Opcode.LSLNM, dt, reg1=pow2reg, labelSymbol = symbol)
|
||||
IRInstruction(Opcode.LSLNM, dt, reg1=pow2reg, labelSymbol = symbol)
|
||||
} else {
|
||||
if (factor == 0) {
|
||||
code += if(knownAddress!=null)
|
||||
IRCodeInstruction(Opcode.STOREZM, dt, value=knownAddress)
|
||||
IRInstruction(Opcode.STOREZM, dt, value=knownAddress)
|
||||
else
|
||||
IRCodeInstruction(Opcode.STOREZM, dt, labelSymbol = symbol)
|
||||
IRInstruction(Opcode.STOREZM, dt, labelSymbol = symbol)
|
||||
}
|
||||
else {
|
||||
val factorReg = vmRegisters.nextFree()
|
||||
code += IRCodeInstruction(Opcode.LOAD, dt, reg1=factorReg, value = factor)
|
||||
code += IRInstruction(Opcode.LOAD, dt, reg1=factorReg, value = factor)
|
||||
code += if(knownAddress!=null)
|
||||
IRCodeInstruction(Opcode.MULM, dt, reg1=factorReg, value = knownAddress)
|
||||
IRInstruction(Opcode.MULM, dt, reg1=factorReg, value = knownAddress)
|
||||
else
|
||||
IRCodeInstruction(Opcode.MULM, dt, reg1=factorReg, labelSymbol = symbol)
|
||||
IRInstruction(Opcode.MULM, dt, reg1=factorReg, labelSymbol = symbol)
|
||||
}
|
||||
}
|
||||
return code
|
||||
@@ -634,9 +646,9 @@ class IRCodeGen(
|
||||
if(factor==1f)
|
||||
return code
|
||||
code += if(factor==0f) {
|
||||
IRCodeInstruction(Opcode.LOAD, VmDataType.FLOAT, fpReg1 = fpReg, fpValue = Float.MAX_VALUE)
|
||||
IRInstruction(Opcode.LOAD, VmDataType.FLOAT, fpReg1 = fpReg, fpValue = Float.MAX_VALUE)
|
||||
} else {
|
||||
IRCodeInstruction(Opcode.DIVS, VmDataType.FLOAT, fpReg1 = fpReg, fpValue=factor)
|
||||
IRInstruction(Opcode.DIVS, VmDataType.FLOAT, fpReg1 = fpReg, fpValue=factor)
|
||||
}
|
||||
return code
|
||||
}
|
||||
@@ -647,18 +659,18 @@ class IRCodeGen(
|
||||
return code
|
||||
if(factor==0f) {
|
||||
val maxvalueReg = vmRegisters.nextFreeFloat()
|
||||
code += IRCodeInstruction(Opcode.LOAD, VmDataType.FLOAT, fpReg1 = maxvalueReg, fpValue = Float.MAX_VALUE)
|
||||
code += IRInstruction(Opcode.LOAD, VmDataType.FLOAT, fpReg1 = maxvalueReg, fpValue = Float.MAX_VALUE)
|
||||
code += if(knownAddress!=null)
|
||||
IRCodeInstruction(Opcode.STOREM, VmDataType.FLOAT, fpReg1 = maxvalueReg, value=knownAddress)
|
||||
IRInstruction(Opcode.STOREM, VmDataType.FLOAT, fpReg1 = maxvalueReg, value=knownAddress)
|
||||
else
|
||||
IRCodeInstruction(Opcode.STOREM, VmDataType.FLOAT, fpReg1 = maxvalueReg, labelSymbol = symbol)
|
||||
IRInstruction(Opcode.STOREM, VmDataType.FLOAT, fpReg1 = maxvalueReg, labelSymbol = symbol)
|
||||
} else {
|
||||
val factorReg = vmRegisters.nextFreeFloat()
|
||||
code += IRCodeInstruction(Opcode.LOAD, VmDataType.FLOAT, fpReg1=factorReg, fpValue = factor)
|
||||
code += IRInstruction(Opcode.LOAD, VmDataType.FLOAT, fpReg1=factorReg, fpValue = factor)
|
||||
code += if(knownAddress!=null)
|
||||
IRCodeInstruction(Opcode.DIVSM, VmDataType.FLOAT, fpReg1 = factorReg, value=knownAddress)
|
||||
IRInstruction(Opcode.DIVSM, VmDataType.FLOAT, fpReg1 = factorReg, value=knownAddress)
|
||||
else
|
||||
IRCodeInstruction(Opcode.DIVSM, VmDataType.FLOAT, fpReg1 = factorReg, labelSymbol = symbol)
|
||||
IRInstruction(Opcode.DIVSM, VmDataType.FLOAT, fpReg1 = factorReg, labelSymbol = symbol)
|
||||
}
|
||||
return code
|
||||
}
|
||||
@@ -669,24 +681,24 @@ class IRCodeGen(
|
||||
return code
|
||||
val pow2 = powersOfTwo.indexOf(factor)
|
||||
if(pow2==1 && !signed) {
|
||||
code += IRCodeInstruction(Opcode.LSR, dt, reg1=reg) // simple single bit shift
|
||||
code += IRInstruction(Opcode.LSR, dt, reg1=reg) // simple single bit shift
|
||||
}
|
||||
else if(pow2>=1 &&!signed) {
|
||||
// just shift multiple bits
|
||||
val pow2reg = vmRegisters.nextFree()
|
||||
code += IRCodeInstruction(Opcode.LOAD, dt, reg1=pow2reg, value=pow2)
|
||||
code += IRInstruction(Opcode.LOAD, dt, reg1=pow2reg, value=pow2)
|
||||
code += if(signed)
|
||||
IRCodeInstruction(Opcode.ASRN, dt, reg1=reg, reg2=pow2reg)
|
||||
IRInstruction(Opcode.ASRN, dt, reg1=reg, reg2=pow2reg)
|
||||
else
|
||||
IRCodeInstruction(Opcode.LSRN, dt, reg1=reg, reg2=pow2reg)
|
||||
IRInstruction(Opcode.LSRN, dt, reg1=reg, reg2=pow2reg)
|
||||
} else {
|
||||
code += if (factor == 0) {
|
||||
IRCodeInstruction(Opcode.LOAD, dt, reg1=reg, value=0xffff)
|
||||
IRInstruction(Opcode.LOAD, dt, reg1=reg, value=0xffff)
|
||||
} else {
|
||||
if(signed)
|
||||
IRCodeInstruction(Opcode.DIVS, dt, reg1=reg, value=factor)
|
||||
IRInstruction(Opcode.DIVS, dt, reg1=reg, value=factor)
|
||||
else
|
||||
IRCodeInstruction(Opcode.DIV, dt, reg1=reg, value=factor)
|
||||
IRInstruction(Opcode.DIV, dt, reg1=reg, value=factor)
|
||||
}
|
||||
}
|
||||
return code
|
||||
@@ -700,49 +712,49 @@ class IRCodeGen(
|
||||
if(pow2==1 && !signed) {
|
||||
// just simple bit shift
|
||||
code += if(knownAddress!=null)
|
||||
IRCodeInstruction(Opcode.LSRM, dt, value=knownAddress)
|
||||
IRInstruction(Opcode.LSRM, dt, value=knownAddress)
|
||||
else
|
||||
IRCodeInstruction(Opcode.LSRM, dt, labelSymbol = symbol)
|
||||
IRInstruction(Opcode.LSRM, dt, labelSymbol = symbol)
|
||||
}
|
||||
else if(pow2>=1 && !signed) {
|
||||
// just shift multiple bits
|
||||
val pow2reg = vmRegisters.nextFree()
|
||||
code += IRCodeInstruction(Opcode.LOAD, dt, reg1=pow2reg, value=pow2)
|
||||
code += IRInstruction(Opcode.LOAD, dt, reg1=pow2reg, value=pow2)
|
||||
code += if(signed) {
|
||||
if(knownAddress!=null)
|
||||
IRCodeInstruction(Opcode.ASRNM, dt, reg1 = pow2reg, value = knownAddress)
|
||||
IRInstruction(Opcode.ASRNM, dt, reg1 = pow2reg, value = knownAddress)
|
||||
else
|
||||
IRCodeInstruction(Opcode.ASRNM, dt, reg1 = pow2reg, labelSymbol = symbol)
|
||||
IRInstruction(Opcode.ASRNM, dt, reg1 = pow2reg, labelSymbol = symbol)
|
||||
}
|
||||
else {
|
||||
if(knownAddress!=null)
|
||||
IRCodeInstruction(Opcode.LSRNM, dt, reg1 = pow2reg, value = knownAddress)
|
||||
IRInstruction(Opcode.LSRNM, dt, reg1 = pow2reg, value = knownAddress)
|
||||
else
|
||||
IRCodeInstruction(Opcode.LSRNM, dt, reg1 = pow2reg, labelSymbol = symbol)
|
||||
IRInstruction(Opcode.LSRNM, dt, reg1 = pow2reg, labelSymbol = symbol)
|
||||
}
|
||||
} else {
|
||||
if (factor == 0) {
|
||||
val reg = vmRegisters.nextFree()
|
||||
code += IRCodeInstruction(Opcode.LOAD, dt, reg1=reg, value=0xffff)
|
||||
code += IRInstruction(Opcode.LOAD, dt, reg1=reg, value=0xffff)
|
||||
code += if(knownAddress!=null)
|
||||
IRCodeInstruction(Opcode.STOREM, dt, reg1=reg, value=knownAddress)
|
||||
IRInstruction(Opcode.STOREM, dt, reg1=reg, value=knownAddress)
|
||||
else
|
||||
IRCodeInstruction(Opcode.STOREM, dt, reg1=reg, labelSymbol = symbol)
|
||||
IRInstruction(Opcode.STOREM, dt, reg1=reg, labelSymbol = symbol)
|
||||
}
|
||||
else {
|
||||
val factorReg = vmRegisters.nextFree()
|
||||
code += IRCodeInstruction(Opcode.LOAD, dt, reg1=factorReg, value= factor)
|
||||
code += IRInstruction(Opcode.LOAD, dt, reg1=factorReg, value= factor)
|
||||
code += if(signed) {
|
||||
if(knownAddress!=null)
|
||||
IRCodeInstruction(Opcode.DIVSM, dt, reg1 = factorReg, value = knownAddress)
|
||||
IRInstruction(Opcode.DIVSM, dt, reg1 = factorReg, value = knownAddress)
|
||||
else
|
||||
IRCodeInstruction(Opcode.DIVSM, dt, reg1 = factorReg, labelSymbol = symbol)
|
||||
IRInstruction(Opcode.DIVSM, dt, reg1 = factorReg, labelSymbol = symbol)
|
||||
}
|
||||
else {
|
||||
if(knownAddress!=null)
|
||||
IRCodeInstruction(Opcode.DIVM, dt, reg1 = factorReg, value = knownAddress)
|
||||
IRInstruction(Opcode.DIVM, dt, reg1 = factorReg, value = knownAddress)
|
||||
else
|
||||
IRCodeInstruction(Opcode.DIVM, dt, reg1 = factorReg, labelSymbol = symbol)
|
||||
IRInstruction(Opcode.DIVM, dt, reg1 = factorReg, labelSymbol = symbol)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -776,16 +788,16 @@ class IRCodeGen(
|
||||
// if and else parts
|
||||
val elseLabel = createLabelName()
|
||||
val afterIfLabel = createLabelName()
|
||||
code += IRCodeInstruction(elseBranch, vmDt, reg1=leftReg, reg2=rightReg, labelSymbol = elseLabel)
|
||||
code += IRInstruction(elseBranch, vmDt, reg1=leftReg, reg2=rightReg, labelSymbol = elseLabel)
|
||||
code += translateNode(ifElse.ifScope)
|
||||
code += IRCodeInstruction(Opcode.JUMP, labelSymbol = afterIfLabel)
|
||||
code += IRInstruction(Opcode.JUMP, labelSymbol = afterIfLabel)
|
||||
code += IRCodeLabel(elseLabel)
|
||||
code += translateNode(ifElse.elseScope)
|
||||
code += IRCodeLabel(afterIfLabel)
|
||||
} else {
|
||||
// only if part
|
||||
val afterIfLabel = createLabelName()
|
||||
code += IRCodeInstruction(elseBranch, vmDt, reg1=leftReg, reg2=rightReg, labelSymbol = afterIfLabel)
|
||||
code += IRInstruction(elseBranch, vmDt, reg1=leftReg, reg2=rightReg, labelSymbol = afterIfLabel)
|
||||
code += translateNode(ifElse.ifScope)
|
||||
code += IRCodeLabel(afterIfLabel)
|
||||
}
|
||||
@@ -800,16 +812,16 @@ class IRCodeGen(
|
||||
// if and else parts
|
||||
val elseLabel = createLabelName()
|
||||
val afterIfLabel = createLabelName()
|
||||
code += IRCodeInstruction(elseBranch, vmDt, reg1=leftReg, labelSymbol = elseLabel)
|
||||
code += IRInstruction(elseBranch, vmDt, reg1=leftReg, labelSymbol = elseLabel)
|
||||
code += translateNode(ifElse.ifScope)
|
||||
code += IRCodeInstruction(Opcode.JUMP, labelSymbol = afterIfLabel)
|
||||
code += IRInstruction(Opcode.JUMP, labelSymbol = afterIfLabel)
|
||||
code += IRCodeLabel(elseLabel)
|
||||
code += translateNode(ifElse.elseScope)
|
||||
code += IRCodeLabel(afterIfLabel)
|
||||
} else {
|
||||
// only if part
|
||||
val afterIfLabel = createLabelName()
|
||||
code += IRCodeInstruction(elseBranch, vmDt, reg1=leftReg, labelSymbol = afterIfLabel)
|
||||
code += IRInstruction(elseBranch, vmDt, reg1=leftReg, labelSymbol = afterIfLabel)
|
||||
code += translateNode(ifElse.ifScope)
|
||||
code += IRCodeLabel(afterIfLabel)
|
||||
}
|
||||
@@ -858,18 +870,18 @@ class IRCodeGen(
|
||||
val array = postIncrDecr.target.array
|
||||
val vmDt = vmType(postIncrDecr.target.type)
|
||||
if(ident!=null) {
|
||||
code += IRCodeInstruction(operationMem, vmDt, labelSymbol = ident.targetName.joinToString("."))
|
||||
code += IRInstruction(operationMem, vmDt, labelSymbol = ident.targetName.joinToString("."))
|
||||
} else if(memory!=null) {
|
||||
if(memory.address is PtNumber) {
|
||||
val address = (memory.address as PtNumber).number.toInt()
|
||||
code += IRCodeInstruction(operationMem, vmDt, value = address)
|
||||
code += IRInstruction(operationMem, vmDt, value = address)
|
||||
} else {
|
||||
val incReg = vmRegisters.nextFree()
|
||||
val addressReg = vmRegisters.nextFree()
|
||||
code += expressionEval.translateExpression(memory.address, addressReg, -1)
|
||||
code += IRCodeInstruction(Opcode.LOADI, vmDt, reg1 = incReg, reg2 = addressReg)
|
||||
code += IRCodeInstruction(operationRegister, vmDt, reg1 = incReg)
|
||||
code += IRCodeInstruction(Opcode.STOREI, vmDt, reg1 = incReg, reg2 = addressReg)
|
||||
code += IRInstruction(Opcode.LOADI, vmDt, reg1 = incReg, reg2 = addressReg)
|
||||
code += IRInstruction(operationRegister, vmDt, reg1 = incReg)
|
||||
code += IRInstruction(Opcode.STOREI, vmDt, reg1 = incReg, reg2 = addressReg)
|
||||
}
|
||||
} else if (array!=null) {
|
||||
val variable = array.variable.targetName.joinToString(".")
|
||||
@@ -877,14 +889,14 @@ class IRCodeGen(
|
||||
val fixedIndex = constIntValue(array.index)
|
||||
if(fixedIndex!=null) {
|
||||
val offset = fixedIndex*itemsize
|
||||
code += IRCodeInstruction(operationMem, vmDt, labelSymbol="$variable+$offset")
|
||||
code += IRInstruction(operationMem, vmDt, labelSymbol="$variable+$offset")
|
||||
} else {
|
||||
val incReg = vmRegisters.nextFree()
|
||||
val indexReg = vmRegisters.nextFree()
|
||||
code += expressionEval.translateExpression(array.index, indexReg, -1)
|
||||
code += IRCodeInstruction(Opcode.LOADX, vmDt, reg1=incReg, reg2=indexReg, labelSymbol=variable)
|
||||
code += IRCodeInstruction(operationRegister, vmDt, reg1=incReg)
|
||||
code += IRCodeInstruction(Opcode.STOREX, vmDt, reg1=incReg, reg2=indexReg, labelSymbol=variable)
|
||||
code += IRInstruction(Opcode.LOADX, vmDt, reg1=incReg, reg2=indexReg, labelSymbol=variable)
|
||||
code += IRInstruction(operationRegister, vmDt, reg1=incReg)
|
||||
code += IRInstruction(Opcode.STOREX, vmDt, reg1=incReg, reg2=indexReg, labelSymbol=variable)
|
||||
}
|
||||
} else
|
||||
throw AssemblyError("weird assigntarget")
|
||||
@@ -902,15 +914,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 += IRInstruction(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 += IRInstruction(Opcode.DEC, vmDt, reg1=counterReg)
|
||||
code += IRInstruction(Opcode.BNZ, vmDt, reg1=counterReg, labelSymbol = repeatLabel)
|
||||
code += IRCodeLabel(skipRepeatLabel)
|
||||
return code
|
||||
}
|
||||
|
||||
@@ -919,9 +934,9 @@ class IRCodeGen(
|
||||
if(jump.address!=null)
|
||||
throw AssemblyError("cannot jump to memory location in the vm target")
|
||||
code += if(jump.generatedLabel!=null)
|
||||
IRCodeInstruction(Opcode.JUMP, labelSymbol = jump.generatedLabel!!)
|
||||
IRInstruction(Opcode.JUMP, labelSymbol = jump.generatedLabel!!)
|
||||
else if(jump.identifier!=null)
|
||||
IRCodeInstruction(Opcode.JUMP, labelSymbol = jump.identifier!!.targetName.joinToString("."))
|
||||
IRInstruction(Opcode.JUMP, labelSymbol = jump.identifier!!.targetName.joinToString("."))
|
||||
else
|
||||
throw AssemblyError("weird jump")
|
||||
return code
|
||||
@@ -943,7 +958,7 @@ class IRCodeGen(
|
||||
else
|
||||
expressionEval.translateExpression(value, 0, -1)
|
||||
}
|
||||
code += IRCodeInstruction(Opcode.RETURN)
|
||||
code += IRInstruction(Opcode.RETURN)
|
||||
return code
|
||||
}
|
||||
|
||||
@@ -983,7 +998,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 {
|
||||
@@ -1020,10 +1036,4 @@ class IRCodeGen(
|
||||
internal fun isZero(expression: PtExpression): Boolean = expression is PtNumber && expression.number==0.0
|
||||
|
||||
internal fun isOne(expression: PtExpression): Boolean = expression is PtNumber && expression.number==1.0
|
||||
|
||||
fun addMemorySlab(name: String, size: UInt, align: UInt, position: Position): String {
|
||||
val scopedName = "prog8_memoryslab_$name"
|
||||
symbolTable.add(StMemorySlab(scopedName, size, align, null, position))
|
||||
return scopedName
|
||||
}
|
||||
}
|
||||
@@ -10,8 +10,8 @@ internal class IRPeepholeOptimizer(private val irprog: IRProgram) {
|
||||
if(chunk is IRCodeChunk) {
|
||||
do {
|
||||
val indexedInstructions = chunk.lines.withIndex()
|
||||
.filter { it.value is IRCodeInstruction }
|
||||
.map { IndexedValue(it.index, (it.value as IRCodeInstruction).ins) }
|
||||
.filter { it.value is IRInstruction }
|
||||
.map { IndexedValue(it.index, it.value as IRInstruction) }
|
||||
val changed = removeNops(chunk, indexedInstructions)
|
||||
|| removeDoubleLoadsAndStores(chunk, indexedInstructions) // TODO not yet implemented
|
||||
|| removeUselessArithmetic(chunk, indexedInstructions)
|
||||
@@ -26,19 +26,19 @@ internal class IRPeepholeOptimizer(private val irprog: IRProgram) {
|
||||
}
|
||||
}
|
||||
|
||||
private fun cleanupPushPop(chunk: IRCodeChunk, indexedInstructions: List<IndexedValue<Instruction>>): Boolean {
|
||||
private fun cleanupPushPop(chunk: IRCodeChunk, indexedInstructions: List<IndexedValue<IRInstruction>>): Boolean {
|
||||
// push followed by pop to same target, or different target->replace with load
|
||||
var changed = false
|
||||
indexedInstructions.reversed().forEach { (idx, ins) ->
|
||||
if(ins.opcode== Opcode.PUSH) {
|
||||
if(idx < chunk.lines.size-1) {
|
||||
val insAfter = chunk.lines[idx+1] as? IRCodeInstruction
|
||||
if(insAfter!=null && insAfter.ins.opcode == Opcode.POP) {
|
||||
if(ins.reg1==insAfter.ins.reg1) {
|
||||
val insAfter = chunk.lines[idx+1] as? IRInstruction
|
||||
if(insAfter!=null && insAfter.opcode == Opcode.POP) {
|
||||
if(ins.reg1==insAfter.reg1) {
|
||||
chunk.lines.removeAt(idx)
|
||||
chunk.lines.removeAt(idx)
|
||||
} else {
|
||||
chunk.lines[idx] = IRCodeInstruction(Opcode.LOADR, ins.type, reg1=insAfter.ins.reg1, reg2=ins.reg1)
|
||||
chunk.lines[idx] = IRInstruction(Opcode.LOADR, ins.type, reg1=insAfter.reg1, reg2=ins.reg1)
|
||||
chunk.lines.removeAt(idx+1)
|
||||
}
|
||||
changed = true
|
||||
@@ -49,23 +49,23 @@ internal class IRPeepholeOptimizer(private val irprog: IRProgram) {
|
||||
return changed
|
||||
}
|
||||
|
||||
private fun removeDoubleSecClc(chunk: IRCodeChunk, indexedInstructions: List<IndexedValue<Instruction>>): Boolean {
|
||||
private fun removeDoubleSecClc(chunk: IRCodeChunk, indexedInstructions: List<IndexedValue<IRInstruction>>): Boolean {
|
||||
// double sec, clc
|
||||
// sec+clc or clc+sec
|
||||
var changed = false
|
||||
indexedInstructions.reversed().forEach { (idx, ins) ->
|
||||
if(ins.opcode== Opcode.SEC || ins.opcode== Opcode.CLC) {
|
||||
if(idx < chunk.lines.size-1) {
|
||||
val insAfter = chunk.lines[idx+1] as? IRCodeInstruction
|
||||
if(insAfter?.ins?.opcode == ins.opcode) {
|
||||
val insAfter = chunk.lines[idx+1] as? IRInstruction
|
||||
if(insAfter?.opcode == ins.opcode) {
|
||||
chunk.lines.removeAt(idx)
|
||||
changed = true
|
||||
}
|
||||
else if(ins.opcode== Opcode.SEC && insAfter?.ins?.opcode== Opcode.CLC) {
|
||||
else if(ins.opcode== Opcode.SEC && insAfter?.opcode== Opcode.CLC) {
|
||||
chunk.lines.removeAt(idx)
|
||||
changed = true
|
||||
}
|
||||
else if(ins.opcode== Opcode.CLC && insAfter?.ins?.opcode== Opcode.SEC) {
|
||||
else if(ins.opcode== Opcode.CLC && insAfter?.opcode== Opcode.SEC) {
|
||||
chunk.lines.removeAt(idx)
|
||||
changed = true
|
||||
}
|
||||
@@ -75,7 +75,7 @@ internal class IRPeepholeOptimizer(private val irprog: IRProgram) {
|
||||
return changed
|
||||
}
|
||||
|
||||
private fun removeWeirdBranches(chunk: IRCodeChunk, indexedInstructions: List<IndexedValue<Instruction>>): Boolean {
|
||||
private fun removeWeirdBranches(chunk: IRCodeChunk, indexedInstructions: List<IndexedValue<IRInstruction>>): Boolean {
|
||||
// jump/branch to label immediately below
|
||||
var changed = false
|
||||
indexedInstructions.reversed().forEach { (idx, ins) ->
|
||||
@@ -84,7 +84,7 @@ internal class IRPeepholeOptimizer(private val irprog: IRProgram) {
|
||||
// if jumping to label immediately following this
|
||||
if(idx < chunk.lines.size-1) {
|
||||
val label = chunk.lines[idx+1] as? IRCodeLabel
|
||||
if(labelSymbol.size==1 && label?.name == labelSymbol[0]) {
|
||||
if(label?.name == labelSymbol) {
|
||||
chunk.lines.removeAt(idx)
|
||||
changed = true
|
||||
}
|
||||
@@ -94,7 +94,7 @@ internal class IRPeepholeOptimizer(private val irprog: IRProgram) {
|
||||
return changed
|
||||
}
|
||||
|
||||
private fun removeUselessArithmetic(chunk: IRCodeChunk, indexedInstructions: List<IndexedValue<Instruction>>): Boolean {
|
||||
private fun removeUselessArithmetic(chunk: IRCodeChunk, indexedInstructions: List<IndexedValue<IRInstruction>>): Boolean {
|
||||
// note: this is hard to solve for the non-immediate instructions atm because the values are loaded into registers first
|
||||
var changed = false
|
||||
indexedInstructions.reversed().forEach { (idx, ins) ->
|
||||
@@ -107,7 +107,7 @@ internal class IRPeepholeOptimizer(private val irprog: IRProgram) {
|
||||
}
|
||||
Opcode.ADD, Opcode.SUB -> {
|
||||
if (ins.value == 1) {
|
||||
chunk.lines[idx] = IRCodeInstruction(
|
||||
chunk.lines[idx] = IRInstruction(
|
||||
if (ins.opcode == Opcode.ADD) Opcode.INC else Opcode.DEC,
|
||||
ins.type,
|
||||
ins.reg1
|
||||
@@ -120,7 +120,7 @@ internal class IRPeepholeOptimizer(private val irprog: IRProgram) {
|
||||
}
|
||||
Opcode.AND -> {
|
||||
if (ins.value == 0) {
|
||||
chunk.lines[idx] = IRCodeInstruction(Opcode.LOAD, ins.type, reg1 = ins.reg1, value = 0)
|
||||
chunk.lines[idx] = IRInstruction(Opcode.LOAD, ins.type, reg1 = ins.reg1, value = 0)
|
||||
changed = true
|
||||
} else if (ins.value == 255 && ins.type == VmDataType.BYTE) {
|
||||
chunk.lines.removeAt(idx)
|
||||
@@ -135,7 +135,7 @@ internal class IRPeepholeOptimizer(private val irprog: IRProgram) {
|
||||
chunk.lines.removeAt(idx)
|
||||
changed = true
|
||||
} else if ((ins.value == 255 && ins.type == VmDataType.BYTE) || (ins.value == 65535 && ins.type == VmDataType.WORD)) {
|
||||
chunk.lines[idx] = IRCodeInstruction(Opcode.LOAD, ins.type, reg1 = ins.reg1, value = ins.value)
|
||||
chunk.lines[idx] = IRInstruction(Opcode.LOAD, ins.type, reg1 = ins.reg1, value = ins.value)
|
||||
changed = true
|
||||
}
|
||||
}
|
||||
@@ -151,7 +151,7 @@ internal class IRPeepholeOptimizer(private val irprog: IRProgram) {
|
||||
return changed
|
||||
}
|
||||
|
||||
private fun removeNops(chunk: IRCodeChunk, indexedInstructions: List<IndexedValue<Instruction>>): Boolean {
|
||||
private fun removeNops(chunk: IRCodeChunk, indexedInstructions: List<IndexedValue<IRInstruction>>): Boolean {
|
||||
var changed = false
|
||||
indexedInstructions.reversed().forEach { (idx, ins) ->
|
||||
if (ins.opcode == Opcode.NOP) {
|
||||
@@ -162,7 +162,7 @@ internal class IRPeepholeOptimizer(private val irprog: IRProgram) {
|
||||
return changed
|
||||
}
|
||||
|
||||
private fun removeDoubleLoadsAndStores(chunk: IRCodeChunk, indexedInstructions: List<IndexedValue<Instruction>>): Boolean {
|
||||
private fun removeDoubleLoadsAndStores(chunk: IRCodeChunk, indexedInstructions: List<IndexedValue<IRInstruction>>): Boolean {
|
||||
var changed = false
|
||||
indexedInstructions.forEach { (idx, ins) ->
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -36,9 +34,9 @@ class TestIRPeepholeOpt: FunSpec({
|
||||
|
||||
test("remove nops") {
|
||||
val irProg = makeIRProgram(listOf(
|
||||
IRCodeInstruction(Opcode.JUMP, labelSymbol = "dummy"),
|
||||
IRCodeInstruction(Opcode.NOP),
|
||||
IRCodeInstruction(Opcode.NOP)
|
||||
IRInstruction(Opcode.JUMP, labelSymbol = "dummy"),
|
||||
IRInstruction(Opcode.NOP),
|
||||
IRInstruction(Opcode.NOP)
|
||||
))
|
||||
irProg.lines().size shouldBe 3
|
||||
val opt = IRPeepholeOptimizer(irProg)
|
||||
@@ -48,13 +46,13 @@ class TestIRPeepholeOpt: FunSpec({
|
||||
|
||||
test("remove jmp to label below") {
|
||||
val irProg = makeIRProgram(listOf(
|
||||
IRCodeInstruction(Opcode.JUMP, labelSymbol = "label"), // removed
|
||||
IRInstruction(Opcode.JUMP, labelSymbol = "label"), // removed
|
||||
IRCodeLabel("label"),
|
||||
IRCodeInstruction(Opcode.JUMP, labelSymbol = "label2"), // removed
|
||||
IRCodeInstruction(Opcode.NOP), // removed
|
||||
IRInstruction(Opcode.JUMP, labelSymbol = "label2"), // removed
|
||||
IRInstruction(Opcode.NOP), // removed
|
||||
IRCodeLabel("label2"),
|
||||
IRCodeInstruction(Opcode.JUMP, labelSymbol = "label3"),
|
||||
IRCodeInstruction(Opcode.INC, VmDataType.BYTE, reg1=1),
|
||||
IRInstruction(Opcode.JUMP, labelSymbol = "label3"),
|
||||
IRInstruction(Opcode.INC, VmDataType.BYTE, reg1=1),
|
||||
IRCodeLabel("label3")
|
||||
))
|
||||
irProg.lines().size shouldBe 8
|
||||
@@ -64,57 +62,57 @@ class TestIRPeepholeOpt: FunSpec({
|
||||
lines.size shouldBe 5
|
||||
(lines[0] as IRCodeLabel).name shouldBe "label"
|
||||
(lines[1] as IRCodeLabel).name shouldBe "label2"
|
||||
(lines[2] as IRCodeInstruction).ins.opcode shouldBe Opcode.JUMP
|
||||
(lines[3] as IRCodeInstruction).ins.opcode shouldBe Opcode.INC
|
||||
(lines[2] as IRInstruction).opcode shouldBe Opcode.JUMP
|
||||
(lines[3] as IRInstruction).opcode shouldBe Opcode.INC
|
||||
(lines[4] as IRCodeLabel).name shouldBe "label3"
|
||||
}
|
||||
|
||||
test("remove double sec/clc") {
|
||||
val irProg = makeIRProgram(listOf(
|
||||
IRCodeInstruction(Opcode.SEC),
|
||||
IRCodeInstruction(Opcode.SEC),
|
||||
IRCodeInstruction(Opcode.SEC),
|
||||
IRCodeInstruction(Opcode.CLC),
|
||||
IRCodeInstruction(Opcode.CLC),
|
||||
IRCodeInstruction(Opcode.CLC)
|
||||
IRInstruction(Opcode.SEC),
|
||||
IRInstruction(Opcode.SEC),
|
||||
IRInstruction(Opcode.SEC),
|
||||
IRInstruction(Opcode.CLC),
|
||||
IRInstruction(Opcode.CLC),
|
||||
IRInstruction(Opcode.CLC)
|
||||
))
|
||||
irProg.lines().size shouldBe 6
|
||||
val opt = IRPeepholeOptimizer(irProg)
|
||||
opt.optimize()
|
||||
val lines = irProg.lines()
|
||||
lines.size shouldBe 1
|
||||
(lines[0] as IRCodeInstruction).ins.opcode shouldBe Opcode.CLC
|
||||
(lines[0] as IRInstruction).opcode shouldBe Opcode.CLC
|
||||
}
|
||||
|
||||
test("push followed by pop") {
|
||||
val irProg = makeIRProgram(listOf(
|
||||
IRCodeInstruction(Opcode.PUSH, VmDataType.BYTE, reg1=42),
|
||||
IRCodeInstruction(Opcode.POP, VmDataType.BYTE, reg1=42),
|
||||
IRCodeInstruction(Opcode.PUSH, VmDataType.BYTE, reg1=99),
|
||||
IRCodeInstruction(Opcode.POP, VmDataType.BYTE, reg1=222)
|
||||
IRInstruction(Opcode.PUSH, VmDataType.BYTE, reg1=42),
|
||||
IRInstruction(Opcode.POP, VmDataType.BYTE, reg1=42),
|
||||
IRInstruction(Opcode.PUSH, VmDataType.BYTE, reg1=99),
|
||||
IRInstruction(Opcode.POP, VmDataType.BYTE, reg1=222)
|
||||
))
|
||||
irProg.lines().size shouldBe 4
|
||||
val opt = IRPeepholeOptimizer(irProg)
|
||||
opt.optimize()
|
||||
val lines = irProg.lines()
|
||||
lines.size shouldBe 1
|
||||
(lines[0] as IRCodeInstruction).ins.opcode shouldBe Opcode.LOADR
|
||||
(lines[0] as IRCodeInstruction).ins.reg1 shouldBe 222
|
||||
(lines[0] as IRCodeInstruction).ins.reg2 shouldBe 99
|
||||
(lines[0] as IRInstruction).opcode shouldBe Opcode.LOADR
|
||||
(lines[0] as IRInstruction).reg1 shouldBe 222
|
||||
(lines[0] as IRInstruction).reg2 shouldBe 99
|
||||
}
|
||||
|
||||
test("remove useless div/mul, add/sub") {
|
||||
val irProg = makeIRProgram(listOf(
|
||||
IRCodeInstruction(Opcode.DIV, VmDataType.BYTE, reg1=42, value = 1),
|
||||
IRCodeInstruction(Opcode.DIVS, VmDataType.BYTE, reg1=42, value = 1),
|
||||
IRCodeInstruction(Opcode.MUL, VmDataType.BYTE, reg1=42, value = 1),
|
||||
IRCodeInstruction(Opcode.MOD, VmDataType.BYTE, reg1=42, value = 1),
|
||||
IRCodeInstruction(Opcode.DIV, VmDataType.BYTE, reg1=42, value = 2),
|
||||
IRCodeInstruction(Opcode.DIVS, VmDataType.BYTE, reg1=42, value = 2),
|
||||
IRCodeInstruction(Opcode.MUL, VmDataType.BYTE, reg1=42, value = 2),
|
||||
IRCodeInstruction(Opcode.MOD, VmDataType.BYTE, reg1=42, value = 2),
|
||||
IRCodeInstruction(Opcode.ADD, VmDataType.BYTE, reg1=42, value = 0),
|
||||
IRCodeInstruction(Opcode.SUB, VmDataType.BYTE, reg1=42, value = 0)
|
||||
IRInstruction(Opcode.DIV, VmDataType.BYTE, reg1=42, value = 1),
|
||||
IRInstruction(Opcode.DIVS, VmDataType.BYTE, reg1=42, value = 1),
|
||||
IRInstruction(Opcode.MUL, VmDataType.BYTE, reg1=42, value = 1),
|
||||
IRInstruction(Opcode.MOD, VmDataType.BYTE, reg1=42, value = 1),
|
||||
IRInstruction(Opcode.DIV, VmDataType.BYTE, reg1=42, value = 2),
|
||||
IRInstruction(Opcode.DIVS, VmDataType.BYTE, reg1=42, value = 2),
|
||||
IRInstruction(Opcode.MUL, VmDataType.BYTE, reg1=42, value = 2),
|
||||
IRInstruction(Opcode.MOD, VmDataType.BYTE, reg1=42, value = 2),
|
||||
IRInstruction(Opcode.ADD, VmDataType.BYTE, reg1=42, value = 0),
|
||||
IRInstruction(Opcode.SUB, VmDataType.BYTE, reg1=42, value = 0)
|
||||
))
|
||||
irProg.lines().size shouldBe 10
|
||||
val opt = IRPeepholeOptimizer(irProg)
|
||||
@@ -125,28 +123,28 @@ class TestIRPeepholeOpt: FunSpec({
|
||||
|
||||
test("replace add/sub 1 by inc/dec") {
|
||||
val irProg = makeIRProgram(listOf(
|
||||
IRCodeInstruction(Opcode.ADD, VmDataType.BYTE, reg1=42, value = 1),
|
||||
IRCodeInstruction(Opcode.SUB, VmDataType.BYTE, reg1=42, value = 1)
|
||||
IRInstruction(Opcode.ADD, VmDataType.BYTE, reg1=42, value = 1),
|
||||
IRInstruction(Opcode.SUB, VmDataType.BYTE, reg1=42, value = 1)
|
||||
))
|
||||
irProg.lines().size shouldBe 2
|
||||
val opt = IRPeepholeOptimizer(irProg)
|
||||
opt.optimize()
|
||||
val lines = irProg.lines()
|
||||
lines.size shouldBe 2
|
||||
(lines[0] as IRCodeInstruction).ins.opcode shouldBe Opcode.INC
|
||||
(lines[1] as IRCodeInstruction).ins.opcode shouldBe Opcode.DEC
|
||||
(lines[0] as IRInstruction).opcode shouldBe Opcode.INC
|
||||
(lines[1] as IRInstruction).opcode shouldBe Opcode.DEC
|
||||
}
|
||||
|
||||
test("remove useless and/or/xor") {
|
||||
val irProg = makeIRProgram(listOf(
|
||||
IRCodeInstruction(Opcode.AND, VmDataType.BYTE, reg1=42, value = 255),
|
||||
IRCodeInstruction(Opcode.AND, VmDataType.WORD, reg1=42, value = 65535),
|
||||
IRCodeInstruction(Opcode.OR, VmDataType.BYTE, reg1=42, value = 0),
|
||||
IRCodeInstruction(Opcode.XOR, VmDataType.BYTE, reg1=42, value = 0),
|
||||
IRCodeInstruction(Opcode.AND, VmDataType.BYTE, reg1=42, value = 200),
|
||||
IRCodeInstruction(Opcode.AND, VmDataType.WORD, reg1=42, value = 60000),
|
||||
IRCodeInstruction(Opcode.OR, VmDataType.BYTE, reg1=42, value = 1),
|
||||
IRCodeInstruction(Opcode.XOR, VmDataType.BYTE, reg1=42, value = 1)
|
||||
IRInstruction(Opcode.AND, VmDataType.BYTE, reg1=42, value = 255),
|
||||
IRInstruction(Opcode.AND, VmDataType.WORD, reg1=42, value = 65535),
|
||||
IRInstruction(Opcode.OR, VmDataType.BYTE, reg1=42, value = 0),
|
||||
IRInstruction(Opcode.XOR, VmDataType.BYTE, reg1=42, value = 0),
|
||||
IRInstruction(Opcode.AND, VmDataType.BYTE, reg1=42, value = 200),
|
||||
IRInstruction(Opcode.AND, VmDataType.WORD, reg1=42, value = 60000),
|
||||
IRInstruction(Opcode.OR, VmDataType.BYTE, reg1=42, value = 1),
|
||||
IRInstruction(Opcode.XOR, VmDataType.BYTE, reg1=42, value = 1)
|
||||
))
|
||||
irProg.lines().size shouldBe 8
|
||||
val opt = IRPeepholeOptimizer(irProg)
|
||||
@@ -157,23 +155,23 @@ class TestIRPeepholeOpt: FunSpec({
|
||||
|
||||
test("replace and/or/xor by constant number") {
|
||||
val irProg = makeIRProgram(listOf(
|
||||
IRCodeInstruction(Opcode.AND, VmDataType.BYTE, reg1=42, value = 0),
|
||||
IRCodeInstruction(Opcode.AND, VmDataType.WORD, reg1=42, value = 0),
|
||||
IRCodeInstruction(Opcode.OR, VmDataType.BYTE, reg1=42, value = 255),
|
||||
IRCodeInstruction(Opcode.OR, VmDataType.WORD, reg1=42, value = 65535)
|
||||
IRInstruction(Opcode.AND, VmDataType.BYTE, reg1=42, value = 0),
|
||||
IRInstruction(Opcode.AND, VmDataType.WORD, reg1=42, value = 0),
|
||||
IRInstruction(Opcode.OR, VmDataType.BYTE, reg1=42, value = 255),
|
||||
IRInstruction(Opcode.OR, VmDataType.WORD, reg1=42, value = 65535)
|
||||
))
|
||||
irProg.lines().size shouldBe 4
|
||||
val opt = IRPeepholeOptimizer(irProg)
|
||||
opt.optimize()
|
||||
val lines = irProg.lines()
|
||||
lines.size shouldBe 4
|
||||
(lines[0] as IRCodeInstruction).ins.opcode shouldBe Opcode.LOAD
|
||||
(lines[1] as IRCodeInstruction).ins.opcode shouldBe Opcode.LOAD
|
||||
(lines[2] as IRCodeInstruction).ins.opcode shouldBe Opcode.LOAD
|
||||
(lines[3] as IRCodeInstruction).ins.opcode shouldBe Opcode.LOAD
|
||||
(lines[0] as IRCodeInstruction).ins.value shouldBe 0
|
||||
(lines[1] as IRCodeInstruction).ins.value shouldBe 0
|
||||
(lines[2] as IRCodeInstruction).ins.value shouldBe 255
|
||||
(lines[3] as IRCodeInstruction).ins.value shouldBe 65535
|
||||
(lines[0] as IRInstruction).opcode shouldBe Opcode.LOAD
|
||||
(lines[1] as IRInstruction).opcode shouldBe Opcode.LOAD
|
||||
(lines[2] as IRInstruction).opcode shouldBe Opcode.LOAD
|
||||
(lines[3] as IRInstruction).opcode shouldBe Opcode.LOAD
|
||||
(lines[0] as IRInstruction).value shouldBe 0
|
||||
(lines[1] as IRInstruction).value shouldBe 0
|
||||
(lines[2] as IRInstruction).value shouldBe 255
|
||||
(lines[3] as IRInstruction).value shouldBe 65535
|
||||
}
|
||||
})
|
||||
@@ -1,65 +0,0 @@
|
||||
|
||||
plugins {
|
||||
id 'java'
|
||||
id 'application'
|
||||
id "org.jetbrains.kotlin.jvm"
|
||||
id "io.kotest" version "0.3.9"
|
||||
}
|
||||
|
||||
java {
|
||||
toolchain {
|
||||
languageVersion = JavaLanguageVersion.of(javaVersion)
|
||||
}
|
||||
}
|
||||
|
||||
compileKotlin {
|
||||
kotlinOptions {
|
||||
jvmTarget = javaVersion
|
||||
}
|
||||
}
|
||||
|
||||
compileTestKotlin {
|
||||
kotlinOptions {
|
||||
jvmTarget = javaVersion
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation project(':codeCore')
|
||||
implementation project(':intermediate')
|
||||
implementation project(':codeGenIntermediate')
|
||||
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"
|
||||
|
||||
testImplementation 'io.kotest:kotest-runner-junit5-jvm:5.3.2'
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
main {
|
||||
java {
|
||||
srcDirs = ["${project.projectDir}/src"]
|
||||
}
|
||||
resources {
|
||||
srcDirs = ["${project.projectDir}/res"]
|
||||
}
|
||||
}
|
||||
test {
|
||||
java {
|
||||
srcDir "${project.projectDir}/test"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
test {
|
||||
// Enable JUnit 5 (Gradle 4.6+).
|
||||
useJUnitPlatform()
|
||||
|
||||
// Always run tests, even when nothing changed.
|
||||
dependsOn 'cleanTest'
|
||||
|
||||
// Show test results.
|
||||
testLogging {
|
||||
events "skipped", "failed"
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="JAVA_MODULE" version="4">
|
||||
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/test" isTestSource="true" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<orderEntry type="library" name="KotlinJavaRuntime" level="project" />
|
||||
<orderEntry type="library" name="michael.bull.kotlin.result.jvm" level="project" />
|
||||
<orderEntry type="library" name="io.kotest.assertions.core.jvm" level="project" />
|
||||
<orderEntry type="library" name="io.kotest.runner.junit5.jvm" level="project" />
|
||||
<orderEntry type="module" module-name="codeCore" />
|
||||
<orderEntry type="module" module-name="intermediate" />
|
||||
<orderEntry type="module" module-name="codeGenIntermediate" />
|
||||
</component>
|
||||
</module>
|
||||
@@ -1,103 +0,0 @@
|
||||
package prog8.codegen.virtual
|
||||
|
||||
import prog8.code.core.AssemblyError
|
||||
import prog8.code.core.CompilationOptions
|
||||
import prog8.code.core.IAssemblyProgram
|
||||
import prog8.intermediate.*
|
||||
import java.io.BufferedWriter
|
||||
import kotlin.io.path.bufferedWriter
|
||||
import kotlin.io.path.div
|
||||
|
||||
internal class VmAssemblyProgram(override val name: String, private val irProgram: IRProgram): IAssemblyProgram {
|
||||
|
||||
override fun assemble(dummyOptions: CompilationOptions): Boolean {
|
||||
val outfile = irProgram.options.outputDir / ("$name.p8virt")
|
||||
println("write code to $outfile")
|
||||
|
||||
// at last, allocate the variables in memory.
|
||||
val allocations = VmVariableAllocator(irProgram.st, irProgram.encoding, irProgram.options.compTarget)
|
||||
|
||||
outfile.bufferedWriter().use { out ->
|
||||
allocations.asVmMemory().forEach { (name, alloc) ->
|
||||
out.write("var ${name.joinToString(".")} $alloc\n")
|
||||
}
|
||||
|
||||
out.write("------PROGRAM------\n")
|
||||
|
||||
if(!irProgram.options.dontReinitGlobals) {
|
||||
out.write("; global var inits\n")
|
||||
irProgram.globalInits.forEach { out.writeLine(it) }
|
||||
}
|
||||
irProgram.blocks.firstOrNull()?.let {
|
||||
if(it.subroutines.any { it.name=="main.start" }) {
|
||||
// there is a "main.start" entrypoint, jump to it
|
||||
out.writeLine(IRCodeInstruction(Opcode.JUMP, labelSymbol = "main.start"))
|
||||
}
|
||||
}
|
||||
|
||||
out.write("; actual program code\n")
|
||||
|
||||
irProgram.blocks.forEach { block ->
|
||||
if(block.address!=null)
|
||||
TODO("blocks can't have a load address for vm")
|
||||
out.write("; BLOCK ${block.name} ${block.position}\n")
|
||||
block.inlineAssembly.forEach { asm ->
|
||||
out.write("; ASM ${asm.position}\n")
|
||||
out.write(asm.assembly)
|
||||
out.write("\n")
|
||||
}
|
||||
block.subroutines.forEach { sub ->
|
||||
out.write("; SUB ${sub.name} ${sub.position}\n")
|
||||
out.write("_${sub.name}:\n")
|
||||
sub.chunks.forEach { chunk ->
|
||||
if(chunk is IRInlineAsmChunk) {
|
||||
out.write("; ASM ${chunk.position}\n")
|
||||
out.write(processInlinedAsm(chunk.assembly, allocations))
|
||||
out.write("\n")
|
||||
} else {
|
||||
chunk.lines.forEach { out.writeLine(it) }
|
||||
}
|
||||
}
|
||||
out.write("; END SUB ${sub.name}\n")
|
||||
}
|
||||
block.asmSubroutines.forEach { sub ->
|
||||
out.write("; ASMSUB ${sub.name} ${sub.position}\n")
|
||||
out.write("_${sub.name}:\n")
|
||||
out.write(processInlinedAsm(sub.assembly, allocations))
|
||||
out.write("\n; END ASMSUB ${sub.name}\n")
|
||||
}
|
||||
out.write("; END BLOCK ${block.name}\n")
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
private fun processInlinedAsm(asm: String, allocations: VmVariableAllocator): String {
|
||||
// TODO do we have to replace variable names by their allocated address???
|
||||
return asm
|
||||
}
|
||||
}
|
||||
|
||||
private fun BufferedWriter.writeLine(line: IRCodeLine) {
|
||||
when(line) {
|
||||
is IRCodeComment -> {
|
||||
write("; ${line.comment}\n")
|
||||
}
|
||||
is IRCodeInstruction -> {
|
||||
write(line.ins.toString() + "\n")
|
||||
}
|
||||
is IRCodeInlineBinary -> {
|
||||
write("!binary ")
|
||||
line.data.withIndex().forEach {(index, byte) ->
|
||||
write(byte.toString(16).padStart(2,'0'))
|
||||
if(index and 63 == 63 && index<line.data.size-1)
|
||||
write("\n!binary ")
|
||||
}
|
||||
write("\n")
|
||||
}
|
||||
is IRCodeLabel -> {
|
||||
write("_${line.name}:\n")
|
||||
}
|
||||
else -> throw AssemblyError("invalid IR code line")
|
||||
}
|
||||
}
|
||||
@@ -1,92 +0,0 @@
|
||||
package prog8.codegen.virtual
|
||||
|
||||
import prog8.code.SymbolTable
|
||||
import prog8.code.core.*
|
||||
import prog8.intermediate.getTypeString
|
||||
|
||||
internal class VmVariableAllocator(val st: SymbolTable, val encoding: IStringEncoding, memsizer: IMemSizer) {
|
||||
|
||||
internal val allocations = mutableMapOf<List<String>, Int>()
|
||||
private var freeMemoryStart: Int
|
||||
|
||||
val freeMem: Int
|
||||
get() = freeMemoryStart
|
||||
|
||||
|
||||
init {
|
||||
var nextLocation = 0
|
||||
for (variable in st.allVariables) {
|
||||
val memsize =
|
||||
when (variable.dt) {
|
||||
DataType.STR -> variable.onetimeInitializationStringValue!!.first.length + 1 // include the zero byte
|
||||
in NumericDatatypes -> memsizer.memorySize(variable.dt)
|
||||
in ArrayDatatypes -> memsizer.memorySize(variable.dt, variable.length!!)
|
||||
else -> throw InternalCompilerException("weird dt")
|
||||
}
|
||||
|
||||
allocations[variable.scopedName] = nextLocation
|
||||
nextLocation += memsize
|
||||
}
|
||||
for(slab in st.allMemorySlabs) {
|
||||
// we ignore the alignment for the VM.
|
||||
allocations[slab.scopedName] = nextLocation
|
||||
nextLocation += slab.size.toInt()
|
||||
}
|
||||
|
||||
freeMemoryStart = nextLocation
|
||||
}
|
||||
|
||||
fun asVmMemory(): List<Pair<List<String>, String>> {
|
||||
val mm = mutableListOf<Pair<List<String>, String>>()
|
||||
|
||||
// normal variables
|
||||
for (variable in st.allVariables) {
|
||||
val location = allocations.getValue(variable.scopedName)
|
||||
val value = when(variable.dt) {
|
||||
DataType.FLOAT -> (variable.onetimeInitializationNumericValue ?: 0.0).toString()
|
||||
in NumericDatatypes -> (variable.onetimeInitializationNumericValue ?: 0).toHex()
|
||||
DataType.STR -> {
|
||||
val encoded = encoding.encodeString(variable.onetimeInitializationStringValue!!.first, variable.onetimeInitializationStringValue!!.second) + listOf(0u)
|
||||
encoded.joinToString(",") { it.toInt().toHex() }
|
||||
}
|
||||
DataType.ARRAY_F -> {
|
||||
if(variable.onetimeInitializationArrayValue!=null) {
|
||||
variable.onetimeInitializationArrayValue!!.joinToString(",") { it.number!!.toString() }
|
||||
} else {
|
||||
(1..variable.length!!).joinToString(",") { "0" }
|
||||
}
|
||||
}
|
||||
in ArrayDatatypes -> {
|
||||
if(variable.onetimeInitializationArrayValue!==null) {
|
||||
variable.onetimeInitializationArrayValue!!.joinToString(",") { it.number!!.toHex() }
|
||||
} else {
|
||||
(1..variable.length!!).joinToString(",") { "0" }
|
||||
}
|
||||
}
|
||||
else -> throw InternalCompilerException("weird dt")
|
||||
}
|
||||
mm.add(Pair(variable.scopedName, "@$location ${getTypeString(variable)} $value"))
|
||||
}
|
||||
|
||||
// memory mapped variables
|
||||
for (variable in st.allMemMappedVariables) {
|
||||
val value = when(variable.dt) {
|
||||
DataType.FLOAT -> "0.0"
|
||||
in NumericDatatypes -> "0"
|
||||
DataType.ARRAY_F -> (1..variable.length!!).joinToString(",") { "0.0" }
|
||||
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"))
|
||||
}
|
||||
|
||||
// memory slabs.
|
||||
for(slab in st.allMemorySlabs) {
|
||||
val address = allocations.getValue(slab.scopedName)
|
||||
mm.add(Pair(slab.scopedName, "@$address ubyte[${slab.size}] 0"))
|
||||
}
|
||||
|
||||
return mm
|
||||
}
|
||||
|
||||
}
|
||||
@@ -31,8 +31,8 @@ dependencies {
|
||||
implementation project(':codeOptimizers')
|
||||
implementation project(':compilerAst')
|
||||
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"
|
||||
|
||||
@@ -21,6 +21,6 @@
|
||||
<orderEntry type="module" module-name="codeOptimizers" />
|
||||
<orderEntry type="module" module-name="codeGenCpu6502" />
|
||||
<orderEntry type="module" module-name="codeGenExperimental" />
|
||||
<orderEntry type="module" module-name="codeGenVirtual" />
|
||||
<orderEntry type="module" module-name="virtualmachine" />
|
||||
</component>
|
||||
</module>
|
||||
</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.2
|
||||
@@ -2,16 +2,20 @@ package prog8
|
||||
|
||||
import kotlinx.cli.*
|
||||
import prog8.ast.base.AstException
|
||||
import prog8.code.core.*
|
||||
import prog8.code.core.CbmPrgLauncherType
|
||||
import prog8.code.core.toHex
|
||||
import prog8.code.target.*
|
||||
import prog8.code.target.virtual.VirtualMachineDefinition
|
||||
import prog8.codegen.virtual.VmCodeGen
|
||||
import prog8.compiler.CompilationResult
|
||||
import prog8.compiler.CompilerArguments
|
||||
import prog8.compiler.compileProgram
|
||||
import java.io.File
|
||||
import java.nio.file.*
|
||||
import java.nio.file.FileSystems
|
||||
import java.nio.file.Path
|
||||
import java.nio.file.StandardWatchEventKinds
|
||||
import java.nio.file.WatchKey
|
||||
import java.time.LocalDateTime
|
||||
import kotlin.io.path.Path
|
||||
import kotlin.system.exitProcess
|
||||
|
||||
|
||||
@@ -47,7 +51,7 @@ private fun compileMain(args: Array<String>): Boolean {
|
||||
val slowCodegenWarnings by cli.option(ArgType.Boolean, fullName = "slowwarn", description="show debug warnings about slow/problematic assembly code generation")
|
||||
val sourceDirs by cli.option(ArgType.String, fullName="srcdirs", description = "list of extra paths, separated with ${File.pathSeparator}, to search in for imported modules").multiple().delimiter(File.pathSeparator)
|
||||
val compilationTarget by cli.option(ArgType.String, fullName = "target", description = "target output of the compiler (one of '${C64Target.NAME}', '${C128Target.NAME}', '${Cx16Target.NAME}', '${AtariTarget.NAME}', '${VMTarget.NAME}')").default(C64Target.NAME)
|
||||
val startVm by cli.option(ArgType.Boolean, fullName = "vm", description = "load and run a p8-virt or p8-ir listing in the VM instead")
|
||||
val startVm by cli.option(ArgType.Boolean, fullName = "vm", description = "load and run a .p8ir IR source file in the VM")
|
||||
val watchMode by cli.option(ArgType.Boolean, fullName = "watch", description = "continuous compilation mode (watch for file changes)")
|
||||
val moduleFiles by cli.argument(ArgType.String, fullName = "modules", description = "main module file(s) to compile").multiple(999)
|
||||
|
||||
@@ -142,7 +146,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.")
|
||||
@@ -244,28 +248,9 @@ private fun processSymbolDefs(symbolDefs: List<String>): Map<String, String>? {
|
||||
return result
|
||||
}
|
||||
|
||||
fun runVm(listingFilename: String): Boolean {
|
||||
if(listingFilename.endsWith(".p8ir")) {
|
||||
val withoutSuffix = listingFilename.substring(0, listingFilename.length-5)
|
||||
val compiled = VmCodeGen.compileIR(withoutSuffix)
|
||||
if (!compiled.assemble(CompilationOptions( // these are just dummy options, the actual options are inside the .p8ir file itself:
|
||||
OutputType.PRG,
|
||||
CbmPrgLauncherType.NONE,
|
||||
ZeropageType.DONTUSE,
|
||||
emptyList(),
|
||||
floats = true,
|
||||
noSysInit = true,
|
||||
compTarget = VMTarget(),
|
||||
loadAddress = VMTarget().machine.PROGRAM_LOAD_ADDRESS
|
||||
))
|
||||
) {
|
||||
return false
|
||||
}
|
||||
val vmdef = VirtualMachineDefinition()
|
||||
vmdef.launchEmulator(0, Paths.get(withoutSuffix))
|
||||
return true
|
||||
}
|
||||
fun runVm(irFilename: String): Boolean {
|
||||
val irFile = Path(irFilename)
|
||||
val vmdef = VirtualMachineDefinition()
|
||||
vmdef.launchEmulator(0, Paths.get(listingFilename))
|
||||
vmdef.launchEmulator(0, irFile)
|
||||
return true
|
||||
}
|
||||
@@ -17,6 +17,7 @@ import prog8.code.target.*
|
||||
import prog8.compiler.astprocessing.*
|
||||
import prog8.optimizer.*
|
||||
import prog8.parser.ParseError
|
||||
import prog8.vm.codegen.VmCodeGen
|
||||
import java.nio.file.Path
|
||||
import kotlin.io.path.Path
|
||||
import kotlin.io.path.nameWithoutExtension
|
||||
@@ -455,7 +456,7 @@ internal fun asmGeneratorFor(program: Program,
|
||||
return prog8.codegen.cpu6502.AsmGen(program, symbolTable, options, errors)
|
||||
if (options.compTarget.name == VMTarget.NAME) {
|
||||
val intermediateAst = IntermediateAstMaker(program).transform()
|
||||
return prog8.codegen.virtual.VmCodeGen(intermediateAst, symbolTable, options, errors)
|
||||
return VmCodeGen(intermediateAst, symbolTable, options, errors)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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 }
|
||||
|
||||
@@ -104,4 +104,16 @@ internal class SymbolTableMaker: IAstVisitor {
|
||||
scopestack.peek().add(node)
|
||||
// st.origAstLinks[label] = node
|
||||
}
|
||||
|
||||
override fun visit(fcall: BuiltinFunctionCall) {
|
||||
if(fcall.name=="memory") {
|
||||
// memory slab allocations are a builtin functioncall in the program, but end up named as well in the symboltable
|
||||
val name = (fcall.args[0] as StringLiteral).value
|
||||
require(name.all { it.isLetterOrDigit() || it=='_' }) {"memory name should be a valid symbol name"}
|
||||
val size = (fcall.args[1] as NumericLiteral).number.toUInt()
|
||||
val align = (fcall.args[2] as NumericLiteral).number.toUInt()
|
||||
st.add(StMemorySlab("prog8_memoryslab_$name", size, align, fcall.position))
|
||||
}
|
||||
super.visit(fcall)
|
||||
}
|
||||
}
|
||||
@@ -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,37 @@
|
||||
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=".p8ir")
|
||||
tmpfile.writeText("""<PROGRAM NAME=test>
|
||||
<OPTIONS>
|
||||
</OPTIONS>
|
||||
|
||||
<VARIABLES>
|
||||
</VARIABLES>
|
||||
|
||||
<MEMORYMAPPEDVARIABLES>
|
||||
</MEMORYMAPPEDVARIABLES>
|
||||
|
||||
<MEMORYSLABS>
|
||||
</MEMORYSLABS>
|
||||
|
||||
<INITGLOBALS>
|
||||
</INITGLOBALS>
|
||||
|
||||
<BLOCK NAME=main ADDRESS=null ALIGN=NONE POS=[unittest: line 42 col 1-9]>
|
||||
</BLOCK>
|
||||
</PROGRAM>
|
||||
""")
|
||||
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") {
|
||||
|
||||
@@ -1,15 +1,10 @@
|
||||
package prog8tests.helpers
|
||||
|
||||
import prog8.ast.Program
|
||||
import prog8.code.core.*
|
||||
import prog8.code.target.C64Target
|
||||
import prog8.code.target.c64.C64Zeropage
|
||||
import prog8.codegen.cpu6502.AsmGen
|
||||
import prog8.code.core.ICompilationTarget
|
||||
import prog8.code.core.IErrorReporter
|
||||
import prog8.compiler.CompilationResult
|
||||
import prog8.compiler.CompilerArguments
|
||||
import prog8.compiler.astprocessing.SymbolTableMaker
|
||||
import prog8.compiler.compileProgram
|
||||
import prog8.compiler.determineProgramLoadAddress
|
||||
import java.nio.file.Path
|
||||
import kotlin.io.path.name
|
||||
|
||||
@@ -22,6 +17,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 +32,7 @@ internal fun compileFile(
|
||||
quietAssembler = true,
|
||||
asmListfile = false,
|
||||
experimentalCodegen = false,
|
||||
keepIR = false,
|
||||
keepIR = keepIR,
|
||||
platform.name,
|
||||
evalStackBaseAddress = null,
|
||||
symbolDefs = emptyMap(),
|
||||
@@ -57,30 +53,12 @@ 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)
|
||||
}
|
||||
|
||||
|
||||
internal fun generateAssembly(
|
||||
program: Program,
|
||||
options: CompilationOptions? = null
|
||||
): IAssemblyProgram? {
|
||||
val coptions = options ?: CompilationOptions(OutputType.RAW, CbmPrgLauncherType.BASIC, ZeropageType.DONTUSE, emptyList(),
|
||||
floats = true,
|
||||
noSysInit = true,
|
||||
compTarget = C64Target(),
|
||||
loadAddress = 0u, outputDir = outputDir)
|
||||
coptions.compTarget.machine.zeropage = C64Zeropage(coptions)
|
||||
val st = SymbolTableMaker().makeFrom(program)
|
||||
val errors = ErrorReporterForTests()
|
||||
determineProgramLoadAddress(program, coptions, errors)
|
||||
errors.report()
|
||||
val asmgen = AsmGen(program, st, coptions, errors)
|
||||
errors.report()
|
||||
return asmgen.compileToAssembly()
|
||||
return compileFile(platform, optimize, filePath.parent, filePath.name,
|
||||
errors=errors, writeAssembly=writeAssembly, optFloatExpr = optFloatExpr, keepIR=keepIR)
|
||||
}
|
||||
@@ -0,0 +1,163 @@
|
||||
package prog8tests.vm
|
||||
|
||||
import io.kotest.core.spec.style.FunSpec
|
||||
import io.kotest.matchers.shouldBe
|
||||
import io.kotest.matchers.shouldNotBe
|
||||
import prog8.ast.expressions.BuiltinFunctionCall
|
||||
import prog8.ast.statements.Assignment
|
||||
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)!!
|
||||
val virtfile = result.compilationOptions.outputDir.resolve(result.program.name + ".p8ir")
|
||||
VmRunner().runProgram(virtfile.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)!!
|
||||
val virtfile = result.compilationOptions.outputDir.resolve(result.program.name + ".p8ir")
|
||||
VmRunner().runProgram(virtfile.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)!!
|
||||
val virtfile = result.compilationOptions.outputDir.resolve(result.program.name + ".p8ir")
|
||||
VmRunner().runProgram(virtfile.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)!!
|
||||
val virtfile = result.compilationOptions.outputDir.resolve(result.program.name + ".p8ir")
|
||||
VmRunner().runProgram(virtfile.readText())
|
||||
}
|
||||
|
||||
test("case sensitive symbols") {
|
||||
val src = """
|
||||
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)!!
|
||||
val virtfile = result.compilationOptions.outputDir.resolve(result.program.name + ".p8ir")
|
||||
VmRunner().runAndTestProgram(virtfile.readText()) { vm ->
|
||||
vm.memory.getUB(0) shouldBe 42u
|
||||
vm.memory.getUB(3) shouldBe 66u
|
||||
}
|
||||
}
|
||||
|
||||
test("memory slabs") {
|
||||
val src = """
|
||||
main {
|
||||
sub start() {
|
||||
uword slab1 = memory("slab1", 2000, 64)
|
||||
slab1[10]=42
|
||||
slab1[11]=43
|
||||
ubyte @shared value1 = slab1[10] ; var at 2
|
||||
ubyte @shared value2 = slab1[11] ; var at 3
|
||||
}
|
||||
}"""
|
||||
val target = VMTarget()
|
||||
val result = compileText(target, true, src, writeAssembly = true)!!
|
||||
val start = result.program.entrypoint
|
||||
start.statements.size shouldBe 9
|
||||
((start.statements[1] as Assignment).value as BuiltinFunctionCall).name shouldBe "memory"
|
||||
val virtfile = result.compilationOptions.outputDir.resolve(result.program.name + ".p8ir")
|
||||
VmRunner().runAndTestProgram(virtfile.readText()) { vm ->
|
||||
vm.memory.getUB(2) shouldBe 42u
|
||||
vm.memory.getUB(3) shouldBe 43u
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -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 {
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
sphinx>=4.0
|
||||
sphinx_rtd_theme==1.0.0
|
||||
sphinx>=4.4.0, !=5.2.0.post0 # https://github.com/sphinx-doc/sphinx/issues/10860
|
||||
sphinx_rtd_theme>=1.0.0
|
||||
@@ -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
|
||||
|
||||
|
||||
Binary file not shown.
@@ -13,13 +13,10 @@
|
||||
<font id="EmbeddedFont_1" horiz-adv-x="2048">
|
||||
<font-face font-family="Bitstream Vera Sans Mono embedded" units-per-em="2048" font-weight="normal" font-style="normal" ascent="1879" descent="476"/>
|
||||
<missing-glyph horiz-adv-x="2048" d="M 0,0 L 2047,0 2047,2047 0,2047 0,0 Z"/>
|
||||
<glyph unicode="v" horiz-adv-x="1060" d="M 100,1120 L 291,1120 616,180 942,1120 1133,1120 735,0 498,0 100,1120 Z"/>
|
||||
<glyph unicode="t" horiz-adv-x="927" d="M 614,1438 L 614,1120 1032,1120 1032,977 614,977 614,369 C 614,286 630,229 661,196 692,163 747,147 825,147 L 1032,147 1032,0 807,0 C 669,0 572,28 515,83 458,138 430,234 430,369 L 430,977 131,977 131,1120 430,1120 430,1438 614,1438 Z"/>
|
||||
<glyph unicode="r" horiz-adv-x="821" d="M 1155,889 C 1116,920 1076,942 1035,956 994,970 950,977 901,977 786,977 699,941 638,869 577,797 547,693 547,557 L 547,0 362,0 362,1120 547,1120 547,901 C 578,980 625,1041 689,1084 752,1126 828,1147 915,1147 960,1147 1003,1141 1042,1130 1081,1119 1119,1101 1155,1077 L 1155,889 Z"/>
|
||||
<glyph unicode="p" horiz-adv-x="927" d="M 375,141 L 375,-426 190,-426 190,1120 375,1120 375,977 C 406,1032 447,1075 498,1104 549,1133 607,1147 674,1147 809,1147 916,1095 993,990 1070,885 1108,740 1108,555 1108,373 1069,230 992,127 915,23 809,-29 674,-29 606,-29 547,-14 496,15 445,44 404,86 375,141 Z M 915,559 C 915,702 893,809 848,882 803,955 736,991 647,991 558,991 490,955 444,882 398,809 375,701 375,559 375,418 398,310 444,237 490,164 558,127 647,127 736,127 803,163 848,236 893,309 915,416 915,559 Z"/>
|
||||
<glyph unicode="o" horiz-adv-x="980" d="M 616,991 C 523,991 452,955 404,882 356,809 332,702 332,559 332,417 356,310 404,237 452,164 523,127 616,127 710,127 781,164 829,237 877,310 901,417 901,559 901,702 877,809 829,882 781,955 710,991 616,991 Z M 616,1147 C 771,1147 890,1097 973,996 1055,895 1096,750 1096,559 1096,368 1055,222 973,122 891,21 772,-29 616,-29 461,-29 342,21 260,122 178,222 137,368 137,559 137,750 178,895 260,996 342,1097 461,1147 616,1147 Z"/>
|
||||
<glyph unicode="l" horiz-adv-x="874" d="M 639,406 C 639,323 654,261 685,219 715,177 760,156 819,156 L 1034,156 1034,0 801,0 C 691,0 606,35 546,106 485,177 455,277 455,406 L 455,1423 160,1423 160,1567 639,1567 639,406 Z"/>
|
||||
<glyph unicode="i" horiz-adv-x="953" d="M 256,1120 L 727,1120 727,143 1092,143 1092,0 178,0 178,143 543,143 543,977 256,977 256,1120 Z M 543,1556 L 727,1556 727,1323 543,1323 543,1556 Z"/>
|
||||
<glyph unicode="h" horiz-adv-x="874" d="M 1051,694 L 1051,0 866,0 866,694 C 866,795 848,869 813,916 778,963 722,987 647,987 561,987 495,957 449,896 402,835 379,747 379,633 L 379,0 195,0 195,1556 379,1556 379,952 C 412,1016 456,1065 512,1098 568,1131 634,1147 711,1147 825,1147 910,1110 967,1035 1023,960 1051,846 1051,694 Z"/>
|
||||
<glyph unicode="g" horiz-adv-x="953" d="M 858,569 C 858,707 836,812 791,884 746,955 680,991 594,991 504,991 435,955 388,884 341,812 317,707 317,569 317,431 341,326 389,254 436,181 505,145 596,145 681,145 746,181 791,254 836,327 858,432 858,569 Z M 1042,72 C 1042,-96 1002,-223 923,-310 844,-397 727,-440 573,-440 522,-440 469,-435 414,-426 359,-417 303,-403 248,-385 L 248,-203 C 313,-234 373,-256 426,-271 479,-286 528,-293 573,-293 672,-293 745,-266 790,-212 835,-158 858,-72 858,45 L 858,53 858,178 C 829,115 789,69 738,38 687,7 626,-8 553,-8 422,-8 318,44 240,149 162,254 123,394 123,569 123,745 162,885 240,990 318,1095 422,1147 553,1147 625,1147 686,1133 736,1104 786,1075 827,1031 858,971 L 858,1116 1042,1116 1042,72 Z"/>
|
||||
<glyph unicode="e" horiz-adv-x="1006" d="M 1112,606 L 1112,516 315,516 315,510 C 315,388 347,294 411,227 474,160 564,127 680,127 739,127 800,136 864,155 928,174 996,202 1069,240 L 1069,57 C 999,28 932,7 867,-8 802,-22 739,-29 678,-29 504,-29 368,23 270,128 172,232 123,376 123,559 123,738 171,880 267,987 363,1094 491,1147 651,1147 794,1147 906,1099 989,1002 1071,905 1112,773 1112,606 Z M 928,659 C 925,767 900,850 852,906 803,963 734,991 643,991 554,991 481,962 424,903 367,844 333,763 322,659 L 928,659 Z"/>
|
||||
@@ -63,6 +60,7 @@
|
||||
<glyph unicode="I" horiz-adv-x="636" d="M 78,0 L 78,86 104,86 C 127,86 149,88 170,91 190,94 208,101 223,112 238,122 250,137 259,156 268,175 272,201 272,233 L 272,1229 C 272,1261 268,1287 259,1306 250,1325 238,1340 223,1351 208,1361 190,1368 170,1371 149,1374 127,1376 104,1376 L 78,1376 78,1462 674,1462 674,1376 647,1376 C 624,1376 603,1374 582,1371 561,1368 544,1361 529,1351 514,1340 502,1325 493,1306 484,1287 479,1261 479,1229 L 479,233 C 479,201 484,175 493,156 502,137 514,122 529,112 544,101 561,94 582,91 603,88 624,86 647,86 L 674,86 674,0 78,0 Z"/>
|
||||
<glyph unicode="G" horiz-adv-x="1324" d="M 821,-20 C 702,-20 599,-2 511,34 422,70 349,121 291,187 232,253 189,332 160,425 131,518 117,620 117,733 117,844 132,945 163,1037 193,1129 238,1208 299,1274 359,1340 434,1391 523,1428 612,1465 716,1483 834,1483 910,1483 976,1477 1033,1466 1089,1454 1136,1438 1174,1417 1211,1396 1239,1372 1258,1344 1277,1316 1286,1286 1286,1253 1286,1231 1281,1211 1271,1194 1261,1176 1248,1161 1231,1149 1214,1136 1194,1127 1171,1120 1148,1113 1123,1110 1096,1110 1096,1142 1091,1174 1082,1205 1073,1236 1057,1265 1036,1290 1015,1315 987,1335 952,1351 917,1366 875,1374 825,1374 738,1374 665,1360 604,1332 543,1303 493,1262 454,1208 415,1153 387,1086 370,1007 353,927 344,836 344,733 344,631 353,540 371,461 389,382 418,315 459,261 500,207 552,166 617,138 682,110 761,96 854,96 893,96 932,98 969,102 1006,106 1038,112 1067,121 L 1067,451 C 1067,481 1063,505 1054,524 1045,542 1032,556 1017,566 1002,575 984,582 964,585 943,588 922,590 899,590 L 891,590 891,676 1423,676 1423,590 1415,590 C 1396,590 1377,588 1360,585 1343,582 1328,575 1315,565 1302,554 1292,539 1285,520 1278,500 1274,474 1274,442 L 1274,74 C 1205,42 1135,18 1062,3 989,-12 909,-20 821,-20 Z"/>
|
||||
<glyph unicode="C" horiz-adv-x="1086" d="M 774,1483 C 844,1483 905,1477 957,1466 1008,1454 1051,1438 1086,1417 1120,1396 1146,1372 1163,1344 1180,1316 1188,1286 1188,1253 1188,1231 1184,1211 1175,1194 1166,1176 1153,1161 1137,1149 1121,1136 1102,1127 1081,1120 1059,1113 1035,1110 1010,1110 1010,1142 1006,1174 998,1205 989,1236 976,1265 957,1290 938,1315 913,1335 883,1351 852,1366 815,1374 770,1374 693,1374 627,1360 573,1332 519,1303 475,1262 441,1208 407,1153 382,1086 367,1007 352,927 344,836 344,733 344,642 352,558 368,482 384,406 409,341 444,287 479,232 523,190 577,160 630,130 695,115 770,115 820,115 864,120 903,130 941,139 975,152 1004,168 1033,184 1059,203 1082,224 1104,245 1124,266 1141,289 1152,282 1162,272 1169,259 1176,246 1180,230 1180,209 1180,183 1171,157 1154,130 1136,103 1109,78 1073,56 1037,34 991,16 936,2 881,-13 815,-20 739,-20 637,-20 547,-2 470,34 393,70 328,121 276,187 223,253 184,332 157,425 130,518 117,620 117,733 117,844 131,945 159,1037 187,1129 229,1208 284,1274 339,1340 407,1391 489,1428 571,1465 666,1483 774,1483 Z"/>
|
||||
<glyph unicode="B" horiz-adv-x="1165" d="M 1153,1096 C 1153,1050 1147,1010 1134,975 1121,940 1103,910 1080,884 1057,858 1031,836 1000,819 969,801 935,786 899,774 L 899,766 C 946,758 988,744 1027,725 1066,706 1099,681 1127,650 1154,619 1176,582 1191,540 1206,497 1214,449 1214,395 1214,263 1171,164 1084,99 997,33 865,0 688,0 L 78,0 78,86 104,86 C 127,86 149,88 170,91 190,94 208,101 223,112 238,122 250,137 259,156 268,175 272,201 272,233 L 272,1237 C 272,1267 268,1291 259,1310 250,1328 237,1342 222,1352 207,1362 189,1369 169,1372 148,1375 127,1376 104,1376 L 78,1376 78,1462 627,1462 C 804,1462 936,1433 1023,1374 1110,1315 1153,1222 1153,1096 Z M 479,102 L 678,102 C 736,102 785,107 825,118 865,129 898,146 923,170 948,193 967,224 978,262 989,300 995,346 995,401 995,454 990,501 980,540 970,579 953,612 929,638 904,664 872,683 832,696 792,709 742,715 682,715 L 479,715 479,102 Z M 479,817 L 621,817 C 681,817 731,822 771,833 811,844 843,860 867,883 891,905 908,934 919,969 929,1004 934,1047 934,1096 934,1146 928,1188 917,1222 906,1255 887,1282 862,1303 837,1324 804,1338 764,1347 723,1356 674,1360 616,1360 L 479,1360 479,817 Z"/>
|
||||
<glyph unicode="A" horiz-adv-x="1456" d="M 414,489 L 336,274 C 330,258 325,242 322,227 319,211 317,197 317,186 317,151 328,126 351,110 373,94 407,86 453,86 L 500,86 500,0 0,0 0,86 39,86 C 59,86 76,88 90,93 104,97 117,105 128,117 139,129 150,145 161,166 171,187 182,213 195,246 L 649,1462 809,1462 1272,195 C 1280,174 1288,156 1297,142 1305,128 1315,117 1326,109 1337,100 1350,94 1365,91 1380,88 1397,86 1417,86 L 1444,86 1444,0 881,0 881,86 928,86 C 1010,86 1051,119 1051,184 1051,195 1050,207 1047,219 1044,231 1039,245 1034,260 L 952,489 414,489 Z M 788,950 C 767,1011 747,1068 730,1121 712,1174 697,1225 686,1274 681,1249 676,1226 670,1203 663,1180 656,1156 649,1132 642,1108 633,1083 624,1057 615,1030 604,1001 592,969 L 453,592 915,592 788,950 Z"/>
|
||||
<glyph unicode="8" horiz-adv-x="980" d="M 94,367 C 94,416 102,460 118,497 133,534 155,567 183,596 211,625 244,651 282,674 320,697 361,720 406,741 367,763 332,787 299,813 266,838 238,866 215,897 192,928 174,961 161,998 148,1034 141,1073 141,1116 141,1163 149,1209 166,1253 183,1297 209,1336 244,1370 279,1404 325,1431 382,1452 438,1473 505,1483 584,1483 648,1483 705,1474 755,1457 805,1439 847,1414 882,1383 916,1352 942,1315 960,1272 978,1229 987,1181 987,1130 987,1085 980,1046 967,1012 954,978 935,948 911,921 887,894 858,869 824,848 790,826 752,805 711,784 762,760 808,734 850,707 891,680 927,650 957,618 987,586 1010,551 1027,514 1043,477 1051,436 1051,391 1051,326 1039,269 1016,218 993,167 960,123 917,88 874,53 823,26 762,8 701,-11 634,-20 559,-20 482,-20 415,-10 357,10 299,30 251,58 212,93 173,128 143,169 124,216 104,263 94,313 94,367 Z M 569,74 C 614,74 654,81 691,95 727,108 758,127 784,151 809,175 829,204 843,237 857,270 864,307 864,346 864,381 858,413 845,443 832,472 812,501 783,529 754,556 717,583 672,610 626,636 570,663 504,692 439,657 388,612 350,557 312,502 293,435 293,358 293,315 299,277 311,242 322,207 340,178 363,153 386,128 414,108 449,95 484,81 524,74 569,74 Z M 805,1135 C 805,1166 801,1197 793,1227 784,1257 771,1284 753,1308 734,1331 710,1350 681,1365 651,1379 614,1386 571,1386 533,1386 499,1380 470,1368 440,1355 415,1338 395,1317 375,1295 360,1269 350,1239 339,1209 334,1176 334,1141 334,1104 340,1071 352,1042 363,1013 381,986 404,962 427,938 455,916 490,895 525,874 565,852 610,831 649,850 681,870 706,891 731,911 750,933 765,958 780,982 790,1009 796,1038 802,1067 805,1099 805,1135 Z"/>
|
||||
<glyph unicode="6" horiz-adv-x="953" d="M 659,1384 C 556,1384 479,1334 426,1233 373,1132 343,981 336,782 353,796 371,810 392,823 412,836 434,847 458,857 482,866 508,874 537,880 565,886 596,889 629,889 692,889 748,879 799,860 850,841 893,813 929,777 964,741 992,697 1011,645 1030,593 1040,534 1040,469 1040,397 1030,331 1011,271 991,211 962,160 925,117 887,74 841,40 787,16 732,-8 670,-20 600,-20 532,-20 469,-6 411,23 353,51 303,96 261,157 218,218 185,296 161,392 137,487 125,602 125,737 125,802 130,865 140,927 149,989 164,1048 183,1103 202,1158 227,1209 256,1256 285,1303 320,1343 360,1377 399,1410 444,1436 494,1455 544,1474 599,1483 659,1483 716,1483 766,1477 809,1465 851,1453 886,1437 914,1417 942,1397 963,1374 977,1348 990,1322 997,1295 997,1268 997,1229 984,1198 957,1177 930,1156 891,1145 840,1145 840,1178 837,1210 831,1239 824,1268 814,1293 799,1315 784,1336 766,1353 743,1366 720,1378 692,1384 659,1384 Z M 588,784 C 561,784 536,781 511,774 486,767 463,758 442,747 420,736 400,723 382,709 364,694 348,680 334,666 335,563 343,476 356,403 369,330 387,271 410,225 433,179 462,145 495,124 528,103 565,92 606,92 679,92 735,121 774,178 812,235 831,325 831,449 831,566 810,651 769,704 727,757 667,784 588,784 Z"/>
|
||||
@@ -120,7 +118,7 @@
|
||||
</font>
|
||||
</defs>
|
||||
<defs class="TextShapeIndex">
|
||||
<g ooo:slide="id1" ooo:id-list="id3 id4 id5 id6 id7 id8 id9 id10 id11 id12 id13 id14 id15 id16 id17 id18 id19 id20 id21 id22 id23 id24 id25 id26 id27 id28 id29 id30 id31 id32 id33 id34 id35 id36 id37 id38 id39 id40 id41 id42 id43 id44 id45"/>
|
||||
<g ooo:slide="id1" ooo:id-list="id3 id4 id5 id6 id7 id8 id9 id10 id11 id12 id13 id14 id15 id16 id17 id18 id19 id20 id21 id22 id23 id24 id25 id26 id27 id28 id29 id30 id31 id32 id33 id34 id35 id36 id37 id38 id39 id40 id41 id42 id43 id44 id45 id46"/>
|
||||
</defs>
|
||||
<defs class="EmbeddedBulletChars">
|
||||
<g id="bullet-char-template-57356" transform="scale(0.00048828125,-0.00048828125)">
|
||||
@@ -177,7 +175,7 @@
|
||||
<rect class="BoundingBox" stroke="none" fill="none" x="1704" y="3500" width="5611" height="1213"/>
|
||||
<path fill="rgb(142,134,174)" stroke="none" d="M 4509,4685 L 1731,4685 1731,3527 7287,3527 7287,4685 4509,4685 Z"/>
|
||||
<path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 4509,4685 L 1731,4685 1731,3527 7287,3527 7287,4685 4509,4685 Z"/>
|
||||
<text class="SVGTextShape"><tspan class="TextParagraph" font-family="Droid Serif, serif" font-size="493px" font-weight="400"><tspan class="TextPosition" x="2619" y="4277"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">Read module.p8</tspan></tspan></tspan></text>
|
||||
<text class="SVGTextShape"><tspan class="TextParagraph" font-family="Droid Serif, serif" font-size="494px" font-weight="400"><tspan class="TextPosition" x="2600" y="4277"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">Read module.p8</tspan></tspan></tspan></text>
|
||||
</g>
|
||||
</g>
|
||||
<g class="com.sun.star.drawing.CustomShape">
|
||||
@@ -185,7 +183,7 @@
|
||||
<rect class="BoundingBox" stroke="none" fill="none" x="1704" y="5583" width="5611" height="1212"/>
|
||||
<path fill="rgb(142,134,174)" stroke="none" d="M 4509,6767 L 1731,6767 1731,5610 7287,5610 7287,6767 4509,6767 Z"/>
|
||||
<path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 4509,6767 L 1731,6767 1731,5610 7287,5610 7287,6767 4509,6767 Z"/>
|
||||
<text class="SVGTextShape"><tspan class="TextParagraph" font-family="Droid Serif, serif" font-size="493px" font-weight="400"><tspan class="TextPosition" x="3459" y="6360"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">Tokenize</tspan></tspan></tspan></text>
|
||||
<text class="SVGTextShape"><tspan class="TextParagraph" font-family="Droid Serif, serif" font-size="494px" font-weight="400"><tspan class="TextPosition" x="3451" y="6360"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">Tokenize</tspan></tspan></tspan></text>
|
||||
</g>
|
||||
</g>
|
||||
<g class="com.sun.star.drawing.CustomShape">
|
||||
@@ -193,7 +191,7 @@
|
||||
<rect class="BoundingBox" stroke="none" fill="none" x="1704" y="9750" width="5611" height="1213"/>
|
||||
<path fill="rgb(114,159,207)" stroke="none" d="M 4509,10935 L 1731,10935 1731,9777 7287,9777 7287,10935 4509,10935 Z"/>
|
||||
<path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 4509,10935 L 1731,10935 1731,9777 7287,9777 7287,10935 4509,10935 Z"/>
|
||||
<text class="SVGTextShape"><tspan class="TextParagraph" font-family="Droid Serif, serif" font-size="493px" font-weight="400"><tspan class="TextPosition" x="2774" y="10527"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">Convert to AST</tspan></tspan></tspan></text>
|
||||
<text class="SVGTextShape"><tspan class="TextParagraph" font-family="Droid Serif, serif" font-size="494px" font-weight="400"><tspan class="TextPosition" x="2761" y="10527"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">Convert to AST</tspan></tspan></tspan></text>
|
||||
</g>
|
||||
</g>
|
||||
<g class="com.sun.star.drawing.CustomShape">
|
||||
@@ -201,7 +199,7 @@
|
||||
<rect class="BoundingBox" stroke="none" fill="none" x="1704" y="13916" width="5611" height="1444"/>
|
||||
<path fill="rgb(114,159,207)" stroke="none" d="M 4509,15332 L 1731,15332 1731,13943 7287,13943 7287,15332 4509,15332 Z"/>
|
||||
<path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 4509,15332 L 1731,15332 1731,13943 7287,13943 7287,15332 4509,15332 Z"/>
|
||||
<text class="SVGTextShape"><tspan class="TextParagraph" font-family="Droid Serif, serif" font-size="493px" font-weight="400"><tspan class="TextPosition" x="2285" y="14523"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">Semantic and Type </tspan></tspan><tspan class="TextPosition" x="3468" y="15095"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">checking</tspan></tspan></tspan></text>
|
||||
<text class="SVGTextShape"><tspan class="TextParagraph" font-family="Droid Serif, serif" font-size="494px" font-weight="400"><tspan class="TextPosition" x="2263" y="14519"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">Semantic and Type </tspan></tspan><tspan class="TextPosition" x="3459" y="15099"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">checking</tspan></tspan></tspan></text>
|
||||
</g>
|
||||
</g>
|
||||
<g class="com.sun.star.drawing.CustomShape">
|
||||
@@ -209,7 +207,7 @@
|
||||
<rect class="BoundingBox" stroke="none" fill="none" x="1704" y="11833" width="5611" height="1212"/>
|
||||
<path fill="rgb(114,159,207)" stroke="none" d="M 4509,13017 L 1731,13017 1731,11860 7287,11860 7287,13017 4509,13017 Z"/>
|
||||
<path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 4509,13017 L 1731,13017 1731,11860 7287,11860 7287,13017 4509,13017 Z"/>
|
||||
<text class="SVGTextShape"><tspan class="TextParagraph" font-family="Droid Serif, serif" font-size="493px" font-weight="400"><tspan class="TextPosition" x="2716" y="12610"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">Preprocess AST</tspan></tspan></tspan></text>
|
||||
<text class="SVGTextShape"><tspan class="TextParagraph" font-family="Droid Serif, serif" font-size="494px" font-weight="400"><tspan class="TextPosition" x="2697" y="12610"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">Preprocess AST</tspan></tspan></tspan></text>
|
||||
</g>
|
||||
</g>
|
||||
<g class="com.sun.star.drawing.ConnectorShape">
|
||||
@@ -260,7 +258,7 @@
|
||||
<rect class="BoundingBox" stroke="none" fill="none" x="5269" y="16315" width="3758" height="1212"/>
|
||||
<path fill="rgb(114,159,207)" stroke="none" d="M 7148,17499 L 5296,17499 5296,16342 8999,16342 8999,17499 7148,17499 Z"/>
|
||||
<path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 7148,17499 L 5296,17499 5296,16342 8999,16342 8999,17499 7148,17499 Z"/>
|
||||
<text class="SVGTextShape"><tspan class="TextParagraph" font-family="Droid Serif, serif" font-size="493px" font-weight="400"><tspan class="TextPosition" x="6083" y="17092"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">Optimize</tspan></tspan></tspan></text>
|
||||
<text class="SVGTextShape"><tspan class="TextParagraph" font-family="Droid Serif, serif" font-size="494px" font-weight="400"><tspan class="TextPosition" x="6072" y="17092"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">Optimize</tspan></tspan></tspan></text>
|
||||
</g>
|
||||
</g>
|
||||
<g class="com.sun.star.drawing.CustomShape">
|
||||
@@ -268,7 +266,7 @@
|
||||
<rect class="BoundingBox" stroke="none" fill="none" x="1704" y="18315" width="5611" height="1213"/>
|
||||
<path fill="rgb(114,159,207)" stroke="none" d="M 4509,19500 L 1731,19500 1731,18342 7287,18342 7287,19500 4509,19500 Z"/>
|
||||
<path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 4509,19500 L 1731,19500 1731,18342 7287,18342 7287,19500 4509,19500 Z"/>
|
||||
<text class="SVGTextShape"><tspan class="TextParagraph" font-family="Droid Serif, serif" font-size="493px" font-weight="400"><tspan class="TextPosition" x="2625" y="19092"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">Postprocess AST</tspan></tspan></tspan></text>
|
||||
<text class="SVGTextShape"><tspan class="TextParagraph" font-family="Droid Serif, serif" font-size="494px" font-weight="400"><tspan class="TextPosition" x="2604" y="19092"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">Postprocess AST</tspan></tspan></tspan></text>
|
||||
</g>
|
||||
</g>
|
||||
<g class="com.sun.star.drawing.ConnectorShape">
|
||||
@@ -290,7 +288,7 @@
|
||||
<rect class="BoundingBox" stroke="none" fill="none" x="1704" y="7665" width="5611" height="1213"/>
|
||||
<path fill="rgb(142,134,174)" stroke="none" d="M 4509,8850 L 1731,8850 1731,7692 7287,7692 7287,8850 4509,8850 Z"/>
|
||||
<path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 4509,8850 L 1731,8850 1731,7692 7287,7692 7287,8850 4509,8850 Z"/>
|
||||
<text class="SVGTextShape"><tspan class="TextParagraph" font-family="Droid Serif, serif" font-size="493px" font-weight="400"><tspan class="TextPosition" x="3866" y="8442"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">Parse</tspan></tspan></tspan></text>
|
||||
<text class="SVGTextShape"><tspan class="TextParagraph" font-family="Droid Serif, serif" font-size="494px" font-weight="400"><tspan class="TextPosition" x="3857" y="8442"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">Parse</tspan></tspan></tspan></text>
|
||||
</g>
|
||||
</g>
|
||||
<g class="com.sun.star.drawing.ConnectorShape">
|
||||
@@ -311,7 +309,7 @@
|
||||
<rect class="BoundingBox" stroke="none" fill="none" x="13167" y="3306" width="5611" height="1213"/>
|
||||
<path fill="rgb(129,172,166)" stroke="none" d="M 15972,4491 L 13194,4491 13194,3333 18750,3333 18750,4491 15972,4491 Z"/>
|
||||
<path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 15972,4491 L 13194,4491 13194,3333 18750,3333 18750,4491 15972,4491 Z"/>
|
||||
<text class="SVGTextShape"><tspan class="TextParagraph" font-family="Droid Serif, serif" font-size="493px" font-weight="400"><tspan class="TextPosition" x="14190" y="3797"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">Select CodeGen</tspan></tspan><tspan class="TextPosition" x="14869" y="4369"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">for target</tspan></tspan></tspan></text>
|
||||
<text class="SVGTextShape"><tspan class="TextParagraph" font-family="Droid Serif, serif" font-size="494px" font-weight="400"><tspan class="TextPosition" x="14169" y="3793"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">Select CodeGen</tspan></tspan><tspan class="TextPosition" x="14861" y="4373"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">for target</tspan></tspan></tspan></text>
|
||||
</g>
|
||||
</g>
|
||||
<g class="com.sun.star.drawing.ConnectorShape">
|
||||
@@ -333,7 +331,7 @@
|
||||
<rect class="BoundingBox" stroke="none" fill="none" x="14723" y="5621" width="4305" height="1907"/>
|
||||
<path fill="rgb(129,172,166)" stroke="none" d="M 16875,7500 L 14750,7500 14750,5648 19000,5648 19000,7500 16875,7500 Z"/>
|
||||
<path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 16875,7500 L 14750,7500 14750,5648 19000,5648 19000,7500 16875,7500 Z"/>
|
||||
<text class="SVGTextShape"><tspan class="TextParagraph" font-family="Droid Serif, serif" font-size="493px" font-weight="400"><tspan class="TextPosition" x="16236" y="6173"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">Make </tspan></tspan><tspan class="TextPosition" x="15343" y="6745"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">Intermediate </tspan></tspan><tspan class="TextPosition" x="16422" y="7317"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">AST</tspan></tspan></tspan></text>
|
||||
<text class="SVGTextShape"><tspan class="TextParagraph" font-family="Droid Serif, serif" font-size="494px" font-weight="400"><tspan class="TextPosition" x="16230" y="6165"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">Make </tspan></tspan><tspan class="TextPosition" x="15330" y="6745"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">Intermediate </tspan></tspan><tspan class="TextPosition" x="16420" y="7325"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">AST</tspan></tspan></tspan></text>
|
||||
</g>
|
||||
</g>
|
||||
<g class="com.sun.star.drawing.ConnectorShape">
|
||||
@@ -348,7 +346,7 @@
|
||||
<rect class="BoundingBox" stroke="none" fill="none" x="14723" y="8472" width="4305" height="1444"/>
|
||||
<path fill="rgb(129,172,166)" stroke="none" d="M 16875,9888 L 14750,9888 14750,8499 19000,8499 19000,9888 16875,9888 Z"/>
|
||||
<path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 16875,9888 L 14750,9888 14750,8499 19000,8499 19000,9888 16875,9888 Z"/>
|
||||
<text class="SVGTextShape"><tspan class="TextParagraph" font-family="Droid Serif, serif" font-size="493px" font-weight="400"><tspan class="TextPosition" x="15434" y="9365"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">Generate IR </tspan></tspan></tspan></text>
|
||||
<text class="SVGTextShape"><tspan class="TextParagraph" font-family="Droid Serif, serif" font-size="494px" font-weight="400"><tspan class="TextPosition" x="15419" y="9365"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">Generate IR </tspan></tspan></tspan></text>
|
||||
</g>
|
||||
</g>
|
||||
<g class="com.sun.star.drawing.ConnectorShape">
|
||||
@@ -363,7 +361,7 @@
|
||||
<rect class="BoundingBox" stroke="none" fill="none" x="14723" y="10972" width="4305" height="1444"/>
|
||||
<path fill="rgb(129,172,166)" stroke="none" d="M 16875,12388 L 14750,12388 14750,10999 19000,10999 19000,12388 16875,12388 Z"/>
|
||||
<path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 16875,12388 L 14750,12388 14750,10999 19000,10999 19000,12388 16875,12388 Z"/>
|
||||
<text class="SVGTextShape"><tspan class="TextParagraph" font-family="Droid Serif, serif" font-size="493px" font-weight="400"><tspan class="TextPosition" x="15495" y="11865"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">Optimize IR</tspan></tspan></tspan></text>
|
||||
<text class="SVGTextShape"><tspan class="TextParagraph" font-family="Droid Serif, serif" font-size="494px" font-weight="400"><tspan class="TextPosition" x="15482" y="11865"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">Optimize IR</tspan></tspan></tspan></text>
|
||||
</g>
|
||||
</g>
|
||||
<g class="com.sun.star.drawing.ConnectorShape">
|
||||
@@ -375,17 +373,17 @@
|
||||
</g>
|
||||
<g class="com.sun.star.drawing.CustomShape">
|
||||
<g id="id31">
|
||||
<rect class="BoundingBox" stroke="none" fill="none" x="13157" y="14223" width="3620" height="1305"/>
|
||||
<path fill="rgb(255,166,166)" stroke="none" d="M 14967,15500 L 13184,15500 13184,14250 16749,14250 16749,15500 14967,15500 Z"/>
|
||||
<path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 14967,15500 L 13184,15500 13184,14250 16749,14250 16749,15500 14967,15500 Z"/>
|
||||
<text class="SVGTextShape"><tspan class="TextParagraph" font-family="Droid Serif, serif" font-size="493px" font-weight="400"><tspan class="TextPosition" x="13904" y="14760"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">Generate </tspan></tspan><tspan class="TextPosition" x="13836" y="15332"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">6502 Asm</tspan></tspan></tspan></text>
|
||||
<rect class="BoundingBox" stroke="none" fill="none" x="13157" y="14223" width="3620" height="1457"/>
|
||||
<path fill="rgb(255,166,166)" stroke="none" d="M 14967,15652 L 13184,15652 13184,14250 16749,14250 16749,15652 14967,15652 Z"/>
|
||||
<path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 14967,15652 L 13184,15652 13184,14250 16749,14250 16749,15652 14967,15652 Z"/>
|
||||
<text class="SVGTextShape"><tspan class="TextParagraph" font-family="Droid Serif, serif" font-size="494px" font-weight="400"><tspan class="TextPosition" x="13891" y="14832"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">Generate </tspan></tspan><tspan class="TextPosition" x="13832" y="15412"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">6502 Asm</tspan></tspan></tspan></text>
|
||||
</g>
|
||||
</g>
|
||||
<g class="com.sun.star.drawing.ConnectorShape">
|
||||
<g id="id32">
|
||||
<rect class="BoundingBox" stroke="none" fill="none" x="12223" y="4464" width="3777" height="10552"/>
|
||||
<path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="miter" stroke-linecap="round" d="M 15972,4491 L 15972,5019 12250,5019 12250,14875 12925,14875"/>
|
||||
<path fill="rgb(52,101,164)" stroke="none" d="M 12906,14736 L 13185,14875 12906,15015 12906,14736 Z"/>
|
||||
<rect class="BoundingBox" stroke="none" fill="none" x="12223" y="4464" width="3777" height="10628"/>
|
||||
<path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="miter" stroke-linecap="round" d="M 15972,4491 L 15972,5019 12250,5019 12250,14951 12925,14951"/>
|
||||
<path fill="rgb(52,101,164)" stroke="none" d="M 12906,14812 L 13185,14951 12906,15091 12906,14812 Z"/>
|
||||
</g>
|
||||
</g>
|
||||
<g class="TextShape">
|
||||
@@ -399,13 +397,13 @@
|
||||
<rect class="BoundingBox" stroke="none" fill="none" x="13167" y="16269" width="3611" height="1509"/>
|
||||
<path fill="rgb(255,166,166)" stroke="none" d="M 14972,17750 L 13194,17750 13194,16296 16750,16296 16750,17750 14972,17750 Z"/>
|
||||
<path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 14972,17750 L 13194,17750 13194,16296 16750,16296 16750,17750 14972,17750 Z"/>
|
||||
<text class="SVGTextShape"><tspan class="TextParagraph" font-family="Droid Serif, serif" font-size="493px" font-weight="400"><tspan class="TextPosition" x="13857" y="16908"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">Assemble </tspan></tspan><tspan class="TextPosition" x="14083" y="17480"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">(64tass)</tspan></tspan></tspan></text>
|
||||
<text class="SVGTextShape"><tspan class="TextParagraph" font-family="Droid Serif, serif" font-size="494px" font-weight="400"><tspan class="TextPosition" x="13844" y="16904"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">Assemble </tspan></tspan><tspan class="TextPosition" x="14073" y="17484"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">(64tass)</tspan></tspan></tspan></text>
|
||||
</g>
|
||||
</g>
|
||||
<g class="com.sun.star.drawing.ConnectorShape">
|
||||
<g id="id35">
|
||||
<rect class="BoundingBox" stroke="none" fill="none" x="14832" y="15473" width="281" height="824"/>
|
||||
<path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="miter" stroke-linecap="round" d="M 14967,15500 L 14967,15898 14972,15898 14972,16036"/>
|
||||
<rect class="BoundingBox" stroke="none" fill="none" x="14832" y="15625" width="281" height="672"/>
|
||||
<path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="miter" stroke-linecap="round" d="M 14967,15652 L 14967,15974 14972,15974 14972,16036"/>
|
||||
<path fill="rgb(52,101,164)" stroke="none" d="M 15112,16017 L 14972,16296 14833,16017 15112,16017 Z"/>
|
||||
</g>
|
||||
</g>
|
||||
@@ -444,44 +442,52 @@
|
||||
<rect class="BoundingBox" stroke="none" fill="none" x="17473" y="14222" width="3305" height="1212"/>
|
||||
<path fill="rgb(255,166,166)" stroke="none" d="M 19125,15406 L 17500,15406 17500,14249 20750,14249 20750,15406 19125,15406 Z"/>
|
||||
<path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 19125,15406 L 17500,15406 17500,14249 20750,14249 20750,15406 19125,15406 Z"/>
|
||||
<text class="SVGTextShape"><tspan class="TextParagraph" font-family="Droid Serif, serif" font-size="493px" font-weight="400"><tspan class="TextPosition" x="18063" y="14713"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">Generate </tspan></tspan><tspan class="TextPosition" x="18122" y="15285"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">VM code</tspan></tspan></tspan></text>
|
||||
<text class="SVGTextShape"><tspan class="TextParagraph" font-family="Droid Serif, serif" font-size="494px" font-weight="400"><tspan class="TextPosition" x="18050" y="14709"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">Generate </tspan></tspan><tspan class="TextPosition" x="18109" y="15289"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">VM code</tspan></tspan></tspan></text>
|
||||
</g>
|
||||
</g>
|
||||
<g class="com.sun.star.drawing.CustomShape">
|
||||
<g class="com.sun.star.drawing.ConnectorShape">
|
||||
<g id="id41">
|
||||
<rect class="BoundingBox" stroke="none" fill="none" x="17595" y="16498" width="3157" height="1393"/>
|
||||
<path fill="rgb(255,245,206)" stroke="none" d="M 17596,16499 L 20750,16499 20750,17616 C 19501,17607 19543,17835 18416,17889 18013,17856 17866,17831 17596,17793 L 17596,16499 Z"/>
|
||||
<path fill="none" stroke="rgb(52,101,164)" d="M 17596,16499 L 20750,16499 20750,17616 C 19501,17607 19543,17835 18416,17889 18013,17856 17866,17831 17596,17793 L 17596,16499 Z"/>
|
||||
<text class="SVGTextShape"><tspan class="TextParagraph" font-family="Bitstream Vera Sans Mono, monospace" font-size="370px" font-weight="400"><tspan class="TextPosition" x="17852" y="17185"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">hello.p8virt</tspan></tspan></tspan></text>
|
||||
<rect class="BoundingBox" stroke="none" fill="none" x="18934" y="15625" width="281" height="778"/>
|
||||
<path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="miter" stroke-linecap="round" d="M 19119,15652 L 19119,16027 19074,16027 19074,16142"/>
|
||||
<path fill="rgb(52,101,164)" stroke="none" d="M 19214,16123 L 19074,16402 18935,16123 19214,16123 Z"/>
|
||||
</g>
|
||||
</g>
|
||||
<g class="com.sun.star.drawing.ConnectorShape">
|
||||
<g id="id42">
|
||||
<rect class="BoundingBox" stroke="none" fill="none" x="19034" y="15380" width="281" height="1121"/>
|
||||
<path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="miter" stroke-linecap="round" d="M 19125,15407 L 19125,15966 19174,15966 19174,16240"/>
|
||||
<path fill="rgb(52,101,164)" stroke="none" d="M 19314,16221 L 19174,16500 19035,16221 19314,16221 Z"/>
|
||||
</g>
|
||||
</g>
|
||||
<g class="com.sun.star.drawing.ConnectorShape">
|
||||
<g id="id43">
|
||||
<rect class="BoundingBox" stroke="none" fill="none" x="14827" y="12362" width="2076" height="1889"/>
|
||||
<path fill="none" stroke="rgb(183,179,202)" stroke-width="53" stroke-linejoin="miter" stroke-linecap="round" d="M 16875,12389 L 16875,13319 14967,13319 14967,13990"/>
|
||||
<path fill="rgb(183,179,202)" stroke="none" d="M 15107,13971 L 14967,14250 14828,13971 15107,13971 Z"/>
|
||||
</g>
|
||||
</g>
|
||||
<g class="com.sun.star.drawing.ConnectorShape">
|
||||
<g id="id44">
|
||||
<g id="id43">
|
||||
<rect class="BoundingBox" stroke="none" fill="none" x="16848" y="12362" width="2418" height="1889"/>
|
||||
<path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="miter" stroke-linecap="round" d="M 16875,12389 L 16875,13319 19125,13319 19125,13990"/>
|
||||
<path fill="rgb(52,101,164)" stroke="none" d="M 19265,13971 L 19125,14250 18986,13971 19265,13971 Z"/>
|
||||
</g>
|
||||
</g>
|
||||
<g class="TextShape">
|
||||
<g id="id45">
|
||||
<g id="id44">
|
||||
<rect class="BoundingBox" stroke="none" fill="none" x="13473" y="13084" width="2778" height="1167"/>
|
||||
<text class="SVGTextShape"><tspan class="TextParagraph" font-family="Liberation Serif, serif" font-size="388px" font-style="italic" font-weight="400"><tspan class="TextPosition" x="13723" y="13556"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">(future)</tspan></tspan></tspan></text>
|
||||
</g>
|
||||
</g>
|
||||
<g class="com.sun.star.drawing.CustomShape">
|
||||
<g id="id45">
|
||||
<rect class="BoundingBox" stroke="none" fill="none" x="17375" y="16375" width="3397" height="1806"/>
|
||||
<path fill="rgb(255,166,166)" stroke="none" d="M 17938,18153 L 17939,18153 C 17845,18153 17752,18113 17670,18036 17589,17959 17521,17848 17474,17715 17427,17582 17402,17431 17402,17278 L 17402,17278 C 17402,17124 17427,16973 17474,16840 17521,16707 17589,16596 17670,16519 17752,16442 17845,16402 17939,16402 L 20207,16402 20207,16402 C 20301,16402 20394,16442 20476,16519 20557,16596 20625,16707 20672,16840 20719,16973 20744,17124 20744,17278 L 20744,17278 20744,17278 C 20744,17431 20719,17582 20672,17715 20625,17848 20557,17959 20476,18036 20394,18113 20301,18153 20207,18153 L 17938,18153 Z"/>
|
||||
<path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 17938,18153 L 17939,18153 C 17845,18153 17752,18113 17670,18036 17589,17959 17521,17848 17474,17715 17427,17582 17402,17431 17402,17278 L 17402,17278 C 17402,17124 17427,16973 17474,16840 17521,16707 17589,16596 17670,16519 17752,16442 17845,16402 17939,16402 L 20207,16402 20207,16402 C 20301,16402 20394,16442 20476,16519 20557,16596 20625,16707 20672,16840 20719,16973 20744,17124 20744,17278 L 20744,17278 20744,17278 C 20744,17431 20719,17582 20672,17715 20625,17848 20557,17959 20476,18036 20394,18113 20301,18153 20207,18153 L 17938,18153 Z"/>
|
||||
<text class="SVGTextShape"><tspan class="TextParagraph" font-family="Droid Serif, serif" font-size="423px" font-weight="400"><tspan class="TextPosition" x="18402" y="16927"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">Run in </tspan></tspan><tspan class="TextPosition" x="18391" y="17423"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">virtual </tspan></tspan><tspan class="TextPosition" x="18199" y="17919"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">machine</tspan></tspan></tspan></text>
|
||||
</g>
|
||||
</g>
|
||||
<g class="com.sun.star.drawing.CustomShape">
|
||||
<g id="id46">
|
||||
<rect class="BoundingBox" stroke="none" fill="none" x="17467" y="14223" width="3305" height="1457"/>
|
||||
<path fill="rgb(255,166,166)" stroke="none" d="M 19119,15652 L 17494,15652 17494,14250 20744,14250 20744,15652 19119,15652 Z"/>
|
||||
<path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 19119,15652 L 17494,15652 17494,14250 20744,14250 20744,15652 19119,15652 Z"/>
|
||||
<text class="SVGTextShape"><tspan class="TextParagraph" font-family="Droid Serif, serif" font-size="494px" font-weight="400"><tspan class="TextPosition" x="18031" y="14832"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">Build VM </tspan></tspan><tspan class="TextPosition" x="18086" y="15412"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">program</tspan></tspan></tspan></text>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
|
||||
|
Before Width: | Height: | Size: 91 KiB After Width: | Height: | Size: 94 KiB |
@@ -139,7 +139,7 @@ Some notes and references into the compiler's source code modules:
|
||||
#. An *Intermediate Representation* has been defined that is generated from the intermediate AST. This IR
|
||||
is more or less a machine code language for a virtual machine - and indeed this is what the built-in
|
||||
prog8 VM will execute if you use the 'virtual' compilaton target and use ``-emu`` to launch the VM.
|
||||
(``intermediate`` and ``codeGenIntermediate`` modules, and ``codeGenVirtual`` and ``virtualmachine`` module for the VM related stuff)
|
||||
(``intermediate`` and ``codeGenIntermediate`` modules, and ``virtualmachine`` module for the VM related stuff)
|
||||
#. Currently the 6502 ASM code generator still works directly on the *Compiler AST*. A future version
|
||||
should replace this by working on the IR code, and should be much smaller and simpler.
|
||||
(``codeGenCpu6502`` module)
|
||||
|
||||
@@ -17,7 +17,6 @@ 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/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,18 +50,15 @@ 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:
|
||||
|
||||
- rethink the whole "isAugmentable" business. Because the way this is determined, should always also be exactly mirrorred in the AugmentableAssignmentAsmGen or you'll get a crash at code gen time.
|
||||
note: the new Ast doesn't need this any more so maybe we can get rid of it altogether in the old AST - but it's still used for something in the UnusedCodeRemover.
|
||||
- can we get rid of pieces of asmgen.AssignmentAsmGen by just reusing the AugmentableAssignment ? generated code should not suffer
|
||||
- rewrite expression tree evaluation such that it doesn't use an eval stack but flatten the tree into linear code that uses a fixed number of predetermined value 'variables'?
|
||||
"Three address code" was mentioned. https://en.wikipedia.org/wiki/Three-address_code
|
||||
these variables have to be unique for each subroutine because they could otherwise be interfered with from irq routines etc.
|
||||
- rewrite expression tree evaluation such that it doesn't use an eval stack but flatten the tree into linear code
|
||||
that, for instance, uses a fixed number of predetermined value 'variables'?
|
||||
The VM IL solves this already (by using unlimited registers) but that still lacks a translation to 6502.
|
||||
- this removes the need for the BinExprSplitter? (which is problematic and very limited now)
|
||||
and perhaps the assignment splitting in BeforeAsmAstChanger too
|
||||
|
||||
+2
-1
@@ -19,7 +19,8 @@ main {
|
||||
const uword SIZEPL = 8191
|
||||
uword @zp flags_ptr = memory("flags", SIZEPL, $100)
|
||||
|
||||
txt.print("calculating...\n")
|
||||
txt.print_ub(ITERS)
|
||||
txt.print(" iterations, calculating...\n")
|
||||
|
||||
repeat ITERS {
|
||||
sys.memset(flags_ptr, SIZEPL, 1)
|
||||
|
||||
+1
-111
@@ -1,115 +1,5 @@
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
}}
|
||||
%asmbinary "bsieve.prg", 10, 200
|
||||
}
|
||||
}
|
||||
|
||||
; 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,37 @@
|
||||
%import textio
|
||||
|
||||
; The "Byte Sieve" test. https://en.wikipedia.org/wiki/Byte_Sieve
|
||||
|
||||
main {
|
||||
sub start() {
|
||||
|
||||
uword count
|
||||
uword i
|
||||
uword prime
|
||||
uword k
|
||||
const uword SIZEPL = 8191
|
||||
uword @zp flags_ptr = memory("flags", SIZEPL, $100)
|
||||
|
||||
txt.print("calculating...\n")
|
||||
|
||||
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,21 @@
|
||||
package prog8.intermediate
|
||||
|
||||
// Calls to builtin operations that are too complex to be implemented as an IR instruction
|
||||
// these use the SYSCALL instruction instead.
|
||||
// Note that in the VM these are translated into whatever the corresponding Syscall number in the VM is.
|
||||
|
||||
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,18 +4,20 @@ 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
|
||||
|
||||
|
||||
class IRFileReader(outputDir: Path, programName: String) {
|
||||
private val infile = outputDir / ("${programName}.p8ir")
|
||||
class IRFileReader {
|
||||
|
||||
fun readFile(): IRProgram {
|
||||
println("Reading intermediate representation from $infile")
|
||||
infile.bufferedReader().use { reader ->
|
||||
val lines = reader.readText().lines()
|
||||
return parseProgram(lines.iterator())
|
||||
fun read(irSourceCode: CharSequence): IRProgram {
|
||||
return parseProgram(irSourceCode.lineSequence().iterator())
|
||||
}
|
||||
|
||||
fun read(irSourceFile: Path): IRProgram {
|
||||
println("Reading intermediate representation from $irSourceFile")
|
||||
irSourceFile.bufferedReader().use { reader ->
|
||||
return parseProgram(reader.lineSequence().iterator())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,7 +33,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 +56,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 +85,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 +100,8 @@ class IRFileReader(outputDir: Path, programName: String) {
|
||||
target,
|
||||
loadAddress,
|
||||
dontReinitGlobals = dontReinitGlobals,
|
||||
evalStackBaseAddress = evalStackBaseAddress
|
||||
evalStackBaseAddress = evalStackBaseAddress,
|
||||
outputDir = outputDir
|
||||
)
|
||||
}
|
||||
|
||||
@@ -130,7 +135,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 +183,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 +193,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 +214,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
|
||||
}
|
||||
@@ -243,8 +254,6 @@ class IRFileReader(outputDir: Path, programName: String) {
|
||||
private val asmsubPattern = Regex("<ASMSUB NAME=(.+) ADDRESS=(.+) CLOBBERS=(.*) RETURNS=(.*) POS=(.+)>")
|
||||
private val subPattern = Regex("<SUB NAME=(.+) RETURNTYPE=(.+) POS=(.+)>")
|
||||
private val posPattern = Regex("\\[(.+): line (.+) col (.+)-(.+)\\]")
|
||||
private val instructionPattern = Regex("""([a-z]+)(\.b|\.w|\.f)?(.*)""", RegexOption.IGNORE_CASE)
|
||||
private val labelPattern = Regex("""_([a-zA-Z\d\._]+):""")
|
||||
|
||||
private fun parseBlock(startline: String, lines: Iterator<String>, variables: List<StStaticVariable>): IRBlock {
|
||||
var line = startline
|
||||
@@ -355,18 +364,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))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -385,201 +395,21 @@ class IRFileReader(outputDir: Path, programName: String) {
|
||||
if (line.isBlank() || line.startsWith(';'))
|
||||
continue
|
||||
if(line=="<BYTES>") {
|
||||
val bytes = mutableListOf<Byte>()
|
||||
val bytes = mutableListOf<UByte>()
|
||||
line = lines.next()
|
||||
while(line!="</BYTES>") {
|
||||
line.trimEnd().windowed(size=2, step=2) {
|
||||
bytes.add(it.toString().toByte(16))
|
||||
bytes.add(it.toString().toUByte(16))
|
||||
}
|
||||
line = lines.next()
|
||||
}
|
||||
chunk += IRCodeInlineBinary(bytes.toByteArray())
|
||||
chunk += IRCodeInlineBinary(bytes)
|
||||
} else {
|
||||
chunk += parseCodeLine(line)
|
||||
chunk += parseIRCodeLine(line, 0, mutableMapOf())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun parseCodeLine(line: String): IRCodeLine {
|
||||
val match = instructionPattern.matchEntire(line.trim())
|
||||
if(match==null) {
|
||||
// it's a label.
|
||||
val labelmatch = labelPattern.matchEntire(line.trim()) ?: throw IRParseException("invalid label")
|
||||
return IRCodeLabel(labelmatch.groupValues[1])
|
||||
}
|
||||
|
||||
// it's an instruction.
|
||||
val (_, instr, typestr, rest) = match.groupValues
|
||||
val opcode = try {
|
||||
Opcode.valueOf(instr.uppercase())
|
||||
} catch (ax: IllegalArgumentException) {
|
||||
throw IRParseException("invalid vmasm instruction: $instr")
|
||||
}
|
||||
var type: VmDataType? = convertVmType(typestr)
|
||||
val formats = instructionFormats.getValue(opcode)
|
||||
val format: InstructionFormat
|
||||
if(type !in formats) {
|
||||
type = VmDataType.BYTE
|
||||
format = if(type !in formats)
|
||||
formats.getValue(null)
|
||||
else
|
||||
formats.getValue(type)
|
||||
} else {
|
||||
format = formats.getValue(type)
|
||||
}
|
||||
// parse the operands
|
||||
val operands = rest.lowercase().split(",").toMutableList()
|
||||
var reg1: Int? = null
|
||||
var reg2: Int? = null
|
||||
var reg3: Int? = null
|
||||
var fpReg1: Int? = null
|
||||
var fpReg2: Int? = null
|
||||
var fpReg3: Int? = null
|
||||
var value: Float? = null
|
||||
var operand: String?
|
||||
var labelSymbol: String? = null
|
||||
if(operands.isNotEmpty() && operands[0].isNotEmpty()) {
|
||||
operand = operands.removeFirst().trim()
|
||||
if(operand[0]=='r')
|
||||
reg1 = operand.substring(1).toInt()
|
||||
else if(operand[0]=='f' && operand[1]=='r')
|
||||
fpReg1 = operand.substring(2).toInt()
|
||||
else {
|
||||
if(operand.startsWith('_')) {
|
||||
// it's a label
|
||||
labelSymbol = rest.split(",")[0].trim().substring(1) // keep the original case
|
||||
value = null
|
||||
} else {
|
||||
value = parseValue(operand)
|
||||
}
|
||||
operands.clear()
|
||||
}
|
||||
if(operands.isNotEmpty()) {
|
||||
operand = operands.removeFirst().trim()
|
||||
if(operand[0]=='r')
|
||||
reg2 = operand.substring(1).toInt()
|
||||
else if(operand[0]=='f' && operand[1]=='r')
|
||||
fpReg2 = operand.substring(2).toInt()
|
||||
else {
|
||||
if(operand.startsWith('_')) {
|
||||
// it's a label
|
||||
labelSymbol = rest.split(",")[1].trim().substring(1) // keep the original case
|
||||
value = null
|
||||
} else {
|
||||
value = parseValue(operand)
|
||||
}
|
||||
operands.clear()
|
||||
}
|
||||
if(operands.isNotEmpty()) {
|
||||
operand = operands.removeFirst().trim()
|
||||
if(operand[0]=='r')
|
||||
reg3 = operand.substring(1).toInt()
|
||||
else if(operand[0]=='f' && operand[1]=='r')
|
||||
fpReg3 = operand.substring(2).toInt()
|
||||
else {
|
||||
if(operand.startsWith('_')) {
|
||||
// it's a label
|
||||
labelSymbol = rest.split(",")[2].trim().substring(1) // keep the original case
|
||||
value = null
|
||||
} else {
|
||||
value = parseValue(operand)
|
||||
}
|
||||
operands.clear()
|
||||
}
|
||||
if(operands.isNotEmpty()) {
|
||||
operand = operands.removeFirst().trim()
|
||||
if(operand.startsWith('_')) {
|
||||
// it's a label
|
||||
labelSymbol = rest.split(",")[3].trim().substring(1) // keep the original case
|
||||
value = null
|
||||
} else {
|
||||
value = parseValue(operand)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// shift the operands back into place
|
||||
while(reg1==null && reg2!=null) {
|
||||
reg1 = reg2
|
||||
reg2 = reg3
|
||||
reg3 = null
|
||||
}
|
||||
while(fpReg1==null && fpReg2!=null) {
|
||||
fpReg1 = fpReg2
|
||||
fpReg2 = fpReg3
|
||||
fpReg3 = null
|
||||
}
|
||||
if(reg3!=null)
|
||||
throw IRParseException("too many reg arguments $line")
|
||||
if(fpReg3!=null)
|
||||
throw IRParseException("too many fpreg arguments $line")
|
||||
|
||||
if(type!=null && type !in formats)
|
||||
throw IRParseException("invalid type code for $line")
|
||||
if(format.reg1 && reg1==null)
|
||||
throw IRParseException("needs reg1 for $line")
|
||||
if(format.reg2 && reg2==null)
|
||||
throw IRParseException("needs reg2 for $line")
|
||||
if(format.value && value==null && labelSymbol==null)
|
||||
throw IRParseException("needs value or label for $line")
|
||||
if(!format.reg1 && reg1!=null)
|
||||
throw IRParseException("invalid reg1 for $line")
|
||||
if(!format.reg2 && reg2!=null)
|
||||
throw IRParseException("invalid reg2 for $line")
|
||||
if(value!=null && opcode !in OpcodesWithAddress) {
|
||||
when (type) {
|
||||
VmDataType.BYTE -> {
|
||||
if (value < -128 || value > 255)
|
||||
throw IRParseException("value out of range for byte: $value")
|
||||
}
|
||||
VmDataType.WORD -> {
|
||||
if (value < -32768 || value > 65535)
|
||||
throw IRParseException("value out of range for word: $value")
|
||||
}
|
||||
VmDataType.FLOAT -> {}
|
||||
null -> {}
|
||||
}
|
||||
}
|
||||
var floatValue: Float? = null
|
||||
var intValue: Int? = null
|
||||
|
||||
if(format.value && value!=null)
|
||||
intValue = value.toInt()
|
||||
if(format.fpValue && value!=null)
|
||||
floatValue = value
|
||||
|
||||
return IRCodeInstruction(opcode, type, reg1, reg2, fpReg1, fpReg2, intValue, floatValue, labelSymbol)
|
||||
}
|
||||
|
||||
private fun convertVmType(typestr: String): VmDataType? {
|
||||
return when(typestr.lowercase()) {
|
||||
"" -> null
|
||||
".b" -> VmDataType.BYTE
|
||||
".w" -> VmDataType.WORD
|
||||
".f" -> VmDataType.FLOAT
|
||||
else -> throw IRParseException("invalid type $typestr")
|
||||
}
|
||||
}
|
||||
|
||||
private fun parseValue(value: String): Float {
|
||||
return if(value.startsWith("-"))
|
||||
-parseValue(value.substring(1))
|
||||
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('_'))
|
||||
throw IRParseException("attempt to parse a label as numeric value")
|
||||
else if(value.startsWith('&'))
|
||||
throw IRParseException("address-of should be done with normal LOAD <symbol>")
|
||||
else
|
||||
return value.toFloat()
|
||||
}
|
||||
|
||||
private fun parseRegisterOrStatusflag(regs: String): RegisterOrStatusflag {
|
||||
var reg: RegisterOrPair? = null
|
||||
var sf: Statusflag? = null
|
||||
|
||||
@@ -2,15 +2,16 @@ package prog8.intermediate
|
||||
|
||||
import prog8.code.core.*
|
||||
import java.io.BufferedWriter
|
||||
import java.nio.file.Path
|
||||
import kotlin.io.path.bufferedWriter
|
||||
import kotlin.io.path.div
|
||||
|
||||
|
||||
class IRFileWriter(private val irProgram: IRProgram) {
|
||||
private val outfile = irProgram.options.outputDir / ("${irProgram.name}.p8ir")
|
||||
class IRFileWriter(private val irProgram: IRProgram, outfileOverride: Path?) {
|
||||
private val outfile = outfileOverride ?: (irProgram.options.outputDir / ("${irProgram.name}.p8ir"))
|
||||
private val out = outfile.bufferedWriter()
|
||||
|
||||
fun writeFile() {
|
||||
fun write(): Path {
|
||||
println("Writing intermediate representation to $outfile")
|
||||
out.write("<PROGRAM NAME=${irProgram.name}>\n")
|
||||
writeOptions()
|
||||
@@ -27,6 +28,7 @@ class IRFileWriter(private val irProgram: IRProgram) {
|
||||
writeBlocks()
|
||||
out.write("</PROGRAM>\n")
|
||||
out.close()
|
||||
return outfile
|
||||
}
|
||||
|
||||
private fun writeBlocks() {
|
||||
@@ -38,7 +40,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) {
|
||||
@@ -68,7 +70,7 @@ class IRFileWriter(private val irProgram: IRProgram) {
|
||||
}
|
||||
out.write("</PARAMS>\n")
|
||||
out.write("<INLINEASM POS=${it.position}>\n")
|
||||
out.write(it.assembly.trimStart('\n').trimEnd(' ', '\n'))
|
||||
out.write(it.assembly.trimStart('\r', '\n').trimEnd(' ', '\r', '\n'))
|
||||
out.write("\n</INLINEASM>\n</ASMSUB>\n")
|
||||
}
|
||||
out.write("</BLOCK>\n")
|
||||
@@ -77,7 +79,7 @@ class IRFileWriter(private val irProgram: IRProgram) {
|
||||
|
||||
private fun writeInlineAsm(chunk: IRInlineAsmChunk) {
|
||||
out.write("<INLINEASM POS=${chunk.position}>\n")
|
||||
out.write(chunk.assembly.trimStart('\n').trimEnd(' ', '\n'))
|
||||
out.write(chunk.assembly.trimStart('\r', '\n').trimEnd(' ', '\r', '\n'))
|
||||
out.write("\n</INLINEASM>\n")
|
||||
}
|
||||
|
||||
@@ -93,13 +95,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 +120,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,28 +133,26 @@ 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")
|
||||
}
|
||||
|
||||
private fun BufferedWriter.writeLine(line: IRCodeLine) {
|
||||
when(line) {
|
||||
is IRCodeComment -> write("; ${line.comment}\n")
|
||||
is IRCodeInstruction -> {
|
||||
write(line.ins.toString() + "\n")
|
||||
}
|
||||
is IRInstruction -> write(line.toString() + "\n")
|
||||
is IRCodeLabel -> write("_${line.name}:\n")
|
||||
is IRCodeInlineBinary -> {
|
||||
write("<BYTES>\n")
|
||||
|
||||
+79
-52
@@ -14,9 +14,10 @@ Value stack, max 128 entries of 1 byte each.
|
||||
Status flags: Carry, Zero, Negative. NOTE: status flags are only affected by the CMP instruction or explicit CLC/SEC!!!
|
||||
logical or arithmetic operations DO NOT AFFECT THE STATUS FLAGS UNLESS EXPLICITLY NOTED!
|
||||
|
||||
Most instructions have an associated data type 'b','w','f'. (omitting it means 'b'/byte).
|
||||
Instruction set is mostly a load/store architecture, there are few instructions operating on memory directly.
|
||||
Most instructions have an associated data type 'b','w','f'. (omitting it defaults to 'b' - byte).
|
||||
Currently NO support for 24 or 32 bits integers.
|
||||
Floating point operations are just 'f' typed regular instructions, and additionally there are a few fp conversion instructions
|
||||
Floating point operations are just 'f' typed regular instructions, however there are a few unique fp conversion instructions.
|
||||
|
||||
|
||||
LOAD/STORE
|
||||
@@ -60,24 +61,24 @@ BRANCHING
|
||||
---------
|
||||
All have type b or w except the branches that only check status bits.
|
||||
|
||||
bstcc location - branch to location if Status bit Carry is Clear
|
||||
bstcs location - branch to location if Status bit Carry is Set
|
||||
bsteq location - branch to location if Status bit Zero is set
|
||||
bstne location - branch to location if Status bit Zero is not set
|
||||
bstneg location - branch to location if Status bit Negative is not set
|
||||
bstpos location - branch to location if Status bit Negative is not set
|
||||
bz reg1, location - branch to location if reg1 is zero
|
||||
bnz reg1, location - branch to location if reg1 is not zero
|
||||
beq reg1, reg2, location - jump to location in program given by location, if reg1 == reg2
|
||||
bne reg1, reg2, location - jump to location in program given by location, if reg1 != reg2
|
||||
blt reg1, reg2, location - jump to location in program given by location, if reg1 < reg2 (unsigned)
|
||||
blts reg1, reg2, location - jump to location in program given by location, if reg1 < reg2 (signed)
|
||||
ble reg1, reg2, location - jump to location in program given by location, if reg1 <= reg2 (unsigned)
|
||||
bles reg1, reg2, location - jump to location in program given by location, if reg1 <= reg2 (signed)
|
||||
bgt reg1, reg2, location - jump to location in program given by location, if reg1 > reg2 (unsigned)
|
||||
bgts reg1, reg2, location - jump to location in program given by location, if reg1 > reg2 (signed)
|
||||
bge reg1, reg2, location - jump to location in program given by location, if reg1 >= reg2 (unsigned)
|
||||
bges reg1, reg2, location - jump to location in program given by location, if reg1 >= reg2 (signed)
|
||||
bstcc address - branch to location if Status bit Carry is Clear
|
||||
bstcs address - branch to location if Status bit Carry is Set
|
||||
bsteq address - branch to location if Status bit Zero is set
|
||||
bstne address - branch to location if Status bit Zero is not set
|
||||
bstneg address - branch to location if Status bit Negative is not set
|
||||
bstpos address - branch to location if Status bit Negative is not set
|
||||
bz reg1, address - branch to location if reg1 is zero
|
||||
bnz reg1, address - branch to location if reg1 is not zero
|
||||
beq reg1, reg2, address - jump to location in program given by location, if reg1 == reg2
|
||||
bne reg1, reg2, address - jump to location in program given by location, if reg1 != reg2
|
||||
blt reg1, reg2, address - jump to location in program given by location, if reg1 < reg2 (unsigned)
|
||||
blts reg1, reg2, address - jump to location in program given by location, if reg1 < reg2 (signed)
|
||||
ble reg1, reg2, address - jump to location in program given by location, if reg1 <= reg2 (unsigned)
|
||||
bles reg1, reg2, address - jump to location in program given by location, if reg1 <= reg2 (signed)
|
||||
bgt reg1, reg2, address - jump to location in program given by location, if reg1 > reg2 (unsigned)
|
||||
bgts reg1, reg2, address - jump to location in program given by location, if reg1 > reg2 (signed)
|
||||
bge reg1, reg2, address - jump to location in program given by location, if reg1 >= reg2 (unsigned)
|
||||
bges reg1, reg2, address - jump to location in program given by location, if reg1 >= reg2 (signed)
|
||||
seq reg1, reg2 - set reg=1 if reg1 == reg2, otherwise set reg1=0
|
||||
sne reg1, reg2 - set reg=1 if reg1 != reg2, otherwise set reg1=0
|
||||
slt reg1, reg2 - set reg=1 if reg1 < reg2 (unsigned), otherwise set reg1=0
|
||||
@@ -380,7 +381,25 @@ val OpcodesWithAddress = setOf(
|
||||
Opcode.ROLM,
|
||||
Opcode.RORM,
|
||||
Opcode.ROXLM,
|
||||
Opcode.ROXRM
|
||||
Opcode.ROXRM,
|
||||
Opcode.BSTCC,
|
||||
Opcode.BSTCS,
|
||||
Opcode.BSTEQ,
|
||||
Opcode.BSTNE,
|
||||
Opcode.BSTNEG,
|
||||
Opcode.BSTPOS,
|
||||
Opcode.BZ,
|
||||
Opcode.BNZ,
|
||||
Opcode.BEQ,
|
||||
Opcode.BNE,
|
||||
Opcode.BLT,
|
||||
Opcode.BLTS,
|
||||
Opcode.BLE,
|
||||
Opcode.BLES,
|
||||
Opcode.BGT,
|
||||
Opcode.BGTS,
|
||||
Opcode.BGE,
|
||||
Opcode.BGES
|
||||
)
|
||||
|
||||
val OpcodesForCpuRegisters = setOf(
|
||||
@@ -397,7 +416,7 @@ enum class VmDataType {
|
||||
// TODO add INT (32-bit)? INT24 (24-bit)?
|
||||
}
|
||||
|
||||
data class Instruction(
|
||||
data class IRInstruction(
|
||||
val opcode: Opcode,
|
||||
val type: VmDataType?=null,
|
||||
val reg1: Int?=null, // 0-$ffff
|
||||
@@ -406,43 +425,50 @@ data class Instruction(
|
||||
val fpReg2: Int?=null, // 0-$ffff
|
||||
val value: Int?=null, // 0-$ffff
|
||||
val fpValue: Float?=null,
|
||||
val labelSymbol: List<String>?=null, // symbolic label name as alternative to value (so only for Branch/jump/call Instructions!)
|
||||
val binaryData: ByteArray?=null
|
||||
) {
|
||||
val labelSymbol: String?=null, // symbolic label name as alternative to value (so only for Branch/jump/call Instructions!)
|
||||
val binaryData: Collection<UByte>?=null
|
||||
): IRCodeLine() {
|
||||
// reg1 and fpreg1 can be IN/OUT/INOUT (all others are readonly INPUT)
|
||||
// This knowledge is useful in IL assembly optimizers to see how registers are used.
|
||||
val reg1direction: OperandDirection
|
||||
val fpReg1direction: OperandDirection
|
||||
|
||||
init {
|
||||
if(opcode==Opcode.BINARYDATA && binaryData==null || binaryData!=null && opcode!=Opcode.BINARYDATA)
|
||||
throw IllegalArgumentException("binarydata inconsistency")
|
||||
require(labelSymbol?.first()!='_') {"label/symbol should not start with underscore $labelSymbol"}
|
||||
require(reg1==null || reg1 in 0..65536) {"reg1 out of bounds"}
|
||||
require(reg2==null || reg2 in 0..65536) {"reg2 out of bounds"}
|
||||
require(fpReg1==null || fpReg1 in 0..65536) {"fpReg1 out of bounds"}
|
||||
require(fpReg2==null || fpReg2 in 0..65536) {"fpReg2 out of bounds"}
|
||||
if(value!=null && opcode !in OpcodesWithAddress) {
|
||||
when (type) {
|
||||
VmDataType.BYTE -> require(value in -128..255) {"value out of range for byte: $value"}
|
||||
VmDataType.WORD -> require(value in -32768..65535) {"value out of range for word: $value"}
|
||||
VmDataType.FLOAT, null -> {}
|
||||
}
|
||||
}
|
||||
|
||||
require((opcode==Opcode.BINARYDATA && binaryData!=null) || (opcode!=Opcode.BINARYDATA && binaryData==null)) {
|
||||
"binarydata inconsistency"
|
||||
}
|
||||
|
||||
val formats = instructionFormats.getValue(opcode)
|
||||
if(type==null && !formats.containsKey(null))
|
||||
throw IllegalArgumentException("missing type")
|
||||
require (type != null || formats.containsKey(null)) { "missing type" }
|
||||
|
||||
val format = formats.getValue(type)
|
||||
if(format.reg1 && reg1==null || format.reg2 && reg2==null)
|
||||
throw IllegalArgumentException("missing a register (int)")
|
||||
|
||||
if(format.fpReg1 && fpReg1==null || format.fpReg2 && fpReg2==null)
|
||||
throw IllegalArgumentException("missing a register (float)")
|
||||
|
||||
if(!format.reg1 && reg1!=null || !format.reg2 && reg2!=null)
|
||||
throw IllegalArgumentException("too many registers (int)")
|
||||
|
||||
if(!format.fpReg1 && fpReg1!=null || !format.fpReg2 && fpReg2!=null)
|
||||
throw IllegalArgumentException("too many registers (float)")
|
||||
if(format.reg1) require(reg1!=null) { "missing reg1" }
|
||||
if(format.reg2) require(reg2!=null) { "missing reg2" }
|
||||
if(format.fpReg1) require(fpReg1!=null) { "missing fpReg1" }
|
||||
if(format.fpReg2) require(fpReg2!=null) { "missing fpReg2" }
|
||||
if(!format.reg1) require(reg1==null) { "invalid reg1" }
|
||||
if(!format.reg2) require(reg2==null) { "invalid reg2" }
|
||||
if(!format.fpReg1) require(fpReg1==null) { "invalid fpReg1" }
|
||||
if(!format.fpReg2) require(fpReg2==null) { "invalid fpReg2" }
|
||||
|
||||
if (type==VmDataType.FLOAT) {
|
||||
if(format.fpValue && (fpValue==null && labelSymbol==null))
|
||||
throw IllegalArgumentException("$opcode: missing a fp-value or labelsymbol")
|
||||
if(format.fpValue) require(fpValue!=null || labelSymbol!=null) {"missing a fp-value or labelsymbol"}
|
||||
} else {
|
||||
if(format.value && (value==null && labelSymbol==null))
|
||||
throw IllegalArgumentException("$opcode: missing a value or labelsymbol")
|
||||
if (fpReg1 != null || fpReg2 != null)
|
||||
throw IllegalArgumentException("$opcode: integer point instruction can't use floating point registers")
|
||||
if(format.value) require(value!=null || labelSymbol!=null) {"missing a value or labelsymbol"}
|
||||
require(fpReg1==null && fpReg2==null) {"integer point instruction can't use floating point registers"}
|
||||
}
|
||||
|
||||
reg1direction = format.reg1direction
|
||||
@@ -454,9 +480,10 @@ data class Instruction(
|
||||
Opcode.SEQ, Opcode.SNE, Opcode.SLT, Opcode.SLTS,
|
||||
Opcode.SGT, Opcode.SGTS, Opcode.SLE, Opcode.SLES,
|
||||
Opcode.SGE, Opcode.SGES)) {
|
||||
if((type==VmDataType.FLOAT && fpReg1==fpReg2) || reg1==reg2) {
|
||||
throw IllegalArgumentException("$opcode: reg1 and reg2 should be different")
|
||||
}
|
||||
if(type==VmDataType.FLOAT)
|
||||
require(fpReg1!=fpReg2) {"$opcode: fpReg1 and fpReg2 should be different"}
|
||||
else
|
||||
require(reg1!=reg2) {"$opcode: reg1 and reg2 should be different"}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -494,10 +521,10 @@ data class Instruction(
|
||||
result.add(",")
|
||||
}
|
||||
labelSymbol?.let {
|
||||
if(labelSymbol[0].startsWith('&'))
|
||||
result.add(it.joinToString(".")) // address-of something
|
||||
if(it.startsWith('&'))
|
||||
result.add(it) // address-of something
|
||||
else
|
||||
result.add("_" + it.joinToString("."))
|
||||
result.add("_$it")
|
||||
}
|
||||
if(result.last() == ",")
|
||||
result.removeLast()
|
||||
@@ -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) {
|
||||
|
||||
@@ -56,8 +55,7 @@ class IRProgram(val name: String,
|
||||
|
||||
fun addGlobalInits(chunk: IRCodeChunk) = globalInits.addAll(chunk.lines)
|
||||
fun addBlock(block: IRBlock) {
|
||||
if(blocks.any { it.name==block.name })
|
||||
throw IllegalArgumentException("duplicate block ${block.name} ${block.position}")
|
||||
require(blocks.all { it.name != block.name}) { "duplicate block ${block.name} ${block.position}" }
|
||||
blocks.add(block)
|
||||
}
|
||||
}
|
||||
@@ -86,17 +84,21 @@ 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 {
|
||||
if(!name.contains('.'))
|
||||
throw IllegalArgumentException("subroutine name is not scoped: $name")
|
||||
if(name.startsWith("main.main."))
|
||||
throw IllegalArgumentException("subroutine name invalid main prefix: $name")
|
||||
require('.' in name) {"subroutine name is not scoped: $name"}
|
||||
require(!name.startsWith("main.main.")) {"subroutine name invalid main prefix: $name"}
|
||||
|
||||
// params and return value should not be str
|
||||
require(parameters.all{ it.dt in NumericDatatypes }) {"non-numeric parameter"}
|
||||
require(returnType==null || returnType in NumericDatatypes) {"non-numeric returntype $returnType"}
|
||||
}
|
||||
|
||||
operator fun plusAssign(chunk: IRCodeChunkBase) { chunks+= chunk }
|
||||
@@ -109,66 +111,19 @@ 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")
|
||||
if(name.startsWith("main.main."))
|
||||
throw IllegalArgumentException("subroutine name invalid main prefix: $name")
|
||||
require('.' in name) { "subroutine name is not scoped: $name" }
|
||||
require(!name.startsWith("main.main.")) { "subroutine name invalid main prefix: $name" }
|
||||
}
|
||||
}
|
||||
|
||||
sealed class IRCodeLine
|
||||
|
||||
class IRCodeInstruction(
|
||||
opcode: Opcode,
|
||||
type: VmDataType?=null,
|
||||
reg1: Int?=null, // 0-$ffff
|
||||
reg2: Int?=null, // 0-$ffff
|
||||
fpReg1: Int?=null, // 0-$ffff
|
||||
fpReg2: Int?=null, // 0-$ffff
|
||||
value: Int?=null, // 0-$ffff
|
||||
fpValue: Float?=null,
|
||||
labelSymbol: String?=null // alternative to value
|
||||
): IRCodeLine() {
|
||||
val ins = Instruction(opcode, type, reg1, reg2, fpReg1, fpReg2, value, fpValue, if(labelSymbol==null) null else listOf(labelSymbol))
|
||||
|
||||
init {
|
||||
if(reg1!=null && (reg1<0 || reg1>65536))
|
||||
throw IllegalArgumentException("reg1 out of bounds")
|
||||
if(reg2!=null && (reg2<0 || reg2>65536))
|
||||
throw IllegalArgumentException("reg2 out of bounds")
|
||||
if(fpReg1!=null && (fpReg1<0 || fpReg1>65536))
|
||||
throw IllegalArgumentException("fpReg1 out of bounds")
|
||||
if(fpReg2!=null && (fpReg2<0 || fpReg2>65536))
|
||||
throw IllegalArgumentException("fpReg2 out of bounds")
|
||||
|
||||
if(value!=null && opcode !in OpcodesWithAddress) {
|
||||
when (type) {
|
||||
VmDataType.BYTE -> {
|
||||
if (value < -128 || value > 255)
|
||||
throw IllegalArgumentException("value out of range for byte: $value")
|
||||
}
|
||||
VmDataType.WORD -> {
|
||||
if (value < -32768 || value > 65535)
|
||||
throw IllegalArgumentException("value out of range for word: $value")
|
||||
}
|
||||
VmDataType.FLOAT, null -> {}
|
||||
}
|
||||
}
|
||||
|
||||
if(labelSymbol?.startsWith('_')==true) {
|
||||
throw IllegalArgumentException("label/symbol should not start with underscore $labelSymbol")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class IRCodeLabel(val name: String): IRCodeLine()
|
||||
|
||||
class IRCodeComment(val comment: String): IRCodeLine()
|
||||
|
||||
class IRCodeInlineBinary(val data: ByteArray): IRCodeLine()
|
||||
class IRCodeInlineBinary(val data: Collection<UByte>): IRCodeLine()
|
||||
|
||||
abstract class IRCodeChunkBase(val position: Position) {
|
||||
val lines = mutableListOf<IRCodeLine>()
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
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 varToadd = if('.' in variable.name)
|
||||
variable
|
||||
else
|
||||
StMemorySlab("prog8_slabs.${variable.name}", variable.size, variable.align, variable.position)
|
||||
table[varToadd.name] = 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"
|
||||
@@ -53,3 +53,195 @@ public fun getTypeString(variable : StStaticVariable): String {
|
||||
else -> throw InternalCompilerException("weird dt")
|
||||
}
|
||||
}
|
||||
|
||||
fun convertIRType(typestr: String): VmDataType? {
|
||||
return when(typestr.lowercase()) {
|
||||
"" -> null
|
||||
".b" -> VmDataType.BYTE
|
||||
".w" -> VmDataType.WORD
|
||||
".f" -> VmDataType.FLOAT
|
||||
else -> throw IRParseException("invalid type $typestr")
|
||||
}
|
||||
}
|
||||
|
||||
fun parseIRValue(value: String): Float {
|
||||
return if(value.startsWith("-"))
|
||||
-parseIRValue(value.substring(1))
|
||||
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('_'))
|
||||
throw IRParseException("attempt to parse a label as numeric value")
|
||||
else if(value.startsWith('&'))
|
||||
throw IRParseException("address-of should be done with normal LOAD <symbol>")
|
||||
else
|
||||
return value.toFloat()
|
||||
}
|
||||
|
||||
|
||||
private val instructionPattern = Regex("""([a-z]+)(\.b|\.w|\.f)?(.*)""", RegexOption.IGNORE_CASE)
|
||||
private val labelPattern = Regex("""_([a-zA-Z\d\._]+):""")
|
||||
|
||||
fun parseIRCodeLine(line: String, pc: Int, placeholders: MutableMap<Int, String>): IRCodeLine {
|
||||
// Note: this function is used from multiple places:
|
||||
// the IR File Reader but also the VirtualMachine itself to make sense of any inline vmasm blocks.
|
||||
val labelmatch = labelPattern.matchEntire(line.trim())
|
||||
if(labelmatch!=null)
|
||||
return IRCodeLabel(labelmatch.groupValues[1])
|
||||
|
||||
val match = instructionPattern.matchEntire(line)
|
||||
?: throw IRParseException("invalid IR instruction: $line")
|
||||
val (instr, typestr, rest) = match.destructured
|
||||
val opcode = try {
|
||||
Opcode.valueOf(instr.uppercase())
|
||||
} catch (ax: IllegalArgumentException) {
|
||||
throw IRParseException("invalid vmasm instruction: $instr")
|
||||
}
|
||||
var type: VmDataType? = convertIRType(typestr)
|
||||
val formats = instructionFormats.getValue(opcode)
|
||||
val format: InstructionFormat
|
||||
if(type !in formats) {
|
||||
type = VmDataType.BYTE
|
||||
format = if(type !in formats)
|
||||
formats.getValue(null)
|
||||
else
|
||||
formats.getValue(type)
|
||||
} else {
|
||||
format = formats.getValue(type)
|
||||
}
|
||||
|
||||
// parse the operands
|
||||
val operands = rest.lowercase().split(",").toMutableList()
|
||||
var reg1: Int? = null
|
||||
var reg2: Int? = null
|
||||
var reg3: Int? = null
|
||||
var fpReg1: Int? = null
|
||||
var fpReg2: Int? = null
|
||||
var fpReg3: Int? = null
|
||||
var value: Float? = null
|
||||
var operand: String?
|
||||
var labelSymbol: String? = null
|
||||
|
||||
fun parseValueOrPlaceholder(operand: String, pc: Int, rest: String, restIndex: Int): Float? {
|
||||
return if(operand.startsWith('_')) {
|
||||
labelSymbol = rest.split(",")[restIndex].trim().drop(1)
|
||||
placeholders[pc] = labelSymbol!!
|
||||
null
|
||||
} else if(operand[0].isLetter()) {
|
||||
labelSymbol = rest.split(",")[restIndex].trim()
|
||||
placeholders[pc] = labelSymbol!!
|
||||
null
|
||||
} else {
|
||||
parseIRValue(operand)
|
||||
}
|
||||
}
|
||||
|
||||
if(operands.isNotEmpty() && operands[0].isNotEmpty()) {
|
||||
operand = operands.removeFirst().trim()
|
||||
if(operand[0]=='r')
|
||||
reg1 = operand.substring(1).toInt()
|
||||
else if(operand[0]=='f' && operand[1]=='r')
|
||||
fpReg1 = operand.substring(2).toInt()
|
||||
else {
|
||||
value = parseValueOrPlaceholder(operand, pc, rest, 0)
|
||||
operands.clear()
|
||||
}
|
||||
if(operands.isNotEmpty()) {
|
||||
operand = operands.removeFirst().trim()
|
||||
if(operand[0]=='r')
|
||||
reg2 = operand.substring(1).toInt()
|
||||
else if(operand[0]=='f' && operand[1]=='r')
|
||||
fpReg2 = operand.substring(2).toInt()
|
||||
else {
|
||||
value = parseValueOrPlaceholder(operand, pc, rest, 1)
|
||||
operands.clear()
|
||||
}
|
||||
if(operands.isNotEmpty()) {
|
||||
operand = operands.removeFirst().trim()
|
||||
if(operand[0]=='r')
|
||||
reg3 = operand.substring(1).toInt()
|
||||
else if(operand[0]=='f' && operand[1]=='r')
|
||||
fpReg3 = operand.substring(2).toInt()
|
||||
else {
|
||||
value = parseValueOrPlaceholder(operand, pc, rest, 2)
|
||||
operands.clear()
|
||||
}
|
||||
if(operands.isNotEmpty()) {
|
||||
TODO("placeholder symbol? $operands rest=$rest'")
|
||||
// operands.clear()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// shift the operands back into place
|
||||
while(reg1==null && reg2!=null) {
|
||||
reg1 = reg2
|
||||
reg2 = reg3
|
||||
reg3 = null
|
||||
}
|
||||
while(fpReg1==null && fpReg2!=null) {
|
||||
fpReg1 = fpReg2
|
||||
fpReg2 = fpReg3
|
||||
fpReg3 = null
|
||||
}
|
||||
if(reg3!=null)
|
||||
throw IRParseException("too many reg arguments $line")
|
||||
if(fpReg3!=null)
|
||||
throw IRParseException("too many fpreg arguments $line")
|
||||
|
||||
if(type!=null && type !in formats)
|
||||
throw IRParseException("invalid type code for $line")
|
||||
if(format.reg1 && reg1==null)
|
||||
throw IRParseException("needs reg1 for $line")
|
||||
if(format.reg2 && reg2==null)
|
||||
throw IRParseException("needs reg2 for $line")
|
||||
if(format.value && value==null && labelSymbol==null)
|
||||
throw IRParseException("needs value or symbol for $line")
|
||||
if(!format.reg1 && reg1!=null)
|
||||
throw IRParseException("invalid reg1 for $line")
|
||||
if(!format.reg2 && reg2!=null)
|
||||
throw IRParseException("invalid reg2 for $line")
|
||||
if(value!=null && opcode !in OpcodesWithAddress) {
|
||||
when (type) {
|
||||
VmDataType.BYTE -> {
|
||||
if (value < -128 || value > 255)
|
||||
throw IRParseException("value out of range for byte: $value")
|
||||
}
|
||||
VmDataType.WORD -> {
|
||||
if (value < -32768 || value > 65535)
|
||||
throw IRParseException("value out of range for word: $value")
|
||||
}
|
||||
VmDataType.FLOAT -> {}
|
||||
null -> {}
|
||||
}
|
||||
}
|
||||
var floatValue: Float? = null
|
||||
var intValue: Int? = null
|
||||
|
||||
if(format.value && value!=null)
|
||||
intValue = value.toInt()
|
||||
if(format.fpValue && value!=null)
|
||||
floatValue = value
|
||||
|
||||
if(opcode in OpcodesForCpuRegisters) {
|
||||
val regStr = rest.split(',').last().lowercase().trim()
|
||||
val reg = if(regStr.startsWith('_')) regStr.substring(1) else regStr
|
||||
if(reg !in setOf(
|
||||
"a", "x", "y",
|
||||
"ax", "ay", "xy",
|
||||
"r0", "r1", "r2", "r3",
|
||||
"r4", "r5", "r6", "r7",
|
||||
"r8", "r9", "r10","r11",
|
||||
"r12", "r13", "r14", "r15",
|
||||
"pc", "pz", "pv","pn"))
|
||||
throw IRParseException("invalid cpu reg: $reg")
|
||||
|
||||
return IRInstruction(opcode, type, reg1, labelSymbol = reg)
|
||||
}
|
||||
|
||||
return IRInstruction(opcode, type, reg1, reg2, fpReg1, fpReg2, intValue, floatValue, labelSymbol = labelSymbol)
|
||||
}
|
||||
@@ -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,10 +29,9 @@ class TestIRFileInOut: FunSpec({
|
||||
loadAddress = target.machine.PROGRAM_LOAD_ADDRESS,
|
||||
outputDir = tempdir
|
||||
)
|
||||
val program = IRProgram("unittest-irwriter", st, options, target)
|
||||
val writer = IRFileWriter(program)
|
||||
writer.writeFile()
|
||||
val generatedFile = tempdir.resolve("unittest-irwriter.p8ir")
|
||||
val program = IRProgram("unittest-irwriter", IRSymbolTable(null), options, target)
|
||||
val writer = IRFileWriter(program, null)
|
||||
val generatedFile = writer.write()
|
||||
val lines = generatedFile.readLines()
|
||||
lines.first() shouldBe "<PROGRAM NAME=unittest-irwriter>"
|
||||
lines.last() shouldBe "</PROGRAM>"
|
||||
@@ -100,14 +94,13 @@ 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)
|
||||
val program = reader.readFile()
|
||||
val program = IRFileReader().read(tempfile)
|
||||
tempfile.deleteExisting()
|
||||
program.name shouldBe "test-ir-reader"
|
||||
program.blocks.size shouldBe 2
|
||||
program.st.allVariables().count() shouldBe 1
|
||||
program.st.lookup("sys.wait.jiffies") shouldBe instanceOf<StStaticVariable>()
|
||||
}
|
||||
})
|
||||
@@ -1,15 +1,15 @@
|
||||
import io.kotest.assertions.throwables.shouldThrow
|
||||
import io.kotest.assertions.throwables.shouldThrowWithMessage
|
||||
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.*
|
||||
|
||||
|
||||
class TestInstructions: FunSpec({
|
||||
|
||||
test("simple") {
|
||||
val ins = Instruction(Opcode.NOP)
|
||||
val ins = IRInstruction(Opcode.NOP)
|
||||
ins.opcode shouldBe Opcode.NOP
|
||||
ins.type shouldBe null
|
||||
ins.reg1 shouldBe null
|
||||
@@ -22,33 +22,33 @@ class TestInstructions: FunSpec({
|
||||
}
|
||||
|
||||
test("with value") {
|
||||
val ins = Instruction(Opcode.BZ, VmDataType.BYTE, reg1=42, value = 9999)
|
||||
val ins = IRInstruction(Opcode.BZ, VmDataType.BYTE, reg1=42, value = 99)
|
||||
ins.opcode shouldBe Opcode.BZ
|
||||
ins.type shouldBe VmDataType.BYTE
|
||||
ins.reg1 shouldBe 42
|
||||
ins.reg2 shouldBe null
|
||||
ins.value shouldBe 9999
|
||||
ins.value shouldBe 99
|
||||
ins.labelSymbol shouldBe null
|
||||
ins.reg1direction shouldBe OperandDirection.INPUT
|
||||
ins.fpReg1direction shouldBe OperandDirection.INPUT
|
||||
ins.toString() shouldBe "bz.b r42,9999"
|
||||
ins.toString() shouldBe "bz.b r42,99"
|
||||
}
|
||||
|
||||
test("with label") {
|
||||
val ins = Instruction(Opcode.BZ, VmDataType.WORD, reg1=11, labelSymbol = listOf("a","b","c"))
|
||||
val ins = IRInstruction(Opcode.BZ, VmDataType.WORD, reg1=11, labelSymbol = "a.b.c")
|
||||
ins.opcode shouldBe Opcode.BZ
|
||||
ins.type shouldBe VmDataType.WORD
|
||||
ins.reg1 shouldBe 11
|
||||
ins.reg2 shouldBe null
|
||||
ins.value shouldBe null
|
||||
ins.labelSymbol shouldBe listOf("a","b","c")
|
||||
ins.labelSymbol shouldBe "a.b.c"
|
||||
ins.reg1direction shouldBe OperandDirection.INPUT
|
||||
ins.fpReg1direction shouldBe OperandDirection.INPUT
|
||||
ins.toString() shouldBe "bz.w r11,_a.b.c"
|
||||
}
|
||||
|
||||
test("with output registers") {
|
||||
val ins = Instruction(Opcode.ADDR, VmDataType.WORD, reg1=11, reg2=22)
|
||||
val ins = IRInstruction(Opcode.ADDR, VmDataType.WORD, reg1=11, reg2=22)
|
||||
ins.opcode shouldBe Opcode.ADDR
|
||||
ins.type shouldBe VmDataType.WORD
|
||||
ins.reg1 shouldBe 11
|
||||
@@ -59,7 +59,7 @@ class TestInstructions: FunSpec({
|
||||
ins.fpReg1direction shouldBe OperandDirection.INPUT
|
||||
ins.toString() shouldBe "addr.w r11,r22"
|
||||
|
||||
val ins2 = Instruction(Opcode.SQRT, VmDataType.BYTE, reg1=11, reg2=22)
|
||||
val ins2 = IRInstruction(Opcode.SQRT, VmDataType.BYTE, reg1=11, reg2=22)
|
||||
ins2.opcode shouldBe Opcode.SQRT
|
||||
ins2.type shouldBe VmDataType.BYTE
|
||||
ins2.reg1 shouldBe 11
|
||||
@@ -74,25 +74,26 @@ class TestInstructions: FunSpec({
|
||||
|
||||
test("missing type should fail") {
|
||||
shouldThrow<IllegalArgumentException> {
|
||||
Instruction(Opcode.BZ, reg1=42, value=9999)
|
||||
IRInstruction(Opcode.BZ, reg1=42, value=99)
|
||||
}
|
||||
}
|
||||
|
||||
test("missing registers should fail") {
|
||||
shouldThrow<IllegalArgumentException> {
|
||||
Instruction(Opcode.BZ, VmDataType.BYTE, value=9999)
|
||||
shouldThrowWithMessage<IllegalArgumentException>("missing reg1") {
|
||||
IRInstruction(Opcode.BZ, VmDataType.BYTE, value=99)
|
||||
}
|
||||
}
|
||||
|
||||
test("missing value should fail") {
|
||||
shouldThrow<IllegalArgumentException> {
|
||||
Instruction(Opcode.BZ, VmDataType.BYTE, reg1=42)
|
||||
shouldThrowWithMessage<IllegalArgumentException>("missing a value or labelsymbol") {
|
||||
IRInstruction(Opcode.BZ, VmDataType.BYTE, reg1=42)
|
||||
}
|
||||
}
|
||||
|
||||
test("all instructionformats") {
|
||||
instructionFormats.size shouldBe Opcode.values().size
|
||||
Opcode.values().forEach {
|
||||
instructionFormats[it] shouldNotBe null
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
#!/usr/bin/env sh
|
||||
|
||||
rm -f *.bin *.xex *.jar *.asm *.prg *.vm.txt *.vice-mon-list *.list *.p8virt a.out imgui.ini
|
||||
rm -f *.bin *.xex *.jar *.asm *.prg *.vm.txt *.vice-mon-list *.list *.p8ir a.out imgui.ini
|
||||
rm -rf build out
|
||||
rm -rf compiler/build codeGenCpu6502/build codeGenExperimental/build codeOptimizers/build compilerAst/build dbusCompilerService/build httpCompilerService/build parser/build parser/src/prog8/parser
|
||||
|
||||
@@ -6,7 +6,6 @@ include(
|
||||
':codeOptimizers',
|
||||
':virtualmachine',
|
||||
':codeGenIntermediate',
|
||||
':codeGenVirtual',
|
||||
':codeGenCpu6502',
|
||||
':codeGenExperimental',
|
||||
':compiler',
|
||||
|
||||
@@ -27,6 +27,7 @@ compileTestKotlin {
|
||||
dependencies {
|
||||
implementation project(':codeCore')
|
||||
implementation project(':intermediate')
|
||||
implementation project(':codeGenIntermediate')
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
|
||||
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.16"
|
||||
testImplementation 'io.kotest:kotest-runner-junit5-jvm:5.3.2'
|
||||
|
||||
@@ -1,330 +0,0 @@
|
||||
package prog8.vm
|
||||
|
||||
import prog8.code.core.unescape
|
||||
import prog8.intermediate.*
|
||||
|
||||
|
||||
class Assembler {
|
||||
private val symbolAddresses = mutableMapOf<String, Int>()
|
||||
private val placeholders = mutableMapOf<Int, String>()
|
||||
var cx16virtualregBaseAdress = 0xff02
|
||||
|
||||
init {
|
||||
require(instructionFormats.size== Opcode.values().size) {
|
||||
"missing " + (Opcode.values().toSet() - instructionFormats.keys)
|
||||
}
|
||||
}
|
||||
|
||||
fun initializeMemory(memsrc: String, memory: Memory) {
|
||||
symbolAddresses.clear()
|
||||
val instrPattern = Regex("""var (.+) @([0-9]+) ([a-z]+)(\[[0-9]+\])? (.+)""", RegexOption.IGNORE_CASE)
|
||||
for(line in memsrc.lines()) {
|
||||
if(line.isBlank() || line.startsWith(';'))
|
||||
continue
|
||||
val match = instrPattern.matchEntire(line.trim())
|
||||
if(match==null)
|
||||
throw IllegalArgumentException("invalid line $line")
|
||||
else {
|
||||
val (name, addrStr, datatype, arrayspec, values) = match.destructured
|
||||
if(name=="cx16.r0") {
|
||||
cx16virtualregBaseAdress = addrStr.toInt()
|
||||
}
|
||||
val numArrayElts = if(arrayspec.isBlank()) 1 else arrayspec.substring(1, arrayspec.length-1).toInt()
|
||||
var address = parseValue(Opcode.LOADCPU, addrStr, 0).toInt()
|
||||
symbolAddresses[name] = address
|
||||
when(datatype) {
|
||||
"str" -> {
|
||||
val string = values.trim('"').unescape()
|
||||
memory.setString(address, string, false)
|
||||
}
|
||||
"strz" -> {
|
||||
val string = values.trim('"').unescape()
|
||||
memory.setString(address, string, true)
|
||||
}
|
||||
"ubyte", "byte" -> {
|
||||
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().toUByte()
|
||||
repeat(numArrayElts) {
|
||||
memory.setUB(address, value)
|
||||
address++
|
||||
}
|
||||
} else {
|
||||
for (value in array) {
|
||||
memory.setUB(address, value.toUByte())
|
||||
address++
|
||||
}
|
||||
}
|
||||
}
|
||||
"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
|
||||
}
|
||||
} else {
|
||||
for (value in array) {
|
||||
memory.setUW(address, value.toUShort())
|
||||
address += 2
|
||||
}
|
||||
}
|
||||
}
|
||||
"float" -> {
|
||||
val array = values.split(',').map { it.toFloat() }
|
||||
require(array.size==numArrayElts || array.size==1)
|
||||
if(numArrayElts>array.size) {
|
||||
val value = array.single()
|
||||
repeat(numArrayElts) {
|
||||
memory.setFloat(address, value)
|
||||
address += 4 // 32-bits floats
|
||||
}
|
||||
} else {
|
||||
for (value in array) {
|
||||
memory.setFloat(address, value)
|
||||
address += 4 // 32-bits floats
|
||||
}
|
||||
}
|
||||
}
|
||||
else -> throw IllegalArgumentException("invalid datatype $datatype")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun assembleProgram(source: CharSequence): List<Instruction> {
|
||||
placeholders.clear()
|
||||
val program = mutableListOf<Instruction>()
|
||||
val instructionPattern = Regex("""([a-z]+)(\.b|\.w|\.f)?(.*)""", RegexOption.IGNORE_CASE)
|
||||
val labelPattern = Regex("""_([a-zA-Z\d\._]+):""")
|
||||
val binaryPattern = Regex("""!binary (.+)""")
|
||||
for (line in source.lines()) {
|
||||
if(line.isBlank() || line.startsWith(';'))
|
||||
continue
|
||||
val match = instructionPattern.matchEntire(line.trim())
|
||||
if(match==null) {
|
||||
val binarymatch = binaryPattern.matchEntire(line.trim())
|
||||
if(binarymatch!=null) {
|
||||
val hex = binarymatch.groups[1]!!.value
|
||||
val binary = hex.windowed(size=2, step=2).map {
|
||||
it.toString().toByte(16)
|
||||
}.toByteArray()
|
||||
program.add(Instruction(Opcode.BINARYDATA, binaryData = binary))
|
||||
} else {
|
||||
val labelmatch = labelPattern.matchEntire(line.trim())
|
||||
if (labelmatch == null)
|
||||
throw IllegalArgumentException("invalid line $line at line ${program.size + 1}")
|
||||
else {
|
||||
val label = labelmatch.groupValues[1]
|
||||
if (label in symbolAddresses)
|
||||
throw IllegalArgumentException("label redefined $label")
|
||||
symbolAddresses[label] = program.size
|
||||
}
|
||||
}
|
||||
} else {
|
||||
val (_, instr, typestr, rest) = match.groupValues
|
||||
if(instr=="incbin") {
|
||||
println("warning: ignoring incbin command: $rest")
|
||||
continue
|
||||
}
|
||||
val opcode = try {
|
||||
Opcode.valueOf(instr.uppercase())
|
||||
} catch (ax: IllegalArgumentException) {
|
||||
throw IllegalArgumentException("invalid vmasm instruction: $instr", ax)
|
||||
}
|
||||
var type: VmDataType? = convertType(typestr)
|
||||
val formats = instructionFormats.getValue(opcode)
|
||||
val format: InstructionFormat
|
||||
if(type !in formats) {
|
||||
type = VmDataType.BYTE
|
||||
format = if(type !in formats)
|
||||
formats.getValue(null)
|
||||
else
|
||||
formats.getValue(type)
|
||||
} else {
|
||||
format = formats.getValue(type)
|
||||
}
|
||||
// parse the operands
|
||||
val operands = rest.lowercase().split(",").toMutableList()
|
||||
var reg1: Int? = null
|
||||
var reg2: Int? = null
|
||||
var reg3: Int? = null
|
||||
var fpReg1: Int? = null
|
||||
var fpReg2: Int? = null
|
||||
var fpReg3: Int? = null
|
||||
var value: Float? = null
|
||||
var operand: String?
|
||||
if(operands.isNotEmpty() && operands[0].isNotEmpty()) {
|
||||
operand = operands.removeFirst().trim()
|
||||
if(operand[0]=='r')
|
||||
reg1 = operand.substring(1).toInt()
|
||||
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)
|
||||
}
|
||||
operands.clear()
|
||||
}
|
||||
if(operands.isNotEmpty()) {
|
||||
operand = operands.removeFirst().trim()
|
||||
if(operand[0]=='r')
|
||||
reg2 = operand.substring(1).toInt()
|
||||
else if(operand[0]=='f' && operand[1]=='r')
|
||||
fpReg2 = operand.substring(2).toInt()
|
||||
else {
|
||||
value = parseValue(opcode, operand, program.size)
|
||||
operands.clear()
|
||||
}
|
||||
if(operands.isNotEmpty()) {
|
||||
operand = operands.removeFirst().trim()
|
||||
if(operand[0]=='r')
|
||||
reg3 = operand.substring(1).toInt()
|
||||
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)
|
||||
operands.clear()
|
||||
}
|
||||
if(operands.isNotEmpty()) {
|
||||
val symbol=rest.split(',').last()
|
||||
value = parseValue(opcode, symbol, program.size)
|
||||
operands.clear()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// shift the operands back into place
|
||||
while(reg1==null && reg2!=null) {
|
||||
reg1 = reg2
|
||||
reg2 = reg3
|
||||
reg3 = null
|
||||
}
|
||||
while(fpReg1==null && fpReg2!=null) {
|
||||
fpReg1 = fpReg2
|
||||
fpReg2 = fpReg3
|
||||
fpReg3 = null
|
||||
}
|
||||
if(reg3!=null)
|
||||
throw IllegalArgumentException("too many reg arguments $line")
|
||||
if(fpReg3!=null)
|
||||
throw IllegalArgumentException("too many fpreg arguments $line")
|
||||
|
||||
if(type!=null && type !in formats)
|
||||
throw IllegalArgumentException("invalid type code for $line")
|
||||
if(format.reg1 && reg1==null)
|
||||
throw IllegalArgumentException("needs reg1 for $line")
|
||||
if(format.reg2 && reg2==null)
|
||||
throw IllegalArgumentException("needs reg2 for $line")
|
||||
if(format.value && value==null)
|
||||
throw IllegalArgumentException("needs value for $line")
|
||||
if(!format.reg1 && reg1!=null)
|
||||
throw IllegalArgumentException("invalid reg1 for $line")
|
||||
if(!format.reg2 && reg2!=null)
|
||||
throw IllegalArgumentException("invalid reg2 for $line")
|
||||
if(value!=null && opcode !in OpcodesWithAddress) {
|
||||
when (type) {
|
||||
VmDataType.BYTE -> {
|
||||
if (value < -128 || value > 255)
|
||||
throw IllegalArgumentException("value out of range for byte: $value")
|
||||
}
|
||||
VmDataType.WORD -> {
|
||||
if (value < -32768 || value > 65535)
|
||||
throw IllegalArgumentException("value out of range for word: $value")
|
||||
}
|
||||
VmDataType.FLOAT -> {}
|
||||
null -> {}
|
||||
}
|
||||
}
|
||||
var floatValue: Float? = null
|
||||
var intValue: Int? = null
|
||||
|
||||
if(format.value)
|
||||
intValue = value!!.toInt()
|
||||
if(format.fpValue)
|
||||
floatValue = value!!
|
||||
|
||||
if(opcode in OpcodesForCpuRegisters) {
|
||||
val regStr = rest.split(',').last().lowercase().trim()
|
||||
val reg = if(regStr.startsWith('_')) regStr.substring(1) else regStr
|
||||
if(reg !in setOf(
|
||||
"a", "x", "y",
|
||||
"ax", "ay", "xy",
|
||||
"r0", "r1", "r2", "r3",
|
||||
"r4", "r5", "r6", "r7",
|
||||
"r8", "r9", "r10","r11",
|
||||
"r12", "r13", "r14", "r15",
|
||||
"pc", "pz", "pv","pn"))
|
||||
throw IllegalArgumentException("invalid cpu reg: $reg")
|
||||
program.add(Instruction(opcode, type, reg1, labelSymbol = listOf(reg)))
|
||||
} else {
|
||||
program.add(Instruction(opcode, type, reg1, reg2, fpReg1, fpReg2, intValue, floatValue))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pass2replaceLabels(program)
|
||||
return program
|
||||
}
|
||||
|
||||
private fun pass2replaceLabels(program: MutableList<Instruction>) {
|
||||
for((line, label) in placeholders) {
|
||||
val replacement = symbolAddresses[label]
|
||||
if(replacement==null) {
|
||||
// it could be an address + index: symbol+42
|
||||
if('+' in label) {
|
||||
val (symbol, indexStr) = label.split('+')
|
||||
val index = indexStr.toInt()
|
||||
val address = symbolAddresses.getValue(symbol) + index
|
||||
program[line] = program[line].copy(value = address)
|
||||
} else {
|
||||
throw IllegalArgumentException("placeholder not found in labels: $label")
|
||||
}
|
||||
} else {
|
||||
program[line] = program[line].copy(value = replacement)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
|
||||
private fun convertType(typestr: String): VmDataType? {
|
||||
return when(typestr.lowercase()) {
|
||||
"" -> null
|
||||
".b" -> VmDataType.BYTE
|
||||
".w" -> VmDataType.WORD
|
||||
".f" -> VmDataType.FLOAT
|
||||
else -> throw IllegalArgumentException("invalid type $typestr")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
package prog8.vm
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
val memsrc = """
|
||||
$4000 strz "Hello from program! "derp" bye.\n"
|
||||
$2000 ubyte 65,66,67,68,0
|
||||
$2100 uword $1111,$2222,$3333,$4444
|
||||
"""
|
||||
val src = """
|
||||
; enable lores gfx screen
|
||||
load r0, 0
|
||||
syscall 8
|
||||
load.w r10, 320
|
||||
load.w r11, 240
|
||||
load.b r12, 0
|
||||
|
||||
_forever:
|
||||
load.w r1, 0
|
||||
_yloop:
|
||||
load.w r0, 0
|
||||
_xloop:
|
||||
mul.b r2,r0,r1
|
||||
add.b r2,r2,r12
|
||||
syscall 10
|
||||
addi.w r0,r0,1
|
||||
blt.w r0, r10, _xloop
|
||||
addi.w r1,r1,1
|
||||
blt.w r1, r11,_yloop
|
||||
addi.b r12,r12,1
|
||||
jump _forever
|
||||
|
||||
load.w r0, 2000
|
||||
syscall 7
|
||||
load.w r0,0
|
||||
return"""
|
||||
val memory = Memory()
|
||||
val assembler = Assembler()
|
||||
assembler.initializeMemory(memsrc, memory)
|
||||
val program = assembler.assembleProgram(src)
|
||||
|
||||
val vm = VirtualMachine(memory, program, assembler.cx16virtualregBaseAdress)
|
||||
vm.run()
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,234 @@
|
||||
package prog8.vm
|
||||
|
||||
import prog8.code.core.DataType
|
||||
import prog8.intermediate.*
|
||||
|
||||
class VmProgramLoader {
|
||||
|
||||
private val placeholders = mutableMapOf<Int, String>() // program index to symbolname
|
||||
|
||||
fun load(irProgram: IRProgram, memory: Memory): List<IRInstruction> {
|
||||
|
||||
// at long last, allocate the variables in memory.
|
||||
placeholders.clear()
|
||||
val allocations = VmVariableAllocator(irProgram.st, irProgram.encoding, irProgram.options.compTarget)
|
||||
val symbolAddresses = allocations.allocations.toMutableMap()
|
||||
val program = mutableListOf<IRInstruction>()
|
||||
|
||||
varsToMemory(irProgram, allocations, symbolAddresses, memory)
|
||||
|
||||
if(!irProgram.options.dontReinitGlobals)
|
||||
addToProgram(irProgram.globalInits, program, symbolAddresses)
|
||||
|
||||
// make sure that if there is a "main.start" entrypoint, we jump to it
|
||||
irProgram.blocks.firstOrNull()?.let {
|
||||
if(it.subroutines.any { sub -> sub.name=="main.start" }) {
|
||||
placeholders[program.size] = "main.start"
|
||||
program += IRInstruction(Opcode.JUMP, labelSymbol = "main.start")
|
||||
}
|
||||
}
|
||||
|
||||
irProgram.blocks.forEach { block ->
|
||||
if(block.address!=null)
|
||||
throw IRParseException("blocks cannot have a load address for vm: ${block.name}")
|
||||
|
||||
block.inlineAssembly.forEach { addAssemblyToProgram(it, program, symbolAddresses) }
|
||||
block.subroutines.forEach {
|
||||
symbolAddresses[it.name] = program.size
|
||||
it.chunks.forEach { chunk ->
|
||||
if(chunk is IRInlineAsmChunk)
|
||||
addAssemblyToProgram(chunk, program, symbolAddresses)
|
||||
else
|
||||
addToProgram(chunk.lines, program, symbolAddresses)
|
||||
}
|
||||
}
|
||||
if(block.asmSubroutines.any())
|
||||
throw IRParseException("vm currently does not support asmsubs: ${block.asmSubroutines.first().name}")
|
||||
}
|
||||
|
||||
pass2replaceLabelsByProgIndex(program, symbolAddresses)
|
||||
|
||||
program.forEach {
|
||||
if(it.opcode in OpcodesWithAddress && it.value==null) {
|
||||
throw IRParseException("instruction missing numeric value, label not replaced? $it")
|
||||
}
|
||||
}
|
||||
|
||||
return program
|
||||
}
|
||||
|
||||
private fun varsToMemory(
|
||||
program: IRProgram,
|
||||
allocations: VmVariableAllocator,
|
||||
symbolAddresses: MutableMap<String, Int>,
|
||||
memory: Memory
|
||||
) {
|
||||
program.st.allVariables().forEach { variable ->
|
||||
var addr = allocations.allocations.getValue(variable.name)
|
||||
variable.onetimeInitializationNumericValue?.let {
|
||||
when(variable.dt) {
|
||||
DataType.UBYTE -> memory.setUB(addr, it.toInt().toUByte())
|
||||
DataType.BYTE -> memory.setSB(addr, it.toInt().toByte())
|
||||
DataType.UWORD -> memory.setUW(addr, it.toInt().toUShort())
|
||||
DataType.WORD -> memory.setSW(addr, it.toInt().toShort())
|
||||
DataType.FLOAT -> memory.setFloat(addr, it.toFloat())
|
||||
else -> throw IRParseException("invalid dt")
|
||||
}
|
||||
}
|
||||
variable.onetimeInitializationArrayValue?.let {
|
||||
require(variable.length==it.size || it.size==1)
|
||||
if(it.size==1) {
|
||||
val value = it[0].number!!
|
||||
when(variable.dt) {
|
||||
DataType.STR, DataType.ARRAY_UB -> {
|
||||
repeat(variable.length!!) {
|
||||
memory.setUB(addr, value.toInt().toUByte())
|
||||
addr++
|
||||
}
|
||||
}
|
||||
DataType.ARRAY_B -> {
|
||||
repeat(variable.length!!) {
|
||||
memory.setSB(addr, value.toInt().toByte())
|
||||
addr++
|
||||
}
|
||||
}
|
||||
DataType.ARRAY_UW -> {
|
||||
repeat(variable.length!!) {
|
||||
memory.setUW(addr, value.toInt().toUShort())
|
||||
addr+=2
|
||||
}
|
||||
}
|
||||
DataType.ARRAY_W -> {
|
||||
repeat(variable.length!!) {
|
||||
memory.setSW(addr, value.toInt().toShort())
|
||||
addr+=2
|
||||
}
|
||||
}
|
||||
DataType.ARRAY_F -> {
|
||||
repeat(variable.length!!) {
|
||||
memory.setFloat(addr, value.toFloat())
|
||||
addr += program.options.compTarget.machine.FLOAT_MEM_SIZE
|
||||
}
|
||||
}
|
||||
else -> throw IRParseException("invalid dt")
|
||||
}
|
||||
} else {
|
||||
when(variable.dt) {
|
||||
DataType.STR, DataType.ARRAY_UB -> {
|
||||
for(elt in it) {
|
||||
memory.setUB(addr, elt.number!!.toInt().toUByte())
|
||||
addr++
|
||||
}
|
||||
}
|
||||
DataType.ARRAY_B -> {
|
||||
for(elt in it) {
|
||||
memory.setSB(addr, elt.number!!.toInt().toByte())
|
||||
addr++
|
||||
}
|
||||
}
|
||||
DataType.ARRAY_UW -> {
|
||||
for(elt in it) {
|
||||
if(elt.addressOf!=null) {
|
||||
val symbolAddress = symbolAddresses.getValue(elt.addressOf!!.joinToString("."))
|
||||
memory.setUW(addr, symbolAddress.toUShort())
|
||||
} else {
|
||||
memory.setUW(addr, elt.number!!.toInt().toUShort())
|
||||
}
|
||||
addr+=2
|
||||
}
|
||||
}
|
||||
DataType.ARRAY_W -> {
|
||||
for(elt in it) {
|
||||
memory.setSW(addr, elt.number!!.toInt().toShort())
|
||||
addr+=2
|
||||
}
|
||||
}
|
||||
DataType.ARRAY_F -> {
|
||||
for(elt in it) {
|
||||
memory.setSW(addr, elt.number!!.toInt().toShort())
|
||||
addr+=program.options.compTarget.machine.FLOAT_MEM_SIZE
|
||||
}
|
||||
}
|
||||
else -> throw IRParseException("invalid dt")
|
||||
}
|
||||
}
|
||||
}
|
||||
require(variable.onetimeInitializationStringValue==null) { "in vm/ir, strings should have been converted into bytearrays." }
|
||||
}
|
||||
}
|
||||
|
||||
private fun pass2replaceLabelsByProgIndex(
|
||||
program: MutableList<IRInstruction>,
|
||||
symbolAddresses: MutableMap<String, Int>
|
||||
) {
|
||||
for((line, label) in placeholders) {
|
||||
val replacement = symbolAddresses[label]
|
||||
if(replacement==null) {
|
||||
// it could be an address + index: symbol+42
|
||||
if('+' in label) {
|
||||
val (symbol, indexStr) = label.split('+')
|
||||
val index = indexStr.toInt()
|
||||
val address = symbolAddresses.getValue(symbol) + index
|
||||
program[line] = program[line].copy(value = address)
|
||||
} else {
|
||||
throw IRParseException("placeholder not found in labels: $label")
|
||||
}
|
||||
} else {
|
||||
program[line] = program[line].copy(value = replacement)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun addToProgram(
|
||||
lines: Iterable<IRCodeLine>,
|
||||
program: MutableList<IRInstruction>,
|
||||
symbolAddresses: MutableMap<String, Int>
|
||||
) {
|
||||
lines.map {
|
||||
when(it) {
|
||||
is IRInstruction -> {
|
||||
it.labelSymbol?.let { symbol -> placeholders[program.size]=symbol }
|
||||
if(it.opcode==Opcode.SYSCALL) {
|
||||
// convert IR Syscall to VM Syscall
|
||||
val vmSyscall = when(it.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 IRParseException("invalid IM syscall number $it")
|
||||
}
|
||||
program += it.copy(value=vmSyscall.ordinal)
|
||||
} else {
|
||||
program += it
|
||||
}
|
||||
}
|
||||
is IRCodeInlineBinary -> program += IRInstruction(Opcode.BINARYDATA, binaryData = it.data)
|
||||
is IRCodeComment -> { /* just ignore */ }
|
||||
is IRCodeLabel -> { symbolAddresses[it.name] = program.size }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun addAssemblyToProgram(
|
||||
asmChunk: IRInlineAsmChunk,
|
||||
program: MutableList<IRInstruction>,
|
||||
symbolAddresses: MutableMap<String, Int>,
|
||||
) {
|
||||
asmChunk.assembly.lineSequence().forEach {
|
||||
val parsed = parseIRCodeLine(it.trim(), program.size, placeholders)
|
||||
if(parsed is IRInstruction)
|
||||
program += parsed
|
||||
else if(parsed is IRCodeLabel)
|
||||
symbolAddresses[parsed.name] = program.size
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package prog8.vm
|
||||
|
||||
import prog8.code.core.*
|
||||
import prog8.intermediate.IRSymbolTable
|
||||
|
||||
internal class VmVariableAllocator(val st: IRSymbolTable, val encoding: IStringEncoding, memsizer: IMemSizer) {
|
||||
|
||||
internal val allocations = mutableMapOf<String, Int>()
|
||||
private var freeMemoryStart: Int
|
||||
|
||||
val freeMem: Int
|
||||
get() = freeMemoryStart
|
||||
|
||||
|
||||
init {
|
||||
var nextLocation = 0
|
||||
for (variable in st.allVariables()) {
|
||||
val memsize =
|
||||
when (variable.dt) {
|
||||
DataType.STR -> variable.onetimeInitializationStringValue!!.first.length + 1 // include the zero byte
|
||||
in NumericDatatypes -> memsizer.memorySize(variable.dt)
|
||||
in ArrayDatatypes -> memsizer.memorySize(variable.dt, variable.length!!)
|
||||
else -> throw InternalCompilerException("weird dt")
|
||||
}
|
||||
|
||||
allocations[variable.name] = nextLocation
|
||||
nextLocation += memsize
|
||||
}
|
||||
for(slab in st.allMemorySlabs()) {
|
||||
// we ignore the alignment for the VM.
|
||||
allocations[slab.name] = nextLocation
|
||||
nextLocation += slab.size.toInt()
|
||||
}
|
||||
|
||||
freeMemoryStart = nextLocation
|
||||
}
|
||||
}
|
||||
+17
-12
@@ -1,4 +1,4 @@
|
||||
package prog8.codegen.virtual
|
||||
package prog8.vm.codegen
|
||||
|
||||
import prog8.code.SymbolTable
|
||||
import prog8.code.ast.PtProgram
|
||||
@@ -9,6 +9,7 @@ import prog8.code.core.IErrorReporter
|
||||
import prog8.codegen.intermediate.IRCodeGen
|
||||
import prog8.intermediate.IRFileReader
|
||||
import prog8.intermediate.IRFileWriter
|
||||
import prog8.intermediate.IRProgram
|
||||
import java.nio.file.Path
|
||||
|
||||
class VmCodeGen(private val program: PtProgram,
|
||||
@@ -21,20 +22,24 @@ class VmCodeGen(private val program: PtProgram,
|
||||
val irCodeGen = IRCodeGen(program, symbolTable, options, errors)
|
||||
val irProgram = irCodeGen.generate()
|
||||
|
||||
return if(options.keepIR) {
|
||||
//create IR file on disk and read it back.
|
||||
IRFileWriter(irProgram).writeFile()
|
||||
val irProgram2 = IRFileReader(options.outputDir, irProgram.name).readFile()
|
||||
VmAssemblyProgram(irProgram2.name, irProgram2)
|
||||
} else {
|
||||
VmAssemblyProgram(irProgram.name, irProgram)
|
||||
}
|
||||
// no need to check options.keepIR, as the VM file format *is* the IR file.
|
||||
return VmAssemblyProgram(irProgram.name, irProgram)
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun compileIR(listingFilename: String): IAssemblyProgram {
|
||||
val irProgram = IRFileReader(Path.of(""), listingFilename).readFile()
|
||||
fun compileIR(irFile: Path): IAssemblyProgram {
|
||||
val irProgram = IRFileReader().read(irFile)
|
||||
return VmAssemblyProgram(irProgram.name, irProgram)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
internal class VmAssemblyProgram(override val name: String, private val irProgram: IRProgram): IAssemblyProgram {
|
||||
|
||||
override fun assemble(options: CompilationOptions): Boolean {
|
||||
// the VM reads the IR file from disk.
|
||||
IRFileWriter(irProgram, null).write()
|
||||
return true
|
||||
}
|
||||
}
|
||||
@@ -1,22 +1,37 @@
|
||||
import io.kotest.assertions.throwables.shouldThrowWithMessage
|
||||
import io.kotest.core.spec.style.FunSpec
|
||||
import io.kotest.matchers.collections.shouldBeEmpty
|
||||
import io.kotest.matchers.shouldBe
|
||||
import prog8.intermediate.Instruction
|
||||
import prog8.intermediate.Opcode
|
||||
import prog8.intermediate.VmDataType
|
||||
import prog8.vm.Memory
|
||||
import prog8.code.core.*
|
||||
import prog8.code.target.VMTarget
|
||||
import prog8.intermediate.*
|
||||
import prog8.vm.VirtualMachine
|
||||
import prog8.vm.VmRunner
|
||||
|
||||
class TestVm: FunSpec( {
|
||||
|
||||
fun getTestOptions(): CompilationOptions {
|
||||
val target = VMTarget()
|
||||
return CompilationOptions(
|
||||
OutputType.RAW,
|
||||
CbmPrgLauncherType.NONE,
|
||||
ZeropageType.DONTUSE,
|
||||
zpReserved = emptyList(),
|
||||
floats = true,
|
||||
noSysInit = false,
|
||||
compTarget = target,
|
||||
loadAddress = target.machine.PROGRAM_LOAD_ADDRESS
|
||||
)
|
||||
}
|
||||
|
||||
test("vm execution: empty program") {
|
||||
val memory = Memory()
|
||||
val vm = VirtualMachine(memory, emptyList(), 0xff00)
|
||||
val program = IRProgram("test", IRSymbolTable(null), getTestOptions(), VMTarget())
|
||||
val vm = VirtualMachine(program)
|
||||
vm.callStack.shouldBeEmpty()
|
||||
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
|
||||
@@ -24,29 +39,81 @@ class TestVm: FunSpec( {
|
||||
}
|
||||
|
||||
test("vm execution: modify memory") {
|
||||
val memory = Memory()
|
||||
val program = listOf(
|
||||
Instruction(Opcode.LOAD, VmDataType.WORD, reg1=1, value=12345),
|
||||
Instruction(Opcode.STOREM, VmDataType.WORD, reg1=1, value=1000),
|
||||
Instruction(Opcode.RETURN)
|
||||
)
|
||||
val vm = VirtualMachine(memory, program, 0xff00)
|
||||
val program = IRProgram("test", IRSymbolTable(null), getTestOptions(), VMTarget())
|
||||
val block = IRBlock("testmain", null, IRBlock.BlockAlignment.NONE, Position.DUMMY)
|
||||
val startSub = IRSubroutine("testmain.testsub", emptyList(), null, Position.DUMMY)
|
||||
val code = IRCodeChunk(Position.DUMMY)
|
||||
code += IRInstruction(Opcode.NOP)
|
||||
code += IRInstruction(Opcode.LOAD, VmDataType.WORD, reg1=1, value=12345)
|
||||
code += IRInstruction(Opcode.STOREM, VmDataType.WORD, reg1=1, value=1000)
|
||||
code += IRInstruction(Opcode.RETURN)
|
||||
startSub += code
|
||||
block += startSub
|
||||
program.addBlock(block)
|
||||
val vm = VirtualMachine(program)
|
||||
|
||||
memory.getUW(1000) shouldBe 0u
|
||||
vm.memory.getUW(1000) shouldBe 0u
|
||||
vm.callStack.shouldBeEmpty()
|
||||
vm.valueStack.shouldBeEmpty()
|
||||
vm.pc shouldBe 0
|
||||
vm.stepCount shouldBe 0
|
||||
vm.run(throttle = false)
|
||||
memory.getUW(1000) shouldBe 12345u
|
||||
vm.run()
|
||||
vm.memory.getUW(1000) shouldBe 12345u
|
||||
vm.callStack.shouldBeEmpty()
|
||||
vm.valueStack.shouldBeEmpty()
|
||||
vm.pc shouldBe 2
|
||||
vm.stepCount shouldBe 3
|
||||
vm.pc shouldBe code.lines.size-1
|
||||
vm.stepCount shouldBe code.lines.size
|
||||
}
|
||||
|
||||
test("vm asmsub not supported") {
|
||||
val program = IRProgram("test", IRSymbolTable(null), getTestOptions(), VMTarget())
|
||||
val block = IRBlock("testmain", null, IRBlock.BlockAlignment.NONE, Position.DUMMY)
|
||||
val startSub = IRSubroutine("testmain.testsub", emptyList(), null, Position.DUMMY)
|
||||
val code = IRCodeChunk(Position.DUMMY)
|
||||
code += IRInstruction(Opcode.BINARYDATA, binaryData = listOf(1u,2u,3u))
|
||||
code += IRInstruction(Opcode.RETURN)
|
||||
startSub += code
|
||||
block += startSub
|
||||
program.addBlock(block)
|
||||
val vm = VirtualMachine(program)
|
||||
shouldThrowWithMessage<NotImplementedError>("An operation is not implemented: BINARYDATA not yet supported in VM") {
|
||||
vm.run()
|
||||
}
|
||||
}
|
||||
|
||||
test("vm asmbinary not supported") {
|
||||
val program = IRProgram("test", IRSymbolTable(null), getTestOptions(), VMTarget())
|
||||
val block = IRBlock("main", null, IRBlock.BlockAlignment.NONE, Position.DUMMY)
|
||||
val startSub = IRAsmSubroutine("main.asmstart", Position.DUMMY, 0x2000u, emptySet(), emptyList(), emptyList(), "inlined asm here")
|
||||
block += startSub
|
||||
program.addBlock(block)
|
||||
shouldThrowWithMessage<IRParseException>("vm currently does not support asmsubs: main.asmstart") {
|
||||
VirtualMachine(program)
|
||||
}
|
||||
}
|
||||
|
||||
test("vmrunner") {
|
||||
val runner = VmRunner()
|
||||
runner.runProgram(";comment\n------PROGRAM------\n;comment\n", false)
|
||||
val irSource="""<PROGRAM NAME=test>
|
||||
<OPTIONS>
|
||||
</OPTIONS>
|
||||
|
||||
<VARIABLES>
|
||||
</VARIABLES>
|
||||
|
||||
<MEMORYMAPPEDVARIABLES>
|
||||
</MEMORYMAPPEDVARIABLES>
|
||||
|
||||
<MEMORYSLABS>
|
||||
</MEMORYSLABS>
|
||||
|
||||
<INITGLOBALS>
|
||||
</INITGLOBALS>
|
||||
|
||||
<BLOCK NAME=main ADDRESS=null ALIGN=NONE POS=[unittest: line 42 col 1-9]>
|
||||
</BLOCK>
|
||||
</PROGRAM>
|
||||
"""
|
||||
runner.runProgram(irSource)
|
||||
}
|
||||
})
|
||||
@@ -14,5 +14,6 @@
|
||||
<orderEntry type="library" name="io.kotest.runner.junit5.jvm" level="project" />
|
||||
<orderEntry type="module" module-name="codeCore" />
|
||||
<orderEntry type="module" module-name="intermediate" />
|
||||
<orderEntry type="module" module-name="codeGenIntermediate" />
|
||||
</component>
|
||||
</module>
|
||||
Reference in new issue
Block a user