mirror of
https://github.com/irmen/prog8.git
synced 2026-10-22 20:02:27 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b68f141568 | ||
|
|
b5d1e8653d | ||
|
|
f6d4c90dea | ||
|
|
b5b24636ae | ||
|
|
9dedbbf47c | ||
|
|
c493c3e5c6 | ||
|
|
61d4ca1d24 | ||
|
|
2cf9af4a6e | ||
|
|
bdcd10512f | ||
|
|
fec8db6a75 | ||
|
|
b400010426 | ||
|
|
28109a39ac | ||
|
|
651f0ec445 | ||
|
|
e61d3df380 | ||
|
|
15710207b2 | ||
|
|
adfddddac6 | ||
|
|
e46982f652 | ||
|
|
900c2aea23 | ||
|
|
42f8e98cab | ||
|
|
bed0e33b4f | ||
|
|
8d6542905d | ||
|
|
39798a1a4f | ||
|
|
befe4b8e9f | ||
|
|
772e48105e | ||
|
|
9afe451b8d | ||
|
|
89d469e77e | ||
|
|
59a43889a5 | ||
|
|
7caa0daffc | ||
|
|
5e854c2cf8 | ||
|
|
9edc92ec29 | ||
|
|
1d178080a3 | ||
|
|
aa94300bdd | ||
|
|
2d768c3f28 | ||
|
|
b79af624ae | ||
|
|
38208a7c9e | ||
|
|
8eff51904e | ||
|
|
c717f4573d | ||
|
|
984d251a6d | ||
|
|
8c3b43f3ed | ||
|
|
0f1485f30b | ||
|
|
eb94c678bd | ||
|
|
50d792a121 | ||
|
|
f0d4654917 | ||
|
|
4ce93b5d9d | ||
|
|
fb0d7a1908 | ||
|
|
bb7b063757 | ||
|
|
c495f54bbb | ||
|
|
1cc1f2d91d | ||
|
|
d837cc11f9 | ||
|
|
cbb7083307 | ||
|
|
d4a17dfad1 | ||
|
|
59f8b91e25 |
No files matched your search
@@ -1,4 +1,5 @@
|
||||
.idea/workspace.xml
|
||||
.idea/discord.xml
|
||||
/build/
|
||||
/dist/
|
||||
/output/
|
||||
|
||||
+10
@@ -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
@@ -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>
|
||||
Generated
+1
-1
@@ -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
@@ -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.
+4
-3
@@ -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
|
||||
|
||||
|
||||
@@ -232,7 +233,7 @@ class AsmGen(private val options: CompilationOptions, private val program: Inter
|
||||
|
||||
private fun block2asm(blk: IntermediateProgram.ProgramBlock) {
|
||||
block = blk
|
||||
out("\n; ---- block: '${block.name}' ----")
|
||||
out("\n\n; ---- block: '${block.name}' ----")
|
||||
if(!blk.force_output)
|
||||
out("${block.name}\t.proc\n")
|
||||
if(block.address!=null) {
|
||||
+4
-1
@@ -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
|
||||
+4
-1
@@ -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
|
||||
@@ -26,7 +26,7 @@ which aims to provide many conveniences over raw assembly code (even when using
|
||||
- abstracting away low level aspects such as ZeroPage handling, program startup, explicit memory addresses
|
||||
- various code optimizations (code structure, logical and numerical expressions, unused code removal...)
|
||||
- inline assembly allows you to have full control when every cycle or byte matters
|
||||
- many built-in functions such as ``sin``, ``cos``, ``rnd``, ``abs``, ``min``, ``max``, ``sqrt``, ``msb``, ``rol``, ``ror``, ``swap``, ``memset``, ``memcopy``
|
||||
- many built-in functions such as ``sin``, ``cos``, ``rnd``, ``abs``, ``min``, ``max``, ``sqrt``, ``msb``, ``rol``, ``ror``, ``swap``, ``memset``, ``memcopy``, ``sort`` and ``reverse``
|
||||
|
||||
Rapid edit-compile-run-debug cycle:
|
||||
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
@@ -81,9 +84,6 @@ artifacts {
|
||||
}
|
||||
|
||||
|
||||
// To create a fat-jar use the 'create_compiler_jar' script for now
|
||||
// @todo investigate https://imperceptiblethoughts.com/shadow/introduction/
|
||||
|
||||
shadowJar {
|
||||
baseName = 'prog8compiler'
|
||||
version = prog8version
|
||||
|
||||
@@ -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
@@ -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
|
||||
}}
|
||||
}
|
||||
|
||||
|
||||
@@ -1384,3 +1384,378 @@ _mod2b lda #0 ; self-modified
|
||||
_done rts
|
||||
.pend
|
||||
|
||||
|
||||
sort_ub .proc
|
||||
; 8bit unsigned sort
|
||||
; sorting subroutine coded by mats rosengren (mats.rosengren@esa.int)
|
||||
; input: address of array to sort in c64.SCRATCH_ZPWORD1, length in c64.SCRATCH_ZPB1
|
||||
; first, put pointer BEFORE array
|
||||
lda c64.SCRATCH_ZPWORD1
|
||||
bne +
|
||||
dec c64.SCRATCH_ZPWORD1+1
|
||||
+ dec c64.SCRATCH_ZPWORD1
|
||||
_sortloop ldy c64.SCRATCH_ZPB1 ;start of subroutine sort
|
||||
lda (c64.SCRATCH_ZPWORD1),y ;last value in (what is left of) sequence to be sorted
|
||||
sta c64.SCRATCH_ZPREG ;save value. will be over-written by largest number
|
||||
jmp _l2
|
||||
_l1 dey
|
||||
beq _l3
|
||||
lda (c64.SCRATCH_ZPWORD1),y
|
||||
cmp c64.SCRATCH_ZPWORD2+1
|
||||
bcc _l1
|
||||
_l2 sty c64.SCRATCH_ZPWORD2 ;index of potentially largest value
|
||||
sta c64.SCRATCH_ZPWORD2+1 ;potentially largest value
|
||||
jmp _l1
|
||||
_l3 ldy c64.SCRATCH_ZPB1 ;where the largest value shall be put
|
||||
lda c64.SCRATCH_ZPWORD2+1 ;the largest value
|
||||
sta (c64.SCRATCH_ZPWORD1),y ;put largest value in place
|
||||
ldy c64.SCRATCH_ZPWORD2 ;index of free space
|
||||
lda c64.SCRATCH_ZPREG ;the over-written value
|
||||
sta (c64.SCRATCH_ZPWORD1),y ;put the over-written value in the free space
|
||||
dec c64.SCRATCH_ZPB1 ;end of the shorter sequence still left
|
||||
bne _sortloop ;start working with the shorter sequence
|
||||
rts
|
||||
.pend
|
||||
|
||||
|
||||
sort_b .proc
|
||||
; 8bit signed sort
|
||||
; sorting subroutine coded by mats rosengren (mats.rosengren@esa.int)
|
||||
; input: address of array to sort in c64.SCRATCH_ZPWORD1, length in c64.SCRATCH_ZPB1
|
||||
; first, put pointer BEFORE array
|
||||
lda c64.SCRATCH_ZPWORD1
|
||||
bne +
|
||||
dec c64.SCRATCH_ZPWORD1+1
|
||||
+ dec c64.SCRATCH_ZPWORD1
|
||||
_sortloop ldy c64.SCRATCH_ZPB1 ;start of subroutine sort
|
||||
lda (c64.SCRATCH_ZPWORD1),y ;last value in (what is left of) sequence to be sorted
|
||||
sta c64.SCRATCH_ZPREG ;save value. will be over-written by largest number
|
||||
jmp _l2
|
||||
_l1 dey
|
||||
beq _l3
|
||||
lda (c64.SCRATCH_ZPWORD1),y
|
||||
cmp c64.SCRATCH_ZPWORD2+1
|
||||
bmi _l1
|
||||
_l2 sty c64.SCRATCH_ZPWORD2 ;index of potentially largest value
|
||||
sta c64.SCRATCH_ZPWORD2+1 ;potentially largest value
|
||||
jmp _l1
|
||||
_l3 ldy c64.SCRATCH_ZPB1 ;where the largest value shall be put
|
||||
lda c64.SCRATCH_ZPWORD2+1 ;the largest value
|
||||
sta (c64.SCRATCH_ZPWORD1),y ;put largest value in place
|
||||
ldy c64.SCRATCH_ZPWORD2 ;index of free space
|
||||
lda c64.SCRATCH_ZPREG ;the over-written value
|
||||
sta (c64.SCRATCH_ZPWORD1),y ;put the over-written value in the free space
|
||||
dec c64.SCRATCH_ZPB1 ;end of the shorter sequence still left
|
||||
bne _sortloop ;start working with the shorter sequence
|
||||
rts
|
||||
.pend
|
||||
|
||||
|
||||
sort_uw .proc
|
||||
; 16bit unsigned sort
|
||||
; sorting subroutine coded by mats rosengren (mats.rosengren@esa.int)
|
||||
; input: address of array to sort in c64.SCRATCH_ZPWORD1, length in c64.SCRATCH_ZPB1
|
||||
; first: subtract 2 of the pointer
|
||||
asl c64.SCRATCH_ZPB1 ; *2 because words
|
||||
lda c64.SCRATCH_ZPWORD1
|
||||
sec
|
||||
sbc #2
|
||||
sta c64.SCRATCH_ZPWORD1
|
||||
bcs _sort_loop
|
||||
dec c64.SCRATCH_ZPWORD1+1
|
||||
_sort_loop ldy c64.SCRATCH_ZPB1 ;start of subroutine sort
|
||||
lda (c64.SCRATCH_ZPWORD1),y ;last value in (what is left of) sequence to be sorted
|
||||
sta _work3 ;save value. will be over-written by largest number
|
||||
iny
|
||||
lda (c64.SCRATCH_ZPWORD1),y
|
||||
sta _work3+1
|
||||
dey
|
||||
jmp _l2
|
||||
_l1 dey
|
||||
dey
|
||||
beq _l3
|
||||
iny
|
||||
lda (c64.SCRATCH_ZPWORD1),y
|
||||
dey
|
||||
cmp c64.SCRATCH_ZPWORD2+1
|
||||
bne +
|
||||
lda (c64.SCRATCH_ZPWORD1),y
|
||||
cmp c64.SCRATCH_ZPWORD2
|
||||
+ bcc _l1
|
||||
_l2 sty _work1 ;index of potentially largest value
|
||||
lda (c64.SCRATCH_ZPWORD1),y
|
||||
sta c64.SCRATCH_ZPWORD2 ;potentially largest value
|
||||
iny
|
||||
lda (c64.SCRATCH_ZPWORD1),y
|
||||
sta c64.SCRATCH_ZPWORD2+1
|
||||
dey
|
||||
jmp _l1
|
||||
_l3 ldy c64.SCRATCH_ZPB1 ;where the largest value shall be put
|
||||
lda c64.SCRATCH_ZPWORD2 ;the largest value
|
||||
sta (c64.SCRATCH_ZPWORD1),y ;put largest value in place
|
||||
iny
|
||||
lda c64.SCRATCH_ZPWORD2+1
|
||||
sta (c64.SCRATCH_ZPWORD1),y
|
||||
ldy _work1 ;index of free space
|
||||
lda _work3 ;the over-written value
|
||||
sta (c64.SCRATCH_ZPWORD1),y ;put the over-written value in the free space
|
||||
iny
|
||||
lda _work3+1
|
||||
sta (c64.SCRATCH_ZPWORD1),y
|
||||
dey
|
||||
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
|
||||
_work1 .byte 0
|
||||
_work3 .word 0
|
||||
.pend
|
||||
|
||||
|
||||
sort_w .proc
|
||||
; 16bit signed sort
|
||||
; sorting subroutine coded by mats rosengren (mats.rosengren@esa.int)
|
||||
; input: address of array to sort in c64.SCRATCH_ZPWORD1, length in c64.SCRATCH_ZPB1
|
||||
; first: subtract 2 of the pointer
|
||||
asl c64.SCRATCH_ZPB1 ; *2 because words
|
||||
lda c64.SCRATCH_ZPWORD1
|
||||
sec
|
||||
sbc #2
|
||||
sta c64.SCRATCH_ZPWORD1
|
||||
bcs _sort_loop
|
||||
dec c64.SCRATCH_ZPWORD1+1
|
||||
_sort_loop ldy c64.SCRATCH_ZPB1 ;start of subroutine sort
|
||||
lda (c64.SCRATCH_ZPWORD1),y ;last value in (what is left of) sequence to be sorted
|
||||
sta _work3 ;save value. will be over-written by largest number
|
||||
iny
|
||||
lda (c64.SCRATCH_ZPWORD1),y
|
||||
sta _work3+1
|
||||
dey
|
||||
jmp _l2
|
||||
_l1 dey
|
||||
dey
|
||||
beq _l3
|
||||
lda (c64.SCRATCH_ZPWORD1),y
|
||||
cmp c64.SCRATCH_ZPWORD2
|
||||
iny
|
||||
lda (c64.SCRATCH_ZPWORD1),y
|
||||
dey
|
||||
sbc c64.SCRATCH_ZPWORD2+1
|
||||
bvc +
|
||||
eor #$80
|
||||
+ bmi _l1
|
||||
_l2 sty _work1 ;index of potentially largest value
|
||||
lda (c64.SCRATCH_ZPWORD1),y
|
||||
sta c64.SCRATCH_ZPWORD2 ;potentially largest value
|
||||
iny
|
||||
lda (c64.SCRATCH_ZPWORD1),y
|
||||
sta c64.SCRATCH_ZPWORD2+1
|
||||
dey
|
||||
jmp _l1
|
||||
_l3 ldy c64.SCRATCH_ZPB1 ;where the largest value shall be put
|
||||
lda c64.SCRATCH_ZPWORD2 ;the largest value
|
||||
sta (c64.SCRATCH_ZPWORD1),y ;put largest value in place
|
||||
iny
|
||||
lda c64.SCRATCH_ZPWORD2+1
|
||||
sta (c64.SCRATCH_ZPWORD1),y
|
||||
ldy _work1 ;index of free space
|
||||
lda _work3 ;the over-written value
|
||||
sta (c64.SCRATCH_ZPWORD1),y ;put the over-written value in the free space
|
||||
iny
|
||||
lda _work3+1
|
||||
sta (c64.SCRATCH_ZPWORD1),y
|
||||
dey
|
||||
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
|
||||
_work1 .byte 0
|
||||
_work3 .word 0
|
||||
.pend
|
||||
|
||||
|
||||
reverse_b .proc
|
||||
; --- reverse an array of bytes (in-place)
|
||||
; inputs: pointer to array in c64.SCRATCH_ZPWORD1, length in A
|
||||
_left_index = c64.SCRATCH_ZPWORD2
|
||||
_right_index = c64.SCRATCH_ZPWORD2+1
|
||||
pha
|
||||
sec
|
||||
sbc #1
|
||||
sta _left_index
|
||||
lda #0
|
||||
sta _right_index
|
||||
pla
|
||||
lsr a
|
||||
tay
|
||||
_loop sty c64.SCRATCH_ZPREG
|
||||
ldy _left_index
|
||||
lda (c64.SCRATCH_ZPWORD1),y
|
||||
pha
|
||||
ldy _right_index
|
||||
lda (c64.SCRATCH_ZPWORD1),y
|
||||
ldy _left_index
|
||||
sta (c64.SCRATCH_ZPWORD1),y
|
||||
pla
|
||||
ldy _right_index
|
||||
sta (c64.SCRATCH_ZPWORD1),y
|
||||
inc _right_index
|
||||
dec _left_index
|
||||
ldy c64.SCRATCH_ZPREG
|
||||
dey
|
||||
bne _loop
|
||||
rts
|
||||
.pend
|
||||
|
||||
|
||||
reverse_w .proc
|
||||
; --- reverse an array of words (in-place)
|
||||
; inputs: pointer to array in c64.SCRATCH_ZPWORD1, length in A
|
||||
_left_index = c64.SCRATCH_ZPWORD2
|
||||
_right_index = c64.SCRATCH_ZPWORD2+1
|
||||
pha
|
||||
asl a ; *2 because words
|
||||
sec
|
||||
sbc #2
|
||||
sta _left_index
|
||||
lda #0
|
||||
sta _right_index
|
||||
pla
|
||||
lsr a
|
||||
pha
|
||||
tay
|
||||
; first reverse the lsbs
|
||||
_loop_lo sty c64.SCRATCH_ZPREG
|
||||
ldy _left_index
|
||||
lda (c64.SCRATCH_ZPWORD1),y
|
||||
pha
|
||||
ldy _right_index
|
||||
lda (c64.SCRATCH_ZPWORD1),y
|
||||
ldy _left_index
|
||||
sta (c64.SCRATCH_ZPWORD1),y
|
||||
pla
|
||||
ldy _right_index
|
||||
sta (c64.SCRATCH_ZPWORD1),y
|
||||
inc _right_index
|
||||
inc _right_index
|
||||
dec _left_index
|
||||
dec _left_index
|
||||
ldy c64.SCRATCH_ZPREG
|
||||
dey
|
||||
bne _loop_lo
|
||||
; now reverse the msbs
|
||||
dec _right_index
|
||||
inc _left_index
|
||||
inc _left_index
|
||||
inc _left_index
|
||||
pla
|
||||
tay
|
||||
_loop_hi sty c64.SCRATCH_ZPREG
|
||||
ldy _left_index
|
||||
lda (c64.SCRATCH_ZPWORD1),y
|
||||
pha
|
||||
ldy _right_index
|
||||
lda (c64.SCRATCH_ZPWORD1),y
|
||||
ldy _left_index
|
||||
sta (c64.SCRATCH_ZPWORD1),y
|
||||
pla
|
||||
ldy _right_index
|
||||
sta (c64.SCRATCH_ZPWORD1),y
|
||||
dec _right_index
|
||||
dec _right_index
|
||||
inc _left_index
|
||||
inc _left_index
|
||||
ldy c64.SCRATCH_ZPREG
|
||||
dey
|
||||
bne _loop_hi
|
||||
|
||||
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 +1 @@
|
||||
1.52
|
||||
1.62
|
||||
@@ -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)
|
||||
}
|
||||
@@ -315,13 +315,6 @@ class AstToSourceCode(val output: (text: String) -> Unit, val program: Program):
|
||||
|
||||
override fun visit(forLoop: ForLoop) {
|
||||
output("for ")
|
||||
if(forLoop.decltype!=null) {
|
||||
output(datatypeString(forLoop.decltype))
|
||||
if (forLoop.zeropage==ZeropageWish.REQUIRE_ZEROPAGE || forLoop.zeropage==ZeropageWish.PREFER_ZEROPAGE)
|
||||
output(" @zp ")
|
||||
else
|
||||
output(" ")
|
||||
}
|
||||
if(forLoop.loopRegister!=null)
|
||||
output(forLoop.loopRegister.toString())
|
||||
else
|
||||
|
||||
@@ -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()) {
|
||||
|
||||
@@ -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())
|
||||
@@ -552,8 +552,6 @@ private fun prog8Parser.BranchconditionContext.toAst() = BranchCondition.valueOf
|
||||
|
||||
private fun prog8Parser.ForloopContext.toAst(): ForLoop {
|
||||
val loopregister = register()?.toAst()
|
||||
val datatype = datatype()?.toAst()
|
||||
val zeropage = if(ZEROPAGE() != null) ZeropageWish.PREFER_ZEROPAGE else ZeropageWish.DONTCARE
|
||||
val loopvar = identifier()?.toAst()
|
||||
val iterable = expression()!!.toAst()
|
||||
val scope =
|
||||
@@ -561,7 +559,7 @@ private fun prog8Parser.ForloopContext.toAst(): ForLoop {
|
||||
AnonymousScope(mutableListOf(statement().toAst()), statement().toPosition())
|
||||
else
|
||||
AnonymousScope(statement_block().toAst(), statement_block().toPosition())
|
||||
return ForLoop(loopregister, datatype, zeropage, loopvar, iterable, scope, toPosition())
|
||||
return ForLoop(loopregister, loopvar, iterable, scope, 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
|
||||
@@ -24,6 +25,16 @@ object InferredTypes {
|
||||
return false
|
||||
return isVoid==other.isVoid && datatype==other.datatype
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return when {
|
||||
datatype!=null -> datatype.toString()
|
||||
isVoid -> "<void>"
|
||||
else -> "<unknown>"
|
||||
}
|
||||
}
|
||||
|
||||
override fun hashCode(): Int = Objects.hash(isVoid, datatype)
|
||||
}
|
||||
|
||||
private val unknownInstance = InferredType.unknown()
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
@file:Suppress("DuplicatedCode")
|
||||
|
||||
package prog8.ast.processing
|
||||
|
||||
import prog8.ast.IFunctionCall
|
||||
import prog8.ast.INameScope
|
||||
import prog8.ast.Module
|
||||
import prog8.ast.Program
|
||||
@@ -144,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))
|
||||
}
|
||||
@@ -694,7 +698,7 @@ internal class AstChecker(private val program: Program,
|
||||
|
||||
super.visit(array)
|
||||
|
||||
if(array.heapId==null)
|
||||
if(array.heapId==null && array.parent !is IFunctionCall)
|
||||
throw FatalAstException("array should have been moved to heap at ${array.position}")
|
||||
}
|
||||
|
||||
@@ -831,7 +835,7 @@ internal class AstChecker(private val program: Program,
|
||||
printWarning("result values of subroutine call are discarded", functionCallStatement.position)
|
||||
}
|
||||
|
||||
if(functionCallStatement.target.nameInSource.last() in setOf("lsl", "lsr", "rol", "ror", "rol2", "ror2", "swap")) {
|
||||
if(functionCallStatement.target.nameInSource.last() in setOf("lsl", "lsr", "rol", "ror", "rol2", "ror2", "swap", "sort", "reverse")) {
|
||||
// in-place modification, can't be done on literals
|
||||
if(functionCallStatement.arglist.any { it !is IdentifierReference && it !is RegisterExpr && it !is ArrayIndexedExpression && it !is DirectMemoryRead }) {
|
||||
checkResult.add(ExpressionError("can't use that as argument to a in-place modifying function", functionCallStatement.position))
|
||||
@@ -888,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)) {
|
||||
@@ -944,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)
|
||||
}
|
||||
|
||||
@@ -964,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))
|
||||
@@ -1205,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.map { it.constValue(program)!! }
|
||||
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
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ import prog8.ast.Program
|
||||
import prog8.ast.base.*
|
||||
import prog8.ast.expressions.*
|
||||
import prog8.ast.statements.*
|
||||
import prog8.compiler.HeapValues
|
||||
import prog8.compiler.target.c64.AssemblyProgram
|
||||
import prog8.functions.BuiltinFunctions
|
||||
|
||||
@@ -68,7 +67,7 @@ internal class AstIdentifiersChecker(private val program: Program) : IAstModifyi
|
||||
checkResult.add(NameError("builtin function cannot be redefined", decl.position))
|
||||
|
||||
if(decl.name in AssemblyProgram.opcodeNames)
|
||||
checkResult.add(NameError("can't use a cpu opcode name as a symbol", decl.position))
|
||||
checkResult.add(NameError("can't use a cpu opcode name as a symbol: '${decl.name}'", decl.position))
|
||||
|
||||
// is it a struct variable? then define all its struct members as mangled names,
|
||||
// and include the original decl as well.
|
||||
@@ -105,7 +104,7 @@ internal class AstIdentifiersChecker(private val program: Program) : IAstModifyi
|
||||
|
||||
override fun visit(subroutine: Subroutine): Statement {
|
||||
if(subroutine.name in AssemblyProgram.opcodeNames) {
|
||||
checkResult.add(NameError("can't use a cpu opcode name as a symbol", subroutine.position))
|
||||
checkResult.add(NameError("can't use a cpu opcode name as a symbol: '${subroutine.name}'", subroutine.position))
|
||||
} else if(subroutine.name in BuiltinFunctions) {
|
||||
// the builtin functions can't be redefined
|
||||
checkResult.add(NameError("builtin function cannot be redefined", subroutine.position))
|
||||
@@ -166,7 +165,7 @@ internal class AstIdentifiersChecker(private val program: Program) : IAstModifyi
|
||||
|
||||
override fun visit(label: Label): Statement {
|
||||
if(label.name in AssemblyProgram.opcodeNames)
|
||||
checkResult.add(NameError("can't use a cpu opcode name as a symbol", label.position))
|
||||
checkResult.add(NameError("can't use a cpu opcode name as a symbol: '${label.name}'", label.position))
|
||||
|
||||
if(label.name in BuiltinFunctions) {
|
||||
// the builtin functions can't be redefined
|
||||
@@ -185,28 +184,11 @@ internal class AstIdentifiersChecker(private val program: Program) : IAstModifyi
|
||||
// For loops that loop over an interable variable (instead of a range of numbers) get an
|
||||
// additional interation count variable in their scope.
|
||||
if(forLoop.loopRegister!=null) {
|
||||
if(forLoop.decltype!=null)
|
||||
checkResult.add(SyntaxError("register loop variables have a fixed implicit datatype", forLoop.position))
|
||||
if(forLoop.loopRegister == Register.X)
|
||||
printWarning("writing to the X register is dangerous, because it's used as an internal pointer", forLoop.position)
|
||||
} else {
|
||||
val loopVar = forLoop.loopVar
|
||||
if (loopVar != null) {
|
||||
val varName = loopVar.nameInSource.last()
|
||||
if (forLoop.decltype != null) {
|
||||
val existing = if (forLoop.body.containsNoCodeNorVars()) null else forLoop.body.lookup(loopVar.nameInSource, forLoop.body.statements.first())
|
||||
if (existing == null) {
|
||||
// create the local scoped for loop variable itself
|
||||
val vardecl = VarDecl(VarDeclType.VAR, forLoop.decltype, forLoop.zeropage, null, varName, null, null,
|
||||
isArray = false, autogeneratedDontRemove = true, position = loopVar.position)
|
||||
vardecl.linkParents(forLoop.body)
|
||||
forLoop.body.statements.add(0, vardecl)
|
||||
loopVar.parent = forLoop.body // loopvar 'is defined in the body'
|
||||
} else if(existing.parent!==forLoop && existing.parent.parent!==forLoop) {
|
||||
checkResult.add(NameError("for loop var was already defined at ${existing.position}", loopVar.position))
|
||||
}
|
||||
}
|
||||
|
||||
val validName = forLoop.body.name.replace("<", "").replace(">", "").replace("-", "")
|
||||
val loopvarName = "prog8_loopvar_$validName"
|
||||
if (forLoop.iterable !is RangeExpr) {
|
||||
@@ -237,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)
|
||||
}
|
||||
@@ -254,16 +229,18 @@ 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.heap)
|
||||
} else {
|
||||
// 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)
|
||||
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
|
||||
@@ -277,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
|
||||
@@ -289,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
|
||||
@@ -306,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)
|
||||
@@ -316,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)
|
||||
@@ -357,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
|
||||
}
|
||||
@@ -384,14 +360,58 @@ internal class AstIdentifiersChecker(private val program: Program) : IAstModifyi
|
||||
|
||||
}
|
||||
|
||||
internal fun fixupArrayDatatype(array: ArrayLiteralValue, vardecl: VarDecl, heap: HeapValues): ArrayLiteralValue {
|
||||
internal fun fixupArrayDatatype(array: ArrayLiteralValue, program: Program): ArrayLiteralValue {
|
||||
val dts = array.value.map {it.inferType(program).typeOrElse(DataType.STRUCT)}.toSet()
|
||||
if(dts.any { it !in NumericDatatypes }) {
|
||||
return array
|
||||
}
|
||||
val dt = when {
|
||||
DataType.FLOAT in dts -> DataType.ARRAY_F
|
||||
DataType.WORD in dts -> DataType.ARRAY_W
|
||||
DataType.UWORD in dts -> DataType.ARRAY_UW
|
||||
DataType.BYTE in dts -> DataType.ARRAY_B
|
||||
else -> DataType.ARRAY_UB
|
||||
}
|
||||
if(dt==array.type)
|
||||
return array
|
||||
|
||||
// convert values and array type
|
||||
val elementType = ArrayElementTypes.getValue(dt)
|
||||
val values = array.value.map { (it as NumericLiteralValue).cast(elementType) as Expression}.toTypedArray()
|
||||
val array2 = ArrayLiteralValue(dt, values, array.heapId, array.position)
|
||||
array2.linkParents(array.parent)
|
||||
return array2
|
||||
}
|
||||
|
||||
internal fun fixupArrayDatatype(array: ArrayLiteralValue, vardecl: VarDecl, program: Program): ArrayLiteralValue {
|
||||
if(array.heapId!=null) {
|
||||
val arrayDt = array.type
|
||||
if(arrayDt!=vardecl.datatype) {
|
||||
// fix the datatype of the array (also on the heap) to match the vardecl
|
||||
val litval2 =
|
||||
try {
|
||||
array.cast(vardecl.datatype)!!
|
||||
val result = array.cast(vardecl.datatype)
|
||||
if(result==null) {
|
||||
val constElements = array.value.mapNotNull { it.constValue(program) }
|
||||
val elementDts = constElements.map { it.type }
|
||||
if(DataType.FLOAT in elementDts) {
|
||||
array.cast(DataType.ARRAY_F) ?: ArrayLiteralValue(DataType.ARRAY_F, array.value, array.heapId, array.position)
|
||||
} else {
|
||||
val numbers = constElements.map { it.number.toInt() }
|
||||
val minValue = numbers.min()!!
|
||||
val maxValue = numbers.max()!!
|
||||
if (minValue >= 0) {
|
||||
// only positive values, so uword or ubyte
|
||||
val dt = if(maxValue<256) DataType.ARRAY_UB else DataType.ARRAY_UW
|
||||
array.cast(dt) ?: ArrayLiteralValue(dt, array.value, array.heapId, array.position)
|
||||
} else {
|
||||
// negative value present, so word or byte
|
||||
val dt = if(minValue >= -128 && maxValue<=127) DataType.ARRAY_B else DataType.ARRAY_W
|
||||
array.cast(dt) ?: ArrayLiteralValue(dt, array.value, array.heapId, array.position)
|
||||
}
|
||||
}
|
||||
}
|
||||
else result
|
||||
} catch(x: ExpressionError) {
|
||||
// couldn't cast permanently.
|
||||
// instead, simply adjust the array type and trust the AstChecker to report the exact error
|
||||
@@ -399,11 +419,9 @@ internal fun fixupArrayDatatype(array: ArrayLiteralValue, vardecl: VarDecl, heap
|
||||
}
|
||||
vardecl.value = litval2
|
||||
litval2.linkParents(vardecl)
|
||||
litval2.addToHeap(heap)
|
||||
litval2.addToHeap()
|
||||
return litval2
|
||||
}
|
||||
} else {
|
||||
array.addToHeap(heap)
|
||||
}
|
||||
return array
|
||||
}
|
||||
@@ -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")
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
package prog8.ast.processing
|
||||
|
||||
import prog8.ast.Module
|
||||
import prog8.ast.Program
|
||||
import prog8.ast.*
|
||||
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.*
|
||||
|
||||
|
||||
@@ -64,7 +62,10 @@ internal class StatementReorderer(private val program: Program): IAstModifyingVi
|
||||
|
||||
private val directivesToMove = setOf("%output", "%launcher", "%zeropage", "%zpreserved", "%address", "%option")
|
||||
|
||||
private val addReturns = mutableListOf<Pair<INameScope, Int>>()
|
||||
|
||||
override fun visit(module: Module) {
|
||||
addReturns.clear()
|
||||
super.visit(module)
|
||||
|
||||
val (blocks, other) = module.statements.partition { it is Block }
|
||||
@@ -92,6 +93,13 @@ internal class StatementReorderer(private val program: Program): IAstModifyingVi
|
||||
val directives = module.statements.filter {it is Directive && it.directive in directivesToMove}
|
||||
module.statements.removeAll(directives)
|
||||
module.statements.addAll(0, directives)
|
||||
|
||||
for(pos in addReturns) {
|
||||
println(pos)
|
||||
val returnStmt = Return(null, pos.first.position)
|
||||
returnStmt.linkParents(pos.first as Node)
|
||||
pos.first.statements.add(pos.second, returnStmt)
|
||||
}
|
||||
}
|
||||
|
||||
override fun visit(block: Block): Statement {
|
||||
@@ -161,6 +169,19 @@ internal class StatementReorderer(private val program: Program): IAstModifyingVi
|
||||
override fun visit(subroutine: Subroutine): Statement {
|
||||
super.visit(subroutine)
|
||||
|
||||
val scope = subroutine.definingScope()
|
||||
if(scope is Subroutine) {
|
||||
for(stmt in scope.statements.withIndex()) {
|
||||
if(stmt.index>0 && stmt.value===subroutine) {
|
||||
val precedingStmt = scope.statements[stmt.index-1]
|
||||
if(precedingStmt !is Jump && precedingStmt !is Subroutine) {
|
||||
// insert a return statement before a nested subroutine, to avoid falling trough inside the subroutine
|
||||
addReturns.add(Pair(scope, stmt.index))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val varDecls = subroutine.statements.filterIsInstance<VarDecl>()
|
||||
subroutine.statements.removeAll(varDecls)
|
||||
subroutine.statements.addAll(0, varDecls)
|
||||
|
||||
@@ -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>,
|
||||
@@ -649,8 +646,6 @@ class BranchStatement(var condition: BranchCondition,
|
||||
}
|
||||
|
||||
class ForLoop(val loopRegister: Register?,
|
||||
val decltype: DataType?,
|
||||
val zeropage: ZeropageWish,
|
||||
var loopVar: IdentifierReference?,
|
||||
var iterable: Expression,
|
||||
var body: AnonymousScope,
|
||||
@@ -660,7 +655,7 @@ class ForLoop(val loopRegister: Register?,
|
||||
|
||||
override fun linkParents(parent: Node) {
|
||||
this.parent=parent
|
||||
loopVar?.linkParents(if(decltype==null) this else body)
|
||||
loopVar?.linkParents(this)
|
||||
iterable.linkParents(this)
|
||||
body.linkParents(this)
|
||||
}
|
||||
@@ -794,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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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")
|
||||
@@ -149,7 +153,7 @@ internal class AsmGen(val program: Program,
|
||||
}
|
||||
|
||||
private fun block2asm(block: Block) {
|
||||
out("\n; ---- block: '${block.name}' ----")
|
||||
out("\n\n; ---- block: '${block.name}' ----")
|
||||
out("${block.name}\t" + (if("force_output" in block.options()) ".block\n" else ".proc\n"))
|
||||
|
||||
if(block.address!=null) {
|
||||
@@ -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,6 +9,7 @@ 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.C64Zeropage
|
||||
import prog8.compiler.target.c64.MachineDefinition.ESTACK_HI_HEX
|
||||
import prog8.compiler.target.c64.MachineDefinition.ESTACK_HI_PLUS1_HEX
|
||||
import prog8.compiler.target.c64.MachineDefinition.ESTACK_LO_HEX
|
||||
@@ -120,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()
|
||||
@@ -155,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")
|
||||
@@ -196,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")
|
||||
@@ -221,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")
|
||||
@@ -231,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")
|
||||
@@ -248,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")
|
||||
}
|
||||
@@ -262,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")
|
||||
}
|
||||
@@ -276,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")
|
||||
}
|
||||
@@ -290,14 +436,129 @@ 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")
|
||||
}
|
||||
}
|
||||
"sort" -> {
|
||||
val variable = fcall.arglist.single()
|
||||
if(variable is IdentifierReference) {
|
||||
val decl = variable.targetVarDecl(program.namespace)!!
|
||||
val varName = asmgen.asmIdentifierName(variable)
|
||||
val numElements = decl.arraysize!!.size()
|
||||
when(decl.datatype) {
|
||||
DataType.ARRAY_UB, DataType.ARRAY_B -> {
|
||||
asmgen.out("""
|
||||
lda #<$varName
|
||||
ldy #>$varName
|
||||
sta ${C64Zeropage.SCRATCH_W1}
|
||||
sty ${C64Zeropage.SCRATCH_W1+1}
|
||||
lda #$numElements
|
||||
sta ${C64Zeropage.SCRATCH_B1}
|
||||
""")
|
||||
asmgen.out(if(decl.datatype==DataType.ARRAY_UB) " jsr prog8_lib.sort_ub" else " jsr prog8_lib.sort_b")
|
||||
}
|
||||
DataType.ARRAY_UW, DataType.ARRAY_W -> {
|
||||
asmgen.out("""
|
||||
lda #<$varName
|
||||
ldy #>$varName
|
||||
sta ${C64Zeropage.SCRATCH_W1}
|
||||
sty ${C64Zeropage.SCRATCH_W1+1}
|
||||
lda #$numElements
|
||||
sta ${C64Zeropage.SCRATCH_B1}
|
||||
""")
|
||||
asmgen.out(if(decl.datatype==DataType.ARRAY_UW) " jsr prog8_lib.sort_uw" else " jsr prog8_lib.sort_w")
|
||||
}
|
||||
DataType.ARRAY_F -> TODO("sort floats (consider another solution if possible - this will be very slow, if ever implemented)")
|
||||
else -> throw AssemblyError("weird type")
|
||||
}
|
||||
}
|
||||
else
|
||||
throw AssemblyError("weird type")
|
||||
}
|
||||
"reverse" -> {
|
||||
val variable = fcall.arglist.single()
|
||||
if (variable is IdentifierReference) {
|
||||
val decl = variable.targetVarDecl(program.namespace)!!
|
||||
val varName = asmgen.asmIdentifierName(variable)
|
||||
val numElements = decl.arraysize!!.size()
|
||||
when (decl.datatype) {
|
||||
DataType.ARRAY_UB, DataType.ARRAY_B -> {
|
||||
asmgen.out("""
|
||||
lda #<$varName
|
||||
ldy #>$varName
|
||||
sta ${C64Zeropage.SCRATCH_W1}
|
||||
sty ${C64Zeropage.SCRATCH_W1 + 1}
|
||||
lda #$numElements
|
||||
jsr prog8_lib.reverse_b
|
||||
""")
|
||||
}
|
||||
DataType.ARRAY_UW, DataType.ARRAY_W -> {
|
||||
asmgen.out("""
|
||||
lda #<$varName
|
||||
ldy #>$varName
|
||||
sta ${C64Zeropage.SCRATCH_W1}
|
||||
sty ${C64Zeropage.SCRATCH_W1 + 1}
|
||||
lda #$numElements
|
||||
jsr prog8_lib.reverse_w
|
||||
""")
|
||||
}
|
||||
DataType.ARRAY_F -> TODO("reverse floats (consider another solution if possible - this will be quite slow, if ever implemented)")
|
||||
else -> throw AssemblyError("weird type")
|
||||
}
|
||||
}
|
||||
}
|
||||
"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")
|
||||
|
||||
@@ -21,37 +21,39 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
|
||||
is NumericLiteralValue -> translateExpression(expression)
|
||||
is RegisterExpr -> translateExpression(expression)
|
||||
is IdentifierReference -> translateExpression(expression)
|
||||
is FunctionCall -> {
|
||||
val functionName = expression.target.nameInSource.last()
|
||||
val builtinFunc = BuiltinFunctions[functionName]
|
||||
if(builtinFunc!=null) {
|
||||
asmgen.translateFunctioncallExpression(expression, builtinFunc)
|
||||
} else {
|
||||
asmgen.translateFunctionCall(expression)
|
||||
val sub = expression.target.targetSubroutine(program.namespace)!!
|
||||
val returns = sub.returntypes.zip(sub.asmReturnvaluesRegisters)
|
||||
for((_, reg) in returns) {
|
||||
if(!reg.stack) {
|
||||
// result value in cpu or status registers, put it on the stack
|
||||
if(reg.registerOrPair!=null) {
|
||||
when(reg.registerOrPair) {
|
||||
RegisterOrPair.A -> asmgen.out(" sta ${MachineDefinition.ESTACK_LO_HEX},x | dex")
|
||||
RegisterOrPair.Y -> asmgen.out(" tya | sta ${MachineDefinition.ESTACK_LO_HEX},x | dex")
|
||||
RegisterOrPair.AY -> asmgen.out(" sta ${MachineDefinition.ESTACK_LO_HEX},x | tya | sta ${MachineDefinition.ESTACK_HI_HEX},x | dex")
|
||||
RegisterOrPair.X, RegisterOrPair.AX, RegisterOrPair.XY -> throw AssemblyError("can't push X register - use a variable")
|
||||
}
|
||||
}
|
||||
// return value from a statusregister is not put on the stack, it should be acted on via a conditional branch such as if_cc
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
is FunctionCall -> translateExpression(expression)
|
||||
is ArrayLiteralValue, is StringLiteralValue -> TODO("string/array/struct assignment?")
|
||||
is StructLiteralValue -> throw AssemblyError("struct literal value assignment should have been flattened")
|
||||
is RangeExpr -> throw AssemblyError("range expression should have been changed into array values")
|
||||
}
|
||||
}
|
||||
|
||||
private fun translateExpression(expression: FunctionCall) {
|
||||
val functionName = expression.target.nameInSource.last()
|
||||
val builtinFunc = BuiltinFunctions[functionName]
|
||||
if (builtinFunc != null) {
|
||||
asmgen.translateFunctioncallExpression(expression, builtinFunc)
|
||||
} else {
|
||||
asmgen.translateFunctionCall(expression)
|
||||
val sub = expression.target.targetSubroutine(program.namespace)!!
|
||||
val returns = sub.returntypes.zip(sub.asmReturnvaluesRegisters)
|
||||
for ((_, reg) in returns) {
|
||||
if (!reg.stack) {
|
||||
// result value in cpu or status registers, put it on the stack
|
||||
if (reg.registerOrPair != null) {
|
||||
when (reg.registerOrPair) {
|
||||
RegisterOrPair.A -> asmgen.out(" sta ${MachineDefinition.ESTACK_LO_HEX},x | dex")
|
||||
RegisterOrPair.Y -> asmgen.out(" tya | sta ${MachineDefinition.ESTACK_LO_HEX},x | dex")
|
||||
RegisterOrPair.AY -> asmgen.out(" sta ${MachineDefinition.ESTACK_LO_HEX},x | tya | sta ${MachineDefinition.ESTACK_HI_HEX},x | dex")
|
||||
RegisterOrPair.X, RegisterOrPair.AX, RegisterOrPair.XY -> throw AssemblyError("can't push X register - use a variable")
|
||||
}
|
||||
}
|
||||
// return value from a statusregister is not put on the stack, it should be acted on via a conditional branch such as if_cc
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun translateExpression(expr: TypecastExpression) {
|
||||
translateExpression(expr.expression)
|
||||
when(expr.expression.inferType(program).typeOrElse(DataType.STRUCT)) {
|
||||
@@ -162,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")
|
||||
}
|
||||
@@ -175,7 +179,7 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
|
||||
|
||||
private val optimizedByteMultiplications = setOf(3,5,6,7,9,10,11,12,13,14,15,20,25,40)
|
||||
private val optimizedWordMultiplications = setOf(3,5,6,7,9,10,12,15,20,25,40)
|
||||
private val powerOfTwos = setOf(0,1,2,4,8,16,32,64,128,256)
|
||||
private val powersOfTwo = setOf(0,1,2,4,8,16,32,64,128,256)
|
||||
|
||||
private fun translateExpression(expr: BinaryExpression) {
|
||||
val leftIDt = expr.left.inferType(program)
|
||||
@@ -215,7 +219,7 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
|
||||
if(value!=null) {
|
||||
if(rightDt in IntegerDatatypes) {
|
||||
val amount = value.number.toInt()
|
||||
if(amount in powerOfTwos)
|
||||
if(amount in powersOfTwo)
|
||||
printWarning("${expr.right.position} multiplication by power of 2 should have been optimized into a left shift instruction: $amount")
|
||||
when(rightDt) {
|
||||
DataType.UBYTE -> {
|
||||
@@ -312,7 +316,6 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun translatePushFromArray(arrayExpr: ArrayIndexedExpression) {
|
||||
// assume *reading* from an array
|
||||
val index = arrayExpr.arrayspec.index
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -27,10 +27,12 @@ val BuiltinFunctions = mapOf(
|
||||
"ror2" to FunctionSignature(false, listOf(BuiltinFunctionParam("item", setOf(DataType.UBYTE, DataType.UWORD))), null),
|
||||
"lsl" to FunctionSignature(false, listOf(BuiltinFunctionParam("item", IntegerDatatypes)), null),
|
||||
"lsr" to FunctionSignature(false, listOf(BuiltinFunctionParam("item", IntegerDatatypes)), null),
|
||||
"sort" to FunctionSignature(false, listOf(BuiltinFunctionParam("array", ArrayDatatypes)), null),
|
||||
"reverse" to FunctionSignature(false, listOf(BuiltinFunctionParam("array", ArrayDatatypes)), null),
|
||||
// these few have a return value depending on the argument(s):
|
||||
"max" to FunctionSignature(true, listOf(BuiltinFunctionParam("values", ArrayDatatypes)), null) { a, p, _ -> collectionArgNeverConst(a, p) }, // type depends on args
|
||||
"min" to FunctionSignature(true, listOf(BuiltinFunctionParam("values", ArrayDatatypes)), null) { a, p, _ -> collectionArgNeverConst(a, p) }, // type depends on args
|
||||
"sum" to FunctionSignature(true, listOf(BuiltinFunctionParam("values", ArrayDatatypes)), null) { a, p, _ -> collectionArgNeverConst(a, p) }, // type depends on args
|
||||
"max" to FunctionSignature(true, listOf(BuiltinFunctionParam("values", ArrayDatatypes)), null) { a, p, prg -> collectionArg(a, p, prg, ::builtinMax) }, // type depends on args
|
||||
"min" to FunctionSignature(true, listOf(BuiltinFunctionParam("values", ArrayDatatypes)), null) { a, p, prg -> collectionArg(a, p, prg, ::builtinMin) }, // type depends on args
|
||||
"sum" to FunctionSignature(true, listOf(BuiltinFunctionParam("values", ArrayDatatypes)), null) { a, p, prg -> collectionArg(a, p, prg, ::builtinSum) }, // type depends on args
|
||||
"abs" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", NumericDatatypes)), null, ::builtinAbs), // type depends on argument
|
||||
"len" to FunctionSignature(true, listOf(BuiltinFunctionParam("values", IterableDatatypes)), null, ::builtinLen), // type is UBYTE or UWORD depending on actual length
|
||||
// normal functions follow:
|
||||
@@ -56,8 +58,8 @@ val BuiltinFunctions = mapOf(
|
||||
"round" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArgOutputWord(a, p, prg, Math::round) },
|
||||
"floor" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArgOutputWord(a, p, prg, Math::floor) },
|
||||
"ceil" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArgOutputWord(a, p, prg, Math::ceil) },
|
||||
"any" to FunctionSignature(true, listOf(BuiltinFunctionParam("values", ArrayDatatypes)), DataType.UBYTE) { a, p, _ -> collectionArgNeverConst(a, p) },
|
||||
"all" to FunctionSignature(true, listOf(BuiltinFunctionParam("values", ArrayDatatypes)), DataType.UBYTE) { a, p, _ -> collectionArgNeverConst(a, p) },
|
||||
"any" to FunctionSignature(true, listOf(BuiltinFunctionParam("values", ArrayDatatypes)), DataType.UBYTE) { a, p, prg -> collectionArg(a, p, prg, ::builtinAny) },
|
||||
"all" to FunctionSignature(true, listOf(BuiltinFunctionParam("values", ArrayDatatypes)), DataType.UBYTE) { a, p, prg -> collectionArg(a, p, prg, ::builtinAll) },
|
||||
"lsb" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", setOf(DataType.UWORD, DataType.WORD))), DataType.UBYTE) { a, p, prg -> oneIntArgOutputInt(a, p, prg) { x: Int -> x and 255 }},
|
||||
"msb" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", setOf(DataType.UWORD, DataType.WORD))), DataType.UBYTE) { a, p, prg -> oneIntArgOutputInt(a, p, prg) { x: Int -> x ushr 8 and 255}},
|
||||
"mkword" to FunctionSignature(true, listOf(
|
||||
@@ -75,64 +77,50 @@ val BuiltinFunctions = mapOf(
|
||||
"read_flags" to FunctionSignature(false, emptyList(), DataType.UBYTE),
|
||||
"swap" to FunctionSignature(false, listOf(BuiltinFunctionParam("first", NumericDatatypes), BuiltinFunctionParam("second", NumericDatatypes)), null),
|
||||
"memcopy" to FunctionSignature(false, listOf(
|
||||
BuiltinFunctionParam("from", IterableDatatypes + setOf(DataType.UWORD)),
|
||||
BuiltinFunctionParam("to", IterableDatatypes + setOf(DataType.UWORD)),
|
||||
BuiltinFunctionParam("from", IterableDatatypes + DataType.UWORD),
|
||||
BuiltinFunctionParam("to", IterableDatatypes + DataType.UWORD),
|
||||
BuiltinFunctionParam("numbytes", setOf(DataType.UBYTE))), null),
|
||||
"memset" to FunctionSignature(false, listOf(
|
||||
BuiltinFunctionParam("address", IterableDatatypes + setOf(DataType.UWORD)),
|
||||
BuiltinFunctionParam("address", IterableDatatypes + DataType.UWORD),
|
||||
BuiltinFunctionParam("numbytes", setOf(DataType.UWORD)),
|
||||
BuiltinFunctionParam("bytevalue", ByteDatatypes)), null),
|
||||
"memsetw" to FunctionSignature(false, listOf(
|
||||
BuiltinFunctionParam("address", IterableDatatypes + setOf(DataType.UWORD)),
|
||||
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),
|
||||
"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() }!!
|
||||
|
||||
fun builtinMin(array: List<Number>): Number = array.minBy { it.toDouble() }!!
|
||||
|
||||
fun builtinSum(array: List<Number>): Number = array.sumByDouble { it.toDouble() }
|
||||
|
||||
fun builtinAny(array: List<Number>): Number = if(array.any { it.toDouble()!=0.0 }) 1 else 0
|
||||
|
||||
fun builtinAll(array: List<Number>): Number = if(array.all { it.toDouble()!=0.0 }) 1 else 0
|
||||
|
||||
|
||||
fun builtinFunctionReturnType(function: String, args: List<Expression>, program: Program): InferredTypes.InferredType {
|
||||
|
||||
fun datatypeFromIterableArg(arglist: Expression): DataType {
|
||||
if(arglist is ArrayLiteralValue) {
|
||||
if(arglist.type== DataType.ARRAY_UB || arglist.type== DataType.ARRAY_UW || arglist.type== DataType.ARRAY_F) {
|
||||
val dt = arglist.value.map {it.inferType(program)}
|
||||
if(dt.any { !(it istype DataType.UBYTE) && !(it istype DataType.UWORD) && !(it istype DataType.FLOAT)}) {
|
||||
throw FatalAstException("fuction $function only accepts arraysize of numeric values")
|
||||
}
|
||||
if(dt.any { it istype DataType.FLOAT }) return DataType.FLOAT
|
||||
if(dt.any { it istype DataType.UWORD }) return DataType.UWORD
|
||||
return DataType.UBYTE
|
||||
val dt = arglist.value.map {it.inferType(program).typeOrElse(DataType.STRUCT)}.toSet()
|
||||
if(dt.any { it !in NumericDatatypes }) {
|
||||
throw FatalAstException("fuction $function only accepts array of numeric values")
|
||||
}
|
||||
if(DataType.FLOAT in dt) return DataType.FLOAT
|
||||
if(DataType.UWORD in dt) return DataType.UWORD
|
||||
if(DataType.WORD in dt) return DataType.WORD
|
||||
if(DataType.BYTE in dt) return DataType.BYTE
|
||||
return DataType.UBYTE
|
||||
}
|
||||
if(arglist is IdentifierReference) {
|
||||
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)
|
||||
@@ -185,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 {
|
||||
@@ -215,12 +203,16 @@ private fun oneIntArgOutputInt(args: List<Expression>, position: Position, progr
|
||||
return numericLiteral(function(integer).toInt(), args[0].position)
|
||||
}
|
||||
|
||||
private fun collectionArgNeverConst(args: List<Expression>, position: Position): NumericLiteralValue {
|
||||
private fun collectionArg(args: List<Expression>, position: Position, program: Program, function: (arg: List<Number>)->Number): NumericLiteralValue {
|
||||
if(args.size!=1)
|
||||
throw SyntaxError("builtin function requires one non-scalar argument", position)
|
||||
|
||||
// max/min/sum etc only work on arrays and these are never considered to be const for these functions
|
||||
throw NotConstArgumentException()
|
||||
val array= args[0] as? ArrayLiteralValue ?: throw NotConstArgumentException()
|
||||
val constElements = array.value.map{it.constValue(program)?.number}
|
||||
if(constElements.contains(null))
|
||||
throw NotConstArgumentException()
|
||||
|
||||
return NumericLiteralValue.optimalNumeric(function(constElements.mapNotNull { it }), args[0].position)
|
||||
}
|
||||
|
||||
private fun builtinAbs(args: List<Expression>, position: Position, program: Program): NumericLiteralValue {
|
||||
@@ -258,6 +250,8 @@ private fun builtinLen(args: List<Expression>, position: Position, program: Prog
|
||||
var arraySize = directMemVar?.arraysize?.size()
|
||||
if(arraySize != null)
|
||||
return NumericLiteralValue.optimalInteger(arraySize, position)
|
||||
if(args[0] is ArrayLiteralValue)
|
||||
return NumericLiteralValue.optimalInteger((args[0] as ArrayLiteralValue).value.size, position)
|
||||
if(args[0] !is IdentifierReference)
|
||||
throw SyntaxError("len argument should be an identifier, but is ${args[0]}", position)
|
||||
val target = (args[0] as IdentifierReference).targetVarDecl(program.namespace)!!
|
||||
|
||||
@@ -39,9 +39,11 @@ class ConstantFolding(private val program: Program) : IAstModifyingVisitor {
|
||||
if(decl.isArray){
|
||||
if(decl.arraysize==null) {
|
||||
// for arrays that have no size specifier (or a non-constant one) attempt to deduce the size
|
||||
val arrayval = (decl.value as ArrayLiteralValue).value
|
||||
decl.arraysize = ArrayIndex(NumericLiteralValue.optimalInteger(arrayval.size, decl.position), decl.position)
|
||||
optimizationsDone++
|
||||
val arrayval = decl.value as? ArrayLiteralValue
|
||||
if(arrayval!=null) {
|
||||
decl.arraysize = ArrayIndex(NumericLiteralValue.optimalInteger(arrayval.value.size, decl.position), decl.position)
|
||||
optimizationsDone++
|
||||
}
|
||||
}
|
||||
else if(decl.arraysize?.size()==null) {
|
||||
val size = decl.arraysize!!.index.accept(this)
|
||||
@@ -122,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++
|
||||
@@ -144,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++
|
||||
@@ -165,6 +167,15 @@ class ConstantFolding(private val program: Program) : IAstModifyingVisitor {
|
||||
* replace identifiers that refer to const value, with the value itself (if it's a simple type)
|
||||
*/
|
||||
override fun visit(identifier: IdentifierReference): Expression {
|
||||
// don't replace when it's an assignment target or loop variable
|
||||
if(identifier.parent is AssignTarget)
|
||||
return identifier
|
||||
var forloop = identifier.parent as? ForLoop
|
||||
if(forloop==null)
|
||||
forloop = identifier.parent.parent as? ForLoop
|
||||
if(forloop!=null && identifier===forloop.loopVar)
|
||||
return identifier
|
||||
|
||||
return try {
|
||||
val cval = identifier.constValue(program) ?: return identifier
|
||||
return when {
|
||||
@@ -183,9 +194,9 @@ class ConstantFolding(private val program: Program) : IAstModifyingVisitor {
|
||||
}
|
||||
|
||||
override fun visit(functionCall: FunctionCall): Expression {
|
||||
super.visit(functionCall)
|
||||
typeCastConstArguments(functionCall)
|
||||
return try {
|
||||
super.visit(functionCall)
|
||||
typeCastConstArguments(functionCall)
|
||||
functionCall.constValue(program) ?: functionCall
|
||||
} catch (ax: AstException) {
|
||||
addError(ax)
|
||||
@@ -551,57 +562,91 @@ 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) {
|
||||
array.addToHeap()
|
||||
val vardecl = array.parent as? VarDecl
|
||||
if (vardecl!=null) {
|
||||
return fixupArrayDatatype(array, vardecl, program.heap)
|
||||
return if (vardecl!=null) {
|
||||
fixupArrayDatatype(array, vardecl, program)
|
||||
} else {
|
||||
// it's not an array associated with a vardecl, attempt to guess the data type from the array values
|
||||
fixupArrayDatatype(array, program)
|
||||
}
|
||||
}
|
||||
return array
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -608,15 +608,13 @@ internal class SimplifyExpressions(private val program: Program) : IAstModifying
|
||||
|
||||
}
|
||||
|
||||
private val powersOfTwo = (1 .. 16).map { (2.0).pow(it) }
|
||||
private val negativePowersOfTwo = powersOfTwo.map { -it }
|
||||
private val powersOfTwo = (1 .. 16).map { (2.0).pow(it) }.toSet()
|
||||
private val negativePowersOfTwo = powersOfTwo.map { -it }.toSet()
|
||||
|
||||
private fun optimizeDivision(expr: BinaryExpression, leftVal: NumericLiteralValue?, rightVal: NumericLiteralValue?): Expression {
|
||||
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
|
||||
@@ -712,7 +710,7 @@ internal class SimplifyExpressions(private val program: Program) : IAstModifying
|
||||
optimizationsDone++
|
||||
return expr.left
|
||||
}
|
||||
2.0, 4.0, 8.0, 16.0, 32.0, 64.0, 128.0, 256.0, 512.0, 1024.0, 2048.0, 4096.0, 8192.0, 16384.0, 32768.0, 65536.0 -> {
|
||||
in powersOfTwo -> {
|
||||
if(leftValue.inferType(program).typeOrElse(DataType.STRUCT) in IntegerDatatypes) {
|
||||
// times a power of two => shift left
|
||||
optimizationsDone++
|
||||
@@ -720,7 +718,7 @@ internal class SimplifyExpressions(private val program: Program) : IAstModifying
|
||||
return BinaryExpression(expr.left, "<<", NumericLiteralValue.optimalInteger(numshifts, expr.position), expr.position)
|
||||
}
|
||||
}
|
||||
-2.0, -4.0, -8.0, -16.0, -32.0, -64.0, -128.0, -256.0, -512.0, -1024.0, -2048.0, -4096.0, -8192.0, -16384.0, -32768.0, -65536.0 -> {
|
||||
in negativePowersOfTwo -> {
|
||||
if(leftValue.inferType(program).typeOrElse(DataType.STRUCT) in IntegerDatatypes) {
|
||||
// times a negative power of two => negate, then shift left
|
||||
optimizationsDone++
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
@@ -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,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
|
||||
|
||||
@@ -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)
|
||||
// }
|
||||
|
||||
}
|
||||
@@ -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
@@ -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())
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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.
|
||||
@@ -158,7 +158,7 @@ Design principles and features
|
||||
- The compiler tries to optimize the program and generated code, but hand-tuning of the
|
||||
performance or space-critical parts will likely still be required. This is supported by
|
||||
the ability to easily write embedded assembly code directly in the program source code.
|
||||
- There are many built-in functions such as ``sin``, ``cos``, ``rnd``, ``abs``, ``min``, ``max``, ``sqrt``, ``msb``, ``rol``, ``ror``, ``swap``, ``memset``, ``memcopy``
|
||||
- There are many built-in functions such as ``sin``, ``cos``, ``rnd``, ``abs``, ``min``, ``max``, ``sqrt``, ``msb``, ``rol``, ``ror``, ``swap``, ``memset``, ``memcopy``, ``sort`` and ``reverse``
|
||||
|
||||
|
||||
.. _requirements:
|
||||
|
||||
+24
-22
@@ -395,8 +395,7 @@ Loops
|
||||
-----
|
||||
|
||||
The *for*-loop is used to let a variable (or register) iterate over a range of values. Iteration is done in steps of 1, but you can change this.
|
||||
The loop variable can be declared as byte or word earlier so you can reuse it for multiple occasions,
|
||||
or you can declare one directly in the for statement which will only be visible in the for loop body.
|
||||
The loop variable must be declared as byte or word earlier so you can reuse it for multiple occasions.
|
||||
Iterating with a floating point variable is not supported. If you want to loop over a floating-point array, use a loop with an integer index variable instead.
|
||||
|
||||
The *while*-loop is used to repeat a piece of code while a certain condition is still true.
|
||||
@@ -408,9 +407,6 @@ You can also create loops by using the ``goto`` statement, but this should usual
|
||||
The value of the loop variable or register after executing the loop *is undefined*. Don't use it immediately
|
||||
after the loop without first assigning a new value to it!
|
||||
(this is an optimization issue to avoid having to deal with mostly useless post-loop logic to adjust the loop variable's value)
|
||||
Loop variables that are declared inline are not different to them being
|
||||
defined in a separate var declaration in the subroutine, it's just a readability convenience.
|
||||
(this may change in the future if the compiler gets more advanced with additional sub-scopes)
|
||||
|
||||
|
||||
Conditional Execution
|
||||
@@ -663,52 +659,58 @@ cos16u(x)
|
||||
Fast 16-bit uword cosine of angle 0..255, result is in range 0..65535
|
||||
|
||||
cos16(x)
|
||||
Fast 16-bit word cosine of angle 0..255, result is in range -32767..32767
|
||||
Fast 16-bit word cosine of angle 0..255, result is in range -32767..32767
|
||||
|
||||
abs(x)
|
||||
Absolute value.
|
||||
Absolute value.
|
||||
|
||||
tan(x)
|
||||
Tangent.
|
||||
Tangent.
|
||||
|
||||
atan(x)
|
||||
Arctangent.
|
||||
Arctangent.
|
||||
|
||||
ln(x)
|
||||
Natural logarithm (base e).
|
||||
Natural logarithm (base e).
|
||||
|
||||
log2(x)
|
||||
Base 2 logarithm.
|
||||
|
||||
sqrt16(w)
|
||||
16 bit unsigned integer Square root. Result is unsigned byte.
|
||||
16 bit unsigned integer Square root. Result is unsigned byte.
|
||||
|
||||
sqrt(x)
|
||||
Floating point Square root.
|
||||
Floating point Square root.
|
||||
|
||||
round(x)
|
||||
Rounds the floating point to the closest integer.
|
||||
Rounds the floating point to the closest integer.
|
||||
|
||||
floor (x)
|
||||
Rounds the floating point down to an integer towards minus infinity.
|
||||
Rounds the floating point down to an integer towards minus infinity.
|
||||
|
||||
ceil(x)
|
||||
Rounds the floating point up to an integer towards positive infinity.
|
||||
Rounds the floating point up to an integer towards positive infinity.
|
||||
|
||||
rad(x)
|
||||
Degrees to radians.
|
||||
Degrees to radians.
|
||||
|
||||
deg(x)
|
||||
Radians to degrees.
|
||||
Radians to degrees.
|
||||
|
||||
max(x)
|
||||
Maximum of the values in the array value x
|
||||
Maximum of the values in the array value x
|
||||
|
||||
min(x)
|
||||
Minimum of the values in the array value x
|
||||
Minimum of the values in the array value x
|
||||
|
||||
sum(x)
|
||||
Sum of the values in the array value x
|
||||
Sum of the values in the array value x
|
||||
|
||||
sort(array)
|
||||
Sort the array in ascending order (in-place)
|
||||
|
||||
reverse(array)
|
||||
Reverse the values in the array (in-place). Can be used after sort() to sort an array in descending order.
|
||||
|
||||
len(x)
|
||||
Number of values in the array value x, or the number of characters in a string (excluding the size or 0-byte).
|
||||
@@ -766,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).
|
||||
|
||||
@@ -778,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).
|
||||
|
||||
|
||||
@@ -517,42 +517,39 @@ Loops
|
||||
for loop
|
||||
^^^^^^^^
|
||||
|
||||
The loop variable must be a register or a byte/word variable. It must be defined in the local scope (to reuse
|
||||
an existing variable), or you can declare it in the for loop directly to make a new one that is only visible
|
||||
in the body of the for loop.
|
||||
The loop variable must be a register or a byte/word variable,
|
||||
and must be defined first in the local scope of the for loop.
|
||||
The expression that you loop over can be anything that supports iteration (such as ranges like ``0 to 100``,
|
||||
array variables and strings) *except* floating-point arrays (because a floating-point loop variable is not supported).
|
||||
|
||||
You can use a single statement, or a statement block like in the example below::
|
||||
|
||||
for [byte | word] <loopvar> in <expression> [ step <amount> ] {
|
||||
for <loopvar> in <expression> [ step <amount> ] {
|
||||
; do something...
|
||||
break ; break out of the loop
|
||||
continue ; immediately enter next iteration
|
||||
}
|
||||
|
||||
For example, this is a for loop using the existing byte variable ``i`` to loop over a certain range of numbers::
|
||||
For example, this is a for loop using a byte variable ``i``, defined before, to loop over a certain range of numbers::
|
||||
|
||||
ubyte i
|
||||
|
||||
...
|
||||
|
||||
for i in 20 to 155 {
|
||||
; do something
|
||||
}
|
||||
|
||||
And this is a loop over the values of the array ``fibonacci_numbers`` where the loop variable is declared in the loop itself::
|
||||
And this is a loop over the values of the array ``fibonacci_numbers``::
|
||||
|
||||
word[] fibonacci_numbers = [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181]
|
||||
uword[] fibonacci_numbers = [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181]
|
||||
|
||||
for word fibnr in fibonacci_numbers {
|
||||
; do something
|
||||
uword number
|
||||
for number in fibonacci_numbers {
|
||||
; do something with number
|
||||
}
|
||||
|
||||
|
||||
You can inline the loop variable declaration in the for statement, including optional zp-tag. In this case
|
||||
the variable is not visible outside of the for loop::
|
||||
|
||||
for ubyte @zp fastindex in 10 to 20 {
|
||||
; do something
|
||||
}
|
||||
|
||||
|
||||
while loop
|
||||
^^^^^^^^^^
|
||||
|
||||
+12
-11
@@ -5,19 +5,18 @@ TODO
|
||||
Memory Block Operations integrated in language?
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
list,string memory block operations?
|
||||
array/string memory block operations?
|
||||
|
||||
- list operations (whole list, individual element)
|
||||
operations: set, get, copy (from another list with the same length), shift-N(left,right), rotate-N(left,right)
|
||||
clear (set whole list to the given value, default 0)
|
||||
- array operations
|
||||
copy (from another array with the same length), shift-N(left,right), rotate-N(left,right)
|
||||
clear (set whole array to the given value, default 0)
|
||||
|
||||
- list operations ofcourse work identical on vars and on memory mapped vars of these types.
|
||||
- array operations ofcourse work identical on vars and on memory mapped vars of these types.
|
||||
|
||||
- strings: identical operations as on lists.
|
||||
- strings: identical operations as on array.
|
||||
|
||||
these should call optimized pieces of assembly code, so they run as fast as possible
|
||||
For now, we have the ``memcopy`` and ``memset`` builtin functions.
|
||||
|
||||
For now, we have the ``memcopy``, ``memset`` and ``strlen`` builtin functions.
|
||||
|
||||
More optimizations
|
||||
^^^^^^^^^^^^^^^^^^
|
||||
@@ -45,12 +44,14 @@ 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
|
||||
^^^^
|
||||
|
||||
Add sort() function that can sort an array (ascending and descending)
|
||||
|
||||
|
||||
Several ideas were discussed on my reddit post
|
||||
https://www.reddit.com/r/programming/comments/alhj59/creating_a_programming_language_and_cross/
|
||||
|
||||
@@ -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
|
||||
|
||||
+4
-2
@@ -17,7 +17,8 @@ sub start() {
|
||||
c64.CLEARSCR()
|
||||
|
||||
while(true) {
|
||||
for uword note in notes {
|
||||
uword note
|
||||
for note in notes {
|
||||
ubyte note1 = lsb(note)
|
||||
ubyte note2 = msb(note)
|
||||
c64.FREQ1 = music_freq_table[note1] ; set lo+hi freq of voice 1
|
||||
@@ -35,7 +36,8 @@ sub start() {
|
||||
}
|
||||
|
||||
sub delay() {
|
||||
for ubyte d in 0 to 12 {
|
||||
ubyte d
|
||||
for d in 0 to 12 {
|
||||
while(c64.RASTER!=0) {
|
||||
; tempo delay synced to screen refresh
|
||||
}
|
||||
|
||||
@@ -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.
@@ -55,7 +55,8 @@ main {
|
||||
float Azy = cosb*sinc
|
||||
float Azz = cosb*cosc
|
||||
|
||||
for ubyte i in 0 to len(xcoor)-1 {
|
||||
ubyte i
|
||||
for i in 0 to len(xcoor)-1 {
|
||||
rotatedx[i] = Axx*xcoor[i] + Axy*ycoor[i] + Axz*zcoor[i]
|
||||
rotatedy[i] = Ayx*xcoor[i] + Ayy*ycoor[i] + Ayz*zcoor[i]
|
||||
rotatedz[i] = Azx*xcoor[i] + Azy*ycoor[i] + Azz*zcoor[i]
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
%import c64lib
|
||||
%import c64utils
|
||||
|
||||
; TODO: some optimizer breaks this.. runs fine without optimization
|
||||
|
||||
spritedata $2000 {
|
||||
; this memory block contains the sprite data
|
||||
; it must start on an address aligned to 64 bytes.
|
||||
@@ -122,7 +120,8 @@ main {
|
||||
word Azy = wcosb*wsinc / 128
|
||||
word Azz = wcosb*wcosc / 128
|
||||
|
||||
for ubyte i in 0 to len(xcoor)-1 {
|
||||
ubyte i
|
||||
for i in 0 to len(xcoor)-1 {
|
||||
; don't normalize by dividing by 128, instead keep some precision for perspective calc later
|
||||
rotatedx[i] = (Axx*xcoor[i] + Axy*ycoor[i] + Axz*zcoor[i])
|
||||
rotatedy[i] = (Ayx*xcoor[i] + Ayy*ycoor[i] + Ayz*zcoor[i])
|
||||
@@ -137,9 +136,10 @@ main {
|
||||
|
||||
; first sort vertices to sprite order so the back/front order is correct as well
|
||||
; (simple bubble sort as it's only 8 items to sort)
|
||||
; TODO make a builtin function sort()
|
||||
for ubyte sorti in 6 to 0 step -1 {
|
||||
for ubyte i1 in 0 to sorti {
|
||||
ubyte i
|
||||
ubyte i1
|
||||
for i in 6 to 0 step -1 {
|
||||
for i1 in 0 to i {
|
||||
ubyte i2 = i1+1
|
||||
if(rotatedz[i1] > rotatedz[i2]) {
|
||||
swap(rotatedx[i1], rotatedx[i2])
|
||||
@@ -151,7 +151,7 @@ main {
|
||||
|
||||
ubyte[] spritecolors = [1,1,7,15,12,11,9,9]
|
||||
|
||||
for ubyte i in 0 to 7 {
|
||||
for i in 0 to 7 {
|
||||
word zc = rotatedz[i]
|
||||
word persp = 300+zc/256
|
||||
ubyte sx = rotatedx[i] / persp + width/2 as ubyte + 20
|
||||
|
||||
+2
-2
@@ -61,7 +61,8 @@ main {
|
||||
word Azy = wcosb*wsinc / 128
|
||||
word Azz = wcosb*wcosc / 128
|
||||
|
||||
for ubyte i in 0 to len(xcoor)-1 {
|
||||
ubyte i
|
||||
for i in 0 to len(xcoor)-1 {
|
||||
; don't normalize by dividing by 128, instead keep some precision for perspective calc later
|
||||
rotatedx[i] = (Axx*xcoor[i] + Axy*ycoor[i] + Axz*zcoor[i])
|
||||
rotatedy[i] = (Ayx*xcoor[i] + Ayy*ycoor[i] + Ayz*zcoor[i])
|
||||
@@ -79,7 +80,6 @@ main {
|
||||
word persp
|
||||
byte sx
|
||||
byte sy
|
||||
ubyte color
|
||||
|
||||
for i in 0 to len(xcoor)-1 {
|
||||
rz = rotatedz[i]
|
||||
|
||||
+4
-3
@@ -17,13 +17,14 @@ main {
|
||||
|
||||
; use iteration to write text
|
||||
str question = "How are you?\n"
|
||||
for ubyte char in question
|
||||
ubyte char
|
||||
for char in question
|
||||
c64.CHROUT(char)
|
||||
|
||||
; use indexed loop to write characters
|
||||
str bye = "Goodbye!\n"
|
||||
for ubyte c in 0 to len(bye)
|
||||
c64.CHROUT(bye[c])
|
||||
for char in 0 to len(bye)
|
||||
c64.CHROUT(bye[char])
|
||||
|
||||
|
||||
float clock_seconds = ((mkword(c64.TIME_LO, c64.TIME_MID) as float) + (c64.TIME_HI as float)*65536.0) / 60
|
||||
|
||||
@@ -16,10 +16,13 @@ main {
|
||||
c64.TIME_MID=0
|
||||
c64.TIME_LO=0
|
||||
|
||||
for ubyte pixely in 0 to height-1 {
|
||||
ubyte pixelx
|
||||
ubyte pixely
|
||||
|
||||
for pixely in 0 to height-1 {
|
||||
float yy = (pixely as float)/0.4/height - 1.0
|
||||
|
||||
for ubyte pixelx in 0 to width-1 {
|
||||
for pixelx in 0 to width-1 {
|
||||
float xx = (pixelx as float)/0.3/width - 2.2
|
||||
|
||||
float xsquared = 0.0
|
||||
|
||||
@@ -10,6 +10,7 @@ main {
|
||||
str name = "????????????????????????????????????????"
|
||||
str input = "??????????"
|
||||
ubyte secretnumber = rnd() % 99 + 1 ; random number 1..100
|
||||
ubyte attempts_left
|
||||
|
||||
c64.VMCSB |= 2 ; switch lowercase chars
|
||||
c64scr.print("Please introduce yourself: ")
|
||||
@@ -18,7 +19,7 @@ main {
|
||||
c64scr.print(name)
|
||||
c64scr.print(".\nLet's play a number guessing game.\nI am thinking of a number from 1 to 100!You'll have to guess it!\n")
|
||||
|
||||
for ubyte attempts_left in 10 to 1 step -1 {
|
||||
for attempts_left in 10 to 1 step -1 {
|
||||
|
||||
c64scr.print("\nYou have ")
|
||||
c64scr.print_ub(attempts_left)
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
%import c64lib
|
||||
%import c64utils
|
||||
%zeropage basicsafe
|
||||
|
||||
main {
|
||||
|
||||
sub start() {
|
||||
|
||||
ubyte[] uba = [10,0,2,8,5,4,3,9]
|
||||
uword[] uwa = [1000,0,200,8000,50,40000,3,900]
|
||||
byte[] ba = [-10,0,-2,8,5,4,-3,9,-99]
|
||||
word[] wa = [-1000,0,-200,8000,50,31111,3,-900]
|
||||
|
||||
c64scr.print("original\n")
|
||||
print_arrays()
|
||||
|
||||
sort(uba)
|
||||
sort(uwa)
|
||||
sort(ba)
|
||||
sort(wa)
|
||||
|
||||
c64scr.print("sorted\n")
|
||||
print_arrays()
|
||||
|
||||
reverse(uba)
|
||||
reverse(uwa)
|
||||
reverse(ba)
|
||||
reverse(wa)
|
||||
|
||||
c64scr.print("reversed\n")
|
||||
print_arrays()
|
||||
return
|
||||
|
||||
|
||||
sub print_arrays() {
|
||||
ubyte ub
|
||||
uword uw
|
||||
byte bb
|
||||
word ww
|
||||
for ub in uba {
|
||||
c64scr.print_ub(ub)
|
||||
c64.CHROUT(',')
|
||||
}
|
||||
c64.CHROUT('\n')
|
||||
|
||||
for uw in uwa {
|
||||
c64scr.print_uw(uw)
|
||||
c64.CHROUT(',')
|
||||
}
|
||||
c64.CHROUT('\n')
|
||||
|
||||
for bb in ba {
|
||||
c64scr.print_b(bb)
|
||||
c64.CHROUT(',')
|
||||
}
|
||||
c64.CHROUT('\n')
|
||||
|
||||
for ww in wa {
|
||||
c64scr.print_w(ww)
|
||||
c64.CHROUT(',')
|
||||
}
|
||||
c64.CHROUT('\n')
|
||||
c64.CHROUT('\n')
|
||||
}
|
||||
}
|
||||
}
|
||||
+4
-2
@@ -40,7 +40,8 @@ main {
|
||||
|
||||
c64scr.print("balloon sprites!\n...we are all floating...\n")
|
||||
|
||||
for ubyte i in 0 to 7 {
|
||||
ubyte @zp i
|
||||
for i in 0 to 7 {
|
||||
c64.SPRPTR[i] = $0a00 / 64
|
||||
c64.SPXY[i*2] = 50+25*i
|
||||
c64.SPXY[i*2+1] = rnd()
|
||||
@@ -58,7 +59,8 @@ irq {
|
||||
c64.EXTCOL--
|
||||
|
||||
; float up & wobble horizontally
|
||||
for ubyte @zp i in 0 to 14 step 2 {
|
||||
ubyte @zp i
|
||||
for i in 0 to 14 step 2 {
|
||||
c64.SPXY[i+1]--
|
||||
ubyte @zp r = rnd()
|
||||
if r>200
|
||||
|
||||
+16
-9
@@ -9,7 +9,6 @@
|
||||
;
|
||||
; @todo show ghost?
|
||||
|
||||
|
||||
main {
|
||||
|
||||
const ubyte boardOffsetX = 14
|
||||
@@ -195,7 +194,8 @@ waitkey:
|
||||
if blocklogic.isLineFull(linepos) {
|
||||
complete_lines[num_lines]=linepos
|
||||
num_lines++
|
||||
for ubyte x in boardOffsetX to boardOffsetX+boardWidth-1
|
||||
ubyte x
|
||||
for x in boardOffsetX to boardOffsetX+boardWidth-1
|
||||
c64scr.setcc(x, linepos, 160, 1)
|
||||
}
|
||||
}
|
||||
@@ -330,7 +330,8 @@ waitkey:
|
||||
|
||||
ubyte[] colors = [6,8,7,5,4]
|
||||
for i in len(colors)-1 to 0 step -1 {
|
||||
for ubyte x in 5 to 0 step -1 {
|
||||
ubyte x
|
||||
for x in 5 to 0 step -1 {
|
||||
c64scr.setcc(6+x-i, 11+2*i, 102, colors[i])
|
||||
}
|
||||
}
|
||||
@@ -350,7 +351,8 @@ waitkey:
|
||||
sub drawNextBlock() {
|
||||
const ubyte nextBlockXpos = 29
|
||||
const ubyte nextBlockYpos = 5
|
||||
for ubyte x in nextBlockXpos+3 to nextBlockXpos step -1 {
|
||||
ubyte x
|
||||
for x in nextBlockXpos+3 to nextBlockXpos step -1 {
|
||||
c64scr.setcc(x, nextBlockYpos, ' ', 0)
|
||||
c64scr.setcc(x, nextBlockYpos+1, ' ', 0)
|
||||
}
|
||||
@@ -365,7 +367,8 @@ waitkey:
|
||||
sub drawHoldBlock() {
|
||||
const ubyte holdBlockXpos = 7
|
||||
const ubyte holdBlockYpos = 6
|
||||
for ubyte x in holdBlockXpos+3 to holdBlockXpos step -1 {
|
||||
ubyte x
|
||||
for x in holdBlockXpos+3 to holdBlockXpos step -1 {
|
||||
c64scr.setcc(x, holdBlockYpos, '@', 0)
|
||||
c64scr.setcc(x, holdBlockYpos+1, '@', 0)
|
||||
}
|
||||
@@ -379,7 +382,8 @@ waitkey:
|
||||
}
|
||||
|
||||
sub drawBlock(ubyte x, ubyte y, ubyte character) {
|
||||
for ubyte i in 15 to 0 step -1 {
|
||||
ubyte i
|
||||
for i in 15 to 0 step -1 {
|
||||
ubyte c=blocklogic.currentBlock[i]
|
||||
if c
|
||||
c64scr.setcc((i&3)+x, (i/4)+y, character, c)
|
||||
@@ -527,7 +531,8 @@ blocklogic {
|
||||
}
|
||||
|
||||
sub noCollision(ubyte xpos, ubyte ypos) -> ubyte {
|
||||
for ubyte i in 15 to 0 step -1 {
|
||||
ubyte i
|
||||
for i in 15 to 0 step -1 {
|
||||
if currentBlock[i] and c64scr.getchr(xpos + (i&3), ypos+i/4)!=32
|
||||
return false
|
||||
}
|
||||
@@ -542,7 +547,8 @@ blocklogic {
|
||||
}
|
||||
|
||||
sub isLineFull(ubyte ypos) -> ubyte {
|
||||
for ubyte x in main.boardOffsetX to main.boardOffsetX+main.boardWidth-1 {
|
||||
ubyte x
|
||||
for x in main.boardOffsetX to main.boardOffsetX+main.boardWidth-1 {
|
||||
if c64scr.getchr(x, ypos)==32
|
||||
return false
|
||||
}
|
||||
@@ -551,7 +557,8 @@ blocklogic {
|
||||
|
||||
sub collapse(ubyte ypos) {
|
||||
while(ypos>main.startYpos+1) {
|
||||
for ubyte x in main.boardOffsetX+main.boardWidth-1 to main.boardOffsetX step -1 {
|
||||
ubyte x
|
||||
for x in main.boardOffsetX+main.boardWidth-1 to main.boardOffsetX step -1 {
|
||||
ubyte char = c64scr.getchr(x, ypos-1)
|
||||
ubyte color = c64scr.getclr(x, ypos-1)
|
||||
c64scr.setcc(x, ypos, char, color)
|
||||
|
||||
+42
-73
@@ -1,83 +1,52 @@
|
||||
%import c64lib
|
||||
%import c64utils
|
||||
%import c64flt
|
||||
%zeropage dontuse
|
||||
%zeropage basicsafe
|
||||
|
||||
main {
|
||||
|
||||
sub start() {
|
||||
byte ub = 100
|
||||
byte ub2
|
||||
word uw = 22222
|
||||
word uw2
|
||||
|
||||
ub = -100
|
||||
c64scr.print_b(ub >> 1)
|
||||
c64.CHROUT('\n')
|
||||
c64scr.print_b(ub >> 2)
|
||||
c64.CHROUT('\n')
|
||||
c64scr.print_b(ub >> 7)
|
||||
c64.CHROUT('\n')
|
||||
c64scr.print_b(ub >> 8)
|
||||
c64.CHROUT('\n')
|
||||
c64scr.print_b(ub >> 9)
|
||||
c64.CHROUT('\n')
|
||||
c64scr.print_b(ub >> 16)
|
||||
c64.CHROUT('\n')
|
||||
c64scr.print_b(ub >> 26)
|
||||
c64.CHROUT('\n')
|
||||
c64.CHROUT('\n')
|
||||
|
||||
ub = 100
|
||||
c64scr.print_b(ub >> 1)
|
||||
c64.CHROUT('\n')
|
||||
c64scr.print_b(ub >> 2)
|
||||
c64.CHROUT('\n')
|
||||
c64scr.print_b(ub >> 7)
|
||||
c64.CHROUT('\n')
|
||||
c64scr.print_b(ub >> 8)
|
||||
c64.CHROUT('\n')
|
||||
c64scr.print_b(ub >> 9)
|
||||
c64.CHROUT('\n')
|
||||
c64scr.print_b(ub >> 16)
|
||||
c64.CHROUT('\n')
|
||||
c64scr.print_b(ub >> 26)
|
||||
c64.CHROUT('\n')
|
||||
c64.CHROUT('\n')
|
||||
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
|
||||
|
||||
|
||||
uw = -22222
|
||||
c64scr.print_w(uw >> 1)
|
||||
c64.CHROUT('\n')
|
||||
c64scr.print_w(uw >> 7)
|
||||
c64.CHROUT('\n')
|
||||
c64scr.print_w(uw >> 8)
|
||||
c64.CHROUT('\n')
|
||||
c64scr.print_w(uw >> 9)
|
||||
c64.CHROUT('\n')
|
||||
c64scr.print_w(uw >> 15)
|
||||
c64.CHROUT('\n')
|
||||
c64scr.print_w(uw >> 16)
|
||||
c64.CHROUT('\n')
|
||||
c64scr.print_w(uw >> 26)
|
||||
c64.CHROUT('\n')
|
||||
c64.CHROUT('\n')
|
||||
; ---------- BYTE var ---------
|
||||
|
||||
uw = 22222
|
||||
c64scr.print_w(uw >> 1)
|
||||
c64.CHROUT('\n')
|
||||
c64scr.print_w(uw >> 7)
|
||||
c64.CHROUT('\n')
|
||||
c64scr.print_w(uw >> 8)
|
||||
c64.CHROUT('\n')
|
||||
c64scr.print_w(uw >> 9)
|
||||
c64.CHROUT('\n')
|
||||
c64scr.print_w(uw >> 15)
|
||||
c64.CHROUT('\n')
|
||||
c64scr.print_w(uw >> 16)
|
||||
c64.CHROUT('\n')
|
||||
c64scr.print_w(uw >> 26)
|
||||
c64.CHROUT('\n')
|
||||
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")
|
||||
|
||||
|
||||
; ---------- 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")
|
||||
|
||||
}
|
||||
}
|
||||
@@ -2,10 +2,6 @@
|
||||
%zeropage basicsafe
|
||||
%option enable_floats
|
||||
|
||||
; TODO complete codegeneration for all lines in this
|
||||
|
||||
|
||||
|
||||
main {
|
||||
|
||||
sub start() {
|
||||
@@ -34,8 +30,6 @@ main {
|
||||
float fl
|
||||
|
||||
; read array
|
||||
@($d020) = ub
|
||||
|
||||
A=s1[2]
|
||||
ub=s1[2]
|
||||
ub=s2[2]
|
||||
|
||||
+921
-85
File diff suppressed because it is too large.
Load diff
@@ -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')
|
||||
|
||||
}
|
||||
}
|
||||
+8
-6
@@ -34,8 +34,8 @@ spritedata $0a00 {
|
||||
main {
|
||||
|
||||
sub start() {
|
||||
|
||||
for ubyte i in 0 to 7 {
|
||||
ubyte i
|
||||
for i in 0 to 7 {
|
||||
c64.SPRPTR[i] = $0a00/64
|
||||
}
|
||||
c64.SPENA = 255 ; enable all sprites
|
||||
@@ -48,16 +48,18 @@ irq {
|
||||
|
||||
sub irq() {
|
||||
ubyte angle ; no initialization value so it keeps the previous one.
|
||||
ubyte @zp spri
|
||||
|
||||
c64.EXTCOL--
|
||||
|
||||
angle++
|
||||
c64.MSIGX=0
|
||||
|
||||
for ubyte @zp i in 7 to 0 step -1 {
|
||||
uword @zp x = sin8u(angle*2-i*16) as uword + 50
|
||||
ubyte @zp y = cos8u(angle*3-i*16) / 2 + 70
|
||||
c64.SPXYW[i] = mkword(lsb(x), y)
|
||||
|
||||
for spri in 7 to 0 step -1 {
|
||||
uword @zp x = sin8u(angle*2-spri*16) as uword + 50
|
||||
ubyte @zp y = cos8u(angle*3-spri*16) / 2 + 70
|
||||
c64.SPXYW[spri] = mkword(lsb(x), y)
|
||||
lsl(c64.MSIGX)
|
||||
if msb(x) c64.MSIGX++
|
||||
c64.EXTCOL++
|
||||
|
||||
+1
-1
@@ -4,4 +4,4 @@ org.gradle.parallel=true
|
||||
org.gradle.jvmargs=-Xmx2500M
|
||||
org.gradle.daemon=true
|
||||
|
||||
kotlinVersion=1.3.41
|
||||
kotlinVersion=1.3.50
|
||||
@@ -285,7 +285,7 @@ branch_stmt : branchcondition EOL? (statement | statement_block) EOL? else_part?
|
||||
branchcondition: 'if_cs' | 'if_cc' | 'if_eq' | 'if_z' | 'if_ne' | 'if_nz' | 'if_pl' | 'if_pos' | 'if_mi' | 'if_neg' | 'if_vs' | 'if_vc' ;
|
||||
|
||||
|
||||
forloop : 'for' datatype? ZEROPAGE? (register | identifier) 'in' expression EOL? (statement | statement_block) ;
|
||||
forloop : 'for' (register | identifier) 'in' expression EOL? (statement | statement_block) ;
|
||||
|
||||
whileloop: 'while' expression EOL? (statement | statement_block) ;
|
||||
|
||||
|
||||
@@ -3,6 +3,9 @@ plugins {
|
||||
id 'java'
|
||||
}
|
||||
|
||||
targetCompatibility = 1.8
|
||||
sourceCompatibility = 1.8
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
@@ -4586,10 +4586,6 @@ public class prog8Parser extends Parser {
|
||||
public Statement_blockContext statement_block() {
|
||||
return getRuleContext(Statement_blockContext.class,0);
|
||||
}
|
||||
public DatatypeContext datatype() {
|
||||
return getRuleContext(DatatypeContext.class,0);
|
||||
}
|
||||
public TerminalNode ZEROPAGE() { return getToken(prog8Parser.ZEROPAGE, 0); }
|
||||
public TerminalNode EOL() { return getToken(prog8Parser.EOL, 0); }
|
||||
public ForloopContext(ParserRuleContext parent, int invokingState) {
|
||||
super(parent, invokingState);
|
||||
@@ -4606,61 +4602,41 @@ public class prog8Parser extends Parser {
|
||||
{
|
||||
setState(715);
|
||||
match(T__104);
|
||||
setState(717);
|
||||
_errHandler.sync(this);
|
||||
_la = _input.LA(1);
|
||||
if ((((_la) & ~0x3f) == 0 && ((1L << _la) & ((1L << T__18) | (1L << T__19) | (1L << T__20) | (1L << T__21) | (1L << T__22) | (1L << T__23) | (1L << T__24))) != 0)) {
|
||||
{
|
||||
setState(716);
|
||||
datatype();
|
||||
}
|
||||
}
|
||||
|
||||
setState(720);
|
||||
_errHandler.sync(this);
|
||||
_la = _input.LA(1);
|
||||
if (_la==ZEROPAGE) {
|
||||
{
|
||||
setState(719);
|
||||
match(ZEROPAGE);
|
||||
}
|
||||
}
|
||||
|
||||
setState(724);
|
||||
setState(718);
|
||||
_errHandler.sync(this);
|
||||
switch (_input.LA(1)) {
|
||||
case T__71:
|
||||
case T__72:
|
||||
case T__73:
|
||||
{
|
||||
setState(722);
|
||||
setState(716);
|
||||
register();
|
||||
}
|
||||
break;
|
||||
case NAME:
|
||||
{
|
||||
setState(723);
|
||||
setState(717);
|
||||
identifier();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new NoViableAltException(this);
|
||||
}
|
||||
setState(726);
|
||||
setState(720);
|
||||
match(T__105);
|
||||
setState(727);
|
||||
setState(721);
|
||||
expression(0);
|
||||
setState(729);
|
||||
setState(723);
|
||||
_errHandler.sync(this);
|
||||
_la = _input.LA(1);
|
||||
if (_la==EOL) {
|
||||
{
|
||||
setState(728);
|
||||
setState(722);
|
||||
match(EOL);
|
||||
}
|
||||
}
|
||||
|
||||
setState(733);
|
||||
setState(727);
|
||||
_errHandler.sync(this);
|
||||
switch (_input.LA(1)) {
|
||||
case T__1:
|
||||
@@ -4713,13 +4689,13 @@ public class prog8Parser extends Parser {
|
||||
case NAME:
|
||||
case ADDRESS_OF:
|
||||
{
|
||||
setState(731);
|
||||
setState(725);
|
||||
statement();
|
||||
}
|
||||
break;
|
||||
case T__16:
|
||||
{
|
||||
setState(732);
|
||||
setState(726);
|
||||
statement_block();
|
||||
}
|
||||
break;
|
||||
@@ -4763,21 +4739,21 @@ public class prog8Parser extends Parser {
|
||||
try {
|
||||
enterOuterAlt(_localctx, 1);
|
||||
{
|
||||
setState(735);
|
||||
setState(729);
|
||||
match(T__106);
|
||||
setState(736);
|
||||
setState(730);
|
||||
expression(0);
|
||||
setState(738);
|
||||
setState(732);
|
||||
_errHandler.sync(this);
|
||||
_la = _input.LA(1);
|
||||
if (_la==EOL) {
|
||||
{
|
||||
setState(737);
|
||||
setState(731);
|
||||
match(EOL);
|
||||
}
|
||||
}
|
||||
|
||||
setState(742);
|
||||
setState(736);
|
||||
_errHandler.sync(this);
|
||||
switch (_input.LA(1)) {
|
||||
case T__1:
|
||||
@@ -4830,13 +4806,13 @@ public class prog8Parser extends Parser {
|
||||
case NAME:
|
||||
case ADDRESS_OF:
|
||||
{
|
||||
setState(740);
|
||||
setState(734);
|
||||
statement();
|
||||
}
|
||||
break;
|
||||
case T__16:
|
||||
{
|
||||
setState(741);
|
||||
setState(735);
|
||||
statement_block();
|
||||
}
|
||||
break;
|
||||
@@ -4880,9 +4856,9 @@ public class prog8Parser extends Parser {
|
||||
try {
|
||||
enterOuterAlt(_localctx, 1);
|
||||
{
|
||||
setState(744);
|
||||
setState(738);
|
||||
match(T__107);
|
||||
setState(747);
|
||||
setState(741);
|
||||
_errHandler.sync(this);
|
||||
switch (_input.LA(1)) {
|
||||
case T__1:
|
||||
@@ -4935,32 +4911,32 @@ public class prog8Parser extends Parser {
|
||||
case NAME:
|
||||
case ADDRESS_OF:
|
||||
{
|
||||
setState(745);
|
||||
setState(739);
|
||||
statement();
|
||||
}
|
||||
break;
|
||||
case T__16:
|
||||
{
|
||||
setState(746);
|
||||
setState(740);
|
||||
statement_block();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new NoViableAltException(this);
|
||||
}
|
||||
setState(750);
|
||||
setState(744);
|
||||
_errHandler.sync(this);
|
||||
_la = _input.LA(1);
|
||||
if (_la==EOL) {
|
||||
{
|
||||
setState(749);
|
||||
setState(743);
|
||||
match(EOL);
|
||||
}
|
||||
}
|
||||
|
||||
setState(752);
|
||||
setState(746);
|
||||
match(T__108);
|
||||
setState(753);
|
||||
setState(747);
|
||||
expression(0);
|
||||
}
|
||||
}
|
||||
@@ -5002,20 +4978,20 @@ public class prog8Parser extends Parser {
|
||||
try {
|
||||
enterOuterAlt(_localctx, 1);
|
||||
{
|
||||
setState(755);
|
||||
setState(749);
|
||||
match(T__109);
|
||||
setState(756);
|
||||
setState(750);
|
||||
expression(0);
|
||||
setState(757);
|
||||
setState(751);
|
||||
match(T__16);
|
||||
setState(758);
|
||||
setState(752);
|
||||
match(EOL);
|
||||
setState(763);
|
||||
setState(757);
|
||||
_errHandler.sync(this);
|
||||
_la = _input.LA(1);
|
||||
while ((((_la) & ~0x3f) == 0 && ((1L << _la) & ((1L << T__16) | (1L << T__25) | (1L << T__40) | (1L << T__41) | (1L << T__42) | (1L << T__62))) != 0) || ((((_la - 64)) & ~0x3f) == 0 && ((1L << (_la - 64)) & ((1L << (T__63 - 64)) | (1L << (T__66 - 64)) | (1L << (T__71 - 64)) | (1L << (T__72 - 64)) | (1L << (T__73 - 64)) | (1L << (T__82 - 64)) | (1L << (T__83 - 64)) | (1L << (T__91 - 64)) | (1L << (EOL - 64)) | (1L << (NAME - 64)) | (1L << (DEC_INTEGER - 64)) | (1L << (HEX_INTEGER - 64)) | (1L << (BIN_INTEGER - 64)) | (1L << (ADDRESS_OF - 64)) | (1L << (FLOAT_NUMBER - 64)) | (1L << (STRING - 64)) | (1L << (SINGLECHAR - 64)))) != 0)) {
|
||||
{
|
||||
setState(761);
|
||||
setState(755);
|
||||
_errHandler.sync(this);
|
||||
switch (_input.LA(1)) {
|
||||
case T__16:
|
||||
@@ -5041,13 +5017,13 @@ public class prog8Parser extends Parser {
|
||||
case STRING:
|
||||
case SINGLECHAR:
|
||||
{
|
||||
setState(759);
|
||||
setState(753);
|
||||
when_choice();
|
||||
}
|
||||
break;
|
||||
case EOL:
|
||||
{
|
||||
setState(760);
|
||||
setState(754);
|
||||
match(EOL);
|
||||
}
|
||||
break;
|
||||
@@ -5055,18 +5031,18 @@ public class prog8Parser extends Parser {
|
||||
throw new NoViableAltException(this);
|
||||
}
|
||||
}
|
||||
setState(765);
|
||||
setState(759);
|
||||
_errHandler.sync(this);
|
||||
_la = _input.LA(1);
|
||||
}
|
||||
setState(766);
|
||||
setState(760);
|
||||
match(T__17);
|
||||
setState(768);
|
||||
setState(762);
|
||||
_errHandler.sync(this);
|
||||
switch ( getInterpreter().adaptivePredict(_input,101,_ctx) ) {
|
||||
switch ( getInterpreter().adaptivePredict(_input,99,_ctx) ) {
|
||||
case 1:
|
||||
{
|
||||
setState(767);
|
||||
setState(761);
|
||||
match(EOL);
|
||||
}
|
||||
break;
|
||||
@@ -5106,7 +5082,7 @@ public class prog8Parser extends Parser {
|
||||
try {
|
||||
enterOuterAlt(_localctx, 1);
|
||||
{
|
||||
setState(772);
|
||||
setState(766);
|
||||
_errHandler.sync(this);
|
||||
switch (_input.LA(1)) {
|
||||
case T__16:
|
||||
@@ -5131,22 +5107,22 @@ public class prog8Parser extends Parser {
|
||||
case STRING:
|
||||
case SINGLECHAR:
|
||||
{
|
||||
setState(770);
|
||||
setState(764);
|
||||
expression_list();
|
||||
}
|
||||
break;
|
||||
case T__91:
|
||||
{
|
||||
setState(771);
|
||||
setState(765);
|
||||
match(T__91);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new NoViableAltException(this);
|
||||
}
|
||||
setState(774);
|
||||
setState(768);
|
||||
match(T__86);
|
||||
setState(777);
|
||||
setState(771);
|
||||
_errHandler.sync(this);
|
||||
switch (_input.LA(1)) {
|
||||
case T__1:
|
||||
@@ -5199,13 +5175,13 @@ public class prog8Parser extends Parser {
|
||||
case NAME:
|
||||
case ADDRESS_OF:
|
||||
{
|
||||
setState(775);
|
||||
setState(769);
|
||||
statement();
|
||||
}
|
||||
break;
|
||||
case T__16:
|
||||
{
|
||||
setState(776);
|
||||
setState(770);
|
||||
statement_block();
|
||||
}
|
||||
break;
|
||||
@@ -5267,7 +5243,7 @@ public class prog8Parser extends Parser {
|
||||
}
|
||||
|
||||
public static final String _serializedATN =
|
||||
"\3\u608b\ua72a\u8133\ub9ed\u417c\u3be7\u7786\u5964\3\177\u030e\4\2\t\2"+
|
||||
"\3\u608b\ua72a\u8133\ub9ed\u417c\u3be7\u7786\u5964\3\177\u0308\4\2\t\2"+
|
||||
"\4\3\t\3\4\4\t\4\4\5\t\5\4\6\t\6\4\7\t\7\4\b\t\b\4\t\t\t\4\n\t\n\4\13"+
|
||||
"\t\13\4\f\t\f\4\r\t\r\4\16\t\16\4\17\t\17\4\20\t\20\4\21\t\21\4\22\t\22"+
|
||||
"\4\23\t\23\4\24\t\24\4\25\t\25\4\26\t\26\4\27\t\27\4\30\t\30\4\31\t\31"+
|
||||
@@ -5319,252 +5295,249 @@ public class prog8Parser extends Parser {
|
||||
"\5<\u0296\n<\3<\7<\u0299\n<\f<\16<\u029c\13<\3=\3=\3=\3=\3=\5=\u02a3\n"+
|
||||
"=\3>\3>\3>\5>\u02a8\n>\3>\3>\5>\u02ac\n>\3>\5>\u02af\n>\3>\5>\u02b2\n"+
|
||||
">\3?\3?\5?\u02b6\n?\3?\3?\5?\u02ba\n?\3@\3@\5@\u02be\n@\3@\3@\5@\u02c2"+
|
||||
"\n@\3@\5@\u02c5\n@\3@\5@\u02c8\n@\3@\3@\3A\3A\3B\3B\5B\u02d0\nB\3B\5B"+
|
||||
"\u02d3\nB\3B\3B\5B\u02d7\nB\3B\3B\3B\5B\u02dc\nB\3B\3B\5B\u02e0\nB\3C"+
|
||||
"\3C\3C\5C\u02e5\nC\3C\3C\5C\u02e9\nC\3D\3D\3D\5D\u02ee\nD\3D\5D\u02f1"+
|
||||
"\nD\3D\3D\3D\3E\3E\3E\3E\3E\3E\7E\u02fc\nE\fE\16E\u02ff\13E\3E\3E\5E\u0303"+
|
||||
"\nE\3F\3F\5F\u0307\nF\3F\3F\3F\5F\u030c\nF\3F\2\3,G\2\4\6\b\n\f\16\20"+
|
||||
"\22\24\26\30\32\34\36 \"$&(*,.\60\62\64\668:<>@BDFHJLNPRTVXZ\\^`bdfhj"+
|
||||
"lnprtvxz|~\u0080\u0082\u0084\u0086\u0088\u008a\2\22\3\2\5\16\3\2\25\33"+
|
||||
"\3\2\36(\3\2)*\3\2+-\3\2/\61\3\2+,\3\2\62\63\3\2\64\67\3\289\3\2JL\3\2"+
|
||||
"JO\3\2PS\3\2vx\3\2UV\3\2_j\2\u0367\2\u0090\3\2\2\2\4\u0097\3\2\2\2\6\u0099"+
|
||||
"\3\2\2\2\b\u00ba\3\2\2\2\n\u00bc\3\2\2\2\f\u00bf\3\2\2\2\16\u00c4\3\2"+
|
||||
"\2\2\20\u00d5\3\2\2\2\22\u00d7\3\2\2\2\24\u00e1\3\2\2\2\26\u00e4\3\2\2"+
|
||||
"\2\30\u00e8\3\2\2\2\32\u00ec\3\2\2\2\34\u00ef\3\2\2\2\36\u00f2\3\2\2\2"+
|
||||
" \u0104\3\2\2\2\"\u0106\3\2\2\2$\u010a\3\2\2\2&\u010e\3\2\2\2(\u0116\3"+
|
||||
"\2\2\2*\u0118\3\2\2\2,\u012b\3\2\2\2.\u01a7\3\2\2\2\60\u01aa\3\2\2\2\62"+
|
||||
"\u01ad\3\2\2\2\64\u01b2\3\2\2\2\66\u01b5\3\2\2\28\u01bc\3\2\2\2:\u01c3"+
|
||||
"\3\2\2\2<\u01ce\3\2\2\2>\u01d2\3\2\2\2@\u01d4\3\2\2\2B\u01d6\3\2\2\2D"+
|
||||
"\u01d8\3\2\2\2F\u01e0\3\2\2\2H\u01e2\3\2\2\2J\u01e4\3\2\2\2L\u01e6\3\2"+
|
||||
"\2\2N\u01ea\3\2\2\2P\u01ec\3\2\2\2R\u01ee\3\2\2\2T\u0202\3\2\2\2V\u0216"+
|
||||
"\3\2\2\2X\u0218\3\2\2\2Z\u021a\3\2\2\2\\\u0223\3\2\2\2^\u0225\3\2\2\2"+
|
||||
"`\u0228\3\2\2\2b\u0235\3\2\2\2d\u0238\3\2\2\2f\u0243\3\2\2\2h\u024e\3"+
|
||||
"\2\2\2j\u0259\3\2\2\2l\u026d\3\2\2\2n\u0270\3\2\2\2p\u027b\3\2\2\2r\u0282"+
|
||||
"\3\2\2\2t\u0289\3\2\2\2v\u0291\3\2\2\2x\u029d\3\2\2\2z\u02a4\3\2\2\2|"+
|
||||
"\u02b3\3\2\2\2~\u02bb\3\2\2\2\u0080\u02cb\3\2\2\2\u0082\u02cd\3\2\2\2"+
|
||||
"\u0084\u02e1\3\2\2\2\u0086\u02ea\3\2\2\2\u0088\u02f5\3\2\2\2\u008a\u0306"+
|
||||
"\3\2\2\2\u008c\u008f\5\4\3\2\u008d\u008f\7t\2\2\u008e\u008c\3\2\2\2\u008e"+
|
||||
"\u008d\3\2\2\2\u008f\u0092\3\2\2\2\u0090\u008e\3\2\2\2\u0090\u0091\3\2"+
|
||||
"\2\2\u0091\u0093\3\2\2\2\u0092\u0090\3\2\2\2\u0093\u0094\7\2\2\3\u0094"+
|
||||
"\3\3\2\2\2\u0095\u0098\5\16\b\2\u0096\u0098\5\6\4\2\u0097\u0095\3\2\2"+
|
||||
"\2\u0097\u0096\3\2\2\2\u0098\5\3\2\2\2\u0099\u009b\5B\"\2\u009a\u009c"+
|
||||
"\5L\'\2\u009b\u009a\3\2\2\2\u009b\u009c\3\2\2\2\u009c\u009d\3\2\2\2\u009d"+
|
||||
"\u009e\5d\63\2\u009e\u009f\7t\2\2\u009f\7\3\2\2\2\u00a0\u00bb\5\16\b\2"+
|
||||
"\u00a1\u00bb\5\26\f\2\u00a2\u00bb\5\30\r\2\u00a3\u00bb\5\22\n\2\u00a4"+
|
||||
"\u00bb\5\24\13\2\u00a5\u00bb\5\32\16\2\u00a6\u00bb\5\34\17\2\u00a7\u00bb"+
|
||||
"\5\36\20\2\u00a8\u00bb\5$\23\2\u00a9\u00bb\5&\24\2\u00aa\u00bb\5\f\7\2"+
|
||||
"\u00ab\u00bb\5*\26\2\u00ac\u00bb\58\35\2\u00ad\u00bb\5z>\2\u00ae\u00bb"+
|
||||
"\5~@\2\u00af\u00bb\5`\61\2\u00b0\u00bb\5j\66\2\u00b1\u00bb\5^\60\2\u00b2"+
|
||||
"\u00bb\5<\37\2\u00b3\u00bb\5\u0082B\2\u00b4\u00bb\5\u0084C\2\u00b5\u00bb"+
|
||||
"\5\u0086D\2\u00b6\u00bb\5\u0088E\2\u00b7\u00bb\5> \2\u00b8\u00bb\5@!\2"+
|
||||
"\u00b9\u00bb\5\n\6\2\u00ba\u00a0\3\2\2\2\u00ba\u00a1\3\2\2\2\u00ba\u00a2"+
|
||||
"\3\2\2\2\u00ba\u00a3\3\2\2\2\u00ba\u00a4\3\2\2\2\u00ba\u00a5\3\2\2\2\u00ba"+
|
||||
"\u00a6\3\2\2\2\u00ba\u00a7\3\2\2\2\u00ba\u00a8\3\2\2\2\u00ba\u00a9\3\2"+
|
||||
"\2\2\u00ba\u00aa\3\2\2\2\u00ba\u00ab\3\2\2\2\u00ba\u00ac\3\2\2\2\u00ba"+
|
||||
"\u00ad\3\2\2\2\u00ba\u00ae\3\2\2\2\u00ba\u00af\3\2\2\2\u00ba\u00b0\3\2"+
|
||||
"\2\2\u00ba\u00b1\3\2\2\2\u00ba\u00b2\3\2\2\2\u00ba\u00b3\3\2\2\2\u00ba"+
|
||||
"\u00b4\3\2\2\2\u00ba\u00b5\3\2\2\2\u00ba\u00b6\3\2\2\2\u00ba\u00b7\3\2"+
|
||||
"\2\2\u00ba\u00b8\3\2\2\2\u00ba\u00b9\3\2\2\2\u00bb\t\3\2\2\2\u00bc\u00bd"+
|
||||
"\5B\"\2\u00bd\u00be\7\3\2\2\u00be\13\3\2\2\2\u00bf\u00c2\7\4\2\2\u00c0"+
|
||||
"\u00c3\5L\'\2\u00c1\u00c3\5D#\2\u00c2\u00c0\3\2\2\2\u00c2\u00c1\3\2\2"+
|
||||
"\2\u00c3\r\3\2\2\2\u00c4\u00d0\t\2\2\2\u00c5\u00c7\5\20\t\2\u00c6\u00c5"+
|
||||
"\3\2\2\2\u00c6\u00c7\3\2\2\2\u00c7\u00d1\3\2\2\2\u00c8\u00cd\5\20\t\2"+
|
||||
"\u00c9\u00ca\7\17\2\2\u00ca\u00cc\5\20\t\2\u00cb\u00c9\3\2\2\2\u00cc\u00cf"+
|
||||
"\3\2\2\2\u00cd\u00cb\3\2\2\2\u00cd\u00ce\3\2\2\2\u00ce\u00d1\3\2\2\2\u00cf"+
|
||||
"\u00cd\3\2\2\2\u00d0\u00c6\3\2\2\2\u00d0\u00c8\3\2\2\2\u00d1\17\3\2\2"+
|
||||
"\2\u00d2\u00d6\5V,\2\u00d3\u00d6\5B\"\2\u00d4\u00d6\5L\'\2\u00d5\u00d2"+
|
||||
"\3\2\2\2\u00d5\u00d3\3\2\2\2\u00d5\u00d4\3\2\2\2\u00d6\21\3\2\2\2\u00d7"+
|
||||
"\u00d9\5 \21\2\u00d8\u00da\7~\2\2\u00d9\u00d8\3\2\2\2\u00d9\u00da\3\2"+
|
||||
"\2\2\u00da\u00dd\3\2\2\2\u00db\u00de\5\"\22\2\u00dc\u00de\7\177\2\2\u00dd"+
|
||||
"\u00db\3\2\2\2\u00dd\u00dc\3\2\2\2\u00dd\u00de\3\2\2\2\u00de\u00df\3\2"+
|
||||
"\2\2\u00df\u00e0\5B\"\2\u00e0\23\3\2\2\2\u00e1\u00e2\5B\"\2\u00e2\u00e3"+
|
||||
"\5B\"\2\u00e3\25\3\2\2\2\u00e4\u00e5\5\22\n\2\u00e5\u00e6\7\20\2\2\u00e6"+
|
||||
"\u00e7\5,\27\2\u00e7\27\3\2\2\2\u00e8\u00e9\5\24\13\2\u00e9\u00ea\7\20"+
|
||||
"\2\2\u00ea\u00eb\5,\27\2\u00eb\31\3\2\2\2\u00ec\u00ed\7\21\2\2\u00ed\u00ee"+
|
||||
"\5\26\f\2\u00ee\33\3\2\2\2\u00ef\u00f0\7y\2\2\u00f0\u00f1\5\26\f\2\u00f1"+
|
||||
"\35\3\2\2\2\u00f2\u00f3\7\22\2\2\u00f3\u00f4\5B\"\2\u00f4\u00f5\7\23\2"+
|
||||
"\2\u00f5\u00f6\7t\2\2\u00f6\u00fb\5\22\n\2\u00f7\u00f8\7t\2\2\u00f8\u00fa"+
|
||||
"\5\22\n\2\u00f9\u00f7\3\2\2\2\u00fa\u00fd\3\2\2\2\u00fb\u00f9\3\2\2\2"+
|
||||
"\u00fb\u00fc\3\2\2\2\u00fc\u00ff\3\2\2\2\u00fd\u00fb\3\2\2\2\u00fe\u0100"+
|
||||
"\7t\2\2\u00ff\u00fe\3\2\2\2\u00ff\u0100\3\2\2\2\u0100\u0101\3\2\2\2\u0101"+
|
||||
"\u0102\7\24\2\2\u0102\u0103\7t\2\2\u0103\37\3\2\2\2\u0104\u0105\t\3\2"+
|
||||
"\2\u0105!\3\2\2\2\u0106\u0107\7\34\2\2\u0107\u0108\5,\27\2\u0108\u0109"+
|
||||
"\7\35\2\2\u0109#\3\2\2\2\u010a\u010b\5(\25\2\u010b\u010c\7\20\2\2\u010c"+
|
||||
"\u010d\5,\27\2\u010d%\3\2\2\2\u010e\u010f\5(\25\2\u010f\u0110\t\4\2\2"+
|
||||
"\u0110\u0111\5,\27\2\u0111\'\3\2\2\2\u0112\u0117\5F$\2\u0113\u0117\5D"+
|
||||
"#\2\u0114\u0117\5\60\31\2\u0115\u0117\5\62\32\2\u0116\u0112\3\2\2\2\u0116"+
|
||||
"\u0113\3\2\2\2\u0116\u0114\3\2\2\2\u0116\u0115\3\2\2\2\u0117)\3\2\2\2"+
|
||||
"\u0118\u0119\5(\25\2\u0119\u011a\t\5\2\2\u011a+\3\2\2\2\u011b\u011c\b"+
|
||||
"\27\1\2\u011c\u012c\5\66\34\2\u011d\u011e\t\6\2\2\u011e\u012c\5,\27\31"+
|
||||
"\u011f\u0120\7A\2\2\u0120\u012c\5,\27\13\u0121\u012c\5\\/\2\u0122\u012c"+
|
||||
"\5F$\2\u0123\u012c\5D#\2\u0124\u012c\5\60\31\2\u0125\u012c\5\62\32\2\u0126"+
|
||||
"\u012c\5\64\33\2\u0127\u0128\7B\2\2\u0128\u0129\5,\27\2\u0129\u012a\7"+
|
||||
"C\2\2\u012a\u012c\3\2\2\2\u012b\u011b\3\2\2\2\u012b\u011d\3\2\2\2\u012b"+
|
||||
"\u011f\3\2\2\2\u012b\u0121\3\2\2\2\u012b\u0122\3\2\2\2\u012b\u0123\3\2"+
|
||||
"\2\2\u012b\u0124\3\2\2\2\u012b\u0125\3\2\2\2\u012b\u0126\3\2\2\2\u012b"+
|
||||
"\u0127\3\2\2\2\u012c\u01a4\3\2\2\2\u012d\u012f\f\30\2\2\u012e\u0130\7"+
|
||||
"t\2\2\u012f\u012e\3\2\2\2\u012f\u0130\3\2\2\2\u0130\u0131\3\2\2\2\u0131"+
|
||||
"\u0133\7.\2\2\u0132\u0134\7t\2\2\u0133\u0132\3\2\2\2\u0133\u0134\3\2\2"+
|
||||
"\2\u0134\u0135\3\2\2\2\u0135\u01a3\5,\27\31\u0136\u0138\f\27\2\2\u0137"+
|
||||
"\u0139\7t\2\2\u0138\u0137\3\2\2\2\u0138\u0139\3\2\2\2\u0139\u013a\3\2"+
|
||||
"\2\2\u013a\u013c\t\7\2\2\u013b\u013d\7t\2\2\u013c\u013b\3\2\2\2\u013c"+
|
||||
"\u013d\3\2\2\2\u013d\u013e\3\2\2\2\u013e\u01a3\5,\27\30\u013f\u0141\f"+
|
||||
"\26\2\2\u0140\u0142\7t\2\2\u0141\u0140\3\2\2\2\u0141\u0142\3\2\2\2\u0142"+
|
||||
"\u0143\3\2\2\2\u0143\u0145\t\b\2\2\u0144\u0146\7t\2\2\u0145\u0144\3\2"+
|
||||
"\2\2\u0145\u0146\3\2\2\2\u0146\u0147\3\2\2\2\u0147\u01a3\5,\27\27\u0148"+
|
||||
"\u014a\f\25\2\2\u0149\u014b\7t\2\2\u014a\u0149\3\2\2\2\u014a\u014b\3\2"+
|
||||
"\2\2\u014b\u014c\3\2\2\2\u014c\u014e\t\t\2\2\u014d\u014f\7t\2\2\u014e"+
|
||||
"\u014d\3\2\2\2\u014e\u014f\3\2\2\2\u014f\u0150\3\2\2\2\u0150\u01a3\5,"+
|
||||
"\27\26\u0151\u0153\f\24\2\2\u0152\u0154\7t\2\2\u0153\u0152\3\2\2\2\u0153"+
|
||||
"\u0154\3\2\2\2\u0154\u0155\3\2\2\2\u0155\u0157\t\n\2\2\u0156\u0158\7t"+
|
||||
"\2\2\u0157\u0156\3\2\2\2\u0157\u0158\3\2\2\2\u0158\u0159\3\2\2\2\u0159"+
|
||||
"\u01a3\5,\27\25\u015a\u015c\f\23\2\2\u015b\u015d\7t\2\2\u015c\u015b\3"+
|
||||
"\2\2\2\u015c\u015d\3\2\2\2\u015d\u015e\3\2\2\2\u015e\u0160\t\13\2\2\u015f"+
|
||||
"\u0161\7t\2\2\u0160\u015f\3\2\2\2\u0160\u0161\3\2\2\2\u0161\u0162\3\2"+
|
||||
"\2\2\u0162\u01a3\5,\27\24\u0163\u0165\f\22\2\2\u0164\u0166\7t\2\2\u0165"+
|
||||
"\u0164\3\2\2\2\u0165\u0166\3\2\2\2\u0166\u0167\3\2\2\2\u0167\u0169\7y"+
|
||||
"\2\2\u0168\u016a\7t\2\2\u0169\u0168\3\2\2\2\u0169\u016a\3\2\2\2\u016a"+
|
||||
"\u016b\3\2\2\2\u016b\u01a3\5,\27\23\u016c\u016e\f\21\2\2\u016d\u016f\7"+
|
||||
"t\2\2\u016e\u016d\3\2\2\2\u016e\u016f\3\2\2\2\u016f\u0170\3\2\2\2\u0170"+
|
||||
"\u0172\7:\2\2\u0171\u0173\7t\2\2\u0172\u0171\3\2\2\2\u0172\u0173\3\2\2"+
|
||||
"\2\u0173\u0174\3\2\2\2\u0174\u01a3\5,\27\22\u0175\u0177\f\20\2\2\u0176"+
|
||||
"\u0178\7t\2\2\u0177\u0176\3\2\2\2\u0177\u0178\3\2\2\2\u0178\u0179\3\2"+
|
||||
"\2\2\u0179\u017b\7;\2\2\u017a\u017c\7t\2\2\u017b\u017a\3\2\2\2\u017b\u017c"+
|
||||
"\3\2\2\2\u017c\u017d\3\2\2\2\u017d\u01a3\5,\27\21\u017e\u0180\f\16\2\2"+
|
||||
"\u017f\u0181\7t\2\2\u0180\u017f\3\2\2\2\u0180\u0181\3\2\2\2\u0181\u0182"+
|
||||
"\3\2\2\2\u0182\u0184\7>\2\2\u0183\u0185\7t\2\2\u0184\u0183\3\2\2\2\u0184"+
|
||||
"\u0185\3\2\2\2\u0185\u0186\3\2\2\2\u0186\u01a3\5,\27\17\u0187\u0189\f"+
|
||||
"\r\2\2\u0188\u018a\7t\2\2\u0189\u0188\3\2\2\2\u0189\u018a\3\2\2\2\u018a"+
|
||||
"\u018b\3\2\2\2\u018b\u018d\7?\2\2\u018c\u018e\7t\2\2\u018d\u018c\3\2\2"+
|
||||
"\2\u018d\u018e\3\2\2\2\u018e\u018f\3\2\2\2\u018f\u01a3\5,\27\16\u0190"+
|
||||
"\u0192\f\f\2\2\u0191\u0193\7t\2\2\u0192\u0191\3\2\2\2\u0192\u0193\3\2"+
|
||||
"\2\2\u0193\u0194\3\2\2\2\u0194\u0196\7@\2\2\u0195\u0197\7t\2\2\u0196\u0195"+
|
||||
"\3\2\2\2\u0196\u0197\3\2\2\2\u0197\u0198\3\2\2\2\u0198\u01a3\5,\27\r\u0199"+
|
||||
"\u019a\f\17\2\2\u019a\u019b\7<\2\2\u019b\u019e\5,\27\2\u019c\u019d\7="+
|
||||
"\2\2\u019d\u019f\5,\27\2\u019e\u019c\3\2\2\2\u019e\u019f\3\2\2\2\u019f"+
|
||||
"\u01a3\3\2\2\2\u01a0\u01a1\f\4\2\2\u01a1\u01a3\5.\30\2\u01a2\u012d\3\2"+
|
||||
"\2\2\u01a2\u0136\3\2\2\2\u01a2\u013f\3\2\2\2\u01a2\u0148\3\2\2\2\u01a2"+
|
||||
"\u0151\3\2\2\2\u01a2\u015a\3\2\2\2\u01a2\u0163\3\2\2\2\u01a2\u016c\3\2"+
|
||||
"\2\2\u01a2\u0175\3\2\2\2\u01a2\u017e\3\2\2\2\u01a2\u0187\3\2\2\2\u01a2"+
|
||||
"\u0190\3\2\2\2\u01a2\u0199\3\2\2\2\u01a2\u01a0\3\2\2\2\u01a3\u01a6\3\2"+
|
||||
"\2\2\u01a4\u01a2\3\2\2\2\u01a4\u01a5\3\2\2\2\u01a5-\3\2\2\2\u01a6\u01a4"+
|
||||
"\3\2\2\2\u01a7\u01a8\7D\2\2\u01a8\u01a9\5 \21\2\u01a9/\3\2\2\2\u01aa\u01ab"+
|
||||
"\5D#\2\u01ab\u01ac\5\"\22\2\u01ac\61\3\2\2\2\u01ad\u01ae\7E\2\2\u01ae"+
|
||||
"\u01af\7B\2\2\u01af\u01b0\5,\27\2\u01b0\u01b1\7C\2\2\u01b1\63\3\2\2\2"+
|
||||
"\u01b2\u01b3\7y\2\2\u01b3\u01b4\5D#\2\u01b4\65\3\2\2\2\u01b5\u01b6\5D"+
|
||||
"#\2\u01b6\u01b8\7B\2\2\u01b7\u01b9\5:\36\2\u01b8\u01b7\3\2\2\2\u01b8\u01b9"+
|
||||
"\3\2\2\2\u01b9\u01ba\3\2\2\2\u01ba\u01bb\7C\2\2\u01bb\67\3\2\2\2\u01bc"+
|
||||
"\u01bd\5D#\2\u01bd\u01bf\7B\2\2\u01be\u01c0\5:\36\2\u01bf\u01be\3\2\2"+
|
||||
"\2\u01bf\u01c0\3\2\2\2\u01c0\u01c1\3\2\2\2\u01c1\u01c2\7C\2\2\u01c29\3"+
|
||||
"\2\2\2\u01c3\u01cb\5,\27\2\u01c4\u01c6\7\17\2\2\u01c5\u01c7\7t\2\2\u01c6"+
|
||||
"\u01c5\3\2\2\2\u01c6\u01c7\3\2\2\2\u01c7\u01c8\3\2\2\2\u01c8\u01ca\5,"+
|
||||
"\27\2\u01c9\u01c4\3\2\2\2\u01ca\u01cd\3\2\2\2\u01cb\u01c9\3\2\2\2\u01cb"+
|
||||
"\u01cc\3\2\2\2\u01cc;\3\2\2\2\u01cd\u01cb\3\2\2\2\u01ce\u01d0\7F\2\2\u01cf"+
|
||||
"\u01d1\5,\27\2\u01d0\u01cf\3\2\2\2\u01d0\u01d1\3\2\2\2\u01d1=\3\2\2\2"+
|
||||
"\u01d2\u01d3\7G\2\2\u01d3?\3\2\2\2\u01d4\u01d5\7H\2\2\u01d5A\3\2\2\2\u01d6"+
|
||||
"\u01d7\7u\2\2\u01d7C\3\2\2\2\u01d8\u01dd\7u\2\2\u01d9\u01da\7I\2\2\u01da"+
|
||||
"\u01dc\7u\2\2\u01db\u01d9\3\2\2\2\u01dc\u01df\3\2\2\2\u01dd\u01db\3\2"+
|
||||
"\2\2\u01dd\u01de\3\2\2\2\u01deE\3\2\2\2\u01df\u01dd\3\2\2\2\u01e0\u01e1"+
|
||||
"\t\f\2\2\u01e1G\3\2\2\2\u01e2\u01e3\t\r\2\2\u01e3I\3\2\2\2\u01e4\u01e5"+
|
||||
"\t\16\2\2\u01e5K\3\2\2\2\u01e6\u01e8\t\17\2\2\u01e7\u01e9\5N(\2\u01e8"+
|
||||
"\u01e7\3\2\2\2\u01e8\u01e9\3\2\2\2\u01e9M\3\2\2\2\u01ea\u01eb\7T\2\2\u01eb"+
|
||||
"O\3\2\2\2\u01ec\u01ed\t\20\2\2\u01edQ\3\2\2\2\u01ee\u01f0\7\34\2\2\u01ef"+
|
||||
"\u01f1\7t\2\2\u01f0\u01ef\3\2\2\2\u01f0\u01f1\3\2\2\2\u01f1\u01f2\3\2"+
|
||||
"\2\2\u01f2\u01fa\5,\27\2\u01f3\u01f5\7\17\2\2\u01f4\u01f6\7t\2\2\u01f5"+
|
||||
"\u01f4\3\2\2\2\u01f5\u01f6\3\2\2\2\u01f6\u01f7\3\2\2\2\u01f7\u01f9\5,"+
|
||||
"\27\2\u01f8\u01f3\3\2\2\2\u01f9\u01fc\3\2\2\2\u01fa\u01f8\3\2\2\2\u01fa"+
|
||||
"\u01fb\3\2\2\2\u01fb\u01fe\3\2\2\2\u01fc\u01fa\3\2\2\2\u01fd\u01ff\7t"+
|
||||
"\2\2\u01fe\u01fd\3\2\2\2\u01fe\u01ff\3\2\2\2\u01ff\u0200\3\2\2\2\u0200"+
|
||||
"\u0201\7\35\2\2\u0201S\3\2\2\2\u0202\u0204\7\23\2\2\u0203\u0205\7t\2\2"+
|
||||
"\u0204\u0203\3\2\2\2\u0204\u0205\3\2\2\2\u0205\u0206\3\2\2\2\u0206\u020e"+
|
||||
"\5,\27\2\u0207\u0209\7\17\2\2\u0208\u020a\7t\2\2\u0209\u0208\3\2\2\2\u0209"+
|
||||
"\u020a\3\2\2\2\u020a\u020b\3\2\2\2\u020b\u020d\5,\27\2\u020c\u0207\3\2"+
|
||||
"\2\2\u020d\u0210\3\2\2\2\u020e\u020c\3\2\2\2\u020e\u020f\3\2\2\2\u020f"+
|
||||
"\u0212\3\2\2\2\u0210\u020e\3\2\2\2\u0211\u0213\7t\2\2\u0212\u0211\3\2"+
|
||||
"\2\2\u0212\u0213\3\2\2\2\u0213\u0214\3\2\2\2\u0214\u0215\7\24\2\2\u0215"+
|
||||
"U\3\2\2\2\u0216\u0217\7{\2\2\u0217W\3\2\2\2\u0218\u0219\7}\2\2\u0219Y"+
|
||||
"\3\2\2\2\u021a\u021b\7z\2\2\u021b[\3\2\2\2\u021c\u0224\5L\'\2\u021d\u0224"+
|
||||
"\5P)\2\u021e\u0224\5R*\2\u021f\u0224\5V,\2\u0220\u0224\5X-\2\u0221\u0224"+
|
||||
"\5Z.\2\u0222\u0224\5T+\2\u0223\u021c\3\2\2\2\u0223\u021d\3\2\2\2\u0223"+
|
||||
"\u021e\3\2\2\2\u0223\u021f\3\2\2\2\u0223\u0220\3\2\2\2\u0223\u0221\3\2"+
|
||||
"\2\2\u0223\u0222\3\2\2\2\u0224]\3\2\2\2\u0225\u0226\7W\2\2\u0226\u0227"+
|
||||
"\7|\2\2\u0227_\3\2\2\2\u0228\u0229\7X\2\2\u0229\u022a\5B\"\2\u022a\u022c"+
|
||||
"\7B\2\2\u022b\u022d\5f\64\2\u022c\u022b\3\2\2\2\u022c\u022d\3\2\2\2\u022d"+
|
||||
"\u022e\3\2\2\2\u022e\u0230\7C\2\2\u022f\u0231\5b\62\2\u0230\u022f\3\2"+
|
||||
"\2\2\u0230\u0231\3\2\2\2\u0231\u0232\3\2\2\2\u0232\u0233\5d\63\2\u0233"+
|
||||
"\u0234\7t\2\2\u0234a\3\2\2\2\u0235\u0236\7Y\2\2\u0236\u0237\5h\65\2\u0237"+
|
||||
"c\3\2\2\2\u0238\u0239\7\23\2\2\u0239\u023e\7t\2\2\u023a\u023d\5\b\5\2"+
|
||||
"\u023b\u023d\7t\2\2\u023c\u023a\3\2\2\2\u023c\u023b\3\2\2\2\u023d\u0240"+
|
||||
"\3\2\2\2\u023e\u023c\3\2\2\2\u023e\u023f\3\2\2\2\u023f\u0241\3\2\2\2\u0240"+
|
||||
"\u023e\3\2\2\2\u0241\u0242\7\24\2\2\u0242e\3\2\2\2\u0243\u024b\5\22\n"+
|
||||
"\2\u0244\u0246\7\17\2\2\u0245\u0247\7t\2\2\u0246\u0245\3\2\2\2\u0246\u0247"+
|
||||
"\3\2\2\2\u0247\u0248\3\2\2\2\u0248\u024a\5\22\n\2\u0249\u0244\3\2\2\2"+
|
||||
"\u024a\u024d\3\2\2\2\u024b\u0249\3\2\2\2\u024b\u024c\3\2\2\2\u024cg\3"+
|
||||
"\2\2\2\u024d\u024b\3\2\2\2\u024e\u0256\5 \21\2\u024f\u0251\7\17\2\2\u0250"+
|
||||
"\u0252\7t\2\2\u0251\u0250\3\2\2\2\u0251\u0252\3\2\2\2\u0252\u0253\3\2"+
|
||||
"\2\2\u0253\u0255\5 \21\2\u0254\u024f\3\2\2\2\u0255\u0258\3\2\2\2\u0256"+
|
||||
"\u0254\3\2\2\2\u0256\u0257\3\2\2\2\u0257i\3\2\2\2\u0258\u0256\3\2\2\2"+
|
||||
"\u0259\u025a\7Z\2\2\u025a\u025b\5B\"\2\u025b\u025d\7B\2\2\u025c\u025e"+
|
||||
"\5n8\2\u025d\u025c\3\2\2\2\u025d\u025e\3\2\2\2\u025e\u025f\3\2\2\2\u025f"+
|
||||
"\u0261\7C\2\2\u0260\u0262\7t\2\2\u0261\u0260\3\2\2\2\u0261\u0262\3\2\2"+
|
||||
"\2\u0262\u0264\3\2\2\2\u0263\u0265\5r:\2\u0264\u0263\3\2\2\2\u0264\u0265"+
|
||||
"\3\2\2\2\u0265\u0267\3\2\2\2\u0266\u0268\5v<\2\u0267\u0266\3\2\2\2\u0267"+
|
||||
"\u0268\3\2\2\2\u0268\u026b\3\2\2\2\u0269\u026c\5l\67\2\u026a\u026c\5d"+
|
||||
"\63\2\u026b\u0269\3\2\2\2\u026b\u026a\3\2\2\2\u026ck\3\2\2\2\u026d\u026e"+
|
||||
"\7\20\2\2\u026e\u026f\5L\'\2\u026fm\3\2\2\2\u0270\u0278\5p9\2\u0271\u0273"+
|
||||
"\7\17\2\2\u0272\u0274\7t\2\2\u0273\u0272\3\2\2\2\u0273\u0274\3\2\2\2\u0274"+
|
||||
"\u0275\3\2\2\2\u0275\u0277\5p9\2\u0276\u0271\3\2\2\2\u0277\u027a\3\2\2"+
|
||||
"\2\u0278\u0276\3\2\2\2\u0278\u0279\3\2\2\2\u0279o\3\2\2\2\u027a\u0278"+
|
||||
"\3\2\2\2\u027b\u027c\5\22\n\2\u027c\u0280\7E\2\2\u027d\u0281\5H%\2\u027e"+
|
||||
"\u0281\5J&\2\u027f\u0281\7[\2\2\u0280\u027d\3\2\2\2\u0280\u027e\3\2\2"+
|
||||
"\2\u0280\u027f\3\2\2\2\u0281q\3\2\2\2\u0282\u0283\7\\\2\2\u0283\u0285"+
|
||||
"\7B\2\2\u0284\u0286\5t;\2\u0285\u0284\3\2\2\2\u0285\u0286\3\2\2\2\u0286"+
|
||||
"\u0287\3\2\2\2\u0287\u0288\7C\2\2\u0288s\3\2\2\2\u0289\u028e\5F$\2\u028a"+
|
||||
"\u028b\7\17\2\2\u028b\u028d\5F$\2\u028c\u028a\3\2\2\2\u028d\u0290\3\2"+
|
||||
"\2\2\u028e\u028c\3\2\2\2\u028e\u028f\3\2\2\2\u028fu\3\2\2\2\u0290\u028e"+
|
||||
"\3\2\2\2\u0291\u0292\7Y\2\2\u0292\u029a\5x=\2\u0293\u0295\7\17\2\2\u0294"+
|
||||
"\u0296\7t\2\2\u0295\u0294\3\2\2\2\u0295\u0296\3\2\2\2\u0296\u0297\3\2"+
|
||||
"\2\2\u0297\u0299\5x=\2\u0298\u0293\3\2\2\2\u0299\u029c\3\2\2\2\u029a\u0298"+
|
||||
"\3\2\2\2\u029a\u029b\3\2\2\2\u029bw\3\2\2\2\u029c\u029a\3\2\2\2\u029d"+
|
||||
"\u029e\5 \21\2\u029e\u02a2\7E\2\2\u029f\u02a3\5H%\2\u02a0\u02a3\5J&\2"+
|
||||
"\u02a1\u02a3\7[\2\2\u02a2\u029f\3\2\2\2\u02a2\u02a0\3\2\2\2\u02a2\u02a1"+
|
||||
"\3\2\2\2\u02a3y\3\2\2\2\u02a4\u02a5\7]\2\2\u02a5\u02a7\5,\27\2\u02a6\u02a8"+
|
||||
"\7t\2\2\u02a7\u02a6\3\2\2\2\u02a7\u02a8\3\2\2\2\u02a8\u02ab\3\2\2\2\u02a9"+
|
||||
"\u02ac\5\b\5\2\u02aa\u02ac\5d\63\2\u02ab\u02a9\3\2\2\2\u02ab\u02aa\3\2"+
|
||||
"\2\2\u02ac\u02ae\3\2\2\2\u02ad\u02af\7t\2\2\u02ae\u02ad\3\2\2\2\u02ae"+
|
||||
"\u02af\3\2\2\2\u02af\u02b1\3\2\2\2\u02b0\u02b2\5|?\2\u02b1\u02b0\3\2\2"+
|
||||
"\2\u02b1\u02b2\3\2\2\2\u02b2{\3\2\2\2\u02b3\u02b5\7^\2\2\u02b4\u02b6\7"+
|
||||
"t\2\2\u02b5\u02b4\3\2\2\2\u02b5\u02b6\3\2\2\2\u02b6\u02b9\3\2\2\2\u02b7"+
|
||||
"\u02ba\5\b\5\2\u02b8\u02ba\5d\63\2\u02b9\u02b7\3\2\2\2\u02b9\u02b8\3\2"+
|
||||
"\2\2\u02ba}\3\2\2\2\u02bb\u02bd\5\u0080A\2\u02bc\u02be\7t\2\2\u02bd\u02bc"+
|
||||
"\3\2\2\2\u02bd\u02be\3\2\2\2\u02be\u02c1\3\2\2\2\u02bf\u02c2\5\b\5\2\u02c0"+
|
||||
"\u02c2\5d\63\2\u02c1\u02bf\3\2\2\2\u02c1\u02c0\3\2\2\2\u02c2\u02c4\3\2"+
|
||||
"\2\2\u02c3\u02c5\7t\2\2\u02c4\u02c3\3\2\2\2\u02c4\u02c5\3\2\2\2\u02c5"+
|
||||
"\u02c7\3\2\2\2\u02c6\u02c8\5|?\2\u02c7\u02c6\3\2\2\2\u02c7\u02c8\3\2\2"+
|
||||
"\2\u02c8\u02c9\3\2\2\2\u02c9\u02ca\7t\2\2\u02ca\177\3\2\2\2\u02cb\u02cc"+
|
||||
"\t\21\2\2\u02cc\u0081\3\2\2\2\u02cd\u02cf\7k\2\2\u02ce\u02d0\5 \21\2\u02cf"+
|
||||
"\u02ce\3\2\2\2\u02cf\u02d0\3\2\2\2\u02d0\u02d2\3\2\2\2\u02d1\u02d3\7~"+
|
||||
"\2\2\u02d2\u02d1\3\2\2\2\u02d2\u02d3\3\2\2\2\u02d3\u02d6\3\2\2\2\u02d4"+
|
||||
"\u02d7\5F$\2\u02d5\u02d7\5B\"\2\u02d6\u02d4\3\2\2\2\u02d6\u02d5\3\2\2"+
|
||||
"\2\u02d7\u02d8\3\2\2\2\u02d8\u02d9\7l\2\2\u02d9\u02db\5,\27\2\u02da\u02dc"+
|
||||
"\7t\2\2\u02db\u02da\3\2\2\2\u02db\u02dc\3\2\2\2\u02dc\u02df\3\2\2\2\u02dd"+
|
||||
"\u02e0\5\b\5\2\u02de\u02e0\5d\63\2\u02df\u02dd\3\2\2\2\u02df\u02de\3\2"+
|
||||
"\2\2\u02e0\u0083\3\2\2\2\u02e1\u02e2\7m\2\2\u02e2\u02e4\5,\27\2\u02e3"+
|
||||
"\u02e5\7t\2\2\u02e4\u02e3\3\2\2\2\u02e4\u02e5\3\2\2\2\u02e5\u02e8\3\2"+
|
||||
"\2\2\u02e6\u02e9\5\b\5\2\u02e7\u02e9\5d\63\2\u02e8\u02e6\3\2\2\2\u02e8"+
|
||||
"\u02e7\3\2\2\2\u02e9\u0085\3\2\2\2\u02ea\u02ed\7n\2\2\u02eb\u02ee\5\b"+
|
||||
"\5\2\u02ec\u02ee\5d\63\2\u02ed\u02eb\3\2\2\2\u02ed\u02ec\3\2\2\2\u02ee"+
|
||||
"\u02f0\3\2\2\2\u02ef\u02f1\7t\2\2\u02f0\u02ef\3\2\2\2\u02f0\u02f1\3\2"+
|
||||
"\2\2\u02f1\u02f2\3\2\2\2\u02f2\u02f3\7o\2\2\u02f3\u02f4\5,\27\2\u02f4"+
|
||||
"\u0087\3\2\2\2\u02f5\u02f6\7p\2\2\u02f6\u02f7\5,\27\2\u02f7\u02f8\7\23"+
|
||||
"\2\2\u02f8\u02fd\7t\2\2\u02f9\u02fc\5\u008aF\2\u02fa\u02fc\7t\2\2\u02fb"+
|
||||
"\u02f9\3\2\2\2\u02fb\u02fa\3\2\2\2\u02fc\u02ff\3\2\2\2\u02fd\u02fb\3\2"+
|
||||
"\2\2\u02fd\u02fe\3\2\2\2\u02fe\u0300\3\2\2\2\u02ff\u02fd\3\2\2\2\u0300"+
|
||||
"\u0302\7\24\2\2\u0301\u0303\7t\2\2\u0302\u0301\3\2\2\2\u0302\u0303\3\2"+
|
||||
"\2\2\u0303\u0089\3\2\2\2\u0304\u0307\5:\36\2\u0305\u0307\7^\2\2\u0306"+
|
||||
"\u0304\3\2\2\2\u0306\u0305\3\2\2\2\u0307\u0308\3\2\2\2\u0308\u030b\7Y"+
|
||||
"\2\2\u0309\u030c\5\b\5\2\u030a\u030c\5d\63\2\u030b\u0309\3\2\2\2\u030b"+
|
||||
"\u030a\3\2\2\2\u030c\u008b\3\2\2\2j\u008e\u0090\u0097\u009b\u00ba\u00c2"+
|
||||
"\n@\3@\5@\u02c5\n@\3@\5@\u02c8\n@\3@\3@\3A\3A\3B\3B\3B\5B\u02d1\nB\3B"+
|
||||
"\3B\3B\5B\u02d6\nB\3B\3B\5B\u02da\nB\3C\3C\3C\5C\u02df\nC\3C\3C\5C\u02e3"+
|
||||
"\nC\3D\3D\3D\5D\u02e8\nD\3D\5D\u02eb\nD\3D\3D\3D\3E\3E\3E\3E\3E\3E\7E"+
|
||||
"\u02f6\nE\fE\16E\u02f9\13E\3E\3E\5E\u02fd\nE\3F\3F\5F\u0301\nF\3F\3F\3"+
|
||||
"F\5F\u0306\nF\3F\2\3,G\2\4\6\b\n\f\16\20\22\24\26\30\32\34\36 \"$&(*,"+
|
||||
".\60\62\64\668:<>@BDFHJLNPRTVXZ\\^`bdfhjlnprtvxz|~\u0080\u0082\u0084\u0086"+
|
||||
"\u0088\u008a\2\22\3\2\5\16\3\2\25\33\3\2\36(\3\2)*\3\2+-\3\2/\61\3\2+"+
|
||||
",\3\2\62\63\3\2\64\67\3\289\3\2JL\3\2JO\3\2PS\3\2vx\3\2UV\3\2_j\2\u035f"+
|
||||
"\2\u0090\3\2\2\2\4\u0097\3\2\2\2\6\u0099\3\2\2\2\b\u00ba\3\2\2\2\n\u00bc"+
|
||||
"\3\2\2\2\f\u00bf\3\2\2\2\16\u00c4\3\2\2\2\20\u00d5\3\2\2\2\22\u00d7\3"+
|
||||
"\2\2\2\24\u00e1\3\2\2\2\26\u00e4\3\2\2\2\30\u00e8\3\2\2\2\32\u00ec\3\2"+
|
||||
"\2\2\34\u00ef\3\2\2\2\36\u00f2\3\2\2\2 \u0104\3\2\2\2\"\u0106\3\2\2\2"+
|
||||
"$\u010a\3\2\2\2&\u010e\3\2\2\2(\u0116\3\2\2\2*\u0118\3\2\2\2,\u012b\3"+
|
||||
"\2\2\2.\u01a7\3\2\2\2\60\u01aa\3\2\2\2\62\u01ad\3\2\2\2\64\u01b2\3\2\2"+
|
||||
"\2\66\u01b5\3\2\2\28\u01bc\3\2\2\2:\u01c3\3\2\2\2<\u01ce\3\2\2\2>\u01d2"+
|
||||
"\3\2\2\2@\u01d4\3\2\2\2B\u01d6\3\2\2\2D\u01d8\3\2\2\2F\u01e0\3\2\2\2H"+
|
||||
"\u01e2\3\2\2\2J\u01e4\3\2\2\2L\u01e6\3\2\2\2N\u01ea\3\2\2\2P\u01ec\3\2"+
|
||||
"\2\2R\u01ee\3\2\2\2T\u0202\3\2\2\2V\u0216\3\2\2\2X\u0218\3\2\2\2Z\u021a"+
|
||||
"\3\2\2\2\\\u0223\3\2\2\2^\u0225\3\2\2\2`\u0228\3\2\2\2b\u0235\3\2\2\2"+
|
||||
"d\u0238\3\2\2\2f\u0243\3\2\2\2h\u024e\3\2\2\2j\u0259\3\2\2\2l\u026d\3"+
|
||||
"\2\2\2n\u0270\3\2\2\2p\u027b\3\2\2\2r\u0282\3\2\2\2t\u0289\3\2\2\2v\u0291"+
|
||||
"\3\2\2\2x\u029d\3\2\2\2z\u02a4\3\2\2\2|\u02b3\3\2\2\2~\u02bb\3\2\2\2\u0080"+
|
||||
"\u02cb\3\2\2\2\u0082\u02cd\3\2\2\2\u0084\u02db\3\2\2\2\u0086\u02e4\3\2"+
|
||||
"\2\2\u0088\u02ef\3\2\2\2\u008a\u0300\3\2\2\2\u008c\u008f\5\4\3\2\u008d"+
|
||||
"\u008f\7t\2\2\u008e\u008c\3\2\2\2\u008e\u008d\3\2\2\2\u008f\u0092\3\2"+
|
||||
"\2\2\u0090\u008e\3\2\2\2\u0090\u0091\3\2\2\2\u0091\u0093\3\2\2\2\u0092"+
|
||||
"\u0090\3\2\2\2\u0093\u0094\7\2\2\3\u0094\3\3\2\2\2\u0095\u0098\5\16\b"+
|
||||
"\2\u0096\u0098\5\6\4\2\u0097\u0095\3\2\2\2\u0097\u0096\3\2\2\2\u0098\5"+
|
||||
"\3\2\2\2\u0099\u009b\5B\"\2\u009a\u009c\5L\'\2\u009b\u009a\3\2\2\2\u009b"+
|
||||
"\u009c\3\2\2\2\u009c\u009d\3\2\2\2\u009d\u009e\5d\63\2\u009e\u009f\7t"+
|
||||
"\2\2\u009f\7\3\2\2\2\u00a0\u00bb\5\16\b\2\u00a1\u00bb\5\26\f\2\u00a2\u00bb"+
|
||||
"\5\30\r\2\u00a3\u00bb\5\22\n\2\u00a4\u00bb\5\24\13\2\u00a5\u00bb\5\32"+
|
||||
"\16\2\u00a6\u00bb\5\34\17\2\u00a7\u00bb\5\36\20\2\u00a8\u00bb\5$\23\2"+
|
||||
"\u00a9\u00bb\5&\24\2\u00aa\u00bb\5\f\7\2\u00ab\u00bb\5*\26\2\u00ac\u00bb"+
|
||||
"\58\35\2\u00ad\u00bb\5z>\2\u00ae\u00bb\5~@\2\u00af\u00bb\5`\61\2\u00b0"+
|
||||
"\u00bb\5j\66\2\u00b1\u00bb\5^\60\2\u00b2\u00bb\5<\37\2\u00b3\u00bb\5\u0082"+
|
||||
"B\2\u00b4\u00bb\5\u0084C\2\u00b5\u00bb\5\u0086D\2\u00b6\u00bb\5\u0088"+
|
||||
"E\2\u00b7\u00bb\5> \2\u00b8\u00bb\5@!\2\u00b9\u00bb\5\n\6\2\u00ba\u00a0"+
|
||||
"\3\2\2\2\u00ba\u00a1\3\2\2\2\u00ba\u00a2\3\2\2\2\u00ba\u00a3\3\2\2\2\u00ba"+
|
||||
"\u00a4\3\2\2\2\u00ba\u00a5\3\2\2\2\u00ba\u00a6\3\2\2\2\u00ba\u00a7\3\2"+
|
||||
"\2\2\u00ba\u00a8\3\2\2\2\u00ba\u00a9\3\2\2\2\u00ba\u00aa\3\2\2\2\u00ba"+
|
||||
"\u00ab\3\2\2\2\u00ba\u00ac\3\2\2\2\u00ba\u00ad\3\2\2\2\u00ba\u00ae\3\2"+
|
||||
"\2\2\u00ba\u00af\3\2\2\2\u00ba\u00b0\3\2\2\2\u00ba\u00b1\3\2\2\2\u00ba"+
|
||||
"\u00b2\3\2\2\2\u00ba\u00b3\3\2\2\2\u00ba\u00b4\3\2\2\2\u00ba\u00b5\3\2"+
|
||||
"\2\2\u00ba\u00b6\3\2\2\2\u00ba\u00b7\3\2\2\2\u00ba\u00b8\3\2\2\2\u00ba"+
|
||||
"\u00b9\3\2\2\2\u00bb\t\3\2\2\2\u00bc\u00bd\5B\"\2\u00bd\u00be\7\3\2\2"+
|
||||
"\u00be\13\3\2\2\2\u00bf\u00c2\7\4\2\2\u00c0\u00c3\5L\'\2\u00c1\u00c3\5"+
|
||||
"D#\2\u00c2\u00c0\3\2\2\2\u00c2\u00c1\3\2\2\2\u00c3\r\3\2\2\2\u00c4\u00d0"+
|
||||
"\t\2\2\2\u00c5\u00c7\5\20\t\2\u00c6\u00c5\3\2\2\2\u00c6\u00c7\3\2\2\2"+
|
||||
"\u00c7\u00d1\3\2\2\2\u00c8\u00cd\5\20\t\2\u00c9\u00ca\7\17\2\2\u00ca\u00cc"+
|
||||
"\5\20\t\2\u00cb\u00c9\3\2\2\2\u00cc\u00cf\3\2\2\2\u00cd\u00cb\3\2\2\2"+
|
||||
"\u00cd\u00ce\3\2\2\2\u00ce\u00d1\3\2\2\2\u00cf\u00cd\3\2\2\2\u00d0\u00c6"+
|
||||
"\3\2\2\2\u00d0\u00c8\3\2\2\2\u00d1\17\3\2\2\2\u00d2\u00d6\5V,\2\u00d3"+
|
||||
"\u00d6\5B\"\2\u00d4\u00d6\5L\'\2\u00d5\u00d2\3\2\2\2\u00d5\u00d3\3\2\2"+
|
||||
"\2\u00d5\u00d4\3\2\2\2\u00d6\21\3\2\2\2\u00d7\u00d9\5 \21\2\u00d8\u00da"+
|
||||
"\7~\2\2\u00d9\u00d8\3\2\2\2\u00d9\u00da\3\2\2\2\u00da\u00dd\3\2\2\2\u00db"+
|
||||
"\u00de\5\"\22\2\u00dc\u00de\7\177\2\2\u00dd\u00db\3\2\2\2\u00dd\u00dc"+
|
||||
"\3\2\2\2\u00dd\u00de\3\2\2\2\u00de\u00df\3\2\2\2\u00df\u00e0\5B\"\2\u00e0"+
|
||||
"\23\3\2\2\2\u00e1\u00e2\5B\"\2\u00e2\u00e3\5B\"\2\u00e3\25\3\2\2\2\u00e4"+
|
||||
"\u00e5\5\22\n\2\u00e5\u00e6\7\20\2\2\u00e6\u00e7\5,\27\2\u00e7\27\3\2"+
|
||||
"\2\2\u00e8\u00e9\5\24\13\2\u00e9\u00ea\7\20\2\2\u00ea\u00eb\5,\27\2\u00eb"+
|
||||
"\31\3\2\2\2\u00ec\u00ed\7\21\2\2\u00ed\u00ee\5\26\f\2\u00ee\33\3\2\2\2"+
|
||||
"\u00ef\u00f0\7y\2\2\u00f0\u00f1\5\26\f\2\u00f1\35\3\2\2\2\u00f2\u00f3"+
|
||||
"\7\22\2\2\u00f3\u00f4\5B\"\2\u00f4\u00f5\7\23\2\2\u00f5\u00f6\7t\2\2\u00f6"+
|
||||
"\u00fb\5\22\n\2\u00f7\u00f8\7t\2\2\u00f8\u00fa\5\22\n\2\u00f9\u00f7\3"+
|
||||
"\2\2\2\u00fa\u00fd\3\2\2\2\u00fb\u00f9\3\2\2\2\u00fb\u00fc\3\2\2\2\u00fc"+
|
||||
"\u00ff\3\2\2\2\u00fd\u00fb\3\2\2\2\u00fe\u0100\7t\2\2\u00ff\u00fe\3\2"+
|
||||
"\2\2\u00ff\u0100\3\2\2\2\u0100\u0101\3\2\2\2\u0101\u0102\7\24\2\2\u0102"+
|
||||
"\u0103\7t\2\2\u0103\37\3\2\2\2\u0104\u0105\t\3\2\2\u0105!\3\2\2\2\u0106"+
|
||||
"\u0107\7\34\2\2\u0107\u0108\5,\27\2\u0108\u0109\7\35\2\2\u0109#\3\2\2"+
|
||||
"\2\u010a\u010b\5(\25\2\u010b\u010c\7\20\2\2\u010c\u010d\5,\27\2\u010d"+
|
||||
"%\3\2\2\2\u010e\u010f\5(\25\2\u010f\u0110\t\4\2\2\u0110\u0111\5,\27\2"+
|
||||
"\u0111\'\3\2\2\2\u0112\u0117\5F$\2\u0113\u0117\5D#\2\u0114\u0117\5\60"+
|
||||
"\31\2\u0115\u0117\5\62\32\2\u0116\u0112\3\2\2\2\u0116\u0113\3\2\2\2\u0116"+
|
||||
"\u0114\3\2\2\2\u0116\u0115\3\2\2\2\u0117)\3\2\2\2\u0118\u0119\5(\25\2"+
|
||||
"\u0119\u011a\t\5\2\2\u011a+\3\2\2\2\u011b\u011c\b\27\1\2\u011c\u012c\5"+
|
||||
"\66\34\2\u011d\u011e\t\6\2\2\u011e\u012c\5,\27\31\u011f\u0120\7A\2\2\u0120"+
|
||||
"\u012c\5,\27\13\u0121\u012c\5\\/\2\u0122\u012c\5F$\2\u0123\u012c\5D#\2"+
|
||||
"\u0124\u012c\5\60\31\2\u0125\u012c\5\62\32\2\u0126\u012c\5\64\33\2\u0127"+
|
||||
"\u0128\7B\2\2\u0128\u0129\5,\27\2\u0129\u012a\7C\2\2\u012a\u012c\3\2\2"+
|
||||
"\2\u012b\u011b\3\2\2\2\u012b\u011d\3\2\2\2\u012b\u011f\3\2\2\2\u012b\u0121"+
|
||||
"\3\2\2\2\u012b\u0122\3\2\2\2\u012b\u0123\3\2\2\2\u012b\u0124\3\2\2\2\u012b"+
|
||||
"\u0125\3\2\2\2\u012b\u0126\3\2\2\2\u012b\u0127\3\2\2\2\u012c\u01a4\3\2"+
|
||||
"\2\2\u012d\u012f\f\30\2\2\u012e\u0130\7t\2\2\u012f\u012e\3\2\2\2\u012f"+
|
||||
"\u0130\3\2\2\2\u0130\u0131\3\2\2\2\u0131\u0133\7.\2\2\u0132\u0134\7t\2"+
|
||||
"\2\u0133\u0132\3\2\2\2\u0133\u0134\3\2\2\2\u0134\u0135\3\2\2\2\u0135\u01a3"+
|
||||
"\5,\27\31\u0136\u0138\f\27\2\2\u0137\u0139\7t\2\2\u0138\u0137\3\2\2\2"+
|
||||
"\u0138\u0139\3\2\2\2\u0139\u013a\3\2\2\2\u013a\u013c\t\7\2\2\u013b\u013d"+
|
||||
"\7t\2\2\u013c\u013b\3\2\2\2\u013c\u013d\3\2\2\2\u013d\u013e\3\2\2\2\u013e"+
|
||||
"\u01a3\5,\27\30\u013f\u0141\f\26\2\2\u0140\u0142\7t\2\2\u0141\u0140\3"+
|
||||
"\2\2\2\u0141\u0142\3\2\2\2\u0142\u0143\3\2\2\2\u0143\u0145\t\b\2\2\u0144"+
|
||||
"\u0146\7t\2\2\u0145\u0144\3\2\2\2\u0145\u0146\3\2\2\2\u0146\u0147\3\2"+
|
||||
"\2\2\u0147\u01a3\5,\27\27\u0148\u014a\f\25\2\2\u0149\u014b\7t\2\2\u014a"+
|
||||
"\u0149\3\2\2\2\u014a\u014b\3\2\2\2\u014b\u014c\3\2\2\2\u014c\u014e\t\t"+
|
||||
"\2\2\u014d\u014f\7t\2\2\u014e\u014d\3\2\2\2\u014e\u014f\3\2\2\2\u014f"+
|
||||
"\u0150\3\2\2\2\u0150\u01a3\5,\27\26\u0151\u0153\f\24\2\2\u0152\u0154\7"+
|
||||
"t\2\2\u0153\u0152\3\2\2\2\u0153\u0154\3\2\2\2\u0154\u0155\3\2\2\2\u0155"+
|
||||
"\u0157\t\n\2\2\u0156\u0158\7t\2\2\u0157\u0156\3\2\2\2\u0157\u0158\3\2"+
|
||||
"\2\2\u0158\u0159\3\2\2\2\u0159\u01a3\5,\27\25\u015a\u015c\f\23\2\2\u015b"+
|
||||
"\u015d\7t\2\2\u015c\u015b\3\2\2\2\u015c\u015d\3\2\2\2\u015d\u015e\3\2"+
|
||||
"\2\2\u015e\u0160\t\13\2\2\u015f\u0161\7t\2\2\u0160\u015f\3\2\2\2\u0160"+
|
||||
"\u0161\3\2\2\2\u0161\u0162\3\2\2\2\u0162\u01a3\5,\27\24\u0163\u0165\f"+
|
||||
"\22\2\2\u0164\u0166\7t\2\2\u0165\u0164\3\2\2\2\u0165\u0166\3\2\2\2\u0166"+
|
||||
"\u0167\3\2\2\2\u0167\u0169\7y\2\2\u0168\u016a\7t\2\2\u0169\u0168\3\2\2"+
|
||||
"\2\u0169\u016a\3\2\2\2\u016a\u016b\3\2\2\2\u016b\u01a3\5,\27\23\u016c"+
|
||||
"\u016e\f\21\2\2\u016d\u016f\7t\2\2\u016e\u016d\3\2\2\2\u016e\u016f\3\2"+
|
||||
"\2\2\u016f\u0170\3\2\2\2\u0170\u0172\7:\2\2\u0171\u0173\7t\2\2\u0172\u0171"+
|
||||
"\3\2\2\2\u0172\u0173\3\2\2\2\u0173\u0174\3\2\2\2\u0174\u01a3\5,\27\22"+
|
||||
"\u0175\u0177\f\20\2\2\u0176\u0178\7t\2\2\u0177\u0176\3\2\2\2\u0177\u0178"+
|
||||
"\3\2\2\2\u0178\u0179\3\2\2\2\u0179\u017b\7;\2\2\u017a\u017c\7t\2\2\u017b"+
|
||||
"\u017a\3\2\2\2\u017b\u017c\3\2\2\2\u017c\u017d\3\2\2\2\u017d\u01a3\5,"+
|
||||
"\27\21\u017e\u0180\f\16\2\2\u017f\u0181\7t\2\2\u0180\u017f\3\2\2\2\u0180"+
|
||||
"\u0181\3\2\2\2\u0181\u0182\3\2\2\2\u0182\u0184\7>\2\2\u0183\u0185\7t\2"+
|
||||
"\2\u0184\u0183\3\2\2\2\u0184\u0185\3\2\2\2\u0185\u0186\3\2\2\2\u0186\u01a3"+
|
||||
"\5,\27\17\u0187\u0189\f\r\2\2\u0188\u018a\7t\2\2\u0189\u0188\3\2\2\2\u0189"+
|
||||
"\u018a\3\2\2\2\u018a\u018b\3\2\2\2\u018b\u018d\7?\2\2\u018c\u018e\7t\2"+
|
||||
"\2\u018d\u018c\3\2\2\2\u018d\u018e\3\2\2\2\u018e\u018f\3\2\2\2\u018f\u01a3"+
|
||||
"\5,\27\16\u0190\u0192\f\f\2\2\u0191\u0193\7t\2\2\u0192\u0191\3\2\2\2\u0192"+
|
||||
"\u0193\3\2\2\2\u0193\u0194\3\2\2\2\u0194\u0196\7@\2\2\u0195\u0197\7t\2"+
|
||||
"\2\u0196\u0195\3\2\2\2\u0196\u0197\3\2\2\2\u0197\u0198\3\2\2\2\u0198\u01a3"+
|
||||
"\5,\27\r\u0199\u019a\f\17\2\2\u019a\u019b\7<\2\2\u019b\u019e\5,\27\2\u019c"+
|
||||
"\u019d\7=\2\2\u019d\u019f\5,\27\2\u019e\u019c\3\2\2\2\u019e\u019f\3\2"+
|
||||
"\2\2\u019f\u01a3\3\2\2\2\u01a0\u01a1\f\4\2\2\u01a1\u01a3\5.\30\2\u01a2"+
|
||||
"\u012d\3\2\2\2\u01a2\u0136\3\2\2\2\u01a2\u013f\3\2\2\2\u01a2\u0148\3\2"+
|
||||
"\2\2\u01a2\u0151\3\2\2\2\u01a2\u015a\3\2\2\2\u01a2\u0163\3\2\2\2\u01a2"+
|
||||
"\u016c\3\2\2\2\u01a2\u0175\3\2\2\2\u01a2\u017e\3\2\2\2\u01a2\u0187\3\2"+
|
||||
"\2\2\u01a2\u0190\3\2\2\2\u01a2\u0199\3\2\2\2\u01a2\u01a0\3\2\2\2\u01a3"+
|
||||
"\u01a6\3\2\2\2\u01a4\u01a2\3\2\2\2\u01a4\u01a5\3\2\2\2\u01a5-\3\2\2\2"+
|
||||
"\u01a6\u01a4\3\2\2\2\u01a7\u01a8\7D\2\2\u01a8\u01a9\5 \21\2\u01a9/\3\2"+
|
||||
"\2\2\u01aa\u01ab\5D#\2\u01ab\u01ac\5\"\22\2\u01ac\61\3\2\2\2\u01ad\u01ae"+
|
||||
"\7E\2\2\u01ae\u01af\7B\2\2\u01af\u01b0\5,\27\2\u01b0\u01b1\7C\2\2\u01b1"+
|
||||
"\63\3\2\2\2\u01b2\u01b3\7y\2\2\u01b3\u01b4\5D#\2\u01b4\65\3\2\2\2\u01b5"+
|
||||
"\u01b6\5D#\2\u01b6\u01b8\7B\2\2\u01b7\u01b9\5:\36\2\u01b8\u01b7\3\2\2"+
|
||||
"\2\u01b8\u01b9\3\2\2\2\u01b9\u01ba\3\2\2\2\u01ba\u01bb\7C\2\2\u01bb\67"+
|
||||
"\3\2\2\2\u01bc\u01bd\5D#\2\u01bd\u01bf\7B\2\2\u01be\u01c0\5:\36\2\u01bf"+
|
||||
"\u01be\3\2\2\2\u01bf\u01c0\3\2\2\2\u01c0\u01c1\3\2\2\2\u01c1\u01c2\7C"+
|
||||
"\2\2\u01c29\3\2\2\2\u01c3\u01cb\5,\27\2\u01c4\u01c6\7\17\2\2\u01c5\u01c7"+
|
||||
"\7t\2\2\u01c6\u01c5\3\2\2\2\u01c6\u01c7\3\2\2\2\u01c7\u01c8\3\2\2\2\u01c8"+
|
||||
"\u01ca\5,\27\2\u01c9\u01c4\3\2\2\2\u01ca\u01cd\3\2\2\2\u01cb\u01c9\3\2"+
|
||||
"\2\2\u01cb\u01cc\3\2\2\2\u01cc;\3\2\2\2\u01cd\u01cb\3\2\2\2\u01ce\u01d0"+
|
||||
"\7F\2\2\u01cf\u01d1\5,\27\2\u01d0\u01cf\3\2\2\2\u01d0\u01d1\3\2\2\2\u01d1"+
|
||||
"=\3\2\2\2\u01d2\u01d3\7G\2\2\u01d3?\3\2\2\2\u01d4\u01d5\7H\2\2\u01d5A"+
|
||||
"\3\2\2\2\u01d6\u01d7\7u\2\2\u01d7C\3\2\2\2\u01d8\u01dd\7u\2\2\u01d9\u01da"+
|
||||
"\7I\2\2\u01da\u01dc\7u\2\2\u01db\u01d9\3\2\2\2\u01dc\u01df\3\2\2\2\u01dd"+
|
||||
"\u01db\3\2\2\2\u01dd\u01de\3\2\2\2\u01deE\3\2\2\2\u01df\u01dd\3\2\2\2"+
|
||||
"\u01e0\u01e1\t\f\2\2\u01e1G\3\2\2\2\u01e2\u01e3\t\r\2\2\u01e3I\3\2\2\2"+
|
||||
"\u01e4\u01e5\t\16\2\2\u01e5K\3\2\2\2\u01e6\u01e8\t\17\2\2\u01e7\u01e9"+
|
||||
"\5N(\2\u01e8\u01e7\3\2\2\2\u01e8\u01e9\3\2\2\2\u01e9M\3\2\2\2\u01ea\u01eb"+
|
||||
"\7T\2\2\u01ebO\3\2\2\2\u01ec\u01ed\t\20\2\2\u01edQ\3\2\2\2\u01ee\u01f0"+
|
||||
"\7\34\2\2\u01ef\u01f1\7t\2\2\u01f0\u01ef\3\2\2\2\u01f0\u01f1\3\2\2\2\u01f1"+
|
||||
"\u01f2\3\2\2\2\u01f2\u01fa\5,\27\2\u01f3\u01f5\7\17\2\2\u01f4\u01f6\7"+
|
||||
"t\2\2\u01f5\u01f4\3\2\2\2\u01f5\u01f6\3\2\2\2\u01f6\u01f7\3\2\2\2\u01f7"+
|
||||
"\u01f9\5,\27\2\u01f8\u01f3\3\2\2\2\u01f9\u01fc\3\2\2\2\u01fa\u01f8\3\2"+
|
||||
"\2\2\u01fa\u01fb\3\2\2\2\u01fb\u01fe\3\2\2\2\u01fc\u01fa\3\2\2\2\u01fd"+
|
||||
"\u01ff\7t\2\2\u01fe\u01fd\3\2\2\2\u01fe\u01ff\3\2\2\2\u01ff\u0200\3\2"+
|
||||
"\2\2\u0200\u0201\7\35\2\2\u0201S\3\2\2\2\u0202\u0204\7\23\2\2\u0203\u0205"+
|
||||
"\7t\2\2\u0204\u0203\3\2\2\2\u0204\u0205\3\2\2\2\u0205\u0206\3\2\2\2\u0206"+
|
||||
"\u020e\5,\27\2\u0207\u0209\7\17\2\2\u0208\u020a\7t\2\2\u0209\u0208\3\2"+
|
||||
"\2\2\u0209\u020a\3\2\2\2\u020a\u020b\3\2\2\2\u020b\u020d\5,\27\2\u020c"+
|
||||
"\u0207\3\2\2\2\u020d\u0210\3\2\2\2\u020e\u020c\3\2\2\2\u020e\u020f\3\2"+
|
||||
"\2\2\u020f\u0212\3\2\2\2\u0210\u020e\3\2\2\2\u0211\u0213\7t\2\2\u0212"+
|
||||
"\u0211\3\2\2\2\u0212\u0213\3\2\2\2\u0213\u0214\3\2\2\2\u0214\u0215\7\24"+
|
||||
"\2\2\u0215U\3\2\2\2\u0216\u0217\7{\2\2\u0217W\3\2\2\2\u0218\u0219\7}\2"+
|
||||
"\2\u0219Y\3\2\2\2\u021a\u021b\7z\2\2\u021b[\3\2\2\2\u021c\u0224\5L\'\2"+
|
||||
"\u021d\u0224\5P)\2\u021e\u0224\5R*\2\u021f\u0224\5V,\2\u0220\u0224\5X"+
|
||||
"-\2\u0221\u0224\5Z.\2\u0222\u0224\5T+\2\u0223\u021c\3\2\2\2\u0223\u021d"+
|
||||
"\3\2\2\2\u0223\u021e\3\2\2\2\u0223\u021f\3\2\2\2\u0223\u0220\3\2\2\2\u0223"+
|
||||
"\u0221\3\2\2\2\u0223\u0222\3\2\2\2\u0224]\3\2\2\2\u0225\u0226\7W\2\2\u0226"+
|
||||
"\u0227\7|\2\2\u0227_\3\2\2\2\u0228\u0229\7X\2\2\u0229\u022a\5B\"\2\u022a"+
|
||||
"\u022c\7B\2\2\u022b\u022d\5f\64\2\u022c\u022b\3\2\2\2\u022c\u022d\3\2"+
|
||||
"\2\2\u022d\u022e\3\2\2\2\u022e\u0230\7C\2\2\u022f\u0231\5b\62\2\u0230"+
|
||||
"\u022f\3\2\2\2\u0230\u0231\3\2\2\2\u0231\u0232\3\2\2\2\u0232\u0233\5d"+
|
||||
"\63\2\u0233\u0234\7t\2\2\u0234a\3\2\2\2\u0235\u0236\7Y\2\2\u0236\u0237"+
|
||||
"\5h\65\2\u0237c\3\2\2\2\u0238\u0239\7\23\2\2\u0239\u023e\7t\2\2\u023a"+
|
||||
"\u023d\5\b\5\2\u023b\u023d\7t\2\2\u023c\u023a\3\2\2\2\u023c\u023b\3\2"+
|
||||
"\2\2\u023d\u0240\3\2\2\2\u023e\u023c\3\2\2\2\u023e\u023f\3\2\2\2\u023f"+
|
||||
"\u0241\3\2\2\2\u0240\u023e\3\2\2\2\u0241\u0242\7\24\2\2\u0242e\3\2\2\2"+
|
||||
"\u0243\u024b\5\22\n\2\u0244\u0246\7\17\2\2\u0245\u0247\7t\2\2\u0246\u0245"+
|
||||
"\3\2\2\2\u0246\u0247\3\2\2\2\u0247\u0248\3\2\2\2\u0248\u024a\5\22\n\2"+
|
||||
"\u0249\u0244\3\2\2\2\u024a\u024d\3\2\2\2\u024b\u0249\3\2\2\2\u024b\u024c"+
|
||||
"\3\2\2\2\u024cg\3\2\2\2\u024d\u024b\3\2\2\2\u024e\u0256\5 \21\2\u024f"+
|
||||
"\u0251\7\17\2\2\u0250\u0252\7t\2\2\u0251\u0250\3\2\2\2\u0251\u0252\3\2"+
|
||||
"\2\2\u0252\u0253\3\2\2\2\u0253\u0255\5 \21\2\u0254\u024f\3\2\2\2\u0255"+
|
||||
"\u0258\3\2\2\2\u0256\u0254\3\2\2\2\u0256\u0257\3\2\2\2\u0257i\3\2\2\2"+
|
||||
"\u0258\u0256\3\2\2\2\u0259\u025a\7Z\2\2\u025a\u025b\5B\"\2\u025b\u025d"+
|
||||
"\7B\2\2\u025c\u025e\5n8\2\u025d\u025c\3\2\2\2\u025d\u025e\3\2\2\2\u025e"+
|
||||
"\u025f\3\2\2\2\u025f\u0261\7C\2\2\u0260\u0262\7t\2\2\u0261\u0260\3\2\2"+
|
||||
"\2\u0261\u0262\3\2\2\2\u0262\u0264\3\2\2\2\u0263\u0265\5r:\2\u0264\u0263"+
|
||||
"\3\2\2\2\u0264\u0265\3\2\2\2\u0265\u0267\3\2\2\2\u0266\u0268\5v<\2\u0267"+
|
||||
"\u0266\3\2\2\2\u0267\u0268\3\2\2\2\u0268\u026b\3\2\2\2\u0269\u026c\5l"+
|
||||
"\67\2\u026a\u026c\5d\63\2\u026b\u0269\3\2\2\2\u026b\u026a\3\2\2\2\u026c"+
|
||||
"k\3\2\2\2\u026d\u026e\7\20\2\2\u026e\u026f\5L\'\2\u026fm\3\2\2\2\u0270"+
|
||||
"\u0278\5p9\2\u0271\u0273\7\17\2\2\u0272\u0274\7t\2\2\u0273\u0272\3\2\2"+
|
||||
"\2\u0273\u0274\3\2\2\2\u0274\u0275\3\2\2\2\u0275\u0277\5p9\2\u0276\u0271"+
|
||||
"\3\2\2\2\u0277\u027a\3\2\2\2\u0278\u0276\3\2\2\2\u0278\u0279\3\2\2\2\u0279"+
|
||||
"o\3\2\2\2\u027a\u0278\3\2\2\2\u027b\u027c\5\22\n\2\u027c\u0280\7E\2\2"+
|
||||
"\u027d\u0281\5H%\2\u027e\u0281\5J&\2\u027f\u0281\7[\2\2\u0280\u027d\3"+
|
||||
"\2\2\2\u0280\u027e\3\2\2\2\u0280\u027f\3\2\2\2\u0281q\3\2\2\2\u0282\u0283"+
|
||||
"\7\\\2\2\u0283\u0285\7B\2\2\u0284\u0286\5t;\2\u0285\u0284\3\2\2\2\u0285"+
|
||||
"\u0286\3\2\2\2\u0286\u0287\3\2\2\2\u0287\u0288\7C\2\2\u0288s\3\2\2\2\u0289"+
|
||||
"\u028e\5F$\2\u028a\u028b\7\17\2\2\u028b\u028d\5F$\2\u028c\u028a\3\2\2"+
|
||||
"\2\u028d\u0290\3\2\2\2\u028e\u028c\3\2\2\2\u028e\u028f\3\2\2\2\u028fu"+
|
||||
"\3\2\2\2\u0290\u028e\3\2\2\2\u0291\u0292\7Y\2\2\u0292\u029a\5x=\2\u0293"+
|
||||
"\u0295\7\17\2\2\u0294\u0296\7t\2\2\u0295\u0294\3\2\2\2\u0295\u0296\3\2"+
|
||||
"\2\2\u0296\u0297\3\2\2\2\u0297\u0299\5x=\2\u0298\u0293\3\2\2\2\u0299\u029c"+
|
||||
"\3\2\2\2\u029a\u0298\3\2\2\2\u029a\u029b\3\2\2\2\u029bw\3\2\2\2\u029c"+
|
||||
"\u029a\3\2\2\2\u029d\u029e\5 \21\2\u029e\u02a2\7E\2\2\u029f\u02a3\5H%"+
|
||||
"\2\u02a0\u02a3\5J&\2\u02a1\u02a3\7[\2\2\u02a2\u029f\3\2\2\2\u02a2\u02a0"+
|
||||
"\3\2\2\2\u02a2\u02a1\3\2\2\2\u02a3y\3\2\2\2\u02a4\u02a5\7]\2\2\u02a5\u02a7"+
|
||||
"\5,\27\2\u02a6\u02a8\7t\2\2\u02a7\u02a6\3\2\2\2\u02a7\u02a8\3\2\2\2\u02a8"+
|
||||
"\u02ab\3\2\2\2\u02a9\u02ac\5\b\5\2\u02aa\u02ac\5d\63\2\u02ab\u02a9\3\2"+
|
||||
"\2\2\u02ab\u02aa\3\2\2\2\u02ac\u02ae\3\2\2\2\u02ad\u02af\7t\2\2\u02ae"+
|
||||
"\u02ad\3\2\2\2\u02ae\u02af\3\2\2\2\u02af\u02b1\3\2\2\2\u02b0\u02b2\5|"+
|
||||
"?\2\u02b1\u02b0\3\2\2\2\u02b1\u02b2\3\2\2\2\u02b2{\3\2\2\2\u02b3\u02b5"+
|
||||
"\7^\2\2\u02b4\u02b6\7t\2\2\u02b5\u02b4\3\2\2\2\u02b5\u02b6\3\2\2\2\u02b6"+
|
||||
"\u02b9\3\2\2\2\u02b7\u02ba\5\b\5\2\u02b8\u02ba\5d\63\2\u02b9\u02b7\3\2"+
|
||||
"\2\2\u02b9\u02b8\3\2\2\2\u02ba}\3\2\2\2\u02bb\u02bd\5\u0080A\2\u02bc\u02be"+
|
||||
"\7t\2\2\u02bd\u02bc\3\2\2\2\u02bd\u02be\3\2\2\2\u02be\u02c1\3\2\2\2\u02bf"+
|
||||
"\u02c2\5\b\5\2\u02c0\u02c2\5d\63\2\u02c1\u02bf\3\2\2\2\u02c1\u02c0\3\2"+
|
||||
"\2\2\u02c2\u02c4\3\2\2\2\u02c3\u02c5\7t\2\2\u02c4\u02c3\3\2\2\2\u02c4"+
|
||||
"\u02c5\3\2\2\2\u02c5\u02c7\3\2\2\2\u02c6\u02c8\5|?\2\u02c7\u02c6\3\2\2"+
|
||||
"\2\u02c7\u02c8\3\2\2\2\u02c8\u02c9\3\2\2\2\u02c9\u02ca\7t\2\2\u02ca\177"+
|
||||
"\3\2\2\2\u02cb\u02cc\t\21\2\2\u02cc\u0081\3\2\2\2\u02cd\u02d0\7k\2\2\u02ce"+
|
||||
"\u02d1\5F$\2\u02cf\u02d1\5B\"\2\u02d0\u02ce\3\2\2\2\u02d0\u02cf\3\2\2"+
|
||||
"\2\u02d1\u02d2\3\2\2\2\u02d2\u02d3\7l\2\2\u02d3\u02d5\5,\27\2\u02d4\u02d6"+
|
||||
"\7t\2\2\u02d5\u02d4\3\2\2\2\u02d5\u02d6\3\2\2\2\u02d6\u02d9\3\2\2\2\u02d7"+
|
||||
"\u02da\5\b\5\2\u02d8\u02da\5d\63\2\u02d9\u02d7\3\2\2\2\u02d9\u02d8\3\2"+
|
||||
"\2\2\u02da\u0083\3\2\2\2\u02db\u02dc\7m\2\2\u02dc\u02de\5,\27\2\u02dd"+
|
||||
"\u02df\7t\2\2\u02de\u02dd\3\2\2\2\u02de\u02df\3\2\2\2\u02df\u02e2\3\2"+
|
||||
"\2\2\u02e0\u02e3\5\b\5\2\u02e1\u02e3\5d\63\2\u02e2\u02e0\3\2\2\2\u02e2"+
|
||||
"\u02e1\3\2\2\2\u02e3\u0085\3\2\2\2\u02e4\u02e7\7n\2\2\u02e5\u02e8\5\b"+
|
||||
"\5\2\u02e6\u02e8\5d\63\2\u02e7\u02e5\3\2\2\2\u02e7\u02e6\3\2\2\2\u02e8"+
|
||||
"\u02ea\3\2\2\2\u02e9\u02eb\7t\2\2\u02ea\u02e9\3\2\2\2\u02ea\u02eb\3\2"+
|
||||
"\2\2\u02eb\u02ec\3\2\2\2\u02ec\u02ed\7o\2\2\u02ed\u02ee\5,\27\2\u02ee"+
|
||||
"\u0087\3\2\2\2\u02ef\u02f0\7p\2\2\u02f0\u02f1\5,\27\2\u02f1\u02f2\7\23"+
|
||||
"\2\2\u02f2\u02f7\7t\2\2\u02f3\u02f6\5\u008aF\2\u02f4\u02f6\7t\2\2\u02f5"+
|
||||
"\u02f3\3\2\2\2\u02f5\u02f4\3\2\2\2\u02f6\u02f9\3\2\2\2\u02f7\u02f5\3\2"+
|
||||
"\2\2\u02f7\u02f8\3\2\2\2\u02f8\u02fa\3\2\2\2\u02f9\u02f7\3\2\2\2\u02fa"+
|
||||
"\u02fc\7\24\2\2\u02fb\u02fd\7t\2\2\u02fc\u02fb\3\2\2\2\u02fc\u02fd\3\2"+
|
||||
"\2\2\u02fd\u0089\3\2\2\2\u02fe\u0301\5:\36\2\u02ff\u0301\7^\2\2\u0300"+
|
||||
"\u02fe\3\2\2\2\u0300\u02ff\3\2\2\2\u0301\u0302\3\2\2\2\u0302\u0305\7Y"+
|
||||
"\2\2\u0303\u0306\5\b\5\2\u0304\u0306\5d\63\2\u0305\u0303\3\2\2\2\u0305"+
|
||||
"\u0304\3\2\2\2\u0306\u008b\3\2\2\2h\u008e\u0090\u0097\u009b\u00ba\u00c2"+
|
||||
"\u00c6\u00cd\u00d0\u00d5\u00d9\u00dd\u00fb\u00ff\u0116\u012b\u012f\u0133"+
|
||||
"\u0138\u013c\u0141\u0145\u014a\u014e\u0153\u0157\u015c\u0160\u0165\u0169"+
|
||||
"\u016e\u0172\u0177\u017b\u0180\u0184\u0189\u018d\u0192\u0196\u019e\u01a2"+
|
||||
@@ -5572,8 +5545,7 @@ public class prog8Parser extends Parser {
|
||||
"\u0204\u0209\u020e\u0212\u0223\u022c\u0230\u023c\u023e\u0246\u024b\u0251"+
|
||||
"\u0256\u025d\u0261\u0264\u0267\u026b\u0273\u0278\u0280\u0285\u028e\u0295"+
|
||||
"\u029a\u02a2\u02a7\u02ab\u02ae\u02b1\u02b5\u02b9\u02bd\u02c1\u02c4\u02c7"+
|
||||
"\u02cf\u02d2\u02d6\u02db\u02df\u02e4\u02e8\u02ed\u02f0\u02fb\u02fd\u0302"+
|
||||
"\u0306\u030b";
|
||||
"\u02d0\u02d5\u02d9\u02de\u02e2\u02e7\u02ea\u02f5\u02f7\u02fc\u0300\u0305";
|
||||
public static final ATN _ATN =
|
||||
new ATNDeserializer().deserialize(_serializedATN.toCharArray());
|
||||
static {
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env sh
|
||||
|
||||
rm *.jar *.asm *.prg *.vm.txt *.vice-mon-list
|
||||
rm -f *.jar *.asm *.prg *.vm.txt *.vice-mon-list
|
||||
rm -rf build out
|
||||
|
||||
@@ -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
@@ -1,2 +1,2 @@
|
||||
include ':parser'
|
||||
include ':compiler'
|
||||
include ':compiler'
|
||||
Reference in new issue
Block a user