Compare commits

...
40 Commits
Author SHA1 Message Date
Irmen de Jong b68f141568 some more old code cleanups 2019-10-21 00:12:26 +02:00
Irmen de Jong b5d1e8653d tiny cleanups 2019-10-20 23:52:26 +02:00
Irmen de Jong f6d4c90dea improved number-to-decimal routines 2019-09-23 20:44:41 +02:00
Irmen de Jong b5b24636ae removed sim65 because it was moved to a separate repository 2019-09-11 02:24:44 +02:00
Irmen de Jong 9dedbbf47c use more modern java date/time api 2019-09-10 01:29:33 +02:00
Irmen de Jong c493c3e5c6 implemented IRQ handling 2019-09-09 23:28:41 +02:00
Irmen de Jong 61d4ca1d24 added functional test files to git 2019-09-09 19:57:51 +02:00
Irmen de Jong 2cf9af4a6e implemented sim timer and clock 2019-09-09 04:51:18 +02:00
Irmen de Jong bdcd10512f 6502 simulator passes all tests for regular opcodes 2019-09-09 00:27:06 +02:00
Irmen de Jong fec8db6a75 fixed sbc and adc 2019-09-08 22:35:08 +02:00
Irmen de Jong b400010426 separated the 6502 test suite into separate unit tests 2019-09-08 19:11:06 +02:00
Irmen de Jong 28109a39ac clean up of c64 tests 2019-09-08 17:19:40 +02:00
Irmen de Jong 651f0ec445 fixed IZY addressing mode address calc
added test harness for Wolfgang Lorenz's 6502 test suite
2019-09-08 16:40:46 +02:00
Irmen de Jong e61d3df380 added missing testfiles 2019-09-06 01:09:23 +02:00
Irmen de Jong 15710207b2 fixed bcd (but the bcd test code still fails, strange) 2019-09-06 00:38:48 +02:00
Irmen de Jong adfddddac6 attempt to fix bcd 2019-09-05 21:38:40 +02:00
Irmen de Jong e46982f652 fixes 2019-09-05 01:41:48 +02:00
Irmen de Jong 900c2aea23 fixed all instructions except BCD arithmetic 2019-09-05 01:26:01 +02:00
Irmen de Jong 42f8e98cab cpu unit test suite ported from Py65 2019-09-04 22:23:31 +02:00
Irmen de Jong bed0e33b4f unit test 2019-09-04 02:41:09 +02:00
Irmen de Jong 8d6542905d beginnings of 6502 cpu simulator 2019-09-03 23:58:46 +02:00
Irmen de Jong 39798a1a4f todos 2019-08-29 22:31:29 +02:00
Irmen de Jong befe4b8e9f try to fix windows path issue with drive letter 2019-08-27 01:02:31 +02:00
Irmen de Jong 772e48105e fixed some type cast compiler errors in for loops 2019-08-26 23:38:59 +02:00
Irmen de Jong 9afe451b8d fix build script to target jdk 1.8 2019-08-26 21:27:45 +02:00
Irmen de Jong 89d469e77e examples 2019-08-25 00:46:46 +02:00
Irmen de Jong 59a43889a5 examples 2019-08-25 00:24:00 +02:00
Irmen de Jong 7caa0daffc examples 2019-08-24 21:40:50 +02:00
Irmen de Jong 5e854c2cf8 more forloop asm 2019-08-24 21:26:29 +02:00
Irmen de Jong 9edc92ec29 more bitshift asm stubs (actual functions still to be done) 2019-08-23 23:06:36 +02:00
Irmen de Jong 1d178080a3 more bitshift asm 2019-08-23 21:33:43 +02:00
Irmen de Jong aa94300bdd added output directory command line option
improved cli parser by using kotlinx.cli
2019-08-23 00:11:08 +02:00
Irmen de Jong 2d768c3f28 code cleanups 2019-08-22 22:06:21 +02:00
Irmen de Jong b79af624ae added more asmgen for bitshift operations 2019-08-22 00:34:17 +02:00
Irmen de Jong 38208a7c9e removed fake vm functions 2019-08-21 22:00:05 +02:00
Irmen de Jong 8eff51904e taking down the heapvalue mess further 2019-08-21 00:29:31 +02:00
Irmen de Jong c717f4573d taking down the heapvalue mess further 2019-08-20 23:02:13 +02:00
Irmen de Jong 984d251a6d taking down the heapvalue mess, RuntimeValue class separation 2019-08-20 00:01:31 +02:00
Irmen de Jong 8c3b43f3ed taking down the heapvalue mess 2019-08-19 22:28:41 +02:00
Irmen de Jong 0f1485f30b added sorted, sgn, reverse to the AstVm 2019-08-18 16:39:08 +02:00
72 changed files with 3020 additions and 8624 deletions

No files matched your search

+10
View File
@@ -1,6 +1,16 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="DuplicatedCode" enabled="true" level="WEAK WARNING" enabled_by_default="true">
<Languages>
<language minSize="100" isEnabled="false" name="JavaScript" />
<language isEnabled="false" name="Groovy" />
<language isEnabled="false" name="Style Sheets" />
<language minSize="70" name="Kotlin" />
<language isEnabled="false" name="TypeScript" />
<language isEnabled="false" name="ActionScript" />
</Languages>
</inspection_tool>
<inspection_tool class="SpellCheckingInspection" enabled="true" level="TYPO" enabled_by_default="true">
<option name="processCode" value="false" />
<option name="processLiterals" value="true" />
+9
View File
@@ -0,0 +1,9 @@
<component name="libraryTable">
<library name="kotlinx-cli-jvm-0.1.0-dev-5">
<CLASSES>
<root url="jar://$PROJECT_DIR$/compiler/lib/kotlinx-cli-jvm-0.1.0-dev-5.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</component>
+1 -1
View File
@@ -2,7 +2,7 @@
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/DeprecatedStackVm/DeprecatedStackVm.iml" filepath="$PROJECT_DIR$/DeprecatedStackVm/DeprecatedStackVm.iml" />
<module fileurl="file://$PROJECT_DIR$/OldCodeGen/OldCodeGen.iml" filepath="$PROJECT_DIR$/OldCodeGen/OldCodeGen.iml" />
<module fileurl="file://$PROJECT_DIR$/compiler/compiler.iml" filepath="$PROJECT_DIR$/compiler/compiler.iml" />
<module fileurl="file://$PROJECT_DIR$/docs/docs.iml" filepath="$PROJECT_DIR$/docs/docs.iml" />
<module fileurl="file://$PROJECT_DIR$/examples/examples.iml" filepath="$PROJECT_DIR$/examples/examples.iml" />
Generated
+1 -1
View File
@@ -3,4 +3,4 @@
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>
</project>
File diff suppressed because it is too large. Load diff
@@ -1,51 +0,0 @@
package compiler.intermediate
import prog8.vm.RuntimeValue
import prog8.vm.stackvm.Syscall
open class Instruction(val opcode: Opcode,
val arg: RuntimeValue? = null,
val arg2: RuntimeValue? = null,
val callLabel: String? = null,
val callLabel2: String? = null)
{
var branchAddress: Int? = null
override fun toString(): String {
val argStr = arg?.toString() ?: ""
val result =
when {
opcode== Opcode.LINE -> "_line $callLabel"
opcode== Opcode.INLINE_ASSEMBLY -> {
// inline assembly is not written out (it can't be processed as intermediate language)
// instead, it is converted into a system call that can be intercepted by the vm
if(callLabel!=null)
"syscall SYSASM.$callLabel\n return"
else
"inline_assembly"
}
opcode== Opcode.INCLUDE_FILE -> {
"include_file \"$callLabel\" $arg $arg2"
}
opcode== Opcode.SYSCALL -> {
val syscall = Syscall.values().find { it.callNr==arg!!.numericValue() }
"syscall $syscall"
}
opcode in opcodesWithVarArgument -> {
// opcodes that manipulate a variable
"${opcode.name.toLowerCase()} ${callLabel?:""} ${callLabel2?:""}".trimEnd()
}
callLabel==null -> "${opcode.name.toLowerCase()} $argStr"
else -> "${opcode.name.toLowerCase()} $callLabel $argStr"
}
.trimEnd()
return " $result"
}
}
class LabelInstr(val name: String, val asmProc: Boolean) : Instruction(Opcode.NOP, null, null) {
override fun toString(): String {
return "\n$name:"
}
}
@@ -1,548 +0,0 @@
package compiler.intermediate
import prog8.ast.antlr.escape
import prog8.ast.base.*
import prog8.ast.expressions.NumericLiteralValue
import prog8.ast.expressions.ReferenceLiteralValue
import prog8.ast.statements.StructDecl
import prog8.ast.statements.VarDecl
import prog8.ast.statements.ZeropageWish
import prog8.compiler.CompilerException
import prog8.compiler.HeapValues
import prog8.compiler.Zeropage
import prog8.compiler.ZeropageDepletedError
import prog8.vm.RuntimeValue
import java.io.PrintStream
import java.nio.file.Path
class IntermediateProgram(val name: String, var loadAddress: Int, val heap: HeapValues, val source: Path) {
class VariableParameters (val zp: ZeropageWish, val memberOfStruct: StructDecl?)
class Variable(val scopedname: String, val value: RuntimeValue, val params: VariableParameters)
class ProgramBlock(val name: String,
var address: Int?,
val instructions: MutableList<Instruction> = mutableListOf(),
val variables: MutableList<Variable> = mutableListOf(),
val memoryPointers: MutableMap<String, Pair<Int, DataType>> = mutableMapOf(),
val labels: MutableMap<String, Instruction> = mutableMapOf(), // names are fully scoped
val force_output: Boolean)
val allocatedZeropageVariables = mutableMapOf<String, Pair<Int, DataType>>()
val blocks = mutableListOf<ProgramBlock>()
val memory = mutableMapOf<Int, List<RuntimeValue>>()
private lateinit var currentBlock: ProgramBlock
fun allocateZeropage(zeropage: Zeropage) { // TODO not used anymore???
// allocates all @zp marked variables on the zeropage (for all blocks, as long as there is space in the ZP)
var notAllocated = 0
for(block in blocks) {
val zpVariables = block.variables.filter { it.params.zp==ZeropageWish.REQUIRE_ZEROPAGE || it.params.zp==ZeropageWish.PREFER_ZEROPAGE }
if (zpVariables.isNotEmpty()) {
for (variable in zpVariables) {
if(variable.params.zp==ZeropageWish.NOT_IN_ZEROPAGE || variable.params.memberOfStruct!=null)
throw CompilerException("zp conflict")
try {
val address = zeropage.allocate(variable.scopedname, variable.value.type, null)
allocatedZeropageVariables[variable.scopedname] = Pair(address, variable.value.type)
} catch (x: ZeropageDepletedError) {
printWarning(x.toString() + " variable ${variable.scopedname} type ${variable.value.type}")
notAllocated++
}
}
}
}
if(notAllocated>0)
printWarning("$notAllocated variables marked for Zeropage could not be allocated there")
}
fun optimize() {
println("Optimizing stackVM code...")
// remove nops (that are not a label)
for (blk in blocks) {
blk.instructions.removeIf { it.opcode== Opcode.NOP && it !is LabelInstr }
}
optimizeDataConversionAndUselessDiscards()
optimizeVariableCopying()
optimizeMultipleSequentialLineInstrs()
optimizeCallReturnIntoJump()
optimizeConditionalBranches()
// todo: add more optimizations to intermediate code!
optimizeRemoveNops() // must be done as the last step
optimizeMultipleSequentialLineInstrs() // once more
optimizeRemoveNops() // once more
}
private fun optimizeConditionalBranches() {
// conditional branches that consume the value on the stack
// sometimes these are just constant values, so we can statically determine the branch
// or, they are preceded by a NOT instruction so we can simply remove that and flip the branch condition
val pushvalue = setOf(Opcode.PUSH_BYTE, Opcode.PUSH_WORD)
val notvalue = setOf(Opcode.NOT_BYTE, Opcode.NOT_WORD)
val branchOpcodes = setOf(Opcode.JZ, Opcode.JNZ, Opcode.JZW, Opcode.JNZW)
for(blk in blocks) {
val instructionsToReplace = mutableMapOf<Int, Instruction>()
blk.instructions.asSequence().withIndex().filter {it.value.opcode!= Opcode.LINE }.windowed(2).toList().forEach {
if (it[1].value.opcode in branchOpcodes) {
if (it[0].value.opcode in pushvalue) {
val value = it[0].value.arg!!.asBoolean
instructionsToReplace[it[0].index] = Instruction(Opcode.NOP)
val replacement: Instruction =
if (value) {
when (it[1].value.opcode) {
Opcode.JNZ -> Instruction(Opcode.JUMP, callLabel = it[1].value.callLabel)
Opcode.JNZW -> Instruction(Opcode.JUMP, callLabel = it[1].value.callLabel)
else -> Instruction(Opcode.NOP)
}
} else {
when (it[1].value.opcode) {
Opcode.JZ -> Instruction(Opcode.JUMP, callLabel = it[1].value.callLabel)
Opcode.JZW -> Instruction(Opcode.JUMP, callLabel = it[1].value.callLabel)
else -> Instruction(Opcode.NOP)
}
}
instructionsToReplace[it[1].index] = replacement
}
else if (it[0].value.opcode in notvalue) {
instructionsToReplace[it[0].index] = Instruction(Opcode.NOP)
val replacement: Instruction =
when (it[1].value.opcode) {
Opcode.JZ -> Instruction(Opcode.JNZ, callLabel = it[1].value.callLabel)
Opcode.JZW -> Instruction(Opcode.JNZW, callLabel = it[1].value.callLabel)
Opcode.JNZ -> Instruction(Opcode.JZ, callLabel = it[1].value.callLabel)
Opcode.JNZW -> Instruction(Opcode.JZW, callLabel = it[1].value.callLabel)
else -> Instruction(Opcode.NOP)
}
instructionsToReplace[it[1].index] = replacement
}
}
}
for (rins in instructionsToReplace) {
blk.instructions[rins.key] = rins.value
}
}
}
private fun optimizeRemoveNops() {
// remove nops (that are not a label)
for (blk in blocks)
blk.instructions.removeIf { it.opcode== Opcode.NOP && it !is LabelInstr }
}
private fun optimizeCallReturnIntoJump() {
// replaces call X followed by return, by jump X
for(blk in blocks) {
val instructionsToReplace = mutableMapOf<Int, Instruction>()
blk.instructions.asSequence().withIndex().filter {it.value.opcode!= Opcode.LINE }.windowed(2).toList().forEach {
if(it[0].value.opcode== Opcode.CALL && it[1].value.opcode== Opcode.RETURN) {
instructionsToReplace[it[1].index] = Instruction(Opcode.JUMP, callLabel = it[0].value.callLabel)
instructionsToReplace[it[0].index] = Instruction(Opcode.NOP)
}
}
for (rins in instructionsToReplace) {
blk.instructions[rins.key] = rins.value
}
}
}
private fun optimizeMultipleSequentialLineInstrs() {
for(blk in blocks) {
val instructionsToReplace = mutableMapOf<Int, Instruction>()
blk.instructions.asSequence().withIndex().windowed(2).toList().forEach {
if (it[0].value.opcode == Opcode.LINE && it[1].value.opcode == Opcode.LINE)
instructionsToReplace[it[0].index] = Instruction(Opcode.NOP)
}
for (rins in instructionsToReplace) {
blk.instructions[rins.key] = rins.value
}
}
}
private fun optimizeVariableCopying() {
for(blk in blocks) {
val instructionsToReplace = mutableMapOf<Int, Instruction>()
blk.instructions.asSequence().withIndex().windowed(2).toList().forEach {
when (it[0].value.opcode) {
Opcode.PUSH_VAR_BYTE ->
if (it[1].value.opcode == Opcode.POP_VAR_BYTE) {
if (it[0].value.callLabel == it[1].value.callLabel) {
instructionsToReplace[it[0].index] = Instruction(Opcode.NOP)
instructionsToReplace[it[1].index] = Instruction(Opcode.NOP)
}
}
Opcode.PUSH_VAR_WORD ->
if (it[1].value.opcode == Opcode.POP_VAR_WORD) {
if (it[0].value.callLabel == it[1].value.callLabel) {
instructionsToReplace[it[0].index] = Instruction(Opcode.NOP)
instructionsToReplace[it[1].index] = Instruction(Opcode.NOP)
}
}
Opcode.PUSH_VAR_FLOAT ->
if (it[1].value.opcode == Opcode.POP_VAR_FLOAT) {
if (it[0].value.callLabel == it[1].value.callLabel) {
instructionsToReplace[it[0].index] = Instruction(Opcode.NOP)
instructionsToReplace[it[1].index] = Instruction(Opcode.NOP)
}
}
Opcode.PUSH_MEM_B, Opcode.PUSH_MEM_UB ->
if(it[1].value.opcode == Opcode.POP_MEM_BYTE) {
if(it[0].value.arg == it[1].value.arg) {
instructionsToReplace[it[0].index] = Instruction(Opcode.NOP)
instructionsToReplace[it[1].index] = Instruction(Opcode.NOP)
}
}
Opcode.PUSH_MEM_W, Opcode.PUSH_MEM_UW ->
if(it[1].value.opcode == Opcode.POP_MEM_WORD) {
if(it[0].value.arg == it[1].value.arg) {
instructionsToReplace[it[0].index] = Instruction(Opcode.NOP)
instructionsToReplace[it[1].index] = Instruction(Opcode.NOP)
}
}
Opcode.PUSH_MEM_FLOAT ->
if(it[1].value.opcode == Opcode.POP_MEM_FLOAT) {
if(it[0].value.arg == it[1].value.arg) {
instructionsToReplace[it[0].index] = Instruction(Opcode.NOP)
instructionsToReplace[it[1].index] = Instruction(Opcode.NOP)
}
}
else -> {}
}
}
for (rins in instructionsToReplace) {
blk.instructions[rins.key] = rins.value
}
}
}
private fun optimizeDataConversionAndUselessDiscards() {
// - push value followed by a data type conversion -> push the value in the correct type and remove the conversion
// - push something followed by a discard -> remove both
val instructionsToReplace = mutableMapOf<Int, Instruction>()
fun optimizeDiscardAfterPush(index0: Int, index1: Int, ins1: Instruction) {
if (ins1.opcode == Opcode.DISCARD_FLOAT || ins1.opcode == Opcode.DISCARD_WORD || ins1.opcode == Opcode.DISCARD_BYTE) {
instructionsToReplace[index0] = Instruction(Opcode.NOP)
instructionsToReplace[index1] = Instruction(Opcode.NOP)
}
}
fun optimizeFloatConversion(index0: Int, index1: Int, ins1: Instruction) {
when (ins1.opcode) {
Opcode.DISCARD_FLOAT -> {
instructionsToReplace[index0] = Instruction(Opcode.NOP)
instructionsToReplace[index1] = Instruction(Opcode.NOP)
}
Opcode.DISCARD_BYTE, Opcode.DISCARD_WORD -> throw CompilerException("invalid discard type following a float")
else -> throw CompilerException("invalid conversion opcode ${ins1.opcode} following a float")
}
}
fun optimizeWordConversion(index0: Int, ins0: Instruction, index1: Int, ins1: Instruction) {
when (ins1.opcode) {
Opcode.CAST_UW_TO_B, Opcode.CAST_W_TO_B -> {
val ins = Instruction(Opcode.PUSH_BYTE, ins0.arg!!.cast(DataType.BYTE))
instructionsToReplace[index0] = ins
instructionsToReplace[index1] = Instruction(Opcode.NOP)
}
Opcode.CAST_W_TO_UB, Opcode.CAST_UW_TO_UB -> {
val ins = Instruction(Opcode.PUSH_BYTE, RuntimeValue(DataType.UBYTE, ins0.arg!!.integerValue() and 255))
instructionsToReplace[index0] = ins
instructionsToReplace[index1] = Instruction(Opcode.NOP)
}
Opcode.MSB -> {
val ins = Instruction(Opcode.PUSH_BYTE, RuntimeValue(DataType.UBYTE, ins0.arg!!.integerValue() ushr 8 and 255))
instructionsToReplace[index0] = ins
instructionsToReplace[index1] = Instruction(Opcode.NOP)
}
Opcode.CAST_W_TO_F, Opcode.CAST_UW_TO_F -> {
val ins = Instruction(Opcode.PUSH_FLOAT, RuntimeValue(DataType.FLOAT, ins0.arg!!.integerValue().toDouble()))
instructionsToReplace[index0] = ins
instructionsToReplace[index1] = Instruction(Opcode.NOP)
}
Opcode.CAST_UW_TO_W -> {
val cv = ins0.arg!!.cast(DataType.WORD)
instructionsToReplace[index0] = Instruction(Opcode.PUSH_WORD, cv)
instructionsToReplace[index1] = Instruction(Opcode.NOP)
}
Opcode.CAST_W_TO_UW -> {
val cv = ins0.arg!!.cast(DataType.UWORD)
instructionsToReplace[index0] = Instruction(Opcode.PUSH_WORD, cv)
instructionsToReplace[index1] = Instruction(Opcode.NOP)
}
Opcode.DISCARD_WORD -> {
instructionsToReplace[index0] = Instruction(Opcode.NOP)
instructionsToReplace[index1] = Instruction(Opcode.NOP)
}
Opcode.DISCARD_BYTE, Opcode.DISCARD_FLOAT -> throw CompilerException("invalid discard type following a byte")
else -> throw CompilerException("invalid conversion opcode ${ins1.opcode} following a word")
}
}
fun optimizeByteConversion(index0: Int, ins0: Instruction, index1: Int, ins1: Instruction) {
when (ins1.opcode) {
Opcode.CAST_B_TO_UB, Opcode.CAST_UB_TO_B,
Opcode.CAST_W_TO_B, Opcode.CAST_W_TO_UB,
Opcode.CAST_UW_TO_B, Opcode.CAST_UW_TO_UB -> instructionsToReplace[index1] = Instruction(Opcode.NOP)
Opcode.MSB -> throw CompilerException("msb of a byte")
Opcode.CAST_UB_TO_UW -> {
val ins = Instruction(Opcode.PUSH_WORD, RuntimeValue(DataType.UWORD, ins0.arg!!.integerValue()))
instructionsToReplace[index0] = ins
instructionsToReplace[index1] = Instruction(Opcode.NOP)
}
Opcode.CAST_B_TO_W -> {
val ins = Instruction(Opcode.PUSH_WORD, RuntimeValue(DataType.WORD, ins0.arg!!.integerValue()))
instructionsToReplace[index0] = ins
instructionsToReplace[index1] = Instruction(Opcode.NOP)
}
Opcode.CAST_B_TO_UW -> {
val ins = Instruction(Opcode.PUSH_WORD, ins0.arg!!.cast(DataType.UWORD))
instructionsToReplace[index0] = ins
instructionsToReplace[index1] = Instruction(Opcode.NOP)
}
Opcode.CAST_UB_TO_W -> {
val ins = Instruction(Opcode.PUSH_WORD, ins0.arg!!.cast(DataType.WORD))
instructionsToReplace[index0] = ins
instructionsToReplace[index1] = Instruction(Opcode.NOP)
}
Opcode.CAST_B_TO_F, Opcode.CAST_UB_TO_F -> {
val ins = Instruction(Opcode.PUSH_FLOAT, RuntimeValue(DataType.FLOAT, ins0.arg!!.integerValue().toDouble()))
instructionsToReplace[index0] = ins
instructionsToReplace[index1] = Instruction(Opcode.NOP)
}
Opcode.CAST_W_TO_F, Opcode.CAST_UW_TO_F -> throw CompilerException("invalid conversion following a byte")
Opcode.DISCARD_BYTE -> {
instructionsToReplace[index0] = Instruction(Opcode.NOP)
instructionsToReplace[index1] = Instruction(Opcode.NOP)
}
Opcode.DISCARD_WORD, Opcode.DISCARD_FLOAT -> throw CompilerException("invalid discard type following a byte")
Opcode.MKWORD -> {}
else -> throw CompilerException("invalid conversion opcode ${ins1.opcode}")
}
}
for(blk in blocks) {
instructionsToReplace.clear()
val typeConversionOpcodes = setOf(
Opcode.MSB,
Opcode.MKWORD,
Opcode.CAST_UB_TO_B,
Opcode.CAST_UB_TO_UW,
Opcode.CAST_UB_TO_W,
Opcode.CAST_UB_TO_F,
Opcode.CAST_B_TO_UB,
Opcode.CAST_B_TO_UW,
Opcode.CAST_B_TO_W,
Opcode.CAST_B_TO_F,
Opcode.CAST_UW_TO_UB,
Opcode.CAST_UW_TO_B,
Opcode.CAST_UW_TO_W,
Opcode.CAST_UW_TO_F,
Opcode.CAST_W_TO_UB,
Opcode.CAST_W_TO_B,
Opcode.CAST_W_TO_UW,
Opcode.CAST_W_TO_F,
Opcode.CAST_F_TO_UB,
Opcode.CAST_F_TO_B,
Opcode.CAST_F_TO_UW,
Opcode.CAST_F_TO_W,
Opcode.DISCARD_BYTE,
Opcode.DISCARD_WORD,
Opcode.DISCARD_FLOAT
)
blk.instructions.asSequence().withIndex().windowed(2).toList().forEach {
if (it[1].value.opcode in typeConversionOpcodes) {
when (it[0].value.opcode) {
Opcode.PUSH_BYTE -> optimizeByteConversion(it[0].index, it[0].value, it[1].index, it[1].value)
Opcode.PUSH_WORD -> optimizeWordConversion(it[0].index, it[0].value, it[1].index, it[1].value)
Opcode.PUSH_FLOAT -> optimizeFloatConversion(it[0].index, it[1].index, it[1].value)
Opcode.PUSH_VAR_FLOAT,
Opcode.PUSH_VAR_WORD,
Opcode.PUSH_VAR_BYTE,
Opcode.PUSH_MEM_B, Opcode.PUSH_MEM_UB,
Opcode.PUSH_MEM_W, Opcode.PUSH_MEM_UW,
Opcode.PUSH_MEM_FLOAT -> optimizeDiscardAfterPush(it[0].index, it[1].index, it[1].value)
else -> {
}
}
}
}
for (rins in instructionsToReplace) {
blk.instructions[rins.key] = rins.value
}
}
}
fun variable(scopedname: String, decl: VarDecl) {
when(decl.type) {
VarDeclType.VAR -> {
// var decls that are defined inside of a StructDecl are skipped in the output
// because every occurrence of the members will have a separate mangled vardecl for that occurrence
if(decl.parent is StructDecl)
return
val valueparams = VariableParameters(decl.zeropage, decl.struct)
val value = when(decl.datatype) {
in NumericDatatypes -> {
RuntimeValue(decl.datatype, (decl.value as NumericLiteralValue).number)
}
in StringDatatypes -> {
val litval = (decl.value as ReferenceLiteralValue)
if(litval.heapId==null)
throw CompilerException("string should already be in the heap")
RuntimeValue(decl.datatype, heapId = litval.heapId)
}
in ArrayDatatypes -> {
val litval = (decl.value as? ReferenceLiteralValue)
if(litval!=null && litval.heapId==null)
throw CompilerException("array should already be in the heap")
if(litval!=null){
RuntimeValue(decl.datatype, heapId = litval.heapId)
} else {
throw CompilerException("initialization value expected")
}
}
DataType.STRUCT -> {
// struct variables have been flattened already
return
}
else -> throw CompilerException("weird datatype")
}
currentBlock.variables.add(Variable(scopedname, value, valueparams))
}
VarDeclType.MEMORY -> {
// note that constants are all folded away, but assembly code may still refer to them
val lv = decl.value as NumericLiteralValue
if(lv.type!= DataType.UWORD && lv.type!= DataType.UBYTE)
throw CompilerException("expected integer memory address $lv")
currentBlock.memoryPointers[scopedname] = Pair(lv.number.toInt(), decl.datatype)
}
VarDeclType.CONST -> {
// note that constants are all folded away, but assembly code may still refer to them (if their integers)
// floating point constants are not generated at all!!
val lv = decl.value as NumericLiteralValue
if(lv.type in IntegerDatatypes)
currentBlock.memoryPointers[scopedname] = Pair(lv.number.toInt(), decl.datatype)
}
}
}
fun instr(opcode: Opcode, arg: RuntimeValue? = null, arg2: RuntimeValue? = null, callLabel: String? = null, callLabel2: String? = null) {
currentBlock.instructions.add(Instruction(opcode, arg, arg2, callLabel, callLabel2))
}
fun label(labelname: String, asmProc: Boolean=false) {
val instr = LabelInstr(labelname, asmProc)
currentBlock.instructions.add(instr)
currentBlock.labels[labelname] = instr
}
fun line(position: Position) {
currentBlock.instructions.add(Instruction(Opcode.LINE, callLabel = "${position.line} ${position.file}"))
}
fun removeLastInstruction() {
currentBlock.instructions.removeAt(currentBlock.instructions.lastIndex)
}
fun memoryPointer(name: String, address: Int, datatype: DataType) {
currentBlock.memoryPointers[name] = Pair(address, datatype)
}
fun newBlock(name: String, address: Int?, options: Set<String>) {
currentBlock = ProgramBlock(name, address, force_output = "force_output" in options)
blocks.add(currentBlock)
}
fun writeCode(out: PrintStream, embeddedLabels: Boolean=true) {
out.println("; stackVM program code for '$name'")
writeMemory(out)
writeHeap(out)
for(blk in blocks) {
writeBlock(out, blk, embeddedLabels)
}
}
private fun writeHeap(out: PrintStream) {
out.println("%heap")
heap.allEntries().forEach {
out.print("${it.key} ${it.value.type.name.toLowerCase()} ")
when {
it.value.str!=null ->
out.println("\"${escape(it.value.str!!)}\"")
it.value.array!=null -> {
// this array can contain both normal integers, and pointer values
val arrayvalues = it.value.array!!.map { av ->
when {
av.integer!=null -> av.integer.toString()
av.addressOf!=null -> {
if(av.addressOf.scopedname==null)
throw CompilerException("AddressOf scopedname should have been set")
else
"&${av.addressOf.scopedname}"
}
else -> throw CompilerException("weird array value")
}
}
out.println(arrayvalues)
}
it.value.doubleArray!=null ->
out.println(it.value.doubleArray!!.toList())
else -> throw CompilerException("invalid heap entry $it")
}
}
out.println("%end_heap")
}
private fun writeBlock(out: PrintStream, blk: ProgramBlock, embeddedLabels: Boolean) {
out.println("\n%block ${blk.name} ${blk.address?.toString(16) ?: ""}")
out.println("%variables")
for (variable in blk.variables) {
if(variable.params.zp==ZeropageWish.REQUIRE_ZEROPAGE)
throw CompilerException("zp conflict")
val valuestr = variable.value.toString()
val struct = if(variable.params.memberOfStruct==null) "" else "struct=${variable.params.memberOfStruct.name}"
out.println("${variable.scopedname} ${variable.value.type.name.toLowerCase()} $valuestr zp=${variable.params.zp} s=$struct")
}
out.println("%end_variables")
out.println("%memorypointers")
for (iconst in blk.memoryPointers) {
out.println("${iconst.key} ${iconst.value.second.name.toLowerCase()} uw:${iconst.value.first.toString(16)}")
}
out.println("%end_memorypointers")
out.println("%instructions")
val labels = blk.labels.entries.associateBy({ it.value }) { it.key }
for (instr in blk.instructions) {
if (!embeddedLabels) {
val label = labels[instr]
if (label != null)
out.println("$label:")
} else {
out.println(instr)
}
}
out.println("%end_instructions")
out.println("%end_block")
}
private fun writeMemory(out: PrintStream) {
out.println("%memory")
if (memory.isNotEmpty())
TODO("add support for writing/reading initial memory values")
out.println("%end_memory")
}
}
@@ -1,291 +0,0 @@
package compiler.intermediate
enum class Opcode {
// pushing values on the (evaluation) stack
PUSH_BYTE, // push byte value
PUSH_WORD, // push word value (or 'address' of string / array)
PUSH_FLOAT, // push float value
PUSH_MEM_B, // push byte value from memory to stack
PUSH_MEM_UB, // push unsigned byte value from memory to stack
PUSH_MEM_W, // push word value from memory to stack
PUSH_MEM_UW, // push unsigned word value from memory to stack
PUSH_MEM_FLOAT, // push float value from memory to stack
PUSH_MEMREAD, // push memory value from address that's on the stack
PUSH_VAR_BYTE, // push byte variable (ubyte, byte)
PUSH_VAR_WORD, // push word variable (uword, word)
PUSH_VAR_FLOAT, // push float variable
PUSH_REGAX_WORD, // push registers A/X as a 16-bit word
PUSH_REGAY_WORD, // push registers A/Y as a 16-bit word
PUSH_REGXY_WORD, // push registers X/Y as a 16-bit word
PUSH_ADDR_HEAPVAR, // push the address of the variable that's on the heap (string or array)
DUP_B, // duplicate the top byte on the stack
DUP_W, // duplicate the top word on the stack
// popping values off the (evaluation) stack, possibly storing them in another location
DISCARD_BYTE, // discard top byte value
DISCARD_WORD, // discard top word value
DISCARD_FLOAT, // discard top float value
POP_MEM_BYTE, // pop (u)byte value into destination memory address
POP_MEM_WORD, // pop (u)word value into destination memory address
POP_MEM_FLOAT, // pop float value into destination memory address
POP_MEMWRITE, // pop address and byte stack and write the byte to the memory address
POP_VAR_BYTE, // pop (u)byte value into variable
POP_VAR_WORD, // pop (u)word value into variable
POP_VAR_FLOAT, // pop float value into variable
POP_REGAX_WORD, // pop uword from stack into A/X registers
POP_REGAY_WORD, // pop uword from stack into A/Y registers
POP_REGXY_WORD, // pop uword from stack into X/Y registers
// numeric arithmetic
ADD_UB,
ADD_B,
ADD_UW,
ADD_W,
ADD_F,
SUB_UB,
SUB_B,
SUB_UW,
SUB_W,
SUB_F,
MUL_UB,
MUL_B,
MUL_UW,
MUL_W,
MUL_F,
IDIV_UB,
IDIV_B,
IDIV_UW,
IDIV_W,
DIV_F,
REMAINDER_UB, // signed remainder is undefined/unimplemented
REMAINDER_UW, // signed remainder is undefined/unimplemented
POW_F,
NEG_B,
NEG_W,
NEG_F,
ABS_B,
ABS_W,
ABS_F,
// bit shifts and bitwise arithmetic
SHIFTEDL_BYTE, // shifts stack value rather than in-place mem/var
SHIFTEDL_WORD, // shifts stack value rather than in-place mem/var
SHIFTEDR_UBYTE, // shifts stack value rather than in-place mem/var
SHIFTEDR_SBYTE, // shifts stack value rather than in-place mem/var
SHIFTEDR_UWORD, // shifts stack value rather than in-place mem/var
SHIFTEDR_SWORD, // shifts stack value rather than in-place mem/var
SHL_BYTE,
SHL_WORD,
SHL_MEM_BYTE,
SHL_MEM_WORD,
SHL_VAR_BYTE,
SHL_VAR_WORD,
SHR_UBYTE,
SHR_SBYTE,
SHR_UWORD,
SHR_SWORD,
SHR_MEM_UBYTE,
SHR_MEM_SBYTE,
SHR_MEM_UWORD,
SHR_MEM_SWORD,
SHR_VAR_UBYTE,
SHR_VAR_SBYTE,
SHR_VAR_UWORD,
SHR_VAR_SWORD,
ROL_BYTE,
ROL_WORD,
ROL_MEM_BYTE,
ROL_MEM_WORD,
ROL_VAR_BYTE,
ROL_VAR_WORD,
ROR_BYTE,
ROR_WORD,
ROR_MEM_BYTE,
ROR_MEM_WORD,
ROR_VAR_BYTE,
ROR_VAR_WORD,
ROL2_BYTE,
ROL2_WORD,
ROL2_MEM_BYTE,
ROL2_MEM_WORD,
ROL2_VAR_BYTE,
ROL2_VAR_WORD,
ROR2_BYTE,
ROR2_WORD,
ROR2_MEM_BYTE,
ROR2_MEM_WORD,
ROR2_VAR_BYTE,
ROR2_VAR_WORD,
BITAND_BYTE,
BITAND_WORD,
BITOR_BYTE,
BITOR_WORD,
BITXOR_BYTE,
BITXOR_WORD,
INV_BYTE,
INV_WORD,
// numeric type conversions
MSB, // note: lsb is equivalent to CAST_UW_TO_UB or CAST_W_TO_UB
MKWORD, // create a word from lsb + msb
CAST_UB_TO_B,
CAST_UB_TO_UW,
CAST_UB_TO_W,
CAST_UB_TO_F,
CAST_B_TO_UB,
CAST_B_TO_UW,
CAST_B_TO_W,
CAST_B_TO_F,
CAST_W_TO_UB,
CAST_W_TO_B,
CAST_W_TO_UW,
CAST_W_TO_F,
CAST_UW_TO_UB,
CAST_UW_TO_B,
CAST_UW_TO_W,
CAST_UW_TO_F,
CAST_F_TO_UB,
CAST_F_TO_B,
CAST_F_TO_UW,
CAST_F_TO_W,
// logical operations
AND_BYTE,
AND_WORD,
OR_BYTE,
OR_WORD,
XOR_BYTE,
XOR_WORD,
NOT_BYTE,
NOT_WORD,
// increment, decrement
INC_VAR_B,
INC_VAR_UB,
INC_VAR_W,
INC_VAR_UW,
INC_VAR_F,
DEC_VAR_B,
DEC_VAR_UB,
DEC_VAR_W,
DEC_VAR_UW,
DEC_VAR_F,
INC_MEMORY, // increment direct address
DEC_MEMORY, // decrement direct address
POP_INC_MEMORY, // increment address from stack
POP_DEC_MEMORY, // decrement address from address
// comparisons
LESS_B,
LESS_UB,
LESS_W,
LESS_UW,
LESS_F,
GREATER_B,
GREATER_UB,
GREATER_W,
GREATER_UW,
GREATER_F,
LESSEQ_B,
LESSEQ_UB,
LESSEQ_W,
LESSEQ_UW,
LESSEQ_F,
GREATEREQ_B,
GREATEREQ_UB,
GREATEREQ_W,
GREATEREQ_UW,
GREATEREQ_F,
EQUAL_BYTE,
EQUAL_WORD,
EQUAL_F,
NOTEQUAL_BYTE,
NOTEQUAL_WORD,
NOTEQUAL_F,
CMP_B, // sets processor status flags based on comparison, instead of pushing a result value
CMP_UB, // sets processor status flags based on comparison, instead of pushing a result value
CMP_W, // sets processor status flags based on comparison, instead of pushing a result value
CMP_UW, // sets processor status flags based on comparison, instead of pushing a result value
// array access and simple manipulations
READ_INDEXED_VAR_BYTE,
READ_INDEXED_VAR_WORD,
READ_INDEXED_VAR_FLOAT,
WRITE_INDEXED_VAR_BYTE,
WRITE_INDEXED_VAR_WORD,
WRITE_INDEXED_VAR_FLOAT,
INC_INDEXED_VAR_B,
INC_INDEXED_VAR_UB,
INC_INDEXED_VAR_W,
INC_INDEXED_VAR_UW,
INC_INDEXED_VAR_FLOAT,
DEC_INDEXED_VAR_B,
DEC_INDEXED_VAR_UB,
DEC_INDEXED_VAR_W,
DEC_INDEXED_VAR_UW,
DEC_INDEXED_VAR_FLOAT,
// branching, without consuming a value from the stack
JUMP,
BCS, // branch if carry set
BCC, // branch if carry clear
BZ, // branch if zero flag
BNZ, // branch if not zero flag
BNEG, // branch if negative flag
BPOS, // branch if not negative flag
BVS, // branch if overflow flag
BVC, // branch if not overflow flag
// branching, based on value on the stack (which is consumed)
JZ, // branch if value is zero (byte)
JNZ, // branch if value is not zero (byte)
JZW, // branch if value is zero (word)
JNZW, // branch if value is not zero (word)
// subroutines
CALL,
RETURN,
SYSCALL,
START_PROCDEF,
END_PROCDEF,
// misc
SEC, // set carry status flag NOTE: is mostly fake, carry flag is not affected by any numeric operations
CLC, // clear carry status flag NOTE: is mostly fake, carry flag is not affected by any numeric operations
SEI, // set irq-disable status flag
CLI, // clear irq-disable status flag
CARRY_TO_A, // load var/register A with carry status bit
RSAVE, // save all internal registers and status flags
RSAVEX, // save just X (the evaluation stack pointer)
RRESTORE, // restore all internal registers and status flags
RRESTOREX, // restore just X (the evaluation stack pointer)
NOP, // do nothing
BREAKPOINT, // breakpoint
TERMINATE, // end the program
LINE, // track source file line number
INLINE_ASSEMBLY, // container to hold inline raw assembly code
INCLUDE_FILE // directive to include a file at this position in the memory of the program
}
val opcodesWithVarArgument = setOf(
Opcode.INC_VAR_B, Opcode.INC_VAR_W, Opcode.DEC_VAR_B, Opcode.DEC_VAR_W,
Opcode.INC_VAR_UB, Opcode.INC_VAR_UW, Opcode.DEC_VAR_UB, Opcode.DEC_VAR_UW,
Opcode.SHR_VAR_SBYTE, Opcode.SHR_VAR_UBYTE, Opcode.SHR_VAR_SWORD, Opcode.SHR_VAR_UWORD,
Opcode.SHL_VAR_BYTE, Opcode.SHL_VAR_WORD,
Opcode.ROL_VAR_BYTE, Opcode.ROL_VAR_WORD, Opcode.ROR_VAR_BYTE, Opcode.ROR_VAR_WORD,
Opcode.ROL2_VAR_BYTE, Opcode.ROL2_VAR_WORD, Opcode.ROR2_VAR_BYTE, Opcode.ROR2_VAR_WORD,
Opcode.POP_VAR_BYTE, Opcode.POP_VAR_WORD, Opcode.POP_VAR_FLOAT,
Opcode.PUSH_VAR_BYTE, Opcode.PUSH_VAR_WORD, Opcode.PUSH_VAR_FLOAT, Opcode.PUSH_ADDR_HEAPVAR,
Opcode.READ_INDEXED_VAR_BYTE, Opcode.READ_INDEXED_VAR_WORD, Opcode.READ_INDEXED_VAR_FLOAT,
Opcode.WRITE_INDEXED_VAR_BYTE, Opcode.WRITE_INDEXED_VAR_WORD, Opcode.WRITE_INDEXED_VAR_FLOAT,
Opcode.INC_INDEXED_VAR_UB, Opcode.INC_INDEXED_VAR_B, Opcode.INC_INDEXED_VAR_UW,
Opcode.INC_INDEXED_VAR_W, Opcode.INC_INDEXED_VAR_FLOAT,
Opcode.DEC_INDEXED_VAR_UB, Opcode.DEC_INDEXED_VAR_B, Opcode.DEC_INDEXED_VAR_UW,
Opcode.DEC_INDEXED_VAR_W, Opcode.DEC_INDEXED_VAR_FLOAT
)
val branchOpcodes = setOf(
Opcode.BCS, Opcode.BCC, Opcode.BZ, Opcode.BNZ,
Opcode.BNEG, Opcode.BPOS, Opcode.BVS, Opcode.BVC
)
@@ -1,47 +0,0 @@
package prog8.vm.stackvm
import prog8.printSoftwareHeader
import prog8.vm.astvm.ScreenDialog
import java.awt.EventQueue
import javax.swing.Timer
import kotlin.system.exitProcess
fun main(args: Array<String>) {
stackVmMain(args)
}
fun stackVmMain(args: Array<String>) {
printSoftwareHeader("StackVM")
if(args.size != 1) {
System.err.println("requires one argument: name of stackvm sourcecode file")
exitProcess(1)
}
val program = Program.load(args.first())
val vm = StackVm(traceOutputFile = null)
val dialog = ScreenDialog("StackVM")
vm.load(program, dialog.canvas)
EventQueue.invokeLater {
dialog.pack()
dialog.isVisible = true
dialog.start()
val programTimer = Timer(10) { a ->
try {
vm.step()
} catch(bp: VmBreakpointException) {
println("Breakpoint: execution halted. Press enter to resume.")
readLine()
} catch (tx: VmTerminationException) {
println("Execution halted: ${tx.message}")
(a.source as Timer).stop()
}
}
val irqTimer = Timer(1000/60) { a -> vm.irq(a.`when`) }
programTimer.start()
irqTimer.start()
}
}
@@ -1,302 +0,0 @@
package prog8.vm.stackvm
import prog8.ast.antlr.unescape
import prog8.ast.base.*
import prog8.ast.expressions.AddressOf
import prog8.ast.expressions.IdentifierReference
import prog8.compiler.HeapValues
import prog8.compiler.IntegerOrAddressOf
import prog8.compiler.intermediate.Instruction
import prog8.compiler.intermediate.LabelInstr
import prog8.compiler.intermediate.Opcode
import prog8.compiler.intermediate.opcodesWithVarArgument
import prog8.vm.RuntimeValue
import java.io.File
import java.util.*
import java.util.regex.Pattern
class Program (val name: String,
val program: MutableList<Instruction>,
val variables: Map<String, RuntimeValue>,
val memoryPointers: Map<String, Pair<Int, DataType>>,
val labels: Map<String, Int>,
val memory: Map<Int, List<RuntimeValue>>,
val heap: HeapValues)
{
init {
// add end of program marker and some sentinel instructions, to correctly connect all others
program.add(LabelInstr("____program_end", false))
program.add(Instruction(Opcode.TERMINATE))
program.add(Instruction(Opcode.NOP))
}
companion object {
fun load(filename: String): Program {
val lines = File(filename).readLines().withIndex().iterator()
val memory = mutableMapOf<Int, List<RuntimeValue>>()
val heap = HeapValues()
val program = mutableListOf<Instruction>()
val variables = mutableMapOf<String, RuntimeValue>()
val memoryPointers = mutableMapOf<String, Pair<Int, DataType>>()
val labels = mutableMapOf<String, Int>()
while(lines.hasNext()) {
val (lineNr, line) = lines.next()
if(line.startsWith(';') || line.isEmpty())
continue
else if(line=="%memory")
loadMemory(lines, memory)
else if(line=="%heap")
loadHeap(lines, heap)
else if(line.startsWith("%block "))
loadBlock(lines, heap, program, variables, memoryPointers, labels)
else throw VmExecutionException("syntax error at line ${lineNr + 1}")
}
return Program(filename, program, variables, memoryPointers, labels, memory, heap)
}
private fun loadBlock(lines: Iterator<IndexedValue<String>>,
heap: HeapValues,
program: MutableList<Instruction>,
variables: MutableMap<String, RuntimeValue>,
memoryPointers: MutableMap<String, Pair<Int, DataType>>,
labels: MutableMap<String, Int>)
{
while(true) {
val (_, line) = lines.next()
if(line.isEmpty())
continue
else if(line=="%end_block")
return
else if(line=="%variables")
loadVars(lines, variables)
else if(line=="%memorypointers")
loadMemoryPointers(lines, memoryPointers, heap)
else if(line=="%instructions") {
val (blockInstructions, blockLabels) = loadInstructions(lines, heap)
val baseIndex = program.size
program.addAll(blockInstructions)
val labelsWithIndex = blockLabels.mapValues { baseIndex+blockInstructions.indexOf(it.value) }
labels.putAll(labelsWithIndex)
}
}
}
private fun loadHeap(lines: Iterator<IndexedValue<String>>, heap: HeapValues) {
val splitpattern = Pattern.compile("\\s+")
val heapvalues = mutableListOf<Triple<Int, DataType, String>>()
while(true) {
val (_, line) = lines.next()
if (line == "%end_heap")
break
val parts = line.split(splitpattern, limit=3)
val value = Triple(parts[0].toInt(), DataType.valueOf(parts[1].toUpperCase()), parts[2])
heapvalues.add(value)
}
heapvalues.sortedBy { it.first }.forEach {
when(it.second) {
DataType.STR, DataType.STR_S -> heap.addString(it.second, unescape(it.third.substring(1, it.third.length - 1), Position("<stackvmsource>", 0, 0, 0)))
DataType.ARRAY_UB, DataType.ARRAY_B,
DataType.ARRAY_UW, DataType.ARRAY_W -> {
val numbers = it.third.substring(1, it.third.length-1).split(',')
val intarray = numbers.map{number->
val num=number.trim()
if(num.startsWith("&")) {
// it's AddressOf
val scopedname = num.substring(1)
val iref = IdentifierReference(scopedname.split('.'), Position("<intermediate>", 0, 0, 0))
val addrOf = AddressOf(iref, Position("<intermediate>", 0, 0, 0))
addrOf.scopedname=scopedname
IntegerOrAddressOf(null, addrOf)
} else {
IntegerOrAddressOf(num.toInt(), null)
}
}.toTypedArray()
heap.addIntegerArray(it.second, intarray)
}
DataType.ARRAY_F -> {
val numbers = it.third.substring(1, it.third.length-1).split(',')
val doublearray = numbers.map{number->number.trim().toDouble()}.toDoubleArray()
heap.addDoublesArray(doublearray)
}
in NumericDatatypes -> throw VmExecutionException("invalid heap value type ${it.second}")
else -> throw VmExecutionException("weird datatype")
}
}
}
private fun loadInstructions(lines: Iterator<IndexedValue<String>>, heap: HeapValues): Pair<MutableList<Instruction>, Map<String, Instruction>> {
val instructions = mutableListOf<Instruction>()
val labels = mutableMapOf<String, Instruction>()
val splitpattern = Pattern.compile("\\s+")
val nextInstructionLabels = Stack<String>() // more than one label can occur on the isSameAs line
while(true) {
val (lineNr, line) = lines.next()
if(line.isEmpty())
continue
if(line=="%end_instructions")
return Pair(instructions, labels)
if(!line.startsWith(' ') && line.endsWith(':')) {
nextInstructionLabels.push(line.substring(0, line.length-1))
} else if(line.startsWith(' ')) {
val parts = line.trimStart().split(splitpattern, limit = 2)
val opcodeStr = parts[0].toUpperCase()
val opcode= Opcode.valueOf(if(opcodeStr.startsWith('_')) opcodeStr.substring(1) else opcodeStr)
val args = if(parts.size==2) parts[1] else null
val instruction = when(opcode) {
Opcode.LINE -> Instruction(opcode, null, callLabel = args)
Opcode.JUMP, Opcode.CALL, Opcode.BNEG, Opcode.BPOS,
Opcode.BZ, Opcode.BNZ, Opcode.BCS, Opcode.BCC,
Opcode.JZ, Opcode.JNZ, Opcode.JZW, Opcode.JNZW -> {
if(args!!.startsWith('$')) {
Instruction(opcode, RuntimeValue(DataType.UWORD, args.substring(1).toInt(16)))
} else {
Instruction(opcode, callLabel = args)
}
}
in opcodesWithVarArgument -> {
val withoutQuotes =
if(args!!.startsWith('"') && args.endsWith('"'))
args.substring(1, args.length-1) else args
Instruction(opcode, callLabel = withoutQuotes)
}
Opcode.SYSCALL -> {
if(args!! in syscallNames) {
val call = Syscall.valueOf(args)
Instruction(opcode, RuntimeValue(DataType.UBYTE, call.callNr))
} else {
val args2 = args.replace('.', '_')
if(args2 in syscallNames) {
val call = Syscall.valueOf(args2)
Instruction(opcode, RuntimeValue(DataType.UBYTE, call.callNr))
} else {
// the syscall is not yet implemented. emit a stub.
Instruction(Opcode.SYSCALL, RuntimeValue(DataType.UBYTE, Syscall.SYSCALLSTUB.callNr), callLabel = args2)
}
}
}
Opcode.INCLUDE_FILE -> {
val argparts = args!!.split(' ')
val filename = argparts[0]
val offset = if(argparts.size>=2 && argparts[1]!="null") getArgValue(argparts[1], heap) else null
val length = if(argparts.size>=3 && argparts[2]!="null") getArgValue(argparts[2], heap) else null
Instruction(opcode, offset, length, filename)
}
else -> {
Instruction(opcode, getArgValue(args, heap))
}
}
instructions.add(instruction)
while(nextInstructionLabels.isNotEmpty()) {
val label = nextInstructionLabels.pop()
labels[label] = instruction
}
} else throw VmExecutionException("syntax error at line ${lineNr + 1}")
}
}
private fun getArgValue(args: String?, heap: HeapValues): RuntimeValue? {
if(args==null)
return null
if(args[0]=='"' && args[args.length-1]=='"') {
throw VmExecutionException("encountered a string arg value, but all strings should already have been moved into the heap")
}
val (type, valueStr) = args.split(':')
return when(type) {
"b" -> RuntimeValue(DataType.BYTE, valueStr.toShort(16))
"ub" -> RuntimeValue(DataType.UBYTE, valueStr.toShort(16))
"w" -> RuntimeValue(DataType.WORD, valueStr.toInt(16))
"uw" -> RuntimeValue(DataType.UWORD, valueStr.toInt(16))
"f" -> RuntimeValue(DataType.FLOAT, valueStr.toDouble())
"heap" -> {
val heapId = valueStr.toInt()
RuntimeValue(heap.get(heapId).type, heapId = heapId)
}
else -> throw VmExecutionException("invalid datatype $type")
}
}
private fun loadVars(lines: Iterator<IndexedValue<String>>,
vars: MutableMap<String, RuntimeValue>) {
val splitpattern = Pattern.compile("\\s+")
while(true) {
val (_, line) = lines.next()
if(line=="%end_variables")
return
val (name, typeStr, valueStr) = line.split(splitpattern, limit = 3)
if(valueStr[0] !='"' && ':' !in valueStr)
throw VmExecutionException("missing value type character")
val value = when(val type = DataType.valueOf(typeStr.toUpperCase())) {
DataType.UBYTE -> RuntimeValue(DataType.UBYTE, valueStr.substring(3).substringBefore(' ').toShort(16))// TODO process ZP and struct info?
DataType.BYTE -> RuntimeValue(DataType.BYTE, valueStr.substring(2).substringBefore(' ').toShort(16))// TODO process ZP and struct info?
DataType.UWORD -> RuntimeValue(DataType.UWORD, valueStr.substring(3).substringBefore(' ').toInt(16))// TODO process ZP and struct info?
DataType.WORD -> RuntimeValue(DataType.WORD, valueStr.substring(2).substringBefore(' ').toInt(16))// TODO process ZP and struct info?
DataType.FLOAT -> RuntimeValue(DataType.FLOAT, valueStr.substring(2).substringBefore(' ').toDouble())// TODO process ZP and struct info?
in StringDatatypes -> {
if(valueStr.startsWith('"') && valueStr.endsWith('"'))
throw VmExecutionException("encountered a var with a string value, but all string values should already have been moved into the heap")
else if(!valueStr.startsWith("heap:"))
throw VmExecutionException("invalid string value, should be a heap reference")
else {
val heapId = valueStr.substring(5).substringBefore(' ').toInt() // TODO process ZP and struct info?
RuntimeValue(type, heapId = heapId)
}
}
in ArrayDatatypes -> {
if(!valueStr.startsWith("heap:"))
throw VmExecutionException("invalid array value, should be a heap reference")
else {
val heapId = valueStr.substring(5).substringBefore(' ').toInt() // TODO process ZP and struct info?
RuntimeValue(type, heapId = heapId)
}
}
else -> throw VmExecutionException("weird datatype")
}
vars[name] = value
}
}
private fun loadMemoryPointers(lines: Iterator<IndexedValue<String>>,
pointers: MutableMap<String, Pair<Int, DataType>>,
heap: HeapValues) {
val splitpattern = Pattern.compile("\\s+")
while(true) {
val (_, line) = lines.next()
if(line=="%end_memorypointers")
return
val (name, typeStr, valueStr) = line.split(splitpattern, limit = 3)
if(valueStr[0] !='"' && ':' !in valueStr)
throw VmExecutionException("missing value type character")
val type = DataType.valueOf(typeStr.toUpperCase())
val value = getArgValue(valueStr, heap)!!.integerValue()
pointers[name] = Pair(value, type)
}
}
private fun loadMemory(lines: Iterator<IndexedValue<String>>, memory: MutableMap<Int, List<RuntimeValue>>): Map<Int, List<RuntimeValue>> {
while(true) {
val (lineNr, line) = lines.next()
if(line=="%end_memory")
return memory
val address = line.substringBefore(' ').toInt(16)
val rest = line.substringAfter(' ').trim()
if(rest.startsWith('"')) {
TODO("memory init with char/string")
} else {
val valueStrings = rest.split(' ')
val values = mutableListOf<RuntimeValue>()
valueStrings.forEach {
when(it.length) {
2 -> values.add(RuntimeValue(DataType.UBYTE, it.toShort(16)))
4 -> values.add(RuntimeValue(DataType.UWORD, it.toInt(16)))
else -> throw VmExecutionException("invalid value at line $lineNr+1")
}
}
memory[address] = values
}
}
}
}
}
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File renamed without changes.
@@ -1,4 +1,6 @@
package compiler.target.c64.codegen
package oldcodegen
/** OLD STACK-VM CODE GEN -- NO LONGER USED **/
// note: to put stuff on the stack, we use Absolute,X addressing mode which is 3 bytes / 4 cycles
// possible space optimization is to use zeropage (indirect),Y which is 2 bytes, but 5 cycles
@@ -17,7 +19,6 @@ import prog8.compiler.target.c64.MachineDefinition
import prog8.compiler.target.c64.Petscii
import prog8.vm.RuntimeValue
import java.io.File
import java.util.*
import kotlin.math.abs
@@ -1,4 +1,7 @@
package compiler.target.c64.codegen
package oldcodegen
/** OLD STACK-VM CODE GEN -- NO LONGER USED **/
import prog8.ast.base.printWarning
import prog8.compiler.intermediate.Instruction
@@ -1,4 +1,7 @@
package compiler.target.c64.codegen
package oldcodegen
/** OLD STACK-VM CODE GEN -- NO LONGER USED **/
import prog8.compiler.CompilerException
import prog8.compiler.intermediate.Instruction
+5 -2
View File
@@ -15,11 +15,14 @@ plugins {
apply plugin: "kotlin"
apply plugin: "java"
targetCompatibility = 1.8
sourceCompatibility = 1.8
repositories {
mavenLocal()
mavenCentral()
jcenter()
maven { url "https://dl.bintray.com/orangy/maven/" }
}
def prog8version = rootProject.file('compiler/res/version.txt').text.trim()
@@ -35,14 +38,14 @@ dependencies {
testImplementation "org.jetbrains.kotlin:kotlin-test-junit5:$kotlinVersion"
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.3.2'
testImplementation 'org.hamcrest:hamcrest-junit:2.0.0.0'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.3.2'
compile 'org.jetbrains.kotlinx:kotlinx-cli-jvm:0.1.0-dev-5'
}
compileKotlin {
kotlinOptions {
jvmTarget = "1.8"
verbose = true
// verbose = true
// freeCompilerArgs += "-XXLanguage:+NewInference"
}
}
+1
View File
@@ -14,5 +14,6 @@
<orderEntry type="library" name="antlr-runtime-4.7.2" level="project" />
<orderEntry type="module" module-name="parser" />
<orderEntry type="library" name="unittest-libs" level="project" />
<orderEntry type="library" name="kotlinx-cli-jvm-0.1.0-dev-5" level="project" />
</component>
</module>
Binary file not shown.
+215 -156
View File
@@ -15,28 +15,199 @@ c64utils {
const uword ESTACK_HI = $cf00
; ----- utility functions ----
; ----- number conversions to decimal strings
asmsub ubyte2decimal (ubyte value @ A) -> ubyte @ Y, ubyte @ X, ubyte @ A {
; ---- A to decimal string in Y/X/A (100s in Y, 10s in X, 1s in A)
%asm {{
ldy #$2f
ldx #$3a
sec
- iny
sbc #100
bcs -
- dex
adc #10
bmi -
adc #$2f
rts
asmsub ubyte2decimal (ubyte value @ A) -> ubyte @ Y, ubyte @ A, ubyte @ X {
; ---- A to decimal string in Y/A/X (100s in Y, 10s in A, 1s in X)
%asm {{
ldy #uword2decimal.ASCII_0_OFFSET
bne uword2decimal.hex_try200
rts
}}
}
asmsub byte2decimal (ubyte value @ A) -> ubyte @ Y, ubyte @ X, ubyte @ A {
; ---- A (signed byte) to decimal string in Y/X/A (100s in Y, 10s in X, 1s in A)
; note: the '-' is not part of the conversion here if it's a negative number
asmsub uword2decimal (uword value @ AY) -> ubyte @Y, ubyte @A, ubyte @X {
; ---- convert 16 bit uword in A/Y to decimal
; output in uword2decimal.decTenThousands, decThousands, decHundreds, decTens, decOnes
; (these are terminated by a zero byte so they can be easily printed)
; also returns Y = 100's, A = 10's, X = 1's
%asm {{
;Convert 16 bit Hex to Decimal (0-65535) Rev 2
;By Omegamatrix Further optimizations by tepples
; routine from http://forums.nesdev.com/viewtopic.php?f=2&t=11341&start=15
;HexToDec99
; start in A
; end with A = 10's, decOnes (also in X)
;HexToDec255
; start in A
; end with Y = 100's, A = 10's, decOnes (also in X)
;HexToDec999
; start with A = high byte, Y = low byte
; end with Y = 100's, A = 10's, decOnes (also in X)
; requires 1 extra temp register on top of decOnes, could combine
; these two if HexToDec65535 was eliminated...
;HexToDec65535
; start with A/Y (low/high) as 16 bit value
; end with decTenThousand, decThousand, Y = 100's, A = 10's, decOnes (also in X)
; (irmen: I store Y and A in decHundreds and decTens too, so all of it can be easily printed)
ASCII_0_OFFSET = $30
temp = c64.SCRATCH_ZPB1 ; byte in zeropage
hexHigh = c64.SCRATCH_ZPWORD1 ; byte in zeropage
hexLow = c64.SCRATCH_ZPWORD1+1 ; byte in zeropage
HexToDec65535; SUBROUTINE
sty hexHigh ;3 @9
sta hexLow ;3 @12
tya
tax ;2 @14
lsr a ;2 @16
lsr a ;2 @18 integer divide 1024 (result 0-63)
cpx #$A7 ;2 @20 account for overflow of multiplying 24 from 43,000 ($A7F8) onward,
adc #1 ;2 @22 we can just round it to $A700, and the divide by 1024 is fine...
;at this point we have a number 1-65 that we have to times by 24,
;add to original sum, and Mod 1024 to get a remainder 0-999
sta temp ;3 @25
asl a ;2 @27
adc temp ;3 @30 x3
tay ;2 @32
lsr a ;2 @34
lsr a ;2 @36
lsr a ;2 @38
lsr a ;2 @40
lsr a ;2 @42
tax ;2 @44
tya ;2 @46
asl a ;2 @48
asl a ;2 @50
asl a ;2 @52
clc ;2 @54
adc hexLow ;3 @57
sta hexLow ;3 @60
txa ;2 @62
adc hexHigh ;3 @65
sta hexHigh ;3 @68
ror a ;2 @70
lsr a ;2 @72
tay ;2 @74 integer divide 1,000 (result 0-65)
lsr a ;2 @76 split the 1,000 and 10,000 digit
tax ;2 @78
lda ShiftedBcdTab,x ;4 @82
tax ;2 @84
rol a ;2 @86
and #$0F ;2 @88
ora #ASCII_0_OFFSET
sta decThousands ;3 @91
txa ;2 @93
lsr a ;2 @95
lsr a ;2 @97
lsr a ;2 @99
ora #ASCII_0_OFFSET
sta decTenThousands ;3 @102
lda hexLow ;3 @105
cpy temp ;3 @108
bmi _doSubtract ;2³ @110/111
beq _useZero ;2³ @112/113
adc #23 + 24 ;2 @114
_doSubtract
sbc #23 ;2 @116
sta hexLow ;3 @119
_useZero
lda hexHigh ;3 @122
sbc #0 ;2 @124
Start100s
and #$03 ;2 @126
tax ;2 @128 0,1,2,3
cmp #2 ;2 @130
rol a ;2 @132 0,2,5,7
ora #ASCII_0_OFFSET
tay ;2 @134 Y = Hundreds digit
lda hexLow ;3 @137
adc Mod100Tab,x ;4 @141 adding remainder of 256, 512, and 256+512 (all mod 100)
bcs hex_doSub200 ;2³ @143/144
hex_try200
cmp #200 ;2 @145
bcc hex_try100 ;2³ @147/148
hex_doSub200
iny ;2 @149
iny ;2 @151
sbc #200 ;2 @153
hex_try100
cmp #100 ;2 @155
bcc HexToDec99 ;2³ @157/158
iny ;2 @159
sbc #100 ;2 @161
HexToDec99; SUBROUTINE
lsr a ;2 @163
tax ;2 @165
lda ShiftedBcdTab,x ;4 @169
tax ;2 @171
rol a ;2 @173
and #$0F ;2 @175
ora #ASCII_0_OFFSET
sta decOnes ;3 @178
txa ;2 @180
lsr a ;2 @182
lsr a ;2 @184
lsr a ;2 @186
ora #ASCII_0_OFFSET
; irmen: load X with ones, and store Y and A too, for easy printing afterwards
sty decHundreds
sta decTens
ldx decOnes
rts ;6 @192 Y=hundreds, A = tens digit, X=ones digit
HexToDec999; SUBROUTINE
sty hexLow ;3 @9
jmp Start100s ;3 @12
Mod100Tab
.byte 0,56,12,56+12
ShiftedBcdTab
.byte $00,$01,$02,$03,$04,$08,$09,$0A,$0B,$0C
.byte $10,$11,$12,$13,$14,$18,$19,$1A,$1B,$1C
.byte $20,$21,$22,$23,$24,$28,$29,$2A,$2B,$2C
.byte $30,$31,$32,$33,$34,$38,$39,$3A,$3B,$3C
.byte $40,$41,$42,$43,$44,$48,$49,$4A,$4B,$4C
decTenThousands .byte 0
decThousands .byte 0
decHundreds .byte 0
decTens .byte 0
decOnes .byte 0
.byte 0 ; zero-terminate the decimal output string
}}
}
; ----- utility functions ----
asmsub byte2decimal (byte value @ A) -> ubyte @ Y, ubyte @ A, ubyte @ X {
; ---- A (signed byte) to decimal string in Y/A/X (100s in Y, 10s in A, 1s in X)
; note: if the number is negative, you have to deal with the '-' yourself!
%asm {{
cmp #0
bpl +
@@ -48,7 +219,7 @@ asmsub byte2decimal (ubyte value @ A) -> ubyte @ Y, ubyte @ X, ubyte @ A {
}
asmsub ubyte2hex (ubyte value @ A) -> ubyte @ A, ubyte @ Y {
; ---- A to hex string in AY (first hex char in A, second hex char in Y)
; ---- A to hex petscii string in AY (first hex char in A, second hex char in Y)
%asm {{
stx c64.SCRATCH_ZPREGX
pha
@@ -69,7 +240,6 @@ _hex_digits .text "0123456789abcdef" ; can probably be reused for other stuff as
}}
}
asmsub uword2hex (uword value @ AY) clobbers(A,Y) {
; ---- convert 16 bit uword in A/Y into 4-character hexadecimal string 'uword2hex.output' (0-terminated)
%asm {{
@@ -87,92 +257,6 @@ output .text "0000", $00 ; 0-terminated output buffer (to make printing ea
}}
}
asmsub uword2bcd (uword value @ AY) clobbers(A,Y) {
; Convert an 16 bit binary value to BCD
;
; This function converts a 16 bit binary value in A/Y into a 24 bit BCD. It
; works by transferring one bit a time from the source and adding it
; into a BCD value that is being doubled on each iteration. As all the
; arithmetic is being done in BCD the result is a binary to decimal
; conversion.
%asm {{
sta c64.SCRATCH_ZPB1
sty c64.SCRATCH_ZPREG
php
pla ; read status register
and #%00000100
sta _had_irqd
sei ; disable interrupts because of bcd math
sed ; switch to decimal mode
lda #0 ; ensure the result is clear
sta bcdbuff+0
sta bcdbuff+1
sta bcdbuff+2
ldy #16 ; the number of source bits
- asl c64.SCRATCH_ZPB1 ; shift out one bit
rol c64.SCRATCH_ZPREG
lda bcdbuff+0 ; and add into result
adc bcdbuff+0
sta bcdbuff+0
lda bcdbuff+1 ; propagating any carry
adc bcdbuff+1
sta bcdbuff+1
lda bcdbuff+2 ; ... thru whole result
adc bcdbuff+2
sta bcdbuff+2
dey ; and repeat for next bit
bne -
cld ; back to binary
lda _had_irqd
bne +
cli ; enable interrupts again (only if they were enabled before)
+ rts
_had_irqd .byte 0
bcdbuff .byte 0,0,0
}}
}
asmsub uword2decimal (uword value @ AY) clobbers(A) -> ubyte @ Y {
; ---- convert 16 bit uword in A/Y into 0-terminated decimal string into memory 'uword2decimal.output'
; returns length of resulting string in Y
%asm {{
jsr uword2bcd
lda uword2bcd.bcdbuff+2
clc
adc #'0'
sta output
ldy #1
lda uword2bcd.bcdbuff+1
jsr +
lda uword2bcd.bcdbuff+0
+ pha
lsr a
lsr a
lsr a
lsr a
clc
adc #'0'
sta output,y
iny
pla
and #$0f
adc #'0'
sta output,y
iny
lda #0
sta output,y
rts
output .text "00000", $00 ; 0 terminated
}}
}
asmsub str2uword(str string @ AY) -> uword @ AY {
; -- returns the unsigned word value of the string number argument in AY
; the number may NOT be preceded by a + sign and may NOT contain spaces
@@ -227,7 +311,6 @@ _result_times_10 ; (W*4 + W)*2
}}
}
asmsub str2word(str string @ AY) -> word @ AY {
; -- returns the signed word value of the string number argument in AY
; the number may be preceded by a + or - sign but may NOT contain spaces
@@ -283,7 +366,6 @@ _negative .byte 0
}}
}
asmsub set_irqvec_excl() clobbers(A) {
%asm {{
sei
@@ -341,6 +423,7 @@ _irq_handler_init
dex
dex
dex
cld
rts
_irq_handler_end
@@ -372,7 +455,6 @@ IRQ_SCRATCH_ZPWORD2 .word 0
}}
}
asmsub restore_irqvec() {
%asm {{
sei
@@ -389,7 +471,6 @@ asmsub restore_irqvec() {
}}
}
asmsub set_rasterirq(uword rasterpos @ AY) clobbers(A) {
%asm {{
sei
@@ -454,13 +535,11 @@ _raster_irq_handler
}
} ; ------ end of block c64utils
c64scr {
; ---- this block contains (character) Screen and text I/O related functions ----
@@ -480,7 +559,6 @@ asmsub clear_screen (ubyte char @ A, ubyte color @ Y) clobbers(A) {
}
asmsub clear_screenchars (ubyte char @ A) clobbers(Y) {
; ---- clear the character screen with the given fill character (leaves colors)
; (assumes screen matrix is at the default address)
@@ -521,7 +599,6 @@ _loop sta c64.Colors,y
}}
}
asmsub scroll_left_full (ubyte alsocolors @ Pc) clobbers(A, Y) {
; ---- scroll the whole screen 1 character to the left
; contents of the rightmost column are unchanged, you should clear/refill this yourself
@@ -582,7 +659,6 @@ _scroll_screen ; scroll the screen memory
}}
}
asmsub scroll_right_full (ubyte alsocolors @ Pc) clobbers(A) {
; ---- scroll the whole screen 1 character to the right
; contents of the leftmost column are unchanged, you should clear/refill this yourself
@@ -635,7 +711,6 @@ _scroll_screen ; scroll the screen memory
}}
}
asmsub scroll_up_full (ubyte alsocolors @ Pc) clobbers(A) {
; ---- scroll the whole screen 1 character up
; contents of the bottom row are unchanged, you should refill/clear this yourself
@@ -688,7 +763,6 @@ _scroll_screen ; scroll the screen memory
}}
}
asmsub scroll_down_full (ubyte alsocolors @ Pc) clobbers(A) {
; ---- scroll the whole screen 1 character down
; contents of the top row are unchanged, you should refill/clear this yourself
@@ -742,7 +816,6 @@ _scroll_screen ; scroll the screen memory
}
asmsub print (str text @ AY) clobbers(A,Y) {
; ---- print null terminated string from A/Y
; note: the compiler contains an optimization that will replace
@@ -761,7 +834,6 @@ asmsub print (str text @ AY) clobbers(A,Y) {
}}
}
asmsub print_ub0 (ubyte value @ A) clobbers(A,Y) {
; ---- print the ubyte in A in decimal form, with left padding 0s (3 positions total)
%asm {{
@@ -770,16 +842,15 @@ asmsub print_ub0 (ubyte value @ A) clobbers(A,Y) {
pha
tya
jsr c64.CHROUT
txa
jsr c64.CHROUT
pla
jsr c64.CHROUT
txa
jsr c64.CHROUT
ldx c64.SCRATCH_ZPREGX
rts
}}
}
asmsub print_ub (ubyte value @ A) clobbers(A,Y) {
; ---- print the ubyte in A in decimal form, without left padding 0s
%asm {{
@@ -788,15 +859,14 @@ asmsub print_ub (ubyte value @ A) clobbers(A,Y) {
_print_byte_digits
pha
cpy #'0'
bne _print_hundreds
cpx #'0'
bne _print_tens
jmp _end
_print_hundreds tya
beq +
tya
jsr c64.CHROUT
_print_tens txa
jsr c64.CHROUT
_end pla
+ pla
cmp #'0'
beq +
jsr c64.CHROUT
+ txa
jsr c64.CHROUT
ldx c64.SCRATCH_ZPREGX
rts
@@ -820,7 +890,6 @@ asmsub print_b (byte value @ A) clobbers(A,Y) {
}}
}
asmsub print_ubhex (ubyte value @ A, ubyte prefix @ Pc) clobbers(A,Y) {
; ---- print the ubyte in A in hex form (if Carry is set, a radix prefix '$' is printed as well)
%asm {{
@@ -839,7 +908,6 @@ asmsub print_ubhex (ubyte value @ A, ubyte prefix @ Pc) clobbers(A,Y) {
}}
}
asmsub print_ubbin (ubyte value @ A, ubyte prefix @ Pc) clobbers(A,Y) {
; ---- print the ubyte in A in binary form (if Carry is set, a radix prefix '%' is printed as well)
%asm {{
@@ -861,7 +929,6 @@ asmsub print_ubbin (ubyte value @ A, ubyte prefix @ Pc) clobbers(A,Y) {
}}
}
asmsub print_uwbin (uword value @ AY, ubyte prefix @ Pc) clobbers(A,Y) {
; ---- print the uword in A/Y in binary form (if Carry is set, a radix prefix '%' is printed as well)
%asm {{
@@ -874,7 +941,6 @@ asmsub print_uwbin (uword value @ AY, ubyte prefix @ Pc) clobbers(A,Y) {
}}
}
asmsub print_uwhex (uword value @ AY, ubyte prefix @ Pc) clobbers(A,Y) {
; ---- print the uword in A/Y in hexadecimal form (4 digits)
; (if Carry is set, a radix prefix '$' is printed as well)
@@ -888,51 +954,44 @@ asmsub print_uwhex (uword value @ AY, ubyte prefix @ Pc) clobbers(A,Y) {
}}
}
asmsub print_uw0 (uword value @ AY) clobbers(A,Y) {
; ---- print the uword in A/Y in decimal form, with left padding 0s (5 positions total)
%asm {{
stx c64.SCRATCH_ZPREGX
jsr c64utils.uword2decimal
ldy #0
- lda c64utils.uword2decimal.output,y
- lda c64utils.uword2decimal.decTenThousands,y
beq +
jsr c64.CHROUT
iny
cpy #5
bne -
+ ldx c64.SCRATCH_ZPREGX
rts
}}
}
asmsub print_uw (uword value @ AY) clobbers(A,Y) {
; ---- print the uword in A/Y in decimal form, without left padding 0s
%asm {{
stx c64.SCRATCH_ZPREGX
jsr c64utils.uword2decimal
ldy #0
lda c64utils.uword2decimal.output
- lda c64utils.uword2decimal.decTenThousands,y
beq _allzero
cmp #'0'
bne _pr_decimal
iny
lda c64utils.uword2decimal.output+1
cmp #'0'
bne _pr_decimal
iny
lda c64utils.uword2decimal.output+2
cmp #'0'
bne _pr_decimal
iny
lda c64utils.uword2decimal.output+3
cmp #'0'
bne _pr_decimal
bne _gotdigit
iny
bne -
_pr_decimal
lda c64utils.uword2decimal.output,y
_gotdigit
jsr c64.CHROUT
iny
cpy #5
bcc _pr_decimal
lda c64utils.uword2decimal.decTenThousands,y
bne _gotdigit
rts
_allzero
lda #'0'
jmp c64.CHROUT
}}
}
+100 -12
View File
@@ -1417,7 +1417,7 @@ _l3 ldy c64.SCRATCH_ZPB1 ;where the largest value shall be put
rts
.pend
sort_b .proc
; 8bit signed sort
; sorting subroutine coded by mats rosengren (mats.rosengren@esa.int)
@@ -1471,7 +1471,7 @@ _sort_loop ldy c64.SCRATCH_ZPB1 ;start of subroutine sort
sta _work3+1
dey
jmp _l2
_l1 dey
_l1 dey
dey
beq _l3
iny
@@ -1506,12 +1506,12 @@ _l3 ldy c64.SCRATCH_ZPB1 ;where the largest value shall be put
dec c64.SCRATCH_ZPB1 ;end of the shorter sequence still left
dec c64.SCRATCH_ZPB1
bne _sort_loop ;start working with the shorter sequence
rts
rts
_work1 .byte 0
_work3 .word 0
.pend
sort_w .proc
; 16bit signed sort
; sorting subroutine coded by mats rosengren (mats.rosengren@esa.int)
@@ -1532,7 +1532,7 @@ _sort_loop ldy c64.SCRATCH_ZPB1 ;start of subroutine sort
sta _work3+1
dey
jmp _l2
_l1 dey
_l1 dey
dey
beq _l3
lda (c64.SCRATCH_ZPWORD1),y
@@ -1543,7 +1543,7 @@ _l1 dey
sbc c64.SCRATCH_ZPWORD2+1
bvc +
eor #$80
+ bmi _l1
+ bmi _l1
_l2 sty _work1 ;index of potentially largest value
lda (c64.SCRATCH_ZPWORD1),y
sta c64.SCRATCH_ZPWORD2 ;potentially largest value
@@ -1568,12 +1568,12 @@ _l3 ldy c64.SCRATCH_ZPB1 ;where the largest value shall be put
dec c64.SCRATCH_ZPB1 ;end of the shorter sequence still left
dec c64.SCRATCH_ZPB1
bne _sort_loop ;start working with the shorter sequence
rts
rts
_work1 .byte 0
_work3 .word 0
.pend
.pend
reverse_b .proc
; --- reverse an array of bytes (in-place)
; inputs: pointer to array in c64.SCRATCH_ZPWORD1, length in A
@@ -1607,7 +1607,7 @@ _loop sty c64.SCRATCH_ZPREG
rts
.pend
reverse_w .proc
; --- reverse an array of words (in-place)
; inputs: pointer to array in c64.SCRATCH_ZPWORD1, length in A
@@ -1669,5 +1669,93 @@ _loop_hi sty c64.SCRATCH_ZPREG
dey
bne _loop_hi
rts
rts
.pend
ror2_mem_ub .proc
; -- in-place 8-bit ror of byte at memory location on stack
inx
lda c64.ESTACK_LO,x
sta c64.SCRATCH_ZPWORD1
lda c64.ESTACK_HI,x
sta c64.SCRATCH_ZPWORD1+1
ldy #0
lda (c64.SCRATCH_ZPWORD1),y
lsr a
bcc +
ora #$80
+ sta (c64.SCRATCH_ZPWORD1),y
rts
.pend
rol2_mem_ub .proc
; -- in-place 8-bit rol of byte at memory location on stack
;" lda ${number.toHex()} | cmp #\$80 | rol a | sta ${number.toHex()}"
inx
lda c64.ESTACK_LO,x
sta c64.SCRATCH_ZPWORD1
lda c64.ESTACK_HI,x
sta c64.SCRATCH_ZPWORD1+1
ldy #0
lda (c64.SCRATCH_ZPWORD1),y
cmp #$80
rol a
sta (c64.SCRATCH_ZPWORD1),y
rts
.pend
lsl_array_b .proc
.warn "lsl_array_b" ; TODO
.pend
lsl_array_w .proc
.warn "lsl_array_w" ; TODO
.pend
lsr_array_ub .proc
.warn "lsr_array_ub" ; TODO
.pend
lsr_array_b .proc
.warn "lsr_array_b" ; TODO
.pend
lsr_array_uw .proc
.warn "lsr_array_uw" ; TODO
.pend
lsr_array_w .proc
.warn "lsr_array_w" ; TODO
.pend
rol_array_ub .proc
.warn "rol_array_ub" ; TODO
.pend
rol_array_uw .proc
.warn "rol_array_uw" ; TODO
.pend
rol2_array_ub .proc
.warn "rol2_array_ub" ; TODO
.pend
rol2_array_uw .proc
.warn "rol2_array_uw" ; TODO
.pend
ror_array_ub .proc
.warn "ror_array_ub" ; TODO
.pend
ror_array_uw .proc
.warn "ror_array_uw" ; TODO
.pend
ror2_array_ub .proc
.warn "ror2_array_ub" ; TODO
.pend
ror2_array_uw .proc
.warn "ror2_array_uw" ; TODO
.pend
+1 -2
View File
@@ -1,2 +1 @@
1.60
1.62
+55 -75
View File
@@ -1,5 +1,6 @@
package prog8
import kotlinx.cli.*
import prog8.ast.base.AstException
import prog8.compiler.CompilationResult
import prog8.compiler.compileProgram
@@ -7,18 +8,14 @@ import prog8.parser.ParsingFailedError
import prog8.vm.astvm.AstVm
import java.nio.file.FileSystems
import java.nio.file.Path
import java.nio.file.Paths
import java.nio.file.StandardWatchEventKinds
import java.util.*
import java.time.LocalDateTime
import kotlin.system.exitProcess
fun main(args: Array<String>) {
printSoftwareHeader("compiler")
if (args.isEmpty())
usage()
compileMain(args)
}
@@ -29,51 +26,48 @@ internal fun printSoftwareHeader(what: String) {
}
fun pathFrom(stringPath: String, vararg rest: String): Path = FileSystems.getDefault().getPath(stringPath, *rest)
private fun compileMain(args: Array<String>) {
var emulatorToStart = ""
var moduleFile = ""
var writeAssembly = true
var optimize = true
var launchAstVm = false
var watchMode = false
for (arg in args) {
if(arg=="-emu")
emulatorToStart = "x64"
else if(arg=="-emu2")
emulatorToStart = "x64sc"
else if(arg=="-noasm")
writeAssembly = false
else if(arg=="-noopt")
optimize = false
else if(arg=="-avm")
launchAstVm = true
else if(arg=="-watch")
watchMode = true
else if(!arg.startsWith("-"))
moduleFile = arg
else
usage()
val cli = CommandLineInterface("prog8compiler")
val startEmulator1 by cli.flagArgument("-emu", "auto-start the 'x64' C-64 emulator after successful compilation")
val startEmulator2 by cli.flagArgument("-emu2", "auto-start the 'x64sc' C-64 emulator after successful compilation")
val outputDir by cli.flagValueArgument("-out", "directory", "directory for output files instead of current directory", ".")
val dontWriteAssembly by cli.flagArgument("-noasm", "don't create assembly code")
val dontOptimize by cli.flagArgument("-noopt", "don't perform any optimizations")
val launchSimulator by cli.flagArgument("-sim", "launch the prog8 virtual machine/simulator after compilation")
val watchMode by cli.flagArgument("-watch", "continuous compilation mode (watches for file changes)")
val moduleFiles by cli.positionalArgumentsList("modules", "main module file(s) to compile", minArgs = 1)
try {
cli.parse(args)
} catch (e: Exception) {
exitProcess(1)
}
if(watchMode) {
if(moduleFile.isBlank())
usage()
val outputPath = pathFrom(outputDir)
if(!outputPath.toFile().isDirectory) {
System.err.println("Output path doesn't exist")
exitProcess(1)
}
if(watchMode && moduleFiles.size<=1) {
val watchservice = FileSystems.getDefault().newWatchService()
while(true) {
val filepath = Paths.get(moduleFile).normalize()
val filepath = pathFrom(moduleFiles.single()).normalize()
println("Continuous watch mode active. Main module: $filepath")
try {
val compilationResult = compileProgram(filepath, optimize, writeAssembly)
val compilationResult = compileProgram(filepath, !dontOptimize, !dontWriteAssembly, outputDir=outputPath)
println("Imported files (now watching:)")
for (importedFile in compilationResult.importedFiles) {
print(" ")
println(importedFile)
importedFile.parent.register(watchservice, StandardWatchEventKinds.ENTRY_MODIFY)
}
println("${Date()}: Waiting for file changes.")
println("[${LocalDateTime.now().withNano(0)}] Waiting for file changes.")
val event = watchservice.take()
for(changed in event.pollEvents()) {
val changedPath = changed.context() as Path
@@ -87,51 +81,37 @@ private fun compileMain(args: Array<String>) {
}
} else {
if(moduleFile.isBlank())
usage()
val filepath = Paths.get(moduleFile).normalize()
val compilationResult: CompilationResult
try {
compilationResult = compileProgram(filepath, optimize, writeAssembly)
if(!compilationResult.success)
for(filepathRaw in moduleFiles) {
val filepath = pathFrom(filepathRaw).normalize()
val compilationResult: CompilationResult
try {
compilationResult = compileProgram(filepath, !dontOptimize, !dontWriteAssembly, outputDir=outputPath)
if(!compilationResult.success)
exitProcess(1)
} catch (x: ParsingFailedError) {
exitProcess(1)
} catch (x: ParsingFailedError) {
exitProcess(1)
} catch (x: AstException) {
exitProcess(1)
}
} catch (x: AstException) {
exitProcess(1)
}
if (launchAstVm) {
println("\nLaunching AST-based vm...")
val vm = AstVm(compilationResult.programAst)
vm.run()
}
if (launchSimulator) {
println("\nLaunching AST-based simulator...")
val vm = AstVm(compilationResult.programAst)
vm.run()
}
if (emulatorToStart.isNotEmpty()) {
if (compilationResult.programName.isEmpty())
println("\nCan't start emulator because no program was assembled.")
else {
println("\nStarting C-64 emulator $emulatorToStart...")
val cmdline = listOf(emulatorToStart, "-silent", "-moncommands", "${compilationResult.programName}.vice-mon-list",
"-autostartprgmode", "1", "-autostart-warp", "-autostart", compilationResult.programName + ".prg")
val process = ProcessBuilder(cmdline).inheritIO().start()
process.waitFor()
if (startEmulator1 || startEmulator2) {
if (compilationResult.programName.isEmpty())
println("\nCan't start emulator because no program was assembled.")
else {
val emulator = if(startEmulator1) "x64" else "x64sc"
println("\nStarting C-64 emulator $emulator...")
val cmdline = listOf(emulator, "-silent", "-moncommands", "${compilationResult.programName}.vice-mon-list",
"-autostartprgmode", "1", "-autostart-warp", "-autostart", compilationResult.programName + ".prg")
val process = ProcessBuilder(cmdline).inheritIO().start()
process.waitFor()
}
}
}
}
}
private fun usage() {
System.err.println("Missing argument(s):")
System.err.println(" [-noasm] don't create assembly code")
System.err.println(" [-noopt] don't perform any optimizations")
System.err.println(" [-emu] auto-start the 'x64' C-64 emulator after successful compilation")
System.err.println(" [-emu2] auto-start the 'x64sc' C-64 emulator after successful compilation")
System.err.println(" [-avm] launch the prog8 ast-based virtual machine after compilation")
System.err.println(" [-watch] continuous compilation mode (watches for file changes)")
System.err.println(" modulefile main module file to compile")
exitProcess(1)
}
+5 -3
View File
@@ -4,7 +4,6 @@ import prog8.ast.base.*
import prog8.ast.expressions.Expression
import prog8.ast.expressions.IdentifierReference
import prog8.ast.statements.*
import prog8.compiler.HeapValues
import prog8.functions.BuiltinFunctions
import java.nio.file.Path
@@ -161,12 +160,15 @@ interface INameScope {
}
}
interface IAssignable {
// just a tag for now
}
/*********** Everything starts from here, the Program; zero or more modules *************/
class Program(val name: String, val modules: MutableList<Module>) {
val namespace = GlobalNamespace(modules)
val heap = HeapValues()
val definedLoadAddress: Int
get() = modules.first().loadAddress
@@ -174,7 +176,7 @@ class Program(val name: String, val modules: MutableList<Module>) {
var actualLoadAddress: Int = 0
fun entrypoint(): Subroutine? {
val mainBlocks = modules.flatMap { it.statements }.filter { b -> b is Block && b.name=="main" }.map { it as Block }
val mainBlocks = allBlocks().filter { it.name=="main" }
if(mainBlocks.size > 1)
throw FatalAstException("more than one 'main' block")
return if(mainBlocks.isEmpty()) {
+1 -1
View File
@@ -429,7 +429,7 @@ private fun prog8Parser.ExpressionContext.toAst() : Expression {
else -> throw FatalAstException("invalid datatype for numeric literal")
}
litval.floatliteral()!=null -> NumericLiteralValue(DataType.FLOAT, litval.floatliteral().toAst(), litval.toPosition())
litval.stringliteral()!=null -> StringLiteralValue(DataType.STR, unescape(litval.stringliteral().text, litval.toPosition()), position = litval.toPosition())
litval.stringliteral()!=null -> StringLiteralValue(DataType.STR, unescape(litval.stringliteral().text, litval.toPosition()), litval.toPosition())
litval.charliteral()!=null -> {
try {
NumericLiteralValue(DataType.UBYTE, Petscii.encodePetscii(unescape(litval.charliteral().text, litval.toPosition()), true)[0], litval.toPosition())
@@ -5,7 +5,7 @@ import prog8.parser.ParsingFailedError
fun printErrors(errors: List<Any>, moduleName: String) {
val reportedMessages = mutableSetOf<String>()
print("\u001b[91m") // bright red
System.err.print("\u001b[91m") // bright red
errors.forEach {
val msg = it.toString()
if(msg !in reportedMessages) {
@@ -13,7 +13,7 @@ fun printErrors(errors: List<Any>, moduleName: String) {
reportedMessages.add(msg)
}
}
print("\u001b[0m") // reset color
System.err.print("\u001b[0m") // reset color
if(reportedMessages.isNotEmpty())
throw ParsingFailedError("There are ${reportedMessages.size} errors in module '$moduleName'.")
}
@@ -1,9 +1,6 @@
package prog8.ast.expressions
import prog8.ast.IFunctionCall
import prog8.ast.INameScope
import prog8.ast.Node
import prog8.ast.Program
import prog8.ast.*
import prog8.ast.antlr.escape
import prog8.ast.base.*
import prog8.ast.processing.IAstModifyingVisitor
@@ -12,13 +9,11 @@ import prog8.ast.statements.ArrayIndex
import prog8.ast.statements.BuiltinFunctionStatementPlaceholder
import prog8.ast.statements.Subroutine
import prog8.ast.statements.VarDecl
import prog8.compiler.HeapValues
import prog8.compiler.IntegerOrAddressOf
import prog8.compiler.target.c64.Petscii
import prog8.functions.BuiltinFunctions
import prog8.functions.NotConstArgumentException
import prog8.functions.builtinFunctionReturnType
import java.util.*
import java.util.Objects
import kotlin.math.abs
@@ -185,7 +180,7 @@ class BinaryExpression(var left: Expression, var operator: String, var right: Ex
class ArrayIndexedExpression(var identifier: IdentifierReference,
val arrayspec: ArrayIndex,
override val position: Position) : Expression() {
override val position: Position) : Expression(), IAssignable {
override lateinit var parent: Node
override fun linkParents(parent: Node) {
this.parent = parent
@@ -254,7 +249,7 @@ data class AddressOf(var identifier: IdentifierReference, override val position:
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
}
class DirectMemoryRead(var addressExpression: Expression, override val position: Position) : Expression() {
class DirectMemoryRead(var addressExpression: Expression, override val position: Position) : Expression(), IAssignable {
override lateinit var parent: Node
override fun linkParents(parent: Node) {
@@ -414,13 +409,9 @@ class StructLiteralValue(var values: List<Expression>,
class StringLiteralValue(val type: DataType, // only string types
val value: String,
initHeapId: Int? =null,
override val position: Position) : Expression() {
override lateinit var parent: Node
var heapId = initHeapId
private set
override fun linkParents(parent: Node) {
this.parent = parent
}
@@ -438,14 +429,18 @@ class StringLiteralValue(val type: DataType, // only string types
return value==other.value && type==other.type
}
fun addToHeap(heap: HeapValues) {
if (heapId != null)
return
else
heapId = heap.addString(type, value)
var heapId: Int? = null
private set
fun addToHeap() {
if(heapId==null)
heapId = ++heapIdSequence
}
}
private var heapIdSequence = 0
class ArrayLiteralValue(val type: DataType, // only array types
val value: Array<Expression>,
initHeapId: Int? =null,
@@ -498,32 +493,9 @@ class ArrayLiteralValue(val type: DataType, // only array types
return null // invalid type conversion from $this to $targettype
}
fun addToHeap(heap: HeapValues) {
if (heapId != null)
return
else {
if(value.any {it is AddressOf }) {
val intArrayWithAddressOfs = value.map {
when (it) {
is AddressOf -> IntegerOrAddressOf(null, it)
is NumericLiteralValue -> IntegerOrAddressOf(it.number.toInt(), null)
else -> throw FatalAstException("invalid datatype in array")
}
}
heapId = heap.addIntegerArray(type, intArrayWithAddressOfs.toTypedArray())
} else {
val valuesInArray = value.map { (it as? NumericLiteralValue)?.number }
if(null !in valuesInArray) {
heapId = if (type == DataType.ARRAY_F) {
val doubleArray = valuesInArray.map { it!!.toDouble() }.toDoubleArray()
heap.addDoublesArray(doubleArray)
} else {
val integerArray = valuesInArray.map { it!!.toInt() }
heap.addIntegerArray(type, integerArray.map { IntegerOrAddressOf(it, null) }.toTypedArray())
}
}
}
}
fun addToHeap() {
if(heapId==null)
heapId = ++heapIdSequence
}
}
@@ -608,7 +580,7 @@ internal fun makeRange(fromVal: Int, toVal: Int, stepVal: Int): IntProgression {
}
}
class RegisterExpr(val register: Register, override val position: Position) : Expression() {
class RegisterExpr(val register: Register, override val position: Position) : Expression(), IAssignable {
override lateinit var parent: Node
override fun linkParents(parent: Node) {
@@ -626,7 +598,7 @@ class RegisterExpr(val register: Register, override val position: Position) : Ex
override fun inferType(program: Program): InferredTypes.InferredType = InferredTypes.knownFor(DataType.UBYTE)
}
data class IdentifierReference(val nameInSource: List<String>, override val position: Position) : Expression() {
data class IdentifierReference(val nameInSource: List<String>, override val position: Position) : Expression(), IAssignable {
override lateinit var parent: Node
fun targetStatement(namespace: INameScope) =
@@ -1,12 +1,13 @@
package prog8.ast.expressions
import java.util.Objects
import prog8.ast.base.DataType
object InferredTypes {
class InferredType private constructor(val isUnknown: Boolean, val isVoid: Boolean, private var datatype: DataType?) {
init {
if(datatype!=null && (isUnknown || isVoid))
throw IllegalArgumentException("invalid combination of args")
require(!(datatype!=null && (isUnknown || isVoid))) { "invalid combination of args" }
}
val isKnown = datatype!=null
@@ -29,9 +30,11 @@ object InferredTypes {
return when {
datatype!=null -> datatype.toString()
isVoid -> "<void>"
else -> "<unkonwn>"
else -> "<unknown>"
}
}
override fun hashCode(): Int = Objects.hash(isVoid, datatype)
}
private val unknownInstance = InferredType.unknown()
+27 -23
View File
@@ -1,3 +1,5 @@
@file:Suppress("DuplicatedCode")
package prog8.ast.processing
import prog8.ast.IFunctionCall
@@ -145,6 +147,7 @@ internal class AstChecker(private val program: Program,
checkResult.add(ExpressionError("uword loop variable can only loop over unsigned bytes, words or strings", forLoop.position))
}
DataType.BYTE -> {
// TODO fix this, it should allow: for bb in [1,2,3]
if(iterableDt!= DataType.BYTE && iterableDt!= DataType.ARRAY_B)
checkResult.add(ExpressionError("byte loop variable can only loop over bytes", forLoop.position))
}
@@ -889,7 +892,7 @@ internal class AstChecker(private val program: Program,
for (arg in args.withIndex().zip(target.parameters)) {
val argIDt = arg.first.value.inferType(program)
if(!argIDt.isKnown) {
throw FatalAstException("can't determine arg dt ${arg.first.value}")
return
}
val argDt=argIDt.typeOrElse(DataType.STRUCT)
if(!(argDt isAssignableTo arg.second.type)) {
@@ -945,9 +948,8 @@ internal class AstChecker(private val program: Program,
if(dt !in NumericDatatypes && dt !in ArrayDatatypes)
checkResult.add(SyntaxError("can only increment or decrement a byte/float/word", postIncrDecr.position))
}
} else if(postIncrDecr.target.memoryAddress != null) {
// a memory location can always be ++/--
}
// else if(postIncrDecr.target.memoryAddress != null) { } // a memory location can always be ++/--
super.visit(postIncrDecr)
}
@@ -965,8 +967,7 @@ internal class AstChecker(private val program: Program,
} else if(target.datatype in StringDatatypes) {
if(target.value is StringLiteralValue) {
// check string lengths for non-memory mapped strings
val heapId = (target.value as StringLiteralValue).heapId!!
val stringLen = program.heap.get(heapId).str!!.length
val stringLen = (target.value as StringLiteralValue).value.length
val index = (arrayIndexedExpression.arrayspec.index as? NumericLiteralValue)?.number?.toInt()
if (index != null && (index < 0 || index >= stringLen))
checkResult.add(ExpressionError("index out of bounds", arrayIndexedExpression.arrayspec.position))
@@ -1206,53 +1207,56 @@ internal class AstChecker(private val program: Program,
}
private fun checkArrayValues(value: ArrayLiteralValue, type: DataType): Boolean {
if(value.heapId==null) {
if (value.heapId == null) {
// hmm weird, array literal that hasn't been moved to the heap yet?
val array = value.value.mapNotNull { it.constValue(program) }
val correct: Boolean
when(type) {
when (type) {
DataType.ARRAY_UB -> {
correct=array.all { it.type==DataType.UBYTE && it.number.toInt() in 0..255 }
correct = array.all { it.type == DataType.UBYTE && it.number.toInt() in 0..255 }
}
DataType.ARRAY_B -> {
correct=array.all { it.type==DataType.BYTE && it.number.toInt() in -128..127 }
correct = array.all { it.type == DataType.BYTE && it.number.toInt() in -128..127 }
}
DataType.ARRAY_UW -> {
correct=array.all { it.type==DataType.UWORD && it.number.toInt() in 0..65535 }
correct = array.all { it.type == DataType.UWORD && it.number.toInt() in 0..65535 }
}
DataType.ARRAY_W -> {
correct=array.all { it.type==DataType.WORD && it.number.toInt() in -32768..32767}
correct = array.all { it.type == DataType.WORD && it.number.toInt() in -32768..32767 }
}
DataType.ARRAY_F -> correct = true
else -> throw AstException("invalid array type $type")
}
if(!correct)
if (!correct)
checkResult.add(ExpressionError("array value out of range for type $type", value.position))
return correct
}
val array = program.heap.get(value.heapId!!)
if(array.type !in ArrayDatatypes || (array.array==null && array.doubleArray==null))
throw FatalAstException("should have an array in the heapvar $array")
val array = value.value.map {
when (it) {
is NumericLiteralValue -> it.number.toInt()
is AddressOf -> it.identifier.heapId(program.namespace)
else -> -9999999
}
}
val correct: Boolean
when(type) {
when (type) {
DataType.ARRAY_UB -> {
correct= array.array?.all { it.integer!=null && it.integer in 0..255 } ?: false
correct = array.all { it in 0..255 }
}
DataType.ARRAY_B -> {
correct=array.array?.all { it.integer!=null && it.integer in -128..127 } ?: false
correct = array.all { it in -128..127 }
}
DataType.ARRAY_UW -> {
correct=array.array?.all { (it.integer!=null && it.integer in 0..65535) || it.addressOf!=null} ?: false
correct = array.all { (it in 0..65535) }
}
DataType.ARRAY_W -> {
correct=array.array?.all { it.integer!=null && it.integer in -32768..32767 } ?: false
correct = array.all { it in -32768..32767 }
}
DataType.ARRAY_F -> correct = array.doubleArray!=null
DataType.ARRAY_F -> correct = true
else -> throw AstException("invalid array type $type")
}
if(!correct)
if (!correct)
checkResult.add(ExpressionError("array value out of range for type $type", value.position))
return correct
}
@@ -219,15 +219,8 @@ internal class AstIdentifiersChecker(private val program: Program) : IAstModifyi
val subroutine = returnStmt.definingSubroutine()!!
if(subroutine.returntypes.size!=1)
return returnStmt // mismatch in number of return values, error will be printed later.
val newValue: Expression
val lval = returnStmt.value as? NumericLiteralValue
if(lval!=null) {
newValue = lval.cast(subroutine.returntypes.single())
} else {
newValue = returnStmt.value!!
}
returnStmt.value = newValue
returnStmt.value = lval?.cast(subroutine.returntypes.single()) ?: returnStmt.value!!
}
return super.visit(returnStmt)
}
@@ -236,18 +229,19 @@ internal class AstIdentifiersChecker(private val program: Program) : IAstModifyi
val array = super.visit(arrayLiteral)
if(array is ArrayLiteralValue) {
val vardecl = array.parent as? VarDecl
return if (vardecl!=null) {
fixupArrayDatatype(array, vardecl, program)
} else if(array.heapId!=null) {
// fix the datatype of the array (also on the heap) to the 'biggest' datatype in the array
// (we don't know the desired datatype here exactly so we guess)
val datatype = determineArrayDt(array.value)
val litval2 = array.cast(datatype)!!
litval2.parent = array.parent
// finally, replace the literal array by a identifier reference.
makeIdentifierFromRefLv(litval2)
} else
array
return when {
vardecl!=null -> fixupArrayDatatype(array, vardecl, program)
array.heapId!=null -> {
// fix the datatype of the array (also on the heap) to the 'biggest' datatype in the array
// (we don't know the desired datatype here exactly so we guess)
val datatype = determineArrayDt(array.value)
val litval2 = array.cast(datatype)!!
litval2.parent = array.parent
// finally, replace the literal array by a identifier reference.
makeIdentifierFromRefLv(litval2)
}
else -> array
}
}
return array
}
@@ -260,7 +254,7 @@ internal class AstIdentifiersChecker(private val program: Program) : IAstModifyi
if (string.value.length !in 1..255)
checkResult.add(ExpressionError("string literal length must be between 1 and 255", string.position))
else {
string.addToHeap(program.heap)
string.addToHeap()
}
return if (vardecl != null)
string
@@ -272,8 +266,7 @@ internal class AstIdentifiersChecker(private val program: Program) : IAstModifyi
private fun determineArrayDt(array: Array<Expression>): DataType {
val datatypesInArray = array.map { it.inferType(program) }
if(datatypesInArray.isEmpty() || datatypesInArray.any { !it.isKnown })
throw IllegalArgumentException("can't determine type of empty array")
require(datatypesInArray.isNotEmpty() && datatypesInArray.all { it.isKnown }) { "can't determine type of empty array" }
val dts = datatypesInArray.map { it.typeOrElse(DataType.STRUCT) }
return when {
DataType.FLOAT in dts -> DataType.ARRAY_F
@@ -289,7 +282,7 @@ internal class AstIdentifiersChecker(private val program: Program) : IAstModifyi
// a referencetype literal value that's not declared as a variable
// we need to introduce an auto-generated variable for this to be able to refer to the value
// note: if the var references the same literal value, it is not yet de-duplicated here.
array.addToHeap(program.heap)
array.addToHeap()
val scope = array.definingScope()
val variable = VarDecl.createAuto(array)
return replaceWithIdentifier(variable, scope, array.parent)
@@ -299,7 +292,7 @@ internal class AstIdentifiersChecker(private val program: Program) : IAstModifyi
// a referencetype literal value that's not declared as a variable
// we need to introduce an auto-generated variable for this to be able to refer to the value
// note: if the var references the same literal value, it is not yet de-duplicated here.
string.addToHeap(program.heap)
string.addToHeap()
val scope = string.definingScope()
val variable = VarDecl.createAuto(string)
return replaceWithIdentifier(variable, scope, string.parent)
@@ -340,14 +333,14 @@ internal class AstIdentifiersChecker(private val program: Program) : IAstModifyi
// repeat a string a number of times
val idt = string.inferType(program)
return StringLiteralValue(idt.typeOrElse(DataType.STR),
string.value.repeat(constvalue.number.toInt()), null, expr.position)
string.value.repeat(constvalue.number.toInt()), expr.position)
}
}
if(expr.operator == "+" && operand is StringLiteralValue) {
// concatenate two strings
val idt = string.inferType(program)
return StringLiteralValue(idt.typeOrElse(DataType.STR),
"${string.value}${operand.value}", null, expr.position)
"${string.value}${operand.value}", expr.position)
}
return expr
}
@@ -426,7 +419,7 @@ internal fun fixupArrayDatatype(array: ArrayLiteralValue, vardecl: VarDecl, prog
}
vardecl.value = litval2
litval2.linkParents(vardecl)
litval2.addToHeap(program.heap)
litval2.addToHeap()
return litval2
}
}
@@ -142,8 +142,7 @@ interface IAstModifyingVisitor {
}
fun visit(forLoop: ForLoop): Statement {
val newloopvar = forLoop.loopVar?.accept(this)
when(newloopvar) {
when(val newloopvar = forLoop.loopVar?.accept(this)) {
is IdentifierReference -> forLoop.loopVar = newloopvar
null -> forLoop.loopVar = null
else -> throw FatalAstException("can't change class of loopvar")
@@ -179,8 +178,7 @@ interface IAstModifyingVisitor {
}
fun visit(assignTarget: AssignTarget): AssignTarget {
val ident = assignTarget.identifier?.accept(this)
when (ident) {
when (val ident = assignTarget.identifier?.accept(this)) {
is IdentifierReference -> assignTarget.identifier = ident
null -> assignTarget.identifier = null
else -> throw FatalAstException("can't change class of assign target identifier")
@@ -5,7 +5,6 @@ import prog8.ast.base.DataType
import prog8.ast.base.FatalAstException
import prog8.ast.base.initvarsSubName
import prog8.ast.expressions.*
import prog8.ast.mangledStructMemberName
import prog8.ast.statements.*
@@ -45,7 +45,7 @@ internal class VarInitValueAndAddressOfCreator(private val program: Program): IA
val array = ArrayLiteralValue(decl.datatype,
Array(arraysize) { NumericLiteralValue.optimalInteger(0, decl.position) },
null, decl.position)
array.addToHeap(program.heap)
array.addToHeap()
decl.value = array
}
@@ -37,7 +37,6 @@ sealed class Statement : Node {
}
}
class BuiltinFunctionStatementPlaceholder(val name: String, override val position: Position) : Statement() {
override var parent: Node = ParentSentinel
override fun linkParents(parent: Node) {}
@@ -47,10 +46,8 @@ class BuiltinFunctionStatementPlaceholder(val name: String, override val positio
override val expensiveToInline = false
}
data class RegisterOrStatusflag(val registerOrPair: RegisterOrPair?, val statusflag: Statusflag?, val stack: Boolean)
class Block(override val name: String,
val address: Int?,
override var statements: MutableList<Statement>,
@@ -792,4 +789,3 @@ class DirectMemoryWrite(var addressExpression: Expression, override val position
fun accept(visitor: IAstVisitor) = visitor.visit(this)
fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
}
-81
View File
@@ -1,13 +1,8 @@
package prog8.compiler
import prog8.ast.base.ArrayDatatypes
import prog8.ast.base.DataType
import prog8.ast.base.StringDatatypes
import prog8.ast.expressions.AddressOf
import java.io.File
import java.io.InputStream
import java.nio.file.Path
import java.util.*
import kotlin.math.abs
enum class OutputType {
@@ -28,8 +23,6 @@ enum class ZeropageType {
DONTUSE
}
data class IntegerOrAddressOf(val integer: Int?, val addressOf: AddressOf?)
data class CompilationOptions(val output: OutputType,
val launcher: LauncherType,
val zeropage: ZeropageType,
@@ -73,77 +66,3 @@ fun loadAsmIncludeFile(filename: String, source: Path): String {
internal fun tryGetEmbeddedResource(name: String): InputStream? {
return object{}.javaClass.getResourceAsStream("/prog8lib/$name")
}
class HeapValues {
data class HeapValue(val type: DataType, val str: String?, val array: Array<IntegerOrAddressOf>?, val doubleArray: DoubleArray?) {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as HeapValue
return type==other.type && str==other.str && Arrays.equals(array, other.array) && Arrays.equals(doubleArray, other.doubleArray)
}
override fun hashCode(): Int = Objects.hash(str, array, doubleArray)
val arraysize: Int = array?.size ?: doubleArray?.size ?: 0
}
private val heap = mutableMapOf<Int, HeapValue>()
private var heapId = 1
fun size(): Int = heap.size
fun addString(type: DataType, str: String): Int {
if (str.length > 255)
throw IllegalArgumentException("string length must be 0-255")
// strings are 'interned' and shared if they're the isSameAs
val value = HeapValue(type, str, null, null)
val existing = heap.filter { it.value==value }.map { it.key }.firstOrNull()
if(existing!=null)
return existing
val newId = heapId++
heap[newId] = value
return newId
}
fun addIntegerArray(type: DataType, array: Array<IntegerOrAddressOf>): Int {
// arrays are never shared, don't check for existing
if(type !in ArrayDatatypes)
throw CompilerException("wrong array type")
val newId = heapId++
heap[newId] = HeapValue(type, null, array, null)
return newId
}
fun addDoublesArray(darray: DoubleArray): Int {
// arrays are never shared, don't check for existing
val newId = heapId++
heap[newId] = HeapValue(DataType.ARRAY_F, null, null, darray)
return newId
}
fun update(heapId: Int, str: String) {
val oldVal = heap[heapId] ?: throw IllegalArgumentException("heapId not found in heap")
if(oldVal.type in StringDatatypes) {
if (oldVal.str!!.length != str.length)
throw IllegalArgumentException("heap string length mismatch")
heap[heapId] = oldVal.copy(str = str)
}
else throw IllegalArgumentException("heap data type mismatch")
}
fun update(heapId: Int, heapval: HeapValue) {
if(heapId !in heap)
throw IllegalArgumentException("heapId not found in heap")
heap[heapId] = heapval
}
fun get(heapId: Int): HeapValue {
return heap[heapId] ?:
throw IllegalArgumentException("heapId $heapId not found in heap")
}
fun allEntries() = heap.entries
}
+3 -2
View File
@@ -25,7 +25,8 @@ class CompilationResult(val success: Boolean,
fun compileProgram(filepath: Path,
optimize: Boolean,
writeAssembly: Boolean): CompilationResult {
writeAssembly: Boolean,
outputDir: Path): CompilationResult {
lateinit var programAst: Program
var programName: String? = null
@@ -102,7 +103,7 @@ fun compileProgram(filepath: Path,
// asm generation directly from the Ast, no need for intermediate code
val zeropage = MachineDefinition.C64Zeropage(compilerOptions)
programAst.anonscopeVarsCleanup()
val assembly = AsmGen(programAst, compilerOptions, zeropage).compileToAssembly(optimize)
val assembly = AsmGen(programAst, zeropage, compilerOptions, outputDir).compileToAssembly(optimize)
assembly.assemble(compilerOptions)
programName = assembly.name
}
@@ -2,12 +2,14 @@ package prog8.compiler.target.c64
import prog8.compiler.CompilationOptions
import prog8.compiler.OutputType
import java.io.File
import java.nio.file.Path
import kotlin.system.exitProcess
class AssemblyProgram(val name: String) {
private val assemblyFile = "$name.asm"
private val viceMonListFile = "$name.vice-mon-list"
class AssemblyProgram(val name: String, outputDir: Path) {
private val assemblyFile = outputDir.resolve("$name.asm")
private val prgFile = outputDir.resolve("$name.prg")
private val binFile = outputDir.resolve("$name.bin")
private val viceMonListFile = outputDir.resolve("$name.vice-mon-list")
companion object {
// 6502 opcodes (including aliases and illegal opcodes), these cannot be used as variable or label names
@@ -27,21 +29,21 @@ class AssemblyProgram(val name: String) {
// add "-Wlong-branch" to see warnings about conversion of branch instructions to jumps
val command = mutableListOf("64tass", "--ascii", "--case-sensitive", "--long-branch",
"-Wall", "-Wno-strict-bool", "-Wno-shadow", "-Werror", "-Wno-error=long-branch",
"--dump-labels", "--vice-labels", "-l", viceMonListFile, "--no-monitor")
"--dump-labels", "--vice-labels", "-l", viceMonListFile.toString(), "--no-monitor")
val outFile = when(options.output) {
OutputType.PRG -> {
command.add("--cbm-prg")
println("\nCreating C-64 prg.")
"$name.prg"
prgFile
}
OutputType.RAW -> {
command.add("--nostart")
println("\nCreating raw binary.")
"$name.bin"
binFile
}
}
command.addAll(listOf("--output", outFile, assemblyFile))
command.addAll(listOf("--output", outFile.toString(), assemblyFile.toString()))
val proc = ProcessBuilder(command).inheritIO().start()
val result = proc.waitFor()
@@ -57,7 +59,7 @@ class AssemblyProgram(val name: String) {
// builds list of breakpoints, appends to monitor list file
val breakpoints = mutableListOf<String>()
val pattern = Regex("""al (\w+) \S+_prog8_breakpoint_\d+.?""") // gather breakpoints by the source label that"s generated for them
for(line in File(viceMonListFile).readLines()) {
for(line in viceMonListFile.toFile().readLines()) {
val match = pattern.matchEntire(line)
if(match!=null)
breakpoints.add("break \$" + match.groupValues[1])
@@ -66,6 +68,6 @@ class AssemblyProgram(val name: String) {
breakpoints.add(0, "; vice monitor breakpoint list now follows")
breakpoints.add(1, "; $num breakpoints have been defined")
breakpoints.add(2, "del")
File(viceMonListFile).appendText(breakpoints.joinToString("\n")+"\n")
viceMonListFile.toFile().appendText(breakpoints.joinToString("\n")+"\n")
}
}
@@ -14,18 +14,21 @@ import prog8.compiler.target.c64.MachineDefinition.ESTACK_LO_HEX
import prog8.compiler.target.c64.Petscii
import prog8.functions.BuiltinFunctions
import prog8.functions.FunctionSignature
import java.io.File
import java.math.RoundingMode
import java.util.*
import java.nio.file.Path
import java.time.LocalDate
import java.time.LocalDateTime
import java.util.ArrayDeque
import kotlin.math.absoluteValue
internal class AssemblyError(msg: String) : RuntimeException(msg)
internal class AsmGen(val program: Program,
val options: CompilationOptions,
val zeropage: Zeropage) {
internal class AsmGen(private val program: Program,
private val zeropage: Zeropage,
private val options: CompilationOptions,
private val outputDir: Path) {
private val assemblyLines = mutableListOf<String>()
private val globalFloatConsts = mutableMapOf<Double, String>() // all float values in the entire program (value -> varname)
@@ -37,8 +40,8 @@ internal class AsmGen(val program: Program,
private val functioncallAsmGen = FunctionCallAsmGen(program, this)
private val assignmentAsmGen = AssignmentAsmGen(program, this)
private val expressionsAsmGen = ExpressionsAsmGen(program, this)
internal val loopEndLabels = Stack<String>()
internal val loopContinueLabels = Stack<String>()
internal val loopEndLabels = ArrayDeque<String>()
internal val loopContinueLabels = ArrayDeque<String>()
internal fun compileToAssembly(optimize: Boolean): AssemblyProgram {
assemblyLines.clear()
@@ -62,17 +65,18 @@ internal class AsmGen(val program: Program,
}
}
File("${program.name}.asm").printWriter().use {
val outputFile = outputDir.resolve("${program.name}.asm").toFile()
outputFile.printWriter().use {
for (line in assemblyLines) { it.println(line) }
}
return AssemblyProgram(program.name)
return AssemblyProgram(program.name, outputDir)
}
private fun header() {
val ourName = this.javaClass.name
out("; 6502 assembly code for '${program.name}'")
out("; generated by $ourName on ${Date()}")
out("; generated by $ourName on ${LocalDateTime.now().withNano(0)}")
out("; assembler syntax is for the 64tasm cross-assembler")
out("; output options: output=${options.output} launcher=${options.launcher} zp=${options.zeropage}")
out("\n.cpu '6502'\n.enc 'none'\n")
@@ -88,7 +92,7 @@ internal class AsmGen(val program: Program,
throw AssemblyError("BASIC output must have load address $0801")
out("; ---- basic program with sys call ----")
out("* = ${program.actualLoadAddress.toHex()}")
val year = Calendar.getInstance().get(Calendar.YEAR)
val year = LocalDate.now().year
out(" .word (+), $year")
out(" .null $9e, format(' %d ', _prog8_entrypoint), $3a, $8f, ' prog8 by idj'")
out("+\t.word 0")
@@ -820,8 +824,7 @@ internal class AsmGen(val program: Program,
}
internal fun translateArrayIndexIntoA(expr: ArrayIndexedExpression) {
val index = expr.arrayspec.index
when (index) {
when (val index = expr.arrayspec.index) {
is NumericLiteralValue -> throw AssemblyError("this should be optimized directly")
is RegisterExpr -> {
when (index.register) {
@@ -101,7 +101,7 @@ fun optimizeSameAssignments(linesByFourteen: List<List<IndexedValue<String>>>):
// optimize sequential assignments of the isSameAs value to various targets (bytes, words, floats)
// the float one is the one that requires 2*7=14 lines of code to check...
// @todo a better place to do this is in the Compiler instead and transform the Ast, and never even create the inefficient asm in the first place...
// @todo a better place to do this is in the Compiler instead and transform the Ast, or the AsmGen, and never even create the inefficient asm in the first place...
val removeLines = mutableListOf<Int>()
for (pair in linesByFourteen) {
@@ -9,7 +9,6 @@ import prog8.ast.base.WordDatatypes
import prog8.ast.expressions.*
import prog8.ast.statements.AssignTarget
import prog8.ast.statements.FunctionCallStatement
import prog8.compiler.target.c64.MachineDefinition
import prog8.compiler.target.c64.MachineDefinition.C64Zeropage
import prog8.compiler.target.c64.MachineDefinition.ESTACK_HI_HEX
import prog8.compiler.target.c64.MachineDefinition.ESTACK_HI_PLUS1_HEX
@@ -122,24 +121,6 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
translateFunctionArguments(fcall.arglist, func)
asmgen.out(" jsr c64flt.func_$functionName")
}
/*
TODO this was the old code for bit rotations:
Opcode.SHL_BYTE -> AsmFragment(" asl $variable+$index", 8)
Opcode.SHR_UBYTE -> AsmFragment(" lsr $variable+$index", 8)
Opcode.SHR_SBYTE -> AsmFragment(" lda $variable+$index | asl a | ror $variable+$index")
Opcode.SHL_WORD -> AsmFragment(" asl $variable+${index * 2 + 1} | rol $variable+${index * 2}", 8)
Opcode.SHR_UWORD -> AsmFragment(" lsr $variable+${index * 2 + 1} | ror $variable+${index * 2}", 8)
Opcode.SHR_SWORD -> AsmFragment(" lda $variable+${index * 2 + 1} | asl a | ror $variable+${index * 2 + 1} | ror $variable+${index * 2}", 8)
Opcode.ROL_BYTE -> AsmFragment(" rol $variable+$index", 8)
Opcode.ROR_BYTE -> AsmFragment(" ror $variable+$index", 8)
Opcode.ROL_WORD -> AsmFragment(" rol $variable+${index * 2 + 1} | rol $variable+${index * 2}", 8)
Opcode.ROR_WORD -> AsmFragment(" ror $variable+${index * 2 + 1} | ror $variable+${index * 2}", 8)
Opcode.ROL2_BYTE -> AsmFragment(" lda $variable+$index | cmp #\$80 | rol $variable+$index", 8)
Opcode.ROR2_BYTE -> AsmFragment(" lda $variable+$index | lsr a | bcc + | ora #\$80 |+ | sta $variable+$index", 10)
Opcode.ROL2_WORD -> AsmFragment(" asl $variable+${index * 2 + 1} | rol $variable+${index * 2} | bcc + | inc $variable+${index * 2 + 1} |+", 20)
Opcode.ROR2_WORD -> AsmFragment(" lsr $variable+${index * 2 + 1} | ror $variable+${index * 2} | bcc + | lda $variable+${index * 2 + 1} | ora #\$80 | sta $variable+${index * 2 + 1} |+", 30)
*/
"lsl" -> {
// in-place
val what = fcall.arglist.single()
@@ -157,20 +138,35 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
is IdentifierReference -> asmgen.out(" asl ${asmgen.asmIdentifierName(what)}")
is DirectMemoryRead -> {
if (what.addressExpression is NumericLiteralValue) {
asmgen.out(" asl ${(what.addressExpression as NumericLiteralValue).number.toHex()}")
val number = (what.addressExpression as NumericLiteralValue).number
asmgen.out(" asl ${number.toHex()}")
} else {
TODO("lsl memory byte $what")
asmgen.translateExpression(what.addressExpression)
asmgen.out("""
inx
lda $ESTACK_LO_HEX,x
sta (+) + 1
lda $ESTACK_HI_HEX,x
sta (+) + 2
+ asl 0 ; modified
""")
}
}
is ArrayIndexedExpression -> {
TODO("lsl byte array $what")
asmgen.translateExpression(what.identifier)
asmgen.translateExpression(what.arrayspec.index)
asmgen.out(" jsr prog8_lib.lsl_array_b")
}
else -> throw AssemblyError("weird type")
}
}
in WordDatatypes -> {
when (what) {
is ArrayIndexedExpression -> TODO("lsl sbyte $what")
is ArrayIndexedExpression -> {
asmgen.translateExpression(what.identifier)
asmgen.translateExpression(what.arrayspec.index)
asmgen.out(" jsr prog8_lib.lsl_array_w")
}
is IdentifierReference -> {
val variable = asmgen.asmIdentifierName(what)
asmgen.out(" asl $variable | rol $variable+1")
@@ -198,22 +194,35 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
is IdentifierReference -> asmgen.out(" lsr ${asmgen.asmIdentifierName(what)}")
is DirectMemoryRead -> {
if (what.addressExpression is NumericLiteralValue) {
asmgen.out(" lsr ${(what.addressExpression as NumericLiteralValue).number.toHex()}")
val number = (what.addressExpression as NumericLiteralValue).number
asmgen.out(" lsr ${number.toHex()}")
} else {
TODO("lsr memory byte $what")
asmgen.translateExpression(what.addressExpression)
asmgen.out("""
inx
lda $ESTACK_LO_HEX,x
sta (+) + 1
lda $ESTACK_HI_HEX,x
sta (+) + 2
+ lsr 0 ; modified
""")
}
}
is ArrayIndexedExpression -> {
TODO("lsr byte array $what")
asmgen.translateExpression(what.identifier)
asmgen.translateExpression(what.arrayspec.index)
asmgen.out(" jsr prog8_lib.lsr_array_ub")
}
else -> throw AssemblyError("weird type")
}
}
DataType.BYTE -> {
when (what) {
is ArrayIndexedExpression -> TODO("lsr sbyte $what")
is DirectMemoryRead -> TODO("lsr sbyte $what")
is RegisterExpr -> TODO("lsr sbyte $what")
is ArrayIndexedExpression -> {
asmgen.translateExpression(what.identifier)
asmgen.translateExpression(what.arrayspec.index)
asmgen.out(" jsr prog8_lib.lsr_array_b")
}
is IdentifierReference -> {
val variable = asmgen.asmIdentifierName(what)
asmgen.out(" lda $variable | asl a | ror $variable")
@@ -223,7 +232,11 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
}
DataType.UWORD -> {
when (what) {
is ArrayIndexedExpression -> TODO("lsr uword $what")
is ArrayIndexedExpression -> {
asmgen.translateExpression(what.identifier)
asmgen.translateExpression(what.arrayspec.index)
asmgen.out(" jsr prog8_lib.lsr_array_uw")
}
is IdentifierReference -> {
val variable = asmgen.asmIdentifierName(what)
asmgen.out(" lsr $variable+1 | ror $variable")
@@ -233,7 +246,11 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
}
DataType.WORD -> {
when (what) {
is ArrayIndexedExpression -> TODO("lsr sword $what")
is ArrayIndexedExpression -> {
asmgen.translateExpression(what.identifier)
asmgen.translateExpression(what.arrayspec.index)
asmgen.out(" jsr prog8_lib.lsr_array_w")
}
is IdentifierReference -> {
val variable = asmgen.asmIdentifierName(what)
asmgen.out(" lda $variable+1 | asl a | ror $variable+1 | ror $variable")
@@ -250,10 +267,55 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
val dt = what.inferType(program)
when (dt.typeOrElse(DataType.STRUCT)) {
DataType.UBYTE -> {
TODO("rol ubyte")
when(what) {
is ArrayIndexedExpression -> {
asmgen.translateExpression(what.identifier)
asmgen.translateExpression(what.arrayspec.index)
asmgen.out(" jsr prog8_lib.rol_array_ub")
}
is DirectMemoryRead -> {
if (what.addressExpression is NumericLiteralValue) {
val number = (what.addressExpression as NumericLiteralValue).number
asmgen.out(" rol ${number.toHex()}")
} else {
asmgen.translateExpression(what.addressExpression)
asmgen.out("""
inx
lda $ESTACK_LO_HEX,x
sta (+) + 1
lda $ESTACK_HI_HEX,x
sta (+) + 2
+ rol 0 ; modified
""")
}
}
is RegisterExpr -> {
when(what.register) {
Register.A -> asmgen.out(" rol a")
Register.X -> asmgen.out(" txa | rol a | tax")
Register.Y -> asmgen.out(" tya | rol a | tay")
}
}
is IdentifierReference -> {
val variable = asmgen.asmIdentifierName(what)
asmgen.out(" rol $variable")
}
else -> throw AssemblyError("weird type")
}
}
DataType.UWORD -> {
TODO("rol uword")
when(what) {
is ArrayIndexedExpression -> {
asmgen.translateExpression(what.identifier)
asmgen.translateExpression(what.arrayspec.index)
asmgen.out(" jsr prog8_lib.rol_array_uw")
}
is IdentifierReference -> {
val variable = asmgen.asmIdentifierName(what)
asmgen.out(" rol $variable | rol $variable+1")
}
else -> throw AssemblyError("weird type")
}
}
else -> throw AssemblyError("weird type")
}
@@ -264,10 +326,48 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
val dt = what.inferType(program)
when (dt.typeOrElse(DataType.STRUCT)) {
DataType.UBYTE -> {
TODO("rol2 ubyte")
when(what) {
is ArrayIndexedExpression -> {
asmgen.translateExpression(what.identifier)
asmgen.translateExpression(what.arrayspec.index)
asmgen.out(" jsr prog8_lib.rol2_array_ub")
}
is DirectMemoryRead -> {
if (what.addressExpression is NumericLiteralValue) {
val number = (what.addressExpression as NumericLiteralValue).number
asmgen.out(" lda ${number.toHex()} | cmp #\$80 | rol a | sta ${number.toHex()}")
} else {
asmgen.translateExpression(what.addressExpression)
asmgen.out(" jsr prog8_lib.rol2_mem_ub")
}
}
is RegisterExpr -> {
when(what.register) {
Register.A -> asmgen.out(" cmp #\$80 | rol a ")
Register.X -> asmgen.out(" txa | cmp #\$80 | rol a | tax")
Register.Y -> asmgen.out(" tya | cmp #\$80 | rol a | tay")
}
}
is IdentifierReference -> {
val variable = asmgen.asmIdentifierName(what)
asmgen.out(" lda $variable | cmp #\$80 | rol a | sta $variable")
}
else -> throw AssemblyError("weird type")
}
}
DataType.UWORD -> {
TODO("rol2 uword")
when(what) {
is ArrayIndexedExpression -> {
asmgen.translateExpression(what.identifier)
asmgen.translateExpression(what.arrayspec.index)
asmgen.out(" jsr prog8_lib.rol2_array_uw")
}
is IdentifierReference -> {
val variable = asmgen.asmIdentifierName(what)
asmgen.out(" asl $variable | rol $variable+1 | bcc + | inc $variable |+ ")
}
else -> throw AssemblyError("weird type")
}
}
else -> throw AssemblyError("weird type")
}
@@ -278,10 +378,54 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
val dt = what.inferType(program)
when (dt.typeOrElse(DataType.STRUCT)) {
DataType.UBYTE -> {
TODO("ror ubyte")
when(what) {
is ArrayIndexedExpression -> {
asmgen.translateExpression(what.identifier)
asmgen.translateExpression(what.arrayspec.index)
asmgen.out(" jsr prog8_lib.ror_array_ub")
}
is DirectMemoryRead -> {
if (what.addressExpression is NumericLiteralValue) {
val number = (what.addressExpression as NumericLiteralValue).number
asmgen.out(" ror ${number.toHex()}")
} else {
asmgen.translateExpression(what.addressExpression)
asmgen.out("""
inx
lda $ESTACK_LO_HEX,x
sta (+) + 1
lda $ESTACK_HI_HEX,x
sta (+) + 2
+ ror 0 ; modified
""") }
}
is RegisterExpr -> {
when(what.register) {
Register.A -> asmgen.out(" ror a")
Register.X -> asmgen.out(" txa | ror a | tax")
Register.Y -> asmgen.out(" tya | ror a | tay")
}
}
is IdentifierReference -> {
val variable = asmgen.asmIdentifierName(what)
asmgen.out(" ror $variable")
}
else -> throw AssemblyError("weird type")
}
}
DataType.UWORD -> {
TODO("ror uword")
when(what) {
is ArrayIndexedExpression -> {
asmgen.translateExpression(what.identifier)
asmgen.translateExpression(what.arrayspec.index)
asmgen.out(" jsr prog8_lib.ror_array_uw")
}
is IdentifierReference -> {
val variable = asmgen.asmIdentifierName(what)
asmgen.out(" ror $variable+1 | ror $variable")
}
else -> throw AssemblyError("weird type")
}
}
else -> throw AssemblyError("weird type")
}
@@ -292,10 +436,48 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
val dt = what.inferType(program)
when (dt.typeOrElse(DataType.STRUCT)) {
DataType.UBYTE -> {
TODO("ror2 ubyte")
when(what) {
is ArrayIndexedExpression -> {
asmgen.translateExpression(what.identifier)
asmgen.translateExpression(what.arrayspec.index)
asmgen.out(" jsr prog8_lib.ror2_array_ub")
}
is DirectMemoryRead -> {
if (what.addressExpression is NumericLiteralValue) {
val number = (what.addressExpression as NumericLiteralValue).number
asmgen.out(" lda ${number.toHex()} | lsr a | bcc + | ora #\$80 |+ | sta ${number.toHex()}")
} else {
asmgen.translateExpression(what.addressExpression)
asmgen.out(" jsr prog8_lib.ror2_mem_ub")
}
}
is RegisterExpr -> {
when(what.register) {
Register.A -> asmgen.out(" lsr a | bcc + | ora #\$80 |+ ")
Register.X -> asmgen.out(" txa | lsr a | bcc + | ora #\$80 |+ tax ")
Register.Y -> asmgen.out(" tya | lsr a | bcc + | ora #\$80 |+ tay ")
}
}
is IdentifierReference -> {
val variable = asmgen.asmIdentifierName(what)
asmgen.out(" lda $variable | lsr a | bcc + | ora #\$80 |+ | sta $variable")
}
else -> throw AssemblyError("weird type")
}
}
DataType.UWORD -> {
TODO("ror2 uword")
when(what) {
is ArrayIndexedExpression -> {
asmgen.translateExpression(what.identifier)
asmgen.translateExpression(what.arrayspec.index)
asmgen.out(" jsr prog8_lib.ror2_array_uw")
}
is IdentifierReference -> {
val variable = asmgen.asmIdentifierName(what)
asmgen.out(" lsr $variable+1 | ror $variable | bcc + | lda $variable+1 | ora #\$80 | sta $variable+1 |+ ")
}
else -> throw AssemblyError("weird type")
}
}
else -> throw AssemblyError("weird type")
}
@@ -368,6 +550,15 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
}
}
}
"rsave" -> {
// save cpu status flag and all registers A, X, Y.
// see http://6502.org/tutorials/register_preservation.html
asmgen.out(" php | sta ${C64Zeropage.SCRATCH_REG} | pha | txa | pha | tya | pha | lda ${C64Zeropage.SCRATCH_REG}")
}
"rrestore" -> {
// restore all registers and cpu status flag
asmgen.out(" pla | tay | pla | tax | pla | plp")
}
else -> {
translateFunctionArguments(fcall.arglist, func)
asmgen.out(" jsr prog8_lib.func_$functionName")
@@ -164,10 +164,12 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
DataType.UBYTE, DataType.BYTE -> {
asmgen.out(" lda $varname | sta ${MachineDefinition.ESTACK_LO_HEX},x | dex")
}
DataType.UWORD, DataType.WORD, in ArrayDatatypes, in StringDatatypes -> {
// (for arrays and strings, push their address)
DataType.UWORD, DataType.WORD -> {
asmgen.out(" lda $varname | sta ${MachineDefinition.ESTACK_LO_HEX},x | lda $varname+1 | sta ${MachineDefinition.ESTACK_HI_HEX},x | dex")
}
in ArrayDatatypes, in StringDatatypes -> {
asmgen.out(" lda #<$varname | sta ${MachineDefinition.ESTACK_LO_HEX},x | lda #>$varname | sta ${MachineDefinition.ESTACK_HI_HEX},x | dex")
}
DataType.FLOAT -> {
asmgen.out(" lda #<$varname | ldy #>$varname| jsr c64flt.push_float")
}
@@ -8,10 +8,15 @@ import prog8.ast.expressions.RangeExpr
import prog8.ast.statements.AssignTarget
import prog8.ast.statements.Assignment
import prog8.ast.statements.ForLoop
import prog8.compiler.target.c64.MachineDefinition
import prog8.compiler.target.c64.MachineDefinition.ESTACK_LO_HEX
import prog8.compiler.target.c64.MachineDefinition.ESTACK_LO_PLUS1_HEX
import prog8.compiler.target.c64.MachineDefinition.ESTACK_HI_PLUS1_HEX
import prog8.compiler.toHex
import kotlin.math.absoluteValue
// todo choose more efficient comparisons to avoid needless lda's
// todo optimize common case step == 2 / -2
internal class ForLoopsAsmGen(private val program: Program, private val asmgen: AsmGen) {
@@ -25,8 +30,6 @@ internal class ForLoopsAsmGen(private val program: Program, private val asmgen:
if(range==null) {
translateForOverNonconstRange(stmt, iterableDt.typeOrElse(DataType.STRUCT), stmt.iterable as RangeExpr)
} else {
if (range.isEmpty())
throw AssemblyError("empty range")
translateForOverConstRange(stmt, iterableDt.typeOrElse(DataType.STRUCT), range)
}
}
@@ -41,68 +44,132 @@ internal class ForLoopsAsmGen(private val program: Program, private val asmgen:
val loopLabel = asmgen.makeLabel("for_loop")
val endLabel = asmgen.makeLabel("for_end")
val continueLabel = asmgen.makeLabel("for_continue")
val counterLabel = asmgen.makeLabel("for_counter")
asmgen.loopEndLabels.push(endLabel)
asmgen.loopContinueLabels.push(continueLabel)
val stepsize=range.step.constValue(program)?.number
when (stepsize) {
1 -> {
when(iterableDt) {
DataType.ARRAY_B, DataType.ARRAY_UB -> {
if (stmt.loopRegister != null) {
// loop register over range
if(stmt.loopRegister!= Register.A)
throw AssemblyError("can only use A")
asmgen.translateExpression(range.to)
asmgen.translateExpression(range.from)
asmgen.out("""
val stepsize=range.step.constValue(program)!!.number.toInt()
when(iterableDt) {
DataType.ARRAY_B, DataType.ARRAY_UB -> {
if (stepsize==1 || stepsize==-1) {
// bytes, step 1 or -1
val incdec = if(stepsize==1) "inc" else "dec"
if (stmt.loopRegister != null) {
// loop register over range
if(stmt.loopRegister!= Register.A)
throw AssemblyError("can only use A")
asmgen.translateExpression(range.to)
asmgen.translateExpression(range.from)
asmgen.out("""
inx
lda ${MachineDefinition.ESTACK_LO_HEX},x
lda ${ESTACK_LO_HEX},x
sta $loopLabel+1
$loopLabel lda #0 ; modified""")
asmgen.translate(stmt.body)
asmgen.out("""
$continueLabel lda $loopLabel+1
cmp $ESTACK_LO_PLUS1_HEX,x
beq $endLabel
$incdec $loopLabel+1
jmp $loopLabel
$endLabel inx""")
} else {
// loop over byte range via loopvar
val varname = asmgen.asmIdentifierName(stmt.loopVar!!)
asmgen.translateExpression(range.to)
asmgen.translateExpression(range.from)
asmgen.out("""
inx
lda ${ESTACK_LO_HEX},x
sta $varname
$loopLabel""")
asmgen.translate(stmt.body)
asmgen.out("""
$continueLabel lda $varname
cmp $ESTACK_LO_PLUS1_HEX,x
beq $endLabel
$incdec $varname
jmp $loopLabel
$endLabel inx""")
}
}
else {
// bytes, step >= 2 or <= -2
if (stmt.loopRegister != null) {
// loop register over range
if(stmt.loopRegister!= Register.A)
throw AssemblyError("can only use A")
asmgen.translateExpression(range.to)
asmgen.translateExpression(range.from)
asmgen.out("""
inx
lda ${MachineDefinition.ESTACK_LO_HEX},x
sec
sbc $loopLabel+1
adc #0
sta $counterLabel
lda ${ESTACK_LO_HEX},x
sta $loopLabel+1
$loopLabel lda #0 ; modified""")
asmgen.translate(stmt.body)
asmgen.translate(stmt.body)
asmgen.out("""
$continueLabel lda $loopLabel+1""")
if(stepsize>0) {
asmgen.out("""
$continueLabel dec $counterLabel
beq $endLabel
inc $loopLabel+1
jmp $loopLabel
$counterLabel .byte 0
$endLabel""")
clc
adc #$stepsize
sta $loopLabel+1
cmp $ESTACK_LO_PLUS1_HEX,x
bcc $loopLabel
beq $loopLabel""")
} else {
// loop over byte range via loopvar
val varname = asmgen.asmIdentifierName(stmt.loopVar!!)
asmgen.translateExpression(range.to)
asmgen.translateExpression(range.from)
asmgen.out("""
sec
sbc #${stepsize.absoluteValue}
sta $loopLabel+1
cmp $ESTACK_LO_PLUS1_HEX,x
bcs $loopLabel""")
}
asmgen.out("""
$endLabel inx""")
} else {
// loop over byte range via loopvar
val varname = asmgen.asmIdentifierName(stmt.loopVar!!)
asmgen.translateExpression(range.to)
asmgen.translateExpression(range.from)
asmgen.out("""
inx
lda ${MachineDefinition.ESTACK_LO_HEX},x
lda ${ESTACK_LO_HEX},x
sta $varname
inx
lda ${MachineDefinition.ESTACK_LO_HEX},x
sec
sbc $varname
adc #0
sta $counterLabel
$loopLabel""")
asmgen.translate(stmt.body)
asmgen.translate(stmt.body)
asmgen.out("""
$continueLabel lda $varname""")
if(stepsize>0) {
asmgen.out("""
$continueLabel dec $counterLabel
beq $endLabel
inc $varname
jmp $loopLabel
$counterLabel .byte 0
$endLabel""")
clc
adc #$stepsize
sta $varname
cmp $ESTACK_LO_PLUS1_HEX,x
bcc $loopLabel
beq $loopLabel""")
} else {
asmgen.out("""
sec
sbc #${stepsize.absoluteValue}
sta $varname
cmp $ESTACK_LO_PLUS1_HEX,x
bcs $loopLabel""")
}
asmgen.out("""
$endLabel inx""")
}
DataType.ARRAY_UW, DataType.ARRAY_W -> {
}
}
DataType.ARRAY_W, DataType.ARRAY_UW -> {
when {
// words, step 1 or -1
stepsize == 1 || stepsize == -1 -> {
asmgen.translateExpression(range.to)
asmgen.out(" inc ${MachineDefinition.ESTACK_LO_HEX}+1,x | bne + | inc ${MachineDefinition.ESTACK_HI_HEX}+1,x |+ ")
val varname = asmgen.asmIdentifierName(stmt.loopVar!!)
val assignLoopvar = Assignment(AssignTarget(null, stmt.loopVar, null, null, stmt.loopVar!!.position),
null, range.from, range.position)
@@ -111,81 +178,34 @@ $endLabel""")
asmgen.out(loopLabel)
asmgen.translate(stmt.body)
asmgen.out("""
inc $varname
lda $varname+1
cmp $ESTACK_HI_PLUS1_HEX,x
bne +
lda $varname
cmp $ESTACK_LO_PLUS1_HEX,x
beq $endLabel""")
if(stepsize==1) {
asmgen.out("""
+ inc $varname
bne +
inc $varname+1
+ lda ${MachineDefinition.ESTACK_HI_HEX}+1,x
cmp $varname+1
""")
} else {
asmgen.out("""
+ lda $varname
bne +
lda ${MachineDefinition.ESTACK_LO_HEX}+1,x
cmp $varname
beq $endLabel
dec $varname+1
+ dec $varname""")
}
asmgen.out("""
+ jmp $loopLabel
$endLabel inx""")
}
else -> throw AssemblyError("range expression can only be byte or word")
}
}
-1 -> {
when(iterableDt){
DataType.ARRAY_B, DataType.ARRAY_UB -> {
if (stmt.loopRegister != null) {
// loop register over range
if(stmt.loopRegister!= Register.A)
throw AssemblyError("can only use A")
asmgen.translateExpression(range.from)
asmgen.translateExpression(range.to)
asmgen.out("""
inx
lda ${MachineDefinition.ESTACK_LO_HEX}+1,x
sta $loopLabel+1
sec
sbc ${MachineDefinition.ESTACK_LO_HEX},x
adc #0
sta $counterLabel
inx
$loopLabel lda #0 ; modified""")
asmgen.translate(stmt.body)
asmgen.out("""
$continueLabel dec $counterLabel
beq $endLabel
dec $loopLabel+1
jmp $loopLabel
$counterLabel .byte 0
$endLabel""")
} else {
// loop over byte range via loopvar
val varname = asmgen.asmIdentifierName(stmt.loopVar!!)
asmgen.translateExpression(range.from)
asmgen.translateExpression(range.to)
asmgen.out("""
inx
lda ${MachineDefinition.ESTACK_LO_HEX}+1,x
sta $varname
sec
sbc ${MachineDefinition.ESTACK_LO_HEX},x
adc #0
sta $counterLabel
inx
$loopLabel""")
asmgen.translate(stmt.body)
asmgen.out("""
$continueLabel dec $counterLabel
beq $endLabel
dec $varname
jmp $loopLabel
$counterLabel .byte 0
$endLabel""")
}
}
DataType.ARRAY_UW, DataType.ARRAY_W -> {
stepsize > 0 -> {
// (u)words, step >= 2
asmgen.translateExpression(range.to)
asmgen.out("""
lda ${MachineDefinition.ESTACK_LO_HEX}+1,x
bne +
dec ${MachineDefinition.ESTACK_HI_HEX}+1,x
+ dec ${MachineDefinition.ESTACK_LO_HEX}+1,x
""")
val varname = asmgen.asmIdentifierName(stmt.loopVar!!)
val assignLoopvar = Assignment(AssignTarget(null, stmt.loopVar, null, null, stmt.loopVar!!.position),
null, range.from, range.position)
@@ -193,29 +213,97 @@ $endLabel""")
asmgen.translate(assignLoopvar)
asmgen.out(loopLabel)
asmgen.translate(stmt.body)
asmgen.out("""
if (iterableDt == DataType.ARRAY_UW) {
asmgen.out("""
lda $varname
bne +
dec $varname+1
+ dec $varname
lda ${MachineDefinition.ESTACK_HI_HEX}+1,x
clc
adc #<$stepsize
sta $varname
lda $varname+1
adc #>$stepsize
sta $varname+1
lda $ESTACK_HI_PLUS1_HEX,x
cmp $varname+1
bne +
lda ${MachineDefinition.ESTACK_LO_HEX}+1,x
cmp $varname
beq $endLabel
+ jmp $loopLabel
bcc $endLabel
bne $loopLabel
lda $varname
cmp $ESTACK_LO_PLUS1_HEX,x
bcc $endLabel
bcs $loopLabel
$endLabel inx""")
} else {
asmgen.out("""
lda $varname
clc
adc #<$stepsize
sta $varname
lda $varname+1
adc #>$stepsize
sta $varname+1
lda $ESTACK_LO_PLUS1_HEX,x
cmp $varname
lda $ESTACK_HI_PLUS1_HEX,x
sbc $varname+1
bvc +
eor #$80
+ bpl $loopLabel
$endLabel inx""")
}
}
else -> {
// (u)words, step <= -2
asmgen.translateExpression(range.to)
val varname = asmgen.asmIdentifierName(stmt.loopVar!!)
val assignLoopvar = Assignment(AssignTarget(null, stmt.loopVar, null, null, stmt.loopVar!!.position),
null, range.from, range.position)
assignLoopvar.linkParents(stmt)
asmgen.translate(assignLoopvar)
asmgen.out(loopLabel)
asmgen.translate(stmt.body)
if(iterableDt==DataType.ARRAY_UW) {
asmgen.out("""
lda $varname
sec
sbc #<${stepsize.absoluteValue}
sta $varname
lda $varname+1
sbc #>${stepsize.absoluteValue}
sta $varname+1
cmp $ESTACK_HI_PLUS1_HEX,x
bcc $endLabel
bne $loopLabel
lda $varname
cmp $ESTACK_LO_PLUS1_HEX,x
bcs $loopLabel
$endLabel inx""")
} else {
asmgen.out("""
lda $varname
sec
sbc #<${stepsize.absoluteValue}
sta $varname
pha
lda $varname+1
sbc #>${stepsize.absoluteValue}
sta $varname+1
pla
cmp $ESTACK_LO_PLUS1_HEX,x
lda $varname+1
sbc $ESTACK_HI_PLUS1_HEX,x
bvc +
eor #$80
+ bpl $loopLabel
$endLabel inx""")
}
}
else -> throw AssemblyError("range expression can only be byte or word")
}
}
else -> when (iterableDt) {
DataType.ARRAY_UB, DataType.ARRAY_B -> TODO("non-const forloop bytes, step >1: $stepsize")
DataType.ARRAY_UW, DataType.ARRAY_W -> TODO("non-const forloop words, step >1: $stepsize")
else -> throw AssemblyError("range expression can only be byte or word")
}
else -> throw AssemblyError("range expression can only be byte or word")
}
asmgen.loopEndLabels.pop()
asmgen.loopContinueLabels.pop()
}
@@ -322,6 +410,8 @@ $endLabel""")
private fun translateForOverConstRange(stmt: ForLoop, iterableDt: DataType, range: IntProgression) {
// TODO: optimize loop code when the range is < 256 iterations, don't need a separate counter in such cases
if (range.isEmpty())
throw AssemblyError("empty range")
val loopLabel = asmgen.makeLabel("for_loop")
val endLabel = asmgen.makeLabel("for_end")
val continueLabel = asmgen.makeLabel("for_continue")
@@ -544,11 +634,59 @@ $endLabel""")
}
range.step >= 2 -> {
// word, step >= 2
TODO("for, word, step>=2")
// note: range.last has already been adjusted by kotlin itself to actually be the last value of the sequence
val lastValue = range.last+range.step
asmgen.out("""
lda #<${range.first}
ldy #>${range.first}
sta $varname
sty $varname+1
$loopLabel""")
asmgen.translate(stmt.body)
asmgen.out("""
$continueLabel clc
lda $varname
adc #<${range.step}
sta $varname
lda $varname+1
adc #>${range.step}
sta $varname+1
lda $varname
cmp #<$lastValue
bne +
lda $varname+1
cmp #>$lastValue
beq $endLabel
+ jmp $loopLabel
$endLabel""")
}
else -> {
// step <= -2
TODO("for, word, step<=-2")
// note: range.last has already been adjusted by kotlin itself to actually be the last value of the sequence
val lastValue = range.last+range.step
asmgen.out("""
lda #<${range.first}
ldy #>${range.first}
sta $varname
sty $varname+1
$loopLabel""")
asmgen.translate(stmt.body)
asmgen.out("""
$continueLabel sec
lda $varname
sbc #<${range.step.absoluteValue}
sta $varname
lda $varname+1
sbc #>${range.step.absoluteValue}
sta $varname+1
lda $varname
cmp #<$lastValue
bne +
lda $varname+1
cmp #>$lastValue
beq $endLabel
+ jmp $loopLabel
$endLabel""")
}
}
}
@@ -57,8 +57,7 @@ internal class PostIncrDecrAsmGen(private val program: Program, private val asmg
}
}
targetMemory!=null -> {
val addressExpr = targetMemory.addressExpression
when (addressExpr) {
when (val addressExpr = targetMemory.addressExpression) {
is NumericLiteralValue -> {
val what = addressExpr.number.toHex()
asmgen.out(if(incr) " inc $what" else " dec $what")
@@ -88,32 +88,7 @@ val BuiltinFunctions = mapOf(
BuiltinFunctionParam("address", IterableDatatypes + DataType.UWORD),
BuiltinFunctionParam("numwords", setOf(DataType.UWORD)),
BuiltinFunctionParam("wordvalue", setOf(DataType.UWORD, DataType.WORD))), null),
"strlen" to FunctionSignature(true, listOf(BuiltinFunctionParam("string", StringDatatypes)), DataType.UBYTE, ::builtinStrlen),
// TODO clean up these vm-specific functions
"vm_write_memchr" to FunctionSignature(false, listOf(BuiltinFunctionParam("address", setOf(DataType.UWORD))), null),
"vm_write_memstr" to FunctionSignature(false, listOf(BuiltinFunctionParam("address", setOf(DataType.UWORD))), null),
"vm_write_num" to FunctionSignature(false, listOf(BuiltinFunctionParam("number", NumericDatatypes)), null),
"vm_write_char" to FunctionSignature(false, listOf(BuiltinFunctionParam("char", setOf(DataType.UBYTE))), null),
"vm_write_str" to FunctionSignature(false, listOf(BuiltinFunctionParam("string", StringDatatypes)), null),
"vm_input_str" to FunctionSignature(false, listOf(BuiltinFunctionParam("intovar", StringDatatypes)), null),
"vm_gfx_clearscr" to FunctionSignature(false, listOf(BuiltinFunctionParam("color", setOf(DataType.UBYTE))), null),
"vm_gfx_pixel" to FunctionSignature(false, listOf(
BuiltinFunctionParam("x", IntegerDatatypes),
BuiltinFunctionParam("y", IntegerDatatypes),
BuiltinFunctionParam("color", IntegerDatatypes)), null),
"vm_gfx_line" to FunctionSignature(false, listOf(
BuiltinFunctionParam("x1", IntegerDatatypes),
BuiltinFunctionParam("y1", IntegerDatatypes),
BuiltinFunctionParam("x2", IntegerDatatypes),
BuiltinFunctionParam("y2", IntegerDatatypes),
BuiltinFunctionParam("color", IntegerDatatypes)), null),
"vm_gfx_text" to FunctionSignature(false, listOf(
BuiltinFunctionParam("x", IntegerDatatypes),
BuiltinFunctionParam("y", IntegerDatatypes),
BuiltinFunctionParam("color", IntegerDatatypes),
BuiltinFunctionParam("text", StringDatatypes)),
null)
"strlen" to FunctionSignature(true, listOf(BuiltinFunctionParam("string", StringDatatypes)), DataType.UBYTE, ::builtinStrlen)
)
fun builtinMax(array: List<Number>): Number = array.maxBy { it.toDouble() }!!
@@ -145,8 +120,7 @@ fun builtinFunctionReturnType(function: String, args: List<Expression>, program:
val idt = arglist.inferType(program)
if(!idt.isKnown)
throw FatalAstException("couldn't determine type of iterable $arglist")
val dt = idt.typeOrElse(DataType.STRUCT)
return when(dt) {
return when(val dt = idt.typeOrElse(DataType.STRUCT)) {
in NumericDatatypes -> dt
in StringDatatypes -> dt
in ArrayDatatypes -> ArrayElementTypes.getValue(dt)
@@ -199,7 +173,7 @@ fun builtinFunctionReturnType(function: String, args: List<Expression>, program:
}
class NotConstArgumentException: AstException("not a const argument to a built-in function") // TODO: ugly, remove throwing exceptions for control flow
class NotConstArgumentException: AstException("not a const argument to a built-in function")
private fun oneDoubleArg(args: List<Expression>, position: Position, program: Program, function: (arg: Double)->Number): NumericLiteralValue {
+43 -14
View File
@@ -124,7 +124,7 @@ class ConstantFolding(private val program: Program) : IAstModifyingVisitor {
// create the array itself, filled with the fillvalue.
val array = Array(size) {fillvalue}.map { NumericLiteralValue.optimalInteger(it, numericLv.position) as Expression}.toTypedArray()
val refValue = ArrayLiteralValue(decl.datatype, array, position = numericLv.position)
refValue.addToHeap(program.heap)
refValue.addToHeap()
decl.value = refValue
refValue.parent=decl
optimizationsDone++
@@ -146,7 +146,7 @@ class ConstantFolding(private val program: Program) : IAstModifyingVisitor {
// create the array itself, filled with the fillvalue.
val array = Array(size) {fillvalue}.map { NumericLiteralValue(DataType.FLOAT, it, litval.position) as Expression}.toTypedArray()
val refValue = ArrayLiteralValue(DataType.ARRAY_F, array, position = litval.position)
refValue.addToHeap(program.heap)
refValue.addToHeap()
decl.value = refValue
refValue.parent=decl
optimizationsDone++
@@ -562,56 +562,85 @@ class ConstantFolding(private val program: Program) : IAstModifyingVisitor {
} catch (x: ExpressionError) {
return range
}
val newStep: Expression = stepLiteral?.cast(targetDt) ?: range.step
val newStep: Expression = try {
stepLiteral?.cast(targetDt)?: range.step
} catch(ee: ExpressionError) {
range.step
}
return RangeExpr(newFrom, newTo, newStep, range.position)
}
val forLoop2 = super.visit(forLoop) as ForLoop
// check if we need to adjust an array literal to the loop variable's datatype
val array = forLoop2.iterable as? ArrayLiteralValue
if(array!=null) {
val loopvarDt: DataType = when {
forLoop.loopVar!=null -> forLoop.loopVar!!.inferType(program).typeOrElse(DataType.UBYTE)
forLoop.loopRegister!=null -> DataType.UBYTE
else -> throw FatalAstException("weird for loop")
}
val arrayType = when(loopvarDt) {
DataType.UBYTE -> DataType.ARRAY_UB
DataType.BYTE -> DataType.ARRAY_B
DataType.UWORD -> DataType.ARRAY_UW
DataType.WORD -> DataType.ARRAY_W
DataType.FLOAT -> DataType.ARRAY_F
else -> throw FatalAstException("invalid array elt type")
}
val array2 = array.cast(arrayType)
if(array2!=null && array2!==array) {
forLoop2.iterable = array2
array2.linkParents(forLoop2)
array2.addToHeap()
}
}
// adjust the datatype of a range expression in for loops to the loop variable.
val resultStmt = super.visit(forLoop) as ForLoop
val iterableRange = resultStmt.iterable as? RangeExpr ?: return resultStmt
val iterableRange = forLoop2.iterable as? RangeExpr ?: return forLoop2
val rangeFrom = iterableRange.from as? NumericLiteralValue
val rangeTo = iterableRange.to as? NumericLiteralValue
if(rangeFrom==null || rangeTo==null) return resultStmt
if(rangeFrom==null || rangeTo==null) return forLoop2
val loopvar = resultStmt.loopVar?.targetVarDecl(program.namespace)
val loopvar = forLoop2.loopVar?.targetVarDecl(program.namespace)
if(loopvar!=null) {
val stepLiteral = iterableRange.step as? NumericLiteralValue
when(loopvar.datatype) {
DataType.UBYTE -> {
if(rangeFrom.type!= DataType.UBYTE) {
// attempt to translate the iterable into ubyte values
resultStmt.iterable = adjustRangeDt(rangeFrom, loopvar.datatype, rangeTo, stepLiteral, iterableRange)
forLoop2.iterable = adjustRangeDt(rangeFrom, loopvar.datatype, rangeTo, stepLiteral, iterableRange)
}
}
DataType.BYTE -> {
if(rangeFrom.type!= DataType.BYTE) {
// attempt to translate the iterable into byte values
resultStmt.iterable = adjustRangeDt(rangeFrom, loopvar.datatype, rangeTo, stepLiteral, iterableRange)
forLoop2.iterable = adjustRangeDt(rangeFrom, loopvar.datatype, rangeTo, stepLiteral, iterableRange)
}
}
DataType.UWORD -> {
if(rangeFrom.type!= DataType.UWORD) {
// attempt to translate the iterable into uword values
resultStmt.iterable = adjustRangeDt(rangeFrom, loopvar.datatype, rangeTo, stepLiteral, iterableRange)
forLoop2.iterable = adjustRangeDt(rangeFrom, loopvar.datatype, rangeTo, stepLiteral, iterableRange)
}
}
DataType.WORD -> {
if(rangeFrom.type!= DataType.WORD) {
// attempt to translate the iterable into word values
resultStmt.iterable = adjustRangeDt(rangeFrom, loopvar.datatype, rangeTo, stepLiteral, iterableRange)
forLoop2.iterable = adjustRangeDt(rangeFrom, loopvar.datatype, rangeTo, stepLiteral, iterableRange)
}
}
else -> throw FatalAstException("invalid loopvar datatype $loopvar")
}
}
return resultStmt
return forLoop2
}
override fun visit(arrayLiteral: ArrayLiteralValue): Expression {
val array = super.visit(arrayLiteral)
if(array is ArrayLiteralValue) {
if(array.heapId==null)
array.addToHeap(program.heap)
array.addToHeap()
val vardecl = array.parent as? VarDecl
return if (vardecl!=null) {
fixupArrayDatatype(array, vardecl, program)
@@ -11,7 +11,7 @@ import kotlin.math.log2
import kotlin.math.pow
/*
todo advanced expression optimization: common (sub) expression elimination (turn common expressions into single subroutine call + introduce variable to hold it)
todo add more expression optimizations
Also see https://egorbo.com/peephole-optimizations.html
@@ -615,8 +615,6 @@ internal class SimplifyExpressions(private val program: Program) : IAstModifying
if(leftVal==null && rightVal==null)
return expr
// TODO fix bug in this routine!
// cannot shuffle assiciativity with division!
if(rightVal!=null) {
// right value is a constant, see if we can optimize
@@ -16,8 +16,8 @@ import kotlin.math.floor
/*
TODO: analyse for unreachable code and remove that (f.i. code after goto or return that has no label so can never be jumped to) + print warning about this
TODO: proper inlining of small subroutines (correctly renaming/relocating all variables in them and refs to those as well, or restrict to subs without variables?)
TODO: remove unreachable code?
TODO: proper inlining of tiny subroutines (correctly renaming/relocating all variables in them and refs to those as well, or restrict to subs without variables?)
*/
@@ -27,10 +27,15 @@ internal class StatementOptimizer(private val program: Program) : IAstModifyingV
private val pureBuiltinFunctions = BuiltinFunctions.filter { it.value.pure }
private val callgraph = CallGraph(program)
private val vardeclsToRemove = mutableListOf<VarDecl>()
override fun visit(program: Program) {
removeUnusedCode(callgraph)
super.visit(program)
for(decl in vardeclsToRemove) {
decl.definingScope().remove(decl)
}
}
private fun removeUnusedCode(callgraph: CallGraph) {
@@ -131,7 +136,7 @@ internal class StatementOptimizer(private val program: Program) : IAstModifyingV
// removes 'duplicate' assignments that assign the isSameAs target
val linesToRemove = mutableListOf<Int>()
var previousAssignmentLine: Int? = null
for (i in 0 until statements.size) {
for (i in statements.indices) {
val stmt = statements[i] as? Assignment
if (stmt != null && stmt.value is NumericLiteralValue) {
if (previousAssignmentLine == null) {
@@ -165,24 +170,31 @@ internal class StatementOptimizer(private val program: Program) : IAstModifyingV
if(functionCallStatement.target.nameInSource==listOf("c64scr", "print") ||
functionCallStatement.target.nameInSource==listOf("c64scr", "print_p")) {
// printing a literal string of just 2 or 1 characters is replaced by directly outputting those characters
val stringVar = functionCallStatement.arglist.single() as? IdentifierReference
val arg = functionCallStatement.arglist.single()
val stringVar: IdentifierReference?
stringVar = if(arg is AddressOf) {
arg.identifier
} else {
arg as? IdentifierReference
}
if(stringVar!=null) {
val heapId = stringVar.heapId(program.namespace)
val string = program.heap.get(heapId).str!!
if(string.length==1) {
val petscii = Petscii.encodePetscii(string, true)[0]
val vardecl = stringVar.targetVarDecl(program.namespace)!!
val string = vardecl.value!! as StringLiteralValue
val encodedString = Petscii.encodePetscii(string.value, true)
if(string.value.length==1) {
functionCallStatement.arglist.clear()
functionCallStatement.arglist.add(NumericLiteralValue.optimalInteger(petscii.toInt(), functionCallStatement.position))
functionCallStatement.arglist.add(NumericLiteralValue.optimalInteger(encodedString[0].toInt(), functionCallStatement.position))
functionCallStatement.target = IdentifierReference(listOf("c64", "CHROUT"), functionCallStatement.target.position)
vardeclsToRemove.add(vardecl)
optimizationsDone++
return functionCallStatement
} else if(string.length==2) {
val petscii = Petscii.encodePetscii(string, true)
} else if(string.value.length==2) {
val scope = AnonymousScope(mutableListOf(), functionCallStatement.position)
scope.statements.add(FunctionCallStatement(IdentifierReference(listOf("c64", "CHROUT"), functionCallStatement.target.position),
mutableListOf(NumericLiteralValue.optimalInteger(petscii[0].toInt(), functionCallStatement.position)), functionCallStatement.position))
mutableListOf(NumericLiteralValue.optimalInteger(encodedString[0].toInt(), functionCallStatement.position)), functionCallStatement.position))
scope.statements.add(FunctionCallStatement(IdentifierReference(listOf("c64", "CHROUT"), functionCallStatement.target.position),
mutableListOf(NumericLiteralValue.optimalInteger(petscii[1].toInt(), functionCallStatement.position)), functionCallStatement.position))
mutableListOf(NumericLiteralValue.optimalInteger(encodedString[1].toInt(), functionCallStatement.position)), functionCallStatement.position))
vardeclsToRemove.add(vardecl)
optimizationsDone++
return scope
}
+6 -5
View File
@@ -9,6 +9,7 @@ import prog8.ast.base.SyntaxError
import prog8.ast.base.checkImportedValid
import prog8.ast.statements.Directive
import prog8.ast.statements.DirectiveArg
import prog8.pathFrom
import java.io.InputStream
import java.nio.file.Files
import java.nio.file.Path
@@ -91,18 +92,18 @@ internal fun importModule(program: Program, stream: CharStream, modulePath: Path
private fun discoverImportedModuleFile(name: String, source: Path, position: Position?): Path {
val fileName = "$name.p8"
val locations = mutableListOf(Paths.get(source.parent.toString()))
val locations = mutableListOf(source.parent)
val propPath = System.getProperty("prog8.libdir")
if(propPath!=null)
locations.add(Paths.get(propPath))
locations.add(pathFrom(propPath))
val envPath = System.getenv("PROG8_LIBDIR")
if(envPath!=null)
locations.add(Paths.get(envPath))
locations.add(pathFrom(envPath))
locations.add(Paths.get(Paths.get("").toAbsolutePath().toString(), "prog8lib"))
locations.forEach {
val file = Paths.get(it.toString(), fileName)
val file = pathFrom(it.toString(), fileName)
if (Files.isReadable(file)) return file
}
@@ -120,7 +121,7 @@ private fun executeImportDirective(program: Program, import: Directive, source:
if(existing!=null)
return null
val resource = tryGetEmbeddedResource(moduleName+".p8")
val resource = tryGetEmbeddedResource("$moduleName.p8")
val importedModule =
if(resource!=null) {
// load the module from the embedded resource
+297 -257
View File
@@ -1,12 +1,14 @@
package prog8.vm
import prog8.ast.base.*
import prog8.ast.base.ByteDatatypes
import prog8.ast.base.DataType
import prog8.ast.base.WordDatatypes
import prog8.ast.expressions.ArrayLiteralValue
import prog8.ast.expressions.NumericLiteralValue
import prog8.ast.expressions.StringLiteralValue
import prog8.compiler.HeapValues
import prog8.compiler.target.c64.Petscii
import java.util.*
import prog8.vm.astvm.VmExecutionException
import java.util.Objects
import kotlin.math.abs
import kotlin.math.pow
@@ -16,8 +18,14 @@ import kotlin.math.pow
* this runtime value can be used to *execute* the parsed Ast (or another intermediary form)
* It contains a value of a variable during run time of the program and provides arithmetic operations on the value.
*/
open class RuntimeValue(val type: DataType, num: Number?=null, val str: String?=null,
val array: Array<Number>?=null, val heapId: Int?=null) {
abstract class RuntimeValueBase(val type: DataType) {
abstract fun numericValue(): Number
abstract fun integerValue(): Int
}
class RuntimeValueNumeric(type: DataType, num: Number): RuntimeValueBase(type) {
val byteval: Short?
val wordval: Int?
@@ -25,116 +33,78 @@ open class RuntimeValue(val type: DataType, num: Number?=null, val str: String?=
val asBoolean: Boolean
companion object {
fun fromLv(literalValue: NumericLiteralValue): RuntimeValue {
return RuntimeValue(literalValue.type, num = literalValue.number)
fun fromLv(literalValue: NumericLiteralValue): RuntimeValueNumeric {
return RuntimeValueNumeric(literalValue.type, num = literalValue.number)
}
fun fromLv(string: StringLiteralValue, heap: HeapValues): RuntimeValue = fromHeapId(string.heapId!!, heap)
fun fromLv(array: ArrayLiteralValue, heap: HeapValues): RuntimeValue = fromHeapId(array.heapId!!, heap)
fun fromHeapId(heapId: Int, heap: HeapValues): RuntimeValue {
val value = heap.get(heapId)
return when {
value.type in StringDatatypes ->
RuntimeValue(value.type, str = value.str!!, heapId = heapId)
value.type in ArrayDatatypes ->
if (value.type == DataType.ARRAY_F) {
RuntimeValue(value.type, array = value.doubleArray!!.toList().toTypedArray(), heapId = heapId)
} else {
val array = value.array!!
val resultArray = mutableListOf<Number>()
for(elt in array.withIndex()){
if(elt.value.integer!=null)
resultArray.add(elt.value.integer!!)
else {
TODO("ADDRESSOF ${elt.value}")
}
}
RuntimeValue(value.type, array = resultArray.toTypedArray(), heapId = heapId)
//RuntimeValue(value.type, array = array.map { it.integer!! }.toTypedArray(), heapId = heapId)
}
else -> throw IllegalArgumentException("weird value type on heap $value")
}
}
}
init {
when(type) {
when (type) {
DataType.UBYTE -> {
val inum = num!!.toInt()
if(inum !in 0 .. 255)
throw IllegalArgumentException("invalid value for ubyte: $inum")
val inum = num.toInt()
require(inum in 0..255) { "invalid value for ubyte: $inum" }
byteval = inum.toShort()
wordval = null
floatval = null
asBoolean = byteval != 0.toShort()
}
DataType.BYTE -> {
val inum = num!!.toInt()
if(inum !in -128 .. 127)
throw IllegalArgumentException("invalid value for byte: $inum")
val inum = num.toInt()
require(inum in -128..127) { "invalid value for byte: $inum" }
byteval = inum.toShort()
wordval = null
floatval = null
asBoolean = byteval != 0.toShort()
}
DataType.UWORD -> {
val inum = num!!.toInt()
if(inum !in 0 .. 65535)
throw IllegalArgumentException("invalid value for uword: $inum")
val inum = num.toInt()
require(inum in 0..65535) { "invalid value for uword: $inum" }
wordval = inum
byteval = null
floatval = null
asBoolean = wordval != 0
}
DataType.WORD -> {
val inum = num!!.toInt()
if(inum !in -32768 .. 32767)
throw IllegalArgumentException("invalid value for word: $inum")
val inum = num.toInt()
require(inum in -32768..32767) { "invalid value for word: $inum" }
wordval = inum
byteval = null
floatval = null
asBoolean = wordval != 0
}
DataType.FLOAT -> {
floatval = num!!.toDouble()
floatval = num.toDouble()
byteval = null
wordval = null
asBoolean = floatval != 0.0
}
else -> {
byteval = null
wordval = null
floatval = null
asBoolean = true
}
else -> throw VmExecutionException("not a numeric value")
}
}
override fun toString(): String {
return when(type) {
return when (type) {
DataType.UBYTE -> "ub:%02x".format(byteval)
DataType.BYTE -> {
if(byteval!!<0)
if (byteval!! < 0)
"b:-%02x".format(abs(byteval.toInt()))
else
"b:%02x".format(byteval)
}
DataType.UWORD -> "uw:%04x".format(wordval)
DataType.WORD -> {
if(wordval!!<0)
if (wordval!! < 0)
"w:-%04x".format(abs(wordval))
else
"w:%04x".format(wordval)
}
DataType.FLOAT -> "f:$floatval"
else -> "heap:$heapId"
else -> "???"
}
}
fun numericValue(): Number {
return when(type) {
override fun numericValue(): Number {
return when (type) {
in ByteDatatypes -> byteval!!
in WordDatatypes -> wordval!!
DataType.FLOAT -> floatval!!
@@ -142,8 +112,8 @@ open class RuntimeValue(val type: DataType, num: Number?=null, val str: String?=
}
}
fun integerValue(): Int {
return when(type) {
override fun integerValue(): Int {
return when (type) {
in ByteDatatypes -> byteval!!.toInt()
in WordDatatypes -> wordval!!
DataType.FLOAT -> throw ArithmeticException("float to integer loss of precision")
@@ -154,75 +124,69 @@ open class RuntimeValue(val type: DataType, num: Number?=null, val str: String?=
override fun hashCode(): Int = Objects.hash(byteval, wordval, floatval, type)
override fun equals(other: Any?): Boolean {
if(other==null || other !is RuntimeValue)
if (other == null || other !is RuntimeValueNumeric)
return false
if(type==other.type)
return if (type in IterableDatatypes) heapId==other.heapId else compareTo(other)==0
return compareTo(other)==0 // note: datatype doesn't matter
return compareTo(other) == 0 // note: datatype doesn't matter
}
operator fun compareTo(other: RuntimeValue): Int {
return if (type in NumericDatatypes && other.type in NumericDatatypes)
numericValue().toDouble().compareTo(other.numericValue().toDouble())
else throw ArithmeticException("comparison can only be done between two numeric values")
}
operator fun compareTo(other: RuntimeValueNumeric): Int = numericValue().toDouble().compareTo(other.numericValue().toDouble())
private fun arithResult(leftDt: DataType, result: Number, rightDt: DataType, op: String): RuntimeValue {
if(leftDt!=rightDt)
private fun arithResult(leftDt: DataType, result: Number, rightDt: DataType, op: String): RuntimeValueNumeric {
if (leftDt != rightDt)
throw ArithmeticException("left and right datatypes are not the same")
if(result.toDouble() < 0 ) {
return when(leftDt) {
if (result.toDouble() < 0) {
return when (leftDt) {
DataType.UBYTE, DataType.UWORD -> {
// storing a negative number in an unsigned one is done by storing the 2's complement instead
val number = abs(result.toDouble().toInt())
if(leftDt== DataType.UBYTE)
RuntimeValue(DataType.UBYTE, (number xor 255) + 1)
if (leftDt == DataType.UBYTE)
RuntimeValueNumeric(DataType.UBYTE, (number xor 255) + 1)
else
RuntimeValue(DataType.UWORD, (number xor 65535) + 1)
RuntimeValueNumeric(DataType.UWORD, (number xor 65535) + 1)
}
DataType.BYTE -> {
val v=result.toInt() and 255
if(v<128)
RuntimeValue(DataType.BYTE, v)
val v = result.toInt() and 255
if (v < 128)
RuntimeValueNumeric(DataType.BYTE, v)
else
RuntimeValue(DataType.BYTE, v-256)
RuntimeValueNumeric(DataType.BYTE, v - 256)
}
DataType.WORD -> {
val v=result.toInt() and 65535
if(v<32768)
RuntimeValue(DataType.WORD, v)
val v = result.toInt() and 65535
if (v < 32768)
RuntimeValueNumeric(DataType.WORD, v)
else
RuntimeValue(DataType.WORD, v-65536)
RuntimeValueNumeric(DataType.WORD, v - 65536)
}
DataType.FLOAT -> RuntimeValue(DataType.FLOAT, result)
DataType.FLOAT -> RuntimeValueNumeric(DataType.FLOAT, result)
else -> throw ArithmeticException("$op on non-numeric type")
}
}
return when(leftDt) {
DataType.UBYTE -> RuntimeValue(DataType.UBYTE, result.toInt() and 255)
return when (leftDt) {
DataType.UBYTE -> RuntimeValueNumeric(DataType.UBYTE, result.toInt() and 255)
DataType.BYTE -> {
val v = result.toInt() and 255
if(v<128)
RuntimeValue(DataType.BYTE, v)
if (v < 128)
RuntimeValueNumeric(DataType.BYTE, v)
else
RuntimeValue(DataType.BYTE, v-256)
RuntimeValueNumeric(DataType.BYTE, v - 256)
}
DataType.UWORD -> RuntimeValue(DataType.UWORD, result.toInt() and 65535)
DataType.UWORD -> RuntimeValueNumeric(DataType.UWORD, result.toInt() and 65535)
DataType.WORD -> {
val v = result.toInt() and 65535
if(v<32768)
RuntimeValue(DataType.WORD, v)
if (v < 32768)
RuntimeValueNumeric(DataType.WORD, v)
else
RuntimeValue(DataType.WORD, v-65536)
RuntimeValueNumeric(DataType.WORD, v - 65536)
}
DataType.FLOAT -> RuntimeValue(DataType.FLOAT, result)
DataType.FLOAT -> RuntimeValueNumeric(DataType.FLOAT, result)
else -> throw ArithmeticException("$op on non-numeric type")
}
}
fun add(other: RuntimeValue): RuntimeValue {
if(other.type == DataType.FLOAT && (type!= DataType.FLOAT))
fun add(other: RuntimeValueNumeric): RuntimeValueNumeric {
if (other.type == DataType.FLOAT && (type != DataType.FLOAT))
throw ArithmeticException("floating point loss of precision on type $type")
val v1 = numericValue()
val v2 = other.numericValue()
@@ -230,8 +194,8 @@ open class RuntimeValue(val type: DataType, num: Number?=null, val str: String?=
return arithResult(type, result, other.type, "add")
}
fun sub(other: RuntimeValue): RuntimeValue {
if(other.type == DataType.FLOAT && (type!= DataType.FLOAT))
fun sub(other: RuntimeValueNumeric): RuntimeValueNumeric {
if (other.type == DataType.FLOAT && (type != DataType.FLOAT))
throw ArithmeticException("floating point loss of precision on type $type")
val v1 = numericValue()
val v2 = other.numericValue()
@@ -239,8 +203,8 @@ open class RuntimeValue(val type: DataType, num: Number?=null, val str: String?=
return arithResult(type, result, other.type, "sub")
}
fun mul(other: RuntimeValue): RuntimeValue {
if(other.type == DataType.FLOAT && (type!= DataType.FLOAT))
fun mul(other: RuntimeValueNumeric): RuntimeValueNumeric {
if (other.type == DataType.FLOAT && (type != DataType.FLOAT))
throw ArithmeticException("floating point loss of precision on type $type")
val v1 = numericValue()
val v2 = other.numericValue()
@@ -248,317 +212,318 @@ open class RuntimeValue(val type: DataType, num: Number?=null, val str: String?=
return arithResult(type, result, other.type, "mul")
}
fun div(other: RuntimeValue): RuntimeValue {
if(other.type == DataType.FLOAT && (type!= DataType.FLOAT))
fun div(other: RuntimeValueNumeric): RuntimeValueNumeric {
if (other.type == DataType.FLOAT && (type != DataType.FLOAT))
throw ArithmeticException("floating point loss of precision on type $type")
val v1 = numericValue()
val v2 = other.numericValue()
if(v2.toDouble()==0.0) {
if (v2.toDouble() == 0.0) {
when (type) {
DataType.UBYTE -> return RuntimeValue(DataType.UBYTE, 255)
DataType.BYTE -> return RuntimeValue(DataType.BYTE, 127)
DataType.UWORD -> return RuntimeValue(DataType.UWORD, 65535)
DataType.WORD -> return RuntimeValue(DataType.WORD, 32767)
else -> {}
DataType.UBYTE -> return RuntimeValueNumeric(DataType.UBYTE, 255)
DataType.BYTE -> return RuntimeValueNumeric(DataType.BYTE, 127)
DataType.UWORD -> return RuntimeValueNumeric(DataType.UWORD, 65535)
DataType.WORD -> return RuntimeValueNumeric(DataType.WORD, 32767)
else -> {
}
}
}
val result = v1.toDouble() / v2.toDouble()
// NOTE: integer division returns integer result!
return when(type) {
DataType.UBYTE -> RuntimeValue(DataType.UBYTE, result)
DataType.BYTE -> RuntimeValue(DataType.BYTE, result)
DataType.UWORD -> RuntimeValue(DataType.UWORD, result)
DataType.WORD -> RuntimeValue(DataType.WORD, result)
DataType.FLOAT -> RuntimeValue(DataType.FLOAT, result)
return when (type) {
DataType.UBYTE -> RuntimeValueNumeric(DataType.UBYTE, result)
DataType.BYTE -> RuntimeValueNumeric(DataType.BYTE, result)
DataType.UWORD -> RuntimeValueNumeric(DataType.UWORD, result)
DataType.WORD -> RuntimeValueNumeric(DataType.WORD, result)
DataType.FLOAT -> RuntimeValueNumeric(DataType.FLOAT, result)
else -> throw ArithmeticException("div on non-numeric type")
}
}
fun remainder(other: RuntimeValue): RuntimeValue {
fun remainder(other: RuntimeValueNumeric): RuntimeValueNumeric {
val v1 = numericValue()
val v2 = other.numericValue()
val result = v1.toDouble() % v2.toDouble()
return arithResult(type, result, other.type, "remainder")
}
fun pow(other: RuntimeValue): RuntimeValue {
fun pow(other: RuntimeValueNumeric): RuntimeValueNumeric {
val v1 = numericValue()
val v2 = other.numericValue()
val result = v1.toDouble().pow(v2.toDouble())
return arithResult(type, result, other.type,"pow")
return arithResult(type, result, other.type, "pow")
}
fun shl(): RuntimeValue {
fun shl(): RuntimeValueNumeric {
val v = integerValue()
return when (type) {
DataType.UBYTE -> RuntimeValue(type, (v shl 1) and 255)
DataType.UWORD -> RuntimeValue(type, (v shl 1) and 65535)
DataType.UBYTE -> RuntimeValueNumeric(type, (v shl 1) and 255)
DataType.UWORD -> RuntimeValueNumeric(type, (v shl 1) and 65535)
DataType.BYTE -> {
val value = v shl 1
if(value<128)
RuntimeValue(type, value)
if (value < 128)
RuntimeValueNumeric(type, value)
else
RuntimeValue(type, value-256)
RuntimeValueNumeric(type, value - 256)
}
DataType.WORD -> {
val value = v shl 1
if(value<32768)
RuntimeValue(type, value)
if (value < 32768)
RuntimeValueNumeric(type, value)
else
RuntimeValue(type, value-65536)
RuntimeValueNumeric(type, value - 65536)
}
else -> throw ArithmeticException("invalid type for shl: $type")
}
}
fun shr(): RuntimeValue {
fun shr(): RuntimeValueNumeric {
val v = integerValue()
return when(type){
DataType.UBYTE -> RuntimeValue(type, v ushr 1)
DataType.BYTE -> RuntimeValue(type, v shr 1)
DataType.UWORD -> RuntimeValue(type, v ushr 1)
DataType.WORD -> RuntimeValue(type, v shr 1)
return when (type) {
DataType.UBYTE -> RuntimeValueNumeric(type, v ushr 1)
DataType.BYTE -> RuntimeValueNumeric(type, v shr 1)
DataType.UWORD -> RuntimeValueNumeric(type, v ushr 1)
DataType.WORD -> RuntimeValueNumeric(type, v shr 1)
else -> throw ArithmeticException("invalid type for shr: $type")
}
}
fun rol(carry: Boolean): Pair<RuntimeValue, Boolean> {
fun rol(carry: Boolean): Pair<RuntimeValueNumeric, Boolean> {
// 9 or 17 bit rotate left (with carry))
return when(type) {
return when (type) {
DataType.UBYTE, DataType.BYTE -> {
val v = byteval!!.toInt()
val newCarry = (v and 0x80) != 0
val newval = (v and 0x7f shl 1) or (if(carry) 1 else 0)
Pair(RuntimeValue(DataType.UBYTE, newval), newCarry)
val newval = (v and 0x7f shl 1) or (if (carry) 1 else 0)
Pair(RuntimeValueNumeric(DataType.UBYTE, newval), newCarry)
}
DataType.UWORD, DataType.WORD -> {
val v = wordval!!
val newCarry = (v and 0x8000) != 0
val newval = (v and 0x7fff shl 1) or (if(carry) 1 else 0)
Pair(RuntimeValue(DataType.UWORD, newval), newCarry)
val newval = (v and 0x7fff shl 1) or (if (carry) 1 else 0)
Pair(RuntimeValueNumeric(DataType.UWORD, newval), newCarry)
}
else -> throw ArithmeticException("rol can only work on byte/word")
}
}
fun ror(carry: Boolean): Pair<RuntimeValue, Boolean> {
fun ror(carry: Boolean): Pair<RuntimeValueNumeric, Boolean> {
// 9 or 17 bit rotate right (with carry)
return when(type) {
return when (type) {
DataType.UBYTE, DataType.BYTE -> {
val v = byteval!!.toInt()
val newCarry = v and 1 != 0
val newval = (v ushr 1) or (if(carry) 0x80 else 0)
Pair(RuntimeValue(DataType.UBYTE, newval), newCarry)
val newval = (v ushr 1) or (if (carry) 0x80 else 0)
Pair(RuntimeValueNumeric(DataType.UBYTE, newval), newCarry)
}
DataType.UWORD, DataType.WORD -> {
val v = wordval!!
val newCarry = v and 1 != 0
val newval = (v ushr 1) or (if(carry) 0x8000 else 0)
Pair(RuntimeValue(DataType.UWORD, newval), newCarry)
val newval = (v ushr 1) or (if (carry) 0x8000 else 0)
Pair(RuntimeValueNumeric(DataType.UWORD, newval), newCarry)
}
else -> throw ArithmeticException("ror2 can only work on byte/word")
}
}
fun rol2(): RuntimeValue {
fun rol2(): RuntimeValueNumeric {
// 8 or 16 bit rotate left
return when(type) {
return when (type) {
DataType.UBYTE, DataType.BYTE -> {
val v = byteval!!.toInt()
val carry = (v and 0x80) ushr 7
val newval = (v and 0x7f shl 1) or carry
RuntimeValue(DataType.UBYTE, newval)
RuntimeValueNumeric(DataType.UBYTE, newval)
}
DataType.UWORD, DataType.WORD -> {
val v = wordval!!
val carry = (v and 0x8000) ushr 15
val newval = (v and 0x7fff shl 1) or carry
RuntimeValue(DataType.UWORD, newval)
RuntimeValueNumeric(DataType.UWORD, newval)
}
else -> throw ArithmeticException("rol2 can only work on byte/word")
}
}
fun ror2(): RuntimeValue {
fun ror2(): RuntimeValueNumeric {
// 8 or 16 bit rotate right
return when(type) {
return when (type) {
DataType.UBYTE, DataType.BYTE -> {
val v = byteval!!.toInt()
val carry = v and 1 shl 7
val newval = (v ushr 1) or carry
RuntimeValue(DataType.UBYTE, newval)
RuntimeValueNumeric(DataType.UBYTE, newval)
}
DataType.UWORD, DataType.WORD -> {
val v = wordval!!
val carry = v and 1 shl 15
val newval = (v ushr 1) or carry
RuntimeValue(DataType.UWORD, newval)
RuntimeValueNumeric(DataType.UWORD, newval)
}
else -> throw ArithmeticException("ror2 can only work on byte/word")
}
}
fun neg(): RuntimeValue {
return when(type) {
DataType.BYTE -> RuntimeValue(DataType.BYTE, -(byteval!!))
DataType.WORD -> RuntimeValue(DataType.WORD, -(wordval!!))
DataType.FLOAT -> RuntimeValue(DataType.FLOAT, -(floatval)!!)
fun neg(): RuntimeValueNumeric {
return when (type) {
DataType.BYTE -> RuntimeValueNumeric(DataType.BYTE, -(byteval!!))
DataType.WORD -> RuntimeValueNumeric(DataType.WORD, -(wordval!!))
DataType.FLOAT -> RuntimeValueNumeric(DataType.FLOAT, -(floatval)!!)
else -> throw ArithmeticException("neg can only work on byte/word/float")
}
}
fun abs(): RuntimeValue {
return when(type) {
DataType.BYTE -> RuntimeValue(DataType.BYTE, abs(byteval!!.toInt()))
DataType.WORD -> RuntimeValue(DataType.WORD, abs(wordval!!))
DataType.FLOAT -> RuntimeValue(DataType.FLOAT, abs(floatval!!))
fun abs(): RuntimeValueNumeric {
return when (type) {
DataType.BYTE -> RuntimeValueNumeric(DataType.BYTE, abs(byteval!!.toInt()))
DataType.WORD -> RuntimeValueNumeric(DataType.WORD, abs(wordval!!))
DataType.FLOAT -> RuntimeValueNumeric(DataType.FLOAT, abs(floatval!!))
else -> throw ArithmeticException("abs can only work on byte/word/float")
}
}
fun bitand(other: RuntimeValue): RuntimeValue {
fun bitand(other: RuntimeValueNumeric): RuntimeValueNumeric {
val v1 = integerValue()
val v2 = other.integerValue()
val result = v1 and v2
return RuntimeValue(type, result)
return RuntimeValueNumeric(type, result)
}
fun bitor(other: RuntimeValue): RuntimeValue {
fun bitor(other: RuntimeValueNumeric): RuntimeValueNumeric {
val v1 = integerValue()
val v2 = other.integerValue()
val result = v1 or v2
return RuntimeValue(type, result)
return RuntimeValueNumeric(type, result)
}
fun bitxor(other: RuntimeValue): RuntimeValue {
fun bitxor(other: RuntimeValueNumeric): RuntimeValueNumeric {
val v1 = integerValue()
val v2 = other.integerValue()
val result = v1 xor v2
return RuntimeValue(type, result)
return RuntimeValueNumeric(type, result)
}
fun and(other: RuntimeValue) = RuntimeValue(DataType.UBYTE, if (this.asBoolean && other.asBoolean) 1 else 0)
fun or(other: RuntimeValue) = RuntimeValue(DataType.UBYTE, if (this.asBoolean || other.asBoolean) 1 else 0)
fun xor(other: RuntimeValue) = RuntimeValue(DataType.UBYTE, if (this.asBoolean xor other.asBoolean) 1 else 0)
fun not() = RuntimeValue(DataType.UBYTE, if (this.asBoolean) 0 else 1)
fun and(other: RuntimeValueNumeric) = RuntimeValueNumeric(DataType.UBYTE, if (this.asBoolean && other.asBoolean) 1 else 0)
fun or(other: RuntimeValueNumeric) = RuntimeValueNumeric(DataType.UBYTE, if (this.asBoolean || other.asBoolean) 1 else 0)
fun xor(other: RuntimeValueNumeric) = RuntimeValueNumeric(DataType.UBYTE, if (this.asBoolean xor other.asBoolean) 1 else 0)
fun not() = RuntimeValueNumeric(DataType.UBYTE, if (this.asBoolean) 0 else 1)
fun inv(): RuntimeValue {
return when(type) {
DataType.UBYTE -> RuntimeValue(type, byteval!!.toInt().inv() and 255)
DataType.UWORD -> RuntimeValue(type, wordval!!.inv() and 65535)
DataType.BYTE -> RuntimeValue(type, byteval!!.toInt().inv())
DataType.WORD -> RuntimeValue(type, wordval!!.inv())
fun inv(): RuntimeValueNumeric {
return when (type) {
DataType.UBYTE -> RuntimeValueNumeric(type, byteval!!.toInt().inv() and 255)
DataType.UWORD -> RuntimeValueNumeric(type, wordval!!.inv() and 65535)
DataType.BYTE -> RuntimeValueNumeric(type, byteval!!.toInt().inv())
DataType.WORD -> RuntimeValueNumeric(type, wordval!!.inv())
else -> throw ArithmeticException("inv can only work on byte/word")
}
}
fun inc(): RuntimeValue {
return when(type) {
DataType.UBYTE -> RuntimeValue(type, (byteval!! + 1) and 255)
DataType.UWORD -> RuntimeValue(type, (wordval!! + 1) and 65535)
fun inc(): RuntimeValueNumeric {
return when (type) {
DataType.UBYTE -> RuntimeValueNumeric(type, (byteval!! + 1) and 255)
DataType.UWORD -> RuntimeValueNumeric(type, (wordval!! + 1) and 65535)
DataType.BYTE -> {
val newval = byteval!! + 1
if(newval == 128)
RuntimeValue(type, -128)
if (newval == 128)
RuntimeValueNumeric(type, -128)
else
RuntimeValue(type, newval)
RuntimeValueNumeric(type, newval)
}
DataType.WORD -> {
val newval = wordval!! + 1
if(newval == 32768)
RuntimeValue(type, -32768)
if (newval == 32768)
RuntimeValueNumeric(type, -32768)
else
RuntimeValue(type, newval)
RuntimeValueNumeric(type, newval)
}
DataType.FLOAT -> RuntimeValue(DataType.FLOAT, floatval!! + 1)
DataType.FLOAT -> RuntimeValueNumeric(DataType.FLOAT, floatval!! + 1)
else -> throw ArithmeticException("inc can only work on numeric types")
}
}
fun dec(): RuntimeValue {
return when(type) {
DataType.UBYTE -> RuntimeValue(type, (byteval!! - 1) and 255)
DataType.UWORD -> RuntimeValue(type, (wordval!! - 1) and 65535)
fun dec(): RuntimeValueNumeric {
return when (type) {
DataType.UBYTE -> RuntimeValueNumeric(type, (byteval!! - 1) and 255)
DataType.UWORD -> RuntimeValueNumeric(type, (wordval!! - 1) and 65535)
DataType.BYTE -> {
val newval = byteval!! - 1
if(newval == -129)
RuntimeValue(type, 127)
if (newval == -129)
RuntimeValueNumeric(type, 127)
else
RuntimeValue(type, newval)
RuntimeValueNumeric(type, newval)
}
DataType.WORD -> {
val newval = wordval!! - 1
if(newval == -32769)
RuntimeValue(type, 32767)
if (newval == -32769)
RuntimeValueNumeric(type, 32767)
else
RuntimeValue(type, newval)
RuntimeValueNumeric(type, newval)
}
DataType.FLOAT -> RuntimeValue(DataType.FLOAT, floatval!! - 1)
DataType.FLOAT -> RuntimeValueNumeric(DataType.FLOAT, floatval!! - 1)
else -> throw ArithmeticException("dec can only work on numeric types")
}
}
fun msb(): RuntimeValue {
return when(type) {
in ByteDatatypes -> RuntimeValue(DataType.UBYTE, 0)
in WordDatatypes -> RuntimeValue(DataType.UBYTE, wordval!! ushr 8 and 255)
fun msb(): RuntimeValueNumeric {
return when (type) {
in ByteDatatypes -> RuntimeValueNumeric(DataType.UBYTE, 0)
in WordDatatypes -> RuntimeValueNumeric(DataType.UBYTE, wordval!! ushr 8 and 255)
else -> throw ArithmeticException("msb can only work on (u)byte/(u)word")
}
}
fun cast(targetType: DataType): RuntimeValue {
fun cast(targetType: DataType): RuntimeValueNumeric {
return when (type) {
DataType.UBYTE -> {
when (targetType) {
DataType.UBYTE -> this
DataType.BYTE -> {
val nval=byteval!!.toInt()
if(nval<128)
RuntimeValue(DataType.BYTE, nval)
val nval = byteval!!.toInt()
if (nval < 128)
RuntimeValueNumeric(DataType.BYTE, nval)
else
RuntimeValue(DataType.BYTE, nval-256)
RuntimeValueNumeric(DataType.BYTE, nval - 256)
}
DataType.UWORD -> RuntimeValue(DataType.UWORD, numericValue())
DataType.UWORD -> RuntimeValueNumeric(DataType.UWORD, numericValue())
DataType.WORD -> {
val nval = numericValue().toInt()
if(nval<32768)
RuntimeValue(DataType.WORD, nval)
if (nval < 32768)
RuntimeValueNumeric(DataType.WORD, nval)
else
RuntimeValue(DataType.WORD, nval-65536)
RuntimeValueNumeric(DataType.WORD, nval - 65536)
}
DataType.FLOAT -> RuntimeValue(DataType.FLOAT, numericValue())
DataType.FLOAT -> RuntimeValueNumeric(DataType.FLOAT, numericValue())
else -> throw ArithmeticException("invalid type cast from $type to $targetType")
}
}
DataType.BYTE -> {
when (targetType) {
DataType.BYTE -> this
DataType.UBYTE -> RuntimeValue(DataType.UBYTE, integerValue() and 255)
DataType.UWORD -> RuntimeValue(DataType.UWORD, integerValue() and 65535)
DataType.WORD -> RuntimeValue(DataType.WORD, integerValue())
DataType.FLOAT -> RuntimeValue(DataType.FLOAT, numericValue())
DataType.UBYTE -> RuntimeValueNumeric(DataType.UBYTE, integerValue() and 255)
DataType.UWORD -> RuntimeValueNumeric(DataType.UWORD, integerValue() and 65535)
DataType.WORD -> RuntimeValueNumeric(DataType.WORD, integerValue())
DataType.FLOAT -> RuntimeValueNumeric(DataType.FLOAT, numericValue())
else -> throw ArithmeticException("invalid type cast from $type to $targetType")
}
}
DataType.UWORD -> {
when (targetType) {
DataType.BYTE -> {
val v=integerValue()
if(v<128)
RuntimeValue(DataType.BYTE, v)
val v = integerValue()
if (v < 128)
RuntimeValueNumeric(DataType.BYTE, v)
else
RuntimeValue(DataType.BYTE, v-256)
RuntimeValueNumeric(DataType.BYTE, v - 256)
}
DataType.UBYTE -> RuntimeValue(DataType.UBYTE, integerValue() and 255)
DataType.UBYTE -> RuntimeValueNumeric(DataType.UBYTE, integerValue() and 255)
DataType.UWORD -> this
DataType.WORD -> {
val v=integerValue()
if(v<32768)
RuntimeValue(DataType.WORD, v)
val v = integerValue()
if (v < 32768)
RuntimeValueNumeric(DataType.WORD, v)
else
RuntimeValue(DataType.WORD, v-65536)
RuntimeValueNumeric(DataType.WORD, v - 65536)
}
DataType.FLOAT -> RuntimeValue(DataType.FLOAT, numericValue())
DataType.FLOAT -> RuntimeValueNumeric(DataType.FLOAT, numericValue())
else -> throw ArithmeticException("invalid type cast from $type to $targetType")
}
}
@@ -566,33 +531,33 @@ open class RuntimeValue(val type: DataType, num: Number?=null, val str: String?=
when (targetType) {
DataType.BYTE -> {
val v = integerValue() and 255
if(v<128)
RuntimeValue(DataType.BYTE, v)
if (v < 128)
RuntimeValueNumeric(DataType.BYTE, v)
else
RuntimeValue(DataType.BYTE, v-256)
RuntimeValueNumeric(DataType.BYTE, v - 256)
}
DataType.UBYTE -> RuntimeValue(DataType.UBYTE, integerValue() and 65535)
DataType.UWORD -> RuntimeValue(DataType.UWORD, integerValue())
DataType.UBYTE -> RuntimeValueNumeric(DataType.UBYTE, integerValue() and 65535)
DataType.UWORD -> RuntimeValueNumeric(DataType.UWORD, integerValue())
DataType.WORD -> this
DataType.FLOAT -> RuntimeValue(DataType.FLOAT, numericValue())
DataType.FLOAT -> RuntimeValueNumeric(DataType.FLOAT, numericValue())
else -> throw ArithmeticException("invalid type cast from $type to $targetType")
}
}
DataType.FLOAT -> {
when (targetType) {
DataType.BYTE -> {
val integer=numericValue().toInt()
if(integer in -128..127)
RuntimeValue(DataType.BYTE, integer)
val integer = numericValue().toInt()
if (integer in -128..127)
RuntimeValueNumeric(DataType.BYTE, integer)
else
throw ArithmeticException("overflow when casting float to byte: $this")
}
DataType.UBYTE -> RuntimeValue(DataType.UBYTE, numericValue().toInt())
DataType.UWORD -> RuntimeValue(DataType.UWORD, numericValue().toInt())
DataType.UBYTE -> RuntimeValueNumeric(DataType.UBYTE, numericValue().toInt())
DataType.UWORD -> RuntimeValueNumeric(DataType.UWORD, numericValue().toInt())
DataType.WORD -> {
val integer=numericValue().toInt()
if(integer in -32768..32767)
RuntimeValue(DataType.WORD, integer)
val integer = numericValue().toInt()
if (integer in -32768..32767)
RuntimeValueNumeric(DataType.WORD, integer)
else
throw ArithmeticException("overflow when casting float to word: $this")
}
@@ -603,22 +568,97 @@ open class RuntimeValue(val type: DataType, num: Number?=null, val str: String?=
else -> throw ArithmeticException("invalid type cast from $type to $targetType")
}
}
}
open fun iterator(): Iterator<Number> {
return when (type) {
in StringDatatypes -> {
Petscii.encodePetscii(str!!, true).iterator()
}
in ArrayDatatypes -> {
array!!.iterator()
}
else -> throw IllegalArgumentException("cannot iterate over $this")
class RuntimeValueString(type: DataType, val str: String, val heapId: Int?): RuntimeValueBase(type) {
companion object {
fun fromLv(string: StringLiteralValue): RuntimeValueString {
return RuntimeValueString(string.type, string.value, string.heapId!!)
}
}
override fun toString(): String {
return when (type) {
DataType.STR -> "str:$str"
DataType.STR_S -> "str_s:$str"
else -> "???"
}
}
override fun hashCode(): Int = Objects.hash(type, str)
override fun equals(other: Any?): Boolean {
if (other == null || other !is RuntimeValueString)
return false
return type == other.type && str == other.str
}
fun iterator(): Iterator<Number> = Petscii.encodePetscii(str, true).iterator()
override fun numericValue(): Number {
throw VmExecutionException("string is not a number")
}
override fun integerValue(): Int {
throw VmExecutionException("string is not a number")
}
}
class RuntimeValueRange(type: DataType, val range: IntProgression): RuntimeValue(type, array=range.toList().toTypedArray()) {
open class RuntimeValueArray(type: DataType, val array: Array<Number>, val heapId: Int?): RuntimeValueBase(type) {
companion object {
fun fromLv(array: ArrayLiteralValue): RuntimeValueArray {
return if (array.type == DataType.ARRAY_F) {
val doubleArray = array.value.map { (it as NumericLiteralValue).number }.toTypedArray()
RuntimeValueArray(array.type, doubleArray, array.heapId!!)
} else {
val resultArray = mutableListOf<Number>()
for (elt in array.value.withIndex()) {
if (elt.value is NumericLiteralValue)
resultArray.add((elt.value as NumericLiteralValue).number.toInt())
else {
TODO("ADDRESSOF ${elt.value}")
}
}
RuntimeValueArray(array.type, resultArray.toTypedArray(), array.heapId!!)
}
}
}
override fun toString(): String {
return when (type) {
DataType.ARRAY_UB -> "array_ub:..."
DataType.ARRAY_B -> "array_b:..."
DataType.ARRAY_UW -> "array_uw:..."
DataType.ARRAY_W -> "array_w:..."
DataType.ARRAY_F -> "array_f:..."
else -> "???"
}
}
override fun hashCode(): Int = Objects.hash(type, array)
override fun equals(other: Any?): Boolean {
if (other == null || other !is RuntimeValueArray)
return false
return type == other.type && array.contentEquals(other.array)
}
open fun iterator(): Iterator<Number> = array.iterator()
override fun numericValue(): Number {
throw VmExecutionException("array is not a number")
}
override fun integerValue(): Int {
throw VmExecutionException("array is not a number")
}
}
class RuntimeValueRange(type: DataType, val range: IntProgression): RuntimeValueArray(type, range.toList().toTypedArray(), null) {
override fun iterator(): Iterator<Number> {
return range.iterator()
}
+191 -146
View File
@@ -7,14 +7,12 @@ import prog8.ast.expressions.Expression
import prog8.ast.expressions.IdentifierReference
import prog8.ast.expressions.NumericLiteralValue
import prog8.ast.statements.*
import prog8.compiler.IntegerOrAddressOf
import prog8.compiler.target.c64.MachineDefinition
import prog8.compiler.target.c64.Petscii
import prog8.vm.RuntimeValue
import prog8.vm.RuntimeValueRange
import prog8.vm.*
import java.awt.EventQueue
import java.io.CharConversionException
import java.util.*
import java.util.ArrayDeque
import kotlin.NoSuchElementException
import kotlin.concurrent.fixedRateTimer
import kotlin.math.*
@@ -72,10 +70,14 @@ class StatusFlags {
class RuntimeVariables {
fun define(scope: INameScope, name: String, initialValue: RuntimeValue) {
fun define(scope: INameScope, name: String, initialValue: RuntimeValueBase) {
val where = vars.getValue(scope)
where[name] = initialValue
vars[scope] = where
if(initialValue is RuntimeValueString)
byHeapId[initialValue.heapId!!] = initialValue
else if(initialValue is RuntimeValueArray)
byHeapId[initialValue.heapId!!] = initialValue
}
fun defineMemory(scope: INameScope, name: String, address: Int) {
@@ -84,7 +86,7 @@ class RuntimeVariables {
memvars[scope] = where
}
fun set(scope: INameScope, name: String, value: RuntimeValue) {
fun set(scope: INameScope, name: String, value: RuntimeValueBase) {
val where = vars.getValue(scope)
val existing = where[name]
if(existing==null) {
@@ -96,9 +98,15 @@ class RuntimeVariables {
throw VmExecutionException("new value is of different datatype ${value.type} expected ${existing.type} for $name")
where[name] = value
vars[scope] = where
if(value is RuntimeValueString)
byHeapId[value.heapId!!] = value
else if(value is RuntimeValueArray)
byHeapId[value.heapId!!] = value
}
fun get(scope: INameScope, name: String): RuntimeValue {
fun getByHeapId(heapId: Int): RuntimeValueBase = byHeapId.getValue(heapId)
fun get(scope: INameScope, name: String): RuntimeValueBase {
val where = vars.getValue(scope)
return where[name] ?: throw NoSuchElementException("no such runtime variable: ${scope.name}.$name")
}
@@ -108,8 +116,6 @@ class RuntimeVariables {
return where[name] ?: throw NoSuchElementException("no such runtime memory-variable: ${scope.name}.$name")
}
fun swap(a1: VarDecl, a2: VarDecl) = swap(a1.definingScope(), a1.name, a2.definingScope(), a2.name)
fun swap(scope1: INameScope, name1: String, scope2: INameScope, name2: String) {
val v1 = get(scope1, name1)
val v2 = get(scope2, name2)
@@ -117,8 +123,9 @@ class RuntimeVariables {
set(scope2, name2, v1)
}
private val vars = mutableMapOf<INameScope, MutableMap<String, RuntimeValue>>().withDefault { mutableMapOf() }
private val vars = mutableMapOf<INameScope, MutableMap<String, RuntimeValueBase>>().withDefault { mutableMapOf() }
private val memvars = mutableMapOf<INameScope, MutableMap<String, Int>>().withDefault { mutableMapOf() }
private val byHeapId = mutableMapOf<Int, RuntimeValueBase>()
}
@@ -133,10 +140,10 @@ class AstVm(val program: Program) {
var rtcOffset = bootTime
private val rnd = Random(0)
private val statusFlagsSave = Stack<StatusFlags>()
private val registerXsave = Stack<RuntimeValue>()
private val registerYsave = Stack<RuntimeValue>()
private val registerAsave = Stack<RuntimeValue>()
private val statusFlagsSave = ArrayDeque<StatusFlags>()
private val registerXsave = ArrayDeque<RuntimeValueNumeric>()
private val registerYsave = ArrayDeque<RuntimeValueNumeric>()
private val registerAsave = ArrayDeque<RuntimeValueNumeric>()
init {
@@ -166,9 +173,9 @@ class AstVm(val program: Program) {
fun memwrite(address: Int, value: Short): Short {
if(address==0xa0 || address==0xa1 || address==0xa2) {
// a write to the jiffy clock, update the clock offset for the irq
val timeHi = if(address==0xa0) value else mem.getUByte_DMA(0xa0)
val timeMid = if(address==0xa1) value else mem.getUByte_DMA(0xa1)
val timeLo = if(address==0xa2) value else mem.getUByte_DMA(0xa2)
val timeHi = if(address==0xa0) value else mem.getUByteDirectly(0xa0)
val timeMid = if(address==0xa1) value else mem.getUByteDirectly(0xa1)
val timeLo = if(address==0xa2) value else mem.getUByteDirectly(0xa2)
val jiffies = (timeHi.toInt() shl 16) + (timeMid.toInt() shl 8) + timeLo
rtcOffset = bootTime - (jiffies*1000/60)
}
@@ -182,7 +189,7 @@ class AstVm(val program: Program) {
fun run() {
try {
val init = VariablesCreator(runtimeVariables, program.heap)
val init = VariablesCreator(runtimeVariables)
init.visit(program)
// initialize all global variables
@@ -253,9 +260,9 @@ class AstVm(val program: Program) {
rtcOffset = timeStamp
}
// update the C-64 60hz jiffy clock in the ZP addresses:
mem.setUByte_DMA(0x00a0, (jiffies ushr 16).toShort())
mem.setUByte_DMA(0x00a1, (jiffies ushr 8 and 255).toShort())
mem.setUByte_DMA(0x00a2, (jiffies and 255).toShort())
mem.setUByteDirectly(0x00a0, (jiffies ushr 16).toShort())
mem.setUByteDirectly(0x00a1, (jiffies ushr 8 and 255).toShort())
mem.setUByteDirectly(0x00a2, (jiffies and 255).toShort())
}
private val runtimeVariables = RuntimeVariables()
@@ -263,11 +270,11 @@ class AstVm(val program: Program) {
class LoopControlBreak : Exception()
class LoopControlContinue : Exception()
class LoopControlReturn(val returnvalue: RuntimeValue?) : Exception()
class LoopControlReturn(val returnvalue: RuntimeValueNumeric?) : Exception()
class LoopControlJump(val identifier: IdentifierReference?, val address: Int?, val generatedLabel: String?) : Exception()
internal fun executeSubroutine(sub: Subroutine, arguments: List<RuntimeValue>, startAtLabel: Label?=null): RuntimeValue? {
internal fun executeSubroutine(sub: Subroutine, arguments: List<RuntimeValueNumeric>, startAtLabel: Label?=null): RuntimeValueNumeric? {
if(sub.isAsmSubroutine) {
return performSyscall(sub, arguments)
}
@@ -335,7 +342,7 @@ class AstVm(val program: Program) {
val target = stmt.target.targetStatement(program.namespace)
when (target) {
is Subroutine -> {
val args = evaluate(stmt.arglist)
val args = evaluate(stmt.arglist).map { it as RuntimeValueNumeric }
if (target.isAsmSubroutine) {
performSyscall(target, args)
} else {
@@ -348,7 +355,7 @@ class AstVm(val program: Program) {
// swap cannot be implemented as a function, so inline it here
executeSwap(stmt)
} else {
val args = evaluate(stmt.arglist)
val args = evaluate(stmt.arglist).map { it as RuntimeValueNumeric }
performBuiltinFunction(target.name, args, statusflags)
}
}
@@ -362,7 +369,7 @@ class AstVm(val program: Program) {
if(stmt.value==null)
null
else
evaluate(stmt.value!!, evalCtx)
evaluate(stmt.value!!, evalCtx) as RuntimeValueNumeric
throw LoopControlReturn(value)
}
is Continue -> throw LoopControlContinue()
@@ -380,10 +387,10 @@ class AstVm(val program: Program) {
val identScope = ident.definingScope()
when(ident.type){
VarDeclType.VAR -> {
var value = runtimeVariables.get(identScope, ident.name)
var value = runtimeVariables.get(identScope, ident.name) as RuntimeValueNumeric
value = when {
stmt.operator == "++" -> value.add(RuntimeValue(value.type, 1))
stmt.operator == "--" -> value.sub(RuntimeValue(value.type, 1))
stmt.operator == "++" -> value.add(RuntimeValueNumeric(value.type, 1))
stmt.operator == "--" -> value.sub(RuntimeValueNumeric(value.type, 1))
else -> throw VmExecutionException("strange postincdec operator $stmt")
}
runtimeVariables.set(identScope, ident.name, value)
@@ -401,7 +408,7 @@ class AstVm(val program: Program) {
}
}
stmt.target.memoryAddress != null -> {
val addr = evaluate(stmt.target.memoryAddress!!.addressExpression, evalCtx).integerValue()
val addr = (evaluate(stmt.target.memoryAddress!!.addressExpression, evalCtx) as RuntimeValueNumeric).integerValue()
val newval = when {
stmt.operator == "++" -> mem.getUByte(addr)+1 and 255
stmt.operator == "--" -> mem.getUByte(addr)-1 and 255
@@ -411,12 +418,12 @@ class AstVm(val program: Program) {
}
stmt.target.arrayindexed != null -> {
val arrayvar = stmt.target.arrayindexed!!.identifier.targetVarDecl(program.namespace)!!
val arrayvalue = runtimeVariables.get(arrayvar.definingScope(), arrayvar.name)
val index = evaluate(stmt.target.arrayindexed!!.arrayspec.index, evalCtx).integerValue()
val arrayvalue = runtimeVariables.get(arrayvar.definingScope(), arrayvar.name) as RuntimeValueArray
val index = (evaluate(stmt.target.arrayindexed!!.arrayspec.index, evalCtx) as RuntimeValueNumeric).integerValue()
val elementType = stmt.target.arrayindexed!!.inferType(program)
if(!elementType.isKnown)
throw VmExecutionException("unknown/void elt type")
var value = RuntimeValue(elementType.typeOrElse(DataType.BYTE), arrayvalue.array!![index].toInt())
var value = RuntimeValueNumeric(elementType.typeOrElse(DataType.BYTE), arrayvalue.array[index].toInt())
value = when {
stmt.operator == "++" -> value.inc()
stmt.operator == "--" -> value.dec()
@@ -425,10 +432,10 @@ class AstVm(val program: Program) {
arrayvalue.array[index] = value.numericValue()
}
stmt.target.register != null -> {
var value = runtimeVariables.get(program.namespace, stmt.target.register!!.name)
var value = runtimeVariables.get(program.namespace, stmt.target.register!!.name) as RuntimeValueNumeric
value = when {
stmt.operator == "++" -> value.add(RuntimeValue(value.type, 1))
stmt.operator == "--" -> value.sub(RuntimeValue(value.type, 1))
stmt.operator == "++" -> value.add(RuntimeValueNumeric(value.type, 1))
stmt.operator == "--" -> value.sub(RuntimeValueNumeric(value.type, 1))
else -> throw VmExecutionException("strange postincdec operator $stmt")
}
runtimeVariables.set(program.namespace, stmt.target.register!!.name, value)
@@ -439,7 +446,7 @@ class AstVm(val program: Program) {
is Jump -> throw LoopControlJump(stmt.identifier, stmt.address, stmt.generatedLabel)
is InlineAssembly -> {
if (sub is Subroutine) {
val args = sub.parameters.map { runtimeVariables.get(sub, it.name) }
val args = sub.parameters.map { runtimeVariables.get(sub, it.name) as RuntimeValueNumeric }
performSyscall(sub, args)
throw LoopControlReturn(null)
}
@@ -447,7 +454,7 @@ class AstVm(val program: Program) {
}
is AnonymousScope -> executeAnonymousScope(stmt)
is IfStatement -> {
val condition = evaluate(stmt.condition, evalCtx)
val condition = evaluate(stmt.condition, evalCtx) as RuntimeValueNumeric
if (condition.asBoolean)
executeAnonymousScope(stmt.truepart)
else
@@ -478,7 +485,13 @@ class AstVm(val program: Program) {
loopvarDt = dt.typeOrElse(DataType.UBYTE)
loopvar = stmt.loopVar!!
}
val iterator = iterable.iterator()
val iterator =
when (iterable) {
is RuntimeValueRange -> iterable.iterator()
is RuntimeValueArray -> iterable.iterator()
is RuntimeValueString -> iterable.iterator()
else -> throw VmExecutionException("not iterable")
}
for (loopvalue in iterator) {
try {
oneForCycle(stmt, loopvarDt, loopvalue, loopvar)
@@ -490,11 +503,11 @@ class AstVm(val program: Program) {
}
}
is WhileLoop -> {
var condition = evaluate(stmt.condition, evalCtx)
var condition = evaluate(stmt.condition, evalCtx) as RuntimeValueNumeric
while (condition.asBoolean) {
try {
executeAnonymousScope(stmt.body)
condition = evaluate(stmt.condition, evalCtx)
condition = evaluate(stmt.condition, evalCtx) as RuntimeValueNumeric
} catch (b: LoopControlBreak) {
break
} catch (c: LoopControlContinue) {
@@ -504,7 +517,7 @@ class AstVm(val program: Program) {
}
is RepeatLoop -> {
do {
val condition = evaluate(stmt.untilCondition, evalCtx)
val condition = evaluate(stmt.untilCondition, evalCtx) as RuntimeValueNumeric
try {
executeAnonymousScope(stmt.body)
} catch (b: LoopControlBreak) {
@@ -515,7 +528,7 @@ class AstVm(val program: Program) {
} while (!condition.asBoolean)
}
is WhenStatement -> {
val condition=evaluate(stmt.condition, evalCtx)
val condition=evaluate(stmt.condition, evalCtx) as RuntimeValueNumeric
for(choice in stmt.choices) {
if(choice.values==null) {
// the 'else' choice
@@ -523,7 +536,7 @@ class AstVm(val program: Program) {
break
} else {
val value = choice.values!!.single().constValue(evalCtx.program) ?: throw VmExecutionException("can only use const values in when choices ${choice.position}")
val rtval = RuntimeValue.fromLv(value)
val rtval = RuntimeValueNumeric.fromLv(value)
if(condition==rtval) {
executeAnonymousScope(choice.statements)
break
@@ -548,7 +561,7 @@ class AstVm(val program: Program) {
performAssignment(target2, value1, swap, evalCtx)
}
fun performAssignment(target: AssignTarget, value: RuntimeValue, contextStmt: Statement, evalCtx: EvalContext) {
fun performAssignment(target: AssignTarget, value: RuntimeValueBase, contextStmt: Statement, evalCtx: EvalContext) {
val targetIdent = target.identifier
val targetArrayIndexed = target.arrayindexed
when {
@@ -558,27 +571,27 @@ class AstVm(val program: Program) {
if (decl.type == VarDeclType.MEMORY) {
val address = runtimeVariables.getMemoryAddress(decl.definingScope(), decl.name)
when (decl.datatype) {
DataType.UBYTE -> mem.setUByte(address, value.byteval!!)
DataType.BYTE -> mem.setSByte(address, value.byteval!!)
DataType.UWORD -> mem.setUWord(address, value.wordval!!)
DataType.WORD -> mem.setSWord(address, value.wordval!!)
DataType.FLOAT -> mem.setFloat(address, value.floatval!!)
DataType.STR -> mem.setString(address, value.str!!)
DataType.STR_S -> mem.setScreencodeString(address, value.str!!)
DataType.UBYTE -> mem.setUByte(address, (value as RuntimeValueNumeric).byteval!!)
DataType.BYTE -> mem.setSByte(address, (value as RuntimeValueNumeric).byteval!!)
DataType.UWORD -> mem.setUWord(address, (value as RuntimeValueNumeric).wordval!!)
DataType.WORD -> mem.setSWord(address, (value as RuntimeValueNumeric).wordval!!)
DataType.FLOAT -> mem.setFloat(address, (value as RuntimeValueNumeric).floatval!!)
DataType.STR -> mem.setString(address, (value as RuntimeValueString).str)
DataType.STR_S -> mem.setScreencodeString(address, (value as RuntimeValueString).str)
else -> throw VmExecutionException("weird memaddress type $decl")
}
} else
runtimeVariables.set(decl.definingScope(), decl.name, value)
}
target.memoryAddress != null -> {
val address = evaluate(target.memoryAddress.addressExpression, evalCtx).wordval!!
evalCtx.mem.setUByte(address, value.byteval!!)
val address = (evaluate(target.memoryAddress.addressExpression, evalCtx) as RuntimeValueNumeric).wordval!!
evalCtx.mem.setUByte(address, (value as RuntimeValueNumeric).byteval!!)
}
targetArrayIndexed != null -> {
val vardecl = targetArrayIndexed.identifier.targetVarDecl(program.namespace)!!
if(vardecl.type==VarDeclType.VAR) {
val array = evaluate(targetArrayIndexed.identifier, evalCtx)
val index = evaluate(targetArrayIndexed.arrayspec.index, evalCtx)
val index = evaluate(targetArrayIndexed.arrayspec.index, evalCtx) as RuntimeValueNumeric
when (array.type) {
DataType.ARRAY_UB -> {
if (value.type != DataType.UBYTE)
@@ -607,21 +620,21 @@ class AstVm(val program: Program) {
else -> throw VmExecutionException("strange array type ${array.type}")
}
if (array.type in ArrayDatatypes)
array.array!![index.integerValue()] = value.numericValue()
(array as RuntimeValueArray).array[index.integerValue()] = value.numericValue()
else if (array.type in StringDatatypes) {
val indexInt = index.integerValue()
val newchr = Petscii.decodePetscii(listOf(value.numericValue().toShort()), true)
val newstr = array.str!!.replaceRange(indexInt, indexInt + 1, newchr)
val newstr = (array as RuntimeValueString).str.replaceRange(indexInt, indexInt + 1, newchr)
val ident = contextStmt.definingScope().lookup(targetArrayIndexed.identifier.nameInSource, contextStmt) as? VarDecl
?: throw VmExecutionException("can't find assignment target ${target.identifier}")
val identScope = ident.definingScope()
program.heap.update(array.heapId!!, newstr)
runtimeVariables.set(identScope, ident.name, RuntimeValue(array.type, str = newstr, heapId = array.heapId))
runtimeVariables.set(identScope, ident.name, RuntimeValueString(array.type, newstr, array.heapId))
}
}
else {
value as RuntimeValueNumeric
val address = (vardecl.value as NumericLiteralValue).number.toInt()
val index = evaluate(targetArrayIndexed.arrayspec.index, evalCtx).integerValue()
val index = (evaluate(targetArrayIndexed.arrayspec.index, evalCtx) as RuntimeValueNumeric).integerValue()
val elementType = targetArrayIndexed.inferType(program)
if(!elementType.isKnown)
throw VmExecutionException("unknown/void array elt type $targetArrayIndexed")
@@ -645,22 +658,22 @@ class AstVm(val program: Program) {
private fun oneForCycle(stmt: ForLoop, loopvarDt: DataType, loopValue: Number, loopVar: IdentifierReference) {
// assign the new loop value to the loopvar, and run the code
performAssignment(AssignTarget(null, loopVar, null, null, loopVar.position),
RuntimeValue(loopvarDt, loopValue), stmt.body.statements.first(), evalCtx)
RuntimeValueNumeric(loopvarDt, loopValue), stmt.body.statements.first(), evalCtx)
executeAnonymousScope(stmt.body)
}
private fun evaluate(args: List<Expression>) = args.map { evaluate(it, evalCtx) }
private fun performSyscall(sub: Subroutine, args: List<RuntimeValue>): RuntimeValue? {
var result: RuntimeValue? = null
private fun performSyscall(sub: Subroutine, args: List<RuntimeValueNumeric>): RuntimeValueNumeric? {
var result: RuntimeValueNumeric? = null
when (sub.scopedname) {
"c64scr.print" -> {
// if the argument is an UWORD, consider it to be the "address" of the string (=heapId)
if (args[0].wordval != null) {
val str = program.heap.get(args[0].wordval!!).str!!
dialog.canvas.printText(str, true)
val encodedStr = getEncodedStringFromRuntimeVars(args[0].wordval!!)
dialog.canvas.printText(encodedStr)
} else
dialog.canvas.printText(args[0].str!!, true)
throw VmExecutionException("print non-heap string")
}
"c64scr.print_ub" -> {
dialog.canvas.printText(args[0].byteval!!.toString(), true)
@@ -733,10 +746,11 @@ class AstVm(val program: Program) {
}
val inputStr = input.joinToString("")
val heapId = args[0].wordval!!
val origStr = program.heap.get(heapId).str!!
val paddedStr=inputStr.padEnd(origStr.length+1, '\u0000').substring(0, origStr.length)
program.heap.update(heapId, paddedStr)
result = RuntimeValue(DataType.UBYTE, paddedStr.indexOf('\u0000'))
val origStrLength = getEncodedStringFromRuntimeVars(heapId).size
val encodedStr = Petscii.encodePetscii(inputStr, true).take(origStrLength).toMutableList()
while(encodedStr.size < origStrLength)
encodedStr.add(0)
result = RuntimeValueNumeric(DataType.UBYTE, encodedStr.indexOf(0))
}
"c64flt.print_f" -> {
dialog.canvas.printText(args[0].floatval.toString(), false)
@@ -752,13 +766,13 @@ class AstVm(val program: Program) {
Thread.sleep(10)
}
val char=dialog.keyboardBuffer.pop()
result = RuntimeValue(DataType.UBYTE, char.toShort())
result = RuntimeValueNumeric(DataType.UBYTE, char.toShort())
}
"c64utils.str2uword" -> {
val heapId = args[0].wordval!!
val argString = program.heap.get(heapId).str!!
val numericpart = argString.takeWhile { it.isDigit() }
result = RuntimeValue(DataType.UWORD, numericpart.toInt() and 65535)
val argString = getEncodedStringFromRuntimeVars(heapId)
val numericpart = argString.takeWhile { it.toChar().isDigit() }.toString()
result = RuntimeValueNumeric(DataType.UWORD, numericpart.toInt() and 65535)
}
else -> TODO("syscall ${sub.scopedname} $sub")
}
@@ -766,144 +780,158 @@ class AstVm(val program: Program) {
return result
}
private fun performBuiltinFunction(name: String, args: List<RuntimeValue>, statusflags: StatusFlags): RuntimeValue? {
private fun getEncodedStringFromRuntimeVars(heapId: Int): List<Short> {
val stringvar = runtimeVariables.getByHeapId(heapId) as RuntimeValueString
return when {
stringvar.type==DataType.STR -> Petscii.encodePetscii(stringvar.str, true)
stringvar.type==DataType.STR_S -> Petscii.encodeScreencode(stringvar.str, true)
else -> throw VmExecutionException("weird string type")
}
}
private fun getArrayFromRuntimeVars(heapId: Int): IntArray {
val arrayvar = runtimeVariables.getByHeapId(heapId) as RuntimeValueArray
return arrayvar.array.map { it.toInt() }.toIntArray()
}
private fun performBuiltinFunction(name: String, args: List<RuntimeValueBase>, statusflags: StatusFlags): RuntimeValueNumeric? {
return when (name) {
"rnd" -> RuntimeValue(DataType.UBYTE, rnd.nextInt() and 255)
"rndw" -> RuntimeValue(DataType.UWORD, rnd.nextInt() and 65535)
"rndf" -> RuntimeValue(DataType.FLOAT, rnd.nextDouble())
"lsb" -> RuntimeValue(DataType.UBYTE, args[0].integerValue() and 255)
"msb" -> RuntimeValue(DataType.UBYTE, (args[0].integerValue() ushr 8) and 255)
"sin" -> RuntimeValue(DataType.FLOAT, sin(args[0].numericValue().toDouble()))
"rnd" -> RuntimeValueNumeric(DataType.UBYTE, rnd.nextInt() and 255)
"rndw" -> RuntimeValueNumeric(DataType.UWORD, rnd.nextInt() and 65535)
"rndf" -> RuntimeValueNumeric(DataType.FLOAT, rnd.nextDouble())
"lsb" -> RuntimeValueNumeric(DataType.UBYTE, args[0].integerValue() and 255)
"msb" -> RuntimeValueNumeric(DataType.UBYTE, (args[0].integerValue() ushr 8) and 255)
"sin" -> RuntimeValueNumeric(DataType.FLOAT, sin(args[0].numericValue().toDouble()))
"sin8" -> {
val rad = args[0].numericValue().toDouble() / 256.0 * 2.0 * PI
RuntimeValue(DataType.BYTE, (127.0 * sin(rad)).toShort())
RuntimeValueNumeric(DataType.BYTE, (127.0 * sin(rad)).toShort())
}
"sin8u" -> {
val rad = args[0].numericValue().toDouble() / 256.0 * 2.0 * PI
RuntimeValue(DataType.UBYTE, (128.0 + 127.5 * sin(rad)).toShort())
RuntimeValueNumeric(DataType.UBYTE, (128.0 + 127.5 * sin(rad)).toShort())
}
"sin16" -> {
val rad = args[0].numericValue().toDouble() / 256.0 * 2.0 * PI
RuntimeValue(DataType.BYTE, (32767.0 * sin(rad)).toShort())
RuntimeValueNumeric(DataType.BYTE, (32767.0 * sin(rad)).toShort())
}
"sin16u" -> {
val rad = args[0].numericValue().toDouble() / 256.0 * 2.0 * PI
RuntimeValue(DataType.UBYTE, (32768.0 + 32767.5 * sin(rad)).toShort())
RuntimeValueNumeric(DataType.UBYTE, (32768.0 + 32767.5 * sin(rad)).toShort())
}
"cos" -> RuntimeValue(DataType.FLOAT, cos(args[0].numericValue().toDouble()))
"cos" -> RuntimeValueNumeric(DataType.FLOAT, cos(args[0].numericValue().toDouble()))
"cos8" -> {
val rad = args[0].numericValue().toDouble() / 256.0 * 2.0 * PI
RuntimeValue(DataType.BYTE, (127.0 * cos(rad)).toShort())
RuntimeValueNumeric(DataType.BYTE, (127.0 * cos(rad)).toShort())
}
"cos8u" -> {
val rad = args[0].numericValue().toDouble() / 256.0 * 2.0 * PI
RuntimeValue(DataType.UBYTE, (128.0 + 127.5 * cos(rad)).toShort())
RuntimeValueNumeric(DataType.UBYTE, (128.0 + 127.5 * cos(rad)).toShort())
}
"cos16" -> {
val rad = args[0].numericValue().toDouble() / 256.0 * 2.0 * PI
RuntimeValue(DataType.BYTE, (32767.0 * cos(rad)).toShort())
RuntimeValueNumeric(DataType.BYTE, (32767.0 * cos(rad)).toShort())
}
"cos16u" -> {
val rad = args[0].numericValue().toDouble() / 256.0 * 2.0 * PI
RuntimeValue(DataType.UBYTE, (32768.0 + 32767.5 * cos(rad)).toShort())
RuntimeValueNumeric(DataType.UBYTE, (32768.0 + 32767.5 * cos(rad)).toShort())
}
"tan" -> RuntimeValue(DataType.FLOAT, tan(args[0].numericValue().toDouble()))
"atan" -> RuntimeValue(DataType.FLOAT, atan(args[0].numericValue().toDouble()))
"ln" -> RuntimeValue(DataType.FLOAT, ln(args[0].numericValue().toDouble()))
"log2" -> RuntimeValue(DataType.FLOAT, log2(args[0].numericValue().toDouble()))
"sqrt" -> RuntimeValue(DataType.FLOAT, sqrt(args[0].numericValue().toDouble()))
"sqrt16" -> RuntimeValue(DataType.UBYTE, sqrt(args[0].wordval!!.toDouble()).toInt())
"rad" -> RuntimeValue(DataType.FLOAT, Math.toRadians(args[0].numericValue().toDouble()))
"deg" -> RuntimeValue(DataType.FLOAT, Math.toDegrees(args[0].numericValue().toDouble()))
"round" -> RuntimeValue(DataType.FLOAT, round(args[0].numericValue().toDouble()))
"floor" -> RuntimeValue(DataType.FLOAT, floor(args[0].numericValue().toDouble()))
"ceil" -> RuntimeValue(DataType.FLOAT, ceil(args[0].numericValue().toDouble()))
"tan" -> RuntimeValueNumeric(DataType.FLOAT, tan(args[0].numericValue().toDouble()))
"atan" -> RuntimeValueNumeric(DataType.FLOAT, atan(args[0].numericValue().toDouble()))
"ln" -> RuntimeValueNumeric(DataType.FLOAT, ln(args[0].numericValue().toDouble()))
"log2" -> RuntimeValueNumeric(DataType.FLOAT, log2(args[0].numericValue().toDouble()))
"sqrt" -> RuntimeValueNumeric(DataType.FLOAT, sqrt(args[0].numericValue().toDouble()))
"sqrt16" -> RuntimeValueNumeric(DataType.UBYTE, sqrt((args[0] as RuntimeValueNumeric).wordval!!.toDouble()).toInt())
"rad" -> RuntimeValueNumeric(DataType.FLOAT, Math.toRadians(args[0].numericValue().toDouble()))
"deg" -> RuntimeValueNumeric(DataType.FLOAT, Math.toDegrees(args[0].numericValue().toDouble()))
"round" -> RuntimeValueNumeric(DataType.FLOAT, round(args[0].numericValue().toDouble()))
"floor" -> RuntimeValueNumeric(DataType.FLOAT, floor(args[0].numericValue().toDouble()))
"ceil" -> RuntimeValueNumeric(DataType.FLOAT, ceil(args[0].numericValue().toDouble()))
"rol" -> {
val (result, newCarry) = args[0].rol(statusflags.carry)
val (result, newCarry) = (args[0] as RuntimeValueNumeric).rol(statusflags.carry)
statusflags.carry = newCarry
return result
}
"rol2" -> args[0].rol2()
"rol2" -> (args[0] as RuntimeValueNumeric).rol2()
"ror" -> {
val (result, newCarry) = args[0].ror(statusflags.carry)
val (result, newCarry) = (args[0] as RuntimeValueNumeric).ror(statusflags.carry)
statusflags.carry = newCarry
return result
}
"ror2" -> args[0].ror2()
"lsl" -> args[0].shl()
"lsr" -> args[0].shr()
"ror2" -> (args[0] as RuntimeValueNumeric).ror2()
"lsl" -> (args[0] as RuntimeValueNumeric).shl()
"lsr" -> (args[0] as RuntimeValueNumeric).shr()
"abs" -> {
when (args[0].type) {
DataType.UBYTE -> args[0]
DataType.BYTE -> RuntimeValue(DataType.UBYTE, abs(args[0].numericValue().toDouble()))
DataType.UWORD -> args[0]
DataType.WORD -> RuntimeValue(DataType.UWORD, abs(args[0].numericValue().toDouble()))
DataType.FLOAT -> RuntimeValue(DataType.FLOAT, abs(args[0].numericValue().toDouble()))
DataType.UBYTE -> (args[0] as RuntimeValueNumeric)
DataType.BYTE -> RuntimeValueNumeric(DataType.UBYTE, abs(args[0].numericValue().toDouble()))
DataType.UWORD -> (args[0] as RuntimeValueNumeric)
DataType.WORD -> RuntimeValueNumeric(DataType.UWORD, abs(args[0].numericValue().toDouble()))
DataType.FLOAT -> RuntimeValueNumeric(DataType.FLOAT, abs(args[0].numericValue().toDouble()))
else -> throw VmExecutionException("strange abs type ${args[0]}")
}
}
"max" -> {
val numbers = args.single().array!!.map { it.toDouble() }
RuntimeValue(ArrayElementTypes.getValue(args[0].type), numbers.max())
val numbers = (args.single() as RuntimeValueArray).array.map { it.toDouble() }
RuntimeValueNumeric(ArrayElementTypes.getValue(args[0].type), numbers.max()!!)
}
"min" -> {
val numbers = args.single().array!!.map { it.toDouble() }
RuntimeValue(ArrayElementTypes.getValue(args[0].type), numbers.min())
val numbers = (args.single() as RuntimeValueArray).array.map { it.toDouble() }
RuntimeValueNumeric(ArrayElementTypes.getValue(args[0].type), numbers.min()!!)
}
"sum" -> {
val sum = args.single().array!!.map { it.toDouble() }.sum()
val sum = (args.single() as RuntimeValueArray).array.map { it.toDouble() }.sum()
when (args[0].type) {
DataType.ARRAY_UB -> RuntimeValue(DataType.UWORD, sum)
DataType.ARRAY_B -> RuntimeValue(DataType.WORD, sum)
DataType.ARRAY_UW -> RuntimeValue(DataType.UWORD, sum)
DataType.ARRAY_W -> RuntimeValue(DataType.WORD, sum)
DataType.ARRAY_F -> RuntimeValue(DataType.FLOAT, sum)
DataType.ARRAY_UB -> RuntimeValueNumeric(DataType.UWORD, sum)
DataType.ARRAY_B -> RuntimeValueNumeric(DataType.WORD, sum)
DataType.ARRAY_UW -> RuntimeValueNumeric(DataType.UWORD, sum)
DataType.ARRAY_W -> RuntimeValueNumeric(DataType.WORD, sum)
DataType.ARRAY_F -> RuntimeValueNumeric(DataType.FLOAT, sum)
else -> throw VmExecutionException("weird sum type ${args[0]}")
}
}
"any" -> {
val numbers = args.single().array!!.map { it.toDouble() }
RuntimeValue(DataType.UBYTE, if (numbers.any { it != 0.0 }) 1 else 0)
val numbers = (args.single() as RuntimeValueArray).array.map { it.toDouble() }
RuntimeValueNumeric(DataType.UBYTE, if (numbers.any { it != 0.0 }) 1 else 0)
}
"all" -> {
val numbers = args.single().array!!.map { it.toDouble() }
RuntimeValue(DataType.UBYTE, if (numbers.all { it != 0.0 }) 1 else 0)
val numbers = (args.single() as RuntimeValueArray).array.map { it.toDouble() }
RuntimeValueNumeric(DataType.UBYTE, if (numbers.all { it != 0.0 }) 1 else 0)
}
"swap" ->
throw VmExecutionException("swap() cannot be implemented as a function")
"strlen" -> {
val zeroIndex = args[0].str!!.indexOf(0.toChar())
val zeroIndex = (args[0] as RuntimeValueString).str.indexOf(0.toChar())
if (zeroIndex >= 0)
RuntimeValue(DataType.UBYTE, zeroIndex)
RuntimeValueNumeric(DataType.UBYTE, zeroIndex)
else
RuntimeValue(DataType.UBYTE, args[0].str!!.length)
RuntimeValueNumeric(DataType.UBYTE, (args[0] as RuntimeValueString).str.length)
}
"memset" -> {
val heapId = args[0].wordval!!
val target = program.heap.get(heapId).array ?: throw VmExecutionException("memset target is not an array")
val heapId = (args[0] as RuntimeValueNumeric).wordval!!
val target = getArrayFromRuntimeVars(heapId)
val amount = args[1].integerValue()
val value = args[2].integerValue()
for (i in 0 until amount) {
target[i] = IntegerOrAddressOf(value, null)
target[i] = value
}
null
}
"memsetw" -> {
val heapId = args[0].wordval!!
val target = program.heap.get(heapId).array ?: throw VmExecutionException("memset target is not an array")
val heapId = (args[0] as RuntimeValueNumeric).wordval!!
val target = getArrayFromRuntimeVars(heapId)
val amount = args[1].integerValue()
val value = args[2].integerValue()
for (i in 0 until amount step 2) {
target[i * 2] = IntegerOrAddressOf(value and 255, null)
target[i * 2 + 1] = IntegerOrAddressOf(value ushr 8, null)
target[i * 2] = value and 255
target[i * 2 + 1] = value ushr 8
}
null
}
"memcopy" -> {
val sourceHeapId = args[0].wordval!!
val destHeapId = args[1].wordval!!
val source = program.heap.get(sourceHeapId).array!!
val dest = program.heap.get(destHeapId).array!!
val sourceHeapId = (args[0] as RuntimeValueNumeric).wordval!!
val destHeapId = (args[1] as RuntimeValueNumeric).wordval!!
val source = getArrayFromRuntimeVars(sourceHeapId)
val dest = getArrayFromRuntimeVars(destHeapId)
val amount = args[2].integerValue()
for(i in 0 until amount) {
dest[i] = source[i]
@@ -912,7 +940,7 @@ class AstVm(val program: Program) {
}
"mkword" -> {
val result = (args[1].integerValue() shl 8) or args[0].integerValue()
RuntimeValue(DataType.UWORD, result)
RuntimeValueNumeric(DataType.UWORD, result)
}
"set_carry" -> {
statusflags.carry=true
@@ -935,13 +963,13 @@ class AstVm(val program: Program) {
val zero = if(statusflags.zero) 2 else 0
val irqd = if(statusflags.irqd) 4 else 0
val negative = if(statusflags.negative) 128 else 0
RuntimeValue(DataType.UBYTE, carry or zero or irqd or negative)
RuntimeValueNumeric(DataType.UBYTE, carry or zero or irqd or negative)
}
"rsave" -> {
statusFlagsSave.push(statusflags)
registerAsave.push(runtimeVariables.get(program.namespace, Register.A.name))
registerXsave.push(runtimeVariables.get(program.namespace, Register.X.name))
registerYsave.push(runtimeVariables.get(program.namespace, Register.Y.name))
registerAsave.push(runtimeVariables.get(program.namespace, Register.A.name) as RuntimeValueNumeric)
registerXsave.push(runtimeVariables.get(program.namespace, Register.X.name) as RuntimeValueNumeric)
registerYsave.push(runtimeVariables.get(program.namespace, Register.Y.name) as RuntimeValueNumeric)
null
}
"rrestore" -> {
@@ -955,8 +983,25 @@ class AstVm(val program: Program) {
runtimeVariables.set(program.namespace, Register.Y.name, registerYsave.pop())
null
}
"sort" -> {
val array=args.single() as RuntimeValueArray
array.array.sort()
null
}
"reverse" -> {
val array=args.single() as RuntimeValueArray
array.array.reverse()
null
}
"sgn" -> {
val value = args.single().numericValue().toDouble()
when {
value<0.0 -> RuntimeValueNumeric(DataType.BYTE, -1)
value==0.0 -> RuntimeValueNumeric(DataType.BYTE, 0)
else -> RuntimeValueNumeric(DataType.BYTE, 1)
}
}
else -> TODO("builtin function $name")
}
}
}
+47 -39
View File
@@ -10,12 +10,11 @@ import prog8.ast.statements.BuiltinFunctionStatementPlaceholder
import prog8.ast.statements.Label
import prog8.ast.statements.Subroutine
import prog8.ast.statements.VarDecl
import prog8.vm.RuntimeValue
import prog8.vm.RuntimeValueRange
import prog8.vm.*
typealias BuiltinfunctionCaller = (name: String, args: List<RuntimeValue>, flags: StatusFlags) -> RuntimeValue?
typealias SubroutineCaller = (sub: Subroutine, args: List<RuntimeValue>, startAtLabel: Label?) -> RuntimeValue?
typealias BuiltinfunctionCaller = (name: String, args: List<RuntimeValueNumeric>, flags: StatusFlags) -> RuntimeValueNumeric?
typealias SubroutineCaller = (sub: Subroutine, args: List<RuntimeValueNumeric>, startAtLabel: Label?) -> RuntimeValueNumeric?
class EvalContext(val program: Program, val mem: Memory, val statusflags: StatusFlags,
@@ -23,34 +22,34 @@ class EvalContext(val program: Program, val mem: Memory, val statusflags: Status
val performBuiltinFunction: BuiltinfunctionCaller,
val executeSubroutine: SubroutineCaller)
fun evaluate(expr: Expression, ctx: EvalContext): RuntimeValue {
fun evaluate(expr: Expression, ctx: EvalContext): RuntimeValueBase {
val constval = expr.constValue(ctx.program)
if(constval!=null)
return RuntimeValue.fromLv(constval)
return RuntimeValueNumeric.fromLv(constval)
when(expr) {
is NumericLiteralValue -> return RuntimeValue.fromLv(expr)
is StringLiteralValue -> return RuntimeValue.fromLv(expr, ctx.program.heap)
is ArrayLiteralValue -> return RuntimeValue.fromLv(expr, ctx.program.heap)
is NumericLiteralValue -> return RuntimeValueNumeric.fromLv(expr)
is StringLiteralValue -> return RuntimeValueString.fromLv(expr)
is ArrayLiteralValue -> return RuntimeValueArray.fromLv(expr)
is PrefixExpression -> {
return when(expr.operator) {
"-" -> evaluate(expr.expression, ctx).neg()
"~" -> evaluate(expr.expression, ctx).inv()
"not" -> evaluate(expr.expression, ctx).not()
"-" -> (evaluate(expr.expression, ctx) as RuntimeValueNumeric).neg()
"~" -> (evaluate(expr.expression, ctx) as RuntimeValueNumeric).inv()
"not" -> (evaluate(expr.expression, ctx) as RuntimeValueNumeric).not()
// unary '+' should have been optimized away
else -> throw VmExecutionException("unsupported prefix operator "+expr.operator)
}
}
is BinaryExpression -> {
val left = evaluate(expr.left, ctx)
val right = evaluate(expr.right, ctx)
val left = evaluate(expr.left, ctx) as RuntimeValueNumeric
val right = evaluate(expr.right, ctx) as RuntimeValueNumeric
return when(expr.operator) {
"<" -> RuntimeValue(DataType.UBYTE, if (left < right) 1 else 0)
"<=" -> RuntimeValue(DataType.UBYTE, if (left <= right) 1 else 0)
">" -> RuntimeValue(DataType.UBYTE, if (left > right) 1 else 0)
">=" -> RuntimeValue(DataType.UBYTE, if (left >= right) 1 else 0)
"==" -> RuntimeValue(DataType.UBYTE, if (left == right) 1 else 0)
"!=" -> RuntimeValue(DataType.UBYTE, if (left != right) 1 else 0)
"<" -> RuntimeValueNumeric(DataType.UBYTE, if (left < right) 1 else 0)
"<=" -> RuntimeValueNumeric(DataType.UBYTE, if (left <= right) 1 else 0)
">" -> RuntimeValueNumeric(DataType.UBYTE, if (left > right) 1 else 0)
">=" -> RuntimeValueNumeric(DataType.UBYTE, if (left >= right) 1 else 0)
"==" -> RuntimeValueNumeric(DataType.UBYTE, if (left == right) 1 else 0)
"!=" -> RuntimeValueNumeric(DataType.UBYTE, if (left != right) 1 else 0)
"+" -> left.add(right)
"-" -> left.sub(right)
"*" -> left.mul(right)
@@ -78,27 +77,36 @@ fun evaluate(expr: Expression, ctx: EvalContext): RuntimeValue {
}
is ArrayIndexedExpression -> {
val array = evaluate(expr.identifier, ctx)
val index = evaluate(expr.arrayspec.index, ctx)
val value = array.array!![index.integerValue()]
return RuntimeValue(ArrayElementTypes.getValue(array.type), value)
val index = evaluate(expr.arrayspec.index, ctx) as RuntimeValueNumeric
return when (array) {
is RuntimeValueString -> {
val value = array.str[index.integerValue()]
RuntimeValueNumeric(ArrayElementTypes.getValue(array.type), value.toShort())
}
is RuntimeValueArray -> {
val value = array.array[index.integerValue()]
RuntimeValueNumeric(ArrayElementTypes.getValue(array.type), value)
}
else -> throw VmExecutionException("weird type")
}
}
is TypecastExpression -> {
return evaluate(expr.expression, ctx).cast(expr.type)
return (evaluate(expr.expression, ctx) as RuntimeValueNumeric).cast(expr.type)
}
is AddressOf -> {
// we support: address of heap var -> the heap id
return try {
val heapId = expr.identifier.heapId(ctx.program.namespace)
RuntimeValue(DataType.UWORD, heapId)
RuntimeValueNumeric(DataType.UWORD, heapId)
} catch( f: FatalAstException) {
// fallback: use the hash of the name, so we have at least *a* value...
val address = expr.identifier.hashCode() and 65535
RuntimeValue(DataType.UWORD, address)
RuntimeValueNumeric(DataType.UWORD, address)
}
}
is DirectMemoryRead -> {
val address = evaluate(expr.addressExpression, ctx).wordval!!
return RuntimeValue(DataType.UBYTE, ctx.mem.getUByte(address))
val address = (evaluate(expr.addressExpression, ctx) as RuntimeValueNumeric).wordval!!
return RuntimeValueNumeric(DataType.UBYTE, ctx.mem.getUByte(address))
}
is RegisterExpr -> return ctx.runtimeVars.get(ctx.program.namespace, expr.register.name)
is IdentifierReference -> {
@@ -111,13 +119,13 @@ fun evaluate(expr: Expression, ctx: EvalContext): RuntimeValue {
else -> {
val address = ctx.runtimeVars.getMemoryAddress(variable.definingScope(), variable.name)
return when(variable.datatype) {
DataType.UBYTE -> RuntimeValue(DataType.UBYTE, ctx.mem.getUByte(address))
DataType.BYTE -> RuntimeValue(DataType.BYTE, ctx.mem.getSByte(address))
DataType.UWORD -> RuntimeValue(DataType.UWORD, ctx.mem.getUWord(address))
DataType.WORD -> RuntimeValue(DataType.WORD, ctx.mem.getSWord(address))
DataType.FLOAT -> RuntimeValue(DataType.FLOAT, ctx.mem.getFloat(address))
DataType.STR -> RuntimeValue(DataType.STR, str = ctx.mem.getString(address))
DataType.STR_S -> RuntimeValue(DataType.STR_S, str = ctx.mem.getScreencodeString(address))
DataType.UBYTE -> RuntimeValueNumeric(DataType.UBYTE, ctx.mem.getUByte(address))
DataType.BYTE -> RuntimeValueNumeric(DataType.BYTE, ctx.mem.getSByte(address))
DataType.UWORD -> RuntimeValueNumeric(DataType.UWORD, ctx.mem.getUWord(address))
DataType.WORD -> RuntimeValueNumeric(DataType.WORD, ctx.mem.getSWord(address))
DataType.FLOAT -> RuntimeValueNumeric(DataType.FLOAT, ctx.mem.getFloat(address))
DataType.STR -> RuntimeValueString(DataType.STR, ctx.mem.getString(address), null)
DataType.STR_S -> RuntimeValueString(DataType.STR_S, ctx.mem.getScreencodeString(address)!!, null)
else -> throw VmExecutionException("unexpected datatype $variable")
}
}
@@ -127,7 +135,7 @@ fun evaluate(expr: Expression, ctx: EvalContext): RuntimeValue {
}
is FunctionCall -> {
val sub = expr.target.targetStatement(ctx.program.namespace)
val args = expr.arglist.map { evaluate(it, ctx) }
val args = expr.arglist.map { evaluate(it, ctx) as RuntimeValueNumeric }
return when(sub) {
is Subroutine -> {
val result = ctx.executeSubroutine(sub, args, null)
@@ -153,9 +161,9 @@ fun evaluate(expr: Expression, ctx: EvalContext): RuntimeValue {
else
throw VmExecutionException("couldn't determine datatype")
}
val fromVal = evaluate(expr.from, ctx).integerValue()
val toVal = evaluate(expr.to, ctx).integerValue()
val stepVal = evaluate(expr.step, ctx).integerValue()
val fromVal = (evaluate(expr.from, ctx) as RuntimeValueNumeric).integerValue()
val toVal = (evaluate(expr.to, ctx) as RuntimeValueNumeric).integerValue()
val stepVal = (evaluate(expr.step, ctx) as RuntimeValueNumeric).integerValue()
val range = makeRange(fromVal, toVal, stepVal)
val dt = expr.inferType(ctx.program)
if(dt.isKnown)
+2 -2
View File
@@ -21,7 +21,7 @@ class Memory(private val readObserver: (address: Int, value: Short) -> Short,
else mem[address]
}
fun getUByte_DMA(address: Int): Short {
fun getUByteDirectly(address: Int): Short {
return mem[address]
}
@@ -39,7 +39,7 @@ class Memory(private val readObserver: (address: Int, value: Short) -> Short,
else value
}
fun setUByte_DMA(address: Int, value: Short) {
fun setUByteDirectly(address: Int, value: Short) {
if(value !in 0..255)
throw VmExecutionException("ubyte value out of range $value")
mem[address] = value
+6 -2
View File
@@ -6,7 +6,7 @@ import java.awt.*
import java.awt.event.KeyEvent
import java.awt.event.KeyListener
import java.awt.image.BufferedImage
import java.util.*
import java.util.ArrayDeque
import javax.swing.JFrame
import javax.swing.JPanel
import javax.swing.Timer
@@ -18,7 +18,7 @@ class BitmapScreenPanel : KeyListener, JPanel() {
private val g2d = image.graphics as Graphics2D
private var cursorX: Int=0
private var cursorY: Int=0
val keyboardBuffer: Deque<Char> = LinkedList()
val keyboardBuffer = ArrayDeque<Char>()
init {
val size = Dimension(image.width * SCALING, image.height * SCALING)
@@ -75,6 +75,10 @@ class BitmapScreenPanel : KeyListener, JPanel() {
}
}
fun printText(text: Iterable<Short>) {
text.forEach { printPetscii(it, false) }
}
fun printPetscii(char: Short, inverseVideo: Boolean=false) {
if(char==13.toShort() || char==141.toShort()) {
cursorX=0
+19 -19
View File
@@ -13,16 +13,17 @@ import prog8.ast.statements.Statement
import prog8.ast.statements.StructDecl
import prog8.ast.statements.VarDecl
import prog8.ast.statements.ZeropageWish
import prog8.compiler.HeapValues
import prog8.vm.RuntimeValue
import prog8.vm.RuntimeValueArray
import prog8.vm.RuntimeValueNumeric
import prog8.vm.RuntimeValueString
class VariablesCreator(private val runtimeVariables: RuntimeVariables, private val heap: HeapValues) : IAstModifyingVisitor {
class VariablesCreator(private val runtimeVariables: RuntimeVariables) : IAstModifyingVisitor {
override fun visit(program: Program) {
// define the three registers as global variables
runtimeVariables.define(program.namespace, Register.A.name, RuntimeValue(DataType.UBYTE, 0))
runtimeVariables.define(program.namespace, Register.X.name, RuntimeValue(DataType.UBYTE, 255))
runtimeVariables.define(program.namespace, Register.Y.name, RuntimeValue(DataType.UBYTE, 0))
runtimeVariables.define(program.namespace, Register.A.name, RuntimeValueNumeric(DataType.UBYTE, 0))
runtimeVariables.define(program.namespace, Register.X.name, RuntimeValueNumeric(DataType.UBYTE, 255))
runtimeVariables.define(program.namespace, Register.Y.name, RuntimeValueNumeric(DataType.UBYTE, 0))
val globalpos = Position("<<global>>", 0, 0, 0)
val vdA = VarDecl(VarDeclType.VAR, DataType.UBYTE, ZeropageWish.DONTCARE, null, Register.A.name, null,
@@ -49,12 +50,19 @@ class VariablesCreator(private val runtimeVariables: RuntimeVariables, private v
if(decl.datatype!=DataType.STRUCT) {
val numericLv = decl.value as? NumericLiteralValue
val value = if(numericLv!=null) {
RuntimeValue.fromLv(numericLv)
RuntimeValueNumeric.fromLv(numericLv)
} else {
if(decl.value is StringLiteralValue)
RuntimeValue.fromLv(decl.value as StringLiteralValue, heap)
else
RuntimeValue.fromLv(decl.value as ArrayLiteralValue, heap)
val strLv = decl.value as? StringLiteralValue
val arrayLv = decl.value as? ArrayLiteralValue
when {
strLv!=null -> {
RuntimeValueString.fromLv(strLv)
}
arrayLv!=null -> {
RuntimeValueArray.fromLv(arrayLv)
}
else -> throw VmExecutionException("weird var type")
}
}
runtimeVariables.define(decl.definingScope(), decl.name, value)
}
@@ -69,12 +77,4 @@ class VariablesCreator(private val runtimeVariables: RuntimeVariables, private v
}
return super.visit(decl)
}
// override fun accept(assignment: Assignment): Statement {
// if(assignment is VariableInitializationAssignment) {
// println("INIT VAR $assignment")
// }
// return super.accept(assignment)
// }
}
+5 -5
View File
@@ -83,8 +83,8 @@ class TestParserNumericLiteralValue {
@Test
fun testEqualsRef() {
assertTrue(StringLiteralValue(DataType.STR, "hello", position = dummyPos) == StringLiteralValue(DataType.STR, "hello", position = dummyPos))
assertFalse(StringLiteralValue(DataType.STR, "hello", position = dummyPos) == StringLiteralValue(DataType.STR, "bye", position = dummyPos))
assertTrue(StringLiteralValue(DataType.STR, "hello", dummyPos) == StringLiteralValue(DataType.STR, "hello", dummyPos))
assertFalse(StringLiteralValue(DataType.STR, "hello", dummyPos) == StringLiteralValue(DataType.STR, "bye", dummyPos))
val lvOne = NumericLiteralValue(DataType.UBYTE, 1, dummyPos)
val lvTwo = NumericLiteralValue(DataType.UBYTE, 2, dummyPos)
@@ -93,9 +93,9 @@ class TestParserNumericLiteralValue {
val lvTwoR = NumericLiteralValue(DataType.UBYTE, 2, dummyPos)
val lvThreeR = NumericLiteralValue(DataType.UBYTE, 3, dummyPos)
val lvFour= NumericLiteralValue(DataType.UBYTE, 4, dummyPos)
val lv1 = ArrayLiteralValue(DataType.ARRAY_UB, arrayOf(lvOne, lvTwo, lvThree), position = dummyPos)
val lv2 = ArrayLiteralValue(DataType.ARRAY_UB, arrayOf(lvOneR, lvTwoR, lvThreeR), position = dummyPos)
val lv3 = ArrayLiteralValue(DataType.ARRAY_UB, arrayOf(lvOneR, lvTwoR, lvFour), position = dummyPos)
val lv1 = ArrayLiteralValue(DataType.ARRAY_UB, arrayOf(lvOne, lvTwo, lvThree), null, dummyPos)
val lv2 = ArrayLiteralValue(DataType.ARRAY_UB, arrayOf(lvOneR, lvTwoR, lvThreeR), null, dummyPos)
val lv3 = ArrayLiteralValue(DataType.ARRAY_UB, arrayOf(lvOneR, lvTwoR, lvFour), null, dummyPos)
assertEquals(lv1, lv2)
assertNotEquals(lv1, lv3)
}
+220 -237
View File
@@ -3,66 +3,66 @@ package prog8tests
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.TestInstance
import prog8.ast.base.DataType
import prog8.vm.RuntimeValue
import prog8.vm.RuntimeValueNumeric
import kotlin.test.*
private fun sameValueAndType(v1: RuntimeValue, v2: RuntimeValue): Boolean {
private fun sameValueAndType(v1: RuntimeValueNumeric, v2: RuntimeValueNumeric): Boolean {
return v1.type==v2.type && v1==v2
}
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class TestRuntimeValue {
class TestRuntimeValueNumeric {
@Test
fun testValueRanges() {
assertEquals(0, RuntimeValue(DataType.UBYTE, 0).integerValue())
assertEquals(255, RuntimeValue(DataType.UBYTE, 255).integerValue())
assertFailsWith<IllegalArgumentException> { RuntimeValue(DataType.UBYTE, -1)}
assertFailsWith<IllegalArgumentException> { RuntimeValue(DataType.UBYTE, 256)}
assertEquals(0, RuntimeValueNumeric(DataType.UBYTE, 0).integerValue())
assertEquals(255, RuntimeValueNumeric(DataType.UBYTE, 255).integerValue())
assertFailsWith<IllegalArgumentException> { RuntimeValueNumeric(DataType.UBYTE, -1)}
assertFailsWith<IllegalArgumentException> { RuntimeValueNumeric(DataType.UBYTE, 256)}
assertEquals(0, RuntimeValue(DataType.BYTE, 0).integerValue())
assertEquals(-128, RuntimeValue(DataType.BYTE, -128).integerValue())
assertEquals(127, RuntimeValue(DataType.BYTE, 127).integerValue())
assertFailsWith<IllegalArgumentException> { RuntimeValue(DataType.BYTE, -129)}
assertFailsWith<IllegalArgumentException> { RuntimeValue(DataType.BYTE, 128)}
assertEquals(0, RuntimeValueNumeric(DataType.BYTE, 0).integerValue())
assertEquals(-128, RuntimeValueNumeric(DataType.BYTE, -128).integerValue())
assertEquals(127, RuntimeValueNumeric(DataType.BYTE, 127).integerValue())
assertFailsWith<IllegalArgumentException> { RuntimeValueNumeric(DataType.BYTE, -129)}
assertFailsWith<IllegalArgumentException> { RuntimeValueNumeric(DataType.BYTE, 128)}
assertEquals(0, RuntimeValue(DataType.UWORD, 0).integerValue())
assertEquals(65535, RuntimeValue(DataType.UWORD, 65535).integerValue())
assertFailsWith<IllegalArgumentException> { RuntimeValue(DataType.UWORD, -1)}
assertFailsWith<IllegalArgumentException> { RuntimeValue(DataType.UWORD, 65536)}
assertEquals(0, RuntimeValueNumeric(DataType.UWORD, 0).integerValue())
assertEquals(65535, RuntimeValueNumeric(DataType.UWORD, 65535).integerValue())
assertFailsWith<IllegalArgumentException> { RuntimeValueNumeric(DataType.UWORD, -1)}
assertFailsWith<IllegalArgumentException> { RuntimeValueNumeric(DataType.UWORD, 65536)}
assertEquals(0, RuntimeValue(DataType.WORD, 0).integerValue())
assertEquals(-32768, RuntimeValue(DataType.WORD, -32768).integerValue())
assertEquals(32767, RuntimeValue(DataType.WORD, 32767).integerValue())
assertFailsWith<IllegalArgumentException> { RuntimeValue(DataType.WORD, -32769)}
assertFailsWith<IllegalArgumentException> { RuntimeValue(DataType.WORD, 32768)}
assertEquals(0, RuntimeValueNumeric(DataType.WORD, 0).integerValue())
assertEquals(-32768, RuntimeValueNumeric(DataType.WORD, -32768).integerValue())
assertEquals(32767, RuntimeValueNumeric(DataType.WORD, 32767).integerValue())
assertFailsWith<IllegalArgumentException> { RuntimeValueNumeric(DataType.WORD, -32769)}
assertFailsWith<IllegalArgumentException> { RuntimeValueNumeric(DataType.WORD, 32768)}
}
@Test
fun testTruthiness()
{
assertFalse(RuntimeValue(DataType.BYTE, 0).asBoolean)
assertFalse(RuntimeValue(DataType.UBYTE, 0).asBoolean)
assertFalse(RuntimeValue(DataType.WORD, 0).asBoolean)
assertFalse(RuntimeValue(DataType.UWORD, 0).asBoolean)
assertFalse(RuntimeValue(DataType.FLOAT, 0.0).asBoolean)
assertFalse(RuntimeValueNumeric(DataType.BYTE, 0).asBoolean)
assertFalse(RuntimeValueNumeric(DataType.UBYTE, 0).asBoolean)
assertFalse(RuntimeValueNumeric(DataType.WORD, 0).asBoolean)
assertFalse(RuntimeValueNumeric(DataType.UWORD, 0).asBoolean)
assertFalse(RuntimeValueNumeric(DataType.FLOAT, 0.0).asBoolean)
assertTrue(RuntimeValue(DataType.BYTE, 42).asBoolean)
assertTrue(RuntimeValue(DataType.UBYTE, 42).asBoolean)
assertTrue(RuntimeValue(DataType.WORD, 42).asBoolean)
assertTrue(RuntimeValue(DataType.UWORD, 42).asBoolean)
assertTrue(RuntimeValue(DataType.FLOAT, 42.0).asBoolean)
assertTrue(RuntimeValue(DataType.BYTE, -42).asBoolean)
assertTrue(RuntimeValue(DataType.WORD, -42).asBoolean)
assertTrue(RuntimeValue(DataType.FLOAT, -42.0).asBoolean)
assertTrue(RuntimeValueNumeric(DataType.BYTE, 42).asBoolean)
assertTrue(RuntimeValueNumeric(DataType.UBYTE, 42).asBoolean)
assertTrue(RuntimeValueNumeric(DataType.WORD, 42).asBoolean)
assertTrue(RuntimeValueNumeric(DataType.UWORD, 42).asBoolean)
assertTrue(RuntimeValueNumeric(DataType.FLOAT, 42.0).asBoolean)
assertTrue(RuntimeValueNumeric(DataType.BYTE, -42).asBoolean)
assertTrue(RuntimeValueNumeric(DataType.WORD, -42).asBoolean)
assertTrue(RuntimeValueNumeric(DataType.FLOAT, -42.0).asBoolean)
}
@Test
fun testIdentity() {
val v = RuntimeValue(DataType.UWORD, 12345)
val v = RuntimeValueNumeric(DataType.UWORD, 12345)
assertEquals(v, v)
assertFalse(v != v)
assertTrue(v<=v)
@@ -70,300 +70,283 @@ class TestRuntimeValue {
assertFalse(v<v)
assertFalse(v>v)
assertTrue(sameValueAndType(RuntimeValue(DataType.UBYTE, 100), RuntimeValue(DataType.UBYTE, 100)))
assertTrue(sameValueAndType(RuntimeValueNumeric(DataType.UBYTE, 100), RuntimeValueNumeric(DataType.UBYTE, 100)))
}
@Test
fun testEqualsAndNotEquals() {
assertEquals(RuntimeValue(DataType.UBYTE, 100), RuntimeValue(DataType.UBYTE, 100))
assertEquals(RuntimeValue(DataType.UBYTE, 100), RuntimeValue(DataType.UWORD, 100))
assertEquals(RuntimeValue(DataType.UBYTE, 100), RuntimeValue(DataType.FLOAT, 100))
assertEquals(RuntimeValue(DataType.UWORD, 254), RuntimeValue(DataType.UBYTE, 254))
assertEquals(RuntimeValue(DataType.UWORD, 12345), RuntimeValue(DataType.UWORD, 12345))
assertEquals(RuntimeValue(DataType.UWORD, 12345), RuntimeValue(DataType.FLOAT, 12345))
assertEquals(RuntimeValue(DataType.FLOAT, 100.0), RuntimeValue(DataType.UBYTE, 100))
assertEquals(RuntimeValue(DataType.FLOAT, 22239.0), RuntimeValue(DataType.UWORD, 22239))
assertEquals(RuntimeValue(DataType.FLOAT, 9.99), RuntimeValue(DataType.FLOAT, 9.99))
assertEquals(RuntimeValueNumeric(DataType.UBYTE, 100), RuntimeValueNumeric(DataType.UBYTE, 100))
assertEquals(RuntimeValueNumeric(DataType.UBYTE, 100), RuntimeValueNumeric(DataType.UWORD, 100))
assertEquals(RuntimeValueNumeric(DataType.UBYTE, 100), RuntimeValueNumeric(DataType.FLOAT, 100))
assertEquals(RuntimeValueNumeric(DataType.UWORD, 254), RuntimeValueNumeric(DataType.UBYTE, 254))
assertEquals(RuntimeValueNumeric(DataType.UWORD, 12345), RuntimeValueNumeric(DataType.UWORD, 12345))
assertEquals(RuntimeValueNumeric(DataType.UWORD, 12345), RuntimeValueNumeric(DataType.FLOAT, 12345))
assertEquals(RuntimeValueNumeric(DataType.FLOAT, 100.0), RuntimeValueNumeric(DataType.UBYTE, 100))
assertEquals(RuntimeValueNumeric(DataType.FLOAT, 22239.0), RuntimeValueNumeric(DataType.UWORD, 22239))
assertEquals(RuntimeValueNumeric(DataType.FLOAT, 9.99), RuntimeValueNumeric(DataType.FLOAT, 9.99))
assertTrue(sameValueAndType(RuntimeValue(DataType.UBYTE, 100), RuntimeValue(DataType.UBYTE, 100)))
assertFalse(sameValueAndType(RuntimeValue(DataType.UBYTE, 100), RuntimeValue(DataType.UWORD, 100)))
assertFalse(sameValueAndType(RuntimeValue(DataType.UBYTE, 100), RuntimeValue(DataType.FLOAT, 100)))
assertFalse(sameValueAndType(RuntimeValue(DataType.UWORD, 254), RuntimeValue(DataType.UBYTE, 254)))
assertTrue(sameValueAndType(RuntimeValue(DataType.UWORD, 12345), RuntimeValue(DataType.UWORD, 12345)))
assertFalse(sameValueAndType(RuntimeValue(DataType.UWORD, 12345), RuntimeValue(DataType.FLOAT, 12345)))
assertFalse(sameValueAndType(RuntimeValue(DataType.FLOAT, 100.0), RuntimeValue(DataType.UBYTE, 100)))
assertFalse(sameValueAndType(RuntimeValue(DataType.FLOAT, 22239.0), RuntimeValue(DataType.UWORD, 22239)))
assertTrue(sameValueAndType(RuntimeValue(DataType.FLOAT, 9.99), RuntimeValue(DataType.FLOAT, 9.99)))
assertTrue(sameValueAndType(RuntimeValueNumeric(DataType.UBYTE, 100), RuntimeValueNumeric(DataType.UBYTE, 100)))
assertFalse(sameValueAndType(RuntimeValueNumeric(DataType.UBYTE, 100), RuntimeValueNumeric(DataType.UWORD, 100)))
assertFalse(sameValueAndType(RuntimeValueNumeric(DataType.UBYTE, 100), RuntimeValueNumeric(DataType.FLOAT, 100)))
assertFalse(sameValueAndType(RuntimeValueNumeric(DataType.UWORD, 254), RuntimeValueNumeric(DataType.UBYTE, 254)))
assertTrue(sameValueAndType(RuntimeValueNumeric(DataType.UWORD, 12345), RuntimeValueNumeric(DataType.UWORD, 12345)))
assertFalse(sameValueAndType(RuntimeValueNumeric(DataType.UWORD, 12345), RuntimeValueNumeric(DataType.FLOAT, 12345)))
assertFalse(sameValueAndType(RuntimeValueNumeric(DataType.FLOAT, 100.0), RuntimeValueNumeric(DataType.UBYTE, 100)))
assertFalse(sameValueAndType(RuntimeValueNumeric(DataType.FLOAT, 22239.0), RuntimeValueNumeric(DataType.UWORD, 22239)))
assertTrue(sameValueAndType(RuntimeValueNumeric(DataType.FLOAT, 9.99), RuntimeValueNumeric(DataType.FLOAT, 9.99)))
assertNotEquals(RuntimeValue(DataType.UBYTE, 100), RuntimeValue(DataType.UBYTE, 101))
assertNotEquals(RuntimeValue(DataType.UBYTE, 100), RuntimeValue(DataType.UWORD, 101))
assertNotEquals(RuntimeValue(DataType.UBYTE, 100), RuntimeValue(DataType.FLOAT, 101))
assertNotEquals(RuntimeValue(DataType.UWORD, 245), RuntimeValue(DataType.UBYTE, 246))
assertNotEquals(RuntimeValue(DataType.UWORD, 12345), RuntimeValue(DataType.UWORD, 12346))
assertNotEquals(RuntimeValue(DataType.UWORD, 12345), RuntimeValue(DataType.FLOAT, 12346))
assertNotEquals(RuntimeValue(DataType.FLOAT, 9.99), RuntimeValue(DataType.UBYTE, 9))
assertNotEquals(RuntimeValue(DataType.FLOAT, 9.99), RuntimeValue(DataType.UWORD, 9))
assertNotEquals(RuntimeValue(DataType.FLOAT, 9.99), RuntimeValue(DataType.FLOAT, 9.0))
assertNotEquals(RuntimeValueNumeric(DataType.UBYTE, 100), RuntimeValueNumeric(DataType.UBYTE, 101))
assertNotEquals(RuntimeValueNumeric(DataType.UBYTE, 100), RuntimeValueNumeric(DataType.UWORD, 101))
assertNotEquals(RuntimeValueNumeric(DataType.UBYTE, 100), RuntimeValueNumeric(DataType.FLOAT, 101))
assertNotEquals(RuntimeValueNumeric(DataType.UWORD, 245), RuntimeValueNumeric(DataType.UBYTE, 246))
assertNotEquals(RuntimeValueNumeric(DataType.UWORD, 12345), RuntimeValueNumeric(DataType.UWORD, 12346))
assertNotEquals(RuntimeValueNumeric(DataType.UWORD, 12345), RuntimeValueNumeric(DataType.FLOAT, 12346))
assertNotEquals(RuntimeValueNumeric(DataType.FLOAT, 9.99), RuntimeValueNumeric(DataType.UBYTE, 9))
assertNotEquals(RuntimeValueNumeric(DataType.FLOAT, 9.99), RuntimeValueNumeric(DataType.UWORD, 9))
assertNotEquals(RuntimeValueNumeric(DataType.FLOAT, 9.99), RuntimeValueNumeric(DataType.FLOAT, 9.0))
assertFalse(sameValueAndType(RuntimeValue(DataType.UBYTE, 100), RuntimeValue(DataType.UBYTE, 101)))
assertFalse(sameValueAndType(RuntimeValue(DataType.UBYTE, 100), RuntimeValue(DataType.UWORD, 101)))
assertFalse(sameValueAndType(RuntimeValue(DataType.UBYTE, 100), RuntimeValue(DataType.FLOAT, 101)))
assertFalse(sameValueAndType(RuntimeValue(DataType.UWORD, 245), RuntimeValue(DataType.UBYTE, 246)))
assertFalse(sameValueAndType(RuntimeValue(DataType.UWORD, 12345), RuntimeValue(DataType.UWORD, 12346)))
assertFalse(sameValueAndType(RuntimeValue(DataType.UWORD, 12345), RuntimeValue(DataType.FLOAT, 12346)))
assertFalse(sameValueAndType(RuntimeValue(DataType.FLOAT, 9.99), RuntimeValue(DataType.UBYTE, 9)))
assertFalse(sameValueAndType(RuntimeValue(DataType.FLOAT, 9.99), RuntimeValue(DataType.UWORD, 9)))
assertFalse(sameValueAndType(RuntimeValue(DataType.FLOAT, 9.99), RuntimeValue(DataType.FLOAT, 9.0)))
}
@Test
fun testEqualityHeapTypes()
{
assertTrue(sameValueAndType(RuntimeValue(DataType.STR, heapId = 999), RuntimeValue(DataType.STR, heapId = 999)))
assertFalse(sameValueAndType(RuntimeValue(DataType.STR, heapId = 999), RuntimeValue(DataType.STR, heapId = 222)))
assertTrue(sameValueAndType(RuntimeValue(DataType.ARRAY_UB, heapId = 99), RuntimeValue(DataType.ARRAY_UB, heapId = 99)))
assertFalse(sameValueAndType(RuntimeValue(DataType.ARRAY_UB, heapId = 99), RuntimeValue(DataType.ARRAY_UB, heapId = 22)))
assertTrue(sameValueAndType(RuntimeValue(DataType.ARRAY_UW, heapId = 999), RuntimeValue(DataType.ARRAY_UW, heapId = 999)))
assertFalse(sameValueAndType(RuntimeValue(DataType.ARRAY_UW, heapId = 999), RuntimeValue(DataType.ARRAY_UW, heapId = 222)))
assertTrue(sameValueAndType(RuntimeValue(DataType.ARRAY_F, heapId = 999), RuntimeValue(DataType.ARRAY_F, heapId = 999)))
assertFalse(sameValueAndType(RuntimeValue(DataType.ARRAY_F, heapId = 999), RuntimeValue(DataType.ARRAY_UW, heapId = 999)))
assertFalse(sameValueAndType(RuntimeValue(DataType.ARRAY_F, heapId = 999), RuntimeValue(DataType.ARRAY_F, heapId = 222)))
assertFalse(sameValueAndType(RuntimeValueNumeric(DataType.UBYTE, 100), RuntimeValueNumeric(DataType.UBYTE, 101)))
assertFalse(sameValueAndType(RuntimeValueNumeric(DataType.UBYTE, 100), RuntimeValueNumeric(DataType.UWORD, 101)))
assertFalse(sameValueAndType(RuntimeValueNumeric(DataType.UBYTE, 100), RuntimeValueNumeric(DataType.FLOAT, 101)))
assertFalse(sameValueAndType(RuntimeValueNumeric(DataType.UWORD, 245), RuntimeValueNumeric(DataType.UBYTE, 246)))
assertFalse(sameValueAndType(RuntimeValueNumeric(DataType.UWORD, 12345), RuntimeValueNumeric(DataType.UWORD, 12346)))
assertFalse(sameValueAndType(RuntimeValueNumeric(DataType.UWORD, 12345), RuntimeValueNumeric(DataType.FLOAT, 12346)))
assertFalse(sameValueAndType(RuntimeValueNumeric(DataType.FLOAT, 9.99), RuntimeValueNumeric(DataType.UBYTE, 9)))
assertFalse(sameValueAndType(RuntimeValueNumeric(DataType.FLOAT, 9.99), RuntimeValueNumeric(DataType.UWORD, 9)))
assertFalse(sameValueAndType(RuntimeValueNumeric(DataType.FLOAT, 9.99), RuntimeValueNumeric(DataType.FLOAT, 9.0)))
}
@Test
fun testGreaterThan(){
assertTrue(RuntimeValue(DataType.UBYTE, 100) > RuntimeValue(DataType.UBYTE, 99))
assertTrue(RuntimeValue(DataType.UWORD, 254) > RuntimeValue(DataType.UWORD, 253))
assertTrue(RuntimeValue(DataType.FLOAT, 100.0) > RuntimeValue(DataType.FLOAT, 99.9))
assertTrue(RuntimeValueNumeric(DataType.UBYTE, 100) > RuntimeValueNumeric(DataType.UBYTE, 99))
assertTrue(RuntimeValueNumeric(DataType.UWORD, 254) > RuntimeValueNumeric(DataType.UWORD, 253))
assertTrue(RuntimeValueNumeric(DataType.FLOAT, 100.0) > RuntimeValueNumeric(DataType.FLOAT, 99.9))
assertTrue(RuntimeValue(DataType.UBYTE, 100) >= RuntimeValue(DataType.UBYTE, 100))
assertTrue(RuntimeValue(DataType.UWORD, 254) >= RuntimeValue(DataType.UWORD, 254))
assertTrue(RuntimeValue(DataType.FLOAT, 100.0) >= RuntimeValue(DataType.FLOAT, 100.0))
assertTrue(RuntimeValueNumeric(DataType.UBYTE, 100) >= RuntimeValueNumeric(DataType.UBYTE, 100))
assertTrue(RuntimeValueNumeric(DataType.UWORD, 254) >= RuntimeValueNumeric(DataType.UWORD, 254))
assertTrue(RuntimeValueNumeric(DataType.FLOAT, 100.0) >= RuntimeValueNumeric(DataType.FLOAT, 100.0))
assertFalse(RuntimeValue(DataType.UBYTE, 100) > RuntimeValue(DataType.UBYTE, 100))
assertFalse(RuntimeValue(DataType.UWORD, 254) > RuntimeValue(DataType.UWORD, 254))
assertFalse(RuntimeValue(DataType.FLOAT, 100.0) > RuntimeValue(DataType.FLOAT, 100.0))
assertFalse(RuntimeValueNumeric(DataType.UBYTE, 100) > RuntimeValueNumeric(DataType.UBYTE, 100))
assertFalse(RuntimeValueNumeric(DataType.UWORD, 254) > RuntimeValueNumeric(DataType.UWORD, 254))
assertFalse(RuntimeValueNumeric(DataType.FLOAT, 100.0) > RuntimeValueNumeric(DataType.FLOAT, 100.0))
assertFalse(RuntimeValue(DataType.UBYTE, 100) >= RuntimeValue(DataType.UBYTE, 101))
assertFalse(RuntimeValue(DataType.UWORD, 254) >= RuntimeValue(DataType.UWORD, 255))
assertFalse(RuntimeValue(DataType.FLOAT, 100.0) >= RuntimeValue(DataType.FLOAT, 100.1))
assertFalse(RuntimeValueNumeric(DataType.UBYTE, 100) >= RuntimeValueNumeric(DataType.UBYTE, 101))
assertFalse(RuntimeValueNumeric(DataType.UWORD, 254) >= RuntimeValueNumeric(DataType.UWORD, 255))
assertFalse(RuntimeValueNumeric(DataType.FLOAT, 100.0) >= RuntimeValueNumeric(DataType.FLOAT, 100.1))
}
@Test
fun testLessThan() {
assertTrue(RuntimeValue(DataType.UBYTE, 100) < RuntimeValue(DataType.UBYTE, 101))
assertTrue(RuntimeValue(DataType.UWORD, 254) < RuntimeValue(DataType.UWORD, 255))
assertTrue(RuntimeValue(DataType.FLOAT, 100.0) < RuntimeValue(DataType.FLOAT, 100.1))
assertTrue(RuntimeValueNumeric(DataType.UBYTE, 100) < RuntimeValueNumeric(DataType.UBYTE, 101))
assertTrue(RuntimeValueNumeric(DataType.UWORD, 254) < RuntimeValueNumeric(DataType.UWORD, 255))
assertTrue(RuntimeValueNumeric(DataType.FLOAT, 100.0) < RuntimeValueNumeric(DataType.FLOAT, 100.1))
assertTrue(RuntimeValue(DataType.UBYTE, 100) <= RuntimeValue(DataType.UBYTE, 100))
assertTrue(RuntimeValue(DataType.UWORD, 254) <= RuntimeValue(DataType.UWORD, 254))
assertTrue(RuntimeValue(DataType.FLOAT, 100.0) <= RuntimeValue(DataType.FLOAT, 100.0))
assertTrue(RuntimeValueNumeric(DataType.UBYTE, 100) <= RuntimeValueNumeric(DataType.UBYTE, 100))
assertTrue(RuntimeValueNumeric(DataType.UWORD, 254) <= RuntimeValueNumeric(DataType.UWORD, 254))
assertTrue(RuntimeValueNumeric(DataType.FLOAT, 100.0) <= RuntimeValueNumeric(DataType.FLOAT, 100.0))
assertFalse(RuntimeValue(DataType.UBYTE, 100) < RuntimeValue(DataType.UBYTE, 100))
assertFalse(RuntimeValue(DataType.UWORD, 254) < RuntimeValue(DataType.UWORD, 254))
assertFalse(RuntimeValue(DataType.FLOAT, 100.0) < RuntimeValue(DataType.FLOAT, 100.0))
assertFalse(RuntimeValueNumeric(DataType.UBYTE, 100) < RuntimeValueNumeric(DataType.UBYTE, 100))
assertFalse(RuntimeValueNumeric(DataType.UWORD, 254) < RuntimeValueNumeric(DataType.UWORD, 254))
assertFalse(RuntimeValueNumeric(DataType.FLOAT, 100.0) < RuntimeValueNumeric(DataType.FLOAT, 100.0))
assertFalse(RuntimeValue(DataType.UBYTE, 100) <= RuntimeValue(DataType.UBYTE, 99))
assertFalse(RuntimeValue(DataType.UWORD, 254) <= RuntimeValue(DataType.UWORD, 253))
assertFalse(RuntimeValue(DataType.FLOAT, 100.0) <= RuntimeValue(DataType.FLOAT, 99.9))
assertFalse(RuntimeValueNumeric(DataType.UBYTE, 100) <= RuntimeValueNumeric(DataType.UBYTE, 99))
assertFalse(RuntimeValueNumeric(DataType.UWORD, 254) <= RuntimeValueNumeric(DataType.UWORD, 253))
assertFalse(RuntimeValueNumeric(DataType.FLOAT, 100.0) <= RuntimeValueNumeric(DataType.FLOAT, 99.9))
}
@Test
fun testNoDtConversion() {
assertFailsWith<ArithmeticException> {
RuntimeValue(DataType.UWORD, 100).add(RuntimeValue(DataType.UBYTE, 120))
RuntimeValueNumeric(DataType.UWORD, 100).add(RuntimeValueNumeric(DataType.UBYTE, 120))
}
assertFailsWith<ArithmeticException> {
RuntimeValue(DataType.UBYTE, 100).add(RuntimeValue(DataType.UWORD, 120))
RuntimeValueNumeric(DataType.UBYTE, 100).add(RuntimeValueNumeric(DataType.UWORD, 120))
}
assertFailsWith<ArithmeticException> {
RuntimeValue(DataType.FLOAT, 100.22).add(RuntimeValue(DataType.UWORD, 120))
RuntimeValueNumeric(DataType.FLOAT, 100.22).add(RuntimeValueNumeric(DataType.UWORD, 120))
}
assertFailsWith<ArithmeticException> {
RuntimeValue(DataType.UWORD, 1002).add(RuntimeValue(DataType.FLOAT, 120.22))
RuntimeValueNumeric(DataType.UWORD, 1002).add(RuntimeValueNumeric(DataType.FLOAT, 120.22))
}
assertFailsWith<ArithmeticException> {
RuntimeValue(DataType.FLOAT, 100.22).add(RuntimeValue(DataType.UBYTE, 120))
RuntimeValueNumeric(DataType.FLOAT, 100.22).add(RuntimeValueNumeric(DataType.UBYTE, 120))
}
assertFailsWith<ArithmeticException> {
RuntimeValue(DataType.UBYTE, 12).add(RuntimeValue(DataType.FLOAT, 120.22))
RuntimeValueNumeric(DataType.UBYTE, 12).add(RuntimeValueNumeric(DataType.FLOAT, 120.22))
}
}
@Test
fun testNoAutoFloatConversion() {
assertFailsWith<ArithmeticException> {
RuntimeValue(DataType.UBYTE, 233).add(RuntimeValue(DataType.FLOAT, 1.234))
RuntimeValueNumeric(DataType.UBYTE, 233).add(RuntimeValueNumeric(DataType.FLOAT, 1.234))
}
assertFailsWith<ArithmeticException> {
RuntimeValue(DataType.UWORD, 233).add(RuntimeValue(DataType.FLOAT, 1.234))
RuntimeValueNumeric(DataType.UWORD, 233).add(RuntimeValueNumeric(DataType.FLOAT, 1.234))
}
assertFailsWith<ArithmeticException> {
RuntimeValue(DataType.UBYTE, 233).mul(RuntimeValue(DataType.FLOAT, 1.234))
RuntimeValueNumeric(DataType.UBYTE, 233).mul(RuntimeValueNumeric(DataType.FLOAT, 1.234))
}
assertFailsWith<ArithmeticException> {
RuntimeValue(DataType.UWORD, 233).mul(RuntimeValue(DataType.FLOAT, 1.234))
RuntimeValueNumeric(DataType.UWORD, 233).mul(RuntimeValueNumeric(DataType.FLOAT, 1.234))
}
assertFailsWith<ArithmeticException> {
RuntimeValue(DataType.UBYTE, 233).div(RuntimeValue(DataType.FLOAT, 1.234))
RuntimeValueNumeric(DataType.UBYTE, 233).div(RuntimeValueNumeric(DataType.FLOAT, 1.234))
}
assertFailsWith<ArithmeticException> {
RuntimeValue(DataType.UWORD, 233).div(RuntimeValue(DataType.FLOAT, 1.234))
RuntimeValueNumeric(DataType.UWORD, 233).div(RuntimeValueNumeric(DataType.FLOAT, 1.234))
}
val result = RuntimeValue(DataType.FLOAT, 233.333).add(RuntimeValue(DataType.FLOAT, 1.234))
val result = RuntimeValueNumeric(DataType.FLOAT, 233.333).add(RuntimeValueNumeric(DataType.FLOAT, 1.234))
}
@Test
fun arithmetictestUbyte() {
assertEquals(255, RuntimeValue(DataType.UBYTE, 200).add(RuntimeValue(DataType.UBYTE, 55)).integerValue())
assertEquals(0, RuntimeValue(DataType.UBYTE, 200).add(RuntimeValue(DataType.UBYTE, 56)).integerValue())
assertEquals(1, RuntimeValue(DataType.UBYTE, 200).add(RuntimeValue(DataType.UBYTE, 57)).integerValue())
assertEquals(255, RuntimeValueNumeric(DataType.UBYTE, 200).add(RuntimeValueNumeric(DataType.UBYTE, 55)).integerValue())
assertEquals(0, RuntimeValueNumeric(DataType.UBYTE, 200).add(RuntimeValueNumeric(DataType.UBYTE, 56)).integerValue())
assertEquals(1, RuntimeValueNumeric(DataType.UBYTE, 200).add(RuntimeValueNumeric(DataType.UBYTE, 57)).integerValue())
assertEquals(1, RuntimeValue(DataType.UBYTE, 2).sub(RuntimeValue(DataType.UBYTE, 1)).integerValue())
assertEquals(0, RuntimeValue(DataType.UBYTE, 2).sub(RuntimeValue(DataType.UBYTE, 2)).integerValue())
assertEquals(255, RuntimeValue(DataType.UBYTE, 2).sub(RuntimeValue(DataType.UBYTE, 3)).integerValue())
assertEquals(1, RuntimeValueNumeric(DataType.UBYTE, 2).sub(RuntimeValueNumeric(DataType.UBYTE, 1)).integerValue())
assertEquals(0, RuntimeValueNumeric(DataType.UBYTE, 2).sub(RuntimeValueNumeric(DataType.UBYTE, 2)).integerValue())
assertEquals(255, RuntimeValueNumeric(DataType.UBYTE, 2).sub(RuntimeValueNumeric(DataType.UBYTE, 3)).integerValue())
assertEquals(255, RuntimeValue(DataType.UBYTE, 254).inc().integerValue())
assertEquals(0, RuntimeValue(DataType.UBYTE, 255).inc().integerValue())
assertEquals(0, RuntimeValue(DataType.UBYTE, 1).dec().integerValue())
assertEquals(255, RuntimeValue(DataType.UBYTE, 0).dec().integerValue())
assertEquals(255, RuntimeValueNumeric(DataType.UBYTE, 254).inc().integerValue())
assertEquals(0, RuntimeValueNumeric(DataType.UBYTE, 255).inc().integerValue())
assertEquals(0, RuntimeValueNumeric(DataType.UBYTE, 1).dec().integerValue())
assertEquals(255, RuntimeValueNumeric(DataType.UBYTE, 0).dec().integerValue())
assertEquals(255, RuntimeValue(DataType.UBYTE, 0).inv().integerValue())
assertEquals(0b00110011, RuntimeValue(DataType.UBYTE, 0b11001100).inv().integerValue())
// assertEquals(0, RuntimeValue(DataType.UBYTE, 0).neg().integerValue())
// assertEquals(0, RuntimeValue(DataType.UBYTE, 0).neg().integerValue())
assertEquals(1, RuntimeValue(DataType.UBYTE, 0).not().integerValue())
assertEquals(0, RuntimeValue(DataType.UBYTE, 1).not().integerValue())
assertEquals(0, RuntimeValue(DataType.UBYTE, 111).not().integerValue())
assertEquals(0, RuntimeValue(DataType.UBYTE, 255).not().integerValue())
assertEquals(255, RuntimeValueNumeric(DataType.UBYTE, 0).inv().integerValue())
assertEquals(0b00110011, RuntimeValueNumeric(DataType.UBYTE, 0b11001100).inv().integerValue())
// assertEquals(0, RuntimeValueNumeric(DataType.UBYTE, 0).neg().integerValue())
// assertEquals(0, RuntimeValueNumeric(DataType.UBYTE, 0).neg().integerValue())
assertEquals(1, RuntimeValueNumeric(DataType.UBYTE, 0).not().integerValue())
assertEquals(0, RuntimeValueNumeric(DataType.UBYTE, 1).not().integerValue())
assertEquals(0, RuntimeValueNumeric(DataType.UBYTE, 111).not().integerValue())
assertEquals(0, RuntimeValueNumeric(DataType.UBYTE, 255).not().integerValue())
assertEquals(200, RuntimeValue(DataType.UBYTE, 20).mul(RuntimeValue(DataType.UBYTE, 10)).integerValue())
assertEquals(144, RuntimeValue(DataType.UBYTE, 20).mul(RuntimeValue(DataType.UBYTE, 20)).integerValue())
assertEquals(200, RuntimeValueNumeric(DataType.UBYTE, 20).mul(RuntimeValueNumeric(DataType.UBYTE, 10)).integerValue())
assertEquals(144, RuntimeValueNumeric(DataType.UBYTE, 20).mul(RuntimeValueNumeric(DataType.UBYTE, 20)).integerValue())
assertEquals(25, RuntimeValue(DataType.UBYTE, 5).pow(RuntimeValue(DataType.UBYTE, 2)).integerValue())
assertEquals(125, RuntimeValue(DataType.UBYTE, 5).pow(RuntimeValue(DataType.UBYTE, 3)).integerValue())
assertEquals(113, RuntimeValue(DataType.UBYTE, 5).pow(RuntimeValue(DataType.UBYTE, 4)).integerValue())
assertEquals(25, RuntimeValueNumeric(DataType.UBYTE, 5).pow(RuntimeValueNumeric(DataType.UBYTE, 2)).integerValue())
assertEquals(125, RuntimeValueNumeric(DataType.UBYTE, 5).pow(RuntimeValueNumeric(DataType.UBYTE, 3)).integerValue())
assertEquals(113, RuntimeValueNumeric(DataType.UBYTE, 5).pow(RuntimeValueNumeric(DataType.UBYTE, 4)).integerValue())
assertEquals(100, RuntimeValue(DataType.UBYTE, 50).shl().integerValue())
assertEquals(200, RuntimeValue(DataType.UBYTE, 100).shl().integerValue())
assertEquals(144, RuntimeValue(DataType.UBYTE, 200).shl().integerValue())
assertEquals(100, RuntimeValueNumeric(DataType.UBYTE, 50).shl().integerValue())
assertEquals(200, RuntimeValueNumeric(DataType.UBYTE, 100).shl().integerValue())
assertEquals(144, RuntimeValueNumeric(DataType.UBYTE, 200).shl().integerValue())
}
@Test
fun arithmetictestUWord() {
assertEquals(65535, RuntimeValue(DataType.UWORD, 60000).add(RuntimeValue(DataType.UWORD, 5535)).integerValue())
assertEquals(0, RuntimeValue(DataType.UWORD, 60000).add(RuntimeValue(DataType.UWORD, 5536)).integerValue())
assertEquals(1, RuntimeValue(DataType.UWORD, 60000).add(RuntimeValue(DataType.UWORD, 5537)).integerValue())
assertEquals(65535, RuntimeValueNumeric(DataType.UWORD, 60000).add(RuntimeValueNumeric(DataType.UWORD, 5535)).integerValue())
assertEquals(0, RuntimeValueNumeric(DataType.UWORD, 60000).add(RuntimeValueNumeric(DataType.UWORD, 5536)).integerValue())
assertEquals(1, RuntimeValueNumeric(DataType.UWORD, 60000).add(RuntimeValueNumeric(DataType.UWORD, 5537)).integerValue())
assertEquals(1, RuntimeValue(DataType.UWORD, 2).sub(RuntimeValue(DataType.UWORD, 1)).integerValue())
assertEquals(0, RuntimeValue(DataType.UWORD, 2).sub(RuntimeValue(DataType.UWORD, 2)).integerValue())
assertEquals(65535, RuntimeValue(DataType.UWORD, 2).sub(RuntimeValue(DataType.UWORD, 3)).integerValue())
assertEquals(1, RuntimeValueNumeric(DataType.UWORD, 2).sub(RuntimeValueNumeric(DataType.UWORD, 1)).integerValue())
assertEquals(0, RuntimeValueNumeric(DataType.UWORD, 2).sub(RuntimeValueNumeric(DataType.UWORD, 2)).integerValue())
assertEquals(65535, RuntimeValueNumeric(DataType.UWORD, 2).sub(RuntimeValueNumeric(DataType.UWORD, 3)).integerValue())
assertEquals(65535, RuntimeValue(DataType.UWORD, 65534).inc().integerValue())
assertEquals(0, RuntimeValue(DataType.UWORD, 65535).inc().integerValue())
assertEquals(0, RuntimeValue(DataType.UWORD, 1).dec().integerValue())
assertEquals(65535, RuntimeValue(DataType.UWORD, 0).dec().integerValue())
assertEquals(65535, RuntimeValueNumeric(DataType.UWORD, 65534).inc().integerValue())
assertEquals(0, RuntimeValueNumeric(DataType.UWORD, 65535).inc().integerValue())
assertEquals(0, RuntimeValueNumeric(DataType.UWORD, 1).dec().integerValue())
assertEquals(65535, RuntimeValueNumeric(DataType.UWORD, 0).dec().integerValue())
assertEquals(65535, RuntimeValue(DataType.UWORD, 0).inv().integerValue())
assertEquals(0b0011001101010101, RuntimeValue(DataType.UWORD, 0b1100110010101010).inv().integerValue())
// assertEquals(0, RuntimeValue(DataType.UWORD, 0).neg().integerValue())
// assertEquals(0, RuntimeValue(DataType.UWORD, 0).neg().integerValue())
assertEquals(1, RuntimeValue(DataType.UWORD, 0).not().integerValue())
assertEquals(0, RuntimeValue(DataType.UWORD, 1).not().integerValue())
assertEquals(0, RuntimeValue(DataType.UWORD, 11111).not().integerValue())
assertEquals(0, RuntimeValue(DataType.UWORD, 65535).not().integerValue())
assertEquals(65535, RuntimeValueNumeric(DataType.UWORD, 0).inv().integerValue())
assertEquals(0b0011001101010101, RuntimeValueNumeric(DataType.UWORD, 0b1100110010101010).inv().integerValue())
// assertEquals(0, RuntimeValueNumeric(DataType.UWORD, 0).neg().integerValue())
// assertEquals(0, RuntimeValueNumeric(DataType.UWORD, 0).neg().integerValue())
assertEquals(1, RuntimeValueNumeric(DataType.UWORD, 0).not().integerValue())
assertEquals(0, RuntimeValueNumeric(DataType.UWORD, 1).not().integerValue())
assertEquals(0, RuntimeValueNumeric(DataType.UWORD, 11111).not().integerValue())
assertEquals(0, RuntimeValueNumeric(DataType.UWORD, 65535).not().integerValue())
assertEquals(2000, RuntimeValue(DataType.UWORD, 200).mul(RuntimeValue(DataType.UWORD, 10)).integerValue())
assertEquals(40000, RuntimeValue(DataType.UWORD, 200).mul(RuntimeValue(DataType.UWORD, 200)).integerValue())
assertEquals(14464, RuntimeValue(DataType.UWORD, 200).mul(RuntimeValue(DataType.UWORD, 400)).integerValue())
assertEquals(2000, RuntimeValueNumeric(DataType.UWORD, 200).mul(RuntimeValueNumeric(DataType.UWORD, 10)).integerValue())
assertEquals(40000, RuntimeValueNumeric(DataType.UWORD, 200).mul(RuntimeValueNumeric(DataType.UWORD, 200)).integerValue())
assertEquals(14464, RuntimeValueNumeric(DataType.UWORD, 200).mul(RuntimeValueNumeric(DataType.UWORD, 400)).integerValue())
assertEquals(15625, RuntimeValue(DataType.UWORD, 5).pow(RuntimeValue(DataType.UWORD, 6)).integerValue())
assertEquals(12589, RuntimeValue(DataType.UWORD, 5).pow(RuntimeValue(DataType.UWORD, 7)).integerValue())
assertEquals(15625, RuntimeValueNumeric(DataType.UWORD, 5).pow(RuntimeValueNumeric(DataType.UWORD, 6)).integerValue())
assertEquals(12589, RuntimeValueNumeric(DataType.UWORD, 5).pow(RuntimeValueNumeric(DataType.UWORD, 7)).integerValue())
assertEquals(10000, RuntimeValue(DataType.UWORD, 5000).shl().integerValue())
assertEquals(60000, RuntimeValue(DataType.UWORD, 30000).shl().integerValue())
assertEquals(14464, RuntimeValue(DataType.UWORD, 40000).shl().integerValue())
assertEquals(10000, RuntimeValueNumeric(DataType.UWORD, 5000).shl().integerValue())
assertEquals(60000, RuntimeValueNumeric(DataType.UWORD, 30000).shl().integerValue())
assertEquals(14464, RuntimeValueNumeric(DataType.UWORD, 40000).shl().integerValue())
}
@Test
fun arithmetictestByte() {
assertEquals(127, RuntimeValue(DataType.BYTE, 100).add(RuntimeValue(DataType.BYTE, 27)).integerValue())
assertEquals(-128, RuntimeValue(DataType.BYTE, 100).add(RuntimeValue(DataType.BYTE, 28)).integerValue())
assertEquals(-127, RuntimeValue(DataType.BYTE, 100).add(RuntimeValue(DataType.BYTE, 29)).integerValue())
assertEquals(127, RuntimeValueNumeric(DataType.BYTE, 100).add(RuntimeValueNumeric(DataType.BYTE, 27)).integerValue())
assertEquals(-128, RuntimeValueNumeric(DataType.BYTE, 100).add(RuntimeValueNumeric(DataType.BYTE, 28)).integerValue())
assertEquals(-127, RuntimeValueNumeric(DataType.BYTE, 100).add(RuntimeValueNumeric(DataType.BYTE, 29)).integerValue())
assertEquals(1, RuntimeValue(DataType.BYTE, 2).sub(RuntimeValue(DataType.BYTE, 1)).integerValue())
assertEquals(0, RuntimeValue(DataType.BYTE, 2).sub(RuntimeValue(DataType.BYTE, 2)).integerValue())
assertEquals(-1, RuntimeValue(DataType.BYTE, 2).sub(RuntimeValue(DataType.BYTE, 3)).integerValue())
assertEquals(-128, RuntimeValue(DataType.BYTE, -100).sub(RuntimeValue(DataType.BYTE, 28)).integerValue())
assertEquals(127, RuntimeValue(DataType.BYTE, -100).sub(RuntimeValue(DataType.BYTE, 29)).integerValue())
assertEquals(1, RuntimeValueNumeric(DataType.BYTE, 2).sub(RuntimeValueNumeric(DataType.BYTE, 1)).integerValue())
assertEquals(0, RuntimeValueNumeric(DataType.BYTE, 2).sub(RuntimeValueNumeric(DataType.BYTE, 2)).integerValue())
assertEquals(-1, RuntimeValueNumeric(DataType.BYTE, 2).sub(RuntimeValueNumeric(DataType.BYTE, 3)).integerValue())
assertEquals(-128, RuntimeValueNumeric(DataType.BYTE, -100).sub(RuntimeValueNumeric(DataType.BYTE, 28)).integerValue())
assertEquals(127, RuntimeValueNumeric(DataType.BYTE, -100).sub(RuntimeValueNumeric(DataType.BYTE, 29)).integerValue())
assertEquals(127, RuntimeValue(DataType.BYTE, 126).inc().integerValue())
assertEquals(-128, RuntimeValue(DataType.BYTE, 127).inc().integerValue())
assertEquals(0, RuntimeValue(DataType.BYTE, 1).dec().integerValue())
assertEquals(-1, RuntimeValue(DataType.BYTE, 0).dec().integerValue())
assertEquals(-128, RuntimeValue(DataType.BYTE, -127).dec().integerValue())
assertEquals(127, RuntimeValue(DataType.BYTE, -128).dec().integerValue())
assertEquals(127, RuntimeValueNumeric(DataType.BYTE, 126).inc().integerValue())
assertEquals(-128, RuntimeValueNumeric(DataType.BYTE, 127).inc().integerValue())
assertEquals(0, RuntimeValueNumeric(DataType.BYTE, 1).dec().integerValue())
assertEquals(-1, RuntimeValueNumeric(DataType.BYTE, 0).dec().integerValue())
assertEquals(-128, RuntimeValueNumeric(DataType.BYTE, -127).dec().integerValue())
assertEquals(127, RuntimeValueNumeric(DataType.BYTE, -128).dec().integerValue())
assertEquals(-1, RuntimeValue(DataType.BYTE, 0).inv().integerValue())
assertEquals(-103, RuntimeValue(DataType.BYTE, 0b01100110).inv().integerValue())
assertEquals(0, RuntimeValue(DataType.BYTE, 0).neg().integerValue())
assertEquals(-2, RuntimeValue(DataType.BYTE, 2).neg().integerValue())
assertEquals(1, RuntimeValue(DataType.BYTE, 0).not().integerValue())
assertEquals(0, RuntimeValue(DataType.BYTE, 1).not().integerValue())
assertEquals(0, RuntimeValue(DataType.BYTE, 111).not().integerValue())
assertEquals(0, RuntimeValue(DataType.BYTE, -33).not().integerValue())
assertEquals(-1, RuntimeValueNumeric(DataType.BYTE, 0).inv().integerValue())
assertEquals(-103, RuntimeValueNumeric(DataType.BYTE, 0b01100110).inv().integerValue())
assertEquals(0, RuntimeValueNumeric(DataType.BYTE, 0).neg().integerValue())
assertEquals(-2, RuntimeValueNumeric(DataType.BYTE, 2).neg().integerValue())
assertEquals(1, RuntimeValueNumeric(DataType.BYTE, 0).not().integerValue())
assertEquals(0, RuntimeValueNumeric(DataType.BYTE, 1).not().integerValue())
assertEquals(0, RuntimeValueNumeric(DataType.BYTE, 111).not().integerValue())
assertEquals(0, RuntimeValueNumeric(DataType.BYTE, -33).not().integerValue())
assertEquals(100, RuntimeValue(DataType.BYTE, 10).mul(RuntimeValue(DataType.BYTE, 10)).integerValue())
assertEquals(-56, RuntimeValue(DataType.BYTE, 20).mul(RuntimeValue(DataType.BYTE, 10)).integerValue())
assertEquals(100, RuntimeValueNumeric(DataType.BYTE, 10).mul(RuntimeValueNumeric(DataType.BYTE, 10)).integerValue())
assertEquals(-56, RuntimeValueNumeric(DataType.BYTE, 20).mul(RuntimeValueNumeric(DataType.BYTE, 10)).integerValue())
assertEquals(25, RuntimeValue(DataType.BYTE, 5).pow(RuntimeValue(DataType.BYTE, 2)).integerValue())
assertEquals(125, RuntimeValue(DataType.BYTE, 5).pow(RuntimeValue(DataType.BYTE, 3)).integerValue())
assertEquals(113, RuntimeValue(DataType.BYTE, 5).pow(RuntimeValue(DataType.BYTE, 4)).integerValue())
assertEquals(25, RuntimeValueNumeric(DataType.BYTE, 5).pow(RuntimeValueNumeric(DataType.BYTE, 2)).integerValue())
assertEquals(125, RuntimeValueNumeric(DataType.BYTE, 5).pow(RuntimeValueNumeric(DataType.BYTE, 3)).integerValue())
assertEquals(113, RuntimeValueNumeric(DataType.BYTE, 5).pow(RuntimeValueNumeric(DataType.BYTE, 4)).integerValue())
assertEquals(100, RuntimeValue(DataType.BYTE, 50).shl().integerValue())
assertEquals(-56, RuntimeValue(DataType.BYTE, 100).shl().integerValue())
assertEquals(-2, RuntimeValue(DataType.BYTE, -1).shl().integerValue())
assertEquals(100, RuntimeValueNumeric(DataType.BYTE, 50).shl().integerValue())
assertEquals(-56, RuntimeValueNumeric(DataType.BYTE, 100).shl().integerValue())
assertEquals(-2, RuntimeValueNumeric(DataType.BYTE, -1).shl().integerValue())
}
@Test
fun arithmetictestWorrd() {
assertEquals(32767, RuntimeValue(DataType.WORD, 32700).add(RuntimeValue(DataType.WORD, 67)).integerValue())
assertEquals(-32768, RuntimeValue(DataType.WORD, 32700).add(RuntimeValue(DataType.WORD, 68)).integerValue())
assertEquals(-32767, RuntimeValue(DataType.WORD, 32700).add(RuntimeValue(DataType.WORD, 69)).integerValue())
assertEquals(32767, RuntimeValueNumeric(DataType.WORD, 32700).add(RuntimeValueNumeric(DataType.WORD, 67)).integerValue())
assertEquals(-32768, RuntimeValueNumeric(DataType.WORD, 32700).add(RuntimeValueNumeric(DataType.WORD, 68)).integerValue())
assertEquals(-32767, RuntimeValueNumeric(DataType.WORD, 32700).add(RuntimeValueNumeric(DataType.WORD, 69)).integerValue())
assertEquals(1, RuntimeValue(DataType.WORD, 2).sub(RuntimeValue(DataType.WORD, 1)).integerValue())
assertEquals(0, RuntimeValue(DataType.WORD, 2).sub(RuntimeValue(DataType.WORD, 2)).integerValue())
assertEquals(-1, RuntimeValue(DataType.WORD, 2).sub(RuntimeValue(DataType.WORD, 3)).integerValue())
assertEquals(-32768, RuntimeValue(DataType.WORD, -32700).sub(RuntimeValue(DataType.WORD, 68)).integerValue())
assertEquals(32767, RuntimeValue(DataType.WORD, -32700).sub(RuntimeValue(DataType.WORD, 69)).integerValue())
assertEquals(1, RuntimeValueNumeric(DataType.WORD, 2).sub(RuntimeValueNumeric(DataType.WORD, 1)).integerValue())
assertEquals(0, RuntimeValueNumeric(DataType.WORD, 2).sub(RuntimeValueNumeric(DataType.WORD, 2)).integerValue())
assertEquals(-1, RuntimeValueNumeric(DataType.WORD, 2).sub(RuntimeValueNumeric(DataType.WORD, 3)).integerValue())
assertEquals(-32768, RuntimeValueNumeric(DataType.WORD, -32700).sub(RuntimeValueNumeric(DataType.WORD, 68)).integerValue())
assertEquals(32767, RuntimeValueNumeric(DataType.WORD, -32700).sub(RuntimeValueNumeric(DataType.WORD, 69)).integerValue())
assertEquals(32767, RuntimeValue(DataType.WORD, 32766).inc().integerValue())
assertEquals(-32768, RuntimeValue(DataType.WORD, 32767).inc().integerValue())
assertEquals(0, RuntimeValue(DataType.WORD, 1).dec().integerValue())
assertEquals(-1, RuntimeValue(DataType.WORD, 0).dec().integerValue())
assertEquals(-32768, RuntimeValue(DataType.WORD, -32767).dec().integerValue())
assertEquals(32767, RuntimeValue(DataType.WORD, -32768).dec().integerValue())
assertEquals(32767, RuntimeValueNumeric(DataType.WORD, 32766).inc().integerValue())
assertEquals(-32768, RuntimeValueNumeric(DataType.WORD, 32767).inc().integerValue())
assertEquals(0, RuntimeValueNumeric(DataType.WORD, 1).dec().integerValue())
assertEquals(-1, RuntimeValueNumeric(DataType.WORD, 0).dec().integerValue())
assertEquals(-32768, RuntimeValueNumeric(DataType.WORD, -32767).dec().integerValue())
assertEquals(32767, RuntimeValueNumeric(DataType.WORD, -32768).dec().integerValue())
assertEquals(-1, RuntimeValue(DataType.WORD, 0).inv().integerValue())
assertEquals(-103, RuntimeValue(DataType.WORD, 0b01100110).inv().integerValue())
assertEquals(0, RuntimeValue(DataType.WORD, 0).neg().integerValue())
assertEquals(-2, RuntimeValue(DataType.WORD, 2).neg().integerValue())
assertEquals(1, RuntimeValue(DataType.WORD, 0).not().integerValue())
assertEquals(0, RuntimeValue(DataType.WORD, 1).not().integerValue())
assertEquals(0, RuntimeValue(DataType.WORD, 111).not().integerValue())
assertEquals(0, RuntimeValue(DataType.WORD, -33).not().integerValue())
assertEquals(-1, RuntimeValueNumeric(DataType.WORD, 0).inv().integerValue())
assertEquals(-103, RuntimeValueNumeric(DataType.WORD, 0b01100110).inv().integerValue())
assertEquals(0, RuntimeValueNumeric(DataType.WORD, 0).neg().integerValue())
assertEquals(-2, RuntimeValueNumeric(DataType.WORD, 2).neg().integerValue())
assertEquals(1, RuntimeValueNumeric(DataType.WORD, 0).not().integerValue())
assertEquals(0, RuntimeValueNumeric(DataType.WORD, 1).not().integerValue())
assertEquals(0, RuntimeValueNumeric(DataType.WORD, 111).not().integerValue())
assertEquals(0, RuntimeValueNumeric(DataType.WORD, -33).not().integerValue())
assertEquals(10000, RuntimeValue(DataType.WORD, 100).mul(RuntimeValue(DataType.WORD, 100)).integerValue())
assertEquals(-25536, RuntimeValue(DataType.WORD, 200).mul(RuntimeValue(DataType.WORD, 200)).integerValue())
assertEquals(10000, RuntimeValueNumeric(DataType.WORD, 100).mul(RuntimeValueNumeric(DataType.WORD, 100)).integerValue())
assertEquals(-25536, RuntimeValueNumeric(DataType.WORD, 200).mul(RuntimeValueNumeric(DataType.WORD, 200)).integerValue())
assertEquals(15625, RuntimeValue(DataType.WORD, 5).pow(RuntimeValue(DataType.WORD, 6)).integerValue())
assertEquals(-6487, RuntimeValue(DataType.WORD, 9).pow(RuntimeValue(DataType.WORD, 5)).integerValue())
assertEquals(15625, RuntimeValueNumeric(DataType.WORD, 5).pow(RuntimeValueNumeric(DataType.WORD, 6)).integerValue())
assertEquals(-6487, RuntimeValueNumeric(DataType.WORD, 9).pow(RuntimeValueNumeric(DataType.WORD, 5)).integerValue())
assertEquals(18000, RuntimeValue(DataType.WORD, 9000).shl().integerValue())
assertEquals(-25536, RuntimeValue(DataType.WORD, 20000).shl().integerValue())
assertEquals(-2, RuntimeValue(DataType.WORD, -1).shl().integerValue())
assertEquals(18000, RuntimeValueNumeric(DataType.WORD, 9000).shl().integerValue())
assertEquals(-25536, RuntimeValueNumeric(DataType.WORD, 20000).shl().integerValue())
assertEquals(-2, RuntimeValueNumeric(DataType.WORD, -1).shl().integerValue())
}
}
+5 -7
View File
@@ -15,11 +15,10 @@ import prog8.compiler.target.c64.MachineDefinition.FLOAT_MAX_NEGATIVE
import prog8.compiler.target.c64.MachineDefinition.FLOAT_MAX_POSITIVE
import prog8.compiler.target.c64.MachineDefinition.Mflpt5
import prog8.compiler.target.c64.Petscii
import prog8.vm.RuntimeValue
import prog8.vm.RuntimeValueNumeric
import java.io.CharConversionException
import kotlin.test.*
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class TestCompiler {
@Test
@@ -54,7 +53,6 @@ class TestCompiler {
assertFailsWith<CompilerException> { 65536L.toHex() }
}
@Test
fun testFloatToMflpt5() {
assertThat(Mflpt5.fromNumber(0), equalTo(Mflpt5(0x00, 0x00, 0x00, 0x00, 0x00)))
@@ -371,8 +369,8 @@ class TestPetscii {
assertTrue(ten <= ten)
assertFalse(ten < ten)
val abc = StringLiteralValue(DataType.STR, "abc", position = Position("", 0, 0, 0))
val abd = StringLiteralValue(DataType.STR, "abd", position = Position("", 0, 0, 0))
val abc = StringLiteralValue(DataType.STR, "abc", Position("", 0, 0, 0))
val abd = StringLiteralValue(DataType.STR, "abd", Position("", 0, 0, 0))
assertEquals(abc, abc)
assertTrue(abc!=abd)
assertFalse(abc!=abc)
@@ -380,8 +378,8 @@ class TestPetscii {
@Test
fun testStackvmValueComparisons() {
val ten = RuntimeValue(DataType.FLOAT, 10)
val nine = RuntimeValue(DataType.UWORD, 9)
val ten = RuntimeValueNumeric(DataType.FLOAT, 10)
val nine = RuntimeValueNumeric(DataType.UWORD, 9)
assertEquals(ten, ten)
assertNotEquals(ten, nine)
assertFalse(ten != ten)
+12 -11
View File
@@ -94,6 +94,13 @@ Start the compiler with the ``-watch`` argument to enable this.
It will compile your program and then instead of exiting, it waits for any changes in the module source files.
As soon as a change happens, the program gets compiled again.
Other options
^^^^^^^^^^^^^
There's an option to specify the output directory if you're not happy with the default (the current working directory).
Also it is possible to specify more than one main module to compile:
this can be useful to quickly recompile multiple separate programs quickly.
(compiling in a batch like this is a lot faster than invoking the compiler again once per main file)
Module source code files
------------------------
@@ -163,22 +170,16 @@ or::
Virtual Machine
---------------
Virtual Machine / Simulator
---------------------------
You may have noticed the ``-avm`` and ``-vm`` command line options for the compiler:
You may have noticed the ``-sim`` command line option for the compiler:
-avm
Launches the "AST virtual machine" that directly executes the parsed program.
-sim
Launches the "AST virtual machine Simulator" that directly executes the parsed program.
No compilation steps will be performed.
Allows for very fast testing and debugging before actually compiling programs
to machine code.
It simulates a bare minimum of features from the target platform, so most stuff
that calls ROM routines or writes into hardware registers won't work. But basic
system routines are emulated.
-vm <vm bytecode file>
Launches the "intermediate code VM"
it interprets the intermediate code that the compiler can write when using the ``-writevm``
option. This is the code that will be fed to the assembly code generator,
so you'll skip that last step.
+2 -2
View File
@@ -768,7 +768,7 @@ rol(x)
Modifies in-place, doesn't return a value (so can't be used in an expression).
rol2(x)
Like _rol but now as 8-bit or 16-bit rotation.
Like ``rol`` but now as 8-bit or 16-bit rotation.
It uses some extra logic to not consider the carry flag as extra rotation bit.
Modifies in-place, doesn't return a value (so can't be used in an expression).
@@ -780,7 +780,7 @@ ror(x)
Modifies in-place, doesn't return a value (so can't be used in an expression).
ror2(x)
Like _ror but now as 8-bit or 16-bit rotation.
Like ``ror`` but now as 8-bit or 16-bit rotation.
It uses some extra logic to not consider the carry flag as extra rotation bit.
Modifies in-place, doesn't return a value (so can't be used in an expression).
+5
View File
@@ -44,6 +44,11 @@ Allocate a fixed word in ZP that is the TOS so we can always operate on TOS dire
without having to to index into the stack?
Bugs
^^^^
Ofcourse there are still bugs to fix ;)
Misc
^^^^
+43 -9
View File
@@ -1,8 +1,6 @@
%import c64utils
%zeropage basicsafe
; TODO implement asm generation for all operation in here
main {
byte bb
@@ -11,15 +9,50 @@ main {
uword uw
&ubyte membyte=9999
&uword memword=9999
ubyte[10] barray
ubyte[] ubarray = [8,8,8]
uword[] uwarray = [8200, 8200, 8200]
byte[] bbarray = [8,8,8]
word[] wwarray = [8200, 8200, 8200]
sub unimplemented() {
; TODO implement these asm routines
lsr(ubarray[1])
lsl(ubarray[1])
ror(ubarray[1])
rol(ubarray[1])
ror2(ubarray[1])
rol2(ubarray[1])
lsr(bbarray[1])
lsl(bbarray[1])
lsr(uwarray[1])
lsl(uwarray[1])
ror(uwarray[1])
rol(uwarray[1])
ror2(uwarray[1])
rol2(uwarray[1])
lsr(wwarray[1])
lsl(wwarray[1])
}
sub start() {
unimplemented()
lsr(A)
lsl(A)
ror(A)
rol(A)
ror2(A)
rol2(A)
lsr(Y)
lsl(Y)
ror(Y)
rol(Y)
ror2(Y)
rol2(Y)
lsr(bb)
lsl(bb)
lsr(membyte)
lsl(membyte)
ror(membyte)
@@ -32,19 +65,20 @@ main {
rol(memword)
ror2(memword)
rol2(memword)
lsl(@(9999))
lsr(@(9999))
ror(@(9999))
rol(@(9999))
ror2(@(9999))
rol2(@(9999))
lsr(barray[1])
lsl(barray[1])
ror(barray[1])
rol(barray[1])
ror2(barray[1])
rol2(barray[1])
lsl(@(9999+A))
lsr(@(9999+A))
ror(@(9999+A))
rol(@(9999+A))
ror2(@(9999+A))
rol2(@(9999+A))
bb /= 2
bb >>= 1
+33
View File
@@ -106,6 +106,39 @@ main {
else
c64scr.print("error in -222>=-222!\n")
v1 = 1000
v2 = 1000
if v1==v2
c64scr.print("ok: 1000 == 1000\n")
else
c64scr.print("error in 1000==1000!\n")
if v1!=v2
c64scr.print("error in 1000!=1000!\n")
else
c64scr.print("ok: 1000 is not != 1000\n")
if v1<v2
c64scr.print("error in 1000<1000!\n")
else
c64scr.print("ok: 1000 is not < 1000\n")
if v1<=v2
c64scr.print("ok: 1000 <= 1000\n")
else
c64scr.print("error in 1000<=1000!\n")
if v1>v2
c64scr.print("error in 1000>1000!\n")
else
c64scr.print("ok: 1000 is not > 1000\n")
if v1>=v2
c64scr.print("ok: 1000 >= 1000\n")
else
c64scr.print("error in 1000>=1000!\n")
ubyte endX = X
if endX == 255
c64scr.print("stack x ok!\n")
Binary file not shown.
Binary file not shown.
Binary file not shown.
+39 -10
View File
@@ -1,23 +1,52 @@
%import c64lib
%import c64utils
%zeropage basicsafe
main {
sub start() {
ubyte[] ubarr = [22,33,44,55,66]
byte[] barr = [22,-33,-44,55,66]
ubyte endub1
byte endb1
ubyte aa
ubyte ub
byte bb
uword uw
word total
uword count
const ubyte i = 33
i=33
i++
const ubyte q=33
for q in [1,3,5,99] {
A=i
; ---------- BYTE var ---------
count = 0
total = 0
c64scr.print("byte var in arrayliteral: ")
for bb in [1,3,5,99] {
count++
total += bb
}
if count==4 and total==108
c64scr.print("ok\n")
else
c64scr.print("fail!!!\n")
while q<10 {
q++
; ---------- WORD var ---------
word[] warr = [-111,222,-333,444]
word endw1
word ww
count = 0
total = 0
c64scr.print("word var in arrayliteral: ")
for ww in [1111,3333,555,999] {
count++
total += ww
}
if count==4 and total==5998
c64scr.print("ok\n")
else
c64scr.print("fail!!!\n")
}
}
+917 -84
View File
File diff suppressed because it is too large. Load diff
+62
View File
@@ -0,0 +1,62 @@
%zeropage basicsafe
main {
sub start() {
c64scr.print("print uw0:")
c64scr.print_uw0(53204)
c64.CHROUT(' ')
c64scr.print_uw0(3204)
c64.CHROUT(' ')
c64scr.print_uw0(204)
c64.CHROUT(' ')
c64scr.print_uw0(14)
c64.CHROUT(' ')
c64scr.print_uw0(4)
c64.CHROUT(' ')
c64scr.print_uw0(0)
c64.CHROUT('\n')
c64scr.print("print uw:")
c64scr.print_uw(53204)
c64.CHROUT(' ')
c64scr.print_uw(3204)
c64.CHROUT(' ')
c64scr.print_uw(204)
c64.CHROUT(' ')
c64scr.print_uw(14)
c64.CHROUT(' ')
c64scr.print_uw(4)
c64.CHROUT(' ')
c64scr.print_uw(0)
c64.CHROUT('\n')
c64scr.print("print w:")
c64scr.print_w(23204)
c64.CHROUT(' ')
c64scr.print_w(3204)
c64.CHROUT(' ')
c64scr.print_w(204)
c64.CHROUT(' ')
c64scr.print_w(14)
c64.CHROUT(' ')
c64scr.print_w(4)
c64.CHROUT(' ')
c64scr.print_w(0)
c64.CHROUT('\n')
c64scr.print_w(-23204)
c64.CHROUT(' ')
c64scr.print_w(-3204)
c64.CHROUT(' ')
c64scr.print_w(-204)
c64.CHROUT(' ')
c64scr.print_w(-14)
c64.CHROUT(' ')
c64scr.print_w(-4)
c64.CHROUT(' ')
c64scr.print_w(-0)
c64.CHROUT('\n')
}
}
+1 -1
View File
@@ -4,4 +4,4 @@ org.gradle.parallel=true
org.gradle.jvmargs=-Xmx2500M
org.gradle.daemon=true
kotlinVersion=1.3.41
kotlinVersion=1.3.50
+3
View File
@@ -3,6 +3,9 @@ plugins {
id 'java'
}
targetCompatibility = 1.8
sourceCompatibility = 1.8
repositories {
mavenCentral()
}
-21
View File
@@ -1,21 +0,0 @@
#!/bin/sh
# this script uses the Gradle build to compile the code,
# and then adds the contents of several jar files into one output jar.
./gradlew jar
mkdir -p compiler_jar/extracted
mkdir -p compiler_jar/source
cp compiler/build/libs/compiler.jar parser/build/libs/parser.jar parser/antlr/lib/antlr-runtime-4.7.2.jar compiler_jar/source/
KOTLINLIBS=$(kotlinc -verbose -script 2>&1 | grep home | cut -d ' ' -f 6-)/lib
cp ${KOTLINLIBS}/kotlin-stdlib-jdk8.jar ${KOTLINLIBS}/kotlin-stdlib.jar compiler_jar/source/
pushd compiler_jar/extracted
for i in ../source/*.jar; do jar xf $i; done
cd ..
jar cfe ../prog8compiler.jar prog8.CompilerMainKt -C extracted .
popd
rm -r compiler_jar
ls -l prog8compiler.jar
+1 -1
View File
@@ -1,2 +1,2 @@
include ':parser'
include ':compiler'
include ':compiler'