Compare commits

..

63 Commits
v8.11 ... v8.13

Author SHA1 Message Date
0371ffa4ce 'amiga' example using iso font 2023-05-14 21:55:35 +02:00
88ce9300bc fix parse cpureg in IR regspec 2023-05-14 21:02:40 +02:00
0e3d75cfeb move irType() to intermediate module 2023-05-14 20:44:32 +02:00
630c8a5faa IR: fix romsub encoding 2023-05-14 18:08:06 +02:00
905921a684 IR: new (sys)call instructions that encapsulate the full subroutine call
to fix the bugs resulting from nesting subroutine calls (as param to another call etc)
2023-05-14 15:20:25 +02:00
bff3c4f95c IR now converts IRInlineAsmChunk (of type IR) into regular code chunks directly.
.p8ir files usually won't contain <INLINEASM> nodes any longer
2023-05-09 21:04:31 +02:00
4c8898a639 fix typecheck crash on certain byte to word assignments 2023-05-08 23:02:48 +02:00
97df33ab1a IR: fix byte to word assignment not doing value extension 2023-05-08 22:47:00 +02:00
ef46fb2685 refactor 2023-05-08 21:51:55 +02:00
d5d6dd3614 optimize typecast expr 2023-05-08 03:30:14 +02:00
6c233c6a0a optimize add/sub expr 2023-05-08 02:41:34 +02:00
6db715d879 optimize multiplication expr 2023-05-08 02:10:54 +02:00
ab02e8a546 optimize more carry flag assembly 2023-05-07 23:55:34 +02:00
8cbfe64f19 optimize some carry flag assembly 2023-05-07 23:27:49 +02:00
68336a76c5 optimized word comparison expressions 2023-05-07 20:40:48 +02:00
393e914a86 optimized word equality comparison expressions 2023-05-07 18:55:17 +02:00
ffb54110e9 optimized byte comparison expressions 2023-05-07 15:15:58 +02:00
533d825f1a optimized ubyte comparison expressions 2023-05-07 14:47:31 +02:00
c65279b672 optimized logical expressions more 2023-05-07 13:29:45 +02:00
f9926beeef fix cx16.psg irq issue 2023-05-04 00:16:24 +02:00
add8a777d8 IR: binarydata fixes 2023-05-03 22:31:04 +02:00
3fc49c001e IR: fix for-loop codegen when step<0 2023-05-02 23:12:11 +02:00
24f37e2062 fix 2023-05-02 01:19:36 +02:00
f465b2e2a0 some improvements to IR peephole optimizer 2023-05-02 00:29:04 +02:00
ce00e49a89 version 8.12 2023-04-30 14:04:54 +02:00
d494f9d66b fix 2023-04-29 18:04:08 +02:00
c35a183a64 extra fix 2023-04-29 17:24:01 +02:00
9cdd5fe7f2 fix byte to word sign extension error in certain cases 2023-04-29 17:14:50 +02:00
c21428215e fix possible mkword() error 2023-04-29 14:39:14 +02:00
64d5af46f5 fix IDEA kotlin version 2023-04-29 14:23:40 +02:00
25846ea18a fix zsound stream example (missing sound file) 2023-04-29 13:02:24 +02:00
798383596d fix %option merge possible error 2023-04-29 00:01:59 +02:00
9ca71bc937 fix %option merge not choosing correct block to merge into 2023-04-28 23:52:02 +02:00
5407429ec0 improve error message 2023-04-28 23:32:19 +02:00
ee5c94f6db c128: fix key status zp location symbols 2023-04-28 20:43:26 +02:00
91045afbee document limited fp support 2023-04-28 18:18:41 +02:00
3f64782023 c128: remove floats module 2023-04-28 17:48:54 +02:00
f8d35f9502 c128: no FP support 2023-04-28 17:43:42 +02:00
ea78d3ec9a c128: better ZP definition 2023-04-28 17:08:56 +02:00
e056a28316 c128: fix memory bank resetting 2023-04-28 04:02:07 +02:00
0bea721c2e docs 2023-04-27 01:26:25 +02:00
e1b89494d0 tiny psg improvement to avoid clicks more on changing freq or envelope, added cx16.vpoke_mask() 2023-04-26 22:45:32 +02:00
cd8e7f3912 psg comment 2023-04-24 01:23:03 +02:00
50604c25c2 remove obsolete comments, updated links and docs. 2023-04-23 15:13:53 +02:00
aa6b2357d8 fix void warnings 2023-04-18 23:47:31 +02:00
5b2d29bef6 improved and added a few system routines for the cx16 2023-04-18 23:20:28 +02:00
a296d26328 api change: renamed cx16.push/pop_vera_context() to save/restore_vera_context()
this better reflects its capability because it doesn't use a stack, only a single buffer
2023-04-17 23:37:15 +02:00
d01a26ec61 fix occasional crash when indexing an undefined array variable 2023-04-16 05:23:06 +02:00
efd7d6f0c0 tweak IR call args setting now via special SETPARAM instruction 2023-04-14 02:10:39 +02:00
b55be093be tweak IR 2023-04-11 22:48:20 +02:00
7c1d5cadd7 fix sort and reverse on strings on 6502 codegen 2023-04-10 19:33:24 +02:00
dd1592b03b ir syscalls args via stack instead of fixed r65500+ 2023-04-10 18:02:37 +02:00
9b37ac483f vm fix str to word conversion
ir SYSCALL puts result(s) on value stack,  instead of on hardcoded r0, r1
2023-04-10 16:26:42 +02:00
090820958e ir divmod returns its results on valuestack, to keep consistency with the rule that only 1 register can be a returnvalue 2023-04-10 15:26:30 +02:00
ac21e1be5c vm syscall instruction no longer fixed to r0 2023-04-10 13:44:05 +02:00
5196443b26 fix 2023-04-10 12:16:52 +02:00
c8531cbeb1 remove unused variables from IR output 2023-04-09 23:09:30 +02:00
c560abedba fix compiler crash on rol/ror array value 2023-04-09 22:29:11 +02:00
9b952fbc44 tweaking IR instruction set branch instructions 2023-04-09 22:17:19 +02:00
ccdf05e922 tweaking IR instruction formats 2023-04-09 16:12:16 +02:00
c3d74f2ae9 fix golden ram area for x16, remove romsub restriction
note: romsubs still won't work in the VM but at least they compile again
2023-04-08 00:40:52 +02:00
f47498888c optimize imports 2023-04-07 22:34:23 +02:00
5665a7f0cb also track ir reg types 2023-04-07 22:24:17 +02:00
81 changed files with 3190 additions and 3103 deletions

2
.idea/kotlinc.xml generated
View File

@ -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>

View File

@ -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.

View File

@ -235,7 +235,7 @@ class StSub(name: String, val parameters: List<StSubroutineParameter>, val retur
class StRomSub(name: String,
val address: UInt,
val address: UInt?, // null in case of asmsub, specified in case of romsub
val parameters: List<StRomSubParameter>,
val returns: List<StRomSubParameter>,
astNode: PtNode) :

View File

@ -40,14 +40,9 @@ class SymbolTableMaker(private val program: PtProgram, private val options: Comp
private fun addToSt(node: PtNode, scope: Stack<StNode>) {
val stNode = when(node) {
is PtAsmSub -> {
if(node.address==null) {
val params = node.parameters.map { StSubroutineParameter(it.second.name, it.second.type) }
StSub(node.name, params, node.returns.singleOrNull()?.second, node)
} else {
val parameters = node.parameters.map { StRomSubParameter(it.first, it.second.type) }
val returns = node.returns.map { StRomSubParameter(it.first, it.second) }
StRomSub(node.name, node.address, parameters, returns, node)
}
val parameters = node.parameters.map { StRomSubParameter(it.first, it.second.type) }
val returns = node.returns.map { StRomSubParameter(it.first, it.second) }
StRomSub(node.name, node.address, parameters, returns, node)
}
is PtBlock -> {
StNode(node.name, StNodeType.BLOCK, node)

View File

@ -63,7 +63,7 @@ class PtProgram(
children.asSequence().filterIsInstance<PtBlock>()
fun entrypoint(): PtSub? =
allBlocks().firstOrNull { it.name == "main" }?.children?.firstOrNull { it is PtSub && it.name == "start" } as PtSub?
allBlocks().firstOrNull { it.name == "main" }?.children?.firstOrNull { it is PtSub && (it.name == "start" || it.name=="main.start") } as PtSub?
}

View File

@ -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

View File

@ -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

View File

@ -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()
}

View File

@ -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()
}

View File

@ -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()

View File

@ -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)
}
}

View File

@ -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()

View File

@ -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,

View File

@ -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])

View File

@ -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

View File

@ -772,13 +772,12 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
"<" -> {
if(dt==DataType.UBYTE) {
asmgen.out("""
lda $name
cmp $otherName
bcc +
lda #0
beq ++
+ lda #1
+ sta $name""")
ldy $name
cpy $otherName
rol a
eor #1
sta $name""")
}
else {
// see http://www.6502.org/tutorials/compare_beyond.html
@ -798,13 +797,11 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
"<=" -> {
if(dt==DataType.UBYTE) {
asmgen.out("""
lda $otherName
cmp $name
bcs +
lda #0
beq ++
+ lda #1
+ sta $name""")
ldy $otherName
cpy $name
rol a
sta $name""")
} else {
// see http://www.6502.org/tutorials/compare_beyond.html
asmgen.out("""
@ -823,13 +820,11 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
">" -> {
if(dt==DataType.UBYTE) {
asmgen.out("""
lda $name
cmp $otherName
lda #0
ldy $name
cpy $otherName
beq +
bcs ++
+ lda #0
beq ++
+ lda #1
rol a
+ sta $name""")
} else {
// see http://www.6502.org/tutorials/compare_beyond.html
@ -849,13 +844,11 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
">=" -> {
if(dt==DataType.UBYTE) {
asmgen.out("""
lda $name
cmp $otherName
bcs +
lda #0
beq ++
+ lda #1
+ sta $name""")
ldy $name
cpy $otherName
rol a
sta $name""")
} else {
// see http://www.6502.org/tutorials/compare_beyond.html
asmgen.out("""
@ -963,13 +956,12 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
"<" -> {
if(dt==DataType.UBYTE) {
asmgen.out("""
lda $name
cmp #$value
bcc +
lda #0
beq ++
+ lda #1
+ sta $name""")
ldy $name
cpy #$value
rol a
eor #1
sta $name""")
}
else {
// see http://www.6502.org/tutorials/compare_beyond.html
@ -989,13 +981,11 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
"<=" -> {
if(dt==DataType.UBYTE) {
asmgen.out("""
lda #$value
cmp $name
bcs +
lda #0
beq ++
+ lda #1
+ sta $name""")
ldy #$value
cpy $name
rol a
sta $name""")
} else {
// see http://www.6502.org/tutorials/compare_beyond.html
asmgen.out("""
@ -1014,13 +1004,11 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
">" -> {
if(dt==DataType.UBYTE) {
asmgen.out("""
lda $name
cmp #$value
lda #0
ldy $name
cpy #$value
beq +
bcs ++
+ lda #0
beq ++
+ lda #1
rol a
+ sta $name""")
} else {
// see http://www.6502.org/tutorials/compare_beyond.html
@ -1040,13 +1028,11 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
">=" -> {
if(dt==DataType.UBYTE) {
asmgen.out("""
lda $name
cmp #$value
bcs +
lda #0
beq ++
+ lda #1
+ sta $name""")
ldy $name
cpy #$value
rol a
sta $name""")
} else {
// see http://www.6502.org/tutorials/compare_beyond.html
asmgen.out("""

View File

@ -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

View File

@ -46,7 +46,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
assignment: PtAugmentedAssign
): IRCodeChunks {
val value = assignment.value
val vmDt = codeGen.irType(value.type)
val vmDt = irType(value.type)
return when(assignment.operator) {
"+" -> expressionEval.operatorPlusInplace(address, null, vmDt, value)
"-" -> expressionEval.operatorMinusInplace(address, null, vmDt, value)
@ -72,7 +72,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
private fun assignVarAugmented(symbol: String, assignment: PtAugmentedAssign): IRCodeChunks {
val value = assignment.value
val targetDt = codeGen.irType(assignment.target.type)
val targetDt = irType(assignment.target.type)
return when (assignment.operator) {
"+=" -> expressionEval.operatorPlusInplace(null, symbol, targetDt, value)
"-=" -> expressionEval.operatorMinusInplace(null, symbol, targetDt, value)
@ -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)
}
@ -161,7 +161,8 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
val targetIdent = assignment.target.identifier
val targetMemory = assignment.target.memory
val targetArray = assignment.target.array
val vmDt = codeGen.irType(assignment.value.type)
val valueDt = irType(assignment.value.type)
val targetDt = irType(assignment.target.type)
val result = mutableListOf<IRCodeChunkBase>()
var valueRegister = -1
@ -169,30 +170,42 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
val zero = codeGen.isZero(assignment.value)
if(!zero) {
// calculate the assignment value
if (vmDt == IRDataType.FLOAT) {
if (valueDt == IRDataType.FLOAT) {
val tr = expressionEval.translateExpression(assignment.value)
valueFpRegister = tr.resultFpReg
addToResult(result, tr, -1, valueFpRegister)
} else {
val extendByteToWord = if(targetDt != valueDt) {
// usually an error EXCEPT when a byte is assigned to a word.
if(targetDt==IRDataType.WORD && valueDt==IRDataType.BYTE)
true
else
throw AssemblyError("assignment value and target dt mismatch")
} else false
if (assignment.value is PtMachineRegister) {
valueRegister = (assignment.value as PtMachineRegister).register
if(extendByteToWord)
addInstr(result, IRInstruction(Opcode.EXT, IRDataType.BYTE, reg1=valueRegister), null)
} else {
val tr = expressionEval.translateExpression(assignment.value)
valueRegister = tr.resultReg
addToResult(result, tr, valueRegister, -1)
if(extendByteToWord) {
val opcode = if(assignment.value.type in SignedDatatypes) Opcode.EXTS else Opcode.EXT
addInstr(result, IRInstruction(opcode, IRDataType.BYTE, reg1 = valueRegister), null)
}
}
}
}
if(targetIdent!=null) {
val instruction = if(zero) {
IRInstruction(Opcode.STOREZM, vmDt, labelSymbol = targetIdent.name)
IRInstruction(Opcode.STOREZM, targetDt, labelSymbol = targetIdent.name)
} else {
if (vmDt == IRDataType.FLOAT) {
IRInstruction(Opcode.STOREM, vmDt, fpReg1 = valueFpRegister, labelSymbol = targetIdent.name)
}
if (targetDt == IRDataType.FLOAT)
IRInstruction(Opcode.STOREM, targetDt, fpReg1 = valueFpRegister, labelSymbol = targetIdent.name)
else
IRInstruction(Opcode.STOREM, vmDt, reg1 = valueRegister, labelSymbol = targetIdent.name)
IRInstruction(Opcode.STOREM, targetDt, reg1 = valueRegister, labelSymbol = targetIdent.name)
}
result += IRCodeChunk(null, null).also { it += instruction }
return result
@ -214,9 +227,9 @@ 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, targetDt, reg1=valueRegister, immediate = 0)
}
code += IRInstruction(Opcode.STOREIX, vmDt, reg1=valueRegister, reg2=idxReg, labelSymbol = variable)
code += IRInstruction(Opcode.STOREIX, targetDt, reg1=valueRegister, reg2=idxReg, labelSymbol = variable)
result += code
return result
}
@ -225,59 +238,59 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
if(zero) {
if(fixedIndex!=null) {
val offset = fixedIndex*itemsize
val chunk = IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREZM, vmDt, labelSymbol = "$variable+$offset") }
val chunk = IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREZM, targetDt, labelSymbol = "$variable+$offset") }
result += chunk
} else {
val (code, indexReg) = loadIndexReg(targetArray, itemsize)
result += code
result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREZX, vmDt, reg1=indexReg, labelSymbol = variable) }
result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREZX, targetDt, reg1=indexReg, labelSymbol = variable) }
}
} else {
if(vmDt== IRDataType.FLOAT) {
if(targetDt== IRDataType.FLOAT) {
if(fixedIndex!=null) {
val offset = fixedIndex*itemsize
val chunk = IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREM, vmDt, fpReg1 = valueFpRegister, labelSymbol = "$variable+$offset") }
val chunk = IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREM, targetDt, fpReg1 = valueFpRegister, labelSymbol = "$variable+$offset") }
result += chunk
} else {
val (code, indexReg) = loadIndexReg(targetArray, itemsize)
result += code
result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREX, vmDt, reg1 = indexReg, fpReg1 = valueFpRegister, labelSymbol = variable) }
result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREX, targetDt, reg1 = indexReg, fpReg1 = valueFpRegister, labelSymbol = variable) }
}
} else {
if(fixedIndex!=null) {
val offset = fixedIndex*itemsize
val chunk = IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREM, vmDt, reg1 = valueRegister, labelSymbol = "$variable+$offset") }
val chunk = IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREM, targetDt, reg1 = valueRegister, labelSymbol = "$variable+$offset") }
result += chunk
} else {
val (code, indexReg) = loadIndexReg(targetArray, itemsize)
result += code
result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREX, vmDt, reg1 = valueRegister, reg2=indexReg, labelSymbol = variable) }
result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREX, targetDt, reg1 = valueRegister, reg2=indexReg, labelSymbol = variable) }
}
}
}
return result
}
else if(targetMemory!=null) {
require(vmDt== IRDataType.BYTE) { "must be byte type ${targetMemory.position}"}
require(targetDt == 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, targetDt, address = (targetMemory.address as PtNumber).number.toInt()) }
result += chunk
} else {
val tr = expressionEval.translateExpression(targetMemory.address)
val addressReg = tr.resultReg
addToResult(result, tr, tr.resultReg, -1)
result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREZI, vmDt, reg1=addressReg) }
result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREZI, targetDt, reg1=addressReg) }
}
} 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, targetDt, reg1=valueRegister, address=(targetMemory.address as PtNumber).number.toInt()) }
result += chunk
} else {
val tr = expressionEval.translateExpression(targetMemory.address)
val addressReg = tr.resultReg
addToResult(result, tr, tr.resultReg, -1)
result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREI, vmDt, reg1=valueRegister, reg2=addressReg) }
result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREI, targetDt, reg1=valueRegister, reg2=addressReg) }
}
}
@ -300,7 +313,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

