mirror of
https://github.com/irmen/prog8.git
synced 2026-10-22 20:02:27 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ce00e49a89 | ||
|
|
d494f9d66b | ||
|
|
c35a183a64 | ||
|
|
9cdd5fe7f2 | ||
|
|
c21428215e | ||
|
|
64d5af46f5 | ||
|
|
25846ea18a | ||
|
|
798383596d | ||
|
|
9ca71bc937 | ||
|
|
5407429ec0 | ||
|
|
ee5c94f6db | ||
|
|
91045afbee | ||
|
|
3f64782023 | ||
|
|
f8d35f9502 | ||
|
|
ea78d3ec9a | ||
|
|
e056a28316 | ||
|
|
0bea721c2e | ||
|
|
e1b89494d0 | ||
|
|
cd8e7f3912 | ||
|
|
50604c25c2 | ||
|
|
aa6b2357d8 | ||
|
|
5b2d29bef6 | ||
|
|
a296d26328 | ||
|
|
d01a26ec61 | ||
|
|
efd7d6f0c0 | ||
|
|
b55be093be | ||
|
|
7c1d5cadd7 | ||
|
|
dd1592b03b | ||
|
|
9b37ac483f | ||
|
|
090820958e | ||
|
|
ac21e1be5c | ||
|
|
5196443b26 | ||
|
|
c8531cbeb1 | ||
|
|
c560abedba | ||
|
|
9b952fbc44 | ||
|
|
ccdf05e922 | ||
|
|
c3d74f2ae9 | ||
|
|
f47498888c | ||
|
|
5665a7f0cb |
No files matched your search
Generated
+1
-1
@@ -4,6 +4,6 @@
|
||||
<option name="jvmTarget" value="11" />
|
||||
</component>
|
||||
<component name="KotlinJpsPluginSettings">
|
||||
<option name="version" value="1.8.20" />
|
||||
<option name="version" value="1.8.20-release-327" />
|
||||
</component>
|
||||
</project>
|
||||
@@ -76,7 +76,9 @@ IntelliJ IDEA with the Kotlin plugin).
|
||||
|
||||
It's handy to have an emulator (or a real machine perhaps!) to run the programs on. The compiler assumes the presence
|
||||
of the [Vice emulator](http://vice-emu.sourceforge.net/) for the C64 target,
|
||||
and the [x16emu emulator](https://github.com/commanderx16/x16-emulator) for the CommanderX16 target.
|
||||
and a recent emulator version (R42 or newer) for the CommanderX16, such as [x16emu](https://cx16forum.com/forum/viewforum.php?f=30)
|
||||
(preferred, this is the official emulator. If required, source code is [here](https://github.com/X16Community/x16-emulator/)).
|
||||
There is also [Box16](https://github.com/indigodarkwolf/box16) which has powerful debugging features.
|
||||
|
||||
**Syntax highlighting:** for a few different editors, syntax highlighting definition files are provided.
|
||||
Look in the [syntax-files](https://github.com/irmen/prog8/tree/master/syntax-files) directory in the github repository to find them.
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
package prog8.code.ast
|
||||
|
||||
import prog8.code.core.*
|
||||
import prog8.code.core.DataType
|
||||
import prog8.code.core.Encoding
|
||||
import prog8.code.core.NumericDatatypes
|
||||
import prog8.code.core.Position
|
||||
import java.util.*
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.round
|
||||
|
||||
@@ -131,6 +131,7 @@ abstract class Zeropage(options: CompilationOptions): MemoryAllocator(options) {
|
||||
}
|
||||
|
||||
|
||||
// TODO: this class is not yet used
|
||||
class GoldenRam(options: CompilationOptions, val region: UIntRange): MemoryAllocator(options) {
|
||||
private var nextLocation: UInt = region.first
|
||||
|
||||
|
||||
@@ -13,6 +13,10 @@ class AtariZeropage(options: CompilationOptions) : Zeropage(options) {
|
||||
override val SCRATCH_W2 = 0xcfu // temp storage 2 for a word $cf+$d0 TODO is $d0 okay to use?
|
||||
|
||||
init {
|
||||
if (options.floats) {
|
||||
throw InternalCompilerException("Atari target doesn't yet support floating point routines")
|
||||
}
|
||||
|
||||
if (options.floats && options.zeropage !in arrayOf(
|
||||
ZeropageType.FLOATSAFE,
|
||||
ZeropageType.BASICSAFE,
|
||||
@@ -39,9 +43,9 @@ class AtariZeropage(options: CompilationOptions) : Zeropage(options) {
|
||||
}
|
||||
}
|
||||
|
||||
val distictFree = free.distinct()
|
||||
val distinctFree = free.distinct()
|
||||
free.clear()
|
||||
free.addAll(distictFree)
|
||||
free.addAll(distinctFree)
|
||||
|
||||
removeReservedFromFreePool()
|
||||
}
|
||||
|
||||
@@ -6,15 +6,22 @@ import prog8.code.core.Zeropage
|
||||
import prog8.code.core.ZeropageType
|
||||
|
||||
|
||||
// reference: "Mapping the C128" zero page chapter.
|
||||
|
||||
class C128Zeropage(options: CompilationOptions) : Zeropage(options) {
|
||||
|
||||
override val SCRATCH_B1 = 0x9bu // temp storage for a single byte
|
||||
override val SCRATCH_REG = 0x9cu // temp storage for a register, must be B1+1
|
||||
override val SCRATCH_B1 = 0x74u // temp storage for a single byte
|
||||
override val SCRATCH_REG = 0x75u // temp storage for a register, must be B1+1
|
||||
override val SCRATCH_W1 = 0xfbu // temp storage 1 for a word $fb+$fc
|
||||
override val SCRATCH_W2 = 0xfdu // temp storage 2 for a word $fd+$fe
|
||||
|
||||
|
||||
init {
|
||||
if (options.floats) {
|
||||
throw InternalCompilerException("C128 target doesn't yet support floating point routines")
|
||||
// note: in git commit labeled 'c128: remove floats module' the floats.p8 and floats.asm files are removed,
|
||||
// they could be retrieved again at a later time if the compiler somehow *does* store the fp variables in bank1.
|
||||
}
|
||||
|
||||
if (options.floats && options.zeropage !in arrayOf(
|
||||
ZeropageType.FLOATSAFE,
|
||||
ZeropageType.BASICSAFE,
|
||||
@@ -24,23 +31,42 @@ class C128Zeropage(options: CompilationOptions) : Zeropage(options) {
|
||||
|
||||
when (options.zeropage) {
|
||||
ZeropageType.FULL -> {
|
||||
// TODO all c128 usable zero page locations, except the ones used by the system's IRQ routine
|
||||
free.addAll(0x0au..0xffu) // TODO c128 what about $02-$09?
|
||||
// TODO c128 free.removeAll(setOf(0xa0u, 0xa1u, 0xa2u, 0x91u, 0xc0u, 0xc5u, 0xcbu, 0xf5u, 0xf6u)) // these are updated by IRQ
|
||||
// $00/$01 are data port IO registers, // $02-$09 are storage locations for JSRFAR and such
|
||||
free.addAll(0x0au..0xffu)
|
||||
free.removeAll(listOf(0x90u, 0x91u, 0xa0u, 0xa1u, 0xa2u, 0xc0u, 0xccu, 0xcdu, 0xd0u, 0xd1u, 0xd2u, 0xd3u, 0xd4u, 0xd5u, 0xf7u)) // these are updated/used by IRQ
|
||||
}
|
||||
ZeropageType.KERNALSAFE -> {
|
||||
free.addAll(0x0au..0x8fu) // BASIC variables
|
||||
free.addAll(listOf(0x92u, 0x96u, 0x9bu, 0x9cu, 0x9eu, 0x9fu, 0xa4u, 0xa7u, 0xa8u, 0xa9u, 0xaau, 0xabu,
|
||||
0xb0u, 0xb1u, 0xb4u, 0xb5u, 0xb6u))
|
||||
}
|
||||
ZeropageType.KERNALSAFE,
|
||||
ZeropageType.FLOATSAFE,
|
||||
ZeropageType.BASICSAFE -> {
|
||||
free.clear() // TODO c128 usable zero page addresses
|
||||
free.addAll(listOf(0x0bu, 0x0cu, 0x0du, 0x0eu, 0x0fu, 0x10u, 0x11u, 0x12u, 0x16u, 0x17u, 0x18u, 0x19u, 0x1au))
|
||||
free.addAll(0x1bu..0x23u)
|
||||
free.addAll(listOf(0x3fu, 0x40u, 0x41u, 0x42u, 0x43u, 0x44u, 0x47u, 0x48u, 0x49u, 0x4au, 0x4bu, 0x4cu, 0x4fu,
|
||||
0x55u, 0x56u, 0x57u, 0x58u,
|
||||
0x74u, 0x75u, 0x78u, 0x80u, 0x83u, 0x87u, 0x88u, 0x89u, 0x8au, 0x8bu, 0x8cu, 0x8du, 0x8eu, 0x8fu,
|
||||
0x92u, 0x96u, 0x9bu, 0x9cu, 0x9eu, 0x9fu, 0xa4u, 0xa7u, 0xa8u, 0xa9u, 0xaau, 0xabu,
|
||||
0xb0u, 0xb1u, 0xb4u, 0xb5u, 0xb6u
|
||||
))
|
||||
|
||||
// if(options.zeropage==ZeropageType.BASICSAFE) {
|
||||
// can also clobber the FP locations (unconditionally, because the C128 target doesn't support floating point calculations in prog8 at this time0
|
||||
free.addAll(listOf(0x14u, 0x28u, 0x29u, 0x2au, 0x2bu, 0x2cu,
|
||||
0x50u, 0x51u, 0x52u, 0x53u, 0x54u, 0x59u, 0x5au, 0x5bu, 0x5cu, 0x5du, 0x5eu, 0x5fu, 0x60u, 0x61u, 0x62u,
|
||||
0x63u, 0x64u, 0x65u, 0x66u, 0x67u, 0x68u,
|
||||
0x6au, 0x6bu, 0x6cu, 0x6du, 0x6eu, 0x6fu, 0x71u))
|
||||
// }
|
||||
}
|
||||
ZeropageType.DONTUSE -> {
|
||||
free.clear() // don't use zeropage at all
|
||||
}
|
||||
}
|
||||
|
||||
val distictFree = free.distinct()
|
||||
val distinctFree = free.distinct()
|
||||
free.clear()
|
||||
free.addAll(distictFree)
|
||||
free.addAll(distinctFree)
|
||||
|
||||
removeReservedFromFreePool()
|
||||
}
|
||||
|
||||
@@ -65,9 +65,9 @@ class C64Zeropage(options: CompilationOptions) : Zeropage(options) {
|
||||
}
|
||||
}
|
||||
|
||||
val distictFree = free.distinct()
|
||||
val distinctFree = free.distinct()
|
||||
free.clear()
|
||||
free.addAll(distictFree)
|
||||
free.addAll(distinctFree)
|
||||
|
||||
removeReservedFromFreePool()
|
||||
|
||||
|
||||
@@ -64,7 +64,7 @@ class CX16MachineDefinition: IMachineDefinition {
|
||||
|
||||
override fun initializeMemoryAreas(compilerOptions: CompilationOptions) {
|
||||
zeropage = CX16Zeropage(compilerOptions)
|
||||
golden = GoldenRam(compilerOptions, 0x0600u until 0x0800u)
|
||||
golden = GoldenRam(compilerOptions, 0x0400u until ESTACK_LO)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -43,9 +43,9 @@ class CX16Zeropage(options: CompilationOptions) : Zeropage(options) {
|
||||
else -> throw InternalCompilerException("for this machine target, zero page type 'floatsafe' is not available. ${options.zeropage}")
|
||||
}
|
||||
|
||||
val distictFree = free.distinct()
|
||||
val distinctFree = free.distinct()
|
||||
free.clear()
|
||||
free.addAll(distictFree)
|
||||
free.addAll(distinctFree)
|
||||
|
||||
removeReservedFromFreePool()
|
||||
|
||||
|
||||
@@ -483,7 +483,7 @@ class AsmGen6502Internal (
|
||||
when(reg) {
|
||||
RegisterOrPair.A,
|
||||
RegisterOrPair.X,
|
||||
RegisterOrPair.Y -> assignmentAsmGen.assignRegisterByte(target, reg.asCpuRegister())
|
||||
RegisterOrPair.Y -> assignmentAsmGen.assignRegisterByte(target, reg.asCpuRegister(), target.datatype in SignedDatatypes)
|
||||
RegisterOrPair.AX,
|
||||
RegisterOrPair.AY,
|
||||
RegisterOrPair.XY,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package prog8.codegen.cpu6502
|
||||
|
||||
import prog8.code.StStaticVariable
|
||||
import prog8.code.ast.*
|
||||
import prog8.code.core.*
|
||||
import prog8.codegen.cpu6502.assignment.*
|
||||
@@ -90,8 +91,8 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
val var3name = asmgen.asmVariableName(fcall.args[3] as PtIdentifier)
|
||||
val divisionTarget = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UBYTE, fcall.definingISub(), fcall.args[2].position, var2name)
|
||||
val remainderTarget = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UBYTE, fcall.definingISub(), fcall.args[3].position, var3name)
|
||||
assignAsmGen.assignRegisterByte(remainderTarget, CpuRegister.A)
|
||||
assignAsmGen.assignRegisterByte(divisionTarget, CpuRegister.Y)
|
||||
assignAsmGen.assignRegisterByte(remainderTarget, CpuRegister.A, false)
|
||||
assignAsmGen.assignRegisterByte(divisionTarget, CpuRegister.Y, false)
|
||||
}
|
||||
|
||||
private fun funcDivmodW(fcall: PtBuiltinFunctionCall) {
|
||||
@@ -303,7 +304,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
asmgen.out(" jsr prog8_lib.func_sqrt16_stack")
|
||||
else {
|
||||
asmgen.out(" jsr prog8_lib.func_sqrt16_into_A")
|
||||
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, false, fcall.position, scope, asmgen), CpuRegister.A)
|
||||
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, false, fcall.position, scope, asmgen), CpuRegister.A, false)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -337,6 +338,16 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
lda #$numElements
|
||||
jsr prog8_lib.func_reverse_w""")
|
||||
}
|
||||
DataType.STR -> {
|
||||
val stringLength = (symbol as StStaticVariable).length!!-1
|
||||
asmgen.out("""
|
||||
lda #<$varName
|
||||
ldy #>$varName
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
lda #$stringLength
|
||||
jsr prog8_lib.func_reverse_b""")
|
||||
}
|
||||
DataType.ARRAY_F -> {
|
||||
asmgen.out("""
|
||||
lda #<$varName
|
||||
@@ -381,6 +392,16 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
lda #$numElements""")
|
||||
asmgen.out(if (decl.type == DataType.ARRAY_UW) " jsr prog8_lib.func_sort_uw" else " jsr prog8_lib.func_sort_w")
|
||||
}
|
||||
DataType.STR -> {
|
||||
val stringLength = (symbol as StStaticVariable).length!!-1
|
||||
asmgen.out("""
|
||||
lda #<$varName
|
||||
ldy #>$varName
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
lda #$stringLength
|
||||
jsr prog8_lib.func_sort_ub""")
|
||||
}
|
||||
DataType.ARRAY_F -> throw AssemblyError("sorting of floating point array is not supported")
|
||||
else -> throw AssemblyError("weird type")
|
||||
}
|
||||
@@ -596,9 +617,10 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
throw AssemblyError("non-array var indexing requires bytes dt")
|
||||
asmgen.assignExpressionToVariable(arrayvar, "prog8_lib.${operation}_array_u${dt}._arg_target", DataType.UWORD)
|
||||
} else {
|
||||
val p = arrayvar.parent
|
||||
val addressOf = PtAddressOf(arrayvar.position)
|
||||
addressOf.add(arrayvar)
|
||||
addressOf.parent = arrayvar.parent.parent
|
||||
addressOf.parent = p
|
||||
asmgen.assignExpressionToVariable(addressOf, "prog8_lib.${operation}_array_u${dt}._arg_target", DataType.UWORD)
|
||||
}
|
||||
asmgen.assignExpressionToVariable(indexer.index, "prog8_lib.${operation}_array_u${dt}._arg_index", DataType.UBYTE)
|
||||
@@ -625,7 +647,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
DataType.FLOAT -> asmgen.out(" jsr floats.func_sign_f_into_A")
|
||||
else -> throw AssemblyError("weird type $dt")
|
||||
}
|
||||
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, false, fcall.position, scope, asmgen), CpuRegister.A)
|
||||
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, false, fcall.position, scope, asmgen), CpuRegister.A, true)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -646,7 +668,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
DataType.ARRAY_F -> asmgen.out(" jsr floats.func_${fcall.name}_f_into_A | ldy #0")
|
||||
else -> throw AssemblyError("weird type $dt")
|
||||
}
|
||||
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, false, fcall.position, scope, asmgen), CpuRegister.A)
|
||||
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, false, fcall.position, scope, asmgen), CpuRegister.A, dt in SignedDatatypes)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -806,9 +828,10 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
|
||||
private fun funcMkword(fcall: PtBuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?) {
|
||||
if(resultToStack) {
|
||||
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.Y) // msb
|
||||
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.A) // msb
|
||||
asmgen.out(" pha")
|
||||
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.A) // lsb
|
||||
asmgen.out(" sta P8ESTACK_LO,x | tya | sta P8ESTACK_HI,x | dex")
|
||||
asmgen.out(" sta P8ESTACK_LO,x | pla | sta P8ESTACK_HI,x | dex")
|
||||
} else {
|
||||
val reg = resultRegister ?: RegisterOrPair.AY
|
||||
var needAsave = asmgen.needAsaveForExpr(fcall.args[0])
|
||||
|
||||
@@ -150,8 +150,7 @@ internal class ProgramAndVarsGen(
|
||||
asmgen.out(" rts")
|
||||
}
|
||||
"c128" -> {
|
||||
asmgen.out(" jsr main.start")
|
||||
// TODO c128: how to bank basic+kernal back in?
|
||||
asmgen.out(" jsr main.start | lda #0 | sta ${"$"}ff00")
|
||||
if(!options.noSysInit)
|
||||
asmgen.out(" jmp ${compTarget.name}.cleanup_at_exit")
|
||||
else
|
||||
|
||||
@@ -1,14 +1,11 @@
|
||||
package prog8.codegen.cpu6502.assignment
|
||||
|
||||
import prog8.code.StStaticVariable
|
||||
import prog8.code.SymbolTable
|
||||
import prog8.code.ast.*
|
||||
import prog8.code.core.*
|
||||
import prog8.codegen.cpu6502.AsmGen6502Internal
|
||||
import prog8.codegen.cpu6502.ExpressionsAsmGen
|
||||
import prog8.codegen.cpu6502.VariableAllocator
|
||||
import prog8.codegen.cpu6502.returnsWhatWhere
|
||||
import java.util.*
|
||||
|
||||
|
||||
internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
@@ -78,7 +75,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
asmgen.out(" lda $arrayVarName | sta P8ZP_SCRATCH_W1 | lda $arrayVarName+1 | sta P8ZP_SCRATCH_W1+1")
|
||||
asmgen.out(" lda (P8ZP_SCRATCH_W1),y")
|
||||
}
|
||||
assignRegisterByte(assign.target, CpuRegister.A)
|
||||
assignRegisterByte(assign.target, CpuRegister.A, elementDt in SignedDatatypes)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -89,7 +86,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
when (elementDt) {
|
||||
in ByteDatatypes -> {
|
||||
asmgen.out(" lda $arrayVarName+$indexValue")
|
||||
assignRegisterByte(assign.target, CpuRegister.A)
|
||||
assignRegisterByte(assign.target, CpuRegister.A, elementDt in SignedDatatypes)
|
||||
}
|
||||
in WordDatatypes -> {
|
||||
asmgen.out(" lda $arrayVarName+$indexValue | ldy $arrayVarName+$indexValue+1")
|
||||
@@ -107,7 +104,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
in ByteDatatypes -> {
|
||||
asmgen.loadScaledArrayIndexIntoRegister(value, elementDt, CpuRegister.Y)
|
||||
asmgen.out(" lda $arrayVarName,y")
|
||||
assignRegisterByte(assign.target, CpuRegister.A)
|
||||
assignRegisterByte(assign.target, CpuRegister.A, elementDt in SignedDatatypes)
|
||||
}
|
||||
in WordDatatypes -> {
|
||||
asmgen.loadScaledArrayIndexIntoRegister(value, elementDt, CpuRegister.Y)
|
||||
@@ -134,7 +131,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
fun assignViaExprEval(expression: PtExpression) {
|
||||
assignExpressionToVariable(expression, "P8ZP_SCRATCH_W2", DataType.UWORD)
|
||||
asmgen.loadAFromZpPointerVar("P8ZP_SCRATCH_W2")
|
||||
assignRegisterByte(assign.target, CpuRegister.A)
|
||||
assignRegisterByte(assign.target, CpuRegister.A, false)
|
||||
}
|
||||
|
||||
val value = assign.source.memory!!
|
||||
@@ -150,7 +147,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
require(!asmgen.options.useNewExprCode)
|
||||
val addrExpr = value.address as PtBinaryExpression
|
||||
if(asmgen.tryOptimizedPointerAccessWithA(addrExpr, addrExpr.operator, false)) {
|
||||
assignRegisterByte(assign.target, CpuRegister.A)
|
||||
assignRegisterByte(assign.target, CpuRegister.A, false)
|
||||
} else {
|
||||
assignViaExprEval(value.address)
|
||||
}
|
||||
@@ -210,9 +207,9 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
else -> {
|
||||
// do NOT restore X register before assigning the result values first
|
||||
when (returnValue.first.registerOrPair) {
|
||||
RegisterOrPair.A -> assignRegisterByte(assign.target, CpuRegister.A)
|
||||
RegisterOrPair.X -> assignRegisterByte(assign.target, CpuRegister.X)
|
||||
RegisterOrPair.Y -> assignRegisterByte(assign.target, CpuRegister.Y)
|
||||
RegisterOrPair.A -> assignRegisterByte(assign.target, CpuRegister.A, returnValue.second in SignedDatatypes)
|
||||
RegisterOrPair.X -> assignRegisterByte(assign.target, CpuRegister.X, returnValue.second in SignedDatatypes)
|
||||
RegisterOrPair.Y -> assignRegisterByte(assign.target, CpuRegister.Y, returnValue.second in SignedDatatypes)
|
||||
RegisterOrPair.AX -> assignVirtualRegister(assign.target, RegisterOrPair.AX)
|
||||
RegisterOrPair.AY -> assignVirtualRegister(assign.target, RegisterOrPair.AY)
|
||||
RegisterOrPair.XY -> assignVirtualRegister(assign.target, RegisterOrPair.XY)
|
||||
@@ -250,7 +247,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
if(assign.target.register==null) {
|
||||
// still need to assign the result to the target variable/etc.
|
||||
when(returnDt) {
|
||||
in ByteDatatypes -> assignRegisterByte(assign.target, CpuRegister.A) // function's byte result is in A
|
||||
in ByteDatatypes -> assignRegisterByte(assign.target, CpuRegister.A, returnDt in SignedDatatypes) // function's byte result is in A
|
||||
in WordDatatypes -> assignRegisterpairWord(assign.target, RegisterOrPair.AY) // function's word result is in AY
|
||||
DataType.STR -> {
|
||||
when (assign.target.datatype) {
|
||||
@@ -299,7 +296,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
}
|
||||
is PtContainmentCheck -> {
|
||||
containmentCheckIntoA(value)
|
||||
assignRegisterByte(assign.target, CpuRegister.A)
|
||||
assignRegisterByte(assign.target, CpuRegister.A, false)
|
||||
}
|
||||
is PtBinaryExpression -> {
|
||||
require(!asmgen.options.useNewExprCode)
|
||||
@@ -340,7 +337,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
when(target.datatype) {
|
||||
in ByteDatatypes -> {
|
||||
asmgen.out(" lda cx16.${register.toString().lowercase()}L")
|
||||
assignRegisterByte(target, CpuRegister.A)
|
||||
assignRegisterByte(target, CpuRegister.A, false)
|
||||
}
|
||||
in WordDatatypes -> assignRegisterpairWord(target, register)
|
||||
else -> throw AssemblyError("expected byte or word")
|
||||
@@ -401,7 +398,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
"^", "xor" -> asmgen.out(" eor P8ZP_SCRATCH_B1")
|
||||
else -> throw AssemblyError("invalid operator")
|
||||
}
|
||||
assignRegisterByte(assign.target, CpuRegister.A)
|
||||
assignRegisterByte(assign.target, CpuRegister.A, false)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -467,7 +464,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
+ lda #0
|
||||
+""")
|
||||
}
|
||||
assignRegisterByte(assign.target, CpuRegister.A)
|
||||
assignRegisterByte(assign.target, CpuRegister.A, false)
|
||||
return true
|
||||
} else if(expr.left.type in WordDatatypes && expr.right.type in WordDatatypes &&
|
||||
expr.left.isSimple() && expr.right.isSimple()) {
|
||||
@@ -498,7 +495,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
+ lda #1
|
||||
+""")
|
||||
}
|
||||
assignRegisterByte(assign.target, CpuRegister.A)
|
||||
assignRegisterByte(assign.target, CpuRegister.A, false)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
@@ -516,7 +513,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
asmgen.out(" clc | adc $symname")
|
||||
else
|
||||
asmgen.out(" sec | sbc $symname")
|
||||
assignRegisterByte(assign.target, CpuRegister.A)
|
||||
assignRegisterByte(assign.target, CpuRegister.A, dt in SignedDatatypes)
|
||||
return true
|
||||
}
|
||||
is PtNumber -> {
|
||||
@@ -525,7 +522,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
asmgen.out(" clc | adc #${right.number.toHex()}")
|
||||
else
|
||||
asmgen.out(" sec | sbc #${right.number.toHex()}")
|
||||
assignRegisterByte(assign.target, CpuRegister.A)
|
||||
assignRegisterByte(assign.target, CpuRegister.A, dt in SignedDatatypes)
|
||||
return true
|
||||
}
|
||||
else -> return false
|
||||
@@ -653,7 +650,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
}
|
||||
}
|
||||
}
|
||||
assignRegisterByte(assign.target, CpuRegister.A)
|
||||
assignRegisterByte(assign.target, CpuRegister.A, signed)
|
||||
return true
|
||||
} else if(dt in WordDatatypes && shifts in 0..7) {
|
||||
val signed = dt == DataType.WORD
|
||||
@@ -701,7 +698,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
"^", "xor" -> asmgen.out(" eor $operand")
|
||||
else -> throw AssemblyError("invalid operator")
|
||||
}
|
||||
assignRegisterByte(target, CpuRegister.A)
|
||||
assignRegisterByte(target, CpuRegister.A, false)
|
||||
}
|
||||
|
||||
private fun assignLogicalWithSimpleRightOperandWord(target: AsmAssignTarget, left: PtExpression, operator: String, right: PtExpression) {
|
||||
@@ -743,7 +740,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
beq ++
|
||||
+ lda #1
|
||||
+""")
|
||||
assignRegisterByte(assign.target, CpuRegister.A)
|
||||
assignRegisterByte(assign.target, CpuRegister.A, false)
|
||||
return true
|
||||
}
|
||||
in WordDatatypes -> {
|
||||
@@ -756,13 +753,13 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
beq ++
|
||||
+ lda #1
|
||||
+""")
|
||||
assignRegisterByte(assign.target, CpuRegister.A)
|
||||
assignRegisterByte(assign.target, CpuRegister.A, false)
|
||||
return true
|
||||
}
|
||||
DataType.FLOAT -> {
|
||||
assignExpressionToRegister(expr.left, RegisterOrPair.FAC1, true)
|
||||
asmgen.out(" jsr floats.SIGN | and #1 | eor #1")
|
||||
assignRegisterByte(assign.target, CpuRegister.A)
|
||||
assignRegisterByte(assign.target, CpuRegister.A, false)
|
||||
return true
|
||||
}
|
||||
else->{
|
||||
@@ -776,7 +773,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
assignExpressionToRegister(expr.left, RegisterOrPair.A, dt==DataType.BYTE)
|
||||
asmgen.out(" beq + | lda #1")
|
||||
asmgen.out("+")
|
||||
assignRegisterByte(assign.target, CpuRegister.A)
|
||||
assignRegisterByte(assign.target, CpuRegister.A, false)
|
||||
return true
|
||||
}
|
||||
in WordDatatypes -> {
|
||||
@@ -784,13 +781,13 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
asmgen.out(" sty P8ZP_SCRATCH_B1 | ora P8ZP_SCRATCH_B1")
|
||||
asmgen.out(" beq + | lda #1")
|
||||
asmgen.out("+")
|
||||
assignRegisterByte(assign.target, CpuRegister.A)
|
||||
assignRegisterByte(assign.target, CpuRegister.A, false)
|
||||
return true
|
||||
}
|
||||
DataType.FLOAT -> {
|
||||
assignExpressionToRegister(expr.left, RegisterOrPair.FAC1, true)
|
||||
asmgen.out(" jsr floats.SIGN")
|
||||
assignRegisterByte(assign.target, CpuRegister.A)
|
||||
assignRegisterByte(assign.target, CpuRegister.A, true)
|
||||
return true
|
||||
}
|
||||
else->{
|
||||
@@ -871,7 +868,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
}
|
||||
else -> throw AssemblyError("can't use Z or N flags as return 'values'")
|
||||
}
|
||||
assignRegisterByte(target, CpuRegister.A)
|
||||
assignRegisterByte(target, CpuRegister.A, false)
|
||||
}
|
||||
|
||||
private fun assignTypeCastedValue(target: AsmAssignTarget, targetDt: DataType, value: PtExpression, origTypeCastExpression: PtTypeCast) {
|
||||
@@ -2060,7 +2057,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
}
|
||||
}
|
||||
|
||||
internal fun assignRegisterByte(target: AsmAssignTarget, register: CpuRegister) {
|
||||
internal fun assignRegisterByte(target: AsmAssignTarget, register: CpuRegister, signed: Boolean) {
|
||||
// we make an exception in the type check for assigning something to a register pair AX, AY or XY
|
||||
// these will be correctly typecasted from a byte to a word value here
|
||||
if(target.register !in setOf(RegisterOrPair.AX, RegisterOrPair.AY, RegisterOrPair.XY))
|
||||
@@ -2102,9 +2099,40 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
RegisterOrPair.A -> {}
|
||||
RegisterOrPair.X -> { asmgen.out(" tax") }
|
||||
RegisterOrPair.Y -> { asmgen.out(" tay") }
|
||||
RegisterOrPair.AY -> { asmgen.out(" ldy #0") }
|
||||
RegisterOrPair.AX -> { asmgen.out(" ldx #0") }
|
||||
RegisterOrPair.XY -> { asmgen.out(" tax | ldy #0") }
|
||||
RegisterOrPair.AY -> {
|
||||
if(signed)
|
||||
asmgen.out("""
|
||||
ldy #0
|
||||
cmp #${'$'}80
|
||||
bcc +
|
||||
dey
|
||||
+""")
|
||||
else
|
||||
asmgen.out(" ldy #0")
|
||||
}
|
||||
RegisterOrPair.AX -> {
|
||||
if(signed)
|
||||
asmgen.out("""
|
||||
ldx #0
|
||||
cmp #${'$'}80
|
||||
bcc +
|
||||
dex
|
||||
+""")
|
||||
else
|
||||
asmgen.out(" ldx #0")
|
||||
}
|
||||
RegisterOrPair.XY -> {
|
||||
if(signed)
|
||||
asmgen.out("""
|
||||
tax
|
||||
ldy #0
|
||||
cpx #${'$'}80
|
||||
bcc +
|
||||
dey
|
||||
+""")
|
||||
else
|
||||
asmgen.out(" tax | ldy #0")
|
||||
}
|
||||
RegisterOrPair.FAC1, RegisterOrPair.FAC2 -> throw AssemblyError("expected type cast to float")
|
||||
in Cx16VirtualRegisters -> {
|
||||
// only assign a single byte to the virtual register's Lsb
|
||||
@@ -2116,9 +2144,41 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
RegisterOrPair.A -> { asmgen.out(" txa") }
|
||||
RegisterOrPair.X -> { }
|
||||
RegisterOrPair.Y -> { asmgen.out(" txy") }
|
||||
RegisterOrPair.AY -> { asmgen.out(" txa | ldy #0") }
|
||||
RegisterOrPair.AX -> { asmgen.out(" txa | ldx #0") }
|
||||
RegisterOrPair.XY -> { asmgen.out(" ldy #0") }
|
||||
RegisterOrPair.AY -> {
|
||||
if(signed)
|
||||
asmgen.out("""
|
||||
txa
|
||||
ldy #0
|
||||
cmp #${'$'}80
|
||||
bcc +
|
||||
dey
|
||||
+""")
|
||||
else
|
||||
asmgen.out(" txa | ldy #0")
|
||||
}
|
||||
RegisterOrPair.AX -> {
|
||||
if(signed)
|
||||
asmgen.out("""
|
||||
txa
|
||||
ldx #0
|
||||
cmp #${'$'}80
|
||||
bcc +
|
||||
dex
|
||||
+""")
|
||||
else
|
||||
asmgen.out(" txa | ldx #0")
|
||||
}
|
||||
RegisterOrPair.XY -> {
|
||||
if(signed)
|
||||
asmgen.out("""
|
||||
ldy #0
|
||||
cpx #${'$'}80
|
||||
bcc +
|
||||
dey
|
||||
+""")
|
||||
else
|
||||
asmgen.out(" ldy #0")
|
||||
}
|
||||
RegisterOrPair.FAC1, RegisterOrPair.FAC2 -> throw AssemblyError("expected type cast to float")
|
||||
in Cx16VirtualRegisters -> {
|
||||
// only assign a single byte to the virtual register's Lsb
|
||||
@@ -2130,9 +2190,43 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
RegisterOrPair.A -> { asmgen.out(" tya") }
|
||||
RegisterOrPair.X -> { asmgen.out(" tyx") }
|
||||
RegisterOrPair.Y -> { }
|
||||
RegisterOrPair.AY -> { asmgen.out(" tya | ldy #0") }
|
||||
RegisterOrPair.AX -> { asmgen.out(" tya | ldx #0") }
|
||||
RegisterOrPair.XY -> { asmgen.out(" tya | tax | ldy #0") }
|
||||
RegisterOrPair.AY -> {
|
||||
if(signed)
|
||||
asmgen.out("""
|
||||
tya
|
||||
ldy #0
|
||||
cmp #${'$'}80
|
||||
bcc +
|
||||
dey
|
||||
+""")
|
||||
else
|
||||
asmgen.out(" tya | ldy #0")
|
||||
}
|
||||
RegisterOrPair.AX -> {
|
||||
if(signed)
|
||||
asmgen.out("""
|
||||
tya
|
||||
ldx #0
|
||||
cmp #${'$'}80
|
||||
bcc +
|
||||
dex
|
||||
+""")
|
||||
else
|
||||
asmgen.out(" tya | ldx #0")
|
||||
}
|
||||
RegisterOrPair.XY -> {
|
||||
if(signed)
|
||||
asmgen.out("""
|
||||
tya
|
||||
tax
|
||||
ldy #0
|
||||
cpx #${'$'}80
|
||||
bcc +
|
||||
dey
|
||||
+""")
|
||||
else
|
||||
asmgen.out(" tya | tax | ldy #0")
|
||||
}
|
||||
RegisterOrPair.FAC1, RegisterOrPair.FAC2 -> throw AssemblyError("expected type cast to float")
|
||||
in Cx16VirtualRegisters -> {
|
||||
// only assign a single byte to the virtual register's Lsb
|
||||
@@ -2669,7 +2763,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
}
|
||||
TargetStorageKind.ARRAY -> {
|
||||
asmgen.out(" lda ${address.toHex()}")
|
||||
assignRegisterByte(target, CpuRegister.A)
|
||||
assignRegisterByte(target, CpuRegister.A, false)
|
||||
}
|
||||
TargetStorageKind.REGISTER -> when(target.register!!) {
|
||||
RegisterOrPair.A -> asmgen.out(" lda ${address.toHex()}")
|
||||
@@ -2709,7 +2803,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
}
|
||||
TargetStorageKind.ARRAY -> {
|
||||
asmgen.loadByteFromPointerIntoA(identifier)
|
||||
assignRegisterByte(target, CpuRegister.A)
|
||||
assignRegisterByte(target, CpuRegister.A, false)
|
||||
}
|
||||
TargetStorageKind.REGISTER -> {
|
||||
asmgen.loadByteFromPointerIntoA(identifier)
|
||||
|
||||
@@ -2,7 +2,10 @@ package prog8.codegen.experimental
|
||||
|
||||
import prog8.code.SymbolTable
|
||||
import prog8.code.ast.PtProgram
|
||||
import prog8.code.core.*
|
||||
import prog8.code.core.CompilationOptions
|
||||
import prog8.code.core.IAssemblyProgram
|
||||
import prog8.code.core.ICodeGeneratorBackend
|
||||
import prog8.code.core.IErrorReporter
|
||||
import prog8.codegen.intermediate.IRCodeGen
|
||||
import prog8.intermediate.IRFileWriter
|
||||
|
||||
|
||||
@@ -138,16 +138,16 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
||||
"+" -> { }
|
||||
"-" -> {
|
||||
code += if(address!=null)
|
||||
IRInstruction(Opcode.NEGM, vmDt, value = address)
|
||||
IRInstruction(Opcode.NEGM, vmDt, address = address)
|
||||
else
|
||||
IRInstruction(Opcode.NEGM, vmDt, labelSymbol = symbol)
|
||||
}
|
||||
"~" -> {
|
||||
val regMask = codeGen.registers.nextFree()
|
||||
val mask = if(vmDt==IRDataType.BYTE) 0x00ff else 0xffff
|
||||
code += IRInstruction(Opcode.LOAD, vmDt, reg1=regMask, value = mask)
|
||||
code += IRInstruction(Opcode.LOAD, vmDt, reg1=regMask, immediate = mask)
|
||||
code += if(address!=null)
|
||||
IRInstruction(Opcode.XORM, vmDt, reg1=regMask, value = address)
|
||||
IRInstruction(Opcode.XORM, vmDt, reg1=regMask, address = address)
|
||||
else
|
||||
IRInstruction(Opcode.XORM, vmDt, reg1=regMask, labelSymbol = symbol)
|
||||
}
|
||||
@@ -214,7 +214,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
||||
if(zero) {
|
||||
// there's no STOREZIX instruction
|
||||
valueRegister = codeGen.registers.nextFree()
|
||||
code += IRInstruction(Opcode.LOAD, vmDt, reg1=valueRegister, value=0)
|
||||
code += IRInstruction(Opcode.LOAD, vmDt, reg1=valueRegister, immediate = 0)
|
||||
}
|
||||
code += IRInstruction(Opcode.STOREIX, vmDt, reg1=valueRegister, reg2=idxReg, labelSymbol = variable)
|
||||
result += code
|
||||
@@ -261,7 +261,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
||||
require(vmDt== IRDataType.BYTE) { "must be byte type ${targetMemory.position}"}
|
||||
if(zero) {
|
||||
if(targetMemory.address is PtNumber) {
|
||||
val chunk = IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREZM, vmDt, value=(targetMemory.address as PtNumber).number.toInt()) }
|
||||
val chunk = IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREZM, vmDt, address = (targetMemory.address as PtNumber).number.toInt()) }
|
||||
result += chunk
|
||||
} else {
|
||||
val tr = expressionEval.translateExpression(targetMemory.address)
|
||||
@@ -271,7 +271,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
||||
}
|
||||
} else {
|
||||
if(targetMemory.address is PtNumber) {
|
||||
val chunk = IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREM, vmDt, reg1=valueRegister, value=(targetMemory.address as PtNumber).number.toInt()) }
|
||||
val chunk = IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREM, vmDt, reg1=valueRegister, address=(targetMemory.address as PtNumber).number.toInt()) }
|
||||
result += chunk
|
||||
} else {
|
||||
val tr = expressionEval.translateExpression(targetMemory.address)
|
||||
@@ -300,7 +300,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
||||
if(codeGen.options.useNewExprCode) {
|
||||
val tr = expressionEval.translateExpression(array.index)
|
||||
result += tr.chunks
|
||||
addInstr(result, IRInstruction(Opcode.MUL, tr.dt, reg1=tr.resultReg, value = itemsize), null)
|
||||
addInstr(result, IRInstruction(Opcode.MUL, tr.dt, reg1=tr.resultReg, immediate = itemsize), null)
|
||||
return Pair(result, tr.resultReg)
|
||||
} else {
|
||||
val mult: PtExpression
|
||||
|
||||
@@ -52,20 +52,28 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
val number = call.args[0]
|
||||
val divident = call.args[1]
|
||||
val divisionReg: Int
|
||||
val remainderReg: Int
|
||||
if(divident is PtNumber) {
|
||||
val tr = exprGen.translateExpression(number)
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
addInstr(result, IRInstruction(Opcode.DIVMOD, type, reg1 = tr.resultReg, value=divident.number.toInt()), null)
|
||||
addInstr(result, IRInstruction(Opcode.DIVMOD, type, reg1 = tr.resultReg, immediate = divident.number.toInt()), null)
|
||||
divisionReg = tr.resultReg
|
||||
remainderReg = codeGen.registers.nextFree()
|
||||
} else {
|
||||
val numTr = exprGen.translateExpression(number)
|
||||
addToResult(result, numTr, numTr.resultReg, -1)
|
||||
val dividentTr = exprGen.translateExpression(divident)
|
||||
addToResult(result, dividentTr, dividentTr.resultReg, -1)
|
||||
addInstr(result, IRInstruction(Opcode.DIVMODR, type, reg1 = numTr.resultReg, reg2=dividentTr.resultReg), null)
|
||||
divisionReg = numTr.resultReg
|
||||
remainderReg = dividentTr.resultReg
|
||||
}
|
||||
// DIVMOD result convention: division in r0, remainder in r1
|
||||
result += assignRegisterTo(call.args[2], 0)
|
||||
result += assignRegisterTo(call.args[3], 1)
|
||||
// DIVMOD result convention: on value stack, division and remainder on top.
|
||||
addInstr(result, IRInstruction(Opcode.POP, type, reg1=remainderReg), null)
|
||||
addInstr(result, IRInstruction(Opcode.POP, type, reg1=divisionReg), null)
|
||||
result += assignRegisterTo(call.args[2], divisionReg)
|
||||
result += assignRegisterTo(call.args[3], remainderReg)
|
||||
return ExpressionCodeResult(result, type, -1, -1)
|
||||
}
|
||||
|
||||
@@ -73,12 +81,13 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
val left = exprGen.translateExpression(call.args[0])
|
||||
val right = exprGen.translateExpression(call.args[1])
|
||||
val targetReg = codeGen.registers.nextFree()
|
||||
addToResult(result, left, 65500, -1)
|
||||
addToResult(result, right, 65501, -1)
|
||||
addInstr(result, IRInstruction(Opcode.SYSCALL, value=IMSyscall.COMPARE_STRINGS.number), null)
|
||||
addInstr(result, IRInstruction(Opcode.LOADR, IRDataType.BYTE, reg1=targetReg, reg2=0), null)
|
||||
return ExpressionCodeResult(result, IRDataType.BYTE, targetReg, -1)
|
||||
addToResult(result, left, left.resultReg, -1)
|
||||
addInstr(result, IRInstruction(Opcode.SETPARAM, IRDataType.WORD, reg1=left.resultReg, immediate = 0), null)
|
||||
addToResult(result, right, right.resultReg, -1)
|
||||
addInstr(result, IRInstruction(Opcode.SETPARAM, IRDataType.WORD, reg1=right.resultReg, immediate = 1), null)
|
||||
addInstr(result, IRInstruction(Opcode.SYSCALL, immediate = IMSyscall.COMPARE_STRINGS.number), null)
|
||||
addInstr(result, IRInstruction(Opcode.POP, IRDataType.BYTE, reg1=left.resultReg), null)
|
||||
return ExpressionCodeResult(result, IRDataType.BYTE, left.resultReg, -1)
|
||||
}
|
||||
|
||||
private fun funcCmp(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||
@@ -108,13 +117,13 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
}
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
val tr = exprGen.translateExpression(call.args[0])
|
||||
addToResult(result, tr, SyscallRegisterBase, -1)
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = SyscallRegisterBase+1, value = array.length)
|
||||
it += IRInstruction(Opcode.SYSCALL, value = syscall.number)
|
||||
// SysCall call convention: return value in register r0
|
||||
if(tr.resultReg!=0)
|
||||
it += IRInstruction(Opcode.LOADR, IRDataType.BYTE, reg1 = tr.resultReg, reg2 = 0)
|
||||
it += IRInstruction(Opcode.SETPARAM, IRDataType.WORD, reg1 = tr.resultReg, immediate = 0)
|
||||
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = tr.resultReg, immediate = array.length)
|
||||
it += IRInstruction(Opcode.SETPARAM, IRDataType.BYTE, reg1 = tr.resultReg, immediate = 1)
|
||||
it += IRInstruction(Opcode.SYSCALL, immediate = syscall.number)
|
||||
it += IRInstruction(Opcode.POP, IRDataType.BYTE, reg1=tr.resultReg)
|
||||
}
|
||||
return ExpressionCodeResult(result, IRDataType.BYTE, tr.resultReg, -1)
|
||||
}
|
||||
@@ -133,13 +142,13 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
}
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
val tr = exprGen.translateExpression(call.args[0])
|
||||
addToResult(result, tr, SyscallRegisterBase, -1)
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = SyscallRegisterBase+1, value = array.length)
|
||||
it += IRInstruction(Opcode.SYSCALL, value = syscall.number)
|
||||
// SysCall call convention: return value in register r0
|
||||
if(tr.resultReg!=0)
|
||||
it += IRInstruction(Opcode.LOADR, IRDataType.BYTE, reg1 = tr.resultReg, reg2 = 0)
|
||||
it += IRInstruction(Opcode.SETPARAM, IRDataType.WORD, reg1 = tr.resultReg, immediate = 0)
|
||||
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = tr.resultReg, immediate = array.length)
|
||||
it += IRInstruction(Opcode.SETPARAM, IRDataType.BYTE, reg1 = tr.resultReg, immediate = 1)
|
||||
it += IRInstruction(Opcode.SYSCALL, immediate = syscall.number)
|
||||
it += IRInstruction(Opcode.POP, IRDataType.BYTE, reg1=tr.resultReg)
|
||||
}
|
||||
return ExpressionCodeResult(result, IRDataType.BYTE, tr.resultReg, -1)
|
||||
}
|
||||
@@ -164,8 +173,8 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
val compareReg = codeGen.registers.nextFree()
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOADR, IRDataType.BYTE, reg1=compareReg, reg2=tr.resultReg)
|
||||
it += IRInstruction(Opcode.AND, IRDataType.BYTE, reg1=compareReg, value=0x80)
|
||||
it += IRInstruction(Opcode.BZ, IRDataType.BYTE, reg1=compareReg, labelSymbol = notNegativeLabel)
|
||||
it += IRInstruction(Opcode.AND, IRDataType.BYTE, reg1=compareReg, immediate = 0x80)
|
||||
it += IRInstruction(Opcode.BEQ, IRDataType.BYTE, reg1=compareReg, immediate = 0, labelSymbol = notNegativeLabel)
|
||||
it += IRInstruction(Opcode.NEG, IRDataType.BYTE, reg1=tr.resultReg)
|
||||
it += IRInstruction(Opcode.EXT, IRDataType.BYTE, reg1=tr.resultReg)
|
||||
}
|
||||
@@ -177,8 +186,8 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
val compareReg = codeGen.registers.nextFree()
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOADR, IRDataType.WORD, reg1=compareReg, reg2=tr.resultReg)
|
||||
it += IRInstruction(Opcode.AND, IRDataType.WORD, reg1=compareReg, value=0x8000)
|
||||
it += IRInstruction(Opcode.BZ, IRDataType.WORD, reg1=compareReg, labelSymbol = notNegativeLabel)
|
||||
it += IRInstruction(Opcode.AND, IRDataType.WORD, reg1=compareReg, immediate = 0x8000)
|
||||
it += IRInstruction(Opcode.BEQ, IRDataType.WORD, reg1=compareReg, immediate = 0, labelSymbol = notNegativeLabel)
|
||||
it += IRInstruction(Opcode.NEG, IRDataType.WORD, reg1=tr.resultReg)
|
||||
}
|
||||
result += IRCodeChunk(notNegativeLabel, null)
|
||||
@@ -261,10 +270,12 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
}
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
val tr = exprGen.translateExpression(call.args[0])
|
||||
addToResult(result, tr, SyscallRegisterBase, -1)
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = SyscallRegisterBase+1, value = array.length)
|
||||
it += IRInstruction(Opcode.SYSCALL, value = syscall.number)
|
||||
it += IRInstruction(Opcode.SETPARAM, IRDataType.WORD, reg1 = tr.resultReg, immediate = 0)
|
||||
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = tr.resultReg, immediate = if(array.dt==DataType.STR) array.length!!-1 else array.length)
|
||||
it += IRInstruction(Opcode.SETPARAM, IRDataType.BYTE, reg1 = tr.resultReg, immediate = 1)
|
||||
it += IRInstruction(Opcode.SYSCALL, immediate = syscall.number)
|
||||
}
|
||||
return ExpressionCodeResult(result, IRDataType.BYTE, -1, -1)
|
||||
}
|
||||
@@ -284,10 +295,12 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
}
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
val tr = exprGen.translateExpression(call.args[0])
|
||||
addToResult(result, tr, SyscallRegisterBase, -1)
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = SyscallRegisterBase+1, value = array.length)
|
||||
it += IRInstruction(Opcode.SYSCALL, value = syscall.number)
|
||||
it += IRInstruction(Opcode.SETPARAM, IRDataType.WORD, reg1 = tr.resultReg, immediate = 0)
|
||||
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = tr.resultReg, immediate = if(array.dt==DataType.STR) array.length!!-1 else array.length)
|
||||
it += IRInstruction(Opcode.SETPARAM, IRDataType.BYTE, reg1 = tr.resultReg, immediate = 1)
|
||||
it += IRInstruction(Opcode.SYSCALL, immediate = syscall.number)
|
||||
}
|
||||
return ExpressionCodeResult(result, IRDataType.BYTE, -1, -1)
|
||||
}
|
||||
@@ -310,7 +323,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
if (call.args[0] is PtNumber) {
|
||||
val address = (call.args[0] as PtNumber).number.toInt()
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.STOREZM, IRDataType.WORD, value = address)
|
||||
it += IRInstruction(Opcode.STOREZM, IRDataType.WORD, address = address)
|
||||
}
|
||||
} else {
|
||||
val tr = exprGen.translateExpression(call.args[0])
|
||||
@@ -325,7 +338,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
val tr = exprGen.translateExpression(call.args[1])
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.STOREM, IRDataType.WORD, reg1 = tr.resultReg, value = address)
|
||||
it += IRInstruction(Opcode.STOREM, IRDataType.WORD, reg1 = tr.resultReg, address = address)
|
||||
}
|
||||
} else {
|
||||
val addressTr = exprGen.translateExpression(call.args[0])
|
||||
@@ -346,7 +359,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
if (call.args[0] is PtNumber) {
|
||||
val address = (call.args[0] as PtNumber).number.toInt()
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.STOREZM, IRDataType.BYTE, value = address)
|
||||
it += IRInstruction(Opcode.STOREZM, IRDataType.BYTE, address = address)
|
||||
}
|
||||
} else {
|
||||
val tr = exprGen.translateExpression(call.args[0])
|
||||
@@ -361,7 +374,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
val tr = exprGen.translateExpression(call.args[1])
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.STOREM, IRDataType.BYTE, reg1 = tr.resultReg, value = address)
|
||||
it += IRInstruction(Opcode.STOREM, IRDataType.BYTE, reg1 = tr.resultReg, address = address)
|
||||
}
|
||||
} else {
|
||||
val addressTr = exprGen.translateExpression(call.args[0])
|
||||
@@ -382,7 +395,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
val resultRegister = codeGen.registers.nextFree()
|
||||
val address = (call.args[0] as PtNumber).number.toInt()
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOADM, IRDataType.WORD, reg1 = resultRegister, value = address)
|
||||
it += IRInstruction(Opcode.LOADM, IRDataType.WORD, reg1 = resultRegister, address = address)
|
||||
}
|
||||
ExpressionCodeResult(result, IRDataType.BYTE, resultRegister, -1)
|
||||
} else {
|
||||
@@ -402,7 +415,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
val resultRegister = codeGen.registers.nextFree()
|
||||
val address = (call.args[0] as PtNumber).number.toInt()
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOADM, IRDataType.BYTE, reg1 = resultRegister, value = address)
|
||||
it += IRInstruction(Opcode.LOADM, IRDataType.BYTE, reg1 = resultRegister, address = address)
|
||||
}
|
||||
ExpressionCodeResult(result, IRDataType.BYTE, resultRegister, -1)
|
||||
} else {
|
||||
|
||||
@@ -35,12 +35,12 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
val code = IRCodeChunk(null, null)
|
||||
if(vmDt==IRDataType.FLOAT) {
|
||||
val resultFpRegister = codeGen.registers.nextFreeFloat()
|
||||
code += IRInstruction(Opcode.LOAD, vmDt, fpReg1 = resultFpRegister, fpValue = expr.number.toFloat())
|
||||
code += IRInstruction(Opcode.LOAD, vmDt, fpReg1 = resultFpRegister, immediateFp = expr.number.toFloat())
|
||||
ExpressionCodeResult(code, vmDt,-1, resultFpRegister)
|
||||
}
|
||||
else {
|
||||
val resultRegister = codeGen.registers.nextFree()
|
||||
code += IRInstruction(Opcode.LOAD, vmDt, reg1 = resultRegister, value = expr.number.toInt())
|
||||
code += IRInstruction(Opcode.LOAD, vmDt, reg1 = resultRegister, immediate = expr.number.toInt())
|
||||
ExpressionCodeResult(code, vmDt, resultRegister, -1)
|
||||
}
|
||||
}
|
||||
@@ -79,7 +79,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
if(expr.address is PtNumber) {
|
||||
val address = (expr.address as PtNumber).number.toInt()
|
||||
val resultRegister = codeGen.registers.nextFree()
|
||||
addInstr(result, IRInstruction(Opcode.LOADM, IRDataType.BYTE, reg1=resultRegister, value = address), null)
|
||||
addInstr(result, IRInstruction(Opcode.LOADM, IRDataType.BYTE, reg1=resultRegister, address = address), null)
|
||||
ExpressionCodeResult(result, IRDataType.BYTE, resultRegister, -1)
|
||||
} else {
|
||||
val tr = translateExpression(expr.address)
|
||||
@@ -111,42 +111,47 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
when(iterable.dt) {
|
||||
DataType.STR -> {
|
||||
tr = translateExpression(check.element)
|
||||
addToResult(result, tr, SyscallRegisterBase, -1)
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
addInstr(result, IRInstruction(Opcode.SETPARAM, IRDataType.BYTE, tr.resultReg, immediate = 0), null)
|
||||
tr = translateExpression(check.iterable)
|
||||
addToResult(result, tr, SyscallRegisterBase+1, -1)
|
||||
val resultReg = codeGen.registers.nextFree()
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
addInstr(result, IRInstruction(Opcode.SETPARAM, IRDataType.WORD, tr.resultReg, immediate = 1), null)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.SYSCALL, value = IMSyscall.STRING_CONTAINS.number)
|
||||
it += IRInstruction(Opcode.LOADR, IRDataType.BYTE, reg1=resultReg, reg2=0)
|
||||
it += IRInstruction(Opcode.SYSCALL, immediate = IMSyscall.STRING_CONTAINS.number)
|
||||
it += IRInstruction(Opcode.POP, IRDataType.BYTE, reg1=tr.resultReg)
|
||||
}
|
||||
return ExpressionCodeResult(result, IRDataType.BYTE, resultReg, -1)
|
||||
return ExpressionCodeResult(result, IRDataType.BYTE, tr.resultReg, -1)
|
||||
}
|
||||
DataType.ARRAY_UB, DataType.ARRAY_B -> {
|
||||
tr = translateExpression(check.element)
|
||||
addToResult(result, tr, SyscallRegisterBase, -1)
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
addInstr(result, IRInstruction(Opcode.SETPARAM, IRDataType.BYTE, tr.resultReg, immediate = 0), null)
|
||||
tr = translateExpression(check.iterable)
|
||||
addToResult(result, tr, SyscallRegisterBase+1, -1)
|
||||
val resultReg = codeGen.registers.nextFree()
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=SyscallRegisterBase+2, value = iterable.length!!)
|
||||
it += IRInstruction(Opcode.SYSCALL, value = IMSyscall.BYTEARRAY_CONTAINS.number)
|
||||
it += IRInstruction(Opcode.LOADR, IRDataType.BYTE, reg1=resultReg, reg2=0)
|
||||
it += IRInstruction(Opcode.SETPARAM, IRDataType.WORD, tr.resultReg, immediate = 1)
|
||||
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=tr.resultReg, immediate = iterable.length!!)
|
||||
it += IRInstruction(Opcode.SETPARAM, IRDataType.BYTE, tr.resultReg, immediate = 2)
|
||||
it += IRInstruction(Opcode.SYSCALL, immediate = IMSyscall.BYTEARRAY_CONTAINS.number)
|
||||
it += IRInstruction(Opcode.POP, IRDataType.BYTE, reg1=tr.resultReg)
|
||||
}
|
||||
// SysCall call convention: return value in register r0
|
||||
return ExpressionCodeResult(result, IRDataType.BYTE, resultReg, -1)
|
||||
return ExpressionCodeResult(result, IRDataType.BYTE, tr.resultReg, -1)
|
||||
}
|
||||
DataType.ARRAY_UW, DataType.ARRAY_W -> {
|
||||
tr = translateExpression(check.element)
|
||||
addToResult(result, tr, SyscallRegisterBase, -1)
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
addInstr(result, IRInstruction(Opcode.SETPARAM, IRDataType.WORD, tr.resultReg, immediate = 0), null)
|
||||
tr = translateExpression(check.iterable)
|
||||
addToResult(result, tr, SyscallRegisterBase+1, -1)
|
||||
val resultReg = codeGen.registers.nextFree()
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=SyscallRegisterBase+2, value = iterable.length!!)
|
||||
it += IRInstruction(Opcode.SYSCALL, value = IMSyscall.WORDARRAY_CONTAINS.number)
|
||||
it += IRInstruction(Opcode.LOADR, IRDataType.BYTE, reg1=resultReg, reg2=0)
|
||||
it += IRInstruction(Opcode.SETPARAM, IRDataType.WORD, tr.resultReg, immediate = 1)
|
||||
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=tr.resultReg, immediate = iterable.length!!)
|
||||
it += IRInstruction(Opcode.SETPARAM, IRDataType.BYTE, tr.resultReg, immediate = 2)
|
||||
it += IRInstruction(Opcode.SYSCALL, immediate = IMSyscall.WORDARRAY_CONTAINS.number)
|
||||
it += IRInstruction(Opcode.POP, IRDataType.BYTE, reg1=tr.resultReg)
|
||||
}
|
||||
return ExpressionCodeResult(result, IRDataType.BYTE, resultReg, -1)
|
||||
return ExpressionCodeResult(result, IRDataType.BYTE, tr.resultReg, -1)
|
||||
}
|
||||
DataType.ARRAY_F -> throw AssemblyError("containment check in float-array not supported")
|
||||
else -> throw AssemblyError("weird iterable dt ${iterable.dt} for ${check.iterable.name}")
|
||||
@@ -216,7 +221,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
}
|
||||
"~" -> {
|
||||
val mask = if(vmDt==IRDataType.BYTE) 0x00ff else 0xffff
|
||||
addInstr(result, IRInstruction(Opcode.XOR, vmDt, reg1 = tr.resultReg, value = mask), null)
|
||||
addInstr(result, IRInstruction(Opcode.XOR, vmDt, reg1 = tr.resultReg, immediate = mask), null)
|
||||
}
|
||||
else -> throw AssemblyError("weird prefix operator")
|
||||
}
|
||||
@@ -353,23 +358,33 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
when (val callTarget = codeGen.symbolTable.flat.getValue(fcall.name)) {
|
||||
is StSub -> {
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
for ((arg, parameter) in fcall.args.zip(callTarget.parameters)) {
|
||||
val paramDt = codeGen.irType(parameter.type)
|
||||
val symbol = "${fcall.name}.${parameter.name}"
|
||||
if(codeGen.isZero(arg)) {
|
||||
addInstr(result, IRInstruction(Opcode.STOREZM, paramDt, labelSymbol = symbol), null)
|
||||
} else {
|
||||
if (paramDt == IRDataType.FLOAT) {
|
||||
val tr = translateExpression(arg)
|
||||
addToResult(result, tr, -1, tr.resultFpReg)
|
||||
addInstr(result, IRInstruction(Opcode.STOREM, paramDt, fpReg1 = tr.resultFpReg, labelSymbol = symbol), null)
|
||||
} else {
|
||||
val tr = translateExpression(arg)
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
addInstr(result, IRInstruction(Opcode.STOREM, paramDt, reg1 = tr.resultReg, labelSymbol = symbol), null)
|
||||
}
|
||||
}
|
||||
for ((index, argspec) in fcall.args.zip(callTarget.parameters).withIndex()) {
|
||||
val (arg, param) = argspec
|
||||
val paramDt = codeGen.irType(param.type)
|
||||
val tr = translateExpression(arg)
|
||||
result += tr.chunks
|
||||
if(paramDt==IRDataType.FLOAT)
|
||||
addInstr(result, IRInstruction(Opcode.SETPARAM, paramDt, fpReg1 = tr.resultFpReg, immediate = index), null)
|
||||
else
|
||||
addInstr(result, IRInstruction(Opcode.SETPARAM, paramDt, reg1 = tr.resultReg, immediate = index), null)
|
||||
}
|
||||
// for ((arg, parameter) in fcall.args.zip(callTarget.parameters)) {
|
||||
// val paramDt = codeGen.irType(parameter.type)
|
||||
// val symbol = "${fcall.name}.${parameter.name}"
|
||||
// if(codeGen.isZero(arg)) {
|
||||
// addInstr(result, IRInstruction(Opcode.STOREZM, paramDt, labelSymbol = symbol), null)
|
||||
// } else {
|
||||
// if (paramDt == IRDataType.FLOAT) {
|
||||
// val tr = translateExpression(arg)
|
||||
// addToResult(result, tr, -1, tr.resultFpReg)
|
||||
// addInstr(result, IRInstruction(Opcode.STOREM, paramDt, fpReg1 = tr.resultFpReg, labelSymbol = symbol), null)
|
||||
// } else {
|
||||
// val tr = translateExpression(arg)
|
||||
// addToResult(result, tr, tr.resultReg, -1)
|
||||
// addInstr(result, IRInstruction(Opcode.STOREM, paramDt, reg1 = tr.resultReg, labelSymbol = symbol), null)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
return if(fcall.void) {
|
||||
addInstr(result, IRInstruction(Opcode.CALL, labelSymbol = fcall.name), null)
|
||||
ExpressionCodeResult(result, IRDataType.BYTE, -1, -1)
|
||||
@@ -378,10 +393,10 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
var resultFpReg = -1
|
||||
if(fcall.type==DataType.FLOAT) {
|
||||
resultFpReg = codeGen.registers.nextFreeFloat()
|
||||
addInstr(result, IRInstruction(Opcode.CALLRVAL, IRDataType.FLOAT, fpReg1=resultFpReg, labelSymbol=fcall.name), null)
|
||||
addInstr(result, IRInstruction(Opcode.CALLR, IRDataType.FLOAT, fpReg1=resultFpReg, labelSymbol=fcall.name), null)
|
||||
} else {
|
||||
resultReg = codeGen.registers.nextFree()
|
||||
addInstr(result, IRInstruction(Opcode.CALLRVAL, codeGen.irType(fcall.type), reg1=resultReg, labelSymbol=fcall.name), null)
|
||||
addInstr(result, IRInstruction(Opcode.CALLR, codeGen.irType(fcall.type), reg1=resultReg, labelSymbol=fcall.name), null)
|
||||
}
|
||||
ExpressionCodeResult(result, codeGen.irType(fcall.type), resultReg, resultFpReg)
|
||||
}
|
||||
@@ -402,7 +417,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
}
|
||||
}
|
||||
// just a regular call without using Vm register call convention: the value is returned in CPU registers!
|
||||
addInstr(result, IRInstruction(Opcode.CALL, value=callTarget.address.toInt()), null)
|
||||
addInstr(result, IRInstruction(Opcode.CALL, address = callTarget.address.toInt()), null)
|
||||
val resultReg = codeGen.registers.nextFree()
|
||||
if(!fcall.void) {
|
||||
when(callTarget.returns.size) {
|
||||
@@ -454,7 +469,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
val resultRegister = codeGen.registers.nextFree()
|
||||
addInstr(result, IRInstruction(Opcode.FCOMP, IRDataType.FLOAT, reg1=resultRegister, fpReg1 = leftTr.resultFpReg, fpReg2 = rightTr.resultFpReg), null)
|
||||
val zeroRegister = codeGen.registers.nextFree()
|
||||
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=zeroRegister, value=0), null)
|
||||
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=zeroRegister, immediate = 0), null)
|
||||
val ins = if (signed) {
|
||||
if (greaterEquals) Opcode.SGES else Opcode.SGTS
|
||||
} else {
|
||||
@@ -464,21 +479,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
return ExpressionCodeResult(result, IRDataType.BYTE, resultRegister, -1)
|
||||
} else {
|
||||
if(binExpr.left.type==DataType.STR && binExpr.right.type==DataType.STR) {
|
||||
val leftTr = translateExpression(binExpr.left)
|
||||
addToResult(result, leftTr, SyscallRegisterBase, -1)
|
||||
val rightTr = translateExpression(binExpr.right)
|
||||
addToResult(result, rightTr, SyscallRegisterBase+1, -1)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.SYSCALL, value = IMSyscall.COMPARE_STRINGS.number)
|
||||
// SysCall call convention: return value in register r0
|
||||
val zeroReg = codeGen.registers.nextFree()
|
||||
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = zeroReg, value = 0)
|
||||
it += if (greaterEquals)
|
||||
IRInstruction(Opcode.SGES, IRDataType.BYTE, reg1 = leftTr.resultReg, reg2 = zeroReg)
|
||||
else
|
||||
IRInstruction(Opcode.SGTS, IRDataType.BYTE, reg1 = leftTr.resultReg, reg2 = zeroReg)
|
||||
}
|
||||
return ExpressionCodeResult(result, IRDataType.BYTE, leftTr.resultReg, -1)
|
||||
throw AssemblyError("str compares should have been replaced with builtin function call to do the compare")
|
||||
} else {
|
||||
val leftTr = translateExpression(binExpr.left)
|
||||
addToResult(result, leftTr, leftTr.resultReg, -1)
|
||||
@@ -510,7 +511,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
val resultRegister = codeGen.registers.nextFree()
|
||||
addInstr(result, IRInstruction(Opcode.FCOMP, IRDataType.FLOAT, reg1=resultRegister, fpReg1 = leftTr.resultFpReg, fpReg2 = rightTr.resultFpReg), null)
|
||||
val zeroRegister = codeGen.registers.nextFree()
|
||||
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=zeroRegister, value=0), null)
|
||||
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=zeroRegister, immediate = 0), null)
|
||||
val ins = if (signed) {
|
||||
if (lessEquals) Opcode.SLES else Opcode.SLTS
|
||||
} else {
|
||||
@@ -520,23 +521,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
return ExpressionCodeResult(result, IRDataType.BYTE, resultRegister, -1)
|
||||
} else {
|
||||
if(binExpr.left.type==DataType.STR && binExpr.right.type==DataType.STR) {
|
||||
val leftTr = translateExpression(binExpr.left)
|
||||
addToResult(result, leftTr, SyscallRegisterBase, -1)
|
||||
val rightTr = translateExpression(binExpr.right)
|
||||
addToResult(result, rightTr, SyscallRegisterBase+1, -1)
|
||||
val resultReg = codeGen.registers.nextFree()
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.SYSCALL, value = IMSyscall.COMPARE_STRINGS.number)
|
||||
// SysCall call convention: return value in register r0
|
||||
val zeroReg = codeGen.registers.nextFree()
|
||||
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = zeroReg, value = 0)
|
||||
it += if (lessEquals)
|
||||
IRInstruction(Opcode.SLES, IRDataType.BYTE, reg1 = 0, reg2 = zeroReg)
|
||||
else
|
||||
IRInstruction(Opcode.SLTS, IRDataType.BYTE, reg1 = 0, reg2 = zeroReg)
|
||||
it += IRInstruction(Opcode.LOADR, IRDataType.BYTE, reg1=resultReg, reg2=0)
|
||||
}
|
||||
return ExpressionCodeResult(result, IRDataType.BYTE, resultReg, -1)
|
||||
throw AssemblyError("str compares should have been replaced with builtin function call to do the compare")
|
||||
} else {
|
||||
val leftTr = translateExpression(binExpr.left)
|
||||
addToResult(result, leftTr, leftTr.resultReg, -1)
|
||||
@@ -563,32 +548,19 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
val resultRegister = codeGen.registers.nextFree()
|
||||
val valueReg = codeGen.registers.nextFree()
|
||||
val label = codeGen.createLabelName()
|
||||
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=resultRegister, value=1), null)
|
||||
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=resultRegister, immediate = 1), null)
|
||||
addInstr(result, IRInstruction(Opcode.FCOMP, IRDataType.FLOAT, reg1=valueReg, fpReg1 = leftTr.resultFpReg, fpReg2 = rightTr.resultFpReg), null)
|
||||
if (notEquals) {
|
||||
addInstr(result, IRInstruction(Opcode.BNZ, IRDataType.BYTE, reg1=valueReg, labelSymbol = label), null)
|
||||
addInstr(result, IRInstruction(Opcode.BNE, IRDataType.BYTE, reg1=valueReg, immediate = 0, labelSymbol = label), null)
|
||||
} else {
|
||||
addInstr(result, IRInstruction(Opcode.BZ, IRDataType.BYTE, reg1=valueReg, labelSymbol = label), null)
|
||||
addInstr(result, IRInstruction(Opcode.BEQ, IRDataType.BYTE, reg1=valueReg, immediate = 0, labelSymbol = label), null)
|
||||
}
|
||||
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=resultRegister, value=0), null)
|
||||
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=resultRegister, immediate = 0), null)
|
||||
result += IRCodeChunk(label, null)
|
||||
return ExpressionCodeResult(result, IRDataType.BYTE, resultRegister, -1)
|
||||
} else {
|
||||
if(binExpr.left.type==DataType.STR && binExpr.right.type==DataType.STR) {
|
||||
val leftTr = translateExpression(binExpr.left)
|
||||
addToResult(result, leftTr, SyscallRegisterBase, -1)
|
||||
val rightTr = translateExpression(binExpr.right)
|
||||
addToResult(result, rightTr, SyscallRegisterBase+1, -1)
|
||||
val resultRegister = codeGen.registers.nextFree()
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.SYSCALL, value = IMSyscall.COMPARE_STRINGS.number)
|
||||
// SysCall call convention: return value in register r0
|
||||
if (!notEquals)
|
||||
it += IRInstruction(Opcode.INV, vmDt, reg1 = 0)
|
||||
it += IRInstruction(Opcode.LOADR, vmDt, reg1=resultRegister, reg2=0)
|
||||
it += IRInstruction(Opcode.AND, vmDt, reg1 = resultRegister, value = 1)
|
||||
}
|
||||
return ExpressionCodeResult(result, IRDataType.BYTE, resultRegister, -1)
|
||||
throw AssemblyError("str compares should have been replaced with builtin function call to do the compare")
|
||||
} else {
|
||||
return if(constValue(binExpr.right)==0.0) {
|
||||
val tr = translateExpression(binExpr.left)
|
||||
@@ -651,7 +623,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
return if(binExpr.right is PtNumber) {
|
||||
val tr = translateExpression(binExpr.left)
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
addInstr(result, IRInstruction(Opcode.XOR, vmDt, reg1 = tr.resultReg, value=(binExpr.right as PtNumber).number.toInt()), null)
|
||||
addInstr(result, IRInstruction(Opcode.XOR, vmDt, reg1 = tr.resultReg, immediate = (binExpr.right as PtNumber).number.toInt()), null)
|
||||
ExpressionCodeResult(result, vmDt, tr.resultReg, -1)
|
||||
} else {
|
||||
val leftTr = translateExpression(binExpr.left)
|
||||
@@ -668,7 +640,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
return if(binExpr.right is PtNumber) {
|
||||
val tr = translateExpression(binExpr.left)
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
addInstr(result, IRInstruction(Opcode.AND, vmDt, reg1 = tr.resultReg, value=(binExpr.right as PtNumber).number.toInt()), null)
|
||||
addInstr(result, IRInstruction(Opcode.AND, vmDt, reg1 = tr.resultReg, immediate = (binExpr.right as PtNumber).number.toInt()), null)
|
||||
ExpressionCodeResult(result, vmDt, tr.resultReg, -1)
|
||||
} else {
|
||||
val leftTr = translateExpression(binExpr.left)
|
||||
@@ -685,7 +657,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
return if(binExpr.right is PtNumber) {
|
||||
val tr = translateExpression(binExpr.left)
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
addInstr(result, IRInstruction(Opcode.OR, vmDt, reg1 = tr.resultReg, value=(binExpr.right as PtNumber).number.toInt()), null)
|
||||
addInstr(result, IRInstruction(Opcode.OR, vmDt, reg1 = tr.resultReg, immediate = (binExpr.right as PtNumber).number.toInt()), null)
|
||||
ExpressionCodeResult(result, vmDt, tr.resultReg, -1)
|
||||
} else {
|
||||
val leftTr = translateExpression(binExpr.left)
|
||||
@@ -703,7 +675,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
return if(binExpr.right is PtNumber) {
|
||||
val tr = translateExpression(binExpr.left)
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
addInstr(result, IRInstruction(Opcode.MOD, vmDt, reg1 = tr.resultReg, value=(binExpr.right as PtNumber).number.toInt()), null)
|
||||
addInstr(result, IRInstruction(Opcode.MOD, vmDt, reg1 = tr.resultReg, immediate = (binExpr.right as PtNumber).number.toInt()), null)
|
||||
ExpressionCodeResult(result, vmDt, tr.resultReg, -1)
|
||||
} else {
|
||||
val leftTr = translateExpression(binExpr.left)
|
||||
@@ -749,9 +721,9 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
val leftTr = translateExpression(binExpr.left)
|
||||
addToResult(result, leftTr, leftTr.resultReg, -1)
|
||||
addInstr(result, if (signed)
|
||||
IRInstruction(Opcode.DIVS, vmDt, reg1 = leftTr.resultReg, value=(binExpr.right as PtNumber).number.toInt())
|
||||
IRInstruction(Opcode.DIVS, vmDt, reg1 = leftTr.resultReg, immediate = (binExpr.right as PtNumber).number.toInt())
|
||||
else
|
||||
IRInstruction(Opcode.DIV, vmDt, reg1 = leftTr.resultReg, value=(binExpr.right as PtNumber).number.toInt())
|
||||
IRInstruction(Opcode.DIV, vmDt, reg1 = leftTr.resultReg, immediate = (binExpr.right as PtNumber).number.toInt())
|
||||
, null)
|
||||
ExpressionCodeResult(result, vmDt, leftTr.resultReg, -1)
|
||||
} else {
|
||||
@@ -832,7 +804,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
return if(binExpr.right is PtNumber) {
|
||||
val tr = translateExpression(binExpr.left)
|
||||
addToResult(result, tr, -1, tr.resultFpReg)
|
||||
addInstr(result, IRInstruction(Opcode.SUB, vmDt, fpReg1 = tr.resultFpReg, fpValue = (binExpr.right as PtNumber).number.toFloat()), null)
|
||||
addInstr(result, IRInstruction(Opcode.SUB, vmDt, fpReg1 = tr.resultFpReg, immediateFp = (binExpr.right as PtNumber).number.toFloat()), null)
|
||||
ExpressionCodeResult(result, vmDt, -1, tr.resultFpReg)
|
||||
} else {
|
||||
val leftTr = translateExpression(binExpr.left)
|
||||
@@ -852,9 +824,9 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
}
|
||||
else {
|
||||
return if(binExpr.right is PtNumber) {
|
||||
val tr = translateExpression(binExpr.left,)
|
||||
val tr = translateExpression(binExpr.left)
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
addInstr(result, IRInstruction(Opcode.SUB, vmDt, reg1 = tr.resultReg, value = (binExpr.right as PtNumber).number.toInt()), null)
|
||||
addInstr(result, IRInstruction(Opcode.SUB, vmDt, reg1 = tr.resultReg, immediate = (binExpr.right as PtNumber).number.toInt()), null)
|
||||
ExpressionCodeResult(result, vmDt, tr.resultReg, -1)
|
||||
} else {
|
||||
val leftTr = translateExpression(binExpr.left)
|
||||
@@ -887,7 +859,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
return if(binExpr.right is PtNumber) {
|
||||
val tr = translateExpression(binExpr.left)
|
||||
addToResult(result, tr, -1, tr.resultFpReg)
|
||||
addInstr(result, IRInstruction(Opcode.ADD, vmDt, fpReg1 = tr.resultFpReg, fpValue = (binExpr.right as PtNumber).number.toFloat()), null)
|
||||
addInstr(result, IRInstruction(Opcode.ADD, vmDt, fpReg1 = tr.resultFpReg, immediateFp = (binExpr.right as PtNumber).number.toFloat()), null)
|
||||
ExpressionCodeResult(result, vmDt, -1, tr.resultFpReg)
|
||||
} else {
|
||||
val leftTr = translateExpression(binExpr.left)
|
||||
@@ -915,7 +887,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
return if(binExpr.right is PtNumber) {
|
||||
val tr = translateExpression(binExpr.left)
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
addInstr(result, IRInstruction(Opcode.ADD, vmDt, reg1 = tr.resultReg, value=(binExpr.right as PtNumber).number.toInt()), null)
|
||||
addInstr(result, IRInstruction(Opcode.ADD, vmDt, reg1 = tr.resultReg, immediate = (binExpr.right as PtNumber).number.toInt()), null)
|
||||
ExpressionCodeResult(result, vmDt, tr.resultReg, -1)
|
||||
} else {
|
||||
val leftTr = translateExpression(binExpr.left)
|
||||
@@ -935,7 +907,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
val tr = translateExpression(operand)
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
addInstr(result, if(knownAddress!=null)
|
||||
IRInstruction(Opcode.ANDM, vmDt, reg1=tr.resultReg, value=knownAddress)
|
||||
IRInstruction(Opcode.ANDM, vmDt, reg1=tr.resultReg, address = knownAddress)
|
||||
else
|
||||
IRInstruction(Opcode.ANDM, vmDt, reg1=tr.resultReg, labelSymbol = symbol)
|
||||
,null)
|
||||
@@ -947,7 +919,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
val tr = translateExpression(operand)
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
addInstr(result, if(knownAddress!=null)
|
||||
IRInstruction(Opcode.ORM, vmDt, reg1=tr.resultReg, value = knownAddress)
|
||||
IRInstruction(Opcode.ORM, vmDt, reg1=tr.resultReg, address = knownAddress)
|
||||
else
|
||||
IRInstruction(Opcode.ORM, vmDt, reg1=tr.resultReg, labelSymbol = symbol)
|
||||
, null)
|
||||
@@ -966,13 +938,13 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
addToResult(result, tr, -1, tr.resultFpReg)
|
||||
val ins = if(signed) {
|
||||
if(knownAddress!=null)
|
||||
IRInstruction(Opcode.DIVSM, vmDt, fpReg1 = tr.resultFpReg, value = knownAddress)
|
||||
IRInstruction(Opcode.DIVSM, vmDt, fpReg1 = tr.resultFpReg, address = knownAddress)
|
||||
else
|
||||
IRInstruction(Opcode.DIVSM, vmDt, fpReg1 = tr.resultFpReg, labelSymbol = symbol)
|
||||
}
|
||||
else {
|
||||
if(knownAddress!=null)
|
||||
IRInstruction(Opcode.DIVM, vmDt, fpReg1 = tr.resultFpReg, value = knownAddress)
|
||||
IRInstruction(Opcode.DIVM, vmDt, fpReg1 = tr.resultFpReg, address = knownAddress)
|
||||
else
|
||||
IRInstruction(Opcode.DIVM, vmDt, fpReg1 = tr.resultFpReg, labelSymbol = symbol)
|
||||
}
|
||||
@@ -987,13 +959,13 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
val ins = if(signed) {
|
||||
if(knownAddress!=null)
|
||||
IRInstruction(Opcode.DIVSM, vmDt, reg1 = tr.resultReg, value = knownAddress)
|
||||
IRInstruction(Opcode.DIVSM, vmDt, reg1 = tr.resultReg, address = knownAddress)
|
||||
else
|
||||
IRInstruction(Opcode.DIVSM, vmDt, reg1 = tr.resultReg, labelSymbol = symbol)
|
||||
}
|
||||
else {
|
||||
if(knownAddress!=null)
|
||||
IRInstruction(Opcode.DIVM, vmDt, reg1 = tr.resultReg, value = knownAddress)
|
||||
IRInstruction(Opcode.DIVM, vmDt, reg1 = tr.resultReg, address = knownAddress)
|
||||
else
|
||||
IRInstruction(Opcode.DIVM, vmDt, reg1 = tr.resultReg, labelSymbol = symbol)
|
||||
}
|
||||
@@ -1014,7 +986,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
val tr = translateExpression(operand)
|
||||
addToResult(result, tr, -1, tr.resultFpReg)
|
||||
addInstr(result, if(knownAddress!=null)
|
||||
IRInstruction(Opcode.MULM, vmDt, fpReg1 = tr.resultFpReg, value = knownAddress)
|
||||
IRInstruction(Opcode.MULM, vmDt, fpReg1 = tr.resultFpReg, address = knownAddress)
|
||||
else
|
||||
IRInstruction(Opcode.MULM, vmDt, fpReg1 = tr.resultFpReg, labelSymbol = symbol)
|
||||
, null)
|
||||
@@ -1027,7 +999,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
val tr = translateExpression(operand)
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
addInstr(result, if(knownAddress!=null)
|
||||
IRInstruction(Opcode.MULM, vmDt, reg1=tr.resultReg, value = knownAddress)
|
||||
IRInstruction(Opcode.MULM, vmDt, reg1=tr.resultReg, address = knownAddress)
|
||||
else
|
||||
IRInstruction(Opcode.MULM, vmDt, reg1=tr.resultReg, labelSymbol = symbol)
|
||||
, null)
|
||||
@@ -1041,7 +1013,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
if(vmDt==IRDataType.FLOAT) {
|
||||
if((operand as? PtNumber)?.number==1.0) {
|
||||
addInstr(result, if(knownAddress!=null)
|
||||
IRInstruction(Opcode.DECM, vmDt, value=knownAddress)
|
||||
IRInstruction(Opcode.DECM, vmDt, address = knownAddress)
|
||||
else
|
||||
IRInstruction(Opcode.DECM, vmDt, labelSymbol = symbol)
|
||||
, null)
|
||||
@@ -1050,7 +1022,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
val tr = translateExpression(operand)
|
||||
addToResult(result, tr, -1, tr.resultFpReg)
|
||||
addInstr(result, if(knownAddress!=null)
|
||||
IRInstruction(Opcode.SUBM, vmDt, fpReg1=tr.resultFpReg, value=knownAddress)
|
||||
IRInstruction(Opcode.SUBM, vmDt, fpReg1=tr.resultFpReg, address = knownAddress)
|
||||
else
|
||||
IRInstruction(Opcode.SUBM, vmDt, fpReg1=tr.resultFpReg, labelSymbol = symbol)
|
||||
, null)
|
||||
@@ -1058,7 +1030,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
} else {
|
||||
if((operand as? PtNumber)?.number==1.0) {
|
||||
addInstr(result, if(knownAddress!=null)
|
||||
IRInstruction(Opcode.DECM, vmDt, value=knownAddress)
|
||||
IRInstruction(Opcode.DECM, vmDt, address = knownAddress)
|
||||
else
|
||||
IRInstruction(Opcode.DECM, vmDt, labelSymbol = symbol)
|
||||
, null)
|
||||
@@ -1067,7 +1039,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
val tr = translateExpression(operand)
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
addInstr(result, if(knownAddress!=null)
|
||||
IRInstruction(Opcode.SUBM, vmDt, reg1=tr.resultReg, value = knownAddress)
|
||||
IRInstruction(Opcode.SUBM, vmDt, reg1=tr.resultReg, address = knownAddress)
|
||||
else
|
||||
IRInstruction(Opcode.SUBM, vmDt, reg1=tr.resultReg, labelSymbol = symbol)
|
||||
, null)
|
||||
@@ -1081,7 +1053,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
if(vmDt==IRDataType.FLOAT) {
|
||||
if((operand as? PtNumber)?.number==1.0) {
|
||||
addInstr(result, if(knownAddress!=null)
|
||||
IRInstruction(Opcode.INCM, vmDt, value = knownAddress)
|
||||
IRInstruction(Opcode.INCM, vmDt, address = knownAddress)
|
||||
else
|
||||
IRInstruction(Opcode.INCM, vmDt, labelSymbol = symbol)
|
||||
, null)
|
||||
@@ -1090,7 +1062,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
val tr = translateExpression(operand)
|
||||
addToResult(result, tr, -1, tr.resultFpReg)
|
||||
addInstr(result, if(knownAddress!=null)
|
||||
IRInstruction(Opcode.ADDM, vmDt, fpReg1=tr.resultFpReg, value = knownAddress)
|
||||
IRInstruction(Opcode.ADDM, vmDt, fpReg1=tr.resultFpReg, address = knownAddress)
|
||||
else
|
||||
IRInstruction(Opcode.ADDM, vmDt, fpReg1=tr.resultFpReg, labelSymbol = symbol)
|
||||
, null)
|
||||
@@ -1098,7 +1070,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
} else {
|
||||
if((operand as? PtNumber)?.number==1.0) {
|
||||
addInstr(result, if(knownAddress!=null)
|
||||
IRInstruction(Opcode.INCM, vmDt, value = knownAddress)
|
||||
IRInstruction(Opcode.INCM, vmDt, address = knownAddress)
|
||||
else
|
||||
IRInstruction(Opcode.INCM, vmDt, labelSymbol = symbol)
|
||||
, null)
|
||||
@@ -1107,7 +1079,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
val tr = translateExpression(operand)
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
addInstr(result, if(knownAddress!=null)
|
||||
IRInstruction(Opcode.ADDM, vmDt, reg1=tr.resultReg, value=knownAddress)
|
||||
IRInstruction(Opcode.ADDM, vmDt, reg1=tr.resultReg, address = knownAddress)
|
||||
else
|
||||
IRInstruction(Opcode.ADDM, vmDt, reg1=tr.resultReg, labelSymbol = symbol)
|
||||
, null)
|
||||
@@ -1121,7 +1093,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
if(codeGen.isOne(operand)) {
|
||||
val opc = if (signed) Opcode.ASRM else Opcode.LSRM
|
||||
val ins = if(knownAddress!=null)
|
||||
IRInstruction(opc, vmDt, value=knownAddress)
|
||||
IRInstruction(opc, vmDt, address = knownAddress)
|
||||
else
|
||||
IRInstruction(opc, vmDt, labelSymbol = symbol)
|
||||
addInstr(result, ins, null)
|
||||
@@ -1130,7 +1102,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
val opc = if (signed) Opcode.ASRNM else Opcode.LSRNM
|
||||
val ins = if(knownAddress!=null)
|
||||
IRInstruction(opc, vmDt, reg1 = tr.resultReg, value=knownAddress)
|
||||
IRInstruction(opc, vmDt, reg1 = tr.resultReg, address = knownAddress)
|
||||
else
|
||||
IRInstruction(opc, vmDt, reg1 = tr.resultReg, labelSymbol = symbol)
|
||||
addInstr(result, ins, null)
|
||||
@@ -1142,7 +1114,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
if(codeGen.isOne(operand)){
|
||||
addInstr(result, if(knownAddress!=null)
|
||||
IRInstruction(Opcode.LSLM, vmDt, value=knownAddress)
|
||||
IRInstruction(Opcode.LSLM, vmDt, address = knownAddress)
|
||||
else
|
||||
IRInstruction(Opcode.LSLM, vmDt, labelSymbol = symbol)
|
||||
, null)
|
||||
@@ -1150,7 +1122,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
val tr = translateExpression(operand)
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
addInstr(result, if(knownAddress!=null)
|
||||
IRInstruction(Opcode.LSLNM, vmDt, reg1=tr.resultReg, value=knownAddress)
|
||||
IRInstruction(Opcode.LSLNM, vmDt, reg1=tr.resultReg, address = knownAddress)
|
||||
else
|
||||
IRInstruction(Opcode.LSLNM, vmDt, reg1=tr.resultReg, labelSymbol = symbol)
|
||||
,null)
|
||||
@@ -1163,7 +1135,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
val tr = translateExpression(operand)
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
addInstr(result, if(knownAddress!=null)
|
||||
IRInstruction(Opcode.XORM, vmDt, reg1=tr.resultReg, value = knownAddress)
|
||||
IRInstruction(Opcode.XORM, vmDt, reg1=tr.resultReg, address = knownAddress)
|
||||
else
|
||||
IRInstruction(Opcode.XORM, vmDt, reg1=tr.resultReg, labelSymbol = symbol)
|
||||
,null)
|
||||
@@ -1178,15 +1150,15 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
if (knownAddress != null) {
|
||||
// @(address) = @(address) %= operand
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOADM, vmDt, reg1 = resultReg, value = knownAddress)
|
||||
it += IRInstruction(Opcode.MOD, vmDt, reg1 = resultReg, value = number)
|
||||
it += IRInstruction(Opcode.STOREM, vmDt, reg1 = resultReg, value = knownAddress)
|
||||
it += IRInstruction(Opcode.LOADM, vmDt, reg1 = resultReg, address = knownAddress)
|
||||
it += IRInstruction(Opcode.MOD, vmDt, reg1 = resultReg, immediate = number)
|
||||
it += IRInstruction(Opcode.STOREM, vmDt, reg1 = resultReg, address = knownAddress)
|
||||
}
|
||||
} else {
|
||||
// symbol = symbol %= operand
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOADM, vmDt, reg1 = resultReg, labelSymbol = symbol)
|
||||
it += IRInstruction(Opcode.MOD, vmDt, reg1 = resultReg, value = number)
|
||||
it += IRInstruction(Opcode.MOD, vmDt, reg1 = resultReg, immediate = number)
|
||||
it += IRInstruction(Opcode.STOREM, vmDt, reg1 = resultReg, labelSymbol = symbol)
|
||||
}
|
||||
}
|
||||
@@ -1196,9 +1168,9 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
if (knownAddress != null) {
|
||||
// @(address) = @(address) %= operand
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOADM, vmDt, reg1 = resultReg, value = knownAddress)
|
||||
it += IRInstruction(Opcode.LOADM, vmDt, reg1 = resultReg, address = knownAddress)
|
||||
it += IRInstruction(Opcode.MODR, vmDt, reg1 = resultReg, reg2 = tr.resultReg)
|
||||
it += IRInstruction(Opcode.STOREM, vmDt, reg1 = resultReg, value = knownAddress)
|
||||
it += IRInstruction(Opcode.STOREM, vmDt, reg1 = resultReg, address = knownAddress)
|
||||
}
|
||||
} else {
|
||||
// symbol = symbol %= operand
|
||||
@@ -1279,16 +1251,16 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
if (knownAddress != null) {
|
||||
// in-place modify a memory location
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOADM, vmDt, reg1 = valueReg, value = knownAddress)
|
||||
it += IRInstruction(Opcode.LOAD, vmDt, reg1=numberReg, value=operand.number.toInt())
|
||||
it += IRInstruction(Opcode.LOADM, vmDt, reg1 = valueReg, address = knownAddress)
|
||||
it += IRInstruction(Opcode.LOAD, vmDt, reg1=numberReg, immediate = operand.number.toInt())
|
||||
it += IRInstruction(compareAndSetOpcode, vmDt, reg1 = valueReg, reg2 = numberReg)
|
||||
it += IRInstruction(Opcode.STOREM, vmDt, reg1 = valueReg, value = knownAddress)
|
||||
it += IRInstruction(Opcode.STOREM, vmDt, reg1 = valueReg, address = knownAddress)
|
||||
}
|
||||
} else {
|
||||
// in-place modify a symbol (variable)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOADM, vmDt, reg1 = valueReg, labelSymbol = symbol)
|
||||
it += IRInstruction(Opcode.LOAD, vmDt, reg1=numberReg, value=operand.number.toInt())
|
||||
it += IRInstruction(Opcode.LOAD, vmDt, reg1=numberReg, immediate = operand.number.toInt())
|
||||
it += IRInstruction(compareAndSetOpcode, vmDt, reg1 = valueReg, reg2 = numberReg)
|
||||
it += IRInstruction(Opcode.STOREM, vmDt, reg1 = valueReg, labelSymbol = symbol)
|
||||
}
|
||||
@@ -1299,9 +1271,9 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
if (knownAddress != null) {
|
||||
// in-place modify a memory location
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOADM, vmDt, reg1 = valueReg, value = knownAddress)
|
||||
it += IRInstruction(Opcode.LOADM, vmDt, reg1 = valueReg, address = knownAddress)
|
||||
it += IRInstruction(compareAndSetOpcode, vmDt, reg1 = valueReg, reg2 = tr.resultReg)
|
||||
it += IRInstruction(Opcode.STOREM, vmDt, reg1 = valueReg, value = knownAddress)
|
||||
it += IRInstruction(Opcode.STOREM, vmDt, reg1 = valueReg, address = knownAddress)
|
||||
}
|
||||
} else {
|
||||
// in-place modify a symbol (variable)
|
||||
@@ -1330,21 +1302,21 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
if (knownAddress != null) {
|
||||
// in-place modify a memory location
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOADM, IRDataType.FLOAT, fpReg1 = valueReg, value = knownAddress)
|
||||
it += IRInstruction(Opcode.LOAD, IRDataType.FLOAT, fpReg1 = numberReg, fpValue = operand.number.toFloat())
|
||||
it += IRInstruction(Opcode.LOADM, IRDataType.FLOAT, fpReg1 = valueReg, address = knownAddress)
|
||||
it += IRInstruction(Opcode.LOAD, IRDataType.FLOAT, fpReg1 = numberReg, immediateFp = operand.number.toFloat())
|
||||
it += IRInstruction(Opcode.FCOMP, IRDataType.FLOAT, reg1=cmpReg, fpReg1 = valueReg, fpReg2 = numberReg)
|
||||
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=zeroReg, value=0)
|
||||
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=zeroReg, immediate = 0)
|
||||
it += IRInstruction(compareAndSetOpcode, IRDataType.BYTE, reg1=cmpReg, reg2=zeroReg)
|
||||
it += IRInstruction(Opcode.FFROMUB, IRDataType.FLOAT, reg1=cmpReg, fpReg1 = valueReg)
|
||||
it += IRInstruction(Opcode.STOREM, IRDataType.FLOAT, fpReg1 = valueReg, value=knownAddress)
|
||||
it += IRInstruction(Opcode.STOREM, IRDataType.FLOAT, fpReg1 = valueReg, address = knownAddress)
|
||||
}
|
||||
} else {
|
||||
// in-place modify a symbol (variable)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOADM, IRDataType.FLOAT, fpReg1 = valueReg, labelSymbol = symbol)
|
||||
it += IRInstruction(Opcode.LOAD, IRDataType.FLOAT, fpReg1 = numberReg, fpValue = operand.number.toFloat())
|
||||
it += IRInstruction(Opcode.LOAD, IRDataType.FLOAT, fpReg1 = numberReg, immediateFp = operand.number.toFloat())
|
||||
it += IRInstruction(Opcode.FCOMP, IRDataType.FLOAT, reg1=cmpReg, fpReg1 = valueReg, fpReg2 = numberReg)
|
||||
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=zeroReg, value=0)
|
||||
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=zeroReg, immediate = 0)
|
||||
it += IRInstruction(compareAndSetOpcode, IRDataType.BYTE, reg1=cmpReg, reg2=zeroReg)
|
||||
it += IRInstruction(Opcode.FFROMUB, IRDataType.FLOAT, reg1=cmpReg, fpReg1 = valueReg)
|
||||
it += IRInstruction(Opcode.STOREM, IRDataType.FLOAT, fpReg1 = valueReg, labelSymbol = symbol)
|
||||
@@ -1356,19 +1328,19 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
if (knownAddress != null) {
|
||||
// in-place modify a memory location
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOADM, IRDataType.FLOAT, fpReg1 = valueReg, value = knownAddress)
|
||||
it += IRInstruction(Opcode.LOADM, IRDataType.FLOAT, fpReg1 = valueReg, address = knownAddress)
|
||||
it += IRInstruction(Opcode.FCOMP, IRDataType.FLOAT, reg1=cmpReg, fpReg1 = valueReg, fpReg2 = tr.resultFpReg)
|
||||
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=zeroReg, value=0)
|
||||
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=zeroReg, immediate = 0)
|
||||
it += IRInstruction(compareAndSetOpcode, IRDataType.BYTE, reg1=cmpReg, reg2=zeroReg)
|
||||
it += IRInstruction(Opcode.FFROMUB, IRDataType.FLOAT, reg1=cmpReg, fpReg1 = valueReg)
|
||||
it += IRInstruction(Opcode.STOREM, IRDataType.FLOAT, fpReg1 = valueReg, value=knownAddress)
|
||||
it += IRInstruction(Opcode.STOREM, IRDataType.FLOAT, fpReg1 = valueReg, address = knownAddress)
|
||||
}
|
||||
} else {
|
||||
// in-place modify a symbol (variable)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOADM, IRDataType.FLOAT, fpReg1 = valueReg, labelSymbol = symbol)
|
||||
it += IRInstruction(Opcode.FCOMP, IRDataType.FLOAT, reg1=cmpReg, fpReg1 = valueReg, fpReg2 = tr.resultFpReg)
|
||||
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=zeroReg, value=0)
|
||||
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=zeroReg, immediate = 0)
|
||||
it += IRInstruction(compareAndSetOpcode, IRDataType.BYTE, reg1=cmpReg, reg2=zeroReg)
|
||||
it += IRInstruction(Opcode.FFROMUB, IRDataType.FLOAT, reg1=cmpReg, fpReg1 = valueReg)
|
||||
it += IRInstruction(Opcode.STOREM, IRDataType.FLOAT, fpReg1 = valueReg, labelSymbol = symbol)
|
||||
|
||||
@@ -58,7 +58,7 @@ class IRCodeGen(
|
||||
val optimizer = IRPeepholeOptimizer(irProg)
|
||||
optimizer.optimize()
|
||||
|
||||
val remover = IRUnusedCodeRemover(irProg, errors)
|
||||
val remover = IRUnusedCodeRemover(irProg, irSymbolTable, errors)
|
||||
do {
|
||||
val numRemoved = remover.optimize()
|
||||
} while(numRemoved>0 && errors.noErrors())
|
||||
@@ -66,6 +66,9 @@ class IRCodeGen(
|
||||
errors.report()
|
||||
|
||||
irProg.linkChunks() // re-link
|
||||
} else {
|
||||
val optimizer = IRPeepholeOptimizer(irProg)
|
||||
optimizer.optimizeOnlyJoinChunks()
|
||||
}
|
||||
|
||||
irProg.validate()
|
||||
@@ -112,10 +115,10 @@ class IRCodeGen(
|
||||
block.children.firstOrNull { it is IRInlineAsmChunk }?.let { first->
|
||||
first as IRInlineAsmChunk
|
||||
if(first.label==null) {
|
||||
val replacement = IRInlineAsmChunk(block.name, first.assembly, first.isIR, first.next)
|
||||
val replacement = IRInlineAsmChunk(block.label, first.assembly, first.isIR, first.next)
|
||||
block.children.removeAt(0)
|
||||
block.children.add(0, replacement)
|
||||
} else if(first.label != block.name) {
|
||||
} else if(first.label != block.label) {
|
||||
throw AssemblyError("first chunk in block has label that differs from block name")
|
||||
}
|
||||
}
|
||||
@@ -182,7 +185,9 @@ class IRCodeGen(
|
||||
old.reg2,
|
||||
old.fpReg1,
|
||||
old.fpReg2,
|
||||
it.third.toInt(),
|
||||
null,
|
||||
null,
|
||||
address = it.third.toInt(),
|
||||
null,
|
||||
null
|
||||
)
|
||||
@@ -311,14 +316,14 @@ class IRCodeGen(
|
||||
val address = goto.address?.toInt()
|
||||
if(address!=null) {
|
||||
val branchIns = when(branch.condition) {
|
||||
BranchCondition.CS -> IRInstruction(Opcode.BSTCS, value = address)
|
||||
BranchCondition.CC -> IRInstruction(Opcode.BSTCC, value = address)
|
||||
BranchCondition.EQ, BranchCondition.Z -> IRInstruction(Opcode.BSTEQ, value = address)
|
||||
BranchCondition.NE, BranchCondition.NZ -> IRInstruction(Opcode.BSTNE, value = address)
|
||||
BranchCondition.MI, BranchCondition.NEG -> IRInstruction(Opcode.BSTNEG, value = address)
|
||||
BranchCondition.PL, BranchCondition.POS -> IRInstruction(Opcode.BSTPOS, value = address)
|
||||
BranchCondition.VC -> IRInstruction(Opcode.BSTVC, value = address)
|
||||
BranchCondition.VS -> IRInstruction(Opcode.BSTVS, value = address)
|
||||
BranchCondition.CS -> IRInstruction(Opcode.BSTCS, address = address)
|
||||
BranchCondition.CC -> IRInstruction(Opcode.BSTCC, address = address)
|
||||
BranchCondition.EQ, BranchCondition.Z -> IRInstruction(Opcode.BSTEQ, address = address)
|
||||
BranchCondition.NE, BranchCondition.NZ -> IRInstruction(Opcode.BSTNE, address = address)
|
||||
BranchCondition.MI, BranchCondition.NEG -> IRInstruction(Opcode.BSTNEG, address = address)
|
||||
BranchCondition.PL, BranchCondition.POS -> IRInstruction(Opcode.BSTPOS, address = address)
|
||||
BranchCondition.VC -> IRInstruction(Opcode.BSTVC, address = address)
|
||||
BranchCondition.VS -> IRInstruction(Opcode.BSTVS, address = address)
|
||||
}
|
||||
addInstr(result, branchIns, null)
|
||||
} else {
|
||||
@@ -414,11 +419,9 @@ class IRCodeGen(
|
||||
} else {
|
||||
val skipLabel = createLabelName()
|
||||
val values = choice.values.children.map {it as PtNumber}
|
||||
val choiceReg = registers.nextFree()
|
||||
if(values.size==1) {
|
||||
val chunk = IRCodeChunk(null, null)
|
||||
chunk += IRInstruction(Opcode.LOAD, valueDt, reg1=choiceReg, value=values[0].number.toInt())
|
||||
chunk += IRInstruction(Opcode.BNE, valueDt, reg1=valueTr.resultReg, reg2=choiceReg, labelSymbol = skipLabel)
|
||||
chunk += IRInstruction(Opcode.BNE, valueDt, reg1=valueTr.resultReg, immediate = values[0].number.toInt(), labelSymbol = skipLabel)
|
||||
result += chunk
|
||||
result += translateNode(choice.statements)
|
||||
if(choice.statements.children.last() !is PtReturn)
|
||||
@@ -427,8 +430,7 @@ class IRCodeGen(
|
||||
val matchLabel = createLabelName()
|
||||
val chunk = IRCodeChunk(null, null)
|
||||
for (value in values) {
|
||||
chunk += IRInstruction(Opcode.LOAD, valueDt, reg1=choiceReg, value=value.number.toInt())
|
||||
chunk += IRInstruction(Opcode.BEQ, valueDt, reg1=valueTr.resultReg, reg2=choiceReg, labelSymbol = matchLabel)
|
||||
chunk += IRInstruction(Opcode.BEQ, valueDt, reg1=valueTr.resultReg, immediate = value.number.toInt(), labelSymbol = matchLabel)
|
||||
}
|
||||
chunk += IRInstruction(Opcode.JUMP, labelSymbol = skipLabel)
|
||||
result += chunk
|
||||
@@ -464,10 +466,10 @@ class IRCodeGen(
|
||||
val endLabel = createLabelName()
|
||||
if(iterableVar.dt==DataType.STR) {
|
||||
// iterate over a zero-terminated string
|
||||
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=indexReg, value=0), null)
|
||||
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=indexReg, immediate = 0), null)
|
||||
val chunk = IRCodeChunk(loopLabel, null)
|
||||
chunk += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1=tmpReg, reg2=indexReg, labelSymbol = iterable.name)
|
||||
chunk += IRInstruction(Opcode.BZ, IRDataType.BYTE, reg1=tmpReg, labelSymbol = endLabel)
|
||||
chunk += IRInstruction(Opcode.BEQ, IRDataType.BYTE, reg1=tmpReg, immediate = 0, labelSymbol = endLabel)
|
||||
chunk += IRInstruction(Opcode.STOREM, IRDataType.BYTE, reg1=tmpReg, labelSymbol = loopvarSymbol)
|
||||
result += chunk
|
||||
result += translateNode(forLoop.statements)
|
||||
@@ -482,10 +484,8 @@ class IRCodeGen(
|
||||
val elementSize = program.memsizer.memorySize(elementDt)
|
||||
val lengthBytes = iterableVar.length!! * elementSize
|
||||
if(lengthBytes<256) {
|
||||
val lengthReg = registers.nextFree()
|
||||
val chunk = IRCodeChunk(null, null)
|
||||
chunk += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=indexReg, value=0)
|
||||
chunk += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=lengthReg, value=lengthBytes)
|
||||
chunk += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=indexReg, immediate = 0)
|
||||
result += chunk
|
||||
val chunk2 = IRCodeChunk(loopLabel, null)
|
||||
chunk2 += IRInstruction(Opcode.LOADX, irType(elementDt), reg1=tmpReg, reg2=indexReg, labelSymbol=iterable.name)
|
||||
@@ -493,16 +493,16 @@ class IRCodeGen(
|
||||
result += chunk2
|
||||
result += translateNode(forLoop.statements)
|
||||
result += addConstReg(IRDataType.BYTE, indexReg, elementSize)
|
||||
addInstr(result, IRInstruction(Opcode.BNE, IRDataType.BYTE, reg1=indexReg, reg2=lengthReg, labelSymbol = loopLabel), null)
|
||||
addInstr(result, IRInstruction(Opcode.BNE, IRDataType.BYTE, reg1=indexReg, immediate = lengthBytes, labelSymbol = loopLabel), null)
|
||||
} else if(lengthBytes==256) {
|
||||
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=indexReg, value=0), null)
|
||||
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=indexReg, immediate = 0), null)
|
||||
val chunk = IRCodeChunk(loopLabel, null)
|
||||
chunk += IRInstruction(Opcode.LOADX, irType(elementDt), reg1=tmpReg, reg2=indexReg, labelSymbol=iterable.name)
|
||||
chunk += IRInstruction(Opcode.STOREM, irType(elementDt), reg1=tmpReg, labelSymbol = loopvarSymbol)
|
||||
result += chunk
|
||||
result += translateNode(forLoop.statements)
|
||||
result += addConstReg(IRDataType.BYTE, indexReg, elementSize)
|
||||
addInstr(result, IRInstruction(Opcode.BNZ, IRDataType.BYTE, reg1=indexReg, labelSymbol = loopLabel), null)
|
||||
addInstr(result, IRInstruction(Opcode.BNE, IRDataType.BYTE, reg1=indexReg, immediate = 0, labelSymbol = loopLabel), null)
|
||||
} else {
|
||||
throw AssemblyError("iterator length should never exceed 256")
|
||||
}
|
||||
@@ -535,7 +535,7 @@ class IRCodeGen(
|
||||
addToResult(result, fromTr, fromTr.resultReg, -1)
|
||||
|
||||
val labelAfterFor = createLabelName()
|
||||
val greaterOpcode = if(loopvarDt in SignedDatatypes) Opcode.BGTS else Opcode.BGT
|
||||
val greaterOpcode = if(loopvarDt in SignedDatatypes) Opcode.BGTSR else Opcode.BGTR
|
||||
addInstr(result, IRInstruction(greaterOpcode, loopvarDtIr, fromTr.resultReg, toTr.resultReg, labelSymbol=labelAfterFor), null)
|
||||
|
||||
addInstr(result, IRInstruction(Opcode.STOREM, loopvarDtIr, reg1=fromTr.resultReg, labelSymbol=loopvarSymbol), null)
|
||||
@@ -543,7 +543,7 @@ class IRCodeGen(
|
||||
result += addConstMem(loopvarDtIr, null, loopvarSymbol, step)
|
||||
addInstr(result, IRInstruction(Opcode.LOADM, loopvarDtIr, reg1 = fromTr.resultReg, labelSymbol = loopvarSymbol), null)
|
||||
// if endvalue >= index, iterate loop
|
||||
val branchOpcode = if(loopvarDt in SignedDatatypes) Opcode.BGES else Opcode.BGE
|
||||
val branchOpcode = if(loopvarDt in SignedDatatypes) Opcode.BGESR else Opcode.BGER
|
||||
addInstr(result, IRInstruction(branchOpcode, loopvarDtIr, reg1=toTr.resultReg, reg2=fromTr.resultReg, labelSymbol=loopLabel), null)
|
||||
|
||||
result += IRCodeChunk(labelAfterFor, null)
|
||||
@@ -570,27 +570,16 @@ class IRCodeGen(
|
||||
if(step>0 && rangeEndUntyped<rangeStart || step<0 && rangeEndUntyped>rangeStart)
|
||||
throw AssemblyError("empty range")
|
||||
val rangeEndWrapped = if(loopvarDtIr==IRDataType.BYTE) rangeEndUntyped and 255 else rangeEndUntyped and 65535
|
||||
val endvalueReg: Int
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
val chunk = IRCodeChunk(null, null)
|
||||
if(rangeEndWrapped!=0) {
|
||||
endvalueReg = registers.nextFree()
|
||||
chunk += IRInstruction(Opcode.LOAD, loopvarDtIr, reg1 = endvalueReg, value = rangeEndWrapped)
|
||||
} else {
|
||||
endvalueReg = -1 // not used
|
||||
}
|
||||
chunk += IRInstruction(Opcode.LOAD, loopvarDtIr, reg1=indexReg, value=rangeStart)
|
||||
chunk += IRInstruction(Opcode.LOAD, loopvarDtIr, reg1=indexReg, immediate = rangeStart)
|
||||
chunk += IRInstruction(Opcode.STOREM, loopvarDtIr, reg1=indexReg, labelSymbol=loopvarSymbol)
|
||||
result += chunk
|
||||
result += labelFirstChunk(translateNode(forLoop.statements), loopLabel)
|
||||
result += addConstMem(loopvarDtIr, null, loopvarSymbol, step)
|
||||
val chunk2 = IRCodeChunk(null, null)
|
||||
chunk2 += IRInstruction(Opcode.LOADM, loopvarDtIr, reg1 = indexReg, labelSymbol = loopvarSymbol)
|
||||
chunk2 += if(rangeEndWrapped==0) {
|
||||
IRInstruction(Opcode.BNZ, loopvarDtIr, reg1 = indexReg, labelSymbol = loopLabel)
|
||||
} else {
|
||||
IRInstruction(Opcode.BNE, loopvarDtIr, reg1 = indexReg, reg2 = endvalueReg, labelSymbol = loopLabel)
|
||||
}
|
||||
chunk2 += IRInstruction(Opcode.BNE, loopvarDtIr, reg1 = indexReg, immediate = rangeEndWrapped, labelSymbol = loopLabel)
|
||||
result += chunk2
|
||||
return result
|
||||
}
|
||||
@@ -615,9 +604,9 @@ class IRCodeGen(
|
||||
}
|
||||
else -> {
|
||||
code += if(value>0) {
|
||||
IRInstruction(Opcode.ADD, dt, reg1 = reg, value=value)
|
||||
IRInstruction(Opcode.ADD, dt, reg1 = reg, immediate = value)
|
||||
} else {
|
||||
IRInstruction(Opcode.SUB, dt, reg1 = reg, value=-value)
|
||||
IRInstruction(Opcode.SUB, dt, reg1 = reg, immediate = -value)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -630,14 +619,14 @@ class IRCodeGen(
|
||||
0 -> { /* do nothing */ }
|
||||
1 -> {
|
||||
code += if(knownAddress!=null)
|
||||
IRInstruction(Opcode.INCM, dt, value=knownAddress.toInt())
|
||||
IRInstruction(Opcode.INCM, dt, address = knownAddress.toInt())
|
||||
else
|
||||
IRInstruction(Opcode.INCM, dt, labelSymbol = symbol)
|
||||
}
|
||||
2 -> {
|
||||
if(knownAddress!=null) {
|
||||
code += IRInstruction(Opcode.INCM, dt, value = knownAddress.toInt())
|
||||
code += IRInstruction(Opcode.INCM, dt, value = knownAddress.toInt())
|
||||
code += IRInstruction(Opcode.INCM, dt, address = knownAddress.toInt())
|
||||
code += IRInstruction(Opcode.INCM, dt, address = knownAddress.toInt())
|
||||
} else {
|
||||
code += IRInstruction(Opcode.INCM, dt, labelSymbol = symbol)
|
||||
code += IRInstruction(Opcode.INCM, dt, labelSymbol = symbol)
|
||||
@@ -645,14 +634,14 @@ class IRCodeGen(
|
||||
}
|
||||
-1 -> {
|
||||
code += if(knownAddress!=null)
|
||||
IRInstruction(Opcode.DECM, dt, value=knownAddress.toInt())
|
||||
IRInstruction(Opcode.DECM, dt, address = knownAddress.toInt())
|
||||
else
|
||||
IRInstruction(Opcode.DECM, dt, labelSymbol = symbol)
|
||||
}
|
||||
-2 -> {
|
||||
if(knownAddress!=null) {
|
||||
code += IRInstruction(Opcode.DECM, dt, value = knownAddress.toInt())
|
||||
code += IRInstruction(Opcode.DECM, dt, value = knownAddress.toInt())
|
||||
code += IRInstruction(Opcode.DECM, dt, address = knownAddress.toInt())
|
||||
code += IRInstruction(Opcode.DECM, dt, address = knownAddress.toInt())
|
||||
} else {
|
||||
code += IRInstruction(Opcode.DECM, dt, labelSymbol = symbol)
|
||||
code += IRInstruction(Opcode.DECM, dt, labelSymbol = symbol)
|
||||
@@ -661,16 +650,16 @@ class IRCodeGen(
|
||||
else -> {
|
||||
val valueReg = registers.nextFree()
|
||||
if(value>0) {
|
||||
code += IRInstruction(Opcode.LOAD, dt, reg1=valueReg, value=value)
|
||||
code += IRInstruction(Opcode.LOAD, dt, reg1=valueReg, immediate = value)
|
||||
code += if(knownAddress!=null)
|
||||
IRInstruction(Opcode.ADDM, dt, reg1=valueReg, value=knownAddress.toInt())
|
||||
IRInstruction(Opcode.ADDM, dt, reg1=valueReg, address = knownAddress.toInt())
|
||||
else
|
||||
IRInstruction(Opcode.ADDM, dt, reg1=valueReg, labelSymbol = symbol)
|
||||
}
|
||||
else {
|
||||
code += IRInstruction(Opcode.LOAD, dt, reg1=valueReg, value=-value)
|
||||
code += IRInstruction(Opcode.LOAD, dt, reg1=valueReg, immediate = -value)
|
||||
code += if(knownAddress!=null)
|
||||
IRInstruction(Opcode.SUBM, dt, reg1=valueReg, value=knownAddress.toInt())
|
||||
IRInstruction(Opcode.SUBM, dt, reg1=valueReg, address = knownAddress.toInt())
|
||||
else
|
||||
IRInstruction(Opcode.SUBM, dt, reg1=valueReg, labelSymbol = symbol)
|
||||
}
|
||||
@@ -684,9 +673,9 @@ class IRCodeGen(
|
||||
if(factor==1f)
|
||||
return code
|
||||
code += if(factor==0f) {
|
||||
IRInstruction(Opcode.LOAD, IRDataType.FLOAT, fpReg1 = fpReg, fpValue = 0f)
|
||||
IRInstruction(Opcode.LOAD, IRDataType.FLOAT, fpReg1 = fpReg, immediateFp = 0f)
|
||||
} else {
|
||||
IRInstruction(Opcode.MUL, IRDataType.FLOAT, fpReg1 = fpReg, fpValue=factor)
|
||||
IRInstruction(Opcode.MUL, IRDataType.FLOAT, fpReg1 = fpReg, immediateFp = factor)
|
||||
}
|
||||
return code
|
||||
}
|
||||
@@ -697,14 +686,14 @@ class IRCodeGen(
|
||||
return code
|
||||
if(factor==0f) {
|
||||
code += if(knownAddress!=null)
|
||||
IRInstruction(Opcode.STOREZM, IRDataType.FLOAT, value = knownAddress)
|
||||
IRInstruction(Opcode.STOREZM, IRDataType.FLOAT, address = knownAddress)
|
||||
else
|
||||
IRInstruction(Opcode.STOREZM, IRDataType.FLOAT, labelSymbol = symbol)
|
||||
} else {
|
||||
val factorReg = registers.nextFreeFloat()
|
||||
code += IRInstruction(Opcode.LOAD, IRDataType.FLOAT, fpReg1=factorReg, fpValue = factor)
|
||||
code += IRInstruction(Opcode.LOAD, IRDataType.FLOAT, fpReg1=factorReg, immediateFp = factor)
|
||||
code += if(knownAddress!=null)
|
||||
IRInstruction(Opcode.MULM, IRDataType.FLOAT, fpReg1 = factorReg, value = knownAddress)
|
||||
IRInstruction(Opcode.MULM, IRDataType.FLOAT, fpReg1 = factorReg, address = knownAddress)
|
||||
else
|
||||
IRInstruction(Opcode.MULM, IRDataType.FLOAT, fpReg1 = factorReg, labelSymbol = symbol)
|
||||
}
|
||||
@@ -725,13 +714,13 @@ class IRCodeGen(
|
||||
else if(pow2>=1) {
|
||||
// just shift multiple bits
|
||||
val pow2reg = registers.nextFree()
|
||||
code += IRInstruction(Opcode.LOAD, dt, reg1=pow2reg, value=pow2)
|
||||
code += IRInstruction(Opcode.LOAD, dt, reg1=pow2reg, immediate = pow2)
|
||||
code += IRInstruction(Opcode.LSLN, dt, reg1=reg, reg2=pow2reg)
|
||||
} else {
|
||||
code += if (factor == 0) {
|
||||
IRInstruction(Opcode.LOAD, dt, reg1=reg, value=0)
|
||||
IRInstruction(Opcode.LOAD, dt, reg1=reg, immediate = 0)
|
||||
} else {
|
||||
IRInstruction(Opcode.MUL, dt, reg1=reg, value=factor)
|
||||
IRInstruction(Opcode.MUL, dt, reg1=reg, immediate = factor)
|
||||
}
|
||||
}
|
||||
return code
|
||||
@@ -745,30 +734,30 @@ class IRCodeGen(
|
||||
if(pow2==1) {
|
||||
// just shift 1 bit
|
||||
code += if(knownAddress!=null)
|
||||
IRInstruction(Opcode.LSLM, dt, value = knownAddress)
|
||||
IRInstruction(Opcode.LSLM, dt, address = knownAddress)
|
||||
else
|
||||
IRInstruction(Opcode.LSLM, dt, labelSymbol = symbol)
|
||||
}
|
||||
else if(pow2>=1) {
|
||||
// just shift multiple bits
|
||||
val pow2reg = registers.nextFree()
|
||||
code += IRInstruction(Opcode.LOAD, dt, reg1=pow2reg, value=pow2)
|
||||
code += IRInstruction(Opcode.LOAD, dt, reg1=pow2reg, immediate = pow2)
|
||||
code += if(knownAddress!=null)
|
||||
IRInstruction(Opcode.LSLNM, dt, reg1=pow2reg, value=knownAddress)
|
||||
IRInstruction(Opcode.LSLNM, dt, reg1=pow2reg, address = knownAddress)
|
||||
else
|
||||
IRInstruction(Opcode.LSLNM, dt, reg1=pow2reg, labelSymbol = symbol)
|
||||
} else {
|
||||
if (factor == 0) {
|
||||
code += if(knownAddress!=null)
|
||||
IRInstruction(Opcode.STOREZM, dt, value=knownAddress)
|
||||
IRInstruction(Opcode.STOREZM, dt, address = knownAddress)
|
||||
else
|
||||
IRInstruction(Opcode.STOREZM, dt, labelSymbol = symbol)
|
||||
}
|
||||
else {
|
||||
val factorReg = registers.nextFree()
|
||||
code += IRInstruction(Opcode.LOAD, dt, reg1=factorReg, value = factor)
|
||||
code += IRInstruction(Opcode.LOAD, dt, reg1=factorReg, immediate = factor)
|
||||
code += if(knownAddress!=null)
|
||||
IRInstruction(Opcode.MULM, dt, reg1=factorReg, value = knownAddress)
|
||||
IRInstruction(Opcode.MULM, dt, reg1=factorReg, address = knownAddress)
|
||||
else
|
||||
IRInstruction(Opcode.MULM, dt, reg1=factorReg, labelSymbol = symbol)
|
||||
}
|
||||
@@ -781,9 +770,9 @@ class IRCodeGen(
|
||||
if(factor==1f)
|
||||
return code
|
||||
code += if(factor==0f) {
|
||||
IRInstruction(Opcode.LOAD, IRDataType.FLOAT, fpReg1 = fpReg, fpValue = Float.MAX_VALUE)
|
||||
IRInstruction(Opcode.LOAD, IRDataType.FLOAT, fpReg1 = fpReg, immediateFp = Float.MAX_VALUE)
|
||||
} else {
|
||||
IRInstruction(Opcode.DIVS, IRDataType.FLOAT, fpReg1 = fpReg, fpValue=factor)
|
||||
IRInstruction(Opcode.DIVS, IRDataType.FLOAT, fpReg1 = fpReg, immediateFp = factor)
|
||||
}
|
||||
return code
|
||||
}
|
||||
@@ -794,16 +783,16 @@ class IRCodeGen(
|
||||
return code
|
||||
if(factor==0f) {
|
||||
val maxvalueReg = registers.nextFreeFloat()
|
||||
code += IRInstruction(Opcode.LOAD, IRDataType.FLOAT, fpReg1 = maxvalueReg, fpValue = Float.MAX_VALUE)
|
||||
code += IRInstruction(Opcode.LOAD, IRDataType.FLOAT, fpReg1 = maxvalueReg, immediateFp = Float.MAX_VALUE)
|
||||
code += if(knownAddress!=null)
|
||||
IRInstruction(Opcode.STOREM, IRDataType.FLOAT, fpReg1 = maxvalueReg, value=knownAddress)
|
||||
IRInstruction(Opcode.STOREM, IRDataType.FLOAT, fpReg1 = maxvalueReg, address = knownAddress)
|
||||
else
|
||||
IRInstruction(Opcode.STOREM, IRDataType.FLOAT, fpReg1 = maxvalueReg, labelSymbol = symbol)
|
||||
} else {
|
||||
val factorReg = registers.nextFreeFloat()
|
||||
code += IRInstruction(Opcode.LOAD, IRDataType.FLOAT, fpReg1=factorReg, fpValue = factor)
|
||||
code += IRInstruction(Opcode.LOAD, IRDataType.FLOAT, fpReg1=factorReg, immediateFp = factor)
|
||||
code += if(knownAddress!=null)
|
||||
IRInstruction(Opcode.DIVSM, IRDataType.FLOAT, fpReg1 = factorReg, value=knownAddress)
|
||||
IRInstruction(Opcode.DIVSM, IRDataType.FLOAT, fpReg1 = factorReg, address = knownAddress)
|
||||
else
|
||||
IRInstruction(Opcode.DIVSM, IRDataType.FLOAT, fpReg1 = factorReg, labelSymbol = symbol)
|
||||
}
|
||||
@@ -821,19 +810,19 @@ class IRCodeGen(
|
||||
else if(pow2>=1 &&!signed) {
|
||||
// just shift multiple bits
|
||||
val pow2reg = registers.nextFree()
|
||||
code += IRInstruction(Opcode.LOAD, dt, reg1=pow2reg, value=pow2)
|
||||
code += IRInstruction(Opcode.LOAD, dt, reg1=pow2reg, immediate = pow2)
|
||||
code += if(signed)
|
||||
IRInstruction(Opcode.ASRN, dt, reg1=reg, reg2=pow2reg)
|
||||
else
|
||||
IRInstruction(Opcode.LSRN, dt, reg1=reg, reg2=pow2reg)
|
||||
} else {
|
||||
code += if (factor == 0) {
|
||||
IRInstruction(Opcode.LOAD, dt, reg1=reg, value=0xffff)
|
||||
IRInstruction(Opcode.LOAD, dt, reg1=reg, immediate = 0xffff)
|
||||
} else {
|
||||
if(signed)
|
||||
IRInstruction(Opcode.DIVS, dt, reg1=reg, value=factor)
|
||||
IRInstruction(Opcode.DIVS, dt, reg1=reg, immediate = factor)
|
||||
else
|
||||
IRInstruction(Opcode.DIV, dt, reg1=reg, value=factor)
|
||||
IRInstruction(Opcode.DIV, dt, reg1=reg, immediate = factor)
|
||||
}
|
||||
}
|
||||
return code
|
||||
@@ -847,47 +836,47 @@ class IRCodeGen(
|
||||
if(pow2==1 && !signed) {
|
||||
// just simple bit shift
|
||||
code += if(knownAddress!=null)
|
||||
IRInstruction(Opcode.LSRM, dt, value=knownAddress)
|
||||
IRInstruction(Opcode.LSRM, dt, address = knownAddress)
|
||||
else
|
||||
IRInstruction(Opcode.LSRM, dt, labelSymbol = symbol)
|
||||
}
|
||||
else if(pow2>=1 && !signed) {
|
||||
// just shift multiple bits
|
||||
val pow2reg = registers.nextFree()
|
||||
code += IRInstruction(Opcode.LOAD, dt, reg1=pow2reg, value=pow2)
|
||||
code += IRInstruction(Opcode.LOAD, dt, reg1=pow2reg, immediate = pow2)
|
||||
code += if(signed) {
|
||||
if(knownAddress!=null)
|
||||
IRInstruction(Opcode.ASRNM, dt, reg1 = pow2reg, value = knownAddress)
|
||||
IRInstruction(Opcode.ASRNM, dt, reg1 = pow2reg, address = knownAddress)
|
||||
else
|
||||
IRInstruction(Opcode.ASRNM, dt, reg1 = pow2reg, labelSymbol = symbol)
|
||||
}
|
||||
else {
|
||||
if(knownAddress!=null)
|
||||
IRInstruction(Opcode.LSRNM, dt, reg1 = pow2reg, value = knownAddress)
|
||||
IRInstruction(Opcode.LSRNM, dt, reg1 = pow2reg, address = knownAddress)
|
||||
else
|
||||
IRInstruction(Opcode.LSRNM, dt, reg1 = pow2reg, labelSymbol = symbol)
|
||||
}
|
||||
} else {
|
||||
if (factor == 0) {
|
||||
val reg = registers.nextFree()
|
||||
code += IRInstruction(Opcode.LOAD, dt, reg1=reg, value=0xffff)
|
||||
code += IRInstruction(Opcode.LOAD, dt, reg1=reg, immediate = 0xffff)
|
||||
code += if(knownAddress!=null)
|
||||
IRInstruction(Opcode.STOREM, dt, reg1=reg, value=knownAddress)
|
||||
IRInstruction(Opcode.STOREM, dt, reg1=reg, address = knownAddress)
|
||||
else
|
||||
IRInstruction(Opcode.STOREM, dt, reg1=reg, labelSymbol = symbol)
|
||||
}
|
||||
else {
|
||||
val factorReg = registers.nextFree()
|
||||
code += IRInstruction(Opcode.LOAD, dt, reg1=factorReg, value= factor)
|
||||
code += IRInstruction(Opcode.LOAD, dt, reg1=factorReg, immediate = factor)
|
||||
code += if(signed) {
|
||||
if(knownAddress!=null)
|
||||
IRInstruction(Opcode.DIVSM, dt, reg1 = factorReg, value = knownAddress)
|
||||
IRInstruction(Opcode.DIVSM, dt, reg1 = factorReg, address = knownAddress)
|
||||
else
|
||||
IRInstruction(Opcode.DIVSM, dt, reg1 = factorReg, labelSymbol = symbol)
|
||||
}
|
||||
else {
|
||||
if(knownAddress!=null)
|
||||
IRInstruction(Opcode.DIVM, dt, reg1 = factorReg, value = knownAddress)
|
||||
IRInstruction(Opcode.DIVM, dt, reg1 = factorReg, address = knownAddress)
|
||||
else
|
||||
IRInstruction(Opcode.DIVM, dt, reg1 = factorReg, labelSymbol = symbol)
|
||||
}
|
||||
@@ -929,18 +918,18 @@ class IRCodeGen(
|
||||
|
||||
|
||||
private fun translateIfFollowedByJustGoto(ifElse: PtIfElse, goto: PtJump, irDtLeft: IRDataType, signed: Boolean): MutableList<IRCodeChunkBase> {
|
||||
val conditionBinExpr = ifElse.condition as? PtBinaryExpression
|
||||
val condition = ifElse.condition as? PtBinaryExpression
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
if(conditionBinExpr==null) {
|
||||
if(condition==null) {
|
||||
if(irDtLeft==IRDataType.FLOAT)
|
||||
throw AssemblyError("condition value should not be float")
|
||||
ifNonZeroIntThenJump(result, ifElse, signed, irDtLeft, goto)
|
||||
return result
|
||||
} else {
|
||||
if (irDtLeft == IRDataType.FLOAT) {
|
||||
val leftTr = expressionEval.translateExpression(conditionBinExpr.left)
|
||||
val leftTr = expressionEval.translateExpression(condition.left)
|
||||
addToResult(result, leftTr, -1, leftTr.resultFpReg)
|
||||
val rightTr = expressionEval.translateExpression(conditionBinExpr.right)
|
||||
val rightTr = expressionEval.translateExpression(condition.right)
|
||||
addToResult(result, rightTr, -1, rightTr.resultFpReg)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
val compResultReg = registers.nextFree()
|
||||
@@ -951,40 +940,25 @@ class IRCodeGen(
|
||||
fpReg1 = leftTr.resultFpReg,
|
||||
fpReg2 = rightTr.resultFpReg
|
||||
)
|
||||
val gotoOpcode = when (conditionBinExpr.operator) {
|
||||
"==" -> Opcode.BZ
|
||||
"!=" -> Opcode.BNZ
|
||||
"<" -> Opcode.BLEZS
|
||||
">" -> Opcode.BGEZS
|
||||
"<=" -> Opcode.BLZS
|
||||
">=" -> Opcode.BGZS
|
||||
val gotoOpcode = when (condition.operator) {
|
||||
"==" -> Opcode.BEQ
|
||||
"!=" -> Opcode.BNE
|
||||
"<" -> Opcode.BLTS
|
||||
">" -> Opcode.BGTS
|
||||
"<=" -> Opcode.BLES
|
||||
">=" -> Opcode.BGES
|
||||
else -> throw AssemblyError("weird operator")
|
||||
}
|
||||
it += if (goto.address != null)
|
||||
IRInstruction(
|
||||
gotoOpcode,
|
||||
IRDataType.BYTE,
|
||||
reg1 = compResultReg,
|
||||
value = goto.address?.toInt()
|
||||
)
|
||||
IRInstruction(gotoOpcode, IRDataType.BYTE, reg1 = compResultReg, immediate = 0, address = goto.address?.toInt())
|
||||
else if (goto.generatedLabel != null)
|
||||
IRInstruction(
|
||||
gotoOpcode,
|
||||
IRDataType.BYTE,
|
||||
reg1 = compResultReg,
|
||||
labelSymbol = goto.generatedLabel
|
||||
)
|
||||
IRInstruction(gotoOpcode, IRDataType.BYTE, reg1 = compResultReg, immediate = 0, labelSymbol = goto.generatedLabel)
|
||||
else
|
||||
IRInstruction(
|
||||
gotoOpcode,
|
||||
IRDataType.BYTE,
|
||||
reg1 = compResultReg,
|
||||
labelSymbol = goto.identifier!!.name
|
||||
)
|
||||
IRInstruction(gotoOpcode, IRDataType.BYTE, reg1 = compResultReg, immediate = 0, labelSymbol = goto.identifier!!.name)
|
||||
}
|
||||
return result
|
||||
} else {
|
||||
val rightConst = conditionBinExpr.right.asConstInteger()
|
||||
val rightConst = condition.right.asConstInteger()
|
||||
if (rightConst == 0)
|
||||
ifZeroIntThenJump(result, ifElse, signed, irDtLeft, goto)
|
||||
else {
|
||||
@@ -1006,20 +980,20 @@ class IRCodeGen(
|
||||
val leftTr = expressionEval.translateExpression(condition.left)
|
||||
addToResult(result, leftTr, leftTr.resultReg, -1)
|
||||
val opcode = when (condition.operator) {
|
||||
"==" -> Opcode.BZ
|
||||
"!=" -> Opcode.BNZ
|
||||
"<" -> if (signed) Opcode.BLZS else throw AssemblyError("unsigned < 0 shouldn't occur in codegen")
|
||||
">" -> if (signed) Opcode.BGZS else throw AssemblyError("unsigned > 0 shouldn't occur in codegen")
|
||||
"<=" -> if (signed) Opcode.BLEZS else throw AssemblyError("unsigned <= 0 shouldn't occur in codegen")
|
||||
">=" -> if (signed) Opcode.BGEZS else throw AssemblyError("unsigned >= 0 shouldn't occur in codegen")
|
||||
"==" -> Opcode.BEQ
|
||||
"!=" -> Opcode.BNE
|
||||
"<" -> if (signed) Opcode.BLTS else throw AssemblyError("unsigned < 0 shouldn't occur in codegen")
|
||||
">" -> if (signed) Opcode.BGTS else throw AssemblyError("unsigned > 0 shouldn't occur in codegen")
|
||||
"<=" -> if (signed) Opcode.BLES else throw AssemblyError("unsigned <= 0 shouldn't occur in codegen")
|
||||
">=" -> if (signed) Opcode.BGES else throw AssemblyError("unsigned >= 0 shouldn't occur in codegen")
|
||||
else -> throw AssemblyError("invalid comparison operator")
|
||||
}
|
||||
if (goto.address != null)
|
||||
addInstr(result, IRInstruction(opcode, irDtLeft, reg1 = leftTr.resultReg, value = goto.address?.toInt()), null)
|
||||
addInstr(result, IRInstruction(opcode, irDtLeft, reg1 = leftTr.resultReg, immediate = 0, address = goto.address?.toInt()), null)
|
||||
else if (goto.generatedLabel != null)
|
||||
addInstr(result, IRInstruction(opcode, irDtLeft, reg1 = leftTr.resultReg, labelSymbol = goto.generatedLabel), null)
|
||||
addInstr(result, IRInstruction(opcode, irDtLeft, reg1 = leftTr.resultReg, immediate = 0, labelSymbol = goto.generatedLabel), null)
|
||||
else
|
||||
addInstr(result, IRInstruction(opcode, irDtLeft, reg1 = leftTr.resultReg, labelSymbol = goto.identifier!!.name), null)
|
||||
addInstr(result, IRInstruction(opcode, irDtLeft, reg1 = leftTr.resultReg, immediate = 0, labelSymbol = goto.identifier!!.name), null)
|
||||
}
|
||||
|
||||
private fun ifNonZeroIntThenJump(
|
||||
@@ -1029,65 +1003,85 @@ class IRCodeGen(
|
||||
irDtLeft: IRDataType,
|
||||
goto: PtJump
|
||||
) {
|
||||
val conditionBinExpr = ifElse.condition as? PtBinaryExpression
|
||||
if(conditionBinExpr==null) {
|
||||
val condition = ifElse.condition as? PtBinaryExpression
|
||||
if(condition==null) {
|
||||
val tr = expressionEval.translateExpression(ifElse.condition)
|
||||
result += tr.chunks
|
||||
if (goto.address != null)
|
||||
addInstr(result, IRInstruction(Opcode.BNZ, irDtLeft, reg1 = tr.resultReg, value = goto.address?.toInt()), null)
|
||||
addInstr(result, IRInstruction(Opcode.BNE, irDtLeft, reg1 = tr.resultReg, immediate = 0, address = goto.address?.toInt()), null)
|
||||
else if (goto.generatedLabel != null)
|
||||
addInstr(result, IRInstruction(Opcode.BNZ, irDtLeft, reg1 = tr.resultReg, labelSymbol = goto.generatedLabel), null)
|
||||
addInstr(result, IRInstruction(Opcode.BNE, irDtLeft, reg1 = tr.resultReg, immediate = 0, labelSymbol = goto.generatedLabel), null)
|
||||
else
|
||||
addInstr(result, IRInstruction(Opcode.BNZ, irDtLeft, reg1 = tr.resultReg, labelSymbol = goto.identifier!!.name), null)
|
||||
addInstr(result, IRInstruction(Opcode.BNE, irDtLeft, reg1 = tr.resultReg, immediate = 0, labelSymbol = goto.identifier!!.name), null)
|
||||
} else {
|
||||
val leftTr = expressionEval.translateExpression(conditionBinExpr.left)
|
||||
val leftTr = expressionEval.translateExpression(condition.left)
|
||||
addToResult(result, leftTr, leftTr.resultReg, -1)
|
||||
val rightTr = expressionEval.translateExpression(conditionBinExpr.right)
|
||||
addToResult(result, rightTr, rightTr.resultReg, -1)
|
||||
val opcode: Opcode
|
||||
val firstReg: Int
|
||||
val secondReg: Int
|
||||
when (conditionBinExpr.operator) {
|
||||
"==" -> {
|
||||
opcode = Opcode.BEQ
|
||||
firstReg = leftTr.resultReg
|
||||
secondReg = rightTr.resultReg
|
||||
val number = (condition.right as? PtNumber)?.number?.toInt()
|
||||
if(number!=null) {
|
||||
val firstReg = leftTr.resultReg
|
||||
opcode = when (condition.operator) {
|
||||
"==" -> Opcode.BEQ
|
||||
"!=" -> Opcode.BNE
|
||||
"<" -> if(signed) Opcode.BLTS else Opcode.BLT
|
||||
">" -> if(signed) Opcode.BGTS else Opcode.BGT
|
||||
"<=" -> if(signed) Opcode.BLES else Opcode.BLE
|
||||
">=" -> if(signed) Opcode.BGES else Opcode.BGE
|
||||
else -> throw AssemblyError("invalid comparison operator")
|
||||
}
|
||||
"!=" -> {
|
||||
opcode = Opcode.BNE
|
||||
firstReg = leftTr.resultReg
|
||||
secondReg = rightTr.resultReg
|
||||
if (goto.address != null)
|
||||
addInstr(result, IRInstruction(opcode, irDtLeft, reg1 = firstReg, immediate = number, address = goto.address?.toInt()), null)
|
||||
else if (goto.generatedLabel != null)
|
||||
addInstr(result, IRInstruction(opcode, irDtLeft, reg1 = firstReg, immediate = number, labelSymbol = goto.generatedLabel), null)
|
||||
else
|
||||
addInstr(result, IRInstruction(opcode, irDtLeft, reg1 = firstReg, immediate = number, labelSymbol = goto.identifier!!.name), null)
|
||||
} else {
|
||||
val rightTr = expressionEval.translateExpression(condition.right)
|
||||
addToResult(result, rightTr, rightTr.resultReg, -1)
|
||||
val firstReg: Int
|
||||
val secondReg: Int
|
||||
when (condition.operator) {
|
||||
"==" -> {
|
||||
opcode = Opcode.BEQR
|
||||
firstReg = leftTr.resultReg
|
||||
secondReg = rightTr.resultReg
|
||||
}
|
||||
"!=" -> {
|
||||
opcode = Opcode.BNER
|
||||
firstReg = leftTr.resultReg
|
||||
secondReg = rightTr.resultReg
|
||||
}
|
||||
"<" -> {
|
||||
// swapped '>'
|
||||
opcode = if (signed) Opcode.BGTSR else Opcode.BGTR
|
||||
firstReg = rightTr.resultReg
|
||||
secondReg = leftTr.resultReg
|
||||
}
|
||||
">" -> {
|
||||
opcode = if (signed) Opcode.BGTSR else Opcode.BGTR
|
||||
firstReg = leftTr.resultReg
|
||||
secondReg = rightTr.resultReg
|
||||
}
|
||||
"<=" -> {
|
||||
// swapped '>='
|
||||
opcode = if (signed) Opcode.BGESR else Opcode.BGER
|
||||
firstReg = rightTr.resultReg
|
||||
secondReg = leftTr.resultReg
|
||||
}
|
||||
">=" -> {
|
||||
opcode = if (signed) Opcode.BGESR else Opcode.BGER
|
||||
firstReg = leftTr.resultReg
|
||||
secondReg = rightTr.resultReg
|
||||
}
|
||||
else -> throw AssemblyError("invalid comparison operator")
|
||||
}
|
||||
"<" -> {
|
||||
// swapped '>'
|
||||
opcode = if (signed) Opcode.BGTS else Opcode.BGT
|
||||
firstReg = rightTr.resultReg
|
||||
secondReg = leftTr.resultReg
|
||||
}
|
||||
">" -> {
|
||||
opcode = if (signed) Opcode.BGTS else Opcode.BGT
|
||||
firstReg = leftTr.resultReg
|
||||
secondReg = rightTr.resultReg
|
||||
}
|
||||
"<=" -> {
|
||||
// swapped '>='
|
||||
opcode = if (signed) Opcode.BGES else Opcode.BGE
|
||||
firstReg = rightTr.resultReg
|
||||
secondReg = leftTr.resultReg
|
||||
}
|
||||
">=" -> {
|
||||
opcode = if (signed) Opcode.BGES else Opcode.BGE
|
||||
firstReg = leftTr.resultReg
|
||||
secondReg = rightTr.resultReg
|
||||
}
|
||||
else -> throw AssemblyError("invalid comparison operator")
|
||||
if (goto.address != null)
|
||||
addInstr(result, IRInstruction(opcode, irDtLeft, reg1 = firstReg, reg2 = secondReg, address = goto.address?.toInt()), null)
|
||||
else if (goto.generatedLabel != null)
|
||||
addInstr(result, IRInstruction(opcode, irDtLeft, reg1 = firstReg, reg2 = secondReg, labelSymbol = goto.generatedLabel), null)
|
||||
else
|
||||
addInstr(result, IRInstruction(opcode, irDtLeft, reg1 = firstReg, reg2 = secondReg, labelSymbol = goto.identifier!!.name), null)
|
||||
}
|
||||
if (goto.address != null)
|
||||
addInstr(result, IRInstruction(opcode, irDtLeft, reg1 = firstReg, reg2 = secondReg, value = goto.address?.toInt()), null)
|
||||
else if (goto.generatedLabel != null)
|
||||
addInstr(result, IRInstruction(opcode, irDtLeft, reg1 = firstReg, reg2 = secondReg, labelSymbol = goto.generatedLabel), null)
|
||||
else
|
||||
addInstr(result, IRInstruction(opcode, irDtLeft, reg1 = firstReg, reg2 = secondReg, labelSymbol = goto.identifier!!.name), null)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1104,16 +1098,16 @@ class IRCodeGen(
|
||||
addToResult(result, leftTr, -1, leftTr.resultFpReg)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
val rightFpReg = registers.nextFreeFloat()
|
||||
it += IRInstruction(Opcode.LOAD, IRDataType.FLOAT, fpReg1 = rightFpReg, fpValue = 0f)
|
||||
it += IRInstruction(Opcode.LOAD, IRDataType.FLOAT, fpReg1 = rightFpReg, immediateFp = 0f)
|
||||
it += IRInstruction(Opcode.FCOMP, IRDataType.FLOAT, reg1=compResultReg, fpReg1 = leftTr.resultFpReg, fpReg2 = rightFpReg)
|
||||
}
|
||||
elseBranch = when (condition.operator) {
|
||||
"==" -> Opcode.BNZ
|
||||
"!=" -> Opcode.BZ
|
||||
"<" -> Opcode.BGEZS
|
||||
">" -> Opcode.BLEZS
|
||||
"<=" -> Opcode.BGZS
|
||||
">=" -> Opcode.BLZS
|
||||
"==" -> Opcode.BNE
|
||||
"!=" -> Opcode.BEQ
|
||||
"<" -> Opcode.BGES
|
||||
">" -> Opcode.BLES
|
||||
"<=" -> Opcode.BGTS
|
||||
">=" -> Opcode.BLTS
|
||||
else -> throw AssemblyError("weird operator")
|
||||
}
|
||||
} else {
|
||||
@@ -1123,12 +1117,12 @@ class IRCodeGen(
|
||||
compResultReg = tr.resultReg
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
elseBranch = when (condition.operator) {
|
||||
"==" -> Opcode.BNZ
|
||||
"!=" -> Opcode.BZ
|
||||
"<" -> if (signed) Opcode.BGEZS else throw AssemblyError("unsigned < 0 shouldn't occur in codegen")
|
||||
">" -> if (signed) Opcode.BLEZS else throw AssemblyError("unsigned > 0 shouldn't occur in codegen")
|
||||
"<=" -> if (signed) Opcode.BGZS else throw AssemblyError("unsigned <= 0 shouldn't occur in codegen")
|
||||
">=" -> if (signed) Opcode.BLZS else throw AssemblyError("unsigned >= 0 shouldn't occur in codegen")
|
||||
"==" -> Opcode.BNE
|
||||
"!=" -> Opcode.BEQ
|
||||
"<" -> if (signed) Opcode.BGES else throw AssemblyError("unsigned < 0 shouldn't occur in codegen")
|
||||
">" -> if (signed) Opcode.BLES else throw AssemblyError("unsigned > 0 shouldn't occur in codegen")
|
||||
"<=" -> if (signed) Opcode.BGTS else throw AssemblyError("unsigned <= 0 shouldn't occur in codegen")
|
||||
">=" -> if (signed) Opcode.BLTS else throw AssemblyError("unsigned >= 0 shouldn't occur in codegen")
|
||||
else -> throw AssemblyError("weird operator")
|
||||
}
|
||||
}
|
||||
@@ -1136,14 +1130,14 @@ class IRCodeGen(
|
||||
if(ifElse.elseScope.children.isEmpty()) {
|
||||
// just if
|
||||
val afterIfLabel = createLabelName()
|
||||
addInstr(result, IRInstruction(elseBranch, branchDt, reg1=compResultReg, labelSymbol = afterIfLabel), null)
|
||||
addInstr(result, IRInstruction(elseBranch, branchDt, reg1=compResultReg, immediate = 0, labelSymbol = afterIfLabel), null)
|
||||
result += translateNode(ifElse.ifScope)
|
||||
result += IRCodeChunk(afterIfLabel, null)
|
||||
} else {
|
||||
// if and else
|
||||
val elseLabel = createLabelName()
|
||||
val afterIfLabel = createLabelName()
|
||||
addInstr(result, IRInstruction(elseBranch, branchDt, reg1=compResultReg, labelSymbol = elseLabel), null)
|
||||
addInstr(result, IRInstruction(elseBranch, branchDt, reg1=compResultReg, immediate = 0, labelSymbol = elseLabel), null)
|
||||
result += translateNode(ifElse.ifScope)
|
||||
addInstr(result, IRInstruction(Opcode.JUMP, labelSymbol = afterIfLabel), null)
|
||||
result += labelFirstChunk(translateNode(ifElse.elseScope), elseLabel)
|
||||
@@ -1167,14 +1161,14 @@ class IRCodeGen(
|
||||
if(ifElse.elseScope.children.isNotEmpty()) {
|
||||
val elseLabel = createLabelName()
|
||||
val afterIfLabel = createLabelName()
|
||||
addInstr(result, IRInstruction(Opcode.BZ, irDtLeft, reg1=tr.resultReg, labelSymbol = elseLabel), null)
|
||||
addInstr(result, IRInstruction(Opcode.BEQ, irDtLeft, reg1=tr.resultReg, immediate = 0, labelSymbol = elseLabel), null)
|
||||
result += translateNode(ifElse.ifScope)
|
||||
addInstr(result, IRInstruction(Opcode.JUMP, labelSymbol = afterIfLabel), null)
|
||||
result += labelFirstChunk(translateNode(ifElse.elseScope), elseLabel)
|
||||
result += IRCodeChunk(afterIfLabel, null)
|
||||
} else {
|
||||
val afterIfLabel = createLabelName()
|
||||
addInstr(result, IRInstruction(Opcode.BZ, irDtLeft, reg1=tr.resultReg, labelSymbol = afterIfLabel), null)
|
||||
addInstr(result, IRInstruction(Opcode.BEQ, irDtLeft, reg1=tr.resultReg, immediate = 0, labelSymbol = afterIfLabel), null)
|
||||
result += translateNode(ifElse.ifScope)
|
||||
result += IRCodeChunk(afterIfLabel, null)
|
||||
}
|
||||
@@ -1198,12 +1192,12 @@ class IRCodeGen(
|
||||
null
|
||||
)
|
||||
val elseBranch = when (condition.operator) {
|
||||
"==" -> Opcode.BNZ
|
||||
"!=" -> Opcode.BZ
|
||||
"<" -> Opcode.BGEZS
|
||||
">" -> Opcode.BLEZS
|
||||
"<=" -> Opcode.BGZS
|
||||
">=" -> Opcode.BLZS
|
||||
"==" -> Opcode.BNE
|
||||
"!=" -> Opcode.BEQ
|
||||
"<" -> Opcode.BGES
|
||||
">" -> Opcode.BLES
|
||||
"<=" -> Opcode.BGTS
|
||||
">=" -> Opcode.BLTS
|
||||
else -> throw AssemblyError("weird operator")
|
||||
}
|
||||
if (ifElse.elseScope.children.isNotEmpty()) {
|
||||
@@ -1235,81 +1229,116 @@ class IRCodeGen(
|
||||
branchDt = irDtLeft
|
||||
val leftTr = expressionEval.translateExpression(condition.left)
|
||||
addToResult(result, leftTr, leftTr.resultReg, -1)
|
||||
val rightTr = expressionEval.translateExpression(condition.right)
|
||||
addToResult(result, rightTr, rightTr.resultReg, -1)
|
||||
when (condition.operator) {
|
||||
"==" -> {
|
||||
elseBranchOpcode = Opcode.BNE
|
||||
elseBranchFirstReg = leftTr.resultReg
|
||||
elseBranchSecondReg = rightTr.resultReg
|
||||
val number = (condition.right as? PtNumber)?.number?.toInt()
|
||||
if (number!=null) {
|
||||
elseBranchOpcode = when (condition.operator) {
|
||||
"==" -> Opcode.BNE
|
||||
"!=" -> Opcode.BEQ
|
||||
"<" -> if(signed) Opcode.BGES else Opcode.BGE
|
||||
">" -> if(signed) Opcode.BLES else Opcode.BLE
|
||||
"<=" -> if(signed) Opcode.BGTS else Opcode.BGT
|
||||
">=" -> if(signed) Opcode.BLTS else Opcode.BLT
|
||||
else -> throw AssemblyError("invalid comparison operator")
|
||||
}
|
||||
|
||||
"!=" -> {
|
||||
elseBranchOpcode = Opcode.BEQ
|
||||
elseBranchFirstReg = leftTr.resultReg
|
||||
elseBranchSecondReg = rightTr.resultReg
|
||||
if (ifElse.elseScope.children.isNotEmpty()) {
|
||||
// if and else parts
|
||||
val elseLabel = createLabelName()
|
||||
val afterIfLabel = createLabelName()
|
||||
addInstr(
|
||||
result, IRInstruction(
|
||||
elseBranchOpcode, branchDt,
|
||||
reg1 = leftTr.resultReg, immediate = number,
|
||||
labelSymbol = elseLabel
|
||||
), null
|
||||
)
|
||||
result += translateNode(ifElse.ifScope)
|
||||
addInstr(result, IRInstruction(Opcode.JUMP, labelSymbol = afterIfLabel), null)
|
||||
result += labelFirstChunk(translateNode(ifElse.elseScope), elseLabel)
|
||||
result += IRCodeChunk(afterIfLabel, null)
|
||||
} else {
|
||||
// only if part
|
||||
val afterIfLabel = createLabelName()
|
||||
addInstr(
|
||||
result, IRInstruction(
|
||||
elseBranchOpcode, branchDt,
|
||||
reg1 = leftTr.resultReg, immediate = number,
|
||||
labelSymbol = afterIfLabel
|
||||
), null
|
||||
)
|
||||
result += translateNode(ifElse.ifScope)
|
||||
result += IRCodeChunk(afterIfLabel, null)
|
||||
}
|
||||
|
||||
"<" -> {
|
||||
// else part when left >= right
|
||||
elseBranchOpcode = if (signed) Opcode.BGES else Opcode.BGE
|
||||
elseBranchFirstReg = leftTr.resultReg
|
||||
elseBranchSecondReg = rightTr.resultReg
|
||||
}
|
||||
|
||||
">" -> {
|
||||
// else part when left <= right --> right >= left
|
||||
elseBranchOpcode = if (signed) Opcode.BGES else Opcode.BGE
|
||||
elseBranchFirstReg = rightTr.resultReg
|
||||
elseBranchSecondReg = leftTr.resultReg
|
||||
}
|
||||
|
||||
"<=" -> {
|
||||
// else part when left > right
|
||||
elseBranchOpcode = if (signed) Opcode.BGTS else Opcode.BGT
|
||||
elseBranchFirstReg = leftTr.resultReg
|
||||
elseBranchSecondReg = rightTr.resultReg
|
||||
}
|
||||
|
||||
">=" -> {
|
||||
// else part when left < right --> right > left
|
||||
elseBranchOpcode = if (signed) Opcode.BGTS else Opcode.BGT
|
||||
elseBranchFirstReg = rightTr.resultReg
|
||||
elseBranchSecondReg = leftTr.resultReg
|
||||
}
|
||||
|
||||
else -> throw AssemblyError("invalid comparison operator")
|
||||
}
|
||||
if (ifElse.elseScope.children.isNotEmpty()) {
|
||||
// if and else parts
|
||||
val elseLabel = createLabelName()
|
||||
val afterIfLabel = createLabelName()
|
||||
addInstr(
|
||||
result, IRInstruction(
|
||||
elseBranchOpcode, branchDt,
|
||||
reg1 = elseBranchFirstReg, reg2 = elseBranchSecondReg,
|
||||
labelSymbol = elseLabel
|
||||
), null
|
||||
)
|
||||
result += translateNode(ifElse.ifScope)
|
||||
addInstr(result, IRInstruction(Opcode.JUMP, labelSymbol = afterIfLabel), null)
|
||||
result += labelFirstChunk(translateNode(ifElse.elseScope), elseLabel)
|
||||
result += IRCodeChunk(afterIfLabel, null)
|
||||
} else {
|
||||
// only if part
|
||||
val afterIfLabel = createLabelName()
|
||||
addInstr(
|
||||
result, IRInstruction(
|
||||
elseBranchOpcode, branchDt,
|
||||
reg1 = elseBranchFirstReg, reg2 = elseBranchSecondReg,
|
||||
labelSymbol = afterIfLabel
|
||||
), null
|
||||
)
|
||||
result += translateNode(ifElse.ifScope)
|
||||
result += IRCodeChunk(afterIfLabel, null)
|
||||
val rightTr = expressionEval.translateExpression(condition.right)
|
||||
addToResult(result, rightTr, rightTr.resultReg, -1)
|
||||
when (condition.operator) {
|
||||
"==" -> {
|
||||
elseBranchOpcode = Opcode.BNER
|
||||
elseBranchFirstReg = leftTr.resultReg
|
||||
elseBranchSecondReg = rightTr.resultReg
|
||||
}
|
||||
"!=" -> {
|
||||
elseBranchOpcode = Opcode.BEQR
|
||||
elseBranchFirstReg = leftTr.resultReg
|
||||
elseBranchSecondReg = rightTr.resultReg
|
||||
}
|
||||
"<" -> {
|
||||
// else part when left >= right
|
||||
elseBranchOpcode = if (signed) Opcode.BGESR else Opcode.BGER
|
||||
elseBranchFirstReg = leftTr.resultReg
|
||||
elseBranchSecondReg = rightTr.resultReg
|
||||
}
|
||||
">" -> {
|
||||
// else part when left <= right --> right >= left
|
||||
elseBranchOpcode = if (signed) Opcode.BGESR else Opcode.BGER
|
||||
elseBranchFirstReg = rightTr.resultReg
|
||||
elseBranchSecondReg = leftTr.resultReg
|
||||
}
|
||||
"<=" -> {
|
||||
// else part when left > right
|
||||
elseBranchOpcode = if (signed) Opcode.BGTSR else Opcode.BGTR
|
||||
elseBranchFirstReg = leftTr.resultReg
|
||||
elseBranchSecondReg = rightTr.resultReg
|
||||
}
|
||||
|
||||
">=" -> {
|
||||
// else part when left < right --> right > left
|
||||
elseBranchOpcode = if (signed) Opcode.BGTSR else Opcode.BGTR
|
||||
elseBranchFirstReg = rightTr.resultReg
|
||||
elseBranchSecondReg = leftTr.resultReg
|
||||
}
|
||||
else -> throw AssemblyError("invalid comparison operator")
|
||||
}
|
||||
if (ifElse.elseScope.children.isNotEmpty()) {
|
||||
// if and else parts
|
||||
val elseLabel = createLabelName()
|
||||
val afterIfLabel = createLabelName()
|
||||
addInstr(
|
||||
result, IRInstruction(
|
||||
elseBranchOpcode, branchDt,
|
||||
reg1 = elseBranchFirstReg, reg2 = elseBranchSecondReg,
|
||||
labelSymbol = elseLabel
|
||||
), null
|
||||
)
|
||||
result += translateNode(ifElse.ifScope)
|
||||
addInstr(result, IRInstruction(Opcode.JUMP, labelSymbol = afterIfLabel), null)
|
||||
result += labelFirstChunk(translateNode(ifElse.elseScope), elseLabel)
|
||||
result += IRCodeChunk(afterIfLabel, null)
|
||||
} else {
|
||||
// only if part
|
||||
val afterIfLabel = createLabelName()
|
||||
addInstr(
|
||||
result, IRInstruction(
|
||||
elseBranchOpcode, branchDt,
|
||||
reg1 = elseBranchFirstReg, reg2 = elseBranchSecondReg,
|
||||
labelSymbol = afterIfLabel
|
||||
), null
|
||||
)
|
||||
result += translateNode(ifElse.ifScope)
|
||||
result += IRCodeChunk(afterIfLabel, null)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
}
|
||||
@@ -1338,7 +1367,7 @@ class IRCodeGen(
|
||||
} else if(memory!=null) {
|
||||
if(memory.address is PtNumber) {
|
||||
val address = (memory.address as PtNumber).number.toInt()
|
||||
addInstr(result, IRInstruction(operationMem, irDt, value = address), null)
|
||||
addInstr(result, IRInstruction(operationMem, irDt, address = address), null)
|
||||
} else {
|
||||
val tr = expressionEval.translateExpression(memory.address)
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
@@ -1388,11 +1417,11 @@ class IRCodeGen(
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
val countTr = expressionEval.translateExpression(repeat.count)
|
||||
addToResult(result, countTr, countTr.resultReg, -1)
|
||||
addInstr(result, IRInstruction(Opcode.BZ, irDt, reg1=countTr.resultReg, labelSymbol = skipRepeatLabel), null)
|
||||
addInstr(result, IRInstruction(Opcode.BEQ, irDt, reg1=countTr.resultReg, immediate = 0, labelSymbol = skipRepeatLabel), null)
|
||||
result += labelFirstChunk(translateNode(repeat.statements), repeatLabel)
|
||||
val chunk = IRCodeChunk(null, null)
|
||||
chunk += IRInstruction(Opcode.DEC, irDt, reg1=countTr.resultReg)
|
||||
chunk += IRInstruction(Opcode.BNZ, irDt, reg1=countTr.resultReg, labelSymbol = repeatLabel)
|
||||
chunk += IRInstruction(Opcode.BNE, irDt, reg1=countTr.resultReg, immediate = 0, labelSymbol = repeatLabel)
|
||||
result += chunk
|
||||
result += IRCodeChunk(skipRepeatLabel, null)
|
||||
return result
|
||||
@@ -1401,7 +1430,7 @@ class IRCodeGen(
|
||||
private fun translate(jump: PtJump): IRCodeChunks {
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
val instr = if(jump.address!=null) {
|
||||
IRInstruction(Opcode.JUMPA, value = jump.address!!.toInt())
|
||||
IRInstruction(Opcode.JUMPA, address = jump.address!!.toInt())
|
||||
} else {
|
||||
if (jump.generatedLabel != null)
|
||||
IRInstruction(Opcode.JUMP, labelSymbol = jump.generatedLabel!!)
|
||||
@@ -1429,12 +1458,12 @@ class IRCodeGen(
|
||||
if(value.type==DataType.FLOAT) {
|
||||
val tr = expressionEval.translateExpression(value)
|
||||
addToResult(result, tr, -1, tr.resultFpReg)
|
||||
addInstr(result, IRInstruction(Opcode.RETURNREG, IRDataType.FLOAT, fpReg1 = tr.resultFpReg), null)
|
||||
addInstr(result, IRInstruction(Opcode.RETURNR, IRDataType.FLOAT, fpReg1 = tr.resultFpReg), null)
|
||||
}
|
||||
else {
|
||||
val tr = expressionEval.translateExpression(value)
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
addInstr(result, IRInstruction(Opcode.RETURNREG, irType(value.type) , reg1=tr.resultReg), null)
|
||||
addInstr(result, IRInstruction(Opcode.RETURNR, irType(value.type) , reg1=tr.resultReg), null)
|
||||
}
|
||||
}
|
||||
return result
|
||||
@@ -1462,9 +1491,12 @@ class IRCodeGen(
|
||||
}
|
||||
} else {
|
||||
// regular asmsub
|
||||
val assemblyChild = child.children.single() as PtInlineAssembly
|
||||
if(child.children.map { (it as PtInlineAssembly).isIR }.toSet().size>1)
|
||||
errors.err("asmsub mixes IR and non-IR assembly code (could be compiler-generated)", child.position)
|
||||
val asmblocks = child.children.map { (it as PtInlineAssembly).assembly.trimEnd() }
|
||||
val assembly = asmblocks.joinToString("\n")
|
||||
val asmChunk = IRInlineAsmChunk(
|
||||
child.name, assemblyChild.assembly, assemblyChild.isIR, null
|
||||
child.name, assembly, (child.children[0] as PtInlineAssembly).isIR , null
|
||||
)
|
||||
irBlock += IRAsmSubroutine(
|
||||
child.name,
|
||||
|
||||
@@ -3,6 +3,14 @@ package prog8.codegen.intermediate
|
||||
import prog8.intermediate.*
|
||||
|
||||
internal class IRPeepholeOptimizer(private val irprog: IRProgram) {
|
||||
fun optimizeOnlyJoinChunks() {
|
||||
irprog.blocks.asSequence().flatMap { it.children.filterIsInstance<IRSubroutine>() }.forEach { sub ->
|
||||
removeEmptyChunks(sub)
|
||||
joinChunks(sub)
|
||||
}
|
||||
irprog.linkChunks() // re-link
|
||||
}
|
||||
|
||||
fun optimize() {
|
||||
irprog.blocks.asSequence().flatMap { it.children.filterIsInstance<IRSubroutine>() }.forEach { sub ->
|
||||
removeEmptyChunks(sub)
|
||||
@@ -38,7 +46,7 @@ internal class IRPeepholeOptimizer(private val irprog: IRProgram) {
|
||||
/*
|
||||
Empty Code chunk with label ->
|
||||
If next chunk has no label -> move label to next chunk, remove original
|
||||
If next chunk has label -> label name should be the same, remove original. Otherwise FOR NOW leave it in place. (TODO: consolidate labels into 1)
|
||||
If next chunk has label -> label name should be the same, remove original. Otherwise FOR NOW leave it in place. (TODO: merge both labels into 1)
|
||||
If is last chunk -> keep chunk in place because of the label.
|
||||
Empty Code chunk without label ->
|
||||
should not have been generated! ERROR.
|
||||
@@ -63,7 +71,7 @@ internal class IRPeepholeOptimizer(private val irprog: IRProgram) {
|
||||
if (chunk.label == nextchunk.label)
|
||||
removeChunks += index
|
||||
else {
|
||||
// TODO: consolidate labels on same chunk
|
||||
// TODO: merge labels on same chunk
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -176,7 +184,7 @@ internal class IRPeepholeOptimizer(private val irprog: IRProgram) {
|
||||
}
|
||||
|
||||
// remove useless RETURN
|
||||
if(idx>0 && (ins.opcode == Opcode.RETURN || ins.opcode==Opcode.RETURNREG)) {
|
||||
if(idx>0 && (ins.opcode == Opcode.RETURN || ins.opcode==Opcode.RETURNR)) {
|
||||
val previous = chunk.instructions[idx-1]
|
||||
if(previous.opcode in OpcodesThatJump) {
|
||||
chunk.instructions.removeAt(idx)
|
||||
@@ -196,8 +204,8 @@ internal class IRPeepholeOptimizer(private val irprog: IRProgram) {
|
||||
// replace call + return --> jump
|
||||
if(idx>0 && ins.opcode==Opcode.RETURN) {
|
||||
val previous = chunk.instructions[idx-1]
|
||||
if(previous.opcode==Opcode.CALL || previous.opcode==Opcode.CALLRVAL) {
|
||||
chunk.instructions[idx-1] = IRInstruction(Opcode.JUMP, value=previous.value, labelSymbol = previous.labelSymbol, branchTarget = previous.branchTarget)
|
||||
if(previous.opcode==Opcode.CALL || previous.opcode==Opcode.CALLR) {
|
||||
chunk.instructions[idx-1] = IRInstruction(Opcode.JUMP, address = previous.address, labelSymbol = previous.labelSymbol, branchTarget = previous.branchTarget)
|
||||
chunk.instructions.removeAt(idx)
|
||||
changed = true
|
||||
}
|
||||
@@ -212,47 +220,47 @@ internal class IRPeepholeOptimizer(private val irprog: IRProgram) {
|
||||
indexedInstructions.reversed().forEach { (idx, ins) ->
|
||||
when (ins.opcode) {
|
||||
Opcode.DIV, Opcode.DIVS, Opcode.MUL, Opcode.MOD -> {
|
||||
if (ins.value == 1) {
|
||||
if (ins.immediate == 1) {
|
||||
chunk.instructions.removeAt(idx)
|
||||
changed = true
|
||||
}
|
||||
}
|
||||
Opcode.ADD, Opcode.SUB -> {
|
||||
if (ins.value == 1) {
|
||||
if (ins.immediate == 1) {
|
||||
chunk.instructions[idx] = IRInstruction(
|
||||
if (ins.opcode == Opcode.ADD) Opcode.INC else Opcode.DEC,
|
||||
ins.type,
|
||||
ins.reg1
|
||||
)
|
||||
changed = true
|
||||
} else if (ins.value == 0) {
|
||||
} else if (ins.immediate == 0) {
|
||||
chunk.instructions.removeAt(idx)
|
||||
changed = true
|
||||
}
|
||||
}
|
||||
Opcode.AND -> {
|
||||
if (ins.value == 0) {
|
||||
chunk.instructions[idx] = IRInstruction(Opcode.LOAD, ins.type, reg1 = ins.reg1, value = 0)
|
||||
if (ins.immediate == 0) {
|
||||
chunk.instructions[idx] = IRInstruction(Opcode.LOAD, ins.type, reg1 = ins.reg1, immediate = 0)
|
||||
changed = true
|
||||
} else if (ins.value == 255 && ins.type == IRDataType.BYTE) {
|
||||
} else if (ins.immediate == 255 && ins.type == IRDataType.BYTE) {
|
||||
chunk.instructions.removeAt(idx)
|
||||
changed = true
|
||||
} else if (ins.value == 65535 && ins.type == IRDataType.WORD) {
|
||||
} else if (ins.immediate == 65535 && ins.type == IRDataType.WORD) {
|
||||
chunk.instructions.removeAt(idx)
|
||||
changed = true
|
||||
}
|
||||
}
|
||||
Opcode.OR -> {
|
||||
if (ins.value == 0) {
|
||||
if (ins.immediate == 0) {
|
||||
chunk.instructions.removeAt(idx)
|
||||
changed = true
|
||||
} else if ((ins.value == 255 && ins.type == IRDataType.BYTE) || (ins.value == 65535 && ins.type == IRDataType.WORD)) {
|
||||
chunk.instructions[idx] = IRInstruction(Opcode.LOAD, ins.type, reg1 = ins.reg1, value = ins.value)
|
||||
} else if ((ins.immediate == 255 && ins.type == IRDataType.BYTE) || (ins.immediate == 65535 && ins.type == IRDataType.WORD)) {
|
||||
chunk.instructions[idx] = IRInstruction(Opcode.LOAD, ins.type, reg1 = ins.reg1, immediate = ins.immediate)
|
||||
changed = true
|
||||
}
|
||||
}
|
||||
Opcode.XOR -> {
|
||||
if (ins.value == 0) {
|
||||
if (ins.immediate == 0) {
|
||||
chunk.instructions.removeAt(idx)
|
||||
changed = true
|
||||
}
|
||||
|
||||
@@ -5,7 +5,11 @@ import prog8.code.core.SourceCode.Companion.libraryFilePrefix
|
||||
import prog8.intermediate.*
|
||||
|
||||
|
||||
internal class IRUnusedCodeRemover(private val irprog: IRProgram, private val errors: IErrorReporter) {
|
||||
internal class IRUnusedCodeRemover(
|
||||
private val irprog: IRProgram,
|
||||
private val symbolTable: IRSymbolTable,
|
||||
private val errors: IErrorReporter
|
||||
) {
|
||||
fun optimize(): Int {
|
||||
val allLabeledChunks = mutableMapOf<String, IRCodeChunkBase>()
|
||||
|
||||
@@ -25,6 +29,7 @@ internal class IRUnusedCodeRemover(private val irprog: IRProgram, private val er
|
||||
errors.warn("unused subroutine ${sub.label}", sub.position)
|
||||
}
|
||||
block.children.remove(sub)
|
||||
symbolTable.removeTree(sub.label)
|
||||
numRemoved++
|
||||
}
|
||||
}
|
||||
@@ -34,6 +39,7 @@ internal class IRUnusedCodeRemover(private val irprog: IRProgram, private val er
|
||||
irprog.blocks.reversed().forEach { block ->
|
||||
if(block.isEmpty()) {
|
||||
irprog.blocks.remove(block)
|
||||
symbolTable.removeTree(block.label)
|
||||
numRemoved++
|
||||
}
|
||||
}
|
||||
@@ -42,7 +48,7 @@ internal class IRUnusedCodeRemover(private val irprog: IRProgram, private val er
|
||||
}
|
||||
|
||||
private fun removeUnreachable(allLabeledChunks: MutableMap<String, IRCodeChunkBase>): Int {
|
||||
val entrypointSub = irprog.blocks.single { it.name=="main" }.children.single { it is IRSubroutine && it.label=="main.start" }
|
||||
val entrypointSub = irprog.blocks.single { it.label=="main" }.children.single { it is IRSubroutine && it.label=="main.start" }
|
||||
val reachable = mutableSetOf((entrypointSub as IRSubroutine).chunks.first())
|
||||
|
||||
fun grow() {
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
package prog8.codegen.intermediate
|
||||
|
||||
import prog8.code.core.AssemblyError
|
||||
import prog8.intermediate.SyscallRegisterBase
|
||||
|
||||
internal class RegisterPool {
|
||||
// reserve 0,1,2 for return values of subroutine calls and syscalls
|
||||
// TODO set this back to 0 once 'resultRegister' has been removed everywhere and SYSCALL/DIVMOD fixed?
|
||||
// reserve 0,1,2 for return values of subroutine calls and syscalls in IR assembly code
|
||||
private var firstFree: Int=3
|
||||
private var firstFreeFloat: Int=3
|
||||
|
||||
@@ -15,16 +11,12 @@ internal class RegisterPool {
|
||||
fun nextFree(): Int {
|
||||
val result = firstFree
|
||||
firstFree++
|
||||
if(firstFree >= SyscallRegisterBase)
|
||||
throw AssemblyError("out of virtual registers (int)")
|
||||
return result
|
||||
}
|
||||
|
||||
fun nextFreeFloat(): Int {
|
||||
val result = firstFreeFloat
|
||||
firstFreeFloat++
|
||||
if(firstFreeFloat >= SyscallRegisterBase)
|
||||
throw AssemblyError("out of virtual registers (fp)")
|
||||
return result
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,10 @@ package prog8.codegen.vm
|
||||
|
||||
import prog8.code.SymbolTable
|
||||
import prog8.code.ast.PtProgram
|
||||
import prog8.code.core.*
|
||||
import prog8.code.core.CompilationOptions
|
||||
import prog8.code.core.IAssemblyProgram
|
||||
import prog8.code.core.ICodeGeneratorBackend
|
||||
import prog8.code.core.IErrorReporter
|
||||
import prog8.codegen.intermediate.IRCodeGen
|
||||
import prog8.intermediate.IRFileWriter
|
||||
import prog8.intermediate.IRProgram
|
||||
|
||||
@@ -40,7 +40,7 @@ class TestIRPeepholeOpt: FunSpec({
|
||||
|
||||
test("remove nops") {
|
||||
val irProg = makeIRProgram(listOf(
|
||||
IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=1, value=42),
|
||||
IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=1, immediate=42),
|
||||
IRInstruction(Opcode.NOP),
|
||||
IRInstruction(Opcode.NOP)
|
||||
))
|
||||
@@ -117,16 +117,16 @@ class TestIRPeepholeOpt: FunSpec({
|
||||
|
||||
test("remove useless div/mul, add/sub") {
|
||||
val irProg = makeIRProgram(listOf(
|
||||
IRInstruction(Opcode.DIV, IRDataType.BYTE, reg1=42, value = 1),
|
||||
IRInstruction(Opcode.DIVS, IRDataType.BYTE, reg1=42, value = 1),
|
||||
IRInstruction(Opcode.MUL, IRDataType.BYTE, reg1=42, value = 1),
|
||||
IRInstruction(Opcode.MOD, IRDataType.BYTE, reg1=42, value = 1),
|
||||
IRInstruction(Opcode.DIV, IRDataType.BYTE, reg1=42, value = 2),
|
||||
IRInstruction(Opcode.DIVS, IRDataType.BYTE, reg1=42, value = 2),
|
||||
IRInstruction(Opcode.MUL, IRDataType.BYTE, reg1=42, value = 2),
|
||||
IRInstruction(Opcode.MOD, IRDataType.BYTE, reg1=42, value = 2),
|
||||
IRInstruction(Opcode.ADD, IRDataType.BYTE, reg1=42, value = 0),
|
||||
IRInstruction(Opcode.SUB, IRDataType.BYTE, reg1=42, value = 0)
|
||||
IRInstruction(Opcode.DIV, IRDataType.BYTE, reg1=42, immediate = 1),
|
||||
IRInstruction(Opcode.DIVS, IRDataType.BYTE, reg1=42, immediate = 1),
|
||||
IRInstruction(Opcode.MUL, IRDataType.BYTE, reg1=42, immediate = 1),
|
||||
IRInstruction(Opcode.MOD, IRDataType.BYTE, reg1=42, immediate = 1),
|
||||
IRInstruction(Opcode.DIV, IRDataType.BYTE, reg1=42, immediate = 2),
|
||||
IRInstruction(Opcode.DIVS, IRDataType.BYTE, reg1=42, immediate = 2),
|
||||
IRInstruction(Opcode.MUL, IRDataType.BYTE, reg1=42, immediate = 2),
|
||||
IRInstruction(Opcode.MOD, IRDataType.BYTE, reg1=42, immediate = 2),
|
||||
IRInstruction(Opcode.ADD, IRDataType.BYTE, reg1=42, immediate = 0),
|
||||
IRInstruction(Opcode.SUB, IRDataType.BYTE, reg1=42, immediate = 0)
|
||||
))
|
||||
irProg.chunks().single().instructions.size shouldBe 10
|
||||
val opt = IRPeepholeOptimizer(irProg)
|
||||
@@ -136,8 +136,8 @@ class TestIRPeepholeOpt: FunSpec({
|
||||
|
||||
test("replace add/sub 1 by inc/dec") {
|
||||
val irProg = makeIRProgram(listOf(
|
||||
IRInstruction(Opcode.ADD, IRDataType.BYTE, reg1=42, value = 1),
|
||||
IRInstruction(Opcode.SUB, IRDataType.BYTE, reg1=42, value = 1)
|
||||
IRInstruction(Opcode.ADD, IRDataType.BYTE, reg1=42, immediate = 1),
|
||||
IRInstruction(Opcode.SUB, IRDataType.BYTE, reg1=42, immediate = 1)
|
||||
))
|
||||
irProg.chunks().single().instructions.size shouldBe 2
|
||||
val opt = IRPeepholeOptimizer(irProg)
|
||||
@@ -150,14 +150,14 @@ class TestIRPeepholeOpt: FunSpec({
|
||||
|
||||
test("remove useless and/or/xor") {
|
||||
val irProg = makeIRProgram(listOf(
|
||||
IRInstruction(Opcode.AND, IRDataType.BYTE, reg1=42, value = 255),
|
||||
IRInstruction(Opcode.AND, IRDataType.WORD, reg1=42, value = 65535),
|
||||
IRInstruction(Opcode.OR, IRDataType.BYTE, reg1=42, value = 0),
|
||||
IRInstruction(Opcode.XOR, IRDataType.BYTE, reg1=42, value = 0),
|
||||
IRInstruction(Opcode.AND, IRDataType.BYTE, reg1=42, value = 200),
|
||||
IRInstruction(Opcode.AND, IRDataType.WORD, reg1=42, value = 60000),
|
||||
IRInstruction(Opcode.OR, IRDataType.BYTE, reg1=42, value = 1),
|
||||
IRInstruction(Opcode.XOR, IRDataType.BYTE, reg1=42, value = 1)
|
||||
IRInstruction(Opcode.AND, IRDataType.BYTE, reg1=42, immediate = 255),
|
||||
IRInstruction(Opcode.AND, IRDataType.WORD, reg1=42, immediate = 65535),
|
||||
IRInstruction(Opcode.OR, IRDataType.BYTE, reg1=42, immediate = 0),
|
||||
IRInstruction(Opcode.XOR, IRDataType.BYTE, reg1=42, immediate = 0),
|
||||
IRInstruction(Opcode.AND, IRDataType.BYTE, reg1=42, immediate = 200),
|
||||
IRInstruction(Opcode.AND, IRDataType.WORD, reg1=42, immediate = 60000),
|
||||
IRInstruction(Opcode.OR, IRDataType.BYTE, reg1=42, immediate = 1),
|
||||
IRInstruction(Opcode.XOR, IRDataType.BYTE, reg1=42, immediate = 1)
|
||||
))
|
||||
irProg.chunks().single().instructions.size shouldBe 8
|
||||
val opt = IRPeepholeOptimizer(irProg)
|
||||
@@ -167,10 +167,10 @@ class TestIRPeepholeOpt: FunSpec({
|
||||
|
||||
test("replace and/or/xor by constant number") {
|
||||
val irProg = makeIRProgram(listOf(
|
||||
IRInstruction(Opcode.AND, IRDataType.BYTE, reg1=42, value = 0),
|
||||
IRInstruction(Opcode.AND, IRDataType.WORD, reg1=42, value = 0),
|
||||
IRInstruction(Opcode.OR, IRDataType.BYTE, reg1=42, value = 255),
|
||||
IRInstruction(Opcode.OR, IRDataType.WORD, reg1=42, value = 65535)
|
||||
IRInstruction(Opcode.AND, IRDataType.BYTE, reg1=42, immediate = 0),
|
||||
IRInstruction(Opcode.AND, IRDataType.WORD, reg1=42, immediate = 0),
|
||||
IRInstruction(Opcode.OR, IRDataType.BYTE, reg1=42, immediate = 255),
|
||||
IRInstruction(Opcode.OR, IRDataType.WORD, reg1=42, immediate = 65535)
|
||||
))
|
||||
irProg.chunks().single().instructions.size shouldBe 4
|
||||
val opt = IRPeepholeOptimizer(irProg)
|
||||
@@ -181,9 +181,9 @@ class TestIRPeepholeOpt: FunSpec({
|
||||
instr[1].opcode shouldBe Opcode.LOAD
|
||||
instr[2].opcode shouldBe Opcode.LOAD
|
||||
instr[3].opcode shouldBe Opcode.LOAD
|
||||
instr[0].value shouldBe 0
|
||||
instr[1].value shouldBe 0
|
||||
instr[2].value shouldBe 255
|
||||
instr[3].value shouldBe 65535
|
||||
instr[0].immediate shouldBe 0
|
||||
instr[1].immediate shouldBe 0
|
||||
instr[2].immediate shouldBe 255
|
||||
instr[3].immediate shouldBe 65535
|
||||
}
|
||||
})
|
||||
@@ -1,5 +1,6 @@
|
||||
import io.kotest.core.spec.style.FunSpec
|
||||
import io.kotest.matchers.ints.shouldBeGreaterThan
|
||||
import io.kotest.matchers.shouldBe
|
||||
import prog8.code.SymbolTableMaker
|
||||
import prog8.code.ast.*
|
||||
import prog8.code.core.*
|
||||
@@ -7,6 +8,7 @@ import prog8.code.target.VMTarget
|
||||
import prog8.codegen.vm.VmAssemblyProgram
|
||||
import prog8.codegen.vm.VmCodeGen
|
||||
import prog8.intermediate.IRSubroutine
|
||||
import prog8.intermediate.Opcode
|
||||
|
||||
class TestVmCodeGen: FunSpec({
|
||||
|
||||
@@ -98,7 +100,7 @@ class TestVmCodeGen: FunSpec({
|
||||
val errors = ErrorReporterForTests()
|
||||
val result = codegen.generate(program, st, options, errors) as VmAssemblyProgram
|
||||
val irChunks = (result.irProgram.blocks.first().children.single() as IRSubroutine).chunks
|
||||
irChunks.size shouldBeGreaterThan 4
|
||||
irChunks.size shouldBe 1
|
||||
}
|
||||
|
||||
test("float comparison expressions against zero") {
|
||||
@@ -267,7 +269,7 @@ class TestVmCodeGen: FunSpec({
|
||||
val errors = ErrorReporterForTests()
|
||||
val result = codegen.generate(program, st, options, errors) as VmAssemblyProgram
|
||||
val irChunks = (result.irProgram.blocks.first().children.single() as IRSubroutine).chunks
|
||||
irChunks.size shouldBeGreaterThan 4
|
||||
irChunks.size shouldBe 1
|
||||
}
|
||||
|
||||
test("integer comparison expressions against zero") {
|
||||
@@ -436,7 +438,36 @@ class TestVmCodeGen: FunSpec({
|
||||
val errors = ErrorReporterForTests()
|
||||
val result = codegen.generate(program, st, options, errors) as VmAssemblyProgram
|
||||
val irChunks = (result.irProgram.blocks.first().children.single() as IRSubroutine).chunks
|
||||
irChunks.size shouldBeGreaterThan 4
|
||||
irChunks.size shouldBe 1
|
||||
}
|
||||
|
||||
test("romsub allowed in codegen") {
|
||||
//main {
|
||||
// romsub $5000 = routine()
|
||||
//
|
||||
// sub start() {
|
||||
// routine()
|
||||
// }
|
||||
//}
|
||||
val codegen = VmCodeGen()
|
||||
val program = PtProgram("test", DummyMemsizer, DummyStringEncoder)
|
||||
val block = PtBlock("main", null, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY)
|
||||
val romsub = PtAsmSub("routine", 0x5000u, emptySet(), emptyList(), emptyList(), false, Position.DUMMY)
|
||||
block.add(romsub)
|
||||
val sub = PtSub("start", emptyList(), null, Position.DUMMY)
|
||||
val call = PtFunctionCall("main.routine", true, DataType.UNDEFINED, Position.DUMMY)
|
||||
sub.add(call)
|
||||
block.add(sub)
|
||||
program.add(block)
|
||||
|
||||
val options = getTestOptions()
|
||||
val st = SymbolTableMaker(program, options).make()
|
||||
val errors = ErrorReporterForTests()
|
||||
val result = codegen.generate(program, st, options, errors) as VmAssemblyProgram
|
||||
val irChunks = (result.irProgram.blocks.first().children.single() as IRSubroutine).chunks
|
||||
irChunks.size shouldBe 1
|
||||
val callInstr = irChunks.single().instructions.single()
|
||||
callInstr.opcode shouldBe Opcode.CALL
|
||||
callInstr.address shouldBe 0x5000
|
||||
}
|
||||
})
|
||||
@@ -170,7 +170,7 @@ class StatementOptimizer(private val program: Program,
|
||||
if(iterationCount!=null) {
|
||||
val loopName = forLoop.loopVar.nameInSource
|
||||
if(!forLoop.iterable.referencesIdentifier(loopName) && !forLoop.body.referencesIdentifier(loopName)) {
|
||||
errors.warn("for loop can be replaced with repeat loop, possibly also remove the loop variable", forLoop.position)
|
||||
errors.warn("for loop can be replaced with repeat loop", forLoop.position)
|
||||
val repeat = RepeatLoop(NumericLiteral.optimalNumeric(iterationCount, forLoop.position), forLoop.body, forLoop.position)
|
||||
return listOf(IAstModification.ReplaceNode(forLoop, repeat, parent))
|
||||
}
|
||||
|
||||
@@ -1,664 +0,0 @@
|
||||
; --- low level floating point assembly routines for the C128
|
||||
; these are almost all identical to the C64 except for a few details
|
||||
; so we have to have a separate library file for the C128 unfortunately.
|
||||
|
||||
|
||||
FL_ONE_const .byte 129 ; 1.0
|
||||
FL_ZERO_const .byte 0,0,0,0,0 ; 0.0
|
||||
FL_LOG2_const .byte $80, $31, $72, $17, $f8 ; log(2)
|
||||
|
||||
|
||||
floats_store_reg .byte 0 ; temp storage
|
||||
|
||||
|
||||
ub2float .proc
|
||||
; -- convert ubyte in SCRATCH_ZPB1 to float at address A/Y
|
||||
; clobbers A, Y
|
||||
stx P8ZP_SCRATCH_REG
|
||||
sta P8ZP_SCRATCH_W2
|
||||
sty P8ZP_SCRATCH_W2+1
|
||||
ldy P8ZP_SCRATCH_B1
|
||||
lda #0
|
||||
jsr GIVAYF
|
||||
_fac_to_mem ldx P8ZP_SCRATCH_W2
|
||||
ldy P8ZP_SCRATCH_W2+1
|
||||
jsr MOVMF
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
.pend
|
||||
|
||||
b2float .proc
|
||||
; -- convert byte in SCRATCH_ZPB1 to float at address A/Y
|
||||
; clobbers A, Y
|
||||
stx P8ZP_SCRATCH_REG
|
||||
sta P8ZP_SCRATCH_W2
|
||||
sty P8ZP_SCRATCH_W2+1
|
||||
lda P8ZP_SCRATCH_B1
|
||||
jsr FREADSA
|
||||
jmp ub2float._fac_to_mem
|
||||
.pend
|
||||
|
||||
uw2float .proc
|
||||
; -- convert uword in SCRATCH_ZPWORD1 to float at address A/Y
|
||||
stx P8ZP_SCRATCH_REG
|
||||
sta P8ZP_SCRATCH_W2
|
||||
sty P8ZP_SCRATCH_W2+1
|
||||
lda P8ZP_SCRATCH_W1
|
||||
ldy P8ZP_SCRATCH_W1+1
|
||||
jsr GIVUAYFAY
|
||||
jmp ub2float._fac_to_mem
|
||||
.pend
|
||||
|
||||
w2float .proc
|
||||
; -- convert word in SCRATCH_ZPWORD1 to float at address A/Y
|
||||
stx P8ZP_SCRATCH_REG
|
||||
sta P8ZP_SCRATCH_W2
|
||||
sty P8ZP_SCRATCH_W2+1
|
||||
ldy P8ZP_SCRATCH_W1
|
||||
lda P8ZP_SCRATCH_W1+1
|
||||
jsr GIVAYF
|
||||
jmp ub2float._fac_to_mem
|
||||
.pend
|
||||
|
||||
|
||||
cast_from_uw .proc
|
||||
; -- uword in A/Y into float var at (P8ZP_SCRATCH_W2)
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr GIVUAYFAY
|
||||
jmp ub2float._fac_to_mem
|
||||
.pend
|
||||
|
||||
|
||||
cast_from_w .proc
|
||||
; -- word in A/Y into float var at (P8ZP_SCRATCH_W2)
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr GIVAYFAY
|
||||
jmp ub2float._fac_to_mem
|
||||
.pend
|
||||
|
||||
|
||||
cast_from_ub .proc
|
||||
; -- ubyte in Y into float var at (P8ZP_SCRATCH_W2)
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr FREADUY
|
||||
jmp ub2float._fac_to_mem
|
||||
.pend
|
||||
|
||||
|
||||
cast_from_b .proc
|
||||
; -- byte in A into float var at (P8ZP_SCRATCH_W2)
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr FREADSA
|
||||
jmp ub2float._fac_to_mem
|
||||
.pend
|
||||
|
||||
cast_as_uw_into_ya .proc ; also used for float 2 ub
|
||||
; -- cast float at A/Y to uword into Y/A
|
||||
jsr MOVFM
|
||||
jmp cast_FAC1_as_uw_into_ya
|
||||
.pend
|
||||
|
||||
cast_as_w_into_ay .proc ; also used for float 2 b
|
||||
; -- cast float at A/Y to word into A/Y
|
||||
jsr MOVFM
|
||||
jmp cast_FAC1_as_w_into_ay
|
||||
.pend
|
||||
|
||||
cast_FAC1_as_uw_into_ya .proc ; also used for float 2 ub
|
||||
; -- cast fac1 to uword into Y/A
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr GETADR ; into Y/A
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
.pend
|
||||
|
||||
cast_FAC1_as_w_into_ay .proc ; also used for float 2 b
|
||||
; -- cast fac1 to word into A/Y
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr AYINT
|
||||
ldy floats.AYINT_facmo
|
||||
lda floats.AYINT_facmo+1
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
.pend
|
||||
|
||||
|
||||
stack_b2float .proc
|
||||
; -- b2float operating on the stack
|
||||
inx
|
||||
lda P8ESTACK_LO,x
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr FREADSA
|
||||
jmp push_fac1._internal
|
||||
.pend
|
||||
|
||||
stack_w2float .proc
|
||||
; -- w2float operating on the stack
|
||||
inx
|
||||
ldy P8ESTACK_LO,x
|
||||
lda P8ESTACK_HI,x
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr GIVAYF
|
||||
jmp push_fac1._internal
|
||||
.pend
|
||||
|
||||
stack_ub2float .proc
|
||||
; -- ub2float operating on the stack
|
||||
inx
|
||||
lda P8ESTACK_LO,x
|
||||
stx P8ZP_SCRATCH_REG
|
||||
tay
|
||||
lda #0
|
||||
jsr GIVAYF
|
||||
jmp push_fac1._internal
|
||||
.pend
|
||||
|
||||
stack_uw2float .proc
|
||||
; -- uw2float operating on the stack
|
||||
inx
|
||||
lda P8ESTACK_LO,x
|
||||
ldy P8ESTACK_HI,x
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr GIVUAYFAY
|
||||
jmp push_fac1._internal
|
||||
.pend
|
||||
|
||||
stack_float2w .proc ; also used for float2b
|
||||
jsr pop_float_fac1
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr AYINT
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
lda floats.AYINT_facmo
|
||||
sta P8ESTACK_HI,x
|
||||
lda floats.AYINT_facmo+1
|
||||
sta P8ESTACK_LO,x
|
||||
dex
|
||||
rts
|
||||
.pend
|
||||
|
||||
stack_float2uw .proc ; also used for float2ub
|
||||
jsr pop_float_fac1
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr GETADR
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
sta P8ESTACK_HI,x
|
||||
tya
|
||||
sta P8ESTACK_LO,x
|
||||
dex
|
||||
rts
|
||||
.pend
|
||||
|
||||
push_float .proc
|
||||
; ---- push mflpt5 in A/Y onto stack
|
||||
; (taking 3 stack positions = 6 bytes of which 1 is padding)
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
ldy #0
|
||||
lda (P8ZP_SCRATCH_W1),y
|
||||
sta P8ESTACK_LO,x
|
||||
iny
|
||||
lda (P8ZP_SCRATCH_W1),y
|
||||
sta P8ESTACK_HI,x
|
||||
dex
|
||||
iny
|
||||
lda (P8ZP_SCRATCH_W1),y
|
||||
sta P8ESTACK_LO,x
|
||||
iny
|
||||
lda (P8ZP_SCRATCH_W1),y
|
||||
sta P8ESTACK_HI,x
|
||||
dex
|
||||
iny
|
||||
lda (P8ZP_SCRATCH_W1),y
|
||||
sta P8ESTACK_LO,x
|
||||
dex
|
||||
rts
|
||||
.pend
|
||||
|
||||
pop_float .proc
|
||||
; ---- pops mflpt5 from stack to memory A/Y
|
||||
; (frees 3 stack positions = 6 bytes of which 1 is padding)
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
ldy #4
|
||||
inx
|
||||
lda P8ESTACK_LO,x
|
||||
sta (P8ZP_SCRATCH_W1),y
|
||||
dey
|
||||
inx
|
||||
lda P8ESTACK_HI,x
|
||||
sta (P8ZP_SCRATCH_W1),y
|
||||
dey
|
||||
lda P8ESTACK_LO,x
|
||||
sta (P8ZP_SCRATCH_W1),y
|
||||
dey
|
||||
inx
|
||||
lda P8ESTACK_HI,x
|
||||
sta (P8ZP_SCRATCH_W1),y
|
||||
dey
|
||||
lda P8ESTACK_LO,x
|
||||
sta (P8ZP_SCRATCH_W1),y
|
||||
rts
|
||||
.pend
|
||||
|
||||
pop_float_fac1 .proc
|
||||
; -- pops float from stack into FAC1
|
||||
lda #<fmath_float1
|
||||
ldy #>fmath_float1
|
||||
jsr pop_float
|
||||
lda #<fmath_float1
|
||||
ldy #>fmath_float1
|
||||
jmp MOVFM
|
||||
.pend
|
||||
|
||||
copy_float .proc
|
||||
; -- copies the 5 bytes of the mflt value pointed to by SCRATCH_W1,
|
||||
; into the 5 bytes pointed to by A/Y. Clobbers A,Y.
|
||||
sta _target+1
|
||||
sty _target+2
|
||||
ldy #4
|
||||
_loop lda (P8ZP_SCRATCH_W1),y
|
||||
_target sta $ffff,y ; modified
|
||||
dey
|
||||
bpl _loop
|
||||
rts
|
||||
.pend
|
||||
|
||||
inc_var_f .proc
|
||||
; -- add 1 to float pointed to by A/Y
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr MOVFM
|
||||
lda #<FL_ONE_const
|
||||
ldy #>FL_ONE_const
|
||||
jsr FADD
|
||||
ldx P8ZP_SCRATCH_W1
|
||||
ldy P8ZP_SCRATCH_W1+1
|
||||
jsr MOVMF
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
.pend
|
||||
|
||||
dec_var_f .proc
|
||||
; -- subtract 1 from float pointed to by A/Y
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
stx P8ZP_SCRATCH_REG
|
||||
lda #<FL_ONE_const
|
||||
ldy #>FL_ONE_const
|
||||
jsr MOVFM
|
||||
lda P8ZP_SCRATCH_W1
|
||||
ldy P8ZP_SCRATCH_W1+1
|
||||
jsr FSUB
|
||||
ldx P8ZP_SCRATCH_W1
|
||||
ldy P8ZP_SCRATCH_W1+1
|
||||
jsr MOVMF
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
.pend
|
||||
|
||||
|
||||
pop_2_floats_f2_in_fac1 .proc
|
||||
; -- pop 2 floats from stack, load the second one in FAC1 as well
|
||||
lda #<fmath_float2
|
||||
ldy #>fmath_float2
|
||||
jsr pop_float
|
||||
lda #<fmath_float1
|
||||
ldy #>fmath_float1
|
||||
jsr pop_float
|
||||
lda #<fmath_float2
|
||||
ldy #>fmath_float2
|
||||
jmp MOVFM
|
||||
.pend
|
||||
|
||||
|
||||
fmath_float1 .byte 0,0,0,0,0 ; storage for a mflpt5 value
|
||||
fmath_float2 .byte 0,0,0,0,0 ; storage for a mflpt5 value
|
||||
|
||||
|
||||
push_fac1 .proc
|
||||
; -- push the float in FAC1 onto the stack
|
||||
stx P8ZP_SCRATCH_REG
|
||||
_internal ldx #<fmath_float1
|
||||
ldy #>fmath_float1
|
||||
jsr MOVMF
|
||||
lda #<fmath_float1
|
||||
ldy #>fmath_float1
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
jmp push_float
|
||||
.pend
|
||||
|
||||
div_f .proc
|
||||
; -- push f1/f2 on stack
|
||||
jsr pop_2_floats_f2_in_fac1
|
||||
stx P8ZP_SCRATCH_REG
|
||||
lda #<fmath_float1
|
||||
ldy #>fmath_float1
|
||||
jsr FDIV
|
||||
jmp push_fac1._internal
|
||||
.pend
|
||||
|
||||
add_f .proc
|
||||
; -- push f1+f2 on stack
|
||||
jsr pop_2_floats_f2_in_fac1
|
||||
stx P8ZP_SCRATCH_REG
|
||||
lda #<fmath_float1
|
||||
ldy #>fmath_float1
|
||||
jsr FADD
|
||||
jmp push_fac1._internal
|
||||
.pend
|
||||
|
||||
sub_f .proc
|
||||
; -- push f1-f2 on stack
|
||||
jsr pop_2_floats_f2_in_fac1
|
||||
stx P8ZP_SCRATCH_REG
|
||||
lda #<fmath_float1
|
||||
ldy #>fmath_float1
|
||||
jsr FSUB
|
||||
jmp push_fac1._internal
|
||||
.pend
|
||||
|
||||
mul_f .proc
|
||||
; -- push f1*f2 on stack
|
||||
jsr pop_2_floats_f2_in_fac1
|
||||
stx P8ZP_SCRATCH_REG
|
||||
lda #<fmath_float1
|
||||
ldy #>fmath_float1
|
||||
jsr FMULT
|
||||
jmp push_fac1._internal
|
||||
.pend
|
||||
|
||||
neg_f .proc
|
||||
; -- toggle the sign bit on the stack
|
||||
lda P8ESTACK_HI+3,x
|
||||
eor #$80
|
||||
sta P8ESTACK_HI+3,x
|
||||
rts
|
||||
.pend
|
||||
|
||||
var_fac1_less_f .proc
|
||||
; -- is the float in FAC1 < the variable AY?
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr FCOMP
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
cmp #255
|
||||
beq +
|
||||
lda #0
|
||||
rts
|
||||
+ lda #1
|
||||
rts
|
||||
.pend
|
||||
|
||||
var_fac1_lesseq_f .proc
|
||||
; -- is the float in FAC1 <= the variable AY?
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr FCOMP
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
cmp #0
|
||||
beq +
|
||||
cmp #255
|
||||
beq +
|
||||
lda #0
|
||||
rts
|
||||
+ lda #1
|
||||
rts
|
||||
.pend
|
||||
|
||||
var_fac1_greater_f .proc
|
||||
; -- is the float in FAC1 > the variable AY?
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr FCOMP
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
cmp #1
|
||||
beq +
|
||||
lda #0
|
||||
rts
|
||||
+ lda #1
|
||||
rts
|
||||
.pend
|
||||
|
||||
var_fac1_greatereq_f .proc
|
||||
; -- is the float in FAC1 >= the variable AY?
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr FCOMP
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
cmp #0
|
||||
beq +
|
||||
cmp #1
|
||||
beq +
|
||||
lda #0
|
||||
rts
|
||||
+ lda #1
|
||||
rts
|
||||
.pend
|
||||
|
||||
var_fac1_notequal_f .proc
|
||||
; -- are the floats numbers in FAC1 and the variable AY *not* identical?
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr FCOMP
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
and #1
|
||||
rts
|
||||
.pend
|
||||
|
||||
vars_equal_f .proc
|
||||
; -- are the mflpt5 numbers in P8ZP_SCRATCH_W1 and AY identical?
|
||||
sta P8ZP_SCRATCH_W2
|
||||
sty P8ZP_SCRATCH_W2+1
|
||||
ldy #0
|
||||
lda (P8ZP_SCRATCH_W1),y
|
||||
cmp (P8ZP_SCRATCH_W2),y
|
||||
bne _false
|
||||
iny
|
||||
lda (P8ZP_SCRATCH_W1),y
|
||||
cmp (P8ZP_SCRATCH_W2),y
|
||||
bne _false
|
||||
iny
|
||||
lda (P8ZP_SCRATCH_W1),y
|
||||
cmp (P8ZP_SCRATCH_W2),y
|
||||
bne _false
|
||||
iny
|
||||
lda (P8ZP_SCRATCH_W1),y
|
||||
cmp (P8ZP_SCRATCH_W2),y
|
||||
bne _false
|
||||
iny
|
||||
lda (P8ZP_SCRATCH_W1),y
|
||||
cmp (P8ZP_SCRATCH_W2),y
|
||||
bne _false
|
||||
lda #1
|
||||
rts
|
||||
_false lda #0
|
||||
rts
|
||||
.pend
|
||||
|
||||
equal_f .proc
|
||||
; -- are the two mflpt5 numbers on the stack identical?
|
||||
inx
|
||||
inx
|
||||
inx
|
||||
inx
|
||||
lda P8ESTACK_LO-3,x
|
||||
cmp P8ESTACK_LO,x
|
||||
bne _equals_false
|
||||
lda P8ESTACK_LO-2,x
|
||||
cmp P8ESTACK_LO+1,x
|
||||
bne _equals_false
|
||||
lda P8ESTACK_LO-1,x
|
||||
cmp P8ESTACK_LO+2,x
|
||||
bne _equals_false
|
||||
lda P8ESTACK_HI-2,x
|
||||
cmp P8ESTACK_HI+1,x
|
||||
bne _equals_false
|
||||
lda P8ESTACK_HI-1,x
|
||||
cmp P8ESTACK_HI+2,x
|
||||
bne _equals_false
|
||||
_equals_true lda #1
|
||||
_equals_store inx
|
||||
sta P8ESTACK_LO+1,x
|
||||
rts
|
||||
_equals_false lda #0
|
||||
beq _equals_store
|
||||
.pend
|
||||
|
||||
notequal_f .proc
|
||||
; -- are the two mflpt5 numbers on the stack different?
|
||||
jsr equal_f
|
||||
eor #1 ; invert the result
|
||||
sta P8ESTACK_LO+1,x
|
||||
rts
|
||||
.pend
|
||||
|
||||
vars_less_f .proc
|
||||
; -- is float in AY < float in P8ZP_SCRATCH_W2 ?
|
||||
jsr MOVFM
|
||||
lda P8ZP_SCRATCH_W2
|
||||
ldy P8ZP_SCRATCH_W2+1
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr FCOMP
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
cmp #255
|
||||
bne +
|
||||
lda #1
|
||||
rts
|
||||
+ lda #0
|
||||
rts
|
||||
.pend
|
||||
|
||||
vars_lesseq_f .proc
|
||||
; -- is float in AY <= float in P8ZP_SCRATCH_W2 ?
|
||||
jsr MOVFM
|
||||
lda P8ZP_SCRATCH_W2
|
||||
ldy P8ZP_SCRATCH_W2+1
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr FCOMP
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
cmp #255
|
||||
bne +
|
||||
- lda #1
|
||||
rts
|
||||
+ cmp #0
|
||||
beq -
|
||||
lda #0
|
||||
rts
|
||||
.pend
|
||||
|
||||
less_f .proc
|
||||
; -- is f1 < f2?
|
||||
jsr compare_floats
|
||||
cmp #255
|
||||
beq compare_floats._return_true
|
||||
bne compare_floats._return_false
|
||||
.pend
|
||||
|
||||
|
||||
lesseq_f .proc
|
||||
; -- is f1 <= f2?
|
||||
jsr compare_floats
|
||||
cmp #255
|
||||
beq compare_floats._return_true
|
||||
cmp #0
|
||||
beq compare_floats._return_true
|
||||
bne compare_floats._return_false
|
||||
.pend
|
||||
|
||||
greater_f .proc
|
||||
; -- is f1 > f2?
|
||||
jsr compare_floats
|
||||
cmp #1
|
||||
beq compare_floats._return_true
|
||||
bne compare_floats._return_false
|
||||
.pend
|
||||
|
||||
greatereq_f .proc
|
||||
; -- is f1 >= f2?
|
||||
jsr compare_floats
|
||||
cmp #1
|
||||
beq compare_floats._return_true
|
||||
cmp #0
|
||||
beq compare_floats._return_true
|
||||
bne compare_floats._return_false
|
||||
.pend
|
||||
|
||||
compare_floats .proc
|
||||
lda #<fmath_float2
|
||||
ldy #>fmath_float2
|
||||
jsr pop_float
|
||||
lda #<fmath_float1
|
||||
ldy #>fmath_float1
|
||||
jsr pop_float
|
||||
lda #<fmath_float1
|
||||
ldy #>fmath_float1
|
||||
jsr MOVFM ; fac1 = flt1
|
||||
lda #<fmath_float2
|
||||
ldy #>fmath_float2
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr FCOMP ; A = flt1 compared with flt2 (0=equal, 1=flt1>flt2, 255=flt1<flt2)
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
_return_false lda #0
|
||||
_return_result sta P8ESTACK_LO,x
|
||||
dex
|
||||
rts
|
||||
_return_true lda #1
|
||||
bne _return_result
|
||||
.pend
|
||||
|
||||
set_array_float_from_fac1 .proc
|
||||
; -- set the float in FAC1 in the array (index in A, array in P8ZP_SCRATCH_W1)
|
||||
sta P8ZP_SCRATCH_B1
|
||||
asl a
|
||||
asl a
|
||||
clc
|
||||
adc P8ZP_SCRATCH_B1
|
||||
ldy P8ZP_SCRATCH_W1+1
|
||||
clc
|
||||
adc P8ZP_SCRATCH_W1
|
||||
bcc +
|
||||
iny
|
||||
+ stx floats_store_reg
|
||||
tax
|
||||
jsr MOVMF
|
||||
ldx floats_store_reg
|
||||
rts
|
||||
.pend
|
||||
|
||||
|
||||
set_0_array_float .proc
|
||||
; -- set a float in an array to zero (index in A, array in P8ZP_SCRATCH_W1)
|
||||
sta P8ZP_SCRATCH_B1
|
||||
asl a
|
||||
asl a
|
||||
clc
|
||||
adc P8ZP_SCRATCH_B1
|
||||
tay
|
||||
lda #0
|
||||
sta (P8ZP_SCRATCH_W1),y
|
||||
iny
|
||||
sta (P8ZP_SCRATCH_W1),y
|
||||
iny
|
||||
sta (P8ZP_SCRATCH_W1),y
|
||||
iny
|
||||
sta (P8ZP_SCRATCH_W1),y
|
||||
iny
|
||||
sta (P8ZP_SCRATCH_W1),y
|
||||
rts
|
||||
.pend
|
||||
|
||||
|
||||
set_array_float .proc
|
||||
; -- set a float in an array to a value (index in A, float in P8ZP_SCRATCH_W1, array in P8ZP_SCRATCH_W2)
|
||||
sta P8ZP_SCRATCH_B1
|
||||
asl a
|
||||
asl a
|
||||
clc
|
||||
adc P8ZP_SCRATCH_B1
|
||||
adc P8ZP_SCRATCH_W2
|
||||
ldy P8ZP_SCRATCH_W2+1
|
||||
bcc +
|
||||
iny
|
||||
+ jmp copy_float
|
||||
; -- copies the 5 bytes of the mflt value pointed to by SCRATCH_ZPWORD1,
|
||||
; into the 5 bytes pointed to by A/Y. Clobbers A,Y.
|
||||
.pend
|
||||
|
||||
|
||||
@@ -1,158 +0,0 @@
|
||||
; Prog8 definitions for floating point handling on the Commodore 128
|
||||
|
||||
%option enable_floats
|
||||
%import floats_functions
|
||||
|
||||
floats {
|
||||
; ---- this block contains C-128 compatible floating point related functions ----
|
||||
|
||||
const float PI = 3.141592653589793
|
||||
const float TWOPI = 6.283185307179586
|
||||
|
||||
|
||||
; ---- ROM float functions ----
|
||||
|
||||
; note: the fac1 and fac2 are working registers and take 6 bytes each,
|
||||
; floats in memory (and rom) are stored in 5-byte MFLPT packed format.
|
||||
|
||||
; note: fac1/2 might get clobbered even if not mentioned in the function's name.
|
||||
; note: for subtraction and division, the left operand is in fac2, the right operand in fac1.
|
||||
|
||||
romsub $af00 = AYINT() clobbers(A,X,Y) ; fac1-> signed word in 102-103 ($66-$67) MSB FIRST. (might throw ILLEGAL QUANTITY)
|
||||
|
||||
; GIVAYF: signed word in Y/A (note different lsb/msb order) -> float in fac1
|
||||
; there is also floats.GIVUAYFAY - unsigned word in A/Y (lo/hi) to fac1
|
||||
; (tip: use GIVAYFAY to use A/Y input; lo/hi switched to normal order)
|
||||
romsub $af03 = GIVAYF(ubyte lo @ Y, ubyte hi @ A) clobbers(A,X,Y)
|
||||
|
||||
romsub $af06 = FOUT() clobbers(X) -> uword @ AY ; fac1 -> string, address returned in AY
|
||||
; romsub $af09 = VAL_1() clobbers(A,X,Y) ; convert ASCII string to floating point [not yet implemented!!!]
|
||||
|
||||
; fac1 -> unsigned word in Y/A (might throw ILLEGAL QUANTITY) (result also in $14/15)
|
||||
; (tip: use GETADRAY to get A/Y output; lo/hi switched to normal little endian order)
|
||||
romsub $af0c = GETADR() clobbers(X) -> ubyte @ Y, ubyte @ A
|
||||
romsub $af0f = FLOATC() clobbers(A,X,Y) ; convert address to floating point
|
||||
|
||||
romsub $af12 = FSUB(uword mflpt @ AY) clobbers(A,X,Y) ; fac1 = mflpt from A/Y - fac1
|
||||
romsub $af15 = FSUBT() clobbers(A,X,Y) ; fac1 = fac2-fac1 mind the order of the operands NOTE: use FSUBT2() instead!
|
||||
romsub $af18 = FADD(uword mflpt @ AY) clobbers(A,X,Y) ; fac1 += mflpt value from A/Y
|
||||
romsub $af1b = FADDT() clobbers(A,X,Y) ; fac1 += fac2 NOTE: use FADDT2() instead!
|
||||
romsub $af1e = FMULT(uword mflpt @ AY) clobbers(A,X,Y) ; fac1 *= mflpt value from A/Y
|
||||
romsub $af21 = FMULTT() clobbers(A,X,Y) ; fac1 *= fac2 NOTE: use FMULTT2() instead!
|
||||
romsub $af24 = FDIV(uword mflpt @ AY) clobbers(A,X,Y) ; fac1 = mflpt in A/Y / fac1
|
||||
romsub $af27 = FDIVT() clobbers(A,X,Y) ; fac1 = fac2/fac1 mind the order of the operands NOTE: use FDIVT2() instead!
|
||||
romsub $af2a = LOG() clobbers(A,X,Y) ; fac1 = LN(fac1) (natural log)
|
||||
romsub $af2d = INT() clobbers(A,X,Y) ; INT() truncates, use FADDH first to round instead of trunc
|
||||
romsub $af30 = SQR() clobbers(A,X,Y) ; fac1 = SQRT(fac1)
|
||||
romsub $af33 = NEGOP() clobbers(A) ; switch the sign of fac1 (fac1 = -fac1)
|
||||
romsub $af36 = FPWR(uword mflpt @ AY) clobbers(A,X,Y) ; fac1 = fac2 ** float in A/Y
|
||||
romsub $af39 = FPWRT() clobbers(A,X,Y) ; fac1 = fac2 ** fac1 NOTE: use FPWRT2() instead!
|
||||
romsub $af3c = EXP() clobbers(A,X,Y) ; fac1 = EXP(fac1) (e ** fac1)
|
||||
romsub $af3f = COS() clobbers(A,X,Y) ; fac1 = COS(fac1)
|
||||
romsub $af42 = SIN() clobbers(A,X,Y) ; fac1 = SIN(fac1)
|
||||
romsub $af45 = TAN() clobbers(A,X,Y) ; fac1 = TAN(fac1)
|
||||
romsub $af48 = ATN() clobbers(A,X,Y) ; fac1 = ATN(fac1)
|
||||
romsub $af4b = ROUND() clobbers(A,X,Y) ; round fac1
|
||||
romsub $af4e = ABS() clobbers(A,X,Y) ; fac1 = ABS(fac1)
|
||||
romsub $af51 = SIGN() clobbers(X,Y) -> ubyte @ A ; SIGN(fac1) to A, $ff, $0, $1 for negative, zero, positive
|
||||
romsub $af54 = FCOMP(uword mflpt @ AY) clobbers(X,Y) -> ubyte @ A ; A = compare fac1 to mflpt in A/Y, 0=equal 1=fac1 is greater, 255=fac1 is less than
|
||||
romsub $af57 = RND_0() clobbers(A,X,Y) ; fac1 = RND(fac1) float random number generator
|
||||
romsub $af57 = RND() clobbers(A,X,Y) ; alias for RND_0
|
||||
romsub $af5a = CONUPK(uword mflpt @ AY) clobbers(A,X,Y) ; load mflpt value from memory in A/Y into fac2
|
||||
romsub $af5d = ROMUPK(uword mflpt @ AY) clobbers(A,X,Y) ; load mflpt value from memory in current bank in A/Y into fac2
|
||||
romsub $af60 = MOVFRM(uword mflpt @ AY) clobbers(A,X,Y) ; load mflpt value from memory in A/Y into fac1 (use MOVFM instead)
|
||||
romsub $af63 = MOVFM(uword mflpt @ AY) clobbers(A,X,Y) ; load mflpt value from memory in A/Y into fac1
|
||||
romsub $af66 = MOVMF(uword mflpt @ XY) clobbers(A,X,Y) ; store fac1 to memory X/Y as 5-byte mflpt
|
||||
romsub $af69 = MOVFA() clobbers(A,X) ; copy fac2 to fac1
|
||||
romsub $af6c = MOVAF() clobbers(A,X) ; copy fac1 to fac2 (rounded)
|
||||
|
||||
; X16 additions TODO so.... not on c128 !?
|
||||
romsub $af6f = FADDH() clobbers(A,X,Y) ; fac1 += 0.5, for rounding- call this before INT
|
||||
romsub $af72 = FADDT2() clobbers(A,X,Y) ; fac1 += fac2
|
||||
romsub $af75 = ZEROFC() clobbers(A,X,Y) ; fac1 = 0
|
||||
romsub $af78 = NORMAL() clobbers(A,X,Y) ; normalize fac1 (?)
|
||||
romsub $af7b = NEGFAC() clobbers(A) ; switch the sign of fac1 (fac1 = -fac1) (juse use NEGOP() instead!)
|
||||
romsub $af7e = FMULTT2() clobbers(A,X,Y) ; fac1 *= fac2
|
||||
romsub $af81 = MUL10() clobbers(A,X,Y) ; fac1 *= 10
|
||||
romsub $af84 = DIV10() clobbers(A,X,Y) ; fac1 /= 10 , CAUTION: result is always positive!
|
||||
romsub $af87 = FDIVT2() clobbers(A,X,Y) ; fac1 = fac2/fac1 mind the order of the operands
|
||||
romsub $af8a = MOVEF() clobbers(A,X) ; copy fac1 to fac2
|
||||
romsub $af8d = SGN() clobbers(A,X,Y) ; fac1 = SGN(fac1), result of SIGN (-1, 0 or 1)
|
||||
romsub $af90 = FLOAT() clobbers(A,X,Y) ; FAC = (s8).A
|
||||
romsub $af93 = FLOATS() clobbers(A,X,Y) ; FAC = (s16)facho+1:facho
|
||||
romsub $af9C = QINT() clobbers(A,X,Y) ; facho:facho+1:facho+2:facho+3 = u32(FAC)
|
||||
romsub $af9f = FINLOG(byte value @A) clobbers (A, X, Y) ; fac1 += signed byte in A
|
||||
romsub $afa5 = FPWRT2() clobbers(A,X,Y) ; fac1 = fac2 ** fac1
|
||||
|
||||
asmsub FREADSA (byte value @A) clobbers(A,X,Y) {
|
||||
; ---- 8 bit signed A -> float in fac1
|
||||
%asm {{
|
||||
tay
|
||||
bpl +
|
||||
lda #$ff
|
||||
jmp GIVAYF
|
||||
+ lda #0
|
||||
jmp GIVAYF
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub GIVUAYFAY (uword value @ AY) clobbers(A,X,Y) {
|
||||
; ---- unsigned 16 bit word in A/Y (lo/hi) to fac1
|
||||
%asm {{
|
||||
phx
|
||||
sty $64 ; facmo
|
||||
sta $65 ; facmo+1
|
||||
ldx #$90
|
||||
sec
|
||||
jsr FLOATC
|
||||
plx
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub GIVAYFAY (uword value @ AY) clobbers(A,X,Y) {
|
||||
; ---- signed 16 bit word in A/Y (lo/hi) to float in fac1
|
||||
%asm {{
|
||||
sta P8ZP_SCRATCH_B1
|
||||
tya
|
||||
ldy P8ZP_SCRATCH_B1
|
||||
jmp GIVAYF ; this uses the inverse order, Y/A
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub GETADRAY () clobbers(X) -> uword @ AY {
|
||||
; ---- fac1 to unsigned word in A/Y
|
||||
%asm {{
|
||||
jsr GETADR ; this uses the inverse order, Y/A
|
||||
sta P8ZP_SCRATCH_B1
|
||||
tya
|
||||
ldy P8ZP_SCRATCH_B1
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub FREADUY (ubyte value @Y) {
|
||||
; -- 8 bit unsigned Y -> float in fac1
|
||||
%asm {{
|
||||
lda #0
|
||||
jmp GIVAYF
|
||||
}}
|
||||
}
|
||||
|
||||
&uword AYINT_facmo = $66 ; $66/$67 contain result of AYINT
|
||||
|
||||
sub rndf() -> float {
|
||||
%asm {{
|
||||
stx P8ZP_SCRATCH_REG
|
||||
lda #1
|
||||
jsr RND_0
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
|
||||
%asminclude "library:c128/floats.asm"
|
||||
%asminclude "library:c64/floats_funcs.asm"
|
||||
|
||||
}
|
||||
@@ -8,10 +8,9 @@ c64 {
|
||||
&ubyte TIME_LO = $a2 ; .. lo byte. Updated by IRQ every 1/60 sec
|
||||
&ubyte STATUS = $90 ; kernal status variable for I/O
|
||||
&ubyte STKEY = $91 ; various keyboard statuses (updated by IRQ)
|
||||
;;&ubyte SFDX = $cb ; current key pressed (matrix value) (updated by IRQ) // TODO c128 ??
|
||||
|
||||
&ubyte COLOR = $00f1 ; cursor color
|
||||
;;&ubyte HIBASE = $0288 ; screen base address / 256 (hi-byte of screen memory address) // TODO c128 ??
|
||||
&ubyte SHFLAG = $d3 ; various modifier key status (updated by IRQ)
|
||||
&ubyte SFDX = $d4 ; current key pressed (matrix value) (updated by IRQ)
|
||||
&ubyte COLOR = $f1 ; cursor color
|
||||
|
||||
&uword IERROR = $0300
|
||||
&uword IMAIN = $0302
|
||||
@@ -480,10 +479,8 @@ asmsub init_system() {
|
||||
%asm {{
|
||||
sei
|
||||
cld
|
||||
;;lda #%00101111 ; TODO c128 ram and rom bank selection how?
|
||||
;;sta $00
|
||||
;;lda #%00100111
|
||||
;;sta $01
|
||||
lda #0
|
||||
sta $ff00 ; select default bank 15
|
||||
jsr c64.IOINIT
|
||||
jsr c64.RESTOR
|
||||
jsr c64.CINT
|
||||
@@ -549,8 +546,8 @@ sys {
|
||||
; Soft-reset the system back to initial power-on Basic prompt.
|
||||
%asm {{
|
||||
sei
|
||||
;lda #14
|
||||
;sta $01 ; bank the kernal in TODO c128 how to do this?
|
||||
lda #0
|
||||
sta $ff00 ; default bank 15
|
||||
jmp (c64.RESET_VEC)
|
||||
}}
|
||||
}
|
||||
@@ -731,8 +728,8 @@ _longcopy
|
||||
inline asmsub exit(ubyte returnvalue @A) {
|
||||
; -- immediately exit the program with a return code in the A register
|
||||
%asm {{
|
||||
;lda #14
|
||||
;sta $01 ; bank the kernal in TODO c128 how to do this?
|
||||
lda #0
|
||||
sta $ff00 ; default bank 15
|
||||
jsr c64.CLRCHN ; reset i/o channels
|
||||
jsr c64.enable_runstop_and_charsetswitch
|
||||
ldx prog8_lib.orig_stackpointer
|
||||
|
||||
@@ -8,6 +8,7 @@ c64 {
|
||||
&ubyte STATUS = $90 ; kernal status variable for I/O
|
||||
&ubyte STKEY = $91 ; various keyboard statuses (updated by IRQ)
|
||||
&ubyte SFDX = $cb ; current key pressed (matrix value) (updated by IRQ)
|
||||
&ubyte SHFLAG = $028d ; various modifier key status (updated by IRQ)
|
||||
|
||||
&ubyte COLOR = $0286 ; cursor color
|
||||
&ubyte HIBASE = $0288 ; screen base address / 256 (hi-byte of screen memory address)
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
|
||||
floats {
|
||||
; ---- this block contains C-64 compatible floating point related functions ----
|
||||
; the addresses are from cx16 V39 emulator and roms! they won't work on older versions.
|
||||
|
||||
const float PI = 3.141592653589793
|
||||
const float TWOPI = 6.283185307179586
|
||||
|
||||
@@ -22,6 +22,7 @@ psg {
|
||||
; waveform = one of PULSE,SAWTOOTH,TRIANGLE,NOISE.
|
||||
; pulsewidth = 0-63. Specifies the pulse width for waveform=PULSE.
|
||||
envelope_states[voice_num] = 255
|
||||
sys.set_irqd()
|
||||
cx16.r0 = $f9c2 + voice_num * 4
|
||||
cx16.VERA_CTRL = 0
|
||||
cx16.VERA_ADDR_L = lsb(cx16.r0)
|
||||
@@ -32,6 +33,7 @@ psg {
|
||||
cx16.VERA_DATA0 = waveform | pulsewidth
|
||||
envelope_volumes[voice_num] = mkword(volume, 0)
|
||||
envelope_maxvolumes[voice_num] = volume
|
||||
sys.clear_irqd()
|
||||
}
|
||||
|
||||
; sub freq_hz(ubyte voice_num, float hertz) {
|
||||
@@ -44,48 +46,49 @@ psg {
|
||||
sub freq(ubyte voice_num, uword vera_freq) {
|
||||
; -- Changes the frequency of the voice's sound.
|
||||
; voice_num = 0-15, vera_freq = 0-65535 calculate this via the formula given in the Vera's PSG documentation.
|
||||
; (https://github.com/commanderx16/x16-docs/blob/master/VERA%20Programmer's%20Reference.md)
|
||||
cx16.r0 = $f9c0 + voice_num * 4
|
||||
; (https://github.com/x16community/x16-docs/blob/master/VERA%20Programmer's%20Reference.md)
|
||||
; Write freq MSB first and then LSB to reduce the chance on clicks
|
||||
sys.set_irqd()
|
||||
cx16.r0 = $f9c1 + voice_num * 4
|
||||
cx16.VERA_CTRL = 0
|
||||
cx16.VERA_ADDR_L = lsb(cx16.r0)
|
||||
cx16.VERA_ADDR_M = msb(cx16.r0)
|
||||
cx16.VERA_ADDR_H = 1
|
||||
cx16.VERA_DATA0 = lsb(vera_freq)
|
||||
cx16.VERA_ADDR_L++
|
||||
cx16.VERA_DATA0 = msb(vera_freq)
|
||||
cx16.VERA_ADDR_L--
|
||||
cx16.VERA_DATA0 = lsb(vera_freq)
|
||||
sys.clear_irqd()
|
||||
}
|
||||
|
||||
sub volume(ubyte voice_num, ubyte vol) {
|
||||
; -- Modifies the volume of this voice.
|
||||
; voice_num = 0-15, vol = 0-63 where 0=silent, 63=loudest.
|
||||
cx16.r0 = $f9c2 + voice_num * 4
|
||||
cx16.vpoke(1, cx16.r0, cx16.vpeek(1, cx16.r0) & %11000000 | vol)
|
||||
envelope_volumes[voice_num] = mkword(vol, 0)
|
||||
cx16.vpoke_mask(1, $f9c2 + voice_num * 4, %11000000, vol)
|
||||
envelope_maxvolumes[voice_num] = vol
|
||||
}
|
||||
|
||||
sub pulse_width(ubyte voice_num, ubyte pw) {
|
||||
; -- Modifies the pulse width of this voice (when waveform=PULSE)
|
||||
; voice_num = 0-15, pw = 0-63 where 0=narrow, 63=50%cycle so square wave.
|
||||
cx16.r0 = $f9c3 + voice_num * 4
|
||||
cx16.vpoke(1, cx16.r0, cx16.vpeek(1, cx16.r0) & %11000000 | pw)
|
||||
cx16.vpoke_mask(1, $f9c3 + voice_num * 4, %11000000, pw)
|
||||
}
|
||||
|
||||
sub envelope(ubyte voice_num, ubyte maxvolume, ubyte attack, ubyte sustain, ubyte release) {
|
||||
; -- Enables AttackSustainRelease volume envelope for a voice.
|
||||
; Note: this requires setting up envelopes_irq() as well, read its description.
|
||||
; voice_num = 0-15 maxvolume = 0-63
|
||||
; attack, sustain, release = 0-255 that determine the speed of the A/D/R.
|
||||
; TODO describe how the speeds are calculated. For now, experiment. Higher values means *slower* enveloping.
|
||||
; attack, sustain, release = 0-255 that determine the speed of the A/D/R:
|
||||
; attack time: MAXVOL/15/attack seconds. higher value = faster attack.
|
||||
; sustain time: sustain/60 seconds higher sustain value = longer sustain (!).
|
||||
; release time: MAXVOL/15/release seconds. higher vaule = faster release.
|
||||
|
||||
envelope_states[voice_num] = 255
|
||||
envelope_attacks[voice_num] = attack
|
||||
envelope_sustains[voice_num] = sustain
|
||||
envelope_releases[voice_num] = release
|
||||
if attack
|
||||
attack = 0
|
||||
else
|
||||
attack = maxvolume ; max volume when no attack is set
|
||||
envelope_volumes[voice_num] = mkword(attack, 0)
|
||||
if maxvolume<envelope_volumes[voice_num]
|
||||
envelope_volumes[voice_num] = maxvolume
|
||||
envelope_maxvolumes[voice_num] = maxvolume
|
||||
envelope_states[voice_num] = 0
|
||||
}
|
||||
@@ -94,7 +97,6 @@ psg {
|
||||
; -- Shut down all PSG voices.
|
||||
for cx16.r1L in 0 to 15 {
|
||||
envelope_states[cx16.r1L] = 255
|
||||
envelope_volumes[cx16.r1L] = 0
|
||||
volume(cx16.r1L, 0)
|
||||
}
|
||||
}
|
||||
@@ -104,6 +106,7 @@ psg {
|
||||
; you have to call this routine every 1/60th second, for example from your vsync irq handler,
|
||||
; or just install this routine as the only irq handler if you don't have to do other things there.
|
||||
; Example: cx16.set_irq(&psg.envelopes_irq, true)
|
||||
; NOTE: this routine calls save/restore_vera_context() for you, don't nest this!
|
||||
|
||||
; cx16.r0 = the volume word (volume scaled by 256)
|
||||
; cx16.r1L = the voice number
|
||||
@@ -148,7 +151,7 @@ psg {
|
||||
}
|
||||
|
||||
; set new volumes of all 16 voices, using vera stride of 4
|
||||
cx16.push_vera_context()
|
||||
cx16.save_vera_context()
|
||||
cx16.VERA_CTRL = 0
|
||||
cx16.VERA_ADDR_L = $c2
|
||||
cx16.VERA_ADDR_M = $f9
|
||||
@@ -160,7 +163,7 @@ psg {
|
||||
for cx16.r1L in 0 to 15 {
|
||||
cx16.VERA_DATA0 = cx16.VERA_DATA1 & %11000000 | msb(envelope_volumes[cx16.r1L])
|
||||
}
|
||||
cx16.pop_vera_context()
|
||||
cx16.restore_vera_context()
|
||||
popw(cx16.r9)
|
||||
pop(cx16.r2L)
|
||||
pop(cx16.r1L)
|
||||
|
||||
@@ -604,39 +604,24 @@ asmsub vpoke_xor(ubyte bank @A, uword address @R0, ubyte value @Y) clobbers (A)
|
||||
}}
|
||||
}
|
||||
|
||||
|
||||
sub FB_set_pixels_from_buf(uword buffer, uword count) {
|
||||
asmsub vpoke_mask(ubyte bank @A, uword address @R0, ubyte mask @X, ubyte value @Y) clobbers (A) {
|
||||
; -- bitwise or a single byte to the value already in the VERA's video memory at that location
|
||||
; after applying the and-mask. Note: inefficient when writing multiple sequential bytes!
|
||||
%asm {{
|
||||
; -- This is replacement code for the normal FB_set_pixels subroutine in ROM
|
||||
; However that routine contains a bug in the current v38 ROM that makes it crash when count > 255.
|
||||
; So the code below replaces that. Once the ROM is patched this routine is no longer necessary.
|
||||
; See https://github.com/commanderx16/x16-rom/issues/179
|
||||
phx
|
||||
lda buffer
|
||||
ldy buffer+1
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
jsr _pixels
|
||||
plx
|
||||
rts
|
||||
|
||||
_pixels lda count+1
|
||||
beq +
|
||||
ldx #0
|
||||
- jsr _loop
|
||||
inc P8ZP_SCRATCH_W1+1
|
||||
dec count+1
|
||||
bne -
|
||||
|
||||
+ ldx count
|
||||
_loop ldy #0
|
||||
- lda (P8ZP_SCRATCH_W1),y
|
||||
sta cx16.VERA_DATA0
|
||||
iny
|
||||
dex
|
||||
bne -
|
||||
rts
|
||||
}}
|
||||
sty P8ZP_SCRATCH_B1
|
||||
stz cx16.VERA_CTRL
|
||||
and #1
|
||||
sta cx16.VERA_ADDR_H
|
||||
lda cx16.r0
|
||||
sta cx16.VERA_ADDR_L
|
||||
lda cx16.r0+1
|
||||
sta cx16.VERA_ADDR_M
|
||||
txa
|
||||
and cx16.VERA_DATA0
|
||||
ora P8ZP_SCRATCH_B1
|
||||
sta cx16.VERA_DATA0
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
; ---- system stuff -----
|
||||
@@ -786,7 +771,7 @@ IRQ_SCRATCH_ZPWORD2 .word 0
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub push_vera_context() clobbers(A) {
|
||||
asmsub save_vera_context() clobbers(A) {
|
||||
; -- use this at the start of your IRQ handler if it uses Vera registers, to save the state
|
||||
%asm {{
|
||||
; note cannot store this on cpu hardware stack because this gets called as a subroutine
|
||||
@@ -813,24 +798,24 @@ _vera_storage: .byte 0,0,0,0,0,0,0,0
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub pop_vera_context() clobbers(A) {
|
||||
asmsub restore_vera_context() clobbers(A) {
|
||||
; -- use this at the end of your IRQ handler if it uses Vera registers, to restore the state
|
||||
%asm {{
|
||||
lda cx16.push_vera_context._vera_storage+7
|
||||
lda cx16.save_vera_context._vera_storage+7
|
||||
sta cx16.VERA_CTRL
|
||||
lda cx16.push_vera_context._vera_storage+6
|
||||
lda cx16.save_vera_context._vera_storage+6
|
||||
sta cx16.VERA_ADDR_H
|
||||
lda cx16.push_vera_context._vera_storage+5
|
||||
lda cx16.save_vera_context._vera_storage+5
|
||||
sta cx16.VERA_ADDR_M
|
||||
lda cx16.push_vera_context._vera_storage+4
|
||||
lda cx16.save_vera_context._vera_storage+4
|
||||
sta cx16.VERA_ADDR_L
|
||||
lda cx16.push_vera_context._vera_storage+3
|
||||
lda cx16.save_vera_context._vera_storage+3
|
||||
sta cx16.VERA_CTRL
|
||||
lda cx16.push_vera_context._vera_storage+2
|
||||
lda cx16.save_vera_context._vera_storage+2
|
||||
sta cx16.VERA_ADDR_H
|
||||
lda cx16.push_vera_context._vera_storage+1
|
||||
lda cx16.save_vera_context._vera_storage+1
|
||||
sta cx16.VERA_ADDR_M
|
||||
lda cx16.push_vera_context._vera_storage+0
|
||||
lda cx16.save_vera_context._vera_storage+0
|
||||
sta cx16.VERA_ADDR_L
|
||||
rts
|
||||
}}
|
||||
@@ -917,13 +902,27 @@ sys {
|
||||
|
||||
asmsub reset_system() {
|
||||
; Soft-reset the system back to initial power-on Basic prompt.
|
||||
; We do this via the SMC so that a true reset is performed that also resets the Vera fully.
|
||||
%asm {{
|
||||
sei
|
||||
stz $01 ; bank the kernal in
|
||||
jmp (cx16.RESET_VEC)
|
||||
ldx #$42
|
||||
ldy #1
|
||||
tya
|
||||
jsr cx16.i2c_write_byte
|
||||
bra *
|
||||
}}
|
||||
}
|
||||
|
||||
sub poweroff_system() {
|
||||
; use the SMC to shutdown the computer
|
||||
void cx16.i2c_write_byte($42, $01, $00)
|
||||
}
|
||||
|
||||
sub set_leds_brightness(ubyte activity, ubyte power) {
|
||||
void cx16.i2c_write_byte($42, $04, power)
|
||||
void cx16.i2c_write_byte($42, $05, activity)
|
||||
}
|
||||
|
||||
asmsub wait(uword jiffies @AY) {
|
||||
; --- wait approximately the given number of jiffies (1/60th seconds) (N or N+1)
|
||||
; note: the system irq handler has to be active for this to work as it depends on the system jiffy clock
|
||||
|
||||
@@ -196,9 +196,11 @@ sub str2uword(str string) -> uword {
|
||||
; the number may NOT be preceded by a + sign and may NOT contain spaces
|
||||
; (any non-digit character will terminate the number string that is parsed)
|
||||
%ir {{
|
||||
loadm.w r65500,conv.str2uword.string
|
||||
loadm.w r65535,conv.str2uword.string
|
||||
setparam.w r65535,0
|
||||
syscall 11
|
||||
returnreg.w r0
|
||||
pop.w r0
|
||||
returnr.w r0
|
||||
}}
|
||||
}
|
||||
|
||||
@@ -207,9 +209,11 @@ sub str2word(str string) -> word {
|
||||
; the number may be preceded by a + or - sign but may NOT contain spaces
|
||||
; (any non-digit character will terminate the number string that is parsed)
|
||||
%ir {{
|
||||
loadm.w r65500,conv.str2word.string
|
||||
loadm.w r65535,conv.str2word.string
|
||||
setparam.w r65535,0
|
||||
syscall 12
|
||||
returnreg.w r0
|
||||
pop.w r0
|
||||
returnr.w r0
|
||||
}}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,8 @@ floats {
|
||||
sub print_f(float value) {
|
||||
; ---- prints the floating point value (without a newline).
|
||||
%ir {{
|
||||
loadm.f fr65500,floats.print_f.value
|
||||
loadm.f fr65535,floats.print_f.value
|
||||
setparam.f fr65535,0
|
||||
syscall 25
|
||||
return
|
||||
}}
|
||||
@@ -21,7 +22,7 @@ sub pow(float value, float power) -> float {
|
||||
loadm.f fr0,floats.pow.value
|
||||
loadm.f fr1,floats.pow.power
|
||||
fpow.f fr0,fr1
|
||||
returnreg.f fr0
|
||||
returnr.f fr0
|
||||
}}
|
||||
}
|
||||
|
||||
@@ -29,7 +30,7 @@ sub fabs(float value) -> float {
|
||||
%ir {{
|
||||
loadm.f fr0,floats.fabs.value
|
||||
fabs.f fr0,fr0
|
||||
returnreg.f fr0
|
||||
returnr.f fr0
|
||||
}}
|
||||
}
|
||||
|
||||
@@ -37,7 +38,7 @@ sub sin(float angle) -> float {
|
||||
%ir {{
|
||||
loadm.f fr0,floats.sin.angle
|
||||
fsin.f fr0,fr0
|
||||
returnreg.f fr0
|
||||
returnr.f fr0
|
||||
}}
|
||||
}
|
||||
|
||||
@@ -45,7 +46,7 @@ sub cos(float angle) -> float {
|
||||
%ir {{
|
||||
loadm.f fr0,floats.cos.angle
|
||||
fcos.f fr0,fr0
|
||||
returnreg.f fr0
|
||||
returnr.f fr0
|
||||
}}
|
||||
}
|
||||
|
||||
@@ -53,7 +54,7 @@ sub tan(float value) -> float {
|
||||
%ir {{
|
||||
loadm.f fr0,floats.tan.value
|
||||
ftan.f fr0,fr0
|
||||
returnreg.f fr0
|
||||
returnr.f fr0
|
||||
}}
|
||||
}
|
||||
|
||||
@@ -61,7 +62,7 @@ sub atan(float value) -> float {
|
||||
%ir {{
|
||||
loadm.f fr0,floats.atan.value
|
||||
fatan.f fr0,fr0
|
||||
returnreg.f fr0
|
||||
returnr.f fr0
|
||||
}}
|
||||
}
|
||||
|
||||
@@ -69,7 +70,7 @@ sub ln(float value) -> float {
|
||||
%ir {{
|
||||
loadm.f fr0,floats.ln.value
|
||||
fln.f fr0,fr0
|
||||
returnreg.f fr0
|
||||
returnr.f fr0
|
||||
}}
|
||||
}
|
||||
|
||||
@@ -77,7 +78,7 @@ sub log2(float value) -> float {
|
||||
%ir {{
|
||||
loadm.f fr0,floats.log2.value
|
||||
flog.f fr0,fr0
|
||||
returnreg.f fr0
|
||||
returnr.f fr0
|
||||
}}
|
||||
}
|
||||
|
||||
@@ -85,7 +86,7 @@ sub sqrt(float value) -> float {
|
||||
%ir {{
|
||||
loadm.f fr0,floats.sqrt.value
|
||||
sqrt.f fr0,fr0
|
||||
returnreg.f fr0
|
||||
returnr.f fr0
|
||||
}}
|
||||
}
|
||||
|
||||
@@ -103,7 +104,7 @@ sub round(float value) -> float {
|
||||
%ir {{
|
||||
loadm.f fr0,floats.round.value
|
||||
fround.f fr0,fr0
|
||||
returnreg.f fr0
|
||||
returnr.f fr0
|
||||
}}
|
||||
}
|
||||
|
||||
@@ -111,7 +112,7 @@ sub floor(float value) -> float {
|
||||
%ir {{
|
||||
loadm.f fr0,floats.floor.value
|
||||
ffloor.f fr0,fr0
|
||||
returnreg.f fr0
|
||||
returnr.f fr0
|
||||
}}
|
||||
}
|
||||
|
||||
@@ -120,20 +121,22 @@ sub ceil(float value) -> float {
|
||||
%ir {{
|
||||
loadm.f fr0,floats.ceil.value
|
||||
fceil.f fr0,fr0
|
||||
returnreg.f fr0
|
||||
returnr.f fr0
|
||||
}}
|
||||
}
|
||||
|
||||
sub rndf() -> float {
|
||||
%ir {{
|
||||
syscall 35
|
||||
returnreg.f fr0
|
||||
pop.f fr0
|
||||
returnr.f fr0
|
||||
}}
|
||||
}
|
||||
|
||||
sub rndseedf(float seed) {
|
||||
%ir {{
|
||||
loadm.f fr65500,floats.rndseedf.seed
|
||||
loadm.f fr65535,floats.rndseedf.seed
|
||||
setparam.f fr65535,0
|
||||
syscall 32
|
||||
}}
|
||||
}
|
||||
|
||||
@@ -162,22 +162,26 @@ math {
|
||||
sub rnd() -> ubyte {
|
||||
%ir {{
|
||||
syscall 33
|
||||
returnreg.b r0
|
||||
pop.b r0
|
||||
returnr.b r0
|
||||
}}
|
||||
}
|
||||
|
||||
sub rndw() -> uword {
|
||||
%ir {{
|
||||
syscall 34
|
||||
returnreg.w r0
|
||||
pop.w r0
|
||||
returnr.w r0
|
||||
}}
|
||||
}
|
||||
|
||||
sub rndseed(uword seed1, uword seed2) {
|
||||
; -- reset the pseudo RNG's seed values. Defaults are: $a55a, $7653.
|
||||
%ir {{
|
||||
loadm.w r65500,math.rndseed.seed1
|
||||
loadm.w r65501,math.rndseed.seed2
|
||||
loadm.w r65535,math.rndseed.seed1
|
||||
setparam.w r65535,0
|
||||
loadm.w r65535,math.rndseed.seed2
|
||||
setparam.w r65535,1
|
||||
syscall 31
|
||||
return
|
||||
}}
|
||||
|
||||
@@ -84,10 +84,13 @@ string {
|
||||
; Note that you can also directly compare strings and string values with eachother using
|
||||
; comparison operators ==, < etcetera (it will use strcmp for you under water automatically).
|
||||
%ir {{
|
||||
loadm.w r65500,string.compare.st1
|
||||
loadm.w r65501,string.compare.st2
|
||||
loadm.w r65535,string.compare.st1
|
||||
setparam.w r65535,0
|
||||
loadm.w r65535,string.compare.st2
|
||||
setparam.w r65535,1
|
||||
syscall 29
|
||||
returnreg.b r0
|
||||
pop.b r0
|
||||
returnr.b r0
|
||||
}}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,8 @@ sys {
|
||||
sub wait(uword jiffies) {
|
||||
; --- wait approximately the given number of jiffies (1/60th seconds)
|
||||
%ir {{
|
||||
loadm.w r65500,sys.wait.jiffies
|
||||
loadm.w r65535,sys.wait.jiffies
|
||||
setparam.w r65535,0
|
||||
syscall 13
|
||||
}}
|
||||
}
|
||||
@@ -62,7 +63,8 @@ sys {
|
||||
sub exit(ubyte returnvalue) {
|
||||
; -- immediately exit the program with a return code in the A register
|
||||
%ir {{
|
||||
loadm.b r65500,sys.exit.returnvalue
|
||||
loadm.b r65535,sys.exit.returnvalue
|
||||
setparam.b r65535,0
|
||||
syscall 1
|
||||
}}
|
||||
}
|
||||
@@ -82,33 +84,41 @@ sys {
|
||||
|
||||
sub gfx_enable(ubyte mode) {
|
||||
%ir {{
|
||||
loadm.b r65500,sys.gfx_enable.mode
|
||||
loadm.b r65535,sys.gfx_enable.mode
|
||||
setparam.b r65535,0
|
||||
syscall 8
|
||||
}}
|
||||
}
|
||||
|
||||
sub gfx_clear(ubyte color) {
|
||||
%ir {{
|
||||
loadm.b r65500,sys.gfx_clear.color
|
||||
loadm.b r65535,sys.gfx_clear.color
|
||||
setparam.b r65535,0
|
||||
syscall 9
|
||||
}}
|
||||
}
|
||||
|
||||
sub gfx_plot(uword xx, uword yy, ubyte color) {
|
||||
%ir {{
|
||||
loadm.w r65500,sys.gfx_plot.xx
|
||||
loadm.w r65501,sys.gfx_plot.yy
|
||||
loadm.b r65502,sys.gfx_plot.color
|
||||
loadm.w r65535,sys.gfx_plot.xx
|
||||
setparam.w r65535,0
|
||||
loadm.w r65535,sys.gfx_plot.yy
|
||||
setparam.w r65535,1
|
||||
loadm.b r65535,sys.gfx_plot.color
|
||||
setparam.b r65535,2
|
||||
syscall 10
|
||||
}}
|
||||
}
|
||||
|
||||
sub gfx_getpixel(uword xx, uword yy) -> ubyte {
|
||||
%ir {{
|
||||
loadm.w r65500,sys.gfx_getpixel.xx
|
||||
loadm.w r65501,sys.gfx_getpixel.yy
|
||||
loadm.w r65535,sys.gfx_getpixel.xx
|
||||
setparam.w r65535,0
|
||||
loadm.w r65535,sys.gfx_getpixel.yy
|
||||
setparam.w r65535,1
|
||||
syscall 30
|
||||
returnreg.b r0
|
||||
pop.b r0
|
||||
returnr.b r0
|
||||
}}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,8 @@ sub height() -> ubyte {
|
||||
sub clear_screen() {
|
||||
str @shared sequence = "\x1b[2J\x1B[H"
|
||||
%ir {{
|
||||
load.w r65500,txt.clear_screen.sequence
|
||||
load.w r65535,txt.clear_screen.sequence
|
||||
setparam.w r65535,0
|
||||
syscall 3
|
||||
}}
|
||||
}
|
||||
@@ -38,14 +39,16 @@ sub uppercase() {
|
||||
|
||||
sub chrout(ubyte char) {
|
||||
%ir {{
|
||||
loadm.b r65500,txt.chrout.char
|
||||
loadm.b r65535,txt.chrout.char
|
||||
setparam.b r65535,0
|
||||
syscall 2
|
||||
}}
|
||||
}
|
||||
|
||||
sub print (str text) {
|
||||
%ir {{
|
||||
loadm.w r65500,txt.print.text
|
||||
loadm.w r65535,txt.print.text
|
||||
setparam.w r65535,0
|
||||
syscall 3
|
||||
}}
|
||||
}
|
||||
@@ -122,9 +125,13 @@ sub input_chars (uword buffer) -> ubyte {
|
||||
; ---- Input a string (max. 80 chars) from the keyboard. Returns length of input. (string is terminated with a 0 byte as well)
|
||||
; It assumes the keyboard is selected as I/O channel!
|
||||
%ir {{
|
||||
loadm.w r65500,txt.input_chars.buffer
|
||||
loadm.w r65535,txt.input_chars.buffer
|
||||
setparam.w r65535,0
|
||||
load.b r65535,80
|
||||
setparam.b r65535,1
|
||||
syscall 6
|
||||
returnreg.b r0
|
||||
pop.b r0
|
||||
returnr.b r0
|
||||
}}
|
||||
}
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
8.11
|
||||
8.12
|
||||
@@ -258,8 +258,9 @@ fun parseMainModule(filepath: Path,
|
||||
for(lib in compTarget.machine.importLibs(compilerOptions, compTarget.name))
|
||||
importer.importImplicitLibraryModule(lib)
|
||||
|
||||
// always import prog8_lib and math
|
||||
importer.importImplicitLibraryModule("math")
|
||||
if(compilerOptions.compTarget.name!=VMTarget.NAME && !compilerOptions.experimentalCodegen) {
|
||||
importer.importImplicitLibraryModule("math")
|
||||
}
|
||||
importer.importImplicitLibraryModule("prog8_lib")
|
||||
|
||||
if (compilerOptions.launcher == CbmPrgLauncherType.BASIC && compilerOptions.output != OutputType.PRG)
|
||||
|
||||
@@ -106,13 +106,18 @@ class ModuleImporter(private val program: Program,
|
||||
removeDirectivesFromImportedModule(importedModule)
|
||||
|
||||
// modules can contain blocks with "merge" option.
|
||||
// their content has to be merged into already existing block with the same name.
|
||||
// their content has to be merged into already existing other block with the same name.
|
||||
val blocks = importedModule.statements.filterIsInstance<Block>()
|
||||
for(block in blocks) {
|
||||
if("merge" in block.options()) {
|
||||
val existingBlock = program.allBlocks.first { it.name==block.name}
|
||||
existingBlock.statements.addAll(block.statements.filter { it !is Directive})
|
||||
importedModule.statements.remove(block)
|
||||
val existingBlock = program.allBlocks.firstOrNull { it.name==block.name && it !== block}
|
||||
if(existingBlock!=null) {
|
||||
existingBlock.statements.addAll(block.statements.filter { it !is Directive })
|
||||
importedModule.statements.remove(block)
|
||||
} else {
|
||||
val merges = block.statements.filter { it is Directive && it.directive=="%option" && it.args.any { a->a.name=="merge" } }
|
||||
block.statements.removeAll(merges)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,6 @@ import prog8.ast.base.SyntaxError
|
||||
import prog8.ast.expressions.*
|
||||
import prog8.ast.statements.*
|
||||
import prog8.ast.walk.IAstVisitor
|
||||
import prog8.code.ast.PtIdentifier
|
||||
import prog8.code.core.*
|
||||
import prog8.code.target.VMTarget
|
||||
import prog8.compiler.builtinFunctionReturnType
|
||||
@@ -327,8 +326,6 @@ internal class AstChecker(private val program: Program,
|
||||
err("subroutines can only be defined in the scope of a block or within another subroutine")
|
||||
|
||||
if(subroutine.isAsmSubroutine) {
|
||||
if(compilerOptions.compTarget.name==VMTarget.NAME)
|
||||
err("cannot use asmsub for vm target, use regular subs")
|
||||
if(subroutine.asmParameterRegisters.size != subroutine.parameters.size)
|
||||
err("number of asm parameter registers is not the isSameAs as number of parameters")
|
||||
if(subroutine.asmReturnvaluesRegisters.size != subroutine.returntypes.size)
|
||||
@@ -1321,14 +1318,15 @@ internal class AstChecker(private val program: Program,
|
||||
val whenStmt = whenChoice.parent as When
|
||||
if(whenChoice.values!=null) {
|
||||
val conditionType = whenStmt.condition.inferType(program)
|
||||
if(!conditionType.isKnown)
|
||||
throw FatalAstException("can't determine when choice datatype $whenChoice")
|
||||
val constvalues = whenChoice.values!!.map { it.constValue(program) }
|
||||
for(constvalue in constvalues) {
|
||||
when {
|
||||
constvalue == null -> errors.err("choice value must be a constant", whenChoice.position)
|
||||
constvalue.type !in IntegerDatatypes -> errors.err("choice value must be a byte or word", whenChoice.position)
|
||||
conditionType isnot constvalue.type -> errors.err("choice value datatype differs from condition value", whenChoice.position)
|
||||
conditionType isnot constvalue.type -> {
|
||||
if(conditionType.isKnown)
|
||||
errors.err("choice value datatype differs from condition value", whenChoice.position)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -142,8 +142,8 @@ _after:
|
||||
val indexExpr = arrayIndexedExpression.indexer.indexExpr
|
||||
val indexerDt = indexExpr.inferType(program)
|
||||
if(indexerDt.isWords) {
|
||||
val arrayVar = arrayIndexedExpression.arrayvar.targetVarDecl(program)!!
|
||||
if(arrayVar.datatype==DataType.UWORD) {
|
||||
val arrayVar = arrayIndexedExpression.arrayvar.targetVarDecl(program)
|
||||
if(arrayVar!=null && arrayVar.datatype==DataType.UWORD) {
|
||||
val add: Expression =
|
||||
if(indexExpr.constValue(program)?.number==0.0)
|
||||
arrayIndexedExpression.arrayvar.copy()
|
||||
|
||||
@@ -168,6 +168,7 @@ class IntermediateAstMaker(private val program: Program, private val options: Co
|
||||
"align_word" -> alignment = PtBlock.BlockAlignment.WORD
|
||||
"align_page" -> alignment = PtBlock.BlockAlignment.PAGE
|
||||
"force_output" -> forceOutput=true
|
||||
"merge" -> { /* ignore this one */ }
|
||||
else -> throw FatalAstException("weird directive option: ${arg.name}")
|
||||
}
|
||||
}
|
||||
@@ -355,9 +356,9 @@ class IntermediateAstMaker(private val program: Program, private val options: Co
|
||||
for (asm in srcSub.statements) {
|
||||
asm as InlineAssembly
|
||||
if(asm.isIR)
|
||||
combinedIrAsm += asm.assembly + "\n"
|
||||
combinedIrAsm += asm.assembly.trimEnd() + "\n"
|
||||
else
|
||||
combinedTrueAsm += asm.assembly + "\n"
|
||||
combinedTrueAsm += asm.assembly.trimEnd() + "\n"
|
||||
}
|
||||
|
||||
if(combinedTrueAsm.isNotEmpty()) {
|
||||
|
||||
@@ -12,6 +12,7 @@ generate:
|
||||
p8compile -noopt -target cx16 *.p8 >/dev/null
|
||||
|
||||
test_prgs:
|
||||
x16emu -run -prg more_compares.prg
|
||||
for program in *.prg; do \
|
||||
echo "RUNNING:" $$program ; \
|
||||
x16emu -run -prg $$program >/dev/null ; \
|
||||
|
||||
File renamed without changes.
@@ -14,7 +14,6 @@ import prog8.intermediate.IRFileReader
|
||||
import prog8.intermediate.IRSubroutine
|
||||
import prog8.intermediate.Opcode
|
||||
import prog8.vm.VmRunner
|
||||
import prog8tests.helpers.ErrorReporterForTests
|
||||
import prog8tests.helpers.compileText
|
||||
import kotlin.io.path.readText
|
||||
|
||||
@@ -212,59 +211,17 @@ main {
|
||||
compileText(othertarget, true, src, writeAssembly = true) shouldNotBe null
|
||||
|
||||
val target = VMTarget()
|
||||
val result = compileText(target, false, src, writeAssembly = true)!!
|
||||
val virtfile = result.compilationOptions.outputDir.resolve(result.compilerAst.name + ".p8ir")
|
||||
var result = compileText(target, true, src, writeAssembly = true)!!
|
||||
var virtfile = result.compilationOptions.outputDir.resolve(result.compilerAst.name + ".p8ir")
|
||||
VmRunner().runAndTestProgram(virtfile.readText()) { vm ->
|
||||
vm.stepCount shouldBe 49
|
||||
vm.stepCount shouldBe 37
|
||||
}
|
||||
}
|
||||
|
||||
test("asmsub for virtual target not supported") {
|
||||
val src = """
|
||||
main {
|
||||
sub start() {
|
||||
void test(42)
|
||||
}
|
||||
|
||||
asmsub test(ubyte xx @A) -> ubyte @Y {
|
||||
%asm {{
|
||||
lda #99
|
||||
tay
|
||||
rts
|
||||
}}
|
||||
}
|
||||
}"""
|
||||
val othertarget = Cx16Target()
|
||||
compileText(othertarget, true, src, writeAssembly = true) shouldNotBe null
|
||||
|
||||
val target = VMTarget()
|
||||
val errors = ErrorReporterForTests()
|
||||
compileText(target, false, src, writeAssembly = false, errors = errors) shouldBe null
|
||||
errors.errors.size shouldBe 1
|
||||
errors.errors[0] shouldContain "cannot use asmsub for vm target"
|
||||
}
|
||||
|
||||
test("asmsub for virtual target not supported even with IR") {
|
||||
val src = """
|
||||
main {
|
||||
sub start() {
|
||||
void test(42)
|
||||
}
|
||||
|
||||
asmsub test(ubyte xx @A) -> ubyte @Y {
|
||||
%ir {{
|
||||
return
|
||||
}}
|
||||
}
|
||||
}"""
|
||||
val othertarget = Cx16Target()
|
||||
compileText(othertarget, true, src, writeAssembly = true) shouldNotBe null
|
||||
|
||||
val target = VMTarget()
|
||||
val errors = ErrorReporterForTests()
|
||||
compileText(target, false, src, writeAssembly = false, errors = errors) shouldBe null
|
||||
errors.errors.size shouldBe 1
|
||||
errors.errors[0] shouldContain "cannot use asmsub for vm target"
|
||||
result = compileText(target, false, src, writeAssembly = true)!!
|
||||
virtfile = result.compilationOptions.outputDir.resolve(result.compilerAst.name + ".p8ir")
|
||||
VmRunner().runAndTestProgram(virtfile.readText()) { vm ->
|
||||
vm.stepCount shouldBe 48
|
||||
}
|
||||
}
|
||||
|
||||
test("inline asm for virtual target should be IR") {
|
||||
|
||||
@@ -68,7 +68,7 @@ Language features
|
||||
- ``in`` expression for concise and efficient multi-value/containment test
|
||||
- Nested subroutines can access variables from outer scopes to avoids the overhead to pass everything via parameters
|
||||
- Variable data types include signed and unsigned bytes and words, arrays, strings.
|
||||
- Floating point math also supported if the target system provides floating point library routines (C64 and Cx16 both do).
|
||||
- Floating point math also supported on select compiler targets (C64, Cx16 and virtual).
|
||||
- Strings can contain escaped characters but also many symbols directly if they have a PETSCII equivalent, such as "♠♥♣♦π▚●○╳". Characters like ^, _, \\, {, } and | are also accepted and converted to the closest PETSCII equivalents.
|
||||
- High-level code optimizations, such as const-folding (zero-allocation constants that are optimized away in expressions), expression and statement simplifications/rewriting.
|
||||
- Many built-in functions, such as ``sin``, ``cos``, ``abs``, ``sqrt``, ``msb``, ``rol``, ``ror``, ``sort`` and ``reverse``
|
||||
@@ -180,9 +180,11 @@ For MacOS you can use the Homebrew system to install a recent version of OpenJDK
|
||||
|
||||
Finally: an **emulator** (or a real machine of course) to test and run your programs on.
|
||||
In C64 mode, the compiler assumes the presence of the `VICE emulator <http://vice-emu.sourceforge.net/>`_.
|
||||
If you're targeting the Commander X16 instead, there's a choice of the official `x16emu <https://github.com/commanderx16/x16-emulator>`_
|
||||
and the unofficial `box16 <https://github.com/indigodarkwolf/box16>`_ (you can select which one you want to launch
|
||||
using the ``-emu`` or ``-emu2`` command line options)
|
||||
If you're targeting the Commander X16 instead,
|
||||
download a recent emulator version (R42 or newer) for the CommanderX16, such as `x16emu <https://cx16forum.com/forum/viewforum.php?f=30>`_
|
||||
(preferred, this is the official emulator. If required, source code is `here <https://github.com/X16Community/x16-emulator/>`_.
|
||||
There is also `Box16 <https://github.com/indigodarkwolf/box16>`_ which has powerful debugging features.
|
||||
You can select which one you want to launch using the ``-emu`` or ``-emu2`` command line options.
|
||||
|
||||
**Syntax highlighting:** for a few different editors, syntax highlighting definition files are provided.
|
||||
Look in the `syntax-files <https://github.com/irmen/prog8/tree/master/syntax-files>`_ directory in the github repository to find them.
|
||||
|
||||
@@ -112,6 +112,12 @@ sys (part of syslib)
|
||||
Soft-reset the system back to initial power-on BASIC prompt.
|
||||
(called automatically by Prog8 when the main subroutine returns and the program is not using basicsafe zeropage option)
|
||||
|
||||
``poweroff_system()`` (commander x16 only)
|
||||
Powers down the computer.
|
||||
|
||||
``set_leds_brightness(ubyte activity, ubyte power)`` (commander x16 only)
|
||||
Sets the brightness of the activity and power leds on the computer.
|
||||
|
||||
|
||||
conv
|
||||
----
|
||||
@@ -214,6 +220,10 @@ Provides string manipulation routines.
|
||||
|
||||
floats
|
||||
------
|
||||
|
||||
.. note::
|
||||
Floating point support is only available on c64, cx16 and virtual targets for now.
|
||||
|
||||
Provides definitions for the ROM/Kernal subroutines and utility routines dealing with floating
|
||||
point variables. This includes ``print_f``, the routine used to print floating point numbers,
|
||||
``fabs`` to get the absolute value of a floating point number, and a dozen or so floating point
|
||||
@@ -377,3 +387,5 @@ Available for the Cx16 target.
|
||||
Contains a simple abstraction for the Vera's PSG (programmable sound generator) to play simple waveforms.
|
||||
It includes an interrupt routine to handle simple Attack/Release envelopes as well.
|
||||
See the examples/cx16/bdmusic.p8 program for ideas how to use it.
|
||||
Read the source of this library module for details about the API.
|
||||
|
||||
@@ -255,11 +255,13 @@ This saves a lot of memory and may be faster as well.
|
||||
Floating point numbers
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Floats are stored in the 5-byte 'MFLPT' format that is used on CBM machines,
|
||||
and currently all floating point operations are specific to the Commodore 64.
|
||||
This is because routines in the C64 BASIC and Kernal ROMs are used for that.
|
||||
So floating point operations will only work if the C64 BASIC ROM (and Kernal ROM)
|
||||
are banked in.
|
||||
Floats are stored in the 5-byte 'MFLPT' format that is used on CBM machines.
|
||||
Floating point support is available on the c64 and cx16 (and virtual) compiler targets.
|
||||
On the c64 and cx16, the rom routines are used for floating point operations,
|
||||
so on both systems the correct rom banks have to be banked in to make this work.
|
||||
Although the C128 shares the same floating point format, Prog8 currently doesn't support
|
||||
using floating point on that system (because the c128 fp routines require the fp variables
|
||||
to be in another ram bank than the program, something Prog8 doesn't do).
|
||||
|
||||
Also your code needs to import the ``floats`` library to enable floating point support
|
||||
in the compiler, and to gain access to the floating point routines.
|
||||
@@ -268,10 +270,6 @@ to worry about this yourself)
|
||||
|
||||
The largest 5-byte MFLPT float that can be stored is: **1.7014118345e+38** (negative: **-1.7014118345e+38**)
|
||||
|
||||
.. note::
|
||||
On the Commander X16, to use floating point operations, ROM bank 4 has to be enabled (BASIC).
|
||||
Importing the ``floats`` library will do this for you if needed.
|
||||
|
||||
|
||||
Arrays
|
||||
^^^^^^
|
||||
|
||||
@@ -77,8 +77,6 @@ Directives
|
||||
to perform a system reset. (A ``system_reset`` subroutine is available in the syslib to help you do this)
|
||||
- style ``dontuse`` -- don't use *any* location in the zeropage.
|
||||
|
||||
Also read :ref:`zeropage`.
|
||||
|
||||
.. note::
|
||||
``kernalsafe`` and ``full`` on the C64 leave enough room in the zeropage to reallocate the
|
||||
16 virtual registers cx16.r0...cx16.r15 from the Commander X16 into the zeropage as well
|
||||
|
||||
@@ -162,9 +162,9 @@ The Commander X16 syslib does provides two additional routines that should be us
|
||||
They take care of saving and restoring the Vera state of the interrupted main program, otherwise the IRQ handler's manipulation
|
||||
will corrupt any Vera operations that were going on in the main program. The routines are::
|
||||
|
||||
cx16.push_vera_context()
|
||||
cx16.save_vera_context()
|
||||
; ... do your work that uses vera here...
|
||||
cx16.pop_vera_context()
|
||||
cx16.restore_vera_context()
|
||||
|
||||
.. caution::
|
||||
The Commander X16's 16 'virtual registers' R0-R15 are located in zeropage and *are not preserved* in the IRQ handler!
|
||||
|
||||
+28
-16
@@ -3,16 +3,34 @@ TODO
|
||||
|
||||
For next minor release
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
- try to optimize newexpr a bit more
|
||||
|
||||
...
|
||||
|
||||
|
||||
For 9.0 major changes
|
||||
^^^^^^^^^^^^^^^^^^^^^
|
||||
- get rid of the disknumber parameter everywhere in diskio, make it a configurable variable that defaults to 8.
|
||||
- rename builtin function sqrt16 to just sqrt
|
||||
- copy (not move) the CBM kernal romsubs to a new 'cbm' block so programs on c128 and cx16 can also
|
||||
simply refer to cbm.CHROUT rather than c64.CHROUT which looks a bit silly on the non-c64 cbm systems.
|
||||
we keep the old definitions intact because of backwards compatibility reasons.
|
||||
- try to reintroduce builtin functions max/maxw/min/minw that take 2 args and return the largest/smallest of them.
|
||||
This is a major change because it will likely break existing code that is now using min and max as variable names.
|
||||
Also add optimization that changes the word variant to byte variant if the operands are bytes.
|
||||
- 6502 codegen: see if we can let for loops skip the loop if startvar>endvar, without adding a lot of code size/duplicating the loop condition.
|
||||
It is documented behavior to now loop 'around' $00 but it's too easy to forget about!
|
||||
Lot of work because of so many special cases in ForLoopsAsmgen.....
|
||||
(vm codegen already behaves like this!)
|
||||
- ?? get rid of the disknumber parameter everywhere in diskio, make it a configurable variable that defaults to 8.
|
||||
the large majority of users will only deal with a single disk drive so why not make it easier for them.
|
||||
But see complaint on github https://github.com/irmen/prog8/issues/106
|
||||
- duplicate diskio for cx16 (get rid of cx16diskio, just copy diskio and tweak everything) + documentation
|
||||
- get f_seek_w working like in the BASIC program - this needs the changes to diskio.f_open to use suffixes ,p,m
|
||||
- add special (u)word array type (or modifier such as @fast? ) that puts the array into memory as 2 separate byte-arrays 1 for LSB 1 for MSB -> allows for word arrays of length 256 and faster indexing
|
||||
this is an enormous amout of work, if this type is to be treated equally as existing (u)word , because all expression / lookup / assignment routines need to know about the distinction....
|
||||
So maybe only allow the bare essentials? (store, get, bitwise operations?)
|
||||
- Some more support for (64tass) SEGMENTS ?
|
||||
- (What, how, isn't current BSS support enough?)
|
||||
- Add a mechanism to allocate variables into golden ram (or segments really) (see GoldenRam class)
|
||||
- maybe treat block "golden" in a special way: can only contain vars, every var will be allocated in the Golden ram area?
|
||||
- maybe or may not needed: the variables can NOT have initialization values, they will all be set to zero on startup (simple memset)
|
||||
@@ -24,7 +42,6 @@ For 9.0 major changes
|
||||
|
||||
Need help with
|
||||
^^^^^^^^^^^^^^
|
||||
- c128 target: various machine specific things (free zp locations, how banking works, getting the floating point routines working, ...)
|
||||
- atari target: more details details about the machine, fixing library routines. I have no clue whatsoever.
|
||||
- see the :ref:`portingguide` for details on what information is needed.
|
||||
|
||||
@@ -33,34 +50,29 @@ Future Things and Ideas
|
||||
^^^^^^^^^^^^^^^^^^^^^^^
|
||||
Compiler:
|
||||
|
||||
- ir: can we determine for the loop variable in forloops if it could be kept in a (virtual) register instead of a real variable? Need to be able to check if the variable is used by another statement beside just the for loop.
|
||||
- ir: idea: (but LLVM IR simply keeps the variables, so not a good idea then?...): replace all scalar variables by an allocated register. Keep a table of the variable to register mapping (including the datatype)
|
||||
global initialization values are simply a list of LOAD instructions.
|
||||
Variables replaced include all subroutine parameters! So the only variables that remain as variables are arrays and strings.
|
||||
- ir: mechanism to determine for chunks which registers are getting input values from "outside"
|
||||
- ir: mechanism to determine for chunks which registers are passing values out? (i.e. are used again in another chunk)
|
||||
- ir: peephole opt: renumber registers in chunks to start with 1 again every time (but keep entry values in mind!)
|
||||
- ir: peephole opt: reuse registers in chunks (but keep result registers in mind that pass values out! and don't renumber registers above SyscallRegisterBase!)
|
||||
- ir: peephole opt: (maybe just integrate this in the variable/register allocator though?) renumber registers in chunks to start with 1 again every time (but keep entry values in mind!)
|
||||
- ir: peephole opt: (maybe just integrate this in the variable/register allocator though?) reuse registers in chunks (but keep result registers in mind that pass values out! and don't renumber registers above SyscallRegisterBase!)
|
||||
- ir: add more optimizations in IRPeepholeOptimizer
|
||||
- ir: for expressions with array indexes that occur multiple times, can we avoid loading them into new virtualregs everytime and just reuse a single virtualreg as indexer? (simple form of common subexpression elimination)
|
||||
- PtAst/IR: more complex common subexpression eliminations
|
||||
- vm: somehow be able to load a label address as value? (VmProgramLoader) this may require storing the program as bytecodes in actual memory though...
|
||||
- 6502 codegen: see if we can let for loops skip the loop if startvar>endvar, without adding a lot of code size/duplicating the loop condition.
|
||||
It is documented behavior to now loop 'around' $00 but it's too easy to forget about!
|
||||
Lot of work because of so many special cases in ForLoopsAsmgen..... (vm codegen already behaves like this)
|
||||
- generate WASM to eventually run prog8 on a browser canvas? Use binaryen toolkit or my binaryen kotlin library?
|
||||
- can we get rid of pieces of asmgen.AssignmentAsmGen by just reusing the AugmentableAssignment ? generated code should not suffer
|
||||
- [problematic due to using 64tass:] add a compiler option to not remove unused subroutines. this allows for building library programs. But this won't work with 64tass's .proc ...
|
||||
- [problematic due to using 64tass:] better support for building library programs, where unused .proc shouldn't be deleted from the assembly?
|
||||
Perhaps replace all uses of .proc/.pend/.endproc by .block/.bend will fix that with a compiler flag?
|
||||
But all library code written in asm uses .proc already..... (textual search/replace when writing the actual asm?)
|
||||
Once new codegen is written that is based on the IR, this point is mostly moot anyway as that will have its own dead code removal on the IR level.
|
||||
- Zig-like try-based error handling where the V flag could indicate error condition? and/or BRK to jump into monitor on failure? (has to set BRK vector for that) But the V flag is also set on certain normal instructions
|
||||
- add special (u)word array type (or modifier?) that puts the array into memory as 2 separate byte-arrays 1 for LSB 1 for MSB -> allows for word arrays of length 256 and faster indexing
|
||||
this is an enormous amout of work, if this type is to be treated equally as existing (u)word , because all expression / lookup / assignment routines need to know about the distinction....
|
||||
So maybe only allow the bare essntials? (store, get)
|
||||
|
||||
- For c128 target; put floating point variables in bank 1 to make the FP routines work (is this even worth it? very few people will use fp)
|
||||
|
||||
Libraries:
|
||||
|
||||
- fix the problems in c128 target, and flesh out its libraries.
|
||||
- fix the problems in atari target, and flesh out its libraries.
|
||||
- c128 target: make syslib more complete (missing kernal routines)?
|
||||
- c64: make the graphics.BITMAP_ADDRESS configurable (VIC banking)
|
||||
- optimize several inner loops in gfx2 even further?
|
||||
- add modes 3 and perhaps even 2 to gfx2 (lores 16 color and 4 color)?
|
||||
@@ -80,7 +92,7 @@ Expressions:
|
||||
Optimizations:
|
||||
|
||||
- VariableAllocator: can we think of a smarter strategy for allocating variables into zeropage, rather than first-come-first-served?
|
||||
for instance, vars used inside loops first, then loopvars, then the rest
|
||||
for instance, vars used inside loops first, then loopvars, then uwords used as pointers, then the rest
|
||||
- various optimizers skip stuff if compTarget.name==VMTarget.NAME. Once 6502-codegen is done from IR code,
|
||||
those checks should probably be removed, or be made permanent
|
||||
|
||||
|
||||
Binary file not shown.
@@ -68,9 +68,10 @@ zsound_lib:
|
||||
size = cx16diskio.f_read(digi_address, ram_bank_size) ; load next bank
|
||||
txt.print_ub(cx16.getrambank())
|
||||
txt.spc()
|
||||
sys.wait(5) ; artificial delay
|
||||
}
|
||||
|
||||
txt.print("file end.\n")
|
||||
txt.print("sound file end reached.\n")
|
||||
diskio.f_close()
|
||||
|
||||
repeat {
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
%import textio
|
||||
%import conv
|
||||
%import math
|
||||
%import test_stack
|
||||
%zeropage basicsafe
|
||||
|
||||
; The classic number guessing game.
|
||||
@@ -61,8 +60,6 @@ main {
|
||||
txt.print("Thanks for playing, ")
|
||||
txt.print(name)
|
||||
txt.print(".\n")
|
||||
|
||||
; test_stack.test()
|
||||
}
|
||||
}
|
||||
}
|
||||
+20
-23
@@ -1,31 +1,28 @@
|
||||
%import textio
|
||||
%import floats
|
||||
%option no_sysinit
|
||||
%zeropage basicsafe
|
||||
|
||||
|
||||
main {
|
||||
sub start() {
|
||||
ubyte[] ba = [11,22,33]
|
||||
uword[] wa = [1111,2222,3333]
|
||||
float[] fa = [1.1, 2.2, 3.3]
|
||||
sub start() {
|
||||
byte v1s = 22
|
||||
byte v2s = -99
|
||||
word ww
|
||||
|
||||
txt.print_ub(ba[1])
|
||||
txt.nl()
|
||||
txt.print_uw(wa[1])
|
||||
txt.nl()
|
||||
floats.print_f(fa[1])
|
||||
txt.nl()
|
||||
txt.print_w(minsb()) ; TODO WRONG RESULT!
|
||||
txt.spc()
|
||||
ww = minsb()
|
||||
txt.print_w(ww)
|
||||
txt.spc()
|
||||
txt.print_b(minsb())
|
||||
txt.spc()
|
||||
v2s = minsb()
|
||||
txt.print_w(v2s)
|
||||
|
||||
ubyte index=1
|
||||
ubyte calc=1
|
||||
ba[index] += 1
|
||||
wa[index] += 1
|
||||
fa[index] += 1
|
||||
txt.print_ub(ba[1])
|
||||
txt.nl()
|
||||
txt.print_uw(wa[1])
|
||||
txt.nl()
|
||||
floats.print_f(fa[1])
|
||||
txt.nl()
|
||||
sub minsb() -> byte {
|
||||
cx16.r0++
|
||||
return v2s
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -5,23 +5,21 @@ package prog8.intermediate
|
||||
// Note that in the VM these are translated into whatever the corresponding Syscall number in the VM is.
|
||||
|
||||
enum class IMSyscall(val number: Int) {
|
||||
SORT_UBYTE(10000),
|
||||
SORT_BYTE(10001),
|
||||
SORT_UWORD(10002),
|
||||
SORT_WORD(10003),
|
||||
ANY_BYTE(10004),
|
||||
ANY_WORD(10005),
|
||||
ANY_FLOAT(10006),
|
||||
ALL_BYTE(10007),
|
||||
ALL_WORD(10008),
|
||||
ALL_FLOAT(10009),
|
||||
REVERSE_BYTES(10010),
|
||||
REVERSE_WORDS(10011),
|
||||
REVERSE_FLOATS(10012),
|
||||
COMPARE_STRINGS(10013),
|
||||
STRING_CONTAINS(10014),
|
||||
BYTEARRAY_CONTAINS(10015),
|
||||
WORDARRAY_CONTAINS(10016)
|
||||
SORT_UBYTE(0x1000),
|
||||
SORT_BYTE(0x1001),
|
||||
SORT_UWORD(0x1002),
|
||||
SORT_WORD(0x1003),
|
||||
ANY_BYTE(0x1004),
|
||||
ANY_WORD(0x1005),
|
||||
ANY_FLOAT(0x1006),
|
||||
ALL_BYTE(0x1007),
|
||||
ALL_WORD(0x1008),
|
||||
ALL_FLOAT(0x1009),
|
||||
REVERSE_BYTES(0x100a),
|
||||
REVERSE_WORDS(0x100b),
|
||||
REVERSE_FLOATS(0x100c),
|
||||
COMPARE_STRINGS(0x100d),
|
||||
STRING_CONTAINS(0x100e),
|
||||
BYTEARRAY_CONTAINS(0x100f),
|
||||
WORDARRAY_CONTAINS(0x1010)
|
||||
}
|
||||
|
||||
const val SyscallRegisterBase = 65500
|
||||
@@ -55,7 +55,7 @@ class IRFileWriter(private val irProgram: IRProgram, outfileOverride: Path?) {
|
||||
private fun writeBlocks() {
|
||||
irProgram.blocks.forEach { block ->
|
||||
xml.writeStartElement("BLOCK")
|
||||
xml.writeAttribute("NAME", block.name)
|
||||
xml.writeAttribute("NAME", block.label)
|
||||
xml.writeAttribute("ADDRESS", block.address?.toHex() ?: "")
|
||||
xml.writeAttribute("ALIGN", block.alignment.toString())
|
||||
xml.writeAttribute("POS", block.position.toString())
|
||||
@@ -124,6 +124,7 @@ class IRFileWriter(private val irProgram: IRProgram, outfileOverride: Path?) {
|
||||
private fun writeCodeChunk(chunk: IRCodeChunk) {
|
||||
xml.writeStartElement("CODE")
|
||||
chunk.label?.let { xml.writeAttribute("LABEL", chunk.label) }
|
||||
xml.writeAttribute("used-registers", chunk.usedRegisters().toString())
|
||||
xml.writeCharacters("\n")
|
||||
chunk.instructions.forEach { instr ->
|
||||
numInstr++
|
||||
|
||||
@@ -17,8 +17,11 @@ Status flags: Carry, Zero, Negative. NOTE: status flags are only affected by t
|
||||
logical or arithmetic operations DO NOT AFFECT THE STATUS FLAGS UNLESS EXPLICITLY NOTED!
|
||||
|
||||
Instruction set is mostly a load/store architecture, there are few instructions operating on memory directly.
|
||||
Most instructions have an associated data type 'b','w','f'. (omitting it defaults to 'b' - byte).
|
||||
Currently NO support for 24 or 32 bits integers.
|
||||
|
||||
Value types: integers (.b=byte=8 bits, .w=word=16 bits) and float (.f=32 bits). Omitting it defaults to b.
|
||||
Currently ther is NO support for 24 or 32 bits integers.
|
||||
There is no distinction between signed and unsigned integers.
|
||||
Instead, a different instruction is used if a distinction should be made (for example div and divs).
|
||||
Floating point operations are just 'f' typed regular instructions, however there are a few unique fp conversion instructions.
|
||||
Instructions taking more than 1 register cannot take the same register multiple times! (to avoid confusing different datatypes)
|
||||
|
||||
@@ -27,7 +30,7 @@ LOAD/STORE
|
||||
----------
|
||||
All have type b or w or f.
|
||||
|
||||
load reg1, value - load immediate value into register. If you supply a symbol, loads the address of the symbol.
|
||||
load reg1, value - load immediate value into register. If you supply a symbol, loads the *address* of the symbol! (variable values are loaded from memory via the loadm instruction)
|
||||
loadm reg1, address - load reg1 with value at memory address
|
||||
loadi reg1, reg2 - load reg1 with value at memory indirect, memory pointed to by reg2
|
||||
loadx reg1, reg2, address - load reg1 with value at memory address indexed by value in reg2
|
||||
@@ -50,11 +53,12 @@ CONTROL FLOW
|
||||
------------
|
||||
jump location - continue running at instruction number given by location
|
||||
jumpa address - continue running at memory address (note: only used to encode a physical cpu jump to fixed address instruction)
|
||||
call location - save current instruction location+1, continue execution at instruction nr given by location. Expect no return value.
|
||||
callrval reg1, location - like call but expects a return value from a returnreg instruction, and puts that in reg1
|
||||
syscall value - do a systemcall identified by call number
|
||||
setparam reg1, argpos - sets reg1 as the value for the parameter in the given position for an upcoming function call (call, callr, syscall, or even jump opcode).
|
||||
call location - save current instruction location+1, continue execution at instruction nr given by location. No return value is expected.
|
||||
callr reg1, location - like call but expects the routine to return a value with a returnr instruction, it then puts that in reg1
|
||||
syscall value - do a systemcall identified by call number, result value(s) are pushed on value stack so need to be POPped off (depends on syscall)
|
||||
return - restore last saved instruction location and continue at that instruction. No return value.
|
||||
returnreg reg1 - like return, but also returns a value to the caller via reg1
|
||||
returnr reg1 - like return, but also returns the value in reg1 to the caller
|
||||
|
||||
|
||||
BRANCHING and CONDITIONALS
|
||||
@@ -69,19 +73,23 @@ bstneg address - branch to location if Status bit Negat
|
||||
bstpos address - branch to location if Status bit Negative is not set
|
||||
bstvc address - branch to location if Status bit Overflow is not set
|
||||
bstvs address - branch to location if Status bit Overflow is not set
|
||||
bz reg1, address - branch to location if reg1 is zero
|
||||
bnz reg1, address - branch to location if reg1 is not zero
|
||||
bgzs reg1, address - branch to location if reg1 > 0 (signed)
|
||||
bgezs reg1, address - branch to location if reg1 >= 0 (signed)
|
||||
blzs reg1, address - branch to location if reg1 < 0 (signed)
|
||||
blezs reg1, address - branch to location if reg1 <= 0 (signed)
|
||||
beq reg1, reg2, address - jump to location in program given by location, if reg1 == reg2
|
||||
bne reg1, reg2, address - jump to location in program given by location, if reg1 != reg2
|
||||
bgt reg1, reg2, address - jump to location in program given by location, if reg1 > reg2 (unsigned)
|
||||
bgts reg1, reg2, address - jump to location in program given by location, if reg1 > reg2 (signed)
|
||||
bge reg1, reg2, address - jump to location in program given by location, if reg1 >= reg2 (unsigned)
|
||||
bges reg1, reg2, address - jump to location in program given by location, if reg1 >= reg2 (signed)
|
||||
( NOTE: there are no blt/ble instructions because these are equivalent to bgt/bge with the operands swapped around.)
|
||||
beqr reg1, reg2, address - jump to location in program given by location, if reg1 == reg2
|
||||
beq reg1, value, address - jump to location in program given by location, if reg1 == immediate value
|
||||
bner reg1, reg2, address - jump to location in program given by location, if reg1 != reg2
|
||||
bne reg1, value, address - jump to location in program given by location, if reg1 != immediate value
|
||||
bgtr reg1, reg2, address - jump to location in program given by location, if reg1 > reg2 (unsigned)
|
||||
bgt reg1, value, address - jump to location in program given by location, if reg1 > immediate value (unsigned)
|
||||
blt reg1, value, address - jump to location in program given by location, if reg1 < immediate value (unsigned)
|
||||
bgtsr reg1, reg2, address - jump to location in program given by location, if reg1 > reg2 (signed)
|
||||
bgts reg1, value, address - jump to location in program given by location, if reg1 > immediate value (signed)
|
||||
blts reg1, value, address - jump to location in program given by location, if reg1 < immediate value (signed)
|
||||
bger reg1, reg2, address - jump to location in program given by location, if reg1 >= reg2 (unsigned)
|
||||
bge reg1, value, address - jump to location in program given by location, if reg1 >= immediate value (unsigned)
|
||||
ble reg1, value, address - jump to location in program given by location, if reg1 <= immediate value (unsigned)
|
||||
bgesr reg1, reg2, address - jump to location in program given by location, if reg1 >= reg2 (signed)
|
||||
bges reg1, value, address - jump to location in program given by location, if reg1 >= immediate value (signed)
|
||||
bles reg1, value, address - jump to location in program given by location, if reg1 <= immediate value (signed)
|
||||
( NOTE: there are no bltr/bler instructions because these are equivalent to bgtr/bger with the register operands swapped around.)
|
||||
sz reg1, reg2 - set reg1=1 if reg2==0, otherwise set reg1=0
|
||||
snz reg1, reg2 - set reg1=1 if reg2!=0, otherwise set reg1=0
|
||||
seq reg1, reg2 - set reg1=1 if reg1 == reg2, otherwise set reg1=0
|
||||
@@ -108,12 +116,12 @@ dec reg1 - reg1 = reg1-1
|
||||
decm address - memory at address -= 1
|
||||
neg reg1 - reg1 = sign negation of reg1
|
||||
negm address - sign negate memory at address
|
||||
addr reg1, reg2 - reg1 += reg2 (unsigned + signed)
|
||||
add reg1, value - reg1 += value (unsigned + signed)
|
||||
addm reg1, address - memory at address += reg1 (unsigned + signed)
|
||||
subr reg1, reg2 - reg1 -= reg2 (unsigned + signed)
|
||||
sub reg1, value - reg1 -= value (unsigned + signed)
|
||||
subm reg1, address - memory at address -= reg1 (unsigned + signed)
|
||||
addr reg1, reg2 - reg1 += reg2
|
||||
add reg1, value - reg1 += value
|
||||
addm reg1, address - memory at address += reg1
|
||||
subr reg1, reg2 - reg1 -= reg2
|
||||
sub reg1, value - reg1 -= value
|
||||
subm reg1, address - memory at address -= reg1
|
||||
mulr reg1, reg2 - unsigned multiply reg1 *= reg2 note: byte*byte->byte, no type extension to word!
|
||||
mul reg1, value - unsigned multiply reg1 *= value note: byte*byte->byte, no type extension to word!
|
||||
mulm reg1, address - memory at address *= reg2 note: byte*byte->byte, no type extension to word!
|
||||
@@ -125,8 +133,8 @@ divs reg1, value - signed division reg1 /= value not
|
||||
divsm reg1, address - signed memory at address /= reg2 note: division by zero yields max signed int 127 / 32767
|
||||
modr reg1, reg2 - remainder (modulo) of unsigned division reg1 %= reg2 note: division by zero yields max signed int $ff/$ffff
|
||||
mod reg1, value - remainder (modulo) of unsigned division reg1 %= value note: division by zero yields max signed int $ff/$ffff
|
||||
divmodr reg1, reg2 - unsigned division reg1/reg2, storing division in r0 and remainder in r2 (by convention, because we can't specify enough registers in the instruction)
|
||||
divmod reg1, value - unsigned division reg1/value, storing division in r0 and remainder in r2 (by convention, because we can't specify enough registers in the instruction)
|
||||
divmodr reg1, reg2 - unsigned division reg1/reg2, storing division and remainder on value stack (so need to be POPped off)
|
||||
divmod reg1, value - unsigned division reg1/value, storing division and remainder on value stack (so need to be POPped off)
|
||||
sqrt reg1, reg2 - reg1 is the square root of reg2
|
||||
sgn reg1, reg2 - reg1 is the sign of reg2 (0, 1 or -1)
|
||||
cmp reg1, reg2 - set processor status bits C, N, Z according to comparison of reg1 with reg2. (semantics taken from 6502/68000 CMP instruction)
|
||||
@@ -204,8 +212,8 @@ nop - do nothing
|
||||
breakpoint - trigger a breakpoint
|
||||
msig [b, w] reg1, reg2 - reg1 becomes the most significant byte (or word) of the word (or int) in reg2 (.w not yet implemented; requires 32 bits regs)
|
||||
concat [b, w] reg1, reg2 - reg1 = concatenated lsb/lsw of reg1 (as lsb) and lsb/lsw of reg2 (as msb) into word or int (int not yet implemented; requires 32bits regs)
|
||||
push [b, w] reg1 - push value in reg1 on the stack
|
||||
pop [b, w] reg1 - pop value from stack into reg1
|
||||
push [b, w, f] reg1 - push value in reg1 on the stack
|
||||
pop [b, w, f] reg1 - pop value from stack into reg1
|
||||
binarydata - 'instruction' to hold inlined binary data bytes
|
||||
*/
|
||||
|
||||
@@ -230,11 +238,12 @@ enum class Opcode {
|
||||
|
||||
JUMP,
|
||||
JUMPA,
|
||||
SETPARAM,
|
||||
CALL,
|
||||
CALLRVAL,
|
||||
CALLR,
|
||||
SYSCALL,
|
||||
RETURN,
|
||||
RETURNREG,
|
||||
RETURNR,
|
||||
|
||||
BSTCC,
|
||||
BSTCS,
|
||||
@@ -244,18 +253,22 @@ enum class Opcode {
|
||||
BSTPOS,
|
||||
BSTVC,
|
||||
BSTVS,
|
||||
BZ,
|
||||
BNZ,
|
||||
BGZS,
|
||||
BGEZS,
|
||||
BLZS,
|
||||
BLEZS,
|
||||
BEQR,
|
||||
BEQ,
|
||||
BNER,
|
||||
BNE,
|
||||
BGTR,
|
||||
BGT,
|
||||
BLT,
|
||||
BGTSR,
|
||||
BGTS,
|
||||
BLTS,
|
||||
BGER,
|
||||
BGE,
|
||||
BLE,
|
||||
BGESR,
|
||||
BGES,
|
||||
BLES,
|
||||
SZ,
|
||||
SNZ,
|
||||
SEQ,
|
||||
@@ -367,16 +380,16 @@ val OpcodesThatJump = setOf(
|
||||
Opcode.JUMP,
|
||||
Opcode.JUMPA,
|
||||
Opcode.RETURN,
|
||||
Opcode.RETURNREG
|
||||
Opcode.RETURNR
|
||||
)
|
||||
|
||||
val OpcodesThatBranch = setOf(
|
||||
Opcode.JUMP,
|
||||
Opcode.JUMPA,
|
||||
Opcode.RETURN,
|
||||
Opcode.RETURNREG,
|
||||
Opcode.RETURNR,
|
||||
Opcode.CALL,
|
||||
Opcode.CALLRVAL,
|
||||
Opcode.CALLR,
|
||||
Opcode.SYSCALL,
|
||||
Opcode.BSTCC,
|
||||
Opcode.BSTCS,
|
||||
@@ -386,18 +399,22 @@ val OpcodesThatBranch = setOf(
|
||||
Opcode.BSTPOS,
|
||||
Opcode.BSTVC,
|
||||
Opcode.BSTVS,
|
||||
Opcode.BZ,
|
||||
Opcode.BNZ,
|
||||
Opcode.BGZS,
|
||||
Opcode.BGEZS,
|
||||
Opcode.BLZS,
|
||||
Opcode.BLEZS,
|
||||
Opcode.BEQR,
|
||||
Opcode.BEQ,
|
||||
Opcode.BNER,
|
||||
Opcode.BNE,
|
||||
Opcode.BGTR,
|
||||
Opcode.BGT,
|
||||
Opcode.BLT,
|
||||
Opcode.BGTSR,
|
||||
Opcode.BGTS,
|
||||
Opcode.BLTS,
|
||||
Opcode.BGER,
|
||||
Opcode.BGE,
|
||||
Opcode.BGES
|
||||
Opcode.BLE,
|
||||
Opcode.BGESR,
|
||||
Opcode.BGES,
|
||||
Opcode.BLES
|
||||
)
|
||||
|
||||
val OpcodesForCpuRegisters = setOf(
|
||||
@@ -406,63 +423,6 @@ val OpcodesForCpuRegisters = setOf(
|
||||
Opcode.STOREZCPU
|
||||
)
|
||||
|
||||
val OpcodesWithMemoryAddressAsValue = setOf(
|
||||
Opcode.LOADM,
|
||||
Opcode.LOADX,
|
||||
Opcode.LOADIX,
|
||||
Opcode.STOREM,
|
||||
Opcode.STOREX,
|
||||
Opcode.STOREIX,
|
||||
Opcode.STOREZM,
|
||||
Opcode.STOREZX,
|
||||
Opcode.JUMP,
|
||||
Opcode.JUMPA,
|
||||
Opcode.CALL,
|
||||
Opcode.CALLRVAL,
|
||||
Opcode.BSTCC,
|
||||
Opcode.BSTCS,
|
||||
Opcode.BSTEQ,
|
||||
Opcode.BSTNE,
|
||||
Opcode.BSTNEG,
|
||||
Opcode.BSTPOS,
|
||||
Opcode.BSTVC,
|
||||
Opcode.BSTVS,
|
||||
Opcode.BZ,
|
||||
Opcode.BNZ,
|
||||
Opcode.BGZS,
|
||||
Opcode.BGEZS,
|
||||
Opcode.BLZS,
|
||||
Opcode.BLEZS,
|
||||
Opcode.BEQ,
|
||||
Opcode.BNE,
|
||||
Opcode.BGT,
|
||||
Opcode.BGTS,
|
||||
Opcode.BGE,
|
||||
Opcode.BGES,
|
||||
Opcode.INCM,
|
||||
Opcode.DECM,
|
||||
Opcode.NEGM,
|
||||
Opcode.ADDM,
|
||||
Opcode.SUBM,
|
||||
Opcode.MULM,
|
||||
Opcode.DIVM,
|
||||
Opcode.DIVSM,
|
||||
Opcode.INVM,
|
||||
Opcode.ORM,
|
||||
Opcode.XORM,
|
||||
Opcode.ANDM,
|
||||
Opcode.ASRM,
|
||||
Opcode.LSRM,
|
||||
Opcode.LSLM,
|
||||
Opcode.LSLNM,
|
||||
Opcode.LSRNM,
|
||||
Opcode.ASRNM,
|
||||
Opcode.ROLM,
|
||||
Opcode.RORM,
|
||||
Opcode.ROXLM,
|
||||
Opcode.ROXRM
|
||||
)
|
||||
|
||||
enum class IRDataType {
|
||||
BYTE,
|
||||
WORD,
|
||||
@@ -482,9 +442,8 @@ data class InstructionFormat(val datatype: IRDataType?,
|
||||
val reg2: OperandDirection,
|
||||
val fpReg1: OperandDirection,
|
||||
val fpReg2: OperandDirection,
|
||||
val valueIn: Boolean,
|
||||
val fpValueIn: Boolean
|
||||
) {
|
||||
val address: OperandDirection,
|
||||
val immediate: Boolean) {
|
||||
companion object {
|
||||
fun from(spec: String): Map<IRDataType?, InstructionFormat> {
|
||||
val result = mutableMapOf<IRDataType?, InstructionFormat>()
|
||||
@@ -493,8 +452,8 @@ data class InstructionFormat(val datatype: IRDataType?,
|
||||
var reg2 = OperandDirection.UNUSED
|
||||
var fpreg1 = OperandDirection.UNUSED
|
||||
var fpreg2 = OperandDirection.UNUSED
|
||||
var valueIn = false
|
||||
var fpvalueIn = false
|
||||
var address = OperandDirection.UNUSED
|
||||
var immediate = false
|
||||
val splits = part.splitToSequence(',').iterator()
|
||||
val typespec = splits.next()
|
||||
while(splits.hasNext()) {
|
||||
@@ -507,23 +466,23 @@ data class InstructionFormat(val datatype: IRDataType?,
|
||||
">fr1" -> { fpreg1=OperandDirection.WRITE }
|
||||
"<>fr1" -> { fpreg1=OperandDirection.READWRITE }
|
||||
"<fr2" -> fpreg2 = OperandDirection.READ
|
||||
"<v" -> {
|
||||
if('F' in typespec)
|
||||
fpvalueIn = true
|
||||
else
|
||||
valueIn = true
|
||||
}
|
||||
">i", "<>i" -> throw IllegalArgumentException("can't write into an immediate value")
|
||||
"<i" -> immediate = true
|
||||
"<a" -> address = OperandDirection.READ
|
||||
">a" -> address = OperandDirection.WRITE
|
||||
"<>a" -> address = OperandDirection.READWRITE
|
||||
else -> throw IllegalArgumentException(spec)
|
||||
}
|
||||
}
|
||||
|
||||
if(typespec=="N")
|
||||
result[null] = InstructionFormat(null, reg1, reg2, fpreg1, fpreg2, valueIn, fpvalueIn)
|
||||
result[null] = InstructionFormat(null, reg1, reg2, fpreg1, fpreg2, address, immediate)
|
||||
if('B' in typespec)
|
||||
result[IRDataType.BYTE] = InstructionFormat(IRDataType.BYTE, reg1, reg2, fpreg1, fpreg2, valueIn, fpvalueIn)
|
||||
result[IRDataType.BYTE] = InstructionFormat(IRDataType.BYTE, reg1, reg2, fpreg1, fpreg2, address, immediate)
|
||||
if('W' in typespec)
|
||||
result[IRDataType.WORD] = InstructionFormat(IRDataType.WORD, reg1, reg2, fpreg1, fpreg2, valueIn, fpvalueIn)
|
||||
result[IRDataType.WORD] = InstructionFormat(IRDataType.WORD, reg1, reg2, fpreg1, fpreg2, address, immediate)
|
||||
if('F' in typespec)
|
||||
result[IRDataType.FLOAT] = InstructionFormat(IRDataType.FLOAT, reg1, reg2, fpreg1, fpreg2, valueIn, fpvalueIn)
|
||||
result[IRDataType.FLOAT] = InstructionFormat(IRDataType.FLOAT, reg1, reg2, fpreg1, fpreg2, address, immediate)
|
||||
}
|
||||
return result
|
||||
}
|
||||
@@ -534,54 +493,62 @@ data class InstructionFormat(val datatype: IRDataType?,
|
||||
<X = X is not modified (readonly value)
|
||||
>X = X is overwritten with output value (write value)
|
||||
<>X = X is modified (read + written)
|
||||
TODO: also encode if *memory* is read/written/modified?
|
||||
where X is one of:
|
||||
r0... = integer register
|
||||
fr0... = fp register
|
||||
a = memory address
|
||||
i = immediate value
|
||||
*/
|
||||
@Suppress("BooleanLiteralArgument")
|
||||
val instructionFormats = mutableMapOf(
|
||||
Opcode.NOP to InstructionFormat.from("N"),
|
||||
Opcode.LOAD to InstructionFormat.from("BW,>r1,<v | F,>fr1,<v"),
|
||||
Opcode.LOADM to InstructionFormat.from("BW,>r1,<v | F,>fr1,<v"),
|
||||
Opcode.LOAD to InstructionFormat.from("BW,>r1,<i | F,>fr1,<i"),
|
||||
Opcode.LOADM to InstructionFormat.from("BW,>r1,<a | F,>fr1,<a"),
|
||||
Opcode.LOADI to InstructionFormat.from("BW,>r1,<r2 | F,>fr1,<r1"),
|
||||
Opcode.LOADX to InstructionFormat.from("BW,>r1,<r2,<v | F,>fr1,<r1,<v"),
|
||||
Opcode.LOADIX to InstructionFormat.from("BW,>r1,<r2,<v | F,>fr1,<r1,<v"),
|
||||
Opcode.LOADX to InstructionFormat.from("BW,>r1,<r2,<a | F,>fr1,<r1,<a"),
|
||||
Opcode.LOADIX to InstructionFormat.from("BW,>r1,<r2,<a | F,>fr1,<r1,<a"),
|
||||
Opcode.LOADR to InstructionFormat.from("BW,>r1,<r2 | F,>fr1,<fr2"),
|
||||
Opcode.LOADCPU to InstructionFormat.from("BW,>r1"),
|
||||
Opcode.STOREM to InstructionFormat.from("BW,<r1,<v | F,<fr1,<v"),
|
||||
Opcode.STOREM to InstructionFormat.from("BW,<r1,>a | F,<fr1,>a"),
|
||||
Opcode.STORECPU to InstructionFormat.from("BW,<r1"),
|
||||
Opcode.STOREI to InstructionFormat.from("BW,<r1,<r2 | F,<fr1,<r1"),
|
||||
Opcode.STOREX to InstructionFormat.from("BW,<r1,<r2,<v | F,<fr1,<r1,<v"),
|
||||
Opcode.STOREIX to InstructionFormat.from("BW,<r1,<r2,<v | F,<fr1,<r1,<v"),
|
||||
Opcode.STOREZM to InstructionFormat.from("BW,<v | F,<v"),
|
||||
Opcode.STOREX to InstructionFormat.from("BW,<r1,<r2,>a | F,<fr1,<r1,>a"),
|
||||
Opcode.STOREIX to InstructionFormat.from("BW,<r1,<r2,>a | F,<fr1,<r1,>a"),
|
||||
Opcode.STOREZM to InstructionFormat.from("BW,>a | F,>a"),
|
||||
Opcode.STOREZCPU to InstructionFormat.from("BW"),
|
||||
Opcode.STOREZI to InstructionFormat.from("BW,<r1 | F,<r1"),
|
||||
Opcode.STOREZX to InstructionFormat.from("BW,<r1,<v | F,<r1,<v"),
|
||||
Opcode.JUMP to InstructionFormat.from("N,<v"),
|
||||
Opcode.JUMPA to InstructionFormat.from("N,<v"),
|
||||
Opcode.CALL to InstructionFormat.from("N,<v"),
|
||||
Opcode.CALLRVAL to InstructionFormat.from("BW,<r1,<v | F,<fr1,<v"),
|
||||
Opcode.SYSCALL to InstructionFormat.from("N,<v"),
|
||||
Opcode.STOREZX to InstructionFormat.from("BW,<r1,>a | F,<r1,>a"),
|
||||
Opcode.JUMP to InstructionFormat.from("N,<a"),
|
||||
Opcode.JUMPA to InstructionFormat.from("N,<a"),
|
||||
Opcode.SETPARAM to InstructionFormat.from("BW,<r1,<i | F,<fr1,<i"),
|
||||
Opcode.CALL to InstructionFormat.from("N,<a"),
|
||||
Opcode.CALLR to InstructionFormat.from("BW,>r1,<a | F,>fr1,<a"),
|
||||
Opcode.SYSCALL to InstructionFormat.from("N,<i"),
|
||||
Opcode.RETURN to InstructionFormat.from("N"),
|
||||
Opcode.RETURNREG to InstructionFormat.from("BW,<r1 | F,<fr1"),
|
||||
Opcode.BSTCC to InstructionFormat.from("N,<v"),
|
||||
Opcode.BSTCS to InstructionFormat.from("N,<v"),
|
||||
Opcode.BSTEQ to InstructionFormat.from("N,<v"),
|
||||
Opcode.BSTNE to InstructionFormat.from("N,<v"),
|
||||
Opcode.BSTNEG to InstructionFormat.from("N,<v"),
|
||||
Opcode.BSTPOS to InstructionFormat.from("N,<v"),
|
||||
Opcode.BSTVC to InstructionFormat.from("N,<v"),
|
||||
Opcode.BSTVS to InstructionFormat.from("N,<v"),
|
||||
Opcode.BZ to InstructionFormat.from("BW,<r1,<v"),
|
||||
Opcode.BNZ to InstructionFormat.from("BW,<r1,<v"),
|
||||
Opcode.BGZS to InstructionFormat.from("BW,<r1,<v"),
|
||||
Opcode.BGEZS to InstructionFormat.from("BW,<r1,<v"),
|
||||
Opcode.BLZS to InstructionFormat.from("BW,<r1,<v"),
|
||||
Opcode.BLEZS to InstructionFormat.from("BW,<r1,<v"),
|
||||
Opcode.BEQ to InstructionFormat.from("BW,<r1,<r2,<v"),
|
||||
Opcode.BNE to InstructionFormat.from("BW,<r1,<r2,<v"),
|
||||
Opcode.BGT to InstructionFormat.from("BW,<r1,<r2,<v"),
|
||||
Opcode.BGTS to InstructionFormat.from("BW,<r1,<r2,<v"),
|
||||
Opcode.BGE to InstructionFormat.from("BW,<r1,<r2,<v"),
|
||||
Opcode.BGES to InstructionFormat.from("BW,<r1,<r2,<v"),
|
||||
Opcode.RETURNR to InstructionFormat.from("BW,>r1 | F,>fr1"),
|
||||
Opcode.BSTCC to InstructionFormat.from("N,<a"),
|
||||
Opcode.BSTCS to InstructionFormat.from("N,<a"),
|
||||
Opcode.BSTEQ to InstructionFormat.from("N,<a"),
|
||||
Opcode.BSTNE to InstructionFormat.from("N,<a"),
|
||||
Opcode.BSTNEG to InstructionFormat.from("N,<a"),
|
||||
Opcode.BSTPOS to InstructionFormat.from("N,<a"),
|
||||
Opcode.BSTVC to InstructionFormat.from("N,<a"),
|
||||
Opcode.BSTVS to InstructionFormat.from("N,<a"),
|
||||
Opcode.BEQR to InstructionFormat.from("BW,<r1,<r2,<a"),
|
||||
Opcode.BEQ to InstructionFormat.from("BW,<r1,<i,<a"),
|
||||
Opcode.BNER to InstructionFormat.from("BW,<r1,<r2,<a"),
|
||||
Opcode.BNE to InstructionFormat.from("BW,<r1,<i,<a"),
|
||||
Opcode.BGTR to InstructionFormat.from("BW,<r1,<r2,<a"),
|
||||
Opcode.BGT to InstructionFormat.from("BW,<r1,<i,<a"),
|
||||
Opcode.BLT to InstructionFormat.from("BW,<r1,<i,<a"),
|
||||
Opcode.BGTSR to InstructionFormat.from("BW,<r1,<r2,<a"),
|
||||
Opcode.BGTS to InstructionFormat.from("BW,<r1,<i,<a"),
|
||||
Opcode.BLTS to InstructionFormat.from("BW,<r1,<i,<a"),
|
||||
Opcode.BGER to InstructionFormat.from("BW,<r1,<r2,<a"),
|
||||
Opcode.BGE to InstructionFormat.from("BW,<r1,<i,<a"),
|
||||
Opcode.BLE to InstructionFormat.from("BW,<r1,<i,<a"),
|
||||
Opcode.BGESR to InstructionFormat.from("BW,<r1,<r2,<a"),
|
||||
Opcode.BGES to InstructionFormat.from("BW,<r1,<i,<a"),
|
||||
Opcode.BLES to InstructionFormat.from("BW,<r1,<i,<a"),
|
||||
Opcode.SZ to InstructionFormat.from("BW,>r1,<r2"),
|
||||
Opcode.SNZ to InstructionFormat.from("BW,>r1,<r2"),
|
||||
Opcode.SEQ to InstructionFormat.from("BW,<>r1,<r2"),
|
||||
@@ -595,66 +562,66 @@ val instructionFormats = mutableMapOf(
|
||||
Opcode.SGE to InstructionFormat.from("BW,<>r1,<r2"),
|
||||
Opcode.SGES to InstructionFormat.from("BW,<>r1,<r2"),
|
||||
Opcode.INC to InstructionFormat.from("BW,<>r1 | F,<>fr1"),
|
||||
Opcode.INCM to InstructionFormat.from("BW,<v | F,<v"),
|
||||
Opcode.INCM to InstructionFormat.from("BW,<>a | F,<>a"),
|
||||
Opcode.DEC to InstructionFormat.from("BW,<>r1 | F,<>fr1"),
|
||||
Opcode.DECM to InstructionFormat.from("BW,<v | F,<v"),
|
||||
Opcode.DECM to InstructionFormat.from("BW,<>a | F,<>a"),
|
||||
Opcode.NEG to InstructionFormat.from("BW,<>r1 | F,<>fr1"),
|
||||
Opcode.NEGM to InstructionFormat.from("BW,<v | F,<v"),
|
||||
Opcode.NEGM to InstructionFormat.from("BW,<>a | F,<>a"),
|
||||
Opcode.ADDR to InstructionFormat.from("BW,<>r1,<r2 | F,<>fr1,<fr2"),
|
||||
Opcode.ADD to InstructionFormat.from("BW,<>r1,<v | F,<>fr1,<v"),
|
||||
Opcode.ADDM to InstructionFormat.from("BW,<r1,<v | F,<fr1,<v"),
|
||||
Opcode.ADD to InstructionFormat.from("BW,<>r1,<i | F,<>fr1,<i"),
|
||||
Opcode.ADDM to InstructionFormat.from("BW,<r1,<>a | F,<fr1,<>a"),
|
||||
Opcode.SUBR to InstructionFormat.from("BW,<>r1,<r2 | F,<>fr1,<fr2"),
|
||||
Opcode.SUB to InstructionFormat.from("BW,<>r1,<v | F,<>fr1,<v"),
|
||||
Opcode.SUBM to InstructionFormat.from("BW,<r1,<v | F,<fr1,<v"),
|
||||
Opcode.SUB to InstructionFormat.from("BW,<>r1,<i | F,<>fr1,<i"),
|
||||
Opcode.SUBM to InstructionFormat.from("BW,<r1,<>a | F,<fr1,<>a"),
|
||||
Opcode.MULR to InstructionFormat.from("BW,<>r1,<r2 | F,<>fr1,<fr2"),
|
||||
Opcode.MUL to InstructionFormat.from("BW,<>r1,<v | F,<>fr1,<v"),
|
||||
Opcode.MULM to InstructionFormat.from("BW,<r1,<v | F,<fr1,<v"),
|
||||
Opcode.MUL to InstructionFormat.from("BW,<>r1,<i | F,<>fr1,<i"),
|
||||
Opcode.MULM to InstructionFormat.from("BW,<r1,<>a | F,<fr1,<>a"),
|
||||
Opcode.DIVR to InstructionFormat.from("BW,<>r1,<r2"),
|
||||
Opcode.DIV to InstructionFormat.from("BW,<>r1,<v"),
|
||||
Opcode.DIVM to InstructionFormat.from("BW,<r1,<v"),
|
||||
Opcode.DIV to InstructionFormat.from("BW,<>r1,<i"),
|
||||
Opcode.DIVM to InstructionFormat.from("BW,<r1,<>a"),
|
||||
Opcode.DIVSR to InstructionFormat.from("BW,<>r1,<r2 | F,<>fr1,<fr2"),
|
||||
Opcode.DIVS to InstructionFormat.from("BW,<>r1,<v | F,<>fr1,<v"),
|
||||
Opcode.DIVSM to InstructionFormat.from("BW,<r1,<v | F,<fr1,<v"),
|
||||
Opcode.DIVS to InstructionFormat.from("BW,<>r1,<i | F,<>fr1,<i"),
|
||||
Opcode.DIVSM to InstructionFormat.from("BW,<r1,<>a | F,<fr1,<>a"),
|
||||
Opcode.SQRT to InstructionFormat.from("BW,>r1,<r2 | F,>fr1,<fr2"),
|
||||
Opcode.SGN to InstructionFormat.from("BW,>r1,<r2 | F,>fr1,<fr2"),
|
||||
Opcode.MODR to InstructionFormat.from("BW,<>r1,<r2"),
|
||||
Opcode.MOD to InstructionFormat.from("BW,<>r1,<v"),
|
||||
Opcode.MOD to InstructionFormat.from("BW,<>r1,<i"),
|
||||
Opcode.DIVMODR to InstructionFormat.from("BW,<>r1,<r2"),
|
||||
Opcode.DIVMOD to InstructionFormat.from("BW,<>r1,<v"),
|
||||
Opcode.DIVMOD to InstructionFormat.from("BW,<>r1,<i"),
|
||||
Opcode.CMP to InstructionFormat.from("BW,<r1,<r2"),
|
||||
Opcode.EXT to InstructionFormat.from("BW,<>r1"),
|
||||
Opcode.EXTS to InstructionFormat.from("BW,<>r1"),
|
||||
Opcode.ANDR to InstructionFormat.from("BW,<>r1,<r2"),
|
||||
Opcode.AND to InstructionFormat.from("BW,<>r1,<v"),
|
||||
Opcode.ANDM to InstructionFormat.from("BW,<r1,<v"),
|
||||
Opcode.AND to InstructionFormat.from("BW,<>r1,<i"),
|
||||
Opcode.ANDM to InstructionFormat.from("BW,<r1,<>a"),
|
||||
Opcode.ORR to InstructionFormat.from("BW,<>r1,<r2"),
|
||||
Opcode.OR to InstructionFormat.from("BW,<>r1,<v"),
|
||||
Opcode.ORM to InstructionFormat.from("BW,<r1,<v"),
|
||||
Opcode.OR to InstructionFormat.from("BW,<>r1,<i"),
|
||||
Opcode.ORM to InstructionFormat.from("BW,<r1,<>a"),
|
||||
Opcode.XORR to InstructionFormat.from("BW,<>r1,<r2"),
|
||||
Opcode.XOR to InstructionFormat.from("BW,<>r1,<v"),
|
||||
Opcode.XORM to InstructionFormat.from("BW,<r1,<v"),
|
||||
Opcode.XOR to InstructionFormat.from("BW,<>r1,<i"),
|
||||
Opcode.XORM to InstructionFormat.from("BW,<r1,<>a"),
|
||||
Opcode.INV to InstructionFormat.from("BW,<>r1"),
|
||||
Opcode.INVM to InstructionFormat.from("BW,<v"),
|
||||
Opcode.INVM to InstructionFormat.from("BW,<>a"),
|
||||
Opcode.ASRN to InstructionFormat.from("BW,<>r1,<r2"),
|
||||
Opcode.ASRNM to InstructionFormat.from("BW,<r1,<v"),
|
||||
Opcode.ASRNM to InstructionFormat.from("BW,<r1,<>a"),
|
||||
Opcode.LSRN to InstructionFormat.from("BW,<>r1,<r2"),
|
||||
Opcode.LSRNM to InstructionFormat.from("BW,<r1,<v"),
|
||||
Opcode.LSRNM to InstructionFormat.from("BW,<r1,<>a"),
|
||||
Opcode.LSLN to InstructionFormat.from("BW,<>r1,<r2"),
|
||||
Opcode.LSLNM to InstructionFormat.from("BW,<r1,<v"),
|
||||
Opcode.LSLNM to InstructionFormat.from("BW,<r1,<>a"),
|
||||
Opcode.ASR to InstructionFormat.from("BW,<>r1"),
|
||||
Opcode.ASRM to InstructionFormat.from("BW,<v"),
|
||||
Opcode.ASRM to InstructionFormat.from("BW,<>a"),
|
||||
Opcode.LSR to InstructionFormat.from("BW,<>r1"),
|
||||
Opcode.LSRM to InstructionFormat.from("BW,<v"),
|
||||
Opcode.LSRM to InstructionFormat.from("BW,<>a"),
|
||||
Opcode.LSL to InstructionFormat.from("BW,<>r1"),
|
||||
Opcode.LSLM to InstructionFormat.from("BW,<v"),
|
||||
Opcode.LSLM to InstructionFormat.from("BW,<>a"),
|
||||
Opcode.ROR to InstructionFormat.from("BW,<>r1"),
|
||||
Opcode.RORM to InstructionFormat.from("BW,<v"),
|
||||
Opcode.RORM to InstructionFormat.from("BW,<>a"),
|
||||
Opcode.ROXR to InstructionFormat.from("BW,<>r1"),
|
||||
Opcode.ROXRM to InstructionFormat.from("BW,<v"),
|
||||
Opcode.ROXRM to InstructionFormat.from("BW,<>a"),
|
||||
Opcode.ROL to InstructionFormat.from("BW,<>r1"),
|
||||
Opcode.ROLM to InstructionFormat.from("BW,<v"),
|
||||
Opcode.ROLM to InstructionFormat.from("BW,<>a"),
|
||||
Opcode.ROXL to InstructionFormat.from("BW,<>r1"),
|
||||
Opcode.ROXLM to InstructionFormat.from("BW,<v"),
|
||||
Opcode.ROXLM to InstructionFormat.from("BW,<>a"),
|
||||
|
||||
Opcode.FFROMUB to InstructionFormat.from("F,>fr1,<r1"),
|
||||
Opcode.FFROMSB to InstructionFormat.from("F,>fr1,<r1"),
|
||||
@@ -678,8 +645,8 @@ val instructionFormats = mutableMapOf(
|
||||
Opcode.FCEIL to InstructionFormat.from("F,>fr1,<fr2"),
|
||||
|
||||
Opcode.MSIG to InstructionFormat.from("BW,>r1,<r2"),
|
||||
Opcode.PUSH to InstructionFormat.from("BW,<r1"),
|
||||
Opcode.POP to InstructionFormat.from("BW,>r1"),
|
||||
Opcode.PUSH to InstructionFormat.from("BW,<r1 | F,<fr1"),
|
||||
Opcode.POP to InstructionFormat.from("BW,>r1 | F,>fr1"),
|
||||
Opcode.CONCAT to InstructionFormat.from("BW,<>r1,<r2"),
|
||||
Opcode.CLC to InstructionFormat.from("N"),
|
||||
Opcode.SEC to InstructionFormat.from("N"),
|
||||
@@ -695,8 +662,9 @@ data class IRInstruction(
|
||||
val reg2: Int?=null, // 0-$ffff
|
||||
val fpReg1: Int?=null, // 0-$ffff
|
||||
val fpReg2: Int?=null, // 0-$ffff
|
||||
val value: Int?=null, // 0-$ffff
|
||||
val fpValue: Float?=null,
|
||||
val immediate: Int?=null, // 0-$ff or $ffff if word
|
||||
val immediateFp: Float?=null,
|
||||
val address: Int?=null, // 0-$ffff
|
||||
val labelSymbol: String?=null, // symbolic label name as alternative to value (so only for Branch/jump/call Instructions!)
|
||||
val binaryData: Collection<UByte>?=null,
|
||||
var branchTarget: IRCodeChunkBase? = null // will be linked after loading
|
||||
@@ -715,13 +683,6 @@ data class IRInstruction(
|
||||
require(fpReg1==null || fpReg1 in 0..65536) {"fpReg1 out of bounds"}
|
||||
require(fpReg2==null || fpReg2 in 0..65536) {"fpReg2 out of bounds"}
|
||||
if(reg1!=null && reg2!=null) require(reg1!=reg2) {"reg1 must not be same as reg2"} // note: this is ok for fpRegs as these are always the same type
|
||||
if(value!=null && opcode !in OpcodesWithMemoryAddressAsValue) {
|
||||
when (type) {
|
||||
IRDataType.BYTE -> require(value in -128..255) {"value out of range for byte: $value"}
|
||||
IRDataType.WORD -> require(value in -32768..65535) {"value out of range for word: $value"}
|
||||
IRDataType.FLOAT, null -> {}
|
||||
}
|
||||
}
|
||||
|
||||
require((opcode==Opcode.BINARYDATA && binaryData!=null) || (opcode!=Opcode.BINARYDATA && binaryData==null)) {
|
||||
"binarydata inconsistency"
|
||||
@@ -739,23 +700,36 @@ data class IRInstruction(
|
||||
if(format.reg2==OperandDirection.UNUSED) require(reg2==null) { "invalid reg2" }
|
||||
if(format.fpReg1==OperandDirection.UNUSED) require(fpReg1==null) { "invalid fpReg1" }
|
||||
if(format.fpReg2==OperandDirection.UNUSED) require(fpReg2==null) { "invalid fpReg2" }
|
||||
|
||||
if (type==IRDataType.FLOAT) {
|
||||
if(format.fpValueIn) require(fpValue!=null || labelSymbol!=null) {"missing a fp-value or labelsymbol"}
|
||||
} else {
|
||||
if(format.valueIn) require(value!=null || labelSymbol!=null) {"missing a value or labelsymbol"}
|
||||
require(fpReg1==null && fpReg2==null) {"integer point instruction can't use floating point registers"}
|
||||
if(format.immediate) {
|
||||
if(type==IRDataType.FLOAT) {
|
||||
if(opcode!=Opcode.SETPARAM)
|
||||
require(immediateFp !=null) {"missing immediate fp value"}
|
||||
else
|
||||
require(immediateFp==null) {"setparam never has immediateFp only immediate"}
|
||||
}
|
||||
else require(immediate!=null || labelSymbol!=null) {"missing immediate value or labelsymbol"}
|
||||
}
|
||||
if(type!=IRDataType.FLOAT)
|
||||
require(fpReg1==null && fpReg2==null) {"int instruction can't use fp reg"}
|
||||
if(format.address!=OperandDirection.UNUSED)
|
||||
require(address!=null || labelSymbol!=null) {"missing an address or labelsymbol"}
|
||||
if(format.immediate && (immediate!=null || immediateFp!=null)) {
|
||||
if(opcode!=Opcode.SYSCALL) {
|
||||
when (type) {
|
||||
IRDataType.BYTE -> require(immediate in -128..255) { "immediate value out of range for byte: $immediate" }
|
||||
IRDataType.WORD -> require(immediate in -32768..65535) { "immediate value out of range for word: $immediate" }
|
||||
IRDataType.FLOAT, null -> {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
reg1direction = format.reg1
|
||||
reg2direction = format.reg2
|
||||
fpReg1direction = format.fpReg1
|
||||
fpReg2direction = format.fpReg2
|
||||
|
||||
if(opcode in setOf(Opcode.BEQ, Opcode.BNE,
|
||||
Opcode.BGT, Opcode.BGTS,
|
||||
Opcode.BGE, Opcode.BGES,
|
||||
if(opcode in setOf(Opcode.BEQR, Opcode.BNER,
|
||||
Opcode.BGTR, Opcode.BGTSR,
|
||||
Opcode.BGER, Opcode.BGESR,
|
||||
Opcode.SEQ, Opcode.SNE, Opcode.SLT, Opcode.SLTS,
|
||||
Opcode.SGT, Opcode.SGTS, Opcode.SLE, Opcode.SLES,
|
||||
Opcode.SGE, Opcode.SGES)) {
|
||||
@@ -764,40 +738,85 @@ data class IRInstruction(
|
||||
else
|
||||
require(reg1!=reg2) {"$opcode: reg1 and reg2 should be different"}
|
||||
}
|
||||
|
||||
if(opcode==Opcode.SYSCALL) {
|
||||
require(immediate!=null) {
|
||||
"syscall needs immediate integer for the syscall number"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun addUsedRegistersCounts(
|
||||
readRegs: MutableMap<Int, Int>,
|
||||
writeRegs: MutableMap<Int, Int>,
|
||||
readFpRegs: MutableMap<Int, Int>,
|
||||
writeFpRegs: MutableMap<Int, Int>
|
||||
readRegsCounts: MutableMap<Int, Int>,
|
||||
writeRegsCounts: MutableMap<Int, Int>,
|
||||
readFpRegsCounts: MutableMap<Int, Int>,
|
||||
writeFpRegsCounts: MutableMap<Int, Int>,
|
||||
regsTypes: MutableMap<Int, MutableSet<IRDataType>>
|
||||
) {
|
||||
when (this.reg1direction) {
|
||||
OperandDirection.UNUSED -> {}
|
||||
OperandDirection.READ -> readRegs[this.reg1!!] = readRegs.getValue(this.reg1)+1
|
||||
OperandDirection.WRITE -> writeRegs[this.reg1!!] = writeRegs.getValue(this.reg1)+1
|
||||
OperandDirection.READ -> {
|
||||
readRegsCounts[this.reg1!!] = readRegsCounts.getValue(this.reg1)+1
|
||||
if(type!=null) {
|
||||
var types = regsTypes[this.reg1]
|
||||
if(types==null) types = mutableSetOf()
|
||||
types += type
|
||||
regsTypes[this.reg1] = types
|
||||
}
|
||||
}
|
||||
OperandDirection.WRITE -> {
|
||||
writeRegsCounts[this.reg1!!] = writeRegsCounts.getValue(this.reg1)+1
|
||||
if(type!=null) {
|
||||
var types = regsTypes[this.reg1]
|
||||
if(types==null) types = mutableSetOf()
|
||||
types += type
|
||||
regsTypes[this.reg1] = types
|
||||
}
|
||||
}
|
||||
OperandDirection.READWRITE -> {
|
||||
readRegs[this.reg1!!] = readRegs.getValue(this.reg1)+1
|
||||
writeRegs[this.reg1] = writeRegs.getValue(this.reg1)+1
|
||||
readRegsCounts[this.reg1!!] = readRegsCounts.getValue(this.reg1)+1
|
||||
if(type!=null) {
|
||||
var types = regsTypes[this.reg1]
|
||||
if(types==null) types = mutableSetOf()
|
||||
types += type
|
||||
regsTypes[this.reg1] = types
|
||||
}
|
||||
writeRegsCounts[this.reg1] = writeRegsCounts.getValue(this.reg1)+1
|
||||
if(type!=null) {
|
||||
var types = regsTypes[this.reg1]
|
||||
if(types==null) types = mutableSetOf()
|
||||
types += type
|
||||
regsTypes[this.reg1] = types
|
||||
}
|
||||
}
|
||||
}
|
||||
when (this.reg2direction) {
|
||||
OperandDirection.UNUSED -> {}
|
||||
OperandDirection.READ -> writeRegs[this.reg2!!] = writeRegs.getValue(this.reg2)+1
|
||||
OperandDirection.READ -> {
|
||||
writeRegsCounts[this.reg2!!] = writeRegsCounts.getValue(this.reg2)+1
|
||||
if(type!=null) {
|
||||
var types = regsTypes[this.reg2]
|
||||
if(types==null) types = mutableSetOf()
|
||||
types += type
|
||||
regsTypes[this.reg2] = types
|
||||
}
|
||||
}
|
||||
else -> throw IllegalArgumentException("reg2 can only be read")
|
||||
}
|
||||
when (this.fpReg1direction) {
|
||||
OperandDirection.UNUSED -> {}
|
||||
OperandDirection.READ -> readFpRegs[this.fpReg1!!] = readFpRegs.getValue(this.fpReg1)+1
|
||||
OperandDirection.WRITE -> writeFpRegs[this.fpReg1!!] = writeFpRegs.getValue(this.fpReg1)+1
|
||||
OperandDirection.READ -> {
|
||||
readFpRegsCounts[this.fpReg1!!] = readFpRegsCounts.getValue(this.fpReg1)+1
|
||||
}
|
||||
OperandDirection.WRITE -> writeFpRegsCounts[this.fpReg1!!] = writeFpRegsCounts.getValue(this.fpReg1)+1
|
||||
OperandDirection.READWRITE -> {
|
||||
readFpRegs[this.fpReg1!!] = readFpRegs.getValue(this.fpReg1)+1
|
||||
writeFpRegs[this.fpReg1] = writeFpRegs.getValue(this.fpReg1)+1
|
||||
readFpRegsCounts[this.fpReg1!!] = readFpRegsCounts.getValue(this.fpReg1)+1
|
||||
writeFpRegsCounts[this.fpReg1] = writeFpRegsCounts.getValue(this.fpReg1)+1
|
||||
}
|
||||
}
|
||||
when (this.fpReg2direction) {
|
||||
OperandDirection.UNUSED -> {}
|
||||
OperandDirection.READ -> readFpRegs[this.fpReg2!!] = readFpRegs.getValue(this.fpReg2)+1
|
||||
OperandDirection.READ -> readFpRegsCounts[this.fpReg2!!] = readFpRegsCounts.getValue(this.fpReg2)+1
|
||||
else -> throw IllegalArgumentException("fpReg2 can only be read")
|
||||
}
|
||||
}
|
||||
@@ -827,14 +846,18 @@ data class IRInstruction(
|
||||
result.add("fr$it")
|
||||
result.add(",")
|
||||
}
|
||||
value?.let {
|
||||
immediate?.let {
|
||||
result.add(it.toHex())
|
||||
result.add(",")
|
||||
}
|
||||
fpValue?.let {
|
||||
immediateFp?.let {
|
||||
result.add(it.toString())
|
||||
result.add(",")
|
||||
}
|
||||
address?.let {
|
||||
result.add(it.toHex())
|
||||
result.add(",")
|
||||
}
|
||||
labelSymbol?.let {
|
||||
result.add(it)
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@ class IRProgram(val name: String,
|
||||
}
|
||||
|
||||
fun addBlock(block: IRBlock) {
|
||||
require(blocks.all { it.name != block.name}) { "duplicate block ${block.name} ${block.position}" }
|
||||
require(blocks.all { it.label != block.label}) { "duplicate block ${block.label} ${block.position}" }
|
||||
blocks.add(block)
|
||||
}
|
||||
|
||||
@@ -134,7 +134,7 @@ class IRProgram(val name: String,
|
||||
|
||||
// link all jump and branching instructions to their target
|
||||
chunk.instructions.forEach {
|
||||
if(it.opcode in OpcodesThatBranch && it.opcode!=Opcode.RETURN && it.opcode!=Opcode.RETURNREG && it.labelSymbol!=null)
|
||||
if(it.opcode in OpcodesThatBranch && it.opcode!=Opcode.RETURN && it.opcode!=Opcode.RETURNR && it.labelSymbol!=null)
|
||||
it.branchTarget = labeledChunks.getValue(it.labelSymbol)
|
||||
// note: branches with an address value cannot be linked to something...
|
||||
}
|
||||
@@ -199,19 +199,29 @@ class IRProgram(val name: String,
|
||||
}
|
||||
|
||||
fun registersUsed(): RegistersUsed {
|
||||
val readRegs = mutableMapOf<Int, Int>().withDefault { 0 }
|
||||
val readFpRegs = mutableMapOf<Int, Int>().withDefault { 0 }
|
||||
val writeRegs = mutableMapOf<Int, Int>().withDefault { 0 }
|
||||
val writeFpRegs = mutableMapOf<Int, Int>().withDefault { 0 }
|
||||
val readRegsCounts = mutableMapOf<Int, Int>().withDefault { 0 }
|
||||
val regsTypes = mutableMapOf<Int, MutableSet<IRDataType>>()
|
||||
val readFpRegsCounts = mutableMapOf<Int, Int>().withDefault { 0 }
|
||||
val writeRegsCounts = mutableMapOf<Int, Int>().withDefault { 0 }
|
||||
val writeFpRegsCounts = mutableMapOf<Int, Int>().withDefault { 0 }
|
||||
|
||||
fun addUsed(usedRegisters: RegistersUsed) {
|
||||
usedRegisters.readRegs.forEach{ (reg, count) -> readRegs[reg] = readRegs.getValue(reg) + count }
|
||||
usedRegisters.writeRegs.forEach{ (reg, count) -> writeRegs[reg] = writeRegs.getValue(reg) + count }
|
||||
usedRegisters.readFpRegs.forEach{ (reg, count) -> readFpRegs[reg] = readFpRegs.getValue(reg) + count }
|
||||
usedRegisters.writeFpRegs.forEach{ (reg, count) -> writeFpRegs[reg] = writeFpRegs.getValue(reg) + count }
|
||||
usedRegisters.readRegs.forEach{ (reg, count) -> readRegsCounts[reg] = readRegsCounts.getValue(reg) + count }
|
||||
usedRegisters.writeRegs.forEach{ (reg, count) -> writeRegsCounts[reg] = writeRegsCounts.getValue(reg) + count }
|
||||
usedRegisters.readFpRegs.forEach{ (reg, count) -> readFpRegsCounts[reg] = readFpRegsCounts.getValue(reg) + count }
|
||||
usedRegisters.writeFpRegs.forEach{ (reg, count) -> writeFpRegsCounts[reg] = writeFpRegsCounts.getValue(reg) + count }
|
||||
usedRegisters.regsTypes.forEach{ (reg, types) ->
|
||||
var t = regsTypes[reg]
|
||||
if(t==null) t = mutableSetOf()
|
||||
t += types
|
||||
regsTypes[reg] = t
|
||||
}
|
||||
}
|
||||
|
||||
globalInits.instructions.forEach {
|
||||
it.addUsedRegistersCounts(readRegsCounts, writeRegsCounts, readFpRegsCounts, writeFpRegsCounts, regsTypes)
|
||||
}
|
||||
|
||||
globalInits.instructions.forEach { it.addUsedRegistersCounts(readRegs, writeRegs, readFpRegs, writeFpRegs) }
|
||||
blocks.forEach {block ->
|
||||
block.children.forEach { child ->
|
||||
when(child) {
|
||||
@@ -223,13 +233,12 @@ class IRProgram(val name: String,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return RegistersUsed(readRegs, writeRegs, readFpRegs, writeFpRegs)
|
||||
return RegistersUsed(readRegsCounts, writeRegsCounts, readFpRegsCounts, writeFpRegsCounts, regsTypes)
|
||||
}
|
||||
}
|
||||
|
||||
class IRBlock(
|
||||
val name: String,
|
||||
val label: String,
|
||||
val address: UInt?,
|
||||
val alignment: BlockAlignment,
|
||||
val position: Position
|
||||
@@ -333,12 +342,13 @@ class IRCodeChunk(label: String?, next: IRCodeChunkBase?): IRCodeChunkBase(label
|
||||
override fun isEmpty() = instructions.isEmpty()
|
||||
override fun isNotEmpty() = instructions.isNotEmpty()
|
||||
override fun usedRegisters(): RegistersUsed {
|
||||
val readRegs = mutableMapOf<Int, Int>().withDefault { 0 }
|
||||
val readFpRegs = mutableMapOf<Int, Int>().withDefault { 0 }
|
||||
val writeRegs = mutableMapOf<Int, Int>().withDefault { 0 }
|
||||
val writeFpRegs = mutableMapOf<Int, Int>().withDefault { 0 }
|
||||
instructions.forEach { it.addUsedRegistersCounts(readRegs, writeRegs, readFpRegs, writeFpRegs) }
|
||||
return RegistersUsed(readRegs, writeRegs, readFpRegs, writeFpRegs)
|
||||
val readRegsCounts = mutableMapOf<Int, Int>().withDefault { 0 }
|
||||
val regsTypes = mutableMapOf<Int, MutableSet<IRDataType>>()
|
||||
val readFpRegsCounts = mutableMapOf<Int, Int>().withDefault { 0 }
|
||||
val writeRegsCounts = mutableMapOf<Int, Int>().withDefault { 0 }
|
||||
val writeFpRegsCounts = mutableMapOf<Int, Int>().withDefault { 0 }
|
||||
instructions.forEach { it.addUsedRegistersCounts(readRegsCounts, writeRegsCounts, readFpRegsCounts, writeFpRegsCounts, regsTypes) }
|
||||
return RegistersUsed(readRegsCounts, writeRegsCounts, readFpRegsCounts, writeFpRegsCounts, regsTypes)
|
||||
}
|
||||
|
||||
operator fun plusAssign(ins: IRInstruction) {
|
||||
@@ -373,7 +383,7 @@ class IRInlineBinaryChunk(label: String?,
|
||||
// note: no instructions, data is in the property
|
||||
override fun isEmpty() = data.isEmpty()
|
||||
override fun isNotEmpty() = data.isNotEmpty()
|
||||
override fun usedRegisters() = RegistersUsed(emptyMap(), emptyMap(), emptyMap(), emptyMap())
|
||||
override fun usedRegisters() = RegistersUsed(emptyMap(), emptyMap(), emptyMap(), emptyMap(), emptyMap())
|
||||
}
|
||||
|
||||
typealias IRCodeChunks = List<IRCodeChunkBase>
|
||||
@@ -384,9 +394,11 @@ class RegistersUsed(
|
||||
val writeRegs: Map<Int, Int>,
|
||||
val readFpRegs: Map<Int, Int>,
|
||||
val writeFpRegs: Map<Int, Int>,
|
||||
val regsTypes: Map<Int, Set<IRDataType>>
|
||||
) {
|
||||
|
||||
override fun toString(): String {
|
||||
return "read=$readRegs, write=$writeRegs, readFp=$readFpRegs, writeFp=$writeFpRegs"
|
||||
return "read=$readRegs, write=$writeRegs, readFp=$readFpRegs, writeFp=$writeFpRegs, types=$regsTypes"
|
||||
}
|
||||
|
||||
fun isEmpty() = readRegs.isEmpty() && writeRegs.isEmpty() && readFpRegs.isEmpty() && writeFpRegs.isEmpty()
|
||||
@@ -394,20 +406,23 @@ class RegistersUsed(
|
||||
}
|
||||
|
||||
private fun registersUsedInAssembly(isIR: Boolean, assembly: String): RegistersUsed {
|
||||
val readRegs = mutableMapOf<Int, Int>().withDefault { 0 }
|
||||
val readFpRegs = mutableMapOf<Int, Int>().withDefault { 0 }
|
||||
val writeRegs = mutableMapOf<Int, Int>().withDefault { 0 }
|
||||
val writeFpRegs = mutableMapOf<Int, Int>().withDefault { 0 }
|
||||
val readRegsCounts = mutableMapOf<Int, Int>().withDefault { 0 }
|
||||
val regsTypes = mutableMapOf<Int, MutableSet<IRDataType>>()
|
||||
val readFpRegsCounts = mutableMapOf<Int, Int>().withDefault { 0 }
|
||||
val writeRegsCounts = mutableMapOf<Int, Int>().withDefault { 0 }
|
||||
val writeFpRegsCounts = mutableMapOf<Int, Int>().withDefault { 0 }
|
||||
|
||||
if(isIR) {
|
||||
assembly.lineSequence().forEach { line ->
|
||||
val result = parseIRCodeLine(line.trim(), null, mutableMapOf())
|
||||
result.fold(
|
||||
ifLeft = { it.addUsedRegistersCounts(readRegs, writeRegs, readFpRegs, writeFpRegs) },
|
||||
ifRight = { /* labels can be skipped */ }
|
||||
)
|
||||
val t = line.trim()
|
||||
if(t.isNotEmpty()) {
|
||||
val result = parseIRCodeLine(t, null, mutableMapOf())
|
||||
result.fold(
|
||||
ifLeft = { it.addUsedRegistersCounts(readRegsCounts, writeRegsCounts,readFpRegsCounts, writeFpRegsCounts, regsTypes) },
|
||||
ifRight = { /* labels can be skipped */ }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return RegistersUsed(readRegs, writeRegs, readFpRegs, writeFpRegs)
|
||||
return RegistersUsed(readRegsCounts, writeRegsCounts, readFpRegsCounts, writeFpRegsCounts, regsTypes)
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import prog8.code.*
|
||||
import prog8.code.ast.PtVariable
|
||||
import prog8.code.core.DataType
|
||||
import prog8.code.core.ZeropageWish
|
||||
import prog8.code.core.internedStringsModuleName
|
||||
|
||||
|
||||
// In the Intermediate Representation, all nesting has been removed.
|
||||
@@ -120,4 +121,15 @@ class IRSymbolTable(sourceSt: SymbolTable?) {
|
||||
}
|
||||
|
||||
fun getAsmSymbols(): Map<String, String> = asmSymbols
|
||||
|
||||
fun removeTree(label: String) {
|
||||
val prefix = "$label."
|
||||
val vars = table.filter { it.key.startsWith(prefix) }
|
||||
vars.forEach {
|
||||
// check if attempt is made to delete interned strings, if so, refuse that.
|
||||
if(!it.key.startsWith(internedStringsModuleName)) {
|
||||
table.remove(it.key)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,52 +5,46 @@ import prog8.code.core.DataType
|
||||
import prog8.code.core.InternalCompilerException
|
||||
|
||||
|
||||
fun getTypeString(dt : DataType): String {
|
||||
return when(dt) {
|
||||
DataType.UBYTE -> "ubyte"
|
||||
DataType.BYTE -> "byte"
|
||||
DataType.UWORD -> "uword"
|
||||
DataType.WORD -> "word"
|
||||
DataType.FLOAT -> "float"
|
||||
DataType.ARRAY_UB, DataType.STR -> "ubyte[]"
|
||||
DataType.ARRAY_B -> "byte[]"
|
||||
DataType.ARRAY_UW -> "uword[]"
|
||||
DataType.ARRAY_W -> "word[]"
|
||||
DataType.ARRAY_F -> "float[]"
|
||||
else -> throw InternalCompilerException("weird dt")
|
||||
}
|
||||
fun getTypeString(dt : DataType): String = when(dt) {
|
||||
DataType.UBYTE -> "ubyte"
|
||||
DataType.BYTE -> "byte"
|
||||
DataType.UWORD -> "uword"
|
||||
DataType.WORD -> "word"
|
||||
DataType.FLOAT -> "float"
|
||||
DataType.ARRAY_UB, DataType.STR -> "ubyte[]"
|
||||
DataType.ARRAY_B -> "byte[]"
|
||||
DataType.ARRAY_UW -> "uword[]"
|
||||
DataType.ARRAY_W -> "word[]"
|
||||
DataType.ARRAY_F -> "float[]"
|
||||
else -> throw InternalCompilerException("weird dt")
|
||||
}
|
||||
|
||||
fun getTypeString(memvar: StMemVar): String {
|
||||
return when(memvar.dt) {
|
||||
DataType.UBYTE -> "ubyte"
|
||||
DataType.BYTE -> "byte"
|
||||
DataType.UWORD -> "uword"
|
||||
DataType.WORD -> "word"
|
||||
DataType.FLOAT -> "float"
|
||||
DataType.ARRAY_UB, DataType.STR -> "ubyte[${memvar.length}]"
|
||||
DataType.ARRAY_B -> "byte[${memvar.length}]"
|
||||
DataType.ARRAY_UW -> "uword[${memvar.length}]"
|
||||
DataType.ARRAY_W -> "word[${memvar.length}]"
|
||||
DataType.ARRAY_F -> "float[${memvar.length}]"
|
||||
else -> throw InternalCompilerException("weird dt")
|
||||
}
|
||||
fun getTypeString(memvar: StMemVar): String = when(memvar.dt) {
|
||||
DataType.UBYTE -> "ubyte"
|
||||
DataType.BYTE -> "byte"
|
||||
DataType.UWORD -> "uword"
|
||||
DataType.WORD -> "word"
|
||||
DataType.FLOAT -> "float"
|
||||
DataType.ARRAY_UB, DataType.STR -> "ubyte[${memvar.length}]"
|
||||
DataType.ARRAY_B -> "byte[${memvar.length}]"
|
||||
DataType.ARRAY_UW -> "uword[${memvar.length}]"
|
||||
DataType.ARRAY_W -> "word[${memvar.length}]"
|
||||
DataType.ARRAY_F -> "float[${memvar.length}]"
|
||||
else -> throw InternalCompilerException("weird dt")
|
||||
}
|
||||
|
||||
fun getTypeString(variable : StStaticVariable): String {
|
||||
return when(variable.dt) {
|
||||
DataType.UBYTE -> "ubyte"
|
||||
DataType.BYTE -> "byte"
|
||||
DataType.UWORD -> "uword"
|
||||
DataType.WORD -> "word"
|
||||
DataType.FLOAT -> "float"
|
||||
DataType.ARRAY_UB, DataType.STR -> "ubyte[${variable.length}]"
|
||||
DataType.ARRAY_B -> "byte[${variable.length}]"
|
||||
DataType.ARRAY_UW -> "uword[${variable.length}]"
|
||||
DataType.ARRAY_W -> "word[${variable.length}]"
|
||||
DataType.ARRAY_F -> "float[${variable.length}]"
|
||||
else -> throw InternalCompilerException("weird dt")
|
||||
}
|
||||
fun getTypeString(variable : StStaticVariable): String = when(variable.dt) {
|
||||
DataType.UBYTE -> "ubyte"
|
||||
DataType.BYTE -> "byte"
|
||||
DataType.UWORD -> "uword"
|
||||
DataType.WORD -> "word"
|
||||
DataType.FLOAT -> "float"
|
||||
DataType.ARRAY_UB, DataType.STR -> "ubyte[${variable.length}]"
|
||||
DataType.ARRAY_B -> "byte[${variable.length}]"
|
||||
DataType.ARRAY_UW -> "uword[${variable.length}]"
|
||||
DataType.ARRAY_W -> "word[${variable.length}]"
|
||||
DataType.ARRAY_F -> "float[${variable.length}]"
|
||||
else -> throw InternalCompilerException("weird dt")
|
||||
}
|
||||
|
||||
fun convertIRType(typestr: String): IRDataType? {
|
||||
@@ -101,129 +95,124 @@ fun parseIRCodeLine(line: String, location: Pair<IRCodeChunk, Int>?, placeholder
|
||||
}
|
||||
var type: IRDataType? = convertIRType(typestr)
|
||||
val formats = instructionFormats.getValue(opcode)
|
||||
val format: InstructionFormat
|
||||
if(type !in formats) {
|
||||
type = IRDataType.BYTE
|
||||
format = if(type !in formats)
|
||||
formats.getValue(null)
|
||||
else
|
||||
val format: InstructionFormat =
|
||||
if(type !in formats) {
|
||||
type = IRDataType.BYTE
|
||||
if(type !in formats)
|
||||
formats.getValue(null)
|
||||
else
|
||||
formats.getValue(type)
|
||||
} else {
|
||||
formats.getValue(type)
|
||||
} else {
|
||||
format = formats.getValue(type)
|
||||
}
|
||||
}
|
||||
|
||||
// parse the operands
|
||||
val operands = rest.lowercase().split(",").toMutableList()
|
||||
val operands = if(rest.isBlank()) emptyList() else rest.split(",").map{ it.trim() }.toMutableList()
|
||||
var reg1: Int? = null
|
||||
var reg2: Int? = null
|
||||
var reg3: Int? = null
|
||||
var fpReg1: Int? = null
|
||||
var fpReg2: Int? = null
|
||||
var fpReg3: Int? = null
|
||||
var value: Float? = null
|
||||
var operand: String?
|
||||
var immediateInt: Int? = null
|
||||
var immediateFp: Float? = null
|
||||
var address: Int? = null
|
||||
var labelSymbol: String? = null
|
||||
|
||||
fun parseValueOrPlaceholder(operand: String, location: Pair<IRCodeChunk, Int>?, rest: String, restIndex: Int): Float? {
|
||||
fun parseValueOrPlaceholder(operand: String, location: Pair<IRCodeChunk, Int>?): Float? {
|
||||
return if(operand[0].isLetter()) {
|
||||
labelSymbol = rest.split(",")[restIndex].trim()
|
||||
if(location!=null)
|
||||
placeholders[location] = labelSymbol!!
|
||||
placeholders[location] = operand
|
||||
null
|
||||
} else {
|
||||
parseIRValue(operand)
|
||||
}
|
||||
}
|
||||
|
||||
if(operands.isNotEmpty() && operands[0].isNotEmpty()) {
|
||||
operand = operands.removeFirst().trim()
|
||||
if(operand[0]=='r')
|
||||
reg1 = operand.substring(1).toInt()
|
||||
else if(operand[0]=='f' && operand[1]=='r')
|
||||
fpReg1 = operand.substring(2).toInt()
|
||||
else {
|
||||
value = parseValueOrPlaceholder(operand, location, rest, 0)
|
||||
operands.clear()
|
||||
}
|
||||
if(operands.isNotEmpty()) {
|
||||
operand = operands.removeFirst().trim()
|
||||
if(operand[0]=='r')
|
||||
reg2 = operand.substring(1).toInt()
|
||||
else if(operand[0]=='f' && operand[1]=='r')
|
||||
fpReg2 = operand.substring(2).toInt()
|
||||
else {
|
||||
value = parseValueOrPlaceholder(operand, location, rest, 1)
|
||||
operands.clear()
|
||||
}
|
||||
if(operands.isNotEmpty()) {
|
||||
operand = operands.removeFirst().trim()
|
||||
if(operand[0]=='r')
|
||||
reg3 = operand.substring(1).toInt()
|
||||
else if(operand[0]=='f' && operand[1]=='r')
|
||||
fpReg3 = operand.substring(2).toInt()
|
||||
else {
|
||||
value = parseValueOrPlaceholder(operand, location, rest, 2)
|
||||
operands.clear()
|
||||
}
|
||||
if(operands.isNotEmpty()) {
|
||||
throw IRParseException("unexpected even more operands? $operands rest=$rest'")
|
||||
operands.forEach { oper ->
|
||||
if(oper[0] == '&')
|
||||
throw IRParseException("address-of should be done with normal LOAD <symbol>")
|
||||
else if(oper[0] in "rR") {
|
||||
if(reg1==null) reg1 = oper.substring(1).toInt()
|
||||
else if(reg2==null) reg2 = oper.substring(1).toInt()
|
||||
else throw IRParseException("too many register operands")
|
||||
} else if (oper[0] in "fF" && oper[1] in "rR") {
|
||||
if(fpReg1==null) fpReg1 = oper.substring(2).toInt()
|
||||
else if(fpReg2==null) fpReg2 = oper.substring(2).toInt()
|
||||
else throw IRParseException("too many fp register operands")
|
||||
} else if (oper[0].isDigit() || oper[0] == '$' || oper[0]=='%' || oper[0]=='-' || oper.startsWith("0x")) {
|
||||
val value = parseIRValue(oper)
|
||||
if(format.immediate) {
|
||||
if(immediateInt==null && immediateFp==null) {
|
||||
if (type == IRDataType.FLOAT)
|
||||
immediateFp = value
|
||||
else
|
||||
immediateInt = value.toInt()
|
||||
} else {
|
||||
address = value.toInt()
|
||||
}
|
||||
} else {
|
||||
address = value.toInt()
|
||||
}
|
||||
} else {
|
||||
if(!oper[0].isLetter())
|
||||
throw IRParseException("expected symbol name: $oper")
|
||||
labelSymbol = oper
|
||||
val value = parseValueOrPlaceholder(oper, location)
|
||||
if(value!=null)
|
||||
address = value.toInt()
|
||||
}
|
||||
}
|
||||
|
||||
// shift the operands back into place
|
||||
while(reg1==null && reg2!=null) {
|
||||
reg1 = reg2
|
||||
reg2 = reg3
|
||||
reg3 = null
|
||||
}
|
||||
while(fpReg1==null && fpReg2!=null) {
|
||||
fpReg1 = fpReg2
|
||||
fpReg2 = fpReg3
|
||||
fpReg3 = null
|
||||
}
|
||||
if(reg3!=null)
|
||||
throw IRParseException("too many reg arguments $line")
|
||||
if(fpReg3!=null)
|
||||
throw IRParseException("too many fpreg arguments $line")
|
||||
|
||||
if(type!=null && type !in formats)
|
||||
throw IRParseException("invalid type code for $line")
|
||||
if(format.reg1!=OperandDirection.UNUSED && reg1==null)
|
||||
throw IRParseException("needs reg1 for $line")
|
||||
if(format.reg2!=OperandDirection.UNUSED && reg2==null)
|
||||
throw IRParseException("needs reg2 for $line")
|
||||
if(format.valueIn && value==null && labelSymbol==null)
|
||||
throw IRParseException("needs value or symbol for $line")
|
||||
if(format.fpReg1!=OperandDirection.UNUSED && fpReg1==null)
|
||||
throw IRParseException("needs fpReg1 for $line")
|
||||
if(format.fpReg2!=OperandDirection.UNUSED && fpReg2==null)
|
||||
throw IRParseException("needs fpReg2 for $line")
|
||||
if(format.address!=OperandDirection.UNUSED && address==null && labelSymbol==null)
|
||||
throw IRParseException("needs address or symbol for $line")
|
||||
if(format.reg1==OperandDirection.UNUSED && reg1!=null)
|
||||
throw IRParseException("invalid reg1 for $line")
|
||||
if(format.reg2==OperandDirection.UNUSED && reg2!=null)
|
||||
throw IRParseException("invalid reg2 for $line")
|
||||
if(value!=null && opcode !in OpcodesWithMemoryAddressAsValue) {
|
||||
if(format.fpReg1==OperandDirection.UNUSED && fpReg1!=null)
|
||||
throw IRParseException("invalid fpReg1 for $line")
|
||||
if(format.fpReg2==OperandDirection.UNUSED && fpReg2!=null)
|
||||
throw IRParseException("invalid fpReg2 for $line")
|
||||
if(format.immediate && opcode!=Opcode.SYSCALL) {
|
||||
if(immediateInt==null && immediateFp==null && labelSymbol==null)
|
||||
throw IRParseException("needs value or symbol for $line")
|
||||
when (type) {
|
||||
IRDataType.BYTE -> {
|
||||
if (value < -128 || value > 255)
|
||||
throw IRParseException("value out of range for byte: $value")
|
||||
if (immediateInt!=null && (immediateInt!! < -128 || immediateInt!! > 255))
|
||||
throw IRParseException("immediate value out of range for byte: $immediateInt")
|
||||
}
|
||||
IRDataType.WORD -> {
|
||||
if (value < -32768 || value > 65535)
|
||||
throw IRParseException("value out of range for word: $value")
|
||||
if (immediateInt!=null && (immediateInt!! < -32768 || immediateInt!! > 65535))
|
||||
throw IRParseException("immediate value out of range for word: $immediateInt")
|
||||
}
|
||||
IRDataType.FLOAT -> {}
|
||||
null -> {}
|
||||
}
|
||||
}
|
||||
var floatValue: Float? = null
|
||||
var intValue: Int? = null
|
||||
if(format.immediate && opcode==Opcode.SETPARAM && immediateInt==null) {
|
||||
immediateInt = immediateFp!!.toInt()
|
||||
immediateFp = null
|
||||
}
|
||||
|
||||
if(format.valueIn && value!=null)
|
||||
intValue = value.toInt()
|
||||
if(format.fpValueIn && value!=null)
|
||||
floatValue = value
|
||||
if(format.address!=OperandDirection.UNUSED && address==null && labelSymbol==null)
|
||||
throw IRParseException("requires address or symbol for $line")
|
||||
|
||||
if(labelSymbol!=null) {
|
||||
if (labelSymbol!![0] == 'r' && labelSymbol!![1].isDigit())
|
||||
throw IRParseException("labelsymbol confused with register?: $labelSymbol")
|
||||
}
|
||||
|
||||
if(opcode in OpcodesForCpuRegisters) {
|
||||
val reg = rest.split(',').last().lowercase().trim()
|
||||
val reg = operands.last().lowercase()
|
||||
if(reg !in setOf(
|
||||
"a", "x", "y",
|
||||
"ax", "ay", "xy",
|
||||
@@ -237,5 +226,5 @@ fun parseIRCodeLine(line: String, location: Pair<IRCodeChunk, Int>?, placeholder
|
||||
return left(IRInstruction(opcode, type, reg1, labelSymbol = reg))
|
||||
}
|
||||
|
||||
return left(IRInstruction(opcode, type, reg1, reg2, fpReg1, fpReg2, intValue, floatValue, labelSymbol = labelSymbol))
|
||||
return left(IRInstruction(opcode, type, reg1, reg2, fpReg1, fpReg2, immediateInt, immediateFp, address, labelSymbol = labelSymbol))
|
||||
}
|
||||
@@ -92,6 +92,7 @@ uword sys.wait.jiffies
|
||||
</PARAMS>
|
||||
<INLINEASM LABEL="sys.wait" IR="true" POS="[library:/prog8lib/virtual/syslib.p8: line 17 col 10-13]">
|
||||
loadm.w r0,sys.wait.jiffies
|
||||
setparam.w r0,0
|
||||
syscall 13
|
||||
</INLINEASM>
|
||||
<CODE>
|
||||
|
||||
@@ -15,35 +15,41 @@ class TestInstructions: FunSpec({
|
||||
ins.fpReg1direction shouldBe OperandDirection.UNUSED
|
||||
ins.reg1 shouldBe null
|
||||
ins.reg2 shouldBe null
|
||||
ins.value shouldBe null
|
||||
ins.address shouldBe null
|
||||
ins.immediate shouldBe null
|
||||
ins.immediateFp shouldBe null
|
||||
ins.labelSymbol shouldBe null
|
||||
ins.toString() shouldBe "nop"
|
||||
}
|
||||
|
||||
test("with value") {
|
||||
val ins = IRInstruction(Opcode.BZ, IRDataType.BYTE, reg1=42, value = 99)
|
||||
ins.opcode shouldBe Opcode.BZ
|
||||
val ins = IRInstruction(Opcode.BEQ, IRDataType.BYTE, reg1=42, immediate = 0, address = 99)
|
||||
ins.opcode shouldBe Opcode.BEQ
|
||||
ins.type shouldBe IRDataType.BYTE
|
||||
ins.reg1direction shouldBe OperandDirection.READ
|
||||
ins.fpReg1direction shouldBe OperandDirection.UNUSED
|
||||
ins.reg1 shouldBe 42
|
||||
ins.reg2 shouldBe null
|
||||
ins.value shouldBe 99
|
||||
ins.address shouldBe 99
|
||||
ins.immediate shouldBe 0
|
||||
ins.immediateFp shouldBe null
|
||||
ins.labelSymbol shouldBe null
|
||||
ins.toString() shouldBe "bz.b r42,$63"
|
||||
ins.toString() shouldBe "beq.b r42,0,$63"
|
||||
}
|
||||
|
||||
test("with label") {
|
||||
val ins = IRInstruction(Opcode.BZ, IRDataType.WORD, reg1=11, labelSymbol = "a.b.c")
|
||||
ins.opcode shouldBe Opcode.BZ
|
||||
val ins = IRInstruction(Opcode.BEQ, IRDataType.WORD, reg1=11, immediate = 0, labelSymbol = "a.b.c")
|
||||
ins.opcode shouldBe Opcode.BEQ
|
||||
ins.type shouldBe IRDataType.WORD
|
||||
ins.reg1direction shouldBe OperandDirection.READ
|
||||
ins.fpReg1direction shouldBe OperandDirection.UNUSED
|
||||
ins.reg1 shouldBe 11
|
||||
ins.reg2 shouldBe null
|
||||
ins.value shouldBe null
|
||||
ins.address shouldBe null
|
||||
ins.immediate shouldBe 0
|
||||
ins.immediateFp shouldBe null
|
||||
ins.labelSymbol shouldBe "a.b.c"
|
||||
ins.toString() shouldBe "bz.w r11,a.b.c"
|
||||
ins.toString() shouldBe "beq.w r11,0,a.b.c"
|
||||
}
|
||||
|
||||
test("with output registers") {
|
||||
@@ -56,7 +62,9 @@ class TestInstructions: FunSpec({
|
||||
ins.fpReg2direction shouldBe OperandDirection.UNUSED
|
||||
ins.reg1 shouldBe 11
|
||||
ins.reg2 shouldBe 22
|
||||
ins.value shouldBe null
|
||||
ins.address shouldBe null
|
||||
ins.immediate shouldBe null
|
||||
ins.immediateFp shouldBe null
|
||||
ins.labelSymbol shouldBe null
|
||||
ins.toString() shouldBe "addr.w r11,r22"
|
||||
|
||||
@@ -69,7 +77,9 @@ class TestInstructions: FunSpec({
|
||||
ins2.fpReg2direction shouldBe OperandDirection.UNUSED
|
||||
ins2.reg1 shouldBe 11
|
||||
ins2.reg2 shouldBe 22
|
||||
ins2.value shouldBe null
|
||||
ins.address shouldBe null
|
||||
ins.immediate shouldBe null
|
||||
ins.immediateFp shouldBe null
|
||||
ins2.labelSymbol shouldBe null
|
||||
ins2.toString() shouldBe "sqrt.b r11,r22"
|
||||
}
|
||||
@@ -86,7 +96,9 @@ class TestInstructions: FunSpec({
|
||||
ins.fpReg2 shouldBe 2
|
||||
ins.reg1 shouldBe null
|
||||
ins.reg2 shouldBe null
|
||||
ins.value shouldBe null
|
||||
ins.address shouldBe null
|
||||
ins.immediate shouldBe null
|
||||
ins.immediateFp shouldBe null
|
||||
ins.labelSymbol shouldBe null
|
||||
ins.toString() shouldBe "fsin.f fr1,fr2"
|
||||
}
|
||||
@@ -94,19 +106,19 @@ class TestInstructions: FunSpec({
|
||||
|
||||
test("missing type should fail") {
|
||||
shouldThrow<IllegalArgumentException> {
|
||||
IRInstruction(Opcode.BZ, reg1=42, value=99)
|
||||
IRInstruction(Opcode.BEQ, reg1=42, address=99)
|
||||
}
|
||||
}
|
||||
|
||||
test("missing registers should fail") {
|
||||
shouldThrowWithMessage<IllegalArgumentException>("missing reg1") {
|
||||
IRInstruction(Opcode.BZ, IRDataType.BYTE, value=99)
|
||||
IRInstruction(Opcode.BEQ, IRDataType.BYTE, immediate = 0, address=99)
|
||||
}
|
||||
}
|
||||
|
||||
test("missing value should fail") {
|
||||
shouldThrowWithMessage<IllegalArgumentException>("missing a value or labelsymbol") {
|
||||
IRInstruction(Opcode.BZ, IRDataType.BYTE, reg1=42)
|
||||
test("missing address should fail") {
|
||||
shouldThrowWithMessage<IllegalArgumentException>("missing an address or labelsymbol") {
|
||||
IRInstruction(Opcode.BEQ, IRDataType.BYTE, immediate = 0, reg1=42)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -680,7 +680,6 @@ syn match prog8BuiltInFunc "\<cx16\.vpoke_or\>"
|
||||
syn match prog8BuiltInFunc "\<cx16\.vpoke_and\>"
|
||||
syn match prog8BuiltInFunc "\<cx16\.vpoke_xor\>"
|
||||
syn match prog8BuiltInFunc "\<cx16\.vload\>"
|
||||
syn match prog8BuiltInFunc "\<cx16\.FB_set_pixels_from_buf\>"
|
||||
syn match prog8BuiltInFunc "\<cx16\.init_system\>"
|
||||
syn match prog8BuiltInFunc "\<cx16\.init_system_phase2\>"
|
||||
syn match prog8BuiltInFunc "\<cx16\.set_irq\>"
|
||||
|
||||
@@ -89,7 +89,7 @@ internal class BitmapScreenPanel(private val drawImage: BufferedImage, val pixel
|
||||
|
||||
override fun paint(graphics: Graphics) {
|
||||
val g2d = graphics as Graphics2D
|
||||
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR)
|
||||
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR)
|
||||
g2d.drawImage(drawImage, 0, 0, size.width, size.height, null)
|
||||
Toolkit.getDefaultToolkit().sync()
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ package prog8.vm
|
||||
*/
|
||||
class Registers {
|
||||
private val registers = Array<UShort>(65536) { 0u }
|
||||
private val floatRegisters = Array(65535) { 0f }
|
||||
private val floatRegisters = Array(65536) { 0f }
|
||||
var cpuA: UByte = 0u
|
||||
var cpuX: UByte = 0u
|
||||
var cpuY: UByte = 0u
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package prog8.vm
|
||||
|
||||
import prog8.code.core.AssemblyError
|
||||
import prog8.intermediate.SyscallRegisterBase
|
||||
import kotlin.math.min
|
||||
|
||||
/*
|
||||
@@ -81,7 +80,12 @@ enum class Syscall {
|
||||
RNDF,
|
||||
STRING_CONTAINS,
|
||||
BYTEARRAY_CONTAINS,
|
||||
WORDARRAY_CONTAINS
|
||||
WORDARRAY_CONTAINS;
|
||||
|
||||
companion object {
|
||||
private val VALUES = values()
|
||||
fun fromInt(value: Int) = VALUES[value]
|
||||
}
|
||||
}
|
||||
|
||||
object SysCalls {
|
||||
@@ -92,14 +96,14 @@ object SysCalls {
|
||||
vm.reset(false)
|
||||
}
|
||||
Syscall.EXIT ->{
|
||||
vm.exit(vm.registers.getUB(SyscallRegisterBase).toInt())
|
||||
vm.exit(vm.valueStack.pop().toInt())
|
||||
}
|
||||
Syscall.PRINT_C -> {
|
||||
val char = vm.registers.getUB(SyscallRegisterBase).toInt()
|
||||
val char = vm.valueStack.pop().toInt()
|
||||
print(Char(char))
|
||||
}
|
||||
Syscall.PRINT_S -> {
|
||||
var addr = vm.registers.getUW(SyscallRegisterBase).toInt()
|
||||
var addr = vm.valueStack.popw().toInt()
|
||||
while(true) {
|
||||
val char = vm.memory.getUB(addr).toInt()
|
||||
if(char==0)
|
||||
@@ -109,21 +113,21 @@ object SysCalls {
|
||||
}
|
||||
}
|
||||
Syscall.PRINT_U8 -> {
|
||||
print(vm.registers.getUB(SyscallRegisterBase))
|
||||
print(vm.valueStack.pop())
|
||||
}
|
||||
Syscall.PRINT_U16 -> {
|
||||
print(vm.registers.getUW(SyscallRegisterBase))
|
||||
print(vm.valueStack.popw())
|
||||
}
|
||||
Syscall.INPUT -> {
|
||||
var input = readln()
|
||||
val maxlen = vm.registers.getUB(SyscallRegisterBase+1).toInt()
|
||||
val maxlen = vm.valueStack.pop().toInt()
|
||||
if(maxlen>0)
|
||||
input = input.substring(0, min(input.length, maxlen))
|
||||
vm.memory.setString(vm.registers.getUW(SyscallRegisterBase).toInt(), input, true)
|
||||
vm.registers.setUW(0, input.length.toUShort())
|
||||
vm.memory.setString(vm.valueStack.popw().toInt(), input, true)
|
||||
vm.valueStack.push(input.length.toUByte())
|
||||
}
|
||||
Syscall.SLEEP -> {
|
||||
val duration = vm.registers.getUW(SyscallRegisterBase).toLong()
|
||||
val duration = vm.valueStack.popw().toLong()
|
||||
Thread.sleep(duration)
|
||||
}
|
||||
Syscall.GFX_ENABLE -> vm.gfx_enable()
|
||||
@@ -131,13 +135,13 @@ object SysCalls {
|
||||
Syscall.GFX_PLOT -> vm.gfx_plot()
|
||||
Syscall.GFX_GETPIXEL ->vm.gfx_getpixel()
|
||||
Syscall.WAIT -> {
|
||||
val millis = vm.registers.getUW(SyscallRegisterBase).toLong() * 1000/60
|
||||
val millis = vm.valueStack.popw().toLong() * 1000/60
|
||||
Thread.sleep(millis)
|
||||
}
|
||||
Syscall.WAITVSYNC -> vm.waitvsync()
|
||||
Syscall.SORT_UBYTE -> {
|
||||
val address = vm.registers.getUW(SyscallRegisterBase).toInt()
|
||||
val length = vm.registers.getUB(SyscallRegisterBase+1).toInt()
|
||||
val length = vm.valueStack.pop().toInt()
|
||||
val address = vm.valueStack.popw().toInt()
|
||||
val array = IntProgression.fromClosedRange(address, address+length-1, 1).map {
|
||||
vm.memory.getUB(it)
|
||||
}.sorted()
|
||||
@@ -146,8 +150,8 @@ object SysCalls {
|
||||
}
|
||||
}
|
||||
Syscall.SORT_BYTE -> {
|
||||
val address = vm.registers.getUW(SyscallRegisterBase).toInt()
|
||||
val length = vm.registers.getUB(SyscallRegisterBase+1).toInt()
|
||||
val length = vm.valueStack.pop().toInt()
|
||||
val address = vm.valueStack.popw().toInt()
|
||||
val array = IntProgression.fromClosedRange(address, address+length-1, 1).map {
|
||||
vm.memory.getSB(it)
|
||||
}.sorted()
|
||||
@@ -156,8 +160,8 @@ object SysCalls {
|
||||
}
|
||||
}
|
||||
Syscall.SORT_UWORD -> {
|
||||
val address = vm.registers.getUW(SyscallRegisterBase).toInt()
|
||||
val length = vm.registers.getUB(SyscallRegisterBase+1).toInt()
|
||||
val length = vm.valueStack.pop().toInt()
|
||||
val address = vm.valueStack.popw().toInt()
|
||||
val array = IntProgression.fromClosedRange(address, address+length*2-2, 2).map {
|
||||
vm.memory.getUW(it)
|
||||
}.sorted()
|
||||
@@ -166,8 +170,8 @@ object SysCalls {
|
||||
}
|
||||
}
|
||||
Syscall.SORT_WORD -> {
|
||||
val address = vm.registers.getUW(SyscallRegisterBase).toInt()
|
||||
val length = vm.registers.getUB(SyscallRegisterBase+1).toInt()
|
||||
val length = vm.valueStack.pop().toInt()
|
||||
val address = vm.valueStack.popw().toInt()
|
||||
val array = IntProgression.fromClosedRange(address, address+length*2-2, 2).map {
|
||||
vm.memory.getSW(it)
|
||||
}.sorted()
|
||||
@@ -176,8 +180,8 @@ object SysCalls {
|
||||
}
|
||||
}
|
||||
Syscall.REVERSE_BYTES -> {
|
||||
val address = vm.registers.getUW(SyscallRegisterBase).toInt()
|
||||
val length = vm.registers.getUB(SyscallRegisterBase+1).toInt()
|
||||
val length = vm.valueStack.pop().toInt()
|
||||
val address = vm.valueStack.popw().toInt()
|
||||
val array = IntProgression.fromClosedRange(address, address+length-1, 1).map {
|
||||
vm.memory.getUB(it)
|
||||
}.reversed()
|
||||
@@ -186,8 +190,8 @@ object SysCalls {
|
||||
}
|
||||
}
|
||||
Syscall.REVERSE_WORDS -> {
|
||||
val address = vm.registers.getUW(SyscallRegisterBase).toInt()
|
||||
val length = vm.registers.getUB(SyscallRegisterBase+1).toInt()
|
||||
val length = vm.valueStack.pop().toInt()
|
||||
val address = vm.valueStack.popw().toInt()
|
||||
val array = IntProgression.fromClosedRange(address, address+length*2-2, 2).map {
|
||||
vm.memory.getUW(it)
|
||||
}.reversed()
|
||||
@@ -196,8 +200,8 @@ object SysCalls {
|
||||
}
|
||||
}
|
||||
Syscall.REVERSE_FLOATS -> {
|
||||
val address = vm.registers.getUW(SyscallRegisterBase).toInt()
|
||||
val length = vm.registers.getUB(SyscallRegisterBase+1).toInt()
|
||||
val length = vm.valueStack.pop().toInt()
|
||||
val address = vm.valueStack.popw().toInt()
|
||||
val array = IntProgression.fromClosedRange(address, address+length*4-2, 4).map {
|
||||
vm.memory.getFloat(it)
|
||||
}.reversed()
|
||||
@@ -206,151 +210,154 @@ object SysCalls {
|
||||
}
|
||||
}
|
||||
Syscall.ANY_BYTE -> {
|
||||
val address = vm.registers.getUW(SyscallRegisterBase).toInt()
|
||||
val length = vm.registers.getUB(SyscallRegisterBase+1).toInt()
|
||||
val length = vm.valueStack.pop().toInt()
|
||||
val address = vm.valueStack.popw().toInt()
|
||||
val addresses = IntProgression.fromClosedRange(address, address+length*2-2, 2)
|
||||
if(addresses.any { vm.memory.getUB(it).toInt()!=0 })
|
||||
vm.registers.setUB(0, 1u)
|
||||
vm.valueStack.push(1u)
|
||||
else
|
||||
vm.registers.setUB(0, 0u)
|
||||
vm.valueStack.push(0u)
|
||||
}
|
||||
Syscall.ANY_WORD -> {
|
||||
val address = vm.registers.getUW(SyscallRegisterBase).toInt()
|
||||
val length = vm.registers.getUB(SyscallRegisterBase+1).toInt()
|
||||
val length = vm.valueStack.pop().toInt()
|
||||
val address = vm.valueStack.popw().toInt()
|
||||
val addresses = IntProgression.fromClosedRange(address, address+length*2-2, 2)
|
||||
if(addresses.any { vm.memory.getUW(it).toInt()!=0 })
|
||||
vm.registers.setUB(0, 1u)
|
||||
vm.valueStack.push(1u)
|
||||
else
|
||||
vm.registers.setUB(0, 0u)
|
||||
vm.valueStack.push(0u)
|
||||
}
|
||||
Syscall.ANY_FLOAT -> {
|
||||
val address = vm.registers.getUW(SyscallRegisterBase).toInt()
|
||||
val length = vm.registers.getUB(SyscallRegisterBase+1).toInt()
|
||||
val length = vm.valueStack.pop().toInt()
|
||||
val address = vm.valueStack.popw().toInt()
|
||||
val addresses = IntProgression.fromClosedRange(address, address+length*4-2, 4)
|
||||
if(addresses.any { vm.memory.getFloat(it).toInt()!=0 })
|
||||
vm.registers.setUB(0, 1u)
|
||||
vm.valueStack.push(1u)
|
||||
else
|
||||
vm.registers.setUB(0, 0u)
|
||||
vm.valueStack.push(0u)
|
||||
}
|
||||
Syscall.ALL_BYTE -> {
|
||||
val address = vm.registers.getUW(SyscallRegisterBase).toInt()
|
||||
val length = vm.registers.getUB(SyscallRegisterBase+1).toInt()
|
||||
val length = vm.valueStack.pop().toInt()
|
||||
val address = vm.valueStack.popw().toInt()
|
||||
val addresses = IntProgression.fromClosedRange(address, address+length*2-2, 2)
|
||||
if(addresses.all { vm.memory.getUB(it).toInt()!=0 })
|
||||
vm.registers.setUB(0, 1u)
|
||||
vm.valueStack.push(1u)
|
||||
else
|
||||
vm.registers.setUB(0, 0u)
|
||||
vm.valueStack.push(0u)
|
||||
}
|
||||
Syscall.ALL_WORD -> {
|
||||
val address = vm.registers.getUW(SyscallRegisterBase).toInt()
|
||||
val length = vm.registers.getUB(SyscallRegisterBase+1).toInt()
|
||||
val length = vm.valueStack.pop().toInt()
|
||||
val address = vm.valueStack.popw().toInt()
|
||||
val addresses = IntProgression.fromClosedRange(address, address+length*2-2, 2)
|
||||
if(addresses.all { vm.memory.getUW(it).toInt()!=0 })
|
||||
vm.registers.setUB(0, 1u)
|
||||
vm.valueStack.push(1u)
|
||||
else
|
||||
vm.registers.setUB(0, 0u)
|
||||
vm.valueStack.push(0u)
|
||||
}
|
||||
Syscall.ALL_FLOAT -> {
|
||||
val address = vm.registers.getUW(SyscallRegisterBase).toInt()
|
||||
val length = vm.registers.getUB(SyscallRegisterBase+1).toInt()
|
||||
val length = vm.valueStack.pop().toInt()
|
||||
val address = vm.valueStack.popw().toInt()
|
||||
val addresses = IntProgression.fromClosedRange(address, address+length*4-2, 4)
|
||||
if(addresses.all { vm.memory.getFloat(it).toInt()!=0 })
|
||||
vm.registers.setUB(0, 1u)
|
||||
vm.valueStack.push(1u)
|
||||
else
|
||||
vm.registers.setUB(0, 0u)
|
||||
vm.valueStack.push(0u)
|
||||
}
|
||||
Syscall.PRINT_F -> {
|
||||
val value = vm.registers.getFloat(SyscallRegisterBase)
|
||||
print(value)
|
||||
print(vm.valueStack.popf())
|
||||
}
|
||||
Syscall.STR_TO_UWORD -> {
|
||||
val stringAddr = vm.registers.getUW(SyscallRegisterBase)
|
||||
val stringAddr = vm.valueStack.popw()
|
||||
val string = vm.memory.getString(stringAddr.toInt()).takeWhile { it.isDigit() }
|
||||
val value = try {
|
||||
string.toUShort()
|
||||
} catch(_: NumberFormatException) {
|
||||
0u
|
||||
}
|
||||
vm.registers.setUW(0, value)
|
||||
vm.valueStack.pushw(value)
|
||||
}
|
||||
Syscall.STR_TO_WORD -> {
|
||||
val stringAddr = vm.registers.getUW(SyscallRegisterBase)
|
||||
val string = vm.memory.getString(stringAddr.toInt()).takeWhile { it.isDigit() }
|
||||
val stringAddr = vm.valueStack.popw()
|
||||
val memstring = vm.memory.getString(stringAddr.toInt())
|
||||
val match = Regex("^[+-]?\\d+").find(memstring)
|
||||
if(match==null) {
|
||||
vm.valueStack.pushw(0u)
|
||||
return
|
||||
}
|
||||
val value = try {
|
||||
string.toShort()
|
||||
match.value.toShort()
|
||||
} catch(_: NumberFormatException) {
|
||||
0
|
||||
}
|
||||
vm.registers.setSW(0, value)
|
||||
vm.valueStack.pushw(value.toUShort())
|
||||
}
|
||||
Syscall.COMPARE_STRINGS -> {
|
||||
val firstAddr = vm.registers.getUW(SyscallRegisterBase)
|
||||
val secondAddr = vm.registers.getUW(SyscallRegisterBase+1)
|
||||
val secondAddr = vm.valueStack.popw()
|
||||
val firstAddr = vm.valueStack.popw()
|
||||
val first = vm.memory.getString(firstAddr.toInt())
|
||||
val second = vm.memory.getString(secondAddr.toInt())
|
||||
val comparison = first.compareTo(second)
|
||||
if(comparison==0)
|
||||
vm.registers.setSB(0, 0)
|
||||
vm.valueStack.push(0u)
|
||||
else if(comparison<0)
|
||||
vm.registers.setSB(0, -1)
|
||||
vm.valueStack.push((-1).toUByte())
|
||||
else
|
||||
vm.registers.setSB(0, 1)
|
||||
vm.valueStack.push(1u)
|
||||
}
|
||||
Syscall.RNDFSEED -> {
|
||||
val seed = vm.registers.getFloat(SyscallRegisterBase)
|
||||
val seed = vm.valueStack.popf()
|
||||
if(seed>0) // always use negative seed, this mimics the behavior on CBM machines
|
||||
vm.randomSeedFloat(-seed)
|
||||
else
|
||||
vm.randomSeedFloat(seed)
|
||||
}
|
||||
Syscall.RNDSEED -> {
|
||||
val seed1 = vm.registers.getUW(SyscallRegisterBase)
|
||||
val seed2 = vm.registers.getUW(SyscallRegisterBase+1)
|
||||
val seed2 = vm.valueStack.popw()
|
||||
val seed1 = vm.valueStack.popw()
|
||||
vm.randomSeed(seed1, seed2)
|
||||
}
|
||||
Syscall.RND -> {
|
||||
vm.registers.setUB(0, vm.randomGenerator.nextInt().toUByte())
|
||||
vm.valueStack.push(vm.randomGenerator.nextInt().toUByte())
|
||||
}
|
||||
Syscall.RNDW -> {
|
||||
vm.registers.setUW(0, vm.randomGenerator.nextInt().toUShort())
|
||||
vm.valueStack.pushw(vm.randomGenerator.nextInt().toUShort())
|
||||
}
|
||||
Syscall.RNDF -> {
|
||||
vm.registers.setFloat(0, vm.randomGeneratorFloats.nextFloat())
|
||||
vm.valueStack.pushf(vm.randomGeneratorFloats.nextFloat())
|
||||
}
|
||||
Syscall.STRING_CONTAINS -> {
|
||||
val char = vm.registers.getUB(SyscallRegisterBase).toInt().toChar()
|
||||
val stringAddr = vm.registers.getUW(SyscallRegisterBase+1)
|
||||
val stringAddr = vm.valueStack.popw()
|
||||
val char = vm.valueStack.pop().toInt().toChar()
|
||||
val string = vm.memory.getString(stringAddr.toInt())
|
||||
vm.registers.setUB(0, if(char in string) 1u else 0u)
|
||||
vm.valueStack.push(if(char in string) 1u else 0u)
|
||||
}
|
||||
Syscall.BYTEARRAY_CONTAINS -> {
|
||||
val value = vm.registers.getUB(SyscallRegisterBase)
|
||||
var array = vm.registers.getUW(SyscallRegisterBase+1).toInt()
|
||||
var length = vm.registers.getUB(SyscallRegisterBase+2)
|
||||
var length = vm.valueStack.pop()
|
||||
var array = vm.valueStack.popw().toInt()
|
||||
val value = vm.valueStack.pop()
|
||||
while(length>0u) {
|
||||
if(vm.memory.getUB(array)==value) {
|
||||
vm.registers.setUB(0, 1u)
|
||||
vm.valueStack.push(1u)
|
||||
return
|
||||
}
|
||||
array++
|
||||
length--
|
||||
}
|
||||
vm.registers.setUB(0, 0u)
|
||||
vm.valueStack.push(0u)
|
||||
}
|
||||
Syscall.WORDARRAY_CONTAINS -> {
|
||||
// r0.w = value, r1.w = array, r2.b = array length
|
||||
val value = vm.registers.getUW(SyscallRegisterBase)
|
||||
var array = vm.registers.getUW(SyscallRegisterBase+1).toInt()
|
||||
var length = vm.registers.getUB(SyscallRegisterBase+2)
|
||||
var length = vm.valueStack.pop()
|
||||
var array = vm.valueStack.popw().toInt()
|
||||
val value = vm.valueStack.popw()
|
||||
while(length>0u) {
|
||||
if(vm.memory.getUW(array)==value) {
|
||||
vm.registers.setUB(0, 1u)
|
||||
vm.valueStack.push(1u)
|
||||
return
|
||||
}
|
||||
array += 2
|
||||
length--
|
||||
}
|
||||
vm.registers.setUB(0, 0u)
|
||||
vm.valueStack.push(0u)
|
||||
}
|
||||
else -> throw AssemblyError("missing syscall ${call.name}")
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -7,10 +7,12 @@ import prog8.intermediate.*
|
||||
|
||||
class VmProgramLoader {
|
||||
private val placeholders = mutableMapOf<Pair<IRCodeChunk, Int>, String>() // program chunk+index to symbolname
|
||||
private val subroutines = mutableMapOf<String, IRSubroutine>() // label to subroutine node
|
||||
|
||||
fun load(irProgram: IRProgram, memory: Memory): List<IRCodeChunk> {
|
||||
placeholders.clear()
|
||||
irProgram.validate()
|
||||
placeholders.clear()
|
||||
subroutines.clear()
|
||||
val allocations = VmVariableAllocator(irProgram.st, irProgram.encoding, irProgram.options.compTarget)
|
||||
val variableAddresses = allocations.allocations.toMutableMap()
|
||||
val programChunks = mutableListOf<IRCodeChunk>()
|
||||
@@ -36,7 +38,7 @@ class VmProgramLoader {
|
||||
val chunkReplacements = mutableListOf<Pair<IRCodeChunkBase, IRCodeChunk>>()
|
||||
irProgram.blocks.forEach { block ->
|
||||
if(block.address!=null)
|
||||
throw IRParseException("blocks cannot have a load address for vm: ${block.name}")
|
||||
throw IRParseException("blocks cannot have a load address for vm: ${block.label}")
|
||||
|
||||
block.children.forEach { child ->
|
||||
when(child) {
|
||||
@@ -48,6 +50,7 @@ class VmProgramLoader {
|
||||
}
|
||||
is IRInlineBinaryChunk -> throw IRParseException("inline binary data not yet supported in the VM") // TODO
|
||||
is IRSubroutine -> {
|
||||
subroutines[child.label] = child
|
||||
child.chunks.forEach { chunk ->
|
||||
when (chunk) {
|
||||
is IRInlineAsmChunk -> {
|
||||
@@ -58,19 +61,20 @@ class VmProgramLoader {
|
||||
is IRCodeChunk -> programChunks += chunk
|
||||
else -> throw AssemblyError("weird chunk type")
|
||||
}
|
||||
} }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pass2translateSyscalls(programChunks)
|
||||
pass2replaceLabelsByProgIndex(programChunks, variableAddresses)
|
||||
pass2replaceLabelsByProgIndex(programChunks, variableAddresses, subroutines)
|
||||
phase2relinkReplacedChunks(chunkReplacements, programChunks)
|
||||
|
||||
programChunks.forEach {
|
||||
it.instructions.forEach { ins ->
|
||||
if (ins.labelSymbol != null && ins.opcode !in OpcodesThatBranch)
|
||||
require(ins.value != null) { "instruction with labelSymbol for a var should have value set to var's memory address" }
|
||||
if (ins.labelSymbol != null && ins.opcode !in OpcodesThatBranch && ins.opcode !in OpcodesForCpuRegisters)
|
||||
require(ins.address != null) { "instruction with labelSymbol for a var should have value set to the memory address" }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,7 +106,7 @@ class VmProgramLoader {
|
||||
chunk.instructions.withIndex().forEach { (index, ins) ->
|
||||
if(ins.opcode == Opcode.SYSCALL) {
|
||||
// convert IR Syscall to VM Syscall
|
||||
val vmSyscall = when(ins.value!!) {
|
||||
val vmSyscall = when(ins.immediate!!) {
|
||||
IMSyscall.SORT_UBYTE.number -> Syscall.SORT_UBYTE
|
||||
IMSyscall.SORT_BYTE.number -> Syscall.SORT_BYTE
|
||||
IMSyscall.SORT_UWORD.number -> Syscall.SORT_UWORD
|
||||
@@ -124,7 +128,7 @@ class VmProgramLoader {
|
||||
}
|
||||
|
||||
if(vmSyscall!=null)
|
||||
chunk.instructions[index] = ins.copy(value = vmSyscall.ordinal)
|
||||
chunk.instructions[index] = ins.copy(immediate = vmSyscall.ordinal)
|
||||
}
|
||||
|
||||
val label = ins.labelSymbol
|
||||
@@ -137,7 +141,8 @@ class VmProgramLoader {
|
||||
|
||||
private fun pass2replaceLabelsByProgIndex(
|
||||
chunks: MutableList<IRCodeChunk>,
|
||||
variableAddresses: MutableMap<String, Int>
|
||||
variableAddresses: MutableMap<String, Int>,
|
||||
subroutines: MutableMap<String, IRSubroutine>
|
||||
) {
|
||||
for((ref, label) in placeholders) {
|
||||
val (chunk, line) = ref
|
||||
@@ -148,25 +153,53 @@ class VmProgramLoader {
|
||||
val (symbol, indexStr) = label.split('+')
|
||||
val index = indexStr.toInt()
|
||||
val address = variableAddresses.getValue(symbol) + index
|
||||
chunk.instructions[line] = chunk.instructions[line].copy(value = address)
|
||||
chunk.instructions[line] = chunk.instructions[line].copy(address = address)
|
||||
} else {
|
||||
// placeholder is not a variable, so it must be a label of a code chunk instead
|
||||
val target: IRCodeChunk? = chunks.firstOrNull { it.label==label }
|
||||
val opcode = chunk.instructions[line].opcode
|
||||
if(target==null)
|
||||
throw IRParseException("placeholder not found in variables nor labels: $label")
|
||||
if(target==null) {
|
||||
// exception allowed: storecpu/loadcpu instructions that refer to CPU registers
|
||||
if(opcode !in OpcodesForCpuRegisters)
|
||||
throw IRParseException("placeholder not found in variables nor labels: $label")
|
||||
}
|
||||
else if(opcode in OpcodesThatBranch) {
|
||||
chunk.instructions[line] = chunk.instructions[line].copy(branchTarget = target, value = null)
|
||||
} else if(opcode in OpcodesWithMemoryAddressAsValue) {
|
||||
throw IRParseException("vm cannot yet load a label address as a value: ${chunk.instructions[line]}") // TODO
|
||||
chunk.instructions[line] = chunk.instructions[line].copy(branchTarget = target, address = null)
|
||||
} else {
|
||||
throw IRParseException("vm cannot yet load a label address as a value: ${chunk.instructions[line]}") // TODO
|
||||
}
|
||||
}
|
||||
} else {
|
||||
chunk.instructions[line] = chunk.instructions[line].copy(value = replacement)
|
||||
chunk.instructions[line] = chunk.instructions[line].copy(address = replacement)
|
||||
}
|
||||
}
|
||||
|
||||
chunks.forEach {
|
||||
it.instructions.withIndex().forEach { (index, ins) ->
|
||||
if(ins.opcode==Opcode.SETPARAM && ins.address==null) {
|
||||
val call = findCall(it, index)
|
||||
if(call.opcode==Opcode.SYSCALL) {
|
||||
// there is no variable to set, SYSCALLs get their args from the stack.
|
||||
} else if(call.labelSymbol!=null) {
|
||||
// set the address in the instruction to the subroutine's parameter variable's address
|
||||
// this avoids having to look it up every time the SETPARAM instruction is encountered during execution
|
||||
val target = subroutines.getValue(call.labelSymbol!!)
|
||||
val paramVar = target.parameters[ins.immediate!!]
|
||||
val address = variableAddresses.getValue(paramVar.name)
|
||||
it.instructions[index] = ins.copy(address = address)
|
||||
} else
|
||||
throw IRParseException("weird call $call")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val functionCallOpcodes = setOf(Opcode.CALL, Opcode.CALLR, Opcode.SYSCALL, Opcode.JUMP, Opcode.JUMPA)
|
||||
private fun findCall(it: IRCodeChunk, startIndex: Int): IRInstruction {
|
||||
var idx = startIndex
|
||||
while(it.instructions[idx].opcode !in functionCallOpcodes)
|
||||
idx++
|
||||
return it.instructions[idx]
|
||||
}
|
||||
|
||||
private fun varsToMemory(
|
||||
|
||||
@@ -47,8 +47,8 @@ class TestVm: FunSpec( {
|
||||
val startSub = IRSubroutine("testmain.testsub", emptyList(), null, Position.DUMMY)
|
||||
val code = IRCodeChunk(startSub.label, null)
|
||||
code += IRInstruction(Opcode.NOP)
|
||||
code += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=1, value=12345)
|
||||
code += IRInstruction(Opcode.STOREM, IRDataType.WORD, reg1=1, value=1000)
|
||||
code += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=1, immediate=12345)
|
||||
code += IRInstruction(Opcode.STOREM, IRDataType.WORD, reg1=1, address=1000)
|
||||
code += IRInstruction(Opcode.RETURN)
|
||||
startSub += code
|
||||
block += startSub
|
||||
|
||||
Reference in new issue
Block a user