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" /> <option name="jvmTarget" value="11" />
</component> </component>
<component name="KotlinJpsPluginSettings"> <component name="KotlinJpsPluginSettings">
<option name="version" value="1.8.20" /> <option name="version" value="1.8.20-release-327" />
</component> </component>
</project> </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 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, 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. **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. 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, 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 parameters: List<StRomSubParameter>,
val returns: List<StRomSubParameter>, val returns: List<StRomSubParameter>,
astNode: PtNode) : 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>) { private fun addToSt(node: PtNode, scope: Stack<StNode>) {
val stNode = when(node) { val stNode = when(node) {
is PtAsmSub -> { is PtAsmSub -> {
if(node.address==null) { val parameters = node.parameters.map { StRomSubParameter(it.first, it.second.type) }
val params = node.parameters.map { StSubroutineParameter(it.second.name, it.second.type) } val returns = node.returns.map { StRomSubParameter(it.first, it.second) }
StSub(node.name, params, node.returns.singleOrNull()?.second, node) StRomSub(node.name, node.address, parameters, returns, 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)
}
} }
is PtBlock -> { is PtBlock -> {
StNode(node.name, StNodeType.BLOCK, node) StNode(node.name, StNodeType.BLOCK, node)

View File

@ -63,7 +63,7 @@ class PtProgram(
children.asSequence().filterIsInstance<PtBlock>() children.asSequence().filterIsInstance<PtBlock>()
fun entrypoint(): PtSub? = 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 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 java.util.*
import kotlin.math.abs import kotlin.math.abs
import kotlin.math.round 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) { class GoldenRam(options: CompilationOptions, val region: UIntRange): MemoryAllocator(options) {
private var nextLocation: UInt = region.first 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? override val SCRATCH_W2 = 0xcfu // temp storage 2 for a word $cf+$d0 TODO is $d0 okay to use?
init { init {
if (options.floats) {
throw InternalCompilerException("Atari target doesn't yet support floating point routines")
}
if (options.floats && options.zeropage !in arrayOf( if (options.floats && options.zeropage !in arrayOf(
ZeropageType.FLOATSAFE, ZeropageType.FLOATSAFE,
ZeropageType.BASICSAFE, ZeropageType.BASICSAFE,
@ -39,9 +43,9 @@ class AtariZeropage(options: CompilationOptions) : Zeropage(options) {
} }
} }
val distictFree = free.distinct() val distinctFree = free.distinct()
free.clear() free.clear()
free.addAll(distictFree) free.addAll(distinctFree)
removeReservedFromFreePool() removeReservedFromFreePool()
} }

View File

@ -6,15 +6,22 @@ import prog8.code.core.Zeropage
import prog8.code.core.ZeropageType import prog8.code.core.ZeropageType
// reference: "Mapping the C128" zero page chapter.
class C128Zeropage(options: CompilationOptions) : Zeropage(options) { class C128Zeropage(options: CompilationOptions) : Zeropage(options) {
override val SCRATCH_B1 = 0x9bu // temp storage for a single byte override val SCRATCH_B1 = 0x74u // temp storage for a single byte
override val SCRATCH_REG = 0x9cu // temp storage for a register, must be B1+1 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_W1 = 0xfbu // temp storage 1 for a word $fb+$fc
override val SCRATCH_W2 = 0xfdu // temp storage 2 for a word $fd+$fe override val SCRATCH_W2 = 0xfdu // temp storage 2 for a word $fd+$fe
init { 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( if (options.floats && options.zeropage !in arrayOf(
ZeropageType.FLOATSAFE, ZeropageType.FLOATSAFE,
ZeropageType.BASICSAFE, ZeropageType.BASICSAFE,
@ -24,23 +31,42 @@ class C128Zeropage(options: CompilationOptions) : Zeropage(options) {
when (options.zeropage) { when (options.zeropage) {
ZeropageType.FULL -> { ZeropageType.FULL -> {
// TODO all c128 usable zero page locations, except the ones used by the system's IRQ routine // $00/$01 are data port IO registers, // $02-$09 are storage locations for JSRFAR and such
free.addAll(0x0au..0xffu) // TODO c128 what about $02-$09? free.addAll(0x0au..0xffu)
// TODO c128 free.removeAll(setOf(0xa0u, 0xa1u, 0xa2u, 0x91u, 0xc0u, 0xc5u, 0xcbu, 0xf5u, 0xf6u)) // these are updated by IRQ 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.FLOATSAFE,
ZeropageType.BASICSAFE -> { 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 -> { ZeropageType.DONTUSE -> {
free.clear() // don't use zeropage at all free.clear() // don't use zeropage at all
} }
} }
val distictFree = free.distinct() val distinctFree = free.distinct()
free.clear() free.clear()
free.addAll(distictFree) free.addAll(distinctFree)
removeReservedFromFreePool() removeReservedFromFreePool()
} }

View File

@ -65,9 +65,9 @@ class C64Zeropage(options: CompilationOptions) : Zeropage(options) {
} }
} }
val distictFree = free.distinct() val distinctFree = free.distinct()
free.clear() free.clear()
free.addAll(distictFree) free.addAll(distinctFree)
removeReservedFromFreePool() removeReservedFromFreePool()

View File

@ -64,7 +64,7 @@ class CX16MachineDefinition: IMachineDefinition {
override fun initializeMemoryAreas(compilerOptions: CompilationOptions) { override fun initializeMemoryAreas(compilerOptions: CompilationOptions) {
zeropage = CX16Zeropage(compilerOptions) 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}") 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.clear()
free.addAll(distictFree) free.addAll(distinctFree)
removeReservedFromFreePool() removeReservedFromFreePool()

View File

@ -483,7 +483,7 @@ class AsmGen6502Internal (
when(reg) { when(reg) {
RegisterOrPair.A, RegisterOrPair.A,
RegisterOrPair.X, RegisterOrPair.X,
RegisterOrPair.Y -> assignmentAsmGen.assignRegisterByte(target, reg.asCpuRegister()) RegisterOrPair.Y -> assignmentAsmGen.assignRegisterByte(target, reg.asCpuRegister(), target.datatype in SignedDatatypes)
RegisterOrPair.AX, RegisterOrPair.AX,
RegisterOrPair.AY, RegisterOrPair.AY,
RegisterOrPair.XY, RegisterOrPair.XY,

View File

@ -1,5 +1,6 @@
package prog8.codegen.cpu6502 package prog8.codegen.cpu6502
import prog8.code.StStaticVariable
import prog8.code.ast.* import prog8.code.ast.*
import prog8.code.core.* import prog8.code.core.*
import prog8.codegen.cpu6502.assignment.* 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 var3name = asmgen.asmVariableName(fcall.args[3] as PtIdentifier)
val divisionTarget = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UBYTE, fcall.definingISub(), fcall.args[2].position, var2name) 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) val remainderTarget = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UBYTE, fcall.definingISub(), fcall.args[3].position, var3name)
assignAsmGen.assignRegisterByte(remainderTarget, CpuRegister.A) assignAsmGen.assignRegisterByte(remainderTarget, CpuRegister.A, false)
assignAsmGen.assignRegisterByte(divisionTarget, CpuRegister.Y) assignAsmGen.assignRegisterByte(divisionTarget, CpuRegister.Y, false)
} }
private fun funcDivmodW(fcall: PtBuiltinFunctionCall) { private fun funcDivmodW(fcall: PtBuiltinFunctionCall) {
@ -303,7 +304,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
asmgen.out(" jsr prog8_lib.func_sqrt16_stack") asmgen.out(" jsr prog8_lib.func_sqrt16_stack")
else { else {
asmgen.out(" jsr prog8_lib.func_sqrt16_into_A") 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 lda #$numElements
jsr prog8_lib.func_reverse_w""") 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 -> { DataType.ARRAY_F -> {
asmgen.out(""" asmgen.out("""
lda #<$varName lda #<$varName
@ -381,6 +392,16 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
lda #$numElements""") lda #$numElements""")
asmgen.out(if (decl.type == DataType.ARRAY_UW) " jsr prog8_lib.func_sort_uw" else " jsr prog8_lib.func_sort_w") 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") DataType.ARRAY_F -> throw AssemblyError("sorting of floating point array is not supported")
else -> throw AssemblyError("weird type") 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") throw AssemblyError("non-array var indexing requires bytes dt")
asmgen.assignExpressionToVariable(arrayvar, "prog8_lib.${operation}_array_u${dt}._arg_target", DataType.UWORD) asmgen.assignExpressionToVariable(arrayvar, "prog8_lib.${operation}_array_u${dt}._arg_target", DataType.UWORD)
} else { } else {
val p = arrayvar.parent
val addressOf = PtAddressOf(arrayvar.position) val addressOf = PtAddressOf(arrayvar.position)
addressOf.add(arrayvar) 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(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) 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") DataType.FLOAT -> asmgen.out(" jsr floats.func_sign_f_into_A")
else -> throw AssemblyError("weird type $dt") 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") DataType.ARRAY_F -> asmgen.out(" jsr floats.func_${fcall.name}_f_into_A | ldy #0")
else -> throw AssemblyError("weird type $dt") 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?) { private fun funcMkword(fcall: PtBuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?) {
if(resultToStack) { 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.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 { } else {
val reg = resultRegister ?: RegisterOrPair.AY val reg = resultRegister ?: RegisterOrPair.AY
var needAsave = asmgen.needAsaveForExpr(fcall.args[0]) var needAsave = asmgen.needAsaveForExpr(fcall.args[0])

View File

@ -150,8 +150,7 @@ internal class ProgramAndVarsGen(
asmgen.out(" rts") asmgen.out(" rts")
} }
"c128" -> { "c128" -> {
asmgen.out(" jsr main.start") asmgen.out(" jsr main.start | lda #0 | sta ${"$"}ff00")
// TODO c128: how to bank basic+kernal back in?
if(!options.noSysInit) if(!options.noSysInit)
asmgen.out(" jmp ${compTarget.name}.cleanup_at_exit") asmgen.out(" jmp ${compTarget.name}.cleanup_at_exit")
else else

View File

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

View File

@ -2,7 +2,10 @@ package prog8.codegen.experimental
import prog8.code.SymbolTable import prog8.code.SymbolTable
import prog8.code.ast.PtProgram 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.codegen.intermediate.IRCodeGen
import prog8.intermediate.IRFileWriter import prog8.intermediate.IRFileWriter

View File

@ -46,7 +46,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
assignment: PtAugmentedAssign assignment: PtAugmentedAssign
): IRCodeChunks { ): IRCodeChunks {
val value = assignment.value val value = assignment.value
val vmDt = codeGen.irType(value.type) val vmDt = irType(value.type)
return when(assignment.operator) { return when(assignment.operator) {
"+" -> expressionEval.operatorPlusInplace(address, null, vmDt, value) "+" -> expressionEval.operatorPlusInplace(address, null, vmDt, value)
"-" -> expressionEval.operatorMinusInplace(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 { private fun assignVarAugmented(symbol: String, assignment: PtAugmentedAssign): IRCodeChunks {
val value = assignment.value val value = assignment.value
val targetDt = codeGen.irType(assignment.target.type) val targetDt = irType(assignment.target.type)
return when (assignment.operator) { return when (assignment.operator) {
"+=" -> expressionEval.operatorPlusInplace(null, symbol, targetDt, value) "+=" -> expressionEval.operatorPlusInplace(null, symbol, targetDt, value)
"-=" -> expressionEval.operatorMinusInplace(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) code += if(address!=null)
IRInstruction(Opcode.NEGM, vmDt, value = address) IRInstruction(Opcode.NEGM, vmDt, address = address)
else else
IRInstruction(Opcode.NEGM, vmDt, labelSymbol = symbol) IRInstruction(Opcode.NEGM, vmDt, labelSymbol = symbol)
} }
"~" -> { "~" -> {
val regMask = codeGen.registers.nextFree() val regMask = codeGen.registers.nextFree()
val mask = if(vmDt==IRDataType.BYTE) 0x00ff else 0xffff 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) code += if(address!=null)
IRInstruction(Opcode.XORM, vmDt, reg1=regMask, value = address) IRInstruction(Opcode.XORM, vmDt, reg1=regMask, address = address)
else else
IRInstruction(Opcode.XORM, vmDt, reg1=regMask, labelSymbol = symbol) 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 targetIdent = assignment.target.identifier
val targetMemory = assignment.target.memory val targetMemory = assignment.target.memory
val targetArray = assignment.target.array 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>() val result = mutableListOf<IRCodeChunkBase>()
var valueRegister = -1 var valueRegister = -1
@ -169,30 +170,42 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
val zero = codeGen.isZero(assignment.value) val zero = codeGen.isZero(assignment.value)
if(!zero) { if(!zero) {
// calculate the assignment value // calculate the assignment value
if (vmDt == IRDataType.FLOAT) { if (valueDt == IRDataType.FLOAT) {
val tr = expressionEval.translateExpression(assignment.value) val tr = expressionEval.translateExpression(assignment.value)
valueFpRegister = tr.resultFpReg valueFpRegister = tr.resultFpReg
addToResult(result, tr, -1, valueFpRegister) addToResult(result, tr, -1, valueFpRegister)
} else { } 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) { if (assignment.value is PtMachineRegister) {
valueRegister = (assignment.value as PtMachineRegister).register valueRegister = (assignment.value as PtMachineRegister).register
if(extendByteToWord)
addInstr(result, IRInstruction(Opcode.EXT, IRDataType.BYTE, reg1=valueRegister), null)
} else { } else {
val tr = expressionEval.translateExpression(assignment.value) val tr = expressionEval.translateExpression(assignment.value)
valueRegister = tr.resultReg valueRegister = tr.resultReg
addToResult(result, tr, valueRegister, -1) 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) { if(targetIdent!=null) {
val instruction = if(zero) { val instruction = if(zero) {
IRInstruction(Opcode.STOREZM, vmDt, labelSymbol = targetIdent.name) IRInstruction(Opcode.STOREZM, targetDt, labelSymbol = targetIdent.name)
} else { } else {
if (vmDt == IRDataType.FLOAT) { if (targetDt == IRDataType.FLOAT)
IRInstruction(Opcode.STOREM, vmDt, fpReg1 = valueFpRegister, labelSymbol = targetIdent.name) IRInstruction(Opcode.STOREM, targetDt, fpReg1 = valueFpRegister, labelSymbol = targetIdent.name)
}
else 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 } result += IRCodeChunk(null, null).also { it += instruction }
return result return result
@ -214,9 +227,9 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
if(zero) { if(zero) {
// there's no STOREZIX instruction // there's no STOREZIX instruction
valueRegister = codeGen.registers.nextFree() 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 result += code
return result return result
} }
@ -225,59 +238,59 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
if(zero) { if(zero) {
if(fixedIndex!=null) { if(fixedIndex!=null) {
val offset = fixedIndex*itemsize 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 result += chunk
} else { } else {
val (code, indexReg) = loadIndexReg(targetArray, itemsize) val (code, indexReg) = loadIndexReg(targetArray, itemsize)
result += code 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 { } else {
if(vmDt== IRDataType.FLOAT) { if(targetDt== IRDataType.FLOAT) {
if(fixedIndex!=null) { if(fixedIndex!=null) {
val offset = fixedIndex*itemsize 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 result += chunk
} else { } else {
val (code, indexReg) = loadIndexReg(targetArray, itemsize) val (code, indexReg) = loadIndexReg(targetArray, itemsize)
result += code 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 { } else {
if(fixedIndex!=null) { if(fixedIndex!=null) {
val offset = fixedIndex*itemsize 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 result += chunk
} else { } else {
val (code, indexReg) = loadIndexReg(targetArray, itemsize) val (code, indexReg) = loadIndexReg(targetArray, itemsize)
result += code 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 return result
} }
else if(targetMemory!=null) { 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(zero) {
if(targetMemory.address is PtNumber) { 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 result += chunk
} else { } else {
val tr = expressionEval.translateExpression(targetMemory.address) val tr = expressionEval.translateExpression(targetMemory.address)
val addressReg = tr.resultReg val addressReg = tr.resultReg
addToResult(result, tr, tr.resultReg, -1) 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 { } else {
if(targetMemory.address is PtNumber) { 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 result += chunk
} else { } else {
val tr = expressionEval.translateExpression(targetMemory.address) val tr = expressionEval.translateExpression(targetMemory.address)
val addressReg = tr.resultReg val addressReg = tr.resultReg
addToResult(result, tr, tr.resultReg, -1) 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) { if(codeGen.options.useNewExprCode) {
val tr = expressionEval.translateExpression(array.index) val tr = expressionEval.translateExpression(array.index)
result += tr.chunks 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) return Pair(result, tr.resultReg)
} else { } else {
val mult: PtExpression val mult: PtExpression

View File

@ -52,20 +52,28 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
val result = mutableListOf<IRCodeChunkBase>() val result = mutableListOf<IRCodeChunkBase>()
val number = call.args[0] val number = call.args[0]
val divident = call.args[1] val divident = call.args[1]
val divisionReg: Int
val remainderReg: Int
if(divident is PtNumber) { if(divident is PtNumber) {
val tr = exprGen.translateExpression(number) val tr = exprGen.translateExpression(number)
addToResult(result, tr, tr.resultReg, -1) 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 { } else {
val numTr = exprGen.translateExpression(number) val numTr = exprGen.translateExpression(number)
addToResult(result, numTr, numTr.resultReg, -1) addToResult(result, numTr, numTr.resultReg, -1)
val dividentTr = exprGen.translateExpression(divident) val dividentTr = exprGen.translateExpression(divident)
addToResult(result, dividentTr, dividentTr.resultReg, -1) addToResult(result, dividentTr, dividentTr.resultReg, -1)
addInstr(result, IRInstruction(Opcode.DIVMODR, type, reg1 = numTr.resultReg, reg2=dividentTr.resultReg), null) 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 // DIVMOD result convention: on value stack, division and remainder on top.
result += assignRegisterTo(call.args[2], 0) addInstr(result, IRInstruction(Opcode.POP, type, reg1=remainderReg), null)
result += assignRegisterTo(call.args[3], 1) 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) 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 result = mutableListOf<IRCodeChunkBase>()
val left = exprGen.translateExpression(call.args[0]) val left = exprGen.translateExpression(call.args[0])
val right = exprGen.translateExpression(call.args[1]) val right = exprGen.translateExpression(call.args[1])
val targetReg = codeGen.registers.nextFree() addToResult(result, left, left.resultReg, -1)
addToResult(result, left, 65500, -1) addToResult(result, right, right.resultReg, -1)
addToResult(result, right, 65501, -1) result += codeGen.makeSyscall(IMSyscall.COMPARE_STRINGS, listOf(IRDataType.WORD to left.resultReg, IRDataType.WORD to right.resultReg), IRDataType.BYTE to left.resultReg)
addInstr(result, IRInstruction(Opcode.SYSCALL, value=IMSyscall.COMPARE_STRINGS.number), null) return ExpressionCodeResult(result, IRDataType.BYTE, left.resultReg, -1)
addInstr(result, IRInstruction(Opcode.LOADR, IRDataType.BYTE, reg1=targetReg, reg2=0), null)
return ExpressionCodeResult(result, IRDataType.BYTE, targetReg, -1)
} }
private fun funcCmp(call: PtBuiltinFunctionCall): ExpressionCodeResult { 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) addToResult(result, leftTr, leftTr.resultReg, -1)
val rightTr = exprGen.translateExpression(call.args[1]) val rightTr = exprGen.translateExpression(call.args[1])
addToResult(result, rightTr, rightTr.resultReg, -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 { result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.CMP, dt, reg1=leftTr.resultReg, reg2=rightTr.resultReg) 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 result = mutableListOf<IRCodeChunkBase>()
val tr = exprGen.translateExpression(call.args[0]) val tr = exprGen.translateExpression(call.args[0])
addToResult(result, tr, SyscallRegisterBase, -1) addToResult(result, tr, tr.resultReg, -1)
result += IRCodeChunk(null, null).also { val lengthReg = codeGen.registers.nextFree()
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = SyscallRegisterBase+1, value = array.length) addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = lengthReg, immediate = array.length), null)
it += IRInstruction(Opcode.SYSCALL, value = syscall.number) result += codeGen.makeSyscall(syscall, listOf(IRDataType.WORD to tr.resultReg, IRDataType.BYTE to lengthReg), IRDataType.BYTE to tr.resultReg)
// SysCall call convention: return value in register r0
if(tr.resultReg!=0)
it += IRInstruction(Opcode.LOADR, IRDataType.BYTE, reg1 = tr.resultReg, reg2 = 0)
}
return ExpressionCodeResult(result, IRDataType.BYTE, tr.resultReg, -1) 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 result = mutableListOf<IRCodeChunkBase>()
val tr = exprGen.translateExpression(call.args[0]) val tr = exprGen.translateExpression(call.args[0])
addToResult(result, tr, SyscallRegisterBase, -1) addToResult(result, tr, tr.resultReg, -1)
result += IRCodeChunk(null, null).also { val lengthReg = codeGen.registers.nextFree()
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = SyscallRegisterBase+1, value = array.length) addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = lengthReg, immediate = array.length), null)
it += IRInstruction(Opcode.SYSCALL, value = syscall.number) result += codeGen.makeSyscall(syscall, listOf(IRDataType.WORD to tr.resultReg, IRDataType.BYTE to lengthReg), IRDataType.BYTE to tr.resultReg)
// SysCall call convention: return value in register r0
if(tr.resultReg!=0)
it += IRInstruction(Opcode.LOADR, IRDataType.BYTE, reg1 = tr.resultReg, reg2 = 0)
}
return ExpressionCodeResult(result, IRDataType.BYTE, tr.resultReg, -1) 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() val compareReg = codeGen.registers.nextFree()
result += IRCodeChunk(null, null).also { result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOADR, IRDataType.BYTE, reg1=compareReg, reg2=tr.resultReg) it += IRInstruction(Opcode.LOADR, IRDataType.BYTE, reg1=compareReg, reg2=tr.resultReg)
it += IRInstruction(Opcode.AND, IRDataType.BYTE, reg1=compareReg, value=0x80) it += IRInstruction(Opcode.AND, IRDataType.BYTE, reg1=compareReg, immediate = 0x80)
it += IRInstruction(Opcode.BZ, IRDataType.BYTE, reg1=compareReg, labelSymbol = notNegativeLabel) it += IRInstruction(Opcode.BEQ, IRDataType.BYTE, reg1=compareReg, immediate = 0, labelSymbol = notNegativeLabel)
it += IRInstruction(Opcode.NEG, IRDataType.BYTE, reg1=tr.resultReg) it += IRInstruction(Opcode.NEG, IRDataType.BYTE, reg1=tr.resultReg)
it += IRInstruction(Opcode.EXT, 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() val compareReg = codeGen.registers.nextFree()
result += IRCodeChunk(null, null).also { result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOADR, IRDataType.WORD, reg1=compareReg, reg2=tr.resultReg) it += IRInstruction(Opcode.LOADR, IRDataType.WORD, reg1=compareReg, reg2=tr.resultReg)
it += IRInstruction(Opcode.AND, IRDataType.WORD, reg1=compareReg, value=0x8000) it += IRInstruction(Opcode.AND, IRDataType.WORD, reg1=compareReg, immediate = 0x8000)
it += IRInstruction(Opcode.BZ, IRDataType.WORD, reg1=compareReg, labelSymbol = notNegativeLabel) it += IRInstruction(Opcode.BEQ, IRDataType.WORD, reg1=compareReg, immediate = 0, labelSymbol = notNegativeLabel)
it += IRInstruction(Opcode.NEG, IRDataType.WORD, reg1=tr.resultReg) it += IRInstruction(Opcode.NEG, IRDataType.WORD, reg1=tr.resultReg)
} }
result += IRCodeChunk(notNegativeLabel, null) result += IRCodeChunk(notNegativeLabel, null)
@ -190,7 +188,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
private fun funcSgn(call: PtBuiltinFunctionCall): ExpressionCodeResult { private fun funcSgn(call: PtBuiltinFunctionCall): ExpressionCodeResult {
val result = mutableListOf<IRCodeChunkBase>() val result = mutableListOf<IRCodeChunkBase>()
val vmDt = codeGen.irType(call.type) val vmDt = irType(call.type)
val tr = exprGen.translateExpression(call.args.single()) val tr = exprGen.translateExpression(call.args.single())
addToResult(result, tr, tr.resultReg, -1) addToResult(result, tr, tr.resultReg, -1)
val resultReg = codeGen.registers.nextFree() val resultReg = codeGen.registers.nextFree()
@ -261,11 +259,10 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
} }
val result = mutableListOf<IRCodeChunkBase>() val result = mutableListOf<IRCodeChunkBase>()
val tr = exprGen.translateExpression(call.args[0]) val tr = exprGen.translateExpression(call.args[0])
addToResult(result, tr, SyscallRegisterBase, -1) addToResult(result, tr, tr.resultReg, -1)
result += IRCodeChunk(null, null).also { val lengthReg = codeGen.registers.nextFree()
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = SyscallRegisterBase+1, value = array.length) addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = lengthReg, immediate = if(array.dt==DataType.STR) array.length!!-1 else array.length), null)
it += IRInstruction(Opcode.SYSCALL, value = syscall.number) result += codeGen.makeSyscall(syscall, listOf(IRDataType.WORD to tr.resultReg, IRDataType.BYTE to lengthReg), null)
}
return ExpressionCodeResult(result, IRDataType.BYTE, -1, -1) 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 result = mutableListOf<IRCodeChunkBase>()
val tr = exprGen.translateExpression(call.args[0]) val tr = exprGen.translateExpression(call.args[0])
addToResult(result, tr, SyscallRegisterBase, -1) addToResult(result, tr, tr.resultReg, -1)
result += IRCodeChunk(null, null).also { val lengthReg = codeGen.registers.nextFree()
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = SyscallRegisterBase+1, value = array.length) addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = lengthReg, immediate = if(array.dt==DataType.STR) array.length!!-1 else array.length), null)
it += IRInstruction(Opcode.SYSCALL, value = syscall.number) result += codeGen.makeSyscall(syscall, listOf(IRDataType.WORD to tr.resultReg, IRDataType.BYTE to lengthReg), null)
}
return ExpressionCodeResult(result, IRDataType.BYTE, -1, -1) 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) { if (call.args[0] is PtNumber) {
val address = (call.args[0] as PtNumber).number.toInt() val address = (call.args[0] as PtNumber).number.toInt()
result += IRCodeChunk(null, null).also { result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.STOREZM, IRDataType.WORD, value = address) it += IRInstruction(Opcode.STOREZM, IRDataType.WORD, address = address)
} }
} else { } else {
val tr = exprGen.translateExpression(call.args[0]) 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]) val tr = exprGen.translateExpression(call.args[1])
addToResult(result, tr, tr.resultReg, -1) addToResult(result, tr, tr.resultReg, -1)
result += IRCodeChunk(null, null).also { 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 { } else {
val addressTr = exprGen.translateExpression(call.args[0]) 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) { if (call.args[0] is PtNumber) {
val address = (call.args[0] as PtNumber).number.toInt() val address = (call.args[0] as PtNumber).number.toInt()
result += IRCodeChunk(null, null).also { result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.STOREZM, IRDataType.BYTE, value = address) it += IRInstruction(Opcode.STOREZM, IRDataType.BYTE, address = address)
} }
} else { } else {
val tr = exprGen.translateExpression(call.args[0]) 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]) val tr = exprGen.translateExpression(call.args[1])
addToResult(result, tr, tr.resultReg, -1) addToResult(result, tr, tr.resultReg, -1)
result += IRCodeChunk(null, null).also { 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 { } else {
val addressTr = exprGen.translateExpression(call.args[0]) 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 resultRegister = codeGen.registers.nextFree()
val address = (call.args[0] as PtNumber).number.toInt() val address = (call.args[0] as PtNumber).number.toInt()
result += IRCodeChunk(null, null).also { 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) ExpressionCodeResult(result, IRDataType.BYTE, resultRegister, -1)
} else { } else {
@ -402,7 +398,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
val resultRegister = codeGen.registers.nextFree() val resultRegister = codeGen.registers.nextFree()
val address = (call.args[0] as PtNumber).number.toInt() val address = (call.args[0] as PtNumber).number.toInt()
result += IRCodeChunk(null, null).also { 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) ExpressionCodeResult(result, IRDataType.BYTE, resultRegister, -1)
} else { } else {
@ -442,7 +438,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
} }
private fun funcRolRor(opcode: Opcode, call: PtBuiltinFunctionCall): ExpressionCodeResult { 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 result = mutableListOf<IRCodeChunkBase>()
val tr = exprGen.translateExpression(call.args[0]) val tr = exprGen.translateExpression(call.args[0])
addToResult(result, tr, tr.resultReg, -1) addToResult(result, tr, tr.resultReg, -1)

View File

@ -28,24 +28,24 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
fun translateExpression(expr: PtExpression): ExpressionCodeResult { fun translateExpression(expr: PtExpression): ExpressionCodeResult {
return when (expr) { return when (expr) {
is PtMachineRegister -> { is PtMachineRegister -> {
ExpressionCodeResult(emptyList(), codeGen.irType(expr.type), expr.register, -1) ExpressionCodeResult(emptyList(), irType(expr.type), expr.register, -1)
} }
is PtNumber -> { is PtNumber -> {
val vmDt = codeGen.irType(expr.type) val vmDt = irType(expr.type)
val code = IRCodeChunk(null, null) val code = IRCodeChunk(null, null)
if(vmDt==IRDataType.FLOAT) { if(vmDt==IRDataType.FLOAT) {
val resultFpRegister = codeGen.registers.nextFreeFloat() 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) ExpressionCodeResult(code, vmDt,-1, resultFpRegister)
} }
else { else {
val resultRegister = codeGen.registers.nextFree() 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) ExpressionCodeResult(code, vmDt, resultRegister, -1)
} }
} }
is PtIdentifier -> { is PtIdentifier -> {
val vmDt = codeGen.irType(expr.type) val vmDt = irType(expr.type)
val code = IRCodeChunk(null, null) val code = IRCodeChunk(null, null)
if (expr.type in PassByValueDatatypes) { if (expr.type in PassByValueDatatypes) {
if(vmDt==IRDataType.FLOAT) { if(vmDt==IRDataType.FLOAT) {
@ -66,7 +66,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
} }
} }
is PtAddressOf -> { is PtAddressOf -> {
val vmDt = codeGen.irType(expr.type) val vmDt = irType(expr.type)
val symbol = expr.identifier.name 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 // 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) val code = IRCodeChunk(null, null)
@ -79,7 +79,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
if(expr.address is PtNumber) { if(expr.address is PtNumber) {
val address = (expr.address as PtNumber).number.toInt() val address = (expr.address as PtNumber).number.toInt()
val resultRegister = codeGen.registers.nextFree() 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) ExpressionCodeResult(result, IRDataType.BYTE, resultRegister, -1)
} else { } else {
val tr = translateExpression(expr.address) val tr = translateExpression(expr.address)
@ -105,48 +105,35 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
private fun translate(check: PtContainmentCheck): ExpressionCodeResult { private fun translate(check: PtContainmentCheck): ExpressionCodeResult {
val result = mutableListOf<IRCodeChunkBase>() 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 val iterable = codeGen.symbolTable.flat.getValue(check.iterable.name) as StStaticVariable
when(iterable.dt) { when(iterable.dt) {
DataType.STR -> { DataType.STR -> {
tr = translateExpression(check.element) val elementTr = translateExpression(check.element)
addToResult(result, tr, SyscallRegisterBase, -1) addToResult(result, elementTr, elementTr.resultReg, -1)
tr = translateExpression(check.iterable) val iterableTr = translateExpression(check.iterable)
addToResult(result, tr, SyscallRegisterBase+1, -1) addToResult(result, iterableTr, iterableTr.resultReg, -1)
val resultReg = codeGen.registers.nextFree() result += codeGen.makeSyscall(IMSyscall.STRING_CONTAINS, listOf(IRDataType.BYTE to elementTr.resultReg, IRDataType.WORD to iterableTr.resultReg), IRDataType.BYTE to elementTr.resultReg)
result += IRCodeChunk(null, null).also { return ExpressionCodeResult(result, IRDataType.BYTE, elementTr.resultReg, -1)
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)
} }
DataType.ARRAY_UB, DataType.ARRAY_B -> { DataType.ARRAY_UB, DataType.ARRAY_B -> {
tr = translateExpression(check.element) val elementTr = translateExpression(check.element)
addToResult(result, tr, SyscallRegisterBase, -1) addToResult(result, elementTr, elementTr.resultReg, -1)
tr = translateExpression(check.iterable) val iterableTr = translateExpression(check.iterable)
addToResult(result, tr, SyscallRegisterBase+1, -1) addToResult(result, iterableTr, iterableTr.resultReg, -1)
val resultReg = codeGen.registers.nextFree() val lengthReg = codeGen.registers.nextFree()
result += IRCodeChunk(null, null).also { addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=lengthReg, immediate = iterable.length!!), null)
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=SyscallRegisterBase+2, value = iterable.length!!) 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)
it += IRInstruction(Opcode.SYSCALL, value = IMSyscall.BYTEARRAY_CONTAINS.number) return ExpressionCodeResult(result, IRDataType.BYTE, elementTr.resultReg, -1)
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)
} }
DataType.ARRAY_UW, DataType.ARRAY_W -> { DataType.ARRAY_UW, DataType.ARRAY_W -> {
tr = translateExpression(check.element) val elementTr = translateExpression(check.element)
addToResult(result, tr, SyscallRegisterBase, -1) addToResult(result, elementTr, elementTr.resultReg, -1)
tr = translateExpression(check.iterable) val iterableTr = translateExpression(check.iterable)
addToResult(result, tr, SyscallRegisterBase+1, -1) addToResult(result, iterableTr, iterableTr.resultReg, -1)
val resultReg = codeGen.registers.nextFree() val lengthReg = codeGen.registers.nextFree()
result += IRCodeChunk(null, null).also { addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=lengthReg, immediate = iterable.length!!), null)
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=SyscallRegisterBase+2, value = iterable.length!!) 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)
it += IRInstruction(Opcode.SYSCALL, value = IMSyscall.WORDARRAY_CONTAINS.number) return ExpressionCodeResult(result, IRDataType.BYTE, elementTr.resultReg, -1)
it += IRInstruction(Opcode.LOADR, IRDataType.BYTE, reg1=resultReg, reg2=0)
}
return ExpressionCodeResult(result, IRDataType.BYTE, resultReg, -1)
} }
DataType.ARRAY_F -> throw AssemblyError("containment check in float-array not supported") DataType.ARRAY_F -> throw AssemblyError("containment check in float-array not supported")
else -> throw AssemblyError("weird iterable dt ${iterable.dt} for ${check.iterable.name}") 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 { private fun translate(arrayIx: PtArrayIndexer): ExpressionCodeResult {
val eltSize = codeGen.program.memsizer.memorySize(arrayIx.type) val eltSize = codeGen.program.memsizer.memorySize(arrayIx.type)
val vmDt = codeGen.irType(arrayIx.type) val vmDt = irType(arrayIx.type)
val result = mutableListOf<IRCodeChunkBase>() val result = mutableListOf<IRCodeChunkBase>()
val arrayVarSymbol = arrayIx.variable.name val arrayVarSymbol = arrayIx.variable.name
@ -205,7 +192,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
val result = mutableListOf<IRCodeChunkBase>() val result = mutableListOf<IRCodeChunkBase>()
val tr = translateExpression(expr.value) val tr = translateExpression(expr.value)
addToResult(result, tr, tr.resultReg, tr.resultFpReg) addToResult(result, tr, tr.resultReg, tr.resultFpReg)
val vmDt = codeGen.irType(expr.type) val vmDt = irType(expr.type)
when(expr.operator) { when(expr.operator) {
"+" -> { } "+" -> { }
"-" -> { "-" -> {
@ -216,7 +203,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
} }
"~" -> { "~" -> {
val mask = if(vmDt==IRDataType.BYTE) 0x00ff else 0xffff 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") else -> throw AssemblyError("weird prefix operator")
} }
@ -321,12 +308,12 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
else -> throw AssemblyError("weird cast type") 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 { private fun translate(binExpr: PtBinaryExpression): ExpressionCodeResult {
require(!codeGen.options.useNewExprCode) require(!codeGen.options.useNewExprCode)
val vmDt = codeGen.irType(binExpr.left.type) val vmDt = irType(binExpr.left.type)
val signed = binExpr.left.type in SignedDatatypes val signed = binExpr.left.type in SignedDatatypes
return when(binExpr.operator) { return when(binExpr.operator) {
"+" -> operatorPlus(binExpr, vmDt) "+" -> operatorPlus(binExpr, vmDt)
@ -353,87 +340,82 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
when (val callTarget = codeGen.symbolTable.flat.getValue(fcall.name)) { when (val callTarget = codeGen.symbolTable.flat.getValue(fcall.name)) {
is StSub -> { is StSub -> {
val result = mutableListOf<IRCodeChunkBase>() val result = mutableListOf<IRCodeChunkBase>()
// assign the arguments
val argRegisters = mutableListOf<FunctionCallArgs.ArgumentSpec>()
for ((arg, parameter) in fcall.args.zip(callTarget.parameters)) { for ((arg, parameter) in fcall.args.zip(callTarget.parameters)) {
val paramDt = codeGen.irType(parameter.type) val paramDt = irType(parameter.type)
val symbol = "${fcall.name}.${parameter.name}" val tr = translateExpression(arg)
if(codeGen.isZero(arg)) { if(paramDt==IRDataType.FLOAT)
addInstr(result, IRInstruction(Opcode.STOREZM, paramDt, labelSymbol = symbol), null) argRegisters.add(FunctionCallArgs.ArgumentSpec(parameter.name, null, FunctionCallArgs.RegSpec(IRDataType.FLOAT, tr.resultFpReg, null)))
} else { else
if (paramDt == IRDataType.FLOAT) { argRegisters.add(FunctionCallArgs.ArgumentSpec(parameter.name, null, FunctionCallArgs.RegSpec(paramDt, tr.resultReg, null)))
val tr = translateExpression(arg) result += tr.chunks
addToResult(result, tr, -1, tr.resultFpReg)
addInstr(result, IRInstruction(Opcode.STOREM, paramDt, fpReg1 = tr.resultFpReg, labelSymbol = symbol), null)
} else {
val tr = translateExpression(arg)
addToResult(result, tr, tr.resultReg, -1)
addInstr(result, IRInstruction(Opcode.STOREM, paramDt, reg1 = tr.resultReg, labelSymbol = symbol), null)
}
}
} }
return if(fcall.void) { // return value
addInstr(result, IRInstruction(Opcode.CALL, labelSymbol = fcall.name), null) 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) ExpressionCodeResult(result, IRDataType.BYTE, -1, -1)
} else { else if(fcall.type==DataType.FLOAT)
var resultReg = -1 ExpressionCodeResult(result, returnRegSpec!!.dt, -1, returnRegSpec.registerNum)
var resultFpReg = -1 else
if(fcall.type==DataType.FLOAT) { ExpressionCodeResult(result, returnRegSpec!!.dt, returnRegSpec.registerNum, -1)
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)
}
} }
is StRomSub -> { is StRomSub -> {
val result = mutableListOf<IRCodeChunkBase>() val result = mutableListOf<IRCodeChunkBase>()
// assign the arguments
val argRegisters = mutableListOf<FunctionCallArgs.ArgumentSpec>()
for ((arg, parameter) in fcall.args.zip(callTarget.parameters)) { for ((arg, parameter) in fcall.args.zip(callTarget.parameters)) {
val paramDt = codeGen.irType(parameter.type) val paramDt = irType(parameter.type)
val paramRegStr = if(parameter.register.registerOrPair!=null) parameter.register.registerOrPair.toString() else parameter.register.statusflag.toString() val tr = translateExpression(arg)
if(codeGen.isZero(arg)) { if(paramDt==IRDataType.FLOAT)
addInstr(result, IRInstruction(Opcode.STOREZCPU, paramDt, labelSymbol = paramRegStr), null) 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 { } else {
if (paramDt == IRDataType.FLOAT) // multiple return values: take the first *register* (not status flag) return value and ignore the rest.
throw AssemblyError("doesn't support float register argument in asm romsub") val returns = callTarget.returns.first { it.register.registerOrPair!=null }
val tr = translateExpression(arg) val returnIrType = irType(returns.type)
addToResult(result, tr, tr.resultReg, -1) if(returnIrType==IRDataType.FLOAT)
addInstr(result, IRInstruction(Opcode.STORECPU, paramDt, reg1 = tr.resultReg, labelSymbol = paramRegStr), null) 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! // create the call
addInstr(result, IRInstruction(Opcode.CALL, value=callTarget.address.toInt()), null) val call =
val resultReg = codeGen.registers.nextFree() if(callTarget.address==null)
if(!fcall.void) { IRInstruction(Opcode.CALL, labelSymbol = fcall.name, fcallArgs = FunctionCallArgs(argRegisters, returnRegSpec))
when(callTarget.returns.size) { else
0 -> throw AssemblyError("expect a return value") IRInstruction(Opcode.CALL, address = callTarget.address!!.toInt(), fcallArgs = FunctionCallArgs(argRegisters, returnRegSpec))
1 -> { addInstr(result, call, null)
if(fcall.type==DataType.FLOAT) return if(fcall.void)
throw AssemblyError("doesn't support float register result in asm romsub") ExpressionCodeResult(result, IRDataType.BYTE, -1, -1)
val returns = callTarget.returns.single() else if(fcall.type==DataType.FLOAT)
val regStr = if(returns.register.registerOrPair!=null) returns.register.registerOrPair.toString() else returns.register.statusflag.toString() ExpressionCodeResult(result, returnRegSpec!!.dt, -1, returnRegSpec.registerNum)
addInstr(result, IRInstruction(Opcode.LOADCPU, codeGen.irType(fcall.type), reg1=resultReg, labelSymbol = regStr), null) else
} ExpressionCodeResult(result, returnRegSpec!!.dt, returnRegSpec.registerNum, -1)
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)
} }
else -> throw AssemblyError("invalid node type") else -> throw AssemblyError("invalid node type")
} }
@ -454,7 +436,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
val resultRegister = codeGen.registers.nextFree() val resultRegister = codeGen.registers.nextFree()
addInstr(result, IRInstruction(Opcode.FCOMP, IRDataType.FLOAT, reg1=resultRegister, fpReg1 = leftTr.resultFpReg, fpReg2 = rightTr.resultFpReg), null) addInstr(result, IRInstruction(Opcode.FCOMP, IRDataType.FLOAT, reg1=resultRegister, fpReg1 = leftTr.resultFpReg, fpReg2 = rightTr.resultFpReg), null)
val zeroRegister = codeGen.registers.nextFree() 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) { val ins = if (signed) {
if (greaterEquals) Opcode.SGES else Opcode.SGTS if (greaterEquals) Opcode.SGES else Opcode.SGTS
} else { } else {
@ -464,21 +446,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
return ExpressionCodeResult(result, IRDataType.BYTE, resultRegister, -1) return ExpressionCodeResult(result, IRDataType.BYTE, resultRegister, -1)
} else { } else {
if(binExpr.left.type==DataType.STR && binExpr.right.type==DataType.STR) { if(binExpr.left.type==DataType.STR && binExpr.right.type==DataType.STR) {
val leftTr = translateExpression(binExpr.left) throw AssemblyError("str compares should have been replaced with builtin function call to do the compare")
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)
} else { } else {
val leftTr = translateExpression(binExpr.left) val leftTr = translateExpression(binExpr.left)
addToResult(result, leftTr, leftTr.resultReg, -1) addToResult(result, leftTr, leftTr.resultReg, -1)
@ -510,7 +478,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
val resultRegister = codeGen.registers.nextFree() val resultRegister = codeGen.registers.nextFree()
addInstr(result, IRInstruction(Opcode.FCOMP, IRDataType.FLOAT, reg1=resultRegister, fpReg1 = leftTr.resultFpReg, fpReg2 = rightTr.resultFpReg), null) addInstr(result, IRInstruction(Opcode.FCOMP, IRDataType.FLOAT, reg1=resultRegister, fpReg1 = leftTr.resultFpReg, fpReg2 = rightTr.resultFpReg), null)
val zeroRegister = codeGen.registers.nextFree() 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) { val ins = if (signed) {
if (lessEquals) Opcode.SLES else Opcode.SLTS if (lessEquals) Opcode.SLES else Opcode.SLTS
} else { } else {
@ -520,23 +488,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
return ExpressionCodeResult(result, IRDataType.BYTE, resultRegister, -1) return ExpressionCodeResult(result, IRDataType.BYTE, resultRegister, -1)
} else { } else {
if(binExpr.left.type==DataType.STR && binExpr.right.type==DataType.STR) { if(binExpr.left.type==DataType.STR && binExpr.right.type==DataType.STR) {
val leftTr = translateExpression(binExpr.left) throw AssemblyError("str compares should have been replaced with builtin function call to do the compare")
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)
} else { } else {
val leftTr = translateExpression(binExpr.left) val leftTr = translateExpression(binExpr.left)
addToResult(result, leftTr, leftTr.resultReg, -1) addToResult(result, leftTr, leftTr.resultReg, -1)
@ -563,32 +515,19 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
val resultRegister = codeGen.registers.nextFree() val resultRegister = codeGen.registers.nextFree()
val valueReg = codeGen.registers.nextFree() val valueReg = codeGen.registers.nextFree()
val label = codeGen.createLabelName() 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) addInstr(result, IRInstruction(Opcode.FCOMP, IRDataType.FLOAT, reg1=valueReg, fpReg1 = leftTr.resultFpReg, fpReg2 = rightTr.resultFpReg), null)
if (notEquals) { 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 { } 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) result += IRCodeChunk(label, null)
return ExpressionCodeResult(result, IRDataType.BYTE, resultRegister, -1) return ExpressionCodeResult(result, IRDataType.BYTE, resultRegister, -1)
} else { } else {
if(binExpr.left.type==DataType.STR && binExpr.right.type==DataType.STR) { if(binExpr.left.type==DataType.STR && binExpr.right.type==DataType.STR) {
val leftTr = translateExpression(binExpr.left) throw AssemblyError("str compares should have been replaced with builtin function call to do the compare")
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)
} else { } else {
return if(constValue(binExpr.right)==0.0) { return if(constValue(binExpr.right)==0.0) {
val tr = translateExpression(binExpr.left) val tr = translateExpression(binExpr.left)
@ -651,7 +590,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
return if(binExpr.right is PtNumber) { return if(binExpr.right is PtNumber) {
val tr = translateExpression(binExpr.left) val tr = translateExpression(binExpr.left)
addToResult(result, tr, tr.resultReg, -1) 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) ExpressionCodeResult(result, vmDt, tr.resultReg, -1)
} else { } else {
val leftTr = translateExpression(binExpr.left) val leftTr = translateExpression(binExpr.left)
@ -668,7 +607,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
return if(binExpr.right is PtNumber) { return if(binExpr.right is PtNumber) {
val tr = translateExpression(binExpr.left) val tr = translateExpression(binExpr.left)
addToResult(result, tr, tr.resultReg, -1) 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) ExpressionCodeResult(result, vmDt, tr.resultReg, -1)
} else { } else {
val leftTr = translateExpression(binExpr.left) val leftTr = translateExpression(binExpr.left)
@ -685,7 +624,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
return if(binExpr.right is PtNumber) { return if(binExpr.right is PtNumber) {
val tr = translateExpression(binExpr.left) val tr = translateExpression(binExpr.left)
addToResult(result, tr, tr.resultReg, -1) 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) ExpressionCodeResult(result, vmDt, tr.resultReg, -1)
} else { } else {
val leftTr = translateExpression(binExpr.left) val leftTr = translateExpression(binExpr.left)
@ -703,7 +642,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
return if(binExpr.right is PtNumber) { return if(binExpr.right is PtNumber) {
val tr = translateExpression(binExpr.left) val tr = translateExpression(binExpr.left)
addToResult(result, tr, tr.resultReg, -1) 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) ExpressionCodeResult(result, vmDt, tr.resultReg, -1)
} else { } else {
val leftTr = translateExpression(binExpr.left) val leftTr = translateExpression(binExpr.left)
@ -749,9 +688,9 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
val leftTr = translateExpression(binExpr.left) val leftTr = translateExpression(binExpr.left)
addToResult(result, leftTr, leftTr.resultReg, -1) addToResult(result, leftTr, leftTr.resultReg, -1)
addInstr(result, if (signed) 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 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) , null)
ExpressionCodeResult(result, vmDt, leftTr.resultReg, -1) ExpressionCodeResult(result, vmDt, leftTr.resultReg, -1)
} else { } else {
@ -832,7 +771,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
return if(binExpr.right is PtNumber) { return if(binExpr.right is PtNumber) {
val tr = translateExpression(binExpr.left) val tr = translateExpression(binExpr.left)
addToResult(result, tr, -1, tr.resultFpReg) 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) ExpressionCodeResult(result, vmDt, -1, tr.resultFpReg)
} else { } else {
val leftTr = translateExpression(binExpr.left) val leftTr = translateExpression(binExpr.left)
@ -852,9 +791,9 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
} }
else { else {
return if(binExpr.right is PtNumber) { return if(binExpr.right is PtNumber) {
val tr = translateExpression(binExpr.left,) val tr = translateExpression(binExpr.left)
addToResult(result, tr, tr.resultReg, -1) 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) ExpressionCodeResult(result, vmDt, tr.resultReg, -1)
} else { } else {
val leftTr = translateExpression(binExpr.left) val leftTr = translateExpression(binExpr.left)
@ -887,7 +826,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
return if(binExpr.right is PtNumber) { return if(binExpr.right is PtNumber) {
val tr = translateExpression(binExpr.left) val tr = translateExpression(binExpr.left)
addToResult(result, tr, -1, tr.resultFpReg) 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) ExpressionCodeResult(result, vmDt, -1, tr.resultFpReg)
} else { } else {
val leftTr = translateExpression(binExpr.left) val leftTr = translateExpression(binExpr.left)
@ -915,7 +854,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
return if(binExpr.right is PtNumber) { return if(binExpr.right is PtNumber) {
val tr = translateExpression(binExpr.left) val tr = translateExpression(binExpr.left)
addToResult(result, tr, tr.resultReg, -1) 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) ExpressionCodeResult(result, vmDt, tr.resultReg, -1)
} else { } else {
val leftTr = translateExpression(binExpr.left) val leftTr = translateExpression(binExpr.left)
@ -935,7 +874,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
val tr = translateExpression(operand) val tr = translateExpression(operand)
addToResult(result, tr, tr.resultReg, -1) addToResult(result, tr, tr.resultReg, -1)
addInstr(result, if(knownAddress!=null) addInstr(result, if(knownAddress!=null)
IRInstruction(Opcode.ANDM, vmDt, reg1=tr.resultReg, value=knownAddress) IRInstruction(Opcode.ANDM, vmDt, reg1=tr.resultReg, address = knownAddress)
else else
IRInstruction(Opcode.ANDM, vmDt, reg1=tr.resultReg, labelSymbol = symbol) IRInstruction(Opcode.ANDM, vmDt, reg1=tr.resultReg, labelSymbol = symbol)
,null) ,null)
@ -947,7 +886,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
val tr = translateExpression(operand) val tr = translateExpression(operand)
addToResult(result, tr, tr.resultReg, -1) addToResult(result, tr, tr.resultReg, -1)
addInstr(result, if(knownAddress!=null) addInstr(result, if(knownAddress!=null)
IRInstruction(Opcode.ORM, vmDt, reg1=tr.resultReg, value = knownAddress) IRInstruction(Opcode.ORM, vmDt, reg1=tr.resultReg, address = knownAddress)
else else
IRInstruction(Opcode.ORM, vmDt, reg1=tr.resultReg, labelSymbol = symbol) IRInstruction(Opcode.ORM, vmDt, reg1=tr.resultReg, labelSymbol = symbol)
, null) , null)
@ -966,13 +905,13 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
addToResult(result, tr, -1, tr.resultFpReg) addToResult(result, tr, -1, tr.resultFpReg)
val ins = if(signed) { val ins = if(signed) {
if(knownAddress!=null) if(knownAddress!=null)
IRInstruction(Opcode.DIVSM, vmDt, fpReg1 = tr.resultFpReg, value = knownAddress) IRInstruction(Opcode.DIVSM, vmDt, fpReg1 = tr.resultFpReg, address = knownAddress)
else else
IRInstruction(Opcode.DIVSM, vmDt, fpReg1 = tr.resultFpReg, labelSymbol = symbol) IRInstruction(Opcode.DIVSM, vmDt, fpReg1 = tr.resultFpReg, labelSymbol = symbol)
} }
else { else {
if(knownAddress!=null) if(knownAddress!=null)
IRInstruction(Opcode.DIVM, vmDt, fpReg1 = tr.resultFpReg, value = knownAddress) IRInstruction(Opcode.DIVM, vmDt, fpReg1 = tr.resultFpReg, address = knownAddress)
else else
IRInstruction(Opcode.DIVM, vmDt, fpReg1 = tr.resultFpReg, labelSymbol = symbol) 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) addToResult(result, tr, tr.resultReg, -1)
val ins = if(signed) { val ins = if(signed) {
if(knownAddress!=null) if(knownAddress!=null)
IRInstruction(Opcode.DIVSM, vmDt, reg1 = tr.resultReg, value = knownAddress) IRInstruction(Opcode.DIVSM, vmDt, reg1 = tr.resultReg, address = knownAddress)
else else
IRInstruction(Opcode.DIVSM, vmDt, reg1 = tr.resultReg, labelSymbol = symbol) IRInstruction(Opcode.DIVSM, vmDt, reg1 = tr.resultReg, labelSymbol = symbol)
} }
else { else {
if(knownAddress!=null) if(knownAddress!=null)
IRInstruction(Opcode.DIVM, vmDt, reg1 = tr.resultReg, value = knownAddress) IRInstruction(Opcode.DIVM, vmDt, reg1 = tr.resultReg, address = knownAddress)
else else
IRInstruction(Opcode.DIVM, vmDt, reg1 = tr.resultReg, labelSymbol = symbol) IRInstruction(Opcode.DIVM, vmDt, reg1 = tr.resultReg, labelSymbol = symbol)
} }
@ -1014,7 +953,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
val tr = translateExpression(operand) val tr = translateExpression(operand)
addToResult(result, tr, -1, tr.resultFpReg) addToResult(result, tr, -1, tr.resultFpReg)
addInstr(result, if(knownAddress!=null) addInstr(result, if(knownAddress!=null)
IRInstruction(Opcode.MULM, vmDt, fpReg1 = tr.resultFpReg, value = knownAddress) IRInstruction(Opcode.MULM, vmDt, fpReg1 = tr.resultFpReg, address = knownAddress)
else else
IRInstruction(Opcode.MULM, vmDt, fpReg1 = tr.resultFpReg, labelSymbol = symbol) IRInstruction(Opcode.MULM, vmDt, fpReg1 = tr.resultFpReg, labelSymbol = symbol)
, null) , null)
@ -1027,7 +966,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
val tr = translateExpression(operand) val tr = translateExpression(operand)
addToResult(result, tr, tr.resultReg, -1) addToResult(result, tr, tr.resultReg, -1)
addInstr(result, if(knownAddress!=null) addInstr(result, if(knownAddress!=null)
IRInstruction(Opcode.MULM, vmDt, reg1=tr.resultReg, value = knownAddress) IRInstruction(Opcode.MULM, vmDt, reg1=tr.resultReg, address = knownAddress)
else else
IRInstruction(Opcode.MULM, vmDt, reg1=tr.resultReg, labelSymbol = symbol) IRInstruction(Opcode.MULM, vmDt, reg1=tr.resultReg, labelSymbol = symbol)
, null) , null)
@ -1041,7 +980,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
if(vmDt==IRDataType.FLOAT) { if(vmDt==IRDataType.FLOAT) {
if((operand as? PtNumber)?.number==1.0) { if((operand as? PtNumber)?.number==1.0) {
addInstr(result, if(knownAddress!=null) addInstr(result, if(knownAddress!=null)
IRInstruction(Opcode.DECM, vmDt, value=knownAddress) IRInstruction(Opcode.DECM, vmDt, address = knownAddress)
else else
IRInstruction(Opcode.DECM, vmDt, labelSymbol = symbol) IRInstruction(Opcode.DECM, vmDt, labelSymbol = symbol)
, null) , null)
@ -1050,7 +989,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
val tr = translateExpression(operand) val tr = translateExpression(operand)
addToResult(result, tr, -1, tr.resultFpReg) addToResult(result, tr, -1, tr.resultFpReg)
addInstr(result, if(knownAddress!=null) addInstr(result, if(knownAddress!=null)
IRInstruction(Opcode.SUBM, vmDt, fpReg1=tr.resultFpReg, value=knownAddress) IRInstruction(Opcode.SUBM, vmDt, fpReg1=tr.resultFpReg, address = knownAddress)
else else
IRInstruction(Opcode.SUBM, vmDt, fpReg1=tr.resultFpReg, labelSymbol = symbol) IRInstruction(Opcode.SUBM, vmDt, fpReg1=tr.resultFpReg, labelSymbol = symbol)
, null) , null)
@ -1058,7 +997,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
} else { } else {
if((operand as? PtNumber)?.number==1.0) { if((operand as? PtNumber)?.number==1.0) {
addInstr(result, if(knownAddress!=null) addInstr(result, if(knownAddress!=null)
IRInstruction(Opcode.DECM, vmDt, value=knownAddress) IRInstruction(Opcode.DECM, vmDt, address = knownAddress)
else else
IRInstruction(Opcode.DECM, vmDt, labelSymbol = symbol) IRInstruction(Opcode.DECM, vmDt, labelSymbol = symbol)
, null) , null)
@ -1067,7 +1006,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
val tr = translateExpression(operand) val tr = translateExpression(operand)
addToResult(result, tr, tr.resultReg, -1) addToResult(result, tr, tr.resultReg, -1)
addInstr(result, if(knownAddress!=null) addInstr(result, if(knownAddress!=null)
IRInstruction(Opcode.SUBM, vmDt, reg1=tr.resultReg, value = knownAddress) IRInstruction(Opcode.SUBM, vmDt, reg1=tr.resultReg, address = knownAddress)
else else
IRInstruction(Opcode.SUBM, vmDt, reg1=tr.resultReg, labelSymbol = symbol) IRInstruction(Opcode.SUBM, vmDt, reg1=tr.resultReg, labelSymbol = symbol)
, null) , null)
@ -1081,7 +1020,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
if(vmDt==IRDataType.FLOAT) { if(vmDt==IRDataType.FLOAT) {
if((operand as? PtNumber)?.number==1.0) { if((operand as? PtNumber)?.number==1.0) {
addInstr(result, if(knownAddress!=null) addInstr(result, if(knownAddress!=null)
IRInstruction(Opcode.INCM, vmDt, value = knownAddress) IRInstruction(Opcode.INCM, vmDt, address = knownAddress)
else else
IRInstruction(Opcode.INCM, vmDt, labelSymbol = symbol) IRInstruction(Opcode.INCM, vmDt, labelSymbol = symbol)
, null) , null)
@ -1090,7 +1029,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
val tr = translateExpression(operand) val tr = translateExpression(operand)
addToResult(result, tr, -1, tr.resultFpReg) addToResult(result, tr, -1, tr.resultFpReg)
addInstr(result, if(knownAddress!=null) addInstr(result, if(knownAddress!=null)
IRInstruction(Opcode.ADDM, vmDt, fpReg1=tr.resultFpReg, value = knownAddress) IRInstruction(Opcode.ADDM, vmDt, fpReg1=tr.resultFpReg, address = knownAddress)
else else
IRInstruction(Opcode.ADDM, vmDt, fpReg1=tr.resultFpReg, labelSymbol = symbol) IRInstruction(Opcode.ADDM, vmDt, fpReg1=tr.resultFpReg, labelSymbol = symbol)
, null) , null)
@ -1098,7 +1037,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
} else { } else {
if((operand as? PtNumber)?.number==1.0) { if((operand as? PtNumber)?.number==1.0) {
addInstr(result, if(knownAddress!=null) addInstr(result, if(knownAddress!=null)
IRInstruction(Opcode.INCM, vmDt, value = knownAddress) IRInstruction(Opcode.INCM, vmDt, address = knownAddress)
else else
IRInstruction(Opcode.INCM, vmDt, labelSymbol = symbol) IRInstruction(Opcode.INCM, vmDt, labelSymbol = symbol)
, null) , null)
@ -1107,7 +1046,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
val tr = translateExpression(operand) val tr = translateExpression(operand)
addToResult(result, tr, tr.resultReg, -1) addToResult(result, tr, tr.resultReg, -1)
addInstr(result, if(knownAddress!=null) addInstr(result, if(knownAddress!=null)
IRInstruction(Opcode.ADDM, vmDt, reg1=tr.resultReg, value=knownAddress) IRInstruction(Opcode.ADDM, vmDt, reg1=tr.resultReg, address = knownAddress)
else else
IRInstruction(Opcode.ADDM, vmDt, reg1=tr.resultReg, labelSymbol = symbol) IRInstruction(Opcode.ADDM, vmDt, reg1=tr.resultReg, labelSymbol = symbol)
, null) , null)
@ -1121,7 +1060,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
if(codeGen.isOne(operand)) { if(codeGen.isOne(operand)) {
val opc = if (signed) Opcode.ASRM else Opcode.LSRM val opc = if (signed) Opcode.ASRM else Opcode.LSRM
val ins = if(knownAddress!=null) val ins = if(knownAddress!=null)
IRInstruction(opc, vmDt, value=knownAddress) IRInstruction(opc, vmDt, address = knownAddress)
else else
IRInstruction(opc, vmDt, labelSymbol = symbol) IRInstruction(opc, vmDt, labelSymbol = symbol)
addInstr(result, ins, null) addInstr(result, ins, null)
@ -1130,7 +1069,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
addToResult(result, tr, tr.resultReg, -1) addToResult(result, tr, tr.resultReg, -1)
val opc = if (signed) Opcode.ASRNM else Opcode.LSRNM val opc = if (signed) Opcode.ASRNM else Opcode.LSRNM
val ins = if(knownAddress!=null) val ins = if(knownAddress!=null)
IRInstruction(opc, vmDt, reg1 = tr.resultReg, value=knownAddress) IRInstruction(opc, vmDt, reg1 = tr.resultReg, address = knownAddress)
else else
IRInstruction(opc, vmDt, reg1 = tr.resultReg, labelSymbol = symbol) IRInstruction(opc, vmDt, reg1 = tr.resultReg, labelSymbol = symbol)
addInstr(result, ins, null) addInstr(result, ins, null)
@ -1142,7 +1081,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
val result = mutableListOf<IRCodeChunkBase>() val result = mutableListOf<IRCodeChunkBase>()
if(codeGen.isOne(operand)){ if(codeGen.isOne(operand)){
addInstr(result, if(knownAddress!=null) addInstr(result, if(knownAddress!=null)
IRInstruction(Opcode.LSLM, vmDt, value=knownAddress) IRInstruction(Opcode.LSLM, vmDt, address = knownAddress)
else else
IRInstruction(Opcode.LSLM, vmDt, labelSymbol = symbol) IRInstruction(Opcode.LSLM, vmDt, labelSymbol = symbol)
, null) , null)
@ -1150,7 +1089,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
val tr = translateExpression(operand) val tr = translateExpression(operand)
addToResult(result, tr, tr.resultReg, -1) addToResult(result, tr, tr.resultReg, -1)
addInstr(result, if(knownAddress!=null) addInstr(result, if(knownAddress!=null)
IRInstruction(Opcode.LSLNM, vmDt, reg1=tr.resultReg, value=knownAddress) IRInstruction(Opcode.LSLNM, vmDt, reg1=tr.resultReg, address = knownAddress)
else else
IRInstruction(Opcode.LSLNM, vmDt, reg1=tr.resultReg, labelSymbol = symbol) IRInstruction(Opcode.LSLNM, vmDt, reg1=tr.resultReg, labelSymbol = symbol)
,null) ,null)
@ -1163,7 +1102,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
val tr = translateExpression(operand) val tr = translateExpression(operand)
addToResult(result, tr, tr.resultReg, -1) addToResult(result, tr, tr.resultReg, -1)
addInstr(result, if(knownAddress!=null) addInstr(result, if(knownAddress!=null)
IRInstruction(Opcode.XORM, vmDt, reg1=tr.resultReg, value = knownAddress) IRInstruction(Opcode.XORM, vmDt, reg1=tr.resultReg, address = knownAddress)
else else
IRInstruction(Opcode.XORM, vmDt, reg1=tr.resultReg, labelSymbol = symbol) IRInstruction(Opcode.XORM, vmDt, reg1=tr.resultReg, labelSymbol = symbol)
,null) ,null)
@ -1178,15 +1117,15 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
if (knownAddress != null) { if (knownAddress != null) {
// @(address) = @(address) %= operand // @(address) = @(address) %= operand
result += IRCodeChunk(null, null).also { 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.MOD, vmDt, reg1 = resultReg, value = number) it += IRInstruction(Opcode.MOD, vmDt, reg1 = resultReg, immediate = number)
it += IRInstruction(Opcode.STOREM, vmDt, reg1 = resultReg, value = knownAddress) it += IRInstruction(Opcode.STOREM, vmDt, reg1 = resultReg, address = knownAddress)
} }
} else { } else {
// symbol = symbol %= operand // symbol = symbol %= operand
result += IRCodeChunk(null, null).also { result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOADM, vmDt, reg1 = resultReg, labelSymbol = symbol) 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) it += IRInstruction(Opcode.STOREM, vmDt, reg1 = resultReg, labelSymbol = symbol)
} }
} }
@ -1196,9 +1135,9 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
if (knownAddress != null) { if (knownAddress != null) {
// @(address) = @(address) %= operand // @(address) = @(address) %= operand
result += IRCodeChunk(null, null).also { 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.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 { } else {
// symbol = symbol %= operand // symbol = symbol %= operand
@ -1279,16 +1218,16 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
if (knownAddress != null) { if (knownAddress != null) {
// in-place modify a memory location // in-place modify a memory location
result += IRCodeChunk(null, null).also { 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(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(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 { } else {
// in-place modify a symbol (variable) // in-place modify a symbol (variable)
result += IRCodeChunk(null, null).also { result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOADM, vmDt, reg1 = valueReg, labelSymbol = symbol) 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(compareAndSetOpcode, vmDt, reg1 = valueReg, reg2 = numberReg)
it += IRInstruction(Opcode.STOREM, vmDt, reg1 = valueReg, labelSymbol = symbol) it += IRInstruction(Opcode.STOREM, vmDt, reg1 = valueReg, labelSymbol = symbol)
} }
@ -1299,9 +1238,9 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
if (knownAddress != null) { if (knownAddress != null) {
// in-place modify a memory location // in-place modify a memory location
result += IRCodeChunk(null, null).also { 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(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 { } else {
// in-place modify a symbol (variable) // in-place modify a symbol (variable)
@ -1330,21 +1269,21 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
if (knownAddress != null) { if (knownAddress != null) {
// in-place modify a memory location // in-place modify a memory location
result += IRCodeChunk(null, null).also { 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.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.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(compareAndSetOpcode, IRDataType.BYTE, reg1=cmpReg, reg2=zeroReg)
it += IRInstruction(Opcode.FFROMUB, IRDataType.FLOAT, reg1=cmpReg, fpReg1 = valueReg) 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 { } else {
// in-place modify a symbol (variable) // in-place modify a symbol (variable)
result += IRCodeChunk(null, null).also { result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOADM, IRDataType.FLOAT, fpReg1 = valueReg, labelSymbol = symbol) 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.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(compareAndSetOpcode, IRDataType.BYTE, reg1=cmpReg, reg2=zeroReg)
it += IRInstruction(Opcode.FFROMUB, IRDataType.FLOAT, reg1=cmpReg, fpReg1 = valueReg) it += IRInstruction(Opcode.FFROMUB, IRDataType.FLOAT, reg1=cmpReg, fpReg1 = valueReg)
it += IRInstruction(Opcode.STOREM, IRDataType.FLOAT, fpReg1 = valueReg, labelSymbol = symbol) it += IRInstruction(Opcode.STOREM, IRDataType.FLOAT, fpReg1 = valueReg, labelSymbol = symbol)
@ -1356,19 +1295,19 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
if (knownAddress != null) { if (knownAddress != null) {
// in-place modify a memory location // in-place modify a memory location
result += IRCodeChunk(null, null).also { 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.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(compareAndSetOpcode, IRDataType.BYTE, reg1=cmpReg, reg2=zeroReg)
it += IRInstruction(Opcode.FFROMUB, IRDataType.FLOAT, reg1=cmpReg, fpReg1 = valueReg) 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 { } else {
// in-place modify a symbol (variable) // in-place modify a symbol (variable)
result += IRCodeChunk(null, null).also { result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOADM, IRDataType.FLOAT, fpReg1 = valueReg, labelSymbol = symbol) 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.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(compareAndSetOpcode, IRDataType.BYTE, reg1=cmpReg, reg2=zeroReg)
it += IRInstruction(Opcode.FFROMUB, IRDataType.FLOAT, reg1=cmpReg, fpReg1 = valueReg) it += IRInstruction(Opcode.FFROMUB, IRDataType.FLOAT, reg1=cmpReg, fpReg1 = valueReg)
it += IRInstruction(Opcode.STOREM, IRDataType.FLOAT, fpReg1 = valueReg, labelSymbol = symbol) it += IRInstruction(Opcode.STOREM, IRDataType.FLOAT, fpReg1 = valueReg, labelSymbol = symbol)

View File

@ -1,10 +1,39 @@
package prog8.codegen.intermediate package prog8.codegen.intermediate
import prog8.code.core.IErrorReporter
import prog8.intermediate.* import prog8.intermediate.*
internal class IRPeepholeOptimizer(private val irprog: IRProgram) { class IRPeepholeOptimizer(private val irprog: IRProgram) {
fun optimize() { 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 -> 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) removeEmptyChunks(sub)
joinChunks(sub) joinChunks(sub)
sub.chunks.withIndex().forEach { (index, chunk1) -> sub.chunks.withIndex().forEach { (index, chunk1) ->
@ -28,7 +57,7 @@ internal class IRPeepholeOptimizer(private val irprog: IRProgram) {
removeEmptyChunks(sub) removeEmptyChunks(sub)
} }
irprog.linkChunks() // re-link irprog.linkChunks() // re-link
} }
private fun removeEmptyChunks(sub: IRSubroutine) { private fun removeEmptyChunks(sub: IRSubroutine) {
@ -38,7 +67,7 @@ internal class IRPeepholeOptimizer(private val irprog: IRProgram) {
/* /*
Empty Code chunk with label -> Empty Code chunk with label ->
If next chunk has no label -> move label to next chunk, remove original 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. If is last chunk -> keep chunk in place because of the label.
Empty Code chunk without label -> Empty Code chunk without label ->
should not have been generated! ERROR. should not have been generated! ERROR.
@ -63,7 +92,7 @@ internal class IRPeepholeOptimizer(private val irprog: IRProgram) {
if (chunk.label == nextchunk.label) if (chunk.label == nextchunk.label)
removeChunks += index removeChunks += index
else { 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()) if(sub.chunks.isEmpty())
return return
fun mayJoin(previous: IRCodeChunkBase, chunk: IRCodeChunkBase): Boolean { fun mayJoinCodeChunks(previous: IRCodeChunkBase, chunk: IRCodeChunkBase): Boolean {
if(chunk.label!=null) if(chunk.label!=null)
return false return false
if(previous is IRCodeChunk && chunk is IRCodeChunk) { if(previous is IRCodeChunk && chunk is IRCodeChunk) {
@ -102,12 +131,39 @@ internal class IRPeepholeOptimizer(private val irprog: IRProgram) {
chunks += sub.chunks[0] chunks += sub.chunks[0]
for(ix in 1 until sub.chunks.size) { for(ix in 1 until sub.chunks.size) {
val lastChunk = chunks.last() val lastChunk = chunks.last()
if(mayJoin(lastChunk, sub.chunks[ix])) { val candidate = sub.chunks[ix]
lastChunk.instructions += sub.chunks[ix].instructions when(candidate) {
lastChunk.next = sub.chunks[ix].next 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.clear()
sub.chunks += chunks sub.chunks += chunks
@ -176,7 +232,7 @@ internal class IRPeepholeOptimizer(private val irprog: IRProgram) {
} }
// remove useless RETURN // 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] val previous = chunk.instructions[idx-1]
if(previous.opcode in OpcodesThatJump) { if(previous.opcode in OpcodesThatJump) {
chunk.instructions.removeAt(idx) chunk.instructions.removeAt(idx)
@ -194,14 +250,16 @@ internal class IRPeepholeOptimizer(private val irprog: IRProgram) {
} }
// replace call + return --> jump // replace call + return --> jump
if(idx>0 && ins.opcode==Opcode.RETURN) { // This can no longer be done here on the IR level, with the current CALL opcode that encodes the full subroutine call setup.
val previous = chunk.instructions[idx-1] // If machine code is ever generated from this IR, *that* should possibly optimize the JSR + RTS into a JMP.
if(previous.opcode==Opcode.CALL || previous.opcode==Opcode.CALLRVAL) { // if(idx>0 && ins.opcode==Opcode.RETURN) {
chunk.instructions[idx-1] = IRInstruction(Opcode.JUMP, value=previous.value, labelSymbol = previous.labelSymbol, branchTarget = previous.branchTarget) // val previous = chunk.instructions[idx-1]
chunk.instructions.removeAt(idx) // if(previous.opcode==Opcode.CALL) {
changed = true // chunk.instructions[idx-1] = IRInstruction(Opcode.JUMP, address = previous.address, labelSymbol = previous.labelSymbol, branchTarget = previous.branchTarget)
} // chunk.instructions.removeAt(idx)
} // changed = true
// }
// }
} }
return changed return changed
} }
@ -212,47 +270,47 @@ internal class IRPeepholeOptimizer(private val irprog: IRProgram) {
indexedInstructions.reversed().forEach { (idx, ins) -> indexedInstructions.reversed().forEach { (idx, ins) ->
when (ins.opcode) { when (ins.opcode) {
Opcode.DIV, Opcode.DIVS, Opcode.MUL, Opcode.MOD -> { Opcode.DIV, Opcode.DIVS, Opcode.MUL, Opcode.MOD -> {
if (ins.value == 1) { if (ins.immediate == 1) {
chunk.instructions.removeAt(idx) chunk.instructions.removeAt(idx)
changed = true changed = true
} }
} }
Opcode.ADD, Opcode.SUB -> { Opcode.ADD, Opcode.SUB -> {
if (ins.value == 1) { if (ins.immediate == 1) {
chunk.instructions[idx] = IRInstruction( chunk.instructions[idx] = IRInstruction(
if (ins.opcode == Opcode.ADD) Opcode.INC else Opcode.DEC, if (ins.opcode == Opcode.ADD) Opcode.INC else Opcode.DEC,
ins.type, ins.type,
ins.reg1 ins.reg1
) )
changed = true changed = true
} else if (ins.value == 0) { } else if (ins.immediate == 0) {
chunk.instructions.removeAt(idx) chunk.instructions.removeAt(idx)
changed = true changed = true
} }
} }
Opcode.AND -> { Opcode.AND -> {
if (ins.value == 0) { if (ins.immediate == 0) {
chunk.instructions[idx] = IRInstruction(Opcode.LOAD, ins.type, reg1 = ins.reg1, value = 0) chunk.instructions[idx] = IRInstruction(Opcode.LOAD, ins.type, reg1 = ins.reg1, immediate = 0)
changed = true changed = true
} else if (ins.value == 255 && ins.type == IRDataType.BYTE) { } else if (ins.immediate == 255 && ins.type == IRDataType.BYTE) {
chunk.instructions.removeAt(idx) chunk.instructions.removeAt(idx)
changed = true changed = true
} else if (ins.value == 65535 && ins.type == IRDataType.WORD) { } else if (ins.immediate == 65535 && ins.type == IRDataType.WORD) {
chunk.instructions.removeAt(idx) chunk.instructions.removeAt(idx)
changed = true changed = true
} }
} }
Opcode.OR -> { Opcode.OR -> {
if (ins.value == 0) { if (ins.immediate == 0) {
chunk.instructions.removeAt(idx) chunk.instructions.removeAt(idx)
changed = true changed = true
} else if ((ins.value == 255 && ins.type == IRDataType.BYTE) || (ins.value == 65535 && ins.type == IRDataType.WORD)) { } 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, value = ins.value) chunk.instructions[idx] = IRInstruction(Opcode.LOAD, ins.type, reg1 = ins.reg1, immediate = ins.immediate)
changed = true changed = true
} }
} }
Opcode.XOR -> { Opcode.XOR -> {
if (ins.value == 0) { if (ins.immediate == 0) {
chunk.instructions.removeAt(idx) chunk.instructions.removeAt(idx)
changed = true changed = true
} }

View File

@ -5,35 +5,18 @@ import prog8.code.core.SourceCode.Companion.libraryFilePrefix
import prog8.intermediate.* 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 { fun optimize(): Int {
val allLabeledChunks = mutableMapOf<String, IRCodeChunkBase>() var numRemoved = removeUnusedSubroutines() + removeUnusedAsmSubroutines()
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++
}
}
}
// remove empty blocks // remove empty blocks
irprog.blocks.reversed().forEach { block -> irprog.blocks.reversed().forEach { block ->
if(block.isEmpty()) { if(block.isEmpty()) {
irprog.blocks.remove(block) irprog.blocks.remove(block)
irprog.st.removeTree(block.label)
numRemoved++ numRemoved++
} }
} }
@ -41,8 +24,106 @@ internal class IRUnusedCodeRemover(private val irprog: IRProgram, private val er
return numRemoved 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 { 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()) val reachable = mutableSetOf((entrypointSub as IRSubroutine).chunks.first())
fun grow() { fun grow() {
@ -92,7 +173,7 @@ internal class IRUnusedCodeRemover(private val irprog: IRProgram, private val er
} }
private fun removeUnlinkedChunks( private fun removeUnlinkedChunks(
linkedChunks: MutableSet<IRCodeChunkBase> linkedChunks: Set<IRCodeChunkBase>
): Int { ): Int {
var numRemoved = 0 var numRemoved = 0
irprog.blocks.asSequence().flatMap { it.children.filterIsInstance<IRSubroutine>() }.forEach { sub -> irprog.blocks.asSequence().flatMap { it.children.filterIsInstance<IRSubroutine>() }.forEach { sub ->

View File

@ -1,11 +1,7 @@
package prog8.codegen.intermediate package prog8.codegen.intermediate
import prog8.code.core.AssemblyError
import prog8.intermediate.SyscallRegisterBase
internal class RegisterPool { internal class RegisterPool {
// reserve 0,1,2 for return values of subroutine calls and syscalls // reserve 0,1,2 for return values of subroutine calls and syscalls in IR assembly code
// TODO set this back to 0 once 'resultRegister' has been removed everywhere and SYSCALL/DIVMOD fixed?
private var firstFree: Int=3 private var firstFree: Int=3
private var firstFreeFloat: Int=3 private var firstFreeFloat: Int=3
@ -15,16 +11,12 @@ internal class RegisterPool {
fun nextFree(): Int { fun nextFree(): Int {
val result = firstFree val result = firstFree
firstFree++ firstFree++
if(firstFree >= SyscallRegisterBase)
throw AssemblyError("out of virtual registers (int)")
return result return result
} }
fun nextFreeFloat(): Int { fun nextFreeFloat(): Int {
val result = firstFreeFloat val result = firstFreeFloat
firstFreeFloat++ firstFreeFloat++
if(firstFreeFloat >= SyscallRegisterBase)
throw AssemblyError("out of virtual registers (fp)")
return result return result
} }
} }

View File

@ -2,7 +2,10 @@ package prog8.codegen.vm
import prog8.code.SymbolTable import prog8.code.SymbolTable
import prog8.code.ast.PtProgram 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.codegen.intermediate.IRCodeGen
import prog8.intermediate.IRFileWriter import prog8.intermediate.IRFileWriter
import prog8.intermediate.IRProgram import prog8.intermediate.IRProgram

View File

@ -8,7 +8,7 @@ import prog8.intermediate.*
class TestIRPeepholeOpt: FunSpec({ class TestIRPeepholeOpt: FunSpec({
fun makeIRProgram(chunks: List<IRCodeChunkBase>): IRProgram { fun makeIRProgram(chunks: List<IRCodeChunkBase>): IRProgram {
require(chunks.first().label=="main.start") 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) val sub = IRSubroutine("main.start", emptyList(), null, Position.DUMMY)
chunks.forEach { sub += it } chunks.forEach { sub += it }
block += sub block += sub
@ -40,13 +40,13 @@ class TestIRPeepholeOpt: FunSpec({
test("remove nops") { test("remove nops") {
val irProg = makeIRProgram(listOf( 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),
IRInstruction(Opcode.NOP) IRInstruction(Opcode.NOP)
)) ))
irProg.chunks().single().instructions.size shouldBe 3 irProg.chunks().single().instructions.size shouldBe 3
val opt = IRPeepholeOptimizer(irProg) val opt = IRPeepholeOptimizer(irProg)
opt.optimize() opt.optimize(true, ErrorReporterForTests())
irProg.chunks().single().instructions.size shouldBe 1 irProg.chunks().single().instructions.size shouldBe 1
} }
@ -65,7 +65,7 @@ class TestIRPeepholeOpt: FunSpec({
irProg.chunks().size shouldBe 4 irProg.chunks().size shouldBe 4
irProg.chunks().flatMap { it.instructions }.size shouldBe 5 irProg.chunks().flatMap { it.instructions }.size shouldBe 5
val opt = IRPeepholeOptimizer(irProg) val opt = IRPeepholeOptimizer(irProg)
opt.optimize() opt.optimize(true, ErrorReporterForTests())
irProg.chunks().size shouldBe 4 irProg.chunks().size shouldBe 4
irProg.chunks()[0].label shouldBe "main.start" irProg.chunks()[0].label shouldBe "main.start"
irProg.chunks()[1].label shouldBe "label" irProg.chunks()[1].label shouldBe "label"
@ -92,7 +92,7 @@ class TestIRPeepholeOpt: FunSpec({
)) ))
irProg.chunks().single().instructions.size shouldBe 6 irProg.chunks().single().instructions.size shouldBe 6
val opt = IRPeepholeOptimizer(irProg) val opt = IRPeepholeOptimizer(irProg)
opt.optimize() opt.optimize(true, ErrorReporterForTests())
val instr = irProg.chunks().single().instructions val instr = irProg.chunks().single().instructions
instr.size shouldBe 1 instr.size shouldBe 1
instr[0].opcode shouldBe Opcode.CLC instr[0].opcode shouldBe Opcode.CLC
@ -107,7 +107,7 @@ class TestIRPeepholeOpt: FunSpec({
)) ))
irProg.chunks().single().instructions.size shouldBe 4 irProg.chunks().single().instructions.size shouldBe 4
val opt = IRPeepholeOptimizer(irProg) val opt = IRPeepholeOptimizer(irProg)
opt.optimize() opt.optimize(true, ErrorReporterForTests())
val instr = irProg.chunks().single().instructions val instr = irProg.chunks().single().instructions
instr.size shouldBe 1 instr.size shouldBe 1
instr[0].opcode shouldBe Opcode.LOADR instr[0].opcode shouldBe Opcode.LOADR
@ -117,31 +117,31 @@ class TestIRPeepholeOpt: FunSpec({
test("remove useless div/mul, add/sub") { test("remove useless div/mul, add/sub") {
val irProg = makeIRProgram(listOf( val irProg = makeIRProgram(listOf(
IRInstruction(Opcode.DIV, IRDataType.BYTE, reg1=42, value = 1), IRInstruction(Opcode.DIV, IRDataType.BYTE, reg1=42, immediate = 1),
IRInstruction(Opcode.DIVS, IRDataType.BYTE, reg1=42, value = 1), IRInstruction(Opcode.DIVS, IRDataType.BYTE, reg1=42, immediate = 1),
IRInstruction(Opcode.MUL, IRDataType.BYTE, reg1=42, value = 1), IRInstruction(Opcode.MUL, IRDataType.BYTE, reg1=42, immediate = 1),
IRInstruction(Opcode.MOD, IRDataType.BYTE, reg1=42, value = 1), IRInstruction(Opcode.MOD, IRDataType.BYTE, reg1=42, immediate = 1),
IRInstruction(Opcode.DIV, IRDataType.BYTE, reg1=42, value = 2), IRInstruction(Opcode.DIV, IRDataType.BYTE, reg1=42, immediate = 2),
IRInstruction(Opcode.DIVS, IRDataType.BYTE, reg1=42, value = 2), IRInstruction(Opcode.DIVS, IRDataType.BYTE, reg1=42, immediate = 2),
IRInstruction(Opcode.MUL, IRDataType.BYTE, reg1=42, value = 2), IRInstruction(Opcode.MUL, IRDataType.BYTE, reg1=42, immediate = 2),
IRInstruction(Opcode.MOD, IRDataType.BYTE, reg1=42, value = 2), IRInstruction(Opcode.MOD, IRDataType.BYTE, reg1=42, immediate = 2),
IRInstruction(Opcode.ADD, IRDataType.BYTE, reg1=42, value = 0), IRInstruction(Opcode.ADD, IRDataType.BYTE, reg1=42, immediate = 0),
IRInstruction(Opcode.SUB, IRDataType.BYTE, reg1=42, value = 0) IRInstruction(Opcode.SUB, IRDataType.BYTE, reg1=42, immediate = 0)
)) ))
irProg.chunks().single().instructions.size shouldBe 10 irProg.chunks().single().instructions.size shouldBe 10
val opt = IRPeepholeOptimizer(irProg) val opt = IRPeepholeOptimizer(irProg)
opt.optimize() opt.optimize(true, ErrorReporterForTests())
irProg.chunks().single().instructions.size shouldBe 4 irProg.chunks().single().instructions.size shouldBe 4
} }
test("replace add/sub 1 by inc/dec") { test("replace add/sub 1 by inc/dec") {
val irProg = makeIRProgram(listOf( val irProg = makeIRProgram(listOf(
IRInstruction(Opcode.ADD, IRDataType.BYTE, reg1=42, value = 1), IRInstruction(Opcode.ADD, IRDataType.BYTE, reg1=42, immediate = 1),
IRInstruction(Opcode.SUB, IRDataType.BYTE, reg1=42, value = 1) IRInstruction(Opcode.SUB, IRDataType.BYTE, reg1=42, immediate = 1)
)) ))
irProg.chunks().single().instructions.size shouldBe 2 irProg.chunks().single().instructions.size shouldBe 2
val opt = IRPeepholeOptimizer(irProg) val opt = IRPeepholeOptimizer(irProg)
opt.optimize() opt.optimize(true, ErrorReporterForTests())
val instr = irProg.chunks().single().instructions val instr = irProg.chunks().single().instructions
instr.size shouldBe 2 instr.size shouldBe 2
instr[0].opcode shouldBe Opcode.INC instr[0].opcode shouldBe Opcode.INC
@ -150,40 +150,40 @@ class TestIRPeepholeOpt: FunSpec({
test("remove useless and/or/xor") { test("remove useless and/or/xor") {
val irProg = makeIRProgram(listOf( val irProg = makeIRProgram(listOf(
IRInstruction(Opcode.AND, IRDataType.BYTE, reg1=42, value = 255), IRInstruction(Opcode.AND, IRDataType.BYTE, reg1=42, immediate = 255),
IRInstruction(Opcode.AND, IRDataType.WORD, reg1=42, value = 65535), IRInstruction(Opcode.AND, IRDataType.WORD, reg1=42, immediate = 65535),
IRInstruction(Opcode.OR, IRDataType.BYTE, reg1=42, value = 0), IRInstruction(Opcode.OR, IRDataType.BYTE, reg1=42, immediate = 0),
IRInstruction(Opcode.XOR, IRDataType.BYTE, reg1=42, value = 0), IRInstruction(Opcode.XOR, IRDataType.BYTE, reg1=42, immediate = 0),
IRInstruction(Opcode.AND, IRDataType.BYTE, reg1=42, value = 200), IRInstruction(Opcode.AND, IRDataType.BYTE, reg1=42, immediate = 200),
IRInstruction(Opcode.AND, IRDataType.WORD, reg1=42, value = 60000), IRInstruction(Opcode.AND, IRDataType.WORD, reg1=42, immediate = 60000),
IRInstruction(Opcode.OR, IRDataType.BYTE, reg1=42, value = 1), IRInstruction(Opcode.OR, IRDataType.BYTE, reg1=42, immediate = 1),
IRInstruction(Opcode.XOR, IRDataType.BYTE, reg1=42, value = 1) IRInstruction(Opcode.XOR, IRDataType.BYTE, reg1=42, immediate = 1)
)) ))
irProg.chunks().single().instructions.size shouldBe 8 irProg.chunks().single().instructions.size shouldBe 8
val opt = IRPeepholeOptimizer(irProg) val opt = IRPeepholeOptimizer(irProg)
opt.optimize() opt.optimize(true, ErrorReporterForTests())
irProg.chunks().single().instructions.size shouldBe 4 irProg.chunks().single().instructions.size shouldBe 4
} }
test("replace and/or/xor by constant number") { test("replace and/or/xor by constant number") {
val irProg = makeIRProgram(listOf( val irProg = makeIRProgram(listOf(
IRInstruction(Opcode.AND, IRDataType.BYTE, reg1=42, value = 0), IRInstruction(Opcode.AND, IRDataType.BYTE, reg1=42, immediate = 0),
IRInstruction(Opcode.AND, IRDataType.WORD, reg1=42, value = 0), IRInstruction(Opcode.AND, IRDataType.WORD, reg1=42, immediate = 0),
IRInstruction(Opcode.OR, IRDataType.BYTE, reg1=42, value = 255), IRInstruction(Opcode.OR, IRDataType.BYTE, reg1=42, immediate = 255),
IRInstruction(Opcode.OR, IRDataType.WORD, reg1=42, value = 65535) IRInstruction(Opcode.OR, IRDataType.WORD, reg1=42, immediate = 65535)
)) ))
irProg.chunks().single().instructions.size shouldBe 4 irProg.chunks().single().instructions.size shouldBe 4
val opt = IRPeepholeOptimizer(irProg) val opt = IRPeepholeOptimizer(irProg)
opt.optimize() opt.optimize(true, ErrorReporterForTests())
val instr = irProg.chunks().single().instructions val instr = irProg.chunks().single().instructions
instr.size shouldBe 4 instr.size shouldBe 4
instr[0].opcode shouldBe Opcode.LOAD instr[0].opcode shouldBe Opcode.LOAD
instr[1].opcode shouldBe Opcode.LOAD instr[1].opcode shouldBe Opcode.LOAD
instr[2].opcode shouldBe Opcode.LOAD instr[2].opcode shouldBe Opcode.LOAD
instr[3].opcode shouldBe Opcode.LOAD instr[3].opcode shouldBe Opcode.LOAD
instr[0].value shouldBe 0 instr[0].immediate shouldBe 0
instr[1].value shouldBe 0 instr[1].immediate shouldBe 0
instr[2].value shouldBe 255 instr[2].immediate shouldBe 255
instr[3].value shouldBe 65535 instr[3].immediate shouldBe 65535
} }
}) })

View File

@ -1,5 +1,6 @@
import io.kotest.core.spec.style.FunSpec import io.kotest.core.spec.style.FunSpec
import io.kotest.matchers.ints.shouldBeGreaterThan import io.kotest.matchers.ints.shouldBeGreaterThan
import io.kotest.matchers.shouldBe
import prog8.code.SymbolTableMaker import prog8.code.SymbolTableMaker
import prog8.code.ast.* import prog8.code.ast.*
import prog8.code.core.* import prog8.code.core.*
@ -7,6 +8,7 @@ import prog8.code.target.VMTarget
import prog8.codegen.vm.VmAssemblyProgram import prog8.codegen.vm.VmAssemblyProgram
import prog8.codegen.vm.VmCodeGen import prog8.codegen.vm.VmCodeGen
import prog8.intermediate.IRSubroutine import prog8.intermediate.IRSubroutine
import prog8.intermediate.Opcode
class TestVmCodeGen: FunSpec({ class TestVmCodeGen: FunSpec({
@ -98,7 +100,7 @@ class TestVmCodeGen: FunSpec({
val errors = ErrorReporterForTests() val errors = ErrorReporterForTests()
val result = codegen.generate(program, st, options, errors) as VmAssemblyProgram val result = codegen.generate(program, st, options, errors) as VmAssemblyProgram
val irChunks = (result.irProgram.blocks.first().children.single() as IRSubroutine).chunks 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") { test("float comparison expressions against zero") {
@ -267,7 +269,7 @@ class TestVmCodeGen: FunSpec({
val errors = ErrorReporterForTests() val errors = ErrorReporterForTests()
val result = codegen.generate(program, st, options, errors) as VmAssemblyProgram val result = codegen.generate(program, st, options, errors) as VmAssemblyProgram
val irChunks = (result.irProgram.blocks.first().children.single() as IRSubroutine).chunks 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") { test("integer comparison expressions against zero") {
@ -436,7 +438,36 @@ class TestVmCodeGen: FunSpec({
val errors = ErrorReporterForTests() val errors = ErrorReporterForTests()
val result = codegen.generate(program, st, options, errors) as VmAssemblyProgram val result = codegen.generate(program, st, options, errors) as VmAssemblyProgram
val irChunks = (result.irProgram.blocks.first().children.single() as IRSubroutine).chunks 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) { if(iterationCount!=null) {
val loopName = forLoop.loopVar.nameInSource val loopName = forLoop.loopVar.nameInSource
if(!forLoop.iterable.referencesIdentifier(loopName) && !forLoop.body.referencesIdentifier(loopName)) { 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) val repeat = RepeatLoop(NumericLiteral.optimalNumeric(iterationCount, forLoop.position), forLoop.body, forLoop.position)
return listOf(IAstModification.ReplaceNode(forLoop, repeat, parent)) 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 TIME_LO = $a2 ; .. lo byte. Updated by IRQ every 1/60 sec
&ubyte STATUS = $90 ; kernal status variable for I/O &ubyte STATUS = $90 ; kernal status variable for I/O
&ubyte STKEY = $91 ; various keyboard statuses (updated by IRQ) &ubyte STKEY = $91 ; various keyboard statuses (updated by IRQ)
;;&ubyte SFDX = $cb ; current key pressed (matrix value) (updated by IRQ) // 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 = $00f1 ; cursor color &ubyte COLOR = $f1 ; cursor color
;;&ubyte HIBASE = $0288 ; screen base address / 256 (hi-byte of screen memory address) // TODO c128 ??
&uword IERROR = $0300 &uword IERROR = $0300
&uword IMAIN = $0302 &uword IMAIN = $0302
@ -311,7 +310,7 @@ asmsub set_irq(uword handler @AY, ubyte useKernal @Pc) clobbers(A) {
sta _modified+1 sta _modified+1
sty _modified+2 sty _modified+2
lda #0 lda #0
adc #0 rol a
sta _use_kernal sta _use_kernal
sei sei
lda #<_irq_handler lda #<_irq_handler
@ -405,7 +404,7 @@ asmsub set_rasterirq(uword handler @AY, uword rasterpos @R0, ubyte useKernal @P
sta _modified+1 sta _modified+1
sty _modified+2 sty _modified+2
lda #0 lda #0
adc #0 rol a
sta set_irq._use_kernal sta set_irq._use_kernal
lda cx16.r0 lda cx16.r0
ldy cx16.r0+1 ldy cx16.r0+1
@ -480,10 +479,8 @@ asmsub init_system() {
%asm {{ %asm {{
sei sei
cld cld
;;lda #%00101111 ; TODO c128 ram and rom bank selection how? lda #0
;;sta $00 sta $ff00 ; select default bank 15
;;lda #%00100111
;;sta $01
jsr c64.IOINIT jsr c64.IOINIT
jsr c64.RESTOR jsr c64.RESTOR
jsr c64.CINT jsr c64.CINT
@ -549,8 +546,8 @@ sys {
; Soft-reset the system back to initial power-on Basic prompt. ; Soft-reset the system back to initial power-on Basic prompt.
%asm {{ %asm {{
sei sei
;lda #14 lda #0
;sta $01 ; bank the kernal in TODO c128 how to do this? sta $ff00 ; default bank 15
jmp (c64.RESET_VEC) jmp (c64.RESET_VEC)
}} }}
} }
@ -731,8 +728,8 @@ _longcopy
inline asmsub exit(ubyte returnvalue @A) { inline asmsub exit(ubyte returnvalue @A) {
; -- immediately exit the program with a return code in the A register ; -- immediately exit the program with a return code in the A register
%asm {{ %asm {{
;lda #14 lda #0
;sta $01 ; bank the kernal in TODO c128 how to do this? sta $ff00 ; default bank 15
jsr c64.CLRCHN ; reset i/o channels jsr c64.CLRCHN ; reset i/o channels
jsr c64.enable_runstop_and_charsetswitch jsr c64.enable_runstop_and_charsetswitch
ldx prog8_lib.orig_stackpointer ldx prog8_lib.orig_stackpointer

View File

@ -8,6 +8,7 @@ c64 {
&ubyte STATUS = $90 ; kernal status variable for I/O &ubyte STATUS = $90 ; kernal status variable for I/O
&ubyte STKEY = $91 ; various keyboard statuses (updated by IRQ) &ubyte STKEY = $91 ; various keyboard statuses (updated by IRQ)
&ubyte SFDX = $cb ; current key pressed (matrix value) (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 COLOR = $0286 ; cursor color
&ubyte HIBASE = $0288 ; screen base address / 256 (hi-byte of screen memory address) &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 sta _modified+1
sty _modified+2 sty _modified+2
lda #0 lda #0
adc #0 rol a
sta _use_kernal sta _use_kernal
sei sei
lda #<_irq_handler lda #<_irq_handler
@ -449,7 +450,7 @@ asmsub set_rasterirq(uword handler @AY, uword rasterpos @R0, ubyte useKernal @P
sta _modified+1 sta _modified+1
sty _modified+2 sty _modified+2
lda #0 lda #0
adc #0 rol a
sta set_irq._use_kernal sta set_irq._use_kernal
lda cx16.r0 lda cx16.r0
ldy cx16.r0+1 ldy cx16.r0+1

View File

@ -5,7 +5,6 @@
floats { floats {
; ---- this block contains C-64 compatible floating point related functions ---- ; ---- 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 PI = 3.141592653589793
const float TWOPI = 6.283185307179586 const float TWOPI = 6.283185307179586

View File

@ -22,6 +22,10 @@ psg {
; waveform = one of PULSE,SAWTOOTH,TRIANGLE,NOISE. ; waveform = one of PULSE,SAWTOOTH,TRIANGLE,NOISE.
; pulsewidth = 0-63. Specifies the pulse width for waveform=PULSE. ; pulsewidth = 0-63. Specifies the pulse width for waveform=PULSE.
envelope_states[voice_num] = 255 envelope_states[voice_num] = 255
%asm {{
php
sei
}}
cx16.r0 = $f9c2 + voice_num * 4 cx16.r0 = $f9c2 + voice_num * 4
cx16.VERA_CTRL = 0 cx16.VERA_CTRL = 0
cx16.VERA_ADDR_L = lsb(cx16.r0) cx16.VERA_ADDR_L = lsb(cx16.r0)
@ -32,6 +36,9 @@ psg {
cx16.VERA_DATA0 = waveform | pulsewidth cx16.VERA_DATA0 = waveform | pulsewidth
envelope_volumes[voice_num] = mkword(volume, 0) envelope_volumes[voice_num] = mkword(volume, 0)
envelope_maxvolumes[voice_num] = volume envelope_maxvolumes[voice_num] = volume
%asm {{
plp
}}
} }
; sub freq_hz(ubyte voice_num, float hertz) { ; sub freq_hz(ubyte voice_num, float hertz) {
@ -44,48 +51,54 @@ psg {
sub freq(ubyte voice_num, uword vera_freq) { sub freq(ubyte voice_num, uword vera_freq) {
; -- Changes the frequency of the voice's sound. ; -- 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. ; 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) ; (https://github.com/x16community/x16-docs/blob/master/VERA%20Programmer's%20Reference.md)
cx16.r0 = $f9c0 + voice_num * 4 ; 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_CTRL = 0
cx16.VERA_ADDR_L = lsb(cx16.r0) cx16.VERA_ADDR_L = lsb(cx16.r0)
cx16.VERA_ADDR_M = msb(cx16.r0) cx16.VERA_ADDR_M = msb(cx16.r0)
cx16.VERA_ADDR_H = 1 cx16.VERA_ADDR_H = 1
cx16.VERA_DATA0 = lsb(vera_freq)
cx16.VERA_ADDR_L++
cx16.VERA_DATA0 = msb(vera_freq) 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) { sub volume(ubyte voice_num, ubyte vol) {
; -- Modifies the volume of this voice. ; -- Modifies the volume of this voice.
; voice_num = 0-15, vol = 0-63 where 0=silent, 63=loudest. ; 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) envelope_volumes[voice_num] = mkword(vol, 0)
cx16.vpoke_mask(1, $f9c2 + voice_num * 4, %11000000, vol)
envelope_maxvolumes[voice_num] = vol envelope_maxvolumes[voice_num] = vol
} }
sub pulse_width(ubyte voice_num, ubyte pw) { sub pulse_width(ubyte voice_num, ubyte pw) {
; -- Modifies the pulse width of this voice (when waveform=PULSE) ; -- 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. ; voice_num = 0-15, pw = 0-63 where 0=narrow, 63=50%cycle so square wave.
cx16.r0 = $f9c3 + voice_num * 4 cx16.vpoke_mask(1, $f9c3 + voice_num * 4, %11000000, pw)
cx16.vpoke(1, cx16.r0, cx16.vpeek(1, cx16.r0) & %11000000 | pw)
} }
sub envelope(ubyte voice_num, ubyte maxvolume, ubyte attack, ubyte sustain, ubyte release) { sub envelope(ubyte voice_num, ubyte maxvolume, ubyte attack, ubyte sustain, ubyte release) {
; -- Enables AttackSustainRelease volume envelope for a voice. ; -- Enables AttackSustainRelease volume envelope for a voice.
; Note: this requires setting up envelopes_irq() as well, read its description. ; Note: this requires setting up envelopes_irq() as well, read its description.
; voice_num = 0-15 maxvolume = 0-63 ; voice_num = 0-15 maxvolume = 0-63
; attack, sustain, release = 0-255 that determine the speed of the A/D/R. ; 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 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_states[voice_num] = 255
envelope_attacks[voice_num] = attack envelope_attacks[voice_num] = attack
envelope_sustains[voice_num] = sustain envelope_sustains[voice_num] = sustain
envelope_releases[voice_num] = release envelope_releases[voice_num] = release
if attack if maxvolume<envelope_volumes[voice_num]
attack = 0 envelope_volumes[voice_num] = maxvolume
else
attack = maxvolume ; max volume when no attack is set
envelope_volumes[voice_num] = mkword(attack, 0)
envelope_maxvolumes[voice_num] = maxvolume envelope_maxvolumes[voice_num] = maxvolume
envelope_states[voice_num] = 0 envelope_states[voice_num] = 0
} }
@ -94,7 +107,6 @@ psg {
; -- Shut down all PSG voices. ; -- Shut down all PSG voices.
for cx16.r1L in 0 to 15 { for cx16.r1L in 0 to 15 {
envelope_states[cx16.r1L] = 255 envelope_states[cx16.r1L] = 255
envelope_volumes[cx16.r1L] = 0
volume(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, ; 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. ; 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) ; 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.r0 = the volume word (volume scaled by 256)
; cx16.r1L = the voice number ; cx16.r1L = the voice number
; cx16.r2L = attack value ; cx16.r2L = attack value
pushw(cx16.r0) pushw(cx16.r0)
push(cx16.r1L) push(cx16.r1L)
push(cx16.r2L) push(cx16.r2L)
@ -148,7 +160,7 @@ psg {
} }
; set new volumes of all 16 voices, using vera stride of 4 ; 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_CTRL = 0
cx16.VERA_ADDR_L = $c2 cx16.VERA_ADDR_L = $c2
cx16.VERA_ADDR_M = $f9 cx16.VERA_ADDR_M = $f9
@ -160,7 +172,7 @@ psg {
for cx16.r1L in 0 to 15 { for cx16.r1L in 0 to 15 {
cx16.VERA_DATA0 = cx16.VERA_DATA1 & %11000000 | msb(envelope_volumes[cx16.r1L]) cx16.VERA_DATA0 = cx16.VERA_DATA1 & %11000000 | msb(envelope_volumes[cx16.r1L])
} }
cx16.pop_vera_context() cx16.restore_vera_context()
popw(cx16.r9) popw(cx16.r9)
pop(cx16.r2L) pop(cx16.r2L)
pop(cx16.r1L) pop(cx16.r1L)

View File

@ -604,39 +604,24 @@ asmsub vpoke_xor(ubyte bank @A, uword address @R0, ubyte value @Y) clobbers (A)
}} }}
} }
asmsub vpoke_mask(ubyte bank @A, uword address @R0, ubyte mask @X, ubyte value @Y) clobbers (A) {
sub FB_set_pixels_from_buf(uword buffer, uword count) { ; -- 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 {{ %asm {{
; -- This is replacement code for the normal FB_set_pixels subroutine in ROM sty P8ZP_SCRATCH_B1
; However that routine contains a bug in the current v38 ROM that makes it crash when count > 255. stz cx16.VERA_CTRL
; So the code below replaces that. Once the ROM is patched this routine is no longer necessary. and #1
; See https://github.com/commanderx16/x16-rom/issues/179 sta cx16.VERA_ADDR_H
phx lda cx16.r0
lda buffer sta cx16.VERA_ADDR_L
ldy buffer+1 lda cx16.r0+1
sta P8ZP_SCRATCH_W1 sta cx16.VERA_ADDR_M
sty P8ZP_SCRATCH_W1+1 txa
jsr _pixels and cx16.VERA_DATA0
plx ora P8ZP_SCRATCH_B1
rts sta cx16.VERA_DATA0
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
}}
} }
; ---- system stuff ----- ; ---- system stuff -----
@ -712,7 +697,7 @@ asmsub set_irq(uword handler @AY, ubyte useKernal @Pc) clobbers(A) {
sta _modified+1 sta _modified+1
sty _modified+2 sty _modified+2
lda #0 lda #0
adc #0 rol a
sta _use_kernal sta _use_kernal
sei sei
lda #<_irq_handler 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 ; -- use this at the start of your IRQ handler if it uses Vera registers, to save the state
%asm {{ %asm {{
; note cannot store this on cpu hardware stack because this gets called as a subroutine ; 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 lda cx16.VERA_CTRL
sta _vera_storage+3 sta _vera_storage+3
eor #1 eor #1
sta _vera_storage+7
sta cx16.VERA_CTRL sta cx16.VERA_CTRL
lda cx16.VERA_ADDR_L lda cx16.VERA_ADDR_L
sta _vera_storage+4 sta _vera_storage+4
@ -806,31 +792,29 @@ asmsub push_vera_context() clobbers(A) {
sta _vera_storage+5 sta _vera_storage+5
lda cx16.VERA_ADDR_H lda cx16.VERA_ADDR_H
sta _vera_storage+6 sta _vera_storage+6
lda cx16.VERA_CTRL
sta _vera_storage+7
rts rts
_vera_storage: .byte 0,0,0,0,0,0,0,0 _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 ; -- use this at the end of your IRQ handler if it uses Vera registers, to restore the state
%asm {{ %asm {{
lda cx16.push_vera_context._vera_storage+7 lda cx16.save_vera_context._vera_storage+7
sta cx16.VERA_CTRL 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 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 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 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 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 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 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 sta cx16.VERA_ADDR_L
rts rts
}} }}
@ -917,13 +901,27 @@ sys {
asmsub reset_system() { asmsub reset_system() {
; Soft-reset the system back to initial power-on Basic prompt. ; 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 {{ %asm {{
sei sei
stz $01 ; bank the kernal in ldx #$42
jmp (cx16.RESET_VEC) 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) { asmsub wait(uword jiffies @AY) {
; --- wait approximately the given number of jiffies (1/60th seconds) (N or N+1) ; --- 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 ; 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 sta modify_pattern2+2
jsr _match jsr _match
lda #0 lda #0
adc #0 rol a
ldx P8ZP_SCRATCH_REG ldx P8ZP_SCRATCH_REG
rts 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 ; 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) ; (any non-digit character will terminate the number string that is parsed)
%ir {{ %ir {{
loadm.w r65500,conv.str2uword.string loadm.w r65535,conv.str2uword.string
syscall 11 syscall 11 (r65535.w) : r0.w
returnreg.w r0 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 ; 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) ; (any non-digit character will terminate the number string that is parsed)
%ir {{ %ir {{
loadm.w r65500,conv.str2word.string loadm.w r65535,conv.str2word.string
syscall 12 syscall 12 (r65535.w) : r0.w
returnreg.w r0 returnr.w r0
}} }}
} }

View File

@ -10,8 +10,8 @@ floats {
sub print_f(float value) { sub print_f(float value) {
; ---- prints the floating point value (without a newline). ; ---- prints the floating point value (without a newline).
%ir {{ %ir {{
loadm.f fr65500,floats.print_f.value loadm.f fr65535,floats.print_f.value
syscall 25 syscall 25 (fr65535.f)
return return
}} }}
} }
@ -21,7 +21,7 @@ sub pow(float value, float power) -> float {
loadm.f fr0,floats.pow.value loadm.f fr0,floats.pow.value
loadm.f fr1,floats.pow.power loadm.f fr1,floats.pow.power
fpow.f fr0,fr1 fpow.f fr0,fr1
returnreg.f fr0 returnr.f fr0
}} }}
} }
@ -29,7 +29,7 @@ sub fabs(float value) -> float {
%ir {{ %ir {{
loadm.f fr0,floats.fabs.value loadm.f fr0,floats.fabs.value
fabs.f fr0,fr0 fabs.f fr0,fr0
returnreg.f fr0 returnr.f fr0
}} }}
} }
@ -37,7 +37,7 @@ sub sin(float angle) -> float {
%ir {{ %ir {{
loadm.f fr0,floats.sin.angle loadm.f fr0,floats.sin.angle
fsin.f fr0,fr0 fsin.f fr0,fr0
returnreg.f fr0 returnr.f fr0
}} }}
} }
@ -45,7 +45,7 @@ sub cos(float angle) -> float {
%ir {{ %ir {{
loadm.f fr0,floats.cos.angle loadm.f fr0,floats.cos.angle
fcos.f fr0,fr0 fcos.f fr0,fr0
returnreg.f fr0 returnr.f fr0
}} }}
} }
@ -53,7 +53,7 @@ sub tan(float value) -> float {
%ir {{ %ir {{
loadm.f fr0,floats.tan.value loadm.f fr0,floats.tan.value
ftan.f fr0,fr0 ftan.f fr0,fr0
returnreg.f fr0 returnr.f fr0
}} }}
} }
@ -61,7 +61,7 @@ sub atan(float value) -> float {
%ir {{ %ir {{
loadm.f fr0,floats.atan.value loadm.f fr0,floats.atan.value
fatan.f fr0,fr0 fatan.f fr0,fr0
returnreg.f fr0 returnr.f fr0
}} }}
} }
@ -69,7 +69,7 @@ sub ln(float value) -> float {
%ir {{ %ir {{
loadm.f fr0,floats.ln.value loadm.f fr0,floats.ln.value
fln.f fr0,fr0 fln.f fr0,fr0
returnreg.f fr0 returnr.f fr0
}} }}
} }
@ -77,7 +77,7 @@ sub log2(float value) -> float {
%ir {{ %ir {{
loadm.f fr0,floats.log2.value loadm.f fr0,floats.log2.value
flog.f fr0,fr0 flog.f fr0,fr0
returnreg.f fr0 returnr.f fr0
}} }}
} }
@ -85,7 +85,7 @@ sub sqrt(float value) -> float {
%ir {{ %ir {{
loadm.f fr0,floats.sqrt.value loadm.f fr0,floats.sqrt.value
sqrt.f fr0,fr0 sqrt.f fr0,fr0
returnreg.f fr0 returnr.f fr0
}} }}
} }
@ -103,7 +103,7 @@ sub round(float value) -> float {
%ir {{ %ir {{
loadm.f fr0,floats.round.value loadm.f fr0,floats.round.value
fround.f fr0,fr0 fround.f fr0,fr0
returnreg.f fr0 returnr.f fr0
}} }}
} }
@ -111,7 +111,7 @@ sub floor(float value) -> float {
%ir {{ %ir {{
loadm.f fr0,floats.floor.value loadm.f fr0,floats.floor.value
ffloor.f fr0,fr0 ffloor.f fr0,fr0
returnreg.f fr0 returnr.f fr0
}} }}
} }
@ -120,21 +120,22 @@ sub ceil(float value) -> float {
%ir {{ %ir {{
loadm.f fr0,floats.ceil.value loadm.f fr0,floats.ceil.value
fceil.f fr0,fr0 fceil.f fr0,fr0
returnreg.f fr0 returnr.f fr0
}} }}
} }
sub rndf() -> float { sub rndf() -> float {
%ir {{ %ir {{
syscall 35 syscall 35 () : fr0.f
returnreg.f fr0 returnr.f fr0
}} }}
} }
sub rndseedf(float seed) { sub rndseedf(float seed) {
%ir {{ %ir {{
loadm.f fr65500,floats.rndseedf.seed loadm.f fr65535,floats.rndseedf.seed
syscall 32 syscall 32 (fr65535.f)
return
}} }}
} }

View File

@ -161,24 +161,24 @@ math {
sub rnd() -> ubyte { sub rnd() -> ubyte {
%ir {{ %ir {{
syscall 33 syscall 33 (): r0.b
returnreg.b r0 returnr.b r0
}} }}
} }
sub rndw() -> uword { sub rndw() -> uword {
%ir {{ %ir {{
syscall 34 syscall 34 (): r0.w
returnreg.w r0 returnr.w r0
}} }}
} }
sub rndseed(uword seed1, uword seed2) { sub rndseed(uword seed1, uword seed2) {
; -- reset the pseudo RNG's seed values. Defaults are: $a55a, $7653. ; -- reset the pseudo RNG's seed values. Defaults are: $a55a, $7653.
%ir {{ %ir {{
loadm.w r65500,math.rndseed.seed1 loadm.w r65534,math.rndseed.seed1
loadm.w r65501,math.rndseed.seed2 loadm.w r65535,math.rndseed.seed2
syscall 31 syscall 31 (r65534.w, r65535.w)
return return
}} }}
} }

View File

@ -84,10 +84,10 @@ string {
; Note that you can also directly compare strings and string values with eachother using ; 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). ; comparison operators ==, < etcetera (it will use strcmp for you under water automatically).
%ir {{ %ir {{
loadm.w r65500,string.compare.st1 loadm.w r65534,string.compare.st1
loadm.w r65501,string.compare.st2 loadm.w r65535,string.compare.st2
syscall 29 syscall 29 (r65534.w, r65535.w) : r0.b
returnreg.b r0 returnr.b r0
}} }}
} }

View File

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

View File

@ -15,8 +15,8 @@ sub height() -> ubyte {
sub clear_screen() { sub clear_screen() {
str @shared sequence = "\x1b[2J\x1B[H" str @shared sequence = "\x1b[2J\x1B[H"
%ir {{ %ir {{
load.w r65500,txt.clear_screen.sequence load.w r65535,txt.clear_screen.sequence
syscall 3 syscall 3 (r65535.w)
}} }}
} }
@ -38,15 +38,15 @@ sub uppercase() {
sub chrout(ubyte char) { sub chrout(ubyte char) {
%ir {{ %ir {{
loadm.b r65500,txt.chrout.char loadm.b r65535,txt.chrout.char
syscall 2 syscall 2 (r65535.b)
}} }}
} }
sub print (str text) { sub print (str text) {
%ir {{ %ir {{
loadm.w r65500,txt.print.text loadm.w r65535,txt.print.text
syscall 3 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) ; ---- 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! ; It assumes the keyboard is selected as I/O channel!
%ir {{ %ir {{
loadm.w r65500,txt.input_chars.buffer loadm.w r65534,txt.input_chars.buffer
syscall 6 load.b r65535,80
returnreg.b r0 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)) for(lib in compTarget.machine.importLibs(compilerOptions, compTarget.name))
importer.importImplicitLibraryModule(lib) importer.importImplicitLibraryModule(lib)
// always import prog8_lib and math if(compilerOptions.compTarget.name!=VMTarget.NAME && !compilerOptions.experimentalCodegen) {
importer.importImplicitLibraryModule("math") importer.importImplicitLibraryModule("math")
}
importer.importImplicitLibraryModule("prog8_lib") importer.importImplicitLibraryModule("prog8_lib")
if (compilerOptions.launcher == CbmPrgLauncherType.BASIC && compilerOptions.output != OutputType.PRG) if (compilerOptions.launcher == CbmPrgLauncherType.BASIC && compilerOptions.output != OutputType.PRG)

View File

@ -106,13 +106,18 @@ class ModuleImporter(private val program: Program,
removeDirectivesFromImportedModule(importedModule) removeDirectivesFromImportedModule(importedModule)
// modules can contain blocks with "merge" option. // 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>() val blocks = importedModule.statements.filterIsInstance<Block>()
for(block in blocks) { for(block in blocks) {
if("merge" in block.options()) { if("merge" in block.options()) {
val existingBlock = program.allBlocks.first { it.name==block.name} val existingBlock = program.allBlocks.firstOrNull { it.name==block.name && it !== block}
existingBlock.statements.addAll(block.statements.filter { it !is Directive}) if(existingBlock!=null) {
importedModule.statements.remove(block) 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.expressions.*
import prog8.ast.statements.* import prog8.ast.statements.*
import prog8.ast.walk.IAstVisitor import prog8.ast.walk.IAstVisitor
import prog8.code.ast.PtIdentifier
import prog8.code.core.* import prog8.code.core.*
import prog8.code.target.VMTarget import prog8.code.target.VMTarget
import prog8.compiler.builtinFunctionReturnType 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") err("subroutines can only be defined in the scope of a block or within another subroutine")
if(subroutine.isAsmSubroutine) { 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) if(subroutine.asmParameterRegisters.size != subroutine.parameters.size)
err("number of asm parameter registers is not the isSameAs as number of parameters") err("number of asm parameter registers is not the isSameAs as number of parameters")
if(subroutine.asmReturnvaluesRegisters.size != subroutine.returntypes.size) if(subroutine.asmReturnvaluesRegisters.size != subroutine.returntypes.size)
@ -1321,14 +1318,15 @@ internal class AstChecker(private val program: Program,
val whenStmt = whenChoice.parent as When val whenStmt = whenChoice.parent as When
if(whenChoice.values!=null) { if(whenChoice.values!=null) {
val conditionType = whenStmt.condition.inferType(program) 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) } val constvalues = whenChoice.values!!.map { it.constValue(program) }
for(constvalue in constvalues) { for(constvalue in constvalues) {
when { when {
constvalue == null -> errors.err("choice value must be a constant", whenChoice.position) 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) 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 { } else {

View File

@ -142,8 +142,8 @@ _after:
val indexExpr = arrayIndexedExpression.indexer.indexExpr val indexExpr = arrayIndexedExpression.indexer.indexExpr
val indexerDt = indexExpr.inferType(program) val indexerDt = indexExpr.inferType(program)
if(indexerDt.isWords) { if(indexerDt.isWords) {
val arrayVar = arrayIndexedExpression.arrayvar.targetVarDecl(program)!! val arrayVar = arrayIndexedExpression.arrayvar.targetVarDecl(program)
if(arrayVar.datatype==DataType.UWORD) { if(arrayVar!=null && arrayVar.datatype==DataType.UWORD) {
val add: Expression = val add: Expression =
if(indexExpr.constValue(program)?.number==0.0) if(indexExpr.constValue(program)?.number==0.0)
arrayIndexedExpression.arrayvar.copy() 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_word" -> alignment = PtBlock.BlockAlignment.WORD
"align_page" -> alignment = PtBlock.BlockAlignment.PAGE "align_page" -> alignment = PtBlock.BlockAlignment.PAGE
"force_output" -> forceOutput=true "force_output" -> forceOutput=true
"merge" -> { /* ignore this one */ }
else -> throw FatalAstException("weird directive option: ${arg.name}") 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) { for (asm in srcSub.statements) {
asm as InlineAssembly asm as InlineAssembly
if(asm.isIR) if(asm.isIR)
combinedIrAsm += asm.assembly + "\n" combinedIrAsm += asm.assembly.trimEnd() + "\n"
else else
combinedTrueAsm += asm.assembly + "\n" combinedTrueAsm += asm.assembly.trimEnd() + "\n"
} }
if(combinedTrueAsm.isNotEmpty()) { if(combinedTrueAsm.isNotEmpty()) {

View File

@ -33,7 +33,7 @@ class TestLaunchEmu: FunSpec({
<INITGLOBALS> <INITGLOBALS>
</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> </BLOCK>
</PROGRAM> </PROGRAM>
""") """)

View File

@ -12,6 +12,7 @@ generate:
p8compile -noopt -target cx16 *.p8 >/dev/null p8compile -noopt -target cx16 *.p8 >/dev/null
test_prgs: test_prgs:
x16emu -run -prg more_compares.prg
for program in *.prg; do \ for program in *.prg; do \
echo "RUNNING:" $$program ; \ echo "RUNNING:" $$program ; \
x16emu -run -prg $$program >/dev/null ; \ 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.shouldBe
import io.kotest.matchers.shouldNotBe import io.kotest.matchers.shouldNotBe
import io.kotest.matchers.string.shouldContain import io.kotest.matchers.string.shouldContain
import io.kotest.matchers.string.shouldNotContain
import prog8.ast.expressions.BuiltinFunctionCall import prog8.ast.expressions.BuiltinFunctionCall
import prog8.ast.statements.Assignment import prog8.ast.statements.Assignment
import prog8.code.target.C64Target import prog8.code.target.C64Target
@ -14,7 +15,6 @@ import prog8.intermediate.IRFileReader
import prog8.intermediate.IRSubroutine import prog8.intermediate.IRSubroutine
import prog8.intermediate.Opcode import prog8.intermediate.Opcode
import prog8.vm.VmRunner import prog8.vm.VmRunner
import prog8tests.helpers.ErrorReporterForTests
import prog8tests.helpers.compileText import prog8tests.helpers.compileText
import kotlin.io.path.readText import kotlin.io.path.readText
@ -212,59 +212,17 @@ main {
compileText(othertarget, true, src, writeAssembly = true) shouldNotBe null compileText(othertarget, true, src, writeAssembly = true) shouldNotBe null
val target = VMTarget() val target = VMTarget()
val result = compileText(target, false, src, writeAssembly = true)!! var result = compileText(target, true, src, writeAssembly = true)!!
val virtfile = result.compilationOptions.outputDir.resolve(result.compilerAst.name + ".p8ir") var virtfile = result.compilationOptions.outputDir.resolve(result.compilerAst.name + ".p8ir")
VmRunner().runAndTestProgram(virtfile.readText()) { vm -> VmRunner().runAndTestProgram(virtfile.readText()) { vm ->
vm.stepCount shouldBe 49 vm.stepCount shouldBe 37
} }
}
test("asmsub for virtual target not supported") { result = compileText(target, false, src, writeAssembly = true)!!
val src = """ virtfile = result.compilationOptions.outputDir.resolve(result.compilerAst.name + ".p8ir")
main { VmRunner().runAndTestProgram(virtfile.readText()) { vm ->
sub start() { vm.stepCount shouldBe 48
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"
} }
test("inline asm for virtual target should be IR") { test("inline asm for virtual target should be IR") {
@ -287,25 +245,26 @@ main {
val exc = shouldThrow<Exception> { val exc = shouldThrow<Exception> {
VmRunner().runProgram(virtfile.readText()) 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 = """ val src = """
main { main {
sub start() { sub start() {
%ir {{ %ir {{
loadr.b r1,r2
return return
}} }}
} }
}""" }"""
val othertarget = Cx16Target()
compileText(othertarget, true, src, writeAssembly = true) shouldNotBe null
val target = VMTarget() val target = VMTarget()
val result = compileText(target, false, src, writeAssembly = true)!! val result = compileText(target, false, src, writeAssembly = true)!!
val virtfile = result.compilationOptions.outputDir.resolve(result.compilerAst.name + ".p8ir") 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") { 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 - ``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 - 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. - 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. - 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. - 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`` - 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. 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/>`_. 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>`_ If you're targeting the Commander X16 instead,
and the unofficial `box16 <https://github.com/indigodarkwolf/box16>`_ (you can select which one you want to launch download a recent emulator version (R42 or newer) for the CommanderX16, such as `x16emu <https://cx16forum.com/forum/viewforum.php?f=30>`_
using the ``-emu`` or ``-emu2`` command line options) (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. **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. 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. 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) (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 conv
---- ----
@ -214,6 +220,10 @@ Provides string manipulation routines.
floats 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 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, 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 ``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. 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. 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. 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 Floating point numbers
^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^
Floats are stored in the 5-byte 'MFLPT' format that is used on CBM machines, 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. Floating point support is available on the c64 and cx16 (and virtual) compiler targets.
This is because routines in the C64 BASIC and Kernal ROMs are used for that. On the c64 and cx16, the rom routines are used for floating point operations,
So floating point operations will only work if the C64 BASIC ROM (and Kernal ROM) so on both systems the correct rom banks have to be banked in to make this work.
are banked in. 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 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. 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**) 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 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) 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. - style ``dontuse`` -- don't use *any* location in the zeropage.
Also read :ref:`zeropage`.
.. note:: .. note::
``kernalsafe`` and ``full`` on the C64 leave enough room in the zeropage to reallocate the ``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 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 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:: 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... ; ... do your work that uses vera here...
cx16.pop_vera_context() cx16.restore_vera_context()
.. caution:: .. caution::
The Commander X16's 16 'virtual registers' R0-R15 are located in zeropage and *are not preserved* in the IRQ handler! 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 For 9.0 major changes are being made in the "version_9" branch. Look there.
^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- 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)
Need help with 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. - 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. - see the :ref:`portingguide` for details on what information is needed.
@ -33,34 +21,30 @@ Future Things and Ideas
^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^
Compiler: 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 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: 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: (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: 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?) 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: 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) - 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 - 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? - 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 - 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? 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?) 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. 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 - 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 - 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)
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)
Libraries: Libraries:
- fix the problems in c128 target, and flesh out its libraries.
- fix the problems in atari 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) - c64: make the graphics.BITMAP_ADDRESS configurable (VIC banking)
- optimize several inner loops in gfx2 even further? - optimize several inner loops in gfx2 even further?
- add modes 3 and perhaps even 2 to gfx2 (lores 16 color and 4 color)? - add modes 3 and perhaps even 2 to gfx2 (lores 16 color and 4 color)?
@ -80,7 +64,7 @@ Expressions:
Optimizations: Optimizations:
- VariableAllocator: can we think of a smarter strategy for allocating variables into zeropage, rather than first-come-first-served? - 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, - 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 those checks should probably be removed, or be made permanent

View File

@ -15,14 +15,14 @@ main {
palette.set_rgb(amigacolors, len(amigacolors)) 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 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() screen_titlebar()
window_workbench() window_workbench()
window_system() window_system()
window_shell() window_shell()
gfx2.text(280, 210, 1, sc:"640x480(240) 4 colors") gfx2.text(280, 210, 1, iso:"640x480(240) 4 colors")
gfx2.text(280, 220, 1, sc:"Mockup drawn using Prog8 gfx2 library") gfx2.text(280, 220, 1, iso:"Mockup drawn using Prog8 gfx2 library")
repeat { repeat {
} }
@ -30,7 +30,7 @@ main {
sub screen_titlebar() { sub screen_titlebar() {
gfx2.fillrect(0, 0, gfx2.width, 10, 2) 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) gfx2.horizontal_line(0, 10, gfx2.width, 1)
widget.window_order_icon(gfx2.width-widget.window_order_icon.width, 0, false) widget.window_order_icon(gfx2.width-widget.window_order_icon.width, 0, false)
} }
@ -42,7 +42,7 @@ main {
const uword width = 600 const uword width = 600
const uword height = 220 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 ; 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_leftborder(win_x, win_y, height, false)
widget.window_bottomborder(win_x, win_y, width, height) 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, win_y+height-20)
vector_v(win_x+width - 390 -14, 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,40, iso:"Ram Disk")
widget.icon(45,90, sc:"Workbench3.1") widget.icon(45,90, iso:"Workbench3.1")
} }
sub vector_v(uword x, uword y) { sub vector_v(uword x, uword y) {
@ -70,17 +70,17 @@ main {
const uword win_x = 320 const uword win_x = 320
const uword win_y = 40 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 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_leftborder(win_x, win_y, height, false)
widget.window_bottomborder(win_x, win_y, width, height) widget.window_bottomborder(win_x, win_y, width, height)
widget.window_rightborder(win_x, win_y, width, height, false) 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, win_y+14, iso:"FixFonts")
widget.icon(win_x+16+80, win_y+14, sc:"NoFastMem") widget.icon(win_x+16+80, win_y+14, iso:"NoFastMem")
widget.icon(win_x+16, win_y+56, sc:"Format") widget.icon(win_x+16, win_y+56, iso:"Format")
widget.icon(win_x+16+80, win_y+56, sc:"RexxMast") widget.icon(win_x+16+80, win_y+56, iso:"RexxMast")
widget.icon(win_x+16+160, win_y+56, sc:"Shell") widget.icon(win_x+16+160, win_y+56, iso:"Shell")
} }
sub window_shell() { sub window_shell() {
@ -89,14 +89,14 @@ main {
const uword width = 500 const uword width = 500
const uword height = 65 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 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_leftborder(win_x, win_y, height, true)
widget.window_bottomborder(win_x, win_y, width, height) widget.window_bottomborder(win_x, win_y, width, height)
widget.window_rightborder(win_x, win_y, width, height, true) 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, 1, iso:"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+8, 1, iso:"3.Workbench3.1:>")
gfx2.fillrect(win_x+5+17*8, win_y+12+8, 8, 8, 1) ; cursor gfx2.fillrect(win_x+5+17*8, win_y+12+8, 8, 8, 1) ; cursor
} }
} }

View File

@ -34,7 +34,7 @@ main {
irq { irq {
const ubyte BAR_Y_OFFSET = 6 const ubyte BAR_Y_OFFSET = 5
uword next_irq_line = 0 uword next_irq_line = 0
ubyte anim1 = 0 ubyte anim1 = 0
ubyte av1 = 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 size = cx16diskio.f_read(digi_address, ram_bank_size) ; load next bank
txt.print_ub(cx16.getrambank()) txt.print_ub(cx16.getrambank())
txt.spc() txt.spc()
sys.wait(5) ; artificial delay
} }
txt.print("file end.\n") txt.print("sound file end reached.\n")
diskio.f_close() diskio.f_close()
repeat { repeat {

View File

@ -1,7 +1,6 @@
%import textio %import textio
%import conv %import conv
%import math %import math
%import test_stack
%zeropage basicsafe %zeropage basicsafe
; The classic number guessing game. ; The classic number guessing game.
@ -61,8 +60,6 @@ main {
txt.print("Thanks for playing, ") txt.print("Thanks for playing, ")
txt.print(name) txt.print(name)
txt.print(".\n") txt.print(".\n")
; test_stack.test()
} }
} }
} }

View File

@ -1,31 +1,16 @@
%import textio
%import floats
%zeropage basicsafe %zeropage basicsafe
main { 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]) sub start() {
txt.nl() ubyte @shared foo = derp(99)
txt.print_uw(wa[1]) }
txt.nl()
floats.print_f(fa[1])
txt.nl()
ubyte index=1 asmsub derp(ubyte xx @Y) -> ubyte @ A {
ubyte calc=1 %asm {{
ba[index] += 1 rts
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()
} }
} }

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. // Note that in the VM these are translated into whatever the corresponding Syscall number in the VM is.
enum class IMSyscall(val number: Int) { enum class IMSyscall(val number: Int) {
SORT_UBYTE(10000), SORT_UBYTE(0x1000),
SORT_BYTE(10001), SORT_BYTE(0x1001),
SORT_UWORD(10002), SORT_UWORD(0x1002),
SORT_WORD(10003), SORT_WORD(0x1003),
ANY_BYTE(10004), ANY_BYTE(0x1004),
ANY_WORD(10005), ANY_WORD(0x1005),
ANY_FLOAT(10006), ANY_FLOAT(0x1006),
ALL_BYTE(10007), ALL_BYTE(0x1007),
ALL_WORD(10008), ALL_WORD(0x1008),
ALL_FLOAT(10009), ALL_FLOAT(0x1009),
REVERSE_BYTES(10010), REVERSE_BYTES(0x100a),
REVERSE_WORDS(10011), REVERSE_WORDS(0x100b),
REVERSE_FLOATS(10012), REVERSE_FLOATS(0x100c),
COMPARE_STRINGS(10013), COMPARE_STRINGS(0x100d),
STRING_CONTAINS(10014), STRING_CONTAINS(0x100e),
BYTEARRAY_CONTAINS(10015), BYTEARRAY_CONTAINS(0x100f),
WORDARRAY_CONTAINS(10016) WORDARRAY_CONTAINS(0x1010)
} }
const val SyscallRegisterBase = 65500

View File

@ -68,6 +68,7 @@ class IRFileReader {
blocks.forEach{ program.addBlock(it) } blocks.forEach{ program.addBlock(it) }
program.linkChunks() program.linkChunks()
program.convertAsmChunks()
program.validate() program.validate()
return program return program
@ -302,7 +303,7 @@ class IRFileReader {
if(text.isNotBlank()) { if(text.isNotBlank()) {
text.lineSequence().forEach { line -> text.lineSequence().forEach { line ->
if (line.isNotBlank() && !line.startsWith(';')) { if (line.isNotBlank() && !line.startsWith(';')) {
val result = parseIRCodeLine(line, null, mutableMapOf()) val result = parseIRCodeLine(line)
result.fold( result.fold(
ifLeft = { ifLeft = {
chunk += it chunk += it
@ -336,6 +337,8 @@ class IRFileReader {
val block = IRBlock( val block = IRBlock(
attrs.getValue("NAME"), attrs.getValue("NAME"),
if(attrs.getValue("ADDRESS")=="") null else parseIRValue(attrs.getValue("ADDRESS")).toUInt(), 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")), IRBlock.BlockAlignment.valueOf(attrs.getValue("ALIGN")),
parsePosition(attrs.getValue("POS"))) parsePosition(attrs.getValue("POS")))
skipText(reader) 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 val posPattern = Regex("\\[(.+): line (.+) col (.+)-(.+)\\]")
private fun parsePosition(strpos: String): Position { private fun parsePosition(strpos: String): Position {

View File

@ -55,8 +55,10 @@ class IRFileWriter(private val irProgram: IRProgram, outfileOverride: Path?) {
private fun writeBlocks() { private fun writeBlocks() {
irProgram.blocks.forEach { block -> irProgram.blocks.forEach { block ->
xml.writeStartElement("BLOCK") xml.writeStartElement("BLOCK")
xml.writeAttribute("NAME", block.name) xml.writeAttribute("NAME", block.label)
xml.writeAttribute("ADDRESS", block.address?.toHex() ?: "") 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("ALIGN", block.alignment.toString())
xml.writeAttribute("POS", block.position.toString()) xml.writeAttribute("POS", block.position.toString())
xml.writeCharacters("\n") xml.writeCharacters("\n")
@ -124,6 +126,7 @@ class IRFileWriter(private val irProgram: IRProgram, outfileOverride: Path?) {
private fun writeCodeChunk(chunk: IRCodeChunk) { private fun writeCodeChunk(chunk: IRCodeChunk) {
xml.writeStartElement("CODE") xml.writeStartElement("CODE")
chunk.label?.let { xml.writeAttribute("LABEL", chunk.label) } chunk.label?.let { xml.writeAttribute("LABEL", chunk.label) }
xml.writeAttribute("used-registers", chunk.usedRegisters().toString())
xml.writeCharacters("\n") xml.writeCharacters("\n")
chunk.instructions.forEach { instr -> chunk.instructions.forEach { instr ->
numInstr++ numInstr++
@ -137,7 +140,6 @@ class IRFileWriter(private val irProgram: IRProgram, outfileOverride: Path?) {
private fun writeInlineBytes(chunk: IRInlineBinaryChunk) { private fun writeInlineBytes(chunk: IRInlineBinaryChunk) {
xml.writeStartElement("BYTES") xml.writeStartElement("BYTES")
chunk.label?.let { xml.writeAttribute("LABEL", chunk.label) } chunk.label?.let { xml.writeAttribute("LABEL", chunk.label) }
xml.writeCharacters("\n")
chunk.data.withIndex().forEach {(index, byte) -> chunk.data.withIndex().forEach {(index, byte) ->
xml.writeCharacters(byte.toString(16).padStart(2,'0')) xml.writeCharacters(byte.toString(16).padStart(2,'0'))
if(index and 63 == 63 && index < chunk.data.size-1) if(index and 63 == 63 && index < chunk.data.size-1)

View File

@ -1,5 +1,6 @@
package prog8.intermediate package prog8.intermediate
import prog8.code.core.RegisterOrStatusflag
import prog8.code.core.toHex 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! 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. 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. 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 LOAD/STORE
---------- ----------
All have type b or w or f. 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 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 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 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 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 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 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 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 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 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 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 storezi reg1 - store zero at memory pointed to by reg1
storezx reg1, address - store zero at memory address, indexed by value in reg 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 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) 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. call label(argument register list) [: resultreg.type]
callrval reg1, location - like call but expects a return value from a returnreg instruction, and puts that in reg1 - calls a subroutine with the given arguments and return value (optional).
syscall value - do a systemcall identified by call number 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. 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 BRANCHING and CONDITIONALS
-------------------------- --------------------------
All have type b or w except the branches that only check status bits. 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 bstcc address - branch to location if Status bit Carry is clear
bstcs address - branch to location if Status bit Carry is Set 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 bsteq address - branch to location if Status bit Zero is set
bstne address - branch to location if Status bit Zero is not set bstpos address - branch to location if Status bit Negative is clear
bstneg address - branch to location if Status bit Negative is not set bstneg address - branch to location if Status bit Negative is set
bstpos address - branch to location if Status bit Negative is not set bstvc address - branch to location if Status bit Overflow is clear
bstvc address - branch to location if Status bit Overflow is not set bstvs address - branch to location if Status bit Overflow is set
bstvs address - branch to location if Status bit Overflow is not set beqr reg1, reg2, address - jump to location in program given by location, if reg1 == reg2
bz reg1, address - branch to location if reg1 is zero beq reg1, value, address - jump to location in program given by location, if reg1 == immediate value
bnz reg1, address - branch to location if reg1 is not zero bner reg1, reg2, address - jump to location in program given by location, if reg1 != reg2
bgzs reg1, address - branch to location if reg1 > 0 (signed) bne reg1, value, address - jump to location in program given by location, if reg1 != immediate value
bgezs reg1, address - branch to location if reg1 >= 0 (signed) bgt reg1, value, address - jump to location in program given by location, if reg1 > immediate value (unsigned)
blzs reg1, address - branch to location if reg1 < 0 (signed) bgts reg1, value, address - jump to location in program given by location, if reg1 > immediate value (signed)
blezs reg1, address - branch to location if reg1 <= 0 (signed) bgtr reg1, reg2, address - jump to location in program given by location, if reg1 > reg2 (unsigned)
beq reg1, reg2, address - jump to location in program given by location, if reg1 == reg2 bgtsr reg1, reg2, address - jump to location in program given by location, if reg1 > reg2 (signed)
bne reg1, reg2, address - jump to location in program given by location, if reg1 != reg2 blt reg1, value, address - jump to location in program given by location, if reg1 < immediate value (unsigned)
bgt reg1, reg2, address - jump to location in program given by location, if reg1 > reg2 (unsigned) blts reg1, value, address - jump to location in program given by location, if reg1 < immediate value (signed)
bgts reg1, reg2, address - jump to location in program given by location, if reg1 > reg2 (signed) bge reg1, value, address - jump to location in program given by location, if reg1 >= immediate value (unsigned)
bge reg1, reg2, address - jump to location in program given by location, if reg1 >= reg2 (unsigned) bges reg1, value, address - jump to location in program given by location, if reg1 >= immediate value (signed)
bges reg1, reg2, address - jump to location in program given by location, if reg1 >= reg2 (signed) bger reg1, reg2, address - jump to location in program given by location, if reg1 >= reg2 (unsigned)
( NOTE: there are no blt/ble instructions because these are equivalent to bgt/bge with the operands swapped around.) bgesr reg1, reg2, address - jump to location in program given by location, if reg1 >= reg2 (signed)
sz reg1, reg2 - set reg1=1 if reg2==0, otherwise set reg1=0 ble reg1, value, address - jump to location in program given by location, if reg1 <= immediate value (unsigned)
snz reg1, reg2 - set reg1=1 if reg2!=0, otherwise set reg1=0 bles reg1, value, address - jump to location in program given by location, if reg1 <= immediate value (signed)
seq reg1, reg2 - set reg1=1 if reg1 == reg2, otherwise set reg1=0 ( NOTE: there are no bltr/bler instructions because these are equivalent to bgtr/bger with the register operands swapped around.)
sne reg1, reg2 - set reg1=1 if reg1 != reg2, otherwise set reg1=0 sz reg1, reg2 - set reg1=1.b if reg2==0, otherwise set reg1=0.b
slt reg1, reg2 - set reg1=1 if reg1 < reg2 (unsigned), otherwise set reg1=0 snz reg1, reg2 - set reg1=1.b if reg2!=0, otherwise set reg1=0.b
slts reg1, reg2 - set reg1=1 if reg1 < reg2 (signed), otherwise set reg1=0 seq reg1, reg2 - set reg1=1.b if reg1 == reg2, otherwise set reg1=0.b
sle reg1, reg2 - set reg1=1 if reg1 <= reg2 (unsigned), otherwise set reg1=0 sne reg1, reg2 - set reg1=1.b if reg1 != reg2, otherwise set reg1=0.b
sles reg1, reg2 - set reg1=1 if reg1 <= reg2 (signed), otherwise set reg1=0 slt reg1, reg2 - set reg1=1.b if reg1 < reg2 (unsigned), otherwise set reg1=0.b
sgt reg1, reg2 - set reg1=1 if reg1 > reg2 (unsigned), otherwise set reg1=0 slts reg1, reg2 - set reg1=1.b if reg1 < reg2 (signed), otherwise set reg1=0.b
sgts reg1, reg2 - set reg1=1 if reg1 > reg2 (signed), otherwise set reg1=0 sle reg1, reg2 - set reg1=1.b if reg1 <= reg2 (unsigned), otherwise set reg1=0.b
sge reg1, reg2 - set reg1=1 if reg1 >= reg2 (unsigned), otherwise set reg1=0 sles reg1, reg2 - set reg1=1.b if reg1 <= reg2 (signed), otherwise set reg1=0.b
sges reg1, reg2 - set reg1=1 if reg1 >= reg2 (signed), otherwise set reg1=0 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 ARITHMETIC
---------- ----------
All have type b or w or f. Note: result types are the same as operand types! E.g. byte*byte->byte. 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) 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 inc reg1 - reg1 = reg1+1
incm address - memory at address += 1 incm address - memory at address += 1
dec reg1 - reg1 = reg1-1 dec reg1 - reg1 = reg1-1
decm address - memory at address -= 1 decm address - memory at address -= 1
neg reg1 - reg1 = sign negation of reg1 neg reg1 - reg1 = sign negation of reg1
negm address - sign negate memory at address negm address - sign negate memory at address
addr reg1, reg2 - reg1 += reg2 (unsigned + signed) addr reg1, reg2 - reg1 += reg2
add reg1, value - reg1 += value (unsigned + signed) add reg1, value - reg1 += value
addm reg1, address - memory at address += reg1 (unsigned + signed) addm reg1, address - memory at address += reg1
subr reg1, reg2 - reg1 -= reg2 (unsigned + signed) subr reg1, reg2 - reg1 -= reg2
sub reg1, value - reg1 -= value (unsigned + signed) sub reg1, value - reg1 -= value
subm reg1, address - memory at address -= reg1 (unsigned + signed) subm reg1, address - memory at address -= reg1
mulr reg1, reg2 - unsigned multiply reg1 *= reg2 note: byte*byte->byte, no type extension to word! 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! 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! 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 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 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 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) 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 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 and remainder on value stack (so need to be POPped off)
sqrt reg1, reg2 - reg1 is the square root of reg2 sqrt reg1, reg2 - reg1 is the square root of reg2
sgn reg1, reg2 - reg1 is the sign of reg2 (0, 1 or -1) 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) 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 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) 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) 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 push [b, w, f] reg1 - push value in reg1 on the stack
pop [b, w] reg1 - pop value from stack into reg1 pop [b, w, f] reg1 - pop value from stack into reg1
binarydata - 'instruction' to hold inlined binary data bytes
*/ */
enum class Opcode { enum class Opcode {
@ -217,24 +229,20 @@ enum class Opcode {
LOADX, LOADX,
LOADIX, LOADIX,
LOADR, LOADR,
LOADCPU,
STOREM, STOREM,
STORECPU,
STOREI, STOREI,
STOREX, STOREX,
STOREIX, STOREIX,
STOREZM, STOREZM,
STOREZCPU,
STOREZI, STOREZI,
STOREZX, STOREZX,
JUMP, JUMP,
JUMPA, JUMPA,
CALL, CALL,
CALLRVAL,
SYSCALL, SYSCALL,
RETURN, RETURN,
RETURNREG, RETURNR,
BSTCC, BSTCC,
BSTCS, BSTCS,
@ -244,18 +252,22 @@ enum class Opcode {
BSTPOS, BSTPOS,
BSTVC, BSTVC,
BSTVS, BSTVS,
BZ, BEQR,
BNZ,
BGZS,
BGEZS,
BLZS,
BLEZS,
BEQ, BEQ,
BNER,
BNE, BNE,
BGTR,
BGT, BGT,
BLT,
BGTSR,
BGTS, BGTS,
BLTS,
BGER,
BGE, BGE,
BLE,
BGESR,
BGES, BGES,
BLES,
SZ, SZ,
SNZ, SNZ,
SEQ, SEQ,
@ -359,24 +371,22 @@ enum class Opcode {
POP, POP,
MSIG, MSIG,
CONCAT, CONCAT,
BREAKPOINT, BREAKPOINT
BINARYDATA
} }
val OpcodesThatJump = setOf( val OpcodesThatJump = setOf(
Opcode.JUMP, Opcode.JUMP,
Opcode.JUMPA, Opcode.JUMPA,
Opcode.RETURN, Opcode.RETURN,
Opcode.RETURNREG Opcode.RETURNR
) )
val OpcodesThatBranch = setOf( val OpcodesThatBranch = setOf(
Opcode.JUMP, Opcode.JUMP,
Opcode.JUMPA, Opcode.JUMPA,
Opcode.RETURN, Opcode.RETURN,
Opcode.RETURNREG, Opcode.RETURNR,
Opcode.CALL, Opcode.CALL,
Opcode.CALLRVAL,
Opcode.SYSCALL, Opcode.SYSCALL,
Opcode.BSTCC, Opcode.BSTCC,
Opcode.BSTCS, Opcode.BSTCS,
@ -386,81 +396,22 @@ val OpcodesThatBranch = setOf(
Opcode.BSTPOS, Opcode.BSTPOS,
Opcode.BSTVC, Opcode.BSTVC,
Opcode.BSTVS, Opcode.BSTVS,
Opcode.BZ, Opcode.BEQR,
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.BEQ, Opcode.BEQ,
Opcode.BNER,
Opcode.BNE, Opcode.BNE,
Opcode.BGTR,
Opcode.BGT, Opcode.BGT,
Opcode.BLT,
Opcode.BGTSR,
Opcode.BGTS, Opcode.BGTS,
Opcode.BLTS,
Opcode.BGER,
Opcode.BGE, Opcode.BGE,
Opcode.BLE,
Opcode.BGESR,
Opcode.BGES, Opcode.BGES,
Opcode.INCM, Opcode.BLES
Opcode.DECM,
Opcode.NEGM,
Opcode.ADDM,
Opcode.SUBM,
Opcode.MULM,
Opcode.DIVM,
Opcode.DIVSM,
Opcode.INVM,
Opcode.ORM,
Opcode.XORM,
Opcode.ANDM,
Opcode.ASRM,
Opcode.LSRM,
Opcode.LSLM,
Opcode.LSLNM,
Opcode.LSRNM,
Opcode.ASRNM,
Opcode.ROLM,
Opcode.RORM,
Opcode.ROXLM,
Opcode.ROXRM
) )
enum class IRDataType { enum class IRDataType {
@ -482,9 +433,10 @@ data class InstructionFormat(val datatype: IRDataType?,
val reg2: OperandDirection, val reg2: OperandDirection,
val fpReg1: OperandDirection, val fpReg1: OperandDirection,
val fpReg2: OperandDirection, val fpReg2: OperandDirection,
val valueIn: Boolean, val address: OperandDirection,
val fpValueIn: Boolean val immediate: Boolean,
) { val funcCall: Boolean,
val sysCall: Boolean) {
companion object { companion object {
fun from(spec: String): Map<IRDataType?, InstructionFormat> { fun from(spec: String): Map<IRDataType?, InstructionFormat> {
val result = mutableMapOf<IRDataType?, InstructionFormat>() val result = mutableMapOf<IRDataType?, InstructionFormat>()
@ -493,37 +445,41 @@ data class InstructionFormat(val datatype: IRDataType?,
var reg2 = OperandDirection.UNUSED var reg2 = OperandDirection.UNUSED
var fpreg1 = OperandDirection.UNUSED var fpreg1 = OperandDirection.UNUSED
var fpreg2 = OperandDirection.UNUSED var fpreg2 = OperandDirection.UNUSED
var valueIn = false var address = OperandDirection.UNUSED
var fpvalueIn = false var immediate = false
val splits = part.splitToSequence(',').iterator() val splits = part.splitToSequence(',').iterator()
val typespec = splits.next() val typespec = splits.next()
var funcCall = false
var sysCall = false
while(splits.hasNext()) { while(splits.hasNext()) {
when(splits.next()) { when(splits.next()) {
"<r1" -> { reg1=OperandDirection.READ } "<r1" -> reg1 = OperandDirection.READ
">r1" -> { reg1=OperandDirection.WRITE } ">r1" -> reg1 = OperandDirection.WRITE
"<>r1" -> { reg1=OperandDirection.READWRITE } "<>r1" -> reg1 = OperandDirection.READWRITE
"<r2" -> reg2 = OperandDirection.READ "<r2" -> reg2 = OperandDirection.READ
"<fr1" -> { fpreg1=OperandDirection.READ } "<fr1" -> fpreg1 = OperandDirection.READ
">fr1" -> { fpreg1=OperandDirection.WRITE } ">fr1" -> fpreg1 = OperandDirection.WRITE
"<>fr1" -> { fpreg1=OperandDirection.READWRITE } "<>fr1" -> fpreg1 = OperandDirection.READWRITE
"<fr2" -> fpreg2 = OperandDirection.READ "<fr2" -> fpreg2 = OperandDirection.READ
"<v" -> { ">i", "<>i" -> throw IllegalArgumentException("can't write into an immediate value")
if('F' in typespec) "<i" -> immediate = true
fpvalueIn = true "<a" -> address = OperandDirection.READ
else ">a" -> address = OperandDirection.WRITE
valueIn = true "<>a" -> address = OperandDirection.READWRITE
} "call" -> funcCall = true
"syscall" -> sysCall = true
else -> throw IllegalArgumentException(spec) else -> throw IllegalArgumentException(spec)
} }
} }
if(typespec=="N") 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) 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) 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) 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 return result
} }
@ -534,54 +490,57 @@ data class InstructionFormat(val datatype: IRDataType?,
<X = X is not modified (readonly value) <X = X is not modified (readonly value)
>X = X is overwritten with output value (write value) >X = X is overwritten with output value (write value)
<>X = X is modified (read + written) <>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( val instructionFormats = mutableMapOf(
Opcode.NOP to InstructionFormat.from("N"), Opcode.NOP to InstructionFormat.from("N"),
Opcode.LOAD 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,<v | F,>fr1,<v"), Opcode.LOADM to InstructionFormat.from("BW,>r1,<a | F,>fr1,<a"),
Opcode.LOADI to InstructionFormat.from("BW,>r1,<r2 | F,>fr1,<r1"), 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.LOADX to InstructionFormat.from("BW,>r1,<r2,<a | F,>fr1,<r1,<a"),
Opcode.LOADIX to InstructionFormat.from("BW,>r1,<r2,<v | F,>fr1,<r1,<v"), 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.LOADR to InstructionFormat.from("BW,>r1,<r2 | F,>fr1,<fr2"),
Opcode.LOADCPU to InstructionFormat.from("BW,>r1"), Opcode.STOREM to InstructionFormat.from("BW,<r1,>a | F,<fr1,>a"),
Opcode.STOREM to InstructionFormat.from("BW,<r1,<v | F,<fr1,<v"),
Opcode.STORECPU to InstructionFormat.from("BW,<r1"),
Opcode.STOREI to InstructionFormat.from("BW,<r1,<r2 | F,<fr1,<r1"), Opcode.STOREI to InstructionFormat.from("BW,<r1,<r2 | F,<fr1,<r1"),
Opcode.STOREX to InstructionFormat.from("BW,<r1,<r2,<v | F,<fr1,<r1,<v"), Opcode.STOREX to InstructionFormat.from("BW,<r1,<r2,>a | F,<fr1,<r1,>a"),
Opcode.STOREIX to InstructionFormat.from("BW,<r1,<r2,<v | F,<fr1,<r1,<v"), Opcode.STOREIX to InstructionFormat.from("BW,<r1,<r2,>a | F,<fr1,<r1,>a"),
Opcode.STOREZM to InstructionFormat.from("BW,<v | F,<v"), Opcode.STOREZM to InstructionFormat.from("BW,>a | F,>a"),
Opcode.STOREZCPU to InstructionFormat.from("BW"),
Opcode.STOREZI to InstructionFormat.from("BW,<r1 | F,<r1"), Opcode.STOREZI to InstructionFormat.from("BW,<r1 | F,<r1"),
Opcode.STOREZX to InstructionFormat.from("BW,<r1,<v | F,<r1,<v"), Opcode.STOREZX to InstructionFormat.from("BW,<r1,>a | F,<r1,>a"),
Opcode.JUMP to InstructionFormat.from("N,<v"), Opcode.JUMP to InstructionFormat.from("N,<a"),
Opcode.JUMPA to InstructionFormat.from("N,<v"), Opcode.JUMPA to InstructionFormat.from("N,<a"),
Opcode.CALL to InstructionFormat.from("N,<v"), Opcode.CALL to InstructionFormat.from("N,call"),
Opcode.CALLRVAL to InstructionFormat.from("BW,<r1,<v | F,<fr1,<v"), Opcode.SYSCALL to InstructionFormat.from("N,syscall"),
Opcode.SYSCALL to InstructionFormat.from("N,<v"),
Opcode.RETURN to InstructionFormat.from("N"), Opcode.RETURN to InstructionFormat.from("N"),
Opcode.RETURNREG to InstructionFormat.from("BW,<r1 | F,<fr1"), Opcode.RETURNR to InstructionFormat.from("BW,>r1 | F,>fr1"),
Opcode.BSTCC to InstructionFormat.from("N,<v"), Opcode.BSTCC to InstructionFormat.from("N,<a"),
Opcode.BSTCS to InstructionFormat.from("N,<v"), Opcode.BSTCS to InstructionFormat.from("N,<a"),
Opcode.BSTEQ to InstructionFormat.from("N,<v"), Opcode.BSTEQ to InstructionFormat.from("N,<a"),
Opcode.BSTNE to InstructionFormat.from("N,<v"), Opcode.BSTNE to InstructionFormat.from("N,<a"),
Opcode.BSTNEG to InstructionFormat.from("N,<v"), Opcode.BSTNEG to InstructionFormat.from("N,<a"),
Opcode.BSTPOS to InstructionFormat.from("N,<v"), Opcode.BSTPOS to InstructionFormat.from("N,<a"),
Opcode.BSTVC to InstructionFormat.from("N,<v"), Opcode.BSTVC to InstructionFormat.from("N,<a"),
Opcode.BSTVS to InstructionFormat.from("N,<v"), Opcode.BSTVS to InstructionFormat.from("N,<a"),
Opcode.BZ to InstructionFormat.from("BW,<r1,<v"), Opcode.BEQR to InstructionFormat.from("BW,<r1,<r2,<a"),
Opcode.BNZ to InstructionFormat.from("BW,<r1,<v"), Opcode.BEQ to InstructionFormat.from("BW,<r1,<i,<a"),
Opcode.BGZS to InstructionFormat.from("BW,<r1,<v"), Opcode.BNER to InstructionFormat.from("BW,<r1,<r2,<a"),
Opcode.BGEZS to InstructionFormat.from("BW,<r1,<v"), Opcode.BNE to InstructionFormat.from("BW,<r1,<i,<a"),
Opcode.BLZS to InstructionFormat.from("BW,<r1,<v"), Opcode.BGTR to InstructionFormat.from("BW,<r1,<r2,<a"),
Opcode.BLEZS to InstructionFormat.from("BW,<r1,<v"), Opcode.BGT to InstructionFormat.from("BW,<r1,<i,<a"),
Opcode.BEQ to InstructionFormat.from("BW,<r1,<r2,<v"), Opcode.BLT to InstructionFormat.from("BW,<r1,<i,<a"),
Opcode.BNE to InstructionFormat.from("BW,<r1,<r2,<v"), Opcode.BGTSR to InstructionFormat.from("BW,<r1,<r2,<a"),
Opcode.BGT to InstructionFormat.from("BW,<r1,<r2,<v"), Opcode.BGTS to InstructionFormat.from("BW,<r1,<i,<a"),
Opcode.BGTS to InstructionFormat.from("BW,<r1,<r2,<v"), Opcode.BLTS to InstructionFormat.from("BW,<r1,<i,<a"),
Opcode.BGE to InstructionFormat.from("BW,<r1,<r2,<v"), Opcode.BGER to InstructionFormat.from("BW,<r1,<r2,<a"),
Opcode.BGES to InstructionFormat.from("BW,<r1,<r2,<v"), 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.SZ to InstructionFormat.from("BW,>r1,<r2"),
Opcode.SNZ to InstructionFormat.from("BW,>r1,<r2"), Opcode.SNZ to InstructionFormat.from("BW,>r1,<r2"),
Opcode.SEQ 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.SGE to InstructionFormat.from("BW,<>r1,<r2"),
Opcode.SGES to InstructionFormat.from("BW,<>r1,<r2"), Opcode.SGES to InstructionFormat.from("BW,<>r1,<r2"),
Opcode.INC to InstructionFormat.from("BW,<>r1 | F,<>fr1"), 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.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.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.ADDR to InstructionFormat.from("BW,<>r1,<r2 | F,<>fr1,<fr2"),
Opcode.ADD 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,<v | F,<fr1,<v"), Opcode.ADDM to InstructionFormat.from("BW,<r1,<>a | F,<fr1,<>a"),
Opcode.SUBR to InstructionFormat.from("BW,<>r1,<r2 | F,<>fr1,<fr2"), Opcode.SUBR to InstructionFormat.from("BW,<>r1,<r2 | F,<>fr1,<fr2"),
Opcode.SUB 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,<v | F,<fr1,<v"), Opcode.SUBM to InstructionFormat.from("BW,<r1,<>a | F,<fr1,<>a"),
Opcode.MULR to InstructionFormat.from("BW,<>r1,<r2 | F,<>fr1,<fr2"), Opcode.MULR to InstructionFormat.from("BW,<>r1,<r2 | F,<>fr1,<fr2"),
Opcode.MUL 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,<v | F,<fr1,<v"), Opcode.MULM to InstructionFormat.from("BW,<r1,<>a | F,<fr1,<>a"),
Opcode.DIVR to InstructionFormat.from("BW,<>r1,<r2"), Opcode.DIVR to InstructionFormat.from("BW,<>r1,<r2"),
Opcode.DIV to InstructionFormat.from("BW,<>r1,<v"), Opcode.DIV to InstructionFormat.from("BW,<>r1,<i"),
Opcode.DIVM to InstructionFormat.from("BW,<r1,<v"), Opcode.DIVM to InstructionFormat.from("BW,<r1,<>a"),
Opcode.DIVSR to InstructionFormat.from("BW,<>r1,<r2 | F,<>fr1,<fr2"), Opcode.DIVSR to InstructionFormat.from("BW,<>r1,<r2 | F,<>fr1,<fr2"),
Opcode.DIVS 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,<v | F,<fr1,<v"), Opcode.DIVSM to InstructionFormat.from("BW,<r1,<>a | F,<fr1,<>a"),
Opcode.SQRT to InstructionFormat.from("BW,>r1,<r2 | F,>fr1,<fr2"), Opcode.SQRT to InstructionFormat.from("BW,>r1,<r2 | F,>fr1,<fr2"),
Opcode.SGN 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.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.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.CMP to InstructionFormat.from("BW,<r1,<r2"),
Opcode.EXT to InstructionFormat.from("BW,<>r1"), Opcode.EXT to InstructionFormat.from("BW,<>r1"),
Opcode.EXTS to InstructionFormat.from("BW,<>r1"), Opcode.EXTS to InstructionFormat.from("BW,<>r1"),
Opcode.ANDR to InstructionFormat.from("BW,<>r1,<r2"), Opcode.ANDR to InstructionFormat.from("BW,<>r1,<r2"),
Opcode.AND to InstructionFormat.from("BW,<>r1,<v"), Opcode.AND to InstructionFormat.from("BW,<>r1,<i"),
Opcode.ANDM to InstructionFormat.from("BW,<r1,<v"), Opcode.ANDM to InstructionFormat.from("BW,<r1,<>a"),
Opcode.ORR to InstructionFormat.from("BW,<>r1,<r2"), Opcode.ORR to InstructionFormat.from("BW,<>r1,<r2"),
Opcode.OR to InstructionFormat.from("BW,<>r1,<v"), Opcode.OR to InstructionFormat.from("BW,<>r1,<i"),
Opcode.ORM to InstructionFormat.from("BW,<r1,<v"), Opcode.ORM to InstructionFormat.from("BW,<r1,<>a"),
Opcode.XORR to InstructionFormat.from("BW,<>r1,<r2"), Opcode.XORR to InstructionFormat.from("BW,<>r1,<r2"),
Opcode.XOR to InstructionFormat.from("BW,<>r1,<v"), Opcode.XOR to InstructionFormat.from("BW,<>r1,<i"),
Opcode.XORM to InstructionFormat.from("BW,<r1,<v"), Opcode.XORM to InstructionFormat.from("BW,<r1,<>a"),
Opcode.INV to InstructionFormat.from("BW,<>r1"), 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.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.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.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.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.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.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.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.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.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.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.FFROMUB to InstructionFormat.from("F,>fr1,<r1"),
Opcode.FFROMSB 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.FCEIL to InstructionFormat.from("F,>fr1,<fr2"),
Opcode.MSIG to InstructionFormat.from("BW,>r1,<r2"), Opcode.MSIG to InstructionFormat.from("BW,>r1,<r2"),
Opcode.PUSH to InstructionFormat.from("BW,<r1"), Opcode.PUSH to InstructionFormat.from("BW,<r1 | F,<fr1"),
Opcode.POP to InstructionFormat.from("BW,>r1"), Opcode.POP to InstructionFormat.from("BW,>r1 | F,>fr1"),
Opcode.CONCAT to InstructionFormat.from("BW,<>r1,<r2"), Opcode.CONCAT to InstructionFormat.from("BW,<>r1,<r2"),
Opcode.CLC to InstructionFormat.from("N"), Opcode.CLC to InstructionFormat.from("N"),
Opcode.SEC to InstructionFormat.from("N"), Opcode.SEC to InstructionFormat.from("N"),
Opcode.BREAKPOINT 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( data class IRInstruction(
val opcode: Opcode, val opcode: Opcode,
val type: IRDataType?=null, val type: IRDataType?=null,
@ -695,11 +661,12 @@ data class IRInstruction(
val reg2: Int?=null, // 0-$ffff val reg2: Int?=null, // 0-$ffff
val fpReg1: Int?=null, // 0-$ffff val fpReg1: Int?=null, // 0-$ffff
val fpReg2: Int?=null, // 0-$ffff val fpReg2: Int?=null, // 0-$ffff
val value: Int?=null, // 0-$ffff val immediate: Int?=null, // 0-$ff or $ffff if word
val fpValue: Float?=null, val immediateFp: Float?=null,
val labelSymbol: String?=null, // symbolic label name as alternative to value (so only for Branch/jump/call Instructions!) val address: Int?=null, // 0-$ffff
val binaryData: Collection<UByte>?=null, 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 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) // 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. // 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(fpReg1==null || fpReg1 in 0..65536) {"fpReg1 out of bounds"}
require(fpReg2==null || fpReg2 in 0..65536) {"fpReg2 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(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) val formats = instructionFormats.getValue(opcode)
require (type != null || formats.containsKey(null)) { "missing type" } 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.reg2==OperandDirection.UNUSED) require(reg2==null) { "invalid reg2" }
if(format.fpReg1==OperandDirection.UNUSED) require(fpReg1==null) { "invalid fpReg1" } if(format.fpReg1==OperandDirection.UNUSED) require(fpReg1==null) { "invalid fpReg1" }
if(format.fpReg2==OperandDirection.UNUSED) require(fpReg2==null) { "invalid fpReg2" } if(format.fpReg2==OperandDirection.UNUSED) require(fpReg2==null) { "invalid fpReg2" }
if(format.immediate) {
if (type==IRDataType.FLOAT) { if(type==IRDataType.FLOAT)
if(format.fpValueIn) require(fpValue!=null || labelSymbol!=null) {"missing a fp-value or labelsymbol"} require(immediateFp !=null) {"missing immediate fp value"}
} else { else
if(format.valueIn) require(value!=null || labelSymbol!=null) {"missing a value or labelsymbol"} require(immediate!=null || labelSymbol!=null) {"missing immediate value or labelsymbol"}
require(fpReg1==null && fpReg2==null) {"integer point instruction can't use floating point registers"} }
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 reg1direction = format.reg1
reg2direction = format.reg2 reg2direction = format.reg2
fpReg1direction = format.fpReg1 fpReg1direction = format.fpReg1
fpReg2direction = format.fpReg2 fpReg2direction = format.fpReg2
if(opcode in setOf(Opcode.BEQ, Opcode.BNE, if(opcode==Opcode.SYSCALL) {
Opcode.BGT, Opcode.BGTS, require(immediate!=null) {
Opcode.BGE, Opcode.BGES, "syscall needs immediate integer for the syscall number"
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"}
} }
} }
fun addUsedRegistersCounts( fun addUsedRegistersCounts(
readRegs: MutableMap<Int, Int>, readRegsCounts: MutableMap<Int, Int>,
writeRegs: MutableMap<Int, Int>, writeRegsCounts: MutableMap<Int, Int>,
readFpRegs: MutableMap<Int, Int>, readFpRegsCounts: MutableMap<Int, Int>,
writeFpRegs: MutableMap<Int, Int> writeFpRegsCounts: MutableMap<Int, Int>,
regsTypes: MutableMap<Int, MutableSet<IRDataType>>
) { ) {
when (this.reg1direction) { when (this.reg1direction) {
OperandDirection.UNUSED -> {} OperandDirection.UNUSED -> {}
OperandDirection.READ -> readRegs[this.reg1!!] = readRegs.getValue(this.reg1)+1 OperandDirection.READ -> {
OperandDirection.WRITE -> 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
}
}
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 -> { OperandDirection.READWRITE -> {
readRegs[this.reg1!!] = readRegs.getValue(this.reg1)+1 readRegsCounts[this.reg1!!] = readRegsCounts.getValue(this.reg1)+1
writeRegs[this.reg1] = writeRegs.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) { when (this.reg2direction) {
OperandDirection.UNUSED -> {} 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") else -> throw IllegalArgumentException("reg2 can only be read")
} }
when (this.fpReg1direction) { when (this.fpReg1direction) {
OperandDirection.UNUSED -> {} OperandDirection.UNUSED -> {}
OperandDirection.READ -> readFpRegs[this.fpReg1!!] = readFpRegs.getValue(this.fpReg1)+1 OperandDirection.READ -> {
OperandDirection.WRITE -> writeFpRegs[this.fpReg1!!] = writeFpRegs.getValue(this.fpReg1)+1 readFpRegsCounts[this.fpReg1!!] = readFpRegsCounts.getValue(this.fpReg1)+1
}
OperandDirection.WRITE -> writeFpRegsCounts[this.fpReg1!!] = writeFpRegsCounts.getValue(this.fpReg1)+1
OperandDirection.READWRITE -> { OperandDirection.READWRITE -> {
readFpRegs[this.fpReg1!!] = readFpRegs.getValue(this.fpReg1)+1 readFpRegsCounts[this.fpReg1!!] = readFpRegsCounts.getValue(this.fpReg1)+1
writeFpRegs[this.fpReg1] = writeFpRegs.getValue(this.fpReg1)+1 writeFpRegsCounts[this.fpReg1] = writeFpRegsCounts.getValue(this.fpReg1)+1
} }
} }
when (this.fpReg2direction) { when (this.fpReg2direction) {
OperandDirection.UNUSED -> {} 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") else -> throw IllegalArgumentException("fpReg2 can only be read")
} }
} }
@ -811,32 +810,84 @@ data class IRInstruction(
IRDataType.FLOAT -> result.add(".f ") IRDataType.FLOAT -> result.add(".f ")
else -> result.add(" ") else -> result.add(" ")
} }
reg1?.let {
result.add("r$it") if(this.fcallArgs!=null) {
result.add(",") immediate?.let { result.add(it.toHex()) } // syscall
} labelSymbol?.let { result.add(it) } // regular subroutine call
reg2?.let { address?.let { result.add(address.toHex()) } // romcall
result.add("r$it") result.add("(")
result.add(",") fcallArgs.arguments.forEach {
} val location = if(it.address==null) {
fpReg1?.let { if(it.name.isBlank()) "" else it.name+"="
result.add("fr$it") } else "${it.address}="
result.add(",")
} val cpuReg = if(it.reg.cpuRegister==null) "" else {
fpReg2?.let { if(it.reg.cpuRegister.registerOrPair!=null)
result.add("fr$it") "@"+it.reg.cpuRegister.registerOrPair.toString()
result.add(",") else
} "@"+it.reg.cpuRegister.statusflag.toString()
value?.let { }
result.add(it.toHex())
result.add(",") when(it.reg.dt) {
} IRDataType.BYTE -> result.add("${location}r${it.reg.registerNum}.b$cpuReg,")
fpValue?.let { IRDataType.WORD -> result.add("${location}r${it.reg.registerNum}.w$cpuReg,")
result.add(it.toString()) IRDataType.FLOAT -> result.add("${location}fr${it.reg.registerNum}.f$cpuReg,")
result.add(",") }
} }
labelSymbol?.let { if(result.last().endsWith(',')) {
result.add(it) 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() == ",") if(result.last() == ",")
result.removeLast() result.removeLast()

View File

@ -60,7 +60,7 @@ class IRProgram(val name: String,
} }
fun addBlock(block: IRBlock) { 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) blocks.add(block)
} }
@ -134,7 +134,7 @@ class IRProgram(val name: String,
// link all jump and branching instructions to their target // link all jump and branching instructions to their target
chunk.instructions.forEach { 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) it.branchTarget = labeledChunks.getValue(it.labelSymbol)
// note: branches with an address value cannot be linked to something... // note: branches with an address value cannot be linked to something...
} }
@ -170,7 +170,10 @@ class IRProgram(val name: String,
fun validate() { fun validate() {
blocks.forEach { block -> blocks.forEach { block ->
if(block.isNotEmpty()) { 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 -> block.children.filterIsInstance<IRSubroutine>().forEach { sub ->
if(sub.chunks.isNotEmpty()) { if(sub.chunks.isNotEmpty()) {
require(sub.chunks.first().label == sub.label) { "first chunk in subroutine should have sub name (label) as its label" } 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) { if (chunk is IRCodeChunk) {
require(chunk.instructions.isNotEmpty() || chunk.label != null) require(chunk.instructions.isNotEmpty() || chunk.label != null)
if(chunk.instructions.lastOrNull()?.opcode in OpcodesThatJump) 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 { else {
// if chunk is NOT the last in the block, it needs to link to next. // if chunk is NOT the last in the block, it needs to link to next.
val isLast = sub.chunks.last() === chunk val isLast = sub.chunks.last() === chunk
require(isLast || chunk.next != null) { "chunk needs to be linked to next" } require(isLast || chunk.next != null) { "chunk needs to be linked to next" }
} }
} }
else else {
require(chunk.instructions.isEmpty()) 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 { chunk.instructions.forEach {
if(it.labelSymbol!=null && it.opcode in OpcodesThatBranch) if(it.labelSymbol!=null && it.opcode in OpcodesThatBranch)
require(it.branchTarget != null) { "branching instruction to label should have branchTarget set" } require(it.branchTarget != null) { "branching instruction to label should have branchTarget set" }
@ -199,19 +205,29 @@ class IRProgram(val name: String,
} }
fun registersUsed(): RegistersUsed { fun registersUsed(): RegistersUsed {
val readRegs = mutableMapOf<Int, Int>().withDefault { 0 } val readRegsCounts = mutableMapOf<Int, Int>().withDefault { 0 }
val readFpRegs = mutableMapOf<Int, Int>().withDefault { 0 } val regsTypes = mutableMapOf<Int, MutableSet<IRDataType>>()
val writeRegs = mutableMapOf<Int, Int>().withDefault { 0 } val readFpRegsCounts = mutableMapOf<Int, Int>().withDefault { 0 }
val writeFpRegs = mutableMapOf<Int, Int>().withDefault { 0 } val writeRegsCounts = mutableMapOf<Int, Int>().withDefault { 0 }
val writeFpRegsCounts = mutableMapOf<Int, Int>().withDefault { 0 }
fun addUsed(usedRegisters: RegistersUsed) { fun addUsed(usedRegisters: RegistersUsed) {
usedRegisters.readRegs.forEach{ (reg, count) -> readRegs[reg] = readRegs.getValue(reg) + count } usedRegisters.readRegs.forEach{ (reg, count) -> readRegsCounts[reg] = readRegsCounts.getValue(reg) + count }
usedRegisters.writeRegs.forEach{ (reg, count) -> writeRegs[reg] = writeRegs.getValue(reg) + count } usedRegisters.writeRegs.forEach{ (reg, count) -> writeRegsCounts[reg] = writeRegsCounts.getValue(reg) + count }
usedRegisters.readFpRegs.forEach{ (reg, count) -> readFpRegs[reg] = readFpRegs.getValue(reg) + count } usedRegisters.readFpRegs.forEach{ (reg, count) -> readFpRegsCounts[reg] = readFpRegsCounts.getValue(reg) + count }
usedRegisters.writeFpRegs.forEach{ (reg, count) -> writeFpRegs[reg] = writeFpRegs.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 -> blocks.forEach {block ->
block.children.forEach { child -> block.children.forEach { child ->
when(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( class IRBlock(
val name: String, val label: String,
val address: UInt?, val address: UInt?,
val library: Boolean,
val forceOutput: Boolean,
val alignment: BlockAlignment, val alignment: BlockAlignment,
val position: Position val position: Position
) { ) {
@ -333,12 +409,13 @@ class IRCodeChunk(label: String?, next: IRCodeChunkBase?): IRCodeChunkBase(label
override fun isEmpty() = instructions.isEmpty() override fun isEmpty() = instructions.isEmpty()
override fun isNotEmpty() = instructions.isNotEmpty() override fun isNotEmpty() = instructions.isNotEmpty()
override fun usedRegisters(): RegistersUsed { override fun usedRegisters(): RegistersUsed {
val readRegs = mutableMapOf<Int, Int>().withDefault { 0 } val readRegsCounts = mutableMapOf<Int, Int>().withDefault { 0 }
val readFpRegs = mutableMapOf<Int, Int>().withDefault { 0 } val regsTypes = mutableMapOf<Int, MutableSet<IRDataType>>()
val writeRegs = mutableMapOf<Int, Int>().withDefault { 0 } val readFpRegsCounts = mutableMapOf<Int, Int>().withDefault { 0 }
val writeFpRegs = mutableMapOf<Int, Int>().withDefault { 0 } val writeRegsCounts = mutableMapOf<Int, Int>().withDefault { 0 }
instructions.forEach { it.addUsedRegistersCounts(readRegs, writeRegs, readFpRegs, writeFpRegs) } val writeFpRegsCounts = mutableMapOf<Int, Int>().withDefault { 0 }
return RegistersUsed(readRegs, writeRegs, readFpRegs, writeFpRegs) instructions.forEach { it.addUsedRegistersCounts(readRegsCounts, writeRegsCounts, readFpRegsCounts, writeFpRegsCounts, regsTypes) }
return RegistersUsed(readRegsCounts, writeRegsCounts, readFpRegsCounts, writeFpRegsCounts, regsTypes)
} }
operator fun plusAssign(ins: IRInstruction) { operator fun plusAssign(ins: IRInstruction) {
@ -373,7 +450,7 @@ class IRInlineBinaryChunk(label: String?,
// note: no instructions, data is in the property // note: no instructions, data is in the property
override fun isEmpty() = data.isEmpty() override fun isEmpty() = data.isEmpty()
override fun isNotEmpty() = data.isNotEmpty() 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> typealias IRCodeChunks = List<IRCodeChunkBase>
@ -384,9 +461,11 @@ class RegistersUsed(
val writeRegs: Map<Int, Int>, val writeRegs: Map<Int, Int>,
val readFpRegs: Map<Int, Int>, val readFpRegs: Map<Int, Int>,
val writeFpRegs: Map<Int, Int>, val writeFpRegs: Map<Int, Int>,
val regsTypes: Map<Int, Set<IRDataType>>
) { ) {
override fun toString(): String { 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() fun isEmpty() = readRegs.isEmpty() && writeRegs.isEmpty() && readFpRegs.isEmpty() && writeFpRegs.isEmpty()
@ -394,20 +473,23 @@ class RegistersUsed(
} }
private fun registersUsedInAssembly(isIR: Boolean, assembly: String): RegistersUsed { private fun registersUsedInAssembly(isIR: Boolean, assembly: String): RegistersUsed {
val readRegs = mutableMapOf<Int, Int>().withDefault { 0 } val readRegsCounts = mutableMapOf<Int, Int>().withDefault { 0 }
val readFpRegs = mutableMapOf<Int, Int>().withDefault { 0 } val regsTypes = mutableMapOf<Int, MutableSet<IRDataType>>()
val writeRegs = mutableMapOf<Int, Int>().withDefault { 0 } val readFpRegsCounts = mutableMapOf<Int, Int>().withDefault { 0 }
val writeFpRegs = mutableMapOf<Int, Int>().withDefault { 0 } val writeRegsCounts = mutableMapOf<Int, Int>().withDefault { 0 }
val writeFpRegsCounts = mutableMapOf<Int, Int>().withDefault { 0 }
if(isIR) { if(isIR) {
assembly.lineSequence().forEach { line -> assembly.lineSequence().forEach { line ->
val result = parseIRCodeLine(line.trim(), null, mutableMapOf()) val t = line.trim()
result.fold( if(t.isNotEmpty()) {
ifLeft = { it.addUsedRegistersCounts(readRegs, writeRegs, readFpRegs, writeFpRegs) }, val result = parseIRCodeLine(t)
ifRight = { /* labels can be skipped */ } result.fold(
) ifLeft = { it.addUsedRegistersCounts(readRegsCounts, writeRegsCounts,readFpRegsCounts, writeFpRegsCounts, regsTypes) },
ifRight = { /* labels can be skipped */ }
)
}
} }
} }
return RegistersUsed(readRegsCounts, writeRegsCounts, readFpRegsCounts, writeFpRegsCounts, regsTypes)
return RegistersUsed(readRegs, writeRegs, readFpRegs, writeFpRegs)
} }

View File

@ -4,6 +4,7 @@ import prog8.code.*
import prog8.code.ast.PtVariable import prog8.code.ast.PtVariable
import prog8.code.core.DataType import prog8.code.core.DataType
import prog8.code.core.ZeropageWish import prog8.code.core.ZeropageWish
import prog8.code.core.internedStringsModuleName
// In the Intermediate Representation, all nesting has been removed. // In the Intermediate Representation, all nesting has been removed.
@ -120,4 +121,15 @@ class IRSymbolTable(sourceSt: SymbolTable?) {
} }
fun getAsmSymbols(): Map<String, String> = asmSymbols 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 package prog8.intermediate
import prog8.code.* import prog8.code.*
import prog8.code.core.DataType import prog8.code.core.*
import prog8.code.core.InternalCompilerException
fun getTypeString(dt : DataType): String { fun getTypeString(dt : DataType): String = when(dt) {
return when(dt) { DataType.UBYTE -> "ubyte"
DataType.UBYTE -> "ubyte" DataType.BYTE -> "byte"
DataType.BYTE -> "byte" DataType.UWORD -> "uword"
DataType.UWORD -> "uword" DataType.WORD -> "word"
DataType.WORD -> "word" DataType.FLOAT -> "float"
DataType.FLOAT -> "float" DataType.ARRAY_UB, DataType.STR -> "ubyte[]"
DataType.ARRAY_UB, DataType.STR -> "ubyte[]" DataType.ARRAY_B -> "byte[]"
DataType.ARRAY_B -> "byte[]" DataType.ARRAY_UW -> "uword[]"
DataType.ARRAY_UW -> "uword[]" DataType.ARRAY_W -> "word[]"
DataType.ARRAY_W -> "word[]" DataType.ARRAY_F -> "float[]"
DataType.ARRAY_F -> "float[]" else -> throw InternalCompilerException("weird dt")
else -> throw InternalCompilerException("weird dt")
}
} }
fun getTypeString(memvar: StMemVar): String { fun getTypeString(memvar: StMemVar): String = when(memvar.dt) {
return when(memvar.dt) { DataType.UBYTE -> "ubyte"
DataType.UBYTE -> "ubyte" DataType.BYTE -> "byte"
DataType.BYTE -> "byte" DataType.UWORD -> "uword"
DataType.UWORD -> "uword" DataType.WORD -> "word"
DataType.WORD -> "word" DataType.FLOAT -> "float"
DataType.FLOAT -> "float" DataType.ARRAY_UB, DataType.STR -> "ubyte[${memvar.length}]"
DataType.ARRAY_UB, DataType.STR -> "ubyte[${memvar.length}]" DataType.ARRAY_B -> "byte[${memvar.length}]"
DataType.ARRAY_B -> "byte[${memvar.length}]" DataType.ARRAY_UW -> "uword[${memvar.length}]"
DataType.ARRAY_UW -> "uword[${memvar.length}]" DataType.ARRAY_W -> "word[${memvar.length}]"
DataType.ARRAY_W -> "word[${memvar.length}]" DataType.ARRAY_F -> "float[${memvar.length}]"
DataType.ARRAY_F -> "float[${memvar.length}]" else -> throw InternalCompilerException("weird dt")
else -> throw InternalCompilerException("weird dt")
}
} }
fun getTypeString(variable : StStaticVariable): String { fun getTypeString(variable : StStaticVariable): String = when(variable.dt) {
return when(variable.dt) { DataType.UBYTE -> "ubyte"
DataType.UBYTE -> "ubyte" DataType.BYTE -> "byte"
DataType.BYTE -> "byte" DataType.UWORD -> "uword"
DataType.UWORD -> "uword" DataType.WORD -> "word"
DataType.WORD -> "word" DataType.FLOAT -> "float"
DataType.FLOAT -> "float" DataType.ARRAY_UB, DataType.STR -> "ubyte[${variable.length}]"
DataType.ARRAY_UB, DataType.STR -> "ubyte[${variable.length}]" DataType.ARRAY_B -> "byte[${variable.length}]"
DataType.ARRAY_B -> "byte[${variable.length}]" DataType.ARRAY_UW -> "uword[${variable.length}]"
DataType.ARRAY_UW -> "uword[${variable.length}]" DataType.ARRAY_W -> "word[${variable.length}]"
DataType.ARRAY_W -> "word[${variable.length}]" DataType.ARRAY_F -> "float[${variable.length}]"
DataType.ARRAY_F -> "float[${variable.length}]" else -> throw InternalCompilerException("weird dt")
else -> throw InternalCompilerException("weird dt")
}
} }
fun convertIRType(typestr: String): IRDataType? { 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 instructionPattern = Regex("""([a-z]+)(\.b|\.w|\.f)?(.*)""", RegexOption.IGNORE_CASE)
private val labelPattern = Regex("""_([a-zA-Z\d\._]+):""") 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> { fun parseIRCodeLine(line: 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.
val labelmatch = labelPattern.matchEntire(line.trim()) val labelmatch = labelPattern.matchEntire(line.trim())
if(labelmatch!=null) if(labelmatch!=null)
return right(labelmatch.groupValues[1]) // it's a label. 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) var type: IRDataType? = convertIRType(typestr)
val formats = instructionFormats.getValue(opcode) val formats = instructionFormats.getValue(opcode)
val format: InstructionFormat val format: InstructionFormat =
if(type !in formats) { if(type !in formats) {
type = IRDataType.BYTE type = IRDataType.BYTE
format = if(type !in formats) if(type !in formats)
formats.getValue(null) formats.getValue(null)
else else
formats.getValue(type)
} else {
formats.getValue(type) formats.getValue(type)
} else { }
format = formats.getValue(type)
}
// parse the operands // 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 reg1: Int? = null
var reg2: Int? = null var reg2: Int? = null
var reg3: Int? = null
var fpReg1: Int? = null var fpReg1: Int? = null
var fpReg2: Int? = null var fpReg2: Int? = null
var fpReg3: Int? = null var immediateInt: Int? = null
var value: Float? = null var immediateFp: Float? = null
var operand: String? var address: Int? = null
var labelSymbol: String? = 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()) { return if(operand[0].isLetter()) {
labelSymbol = rest.split(",")[restIndex].trim()
if(location!=null)
placeholders[location] = labelSymbol!!
null null
} else { } else {
parseIRValue(operand) parseIRValue(operand)
} }
} }
if(format.sysCall) {
if(operands.isNotEmpty() && operands[0].isNotEmpty()) { val call = parseCall(rest)
operand = operands.removeFirst().trim() val syscallNum = parseIRValue(call.target).toInt()
if(operand[0]=='r') return left(IRInstruction(Opcode.SYSCALL, immediate = syscallNum, fcallArgs = FunctionCallArgs(call.args, call.returns)))
reg1 = operand.substring(1).toInt() } else if (format.funcCall) {
else if(operand[0]=='f' && operand[1]=='r') val call = parseCall(rest)
fpReg1 = operand.substring(2).toInt() return left(IRInstruction(Opcode.CALL, labelSymbol = call.target, fcallArgs = FunctionCallArgs(call.args, call.returns)))
else { } else {
value = parseValueOrPlaceholder(operand, location, rest, 0) operands.forEach { oper ->
operands.clear() if (oper[0] == '&')
} throw IRParseException("address-of should be done with normal LOAD <symbol>")
if(operands.isNotEmpty()) { else if (oper[0] in "rR") {
operand = operands.removeFirst().trim() if (reg1 == null) reg1 = oper.substring(1).toInt()
if(operand[0]=='r') else if (reg2 == null) reg2 = oper.substring(1).toInt()
reg2 = operand.substring(1).toInt() else throw IRParseException("too many register operands")
else if(operand[0]=='f' && operand[1]=='r') } else if (oper[0] in "fF" && oper[1] in "rR") {
fpReg2 = operand.substring(2).toInt() if (fpReg1 == null) fpReg1 = oper.substring(2).toInt()
else { else if (fpReg2 == null) fpReg2 = oper.substring(2).toInt()
value = parseValueOrPlaceholder(operand, location, rest, 1) else throw IRParseException("too many fp register operands")
operands.clear() } else if (oper[0].isDigit() || oper[0] == '$' || oper[0] == '%' || oper[0] == '-' || oper.startsWith("0x")) {
} val value = parseIRValue(oper)
if(operands.isNotEmpty()) { if (format.immediate) {
operand = operands.removeFirst().trim() if (immediateInt == null && immediateFp == null) {
if(operand[0]=='r') if (type == IRDataType.FLOAT)
reg3 = operand.substring(1).toInt() immediateFp = value
else if(operand[0]=='f' && operand[1]=='r') else
fpReg3 = operand.substring(2).toInt() immediateInt = value.toInt()
else { } else {
value = parseValueOrPlaceholder(operand, location, rest, 2) address = value.toInt()
operands.clear() }
} } else {
if(operands.isNotEmpty()) { address = value.toInt()
throw IRParseException("unexpected even more operands? $operands rest=$rest'")
} }
} 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) if(type!=null && type !in formats)
throw IRParseException("invalid type code for $line") throw IRParseException("invalid type code for $line")
if(format.reg1!=OperandDirection.UNUSED && reg1==null) if(format.reg1!=OperandDirection.UNUSED && reg1==null)
throw IRParseException("needs reg1 for $line") throw IRParseException("needs reg1 for $line")
if(format.reg2!=OperandDirection.UNUSED && reg2==null) if(format.reg2!=OperandDirection.UNUSED && reg2==null)
throw IRParseException("needs reg2 for $line") throw IRParseException("needs reg2 for $line")
if(format.valueIn && value==null && labelSymbol==null) if(format.fpReg1!=OperandDirection.UNUSED && fpReg1==null)
throw IRParseException("needs value or symbol for $line") 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) if(format.reg1==OperandDirection.UNUSED && reg1!=null)
throw IRParseException("invalid reg1 for $line") throw IRParseException("invalid reg1 for $line")
if(format.reg2==OperandDirection.UNUSED && reg2!=null) if(format.reg2==OperandDirection.UNUSED && reg2!=null)
throw IRParseException("invalid reg2 for $line") 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) { when (type) {
IRDataType.BYTE -> { IRDataType.BYTE -> {
if (value < -128 || value > 255) if (immediateInt!=null && (immediateInt!! < -128 || immediateInt!! > 255))
throw IRParseException("value out of range for byte: $value") throw IRParseException("immediate value out of range for byte: $immediateInt")
} }
IRDataType.WORD -> { IRDataType.WORD -> {
if (value < -32768 || value > 65535) if (immediateInt!=null && (immediateInt!! < -32768 || immediateInt!! > 65535))
throw IRParseException("value out of range for word: $value") throw IRParseException("immediate value out of range for word: $immediateInt")
} }
IRDataType.FLOAT -> {} IRDataType.FLOAT -> {}
null -> {} null -> {}
} }
} }
var floatValue: Float? = null
var intValue: Int? = null
if(format.valueIn && value!=null) if(format.address!=OperandDirection.UNUSED && address==null && labelSymbol==null)
intValue = value.toInt() throw IRParseException("requires address or symbol for $line")
if(format.fpValueIn && value!=null)
floatValue = value
if(opcode in OpcodesForCpuRegisters) { if(labelSymbol!=null) {
val reg = rest.split(',').last().lowercase().trim() if (labelSymbol!![0] == 'r' && labelSymbol!![1].isDigit())
if(reg !in setOf( throw IRParseException("labelsymbol confused with register?: $labelSymbol")
"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))
} }
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> </CODE>
</INITGLOBALS> </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]"> <SUB NAME="main.start" RETURNTYPE="" POS="[examples/test.p8: line 4 col 6-8]">
<PARAMS> <PARAMS>
</PARAMS> </PARAMS>
@ -85,14 +85,13 @@ return
</SUB> </SUB>
</BLOCK> </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]"> <SUB NAME="sys.wait" RETURNTYPE="" POS="[library:/prog8lib/virtual/syslib.p8: line 15 col 6-8]">
<PARAMS> <PARAMS>
uword sys.wait.jiffies uword sys.wait.jiffies
</PARAMS> </PARAMS>
<INLINEASM LABEL="sys.wait" IR="true" POS="[library:/prog8lib/virtual/syslib.p8: line 17 col 10-13]"> <INLINEASM LABEL="sys.wait" IR="true" POS="[library:/prog8lib/virtual/syslib.p8: line 17 col 10-13]">
loadm.w r0,sys.wait.jiffies loadm.w r0,sys.wait.jiffies
syscall 13
</INLINEASM> </INLINEASM>
<CODE> <CODE>
return return

View File

@ -15,35 +15,41 @@ class TestInstructions: FunSpec({
ins.fpReg1direction shouldBe OperandDirection.UNUSED ins.fpReg1direction shouldBe OperandDirection.UNUSED
ins.reg1 shouldBe null ins.reg1 shouldBe null
ins.reg2 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.labelSymbol shouldBe null
ins.toString() shouldBe "nop" ins.toString() shouldBe "nop"
} }
test("with value") { test("with value") {
val ins = IRInstruction(Opcode.BZ, IRDataType.BYTE, reg1=42, value = 99) val ins = IRInstruction(Opcode.BEQ, IRDataType.BYTE, reg1=42, immediate = 0, address = 99)
ins.opcode shouldBe Opcode.BZ ins.opcode shouldBe Opcode.BEQ
ins.type shouldBe IRDataType.BYTE ins.type shouldBe IRDataType.BYTE
ins.reg1direction shouldBe OperandDirection.READ ins.reg1direction shouldBe OperandDirection.READ
ins.fpReg1direction shouldBe OperandDirection.UNUSED ins.fpReg1direction shouldBe OperandDirection.UNUSED
ins.reg1 shouldBe 42 ins.reg1 shouldBe 42
ins.reg2 shouldBe null 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.labelSymbol shouldBe null
ins.toString() shouldBe "bz.b r42,$63" ins.toString() shouldBe "beq.b r42,0,$63"
} }
test("with label") { test("with label") {
val ins = IRInstruction(Opcode.BZ, IRDataType.WORD, reg1=11, labelSymbol = "a.b.c") val ins = IRInstruction(Opcode.BEQ, IRDataType.WORD, reg1=11, immediate = 0, labelSymbol = "a.b.c")
ins.opcode shouldBe Opcode.BZ ins.opcode shouldBe Opcode.BEQ
ins.type shouldBe IRDataType.WORD ins.type shouldBe IRDataType.WORD
ins.reg1direction shouldBe OperandDirection.READ ins.reg1direction shouldBe OperandDirection.READ
ins.fpReg1direction shouldBe OperandDirection.UNUSED ins.fpReg1direction shouldBe OperandDirection.UNUSED
ins.reg1 shouldBe 11 ins.reg1 shouldBe 11
ins.reg2 shouldBe null 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.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") { test("with output registers") {
@ -56,7 +62,9 @@ class TestInstructions: FunSpec({
ins.fpReg2direction shouldBe OperandDirection.UNUSED ins.fpReg2direction shouldBe OperandDirection.UNUSED
ins.reg1 shouldBe 11 ins.reg1 shouldBe 11
ins.reg2 shouldBe 22 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.labelSymbol shouldBe null
ins.toString() shouldBe "addr.w r11,r22" ins.toString() shouldBe "addr.w r11,r22"
@ -69,7 +77,9 @@ class TestInstructions: FunSpec({
ins2.fpReg2direction shouldBe OperandDirection.UNUSED ins2.fpReg2direction shouldBe OperandDirection.UNUSED
ins2.reg1 shouldBe 11 ins2.reg1 shouldBe 11
ins2.reg2 shouldBe 22 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.labelSymbol shouldBe null
ins2.toString() shouldBe "sqrt.b r11,r22" ins2.toString() shouldBe "sqrt.b r11,r22"
} }
@ -86,7 +96,9 @@ class TestInstructions: FunSpec({
ins.fpReg2 shouldBe 2 ins.fpReg2 shouldBe 2
ins.reg1 shouldBe null ins.reg1 shouldBe null
ins.reg2 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.labelSymbol shouldBe null
ins.toString() shouldBe "fsin.f fr1,fr2" ins.toString() shouldBe "fsin.f fr1,fr2"
} }
@ -94,19 +106,19 @@ class TestInstructions: FunSpec({
test("missing type should fail") { test("missing type should fail") {
shouldThrow<IllegalArgumentException> { shouldThrow<IllegalArgumentException> {
IRInstruction(Opcode.BZ, reg1=42, value=99) IRInstruction(Opcode.BEQ, reg1=42, address=99)
} }
} }
test("missing registers should fail") { test("missing registers should fail") {
shouldThrowWithMessage<IllegalArgumentException>("missing reg1") { 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") { test("missing address should fail") {
shouldThrowWithMessage<IllegalArgumentException>("missing a value or labelsymbol") { shouldThrowWithMessage<IllegalArgumentException>("missing an address or labelsymbol") {
IRInstruction(Opcode.BZ, IRDataType.BYTE, reg1=42) 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_and\>"
syn match prog8BuiltInFunc "\<cx16\.vpoke_xor\>" syn match prog8BuiltInFunc "\<cx16\.vpoke_xor\>"
syn match prog8BuiltInFunc "\<cx16\.vload\>" 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\>"
syn match prog8BuiltInFunc "\<cx16\.init_system_phase2\>" syn match prog8BuiltInFunc "\<cx16\.init_system_phase2\>"
syn match prog8BuiltInFunc "\<cx16\.set_irq\>" 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) { override fun paint(graphics: Graphics) {
val g2d = graphics as Graphics2D 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) g2d.drawImage(drawImage, 0, 0, size.width, size.height, null)
Toolkit.getDefaultToolkit().sync() Toolkit.getDefaultToolkit().sync()
} }

View File

@ -7,7 +7,7 @@ package prog8.vm
*/ */
class Registers { class Registers {
private val registers = Array<UShort>(65536) { 0u } private val registers = Array<UShort>(65536) { 0u }
private val floatRegisters = Array(65535) { 0f } private val floatRegisters = Array(65536) { 0f }
var cpuA: UByte = 0u var cpuA: UByte = 0u
var cpuX: UByte = 0u var cpuX: UByte = 0u
var cpuY: UByte = 0u var cpuY: UByte = 0u

View File

@ -1,7 +1,8 @@
package prog8.vm package prog8.vm
import prog8.code.core.AssemblyError import prog8.code.core.AssemblyError
import prog8.intermediate.SyscallRegisterBase import prog8.intermediate.FunctionCallArgs
import prog8.intermediate.IRDataType
import kotlin.math.min import kotlin.math.min
/* /*
@ -81,25 +82,59 @@ enum class Syscall {
RNDF, RNDF,
STRING_CONTAINS, STRING_CONTAINS,
BYTEARRAY_CONTAINS, BYTEARRAY_CONTAINS,
WORDARRAY_CONTAINS WORDARRAY_CONTAINS;
companion object {
private val VALUES = values()
fun fromInt(value: Int) = VALUES[value]
}
} }
object SysCalls { 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) { when(call) {
Syscall.RESET -> { Syscall.RESET -> {
vm.reset(false) vm.reset(false)
} }
Syscall.EXIT ->{ 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 -> { Syscall.PRINT_C -> {
val char = vm.registers.getUB(SyscallRegisterBase).toInt() val char = getArgValues(callspec.arguments, vm).single() as UByte
print(Char(char)) print(Char(char.toInt()))
} }
Syscall.PRINT_S -> { Syscall.PRINT_S -> {
var addr = vm.registers.getUW(SyscallRegisterBase).toInt() var addr = (getArgValues(callspec.arguments, vm).single() as UShort).toInt()
while(true) { while(true) {
val char = vm.memory.getUB(addr).toInt() val char = vm.memory.getUB(addr).toInt()
if(char==0) if(char==0)
@ -109,35 +144,52 @@ object SysCalls {
} }
} }
Syscall.PRINT_U8 -> { Syscall.PRINT_U8 -> {
print(vm.registers.getUB(SyscallRegisterBase)) val value = getArgValues(callspec.arguments, vm).single()
print(value)
} }
Syscall.PRINT_U16 -> { Syscall.PRINT_U16 -> {
print(vm.registers.getUW(SyscallRegisterBase)) val value = getArgValues(callspec.arguments, vm).single()
print(value)
} }
Syscall.INPUT -> { Syscall.INPUT -> {
val (address, maxlen) = getArgValues(callspec.arguments, vm)
var input = readln() var input = readln()
val maxlen = vm.registers.getUB(SyscallRegisterBase+1).toInt() val maxlenvalue = (maxlen as UByte).toInt()
if(maxlen>0) if(maxlenvalue>0)
input = input.substring(0, min(input.length, maxlen)) input = input.substring(0, min(input.length, maxlenvalue))
vm.memory.setString(vm.registers.getUW(SyscallRegisterBase).toInt(), input, true) vm.memory.setString((address as UShort).toInt(), input, true)
vm.registers.setUW(0, input.length.toUShort()) returnValue(callspec.returns!!, input.length, vm)
} }
Syscall.SLEEP -> { Syscall.SLEEP -> {
val duration = vm.registers.getUW(SyscallRegisterBase).toLong() val duration = getArgValues(callspec.arguments, vm).single() as UShort
Thread.sleep(duration) 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 -> { Syscall.WAIT -> {
val millis = vm.registers.getUW(SyscallRegisterBase).toLong() * 1000/60 val time = getArgValues(callspec.arguments, vm).single() as UShort
Thread.sleep(millis) Thread.sleep(time.toLong() * 1000/60)
} }
Syscall.WAITVSYNC -> vm.waitvsync() Syscall.WAITVSYNC -> vm.waitvsync()
Syscall.SORT_UBYTE -> { Syscall.SORT_UBYTE -> {
val address = vm.registers.getUW(SyscallRegisterBase).toInt() val (addressV, lengthV) = getArgValues(callspec.arguments, vm)
val length = vm.registers.getUB(SyscallRegisterBase+1).toInt() val address = (addressV as UShort).toInt()
val length = (lengthV as UByte).toInt()
val array = IntProgression.fromClosedRange(address, address+length-1, 1).map { val array = IntProgression.fromClosedRange(address, address+length-1, 1).map {
vm.memory.getUB(it) vm.memory.getUB(it)
}.sorted() }.sorted()
@ -146,8 +198,9 @@ object SysCalls {
} }
} }
Syscall.SORT_BYTE -> { Syscall.SORT_BYTE -> {
val address = vm.registers.getUW(SyscallRegisterBase).toInt() val (addressV, lengthV) = getArgValues(callspec.arguments, vm)
val length = vm.registers.getUB(SyscallRegisterBase+1).toInt() val address = (addressV as UShort).toInt()
val length = (lengthV as UByte).toInt()
val array = IntProgression.fromClosedRange(address, address+length-1, 1).map { val array = IntProgression.fromClosedRange(address, address+length-1, 1).map {
vm.memory.getSB(it) vm.memory.getSB(it)
}.sorted() }.sorted()
@ -156,8 +209,9 @@ object SysCalls {
} }
} }
Syscall.SORT_UWORD -> { Syscall.SORT_UWORD -> {
val address = vm.registers.getUW(SyscallRegisterBase).toInt() val (addressV, lengthV) = getArgValues(callspec.arguments, vm)
val length = vm.registers.getUB(SyscallRegisterBase+1).toInt() val address = (addressV as UShort).toInt()
val length = (lengthV as UByte).toInt()
val array = IntProgression.fromClosedRange(address, address+length*2-2, 2).map { val array = IntProgression.fromClosedRange(address, address+length*2-2, 2).map {
vm.memory.getUW(it) vm.memory.getUW(it)
}.sorted() }.sorted()
@ -166,8 +220,9 @@ object SysCalls {
} }
} }
Syscall.SORT_WORD -> { Syscall.SORT_WORD -> {
val address = vm.registers.getUW(SyscallRegisterBase).toInt() val (addressV, lengthV) = getArgValues(callspec.arguments, vm)
val length = vm.registers.getUB(SyscallRegisterBase+1).toInt() val address = (addressV as UShort).toInt()
val length = (lengthV as UByte).toInt()
val array = IntProgression.fromClosedRange(address, address+length*2-2, 2).map { val array = IntProgression.fromClosedRange(address, address+length*2-2, 2).map {
vm.memory.getSW(it) vm.memory.getSW(it)
}.sorted() }.sorted()
@ -176,8 +231,9 @@ object SysCalls {
} }
} }
Syscall.REVERSE_BYTES -> { Syscall.REVERSE_BYTES -> {
val address = vm.registers.getUW(SyscallRegisterBase).toInt() val (addressV, lengthV) = getArgValues(callspec.arguments, vm)
val length = vm.registers.getUB(SyscallRegisterBase+1).toInt() val address = (addressV as UShort).toInt()
val length = (lengthV as UByte).toInt()
val array = IntProgression.fromClosedRange(address, address+length-1, 1).map { val array = IntProgression.fromClosedRange(address, address+length-1, 1).map {
vm.memory.getUB(it) vm.memory.getUB(it)
}.reversed() }.reversed()
@ -186,8 +242,9 @@ object SysCalls {
} }
} }
Syscall.REVERSE_WORDS -> { Syscall.REVERSE_WORDS -> {
val address = vm.registers.getUW(SyscallRegisterBase).toInt() val (addressV, lengthV) = getArgValues(callspec.arguments, vm)
val length = vm.registers.getUB(SyscallRegisterBase+1).toInt() val address = (addressV as UShort).toInt()
val length = (lengthV as UByte).toInt()
val array = IntProgression.fromClosedRange(address, address+length*2-2, 2).map { val array = IntProgression.fromClosedRange(address, address+length*2-2, 2).map {
vm.memory.getUW(it) vm.memory.getUW(it)
}.reversed() }.reversed()
@ -196,8 +253,9 @@ object SysCalls {
} }
} }
Syscall.REVERSE_FLOATS -> { Syscall.REVERSE_FLOATS -> {
val address = vm.registers.getUW(SyscallRegisterBase).toInt() val (addressV, lengthV) = getArgValues(callspec.arguments, vm)
val length = vm.registers.getUB(SyscallRegisterBase+1).toInt() val address = (addressV as UShort).toInt()
val length = (lengthV as UByte).toInt()
val array = IntProgression.fromClosedRange(address, address+length*4-2, 4).map { val array = IntProgression.fromClosedRange(address, address+length*4-2, 4).map {
vm.memory.getFloat(it) vm.memory.getFloat(it)
}.reversed() }.reversed()
@ -206,151 +264,154 @@ object SysCalls {
} }
} }
Syscall.ANY_BYTE -> { Syscall.ANY_BYTE -> {
val address = vm.registers.getUW(SyscallRegisterBase).toInt() val (addressV, lengthV) = getArgValues(callspec.arguments, vm)
val length = vm.registers.getUB(SyscallRegisterBase+1).toInt() val address = (addressV as UShort).toInt()
val length = (lengthV as UByte).toInt()
val addresses = IntProgression.fromClosedRange(address, address+length*2-2, 2) val addresses = IntProgression.fromClosedRange(address, address+length*2-2, 2)
if(addresses.any { vm.memory.getUB(it).toInt()!=0 }) if(addresses.any { vm.memory.getUB(it).toInt()!=0 })
vm.registers.setUB(0, 1u) returnValue(callspec.returns!!, 1, vm)
else else
vm.registers.setUB(0, 0u) returnValue(callspec.returns!!, 0, vm)
} }
Syscall.ANY_WORD -> { Syscall.ANY_WORD -> {
val address = vm.registers.getUW(SyscallRegisterBase).toInt() val (addressV, lengthV) = getArgValues(callspec.arguments, vm)
val length = vm.registers.getUB(SyscallRegisterBase+1).toInt() val address = (addressV as UShort).toInt()
val length = (lengthV as UByte).toInt()
val addresses = IntProgression.fromClosedRange(address, address+length*2-2, 2) val addresses = IntProgression.fromClosedRange(address, address+length*2-2, 2)
if(addresses.any { vm.memory.getUW(it).toInt()!=0 }) if(addresses.any { vm.memory.getUW(it).toInt()!=0 })
vm.registers.setUB(0, 1u) returnValue(callspec.returns!!, 1, vm)
else else
vm.registers.setUB(0, 0u) returnValue(callspec.returns!!, 0, vm)
} }
Syscall.ANY_FLOAT -> { Syscall.ANY_FLOAT -> {
val address = vm.registers.getUW(SyscallRegisterBase).toInt() val (addressV, lengthV) = getArgValues(callspec.arguments, vm)
val length = vm.registers.getUB(SyscallRegisterBase+1).toInt() val address = (addressV as UShort).toInt()
val length = (lengthV as UByte).toInt()
val addresses = IntProgression.fromClosedRange(address, address+length*4-2, 4) val addresses = IntProgression.fromClosedRange(address, address+length*4-2, 4)
if(addresses.any { vm.memory.getFloat(it).toInt()!=0 }) if(addresses.any { vm.memory.getFloat(it).toInt()!=0 })
vm.registers.setUB(0, 1u) returnValue(callspec.returns!!, 1, vm)
else else
vm.registers.setUB(0, 0u) returnValue(callspec.returns!!, 0, vm)
} }
Syscall.ALL_BYTE -> { Syscall.ALL_BYTE -> {
val address = vm.registers.getUW(SyscallRegisterBase).toInt() val (addressV, lengthV) = getArgValues(callspec.arguments, vm)
val length = vm.registers.getUB(SyscallRegisterBase+1).toInt() val address = (addressV as UShort).toInt()
val length = (lengthV as UByte).toInt()
val addresses = IntProgression.fromClosedRange(address, address+length*2-2, 2) val addresses = IntProgression.fromClosedRange(address, address+length*2-2, 2)
if(addresses.all { vm.memory.getUB(it).toInt()!=0 }) if(addresses.all { vm.memory.getUB(it).toInt()!=0 })
vm.registers.setUB(0, 1u) returnValue(callspec.returns!!, 1, vm)
else else
vm.registers.setUB(0, 0u) returnValue(callspec.returns!!, 0, vm)
} }
Syscall.ALL_WORD -> { Syscall.ALL_WORD -> {
val address = vm.registers.getUW(SyscallRegisterBase).toInt() val (addressV, lengthV) = getArgValues(callspec.arguments, vm)
val length = vm.registers.getUB(SyscallRegisterBase+1).toInt() val address = (addressV as UShort).toInt()
val length = (lengthV as UByte).toInt()
val addresses = IntProgression.fromClosedRange(address, address+length*2-2, 2) val addresses = IntProgression.fromClosedRange(address, address+length*2-2, 2)
if(addresses.all { vm.memory.getUW(it).toInt()!=0 }) if(addresses.all { vm.memory.getUW(it).toInt()!=0 })
vm.registers.setUB(0, 1u) returnValue(callspec.returns!!, 1, vm)
else else
vm.registers.setUB(0, 0u) returnValue(callspec.returns!!, 0, vm)
} }
Syscall.ALL_FLOAT -> { Syscall.ALL_FLOAT -> {
val address = vm.registers.getUW(SyscallRegisterBase).toInt() val (addressV, lengthV) = getArgValues(callspec.arguments, vm)
val length = vm.registers.getUB(SyscallRegisterBase+1).toInt() val address = (addressV as UShort).toInt()
val length = (lengthV as UByte).toInt()
val addresses = IntProgression.fromClosedRange(address, address+length*4-2, 4) val addresses = IntProgression.fromClosedRange(address, address+length*4-2, 4)
if(addresses.all { vm.memory.getFloat(it).toInt()!=0 }) if(addresses.all { vm.memory.getFloat(it).toInt()!=0 })
vm.registers.setUB(0, 1u) returnValue(callspec.returns!!, 1, vm)
else else
vm.registers.setUB(0, 0u) returnValue(callspec.returns!!, 0, vm)
} }
Syscall.PRINT_F -> { Syscall.PRINT_F -> {
val value = vm.registers.getFloat(SyscallRegisterBase) val value = getArgValues(callspec.arguments, vm).single() as Float
print(value) print(value)
} }
Syscall.STR_TO_UWORD -> { 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 string = vm.memory.getString(stringAddr.toInt()).takeWhile { it.isDigit() }
val value = try { val value = try {
string.toUShort() string.toUShort()
} catch(_: NumberFormatException) { } catch(_: NumberFormatException) {
0u 0u
} }
vm.registers.setUW(0, value) returnValue(callspec.returns!!, value, vm)
} }
Syscall.STR_TO_WORD -> { Syscall.STR_TO_WORD -> {
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 memstring = vm.memory.getString(stringAddr.toInt())
val match = Regex("^[+-]?\\d+").find(memstring) ?: return returnValue(callspec.returns!!, 0, vm)
val value = try { val value = try {
string.toShort() match.value.toShort()
} catch(_: NumberFormatException) { } catch(_: NumberFormatException) {
0 0
} }
vm.registers.setSW(0, value) return returnValue(callspec.returns!!, value, vm)
} }
Syscall.COMPARE_STRINGS -> { Syscall.COMPARE_STRINGS -> {
val firstAddr = vm.registers.getUW(SyscallRegisterBase) val (firstV, secondV) = getArgValues(callspec.arguments, vm)
val secondAddr = vm.registers.getUW(SyscallRegisterBase+1) val firstAddr = firstV as UShort
val secondAddr = secondV as UShort
val first = vm.memory.getString(firstAddr.toInt()) val first = vm.memory.getString(firstAddr.toInt())
val second = vm.memory.getString(secondAddr.toInt()) val second = vm.memory.getString(secondAddr.toInt())
val comparison = first.compareTo(second) val comparison = first.compareTo(second)
if(comparison==0) if(comparison==0)
vm.registers.setSB(0, 0) returnValue(callspec.returns!!, 0, vm)
else if(comparison<0) else if(comparison<0)
vm.registers.setSB(0, -1) returnValue(callspec.returns!!, -1, vm)
else else
vm.registers.setSB(0, 1) returnValue(callspec.returns!!, 1, vm)
} }
Syscall.RNDFSEED -> { 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 if(seed>0) // always use negative seed, this mimics the behavior on CBM machines
vm.randomSeedFloat(-seed) vm.randomSeedFloat(-seed)
else else
vm.randomSeedFloat(seed) vm.randomSeedFloat(seed)
} }
Syscall.RNDSEED -> { Syscall.RNDSEED -> {
val seed1 = vm.registers.getUW(SyscallRegisterBase) val (seed1, seed2) = getArgValues(callspec.arguments, vm)
val seed2 = vm.registers.getUW(SyscallRegisterBase+1) vm.randomSeed(seed1 as UShort, seed2 as UShort)
vm.randomSeed(seed1, seed2)
} }
Syscall.RND -> { Syscall.RND -> {
vm.registers.setUB(0, vm.randomGenerator.nextInt().toUByte()) returnValue(callspec.returns!!, vm.randomGenerator.nextInt().toUByte(), vm)
} }
Syscall.RNDW -> { Syscall.RNDW -> {
vm.registers.setUW(0, vm.randomGenerator.nextInt().toUShort()) returnValue(callspec.returns!!, vm.randomGenerator.nextInt().toUShort(), vm)
} }
Syscall.RNDF -> { Syscall.RNDF -> {
vm.registers.setFloat(0, vm.randomGeneratorFloats.nextFloat()) returnValue(callspec.returns!!, vm.randomGeneratorFloats.nextFloat(), vm)
} }
Syscall.STRING_CONTAINS -> { Syscall.STRING_CONTAINS -> {
val char = vm.registers.getUB(SyscallRegisterBase).toInt().toChar() val (charV, addr) = getArgValues(callspec.arguments, vm)
val stringAddr = vm.registers.getUW(SyscallRegisterBase+1) val stringAddr = addr as UShort
val char = (charV as UByte).toInt().toChar()
val string = vm.memory.getString(stringAddr.toInt()) 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 -> { Syscall.BYTEARRAY_CONTAINS -> {
val value = vm.registers.getUB(SyscallRegisterBase) val (value, arrayV, lengthV) = getArgValues(callspec.arguments, vm)
var array = vm.registers.getUW(SyscallRegisterBase+1).toInt() var length = lengthV as UByte
var length = vm.registers.getUB(SyscallRegisterBase+2) var array = (arrayV as UShort).toInt()
while(length>0u) { while(length>0u) {
if(vm.memory.getUB(array)==value) { if(vm.memory.getUB(array)==value)
vm.registers.setUB(0, 1u) return returnValue(callspec.returns!!, 1u, vm)
return
}
array++ array++
length-- length--
} }
vm.registers.setUB(0, 0u) returnValue(callspec.returns!!, 0u, vm)
} }
Syscall.WORDARRAY_CONTAINS -> { Syscall.WORDARRAY_CONTAINS -> {
// r0.w = value, r1.w = array, r2.b = array length val (value, arrayV, lengthV) = getArgValues(callspec.arguments, vm)
val value = vm.registers.getUW(SyscallRegisterBase) var length = lengthV as UByte
var array = vm.registers.getUW(SyscallRegisterBase+1).toInt() var array = (arrayV as UShort).toInt()
var length = vm.registers.getUB(SyscallRegisterBase+2)
while(length>0u) { while(length>0u) {
if(vm.memory.getUW(array)==value) { if(vm.memory.getUW(array)==value)
vm.registers.setUB(0, 1u) return returnValue(callspec.returns!!, 1u, vm)
return
}
array += 2 array += 2
length-- length--
} }
vm.registers.setUB(0, 0u) returnValue(callspec.returns!!, 0u, vm)
} }
else -> throw AssemblyError("missing syscall ${call.name}") 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 { class VmProgramLoader {
private val placeholders = mutableMapOf<Pair<IRCodeChunk, Int>, String>() // program chunk+index to symbolname 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> { fun load(irProgram: IRProgram, memory: Memory): List<IRCodeChunk> {
placeholders.clear()
irProgram.validate() irProgram.validate()
placeholders.clear()
subroutines.clear()
val allocations = VmVariableAllocator(irProgram.st, irProgram.encoding, irProgram.options.compTarget) val allocations = VmVariableAllocator(irProgram.st, irProgram.encoding, irProgram.options.compTarget)
val variableAddresses = allocations.allocations.toMutableMap() val variableAddresses = allocations.allocations.toMutableMap()
val programChunks = mutableListOf<IRCodeChunk>() val programChunks = mutableListOf<IRCodeChunk>()
@ -36,41 +38,37 @@ class VmProgramLoader {
val chunkReplacements = mutableListOf<Pair<IRCodeChunkBase, IRCodeChunk>>() val chunkReplacements = mutableListOf<Pair<IRCodeChunkBase, IRCodeChunk>>()
irProgram.blocks.forEach { block -> irProgram.blocks.forEach { block ->
if(block.address!=null) 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 -> block.children.forEach { child ->
when(child) { when(child) {
is IRAsmSubroutine -> throw IRParseException("vm does not support asmsubs (use normal sub): ${child.label}") is IRAsmSubroutine -> throw IRParseException("vm does not support asmsubs (use normal sub): ${child.label}")
is IRCodeChunk -> programChunks += child is IRCodeChunk -> programChunks += child
is IRInlineAsmChunk -> { is IRInlineAsmChunk -> throw IRParseException("encountered unconverted inline assembly chunk")
val replacement = addAssemblyToProgram(child, programChunks, variableAddresses) is IRInlineBinaryChunk -> throw IRParseException("inline binary data not yet supported in the VM")
chunkReplacements += replacement
}
is IRInlineBinaryChunk -> throw IRParseException("inline binary data not yet supported in the VM") // TODO
is IRSubroutine -> { is IRSubroutine -> {
subroutines[child.label] = child
child.chunks.forEach { chunk -> child.chunks.forEach { chunk ->
when (chunk) { when (chunk) {
is IRInlineAsmChunk -> { is IRInlineAsmChunk -> throw IRParseException("encountered unconverted inline assembly chunk")
val replacement = addAssemblyToProgram(chunk, programChunks, variableAddresses) is IRInlineBinaryChunk -> throw IRParseException("inline binary data not yet supported in the VM")
chunkReplacements += replacement
}
is IRInlineBinaryChunk -> throw IRParseException("inline binary data not yet supported in the VM") // TODO
is IRCodeChunk -> programChunks += chunk is IRCodeChunk -> programChunks += chunk
else -> throw AssemblyError("weird chunk type") else -> throw AssemblyError("weird chunk type")
} }
} } }
}
} }
} }
} }
pass2translateSyscalls(programChunks) pass2translateSyscalls(programChunks)
pass2replaceLabelsByProgIndex(programChunks, variableAddresses) pass2replaceLabelsByProgIndex(programChunks, variableAddresses, subroutines)
phase2relinkReplacedChunks(chunkReplacements, programChunks) phase2relinkReplacedChunks(chunkReplacements, programChunks)
programChunks.forEach { programChunks.forEach {
it.instructions.forEach { ins -> it.instructions.forEach { ins ->
if (ins.labelSymbol != null && ins.opcode !in OpcodesThatBranch) 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) -> chunk.instructions.withIndex().forEach { (index, ins) ->
if(ins.opcode == Opcode.SYSCALL) { if(ins.opcode == Opcode.SYSCALL) {
// convert IR Syscall to VM 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_UBYTE.number -> Syscall.SORT_UBYTE
IMSyscall.SORT_BYTE.number -> Syscall.SORT_BYTE IMSyscall.SORT_BYTE.number -> Syscall.SORT_BYTE
IMSyscall.SORT_UWORD.number -> Syscall.SORT_UWORD IMSyscall.SORT_UWORD.number -> Syscall.SORT_UWORD
@ -124,7 +122,7 @@ class VmProgramLoader {
} }
if(vmSyscall!=null) if(vmSyscall!=null)
chunk.instructions[index] = ins.copy(value = vmSyscall.ordinal) chunk.instructions[index] = ins.copy(immediate = vmSyscall.ordinal)
} }
val label = ins.labelSymbol val label = ins.labelSymbol
@ -137,7 +135,8 @@ class VmProgramLoader {
private fun pass2replaceLabelsByProgIndex( private fun pass2replaceLabelsByProgIndex(
chunks: MutableList<IRCodeChunk>, chunks: MutableList<IRCodeChunk>,
variableAddresses: MutableMap<String, Int> variableAddresses: MutableMap<String, Int>,
subroutines: MutableMap<String, IRSubroutine>
) { ) {
for((ref, label) in placeholders) { for((ref, label) in placeholders) {
val (chunk, line) = ref val (chunk, line) = ref
@ -148,25 +147,49 @@ class VmProgramLoader {
val (symbol, indexStr) = label.split('+') val (symbol, indexStr) = label.split('+')
val index = indexStr.toInt() val index = indexStr.toInt()
val address = variableAddresses.getValue(symbol) + index 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 { } else {
// placeholder is not a variable, so it must be a label of a code chunk instead // 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 target: IRCodeChunk? = chunks.firstOrNull { it.label==label }
val opcode = chunk.instructions[line].opcode val opcode = chunk.instructions[line].opcode
if(target==null) if(target==null)
throw IRParseException("placeholder not found in variables nor labels: $label") throw IRParseException("placeholder not found in variables nor labels: $label")
else if(opcode in OpcodesThatBranch) { else if(opcode in OpcodesThatBranch)
chunk.instructions[line] = chunk.instructions[line].copy(branchTarget = target, value = null) chunk.instructions[line] = chunk.instructions[line].copy(branchTarget = target, address = null)
} else if(opcode in OpcodesWithMemoryAddressAsValue) { else
throw IRParseException("vm cannot yet load a label address as a value: ${chunk.instructions[line]}") // TODO 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 { } 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( private fun varsToMemory(
@ -300,26 +323,4 @@ class VmProgramLoader {
require(variable.onetimeInitializationStringValue==null) { "in vm/ir, strings should have been converted into bytearrays." } 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") { test("vm execution: modify memory") {
val program = IRProgram("test", IRSymbolTable(null), getTestOptions(), VMTarget()) 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 startSub = IRSubroutine("testmain.testsub", emptyList(), null, Position.DUMMY)
val code = IRCodeChunk(startSub.label, null) val code = IRCodeChunk(startSub.label, null)
code += IRInstruction(Opcode.NOP) code += IRInstruction(Opcode.NOP)
code += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=1, value=12345) code += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=1, immediate=12345)
code += IRInstruction(Opcode.STOREM, IRDataType.WORD, reg1=1, value=1000) code += IRInstruction(Opcode.STOREM, IRDataType.WORD, reg1=1, address=1000)
code += IRInstruction(Opcode.RETURN) code += IRInstruction(Opcode.RETURN)
startSub += code startSub += code
block += startSub block += startSub
@ -70,25 +70,9 @@ class TestVm: FunSpec( {
vm.stepCount shouldBe code.instructions.size 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") { test("asmsub not supported in vm even with IR") {
val program = IRProgram("test", IRSymbolTable(null), getTestOptions(), VMTarget()) 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( val startSub = IRAsmSubroutine(
"main.asmstart", "main.asmstart",
0x2000u, 0x2000u,
@ -129,7 +113,7 @@ class TestVm: FunSpec( {
<INITGLOBALS> <INITGLOBALS>
</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> </BLOCK>
</PROGRAM> </PROGRAM>
""" """