View File

@ -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,10 @@ 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)
addToResult(result, right, right.resultReg, -1)
result += codeGen.makeSyscall(IMSyscall.COMPARE_STRINGS, listOf(IRDataType.WORD to left.resultReg, IRDataType.WORD to right.resultReg), IRDataType.BYTE to left.resultReg)
return ExpressionCodeResult(result, IRDataType.BYTE, left.resultReg, -1)
}
private fun funcCmp(call: PtBuiltinFunctionCall): ExpressionCodeResult {
@ -87,7 +93,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
addToResult(result, leftTr, leftTr.resultReg, -1)
val rightTr = exprGen.translateExpression(call.args[1])
addToResult(result, rightTr, rightTr.resultReg, -1)
val dt = codeGen.irType(call.args[0].type)
val dt = irType(call.args[0].type)
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.CMP, dt, reg1=leftTr.resultReg, reg2=rightTr.resultReg)
}
@ -108,14 +114,10 @@ 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)
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)
}
addToResult(result, tr, tr.resultReg, -1)
val lengthReg = codeGen.registers.nextFree()
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = lengthReg, immediate = array.length), null)
result += codeGen.makeSyscall(syscall, listOf(IRDataType.WORD to tr.resultReg, IRDataType.BYTE to lengthReg), IRDataType.BYTE to tr.resultReg)
return ExpressionCodeResult(result, IRDataType.BYTE, tr.resultReg, -1)
}
@ -133,14 +135,10 @@ 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)
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)
}
addToResult(result, tr, tr.resultReg, -1)
val lengthReg = codeGen.registers.nextFree()
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = lengthReg, immediate = array.length), null)
result += codeGen.makeSyscall(syscall, listOf(IRDataType.WORD to tr.resultReg, IRDataType.BYTE to lengthReg), IRDataType.BYTE to tr.resultReg)
return ExpressionCodeResult(result, IRDataType.BYTE, tr.resultReg, -1)
}
@ -164,8 +162,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 +175,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)
@ -190,7 +188,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
private fun funcSgn(call: PtBuiltinFunctionCall): ExpressionCodeResult {
val result = mutableListOf<IRCodeChunkBase>()
val vmDt = codeGen.irType(call.type)
val vmDt = irType(call.type)
val tr = exprGen.translateExpression(call.args.single())
addToResult(result, tr, tr.resultReg, -1)
val resultReg = codeGen.registers.nextFree()
@ -261,11 +259,10 @@ 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)
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = SyscallRegisterBase+1, value = array.length)
it += IRInstruction(Opcode.SYSCALL, value = syscall.number)
}
addToResult(result, tr, tr.resultReg, -1)
val lengthReg = codeGen.registers.nextFree()
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = lengthReg, immediate = if(array.dt==DataType.STR) array.length!!-1 else array.length), null)
result += codeGen.makeSyscall(syscall, listOf(IRDataType.WORD to tr.resultReg, IRDataType.BYTE to lengthReg), null)
return ExpressionCodeResult(result, IRDataType.BYTE, -1, -1)
}
@ -284,11 +281,10 @@ 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)
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = SyscallRegisterBase+1, value = array.length)
it += IRInstruction(Opcode.SYSCALL, value = syscall.number)
}
addToResult(result, tr, tr.resultReg, -1)
val lengthReg = codeGen.registers.nextFree()
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = lengthReg, immediate = if(array.dt==DataType.STR) array.length!!-1 else array.length), null)
result += codeGen.makeSyscall(syscall, listOf(IRDataType.WORD to tr.resultReg, IRDataType.BYTE to lengthReg), null)
return ExpressionCodeResult(result, IRDataType.BYTE, -1, -1)
}
@ -310,7 +306,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 +321,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 +342,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 +357,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 +378,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 +398,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 {
@ -442,7 +438,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
}
private fun funcRolRor(opcode: Opcode, call: PtBuiltinFunctionCall): ExpressionCodeResult {
val vmDt = codeGen.irType(call.args[0].type)
val vmDt = irType(call.args[0].type)
val result = mutableListOf<IRCodeChunkBase>()
val tr = exprGen.translateExpression(call.args[0])
addToResult(result, tr, tr.resultReg, -1)

View File

@ -28,24 +28,24 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
fun translateExpression(expr: PtExpression): ExpressionCodeResult {
return when (expr) {
is PtMachineRegister -> {
ExpressionCodeResult(emptyList(), codeGen.irType(expr.type), expr.register, -1)
ExpressionCodeResult(emptyList(), irType(expr.type), expr.register, -1)
}
is PtNumber -> {
val vmDt = codeGen.irType(expr.type)
val vmDt = irType(expr.type)
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)
}
}
is PtIdentifier -> {
val vmDt = codeGen.irType(expr.type)
val vmDt = irType(expr.type)
val code = IRCodeChunk(null, null)
if (expr.type in PassByValueDatatypes) {
if(vmDt==IRDataType.FLOAT) {
@ -66,7 +66,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
}
}
is PtAddressOf -> {
val vmDt = codeGen.irType(expr.type)
val vmDt = irType(expr.type)
val symbol = expr.identifier.name
// note: LOAD <symbol> gets you the address of the symbol, whereas LOADM <symbol> would get you the value stored at that location
val code = IRCodeChunk(null, null)
@ -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)
@ -105,48 +105,35 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
private fun translate(check: PtContainmentCheck): ExpressionCodeResult {
val result = mutableListOf<IRCodeChunkBase>()
var tr = translateExpression(check.element)
addToResult(result, tr, tr.resultReg, -1)
val iterable = codeGen.symbolTable.flat.getValue(check.iterable.name) as StStaticVariable
when(iterable.dt) {
DataType.STR -> {
tr = translateExpression(check.element)
addToResult(result, tr, SyscallRegisterBase, -1)
tr = translateExpression(check.iterable)
addToResult(result, tr, SyscallRegisterBase+1, -1)
val resultReg = codeGen.registers.nextFree()
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.SYSCALL, value = IMSyscall.STRING_CONTAINS.number)
it += IRInstruction(Opcode.LOADR, IRDataType.BYTE, reg1=resultReg, reg2=0)
}
return ExpressionCodeResult(result, IRDataType.BYTE, resultReg, -1)
val elementTr = translateExpression(check.element)
addToResult(result, elementTr, elementTr.resultReg, -1)
val iterableTr = translateExpression(check.iterable)
addToResult(result, iterableTr, iterableTr.resultReg, -1)
result += codeGen.makeSyscall(IMSyscall.STRING_CONTAINS, listOf(IRDataType.BYTE to elementTr.resultReg, IRDataType.WORD to iterableTr.resultReg), IRDataType.BYTE to elementTr.resultReg)
return ExpressionCodeResult(result, IRDataType.BYTE, elementTr.resultReg, -1)
}
DataType.ARRAY_UB, DataType.ARRAY_B -> {
tr = translateExpression(check.element)
addToResult(result, tr, SyscallRegisterBase, -1)
tr = translateExpression(check.iterable)
addToResult(result, tr, SyscallRegisterBase+1, -1)
val resultReg = codeGen.registers.nextFree()
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)
}
// SysCall call convention: return value in register r0
return ExpressionCodeResult(result, IRDataType.BYTE, resultReg, -1)
val elementTr = translateExpression(check.element)
addToResult(result, elementTr, elementTr.resultReg, -1)
val iterableTr = translateExpression(check.iterable)
addToResult(result, iterableTr, iterableTr.resultReg, -1)
val lengthReg = codeGen.registers.nextFree()
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=lengthReg, immediate = iterable.length!!), null)
result += codeGen.makeSyscall(IMSyscall.BYTEARRAY_CONTAINS, listOf(IRDataType.BYTE to elementTr.resultReg, IRDataType.WORD to iterableTr.resultReg, IRDataType.BYTE to lengthReg), IRDataType.BYTE to elementTr.resultReg)
return ExpressionCodeResult(result, IRDataType.BYTE, elementTr.resultReg, -1)
}
DataType.ARRAY_UW, DataType.ARRAY_W -> {
tr = translateExpression(check.element)
addToResult(result, tr, SyscallRegisterBase, -1)
tr = translateExpression(check.iterable)
addToResult(result, tr, SyscallRegisterBase+1, -1)
val resultReg = codeGen.registers.nextFree()
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)
}
return ExpressionCodeResult(result, IRDataType.BYTE, resultReg, -1)
val elementTr = translateExpression(check.element)
addToResult(result, elementTr, elementTr.resultReg, -1)
val iterableTr = translateExpression(check.iterable)
addToResult(result, iterableTr, iterableTr.resultReg, -1)
val lengthReg = codeGen.registers.nextFree()
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=lengthReg, immediate = iterable.length!!), null)
result += codeGen.makeSyscall(IMSyscall.WORDARRAY_CONTAINS, listOf(IRDataType.WORD to elementTr.resultReg, IRDataType.WORD to iterableTr.resultReg, IRDataType.BYTE to lengthReg), IRDataType.BYTE to elementTr.resultReg)
return ExpressionCodeResult(result, IRDataType.BYTE, elementTr.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}")
@ -155,7 +142,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
private fun translate(arrayIx: PtArrayIndexer): ExpressionCodeResult {
val eltSize = codeGen.program.memsizer.memorySize(arrayIx.type)
val vmDt = codeGen.irType(arrayIx.type)
val vmDt = irType(arrayIx.type)
val result = mutableListOf<IRCodeChunkBase>()
val arrayVarSymbol = arrayIx.variable.name
@ -205,7 +192,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
val result = mutableListOf<IRCodeChunkBase>()
val tr = translateExpression(expr.value)
addToResult(result, tr, tr.resultReg, tr.resultFpReg)
val vmDt = codeGen.irType(expr.type)
val vmDt = irType(expr.type)
when(expr.operator) {
"+" -> { }
"-" -> {
@ -216,7 +203,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")
}
@ -321,12 +308,12 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
else -> throw AssemblyError("weird cast type")
}
return ExpressionCodeResult(result, codeGen.irType(cast.type), actualResultReg2, actualResultFpReg2)
return ExpressionCodeResult(result, irType(cast.type), actualResultReg2, actualResultFpReg2)
}
private fun translate(binExpr: PtBinaryExpression): ExpressionCodeResult {
require(!codeGen.options.useNewExprCode)
val vmDt = codeGen.irType(binExpr.left.type)
val vmDt = irType(binExpr.left.type)
val signed = binExpr.left.type in SignedDatatypes
return when(binExpr.operator) {
"+" -> operatorPlus(binExpr, vmDt)
@ -353,87 +340,82 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
when (val callTarget = codeGen.symbolTable.flat.getValue(fcall.name)) {
is StSub -> {
val result = mutableListOf<IRCodeChunkBase>()
// assign the arguments
val argRegisters = mutableListOf<FunctionCallArgs.ArgumentSpec>()
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)
}
}
val paramDt = irType(parameter.type)
val tr = translateExpression(arg)
if(paramDt==IRDataType.FLOAT)
argRegisters.add(FunctionCallArgs.ArgumentSpec(parameter.name, null, FunctionCallArgs.RegSpec(IRDataType.FLOAT, tr.resultFpReg, null)))
else
argRegisters.add(FunctionCallArgs.ArgumentSpec(parameter.name, null, FunctionCallArgs.RegSpec(paramDt, tr.resultReg, null)))
result += tr.chunks
}
return if(fcall.void) {
addInstr(result, IRInstruction(Opcode.CALL, labelSymbol = fcall.name), null)
// return value
val returnRegSpec = if(fcall.void) null else {
val returnIrType = irType(callTarget.returnType!!)
if(returnIrType==IRDataType.FLOAT)
FunctionCallArgs.RegSpec(returnIrType, codeGen.registers.nextFreeFloat(), null)
else
FunctionCallArgs.RegSpec(returnIrType, codeGen.registers.nextFree(), null)
}
// create the call
val call = IRInstruction(Opcode.CALL, labelSymbol = fcall.name, fcallArgs = FunctionCallArgs(argRegisters, returnRegSpec))
addInstr(result, call, null)
return if(fcall.void)
ExpressionCodeResult(result, IRDataType.BYTE, -1, -1)
} else {
var resultReg = -1
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)
} else {
resultReg = codeGen.registers.nextFree()
addInstr(result, IRInstruction(Opcode.CALLRVAL, codeGen.irType(fcall.type), reg1=resultReg, labelSymbol=fcall.name), null)
}
ExpressionCodeResult(result, codeGen.irType(fcall.type), resultReg, resultFpReg)
}
else if(fcall.type==DataType.FLOAT)
ExpressionCodeResult(result, returnRegSpec!!.dt, -1, returnRegSpec.registerNum)
else
ExpressionCodeResult(result, returnRegSpec!!.dt, returnRegSpec.registerNum, -1)
}
is StRomSub -> {
val result = mutableListOf<IRCodeChunkBase>()
// assign the arguments
val argRegisters = mutableListOf<FunctionCallArgs.ArgumentSpec>()
for ((arg, parameter) in fcall.args.zip(callTarget.parameters)) {
val paramDt = codeGen.irType(parameter.type)
val paramRegStr = if(parameter.register.registerOrPair!=null) parameter.register.registerOrPair.toString() else parameter.register.statusflag.toString()
if(codeGen.isZero(arg)) {
addInstr(result, IRInstruction(Opcode.STOREZCPU, paramDt, labelSymbol = paramRegStr), null)
val paramDt = irType(parameter.type)
val tr = translateExpression(arg)
if(paramDt==IRDataType.FLOAT)
argRegisters.add(FunctionCallArgs.ArgumentSpec("", null, FunctionCallArgs.RegSpec(IRDataType.FLOAT, tr.resultFpReg, parameter.register)))
else
argRegisters.add(FunctionCallArgs.ArgumentSpec("", null, FunctionCallArgs.RegSpec(paramDt, tr.resultReg, parameter.register)))
result += tr.chunks
}
// return value
val returnRegSpec = if(fcall.void) null else {
if(callTarget.returns.isEmpty())
null
else if(callTarget.returns.size==1) {
val returns = callTarget.returns[0]
val returnIrType = irType(returns.type)
if(returnIrType==IRDataType.FLOAT)
FunctionCallArgs.RegSpec(returnIrType, codeGen.registers.nextFreeFloat(), returns.register)
else
FunctionCallArgs.RegSpec(returnIrType, codeGen.registers.nextFree(), returns.register)
} else {
if (paramDt == IRDataType.FLOAT)
throw AssemblyError("doesn't support float register argument in asm romsub")
val tr = translateExpression(arg)
addToResult(result, tr, tr.resultReg, -1)
addInstr(result, IRInstruction(Opcode.STORECPU, paramDt, reg1 = tr.resultReg, labelSymbol = paramRegStr), null)
// multiple return values: take the first *register* (not status flag) return value and ignore the rest.
val returns = callTarget.returns.first { it.register.registerOrPair!=null }
val returnIrType = irType(returns.type)
if(returnIrType==IRDataType.FLOAT)
FunctionCallArgs.RegSpec(returnIrType, codeGen.registers.nextFreeFloat(), returns.register)
else
FunctionCallArgs.RegSpec(returnIrType, codeGen.registers.nextFree(), returns.register)
}
}
// 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)
val resultReg = codeGen.registers.nextFree()
if(!fcall.void) {
when(callTarget.returns.size) {
0 -> throw AssemblyError("expect a return value")
1 -> {
if(fcall.type==DataType.FLOAT)
throw AssemblyError("doesn't support float register result in asm romsub")
val returns = callTarget.returns.single()
val regStr = if(returns.register.registerOrPair!=null) returns.register.registerOrPair.toString() else returns.register.statusflag.toString()
addInstr(result, IRInstruction(Opcode.LOADCPU, codeGen.irType(fcall.type), reg1=resultReg, labelSymbol = regStr), null)
}
else -> {
val returnRegister = callTarget.returns.singleOrNull{ it.register.registerOrPair!=null }
if(returnRegister!=null) {
// we skip the other values returned in the status flags.
val regStr = returnRegister.register.registerOrPair.toString()
addInstr(result, IRInstruction(Opcode.LOADCPU, codeGen.irType(fcall.type), reg1=resultReg, labelSymbol = regStr), null)
} else {
val firstReturnRegister = callTarget.returns.firstOrNull{ it.register.registerOrPair!=null }
if(firstReturnRegister!=null) {
// we just take the first register return value and ignore the rest.
val regStr = firstReturnRegister.register.registerOrPair.toString()
addInstr(result, IRInstruction(Opcode.LOADCPU, codeGen.irType(fcall.type), reg1=resultReg, labelSymbol = regStr), null)
} else {
throw AssemblyError("invalid number of return values from call")
}
}
}
}
}
return ExpressionCodeResult(result, if(fcall.void) IRDataType.BYTE else codeGen.irType(fcall.type), resultReg, -1)
// create the call
val call =
if(callTarget.address==null)
IRInstruction(Opcode.CALL, labelSymbol = fcall.name, fcallArgs = FunctionCallArgs(argRegisters, returnRegSpec))
else
IRInstruction(Opcode.CALL, address = callTarget.address!!.toInt(), fcallArgs = FunctionCallArgs(argRegisters, returnRegSpec))
addInstr(result, call, null)
return if(fcall.void)
ExpressionCodeResult(result, IRDataType.BYTE, -1, -1)
else if(fcall.type==DataType.FLOAT)
ExpressionCodeResult(result, returnRegSpec!!.dt, -1, returnRegSpec.registerNum)
else
ExpressionCodeResult(result, returnRegSpec!!.dt, returnRegSpec.registerNum, -1)
}
else -> throw AssemblyError("invalid node type")
}
@ -454,7 +436,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 +446,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 +478,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 +488,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 +515,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 +590,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 +607,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 +624,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 +642,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 +688,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 +771,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 +791,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 +826,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 +854,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 +874,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 +886,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 +905,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 +926,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 +953,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 +966,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 +980,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 +989,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 +997,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 +1006,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 +1020,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 +1029,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 +1037,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 +1046,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 +1060,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 +1069,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 +1081,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 +1089,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 +1102,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 +1117,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 +1135,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 +1218,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 +1238,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 +1269,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 +1295,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)

View File

@ -1,10 +1,39 @@
package prog8.codegen.intermediate
import prog8.code.core.IErrorReporter
import prog8.intermediate.*
internal class IRPeepholeOptimizer(private val irprog: IRProgram) {
fun optimize() {
class IRPeepholeOptimizer(private val irprog: IRProgram) {
fun optimize(optimizationsEnabled: Boolean, errors: IErrorReporter) {
if(!optimizationsEnabled)
return optimizeOnlyJoinChunks()
peepholeOptimize()
val remover = IRUnusedCodeRemover(irprog, errors)
var totalRemovals = 0
do {
val numRemoved = remover.optimize()
totalRemovals += numRemoved
} while(numRemoved>0 && errors.noErrors())
errors.report()
if(totalRemovals>0) {
irprog.linkChunks() // re-link again.
}
}
private fun optimizeOnlyJoinChunks() {
irprog.blocks.asSequence().flatMap { it.children.filterIsInstance<IRSubroutine>() }.forEach { sub ->
joinChunks(sub)
removeEmptyChunks(sub)
joinChunks(sub)
}
irprog.linkChunks() // re-link
}
private fun peepholeOptimize() {
irprog.blocks.asSequence().flatMap { it.children.filterIsInstance<IRSubroutine>() }.forEach { sub ->
joinChunks(sub)
removeEmptyChunks(sub)
joinChunks(sub)
sub.chunks.withIndex().forEach { (index, chunk1) ->
@ -28,7 +57,7 @@ internal class IRPeepholeOptimizer(private val irprog: IRProgram) {
removeEmptyChunks(sub)
}
irprog.linkChunks() // re-link
irprog.linkChunks() // re-link
}
private fun removeEmptyChunks(sub: IRSubroutine) {
@ -38,7 +67,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 +92,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
}
}
}
@ -85,7 +114,7 @@ internal class IRPeepholeOptimizer(private val irprog: IRProgram) {
if(sub.chunks.isEmpty())
return
fun mayJoin(previous: IRCodeChunkBase, chunk: IRCodeChunkBase): Boolean {
fun mayJoinCodeChunks(previous: IRCodeChunkBase, chunk: IRCodeChunkBase): Boolean {
if(chunk.label!=null)
return false
if(previous is IRCodeChunk && chunk is IRCodeChunk) {
@ -102,12 +131,39 @@ internal class IRPeepholeOptimizer(private val irprog: IRProgram) {
chunks += sub.chunks[0]
for(ix in 1 until sub.chunks.size) {
val lastChunk = chunks.last()
if(mayJoin(lastChunk, sub.chunks[ix])) {
lastChunk.instructions += sub.chunks[ix].instructions
lastChunk.next = sub.chunks[ix].next
val candidate = sub.chunks[ix]
when(candidate) {
is IRCodeChunk -> {
if(mayJoinCodeChunks(lastChunk, candidate)) {
lastChunk.instructions += candidate.instructions
lastChunk.next = candidate.next
}
else
chunks += candidate
}
is IRInlineAsmChunk -> {
if(candidate.label!=null)
chunks += candidate
else if(lastChunk.isEmpty()) {
val label = lastChunk.label
if(label!=null)
chunks += IRInlineAsmChunk(label, candidate.assembly, candidate.isIR, candidate.next)
else
chunks += candidate
}
}
is IRInlineBinaryChunk -> {
if(candidate.label!=null)
chunks += candidate
else if(lastChunk.isEmpty()) {
val label = lastChunk.label
if(label!=null)
chunks += IRInlineBinaryChunk(label, candidate.data, candidate.next)
else
chunks += candidate
}
}
}
else
chunks += sub.chunks[ix]
}
sub.chunks.clear()
sub.chunks += chunks
@ -176,7 +232,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)
@ -194,14 +250,16 @@ 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)
chunk.instructions.removeAt(idx)
changed = true
}
}
// This can no longer be done here on the IR level, with the current CALL opcode that encodes the full subroutine call setup.
// If machine code is ever generated from this IR, *that* should possibly optimize the JSR + RTS into a JMP.
// if(idx>0 && ins.opcode==Opcode.RETURN) {
// val previous = chunk.instructions[idx-1]
// if(previous.opcode==Opcode.CALL) {
// chunk.instructions[idx-1] = IRInstruction(Opcode.JUMP, address = previous.address, labelSymbol = previous.labelSymbol, branchTarget = previous.branchTarget)
// chunk.instructions.removeAt(idx)
// changed = true
// }
// }
}
return changed
}
@ -212,47 +270,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
}

View File

@ -5,35 +5,18 @@ import prog8.code.core.SourceCode.Companion.libraryFilePrefix
import prog8.intermediate.*
internal class IRUnusedCodeRemover(private val irprog: IRProgram, private val errors: IErrorReporter) {
class IRUnusedCodeRemover(
private val irprog: IRProgram,
private val errors: IErrorReporter
) {
fun optimize(): Int {
val allLabeledChunks = mutableMapOf<String, IRCodeChunkBase>()
irprog.blocks.asSequence().flatMap { it.children.filterIsInstance<IRSubroutine>() }.forEach { sub ->
sub.chunks.forEach { chunk ->
chunk.label?.let { allLabeledChunks[it] = chunk }
}
}
var numRemoved = removeSimpleUnlinked(allLabeledChunks) + removeUnreachable(allLabeledChunks)
// remove empty subs
irprog.blocks.forEach { block ->
block.children.filterIsInstance<IRSubroutine>().reversed().forEach { sub ->
if(sub.isEmpty()) {
if(!sub.position.file.startsWith(libraryFilePrefix)) {
errors.warn("unused subroutine ${sub.label}", sub.position)
}
block.children.remove(sub)
numRemoved++
}
}
}
var numRemoved = removeUnusedSubroutines() + removeUnusedAsmSubroutines()
// remove empty blocks
irprog.blocks.reversed().forEach { block ->
if(block.isEmpty()) {
irprog.blocks.remove(block)
irprog.st.removeTree(block.label)
numRemoved++
}
}
@ -41,8 +24,106 @@ internal class IRUnusedCodeRemover(private val irprog: IRProgram, private val er
return numRemoved
}
private fun removeUnusedSubroutines(): Int {
val allLabeledChunks = mutableMapOf<String, IRCodeChunkBase>()
irprog.blocks.asSequence().flatMap { it.children.filterIsInstance<IRSubroutine>() }.forEach { sub ->
sub.chunks.forEach { chunk ->
chunk.label?.let { allLabeledChunks[it] = chunk }
}
}
var numRemoved = removeSimpleUnlinked(allLabeledChunks) + removeUnreachable(allLabeledChunks)
irprog.blocks.forEach { block ->
block.children.filterIsInstance<IRSubroutine>().reversed().forEach { sub ->
if(sub.isEmpty()) {
if(!sub.position.file.startsWith(libraryFilePrefix)) {
errors.warn("unused subroutine ${sub.label}", sub.position)
}
block.children.remove(sub)
irprog.st.removeTree(sub.label)
numRemoved++
}
}
}
return numRemoved
}
private fun removeUnusedAsmSubroutines(): Int {
val allLabeledAsmsubs = irprog.blocks.asSequence().flatMap { it.children.filterIsInstance<IRAsmSubroutine>() }
.associateBy { it.label }
var numRemoved = removeSimpleUnlinkedAsmsubs(allLabeledAsmsubs)
irprog.blocks.forEach { block ->
block.children.filterIsInstance<IRAsmSubroutine>().reversed().forEach { sub ->
if(sub.isEmpty()) {
if(!sub.position.file.startsWith(libraryFilePrefix)) {
errors.warn("unused asmsubroutine ${sub.label}", sub.position)
}
block.children.remove(sub)
irprog.st.removeTree(sub.label)
numRemoved++
}
}
}
return numRemoved
}
private fun removeSimpleUnlinkedAsmsubs(allSubs: Map<String, IRAsmSubroutine>): Int {
val linkedAsmSubs = mutableSetOf<IRAsmSubroutine>()
// TODO: asmsubs in library modules are never removed, we can't really tell here if they're actually being called or not...
// check if asmsub is called from another asmsub
irprog.blocks.asSequence().forEach { block ->
block.children.filterIsInstance<IRAsmSubroutine>().forEach { sub ->
if (block.forceOutput || block.library)
linkedAsmSubs += sub
if (sub.asmChunk.isNotEmpty()) {
allSubs.forEach { (label, asmsub) ->
if (sub.asmChunk.assembly.contains(label))
linkedAsmSubs += asmsub
}
}
val inlineAsm = sub.asmChunk.next as? IRInlineAsmChunk
if(inlineAsm!=null) {
allSubs.forEach { (label, asmsub) ->
if (inlineAsm.assembly.contains(label))
linkedAsmSubs += asmsub
}
}
}
}
// check if asmsub is linked or called from another regular subroutine
irprog.blocks.asSequence().flatMap { it.children.filterIsInstance<IRSubroutine>() }.forEach { sub ->
sub.chunks.forEach { chunk ->
chunk.instructions.forEach {
it.labelSymbol?.let { label -> allSubs[label]?.let { cc -> linkedAsmSubs += cc } }
// note: branchTarget can't yet point to another IRAsmSubroutine, so do nothing when it's set
}
}
}
return removeUnlinkedAsmsubs(linkedAsmSubs)
}
private fun removeUnlinkedAsmsubs(linkedAsmSubs: Set<IRAsmSubroutine>): Int {
var numRemoved = 0
irprog.blocks.asSequence().forEach { block ->
block.children.withIndex().reversed().forEach { (index, child) ->
if(child is IRAsmSubroutine && child !in linkedAsmSubs) {
block.children.removeAt(index)
numRemoved++
}
}
}
return numRemoved
}
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() {
@ -92,7 +173,7 @@ internal class IRUnusedCodeRemover(private val irprog: IRProgram, private val er
}
private fun removeUnlinkedChunks(
linkedChunks: MutableSet<IRCodeChunkBase>
linkedChunks: Set<IRCodeChunkBase>
): Int {
var numRemoved = 0
irprog.blocks.asSequence().flatMap { it.children.filterIsInstance<IRSubroutine>() }.forEach { sub ->

View File

@ -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
}
}

View File

@ -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

View File

@ -8,7 +8,7 @@ import prog8.intermediate.*
class TestIRPeepholeOpt: FunSpec({
fun makeIRProgram(chunks: List<IRCodeChunkBase>): IRProgram {
require(chunks.first().label=="main.start")
val block = IRBlock("main", null, IRBlock.BlockAlignment.NONE, Position.DUMMY)
val block = IRBlock("main", null, false, false, IRBlock.BlockAlignment.NONE, Position.DUMMY)
val sub = IRSubroutine("main.start", emptyList(), null, Position.DUMMY)
chunks.forEach { sub += it }
block += sub
@ -40,13 +40,13 @@ 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)
))
irProg.chunks().single().instructions.size shouldBe 3
val opt = IRPeepholeOptimizer(irProg)
opt.optimize()
opt.optimize(true, ErrorReporterForTests())
irProg.chunks().single().instructions.size shouldBe 1
}
@ -65,7 +65,7 @@ class TestIRPeepholeOpt: FunSpec({
irProg.chunks().size shouldBe 4
irProg.chunks().flatMap { it.instructions }.size shouldBe 5
val opt = IRPeepholeOptimizer(irProg)
opt.optimize()
opt.optimize(true, ErrorReporterForTests())
irProg.chunks().size shouldBe 4
irProg.chunks()[0].label shouldBe "main.start"
irProg.chunks()[1].label shouldBe "label"
@ -92,7 +92,7 @@ class TestIRPeepholeOpt: FunSpec({
))
irProg.chunks().single().instructions.size shouldBe 6
val opt = IRPeepholeOptimizer(irProg)
opt.optimize()
opt.optimize(true, ErrorReporterForTests())
val instr = irProg.chunks().single().instructions
instr.size shouldBe 1
instr[0].opcode shouldBe Opcode.CLC
@ -107,7 +107,7 @@ class TestIRPeepholeOpt: FunSpec({
))
irProg.chunks().single().instructions.size shouldBe 4
val opt = IRPeepholeOptimizer(irProg)
opt.optimize()
opt.optimize(true, ErrorReporterForTests())
val instr = irProg.chunks().single().instructions
instr.size shouldBe 1
instr[0].opcode shouldBe Opcode.LOADR
@ -117,31 +117,31 @@ 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)
opt.optimize()
opt.optimize(true, ErrorReporterForTests())
irProg.chunks().single().instructions.size shouldBe 4
}
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)
opt.optimize()
opt.optimize(true, ErrorReporterForTests())
val instr = irProg.chunks().single().instructions
instr.size shouldBe 2
instr[0].opcode shouldBe Opcode.INC
@ -150,40 +150,40 @@ 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)
opt.optimize()
opt.optimize(true, ErrorReporterForTests())
irProg.chunks().single().instructions.size shouldBe 4
}
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)
opt.optimize()
opt.optimize(true, ErrorReporterForTests())
val instr = irProg.chunks().single().instructions
instr.size shouldBe 4
instr[0].opcode shouldBe Opcode.LOAD
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
}
})

View File

@ -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 ir-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, setOf(CpuRegister.Y), 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
}
})

View File

@ -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))
}

View File

@ -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

View File

@ -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"
}

View File

@ -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
@ -311,7 +310,7 @@ asmsub set_irq(uword handler @AY, ubyte useKernal @Pc) clobbers(A) {
sta _modified+1
sty _modified+2
lda #0
adc #0
rol a
sta _use_kernal
sei
lda #<_irq_handler
@ -405,7 +404,7 @@ asmsub set_rasterirq(uword handler @AY, uword rasterpos @R0, ubyte useKernal @P
sta _modified+1
sty _modified+2
lda #0
adc #0
rol a
sta set_irq._use_kernal
lda cx16.r0
ldy cx16.r0+1
@ -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

View File

@ -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)
@ -355,7 +356,7 @@ asmsub set_irq(uword handler @AY, ubyte useKernal @Pc) clobbers(A) {
sta _modified+1
sty _modified+2
lda #0
adc #0
rol a
sta _use_kernal
sei
lda #<_irq_handler
@ -449,7 +450,7 @@ asmsub set_rasterirq(uword handler @AY, uword rasterpos @R0, ubyte useKernal @P
sta _modified+1
sty _modified+2
lda #0
adc #0
rol a
sta set_irq._use_kernal
lda cx16.r0
ldy cx16.r0+1

View File

@ -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

View File

@ -22,6 +22,10 @@ psg {
; waveform = one of PULSE,SAWTOOTH,TRIANGLE,NOISE.
; pulsewidth = 0-63. Specifies the pulse width for waveform=PULSE.
envelope_states[voice_num] = 255
%asm {{
php
sei
}}
cx16.r0 = $f9c2 + voice_num * 4
cx16.VERA_CTRL = 0
cx16.VERA_ADDR_L = lsb(cx16.r0)
@ -32,6 +36,9 @@ psg {
cx16.VERA_DATA0 = waveform | pulsewidth
envelope_volumes[voice_num] = mkword(volume, 0)
envelope_maxvolumes[voice_num] = volume
%asm {{
plp
}}
}
; sub freq_hz(ubyte voice_num, float hertz) {
@ -44,48 +51,54 @@ 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
%asm {{
php
sei
}}
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)
%asm {{
plp
}}
}
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 +107,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,11 +116,11 @@ 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 or call it yourself!
; cx16.r0 = the volume word (volume scaled by 256)
; cx16.r1L = the voice number
; cx16.r2L = attack value
pushw(cx16.r0)
push(cx16.r1L)
push(cx16.r2L)
@ -148,7 +160,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 +172,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)

View File

@ -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 -----
@ -712,7 +697,7 @@ asmsub set_irq(uword handler @AY, ubyte useKernal @Pc) clobbers(A) {
sta _modified+1
sty _modified+2
lda #0
adc #0
rol a
sta _use_kernal
sei
lda #<_irq_handler
@ -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
@ -799,6 +784,7 @@ asmsub push_vera_context() clobbers(A) {
lda cx16.VERA_CTRL
sta _vera_storage+3
eor #1
sta _vera_storage+7
sta cx16.VERA_CTRL
lda cx16.VERA_ADDR_L
sta _vera_storage+4
@ -806,31 +792,29 @@ asmsub push_vera_context() clobbers(A) {
sta _vera_storage+5
lda cx16.VERA_ADDR_H
sta _vera_storage+6
lda cx16.VERA_CTRL
sta _vera_storage+7
rts
_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 +901,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

View File

@ -292,7 +292,7 @@ str = P8ZP_SCRATCH_W1
sta modify_pattern2+2
jsr _match
lda #0
adc #0
rol a
ldx P8ZP_SCRATCH_REG
rts

View File

@ -196,9 +196,9 @@ 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
syscall 11
returnreg.w r0
loadm.w r65535,conv.str2uword.string
syscall 11 (r65535.w) : r0.w
returnr.w r0
}}
}
@ -207,9 +207,9 @@ 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
syscall 12
returnreg.w r0
loadm.w r65535,conv.str2word.string
syscall 12 (r65535.w) : r0.w
returnr.w r0
}}
}

View File

@ -10,8 +10,8 @@ floats {
sub print_f(float value) {
; ---- prints the floating point value (without a newline).
%ir {{
loadm.f fr65500,floats.print_f.value
syscall 25
loadm.f fr65535,floats.print_f.value
syscall 25 (fr65535.f)
return
}}
}
@ -21,7 +21,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 +29,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 +37,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 +45,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 +53,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 +61,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 +69,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 +77,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 +85,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 +103,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 +111,7 @@ sub floor(float value) -> float {
%ir {{
loadm.f fr0,floats.floor.value
ffloor.f fr0,fr0
returnreg.f fr0
returnr.f fr0
}}
}
@ -120,21 +120,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
syscall 35 () : fr0.f
returnr.f fr0
}}
}
sub rndseedf(float seed) {
%ir {{
loadm.f fr65500,floats.rndseedf.seed
syscall 32
loadm.f fr65535,floats.rndseedf.seed
syscall 32 (fr65535.f)
return
}}
}

View File

@ -161,24 +161,24 @@ math {
sub rnd() -> ubyte {
%ir {{
syscall 33
returnreg.b r0
syscall 33 (): r0.b
returnr.b r0
}}
}
sub rndw() -> uword {
%ir {{
syscall 34
returnreg.w r0
syscall 34 (): r0.w
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
syscall 31
loadm.w r65534,math.rndseed.seed1
loadm.w r65535,math.rndseed.seed2
syscall 31 (r65534.w, r65535.w)
return
}}
}

View File

@ -84,10 +84,10 @@ 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
syscall 29
returnreg.b r0
loadm.w r65534,string.compare.st1
loadm.w r65535,string.compare.st2
syscall 29 (r65534.w, r65535.w) : r0.b
returnr.b r0
}}
}

View File

@ -8,22 +8,22 @@ sys {
sub reset_system() {
; Soft-reset the system back to initial power-on Basic prompt.
%ir {{
syscall 0
syscall 0 ()
}}
}
sub wait(uword jiffies) {
; --- wait approximately the given number of jiffies (1/60th seconds)
%ir {{
loadm.w r65500,sys.wait.jiffies
syscall 13
loadm.w r65535,sys.wait.jiffies
syscall 13 (r65535.w)
}}
}
sub waitvsync() {
; --- busy wait till the next vsync has occurred (approximately), without depending on custom irq handling.
%ir {{
syscall 14
syscall 14()
}}
}
@ -62,8 +62,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
syscall 1
loadm.b r65535,sys.exit.returnvalue
syscall 1 (r65535.b)
}}
}
@ -82,33 +82,33 @@ sys {
sub gfx_enable(ubyte mode) {
%ir {{
loadm.b r65500,sys.gfx_enable.mode
syscall 8
loadm.b r65535,sys.gfx_enable.mode
syscall 8 (r65535.b)
}}
}
sub gfx_clear(ubyte color) {
%ir {{
loadm.b r65500,sys.gfx_clear.color
syscall 9
loadm.b r65535,sys.gfx_clear.color
syscall 9 (r65535.b)
}}
}
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
syscall 10
loadm.w r65533,sys.gfx_plot.xx
loadm.w r65534,sys.gfx_plot.yy
loadm.b r65535,sys.gfx_plot.color
syscall 10 (r65533.w, r65534.w, r65535.b)
}}
}
sub gfx_getpixel(uword xx, uword yy) -> ubyte {
%ir {{
loadm.w r65500,sys.gfx_getpixel.xx
loadm.w r65501,sys.gfx_getpixel.yy
syscall 30
returnreg.b r0
loadm.w r65534,sys.gfx_getpixel.xx
loadm.w r65535,sys.gfx_getpixel.yy
syscall 30 (r65534.w, r65535.w): r0.b
returnr.b r0
}}
}
}

View File

@ -15,8 +15,8 @@ sub height() -> ubyte {
sub clear_screen() {
str @shared sequence = "\x1b[2J\x1B[H"
%ir {{
load.w r65500,txt.clear_screen.sequence
syscall 3
load.w r65535,txt.clear_screen.sequence
syscall 3 (r65535.w)
}}
}
@ -38,15 +38,15 @@ sub uppercase() {
sub chrout(ubyte char) {
%ir {{
loadm.b r65500,txt.chrout.char
syscall 2
loadm.b r65535,txt.chrout.char
syscall 2 (r65535.b)
}}
}
sub print (str text) {
%ir {{
loadm.w r65500,txt.print.text
syscall 3
loadm.w r65535,txt.print.text
syscall 3 (r65535.w)
}}
}
@ -122,9 +122,10 @@ 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
syscall 6
returnreg.b r0
loadm.w r65534,txt.input_chars.buffer
load.b r65535,80
syscall 6 (r65534.w, r65535.b): r0.b
returnr.b r0
}}
}

View File

@ -1 +1 @@
8.11
8.13

View File

@ -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)

View File

@ -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)
}
}
}

View File

@ -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 {

View File

@ -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()

View File

@ -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()) {

View File

@ -33,7 +33,7 @@ class TestLaunchEmu: FunSpec({
<INITGLOBALS>
</INITGLOBALS>
<BLOCK NAME="main" ADDRESS="" ALIGN="NONE" POS="[unittest: line 42 col 1-9]">
<BLOCK NAME="main" ADDRESS="" LIBRARY="false" FORCEOUTPUT="false" ALIGN="NONE" POS="[unittest: line 42 col 1-9]">
</BLOCK>
</PROGRAM>
""")

View File

@ -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 ; \

View File

@ -5,6 +5,7 @@ import io.kotest.core.spec.style.FunSpec
import io.kotest.matchers.shouldBe
import io.kotest.matchers.shouldNotBe
import io.kotest.matchers.string.shouldContain
import io.kotest.matchers.string.shouldNotContain
import prog8.ast.expressions.BuiltinFunctionCall
import prog8.ast.statements.Assignment
import prog8.code.target.C64Target
@ -14,7 +15,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 +212,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") {
@ -287,25 +245,26 @@ main {
val exc = shouldThrow<Exception> {
VmRunner().runProgram(virtfile.readText())
}
exc.message shouldContain("does not support real inlined assembly")
exc.message shouldContain("encountered unconverted inline assembly chunk")
}
test("inline asm for virtual target with IR is accepted") {
test("inline asm for virtual target with IR is accepted and converted to regular instructions") {
val src = """
main {
sub start() {
%ir {{
loadr.b r1,r2
return
}}
}
}"""
val othertarget = Cx16Target()
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")
VmRunner().runProgram(virtfile.readText())
val irSrc = virtfile.readText()
irSrc.shouldContain("loadr.b r1,r2")
irSrc.shouldNotContain("INLINEASM")
VmRunner().runProgram(irSrc)
}
test("addresses from labels/subroutines not yet supported in VM") {

View File

@ -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.

View File

@ -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.

View File

@ -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
^^^^^^

View File

@ -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

View File

@ -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!

View File

@ -6,25 +6,13 @@ For next minor release
...
For 9.0 major changes
^^^^^^^^^^^^^^^^^^^^^
- 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.
- 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
- Some more support for (64tass) SEGMENTS ?
- 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)
just initialize them yourself in start() if you need a non-zero value .
- OR.... do all this automatically if 'golden' is enabled as a compiler option? So compiler allocates in ZP first, then Golden Ram, then regular ram
- OR.... make all this more generic and use some %segment option to create real segments for 64tass?
- (need separate step in codegen and IR to write the "golden" variables)
For 9.0 major changes are being made in the "version_9" branch. Look there.
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
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 +21,30 @@ 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)
- try to optimize newexpr a bit more? Although maybe just spend effort on a new codegen based on the IR.
- 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 +64,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

View File

@ -15,14 +15,14 @@ main {
palette.set_rgb(amigacolors, len(amigacolors))
cx16.VERA_DC_VSCALE = 64 ; have the vertical resolution so it is 640*240 - more or less Amiga's default non interlaced mode
gfx2.text_charset(3)
gfx2.text_charset(1)
screen_titlebar()
window_workbench()
window_system()
window_shell()
gfx2.text(280, 210, 1, sc:"640x480(240) 4 colors")
gfx2.text(280, 220, 1, sc:"Mockup drawn using Prog8 gfx2 library")
gfx2.text(280, 210, 1, iso:"640x480(240) 4 colors")
gfx2.text(280, 220, 1, iso:"Mockup drawn using Prog8 gfx2 library")
repeat {
}
@ -30,7 +30,7 @@ main {
sub screen_titlebar() {
gfx2.fillrect(0, 0, gfx2.width, 10, 2)
gfx2.text(8,1, 1, sc:"AmigaOS 3.1 2,002,448 graphics mem 16,504,384 other mem")
gfx2.text(8,1, 1, iso:"AmigaOS 3.1 2,002,448 graphics mem 16,504,384 other mem")
gfx2.horizontal_line(0, 10, gfx2.width, 1)
widget.window_order_icon(gfx2.width-widget.window_order_icon.width, 0, false)
}
@ -42,7 +42,7 @@ main {
const uword width = 600
const uword height = 220
widget.window_titlebar(win_x, win_y, width, sc:"Workbench", false)
widget.window_titlebar(win_x, win_y, width, iso:"Workbench", false)
; gfx2.fillrect(win_x+3, win_y+11, width-4, height-11-2,0) ; clear window pane
widget.window_leftborder(win_x, win_y, height, false)
widget.window_bottomborder(win_x, win_y, width, height)
@ -51,8 +51,8 @@ main {
vector_v(win_x+width - 390, win_y+height-20)
vector_v(win_x+width - 390 -14, win_y+height-20)
widget.icon(45,40, sc:"Ram Disk")
widget.icon(45,90, sc:"Workbench3.1")
widget.icon(45,40, iso:"Ram Disk")
widget.icon(45,90, iso:"Workbench3.1")
}
sub vector_v(uword x, uword y) {
@ -70,17 +70,17 @@ main {
const uword win_x = 320
const uword win_y = 40
widget.window_titlebar(win_x, win_y, width, sc:"System", false)
widget.window_titlebar(win_x, win_y, width, iso:"System", false)
gfx2.fillrect(win_x+3, win_y+11, width-4, height-11-2, 0) ; clear window pane
widget.window_leftborder(win_x, win_y, height, false)
widget.window_bottomborder(win_x, win_y, width, height)
widget.window_rightborder(win_x, win_y, width, height, false)
widget.icon(win_x+16, win_y+14, sc:"FixFonts")
widget.icon(win_x+16+80, win_y+14, sc:"NoFastMem")
widget.icon(win_x+16, win_y+56, sc:"Format")
widget.icon(win_x+16+80, win_y+56, sc:"RexxMast")
widget.icon(win_x+16+160, win_y+56, sc:"Shell")
widget.icon(win_x+16, win_y+14, iso:"FixFonts")
widget.icon(win_x+16+80, win_y+14, iso:"NoFastMem")
widget.icon(win_x+16, win_y+56, iso:"Format")
widget.icon(win_x+16+80, win_y+56, iso:"RexxMast")
widget.icon(win_x+16+160, win_y+56, iso:"Shell")
}
sub window_shell() {
@ -89,14 +89,14 @@ main {
const uword width = 500
const uword height = 65
widget.window_titlebar(win_x, win_y, width, sc:"AmigaShell", true)
widget.window_titlebar(win_x, win_y, width, iso:"AmigaShell", true)
gfx2.fillrect(win_x+3, win_y+11, width-4, height-11-2,0) ; clear window pane
widget.window_leftborder(win_x, win_y, height, true)
widget.window_bottomborder(win_x, win_y, width, height)
widget.window_rightborder(win_x, win_y, width, height, true)
gfx2.text(win_x+5, win_y+12, 1, sc:"New Shell process 3")
gfx2.text(win_x+5, win_y+12+8, 1, sc:"3.Workbench3.1:>")
gfx2.text(win_x+5, win_y+12, 1, iso:"New Shell process 3")
gfx2.text(win_x+5, win_y+12+8, 1, iso:"3.Workbench3.1:>")
gfx2.fillrect(win_x+5+17*8, win_y+12+8, 8, 8, 1) ; cursor
}
}

View File

@ -34,7 +34,7 @@ main {
irq {
const ubyte BAR_Y_OFFSET = 6
const ubyte BAR_Y_OFFSET = 5
uword next_irq_line = 0
ubyte anim1 = 0
ubyte av1 = 0

Binary file not shown.

View File

@ -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 {

View File

@ -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()
}
}
}

View File

@ -1,31 +1,16 @@
%import textio
%import floats
%zeropage basicsafe
main {
sub start() {
ubyte[] ba = [11,22,33]
uword[] wa = [1111,2222,3333]
float[] fa = [1.1, 2.2, 3.3]
txt.print_ub(ba[1])
txt.nl()
txt.print_uw(wa[1])
txt.nl()
floats.print_f(fa[1])
txt.nl()
sub start() {
ubyte @shared foo = derp(99)
}
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()
asmsub derp(ubyte xx @Y) -> ubyte @ A {
%asm {{
rts
}}
}
}

View File

@ -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

View File

@ -68,6 +68,7 @@ class IRFileReader {
blocks.forEach{ program.addBlock(it) }
program.linkChunks()
program.convertAsmChunks()
program.validate()
return program
@ -302,7 +303,7 @@ class IRFileReader {
if(text.isNotBlank()) {
text.lineSequence().forEach { line ->
if (line.isNotBlank() && !line.startsWith(';')) {
val result = parseIRCodeLine(line, null, mutableMapOf())
val result = parseIRCodeLine(line)
result.fold(
ifLeft = {
chunk += it
@ -336,6 +337,8 @@ class IRFileReader {
val block = IRBlock(
attrs.getValue("NAME"),
if(attrs.getValue("ADDRESS")=="") null else parseIRValue(attrs.getValue("ADDRESS")).toUInt(),
if(attrs.getValue("LIBRARY")=="") false else attrs.getValue("LIBRARY").toBoolean(),
if(attrs.getValue("FORCEOUTPUT")=="") false else attrs.getValue("FORCEOUTPUT").toBoolean(),
IRBlock.BlockAlignment.valueOf(attrs.getValue("ALIGN")),
parsePosition(attrs.getValue("POS")))
skipText(reader)
@ -501,17 +504,6 @@ class IRFileReader {
}
}
private fun parseRegisterOrStatusflag(regs: String): RegisterOrStatusflag {
var reg: RegisterOrPair? = null
var sf: Statusflag? = null
try {
reg = RegisterOrPair.valueOf(regs)
} catch (x: IllegalArgumentException) {
sf = Statusflag.valueOf(regs)
}
return RegisterOrStatusflag(reg, sf)
}
private val posPattern = Regex("\\[(.+): line (.+) col (.+)-(.+)\\]")
private fun parsePosition(strpos: String): Position {

View File

@ -55,8 +55,10 @@ 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("LIBRARY", block.library.toString())
xml.writeAttribute("FORCEOUTPUT", block.forceOutput.toString())
xml.writeAttribute("ALIGN", block.alignment.toString())
xml.writeAttribute("POS", block.position.toString())
xml.writeCharacters("\n")
@ -124,6 +126,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++
@ -137,7 +140,6 @@ class IRFileWriter(private val irProgram: IRProgram, outfileOverride: Path?) {
private fun writeInlineBytes(chunk: IRInlineBinaryChunk) {
xml.writeStartElement("BYTES")
chunk.label?.let { xml.writeAttribute("LABEL", chunk.label) }
xml.writeCharacters("\n")
chunk.data.withIndex().forEach {(index, byte) ->
xml.writeCharacters(byte.toString(16).padStart(2,'0'))
if(index and 63 == 63 && index < chunk.data.size-1)

View File

@ -1,5 +1,6 @@
package prog8.intermediate
import prog8.code.core.RegisterOrStatusflag
import prog8.code.core.toHex
/*
@ -17,31 +18,31 @@ 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)
NOTE: Labels in source text should always start with an underscore.
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
loadix reg1, reg2, pointeraddr - load reg1 with value at memory indirect, pointed to by pointeraddr indexed by value in reg2
loadr reg1, reg2 - load reg1 with value in register reg2
loadcpu reg1, cpureg - load reg1 with value from cpu register (register/registerpair/statusflag)
storem reg1, address - store reg1 at memory address
storecpu reg1, cpureg - store reg1 in cpu register (register/registerpair/statusflag)
storei reg1, reg2 - store reg1 at memory indirect, memory pointed to by reg2
storex reg1, reg2, address - store reg1 at memory address, indexed by value in reg2
storeix reg1, reg2, pointeraddr - store reg1 at memory indirect, pointed to by pointeraddr indexed by value in reg2
storezm address - store zero at memory address
storezcpu cpureg - store zero in cpu register (register/registerpair/statusflag)
storezi reg1 - store zero at memory pointed to by reg1
storezx reg1, address - store zero at memory address, indexed by value in reg
@ -50,70 +51,82 @@ 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
call label(argument register list) [: resultreg.type]
- calls a subroutine with the given arguments and return value (optional).
save current instruction location+1, continue execution at instruction nr of the label.
the argument register list is positional and includes the datatype, ex.: r4.b,r5.w,fp1.f
If the call is to a rom-routine, 'label' will be a hexadecimal address instead such as $ffd2
If the arguments should be passed in CPU registers, they'll have a @REGISTER postfix.
For example: call $ffd2(r5.b@A)
syscall number (argument register list) [: resultreg.type]
- do a systemcall identified by number, result value(s) are pushed on value stack by the syscall code so
will be POPped off into the given resultregister if any.
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
--------------------------
All have type b or w except the branches that only check status bits.
bstcc address - branch to location if Status bit Carry is Clear
bstcs address - branch to location if Status bit Carry is Set
bstcc address - branch to location if Status bit Carry is clear
bstcs address - branch to location if Status bit Carry is set
bstne address - branch to location if Status bit Zero is clear
bsteq address - branch to location if Status bit Zero is set
bstne address - branch to location if Status bit Zero is not set
bstneg address - branch to location if Status bit Negative is not set
bstpos address - branch to location if Status bit Negative is not set
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.)
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
sne reg1, reg2 - set reg1=1 if reg1 != reg2, otherwise set reg1=0
slt reg1, reg2 - set reg1=1 if reg1 < reg2 (unsigned), otherwise set reg1=0
slts reg1, reg2 - set reg1=1 if reg1 < reg2 (signed), otherwise set reg1=0
sle reg1, reg2 - set reg1=1 if reg1 <= reg2 (unsigned), otherwise set reg1=0
sles reg1, reg2 - set reg1=1 if reg1 <= reg2 (signed), otherwise set reg1=0
sgt reg1, reg2 - set reg1=1 if reg1 > reg2 (unsigned), otherwise set reg1=0
sgts reg1, reg2 - set reg1=1 if reg1 > reg2 (signed), otherwise set reg1=0
sge reg1, reg2 - set reg1=1 if reg1 >= reg2 (unsigned), otherwise set reg1=0
sges reg1, reg2 - set reg1=1 if reg1 >= reg2 (signed), otherwise set reg1=0
bstpos address - branch to location if Status bit Negative is clear
bstneg address - branch to location if Status bit Negative is set
bstvc address - branch to location if Status bit Overflow is clear
bstvs address - branch to location if Status bit Overflow is set
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
bgt reg1, value, address - jump to location in program given by location, if reg1 > immediate value (unsigned)
bgts reg1, value, address - jump to location in program given by location, if reg1 > immediate value (signed)
bgtr reg1, reg2, address - jump to location in program given by location, if reg1 > reg2 (unsigned)
bgtsr reg1, reg2, address - jump to location in program given by location, if reg1 > reg2 (signed)
blt reg1, value, address - jump to location in program given by location, if reg1 < immediate value (unsigned)
blts reg1, value, address - jump to location in program given by location, if reg1 < immediate value (signed)
bge reg1, value, address - jump to location in program given by location, if reg1 >= immediate value (unsigned)
bges 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)
bgesr reg1, reg2, address - jump to location in program given by location, if reg1 >= reg2 (signed)
ble reg1, value, address - jump to location in program given by location, if reg1 <= immediate value (unsigned)
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.b if reg2==0, otherwise set reg1=0.b
snz reg1, reg2 - set reg1=1.b if reg2!=0, otherwise set reg1=0.b
seq reg1, reg2 - set reg1=1.b if reg1 == reg2, otherwise set reg1=0.b
sne reg1, reg2 - set reg1=1.b if reg1 != reg2, otherwise set reg1=0.b
slt reg1, reg2 - set reg1=1.b if reg1 < reg2 (unsigned), otherwise set reg1=0.b
slts reg1, reg2 - set reg1=1.b if reg1 < reg2 (signed), otherwise set reg1=0.b
sle reg1, reg2 - set reg1=1.b if reg1 <= reg2 (unsigned), otherwise set reg1=0.b
sles reg1, reg2 - set reg1=1.b if reg1 <= reg2 (signed), otherwise set reg1=0.b
sgt reg1, reg2 - set reg1=1.b if reg1 > reg2 (unsigned), otherwise set reg1=0.b
sgts reg1, reg2 - set reg1=1.b if reg1 > reg2 (signed), otherwise set reg1=0.b
sge reg1, reg2 - set reg1=1.b if reg1 >= reg2 (unsigned), otherwise set reg1=0.b
sges reg1, reg2 - set reg1=1.b if reg1 >= reg2 (signed), otherwise set reg1=0.b
ARITHMETIC
----------
All have type b or w or f. Note: result types are the same as operand types! E.g. byte*byte->byte.
ext reg1 - reg1 = unsigned extension of reg1 (which in practice just means clearing the MSB / MSW) (ext.w not yet implemented as we don't have longs yet)
exts reg1 - reg1 = signed extension of reg1 (byte to word, or word to long) (note: ext.w is not yet implemented as we don't have longs yet)
ext reg1 - reg1 = unsigned extension of reg1 (which in practice just means clearing the MSB / MSW) (ext.w not yet implemented as we don't have longs yet)
inc reg1 - reg1 = reg1+1
incm address - memory at address += 1
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 +138,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,9 +217,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
binarydata - 'instruction' to hold inlined binary data bytes
push [b, w, f] reg1 - push value in reg1 on the stack
pop [b, w, f] reg1 - pop value from stack into reg1
*/
enum class Opcode {
@ -217,24 +229,20 @@ enum class Opcode {
LOADX,
LOADIX,
LOADR,
LOADCPU,
STOREM,
STORECPU,
STOREI,
STOREX,
STOREIX,
STOREZM,
STOREZCPU,
STOREZI,
STOREZX,
JUMP,
JUMPA,
CALL,
CALLRVAL,
SYSCALL,
RETURN,
RETURNREG,
RETURNR,
BSTCC,
BSTCS,
@ -244,18 +252,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,
@ -359,24 +371,22 @@ enum class Opcode {
POP,
MSIG,
CONCAT,
BREAKPOINT,
BINARYDATA
BREAKPOINT
}
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.SYSCALL,
Opcode.BSTCC,
Opcode.BSTCS,
@ -386,81 +396,22 @@ val OpcodesThatBranch = setOf(
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
)
val OpcodesForCpuRegisters = setOf(
Opcode.LOADCPU,
Opcode.STORECPU,
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.BEQR,
Opcode.BEQ,
Opcode.BNER,
Opcode.BNE,
Opcode.BGTR,
Opcode.BGT,
Opcode.BLT,
Opcode.BGTSR,
Opcode.BGTS,
Opcode.BLTS,
Opcode.BGER,
Opcode.BGE,
Opcode.BLE,
Opcode.BGESR,
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
Opcode.BLES
)
enum class IRDataType {
@ -482,9 +433,10 @@ 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,
val funcCall: Boolean,
val sysCall: Boolean) {
companion object {
fun from(spec: String): Map<IRDataType?, InstructionFormat> {
val result = mutableMapOf<IRDataType?, InstructionFormat>()
@ -493,37 +445,41 @@ 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()
var funcCall = false
var sysCall = false
while(splits.hasNext()) {
when(splits.next()) {
"<r1" -> { reg1=OperandDirection.READ }
">r1" -> { reg1=OperandDirection.WRITE }
"<>r1" -> { reg1=OperandDirection.READWRITE }
"<r1" -> reg1 = OperandDirection.READ
">r1" -> reg1 = OperandDirection.WRITE
"<>r1" -> reg1 = OperandDirection.READWRITE
"<r2" -> reg2 = OperandDirection.READ
"<fr1" -> { fpreg1=OperandDirection.READ }
">fr1" -> { fpreg1=OperandDirection.WRITE }
"<>fr1" -> { fpreg1=OperandDirection.READWRITE }
"<fr1" -> fpreg1 = OperandDirection.READ
">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
"call" -> funcCall = true
"syscall" -> sysCall = true
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, funcCall, sysCall)
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, funcCall, sysCall)
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, funcCall, sysCall)
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, funcCall, sysCall)
}
return result
}
@ -534,54 +490,57 @@ 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.STORECPU to InstructionFormat.from("BW,<r1"),
Opcode.STOREM to InstructionFormat.from("BW,<r1,>a | F,<fr1,>a"),
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.STOREZCPU to InstructionFormat.from("BW"),
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.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.CALL to InstructionFormat.from("N,call"),
Opcode.SYSCALL to InstructionFormat.from("N,syscall"),
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 +554,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,16 +637,23 @@ 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"),
Opcode.BREAKPOINT to InstructionFormat.from("N"),
Opcode.BINARYDATA to InstructionFormat.from("N"),
)
class FunctionCallArgs(
var arguments: List<ArgumentSpec>,
val returns: RegSpec?
) {
class RegSpec(val dt: IRDataType, val registerNum: Int, val cpuRegister: RegisterOrStatusflag?)
class ArgumentSpec(val name: String, val address: Int?, val reg: RegSpec)
}
data class IRInstruction(
val opcode: Opcode,
val type: IRDataType?=null,
@ -695,11 +661,12 @@ 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 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
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 address (so only for Branch/jump/call Instructions!)
var branchTarget: IRCodeChunkBase? = null, // Will be linked after loading in IRProgram.linkChunks()! This is the chunk that the branch labelSymbol points to.
val fcallArgs: FunctionCallArgs? = null // will be set for the CALL and SYSCALL instructions.
) {
// reg1 and fpreg1 can be IN/OUT/INOUT (all others are readonly INPUT)
// This knowledge is useful in IL assembly optimizers to see how registers are used.
@ -715,17 +682,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"
}
val formats = instructionFormats.getValue(opcode)
require (type != null || formats.containsKey(null)) { "missing type" }
@ -739,65 +695,108 @@ 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)
require(immediateFp !=null) {"missing immediate fp value"}
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,
Opcode.SEQ, Opcode.SNE, Opcode.SLT, Opcode.SLTS,
Opcode.SGT, Opcode.SGTS, Opcode.SLE, Opcode.SLES,
Opcode.SGE, Opcode.SGES)) {
if(type==IRDataType.FLOAT)
require(fpReg1!=fpReg2) {"$opcode: fpReg1 and fpReg2 should be different"}
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")
}
}
@ -811,32 +810,84 @@ data class IRInstruction(
IRDataType.FLOAT -> result.add(".f ")
else -> result.add(" ")
}
reg1?.let {
result.add("r$it")
result.add(",")
}
reg2?.let {
result.add("r$it")
result.add(",")
}
fpReg1?.let {
result.add("fr$it")
result.add(",")
}
fpReg2?.let {
result.add("fr$it")
result.add(",")
}
value?.let {
result.add(it.toHex())
result.add(",")
}
fpValue?.let {
result.add(it.toString())
result.add(",")
}
labelSymbol?.let {
result.add(it)
if(this.fcallArgs!=null) {
immediate?.let { result.add(it.toHex()) } // syscall
labelSymbol?.let { result.add(it) } // regular subroutine call
address?.let { result.add(address.toHex()) } // romcall
result.add("(")
fcallArgs.arguments.forEach {
val location = if(it.address==null) {
if(it.name.isBlank()) "" else it.name+"="
} else "${it.address}="
val cpuReg = if(it.reg.cpuRegister==null) "" else {
if(it.reg.cpuRegister.registerOrPair!=null)
"@"+it.reg.cpuRegister.registerOrPair.toString()
else
"@"+it.reg.cpuRegister.statusflag.toString()
}
when(it.reg.dt) {
IRDataType.BYTE -> result.add("${location}r${it.reg.registerNum}.b$cpuReg,")
IRDataType.WORD -> result.add("${location}r${it.reg.registerNum}.w$cpuReg,")
IRDataType.FLOAT -> result.add("${location}fr${it.reg.registerNum}.f$cpuReg,")
}
}
if(result.last().endsWith(',')) {
result.add(result.removeLast().trimEnd(','))
}
result.add(")")
val returns = fcallArgs.returns
if(returns!=null) {
result.add(":")
when (returns.dt) {
IRDataType.BYTE -> result.add("r${returns.registerNum}.b")
IRDataType.WORD -> result.add("r${returns.registerNum}.w")
IRDataType.FLOAT -> result.add("fr${returns.registerNum}.f")
}
if(returns.cpuRegister!=null) {
val cpuReg =
if(returns.cpuRegister.registerOrPair!=null)
returns.cpuRegister.registerOrPair.toString()
else
returns.cpuRegister.statusflag.toString()
result.add("@"+cpuReg)
}
}
} else {
reg1?.let {
result.add("r$it")
result.add(",")
}
reg2?.let {
result.add("r$it")
result.add(",")
}
fpReg1?.let {
result.add("fr$it")
result.add(",")
}
fpReg2?.let {
result.add("fr$it")
result.add(",")
}
immediate?.let {
result.add(it.toHex())
result.add(",")
}
immediateFp?.let {
result.add(it.toString())
result.add(",")
}
address?.let {
result.add(it.toHex())
result.add(",")
}
labelSymbol?.let {
result.add(it)
}
}
if(result.last() == ",")
result.removeLast()

View File

@ -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...
}
@ -170,7 +170,10 @@ class IRProgram(val name: String,
fun validate() {
blocks.forEach { block ->
if(block.isNotEmpty()) {
block.children.filterIsInstance<IRInlineAsmChunk>().forEach { chunk -> require(chunk.instructions.isEmpty()) }
block.children.filterIsInstance<IRInlineAsmChunk>().forEach { chunk ->
require(chunk.instructions.isEmpty())
require(!chunk.isIR) { "inline IR-asm should have been converted into regular code chunk"}
}
block.children.filterIsInstance<IRSubroutine>().forEach { sub ->
if(sub.chunks.isNotEmpty()) {
require(sub.chunks.first().label == sub.label) { "first chunk in subroutine should have sub name (label) as its label" }
@ -179,15 +182,18 @@ class IRProgram(val name: String,
if (chunk is IRCodeChunk) {
require(chunk.instructions.isNotEmpty() || chunk.label != null)
if(chunk.instructions.lastOrNull()?.opcode in OpcodesThatJump)
require(chunk.next == null) { "chunk ending with a jump shouldn't be linked to next" }
require(chunk.next == null) { "chunk ending with a jump or return shouldn't be linked to next" }
else {
// if chunk is NOT the last in the block, it needs to link to next.
val isLast = sub.chunks.last() === chunk
require(isLast || chunk.next != null) { "chunk needs to be linked to next" }
}
}
else
else {
require(chunk.instructions.isEmpty())
if(chunk is IRInlineAsmChunk)
require(!chunk.isIR) { "inline IR-asm should have been converted into regular code chunk"}
}
chunk.instructions.forEach {
if(it.labelSymbol!=null && it.opcode in OpcodesThatBranch)
require(it.branchTarget != null) { "branching instruction to label should have branchTarget set" }
@ -199,19 +205,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,14 +239,74 @@ class IRProgram(val name: String,
}
}
}
return RegistersUsed(readRegsCounts, writeRegsCounts, readFpRegsCounts, writeFpRegsCounts, regsTypes)
}
return RegistersUsed(readRegs, writeRegs, readFpRegs, writeFpRegs)
fun convertAsmChunks() {
fun convert(asmChunk: IRInlineAsmChunk): IRCodeChunks {
val chunks = mutableListOf<IRCodeChunkBase>()
var chunk = IRCodeChunk(asmChunk.label, null)
asmChunk.assembly.lineSequence().forEach {
val parsed = parseIRCodeLine(it.trim())
parsed.fold(
ifLeft = { instruction -> chunk += instruction },
ifRight = { label ->
val lastChunk = chunk
if(chunk.isNotEmpty() || chunk.label!=null)
chunks += chunk
chunk = IRCodeChunk(label, null)
val lastInstr = lastChunk.instructions.lastOrNull()
if(lastInstr==null || lastInstr.opcode !in OpcodesThatJump)
lastChunk.next = chunk
}
)
}
if(chunk.isNotEmpty() || chunk.label!=null)
chunks += chunk
chunks.lastOrNull()?.let {
val lastInstr = it.instructions.lastOrNull()
if(lastInstr==null || lastInstr.opcode !in OpcodesThatJump)
it.next = asmChunk.next
}
return chunks
}
blocks.forEach { block ->
val chunkReplacementsInBlock = mutableListOf<Pair<IRCodeChunkBase, IRCodeChunks>>()
block.children.filterIsInstance<IRInlineAsmChunk>().forEach { asmchunk ->
if(asmchunk.isIR) chunkReplacementsInBlock += asmchunk to convert(asmchunk)
// non-IR asm cannot be converted
}
chunkReplacementsInBlock.reversed().forEach { (old, new) ->
val index = block.children.indexOf(old)
block.children.removeAt(index)
new.reversed().forEach { block.children.add(index, it) }
}
chunkReplacementsInBlock.clear()
block.children.filterIsInstance<IRSubroutine>().forEach { sub ->
val chunkReplacementsInSub = mutableListOf<Pair<IRCodeChunkBase, IRCodeChunks>>()
sub.chunks.filterIsInstance<IRInlineAsmChunk>().forEach { asmchunk ->
if(asmchunk.isIR) chunkReplacementsInSub += asmchunk to convert(asmchunk)
// non-IR asm cannot be converted
}
chunkReplacementsInSub.reversed().forEach { (old, new) ->
val index = sub.chunks.indexOf(old)
sub.chunks.removeAt(index)
new.reversed().forEach { sub.chunks.add(index, it) }
}
chunkReplacementsInSub.clear()
}
}
}
}
class IRBlock(
val name: String,
val label: String,
val address: UInt?,
val library: Boolean,
val forceOutput: Boolean,
val alignment: BlockAlignment,
val position: Position
) {
@ -333,12 +409,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 +450,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 +461,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 +473,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)
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)
}

View File

@ -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)
}
}
}
}

View File

@ -1,56 +1,49 @@
package prog8.intermediate
import prog8.code.*
import prog8.code.core.DataType
import prog8.code.core.InternalCompilerException
import prog8.code.core.*
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? {
@ -84,9 +77,7 @@ fun parseIRValue(value: String): Float {
private val instructionPattern = Regex("""([a-z]+)(\.b|\.w|\.f)?(.*)""", RegexOption.IGNORE_CASE)
private val labelPattern = Regex("""_([a-zA-Z\d\._]+):""")
fun parseIRCodeLine(line: String, location: Pair<IRCodeChunk, Int>?, placeholders: MutableMap<Pair<IRCodeChunk, Int>, String>): Either<IRInstruction, String> {
// Note: this function is used from multiple places:
// the IR File Reader but also the VirtualMachine itself to make sense of any inline vmasm blocks.
fun parseIRCodeLine(line: String): Either<IRInstruction, String> {
val labelmatch = labelPattern.matchEntire(line.trim())
if(labelmatch!=null)
return right(labelmatch.groupValues[1]) // it's a label.
@ -101,141 +92,201 @@ 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): Float? {
return if(operand[0].isLetter()) {
labelSymbol = rest.split(",")[restIndex].trim()
if(location!=null)
placeholders[location] = labelSymbol!!
null
} else {
parseIRValue(operand)
}
}
if(operands.isNotEmpty() && operands[0].isNotEmpty()) {
operand = operands.removeFirst().trim()
if(operand[0]=='r')
reg1 = operand.substring(1).toInt()
else if(operand[0]=='f' && operand[1]=='r')
fpReg1 = operand.substring(2).toInt()
else {
value = parseValueOrPlaceholder(operand, 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'")
if(format.sysCall) {
val call = parseCall(rest)
val syscallNum = parseIRValue(call.target).toInt()
return left(IRInstruction(Opcode.SYSCALL, immediate = syscallNum, fcallArgs = FunctionCallArgs(call.args, call.returns)))
} else if (format.funcCall) {
val call = parseCall(rest)
return left(IRInstruction(Opcode.CALL, labelSymbol = call.target, fcallArgs = FunctionCallArgs(call.args, call.returns)))
} else {
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)
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.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(opcode in OpcodesForCpuRegisters) {
val reg = rest.split(',').last().lowercase().trim()
if(reg !in setOf(
"a", "x", "y",
"ax", "ay", "xy",
"r0", "r1", "r2", "r3",
"r4", "r5", "r6", "r7",
"r8", "r9", "r10","r11",
"r12", "r13", "r14", "r15",
"pc", "pz", "pv","pn"))
throw IRParseException("invalid cpu reg: $reg")
return left(IRInstruction(opcode, type, reg1, labelSymbol = reg))
if(labelSymbol!=null) {
if (labelSymbol!![0] == 'r' && labelSymbol!![1].isDigit())
throw IRParseException("labelsymbol confused with register?: $labelSymbol")
}
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))
}
private class ParsedCall(
val target: String,
val args: List<FunctionCallArgs.ArgumentSpec>,
val returns: FunctionCallArgs.RegSpec?
)
private fun parseCall(rest: String): ParsedCall {
fun parseRegspec(reg: String): FunctionCallArgs.RegSpec {
val pattern = Regex("f?r([0-9]+)\\.(.)(@.{1,2})?$")
val match = pattern.matchEntire(reg) ?: throw IRParseException("invalid regspec $reg")
val num = match.groups[1]!!.value.toInt()
val type = when(match.groups[2]!!.value) {
"b" -> IRDataType.BYTE
"w" -> IRDataType.WORD
"f" -> IRDataType.FLOAT
else -> throw IRParseException("invalid type spec in $reg")
}
val cpuRegister: RegisterOrStatusflag? =
if(match.groups[3]!=null) {
val cpuRegStr = match.groups[3]!!.value.drop(1)
parseRegisterOrStatusflag(cpuRegStr)
} else null
return FunctionCallArgs.RegSpec(type, num, cpuRegister)
}
fun parseArgs(args: String): List<FunctionCallArgs.ArgumentSpec> {
if(args.isBlank())
return emptyList()
return args.split(',').map {
if(it.contains('=')) {
val (argVar, argReg) = it.split('=')
FunctionCallArgs.ArgumentSpec(argVar, null, parseRegspec(argReg)) // address will be set later
} else {
FunctionCallArgs.ArgumentSpec("", null, parseRegspec(it)) // address will be set later
}
}
}
val pattern = Regex("(?<target>.+?)\\((?<arglist>.*?)\\)(:(?<returns>.+?))?")
val match = pattern.matchEntire(rest.replace(" ","")) ?: throw IRParseException("invalid call spec $rest")
val target = match.groups["target"]!!.value
val args = match.groups["arglist"]!!.value
val arguments = parseArgs(args)
val returns = match.groups["returns"]?.value
return ParsedCall(
target,
arguments,
if(returns==null) null else parseRegspec(returns)
)
}
internal fun parseRegisterOrStatusflag(regs: String): RegisterOrStatusflag {
var reg: RegisterOrPair? = null
var sf: Statusflag? = null
try {
reg = RegisterOrPair.valueOf(regs)
} catch (x: IllegalArgumentException) {
sf = Statusflag.valueOf(regs)
}
return RegisterOrStatusflag(reg, sf)
}
fun irType(type: DataType): IRDataType {
return when(type) {
DataType.BOOL,
DataType.UBYTE,
DataType.BYTE -> IRDataType.BYTE
DataType.UWORD,
DataType.WORD -> IRDataType.WORD
DataType.FLOAT -> IRDataType.FLOAT
in PassByReferenceDatatypes -> IRDataType.WORD
else -> throw AssemblyError("no IR datatype for $type")
}
}

View File

@ -75,7 +75,7 @@ load.b r1,42
</CODE>
</INITGLOBALS>
<BLOCK NAME="main" ADDRESS="" ALIGN="NONE" POS="[examples/test.p8: line 2 col 2-5]">
<BLOCK NAME="main" ADDRESS="" LIBRARY="false" FORCEOUTPUT="false" ALIGN="NONE" POS="[examples/test.p8: line 2 col 2-5]">
<SUB NAME="main.start" RETURNTYPE="" POS="[examples/test.p8: line 4 col 6-8]">
<PARAMS>
</PARAMS>
@ -85,14 +85,13 @@ return
</SUB>
</BLOCK>
<BLOCK NAME="sys" ADDRESS="" ALIGN="NONE" POS="[library:/prog8lib/virtual/syslib.p8: line 3 col 2-4]">
<BLOCK NAME="sys" ADDRESS="" LIBRARY="false" FORCEOUTPUT="false" ALIGN="NONE" POS="[library:/prog8lib/virtual/syslib.p8: line 3 col 2-4]">
<SUB NAME="sys.wait" RETURNTYPE="" POS="[library:/prog8lib/virtual/syslib.p8: line 15 col 6-8]">
<PARAMS>
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
syscall 13
</INLINEASM>
<CODE>
return

View File

@ -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)
}
}

View File

@ -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\>"

View File

@ -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()
}

View File

@ -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

View File

@ -1,7 +1,8 @@
package prog8.vm
import prog8.code.core.AssemblyError
import prog8.intermediate.SyscallRegisterBase
import prog8.intermediate.FunctionCallArgs
import prog8.intermediate.IRDataType
import kotlin.math.min
/*
@ -81,25 +82,59 @@ 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 {
fun call(call: Syscall, vm: VirtualMachine) {
private fun getArgValues(argspec: List<FunctionCallArgs.ArgumentSpec>, vm: VirtualMachine): List<Comparable<Nothing>> {
return argspec.map {
when(it.reg.dt) {
IRDataType.BYTE -> vm.registers.getUB(it.reg.registerNum)
IRDataType.WORD -> vm.registers.getUW(it.reg.registerNum)
IRDataType.FLOAT -> vm.registers.getFloat(it.reg.registerNum)
}
}
}
private fun returnValue(returns: FunctionCallArgs.RegSpec, value: Comparable<Nothing>, vm: VirtualMachine) {
val vv: Float = when(value) {
is UByte -> value.toFloat()
is UShort -> value.toFloat()
is UInt -> value.toFloat()
is Byte -> value.toFloat()
is Short -> value.toFloat()
is Int -> value.toFloat()
is Float -> value
else -> (value as Number).toFloat()
}
when(returns.dt) {
IRDataType.BYTE -> vm.registers.setUB(returns.registerNum, vv.toInt().toUByte())
IRDataType.WORD -> vm.registers.setUW(returns.registerNum, vv.toInt().toUShort())
IRDataType.FLOAT -> vm.registers.setFloat(returns.registerNum, vv)
}
}
fun call(call: Syscall, callspec: FunctionCallArgs, vm: VirtualMachine) {
when(call) {
Syscall.RESET -> {
vm.reset(false)
}
Syscall.EXIT ->{
vm.exit(vm.registers.getUB(SyscallRegisterBase).toInt())
val exitValue = getArgValues(callspec.arguments, vm).single() as UByte
vm.exit(exitValue.toInt())
}
Syscall.PRINT_C -> {
val char = vm.registers.getUB(SyscallRegisterBase).toInt()
print(Char(char))
val char = getArgValues(callspec.arguments, vm).single() as UByte
print(Char(char.toInt()))
}
Syscall.PRINT_S -> {
var addr = vm.registers.getUW(SyscallRegisterBase).toInt()
var addr = (getArgValues(callspec.arguments, vm).single() as UShort).toInt()
while(true) {
val char = vm.memory.getUB(addr).toInt()
if(char==0)
@ -109,35 +144,52 @@ object SysCalls {
}
}
Syscall.PRINT_U8 -> {
print(vm.registers.getUB(SyscallRegisterBase))
val value = getArgValues(callspec.arguments, vm).single()
print(value)
}
Syscall.PRINT_U16 -> {
print(vm.registers.getUW(SyscallRegisterBase))
val value = getArgValues(callspec.arguments, vm).single()
print(value)
}
Syscall.INPUT -> {
val (address, maxlen) = getArgValues(callspec.arguments, vm)
var input = readln()
val maxlen = vm.registers.getUB(SyscallRegisterBase+1).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())
val maxlenvalue = (maxlen as UByte).toInt()
if(maxlenvalue>0)
input = input.substring(0, min(input.length, maxlenvalue))
vm.memory.setString((address as UShort).toInt(), input, true)
returnValue(callspec.returns!!, input.length, vm)
}
Syscall.SLEEP -> {
val duration = vm.registers.getUW(SyscallRegisterBase).toLong()
Thread.sleep(duration)
val duration = getArgValues(callspec.arguments, vm).single() as UShort
Thread.sleep(duration.toLong())
}
Syscall.GFX_ENABLE -> {
val mode = getArgValues(callspec.arguments, vm).single() as UByte
vm.gfx_enable(mode)
}
Syscall.GFX_CLEAR -> {
val color = getArgValues(callspec.arguments, vm).single() as UByte
vm.gfx_clear(color)
}
Syscall.GFX_PLOT -> {
val (x,y,color) = getArgValues(callspec.arguments, vm)
vm.gfx_plot(x as UShort, y as UShort, color as UByte)
}
Syscall.GFX_GETPIXEL -> {
val (x,y) = getArgValues(callspec.arguments, vm)
val color = vm.gfx_getpixel(x as UShort, y as UShort)
returnValue(callspec.returns!!, color, vm)
}
Syscall.GFX_ENABLE -> vm.gfx_enable()
Syscall.GFX_CLEAR -> vm.gfx_clear()
Syscall.GFX_PLOT -> vm.gfx_plot()
Syscall.GFX_GETPIXEL ->vm.gfx_getpixel()
Syscall.WAIT -> {
val millis = vm.registers.getUW(SyscallRegisterBase).toLong() * 1000/60
Thread.sleep(millis)
val time = getArgValues(callspec.arguments, vm).single() as UShort
Thread.sleep(time.toLong() * 1000/60)
}
Syscall.WAITVSYNC -> vm.waitvsync()
Syscall.SORT_UBYTE -> {
val address = vm.registers.getUW(SyscallRegisterBase).toInt()
val length = vm.registers.getUB(SyscallRegisterBase+1).toInt()
val (addressV, lengthV) = getArgValues(callspec.arguments, vm)
val address = (addressV as UShort).toInt()
val length = (lengthV as UByte).toInt()
val array = IntProgression.fromClosedRange(address, address+length-1, 1).map {
vm.memory.getUB(it)
}.sorted()
@ -146,8 +198,9 @@ object SysCalls {
}
}
Syscall.SORT_BYTE -> {
val address = vm.registers.getUW(SyscallRegisterBase).toInt()
val length = vm.registers.getUB(SyscallRegisterBase+1).toInt()
val (addressV, lengthV) = getArgValues(callspec.arguments, vm)
val address = (addressV as UShort).toInt()
val length = (lengthV as UByte).toInt()
val array = IntProgression.fromClosedRange(address, address+length-1, 1).map {
vm.memory.getSB(it)
}.sorted()
@ -156,8 +209,9 @@ object SysCalls {
}
}
Syscall.SORT_UWORD -> {
val address = vm.registers.getUW(SyscallRegisterBase).toInt()
val length = vm.registers.getUB(SyscallRegisterBase+1).toInt()
val (addressV, lengthV) = getArgValues(callspec.arguments, vm)
val address = (addressV as UShort).toInt()
val length = (lengthV as UByte).toInt()
val array = IntProgression.fromClosedRange(address, address+length*2-2, 2).map {
vm.memory.getUW(it)
}.sorted()
@ -166,8 +220,9 @@ object SysCalls {
}
}
Syscall.SORT_WORD -> {
val address = vm.registers.getUW(SyscallRegisterBase).toInt()
val length = vm.registers.getUB(SyscallRegisterBase+1).toInt()
val (addressV, lengthV) = getArgValues(callspec.arguments, vm)
val address = (addressV as UShort).toInt()
val length = (lengthV as UByte).toInt()
val array = IntProgression.fromClosedRange(address, address+length*2-2, 2).map {
vm.memory.getSW(it)
}.sorted()
@ -176,8 +231,9 @@ object SysCalls {
}
}
Syscall.REVERSE_BYTES -> {
val address = vm.registers.getUW(SyscallRegisterBase).toInt()
val length = vm.registers.getUB(SyscallRegisterBase+1).toInt()
val (addressV, lengthV) = getArgValues(callspec.arguments, vm)
val address = (addressV as UShort).toInt()
val length = (lengthV as UByte).toInt()
val array = IntProgression.fromClosedRange(address, address+length-1, 1).map {
vm.memory.getUB(it)
}.reversed()
@ -186,8 +242,9 @@ object SysCalls {
}
}
Syscall.REVERSE_WORDS -> {
val address = vm.registers.getUW(SyscallRegisterBase).toInt()
val length = vm.registers.getUB(SyscallRegisterBase+1).toInt()
val (addressV, lengthV) = getArgValues(callspec.arguments, vm)
val address = (addressV as UShort).toInt()
val length = (lengthV as UByte).toInt()
val array = IntProgression.fromClosedRange(address, address+length*2-2, 2).map {
vm.memory.getUW(it)
}.reversed()
@ -196,8 +253,9 @@ object SysCalls {
}
}
Syscall.REVERSE_FLOATS -> {
val address = vm.registers.getUW(SyscallRegisterBase).toInt()
val length = vm.registers.getUB(SyscallRegisterBase+1).toInt()
val (addressV, lengthV) = getArgValues(callspec.arguments, vm)
val address = (addressV as UShort).toInt()
val length = (lengthV as UByte).toInt()
val array = IntProgression.fromClosedRange(address, address+length*4-2, 4).map {
vm.memory.getFloat(it)
}.reversed()
@ -206,151 +264,154 @@ object SysCalls {
}
}
Syscall.ANY_BYTE -> {
val address = vm.registers.getUW(SyscallRegisterBase).toInt()
val length = vm.registers.getUB(SyscallRegisterBase+1).toInt()
val (addressV, lengthV) = getArgValues(callspec.arguments, vm)
val address = (addressV as UShort).toInt()
val length = (lengthV as UByte).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)
returnValue(callspec.returns!!, 1, vm)
else
vm.registers.setUB(0, 0u)
returnValue(callspec.returns!!, 0, vm)
}
Syscall.ANY_WORD -> {
val address = vm.registers.getUW(SyscallRegisterBase).toInt()
val length = vm.registers.getUB(SyscallRegisterBase+1).toInt()
val (addressV, lengthV) = getArgValues(callspec.arguments, vm)
val address = (addressV as UShort).toInt()
val length = (lengthV as UByte).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)
returnValue(callspec.returns!!, 1, vm)
else
vm.registers.setUB(0, 0u)
returnValue(callspec.returns!!, 0, vm)
}
Syscall.ANY_FLOAT -> {
val address = vm.registers.getUW(SyscallRegisterBase).toInt()
val length = vm.registers.getUB(SyscallRegisterBase+1).toInt()
val (addressV, lengthV) = getArgValues(callspec.arguments, vm)
val address = (addressV as UShort).toInt()
val length = (lengthV as UByte).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)
returnValue(callspec.returns!!, 1, vm)
else
vm.registers.setUB(0, 0u)
returnValue(callspec.returns!!, 0, vm)
}
Syscall.ALL_BYTE -> {
val address = vm.registers.getUW(SyscallRegisterBase).toInt()
val length = vm.registers.getUB(SyscallRegisterBase+1).toInt()
val (addressV, lengthV) = getArgValues(callspec.arguments, vm)
val address = (addressV as UShort).toInt()
val length = (lengthV as UByte).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)
returnValue(callspec.returns!!, 1, vm)
else
vm.registers.setUB(0, 0u)
returnValue(callspec.returns!!, 0, vm)
}
Syscall.ALL_WORD -> {
val address = vm.registers.getUW(SyscallRegisterBase).toInt()
val length = vm.registers.getUB(SyscallRegisterBase+1).toInt()
val (addressV, lengthV) = getArgValues(callspec.arguments, vm)
val address = (addressV as UShort).toInt()
val length = (lengthV as UByte).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)
returnValue(callspec.returns!!, 1, vm)
else
vm.registers.setUB(0, 0u)
returnValue(callspec.returns!!, 0, vm)
}
Syscall.ALL_FLOAT -> {
val address = vm.registers.getUW(SyscallRegisterBase).toInt()
val length = vm.registers.getUB(SyscallRegisterBase+1).toInt()
val (addressV, lengthV) = getArgValues(callspec.arguments, vm)
val address = (addressV as UShort).toInt()
val length = (lengthV as UByte).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)
returnValue(callspec.returns!!, 1, vm)
else
vm.registers.setUB(0, 0u)
returnValue(callspec.returns!!, 0, vm)
}
Syscall.PRINT_F -> {
val value = vm.registers.getFloat(SyscallRegisterBase)
val value = getArgValues(callspec.arguments, vm).single() as Float
print(value)
}
Syscall.STR_TO_UWORD -> {
val stringAddr = vm.registers.getUW(SyscallRegisterBase)
val stringAddr = getArgValues(callspec.arguments, vm).single() as UShort
val string = vm.memory.getString(stringAddr.toInt()).takeWhile { it.isDigit() }
val value = try {
string.toUShort()
} catch(_: NumberFormatException) {
0u
}
vm.registers.setUW(0, value)
returnValue(callspec.returns!!, value, vm)
}
Syscall.STR_TO_WORD -> {
val stringAddr = vm.registers.getUW(SyscallRegisterBase)
val string = vm.memory.getString(stringAddr.toInt()).takeWhile { it.isDigit() }
val stringAddr = getArgValues(callspec.arguments, vm).single() as UShort
val memstring = vm.memory.getString(stringAddr.toInt())
val match = Regex("^[+-]?\\d+").find(memstring) ?: return returnValue(callspec.returns!!, 0, vm)
val value = try {
string.toShort()
match.value.toShort()
} catch(_: NumberFormatException) {
0
}
vm.registers.setSW(0, value)
return returnValue(callspec.returns!!, value, vm)
}
Syscall.COMPARE_STRINGS -> {
val firstAddr = vm.registers.getUW(SyscallRegisterBase)
val secondAddr = vm.registers.getUW(SyscallRegisterBase+1)
val (firstV, secondV) = getArgValues(callspec.arguments, vm)
val firstAddr = firstV as UShort
val secondAddr = secondV as UShort
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)
returnValue(callspec.returns!!, 0, vm)
else if(comparison<0)
vm.registers.setSB(0, -1)
returnValue(callspec.returns!!, -1, vm)
else
vm.registers.setSB(0, 1)
returnValue(callspec.returns!!, 1, vm)
}
Syscall.RNDFSEED -> {
val seed = vm.registers.getFloat(SyscallRegisterBase)
val seed = getArgValues(callspec.arguments, vm).single() as Float
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)
vm.randomSeed(seed1, seed2)
val (seed1, seed2) = getArgValues(callspec.arguments, vm)
vm.randomSeed(seed1 as UShort, seed2 as UShort)
}
Syscall.RND -> {
vm.registers.setUB(0, vm.randomGenerator.nextInt().toUByte())
returnValue(callspec.returns!!, vm.randomGenerator.nextInt().toUByte(), vm)
}
Syscall.RNDW -> {
vm.registers.setUW(0, vm.randomGenerator.nextInt().toUShort())
returnValue(callspec.returns!!, vm.randomGenerator.nextInt().toUShort(), vm)
}
Syscall.RNDF -> {
vm.registers.setFloat(0, vm.randomGeneratorFloats.nextFloat())
returnValue(callspec.returns!!, vm.randomGeneratorFloats.nextFloat(), vm)
}
Syscall.STRING_CONTAINS -> {
val char = vm.registers.getUB(SyscallRegisterBase).toInt().toChar()
val stringAddr = vm.registers.getUW(SyscallRegisterBase+1)
val (charV, addr) = getArgValues(callspec.arguments, vm)
val stringAddr = addr as UShort
val char = (charV as UByte).toInt().toChar()
val string = vm.memory.getString(stringAddr.toInt())
vm.registers.setUB(0, if(char in string) 1u else 0u)
returnValue(callspec.returns!!, if(char in string) 1u else 0u, vm)
}
Syscall.BYTEARRAY_CONTAINS -> {
val value = vm.registers.getUB(SyscallRegisterBase)
var array = vm.registers.getUW(SyscallRegisterBase+1).toInt()
var length = vm.registers.getUB(SyscallRegisterBase+2)
val (value, arrayV, lengthV) = getArgValues(callspec.arguments, vm)
var length = lengthV as UByte
var array = (arrayV as UShort).toInt()
while(length>0u) {
if(vm.memory.getUB(array)==value) {
vm.registers.setUB(0, 1u)
return
}
if(vm.memory.getUB(array)==value)
return returnValue(callspec.returns!!, 1u, vm)
array++
length--
}
vm.registers.setUB(0, 0u)
returnValue(callspec.returns!!, 0u, vm)
}
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)
val (value, arrayV, lengthV) = getArgValues(callspec.arguments, vm)
var length = lengthV as UByte
var array = (arrayV as UShort).toInt()
while(length>0u) {
if(vm.memory.getUW(array)==value) {
vm.registers.setUB(0, 1u)
return
}
if(vm.memory.getUW(array)==value)
return returnValue(callspec.returns!!, 1u, vm)
array += 2
length--
}
vm.registers.setUB(0, 0u)
returnValue(callspec.returns!!, 0u, vm)
}
else -> throw AssemblyError("missing syscall ${call.name}")
}

File diff suppressed because it is too large Load Diff

View File

@ -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,41 +38,37 @@ 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) {
is IRAsmSubroutine -> throw IRParseException("vm does not support asmsubs (use normal sub): ${child.label}")
is IRCodeChunk -> programChunks += child
is IRInlineAsmChunk -> {
val replacement = addAssemblyToProgram(child, programChunks, variableAddresses)
chunkReplacements += replacement
}
is IRInlineBinaryChunk -> throw IRParseException("inline binary data not yet supported in the VM") // TODO
is IRInlineAsmChunk -> throw IRParseException("encountered unconverted inline assembly chunk")
is IRInlineBinaryChunk -> throw IRParseException("inline binary data not yet supported in the VM")
is IRSubroutine -> {
subroutines[child.label] = child
child.chunks.forEach { chunk ->
when (chunk) {
is IRInlineAsmChunk -> {
val replacement = addAssemblyToProgram(chunk, programChunks, variableAddresses)
chunkReplacements += replacement
}
is IRInlineBinaryChunk -> throw IRParseException("inline binary data not yet supported in the VM") // TODO
is IRInlineAsmChunk -> throw IRParseException("encountered unconverted inline assembly chunk")
is IRInlineBinaryChunk -> throw IRParseException("inline binary data not yet supported in the VM")
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" }
require(ins.address != null) { "instruction with labelSymbol for a var should have value set to the memory address" }
}
}
@ -102,7 +100,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 +122,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 +135,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 +147,49 @@ 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")
else if(opcode in OpcodesThatBranch) {
chunk.instructions[line] = chunk.instructions[line].copy(branchTarget = target, value = null)
} else if(opcode in OpcodesWithMemoryAddressAsValue) {
else if(opcode in OpcodesThatBranch)
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 {
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)
}
}
subroutines.forEach {
it.value.chunks.forEach { chunk ->
chunk.instructions.withIndex().forEach { (index, ins) ->
if(ins.opcode==Opcode.CALL) {
val fcallspec = ins.fcallArgs!!
val argsWithAddresses = fcallspec.arguments.map { arg ->
if(arg.address!=null)
arg
else {
val address = variableAddresses.getValue(ins.labelSymbol + "." + arg.name)
FunctionCallArgs.ArgumentSpec(arg.name, address, arg.reg)
}
}
fcallspec.arguments = argsWithAddresses
}
}
}
}
}
private val functionCallOpcodes = setOf(Opcode.CALL, 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(
@ -300,26 +323,4 @@ class VmProgramLoader {
require(variable.onetimeInitializationStringValue==null) { "in vm/ir, strings should have been converted into bytearrays." }
}
}
private fun addAssemblyToProgram(
asmChunk: IRInlineAsmChunk,
chunks: MutableList<IRCodeChunk>,
symbolAddresses: MutableMap<String, Int>,
): Pair<IRCodeChunkBase, IRCodeChunk> {
if(asmChunk.isIR) {
val chunk = IRCodeChunk(asmChunk.label, asmChunk.next)
asmChunk.assembly.lineSequence().forEach {
val parsed = parseIRCodeLine(it.trim(), Pair(chunk, chunk.instructions.size), placeholders)
parsed.fold(
ifLeft = { instruction -> chunk += instruction },
ifRight = { label -> symbolAddresses[label] = chunk.instructions.size }
)
}
chunks += chunk
return Pair(asmChunk, chunk)
} else {
throw IRParseException("vm currently does not support real inlined assembly (only IR): ${asmChunk.label}")
}
}
}

View File

@ -43,12 +43,12 @@ class TestVm: FunSpec( {
test("vm execution: modify memory") {
val program = IRProgram("test", IRSymbolTable(null), getTestOptions(), VMTarget())
val block = IRBlock("testmain", null, IRBlock.BlockAlignment.NONE, Position.DUMMY)
val block = IRBlock("testmain", null, false, false, IRBlock.BlockAlignment.NONE, Position.DUMMY)
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
@ -70,25 +70,9 @@ class TestVm: FunSpec( {
vm.stepCount shouldBe code.instructions.size
}
test("vm asmbinary not supported") {
val program = IRProgram("test", IRSymbolTable(null), getTestOptions(), VMTarget())
val block = IRBlock("testmain", null, IRBlock.BlockAlignment.NONE, Position.DUMMY)
val startSub = IRSubroutine("testmain.testsub", emptyList(), null, Position.DUMMY)
val code = IRCodeChunk(startSub.label, null)
code += IRInstruction(Opcode.BINARYDATA, binaryData = listOf(1u,2u,3u))
code += IRInstruction(Opcode.RETURN)
startSub += code
block += startSub
program.addBlock(block)
val vm = VirtualMachine(program)
shouldThrowWithMessage<NotImplementedError>("An operation is not implemented: BINARYDATA not yet supported in VM") {
vm.run()
}
}
test("asmsub not supported in vm even with IR") {
val program = IRProgram("test", IRSymbolTable(null), getTestOptions(), VMTarget())
val block = IRBlock("main", null, IRBlock.BlockAlignment.NONE, Position.DUMMY)
val block = IRBlock("main", null, false, false, IRBlock.BlockAlignment.NONE, Position.DUMMY)
val startSub = IRAsmSubroutine(
"main.asmstart",
0x2000u,
@ -129,7 +113,7 @@ class TestVm: FunSpec( {
<INITGLOBALS>
</INITGLOBALS>
<BLOCK NAME="main" ADDRESS="" ALIGN="NONE" POS="[unittest: line 42 col 1-9]">
<BLOCK NAME="main" ADDRESS="" LIBRARY="false" FORCEOUTPUT="false" ALIGN="NONE" POS="[unittest: line 42 col 1-9]">
</BLOCK>
</PROGRAM>
"""