Compare commits

...

106 Commits
v8.5 ... v8.7

Author SHA1 Message Date
e426fc0922 version 8.7 2022-11-06 22:58:39 +01:00
5d4bfffc7e float.rndseedf() now takes float seed value and is consistent for all CBM compilation targets 2022-11-06 22:53:57 +01:00
207cdaf7a4 fix kefrenbars example (use gfx2 instead of kernal routines) 2022-11-06 17:33:30 +01:00
7315b581ce added gfx2.pget(x,y) to get the pixel color value 2022-11-06 13:40:55 +01:00
38efaae7b2 ir/vm: syscall params in high base register to avoid push/pop 2022-11-06 12:52:09 +01:00
469e042216 vm: replaced prog8_lib.string_compare and others with syscalls 2022-11-04 23:12:13 +01:00
0f1a4b9d8f fixed certain type check error when passing boolean value to ubyte function parameter
fixed virtual machine string comparison syscall
2022-11-03 23:06:03 +01:00
7303c00296 vm: prog8lib.wordarray_contains() fixed 2022-11-03 22:48:47 +01:00
fc55b34d84 ir: fix asmsub multi-value return codegen 2022-11-03 22:29:41 +01:00
6f67fc0e02 ir: get rid of '_' symbol prefix 2022-11-03 21:54:53 +01:00
562d722ad5 codegen: added missing codegen for float array inplace modification 2022-11-03 20:08:46 +01:00
144c1ba3a6 ir: fix float instruction value in formatspec 2022-11-03 19:08:38 +01:00
06b032af91 refactor 2022-11-03 00:20:31 +01:00
3603140114 ir: fix unused code remover 2022-11-02 23:54:52 +01:00
e094785cbd ir: fix unused code remover 2022-11-02 23:16:51 +01:00
e7408224ac ir: remove position tracking from codechunk for now 2022-11-02 22:12:42 +01:00
e67c05c274 ir: fix asmsub contents not appearing in IR file 2022-11-02 20:50:51 +01:00
b22804efaf ir: fix inlineasm linking 2022-10-31 23:59:33 +01:00
890f55f91a fixup compiler internals diagram 2022-10-31 00:39:43 +01:00
cc5fc0b892 Merge branch 'master' into labeledchunks
# Conflicts:
#	examples/test.p8
2022-10-30 23:46:44 +01:00
5efe2b027a ir: fix chunk linkage in optimizer 2022-10-30 23:42:41 +01:00
5b6569d0f9 ir: fix overwriting chunk label 2022-10-30 19:03:02 +01:00
0eda7ac498 vm: don't crash on empty code chunks 2022-10-30 17:05:08 +01:00
a5ef353484 ir: fix memory mapped var as for loop counter 2022-10-30 14:54:47 +01:00
67a36d8d31 more robust 'return' statement checks in subroutines 2022-10-30 14:41:28 +01:00
7cc3cc3990 ir: fix non-code chunk linkage 2022-10-30 12:55:06 +01:00
dc0edc4c2b break also in for 2022-10-29 23:34:59 +02:00
71d2f091e5 Merge pull request #88 from markjreed/fix-mouse_config2
fix: don't ignore shape argument to cx16.mouse_config2
2022-10-29 23:22:14 +02:00
c2f062a391 fix: don't ignore shape argument to cx16.mouse_config2 2022-10-29 17:10:06 -04:00
224f490455 Merge branch 'master' into labeledchunks
# Conflicts:
#	codeGenIntermediate/src/prog8/codegen/intermediate/AssignmentGen.kt
#	codeGenIntermediate/src/prog8/codegen/intermediate/ExpressionGen.kt
#	examples/test.p8
2022-10-29 18:26:09 +02:00
5b35232ab4 fix "fpReg1 out of bounds" crash for vm target for in-place float array assignment. #85 2022-10-29 17:04:39 +02:00
6d6db70e42 remove type widening for bit shifts, to be consistent with other arithmetic operations. Fixes #83 2022-10-29 16:29:41 +02:00
6830e15b4e print warning when bit shifts are too large and result in 0. #83 2022-10-29 15:23:39 +02:00
3f07cad35d remove missing feature from docs 2022-10-29 14:31:40 +02:00
e951340033 BASIC, VICE, C64, zeropage spelling 2022-10-29 14:17:40 +02:00
db8912a735 Kernal spelling 2022-10-29 14:10:11 +02:00
0e297731a3 PETSCII spelling 2022-10-29 14:07:04 +02:00
f20c4f98ac Merge pull request #86 from Frosty-J/docs
Fix typos in documentation
2022-10-29 12:57:55 +02:00
05e60cc7c0 fix array type typo 2022-10-29 12:57:33 +02:00
55b4469767 Merge pull request #87 from Frosty-J/basicsafe
`%zeropage basicsafe` in Hello World
2022-10-29 12:31:28 +02:00
f15516e478 Bracket space 2022-10-29 00:25:54 +01:00
17ceadbadf %zeropage basicsafe in Hello World 2022-10-28 22:49:23 +01:00
8c25b2b316 CommanderX16 -> Commander X16 2022-10-28 22:47:14 +01:00
8b1ae404a3 Commodore-64 -> Commodore 64 2022-10-28 22:45:09 +01:00
13534cd4a9 lowlevel -> low-level 2022-10-28 22:40:36 +01:00
abfb345503 ofcourse -> of course 2022-10-28 22:39:54 +01:00
42ae935496 Various typo fixes 2022-10-28 22:39:15 +01:00
434515d957 fix: array[x] = ~array[x] no longer crashes the codegen 2022-10-27 23:56:38 +02:00
094f7803b7 fix: array[x] = -array[x] no longer crashes the codegen 2022-10-27 23:20:40 +02:00
b0c7bad391 fix: array[x] = -value no longer crashes the codegen 2022-10-27 21:58:37 +02:00
e9a4a905ef preparing to fix the array indexing compiler issue 2022-10-26 23:53:17 +02:00
7b6cd0cfbe cx16.macptr() now has additional argument in the carry flag, to reflect recent X16 kernal api change.
Also now allow bool type for status flag args and returnvalues.
2022-10-26 20:41:10 +02:00
b718b12083 ir/vm fix chunk linkage 2022-10-26 00:12:56 +02:00
cfa7258ff4 various 2022-10-25 23:18:42 +02:00
b70e0a0870 mention syntax highlighting files in the docs 2022-10-25 21:24:38 +02:00
da8eb464b8 add cx16diskio.vload_raw() to load headerless files into vram 2022-10-25 21:12:11 +02:00
8f9d1cfa30 fix regression: indexing pointer variable with word (>255) didn't work anymore since release 8.2 or so 2022-10-24 23:43:47 +02:00
585009ac5c ir: fix syscall numbers and more 2022-10-24 01:57:37 +02:00
30ee65fd14 ir: ensure that block and sub labels are also on the first chunk in said block/sub 2022-10-23 18:54:08 +02:00
76428b16f0 Merge branch 'master' into labeledchunks
# Conflicts:
#	codeGenIntermediate/src/prog8/codegen/intermediate/BuiltinFuncGen.kt
#	docs/source/todo.rst
#	examples/test.p8
#	virtualmachine/src/prog8/vm/VirtualMachine.kt
2022-10-23 12:19:02 +02:00
0d7b14e2d8 fix crash when assigning certain memory read to word variable. Fixes #82 2022-10-23 11:57:23 +02:00
a9d19d02b3 helpful error for programs still using the old builtin rnd() and rndw() 2022-10-22 22:36:44 +02:00
adcbe55307 replaced integer RNG with smaller and faster routine. 2022-10-22 22:01:57 +02:00
aa99a7df64 seed info 2022-10-22 17:54:24 +02:00
00afa1ce52 ir: replace RND opcode by syscalls 2022-10-22 17:20:46 +02:00
e94bf4c63c replace rnd()/rndw() builtin functions by regular routines in math module 2022-10-22 17:02:43 +02:00
ec5adffdc2 rnd()/rndf() routines can now be seeded with new rndseed()/rndseedf() routines. fixes #80 2022-10-22 13:34:22 +02:00
733c17ad3a improve docs on if syntax. fixes #81 2022-10-19 23:53:15 +02:00
53b0b562e6 fix check for routine that returns multiple values but in status bit. Fixes #79 2022-10-19 23:23:49 +02:00
fabae6e970 ir: fix handling of labeled chunks 2022-10-16 23:53:17 +02:00
a9f9c40d8a ir: fix handling of labeled chunks 2022-10-13 00:56:44 +02:00
6fc89607d3 ir: moving to labeled chunks, no more IRLabel nodes 2022-10-07 00:34:56 +02:00
2340760f53 rename 2022-10-04 22:54:14 +02:00
39d6d2857e ir: change inline binary a bit 2022-10-04 00:57:08 +02:00
7b722a0001 ir: fix count register uses 2022-10-04 00:25:55 +02:00
e7682119e0 ir: count register uses 2022-10-02 15:56:06 +02:00
af6be44676 ir: adding register usage inspections
fix compiler problems with untrimmed inlined asm, and when only a single return statement is present in a subroutine
2022-09-30 20:25:00 +02:00
5a8f97a0b6 ir: adding last missing features to be able to encode all of Prog8 2022-09-30 16:01:00 +02:00
0d4dd385b8 added '%ir' to write inline IR code, '%asm' is now only for real 6502 assembly.
(%ir is probably only used in the library modules for the virtual machine target)
2022-09-30 15:12:26 +02:00
94f0f3e966 ir: join code chunks 2022-09-30 02:47:33 +02:00
43e31765e5 kotlin 1.7.20 2022-09-29 18:41:20 +02:00
7c1bdfe713 ir: uninitialized vars remain empty, bss section classifier (unused for now as there are no segements yet) 2022-09-28 16:56:50 +02:00
9f09784b55 version 8.6.2 2022-09-27 22:45:48 +02:00
e7a3a89bfb fix windows issue 2022-09-27 22:41:48 +02:00
7ea7e63f44 use require() more often 2022-09-27 18:27:55 +02:00
1d2ce2cbeb consolidate IR line parse function 2022-09-27 18:02:57 +02:00
06cf2e0bd7 vm: fix memory slabs (bsieve example) 2022-09-27 16:32:44 +02:00
9d219ae4b9 refactor 2022-09-27 03:32:39 +02:00
71f5a6c50e remove p8virt from compiler diagram 2022-09-27 02:52:29 +02:00
90b2be2bf4 vm: new memory initialization of array vars 2022-09-27 02:43:50 +02:00
db1aa8fcbd vm: new translation of IRProgram into vm program list 2022-09-27 01:50:00 +02:00
11c000f764 moved codeGenVirtual module into virtualmachine module 2022-09-26 20:00:40 +02:00
4d6dcbd173 ir: consolidate IRCodeInstruction and Instruction 2022-09-26 19:46:44 +02:00
0da117efd2 vm: get rid of .p8virt file and cruft 2022-09-26 19:28:40 +02:00
533c368e32 make IRFileReader's file source more general 2022-09-26 14:47:28 +02:00
8883513b0e attempt to fix readthedocs.io build 2022-09-25 22:19:32 +02:00
dcc9a71455 version 8.6.1 2022-09-25 21:54:35 +02:00
1a56743bb1 fix IR repeat loop codegen when amount is 0 2022-09-25 20:48:17 +02:00
387a4b7c35 added string.lowerchar() and string.upperchar() 2022-09-25 20:20:38 +02:00
1d65d63bd9 ir: making sure all names are scoped properly. textelite now runs in vm 2022-09-25 18:02:35 +02:00
dda19c29fe vm: fix symbols to be case sensitive properly in p8virt assembler 2022-09-25 15:51:50 +02:00
ca41669f4f vm: fix scoped name in address-of inside array 2022-09-24 18:26:35 +02:00
0e1886e6bd vm: fix nested label prefixing 2022-09-24 16:00:25 +02:00
c26e116f0e vm: fix crashes when array contains pointers/strings 2022-09-24 14:42:07 +02:00
5c9c7f2c5e adding more complex vm examples 2022-09-23 14:56:06 +02:00
ca2fb6cef3 IR no longer depends on VM syscalls but has its own syscall list for the few builtin functions that still require it 2022-09-23 14:27:51 +02:00
145 changed files with 6738 additions and 4211 deletions

View File

@ -78,6 +78,9 @@ It's handy to have an emulator (or a real machine perhaps!) to run the programs
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 the [x16emu emulator](https://github.com/commanderx16/x16-emulator) for the CommanderX16 target.
**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.
Example code Example code
------------ ------------

View File

@ -1,8 +1,7 @@
package prog8 package prog8.code
/** /**
* By convention, the right side of an `Either` is used to hold successful values. * By convention, the right side of an `Either` is used to hold successful values.
*
*/ */
sealed class Either<out L, out R> { sealed class Either<out L, out R> {

View File

@ -169,6 +169,7 @@ open class StNode(val name: String,
class StStaticVariable(name: String, class StStaticVariable(name: String,
val dt: DataType, val dt: DataType,
val bss: Boolean,
val onetimeInitializationNumericValue: Double?, // regular (every-run-time) initialization is done via regular assignments val onetimeInitializationNumericValue: Double?, // regular (every-run-time) initialization is done via regular assignments
val onetimeInitializationStringValue: StString?, val onetimeInitializationStringValue: StString?,
val onetimeInitializationArrayValue: StArray?, val onetimeInitializationArrayValue: StArray?,
@ -177,10 +178,19 @@ class StStaticVariable(name: String,
position: Position) : StNode(name, StNodeType.STATICVAR, position) { position: Position) : StNode(name, StNodeType.STATICVAR, position) {
init { init {
if(bss) {
require(onetimeInitializationNumericValue==null)
require(onetimeInitializationStringValue==null)
require(onetimeInitializationArrayValue.isNullOrEmpty())
} else {
require(onetimeInitializationNumericValue!=null ||
onetimeInitializationStringValue!=null ||
onetimeInitializationArrayValue!=null)
}
if(length!=null) { if(length!=null) {
require(onetimeInitializationNumericValue == null) require(onetimeInitializationNumericValue == null)
if(onetimeInitializationArrayValue!=null) if(onetimeInitializationArrayValue!=null)
require(length == onetimeInitializationArrayValue.size) require(onetimeInitializationArrayValue.isEmpty() ||onetimeInitializationArrayValue.size==length)
} }
if(onetimeInitializationNumericValue!=null) if(onetimeInitializationNumericValue!=null)
require(dt in NumericDatatypes) require(dt in NumericDatatypes)
@ -193,7 +203,7 @@ class StStaticVariable(name: String,
} }
override fun printProperties() { override fun printProperties() {
print("$name dt=$dt zpw=$zpwish") print("$name dt=$dt zpw=$zpwish bss=$bss")
} }
} }
@ -221,12 +231,11 @@ class StMemorySlab(
name: String, name: String,
val size: UInt, val size: UInt,
val align: UInt, val align: UInt,
val allocatedAddress: UInt? = null, // this is used (for now) in the VM code generator. TODO remove this once no longer used
position: Position position: Position
): ):
StNode(name, StNodeType.MEMORYSLAB, position) { StNode(name, StNodeType.MEMORYSLAB, position) {
override fun printProperties() { override fun printProperties() {
print("$name size=$size align=$align address=$allocatedAddress") print("$name size=$size align=$align")
} }
} }

View File

@ -42,7 +42,7 @@ class PtNodeGroup : PtNode(Position.DUMMY) {
} }
abstract class PtNamedNode(val name: String, position: Position): PtNode(position) { sealed class PtNamedNode(val name: String, position: Position): PtNode(position) {
val scopedName: List<String> by lazy { val scopedName: List<String> by lazy {
var namedParent: PtNode = this.parent var namedParent: PtNode = this.parent
if(namedParent is PtProgram) if(namedParent is PtProgram)
@ -96,8 +96,13 @@ class PtBlock(name: String,
} }
class PtInlineAssembly(val assembly: String, position: Position) : PtNode(position) { class PtInlineAssembly(val assembly: String, val isIR: Boolean, position: Position) : PtNode(position) {
override fun printProperties() {} override fun printProperties() {}
init {
require(!assembly.startsWith('\n') && !assembly.startsWith('\r')) { "inline assembly should be trimmed" }
require(!assembly.endsWith('\n') && !assembly.endsWith('\r')) { "inline assembly should be trimmed" }
}
} }

View File

@ -173,8 +173,7 @@ class PtPrefix(val operator: String, type: DataType, position: Position): PtExpr
init { init {
// note: the "not" operator may no longer occur in the ast; not x should have been replaced with x==0 // note: the "not" operator may no longer occur in the ast; not x should have been replaced with x==0
if(operator !in setOf("+", "-", "~")) require(operator in setOf("+", "-", "~")) { "invalid prefix operator: $operator" }
throw IllegalArgumentException("invalid prefix operator: $operator")
} }
override fun printProperties() { override fun printProperties() {

View File

@ -29,6 +29,14 @@ class PtSub(
override fun printProperties() { override fun printProperties() {
print(name) print(name)
} }
init {
// params and return value should not be str
if(parameters.any{ it.type !in NumericDatatypes })
throw AssemblyError("non-numeric parameter")
if(returntype!=null && returntype !in NumericDatatypes)
throw AssemblyError("non-numeric returntype $returntype")
}
} }

View File

@ -106,7 +106,7 @@ sealed class SourceCode {
* [origin]: `library:/x/y/z.p8` for a given `pathString` of "x/y/z.p8" * [origin]: `library:/x/y/z.p8` for a given `pathString` of "x/y/z.p8"
*/ */
class Resource(pathString: String): SourceCode() { class Resource(pathString: String): SourceCode() {
private val normalized = "/" + Path.of(pathString).normalize().toMutableList().joinToString("/") private val normalized = "/" + Path(pathString).normalize().toMutableList().joinToString("/")
override val isFromResources = true override val isFromResources = true
override val isFromFilesystem = false override val isFromFilesystem = false
@ -125,7 +125,7 @@ sealed class SourceCode {
} }
val stream = object {}.javaClass.getResourceAsStream(normalized) val stream = object {}.javaClass.getResourceAsStream(normalized)
text = stream!!.reader().use { it.readText() } text = stream!!.reader().use { it.readText() }
name = Path.of(pathString).toFile().nameWithoutExtension name = Path(pathString).toFile().nameWithoutExtension
} }
} }

View File

@ -4,12 +4,11 @@ import prog8.code.core.CompilationOptions
import prog8.code.core.CpuType import prog8.code.core.CpuType
import prog8.code.core.IMachineDefinition import prog8.code.core.IMachineDefinition
import prog8.code.core.Zeropage import prog8.code.core.Zeropage
import java.io.File
import java.nio.file.Path import java.nio.file.Path
import kotlin.io.path.isReadable
import kotlin.io.path.name import kotlin.io.path.name
import kotlin.io.path.readText import kotlin.io.path.readText
class VirtualMachineDefinition: IMachineDefinition { class VirtualMachineDefinition: IMachineDefinition {
override val cpu = CpuType.VIRTUAL override val cpu = CpuType.VIRTUAL
@ -32,17 +31,18 @@ class VirtualMachineDefinition: IMachineDefinition {
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path) { override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path) {
println("\nStarting Virtual Machine...") println("\nStarting Virtual Machine...")
// to not have external module dependencies we launch the virtual machine via reflection // to not have external module dependencies in our own module, we launch the virtual machine via reflection
val vm = Class.forName("prog8.vm.VmRunner").getDeclaredConstructor().newInstance() as IVirtualMachineRunner val vm = Class.forName("prog8.vm.VmRunner").getDeclaredConstructor().newInstance() as IVirtualMachineRunner
val filename = programNameWithPath.name val filename = programNameWithPath.name
if(filename.endsWith(".p8virt")) { if(programNameWithPath.isReadable()) {
vm.runProgram(programNameWithPath.readText(), true) vm.runProgram(programNameWithPath.readText())
} else if(File("$filename.p8virt").isFile) { } else {
val source = File("$filename.p8virt").readText() val withExt = programNameWithPath.resolveSibling("$filename.p8ir")
vm.runProgram(source, true) if(withExt.isReadable())
vm.runProgram(withExt.readText())
else
throw NoSuchFileException(withExt.toFile(), reason="not a .p8ir file")
} }
else
throw IllegalArgumentException("vm can only run .p8virt or .p8ir files")
} }
override fun isIOAddress(address: UInt): Boolean = false override fun isIOAddress(address: UInt): Boolean = false
@ -53,5 +53,5 @@ class VirtualMachineDefinition: IMachineDefinition {
} }
interface IVirtualMachineRunner { interface IVirtualMachineRunner {
fun runProgram(source: String, throttle: Boolean) fun runProgram(irSource: CharSequence)
} }

View File

@ -8,7 +8,6 @@ import prog8.ast.Program
import prog8.ast.base.FatalAstException import prog8.ast.base.FatalAstException
import prog8.ast.expressions.* import prog8.ast.expressions.*
import prog8.ast.statements.* import prog8.ast.statements.*
import prog8.code.StMemorySlab
import prog8.code.SymbolTable import prog8.code.SymbolTable
import prog8.code.core.* import prog8.code.core.*
import prog8.codegen.cpu6502.assignment.* import prog8.codegen.cpu6502.assignment.*
@ -42,7 +41,7 @@ class AsmGen(internal val program: Program,
private val expressionsAsmGen = ExpressionsAsmGen(program, this, allocator) private val expressionsAsmGen = ExpressionsAsmGen(program, this, allocator)
private val programGen = ProgramAndVarsGen(program, options, errors, symbolTable, functioncallAsmGen, this, allocator, zeropage) private val programGen = ProgramAndVarsGen(program, options, errors, symbolTable, functioncallAsmGen, this, allocator, zeropage)
private val assignmentAsmGen = AssignmentAsmGen(program, this, allocator) private val assignmentAsmGen = AssignmentAsmGen(program, this, allocator)
private val builtinFunctionsAsmGen = BuiltinFunctionsAsmGen(program, this, assignmentAsmGen, allocator) private val builtinFunctionsAsmGen = BuiltinFunctionsAsmGen(program, this, assignmentAsmGen)
override fun compileToAssembly(): IAssemblyProgram? { override fun compileToAssembly(): IAssemblyProgram? {
@ -78,7 +77,7 @@ class AsmGen(internal val program: Program,
} }
internal fun out(str: String, splitlines: Boolean = true) { internal fun out(str: String, splitlines: Boolean = true) {
val fragment = (if(splitlines && " | " in str) str.replace("|", "\n") else str).trim('\n') val fragment = (if(splitlines && " | " in str) str.replace("|", "\n") else str).trim('\r', '\n')
if (splitlines) { if (splitlines) {
for (line in fragment.splitToSequence('\n')) { for (line in fragment.splitToSequence('\n')) {
val trimmed = if (line.startsWith(' ')) "\t" + line.trim() else line val trimmed = if (line.startsWith(' ')) "\t" + line.trim() else line
@ -107,7 +106,7 @@ class AsmGen(internal val program: Program,
DataType.BYTE -> listOf("cx16", "r9sL") DataType.BYTE -> listOf("cx16", "r9sL")
DataType.UWORD -> listOf("cx16", "r9") DataType.UWORD -> listOf("cx16", "r9")
DataType.WORD -> listOf("cx16", "r9s") DataType.WORD -> listOf("cx16", "r9s")
DataType.FLOAT -> listOf("floats", "tempvar_swap_float") // defined in floats.p8 DataType.FLOAT -> TODO("no temporary float var available")
else -> throw FatalAstException("invalid dt $dt") else -> throw FatalAstException("invalid dt $dt")
} }
} }
@ -839,7 +838,7 @@ $repeatLabel lda $counterVar
if(stmt.definingModule.source is SourceCode.Generated) if(stmt.definingModule.source is SourceCode.Generated)
throw AssemblyError("%asminclude inside non-library/non-filesystem module not yet supported") throw AssemblyError("%asminclude inside non-library/non-filesystem module not yet supported")
loadAsmIncludeFile(includedName, stmt.definingModule.source).fold( loadAsmIncludeFile(includedName, stmt.definingModule.source).fold(
success = { assemblyLines.add(it.trimEnd().trimStart('\n')) }, success = { assemblyLines.add(it.trimEnd().trimStart('\r', '\n')) },
failure = { errors.err(it.toString(), stmt.position) } failure = { errors.err(it.toString(), stmt.position) }
) )
} }
@ -910,8 +909,7 @@ $repeatLabel lda $counterVar
} }
private fun translate(asm: InlineAssembly) { private fun translate(asm: InlineAssembly) {
val assembly = asm.assembly.trimEnd().trimStart('\n') assemblyLines.add(asm.assembly.trimEnd().trimStart('\r', '\n'))
assemblyLines.add(assembly)
} }
internal fun returnRegisterOfFunction(it: IdentifierReference): RegisterOrPair { internal fun returnRegisterOfFunction(it: IdentifierReference): RegisterOrPair {
@ -2897,12 +2895,6 @@ $repeatLabel lda $counterVar
else else
extra extra
} }
fun addMemorySlab(name: String, size: UInt, align: UInt, position: Position): String {
val prefixedName = "prog8_memoryslab_$name"
symbolTable.add(StMemorySlab(prefixedName, size, align, null, position))
return prefixedName
}
} }

View File

@ -15,8 +15,7 @@ import prog8.compiler.FSignature
internal class BuiltinFunctionsAsmGen(private val program: Program, internal class BuiltinFunctionsAsmGen(private val program: Program,
private val asmgen: AsmGen, private val asmgen: AsmGen,
private val assignAsmGen: AssignmentAsmGen, private val assignAsmGen: AssignmentAsmGen) {
private val allocations: VariableAllocator) {
internal fun translateFunctioncallExpression(fcall: BuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?) { internal fun translateFunctioncallExpression(fcall: BuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?) {
val func = BuiltinFunctions.getValue(fcall.target.nameInSource.single()) val func = BuiltinFunctions.getValue(fcall.target.nameInSource.single())
@ -44,7 +43,6 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
"abs" -> funcAbs(fcall, func, resultToStack, resultRegister, sscope) "abs" -> funcAbs(fcall, func, resultToStack, resultRegister, sscope)
"any", "all" -> funcAnyAll(fcall, func, resultToStack, resultRegister, sscope) "any", "all" -> funcAnyAll(fcall, func, resultToStack, resultRegister, sscope)
"sgn" -> funcSgn(fcall, func, resultToStack, resultRegister, sscope) "sgn" -> funcSgn(fcall, func, resultToStack, resultRegister, sscope)
"rnd", "rndw" -> funcRnd(func, resultToStack, resultRegister, sscope)
"sqrt16" -> funcSqrt16(fcall, func, resultToStack, resultRegister, sscope) "sqrt16" -> funcSqrt16(fcall, func, resultToStack, resultRegister, sscope)
"rol" -> funcRol(fcall) "rol" -> funcRol(fcall)
"rol2" -> funcRol2(fcall) "rol2" -> funcRol2(fcall)
@ -309,10 +307,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
throw AssemblyError("should not discard result of memory allocation at $fcall") throw AssemblyError("should not discard result of memory allocation at $fcall")
val name = (fcall.args[0] as StringLiteral).value val name = (fcall.args[0] as StringLiteral).value
require(name.all { it.isLetterOrDigit() || it=='_' }) {"memory name should be a valid symbol name"} require(name.all { it.isLetterOrDigit() || it=='_' }) {"memory name should be a valid symbol name"}
val size = (fcall.args[1] as NumericLiteral).number.toUInt() val slabname = IdentifierReference(listOf("prog8_slabs", "prog8_memoryslab_$name"), fcall.position)
val align = (fcall.args[2] as NumericLiteral).number.toUInt()
val prefixedName = asmgen.addMemorySlab(name, size, align, fcall.position)
val slabname = IdentifierReference(listOf("prog8_slabs", prefixedName), fcall.position)
slabname.linkParents(fcall) slabname.linkParents(fcall)
val src = AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, DataType.UWORD, expression = AddressOf(slabname, fcall.position)) val src = AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, DataType.UWORD, expression = AddressOf(slabname, fcall.position))
val target = val target =
@ -691,28 +686,6 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
} }
} }
private fun funcRnd(func: FSignature, resultToStack: Boolean, resultRegister: RegisterOrPair?, scope: Subroutine?) {
when(func.name) {
"rnd" -> {
if(resultToStack)
asmgen.out(" jsr prog8_lib.func_rnd_stack")
else {
asmgen.out(" jsr math.randbyte")
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, false, scope, asmgen), CpuRegister.A)
}
}
"rndw" -> {
if(resultToStack)
asmgen.out(" jsr prog8_lib.func_rndw_stack")
else {
asmgen.out(" jsr math.randword")
assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, false, scope, asmgen), RegisterOrPair.AY)
}
}
else -> throw AssemblyError("wrong func")
}
}
private fun funcPokeW(fcall: IFunctionCall) { private fun funcPokeW(fcall: IFunctionCall) {
when(val addrExpr = fcall.args[0]) { when(val addrExpr = fcall.args[0]) {
is NumericLiteral -> { is NumericLiteral -> {

View File

@ -53,7 +53,7 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
(sub.parameters.size==1 && sub.parameters[0].type in IntegerDatatypes) (sub.parameters.size==1 && sub.parameters[0].type in IntegerDatatypes)
|| (sub.parameters.size==2 && sub.parameters[0].type in ByteDatatypes && sub.parameters[1].type in ByteDatatypes) || (sub.parameters.size==2 && sub.parameters[0].type in ByteDatatypes && sub.parameters[1].type in ByteDatatypes)
internal fun translateFunctionCall(call: IFunctionCall, isExpression: Boolean) { internal fun translateFunctionCall(call: IFunctionCall, isExpression: Boolean) { // TODO remove isExpression unused parameter
// Output only the code to set up the parameters and perform the actual call // Output only the code to set up the parameters and perform the actual call
// NOTE: does NOT output the code to deal with the result values! // NOTE: does NOT output the code to deal with the result values!
// NOTE: does NOT output code to save/restore the X register for this call! Every caller should deal with this in their own way!! // NOTE: does NOT output code to save/restore the X register for this call! Every caller should deal with this in their own way!!

View File

@ -287,18 +287,24 @@ internal class AssignmentAsmGen(private val program: Program,
} }
} }
is PrefixExpression -> { is PrefixExpression -> {
// first assign the value to the target then apply the operator in place on the target. if(assign.target.array==null) {
translateNormalAssignment(AsmAssignment( // First assign the value to the target then apply the operator in place on the target.
AsmAssignSource.fromAstSource(value.expression, program, asmgen), // This saves a temporary variable
assign.target, translateNormalAssignment(
false, program.memsizer, assign.position AsmAssignment(
)) AsmAssignSource.fromAstSource(value.expression, program, asmgen),
val target = virtualRegsToVariables(assign.target) assign.target,
when(value.operator) { false, program.memsizer, assign.position
"+" -> {} )
"-" -> augmentableAsmGen.inplaceNegate(target, target.datatype) )
"~" -> augmentableAsmGen.inplaceInvert(target, target.datatype) when (value.operator) {
else -> throw AssemblyError("invalid prefix operator") "+" -> {}
"-" -> augmentableAsmGen.inplaceNegate(assign)
"~" -> augmentableAsmGen.inplaceInvert(assign)
else -> throw AssemblyError("invalid prefix operator")
}
} else {
assignPrefixedExpressionToArrayElt(assign)
} }
} }
is ContainmentCheck -> { is ContainmentCheck -> {
@ -317,6 +323,28 @@ internal class AssignmentAsmGen(private val program: Program,
} }
} }
internal fun assignPrefixedExpressionToArrayElt(assign: AsmAssignment) {
require(assign.source.expression is PrefixExpression)
if(assign.source.datatype==DataType.FLOAT) {
// floatarray[x] = -value ... just use FAC1 to calculate the expression into and then store that back into the array.
assignExpressionToRegister(assign.source.expression, RegisterOrPair.FAC1, true)
assignFAC1float(assign.target)
} else {
// array[x] = -value ... use a tempvar then store that back into the array.
val tempvar = asmgen.getTempVarName(assign.target.datatype).joinToString(".")
val assignToTempvar = AsmAssignment(assign.source,
AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, assign.target.datatype, assign.target.scope, variableAsmName=tempvar, origAstTarget = assign.target.origAstTarget),
false, program.memsizer, assign.position)
asmgen.translateNormalAssignment(assignToTempvar)
when(assign.target.datatype) {
in ByteDatatypes -> assignVariableByte(assign.target, tempvar)
in WordDatatypes -> assignVariableWord(assign.target, tempvar)
DataType.FLOAT -> assignVariableFloat(assign.target, tempvar)
else -> throw AssemblyError("weird dt")
}
}
}
private fun assignVirtualRegister(target: AsmAssignTarget, register: RegisterOrPair) { private fun assignVirtualRegister(target: AsmAssignTarget, register: RegisterOrPair) {
when(target.datatype) { when(target.datatype) {
in ByteDatatypes -> { in ByteDatatypes -> {
@ -852,7 +880,8 @@ internal class AssignmentAsmGen(private val program: Program,
fun assignViaExprEval(addressExpression: Expression) { fun assignViaExprEval(addressExpression: Expression) {
asmgen.assignExpressionToVariable(addressExpression, "P8ZP_SCRATCH_W2", DataType.UWORD, null) asmgen.assignExpressionToVariable(addressExpression, "P8ZP_SCRATCH_W2", DataType.UWORD, null)
asmgen.loadAFromZpPointerVar("P8ZP_SCRATCH_W2") asmgen.loadAFromZpPointerVar("P8ZP_SCRATCH_W2")
assignRegisterByte(target, CpuRegister.A) asmgen.out(" ldy #0")
assignRegisterpairWord(target, RegisterOrPair.AY)
} }
when (value.addressExpression) { when (value.addressExpression) {
@ -1972,18 +2001,10 @@ internal class AssignmentAsmGen(private val program: Program,
} }
internal fun assignRegisterByte(target: AsmAssignTarget, register: CpuRegister) { internal fun assignRegisterByte(target: AsmAssignTarget, register: CpuRegister) {
// we make an exception in the type check for assigning something to a cx16 virtual register, or a register pair // we make an exception in the type check for assigning something to a register pair AX, AY or XY
// these will be correctly typecasted from a byte to a word value // these will be correctly typecasted from a byte to a word value here
if(target.register !in Cx16VirtualRegisters && if(target.register !in setOf(RegisterOrPair.AX, RegisterOrPair.AY, RegisterOrPair.XY))
target.register!=RegisterOrPair.AX && target.register!=RegisterOrPair.AY && target.register!=RegisterOrPair.XY) { require(target.datatype in ByteDatatypes)
if(target.kind== TargetStorageKind.VARIABLE) {
val parts = target.asmVarname.split('.')
if (parts.size != 2 || parts[0] != "cx16")
require(target.datatype in ByteDatatypes)
} else {
require(target.datatype in ByteDatatypes)
}
}
when(target.kind) { when(target.kind) {
TargetStorageKind.VARIABLE -> { TargetStorageKind.VARIABLE -> {

View File

@ -21,13 +21,10 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
when (val value = assign.source.expression!!) { when (val value = assign.source.expression!!) {
is PrefixExpression -> { is PrefixExpression -> {
// A = -A , A = +A, A = ~A, A = not A // A = -A , A = +A, A = ~A, A = not A
val target = assignmentAsmGen.virtualRegsToVariables(assign.target)
val itype = value.inferType(program)
val type = itype.getOrElse { throw AssemblyError("unknown dt") }
when (value.operator) { when (value.operator) {
"+" -> {} "+" -> {}
"-" -> inplaceNegate(target, type) "-" -> inplaceNegate(assign)
"~" -> inplaceInvert(target, type) "~" -> inplaceInvert(assign)
else -> throw AssemblyError("invalid prefix operator") else -> throw AssemblyError("invalid prefix operator")
} }
} }
@ -1796,8 +1793,9 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
} }
} }
internal fun inplaceInvert(target: AsmAssignTarget, dt: DataType) { internal fun inplaceInvert(assign: AsmAssignment) {
when (dt) { val target = assign.target
when (assign.target.datatype) {
DataType.UBYTE -> { DataType.UBYTE -> {
when (target.kind) { when (target.kind) {
TargetStorageKind.VARIABLE -> { TargetStorageKind.VARIABLE -> {
@ -1840,7 +1838,8 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
} }
} }
TargetStorageKind.STACK -> TODO("no asm gen for byte stack invert") TargetStorageKind.STACK -> TODO("no asm gen for byte stack invert")
else -> throw AssemblyError("no asm gen for in-place invert ubyte for ${target.kind}") TargetStorageKind.ARRAY -> assignmentAsmGen.assignPrefixedExpressionToArrayElt(assign)
else -> throw AssemblyError("weird target")
} }
} }
DataType.UWORD -> { DataType.UWORD -> {
@ -1864,15 +1863,17 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
} }
} }
TargetStorageKind.STACK -> TODO("no asm gen for word stack invert") TargetStorageKind.STACK -> TODO("no asm gen for word stack invert")
else -> throw AssemblyError("no asm gen for in-place invert uword for ${target.kind}") TargetStorageKind.ARRAY -> assignmentAsmGen.assignPrefixedExpressionToArrayElt(assign)
else -> throw AssemblyError("weird target")
} }
} }
else -> throw AssemblyError("invert of invalid type") else -> throw AssemblyError("invert of invalid type")
} }
} }
internal fun inplaceNegate(target: AsmAssignTarget, dt: DataType) { internal fun inplaceNegate(assign: AsmAssignment) {
when (dt) { val target = assign.target
when (assign.target.datatype) {
DataType.BYTE -> { DataType.BYTE -> {
when (target.kind) { when (target.kind) {
TargetStorageKind.VARIABLE -> { TargetStorageKind.VARIABLE -> {
@ -1896,9 +1897,10 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
else -> throw AssemblyError("invalid reg dt for byte negate") else -> throw AssemblyError("invalid reg dt for byte negate")
} }
} }
TargetStorageKind.MEMORY -> throw AssemblyError("memory is ubyte, can't in-place negate") TargetStorageKind.MEMORY -> throw AssemblyError("memory is ubyte, can't negate that")
TargetStorageKind.STACK -> TODO("no asm gen for byte stack negate") TargetStorageKind.STACK -> TODO("no asm gen for byte stack negate")
else -> throw AssemblyError("no asm gen for in-place negate byte") TargetStorageKind.ARRAY -> assignmentAsmGen.assignPrefixedExpressionToArrayElt(assign)
else -> throw AssemblyError("weird target")
} }
} }
DataType.WORD -> { DataType.WORD -> {
@ -1955,12 +1957,21 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
else -> throw AssemblyError("invalid reg dt for word neg") else -> throw AssemblyError("invalid reg dt for word neg")
} }
} }
TargetStorageKind.MEMORY -> throw AssemblyError("memory is ubyte, can't negate that")
TargetStorageKind.STACK -> TODO("no asm gen for word stack negate") TargetStorageKind.STACK -> TODO("no asm gen for word stack negate")
else -> throw AssemblyError("no asm gen for in-place negate word") TargetStorageKind.ARRAY -> assignmentAsmGen.assignPrefixedExpressionToArrayElt(assign)
else -> throw AssemblyError("weird target")
} }
} }
DataType.FLOAT -> { DataType.FLOAT -> {
when (target.kind) { when (target.kind) {
TargetStorageKind.REGISTER -> {
when(target.register!!) {
RegisterOrPair.FAC1 -> asmgen.out(" jsr floats.NEGOP")
RegisterOrPair.FAC2 -> asmgen.out(" jsr floats.MOVFA | jsr floats.NEGOP | jsr floats.MOVEF")
else -> throw AssemblyError("invalid float register")
}
}
TargetStorageKind.VARIABLE -> { TargetStorageKind.VARIABLE -> {
// simply flip the sign bit in the float // simply flip the sign bit in the float
asmgen.out(""" asmgen.out("""
@ -1970,10 +1981,11 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
""") """)
} }
TargetStorageKind.STACK -> TODO("no asm gen for float stack negate") TargetStorageKind.STACK -> TODO("no asm gen for float stack negate")
else -> throw AssemblyError("weird target kind for inplace negate float ${target.kind}") TargetStorageKind.ARRAY -> assignmentAsmGen.assignPrefixedExpressionToArrayElt(assign)
else -> throw AssemblyError("weird target for in-place float negation")
} }
} }
else -> throw AssemblyError("negate of invalid type $dt") else -> throw AssemblyError("negate of invalid type")
} }
} }

View File

@ -22,7 +22,7 @@ class CodeGen(private val program: PtProgram,
val irProgram = irCodeGen.generate() val irProgram = irCodeGen.generate()
// this stub only writes the IR program to disk but doesn't generate anything else. // this stub only writes the IR program to disk but doesn't generate anything else.
IRFileWriter(irProgram).writeFile() IRFileWriter(irProgram, null).write()
println("** experimental codegen stub: no assembly generated **") println("** experimental codegen stub: no assembly generated **")
return null return null

View File

@ -26,7 +26,6 @@ compileTestKotlin {
dependencies { dependencies {
implementation project(':codeCore') implementation project(':codeCore')
implementation project(':intermediate') implementation project(':intermediate')
implementation project(':virtualmachine')
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
// implementation "org.jetbrains.kotlin:kotlin-reflect" // implementation "org.jetbrains.kotlin:kotlin-reflect"
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.16" implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.16"

View File

@ -12,7 +12,6 @@
<orderEntry type="library" name="KotlinJavaRuntime" level="project" /> <orderEntry type="library" name="KotlinJavaRuntime" level="project" />
<orderEntry type="module" module-name="codeCore" /> <orderEntry type="module" module-name="codeCore" />
<orderEntry type="module" module-name="intermediate" /> <orderEntry type="module" module-name="intermediate" />
<orderEntry type="module" module-name="virtualmachine" />
<orderEntry type="library" name="io.kotest.assertions.core.jvm" level="project" /> <orderEntry type="library" name="io.kotest.assertions.core.jvm" level="project" />
<orderEntry type="library" name="io.kotest.runner.junit5.jvm" level="project" /> <orderEntry type="library" name="io.kotest.runner.junit5.jvm" level="project" />
</component> </component>

View File

@ -3,16 +3,12 @@ package prog8.codegen.intermediate
import prog8.code.ast.* import prog8.code.ast.*
import prog8.code.core.AssemblyError import prog8.code.core.AssemblyError
import prog8.code.core.DataType import prog8.code.core.DataType
import prog8.code.core.Position
import prog8.code.core.SignedDatatypes import prog8.code.core.SignedDatatypes
import prog8.intermediate.IRCodeChunk import prog8.intermediate.*
import prog8.intermediate.IRCodeInstruction
import prog8.intermediate.Opcode
import prog8.intermediate.VmDataType
internal class AssignmentGen(private val codeGen: IRCodeGen, private val expressionEval: ExpressionGen) { internal class AssignmentGen(private val codeGen: IRCodeGen, private val expressionEval: ExpressionGen) {
internal fun translate(assignment: PtAssignment): IRCodeChunk { internal fun translate(assignment: PtAssignment): IRCodeChunks {
if(assignment.target.children.single() is PtMachineRegister) if(assignment.target.children.single() is PtMachineRegister)
throw AssemblyError("assigning to a register should be done by just evaluating the expression into resultregister") throw AssemblyError("assigning to a register should be done by just evaluating the expression into resultregister")
@ -22,7 +18,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
translateRegularAssign(assignment) translateRegularAssign(assignment)
} }
private fun translateInplaceAssign(assignment: PtAssignment): IRCodeChunk { private fun translateInplaceAssign(assignment: PtAssignment): IRCodeChunks {
val ident = assignment.target.identifier val ident = assignment.target.identifier
val memory = assignment.target.memory val memory = assignment.target.memory
val array = assignment.target.array val array = assignment.target.array
@ -48,23 +44,23 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
address: Int, address: Int,
value: PtExpression, value: PtExpression,
origAssign: PtAssignment origAssign: PtAssignment
): IRCodeChunk { ): IRCodeChunks {
val vmDt = codeGen.vmType(value.type) val vmDt = codeGen.irType(value.type)
val code = IRCodeChunk(origAssign.position)
when(value) { when(value) {
is PtIdentifier -> return code // do nothing, x=x null assignment. is PtIdentifier -> return emptyList() // do nothing, x=x null assignment.
is PtMachineRegister -> return code // do nothing, reg=reg null assignment is PtMachineRegister -> return emptyList() // do nothing, reg=reg null assignment
is PtPrefix -> return inplacePrefix(value.operator, vmDt, address, null, value.position) is PtPrefix -> return inplacePrefix(value.operator, vmDt, address, null)
is PtBinaryExpression -> return inplaceBinexpr(value.operator, value.right, vmDt, value.type in SignedDatatypes, address, null, origAssign) is PtBinaryExpression -> return inplaceBinexpr(value.operator, value.right, vmDt, value.type in SignedDatatypes, address, null, origAssign)
is PtMemoryByte -> { is PtMemoryByte -> {
return if (!codeGen.options.compTarget.machine.isIOAddress(address.toUInt())) return if (!codeGen.options.compTarget.machine.isIOAddress(address.toUInt()))
code // do nothing, mem=mem null assignment. emptyList() // do nothing, mem=mem null assignment.
else { else {
// read and write a (i/o) memory location to itself. // read and write a (i/o) memory location to itself.
val tempReg = codeGen.vmRegisters.nextFree() val tempReg = codeGen.registers.nextFree()
code += IRCodeInstruction(Opcode.LOADM, vmDt, reg1 = tempReg, value = address) val code = IRCodeChunk(null, null)
code += IRCodeInstruction(Opcode.STOREM, vmDt, reg1 = tempReg, value = address) code += IRInstruction(Opcode.LOADM, vmDt, reg1 = tempReg, value = address)
code code += IRInstruction(Opcode.STOREM, vmDt, reg1 = tempReg, value = address)
listOf(code)
} }
} }
else -> return fallbackAssign(origAssign) else -> return fallbackAssign(origAssign)
@ -75,25 +71,26 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
symbol: String, symbol: String,
value: PtExpression, value: PtExpression,
origAssign: PtAssignment origAssign: PtAssignment
): IRCodeChunk { ): IRCodeChunks {
val vmDt = codeGen.vmType(value.type) val vmDt = codeGen.irType(value.type)
val code = IRCodeChunk(origAssign.position) return when(value) {
when(value) { is PtIdentifier -> emptyList() // do nothing, x=x null assignment.
is PtIdentifier -> return code // do nothing, x=x null assignment. is PtMachineRegister -> emptyList() // do nothing, reg=reg null assignment
is PtMachineRegister -> return code // do nothing, reg=reg null assignment is PtPrefix -> inplacePrefix(value.operator, vmDt, null, symbol)
is PtPrefix -> return inplacePrefix(value.operator, vmDt, null, symbol, value.position) is PtBinaryExpression -> inplaceBinexpr(value.operator, value.right, vmDt, value.type in SignedDatatypes, null, symbol, origAssign)
is PtBinaryExpression -> return inplaceBinexpr(value.operator, value.right, vmDt, value.type in SignedDatatypes, null, symbol, origAssign)
is PtMemoryByte -> { is PtMemoryByte -> {
val tempReg = codeGen.vmRegisters.nextFree() val code = IRCodeChunk(null, null)
code += IRCodeInstruction(Opcode.LOADM, vmDt, reg1 = tempReg, labelSymbol = symbol) val tempReg = codeGen.registers.nextFree()
code += IRCodeInstruction(Opcode.STOREM, vmDt, reg1 = tempReg, labelSymbol = symbol) code += IRInstruction(Opcode.LOADM, vmDt, reg1 = tempReg, labelSymbol = symbol)
return code code += IRInstruction(Opcode.STOREM, vmDt, reg1 = tempReg, labelSymbol = symbol)
listOf(code)
} }
else -> return fallbackAssign(origAssign)
else -> fallbackAssign(origAssign)
} }
} }
private fun fallbackAssign(origAssign: PtAssignment): IRCodeChunk { private fun fallbackAssign(origAssign: PtAssignment): IRCodeChunks {
if (codeGen.options.slowCodegenWarnings) if (codeGen.options.slowCodegenWarnings)
codeGen.errors.warn("indirect code for in-place assignment", origAssign.position) codeGen.errors.warn("indirect code for in-place assignment", origAssign.position)
return translateRegularAssign(origAssign) return translateRegularAssign(origAssign)
@ -102,12 +99,12 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
private fun inplaceBinexpr( private fun inplaceBinexpr(
operator: String, operator: String,
operand: PtExpression, operand: PtExpression,
vmDt: VmDataType, vmDt: IRDataType,
signed: Boolean, signed: Boolean,
knownAddress: Int?, knownAddress: Int?,
symbol: String?, symbol: String?,
origAssign: PtAssignment origAssign: PtAssignment
): IRCodeChunk { ): IRCodeChunks {
if(knownAddress!=null) { if(knownAddress!=null) {
when (operator) { when (operator) {
"+" -> return expressionEval.operatorPlusInplace(knownAddress, null, vmDt, operand) "+" -> return expressionEval.operatorPlusInplace(knownAddress, null, vmDt, operand)
@ -139,66 +136,67 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
return fallbackAssign(origAssign) return fallbackAssign(origAssign)
} }
private fun inplacePrefix(operator: String, vmDt: VmDataType, knownAddress: Int?, addressSymbol: String?, position: Position): IRCodeChunk { private fun inplacePrefix(operator: String, vmDt: IRDataType, knownAddress: Int?, addressSymbol: String?): IRCodeChunks {
val code= IRCodeChunk(position) val code= IRCodeChunk(null, null)
when(operator) { when(operator) {
"+" -> { } "+" -> { }
"-" -> { "-" -> {
code += if(knownAddress!=null) code += if(knownAddress!=null)
IRCodeInstruction(Opcode.NEGM, vmDt, value = knownAddress) IRInstruction(Opcode.NEGM, vmDt, value = knownAddress)
else else
IRCodeInstruction(Opcode.NEGM, vmDt, labelSymbol = addressSymbol) IRInstruction(Opcode.NEGM, vmDt, labelSymbol = addressSymbol)
} }
"~" -> { "~" -> {
val regMask = codeGen.vmRegisters.nextFree() val regMask = codeGen.registers.nextFree()
val mask = if(vmDt==VmDataType.BYTE) 0x00ff else 0xffff val mask = if(vmDt==IRDataType.BYTE) 0x00ff else 0xffff
code += IRCodeInstruction(Opcode.LOAD, vmDt, reg1=regMask, value = mask) code += IRInstruction(Opcode.LOAD, vmDt, reg1=regMask, value = mask)
code += if(knownAddress!=null) code += if(knownAddress!=null)
IRCodeInstruction(Opcode.XORM, vmDt, reg1=regMask, value = knownAddress) IRInstruction(Opcode.XORM, vmDt, reg1=regMask, value = knownAddress)
else else
IRCodeInstruction(Opcode.XORM, vmDt, reg1=regMask, labelSymbol = addressSymbol) IRInstruction(Opcode.XORM, vmDt, reg1=regMask, labelSymbol = addressSymbol)
} }
else -> throw AssemblyError("weird prefix operator") else -> throw AssemblyError("weird prefix operator")
} }
return code return listOf(code)
} }
private fun translateRegularAssign(assignment: PtAssignment): IRCodeChunk { private fun translateRegularAssign(assignment: PtAssignment): IRCodeChunks {
// note: assigning array and string values is done via an explicit memcopy/stringcopy function call. // note: assigning array and string values is done via an explicit memcopy/stringcopy function call.
val ident = assignment.target.identifier val ident = assignment.target.identifier
val memory = assignment.target.memory val memory = assignment.target.memory
val array = assignment.target.array val array = assignment.target.array
val vmDt = codeGen.vmType(assignment.value.type) val vmDt = codeGen.irType(assignment.value.type)
val result = mutableListOf<IRCodeChunkBase>()
val code = IRCodeChunk(assignment.position)
var resultRegister = -1 var resultRegister = -1
var resultFpRegister = -1 var resultFpRegister = -1
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 == VmDataType.FLOAT) { if (vmDt == IRDataType.FLOAT) {
resultFpRegister = codeGen.vmRegisters.nextFreeFloat() resultFpRegister = codeGen.registers.nextFreeFloat()
code += expressionEval.translateExpression(assignment.value, -1, resultFpRegister) result += expressionEval.translateExpression(assignment.value, -1, resultFpRegister)
} else { } else {
resultRegister = if (assignment.value is PtMachineRegister) { resultRegister = if (assignment.value is PtMachineRegister) {
(assignment.value as PtMachineRegister).register (assignment.value as PtMachineRegister).register
} else { } else {
val reg = codeGen.vmRegisters.nextFree() val reg = codeGen.registers.nextFree()
code += expressionEval.translateExpression(assignment.value, reg, -1) result += expressionEval.translateExpression(assignment.value, reg, -1)
reg reg
} }
} }
} }
if(ident!=null) { if(ident!=null) {
val symbol = ident.targetName.joinToString(".") val symbol = ident.targetName.joinToString(".")
code += if(zero) { val instruction = if(zero) {
IRCodeInstruction(Opcode.STOREZM, vmDt, labelSymbol = symbol) IRInstruction(Opcode.STOREZM, vmDt, labelSymbol = symbol)
} else { } else {
if (vmDt == VmDataType.FLOAT) if (vmDt == IRDataType.FLOAT)
IRCodeInstruction(Opcode.STOREM, vmDt, fpReg1 = resultFpRegister, labelSymbol = symbol) IRInstruction(Opcode.STOREM, vmDt, fpReg1 = resultFpRegister, labelSymbol = symbol)
else else
IRCodeInstruction(Opcode.STOREM, vmDt, reg1 = resultRegister, labelSymbol = symbol) IRInstruction(Opcode.STOREM, vmDt, reg1 = resultRegister, labelSymbol = symbol)
} }
result += IRCodeChunk(null, null).also { it += instruction }
return result
} }
else if(array!=null) { else if(array!=null) {
val variable = array.variable.targetName.joinToString(".") val variable = array.variable.targetName.joinToString(".")
@ -210,85 +208,91 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
throw AssemblyError("non-array var indexing requires bytes dt") throw AssemblyError("non-array var indexing requires bytes dt")
if(array.index.type!=DataType.UBYTE) if(array.index.type!=DataType.UBYTE)
throw AssemblyError("non-array var indexing requires bytes index") throw AssemblyError("non-array var indexing requires bytes index")
val idxReg = codeGen.vmRegisters.nextFree() val idxReg = codeGen.registers.nextFree()
code += expressionEval.translateExpression(array.index, idxReg, -1) result += expressionEval.translateExpression(array.index, idxReg, -1)
val code = IRCodeChunk(null, null)
if(zero) { if(zero) {
// there's no STOREZIX instruction // there's no STOREZIX instruction
resultRegister = codeGen.vmRegisters.nextFree() resultRegister = codeGen.registers.nextFree()
code += IRCodeInstruction(Opcode.LOAD, vmDt, reg1=resultRegister, value=0) code += IRInstruction(Opcode.LOAD, vmDt, reg1=resultRegister, value=0)
} }
code += IRCodeInstruction(Opcode.STOREIX, vmDt, reg1=resultRegister, reg2=idxReg, labelSymbol = variable) code += IRInstruction(Opcode.STOREIX, vmDt, reg1=resultRegister, reg2=idxReg, labelSymbol = variable)
return code result += code
return result
} }
val fixedIndex = constIntValue(array.index) val fixedIndex = constIntValue(array.index)
if(zero) { if(zero) {
if(fixedIndex!=null) { if(fixedIndex!=null) {
val offset = fixedIndex*itemsize val offset = fixedIndex*itemsize
code += IRCodeInstruction(Opcode.STOREZM, vmDt, labelSymbol = "$variable+$offset") val chunk = IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREZM, vmDt, labelSymbol = "$variable+$offset") }
result += chunk
} else { } else {
val indexReg = codeGen.vmRegisters.nextFree() val indexReg = codeGen.registers.nextFree()
code += loadIndexReg(array, itemsize, indexReg, array.position) result += loadIndexReg(array, itemsize, indexReg)
code += IRCodeInstruction(Opcode.STOREZX, vmDt, reg1=indexReg, labelSymbol = variable) result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREZX, vmDt, reg1=indexReg, labelSymbol = variable) }
} }
} else { } else {
if(vmDt== VmDataType.FLOAT) { if(vmDt== IRDataType.FLOAT) {
if(fixedIndex!=null) { if(fixedIndex!=null) {
val offset = fixedIndex*itemsize val offset = fixedIndex*itemsize
code += IRCodeInstruction(Opcode.STOREM, vmDt, fpReg1 = resultFpRegister, labelSymbol = "$variable+$offset") val chunk = IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREM, vmDt, fpReg1 = resultFpRegister, labelSymbol = "$variable+$offset") }
result += chunk
} else { } else {
val indexReg = codeGen.vmRegisters.nextFree() val indexReg = codeGen.registers.nextFree()
code += loadIndexReg(array, itemsize, indexReg, array.position) result += loadIndexReg(array, itemsize, indexReg)
code += IRCodeInstruction(Opcode.STOREX, vmDt, reg1 = resultRegister, reg2=indexReg, labelSymbol = variable) result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREX, vmDt, reg1 = indexReg, fpReg1 = resultFpRegister, labelSymbol = variable) }
} }
} else { } else {
if(fixedIndex!=null) { if(fixedIndex!=null) {
val offset = fixedIndex*itemsize val offset = fixedIndex*itemsize
code += IRCodeInstruction(Opcode.STOREM, vmDt, reg1 = resultRegister, labelSymbol = "$variable+$offset") val chunk = IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREM, vmDt, reg1 = resultRegister, labelSymbol = "$variable+$offset") }
result += chunk
} else { } else {
val indexReg = codeGen.vmRegisters.nextFree() val indexReg = codeGen.registers.nextFree()
code += loadIndexReg(array, itemsize, indexReg, array.position) result += loadIndexReg(array, itemsize, indexReg)
code += IRCodeInstruction(Opcode.STOREX, vmDt, reg1 = resultRegister, reg2=indexReg, labelSymbol = variable) result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREX, vmDt, reg1 = resultRegister, reg2=indexReg, labelSymbol = variable) }
} }
} }
} }
return result
} }
else if(memory!=null) { else if(memory!=null) {
require(vmDt== VmDataType.BYTE) require(vmDt== IRDataType.BYTE)
if(zero) { if(zero) {
if(memory.address is PtNumber) { if(memory.address is PtNumber) {
code += IRCodeInstruction(Opcode.STOREZM, vmDt, value=(memory.address as PtNumber).number.toInt()) val chunk = IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREZM, vmDt, value=(memory.address as PtNumber).number.toInt()) }
result += chunk
} else { } else {
val addressReg = codeGen.vmRegisters.nextFree() val addressReg = codeGen.registers.nextFree()
code += expressionEval.translateExpression(memory.address, addressReg, -1) result += expressionEval.translateExpression(memory.address, addressReg, -1)
code += IRCodeInstruction(Opcode.STOREZI, vmDt, reg1=addressReg) result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREZI, vmDt, reg1=addressReg) }
} }
} else { } else {
if(memory.address is PtNumber) { if(memory.address is PtNumber) {
code += IRCodeInstruction(Opcode.STOREM, vmDt, reg1=resultRegister, value=(memory.address as PtNumber).number.toInt()) val chunk = IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREM, vmDt, reg1=resultRegister, value=(memory.address as PtNumber).number.toInt()) }
result += chunk
} else { } else {
val addressReg = codeGen.vmRegisters.nextFree() val addressReg = codeGen.registers.nextFree()
code += expressionEval.translateExpression(memory.address, addressReg, -1) result += expressionEval.translateExpression(memory.address, addressReg, -1)
code += IRCodeInstruction(Opcode.STOREI, vmDt, reg1=resultRegister, reg2=addressReg) result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREI, vmDt, reg1=resultRegister, reg2=addressReg) }
} }
} }
return result
} }
else else
throw AssemblyError("weird assigntarget") throw AssemblyError("weird assigntarget")
return code
} }
private fun loadIndexReg(array: PtArrayIndexer, itemsize: Int, indexReg: Int, position: Position): IRCodeChunk { private fun loadIndexReg(array: PtArrayIndexer, itemsize: Int, indexReg: Int): IRCodeChunks {
val code = IRCodeChunk(position) return if(itemsize==1) {
if(itemsize==1) { expressionEval.translateExpression(array.index, indexReg, -1)
code += expressionEval.translateExpression(array.index, indexReg, -1) } else {
}
else {
val mult = PtBinaryExpression("*", DataType.UBYTE, array.position) val mult = PtBinaryExpression("*", DataType.UBYTE, array.position)
mult.children += array.index mult.children += array.index
mult.children += PtNumber(DataType.UBYTE, itemsize.toDouble(), array.position) mult.children += PtNumber(DataType.UBYTE, itemsize.toDouble(), array.position)
code += expressionEval.translateExpression(mult, indexReg, -1) expressionEval.translateExpression(mult, indexReg, -1)
} }
return code
} }
} }

View File

@ -4,14 +4,12 @@ import prog8.code.StStaticVariable
import prog8.code.ast.* import prog8.code.ast.*
import prog8.code.core.AssemblyError import prog8.code.core.AssemblyError
import prog8.code.core.DataType import prog8.code.core.DataType
import prog8.code.core.Position
import prog8.intermediate.* import prog8.intermediate.*
import prog8.vm.Syscall
internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGen: ExpressionGen) { internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGen: ExpressionGen) {
fun translate(call: PtBuiltinFunctionCall, resultRegister: Int): IRCodeChunk { fun translate(call: PtBuiltinFunctionCall, resultRegister: Int): IRCodeChunks {
return when(call.name) { return when(call.name) {
"any" -> funcAny(call, resultRegister) "any" -> funcAny(call, resultRegister)
"all" -> funcAll(call, resultRegister) "all" -> funcAll(call, resultRegister)
@ -26,9 +24,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
"rsave", "rsave",
"rsavex", "rsavex",
"rrestore", "rrestore",
"rrestorex" -> IRCodeChunk(call.position) // vm doesn't have registers to save/restore "rrestorex" -> emptyList() // vm doesn't have registers to save/restore
"rnd" -> funcRnd(resultRegister, call.position)
"rndw" -> funcRndw(resultRegister, call.position)
"callfar" -> throw AssemblyError("callfar() is for cx16 target only") "callfar" -> throw AssemblyError("callfar() is for cx16 target only")
"callrom" -> throw AssemblyError("callrom() is for cx16 target only") "callrom" -> throw AssemblyError("callrom() is for cx16 target only")
"msb" -> funcMsb(call, resultRegister) "msb" -> funcMsb(call, resultRegister)
@ -38,7 +34,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
"peekw" -> funcPeekW(call, resultRegister) "peekw" -> funcPeekW(call, resultRegister)
"poke" -> funcPoke(call) "poke" -> funcPoke(call)
"pokew" -> funcPokeW(call) "pokew" -> funcPokeW(call)
"pokemon" -> IRCodeChunk(call.position) "pokemon" -> emptyList()
"mkword" -> funcMkword(call, resultRegister) "mkword" -> funcMkword(call, resultRegister)
"sort" -> funcSort(call) "sort" -> funcSort(call)
"reverse" -> funcReverse(call) "reverse" -> funcReverse(call)
@ -50,320 +46,359 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
} }
} }
private fun funcCmp(call: PtBuiltinFunctionCall): IRCodeChunk { private fun funcCmp(call: PtBuiltinFunctionCall): IRCodeChunks {
val code = IRCodeChunk(call.position) val leftRegister = codeGen.registers.nextFree()
val leftRegister = codeGen.vmRegisters.nextFree() val rightRegister = codeGen.registers.nextFree()
val rightRegister = codeGen.vmRegisters.nextFree() val result = mutableListOf<IRCodeChunkBase>()
code += exprGen.translateExpression(call.args[0], leftRegister, -1) result += exprGen.translateExpression(call.args[0], leftRegister, -1)
code += exprGen.translateExpression(call.args[1], rightRegister, -1) result += exprGen.translateExpression(call.args[1], rightRegister, -1)
code += IRCodeInstruction(Opcode.CMP, codeGen.vmType(call.args[0].type), reg1=leftRegister, reg2=rightRegister) result += IRCodeChunk(null, null).also {
return code it += IRInstruction(Opcode.CMP, codeGen.irType(call.args[0].type), reg1=leftRegister, reg2=rightRegister)
}
return result
} }
private fun funcAny(call: PtBuiltinFunctionCall, resultRegister: Int): IRCodeChunk { private fun funcAny(call: PtBuiltinFunctionCall, resultRegister: Int): IRCodeChunks {
val arrayName = call.args[0] as PtIdentifier val arrayName = call.args[0] as PtIdentifier
val array = codeGen.symbolTable.flat.getValue(arrayName.targetName) as StStaticVariable val array = codeGen.symbolTable.flat.getValue(arrayName.targetName) as StStaticVariable
val code = IRCodeChunk(call.position)
val syscall = val syscall =
when (array.dt) { when (array.dt) {
DataType.ARRAY_UB, DataType.ARRAY_UB,
DataType.ARRAY_B -> Syscall.ANY_BYTE DataType.ARRAY_B -> IMSyscall.ANY_BYTE
DataType.ARRAY_UW, DataType.ARRAY_UW,
DataType.ARRAY_W -> Syscall.ANY_WORD DataType.ARRAY_W -> IMSyscall.ANY_WORD
DataType.ARRAY_F -> Syscall.ANY_FLOAT DataType.ARRAY_F -> IMSyscall.ANY_FLOAT
else -> throw IllegalArgumentException("weird type") else -> throw IllegalArgumentException("weird type")
} }
code += exprGen.translateExpression(call.args[0], 0, -1) val result = mutableListOf<IRCodeChunkBase>()
code += IRCodeInstruction(Opcode.LOAD, VmDataType.BYTE, reg1 = 1, value = array.length) result += exprGen.translateExpression(call.args[0], SyscallRegisterBase, -1)
code += IRCodeInstruction(Opcode.SYSCALL, value = syscall.ordinal) result += IRCodeChunk(null, null).also {
if (resultRegister != 0) it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = SyscallRegisterBase+1, value = array.length)
code += IRCodeInstruction(Opcode.LOADR, VmDataType.BYTE, reg1 = resultRegister, reg2 = 0) it += IRInstruction(Opcode.SYSCALL, value = syscall.number)
return code if(resultRegister!=0)
it += IRInstruction(Opcode.LOADR, IRDataType.BYTE, reg1 = resultRegister, reg2 = 0)
}
return result
} }
private fun funcAll(call: PtBuiltinFunctionCall, resultRegister: Int): IRCodeChunk { private fun funcAll(call: PtBuiltinFunctionCall, resultRegister: Int): IRCodeChunks {
val arrayName = call.args[0] as PtIdentifier val arrayName = call.args[0] as PtIdentifier
val array = codeGen.symbolTable.flat.getValue(arrayName.targetName) as StStaticVariable val array = codeGen.symbolTable.flat.getValue(arrayName.targetName) as StStaticVariable
val syscall = val syscall =
when(array.dt) { when(array.dt) {
DataType.ARRAY_UB, DataType.ARRAY_UB,
DataType.ARRAY_B -> Syscall.ALL_BYTE DataType.ARRAY_B -> IMSyscall.ALL_BYTE
DataType.ARRAY_UW, DataType.ARRAY_UW,
DataType.ARRAY_W -> Syscall.ALL_WORD DataType.ARRAY_W -> IMSyscall.ALL_WORD
DataType.ARRAY_F -> Syscall.ALL_FLOAT DataType.ARRAY_F -> IMSyscall.ALL_FLOAT
else -> throw IllegalArgumentException("weird type") else -> throw IllegalArgumentException("weird type")
} }
val code = IRCodeChunk(call.position) val result = mutableListOf<IRCodeChunkBase>()
code += exprGen.translateExpression(call.args[0], 0, -1) result += exprGen.translateExpression(call.args[0], SyscallRegisterBase, -1)
code += IRCodeInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=1, value=array.length) result += IRCodeChunk(null, null).also {
code += IRCodeInstruction(Opcode.SYSCALL, value=syscall.ordinal) it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = SyscallRegisterBase+1, value = array.length)
if(resultRegister!=0) it += IRInstruction(Opcode.SYSCALL, value = syscall.number)
code += IRCodeInstruction(Opcode.LOADR, VmDataType.BYTE, reg1=resultRegister, reg2=0) if(resultRegister!=0)
return code it += IRInstruction(Opcode.LOADR, IRDataType.BYTE, reg1 = resultRegister, reg2 = 0)
}
return result
} }
private fun funcAbs(call: PtBuiltinFunctionCall, resultRegister: Int): IRCodeChunk { private fun funcAbs(call: PtBuiltinFunctionCall, resultRegister: Int): IRCodeChunks {
val code = IRCodeChunk(call.position)
val sourceDt = call.args.single().type val sourceDt = call.args.single().type
val result = mutableListOf<IRCodeChunkBase>()
if(sourceDt!=DataType.UWORD) { if(sourceDt!=DataType.UWORD) {
code += exprGen.translateExpression(call.args[0], resultRegister, -1) result += exprGen.translateExpression(call.args[0], resultRegister, -1)
when (sourceDt) { when (sourceDt) {
DataType.UBYTE -> { DataType.UBYTE -> {
code += IRCodeInstruction(Opcode.EXT, VmDataType.BYTE, reg1=resultRegister) result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.EXT, IRDataType.BYTE, reg1 = resultRegister)
}
} }
DataType.BYTE -> { DataType.BYTE -> {
val notNegativeLabel = codeGen.createLabelName() val notNegativeLabel = codeGen.createLabelName()
val compareReg = codeGen.vmRegisters.nextFree() val compareReg = codeGen.registers.nextFree()
code += IRCodeInstruction(Opcode.LOADR, VmDataType.BYTE, reg1=compareReg, reg2=resultRegister) result += IRCodeChunk(null, null).also {
code += IRCodeInstruction(Opcode.AND, VmDataType.BYTE, reg1=compareReg, value=0x80) it += IRInstruction(Opcode.LOADR, IRDataType.BYTE, reg1=compareReg, reg2=resultRegister)
code += IRCodeInstruction(Opcode.BZ, VmDataType.BYTE, reg1=compareReg, labelSymbol = notNegativeLabel) it += IRInstruction(Opcode.AND, IRDataType.BYTE, reg1=compareReg, value=0x80)
code += IRCodeInstruction(Opcode.NEG, VmDataType.BYTE, reg1=resultRegister) it += IRInstruction(Opcode.BZ, IRDataType.BYTE, reg1=compareReg, labelSymbol = notNegativeLabel)
code += IRCodeInstruction(Opcode.EXT, VmDataType.BYTE, reg1=resultRegister) it += IRInstruction(Opcode.NEG, IRDataType.BYTE, reg1=resultRegister)
code += IRCodeLabel(notNegativeLabel) it += IRInstruction(Opcode.EXT, IRDataType.BYTE, reg1=resultRegister)
}
result += IRCodeChunk(notNegativeLabel, null)
} }
DataType.WORD -> { DataType.WORD -> {
val notNegativeLabel = codeGen.createLabelName() val notNegativeLabel = codeGen.createLabelName()
val compareReg = codeGen.vmRegisters.nextFree() val compareReg = codeGen.registers.nextFree()
code += IRCodeInstruction(Opcode.LOADR, VmDataType.WORD, reg1=compareReg, reg2=resultRegister) result += IRCodeChunk(null, null).also {
code += IRCodeInstruction(Opcode.AND, VmDataType.WORD, reg1=compareReg, value=0x8000) it += IRInstruction(Opcode.LOADR, IRDataType.WORD, reg1=compareReg, reg2=resultRegister)
code += IRCodeInstruction(Opcode.BZ, VmDataType.WORD, reg1=compareReg, labelSymbol = notNegativeLabel) it += IRInstruction(Opcode.AND, IRDataType.WORD, reg1=compareReg, value=0x8000)
code += IRCodeInstruction(Opcode.NEG, VmDataType.WORD, reg1=resultRegister) it += IRInstruction(Opcode.BZ, IRDataType.WORD, reg1=compareReg, labelSymbol = notNegativeLabel)
code += IRCodeLabel(notNegativeLabel) it += IRInstruction(Opcode.NEG, IRDataType.WORD, reg1=resultRegister)
}
result += IRCodeChunk(notNegativeLabel, null)
} }
else -> throw AssemblyError("weird type") else -> throw AssemblyError("weird type")
} }
} }
return code return result
} }
private fun funcSgn(call: PtBuiltinFunctionCall, resultRegister: Int): IRCodeChunk { private fun funcSgn(call: PtBuiltinFunctionCall, resultRegister: Int): IRCodeChunks {
val code = IRCodeChunk(call.position) val reg = codeGen.registers.nextFree()
val reg = codeGen.vmRegisters.nextFree() val result = mutableListOf<IRCodeChunkBase>()
code += exprGen.translateExpression(call.args.single(), reg, -1) result += exprGen.translateExpression(call.args.single(), reg, -1)
code += IRCodeInstruction(Opcode.SGN, codeGen.vmType(call.type), reg1=resultRegister, reg2=reg) result += IRCodeChunk(null, null).also {
return code it += IRInstruction(Opcode.SGN, codeGen.irType(call.type), reg1 = resultRegister, reg2 = reg)
}
return result
} }
private fun funcSqrt16(call: PtBuiltinFunctionCall, resultRegister: Int): IRCodeChunk { private fun funcSqrt16(call: PtBuiltinFunctionCall, resultRegister: Int): IRCodeChunks {
val code = IRCodeChunk(call.position) val reg = codeGen.registers.nextFree()
val reg = codeGen.vmRegisters.nextFree() val result = mutableListOf<IRCodeChunkBase>()
code += exprGen.translateExpression(call.args.single(), reg, -1) result += exprGen.translateExpression(call.args.single(), reg, -1)
code += IRCodeInstruction(Opcode.SQRT, VmDataType.WORD, reg1=resultRegister, reg2=reg) result += IRCodeChunk(null, null).also {
return code it += IRInstruction(Opcode.SQRT, IRDataType.WORD, reg1=resultRegister, reg2=reg)
}
return result
} }
private fun funcPop(call: PtBuiltinFunctionCall): IRCodeChunk { private fun funcPop(call: PtBuiltinFunctionCall): IRCodeChunks {
val code = IRCodeChunk(call.position) val code = IRCodeChunk(null, null)
val reg = codeGen.vmRegisters.nextFree() val reg = codeGen.registers.nextFree()
code += IRCodeInstruction(Opcode.POP, VmDataType.BYTE, reg1=reg) code += IRInstruction(Opcode.POP, IRDataType.BYTE, reg1=reg)
code += assignRegisterTo(call.args.single(), reg) val result = mutableListOf<IRCodeChunkBase>(code)
return code result += assignRegisterTo(call.args.single(), reg)
return result
} }
private fun funcPopw(call: PtBuiltinFunctionCall): IRCodeChunk { private fun funcPopw(call: PtBuiltinFunctionCall): IRCodeChunks {
val code = IRCodeChunk(call.position) val code = IRCodeChunk(null, null)
val reg = codeGen.vmRegisters.nextFree() val reg = codeGen.registers.nextFree()
code += IRCodeInstruction(Opcode.POP, VmDataType.WORD, reg1=reg) code += IRInstruction(Opcode.POP, IRDataType.WORD, reg1=reg)
code += assignRegisterTo(call.args.single(), reg) val result = mutableListOf<IRCodeChunkBase>(code)
return code result += assignRegisterTo(call.args.single(), reg)
return result
} }
private fun funcPush(call: PtBuiltinFunctionCall): IRCodeChunk { private fun funcPush(call: PtBuiltinFunctionCall): IRCodeChunks {
val code = IRCodeChunk(call.position) val result = mutableListOf<IRCodeChunkBase>()
val reg = codeGen.vmRegisters.nextFree() val reg = codeGen.registers.nextFree()
code += exprGen.translateExpression(call.args.single(), reg, -1) result += exprGen.translateExpression(call.args.single(), reg, -1)
code += IRCodeInstruction(Opcode.PUSH, VmDataType.BYTE, reg1=reg) result += IRCodeChunk(null, null).also {
return code it += IRInstruction(Opcode.PUSH, IRDataType.BYTE, reg1=reg)
}
return result
} }
private fun funcPushw(call: PtBuiltinFunctionCall): IRCodeChunk { private fun funcPushw(call: PtBuiltinFunctionCall): IRCodeChunks {
val code = IRCodeChunk(call.position) val result = mutableListOf<IRCodeChunkBase>()
val reg = codeGen.vmRegisters.nextFree() val reg = codeGen.registers.nextFree()
code += exprGen.translateExpression(call.args.single(), reg, -1) result += exprGen.translateExpression(call.args.single(), reg, -1)
code += IRCodeInstruction(Opcode.PUSH, VmDataType.WORD, reg1=reg) result += IRCodeChunk(null, null).also {
return code it += IRInstruction(Opcode.PUSH, IRDataType.WORD, reg1 = reg)
}
return result
} }
private fun funcReverse(call: PtBuiltinFunctionCall): IRCodeChunk { private fun funcReverse(call: PtBuiltinFunctionCall): IRCodeChunks {
val arrayName = call.args[0] as PtIdentifier val arrayName = call.args[0] as PtIdentifier
val array = codeGen.symbolTable.flat.getValue(arrayName.targetName) as StStaticVariable val array = codeGen.symbolTable.flat.getValue(arrayName.targetName) as StStaticVariable
val sortSyscall = val syscall =
when(array.dt) { when(array.dt) {
DataType.ARRAY_UB, DataType.ARRAY_B, DataType.STR -> Syscall.REVERSE_BYTES DataType.ARRAY_UB, DataType.ARRAY_B, DataType.STR -> IMSyscall.REVERSE_BYTES
DataType.ARRAY_UW, DataType.ARRAY_W -> Syscall.REVERSE_WORDS DataType.ARRAY_UW, DataType.ARRAY_W -> IMSyscall.REVERSE_WORDS
DataType.ARRAY_F -> Syscall.REVERSE_FLOATS DataType.ARRAY_F -> IMSyscall.REVERSE_FLOATS
else -> throw IllegalArgumentException("weird type to reverse") else -> throw IllegalArgumentException("weird type to reverse")
} }
val code = IRCodeChunk(call.position) val result = mutableListOf<IRCodeChunkBase>()
code += exprGen.translateExpression(call.args[0], 0, -1) result += exprGen.translateExpression(call.args[0], SyscallRegisterBase, -1)
code += IRCodeInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=1, value=array.length) result += IRCodeChunk(null, null).also {
code += IRCodeInstruction(Opcode.SYSCALL, value=sortSyscall.ordinal) it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = SyscallRegisterBase+1, value = array.length)
return code it += IRInstruction(Opcode.SYSCALL, value = syscall.number)
}
return result
} }
private fun funcSort(call: PtBuiltinFunctionCall): IRCodeChunk { private fun funcSort(call: PtBuiltinFunctionCall): IRCodeChunks {
val arrayName = call.args[0] as PtIdentifier val arrayName = call.args[0] as PtIdentifier
val array = codeGen.symbolTable.flat.getValue(arrayName.targetName) as StStaticVariable val array = codeGen.symbolTable.flat.getValue(arrayName.targetName) as StStaticVariable
val sortSyscall = val syscall =
when(array.dt) { when(array.dt) {
DataType.ARRAY_UB -> Syscall.SORT_UBYTE DataType.ARRAY_UB -> IMSyscall.SORT_UBYTE
DataType.ARRAY_B -> Syscall.SORT_BYTE DataType.ARRAY_B -> IMSyscall.SORT_BYTE
DataType.ARRAY_UW -> Syscall.SORT_UWORD DataType.ARRAY_UW -> IMSyscall.SORT_UWORD
DataType.ARRAY_W -> Syscall.SORT_WORD DataType.ARRAY_W -> IMSyscall.SORT_WORD
DataType.STR -> Syscall.SORT_UBYTE DataType.STR -> IMSyscall.SORT_UBYTE
DataType.ARRAY_F -> throw IllegalArgumentException("sorting a floating point array is not supported") DataType.ARRAY_F -> throw IllegalArgumentException("sorting a floating point array is not supported")
else -> throw IllegalArgumentException("weird type to sort") else -> throw IllegalArgumentException("weird type to sort")
} }
val code = IRCodeChunk(call.position) val result = mutableListOf<IRCodeChunkBase>()
code += exprGen.translateExpression(call.args[0], 0, -1) result += exprGen.translateExpression(call.args[0], SyscallRegisterBase, -1)
code += IRCodeInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=1, value=array.length) result += IRCodeChunk(null, null).also {
code += IRCodeInstruction(Opcode.SYSCALL, value=sortSyscall.ordinal) it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = SyscallRegisterBase+1, value = array.length)
return code it += IRInstruction(Opcode.SYSCALL, value = syscall.number)
}
return result
} }
private fun funcMkword(call: PtBuiltinFunctionCall, resultRegister: Int): IRCodeChunk { private fun funcMkword(call: PtBuiltinFunctionCall, resultRegister: Int): IRCodeChunks {
val msbReg = codeGen.vmRegisters.nextFree() val msbReg = codeGen.registers.nextFree()
val code = IRCodeChunk(call.position) val result = mutableListOf<IRCodeChunkBase>()
code += exprGen.translateExpression(call.args[0], msbReg, -1) result += exprGen.translateExpression(call.args[0], msbReg, -1)
code += exprGen.translateExpression(call.args[1], resultRegister, -1) result += exprGen.translateExpression(call.args[1], resultRegister, -1)
code += IRCodeInstruction(Opcode.CONCAT, VmDataType.BYTE, reg1=resultRegister, reg2=msbReg) result += IRCodeChunk(null, null).also {
return code it += IRInstruction(Opcode.CONCAT, IRDataType.BYTE, reg1 = resultRegister, reg2 = msbReg)
}
return result
} }
private fun funcPokeW(call: PtBuiltinFunctionCall): IRCodeChunk { private fun funcPokeW(call: PtBuiltinFunctionCall): IRCodeChunks {
val code = IRCodeChunk(call.position) val result = mutableListOf<IRCodeChunkBase>()
if(codeGen.isZero(call.args[1])) { if(codeGen.isZero(call.args[1])) {
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()
code += IRCodeInstruction(Opcode.STOREZM, VmDataType.WORD, value = address) result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.STOREZM, IRDataType.WORD, value = address)
}
} else { } else {
val addressReg = codeGen.vmRegisters.nextFree() val addressReg = codeGen.registers.nextFree()
code += exprGen.translateExpression(call.args[0], addressReg, -1) result += exprGen.translateExpression(call.args[0], addressReg, -1)
code += IRCodeInstruction(Opcode.STOREZI, VmDataType.WORD, reg2 = addressReg) result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.STOREZI, IRDataType.WORD, reg2 = addressReg)
}
} }
} else { } else {
val valueReg = codeGen.vmRegisters.nextFree() val valueReg = codeGen.registers.nextFree()
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()
code += exprGen.translateExpression(call.args[1], valueReg, -1) result += exprGen.translateExpression(call.args[1], valueReg, -1)
code += IRCodeInstruction(Opcode.STOREM, VmDataType.WORD, reg1 = valueReg, value = address) result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.STOREM, IRDataType.WORD, reg1 = valueReg, value = address)
}
} else { } else {
val addressReg = codeGen.vmRegisters.nextFree() val addressReg = codeGen.registers.nextFree()
code += exprGen.translateExpression(call.args[0], addressReg, -1) result += exprGen.translateExpression(call.args[0], addressReg, -1)
code += exprGen.translateExpression(call.args[1], valueReg, -1) result += exprGen.translateExpression(call.args[1], valueReg, -1)
code += IRCodeInstruction(Opcode.STOREI, VmDataType.WORD, reg1 = valueReg, reg2 = addressReg) result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.STOREI, IRDataType.WORD, reg1 = valueReg, reg2 = addressReg)
}
} }
} }
return code return result
} }
private fun funcPoke(call: PtBuiltinFunctionCall): IRCodeChunk { private fun funcPoke(call: PtBuiltinFunctionCall): IRCodeChunks {
val code = IRCodeChunk(call.position) val result = mutableListOf<IRCodeChunkBase>()
if(codeGen.isZero(call.args[1])) { if(codeGen.isZero(call.args[1])) {
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()
code += IRCodeInstruction(Opcode.STOREZM, VmDataType.BYTE, value = address) result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.STOREZM, IRDataType.BYTE, value = address)
}
} else { } else {
val addressReg = codeGen.vmRegisters.nextFree() val addressReg = codeGen.registers.nextFree()
code += exprGen.translateExpression(call.args[0], addressReg, -1) result += exprGen.translateExpression(call.args[0], addressReg, -1)
code += IRCodeInstruction(Opcode.STOREZI, VmDataType.BYTE, reg2 = addressReg) result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.STOREZI, IRDataType.BYTE, reg2 = addressReg)
}
} }
} else { } else {
val valueReg = codeGen.vmRegisters.nextFree() val valueReg = codeGen.registers.nextFree()
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()
code += exprGen.translateExpression(call.args[1], valueReg, -1) result += exprGen.translateExpression(call.args[1], valueReg, -1)
code += IRCodeInstruction(Opcode.STOREM, VmDataType.BYTE, reg1 = valueReg, value = address) result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.STOREM, IRDataType.BYTE, reg1 = valueReg, value = address)
}
} else { } else {
val addressReg = codeGen.vmRegisters.nextFree() val addressReg = codeGen.registers.nextFree()
code += exprGen.translateExpression(call.args[0], addressReg, -1) result += exprGen.translateExpression(call.args[0], addressReg, -1)
code += exprGen.translateExpression(call.args[1], valueReg, -1) result += exprGen.translateExpression(call.args[1], valueReg, -1)
code += IRCodeInstruction(Opcode.STOREI, VmDataType.BYTE, reg1 = valueReg, reg2 = addressReg) result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.STOREI, IRDataType.BYTE, reg1 = valueReg, reg2 = addressReg)
}
} }
} }
return code return result
} }
private fun funcPeekW(call: PtBuiltinFunctionCall, resultRegister: Int): IRCodeChunk { private fun funcPeekW(call: PtBuiltinFunctionCall, resultRegister: Int): IRCodeChunks {
val code = IRCodeChunk(call.position) val result = mutableListOf<IRCodeChunkBase>()
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()
code += IRCodeInstruction(Opcode.LOADM, VmDataType.WORD, reg1 = resultRegister, value = address) result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOADM, IRDataType.WORD, reg1 = resultRegister, value = address)
}
} else { } else {
val addressReg = codeGen.vmRegisters.nextFree() val addressReg = codeGen.registers.nextFree()
code += exprGen.translateExpression(call.args.single(), addressReg, -1) result += exprGen.translateExpression(call.args.single(), addressReg, -1)
code += IRCodeInstruction(Opcode.LOADI, VmDataType.WORD, reg1 = resultRegister, reg2 = addressReg) result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOADI, IRDataType.WORD, reg1 = resultRegister, reg2 = addressReg)
}
} }
return code return result
} }
private fun funcPeek(call: PtBuiltinFunctionCall, resultRegister: Int): IRCodeChunk { private fun funcPeek(call: PtBuiltinFunctionCall, resultRegister: Int): IRCodeChunks {
val code = IRCodeChunk(call.position) val result = mutableListOf<IRCodeChunkBase>()
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()
code += IRCodeInstruction(Opcode.LOADM, VmDataType.BYTE, reg1 = resultRegister, value = address) result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOADM, IRDataType.BYTE, reg1 = resultRegister, value = address)
}
} else { } else {
val addressReg = codeGen.vmRegisters.nextFree() val addressReg = codeGen.registers.nextFree()
code += exprGen.translateExpression(call.args.single(), addressReg, -1) result += exprGen.translateExpression(call.args.single(), addressReg, -1)
code += IRCodeInstruction(Opcode.LOADI, VmDataType.BYTE, reg1 = resultRegister, reg2 = addressReg) result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOADI, IRDataType.BYTE, reg1 = resultRegister, reg2 = addressReg)
}
} }
return code return result
} }
private fun funcRnd(resultRegister: Int, position: Position): IRCodeChunk { private fun funcMemory(call: PtBuiltinFunctionCall, resultRegister: Int): IRCodeChunks {
val code = IRCodeChunk(position)
code += IRCodeInstruction(Opcode.RND, VmDataType.BYTE, reg1=resultRegister)
return code
}
private fun funcRndw(resultRegister: Int, position: Position): IRCodeChunk {
val code = IRCodeChunk(position)
code += IRCodeInstruction(Opcode.RND, VmDataType.WORD, reg1=resultRegister)
return code
}
private fun funcMemory(call: PtBuiltinFunctionCall, resultRegister: Int): IRCodeChunk {
val name = (call.args[0] as PtString).value val name = (call.args[0] as PtString).value
val size = (call.args[1] as PtNumber).number.toUInt() val code = IRCodeChunk(null, null)
val align = (call.args[2] as PtNumber).number.toUInt() code += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=resultRegister, labelSymbol = "prog8_slabs.prog8_memoryslab_$name")
val label = codeGen.addMemorySlab(name, size, align, call.position) return listOf(code)
val code = IRCodeChunk(call.position)
code += IRCodeInstruction(Opcode.LOAD, VmDataType.WORD, reg1=resultRegister, labelSymbol = label)
return code
} }
private fun funcLsb(call: PtBuiltinFunctionCall, resultRegister: Int): IRCodeChunk { private fun funcLsb(call: PtBuiltinFunctionCall, resultRegister: Int): IRCodeChunks {
val code = IRCodeChunk(call.position) return exprGen.translateExpression(call.args.single(), resultRegister, -1)
code += exprGen.translateExpression(call.args.single(), resultRegister, -1)
// note: if a word result is needed, the upper byte is cleared by the typecast that follows. No need to do it here. // note: if a word result is needed, the upper byte is cleared by the typecast that follows. No need to do it here.
return code
} }
private fun funcMsb(call: PtBuiltinFunctionCall, resultRegister: Int): IRCodeChunk { private fun funcMsb(call: PtBuiltinFunctionCall, resultRegister: Int): IRCodeChunks {
val code = IRCodeChunk(call.position) val result = mutableListOf<IRCodeChunkBase>()
code += exprGen.translateExpression(call.args.single(), resultRegister, -1) result += exprGen.translateExpression(call.args.single(), resultRegister, -1)
code += IRCodeInstruction(Opcode.MSIG, VmDataType.BYTE, reg1 = resultRegister, reg2=resultRegister) result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.MSIG, IRDataType.BYTE, reg1 = resultRegister, reg2 = resultRegister)
}
// note: if a word result is needed, the upper byte is cleared by the typecast that follows. No need to do it here. // note: if a word result is needed, the upper byte is cleared by the typecast that follows. No need to do it here.
return code return result
} }
private fun funcRolRor(opcode: Opcode, call: PtBuiltinFunctionCall, resultRegister: Int): IRCodeChunk { private fun funcRolRor(opcode: Opcode, call: PtBuiltinFunctionCall, resultRegister: Int): IRCodeChunks {
val vmDt = codeGen.vmType(call.args[0].type) val vmDt = codeGen.irType(call.args[0].type)
val code = IRCodeChunk(call.position) val result = mutableListOf<IRCodeChunkBase>()
code += exprGen.translateExpression(call.args[0], resultRegister, -1) result += exprGen.translateExpression(call.args[0], resultRegister, -1)
code += IRCodeInstruction(opcode, vmDt, reg1=resultRegister) result += IRCodeChunk(null, null).also {
code += assignRegisterTo(call.args[0], resultRegister) it += IRInstruction(opcode, vmDt, reg1 = resultRegister)
return code }
result += assignRegisterTo(call.args[0], resultRegister)
return result
} }
private fun assignRegisterTo(target: PtExpression, register: Int): IRCodeChunk { private fun assignRegisterTo(target: PtExpression, register: Int): IRCodeChunks {
val code = IRCodeChunk(target.position)
val assignment = PtAssignment(target.position) val assignment = PtAssignment(target.position)
val assignTarget = PtAssignTarget(target.position) val assignTarget = PtAssignTarget(target.position)
assignTarget.children.add(target) assignTarget.children.add(target)
assignment.children.add(assignTarget) assignment.children.add(assignTarget)
assignment.children.add(PtMachineRegister(register, target.type, target.position)) assignment.children.add(PtMachineRegister(register, target.type, target.position))
code += codeGen.translateNode(assignment) val result = mutableListOf<IRCodeChunkBase>()
return code result += codeGen.translateNode(assignment)
return result
} }
} }

View File

@ -5,41 +5,128 @@ import prog8.intermediate.*
internal class IRPeepholeOptimizer(private val irprog: IRProgram) { internal class IRPeepholeOptimizer(private val irprog: IRProgram) {
fun optimize() { fun optimize() {
irprog.blocks.asSequence().flatMap { it.subroutines }.forEach { sub -> irprog.blocks.asSequence().flatMap { it.subroutines }.forEach { sub ->
sub.chunks.forEach { chunk -> removeEmptyChunks(sub)
joinChunks(sub)
sub.chunks.withIndex().forEach { (index, chunk1) ->
// we don't optimize Inline Asm chunks here. // we don't optimize Inline Asm chunks here.
if(chunk is IRCodeChunk) { val chunk2 = if(index<sub.chunks.size-1) sub.chunks[index+1] else null
if(chunk1 is IRCodeChunk) {
do { do {
val indexedInstructions = chunk.lines.withIndex() val indexedInstructions = chunk1.instructions.withIndex()
.filter { it.value is IRCodeInstruction } .map { IndexedValue(it.index, it.value) }
.map { IndexedValue(it.index, (it.value as IRCodeInstruction).ins) } val changed = removeNops(chunk1, indexedInstructions)
val changed = removeNops(chunk, indexedInstructions) || removeDoubleLoadsAndStores(chunk1, indexedInstructions) // TODO not yet implemented
|| removeDoubleLoadsAndStores(chunk, indexedInstructions) // TODO not yet implemented || removeUselessArithmetic(chunk1, indexedInstructions)
|| removeUselessArithmetic(chunk, indexedInstructions) || removeWeirdBranches(chunk1, chunk2, indexedInstructions)
|| removeWeirdBranches(chunk, indexedInstructions) || removeDoubleSecClc(chunk1, indexedInstructions)
|| removeDoubleSecClc(chunk, indexedInstructions) || cleanupPushPop(chunk1, indexedInstructions)
|| cleanupPushPop(chunk, indexedInstructions)
// TODO other optimizations: // TODO other optimizations:
// more complex optimizations such as unused registers // more complex optimizations such as unused registers
} while (changed) } while (changed)
} }
} }
removeEmptyChunks(sub)
} }
irprog.linkChunks() // re-link
} }
private fun cleanupPushPop(chunk: IRCodeChunk, indexedInstructions: List<IndexedValue<Instruction>>): Boolean { private fun removeEmptyChunks(sub: IRSubroutine) {
if(sub.chunks.isEmpty())
return
/*
Empty Code chunk with label ->
If next chunk has no label -> move label to next chunk, remove original
If next chunk has label -> label name should be the same, remove original. Otherwise FOR NOW leave it in place. (TODO: consolidate labels into 1)
If is last chunk -> keep chunk in place because of the label.
Empty Code chunk without label ->
should not have been generated! ERROR.
*/
val relabelChunks = mutableListOf<Pair<Int, String>>()
val removeChunks = mutableListOf<Int>()
sub.chunks.withIndex().forEach { (index, chunk) ->
if(chunk is IRCodeChunk && chunk.instructions.isEmpty()) {
if(chunk.label==null) {
removeChunks += index
} else {
if (index < sub.chunks.size - 1) {
val nextchunk = sub.chunks[index + 1]
if (nextchunk.label == null) {
// can transplant label to next chunk and remove this empty one.
relabelChunks += Pair(index + 1, chunk.label!!)
removeChunks += index
} else {
if (chunk.label == nextchunk.label)
removeChunks += index
else {
// TODO: consolidate labels on same chunk
}
}
}
}
}
}
relabelChunks.forEach { (index, label) ->
val chunk = IRCodeChunk(label, null)
chunk.instructions += sub.chunks[index].instructions
sub.chunks[index] = chunk
}
removeChunks.reversed().forEach { index -> sub.chunks.removeAt(index) }
}
private fun joinChunks(sub: IRSubroutine) {
// Subroutine contains a list of chunks. Some can be joined into one.
if(sub.chunks.isEmpty())
return
fun mayJoin(previous: IRCodeChunkBase, chunk: IRCodeChunkBase): Boolean {
if(chunk.label!=null)
return false
if(previous is IRCodeChunk && chunk is IRCodeChunk) {
// if the previous chunk doesn't end in a jump or a return, flow continues into the next chunk
val lastInstruction = previous.instructions.lastOrNull()
if(lastInstruction!=null)
return lastInstruction.opcode !in OpcodesThatJump
return true
}
return false
}
val chunks = mutableListOf<IRCodeChunkBase>()
chunks += sub.chunks[0]
for(ix in 1 until sub.chunks.size) {
val lastChunk = chunks.last()
if(mayJoin(lastChunk, sub.chunks[ix])) {
lastChunk.instructions += sub.chunks[ix].instructions
lastChunk.next = sub.chunks[ix].next
}
else
chunks += sub.chunks[ix]
}
sub.chunks.clear()
sub.chunks += chunks
}
private fun cleanupPushPop(chunk: IRCodeChunk, indexedInstructions: List<IndexedValue<IRInstruction>>): Boolean {
// push followed by pop to same target, or different target->replace with load // push followed by pop to same target, or different target->replace with load
var changed = false var changed = false
indexedInstructions.reversed().forEach { (idx, ins) -> indexedInstructions.reversed().forEach { (idx, ins) ->
if(ins.opcode== Opcode.PUSH) { if(ins.opcode== Opcode.PUSH) {
if(idx < chunk.lines.size-1) { if(idx < chunk.instructions.size-1) {
val insAfter = chunk.lines[idx+1] as? IRCodeInstruction val insAfter = chunk.instructions[idx+1] as? IRInstruction
if(insAfter!=null && insAfter.ins.opcode == Opcode.POP) { if(insAfter!=null && insAfter.opcode == Opcode.POP) {
if(ins.reg1==insAfter.ins.reg1) { if(ins.reg1==insAfter.reg1) {
chunk.lines.removeAt(idx) chunk.instructions.removeAt(idx)
chunk.lines.removeAt(idx) chunk.instructions.removeAt(idx)
} else { } else {
chunk.lines[idx] = IRCodeInstruction(Opcode.LOADR, ins.type, reg1=insAfter.ins.reg1, reg2=ins.reg1) chunk.instructions[idx] = IRInstruction(Opcode.LOADR, ins.type, reg1=insAfter.reg1, reg2=ins.reg1)
chunk.lines.removeAt(idx+1) chunk.instructions.removeAt(idx+1)
} }
changed = true changed = true
} }
@ -49,24 +136,24 @@ internal class IRPeepholeOptimizer(private val irprog: IRProgram) {
return changed return changed
} }
private fun removeDoubleSecClc(chunk: IRCodeChunk, indexedInstructions: List<IndexedValue<Instruction>>): Boolean { private fun removeDoubleSecClc(chunk: IRCodeChunk, indexedInstructions: List<IndexedValue<IRInstruction>>): Boolean {
// double sec, clc // double sec, clc
// sec+clc or clc+sec // sec+clc or clc+sec
var changed = false var changed = false
indexedInstructions.reversed().forEach { (idx, ins) -> indexedInstructions.reversed().forEach { (idx, ins) ->
if(ins.opcode== Opcode.SEC || ins.opcode== Opcode.CLC) { if(ins.opcode== Opcode.SEC || ins.opcode== Opcode.CLC) {
if(idx < chunk.lines.size-1) { if(idx < chunk.instructions.size-1) {
val insAfter = chunk.lines[idx+1] as? IRCodeInstruction val insAfter = chunk.instructions[idx+1] as? IRInstruction
if(insAfter?.ins?.opcode == ins.opcode) { if(insAfter?.opcode == ins.opcode) {
chunk.lines.removeAt(idx) chunk.instructions.removeAt(idx)
changed = true changed = true
} }
else if(ins.opcode== Opcode.SEC && insAfter?.ins?.opcode== Opcode.CLC) { else if(ins.opcode== Opcode.SEC && insAfter?.opcode== Opcode.CLC) {
chunk.lines.removeAt(idx) chunk.instructions.removeAt(idx)
changed = true changed = true
} }
else if(ins.opcode== Opcode.CLC && insAfter?.ins?.opcode== Opcode.SEC) { else if(ins.opcode== Opcode.CLC && insAfter?.opcode== Opcode.SEC) {
chunk.lines.removeAt(idx) chunk.instructions.removeAt(idx)
changed = true changed = true
} }
} }
@ -75,73 +162,78 @@ internal class IRPeepholeOptimizer(private val irprog: IRProgram) {
return changed return changed
} }
private fun removeWeirdBranches(chunk: IRCodeChunk, indexedInstructions: List<IndexedValue<Instruction>>): Boolean { private fun removeWeirdBranches(chunk: IRCodeChunk, nextChunk: IRCodeChunkBase?, indexedInstructions: List<IndexedValue<IRInstruction>>): Boolean {
// jump/branch to label immediately below
var changed = false var changed = false
indexedInstructions.reversed().forEach { (idx, ins) -> indexedInstructions.reversed().forEach { (idx, ins) ->
val labelSymbol = ins.labelSymbol val labelSymbol = ins.labelSymbol
// remove jump/branch to label immediately below (= next chunk if it has that label)
if(ins.opcode== Opcode.JUMP && labelSymbol!=null) { if(ins.opcode== Opcode.JUMP && labelSymbol!=null) {
// if jumping to label immediately following this if(idx==chunk.instructions.size-1 && ins.branchTarget===nextChunk) {
if(idx < chunk.lines.size-1) { chunk.instructions.removeAt(idx)
val label = chunk.lines[idx+1] as? IRCodeLabel changed = true
if(labelSymbol.size==1 && label?.name == labelSymbol[0]) { }
chunk.lines.removeAt(idx) }
changed = true // remove useless RETURN
} if(ins.opcode == Opcode.RETURN && idx>0) {
val previous = chunk.instructions[idx-1] as? IRInstruction
if(previous?.opcode in OpcodesThatJump) {
chunk.instructions.removeAt(idx)
changed = true
} }
} }
} }
return changed return changed
} }
private fun removeUselessArithmetic(chunk: IRCodeChunk, indexedInstructions: List<IndexedValue<Instruction>>): Boolean { private fun removeUselessArithmetic(chunk: IRCodeChunk, indexedInstructions: List<IndexedValue<IRInstruction>>): Boolean {
// note: this is hard to solve for the non-immediate instructions atm because the values are loaded into registers first // note: this is hard to solve for the non-immediate instructions atm because the values are loaded into registers first
var changed = false var changed = false
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.value == 1) {
chunk.lines.removeAt(idx) chunk.instructions.removeAt(idx)
changed = true changed = true
} }
} }
Opcode.ADD, Opcode.SUB -> { Opcode.ADD, Opcode.SUB -> {
if (ins.value == 1) { if (ins.value == 1) {
chunk.lines[idx] = IRCodeInstruction( 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.value == 0) {
chunk.lines.removeAt(idx) chunk.instructions.removeAt(idx)
changed = true changed = true
} }
} }
Opcode.AND -> { Opcode.AND -> {
if (ins.value == 0) { if (ins.value == 0) {
chunk.lines[idx] = IRCodeInstruction(Opcode.LOAD, ins.type, reg1 = ins.reg1, value = 0) chunk.instructions[idx] = IRInstruction(Opcode.LOAD, ins.type, reg1 = ins.reg1, value = 0)
changed = true changed = true
} else if (ins.value == 255 && ins.type == VmDataType.BYTE) { } else if (ins.value == 255 && ins.type == IRDataType.BYTE) {
chunk.lines.removeAt(idx) chunk.instructions.removeAt(idx)
changed = true changed = true
} else if (ins.value == 65535 && ins.type == VmDataType.WORD) { } else if (ins.value == 65535 && ins.type == IRDataType.WORD) {
chunk.lines.removeAt(idx) chunk.instructions.removeAt(idx)
changed = true changed = true
} }
} }
Opcode.OR -> { Opcode.OR -> {
if (ins.value == 0) { if (ins.value == 0) {
chunk.lines.removeAt(idx) chunk.instructions.removeAt(idx)
changed = true changed = true
} else if ((ins.value == 255 && ins.type == VmDataType.BYTE) || (ins.value == 65535 && ins.type == VmDataType.WORD)) { } else if ((ins.value == 255 && ins.type == IRDataType.BYTE) || (ins.value == 65535 && ins.type == IRDataType.WORD)) {
chunk.lines[idx] = IRCodeInstruction(Opcode.LOAD, ins.type, reg1 = ins.reg1, value = ins.value) chunk.instructions[idx] = IRInstruction(Opcode.LOAD, ins.type, reg1 = ins.reg1, value = ins.value)
changed = true changed = true
} }
} }
Opcode.XOR -> { Opcode.XOR -> {
if (ins.value == 0) { if (ins.value == 0) {
chunk.lines.removeAt(idx) chunk.instructions.removeAt(idx)
changed = true changed = true
} }
} }
@ -151,18 +243,18 @@ internal class IRPeepholeOptimizer(private val irprog: IRProgram) {
return changed return changed
} }
private fun removeNops(chunk: IRCodeChunk, indexedInstructions: List<IndexedValue<Instruction>>): Boolean { private fun removeNops(chunk: IRCodeChunk, indexedInstructions: List<IndexedValue<IRInstruction>>): Boolean {
var changed = false var changed = false
indexedInstructions.reversed().forEach { (idx, ins) -> indexedInstructions.reversed().forEach { (idx, ins) ->
if (ins.opcode == Opcode.NOP) { if (ins.opcode == Opcode.NOP) {
changed = true changed = true
chunk.lines.removeAt(idx) chunk.instructions.removeAt(idx)
} }
} }
return changed return changed
} }
private fun removeDoubleLoadsAndStores(chunk: IRCodeChunk, indexedInstructions: List<IndexedValue<Instruction>>): Boolean { private fun removeDoubleLoadsAndStores(chunk: IRCodeChunk, indexedInstructions: List<IndexedValue<IRInstruction>>): Boolean {
var changed = false var changed = false
indexedInstructions.forEach { (idx, ins) -> indexedInstructions.forEach { (idx, ins) ->

View File

@ -0,0 +1,103 @@
package prog8.codegen.intermediate
import prog8.code.core.IErrorReporter
import prog8.code.core.SourceCode.Companion.libraryFilePrefix
import prog8.intermediate.*
internal class IRUnusedCodeRemover(private val irprog: IRProgram, private val errors: IErrorReporter) {
fun optimize(): Int {
var numRemoved = removeSimpleUnlinked() + removeUnreachable()
// remove empty subs
irprog.blocks.forEach { block ->
block.subroutines.reversed().forEach { sub ->
if(sub.isEmpty()) {
if(!sub.position.file.startsWith(libraryFilePrefix))
errors.warn("unused subroutine ${sub.name}", sub.position)
block.subroutines.remove(sub)
numRemoved++
}
}
}
// remove empty blocks
irprog.blocks.reversed().forEach { block ->
if(block.isEmpty()) {
irprog.blocks.remove(block)
numRemoved++
}
}
return numRemoved
}
private fun removeUnreachable(): Int {
val reachable = mutableSetOf(irprog.blocks.single { it.name=="main" }.subroutines.single { it.name=="main.start" }.chunks.first())
fun grow() {
val new = mutableSetOf<IRCodeChunkBase>()
reachable.forEach {
it.next?.let { next -> new += next }
it.instructions.forEach { instr -> instr.branchTarget?.let { target -> new += target} }
}
reachable += new
}
var previousCount = reachable.size
while(true) {
grow()
if(reachable.size<=previousCount)
break
previousCount = reachable.size
}
return removeUnlinkedChunks(reachable)
}
private fun removeSimpleUnlinked(): Int {
val linkedChunks = mutableSetOf<IRCodeChunkBase>()
irprog.blocks.asSequence().flatMap { it.subroutines }.forEach { sub ->
sub.chunks.forEach { chunk ->
chunk.next?.let { next -> linkedChunks += next }
chunk.instructions.forEach { it.branchTarget?.let { target -> linkedChunks += target } }
if (chunk.label == "main.start")
linkedChunks += chunk
}
}
return removeUnlinkedChunks(linkedChunks)
}
private fun removeUnlinkedChunks(
linkedChunks: MutableSet<IRCodeChunkBase>
): Int {
var numRemoved = 0
irprog.blocks.asSequence().flatMap { it.subroutines }.forEach { sub ->
sub.chunks.withIndex().reversed().forEach { (index, chunk) ->
if (chunk !in linkedChunks) {
if (chunk === sub.chunks[0]) {
when(chunk) {
is IRCodeChunk -> {
if (chunk.isNotEmpty()) {
// don't remove the first chunk of the sub itself because it has to have the name of the sub as label
chunk.instructions.clear()
numRemoved++
}
}
is IRInlineAsmChunk, is IRInlineBinaryChunk -> {
sub.chunks[index] = IRCodeChunk(chunk.label, chunk.next)
numRemoved++
}
}
} else {
sub.chunks.removeAt(index)
numRemoved++
}
}
}
}
return numRemoved
}
}

View File

@ -1,10 +1,12 @@
package prog8.codegen.intermediate package prog8.codegen.intermediate
import prog8.code.core.AssemblyError import prog8.code.core.AssemblyError
import prog8.intermediate.SyscallRegisterBase
internal class RegisterPool { internal class RegisterPool {
private var firstFree: Int=3 // integer registers 0,1,2 are reserved // reserve 0,1,2 for return values of subroutine calls and syscalls
private var firstFreeFloat: Int=0 private var firstFree: Int=3
private var firstFreeFloat: Int=3
fun peekNext() = firstFree fun peekNext() = firstFree
fun peekNextFloat() = firstFreeFloat fun peekNextFloat() = firstFreeFloat
@ -12,7 +14,7 @@ internal class RegisterPool {
fun nextFree(): Int { fun nextFree(): Int {
val result = firstFree val result = firstFree
firstFree++ firstFree++
if(firstFree>65535) if(firstFree >= SyscallRegisterBase)
throw AssemblyError("out of virtual registers (int)") throw AssemblyError("out of virtual registers (int)")
return result return result
} }
@ -20,7 +22,7 @@ internal class RegisterPool {
fun nextFreeFloat(): Int { fun nextFreeFloat(): Int {
val result = firstFreeFloat val result = firstFreeFloat
firstFreeFloat++ firstFreeFloat++
if(firstFreeFloat>65535) if(firstFreeFloat >= SyscallRegisterBase)
throw AssemblyError("out of virtual registers (fp)") throw AssemblyError("out of virtual registers (fp)")
return result return result
} }

View File

@ -1,4 +1,4 @@
package prog8.codegen.virtual package prog8.codegen.vm
import prog8.code.SymbolTable import prog8.code.SymbolTable
import prog8.code.ast.PtProgram import prog8.code.ast.PtProgram
@ -7,9 +7,8 @@ import prog8.code.core.IAssemblyGenerator
import prog8.code.core.IAssemblyProgram import prog8.code.core.IAssemblyProgram
import prog8.code.core.IErrorReporter import prog8.code.core.IErrorReporter
import prog8.codegen.intermediate.IRCodeGen import prog8.codegen.intermediate.IRCodeGen
import prog8.intermediate.IRFileReader
import prog8.intermediate.IRFileWriter import prog8.intermediate.IRFileWriter
import java.nio.file.Path import prog8.intermediate.IRProgram
class VmCodeGen(private val program: PtProgram, class VmCodeGen(private val program: PtProgram,
private val symbolTable: SymbolTable, private val symbolTable: SymbolTable,
@ -21,20 +20,17 @@ class VmCodeGen(private val program: PtProgram,
val irCodeGen = IRCodeGen(program, symbolTable, options, errors) val irCodeGen = IRCodeGen(program, symbolTable, options, errors)
val irProgram = irCodeGen.generate() val irProgram = irCodeGen.generate()
return if(options.keepIR) { // no need to check options.keepIR, as the VM file format *is* the IR file.
//create IR file on disk and read it back. return VmAssemblyProgram(irProgram.name, irProgram)
IRFileWriter(irProgram).writeFile()
val irProgram2 = IRFileReader(options.outputDir, irProgram.name).readFile()
VmAssemblyProgram(irProgram2.name, irProgram2)
} else {
VmAssemblyProgram(irProgram.name, irProgram)
}
} }
}
companion object {
fun compileIR(listingFilename: String): IAssemblyProgram { internal class VmAssemblyProgram(override val name: String, private val irProgram: IRProgram): IAssemblyProgram {
val irProgram = IRFileReader(Path.of(""), listingFilename).readFile()
return VmAssemblyProgram(irProgram.name, irProgram) override fun assemble(options: CompilationOptions): Boolean {
} // the VM reads the IR file from disk.
IRFileWriter(irProgram, null).write()
return true
} }
} }

View File

@ -1,21 +1,17 @@
import io.kotest.core.spec.style.FunSpec import io.kotest.core.spec.style.FunSpec
import io.kotest.matchers.shouldBe import io.kotest.matchers.shouldBe
import prog8.code.SymbolTable
import prog8.code.core.* import prog8.code.core.*
import prog8.code.target.VMTarget import prog8.code.target.VMTarget
import prog8.codegen.intermediate.IRPeepholeOptimizer import prog8.codegen.intermediate.IRPeepholeOptimizer
import prog8.intermediate.* import prog8.intermediate.*
class TestIRPeepholeOpt: FunSpec({ class TestIRPeepholeOpt: FunSpec({
fun makeIRProgram(lines: List<IRCodeLine>): IRProgram { fun makeIRProgram(chunks: List<IRCodeChunkBase>): IRProgram {
require(chunks.first().label=="main.start")
val block = IRBlock("main", null, IRBlock.BlockAlignment.NONE, Position.DUMMY) val block = IRBlock("main", null, IRBlock.BlockAlignment.NONE, Position.DUMMY)
val sub = IRSubroutine("main.start", emptyList(), null, Position.DUMMY) val sub = IRSubroutine("main.start", emptyList(), null, Position.DUMMY)
val chunk = IRCodeChunk(Position.DUMMY) chunks.forEach { sub += it }
for(line in lines)
chunk += line
sub += chunk
block += sub block += sub
val st = SymbolTable()
val target = VMTarget() val target = VMTarget()
val options = CompilationOptions( val options = CompilationOptions(
OutputType.RAW, OutputType.RAW,
@ -27,153 +23,167 @@ class TestIRPeepholeOpt: FunSpec({
compTarget = target, compTarget = target,
loadAddress = target.machine.PROGRAM_LOAD_ADDRESS loadAddress = target.machine.PROGRAM_LOAD_ADDRESS
) )
val prog = IRProgram("test", st, options, target) val prog = IRProgram("test", IRSymbolTable(null), options, target)
prog.addBlock(block) prog.addBlock(block)
prog.linkChunks()
prog.validate()
return prog return prog
} }
fun IRProgram.lines(): List<IRCodeLine> = this.blocks.flatMap { it.subroutines }.flatMap { it.chunks }.flatMap { it.lines } fun makeIRProgram(instructions: List<IRInstruction>): IRProgram {
val chunk = IRCodeChunk("main.start", null)
instructions.forEach { chunk += it }
return makeIRProgram(listOf(chunk))
}
fun IRProgram.chunks(): List<IRCodeChunkBase> = this.blocks.flatMap { it.subroutines }.flatMap { it.chunks }
test("remove nops") { test("remove nops") {
val irProg = makeIRProgram(listOf( val irProg = makeIRProgram(listOf(
IRCodeInstruction(Opcode.JUMP, labelSymbol = "dummy"), IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=1, value=42),
IRCodeInstruction(Opcode.NOP), IRInstruction(Opcode.NOP),
IRCodeInstruction(Opcode.NOP) IRInstruction(Opcode.NOP)
)) ))
irProg.lines().size shouldBe 3 irProg.chunks().single().instructions.size shouldBe 3
val opt = IRPeepholeOptimizer(irProg) val opt = IRPeepholeOptimizer(irProg)
opt.optimize() opt.optimize()
irProg.lines().size shouldBe 1 irProg.chunks().single().instructions.size shouldBe 1
} }
test("remove jmp to label below") { test("remove jmp to label below") {
val irProg = makeIRProgram(listOf( val c1 = IRCodeChunk("main.start", null)
IRCodeInstruction(Opcode.JUMP, labelSymbol = "label"), // removed c1 += IRInstruction(Opcode.JUMP, labelSymbol = "label") // removed, but chunk stays because of label
IRCodeLabel("label"), val c2 = IRCodeChunk("label", null)
IRCodeInstruction(Opcode.JUMP, labelSymbol = "label2"), // removed c2 += IRInstruction(Opcode.JUMP, labelSymbol = "label2") // removed, but chunk stays because of label
IRCodeInstruction(Opcode.NOP), // removed c2 += IRInstruction(Opcode.NOP) // removed
IRCodeLabel("label2"), val c3 = IRCodeChunk("label2", null)
IRCodeInstruction(Opcode.JUMP, labelSymbol = "label3"), c3 += IRInstruction(Opcode.JUMP, labelSymbol = "label3")
IRCodeInstruction(Opcode.INC, VmDataType.BYTE, reg1=1), c3 += IRInstruction(Opcode.INC, IRDataType.BYTE, reg1=1)
IRCodeLabel("label3") val c4 = IRCodeChunk("label3", null)
)) val irProg = makeIRProgram(listOf(c1, c2, c3, c4))
irProg.lines().size shouldBe 8
irProg.chunks().size shouldBe 4
irProg.chunks().flatMap { it.instructions }.size shouldBe 5
val opt = IRPeepholeOptimizer(irProg) val opt = IRPeepholeOptimizer(irProg)
opt.optimize() opt.optimize()
val lines = irProg.lines() irProg.chunks().size shouldBe 4
lines.size shouldBe 5 irProg.chunks()[0].label shouldBe "main.start"
(lines[0] as IRCodeLabel).name shouldBe "label" irProg.chunks()[1].label shouldBe "label"
(lines[1] as IRCodeLabel).name shouldBe "label2" irProg.chunks()[2].label shouldBe "label2"
(lines[2] as IRCodeInstruction).ins.opcode shouldBe Opcode.JUMP irProg.chunks()[3].label shouldBe "label3"
(lines[3] as IRCodeInstruction).ins.opcode shouldBe Opcode.INC irProg.chunks()[0].isEmpty() shouldBe true
(lines[4] as IRCodeLabel).name shouldBe "label3" irProg.chunks()[1].isEmpty() shouldBe true
irProg.chunks()[2].isEmpty() shouldBe false
irProg.chunks()[3].isEmpty() shouldBe true
val instr = irProg.chunks().flatMap { it.instructions }
instr.size shouldBe 2
instr[0].opcode shouldBe Opcode.JUMP
instr[1].opcode shouldBe Opcode.INC
} }
test("remove double sec/clc") { test("remove double sec/clc") {
val irProg = makeIRProgram(listOf( val irProg = makeIRProgram(listOf(
IRCodeInstruction(Opcode.SEC), IRInstruction(Opcode.SEC),
IRCodeInstruction(Opcode.SEC), IRInstruction(Opcode.SEC),
IRCodeInstruction(Opcode.SEC), IRInstruction(Opcode.SEC),
IRCodeInstruction(Opcode.CLC), IRInstruction(Opcode.CLC),
IRCodeInstruction(Opcode.CLC), IRInstruction(Opcode.CLC),
IRCodeInstruction(Opcode.CLC) IRInstruction(Opcode.CLC)
)) ))
irProg.lines().size shouldBe 6 irProg.chunks().single().instructions.size shouldBe 6
val opt = IRPeepholeOptimizer(irProg) val opt = IRPeepholeOptimizer(irProg)
opt.optimize() opt.optimize()
val lines = irProg.lines() val instr = irProg.chunks().single().instructions
lines.size shouldBe 1 instr.size shouldBe 1
(lines[0] as IRCodeInstruction).ins.opcode shouldBe Opcode.CLC instr[0].opcode shouldBe Opcode.CLC
} }
test("push followed by pop") { test("push followed by pop") {
val irProg = makeIRProgram(listOf( val irProg = makeIRProgram(listOf(
IRCodeInstruction(Opcode.PUSH, VmDataType.BYTE, reg1=42), IRInstruction(Opcode.PUSH, IRDataType.BYTE, reg1=42),
IRCodeInstruction(Opcode.POP, VmDataType.BYTE, reg1=42), IRInstruction(Opcode.POP, IRDataType.BYTE, reg1=42),
IRCodeInstruction(Opcode.PUSH, VmDataType.BYTE, reg1=99), IRInstruction(Opcode.PUSH, IRDataType.BYTE, reg1=99),
IRCodeInstruction(Opcode.POP, VmDataType.BYTE, reg1=222) IRInstruction(Opcode.POP, IRDataType.BYTE, reg1=222)
)) ))
irProg.lines().size shouldBe 4 irProg.chunks().single().instructions.size shouldBe 4
val opt = IRPeepholeOptimizer(irProg) val opt = IRPeepholeOptimizer(irProg)
opt.optimize() opt.optimize()
val lines = irProg.lines() val instr = irProg.chunks().single().instructions
lines.size shouldBe 1 instr.size shouldBe 1
(lines[0] as IRCodeInstruction).ins.opcode shouldBe Opcode.LOADR instr[0].opcode shouldBe Opcode.LOADR
(lines[0] as IRCodeInstruction).ins.reg1 shouldBe 222 instr[0].reg1 shouldBe 222
(lines[0] as IRCodeInstruction).ins.reg2 shouldBe 99 instr[0].reg2 shouldBe 99
} }
test("remove useless div/mul, add/sub") { test("remove useless div/mul, add/sub") {
val irProg = makeIRProgram(listOf( val irProg = makeIRProgram(listOf(
IRCodeInstruction(Opcode.DIV, VmDataType.BYTE, reg1=42, value = 1), IRInstruction(Opcode.DIV, IRDataType.BYTE, reg1=42, value = 1),
IRCodeInstruction(Opcode.DIVS, VmDataType.BYTE, reg1=42, value = 1), IRInstruction(Opcode.DIVS, IRDataType.BYTE, reg1=42, value = 1),
IRCodeInstruction(Opcode.MUL, VmDataType.BYTE, reg1=42, value = 1), IRInstruction(Opcode.MUL, IRDataType.BYTE, reg1=42, value = 1),
IRCodeInstruction(Opcode.MOD, VmDataType.BYTE, reg1=42, value = 1), IRInstruction(Opcode.MOD, IRDataType.BYTE, reg1=42, value = 1),
IRCodeInstruction(Opcode.DIV, VmDataType.BYTE, reg1=42, value = 2), IRInstruction(Opcode.DIV, IRDataType.BYTE, reg1=42, value = 2),
IRCodeInstruction(Opcode.DIVS, VmDataType.BYTE, reg1=42, value = 2), IRInstruction(Opcode.DIVS, IRDataType.BYTE, reg1=42, value = 2),
IRCodeInstruction(Opcode.MUL, VmDataType.BYTE, reg1=42, value = 2), IRInstruction(Opcode.MUL, IRDataType.BYTE, reg1=42, value = 2),
IRCodeInstruction(Opcode.MOD, VmDataType.BYTE, reg1=42, value = 2), IRInstruction(Opcode.MOD, IRDataType.BYTE, reg1=42, value = 2),
IRCodeInstruction(Opcode.ADD, VmDataType.BYTE, reg1=42, value = 0), IRInstruction(Opcode.ADD, IRDataType.BYTE, reg1=42, value = 0),
IRCodeInstruction(Opcode.SUB, VmDataType.BYTE, reg1=42, value = 0) IRInstruction(Opcode.SUB, IRDataType.BYTE, reg1=42, value = 0)
)) ))
irProg.lines().size shouldBe 10 irProg.chunks().single().instructions.size shouldBe 10
val opt = IRPeepholeOptimizer(irProg) val opt = IRPeepholeOptimizer(irProg)
opt.optimize() opt.optimize()
val lines = irProg.lines() irProg.chunks().single().instructions.size shouldBe 4
lines.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(
IRCodeInstruction(Opcode.ADD, VmDataType.BYTE, reg1=42, value = 1), IRInstruction(Opcode.ADD, IRDataType.BYTE, reg1=42, value = 1),
IRCodeInstruction(Opcode.SUB, VmDataType.BYTE, reg1=42, value = 1) IRInstruction(Opcode.SUB, IRDataType.BYTE, reg1=42, value = 1)
)) ))
irProg.lines().size shouldBe 2 irProg.chunks().single().instructions.size shouldBe 2
val opt = IRPeepholeOptimizer(irProg) val opt = IRPeepholeOptimizer(irProg)
opt.optimize() opt.optimize()
val lines = irProg.lines() val instr = irProg.chunks().single().instructions
lines.size shouldBe 2 instr.size shouldBe 2
(lines[0] as IRCodeInstruction).ins.opcode shouldBe Opcode.INC instr[0].opcode shouldBe Opcode.INC
(lines[1] as IRCodeInstruction).ins.opcode shouldBe Opcode.DEC instr[1].opcode shouldBe Opcode.DEC
} }
test("remove useless and/or/xor") { test("remove useless and/or/xor") {
val irProg = makeIRProgram(listOf( val irProg = makeIRProgram(listOf(
IRCodeInstruction(Opcode.AND, VmDataType.BYTE, reg1=42, value = 255), IRInstruction(Opcode.AND, IRDataType.BYTE, reg1=42, value = 255),
IRCodeInstruction(Opcode.AND, VmDataType.WORD, reg1=42, value = 65535), IRInstruction(Opcode.AND, IRDataType.WORD, reg1=42, value = 65535),
IRCodeInstruction(Opcode.OR, VmDataType.BYTE, reg1=42, value = 0), IRInstruction(Opcode.OR, IRDataType.BYTE, reg1=42, value = 0),
IRCodeInstruction(Opcode.XOR, VmDataType.BYTE, reg1=42, value = 0), IRInstruction(Opcode.XOR, IRDataType.BYTE, reg1=42, value = 0),
IRCodeInstruction(Opcode.AND, VmDataType.BYTE, reg1=42, value = 200), IRInstruction(Opcode.AND, IRDataType.BYTE, reg1=42, value = 200),
IRCodeInstruction(Opcode.AND, VmDataType.WORD, reg1=42, value = 60000), IRInstruction(Opcode.AND, IRDataType.WORD, reg1=42, value = 60000),
IRCodeInstruction(Opcode.OR, VmDataType.BYTE, reg1=42, value = 1), IRInstruction(Opcode.OR, IRDataType.BYTE, reg1=42, value = 1),
IRCodeInstruction(Opcode.XOR, VmDataType.BYTE, reg1=42, value = 1) IRInstruction(Opcode.XOR, IRDataType.BYTE, reg1=42, value = 1)
)) ))
irProg.lines().size shouldBe 8 irProg.chunks().single().instructions.size shouldBe 8
val opt = IRPeepholeOptimizer(irProg) val opt = IRPeepholeOptimizer(irProg)
opt.optimize() opt.optimize()
val lines = irProg.lines() irProg.chunks().single().instructions.size shouldBe 4
lines.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(
IRCodeInstruction(Opcode.AND, VmDataType.BYTE, reg1=42, value = 0), IRInstruction(Opcode.AND, IRDataType.BYTE, reg1=42, value = 0),
IRCodeInstruction(Opcode.AND, VmDataType.WORD, reg1=42, value = 0), IRInstruction(Opcode.AND, IRDataType.WORD, reg1=42, value = 0),
IRCodeInstruction(Opcode.OR, VmDataType.BYTE, reg1=42, value = 255), IRInstruction(Opcode.OR, IRDataType.BYTE, reg1=42, value = 255),
IRCodeInstruction(Opcode.OR, VmDataType.WORD, reg1=42, value = 65535) IRInstruction(Opcode.OR, IRDataType.WORD, reg1=42, value = 65535)
)) ))
irProg.lines().size shouldBe 4 irProg.chunks().single().instructions.size shouldBe 4
val opt = IRPeepholeOptimizer(irProg) val opt = IRPeepholeOptimizer(irProg)
opt.optimize() opt.optimize()
val lines = irProg.lines() val instr = irProg.chunks().single().instructions
lines.size shouldBe 4 instr.size shouldBe 4
(lines[0] as IRCodeInstruction).ins.opcode shouldBe Opcode.LOAD instr[0].opcode shouldBe Opcode.LOAD
(lines[1] as IRCodeInstruction).ins.opcode shouldBe Opcode.LOAD instr[1].opcode shouldBe Opcode.LOAD
(lines[2] as IRCodeInstruction).ins.opcode shouldBe Opcode.LOAD instr[2].opcode shouldBe Opcode.LOAD
(lines[3] as IRCodeInstruction).ins.opcode shouldBe Opcode.LOAD instr[3].opcode shouldBe Opcode.LOAD
(lines[0] as IRCodeInstruction).ins.value shouldBe 0 instr[0].value shouldBe 0
(lines[1] as IRCodeInstruction).ins.value shouldBe 0 instr[1].value shouldBe 0
(lines[2] as IRCodeInstruction).ins.value shouldBe 255 instr[2].value shouldBe 255
(lines[3] as IRCodeInstruction).ins.value shouldBe 65535 instr[3].value shouldBe 65535
} }
}) })

View File

@ -1,65 +0,0 @@
plugins {
id 'java'
id 'application'
id "org.jetbrains.kotlin.jvm"
id "io.kotest" version "0.3.9"
}
java {
toolchain {
languageVersion = JavaLanguageVersion.of(javaVersion)
}
}
compileKotlin {
kotlinOptions {
jvmTarget = javaVersion
}
}
compileTestKotlin {
kotlinOptions {
jvmTarget = javaVersion
}
}
dependencies {
implementation project(':codeCore')
implementation project(':intermediate')
implementation project(':codeGenIntermediate')
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
// implementation "org.jetbrains.kotlin:kotlin-reflect"
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.16"
testImplementation 'io.kotest:kotest-runner-junit5-jvm:5.3.2'
}
sourceSets {
main {
java {
srcDirs = ["${project.projectDir}/src"]
}
resources {
srcDirs = ["${project.projectDir}/res"]
}
}
test {
java {
srcDir "${project.projectDir}/test"
}
}
}
test {
// Enable JUnit 5 (Gradle 4.6+).
useJUnitPlatform()
// Always run tests, even when nothing changed.
dependsOn 'cleanTest'
// Show test results.
testLogging {
events "skipped", "failed"
}
}

View File

@ -1,20 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/test" isTestSource="true" />
<excludeFolder url="file://$MODULE_DIR$/build" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="KotlinJavaRuntime" level="project" />
<orderEntry type="library" name="michael.bull.kotlin.result.jvm" level="project" />
<orderEntry type="library" name="io.kotest.assertions.core.jvm" level="project" />
<orderEntry type="library" name="io.kotest.runner.junit5.jvm" level="project" />
<orderEntry type="module" module-name="codeCore" />
<orderEntry type="module" module-name="intermediate" />
<orderEntry type="module" module-name="codeGenIntermediate" />
</component>
</module>

View File

@ -1,103 +0,0 @@
package prog8.codegen.virtual
import prog8.code.core.AssemblyError
import prog8.code.core.CompilationOptions
import prog8.code.core.IAssemblyProgram
import prog8.intermediate.*
import java.io.BufferedWriter
import kotlin.io.path.bufferedWriter
import kotlin.io.path.div
internal class VmAssemblyProgram(override val name: String, private val irProgram: IRProgram): IAssemblyProgram {
override fun assemble(dummyOptions: CompilationOptions): Boolean {
val outfile = irProgram.options.outputDir / ("$name.p8virt")
println("write code to $outfile")
// at last, allocate the variables in memory.
val allocations = VmVariableAllocator(irProgram.st, irProgram.encoding, irProgram.options.compTarget)
outfile.bufferedWriter().use { out ->
allocations.asVmMemory().forEach { (name, alloc) ->
out.write("var ${name.joinToString(".")} $alloc\n")
}
out.write("------PROGRAM------\n")
if(!irProgram.options.dontReinitGlobals) {
out.write("; global var inits\n")
irProgram.globalInits.forEach { out.writeLine(it) }
}
irProgram.blocks.firstOrNull()?.let {
if(it.subroutines.any { it.name=="main.start" }) {
// there is a "main.start" entrypoint, jump to it
out.writeLine(IRCodeInstruction(Opcode.JUMP, labelSymbol = "main.start"))
}
}
out.write("; actual program code\n")
irProgram.blocks.forEach { block ->
if(block.address!=null)
TODO("blocks can't have a load address for vm")
out.write("; BLOCK ${block.name} ${block.position}\n")
block.inlineAssembly.forEach { asm ->
out.write("; ASM ${asm.position}\n")
out.write(asm.assembly)
out.write("\n")
}
block.subroutines.forEach { sub ->
out.write("; SUB ${sub.name} ${sub.position}\n")
out.write("_${sub.name}:\n")
sub.chunks.forEach { chunk ->
if(chunk is IRInlineAsmChunk) {
out.write("; ASM ${chunk.position}\n")
out.write(processInlinedAsm(chunk.assembly, allocations))
out.write("\n")
} else {
chunk.lines.forEach { out.writeLine(it) }
}
}
out.write("; END SUB ${sub.name}\n")
}
block.asmSubroutines.forEach { sub ->
out.write("; ASMSUB ${sub.name} ${sub.position}\n")
out.write("_${sub.name}:\n")
out.write(processInlinedAsm(sub.assembly, allocations))
out.write("\n; END ASMSUB ${sub.name}\n")
}
out.write("; END BLOCK ${block.name}\n")
}
}
return true
}
private fun processInlinedAsm(asm: String, allocations: VmVariableAllocator): String {
// TODO do we have to replace variable names by their allocated address???
return asm
}
}
private fun BufferedWriter.writeLine(line: IRCodeLine) {
when(line) {
is IRCodeComment -> {
write("; ${line.comment}\n")
}
is IRCodeInstruction -> {
write(line.ins.toString() + "\n")
}
is IRCodeInlineBinary -> {
write("!binary ")
line.data.withIndex().forEach {(index, byte) ->
write(byte.toString(16).padStart(2,'0'))
if(index and 63 == 63 && index<line.data.size-1)
write("\n!binary ")
}
write("\n")
}
is IRCodeLabel -> {
write("_${line.name}:\n")
}
else -> throw AssemblyError("invalid IR code line")
}
}

View File

@ -1,92 +0,0 @@
package prog8.codegen.virtual
import prog8.code.SymbolTable
import prog8.code.core.*
import prog8.intermediate.getTypeString
internal class VmVariableAllocator(val st: SymbolTable, val encoding: IStringEncoding, memsizer: IMemSizer) {
internal val allocations = mutableMapOf<List<String>, Int>()
private var freeMemoryStart: Int
val freeMem: Int
get() = freeMemoryStart
init {
var nextLocation = 0
for (variable in st.allVariables) {
val memsize =
when (variable.dt) {
DataType.STR -> variable.onetimeInitializationStringValue!!.first.length + 1 // include the zero byte
in NumericDatatypes -> memsizer.memorySize(variable.dt)
in ArrayDatatypes -> memsizer.memorySize(variable.dt, variable.length!!)
else -> throw InternalCompilerException("weird dt")
}
allocations[variable.scopedName] = nextLocation
nextLocation += memsize
}
for(slab in st.allMemorySlabs) {
// we ignore the alignment for the VM.
allocations[slab.scopedName] = nextLocation
nextLocation += slab.size.toInt()
}
freeMemoryStart = nextLocation
}
fun asVmMemory(): List<Pair<List<String>, String>> {
val mm = mutableListOf<Pair<List<String>, String>>()
// normal variables
for (variable in st.allVariables) {
val location = allocations.getValue(variable.scopedName)
val value = when(variable.dt) {
DataType.FLOAT -> (variable.onetimeInitializationNumericValue ?: 0.0).toString()
in NumericDatatypes -> (variable.onetimeInitializationNumericValue ?: 0).toHex()
DataType.STR -> {
val encoded = encoding.encodeString(variable.onetimeInitializationStringValue!!.first, variable.onetimeInitializationStringValue!!.second) + listOf(0u)
encoded.joinToString(",") { it.toInt().toHex() }
}
DataType.ARRAY_F -> {
if(variable.onetimeInitializationArrayValue!=null) {
variable.onetimeInitializationArrayValue!!.joinToString(",") { it.number!!.toString() }
} else {
(1..variable.length!!).joinToString(",") { "0" }
}
}
in ArrayDatatypes -> {
if(variable.onetimeInitializationArrayValue!==null) {
variable.onetimeInitializationArrayValue!!.joinToString(",") { it.number!!.toHex() }
} else {
(1..variable.length!!).joinToString(",") { "0" }
}
}
else -> throw InternalCompilerException("weird dt")
}
mm.add(Pair(variable.scopedName, "@$location ${getTypeString(variable)} $value"))
}
// memory mapped variables
for (variable in st.allMemMappedVariables) {
val value = when(variable.dt) {
DataType.FLOAT -> "0.0"
in NumericDatatypes -> "0"
DataType.ARRAY_F -> (1..variable.length!!).joinToString(",") { "0.0" }
in ArrayDatatypes -> (1..variable.length!!).joinToString(",") { "0" }
else -> throw InternalCompilerException("weird dt for mem mapped var")
}
mm.add(Pair(variable.scopedName, "@${variable.address} ${getTypeString(variable)} $value"))
}
// memory slabs.
for(slab in st.allMemorySlabs) {
val address = allocations.getValue(slab.scopedName)
mm.add(Pair(slab.scopedName, "@$address ubyte[${slab.size}] 0"))
}
return mm
}
}

View File

@ -20,7 +20,9 @@ import kotlin.math.pow
// TODO add more peephole expression optimizations? Investigate what optimizations binaryen has, also see https://egorbo.com/peephole-optimizations.html // TODO add more peephole expression optimizations? Investigate what optimizations binaryen has, also see https://egorbo.com/peephole-optimizations.html
class ExpressionSimplifier(private val program: Program, private val compTarget: ICompilationTarget) : AstWalker() { class ExpressionSimplifier(private val program: Program,
private val errors: IErrorReporter,
private val compTarget: ICompilationTarget) : AstWalker() {
private val powersOfTwo = (1..16).map { (2.0).pow(it) }.toSet() private val powersOfTwo = (1..16).map { (2.0).pow(it) }.toSet()
private val negativePowersOfTwo = powersOfTwo.map { -it }.toSet() private val negativePowersOfTwo = powersOfTwo.map { -it }.toSet()
@ -586,11 +588,13 @@ class ExpressionSimplifier(private val program: Program, private val compTarget:
when (val targetDt = targetIDt.getOr(DataType.UNDEFINED)) { when (val targetDt = targetIDt.getOr(DataType.UNDEFINED)) {
DataType.UBYTE, DataType.BYTE -> { DataType.UBYTE, DataType.BYTE -> {
if (amount >= 8) { if (amount >= 8) {
errors.warn("shift always results in 0", expr.position)
return NumericLiteral(targetDt, 0.0, expr.position) return NumericLiteral(targetDt, 0.0, expr.position)
} }
} }
DataType.UWORD, DataType.WORD -> { DataType.UWORD, DataType.WORD -> {
if (amount >= 16) { if (amount >= 16) {
errors.warn("shift always results in 0", expr.position)
return NumericLiteral(targetDt, 0.0, expr.position) return NumericLiteral(targetDt, 0.0, expr.position)
} }
else if(amount==8) { else if(amount==8) {
@ -625,6 +629,7 @@ class ExpressionSimplifier(private val program: Program, private val compTarget:
when (idt.getOr(DataType.UNDEFINED)) { when (idt.getOr(DataType.UNDEFINED)) {
DataType.UBYTE -> { DataType.UBYTE -> {
if (amount >= 8) { if (amount >= 8) {
errors.warn("shift always results in 0", expr.position)
return NumericLiteral.optimalInteger(0, expr.position) return NumericLiteral.optimalInteger(0, expr.position)
} }
} }
@ -636,6 +641,7 @@ class ExpressionSimplifier(private val program: Program, private val compTarget:
} }
DataType.UWORD -> { DataType.UWORD -> {
if (amount >= 16) { if (amount >= 16) {
errors.warn("shift always results in 0", expr.position)
return NumericLiteral.optimalInteger(0, expr.position) return NumericLiteral.optimalInteger(0, expr.position)
} }
else if(amount==8) { else if(amount==8) {

View File

@ -60,8 +60,8 @@ fun Program.inlineSubroutines(): Int {
return inliner.applyModifications() return inliner.applyModifications()
} }
fun Program.simplifyExpressions(target: ICompilationTarget) : Int { fun Program.simplifyExpressions(errors: IErrorReporter, target: ICompilationTarget) : Int {
val opti = ExpressionSimplifier(this, target) val opti = ExpressionSimplifier(this, errors, target)
opti.visit(this) opti.visit(this)
return opti.applyModifications() return opti.applyModifications()
} }

View File

@ -38,6 +38,8 @@ class Inliner(val program: Program): AstWalker() {
is Return -> { is Return -> {
if(stmt.value is NumericLiteral) if(stmt.value is NumericLiteral)
true true
else if(stmt.value==null)
true
else if (stmt.value is IdentifierReference) { else if (stmt.value is IdentifierReference) {
makeFullyScoped(stmt.value as IdentifierReference) makeFullyScoped(stmt.value as IdentifierReference)
true true

View File

@ -97,11 +97,13 @@ class StatementOptimizer(private val program: Program,
if(constvalue!=null) { if(constvalue!=null) {
return if(constvalue.asBooleanValue){ return if(constvalue.asBooleanValue){
// always true -> keep only if-part // always true -> keep only if-part
errors.warn("condition is always true", ifElse.condition.position) if(!ifElse.definingModule.isLibrary)
errors.warn("condition is always true", ifElse.condition.position)
listOf(IAstModification.ReplaceNode(ifElse, ifElse.truepart, parent)) listOf(IAstModification.ReplaceNode(ifElse, ifElse.truepart, parent))
} else { } else {
// always false -> keep only else-part // always false -> keep only else-part
errors.warn("condition is always false", ifElse.condition.position) if(!ifElse.definingModule.isLibrary)
errors.warn("condition is always false", ifElse.condition.position)
listOf(IAstModification.ReplaceNode(ifElse, ifElse.elsepart, parent)) listOf(IAstModification.ReplaceNode(ifElse, ifElse.elsepart, parent))
} }
} }
@ -291,9 +293,9 @@ class StatementOptimizer(private val program: Program,
val bexpr=assignment.value as? BinaryExpression val bexpr=assignment.value as? BinaryExpression
if(bexpr!=null) { if(bexpr!=null) {
val rightCv = bexpr.right.constValue(program)?.number val rightCv = bexpr.right.constValue(program)?.number
if(bexpr.operator=="-" && rightCv==null) { if(bexpr.operator=="-" && rightCv==null && targetIDt.isInteger) {
if(bexpr.right isSameAs assignment.target) { if(bexpr.right.isSimple && bexpr.right isSameAs assignment.target) {
// X = value - X --> X = -X ; X += value (to avoid need of stack-evaluation) // X = value - X --> X = -X ; X += value (to avoid need of stack-evaluation, for integers)
val negation = PrefixExpression("-", bexpr.right.copy(), bexpr.position) val negation = PrefixExpression("-", bexpr.right.copy(), bexpr.position)
val addValue = Assignment(assignment.target.copy(), BinaryExpression(bexpr.right, "+", bexpr.left, bexpr.position), AssignmentOrigin.OPTIMIZER, assignment.position) val addValue = Assignment(assignment.target.copy(), BinaryExpression(bexpr.right, "+", bexpr.left, bexpr.position), AssignmentOrigin.OPTIMIZER, assignment.position)
return listOf( return listOf(

View File

@ -31,8 +31,9 @@ dependencies {
implementation project(':codeOptimizers') implementation project(':codeOptimizers')
implementation project(':compilerAst') implementation project(':compilerAst')
implementation project(':codeGenCpu6502') implementation project(':codeGenCpu6502')
implementation project(':codeGenVirtual') implementation project(':codeGenIntermediate')
implementation project(':codeGenExperimental') implementation project(':codeGenExperimental')
implementation project(':virtualmachine')
implementation 'org.antlr:antlr4-runtime:4.10.1' implementation 'org.antlr:antlr4-runtime:4.10.1'
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
// implementation "org.jetbrains.kotlin:kotlin-reflect" // implementation "org.jetbrains.kotlin:kotlin-reflect"

View File

@ -21,6 +21,7 @@
<orderEntry type="module" module-name="codeOptimizers" /> <orderEntry type="module" module-name="codeOptimizers" />
<orderEntry type="module" module-name="codeGenCpu6502" /> <orderEntry type="module" module-name="codeGenCpu6502" />
<orderEntry type="module" module-name="codeGenExperimental" /> <orderEntry type="module" module-name="codeGenExperimental" />
<orderEntry type="module" module-name="codeGenVirtual" /> <orderEntry type="module" module-name="codeGenIntermediate" />
<orderEntry type="module" module-name="virtualmachine" />
</component> </component>
</module> </module>

View File

@ -56,7 +56,8 @@ romsub $af4b = ROUND() clobbers(A,X,Y) ; round fac1
romsub $af4e = ABS() clobbers(A,X,Y) ; fac1 = ABS(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 $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 $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 NOTE: special cx16 setup required, use RND() stub instead!! 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 $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 $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 $af60 = MOVFRM(uword mflpt @ AY) clobbers(A,X,Y) ; load mflpt value from memory in A/Y into fac1 (use MOVFM instead)
@ -151,6 +152,17 @@ asmsub FREADUY (ubyte value @Y) {
&uword AYINT_facmo = $66 ; $66/$67 contain result of AYINT &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:c128/floats.asm"
%asminclude "library:c64/floats_funcs.asm" %asminclude "library:c64/floats_funcs.asm"

View File

@ -171,6 +171,18 @@ asmsub GETADRAY () clobbers(X) -> uword @ AY {
&uword AYINT_facmo = $64 ; $64/$65 contain result of AYINT &uword AYINT_facmo = $64 ; $64/$65 contain result of AYINT
sub rndf() -> float {
%asm {{
stx P8ZP_SCRATCH_REG
lda #1
jsr FREADSA
jsr RND ; rng into fac1
ldx P8ZP_SCRATCH_REG
rts
}}
}
%asminclude "library:c64/floats.asm" %asminclude "library:c64/floats.asm"
%asminclude "library:c64/floats_funcs.asm" %asminclude "library:c64/floats_funcs.asm"

View File

@ -37,14 +37,19 @@ cx16diskio {
asmsub vload(str name @R0, ubyte device @Y, ubyte bank @A, uword address @R1) -> ubyte @A { asmsub vload(str name @R0, ubyte device @Y, ubyte bank @A, uword address @R1) -> ubyte @A {
; -- like the basic command VLOAD "filename",device,bank,address ; -- like the basic command VLOAD "filename",device,bank,address
; loads a file into Vera's video memory in the given bank:address, returns success in A ; loads a file into Vera's video memory in the given bank:address, returns success in A
; the file has to have the usual 2 byte header (which will be skipped)
%asm {{ %asm {{
; -- load a file into video ram clc
internal_vload:
phx phx
pha pha
tya tya
tax tax
lda #1 bcc +
ldy #0 ldy #%00000010 ; headerless load mode
bne ++
+ ldy #0 ; normal load mode
+ lda #1
jsr c64.SETLFS jsr c64.SETLFS
lda cx16.r0 lda cx16.r0
ldy cx16.r0+1 ldy cx16.r0+1
@ -71,6 +76,15 @@ cx16diskio {
}} }}
} }
asmsub vload_raw(str name @R0, ubyte device @Y, ubyte bank @A, uword address @R1) -> ubyte @A {
; -- like the basic command BVLOAD "filename",device,bank,address
; loads a file into Vera's video memory in the given bank:address, returns success in A
; the file is read fully including the first two bytes.
%asm {{
sec
jmp vload.internal_vload
}}
}
; replacement function that makes use of fast block read capability of the X16 ; replacement function that makes use of fast block read capability of the X16
; use this in place of regular diskio.f_read() ; use this in place of regular diskio.f_read()
@ -97,7 +111,7 @@ cx16diskio {
size = 255 size = 255
if num_bytes<size if num_bytes<size
size = num_bytes size = num_bytes
size = cx16.macptr(lsb(size), bufferpointer) size = cx16.macptr(lsb(size), bufferpointer, false)
if_cs if_cs
goto byte_read_loop ; macptr block read not supported, do fallback loop goto byte_read_loop ; macptr block read not supported, do fallback loop
diskio.list_blocks += size diskio.list_blocks += size

View File

@ -57,7 +57,8 @@ romsub $fe4b = ROUND() clobbers(A,X,Y) ; round fac1
romsub $fe4e = ABS() clobbers(A,X,Y) ; fac1 = ABS(fac1) romsub $fe4e = ABS() clobbers(A,X,Y) ; fac1 = ABS(fac1)
romsub $fe51 = SIGN() clobbers(X,Y) -> ubyte @ A ; SIGN(fac1) to A, $ff, $0, $1 for negative, zero, positive romsub $fe51 = SIGN() clobbers(X,Y) -> ubyte @ A ; SIGN(fac1) to A, $ff, $0, $1 for negative, zero, positive
romsub $fe54 = 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 $fe54 = 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 $fe57 = RND_0() clobbers(A,X,Y) ; fac1 = RND(fac1) float random number generator NOTE: special cx16 setup required, use RND() stub instead!! romsub $fe57 = RND_0() clobbers(A,X,Y) ; fac1 = RND(fac1) float random number generator NOTE: incompatible with C64's RND routine
romsub $fe57 = RND() clobbers(A,X,Y) ; alias for RND_0
romsub $fe5a = CONUPK(uword mflpt @ AY) clobbers(A,X,Y) ; load mflpt value from memory in A/Y into fac2 romsub $fe5a = CONUPK(uword mflpt @ AY) clobbers(A,X,Y) ; load mflpt value from memory in A/Y into fac2
romsub $fe5d = ROMUPK(uword mflpt @ AY) clobbers(A,X,Y) ; load mflpt value from memory in current bank in A/Y into fac2 romsub $fe5d = ROMUPK(uword mflpt @ AY) clobbers(A,X,Y) ; load mflpt value from memory in current bank in A/Y into fac2
romsub $fe60 = MOVFRM(uword mflpt @ AY) clobbers(A,X,Y) ; load mflpt value from memory in A/Y into fac1 (use MOVFM instead) romsub $fe60 = MOVFRM(uword mflpt @ AY) clobbers(A,X,Y) ; load mflpt value from memory in A/Y into fac1 (use MOVFM instead)
@ -147,19 +148,21 @@ asmsub FREADUY (ubyte value @Y) {
}} }}
} }
asmsub RND() clobbers(A,X,Y) {
%asm {{
lda #0
php
jsr cx16.entropy_get
plp
jmp RND_0
}}
}
&uword AYINT_facmo = $c6 ; $c6/$c7 contain result of AYINT &uword AYINT_facmo = $c6 ; $c6/$c7 contain result of AYINT
sub rndf() -> float {
%asm {{
phx
lda #1
jsr RND_0
plx
rts
}}
}
%asminclude "library:c64/floats.asm" %asminclude "library:c64/floats.asm"
%asminclude "library:c64/floats_funcs.asm" %asminclude "library:c64/floats_funcs.asm"
} }

View File

@ -366,7 +366,7 @@ _done
position2(x,y,true) position2(x,y,true)
set_both_strides(13) ; 160 increment = 1 line in 640 px 4c mode set_both_strides(13) ; 160 increment = 1 line in 640 px 4c mode
color &= 3 color &= 3
color <<= gfx2.plot.shift4c[lsb(x) & 3] color <<= gfx2.plot.shift4c[lsb(x) & 3] ; TODO with lookup table
ubyte @shared mask = gfx2.plot.mask4c[lsb(x) & 3] ubyte @shared mask = gfx2.plot.mask4c[lsb(x) & 3]
repeat lheight { repeat lheight {
%asm {{ %asm {{
@ -561,7 +561,11 @@ _done
and #1 and #1
}} }}
if_nz { if_nz {
cx16.r0L = lsb(x) & 7 ; xbits %asm {{
lda x
and #7
pha ; xbits
}}
x /= 8 x /= 8
x += y*(320/8) x += y*(320/8)
%asm {{ %asm {{
@ -571,7 +575,7 @@ _done
sta cx16.VERA_ADDR_M sta cx16.VERA_ADDR_M
lda x lda x
sta cx16.VERA_ADDR_L sta cx16.VERA_ADDR_L
ldy cx16.r0L ; xbits ply ; xbits
lda bits,y lda bits,y
ldy color ldy color
beq + beq +
@ -608,7 +612,11 @@ _done
and #1 and #1
}} }}
if_nz { if_nz {
cx16.r0L = lsb(x) & 7 ; xbits %asm {{
lda x
and #7
pha ; xbits
}}
x /= 8 x /= 8
x += y*(640/8) x += y*(640/8)
%asm {{ %asm {{
@ -618,7 +626,7 @@ _done
sta cx16.VERA_ADDR_M sta cx16.VERA_ADDR_M
lda x lda x
sta cx16.VERA_ADDR_L sta cx16.VERA_ADDR_L
ldy cx16.r0L ; xbits ply ; xbits
lda bits,y lda bits,y
ldy color ldy color
beq + beq +
@ -635,7 +643,7 @@ _done
void addr_mul_24_for_highres_4c(y, x) ; 24 bits result is in r0 and r1L (highest byte) void addr_mul_24_for_highres_4c(y, x) ; 24 bits result is in r0 and r1L (highest byte)
cx16.r2L = lsb(x) & 3 ; xbits cx16.r2L = lsb(x) & 3 ; xbits
color &= 3 color &= 3
color <<= shift4c[cx16.r2L] color <<= shift4c[cx16.r2L] ; TODO with lookup table
%asm {{ %asm {{
stz cx16.VERA_CTRL stz cx16.VERA_CTRL
lda cx16.r1L lda cx16.r1L
@ -654,6 +662,93 @@ _done
} }
} }
sub pget(uword @zp x, uword y) -> ubyte {
when active_mode {
1 -> {
; lores monochrome
%asm {{
lda x
and #7
pha ; xbits
}}
x /= 8
x += y*(320/8)
%asm {{
stz cx16.VERA_CTRL
stz cx16.VERA_ADDR_H
lda x+1
sta cx16.VERA_ADDR_M
lda x
sta cx16.VERA_ADDR_L
ply ; xbits
lda plot.bits,y
and cx16.VERA_DATA0
beq +
lda #1
+
}}
}
; TODO mode 2 and 3
4 -> {
; lores 256c
void addr_mul_24_for_lores_256c(y, x) ; 24 bits result is in r0 and r1L (highest byte)
%asm {{
stz cx16.VERA_CTRL
lda cx16.r1
sta cx16.VERA_ADDR_H
lda cx16.r0+1
sta cx16.VERA_ADDR_M
lda cx16.r0
sta cx16.VERA_ADDR_L
lda cx16.VERA_DATA0
}}
}
5 -> {
; hires monochrome
%asm {{
lda x
and #7
pha ; xbits
}}
x /= 8
x += y*(640/8)
%asm {{
stz cx16.VERA_CTRL
stz cx16.VERA_ADDR_H
lda x+1
sta cx16.VERA_ADDR_M
lda x
sta cx16.VERA_ADDR_L
ply ; xbits
lda plot.bits,y
and cx16.VERA_DATA0
beq +
lda #1
+
}}
}
6 -> {
; hires 4c
void addr_mul_24_for_highres_4c(y, x) ; 24 bits result is in r0 and r1L (highest byte)
%asm {{
stz cx16.VERA_CTRL
lda cx16.r1L
sta cx16.VERA_ADDR_H
lda cx16.r0H
sta cx16.VERA_ADDR_M
lda cx16.r0L
sta cx16.VERA_ADDR_L
lda cx16.VERA_DATA0
sta cx16.r0L
}}
cx16.r1L = lsb(x) & 3
cx16.r0L >>= gfx2.plot.shift4c[cx16.r1L] ; TODO with lookup table
return cx16.r0L & 3
}
else -> return 0
}
}
sub position(uword @zp x, uword y) { sub position(uword @zp x, uword y) {
ubyte bank ubyte bank
when active_mode { when active_mode {

View File

@ -26,7 +26,7 @@ palette {
} }
sub set_rgb(uword palette_words_ptr, uword num_colors) { sub set_rgb(uword palette_words_ptr, uword num_colors) {
; 1 word per color entry (in little endian format as layed out in video memory, so $gb0r) ; 1 word per color entry (in little endian format as layed out in video memory, so $gb;$0r)
vera_palette_ptr = $fa00 vera_palette_ptr = $fa00
repeat num_colors*2 { repeat num_colors*2 {
cx16.vpoke(1, vera_palette_ptr, @(palette_words_ptr)) cx16.vpoke(1, vera_palette_ptr, @(palette_words_ptr))

View File

@ -363,7 +363,7 @@ romsub $fed5 = console_set_paging_message(uword msgptr @R0) clobbers(A,X,Y)
romsub $fecf = entropy_get() -> ubyte @A, ubyte @X, ubyte @Y romsub $fecf = entropy_get() -> ubyte @A, ubyte @X, ubyte @Y
romsub $fecc = monitor() clobbers(A,X,Y) romsub $fecc = monitor() clobbers(A,X,Y)
romsub $ff44 = macptr(ubyte length @A, uword buffer @XY) clobbers(A) -> ubyte @Pc, uword @XY romsub $ff44 = macptr(ubyte length @A, uword buffer @XY, bool dontAdvance @Pc) clobbers(A) -> bool @Pc, uword @XY
romsub $ff47 = enter_basic(ubyte cold_or_warm @Pc) clobbers(A,X,Y) romsub $ff47 = enter_basic(ubyte cold_or_warm @Pc) clobbers(A,X,Y)
romsub $ff4d = clock_set_date_time(uword yearmonth @R0, uword dayhours @R1, uword minsecs @R2, ubyte jiffies @R3) clobbers(A, X, Y) romsub $ff4d = clock_set_date_time(uword yearmonth @R0, uword dayhours @R1, uword minsecs @R2, ubyte jiffies @R3) clobbers(A, X, Y)
romsub $ff50 = clock_get_date_time() clobbers(A, X, Y) -> uword @R0, uword @R1, uword @R2, ubyte @R3 ; result registers see clock_set_date_time() romsub $ff50 = clock_get_date_time() clobbers(A, X, Y) -> uword @R0, uword @R1, uword @R2, ubyte @R3 ; result registers see clock_set_date_time()
@ -393,9 +393,10 @@ asmsub kbdbuf_clear() {
asmsub mouse_config2(ubyte shape @A) clobbers (A, X, Y) { asmsub mouse_config2(ubyte shape @A) clobbers (A, X, Y) {
; -- convenience wrapper function that handles the screen resolution for mouse_config() for you ; -- convenience wrapper function that handles the screen resolution for mouse_config() for you
%asm {{ %asm {{
pha ; save shape
sec sec
jsr cx16.screen_mode ; set current screen mode and res in A, X, Y jsr cx16.screen_mode ; set current screen mode and res in A, X, Y
lda #1 pla ; get shape back
jmp cx16.mouse_config jmp cx16.mouse_config
}} }}
} }

View File

@ -221,13 +221,18 @@ sub ceil(float value) -> float {
}} }}
} }
sub rndf() -> float { sub rndseedf(float seed) {
if seed>0
seed = -seed ; make sure fp seed is always negative
%asm {{ %asm {{
stx P8ZP_SCRATCH_REG stx floats_store_reg
lda #1 lda #<seed
jsr FREADSA ldy #>seed
jsr RND ; rng into fac1 jsr MOVFM ; load float into fac1
ldx P8ZP_SCRATCH_REG lda #-1
jsr floats.RND
ldx floats_store_reg
rts rts
}} }}
} }

View File

@ -229,60 +229,32 @@ _divisor .word 0
.pend .pend
randseed .proc
; -- reset the random seeds for the byte and word random generators
; arguments: uword seed in A/Y clobbers A
; (default starting values are: A=$2c Y=$9e)
sta randword._seed
sty randword._seed+1
clc
adc #14
sta randbyte._seed
rts
.pend
randbyte .proc
; -- 8 bit pseudo random number generator into A (by just reusing randword)
jmp randword
.pend
randword .proc randword .proc
; -- 16 bit pseudo random number generator into AY ; -- 16 bit pseudo random number generator into AY
; default seed = $00c2 $1137
; rand64k ;Factors of 65535: 3 5 17 257 ; routine from https://codebase64.org/doku.php?id=base:x_abc_random_number_generator_8_16_bit
lda sr1+1 inc x1
asl a clc
asl a x1=*+1
eor sr1+1 lda #$00 ;x1
asl a c1=*+1
eor sr1+1 eor #$c2 ;c1
asl a a1=*+1
asl a eor #$11 ;a1
eor sr1+1 sta a1
asl a b1=*+1
rol sr1 ;shift this left, "random" bit comes from low adc #$37 ;b1
rol sr1+1 sta b1
; rand32k ;Factors of 32767: 7 31 151 are independent and can be combined lsr a
lda sr2+1 eor a1
asl a adc c1
eor sr2+1 sta c1
asl a ldy b1
asl a
ror sr2 ;shift this right, random bit comes from high - nicer when eor with sr1
rol sr2+1
lda sr1+1 ;can be left out
eor sr2+1 ;if you dont use
tay ;y as suggested
lda sr1 ;mix up lowbytes of SR1
eor sr2 ;and SR2 to combine both
rts rts
sr1 .word $a55a
sr2 .word $7653
.pend .pend
randbyte = randword ; -- 8 bit pseudo random number generator into A (by just reusing randword)
; ----------- optimized multiplications (stack) : --------- ; ----------- optimized multiplications (stack) : ---------
stack_mul_byte_3 .proc stack_mul_byte_3 .proc

View File

@ -71,4 +71,28 @@ _sinecosR8 .char trunc(127.0 * sin(range(180+45) * rad(360.0/180.0)))
}} }}
} }
asmsub rnd() -> ubyte @A {
%asm {{
jmp math.randbyte
}}
}
asmsub rndw() -> uword @AY {
%asm {{
jmp math.randword
}}
}
asmsub rndseed(uword seed1 @AY, uword seed2 @R0) clobbers(A,Y) {
; -- set new pseudo RNG's seed values. Defaults are: $00c2, $1137
%asm {{
sta math.randword.x1
sty math.randword.c1
lda cx16.r0L
sta math.randword.a1
lda cx16.r0H
sta math.randword.b1
rts
}}
}
} }

View File

@ -244,24 +244,6 @@ func_sqrt16_into_A .proc
rts rts
.pend .pend
func_rnd_stack .proc
; -- put a random ubyte on the estack
jsr math.randbyte
sta P8ESTACK_LO,x
dex
rts
.pend
func_rndw_stack .proc
; -- put a random uword on the estack
jsr math.randword
sta P8ESTACK_LO,x
tya
sta P8ESTACK_HI,x
dex
rts
.pend
func_sort_ub .proc func_sort_ub .proc
; 8bit unsigned sort ; 8bit unsigned sort

View File

@ -224,6 +224,29 @@ _done rts
}} }}
} }
asmsub lowerchar(ubyte char @A) -> ubyte @A {
%asm {{
and #$7f
cmp #97
bcc +
cmp #123
bcs +
and #%11011111
+ rts
}}
}
asmsub upperchar(ubyte char @A) -> ubyte @A {
%asm {{
cmp #65
bcc +
cmp #91
bcs +
ora #%00100000
+ rts
}}
}
sub startswith(str st, str prefix) -> bool { sub startswith(str st, str prefix) -> bool {
ubyte prefix_len = length(prefix) ubyte prefix_len = length(prefix)
ubyte str_len = length(st) ubyte str_len = length(st)

View File

@ -195,8 +195,8 @@ sub str2uword(str string) -> uword {
; -- returns the unsigned word value of the string number argument in AY ; -- returns the unsigned word value of the string number argument in AY
; the number may NOT be preceded by a + sign and may NOT contain spaces ; 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)
%asm {{ %ir {{
loadm.w r0,conv.str2uword.string loadm.w r65500,conv.str2uword.string
syscall 11 syscall 11
return return
}} }}
@ -206,8 +206,8 @@ sub str2word(str string) -> word {
; -- returns the signed word value of the string number argument in AY ; -- returns the signed word value of the string number argument in AY
; the number may be preceded by a + or - sign but may NOT contain spaces ; 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)
%asm {{ %ir {{
loadm.w r0,conv.str2word.string loadm.w r65500,conv.str2word.string
syscall 12 syscall 12
return return
}} }}

View File

@ -9,15 +9,15 @@ 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).
%asm {{ %ir {{
loadm.f fr0,floats.print_f.value loadm.f fr65500,floats.print_f.value
syscall 25 syscall 25
return return
}} }}
} }
sub pow(float value, float power) -> float { sub pow(float value, float power) -> float {
%asm {{ %ir {{
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
@ -26,7 +26,7 @@ sub pow(float value, float power) -> float {
} }
sub fabs(float value) -> float { sub fabs(float value) -> float {
%asm {{ %ir {{
loadm.f fr0,floats.fabs.value loadm.f fr0,floats.fabs.value
fabs.f fr0,fr0 fabs.f fr0,fr0
return return
@ -34,7 +34,7 @@ sub fabs(float value) -> float {
} }
sub sin(float angle) -> float { sub sin(float angle) -> float {
%asm {{ %ir {{
loadm.f fr0,floats.sin.angle loadm.f fr0,floats.sin.angle
fsin.f fr0,fr0 fsin.f fr0,fr0
return return
@ -42,7 +42,7 @@ sub sin(float angle) -> float {
} }
sub cos(float angle) -> float { sub cos(float angle) -> float {
%asm {{ %ir {{
loadm.f fr0,floats.cos.angle loadm.f fr0,floats.cos.angle
fcos.f fr0,fr0 fcos.f fr0,fr0
return return
@ -50,7 +50,7 @@ sub cos(float angle) -> float {
} }
sub tan(float value) -> float { sub tan(float value) -> float {
%asm {{ %ir {{
loadm.f fr0,floats.tan.value loadm.f fr0,floats.tan.value
ftan.f fr0,fr0 ftan.f fr0,fr0
return return
@ -58,7 +58,7 @@ sub tan(float value) -> float {
} }
sub atan(float value) -> float { sub atan(float value) -> float {
%asm {{ %ir {{
loadm.f fr0,floats.atan.value loadm.f fr0,floats.atan.value
fatan.f fr0,fr0 fatan.f fr0,fr0
return return
@ -66,7 +66,7 @@ sub atan(float value) -> float {
} }
sub ln(float value) -> float { sub ln(float value) -> float {
%asm {{ %ir {{
loadm.f fr0,floats.ln.value loadm.f fr0,floats.ln.value
fln.f fr0,fr0 fln.f fr0,fr0
return return
@ -74,7 +74,7 @@ sub ln(float value) -> float {
} }
sub log2(float value) -> float { sub log2(float value) -> float {
%asm {{ %ir {{
loadm.f fr0,floats.log2.value loadm.f fr0,floats.log2.value
flog.f fr0,fr0 flog.f fr0,fr0
return return
@ -82,7 +82,7 @@ sub log2(float value) -> float {
} }
sub sqrt(float value) -> float { sub sqrt(float value) -> float {
%asm {{ %ir {{
loadm.f fr0,floats.sqrt.value loadm.f fr0,floats.sqrt.value
sqrt.f fr0,fr0 sqrt.f fr0,fr0
return return
@ -100,7 +100,7 @@ sub deg(float angle) -> float {
} }
sub round(float value) -> float { sub round(float value) -> float {
%asm {{ %ir {{
loadm.f fr0,floats.round.value loadm.f fr0,floats.round.value
fround.f fr0,fr0 fround.f fr0,fr0
return return
@ -108,7 +108,7 @@ sub round(float value) -> float {
} }
sub floor(float value) -> float { sub floor(float value) -> float {
%asm {{ %ir {{
loadm.f fr0,floats.floor.value loadm.f fr0,floats.floor.value
ffloor.f fr0,fr0 ffloor.f fr0,fr0
return return
@ -117,7 +117,7 @@ sub floor(float value) -> float {
sub ceil(float value) -> float { sub ceil(float value) -> float {
; -- ceil: tr = int(f); if tr==f -> return else return tr+1 ; -- ceil: tr = int(f); if tr==f -> return else return tr+1
%asm {{ %ir {{
loadm.f fr0,floats.ceil.value loadm.f fr0,floats.ceil.value
fceil.f fr0,fr0 fceil.f fr0,fr0
return return
@ -125,9 +125,17 @@ sub ceil(float value) -> float {
} }
sub rndf() -> float { sub rndf() -> float {
%asm {{ %ir {{
rnd.f fr0 syscall 35
return return
}} }}
} }
sub rndseedf(float seed) {
%ir {{
loadm.f fr65500,floats.rndseedf.seed
syscall 32
}}
}
} }

View File

@ -159,4 +159,27 @@ math {
return costab[radians] as byte return costab[radians] as byte
} }
sub rnd() -> ubyte {
%ir {{
syscall 33
return
}}
}
sub rndw() -> uword {
%ir {{
syscall 34
return
}}
}
sub rndseed(uword seed1, uword seed2) {
; -- reset the pseudo RNG's seed values. Defaults are: $a55a, $7653.
%ir {{
loadm.w r65500,math.rndseed.seed1
loadm.w r65501,math.rndseed.seed2
syscall 31
return
}}
}
} }

View File

@ -1,53 +1,6 @@
; Internal library routines - always included by the compiler ; Internal library routines - always included by the compiler
%import textio
prog8_lib { prog8_lib {
%option force_output %option force_output
sub string_contains(ubyte needle, str haystack) -> ubyte {
repeat {
if @(haystack)==0
return false
if @(haystack)==needle
return true
haystack++
}
}
sub bytearray_contains(ubyte needle, uword haystack_ptr, ubyte num_elements) -> ubyte {
haystack_ptr--
while num_elements {
if haystack_ptr[num_elements]==needle
return true
num_elements--
}
return false
}
sub wordarray_contains(ubyte needle, uword haystack_ptr, ubyte num_elements) -> ubyte {
haystack_ptr += (num_elements-1) * 2
while num_elements {
if peekw(haystack_ptr)==needle
return true
haystack_ptr -= 2
num_elements--
}
return false
}
sub string_compare(str st1, str st2) -> byte {
; Compares two strings for sorting.
; Returns -1 (255), 0 or 1 depending on wether string1 sorts before, equal or after string2.
; 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).
%asm {{
loadm.w r0,prog8_lib.string_compare.st1
loadm.w r1,prog8_lib.string_compare.st2
syscall 29
return
}}
}
} }

View File

@ -83,7 +83,12 @@ string {
; Returns -1 (255), 0 or 1 depending on wether string1 sorts before, equal or after string2. ; Returns -1 (255), 0 or 1 depending on wether string1 sorts before, equal or after string2.
; 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).
return prog8_lib.string_compare(st1, st2) %ir {{
loadm.w r65500,string.compare.st1
loadm.w r65501,string.compare.st2
syscall 29
return
}}
} }
sub lower(str st) -> ubyte { sub lower(str st) -> ubyte {
@ -114,6 +119,18 @@ string {
} }
} }
sub lowerchar(ubyte char) -> ubyte {
if char >= 'A' and char <= 'Z'
char |= %00100000
return char
}
sub upperchar(ubyte char) -> ubyte {
if char >= 'a' and char <= 'z'
char &= %11011111
return char
}
sub startswith(str st, str prefix) -> bool { sub startswith(str st, str prefix) -> bool {
ubyte prefix_len = length(prefix) ubyte prefix_len = length(prefix)
ubyte str_len = length(st) ubyte str_len = length(st)

View File

@ -7,22 +7,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.
%asm {{ %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)
%asm {{ %ir {{
loadm.w r0,sys.wait.jiffies loadm.w r65500,sys.wait.jiffies
syscall 13 syscall 13
}} }}
} }
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.
%asm {{ %ir {{
syscall 14 syscall 14
}} }}
} }
@ -61,52 +61,52 @@ 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
%asm {{ %ir {{
loadm.b r0,sys.exit.returnvalue loadm.b r65500,sys.exit.returnvalue
syscall 1 syscall 1
}} }}
} }
sub set_carry() { sub set_carry() {
%asm {{ %ir {{
sec sec
}} }}
} }
sub clear_carry() { sub clear_carry() {
%asm {{ %ir {{
clc clc
}} }}
} }
sub gfx_enable(ubyte mode) { sub gfx_enable(ubyte mode) {
%asm {{ %ir {{
loadm.b r0,sys.gfx_enable.mode loadm.b r65500,sys.gfx_enable.mode
syscall 8 syscall 8
}} }}
} }
sub gfx_clear(ubyte color) { sub gfx_clear(ubyte color) {
%asm {{ %ir {{
loadm.b r0,sys.gfx_clear.color loadm.b r65500,sys.gfx_clear.color
syscall 9 syscall 9
}} }}
} }
sub gfx_plot(uword xx, uword yy, ubyte color) { sub gfx_plot(uword xx, uword yy, ubyte color) {
%asm {{ %ir {{
loadm.w r0,sys.gfx_plot.xx loadm.w r65500,sys.gfx_plot.xx
loadm.w r1,sys.gfx_plot.yy loadm.w r65501,sys.gfx_plot.yy
loadm.b r2,sys.gfx_plot.color loadm.b r65502,sys.gfx_plot.color
syscall 10 syscall 10
}} }}
} }
sub gfx_getpixel(uword xx, uword yy) -> ubyte { sub gfx_getpixel(uword xx, uword yy) -> ubyte {
%asm {{ %ir {{
loadm.w r0,sys.gfx_getpixel.xx loadm.w r65500,sys.gfx_getpixel.xx
loadm.w r1,sys.gfx_getpixel.yy loadm.w r65501,sys.gfx_getpixel.yy
syscall 30 syscall 30
return return
}} }}

View File

@ -6,8 +6,8 @@ txt {
sub clear_screen() { sub clear_screen() {
str @shared sequence = "\x1b[2J\x1B[H" str @shared sequence = "\x1b[2J\x1B[H"
%asm {{ %ir {{
load.w r0,txt.clear_screen.sequence load.w r65500,txt.clear_screen.sequence
syscall 3 syscall 3
}} }}
} }
@ -29,15 +29,15 @@ sub uppercase() {
} }
sub chrout(ubyte char) { sub chrout(ubyte char) {
%asm {{ %ir {{
loadm.b r0,txt.chrout.char loadm.b r65500,txt.chrout.char
syscall 2 syscall 2
}} }}
} }
sub print (str text) { sub print (str text) {
%asm {{ %ir {{
loadm.w r0,txt.print.text loadm.w r65500,txt.print.text
syscall 3 syscall 3
}} }}
} }
@ -113,11 +113,26 @@ sub print_w (word value) {
sub input_chars (uword buffer) -> ubyte { 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!
%asm {{ %ir {{
loadm.w r0,txt.input_chars.buffer loadm.w r65500,txt.input_chars.buffer
syscall 6 syscall 6
return return
}} }}
} }
sub plot (ubyte col, ubyte row) {
; use ANSI escape sequence to position the cursor
txt.chrout(27)
txt.chrout('[')
txt.print_ub(row)
txt.chrout(';')
txt.print_ub(col)
txt.chrout('H')
}
sub setchr (ubyte col, ubyte row, ubyte char) {
plot(col, row)
txt.chrout(char)
}
} }

View File

@ -1 +1 @@
8.5 8.7

View File

@ -2,16 +2,20 @@ package prog8
import kotlinx.cli.* import kotlinx.cli.*
import prog8.ast.base.AstException import prog8.ast.base.AstException
import prog8.code.core.* import prog8.code.core.CbmPrgLauncherType
import prog8.code.core.toHex
import prog8.code.target.* import prog8.code.target.*
import prog8.code.target.virtual.VirtualMachineDefinition import prog8.code.target.virtual.VirtualMachineDefinition
import prog8.codegen.virtual.VmCodeGen
import prog8.compiler.CompilationResult import prog8.compiler.CompilationResult
import prog8.compiler.CompilerArguments import prog8.compiler.CompilerArguments
import prog8.compiler.compileProgram import prog8.compiler.compileProgram
import java.io.File import java.io.File
import java.nio.file.* import java.nio.file.FileSystems
import java.nio.file.Path
import java.nio.file.StandardWatchEventKinds
import java.nio.file.WatchKey
import java.time.LocalDateTime import java.time.LocalDateTime
import kotlin.io.path.Path
import kotlin.system.exitProcess import kotlin.system.exitProcess
@ -47,7 +51,7 @@ private fun compileMain(args: Array<String>): Boolean {
val slowCodegenWarnings by cli.option(ArgType.Boolean, fullName = "slowwarn", description="show debug warnings about slow/problematic assembly code generation") val slowCodegenWarnings by cli.option(ArgType.Boolean, fullName = "slowwarn", description="show debug warnings about slow/problematic assembly code generation")
val sourceDirs by cli.option(ArgType.String, fullName="srcdirs", description = "list of extra paths, separated with ${File.pathSeparator}, to search in for imported modules").multiple().delimiter(File.pathSeparator) val sourceDirs by cli.option(ArgType.String, fullName="srcdirs", description = "list of extra paths, separated with ${File.pathSeparator}, to search in for imported modules").multiple().delimiter(File.pathSeparator)
val compilationTarget by cli.option(ArgType.String, fullName = "target", description = "target output of the compiler (one of '${C64Target.NAME}', '${C128Target.NAME}', '${Cx16Target.NAME}', '${AtariTarget.NAME}', '${VMTarget.NAME}')").default(C64Target.NAME) val compilationTarget by cli.option(ArgType.String, fullName = "target", description = "target output of the compiler (one of '${C64Target.NAME}', '${C128Target.NAME}', '${Cx16Target.NAME}', '${AtariTarget.NAME}', '${VMTarget.NAME}')").default(C64Target.NAME)
val startVm by cli.option(ArgType.Boolean, fullName = "vm", description = "load and run a p8-virt or p8-ir listing in the VM instead") val startVm by cli.option(ArgType.Boolean, fullName = "vm", description = "load and run a .p8ir IR source file in the VM")
val watchMode by cli.option(ArgType.Boolean, fullName = "watch", description = "continuous compilation mode (watch for file changes)") val watchMode by cli.option(ArgType.Boolean, fullName = "watch", description = "continuous compilation mode (watch for file changes)")
val moduleFiles by cli.argument(ArgType.String, fullName = "modules", description = "main module file(s) to compile").multiple(999) val moduleFiles by cli.argument(ArgType.String, fullName = "modules", description = "main module file(s) to compile").multiple(999)
@ -142,7 +146,7 @@ private fun compileMain(args: Array<String>): Boolean {
for (importedFile in allImportedFiles) { for (importedFile in allImportedFiles) {
print(" ") print(" ")
println(importedFile) println(importedFile)
val watchDir = importedFile.parent ?: Path.of("") val watchDir = importedFile.parent ?: Path("")
watchDir.register(watchservice, StandardWatchEventKinds.ENTRY_MODIFY) watchDir.register(watchservice, StandardWatchEventKinds.ENTRY_MODIFY)
} }
println("[${LocalDateTime.now().withNano(0)}] Waiting for file changes.") println("[${LocalDateTime.now().withNano(0)}] Waiting for file changes.")
@ -244,28 +248,9 @@ private fun processSymbolDefs(symbolDefs: List<String>): Map<String, String>? {
return result return result
} }
fun runVm(listingFilename: String): Boolean { fun runVm(irFilename: String): Boolean {
if(listingFilename.endsWith(".p8ir")) { val irFile = Path(irFilename)
val withoutSuffix = listingFilename.substring(0, listingFilename.length-5)
val compiled = VmCodeGen.compileIR(withoutSuffix)
if (!compiled.assemble(CompilationOptions( // these are just dummy options, the actual options are inside the .p8ir file itself:
OutputType.PRG,
CbmPrgLauncherType.NONE,
ZeropageType.DONTUSE,
emptyList(),
floats = true,
noSysInit = true,
compTarget = VMTarget(),
loadAddress = VMTarget().machine.PROGRAM_LOAD_ADDRESS
))
) {
return false
}
val vmdef = VirtualMachineDefinition()
vmdef.launchEmulator(0, Paths.get(withoutSuffix))
return true
}
val vmdef = VirtualMachineDefinition() val vmdef = VirtualMachineDefinition()
vmdef.launchEmulator(0, Paths.get(listingFilename)) vmdef.launchEmulator(0, irFile)
return true return true
} }

View File

@ -14,6 +14,7 @@ import prog8.ast.walk.IAstVisitor
import prog8.code.SymbolTable import prog8.code.SymbolTable
import prog8.code.core.* import prog8.code.core.*
import prog8.code.target.* import prog8.code.target.*
import prog8.codegen.vm.VmCodeGen
import prog8.compiler.astprocessing.* import prog8.compiler.astprocessing.*
import prog8.optimizer.* import prog8.optimizer.*
import prog8.parser.ParseError import prog8.parser.ParseError
@ -360,7 +361,7 @@ private fun optimizeAst(program: Program, compilerOptions: CompilationOptions, e
remover.applyModifications() remover.applyModifications()
while (true) { while (true) {
// keep optimizing expressions and statements until no more steps remain // keep optimizing expressions and statements until no more steps remain
val optsDone1 = program.simplifyExpressions(compTarget) val optsDone1 = program.simplifyExpressions(errors, compTarget)
val optsDone2 = program.splitBinaryExpressions(compilerOptions) val optsDone2 = program.splitBinaryExpressions(compilerOptions)
val optsDone3 = program.optimizeStatements(errors, functions, compTarget) val optsDone3 = program.optimizeStatements(errors, functions, compTarget)
val optsDone4 = program.inlineSubroutines() val optsDone4 = program.inlineSubroutines()
@ -393,7 +394,7 @@ private fun createAssemblyAndAssemble(program: Program,
compilerOptions.compTarget.machine.initializeZeropage(compilerOptions) compilerOptions.compTarget.machine.initializeZeropage(compilerOptions)
program.processAstBeforeAsmGeneration(compilerOptions, errors) program.processAstBeforeAsmGeneration(compilerOptions, errors)
errors.report() errors.report()
val symbolTable = SymbolTableMaker().makeFrom(program) val symbolTable = SymbolTableMaker().makeFrom(program, compilerOptions)
// TODO make removing all VarDecls work, but this needs inferType to be able to get its information from somewhere else as the VarDecl nodes in the Ast, // TODO make removing all VarDecls work, but this needs inferType to be able to get its information from somewhere else as the VarDecl nodes in the Ast,
// or don't use inferType at all anymore and "bake the type information" into the Ast somehow. // or don't use inferType at all anymore and "bake the type information" into the Ast somehow.
@ -455,7 +456,7 @@ internal fun asmGeneratorFor(program: Program,
return prog8.codegen.cpu6502.AsmGen(program, symbolTable, options, errors) return prog8.codegen.cpu6502.AsmGen(program, symbolTable, options, errors)
if (options.compTarget.name == VMTarget.NAME) { if (options.compTarget.name == VMTarget.NAME) {
val intermediateAst = IntermediateAstMaker(program).transform() val intermediateAst = IntermediateAstMaker(program).transform()
return prog8.codegen.virtual.VmCodeGen(intermediateAst, symbolTable, options, errors) return VmCodeGen(intermediateAst, symbolTable, options, errors)
} }
} }

View File

@ -7,7 +7,6 @@ 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.core.* import prog8.code.core.*
import prog8.code.target.VMTarget
import prog8.compiler.BuiltinFunctions import prog8.compiler.BuiltinFunctions
import prog8.compiler.InplaceModifyingBuiltinFunctions import prog8.compiler.InplaceModifyingBuiltinFunctions
import prog8.compiler.builtinFunctionReturnType import prog8.compiler.builtinFunctionReturnType
@ -252,16 +251,8 @@ internal class AstChecker(private val program: Program,
} }
override fun visit(inlineAssembly: InlineAssembly) { override fun visit(inlineAssembly: InlineAssembly) {
val assembly = inlineAssembly.assembly if(inlineAssembly.hasReturnOrRts(compilerOptions.compTarget))
if(compilerOptions.compTarget.name!=VMTarget.NAME) { count++
if (" rti" in assembly || "\trti" in assembly || " rts" in assembly || "\trts" in assembly ||
" jmp" in assembly || "\tjmp" in assembly || " bra" in assembly || "\tbra" in assembly
)
count++
} else {
if(" return" in assembly || "\treturn" in assembly || " jump" in assembly || "\tjump" in assembly)
count++
}
} }
} }
@ -319,12 +310,12 @@ internal class AstChecker(private val program: Program,
} }
else if(param.second.registerOrPair in arrayOf(RegisterOrPair.AX, RegisterOrPair.AY, RegisterOrPair.XY)) { else if(param.second.registerOrPair in arrayOf(RegisterOrPair.AX, RegisterOrPair.AY, RegisterOrPair.XY)) {
if (param.first.type != DataType.UWORD && param.first.type != DataType.WORD if (param.first.type != DataType.UWORD && param.first.type != DataType.WORD
&& param.first.type != DataType.STR && param.first.type !in ArrayDatatypes && param.first.type != DataType.FLOAT) && param.first.type != DataType.STR && param.first.type !in ArrayDatatypes)
err("parameter '${param.first.name}' should be (u)word (an address) or str") err("parameter '${param.first.name}' should be (u)word (an address) or str")
} }
else if(param.second.statusflag!=null) { else if(param.second.statusflag!=null) {
if (param.first.type != DataType.UBYTE) if (param.first.type != DataType.UBYTE && param.first.type != DataType.BOOL)
err("parameter '${param.first.name}' should be ubyte") err("parameter '${param.first.name}' should be bool or ubyte")
} }
} }
subroutine.returntypes.zip(subroutine.asmReturnvaluesRegisters).forEachIndexed { index, pair -> subroutine.returntypes.zip(subroutine.asmReturnvaluesRegisters).forEachIndexed { index, pair ->
@ -334,12 +325,12 @@ internal class AstChecker(private val program: Program,
} }
else if(pair.second.registerOrPair in setOf(RegisterOrPair.AX, RegisterOrPair.AY, RegisterOrPair.XY)) { else if(pair.second.registerOrPair in setOf(RegisterOrPair.AX, RegisterOrPair.AY, RegisterOrPair.XY)) {
if (pair.first != DataType.UWORD && pair.first != DataType.WORD if (pair.first != DataType.UWORD && pair.first != DataType.WORD
&& pair.first != DataType.STR && pair.first !in ArrayDatatypes && pair.first != DataType.FLOAT) && pair.first != DataType.STR && pair.first !in ArrayDatatypes)
err("return type #${index + 1} should be (u)word/address") err("return type #${index + 1} should be (u)word/address")
} }
else if(pair.second.statusflag!=null) { else if(pair.second.statusflag!=null) {
if (pair.first != DataType.UBYTE) if (pair.first != DataType.UBYTE && pair.first != DataType.BOOL)
err("return type #${index + 1} should be ubyte") err("return type #${index + 1} should be bool or ubyte")
} }
} }
@ -1005,7 +996,7 @@ internal class AstChecker(private val program: Program,
// It's not (yet) possible to handle these multiple return values because assignments // It's not (yet) possible to handle these multiple return values because assignments
// are only to a single unique target at the same time. // are only to a single unique target at the same time.
// EXCEPTION: // EXCEPTION:
// if the asmsub returns multiple values and one of them is via a status register bit, // if the asmsub returns multiple values and one of them is via a status register bit (such as carry),
// it *is* possible to handle them by just actually assigning the register value and // it *is* possible to handle them by just actually assigning the register value and
// dealing with the status bit as just being that, the status bit after the call. // dealing with the status bit as just being that, the status bit after the call.
val (returnRegisters, _) = stmt.asmReturnvaluesRegisters.partition { rr -> rr.registerOrPair != null } val (returnRegisters, _) = stmt.asmReturnvaluesRegisters.partition { rr -> rr.registerOrPair != null }

View File

@ -12,7 +12,6 @@ import prog8.ast.statements.VarDeclOrigin
import prog8.ast.walk.AstWalker import prog8.ast.walk.AstWalker
import prog8.ast.walk.IAstModification import prog8.ast.walk.IAstModification
import prog8.code.core.* import prog8.code.core.*
import prog8.code.target.VMTarget
internal fun Program.checkValid(errors: IErrorReporter, compilerOptions: CompilationOptions) { internal fun Program.checkValid(errors: IErrorReporter, compilerOptions: CompilationOptions) {
@ -170,15 +169,8 @@ internal fun IdentifierReference.isSubroutineParameter(program: Program): Boolea
} }
internal fun Subroutine.hasRtsInAsm(compTarget: ICompilationTarget): Boolean { internal fun Subroutine.hasRtsInAsm(compTarget: ICompilationTarget): Boolean {
val instructions =
if(compTarget.name == VMTarget.NAME)
listOf(" return", "\treturn", " jump", "\tjump")
else
listOf(" rti", "\trti", " rts", "\trts", " jmp", "\tjmp", " bra", "\tbra")
return statements return statements
.asSequence() .asSequence()
.filterIsInstance<InlineAssembly>() .filterIsInstance<InlineAssembly>()
.any { .any { it.hasReturnOrRts(compTarget) }
instructions.any { instr->instr in it.assembly }
}
} }

View File

@ -150,6 +150,13 @@ internal class AstIdentifiersChecker(private val errors: IErrorReporter,
override fun visit(functionCallStatement: FunctionCallStatement) = visitFunctionCall(functionCallStatement) override fun visit(functionCallStatement: FunctionCallStatement) = visitFunctionCall(functionCallStatement)
private fun visitFunctionCall(call: IFunctionCall) { private fun visitFunctionCall(call: IFunctionCall) {
if(call.target.nameInSource==listOf("rnd") || call.target.nameInSource==listOf("rndw")) {
val target = call.target.targetStatement(program)
if(target==null) {
errors.err("rnd() and rndw() builtin functions have been moved into the math module", call.position)
return
}
}
when (val target = call.target.targetStatement(program)) { when (val target = call.target.targetStatement(program)) {
is Subroutine -> { is Subroutine -> {
val expectedNumberOfArgs: Int = target.parameters.size val expectedNumberOfArgs: Int = target.parameters.size

View File

@ -18,9 +18,8 @@ internal class AstOnetimeTransforms(private val program: Program, private val op
override fun after(arrayIndexedExpression: ArrayIndexedExpression, parent: Node): Iterable<IAstModification> { override fun after(arrayIndexedExpression: ArrayIndexedExpression, parent: Node): Iterable<IAstModification> {
if(parent !is VarDecl) { if(parent !is VarDecl) {
// TODO move this / remove this, and make the codegen better instead. if(options.compTarget.name == VMTarget.NAME)
// If the expression is pointervar[idx] where pointervar is uword and not a real array, return noModifications // vm codegen deals correctly with all cases
// replace it by a @(pointervar+idx) expression.
// Don't replace the initializer value in a vardecl - this will be moved to a separate // Don't replace the initializer value in a vardecl - this will be moved to a separate
// assignment statement soon in after(VarDecl) // assignment statement soon in after(VarDecl)
return replacePointerVarIndexWithMemreadOrMemwrite(arrayIndexedExpression, parent) return replacePointerVarIndexWithMemreadOrMemwrite(arrayIndexedExpression, parent)
@ -29,15 +28,16 @@ internal class AstOnetimeTransforms(private val program: Program, private val op
} }
private fun replacePointerVarIndexWithMemreadOrMemwrite(arrayIndexedExpression: ArrayIndexedExpression, parent: Node): Iterable<IAstModification> { private fun replacePointerVarIndexWithMemreadOrMemwrite(arrayIndexedExpression: ArrayIndexedExpression, parent: Node): Iterable<IAstModification> {
if(options.compTarget.name==VMTarget.NAME) // note: The CodeDesugarer already does something similar, but that is meant ONLY to take
return noModifications // vm codegen deals correctly with all cases // into account the case where the index value is a word type.
// The replacement here is to fix missing cases in the 6502 codegen.
// TODO make the 6502 codegen better so this work around can be removed
val arrayVar = arrayIndexedExpression.arrayvar.targetVarDecl(program) val arrayVar = arrayIndexedExpression.arrayvar.targetVarDecl(program)
if(arrayVar!=null && arrayVar.datatype == DataType.UWORD) { if(arrayVar!=null && arrayVar.datatype == DataType.UWORD) {
if(parent is AssignTarget) { if(parent is AssignTarget) {
val assignment = parent.parent as? Assignment val assignment = parent.parent as? Assignment
if(assignment?.value is NumericLiteral || assignment?.value is IdentifierReference) { if(assignment?.value is NumericLiteral || assignment?.value is IdentifierReference) {
// ONLY for a constant assignment, or direct variable assignment, the codegen contains correct optimized code. // the codegen contains correct optimized code ONLY for a constant assignment, or direct variable assignment.
return noModifications return noModifications
} }
// Other cases aren't covered correctly by the 6502 codegen, and there are a LOT of cases. // Other cases aren't covered correctly by the 6502 codegen, and there are a LOT of cases.

View File

@ -135,15 +135,20 @@ internal class BeforeAsmAstChanger(val program: Program,
val mods = mutableListOf<IAstModification>() val mods = mutableListOf<IAstModification>()
// add the implicit return statement at the end (if it's not there yet), but only if it's not a kernal routine. // add the implicit return statement at the end (if it's not there yet), but only if it's not a kernal routine.
// and if an assembly block doesn't contain a rts/rti, and some other situations. // and if an assembly block doesn't contain a rts/rti.
if (!subroutine.isAsmSubroutine) { if (!subroutine.isAsmSubroutine) {
if(subroutine.statements.isEmpty() || if(subroutine.isEmpty()) {
(!subroutine.hasRtsInAsm(options.compTarget)
&& subroutine.statements.lastOrNull { it !is VarDecl } !is Return
&& subroutine.statements.last() !is Subroutine
&& subroutine.statements.last() !is Return)) {
val returnStmt = Return(null, subroutine.position) val returnStmt = Return(null, subroutine.position)
mods += IAstModification.InsertLast(returnStmt, subroutine) mods += IAstModification.InsertLast(returnStmt, subroutine)
} else {
val last = subroutine.statements.last()
if((last !is InlineAssembly || !last.hasReturnOrRts(options.compTarget)) && last !is Return) {
val lastStatement = subroutine.statements.reversed().firstOrNull { it !is Subroutine }
if(lastStatement !is Return) {
val returnStmt = Return(null, subroutine.position)
mods += IAstModification.InsertLast(returnStmt, subroutine)
}
}
} }
} }
@ -167,8 +172,18 @@ internal class BeforeAsmAstChanger(val program: Program,
if (subroutine.isAsmSubroutine && subroutine.asmAddress==null && !subroutine.hasRtsInAsm(options.compTarget)) { if (subroutine.isAsmSubroutine && subroutine.asmAddress==null && !subroutine.hasRtsInAsm(options.compTarget)) {
// make sure the NOT INLINED asm subroutine actually has a rts at the end // make sure the NOT INLINED asm subroutine actually has a rts at the end
// (non-asm routines get a Return statement as needed, above) // (non-asm routines get a Return statement as needed, above)
val instruction = if(options.compTarget.name==VMTarget.NAME) " return\n" else " rts\n" mods += if(options.compTarget.name==VMTarget.NAME)
mods += IAstModification.InsertLast(InlineAssembly(instruction, Position.DUMMY), subroutine) IAstModification.InsertLast(InlineAssembly(" return\n", true, Position.DUMMY), subroutine)
else
IAstModification.InsertLast(InlineAssembly(" rts\n", false, Position.DUMMY), subroutine)
}
}
if(subroutine.isNotEmpty() && subroutine.statements.last() is Return) {
// maybe the last return can be removed because there is a fall-through prevention above it
val lastStatementBefore = subroutine.statements.reversed().drop(1).firstOrNull { it !is Subroutine }
if(lastStatementBefore is Return) {
mods += IAstModification.Remove(subroutine.statements.last(), subroutine)
} }
} }

View File

@ -129,4 +129,18 @@ internal class BeforeAsmTypecastCleaner(val program: Program,
} }
return noModifications return noModifications
} }
override fun after(expr: BinaryExpression, parent: Node): Iterable<IAstModification> {
if(expr.operator=="<<" || expr.operator==">>") {
val shifts = expr.right.constValue(program)
if(shifts!=null) {
val dt = expr.left.inferType(program)
if(dt.istype(DataType.UBYTE) && shifts.number>=8.0)
errors.warn("shift always results in 0", expr.position)
if(dt.istype(DataType.UWORD) && shifts.number>=16.0)
errors.warn("shift always results in 0", expr.position)
}
}
return noModifications
}
} }

View File

@ -10,8 +10,7 @@ import prog8.code.core.IErrorReporter
import prog8.code.core.Position import prog8.code.core.Position
internal class CodeDesugarer(val program: Program, internal class CodeDesugarer(val program: Program, private val errors: IErrorReporter) : AstWalker() {
private val errors: IErrorReporter) : AstWalker() {
// Some more code shuffling to simplify the Ast that the codegenerator has to process. // Some more code shuffling to simplify the Ast that the codegenerator has to process.
// Several changes have already been done by the StatementReorderer ! // Several changes have already been done by the StatementReorderer !
@ -23,6 +22,7 @@ internal class CodeDesugarer(val program: Program,
// - replace while and do-until loops by just jumps. // - replace while and do-until loops by just jumps.
// - replace peek() and poke() by direct memory accesses. // - replace peek() and poke() by direct memory accesses.
// - repeat-forever loops replaced by label+jump. // - repeat-forever loops replaced by label+jump.
// - pointer[word] replaced by @(pointer+word)
override fun before(breakStmt: Break, parent: Node): Iterable<IAstModification> { override fun before(breakStmt: Break, parent: Node): Iterable<IAstModification> {
@ -135,4 +135,32 @@ _after:
} }
return noModifications return noModifications
} }
override fun after(arrayIndexedExpression: ArrayIndexedExpression, parent: Node): Iterable<IAstModification> {
// replace pointervar[word] by @(pointervar+word) to avoid the
// "array indexing is limited to byte size 0..255" error for pointervariables.
val indexExpr = arrayIndexedExpression.indexer.indexExpr
val indexerDt = indexExpr.inferType(program)
if(indexerDt.isWords) {
val arrayVar = arrayIndexedExpression.arrayvar.targetVarDecl(program)!!
if(arrayVar.datatype==DataType.UWORD) {
val add: Expression =
if(indexExpr.constValue(program)?.number==0.0)
arrayIndexedExpression.arrayvar.copy()
else
BinaryExpression(arrayIndexedExpression.arrayvar.copy(), "+", indexExpr, arrayIndexedExpression.position)
return if(parent is AssignTarget) {
// assignment to array
val memwrite = DirectMemoryWrite(add, arrayIndexedExpression.position)
val newtarget = AssignTarget(null, null, memwrite, arrayIndexedExpression.position)
listOf(IAstModification.ReplaceNode(parent, newtarget, parent.parent))
} else {
// read from array
val memread = DirectMemoryRead(add, arrayIndexedExpression.position)
listOf(IAstModification.ReplaceNode(arrayIndexedExpression, memread, parent))
}
}
}
return noModifications
}
} }

View File

@ -197,7 +197,7 @@ class IntermediateAstMaker(val program: Program) {
"%asminclude" -> { "%asminclude" -> {
val result = loadAsmIncludeFile(directive.args[0].str!!, directive.definingModule.source) val result = loadAsmIncludeFile(directive.args[0].str!!, directive.definingModule.source)
val assembly = result.getOrElse { throw it } val assembly = result.getOrElse { throw it }
PtInlineAssembly(assembly, directive.position) PtInlineAssembly(assembly.trimEnd().trimStart('\r', '\n'), false, directive.position)
} }
else -> { else -> {
// other directives don't output any code (but could end up in option flags somewhere else) // other directives don't output any code (but could end up in option flags somewhere else)
@ -251,8 +251,10 @@ class IntermediateAstMaker(val program: Program) {
return ifelse return ifelse
} }
private fun transform(srcNode: InlineAssembly): PtInlineAssembly = private fun transform(srcNode: InlineAssembly): PtInlineAssembly {
PtInlineAssembly(srcNode.assembly, srcNode.position) val assembly = srcNode.assembly.trimEnd().trimStart('\r', '\n')
return PtInlineAssembly(assembly, srcNode.isIR, srcNode.position)
}
private fun transform(srcJump: Jump): PtJump { private fun transform(srcJump: Jump): PtJump {
val identifier = if(srcJump.identifier!=null) transform(srcJump.identifier!!) else null val identifier = if(srcJump.identifier!=null) transform(srcJump.identifier!!) else null
@ -306,13 +308,26 @@ class IntermediateAstMaker(val program: Program) {
sub.parameters.forEach { it.first.parent=sub } sub.parameters.forEach { it.first.parent=sub }
if(srcSub.asmAddress==null) { if(srcSub.asmAddress==null) {
var combinedAsm = "" var combinedTrueAsm = ""
for (asm in srcSub.statements) var combinedIrAsm = ""
combinedAsm += (asm as InlineAssembly).assembly + "\n" for (asm in srcSub.statements) {
if(combinedAsm.isNotEmpty()) asm as InlineAssembly
sub.add(PtInlineAssembly(combinedAsm, srcSub.statements[0].position)) if(asm.isIR)
else combinedIrAsm += asm.assembly + "\n"
sub.add(PtInlineAssembly("", srcSub.position)) else
combinedTrueAsm += asm.assembly + "\n"
}
if(combinedTrueAsm.isNotEmpty()) {
combinedTrueAsm = combinedTrueAsm.trimEnd().trimStart('\r', '\n')
sub.add(PtInlineAssembly(combinedTrueAsm, false, srcSub.statements[0].position))
}
if(combinedIrAsm.isNotEmpty()) {
combinedIrAsm = combinedIrAsm.trimEnd().trimStart('\r', '\n')
sub.add(PtInlineAssembly(combinedIrAsm, true, srcSub.statements[0].position))
}
if(combinedIrAsm.isEmpty() && combinedTrueAsm.isEmpty())
sub.add(PtInlineAssembly("", true, srcSub.position))
} }
return sub return sub
@ -320,9 +335,12 @@ class IntermediateAstMaker(val program: Program) {
private fun transformSub(srcSub: Subroutine): PtSub { private fun transformSub(srcSub: Subroutine): PtSub {
val (vardecls, statements) = srcSub.statements.partition { it is VarDecl } val (vardecls, statements) = srcSub.statements.partition { it is VarDecl }
var returntype = srcSub.returntypes.singleOrNull()
if(returntype==DataType.STR)
returntype=DataType.UWORD // if a sub returns 'str', replace with uword. Intermediate AST and I.R. don't contain 'str' datatype anymore.
val sub = PtSub(srcSub.name, val sub = PtSub(srcSub.name,
srcSub.parameters.map { PtSubroutineParameter(it.name, it.type, it.position) }, srcSub.parameters.map { PtSubroutineParameter(it.name, it.type, it.position) },
srcSub.returntypes.singleOrNull(), returntype,
srcSub.inline, srcSub.inline,
srcSub.position) srcSub.position)
sub.parameters.forEach { it.parent=sub } sub.parameters.forEach { it.parent=sub }

View File

@ -1,13 +1,11 @@
package prog8.compiler.astprocessing package prog8.compiler.astprocessing
import prog8.ast.* import prog8.ast.*
import prog8.ast.base.FatalAstException
import prog8.ast.expressions.* import prog8.ast.expressions.*
import prog8.ast.statements.* import prog8.ast.statements.*
import prog8.ast.walk.AstWalker import prog8.ast.walk.AstWalker
import prog8.ast.walk.IAstModification import prog8.ast.walk.IAstModification
import prog8.code.core.* import prog8.code.core.*
import prog8.compiler.BuiltinFunctions
internal class StatementReorderer(val program: Program, internal class StatementReorderer(val program: Program,
val errors: IErrorReporter, val errors: IErrorReporter,
@ -195,54 +193,6 @@ internal class StatementReorderer(val program: Program,
&& maySwapOperandOrder(expr)) && maySwapOperandOrder(expr))
return listOf(IAstModification.SwapOperands(expr)) return listOf(IAstModification.SwapOperands(expr))
// when using a simple bit shift and assigning it to a variable of a different type,
// try to make the bit shifting 'wide enough' to fall into the variable's type.
// with this, for instance, uword x = 1 << 10 will result in 1024 rather than 0 (the ubyte result).
if(expr.operator=="<<" || expr.operator==">>") {
val leftDt = expr.left.inferType(program)
when (parent) {
is Assignment -> {
val targetDt = parent.target.inferType(program)
if(leftDt != targetDt) {
val cast = TypecastExpression(expr.left, targetDt.getOr(DataType.UNDEFINED), true, parent.position)
return listOf(IAstModification.ReplaceNode(expr.left, cast, expr))
}
}
is VarDecl -> {
if(leftDt isnot parent.datatype) {
val cast = TypecastExpression(expr.left, parent.datatype, true, parent.position)
return listOf(IAstModification.ReplaceNode(expr.left, cast, expr))
}
}
is IFunctionCall -> {
val argnum = parent.args.indexOf(expr)
when (val callee = parent.target.targetStatement(program)) {
is Subroutine -> {
val paramType = callee.parameters[argnum].type
if(leftDt isAssignableTo paramType) {
val (replaced, cast) = expr.left.typecastTo(paramType, leftDt.getOr(DataType.UNDEFINED), true)
if(replaced)
return listOf(IAstModification.ReplaceNode(expr.left, cast, expr))
}
}
is BuiltinFunctionPlaceholder -> {
val func = BuiltinFunctions.getValue(callee.name)
val paramTypes = func.parameters[argnum].possibleDatatypes
for(type in paramTypes) {
if(leftDt isAssignableTo type) {
val (replaced, cast) = expr.left.typecastTo(type, leftDt.getOr(DataType.UNDEFINED), true)
if(replaced)
return listOf(IAstModification.ReplaceNode(expr.left, cast, expr))
}
}
}
else -> throw FatalAstException("weird callee")
}
}
else -> return noModifications
}
}
return noModifications return noModifications
} }

View File

@ -7,6 +7,8 @@ import prog8.ast.statements.*
import prog8.ast.walk.IAstVisitor import prog8.ast.walk.IAstVisitor
import prog8.code.* import prog8.code.*
import prog8.code.core.ArrayDatatypes import prog8.code.core.ArrayDatatypes
import prog8.code.core.CompilationOptions
import prog8.code.core.DataType
import prog8.code.core.Position import prog8.code.core.Position
import java.util.* import java.util.*
@ -14,10 +16,12 @@ internal class SymbolTableMaker: IAstVisitor {
private val st = SymbolTable() private val st = SymbolTable()
private val scopestack = Stack<StNode>() private val scopestack = Stack<StNode>()
private var dontReinitGlobals = false
fun makeFrom(program: Program): SymbolTable { fun makeFrom(program: Program, options: CompilationOptions): SymbolTable {
scopestack.clear() scopestack.clear()
st.children.clear() st.children.clear()
dontReinitGlobals = options.dontReinitGlobals
this.visit(program) this.visit(program)
program.builtinFunctions.names.forEach { program.builtinFunctions.names.forEach {
val node = StNode(it, StNodeType.BUILTINFUNC, Position.DUMMY) val node = StNode(it, StNodeType.BUILTINFUNC, Position.DUMMY)
@ -38,7 +42,7 @@ internal class SymbolTableMaker: IAstVisitor {
override fun visit(subroutine: Subroutine) { override fun visit(subroutine: Subroutine) {
if(subroutine.asmAddress!=null) { if(subroutine.asmAddress!=null) {
val parameters = subroutine.parameters.zip(subroutine.asmParameterRegisters).map { StRomSubParameter(it.second, it.first.type) } val parameters = subroutine.parameters.zip(subroutine.asmParameterRegisters).map { StRomSubParameter(it.second, it.first.type) }
val node = StRomSub(subroutine.name, subroutine.asmAddress!!, parameters, subroutine.asmParameterRegisters, subroutine.position) val node = StRomSub(subroutine.name, subroutine.asmAddress!!, parameters, subroutine.asmReturnvaluesRegisters, subroutine.position)
scopestack.peek().add(node) scopestack.peek().add(node)
// st.origAstLinks[subroutine] = node // st.origAstLinks[subroutine] = node
} else { } else {
@ -57,7 +61,7 @@ internal class SymbolTableMaker: IAstVisitor {
val node = val node =
when(decl.type) { when(decl.type) {
VarDeclType.VAR -> { VarDeclType.VAR -> {
val initialNumeric = (decl.value as? NumericLiteral)?.number var initialNumeric = (decl.value as? NumericLiteral)?.number
val initialStringLit = decl.value as? StringLiteral val initialStringLit = decl.value as? StringLiteral
val initialString = if(initialStringLit==null) null else Pair(initialStringLit.value, initialStringLit.encoding) val initialString = if(initialStringLit==null) null else Pair(initialStringLit.value, initialStringLit.encoding)
val initialArrayLit = decl.value as? ArrayLiteral val initialArrayLit = decl.value as? ArrayLiteral
@ -71,7 +75,15 @@ internal class SymbolTableMaker: IAstVisitor {
initialStringLit.value.length+1 // include the terminating 0-byte initialStringLit.value.length+1 // include the terminating 0-byte
else else
null null
StStaticVariable(decl.name, decl.datatype, initialNumeric, initialString, initialArray, numElements, decl.zeropage, decl.position) val bss = if(decl.datatype==DataType.STR)
false
else if(decl.isArray)
initialArray.isNullOrEmpty()
else {
if(dontReinitGlobals) initialNumeric = initialNumeric ?: 0.0
initialNumeric == null
}
StStaticVariable(decl.name, decl.datatype, bss, initialNumeric, initialString, initialArray, numElements, decl.zeropage, decl.position)
} }
VarDeclType.CONST -> StConstant(decl.name, decl.datatype, (decl.value as NumericLiteral).number, decl.position) VarDeclType.CONST -> StConstant(decl.name, decl.datatype, (decl.value as NumericLiteral).number, decl.position)
VarDeclType.MEMORY -> { VarDeclType.MEMORY -> {
@ -104,4 +116,16 @@ internal class SymbolTableMaker: IAstVisitor {
scopestack.peek().add(node) scopestack.peek().add(node)
// st.origAstLinks[label] = node // st.origAstLinks[label] = node
} }
override fun visit(bfc: BuiltinFunctionCall) {
if(bfc.name=="memory") {
// memory slab allocations are a builtin functioncall in the program, but end up named as well in the symboltable
val name = (bfc.args[0] as StringLiteral).value
require(name.all { it.isLetterOrDigit() || it=='_' }) {"memory name should be a valid symbol name"}
val size = (bfc.args[1] as NumericLiteral).number.toUInt()
val align = (bfc.args[2] as NumericLiteral).number.toUInt()
st.add(StMemorySlab("prog8_memoryslab_$name", size, align, bfc.position))
}
super.visit(bfc)
}
} }

View File

@ -5,6 +5,7 @@ import prog8.ast.Program
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.core.ByteDatatypes
import prog8.code.core.DataType import prog8.code.core.DataType
import prog8.code.core.IErrorReporter import prog8.code.core.IErrorReporter
import prog8.code.core.Position import prog8.code.core.Position
@ -82,7 +83,9 @@ internal class VerifyFunctionArgTypes(val program: Program, val errors: IErrorRe
if(mismatch>=0) { if(mismatch>=0) {
val actual = argtypes[mismatch] val actual = argtypes[mismatch]
val expected = consideredParamTypes[mismatch] val expected = consideredParamTypes[mismatch]
return if(expected==DataType.BOOL && actual==DataType.UBYTE && call.args[mismatch].constValue(program)?.number in setOf(0.0, 1.0)) return if(actual==DataType.BOOL && expected in ByteDatatypes)
null // a bool is just 1 or 0.
else if(expected==DataType.BOOL && actual==DataType.UBYTE && call.args[mismatch].constValue(program)?.number in setOf(0.0, 1.0))
null // specifying a 1 or 0 as a BOOL is okay null // specifying a 1 or 0 as a BOOL is okay
else else
Pair("argument ${mismatch + 1} type mismatch, was: $actual expected: $expected", call.args[mismatch].position) Pair("argument ${mismatch + 1} type mismatch, was: $actual expected: $expected", call.args[mismatch].position)
@ -91,6 +94,10 @@ internal class VerifyFunctionArgTypes(val program: Program, val errors: IErrorRe
if(target.asmReturnvaluesRegisters.size>1) { if(target.asmReturnvaluesRegisters.size>1) {
// multiple return values will NOT work inside an expression. // multiple return values will NOT work inside an expression.
// they MIGHT work in a regular assignment or just a function call statement. // they MIGHT work in a regular assignment or just a function call statement.
// EXCEPTION:
// if the asmsub returns multiple values and one of them is via a status register bit (such as carry),
// it *is* possible to handle them by just actually assigning the register value and
// dealing with the status bit as just being that, the status bit after the call.
val parent = if(call is Statement) call.parent else if(call is Expression) call.parent else null val parent = if(call is Statement) call.parent else if(call is Expression) call.parent else null
if (call !is FunctionCallStatement) { if (call !is FunctionCallStatement) {
val checkParent = val checkParent =
@ -99,7 +106,10 @@ internal class VerifyFunctionArgTypes(val program: Program, val errors: IErrorRe
else else
parent parent
if (checkParent !is Assignment && checkParent !is VarDecl) { if (checkParent !is Assignment && checkParent !is VarDecl) {
return Pair("can't use subroutine call that returns multiple return values here", call.position) val (returnRegisters, _) = target.asmReturnvaluesRegisters.partition { rr -> rr.registerOrPair != null }
if (returnRegisters.size>1) {
return Pair("can't use subroutine call that returns multiple return values here", call.position)
}
} }
} }
} }

View File

@ -44,18 +44,18 @@ class TestBuiltinFunctions: FunSpec({
conv.returns.reg shouldBe RegisterOrPair.A conv.returns.reg shouldBe RegisterOrPair.A
} }
test("not-pure func with fixed type") { test("not-pure func with varying result value type") {
val func = BuiltinFunctions.getValue("rnd") val func = BuiltinFunctions.getValue("cmp")
func.name shouldBe "rnd" func.name shouldBe "cmp"
func.parameters.size shouldBe 0 func.parameters.size shouldBe 2
func.pure shouldBe false func.pure shouldBe false
func.returnType shouldBe DataType.UBYTE func.returnType shouldBe null
val conv = func.callConvention(emptyList()) val conv = func.callConvention(listOf(DataType.UWORD, DataType.UWORD))
conv.params.size shouldBe 0 conv.params.size shouldBe 2
conv.returns.dt shouldBe DataType.UBYTE conv.returns.dt shouldBe null
conv.returns.floatFac1 shouldBe false conv.returns.floatFac1 shouldBe false
conv.returns.reg shouldBe RegisterOrPair.A conv.returns.reg shouldBe null
} }
test("func without return type") { test("func without return type") {

View File

@ -3,18 +3,15 @@ package prog8tests
import io.kotest.core.spec.style.FunSpec import io.kotest.core.spec.style.FunSpec
import io.kotest.matchers.shouldNotBe import io.kotest.matchers.shouldNotBe
import prog8.code.core.ICompilationTarget import prog8.code.core.ICompilationTarget
import prog8.code.target.C64Target import prog8.code.target.*
import prog8.code.target.Cx16Target
import prog8.compiler.CompilationResult import prog8.compiler.CompilationResult
import prog8.compiler.CompilerArguments import prog8.compiler.CompilerArguments
import prog8.compiler.compileProgram import prog8.compiler.compileProgram
import prog8tests.helpers.assumeDirectory import prog8tests.helpers.*
import prog8tests.helpers.cartesianProduct
import prog8tests.helpers.outputDir
import prog8tests.helpers.workingDir
import java.nio.file.Path import java.nio.file.Path
import kotlin.io.path.absolute import kotlin.io.path.absolute
import kotlin.io.path.exists import kotlin.io.path.exists
import kotlin.io.path.readText
/** /**
@ -47,8 +44,11 @@ private fun compileTheThing(filepath: Path, optimize: Boolean, target: ICompilat
private fun prepareTestFiles(source: String, optimize: Boolean, target: ICompilationTarget): Pair<String, Path> { private fun prepareTestFiles(source: String, optimize: Boolean, target: ICompilationTarget): Pair<String, Path> {
val searchIn = mutableListOf(examplesDir) val searchIn = mutableListOf(examplesDir)
if (target is Cx16Target) { when (target) {
searchIn.add(0, assumeDirectory(examplesDir, "cx16")) is Cx16Target -> searchIn.add(0, assumeDirectory(examplesDir, "cx16"))
is VMTarget -> searchIn.add(0, assumeDirectory(examplesDir, "vm"))
is C128Target -> searchIn.add(0, assumeDirectory(examplesDir, "c128"))
is AtariTarget -> searchIn.add(0, assumeDirectory(examplesDir, "atari"))
} }
val filepath = searchIn.asSequence() val filepath = searchIn.asSequence()
.map { it.resolve("$source.p8") } .map { it.resolve("$source.p8") }
@ -168,3 +168,24 @@ class TestCompilerOnExamplesBothC64andCx16: FunSpec({
} }
} }
}) })
class TestCompilerOnExamplesVirtual: FunSpec({
val onlyVirtual = listOf(
"bouncegfx",
"bsieve",
"pixelshader",
"sincos",
"textelite"
)
onlyVirtual.forEach {
val target = VMTarget()
val (displayName, filepath) = prepareTestFiles(it, false, target)
test(displayName) {
val src = filepath.readText()
compileText(target, false, src, writeAssembly = true, keepIR=false) shouldNotBe null
compileText(target, false, src, writeAssembly = true, keepIR=true) shouldNotBe null
}
}
})

View File

@ -87,7 +87,7 @@ class TestCompilerOptionSourcedirs: FunSpec({
test("testFilePathOutsideWorkingDirRelativeTo1stInSourcedirs") { test("testFilePathOutsideWorkingDirRelativeTo1stInSourcedirs") {
val filepath = assumeReadableFile(fixturesDir, "ast_simple_main.p8") val filepath = assumeReadableFile(fixturesDir, "ast_simple_main.p8")
val sourcedirs = listOf("${fixturesDir}") val sourcedirs = listOf("$fixturesDir")
compileFile(filepath.fileName, sourcedirs) shouldNotBe null compileFile(filepath.fileName, sourcedirs) shouldNotBe null
} }

View File

@ -0,0 +1,40 @@
package prog8tests
import io.kotest.core.spec.style.FunSpec
import prog8.code.target.VMTarget
import kotlin.io.path.deleteExisting
import kotlin.io.path.writeText
class TestLaunchEmu: FunSpec({
test("test launch virtualmachine via target") {
val target = VMTarget()
val tmpfile = kotlin.io.path.createTempFile(suffix=".p8ir")
tmpfile.writeText("""<PROGRAM NAME=test>
<OPTIONS>
</OPTIONS>
<ASMSYMBOLS>
</ASMSYMBOLS>
<VARIABLES>
</VARIABLES>
<MEMORYMAPPEDVARIABLES>
</MEMORYMAPPEDVARIABLES>
<MEMORYSLABS>
</MEMORYSLABS>
<INITGLOBALS>
</INITGLOBALS>
<BLOCK NAME=main ADDRESS=null ALIGN=NONE POS=[unittest: line 42 col 1-9]>
</BLOCK>
</PROGRAM>
""")
target.machine.launchEmulator(0, tmpfile)
tmpfile.deleteExisting()
}
})

View File

@ -258,8 +258,8 @@ class TestSubroutines: FunSpec({
sub start() { sub start() {
label() label()
label(1) label(1)
void rnd() void cmp(22,44)
void rnd(1) void cmp(11)
} }
} }
""" """

View File

@ -78,10 +78,10 @@ private fun makeSt(): SymbolTable {
block1.add(sub12) block1.add(sub12)
block1.add(StConstant("c1", DataType.UWORD, 12345.0, Position.DUMMY)) block1.add(StConstant("c1", DataType.UWORD, 12345.0, Position.DUMMY))
block1.add(StConstant("blockc", DataType.UWORD, 999.0, Position.DUMMY)) block1.add(StConstant("blockc", DataType.UWORD, 999.0, Position.DUMMY))
sub11.add(StStaticVariable("v1", DataType.BYTE, null, null, null, null, ZeropageWish.DONTCARE, Position.DUMMY)) sub11.add(StStaticVariable("v1", DataType.BYTE, true, null, null, null, null, ZeropageWish.DONTCARE, Position.DUMMY))
sub11.add(StStaticVariable("v2", DataType.BYTE, null, null, null, null, ZeropageWish.DONTCARE, Position.DUMMY)) sub11.add(StStaticVariable("v2", DataType.BYTE, true, null, null, null, null, ZeropageWish.DONTCARE, Position.DUMMY))
sub12.add(StStaticVariable("v1", DataType.BYTE, null, null, null, null, ZeropageWish.DONTCARE, Position.DUMMY)) sub12.add(StStaticVariable("v1", DataType.BYTE, true, null, null, null, null, ZeropageWish.DONTCARE, Position.DUMMY))
sub12.add(StStaticVariable("v2", DataType.BYTE, null, null, null, null, ZeropageWish.DONTCARE, Position.DUMMY)) sub12.add(StStaticVariable("v2", DataType.BYTE, true, null, null, null, null, ZeropageWish.DONTCARE, Position.DUMMY))
val block2 = StNode("block2", StNodeType.BLOCK, Position.DUMMY) val block2 = StNode("block2", StNodeType.BLOCK, Position.DUMMY)
val sub21 = StNode("sub1", StNodeType.SUBROUTINE, Position.DUMMY) val sub21 = StNode("sub1", StNodeType.SUBROUTINE, Position.DUMMY)

View File

@ -14,7 +14,6 @@ import prog8.ast.statements.VarDecl
import prog8.code.core.DataType import prog8.code.core.DataType
import prog8.code.core.Position import prog8.code.core.Position
import prog8.code.target.C64Target import prog8.code.target.C64Target
import prog8.code.target.VMTarget
import prog8tests.helpers.ErrorReporterForTests import prog8tests.helpers.ErrorReporterForTests
import prog8tests.helpers.compileText import prog8tests.helpers.compileText
@ -31,7 +30,7 @@ class TestTypecasts: FunSpec({
} }
}""" }"""
val errors = ErrorReporterForTests() val errors = ErrorReporterForTests()
val result = compileText(VMTarget(), false, text, writeAssembly = false, errors=errors) val result = compileText(C64Target(), false, text, writeAssembly = false, errors=errors)
result shouldBe null result shouldBe null
errors.errors.size shouldBe 1 errors.errors.size shouldBe 1
errors.errors[0] shouldContain "type mismatch, was: FLOAT expected one of: [UBYTE, BYTE, UWORD, WORD]" errors.errors[0] shouldContain "type mismatch, was: FLOAT expected one of: [UBYTE, BYTE, UWORD, WORD]"
@ -627,9 +626,9 @@ main {
ubyte @shared wordNr2 = (interlaced >= ${'$'}33) + (interlaced >= ${'$'}66) + (interlaced >= ${'$'}99) + (interlaced >= ${'$'}CC) ubyte @shared wordNr2 = (interlaced >= ${'$'}33) + (interlaced >= ${'$'}66) + (interlaced >= ${'$'}99) + (interlaced >= ${'$'}CC)
} }
}""" }"""
val result = compileText(VMTarget(), false, text, writeAssembly = true)!! val result = compileText(C64Target(), false, text, writeAssembly = true)!!
val stmts = result.program.entrypoint.statements val stmts = result.program.entrypoint.statements
stmts.size shouldBe 14 stmts.size shouldBeGreaterThan 10
} }
test("word to byte casts") { test("word to byte casts") {
@ -920,4 +919,19 @@ main {
}""" }"""
compileText(C64Target(), true, text, writeAssembly = true) shouldNotBe null compileText(C64Target(), true, text, writeAssembly = true) shouldNotBe null
} }
test("memory reads byte into word variable") {
val text = """
main {
sub start() {
uword @shared ww
uword address = $1000
ww = @(address+100)
ww = @(address+1000)
cx16.r0 = @(address+100)
cx16.r0 = @(address+1000)
}
}"""
compileText(C64Target(), false, text, writeAssembly = true) shouldNotBe null
}
}) })

View File

@ -2,6 +2,7 @@
%import floats %import floats
%import string %import string
%import syslib %import syslib
%import math
%import test_stack %import test_stack
%zeropage basicsafe %zeropage basicsafe
@ -281,17 +282,17 @@ main {
txt.nl() txt.nl()
ub = rnd() ub = math.rnd()
txt.print_ub(ub) txt.print_ub(ub)
txt.nl() txt.nl()
ub = zero+rnd()*1+zero ub = zero+math.rnd()*1+zero
txt.print_ub(ub) txt.print_ub(ub)
txt.nl() txt.nl()
uw = rndw() uw = math.rndw()
txt.print_uw(uw) txt.print_uw(uw)
txt.nl() txt.nl()
uw = zero+rndw()*1+zero uw = zero+math.rndw()*1+zero
txt.print_uw(uw) txt.print_uw(uw)
txt.nl() txt.nl()

View File

@ -10,14 +10,13 @@ import prog8.ast.statements.InlineAssembly
import prog8.ast.statements.VarDecl import prog8.ast.statements.VarDecl
import prog8.code.core.Position import prog8.code.core.Position
import prog8.code.target.C64Target import prog8.code.target.C64Target
import prog8.compiler.printProgram
import prog8tests.helpers.compileText import prog8tests.helpers.compileText
class TestVarious: FunSpec({ class TestVarious: FunSpec({
test("symbol names in inline assembly blocks") { test("symbol names in inline assembly blocks") {
val names1 = InlineAssembly(""" val names1 = InlineAssembly("""
""", Position.DUMMY).names """, false, Position.DUMMY).names
names1 shouldBe emptySet() names1 shouldBe emptySet()
val names2 = InlineAssembly(""" val names2 = InlineAssembly("""
@ -29,7 +28,7 @@ label2:
; also not these ; also not these
;; ...or these ;; ...or these
// valid words 123456 // valid words 123456
""", Position.DUMMY).names """, false, Position.DUMMY).names
names2 shouldBe setOf("label", "lda", "sta", "ea", "value", "label2", "othervalue", "valid", "words") names2 shouldBe setOf("label", "lda", "sta", "ea", "value", "label2", "othervalue", "valid", "words")
} }
@ -100,7 +99,6 @@ main {
} }
}""" }"""
val result = compileText(C64Target(), optimize=false, src, writeAssembly=true)!! val result = compileText(C64Target(), optimize=false, src, writeAssembly=true)!!
printProgram(result.program)
val stmts = result.program.entrypoint.statements val stmts = result.program.entrypoint.statements
stmts.size shouldBe 6 stmts.size shouldBe 6
val name1 = stmts[0] as VarDecl val name1 = stmts[0] as VarDecl
@ -114,5 +112,22 @@ main {
(name2.targetVarDecl(result.program)!!.value as StringLiteral).value shouldBe "xx1xx2" (name2.targetVarDecl(result.program)!!.value as StringLiteral).value shouldBe "xx1xx2"
(rept2.targetVarDecl(result.program)!!.value as StringLiteral).value shouldBe "xyzxyzxyzxyz" (rept2.targetVarDecl(result.program)!!.value as StringLiteral).value shouldBe "xyzxyzxyzxyz"
} }
test("pointervariable indexing allowed with >255") {
val src="""
main {
sub start() {
uword pointer = ${'$'}2000
@(pointer+${'$'}1000) = 123
ubyte @shared ub = @(pointer+${'$'}1000)
pointer[${'$'}1000] = 99
ub = pointer[${'$'}1000]
uword index = ${'$'}1000
pointer[index] = 55
ub = pointer[index]
}
}"""
compileText(C64Target(), optimize=false, src, writeAssembly=false) shouldNotBe null
}
}) })

View File

@ -0,0 +1,93 @@
package prog8tests.codegeneration
import io.kotest.core.spec.style.FunSpec
import io.kotest.matchers.shouldNotBe
import prog8.code.target.C64Target
import prog8.code.target.VMTarget
import prog8tests.helpers.compileText
class TestArrayInplaceAssign: FunSpec({
test("assign prefix var to array should compile fine and is not split into inplace array modification") {
val text = """
main {
sub start() {
byte[5] array
byte bb
array[1] = -bb
}
}
"""
compileText(C64Target(), false, text, writeAssembly = true) shouldNotBe null
compileText(VMTarget(), false, text, writeAssembly = true) shouldNotBe null
}
test("array in-place negation (integer types)") {
val text = """
main {
byte[10] foo
ubyte[10] foou
word[10] foow
uword[10] foowu
sub start() {
foo[1] = 42
foo[1] = -foo[1]
foow[1] = 4242
foow[1] = -foow[1]
}
}"""
compileText(C64Target(), false, text, writeAssembly = true) shouldNotBe null
compileText(VMTarget(), false, text, writeAssembly = true) shouldNotBe null
}
test("array in-place negation (float type) vm target") {
val text = """
%import floats
main {
float[10] flt
sub start() {
flt[1] = 42.42
flt[1] = -flt[1]
}
}"""
compileText(VMTarget(), false, text, writeAssembly = true) shouldNotBe null
}
// TODO implement this in 6502 codegen and re-enable test
xtest("array in-place negation (float type) 6502 target") {
val text = """
%import floats
main {
float[10] flt
sub start() {
flt[1] = 42.42
flt[1] = -flt[1]
}
}"""
compileText(C64Target(), false, text, writeAssembly = true) shouldNotBe null
}
test("array in-place invert") {
val text = """
main {
ubyte[10] foo
uword[10] foow
sub start() {
foo[1] = 42
foo[1] = ~foo[1]
foow[1] = 4242
foow[1] = ~foow[1]
}
}"""
compileText(C64Target(), false, text, writeAssembly = true) shouldNotBe null
compileText(VMTarget(), false, text, writeAssembly = true) shouldNotBe null
}
})

View File

@ -71,7 +71,7 @@ class TestAsmGenSymbols: StringSpec({
val errors = ErrorReporterForTests() val errors = ErrorReporterForTests()
val options = CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FULL, emptyList(), false, true, C64Target(), 999u) val options = CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FULL, emptyList(), false, true, C64Target(), 999u)
options.compTarget.machine.zeropage = C64Zeropage(options) options.compTarget.machine.zeropage = C64Zeropage(options)
val st = SymbolTableMaker().makeFrom(program) val st = SymbolTableMaker().makeFrom(program, options)
return AsmGen(program, st, options, errors) return AsmGen(program, st, options, errors)
} }

View File

@ -1,15 +1,10 @@
package prog8tests.helpers package prog8tests.helpers
import prog8.ast.Program import prog8.code.core.ICompilationTarget
import prog8.code.core.* import prog8.code.core.IErrorReporter
import prog8.code.target.C64Target
import prog8.code.target.c64.C64Zeropage
import prog8.codegen.cpu6502.AsmGen
import prog8.compiler.CompilationResult import prog8.compiler.CompilationResult
import prog8.compiler.CompilerArguments import prog8.compiler.CompilerArguments
import prog8.compiler.astprocessing.SymbolTableMaker
import prog8.compiler.compileProgram import prog8.compiler.compileProgram
import prog8.compiler.determineProgramLoadAddress
import java.nio.file.Path import java.nio.file.Path
import kotlin.io.path.name import kotlin.io.path.name
@ -22,6 +17,7 @@ internal fun compileFile(
outputDir: Path = prog8tests.helpers.outputDir, outputDir: Path = prog8tests.helpers.outputDir,
errors: IErrorReporter? = null, errors: IErrorReporter? = null,
writeAssembly: Boolean = true, writeAssembly: Boolean = true,
keepIR: Boolean = true,
optFloatExpr: Boolean = true optFloatExpr: Boolean = true
) : CompilationResult? { ) : CompilationResult? {
val filepath = fileDir.resolve(fileName) val filepath = fileDir.resolve(fileName)
@ -36,7 +32,7 @@ internal fun compileFile(
quietAssembler = true, quietAssembler = true,
asmListfile = false, asmListfile = false,
experimentalCodegen = false, experimentalCodegen = false,
keepIR = false, keepIR = keepIR,
platform.name, platform.name,
evalStackBaseAddress = null, evalStackBaseAddress = null,
symbolDefs = emptyMap(), symbolDefs = emptyMap(),
@ -57,30 +53,12 @@ internal fun compileText(
sourceText: String, sourceText: String,
errors: IErrorReporter? = null, errors: IErrorReporter? = null,
writeAssembly: Boolean = true, writeAssembly: Boolean = true,
keepIR: Boolean = true,
optFloatExpr: Boolean = true optFloatExpr: Boolean = true
) : CompilationResult? { ) : CompilationResult? {
val filePath = outputDir.resolve("on_the_fly_test_" + sourceText.hashCode().toUInt().toString(16) + ".p8") val filePath = outputDir.resolve("on_the_fly_test_" + sourceText.hashCode().toUInt().toString(16) + ".p8")
// we don't assumeNotExists(filePath) - should be ok to just overwrite it // we don't assumeNotExists(filePath) - should be ok to just overwrite it
filePath.toFile().writeText(sourceText) filePath.toFile().writeText(sourceText)
return compileFile(platform, optimize, filePath.parent, filePath.name, errors=errors, writeAssembly=writeAssembly, optFloatExpr = optFloatExpr) return compileFile(platform, optimize, filePath.parent, filePath.name,
} errors=errors, writeAssembly=writeAssembly, optFloatExpr = optFloatExpr, keepIR=keepIR)
internal fun generateAssembly(
program: Program,
options: CompilationOptions? = null
): IAssemblyProgram? {
val coptions = options ?: CompilationOptions(OutputType.RAW, CbmPrgLauncherType.BASIC, ZeropageType.DONTUSE, emptyList(),
floats = true,
noSysInit = true,
compTarget = C64Target(),
loadAddress = 0u, outputDir = outputDir)
coptions.compTarget.machine.zeropage = C64Zeropage(coptions)
val st = SymbolTableMaker().makeFrom(program)
val errors = ErrorReporterForTests()
determineProgramLoadAddress(program, coptions, errors)
errors.report()
val asmgen = AsmGen(program, st, coptions, errors)
errors.report()
return asmgen.compileToAssembly()
} }

View File

@ -57,7 +57,7 @@ class PathsHelpersTests: FunSpec({
test("on existing directory") { test("on existing directory") {
shouldThrow<java.lang.AssertionError> { shouldThrow<java.lang.AssertionError> {
assumeNotExists("${fixturesDir}") assumeNotExists("$fixturesDir")
} }
} }
} }
@ -157,13 +157,13 @@ class PathsHelpersTests: FunSpec({
context("WithStringAndStringArgs") { context("WithStringAndStringArgs") {
test("on non-existing path") { test("on non-existing path") {
shouldThrow<AssertionError> { shouldThrow<AssertionError> {
assumeDirectory("${fixturesDir}", "i_do_not_exist") assumeDirectory("$fixturesDir", "i_do_not_exist")
} }
} }
test("on existing file") { test("on existing file") {
shouldThrow<AssertionError> { shouldThrow<AssertionError> {
assumeDirectory("${fixturesDir}", "ast_simple_main.p8") assumeDirectory("$fixturesDir", "ast_simple_main.p8")
} }
} }
@ -178,13 +178,13 @@ class PathsHelpersTests: FunSpec({
context("WithStringAndPathArgs") { context("WithStringAndPathArgs") {
test("on non-existing path") { test("on non-existing path") {
shouldThrow<AssertionError> { shouldThrow<AssertionError> {
assumeDirectory("${fixturesDir}", Path("i_do_not_exist")) assumeDirectory("$fixturesDir", Path("i_do_not_exist"))
} }
} }
test("on existing file") { test("on existing file") {
shouldThrow<AssertionError> { shouldThrow<AssertionError> {
assumeDirectory("${fixturesDir}", Path("ast_simple_main.p8")) assumeDirectory("$fixturesDir", Path("ast_simple_main.p8"))
} }
} }
@ -240,7 +240,7 @@ class PathsHelpersTests: FunSpec({
test("on directory") { test("on directory") {
shouldThrow<AssertionError> { shouldThrow<AssertionError> {
assumeReadableFile("${fixturesDir}") assumeReadableFile("$fixturesDir")
} }
} }
} }
@ -289,7 +289,7 @@ class PathsHelpersTests: FunSpec({
context("WithStringAndStringArgs") { context("WithStringAndStringArgs") {
test("on non-existing path") { test("on non-existing path") {
shouldThrow<java.lang.AssertionError> { shouldThrow<java.lang.AssertionError> {
assumeReadableFile("${fixturesDir}", "i_do_not_exist") assumeReadableFile("$fixturesDir", "i_do_not_exist")
} }
} }
@ -301,7 +301,7 @@ class PathsHelpersTests: FunSpec({
test("on directory") { test("on directory") {
shouldThrow<AssertionError> { shouldThrow<AssertionError> {
assumeReadableFile("${fixturesDir}", "..") assumeReadableFile("$fixturesDir", "..")
} }
} }
} }

View File

@ -0,0 +1,245 @@
package prog8tests.vm
import io.kotest.assertions.throwables.shouldThrow
import io.kotest.core.spec.style.FunSpec
import io.kotest.matchers.shouldBe
import io.kotest.matchers.shouldNotBe
import io.kotest.matchers.string.shouldContain
import prog8.ast.expressions.BuiltinFunctionCall
import prog8.ast.statements.Assignment
import prog8.code.target.C64Target
import prog8.code.target.Cx16Target
import prog8.code.target.VMTarget
import prog8.vm.VmRunner
import prog8tests.helpers.compileText
import kotlin.io.path.readText
class TestCompilerVirtual: FunSpec({
test("compile virtual: any all sort reverse builtin funcs") {
val src = """
main {
sub start() {
uword[] words = [1111,2222,0,4444,3333]
ubyte result = all(words)
result++
result = any(words)
result++
sort(words)
reverse(words)
}
}"""
val target = VMTarget()
val result = compileText(target, true, src, writeAssembly = true)!!
val virtfile = result.compilationOptions.outputDir.resolve(result.program.name + ".p8ir")
VmRunner().runProgram(virtfile.readText())
}
test("compile virtual: array with pointers") {
val src = """
main {
sub start() {
str localstr = "hello"
ubyte[] otherarray = [1,2,3]
uword[] words = [1111,2222,"three",&localstr,&otherarray]
uword @shared zz = &words
ubyte result = 2222 in words
zz = words[2]
zz++
zz = words[3]
}
}"""
val othertarget = Cx16Target()
compileText(othertarget, true, src, writeAssembly = true, keepIR=true) shouldNotBe null
val target = VMTarget()
val result = compileText(target, true, src, writeAssembly = true)!!
val virtfile = result.compilationOptions.outputDir.resolve(result.program.name + ".p8ir")
VmRunner().runProgram(virtfile.readText())
}
test("compile virtual: str args and return type") {
val src = """
main {
sub start() {
sub testsub(str s1) -> str {
return "result"
}
uword result = testsub("arg")
}
}"""
val target = VMTarget()
var result = compileText(target, false, src, writeAssembly = true)!!
var virtfile = result.compilationOptions.outputDir.resolve(result.program.name + ".p8ir")
VmRunner().runProgram(virtfile.readText())
result = compileText(target, true, src, writeAssembly = true)!!
virtfile = result.compilationOptions.outputDir.resolve(result.program.name + ".p8ir")
VmRunner().runProgram(virtfile.readText())
}
test("compile virtual: nested labels") {
val src = """
main {
sub start() {
uword i
uword k
repeat {
mylabel0:
goto mylabel0
}
while cx16.r0 {
mylabel1:
goto mylabel1
}
do {
mylabel2:
goto mylabel2
} until cx16.r0
repeat cx16.r0 {
mylabel3:
goto mylabel3
}
for cx16.r0L in 0 to 2 {
mylabel4:
goto mylabel4
}
for cx16.r0L in cx16.r1L to cx16.r2L {
mylabel5:
goto mylabel5
}
mylabel_outside:
for i in 0 to 10 {
mylabel_inside:
if i==100 {
goto mylabel_outside
goto mylabel_inside
}
while k <= 10 {
k++
}
do {
k--
} until k==0
for k in 0 to 5 {
i++
}
repeat 10 {
k++
}
}
}
}"""
val target1 = C64Target()
compileText(target1, false, src, writeAssembly = false) shouldNotBe null
val target = VMTarget()
compileText(target, false, src, writeAssembly = true) shouldNotBe null
}
test("case sensitive symbols") {
val src = """
main {
sub start() {
ubyte bytevar = 11 ; var at 0
ubyte byteVAR = 22 ; var at 1
ubyte ByteVar = 33 ; var at 2
ubyte @shared total = bytevar+byteVAR+ByteVar ; var at 3
goto skipLABEL
SkipLabel:
return
skipLABEL:
bytevar = 42
}
}"""
val othertarget = Cx16Target()
compileText(othertarget, true, src, writeAssembly = true, keepIR=true) shouldNotBe null
val target = VMTarget()
val result = compileText(target, true, src, writeAssembly = true)!!
val virtfile = result.compilationOptions.outputDir.resolve(result.program.name + ".p8ir")
VmRunner().runAndTestProgram(virtfile.readText()) { vm ->
vm.memory.getUB(0) shouldBe 42u
vm.memory.getUB(3) shouldBe 66u
}
}
test("memory slabs") {
val src = """
main {
sub start() {
uword slab1 = memory("slab1", 2000, 64)
slab1[10]=42
slab1[11]=43
ubyte @shared value1 = slab1[10] ; var at 2
ubyte @shared value2 = slab1[11] ; var at 3
}
}"""
val target = VMTarget()
val result = compileText(target, true, src, writeAssembly = true)!!
val start = result.program.entrypoint
start.statements.size shouldBe 9
((start.statements[1] as Assignment).value as BuiltinFunctionCall).name shouldBe "memory"
val virtfile = result.compilationOptions.outputDir.resolve(result.program.name + ".p8ir")
VmRunner().runAndTestProgram(virtfile.readText()) { vm ->
vm.memory.getUB(2) shouldBe 42u
vm.memory.getUB(3) shouldBe 43u
}
}
test("memory mapped var as for loop counter") {
val src = """
main {
sub start() {
for cx16.r0 in 0 to 10 {
cx16.r1++
}
}
}"""
val othertarget = Cx16Target()
compileText(othertarget, true, src, writeAssembly = true, keepIR=true) shouldNotBe null
val target = VMTarget()
val result = compileText(target, false, src, writeAssembly = true)!!
val virtfile = result.compilationOptions.outputDir.resolve(result.program.name + ".p8ir")
VmRunner().runAndTestProgram(virtfile.readText()) { vm ->
vm.stepCount shouldBe 49
}
}
test("asmsub for virtual target") {
val src = """
main {
sub start() {
void test(42)
}
asmsub test(ubyte xx @A) -> ubyte @Y {
%asm {{
lda #99
tay
rts
return
}}
}
}"""
val othertarget = Cx16Target()
compileText(othertarget, true, src, writeAssembly = true, keepIR=true) shouldNotBe null
val target = VMTarget()
val result = compileText(target, false, src, writeAssembly = true)!!
val virtfile = result.compilationOptions.outputDir.resolve(result.program.name + ".p8ir")
val exc = shouldThrow<Exception> {
VmRunner().runProgram(virtfile.readText())
}
exc.message shouldContain("does not support non-IR asmsubs")
}
})

View File

@ -8,7 +8,7 @@ import prog8.ast.expressions.*
import prog8.ast.statements.* import prog8.ast.statements.*
import prog8.code.core.* import prog8.code.core.*
import prog8.parser.Prog8ANTLRParser import prog8.parser.Prog8ANTLRParser
import java.nio.file.Path import kotlin.io.path.Path
import kotlin.io.path.isRegularFile import kotlin.io.path.isRegularFile
@ -20,7 +20,7 @@ private data class NumericLiteralNode(val number: Double, val datatype: DataType
private fun ParserRuleContext.toPosition() : Position { private fun ParserRuleContext.toPosition() : Position {
val pathString = start.inputStream.sourceName val pathString = start.inputStream.sourceName
val filename = if(SourceCode.isRegularFilesystemPath(pathString)) { val filename = if(SourceCode.isRegularFilesystemPath(pathString)) {
val path = Path.of(pathString) val path = Path(pathString)
if(path.isRegularFile()) { if(path.isRegularFile()) {
SourceCode.relative(path).toString() SourceCode.relative(path).toString()
} else { } else {
@ -40,6 +40,7 @@ internal fun Prog8ANTLRParser.BlockContext.toAst(isInLibrary: Boolean) : Block {
it.subroutinedeclaration()!=null -> it.subroutinedeclaration().toAst() it.subroutinedeclaration()!=null -> it.subroutinedeclaration().toAst()
it.directive()!=null -> it.directive().toAst() it.directive()!=null -> it.directive().toAst()
it.inlineasm()!=null -> it.inlineasm().toAst() it.inlineasm()!=null -> it.inlineasm().toAst()
it.inlineir()!=null -> it.inlineir().toAst()
it.labeldef()!=null -> it.labeldef().toAst() it.labeldef()!=null -> it.labeldef().toAst()
else -> throw FatalAstException("weird block node $it") else -> throw FatalAstException("weird block node $it")
} }
@ -125,6 +126,9 @@ private fun Prog8ANTLRParser.StatementContext.toAst() : Statement {
val asm = inlineasm()?.toAst() val asm = inlineasm()?.toAst()
if(asm!=null) return asm if(asm!=null) return asm
val ir = inlineir()?.toAst()
if(ir!=null) return ir
val branchstmt = branch_stmt()?.toAst() val branchstmt = branch_stmt()?.toAst()
if(branchstmt!=null) return branchstmt if(branchstmt!=null) return branchstmt
@ -252,7 +256,12 @@ private fun Prog8ANTLRParser.FunctioncallContext.toAst(): FunctionCallExpression
private fun Prog8ANTLRParser.InlineasmContext.toAst(): InlineAssembly { private fun Prog8ANTLRParser.InlineasmContext.toAst(): InlineAssembly {
val text = INLINEASMBLOCK().text val text = INLINEASMBLOCK().text
return InlineAssembly(text.substring(2, text.length-2), toPosition()) return InlineAssembly(text.substring(2, text.length-2), false, toPosition())
}
private fun Prog8ANTLRParser.InlineirContext.toAst(): InlineAssembly {
val text = INLINEASMBLOCK().text
return InlineAssembly(text.substring(2, text.length-2), true, toPosition())
} }
private fun Prog8ANTLRParser.ReturnstmtContext.toAst() : Return { private fun Prog8ANTLRParser.ReturnstmtContext.toAst() : Return {

View File

@ -7,6 +7,7 @@ import prog8.ast.expressions.*
import prog8.ast.walk.AstWalker import prog8.ast.walk.AstWalker
import prog8.ast.walk.IAstVisitor import prog8.ast.walk.IAstVisitor
import prog8.code.core.* import prog8.code.core.*
import prog8.code.target.VMTarget
interface INamedStatement { interface INamedStatement {
@ -616,19 +617,28 @@ class FunctionCallStatement(override var target: IdentifierReference,
override fun toString() = "FunctionCallStatement(target=$target, pos=$position)" override fun toString() = "FunctionCallStatement(target=$target, pos=$position)"
} }
class InlineAssembly(val assembly: String, override val position: Position) : Statement() { class InlineAssembly(val assembly: String, val isIR: Boolean, override val position: Position) : Statement() {
override lateinit var parent: Node override lateinit var parent: Node
override fun linkParents(parent: Node) { override fun linkParents(parent: Node) {
this.parent = parent this.parent = parent
} }
override fun copy() = InlineAssembly(assembly, position) override fun copy() = InlineAssembly(assembly, isIR, position)
override fun replaceChildNode(node: Node, replacement: Node) = throw FatalAstException("can't replace here") override fun replaceChildNode(node: Node, replacement: Node) = throw FatalAstException("can't replace here")
override fun accept(visitor: IAstVisitor) = visitor.visit(this) override fun accept(visitor: IAstVisitor) = visitor.visit(this)
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent) override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
fun hasReturnOrRts(target: ICompilationTarget): Boolean {
return if(target.name!= VMTarget.NAME) {
" rti" in assembly || "\trti" in assembly || " rts" in assembly || "\trts" in assembly ||
" jmp" in assembly || "\tjmp" in assembly || " bra" in assembly || "\tbra" in assembly
} else {
" return" in assembly || "\treturn" in assembly || " jump" in assembly || "\tjump" in assembly || " jumpa" in assembly || "\tjumpa" in assembly
}
}
val names: Set<String> by lazy { val names: Set<String> by lazy {
// A cache of all the words (identifiers) present in this block of assembly code // A cache of all the words (identifiers) present in this block of assembly code

View File

@ -119,8 +119,6 @@ private val functionSignatures: List<FSignature> = listOf(
FSignature("rsavex" , false, emptyList(), null), FSignature("rsavex" , false, emptyList(), null),
FSignature("rrestore" , false, emptyList(), null), FSignature("rrestore" , false, emptyList(), null),
FSignature("rrestorex" , false, emptyList(), null), FSignature("rrestorex" , false, emptyList(), null),
FSignature("rnd" , false, emptyList(), DataType.UBYTE),
FSignature("rndw" , false, emptyList(), DataType.UWORD),
FSignature("memory" , true, listOf(FParam("name", arrayOf(DataType.STR)), FParam("size", arrayOf(DataType.UWORD)), FParam("alignment", arrayOf(DataType.UWORD))), DataType.UWORD), FSignature("memory" , true, listOf(FParam("name", arrayOf(DataType.STR)), FParam("size", arrayOf(DataType.UWORD)), FParam("alignment", arrayOf(DataType.UWORD))), DataType.UWORD),
FSignature("callfar" , false, listOf(FParam("bank", arrayOf(DataType.UBYTE)), FParam("address", arrayOf(DataType.UWORD)), FParam("arg", arrayOf(DataType.UWORD))), null), FSignature("callfar" , false, listOf(FParam("bank", arrayOf(DataType.UBYTE)), FParam("address", arrayOf(DataType.UWORD)), FParam("arg", arrayOf(DataType.UWORD))), null),
FSignature("callrom" , false, listOf(FParam("bank", arrayOf(DataType.UBYTE)), FParam("address", arrayOf(DataType.UWORD)), FParam("arg", arrayOf(DataType.UWORD))), null), FSignature("callrom" , false, listOf(FParam("bank", arrayOf(DataType.UBYTE)), FParam("address", arrayOf(DataType.UWORD)), FParam("arg", arrayOf(DataType.UWORD))), null),

View File

@ -1,2 +1,2 @@
sphinx>=4.0 sphinx>=4.4.0, !=5.2.0.post0 # https://github.com/sphinx-doc/sphinx/issues/10860
sphinx_rtd_theme==1.0.0 sphinx_rtd_theme>=1.0.0

View File

@ -68,7 +68,7 @@ It contains all of the program's code and data and has a certain file format tha
allows it to be loaded directly on the target system. Prog8 currently has no built-in allows it to be loaded directly on the target system. Prog8 currently has no built-in
support for programs that exceed 64 Kb of memory, nor for multi-part loaders. support for programs that exceed 64 Kb of memory, nor for multi-part loaders.
For the Commodore-64, most programs will have a tiny BASIC launcher that does a SYS into the generated machine code. For the Commodore 64, most programs will have a tiny BASIC launcher that does a SYS into the generated machine code.
This way the user can load it as any other program and simply RUN it to start. (This is a regular ".prg" program). This way the user can load it as any other program and simply RUN it to start. (This is a regular ".prg" program).
Prog8 can create those, but it is also possible to output plain binary programs Prog8 can create those, but it is also possible to output plain binary programs
that can be loaded into memory anywhere. that can be loaded into memory anywhere.
@ -95,7 +95,7 @@ For normal use the compiler can be invoked with the command:
By default, assembly code is generated and written to ``sourcefile.asm``. By default, assembly code is generated and written to ``sourcefile.asm``.
It is then (automatically) fed to the `64tass <https://sourceforge.net/projects/tass64/>`_ assembler tool It is then (automatically) fed to the `64tass <https://sourceforge.net/projects/tass64/>`_ assembler tool
that creastes the final runnable program. that creates the final runnable program.
Command line options Command line options
@ -138,7 +138,7 @@ One or more .p8 module files
``-noreinit`` ``-noreinit``
Don't create code to reinitialize the global (block level) variables on every run of the program. Don't create code to reinitialize the global (block level) variables on every run of the program.
Also means that all such variables are no longer placed in the zero page. Also means that all such variables are no longer placed in the zeropage.
Sometimes the program will be a lot shorter when using this, but sometimes the opposite happens. Sometimes the program will be a lot shorter when using this, but sometimes the opposite happens.
When using this option, it is no longer be possible to run the program correctly more than once! When using this option, it is no longer be possible to run the program correctly more than once!
*Experimental feature*: still has some problems! *Experimental feature*: still has some problems!
@ -182,7 +182,7 @@ One or more .p8 module files
``-esa <address>`` ``-esa <address>``
Override the base address of the evaluation stack. Has to be page-aligned. Override the base address of the evaluation stack. Has to be page-aligned.
You can specify an integer or hexadecimal address. You can specify an integer or hexadecimal address.
When not compiling for the CommanderX16 target, the location of the 16 virtual registers cx16.r0..r15 When not compiling for the Commander X16 target, the location of the 16 virtual registers cx16.r0..r15
is changed accordingly (to keep them in the same memory space as the evaluation stack). is changed accordingly (to keep them in the same memory space as the evaluation stack).
@ -210,26 +210,26 @@ the ``srcdirs`` command line option.
.. _debugging: .. _debugging:
Debugging (with Vice) Debugging (with VICE)
--------------------- ---------------------
There's support for using the monitor and debugging capabilities of the rather excellent There's support for using the monitor and debugging capabilities of the rather excellent
`Vice emulator <http://vice-emu.sourceforge.net/>`_. `VICE emulator <http://vice-emu.sourceforge.net/>`_.
The ``%breakpoint`` directive (see :ref:`directives`) in the source code instructs the compiler to put The ``%breakpoint`` directive (see :ref:`directives`) in the source code instructs the compiler to put
a *breakpoint* at that position. Some systems use a BRK instruction for this, but a *breakpoint* at that position. Some systems use a BRK instruction for this, but
this will usually halt the machine altogether instead of just suspending execution. this will usually halt the machine altogether instead of just suspending execution.
Prog8 issues a NOP instruction instead and creates a 'virtual' breakpoint at this position. Prog8 issues a NOP instruction instead and creates a 'virtual' breakpoint at this position.
All breakpoints are then written to a file called "programname.vice-mon-list", All breakpoints are then written to a file called "programname.vice-mon-list",
which is meant to be used by the Vice emulator. which is meant to be used by the VICE emulator.
It contains a series of commands for Vice's monitor, including source labels and the breakpoint settings. It contains a series of commands for VICE's monitor, including source labels and the breakpoint settings.
If you use the emulator autostart feature of the compiler, it will take care of this for you. If you use the emulator autostart feature of the compiler, it will take care of this for you.
If you launch Vice manually, you'll have to use a command line option to load this file: If you launch VICE manually, you'll have to use a command line option to load this file:
``$ x64 -moncommands programname.vice-mon-list`` ``$ x64 -moncommands programname.vice-mon-list``
Vice will then use the label names in memory disassembly, and will activate any breakpoints as well. VICE will then use the label names in memory disassembly, and will activate any breakpoints as well.
If your running program hits one of the breakpoints, Vice will halt execution and drop you into the monitor. If your running program hits one of the breakpoints, VICE will halt execution and drop you into the monitor.
Troubleshooting Troubleshooting

View File

@ -15,7 +15,7 @@ This is a compiled programming language targeting the 8-bit
`6510 <https://en.wikipedia.org/wiki/MOS_Technology_6510>`_ / `6510 <https://en.wikipedia.org/wiki/MOS_Technology_6510>`_ /
`65c02 <https://en.wikipedia.org/wiki/MOS_Technology_65C02>`_ microprocessors. `65c02 <https://en.wikipedia.org/wiki/MOS_Technology_65C02>`_ microprocessors.
This CPU is from the late 1970's and early 1980's and was used in many home computers from that era, This CPU is from the late 1970's and early 1980's and was used in many home computers from that era,
such as the `Commodore-64 <https://en.wikipedia.org/wiki/Commodore_64>`_. such as the `Commodore 64 <https://en.wikipedia.org/wiki/Commodore_64>`_.
The language aims to provide many conveniences over raw assembly code (even when using a macro assembler), The language aims to provide many conveniences over raw assembly code (even when using a macro assembler),
while still being low level enough to create high performance programs. while still being low level enough to create high performance programs.
You can compile programs for various machines with this CPU: You can compile programs for various machines with this CPU:
@ -69,12 +69,12 @@ Language features
- 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 if the target system provides floating point library routines (C64 and Cx16 both do).
- 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, expression and statement simplifications/rewriting. - High-level code optimizations, such as const-folding, expression and statement simplifications/rewriting.
- Many built-in functions, such as ``sin``, ``cos``, ``rnd``, ``abs``, ``sqrt``, ``msb``, ``rol``, ``ror``, ``sort`` and ``reverse`` - Many built-in functions, such as ``sin``, ``cos``, ``abs``, ``sqrt``, ``msb``, ``rol``, ``ror``, ``sort`` and ``reverse``
- Programs can be run multiple times without reloading because of automatic variable (re)initializations. - Programs can be run multiple times without reloading because of automatic variable (re)initializations.
- Supports the sixteen 'virtual' 16-bit registers R0 .. R15 from the Commander X16, also on the other machines. - Supports the sixteen 'virtual' 16-bit registers R0 .. R15 from the Commander X16, also on the other machines.
- If you only use standard kernal and core prog8 library routines, it is possible to compile the *exact same program* for different machines (just change the compilation target flag)! - If you only use standard Kernal and core prog8 library routines, it is possible to compile the *exact same program* for different machines (just change the compilation target flag)!
Code example Code example
@ -83,6 +83,7 @@ Code example
Here is a hello world program:: Here is a hello world program::
%import textio %import textio
%zeropage basicsafe
main { main {
sub start() { sub start() {
@ -139,11 +140,11 @@ This code calculates prime numbers using the Sieve of Eratosthenes algorithm::
} }
when compiled an ran on a C-64 you get this: when compiled an ran on a C64 you get this:
.. image:: _static/primes_example.png .. image:: _static/primes_example.png
:align: center :align: center
:alt: result when run on C-64 :alt: result when run on C64
when the exact same program is compiled for the Commander X16 target, and run on the emulator, you get this: when the exact same program is compiled for the Commander X16 target, and run on the emulator, you get this:
@ -169,19 +170,23 @@ Required additional tools
It's very easy to compile yourself. It's very easy to compile yourself.
A recent precompiled .exe (only for Windows) can be obtained from my `clone <https://github.com/irmen/64tass/releases>`_ of this project. A recent precompiled .exe (only for Windows) can be obtained from my `clone <https://github.com/irmen/64tass/releases>`_ of this project.
*You need at least version 1.55.2257 of this assembler to correctly use the breakpoints feature.* *You need at least version 1.55.2257 of this assembler to correctly use the breakpoints feature.*
It's possible to use older versions, but it is very likely that the automatic Vice breakpoints won't work with them. It's possible to use older versions, but it is very likely that the automatic VICE breakpoints won't work with them.
A **Java runtime (jre or jdk), version 11 or newer** is required to run the prog8 compiler itself. A **Java runtime (jre or jdk), version 11 or newer** is required to run the prog8 compiler itself.
If you're scared of Oracle's licensing terms, most Linux distributions ship OpenJDK in their packages repository instead. If you're scared of Oracle's licensing terms, most Linux distributions ship OpenJDK in their packages repository instead.
For Windows it's possible to get that as well; check out `AdoptOpenJDK <https://adoptopenjdk.net/>`_ . For Windows it's possible to get that as well; check out `AdoptOpenJDK <https://adoptopenjdk.net/>`_ .
For MacOS you can use the Homebrew system to install a recent version of OpenJDK. For MacOS you can use the Homebrew system to install a recent version of OpenJDK.
Finally: an **emulator** (or a real machine ofcourse) 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 CommanderX16 instead, there's a choice of the official `x16emu <https://github.com/commanderx16/x16-emulator>`_ If you're targeting the Commander X16 instead, there's a choice of the official `x16emu <https://github.com/commanderx16/x16-emulator>`_
and the unofficial `box16 <https://github.com/indigodarkwolf/box16>`_ (you can select which one you want to launch and the unofficial `box16 <https://github.com/indigodarkwolf/box16>`_ (you can select which one you want to launch
using the ``-emu`` or ``-emu2`` command line options) using the ``-emu`` or ``-emu2`` command line options)
**Syntax highlighting:** for a few different editors, syntax highlighting definition files are provided.
Look in the `syntax-files <https://github.com/irmen/prog8/tree/master/syntax-files>`_ directory in the github repository to find them.
.. toctree:: .. toctree::
:maxdepth: 2 :maxdepth: 2

View File

@ -29,7 +29,7 @@ of these library modules automatically as required.
syslib syslib
------ ------
The "system library" for your target machine. It contains many system-specific definitions such The "system library" for your target machine. It contains many system-specific definitions such
as ROM/kernal subroutine definitions, memory location constants, and utility subroutines. as ROM/Kernal subroutine definitions, memory location constants, and utility subroutines.
Depending on the compilation target, other routines may also be available in here specific to that target. Depending on the compilation target, other routines may also be available in here specific to that target.
Best is to check the source code of the correct syslib module. Best is to check the source code of the correct syslib module.
@ -45,8 +45,8 @@ sys (part of syslib)
system when the program is running. system when the program is running.
The following return values are currently defined: The following return values are currently defined:
- 16 = compiled for CommanderX16 with 65C02 CPU - 16 = compiled for Commander X16 with 65C02 CPU
- 64 = compiled for Commodore-64 with 6502/6510 CPU - 64 = compiled for Commodore 64 with 6502/6510 CPU
``exit(returncode)`` ``exit(returncode)``
Immediately stops the program and exits it, with the returncode in the A register. Immediately stops the program and exits it, with the returncode in the A register.
@ -105,7 +105,7 @@ sys (part of syslib)
note: a more accurate way to do this is by using a raster irq handler instead. note: a more accurate way to do this is by using a raster irq handler instead.
``reset_system()`` ``reset_system()``
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)
@ -140,7 +140,7 @@ Provides several routines that deal with disk drive I/O, such as:
- delete and rename files on the disk - delete and rename files on the disk
- send arbitrary CbmDos command to disk drive - send arbitrary CbmDos command to disk drive
On the Commander X16 it tries to use that machine's fast kernal loading routines if possible. On the Commander X16 it tries to use that machine's fast Kernal loading routines if possible.
string string
@ -150,7 +150,7 @@ Provides string manipulation routines.
``length(str) -> ubyte length`` ``length(str) -> ubyte length``
Number of bytes in the string. This value is determined during runtime and counts upto Number of bytes in the string. This value is determined during runtime and counts upto
the first terminating 0 byte in the string, regardless of the size of the string during compilation time. the first terminating 0 byte in the string, regardless of the size of the string during compilation time.
Don't confuse this with ``len`` and ``sizeof`` Don't confuse this with ``len`` and ``sizeof``!
``left(source, length, target)`` ``left(source, length, target)``
Copies the left side of the source string of the given length to target string. Copies the left side of the source string of the given length to target string.
@ -176,8 +176,8 @@ Provides string manipulation routines.
and the index in the string. Or 0+carry bit clear if the character was not found. and the index in the string. Or 0+carry bit clear if the character was not found.
``compare(string1, string2) -> ubyte result`` ``compare(string1, string2) -> ubyte result``
Returns -1, 0 or 1 depeding on wether string1 sorts before, equal or after string2. Returns -1, 0 or 1 depending on whether string1 sorts before, equal or after string2.
Note that you can also directly compare strings and string values with eachother Note that you can also directly compare strings and string values with each other
using ``==``, ``<`` etcetera (it will use string.compare for you under water automatically). using ``==``, ``<`` etcetera (it will use string.compare for you under water automatically).
``copy(from, to) -> ubyte length`` ``copy(from, to) -> ubyte length``
@ -186,10 +186,16 @@ Provides string manipulation routines.
but this function is useful if you're dealing with addresses for instance. but this function is useful if you're dealing with addresses for instance.
``lower(string)`` ``lower(string)``
Lowercases the petscii-string in place. Lowercases the PETSCII-string in place.
``upper(string)`` ``upper(string)``
Uppercases the petscii-string in place. Uppercases the PETSCII-string in place.
``lowerchar(char)``
Returns lowercased character.
``upperchar(char)``
Returns uppercased character.
``startswith(string, prefix) -> bool`` ``startswith(string, prefix) -> bool``
Returns true if string starts with prefix, otherwise false Returns true if string starts with prefix, otherwise false
@ -204,7 +210,7 @@ Provides string manipulation routines.
floats floats
------ ------
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
math routines. math routines.
@ -251,7 +257,10 @@ tan(x)
Tangent. Tangent.
rndf() rndf()
returns a pseudo-random float between 0.0 and 1.0 returns the next random float between 0.0 and 1.0 from the Pseudo RNG sequence.
rndseedf(seed)
Sets a new seed for the float pseudo-RNG sequence. Use a negative non-zero number as seed value.
graphics graphics
@ -270,12 +279,22 @@ Use the ``gfx2`` library if you want full-screen graphics or non-monochrome draw
math math
---- ----
Low level math routines. You should not normally have to bother with this directly. Low-level integer math routines (which you usually don't have to bother with directly, but they are used by the compiler internally).
The compiler needs it to implement most of the math operations in your programs. Pseudo-Random number generators (byte and word).
Various 8-bit integer trig functions that use lookup tables to quickly calculate sine and cosines.
Usually a custom lookup table is the way to go if your application needs these,
but perhaps the provided ones can be of service too.
However there's a bunch of 8-bit integer trig functions in here too that use lookup tables
to quickly calculate sine and cosines. Usually a custom lookup table is the way to go if your rnd()
application needs this, but perhaps the provided ones can be of service too: Returns next random byte 0-255 from the pseudo-RNG sequence.
rndw()
Returns next random word 0-65535 from the pseudo-RNG sequence.
rndseed(uword seed1, uword seed2)
Sets a new seed for the pseudo-RNG sequence (both rnd and rndw). The seed consists of two words.
Do not use zeros for the seed!
sin8u(x) sin8u(x)
Fast 8-bit ubyte sine of angle 0..255, result is in range 0..255 Fast 8-bit ubyte sine of angle 0..255, result is in range 0..255
@ -314,7 +333,7 @@ and allows you to print it anywhere on the screen.
prog8_lib prog8_lib
--------- ---------
Low level language support. You should not normally have to bother with this directly. Low-level language support. You should not normally have to bother with this directly.
The compiler needs it for various built-in system routines. The compiler needs it for various built-in system routines.
@ -324,7 +343,7 @@ Full-screen multicolor bitmap graphics routines, available on the Cx16 machine o
- multiple full-screen resolutions: 640 * 480 monochrome, and 320 * 240 monochrome and 256 colors - multiple full-screen resolutions: 640 * 480 monochrome, and 320 * 240 monochrome and 256 colors
- clearing screen, switching screen mode, also back to text mode is possible. - clearing screen, switching screen mode, also back to text mode is possible.
- drawing individual pixels - drawing and reading individual pixels
- drawing lines, rectangles, filled rectangles, circles, discs - drawing lines, rectangles, filled rectangles, circles, discs
- drawing text inside the bitmap - drawing text inside the bitmap
- in monochrome mode, it's possible to use a stippled drawing pattern to simulate a shade of gray. - in monochrome mode, it's possible to use a stippled drawing pattern to simulate a shade of gray.
@ -333,15 +352,16 @@ Full-screen multicolor bitmap graphics routines, available on the Cx16 machine o
palette (cx16 only) palette (cx16 only)
-------------------- --------------------
Available for the Cx16 target. Various routines to set the display color palette. Available for the Cx16 target. Various routines to set the display color palette.
There are also a few better looking Commodore-64 color palettes available here, There are also a few better looking Commodore 64 color palettes available here,
because the Commander X16's default colors for this (the first 16 colors) are too saturated because the Commander X16's default colors for this (the first 16 colors) are too saturated
and are quite different than how they looked on a VIC-II chip in a C-64. and are quite different than how they looked on a VIC-II chip in a C64.
cx16diskio (cx16 only) cx16diskio (cx16 only)
----------------------- -----------------------
Available for the Cx16 target. Contains extensions to the load and load_raw routines from the regular Available for the Cx16 target. Contains extensions to the load and load_raw routines from the regular
diskio module, to deal with loading of potentially large files in to banked ram (HiRam). diskio module, to deal with loading of potentially large files in to banked ram (HiRam).
Routines to directly load data into video ram are also present (vload and vload_raw).
Also contains a helper function to calculate the file size of a loaded file (although that is truncated Also contains a helper function to calculate the file size of a loaded file (although that is truncated
to 16 bits, 64Kb) to 16 bits, 64Kb)

View File

@ -17,15 +17,15 @@ CPU
Memory Map Memory Map
---------- ----------
Zero page zeropage
========= =========
#. *Absolute requirement:* Provide three times 2 consecutive bytes (i.e. three 16-bit pointers) in the Zero page that are free to use at all times. #. *Absolute requirement:* Provide three times 2 consecutive bytes (i.e. three 16-bit pointers) in the zeropage that are free to use at all times.
#. Provide list of any additional free Zero page locations for a normal running system (basic + kernal enabled) #. Provide list of any additional free zeropage locations for a normal running system (BASIC + Kernal enabled)
#. Provide list of any additional free Zero page locations when basic is off, but floating point routines should still work #. Provide list of any additional free zeropage locations when BASIC is off, but floating point routines should still work
#. Provide list of any additional free Zero page locations when only the kernal remains enabled #. Provide list of any additional free zeropage locations when only the Kernal remains enabled
Only the three 16-bit pointers are absolutely required to be able to use prog8 on the system. Only the three 16-bit pointers are absolutely required to be able to use prog8 on the system.
But more known available Zero page locations mean smaller and faster programs. But more known available zeropage locations mean smaller and faster programs.
RAM, ROM, I/O RAM, ROM, I/O
@ -40,7 +40,7 @@ RAM, ROM, I/O
Character encodings Character encodings
------------------- -------------------
#. if not Petscii or CBM screencodes: provide the primary character encoding table that the system uses (i.e. how is text represented in memory) #. if not PETSCII or CBM screencodes: provide the primary character encoding table that the system uses (i.e. how is text represented in memory)
#. provide alternate character encodings (if any) #. provide alternate character encodings (if any)
#. what are the system's standard character screen dimensions? #. what are the system's standard character screen dimensions?
#. is there a screen character matrix directly accessible in Ram? What's it address? Same for color attributes if any. #. is there a screen character matrix directly accessible in Ram? What's it address? Same for color attributes if any.
@ -50,7 +50,7 @@ ROM routines
------------ ------------
#. provide a list of the core ROM routines on the system, with names, addresses, and call signatures. #. provide a list of the core ROM routines on the system, with names, addresses, and call signatures.
Ideally there are at least some routines to manipulate the screen and get some user input(clear, print text, print numbers, input strings from the keyboard) Ideally there are at least some routines to manipulate the screen and get some user input (clear, print text, print numbers, input strings from the keyboard)
Routines to initialize the system to a sane state and to do a warm reset are useful too. Routines to initialize the system to a sane state and to do a warm reset are useful too.
The more the merrier. The more the merrier.
@ -74,7 +74,7 @@ the new target system.
There are several other support libraries that you may want to port (``diskio``, ``graphics`` to name a few). There are several other support libraries that you may want to port (``diskio``, ``graphics`` to name a few).
Also ofcourse if there are unique things available on the new target system, don't hesitate to provide Also of course if there are unique things available on the new target system, don't hesitate to provide
extensions to the ``syslib`` or perhaps a new special custom library altogether. extensions to the ``syslib`` or perhaps a new special custom library altogether.

Binary file not shown.

View File

@ -1,20 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.2" width="215.9mm" height="210mm" viewBox="0 0 21590 21000" preserveAspectRatio="xMidYMid" fill-rule="evenodd" stroke-width="28.222" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" xmlns:ooo="http://xml.openoffice.org/svg/export" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:presentation="http://sun.com/xmlns/staroffice/presentation" xmlns:smil="http://www.w3.org/2001/SMIL20/" xmlns:anim="urn:oasis:names:tc:opendocument:xmlns:animation:1.0" xml:space="preserve"> <svg version="1.2" width="215.9mm" height="235mm" viewBox="0 0 21590 23500" preserveAspectRatio="xMidYMid" fill-rule="evenodd" stroke-width="28.222" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" xmlns:ooo="http://xml.openoffice.org/svg/export" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:presentation="http://sun.com/xmlns/staroffice/presentation" xmlns:smil="http://www.w3.org/2001/SMIL20/" xmlns:anim="urn:oasis:names:tc:opendocument:xmlns:animation:1.0" xml:space="preserve">
<defs class="ClipPathGroup"> <defs class="ClipPathGroup">
<clipPath id="presentation_clip_path" clipPathUnits="userSpaceOnUse"> <clipPath id="presentation_clip_path" clipPathUnits="userSpaceOnUse">
<rect x="0" y="0" width="21590" height="21000"/> <rect x="0" y="0" width="21590" height="23500"/>
</clipPath> </clipPath>
<clipPath id="presentation_clip_path_shrink" clipPathUnits="userSpaceOnUse"> <clipPath id="presentation_clip_path_shrink" clipPathUnits="userSpaceOnUse">
<rect x="21" y="21" width="21547" height="20958"/> <rect x="21" y="23" width="21547" height="23453"/>
</clipPath> </clipPath>
</defs> </defs>
<defs> <defs>
<font id="EmbeddedFont_1" horiz-adv-x="2048"> <font id="EmbeddedFont_1" horiz-adv-x="2048">
<font-face font-family="Bitstream Vera Sans Mono embedded" units-per-em="2048" font-weight="normal" font-style="normal" ascent="1879" descent="476"/> <font-face font-family="Bitstream Vera Sans Mono embedded" units-per-em="2048" font-weight="normal" font-style="normal" ascent="1879" descent="476"/>
<missing-glyph horiz-adv-x="2048" d="M 0,0 L 2047,0 2047,2047 0,2047 0,0 Z"/> <missing-glyph horiz-adv-x="2048" d="M 0,0 L 2047,0 2047,2047 0,2047 0,0 Z"/>
<glyph unicode="v" horiz-adv-x="1060" d="M 100,1120 L 291,1120 616,180 942,1120 1133,1120 735,0 498,0 100,1120 Z"/>
<glyph unicode="t" horiz-adv-x="927" d="M 614,1438 L 614,1120 1032,1120 1032,977 614,977 614,369 C 614,286 630,229 661,196 692,163 747,147 825,147 L 1032,147 1032,0 807,0 C 669,0 572,28 515,83 458,138 430,234 430,369 L 430,977 131,977 131,1120 430,1120 430,1438 614,1438 Z"/>
<glyph unicode="r" horiz-adv-x="821" d="M 1155,889 C 1116,920 1076,942 1035,956 994,970 950,977 901,977 786,977 699,941 638,869 577,797 547,693 547,557 L 547,0 362,0 362,1120 547,1120 547,901 C 578,980 625,1041 689,1084 752,1126 828,1147 915,1147 960,1147 1003,1141 1042,1130 1081,1119 1119,1101 1155,1077 L 1155,889 Z"/> <glyph unicode="r" horiz-adv-x="821" d="M 1155,889 C 1116,920 1076,942 1035,956 994,970 950,977 901,977 786,977 699,941 638,869 577,797 547,693 547,557 L 547,0 362,0 362,1120 547,1120 547,901 C 578,980 625,1041 689,1084 752,1126 828,1147 915,1147 960,1147 1003,1141 1042,1130 1081,1119 1119,1101 1155,1077 L 1155,889 Z"/>
<glyph unicode="p" horiz-adv-x="927" d="M 375,141 L 375,-426 190,-426 190,1120 375,1120 375,977 C 406,1032 447,1075 498,1104 549,1133 607,1147 674,1147 809,1147 916,1095 993,990 1070,885 1108,740 1108,555 1108,373 1069,230 992,127 915,23 809,-29 674,-29 606,-29 547,-14 496,15 445,44 404,86 375,141 Z M 915,559 C 915,702 893,809 848,882 803,955 736,991 647,991 558,991 490,955 444,882 398,809 375,701 375,559 375,418 398,310 444,237 490,164 558,127 647,127 736,127 803,163 848,236 893,309 915,416 915,559 Z"/> <glyph unicode="p" horiz-adv-x="927" d="M 375,141 L 375,-426 190,-426 190,1120 375,1120 375,977 C 406,1032 447,1075 498,1104 549,1133 607,1147 674,1147 809,1147 916,1095 993,990 1070,885 1108,740 1108,555 1108,373 1069,230 992,127 915,23 809,-29 674,-29 606,-29 547,-14 496,15 445,44 404,86 375,141 Z M 915,559 C 915,702 893,809 848,882 803,955 736,991 647,991 558,991 490,955 444,882 398,809 375,701 375,559 375,418 398,310 444,237 490,164 558,127 647,127 736,127 803,163 848,236 893,309 915,416 915,559 Z"/>
<glyph unicode="o" horiz-adv-x="980" d="M 616,991 C 523,991 452,955 404,882 356,809 332,702 332,559 332,417 356,310 404,237 452,164 523,127 616,127 710,127 781,164 829,237 877,310 901,417 901,559 901,702 877,809 829,882 781,955 710,991 616,991 Z M 616,1147 C 771,1147 890,1097 973,996 1055,895 1096,750 1096,559 1096,368 1055,222 973,122 891,21 772,-29 616,-29 461,-29 342,21 260,122 178,222 137,368 137,559 137,750 178,895 260,996 342,1097 461,1147 616,1147 Z"/> <glyph unicode="o" horiz-adv-x="980" d="M 616,991 C 523,991 452,955 404,882 356,809 332,702 332,559 332,417 356,310 404,237 452,164 523,127 616,127 710,127 781,164 829,237 877,310 901,417 901,559 901,702 877,809 829,882 781,955 710,991 616,991 Z M 616,1147 C 771,1147 890,1097 973,996 1055,895 1096,750 1096,559 1096,368 1055,222 973,122 891,21 772,-29 616,-29 461,-29 342,21 260,122 178,222 137,368 137,559 137,750 178,895 260,996 342,1097 461,1147 616,1147 Z"/>
@ -63,6 +61,7 @@
<glyph unicode="I" horiz-adv-x="636" d="M 78,0 L 78,86 104,86 C 127,86 149,88 170,91 190,94 208,101 223,112 238,122 250,137 259,156 268,175 272,201 272,233 L 272,1229 C 272,1261 268,1287 259,1306 250,1325 238,1340 223,1351 208,1361 190,1368 170,1371 149,1374 127,1376 104,1376 L 78,1376 78,1462 674,1462 674,1376 647,1376 C 624,1376 603,1374 582,1371 561,1368 544,1361 529,1351 514,1340 502,1325 493,1306 484,1287 479,1261 479,1229 L 479,233 C 479,201 484,175 493,156 502,137 514,122 529,112 544,101 561,94 582,91 603,88 624,86 647,86 L 674,86 674,0 78,0 Z"/> <glyph unicode="I" horiz-adv-x="636" d="M 78,0 L 78,86 104,86 C 127,86 149,88 170,91 190,94 208,101 223,112 238,122 250,137 259,156 268,175 272,201 272,233 L 272,1229 C 272,1261 268,1287 259,1306 250,1325 238,1340 223,1351 208,1361 190,1368 170,1371 149,1374 127,1376 104,1376 L 78,1376 78,1462 674,1462 674,1376 647,1376 C 624,1376 603,1374 582,1371 561,1368 544,1361 529,1351 514,1340 502,1325 493,1306 484,1287 479,1261 479,1229 L 479,233 C 479,201 484,175 493,156 502,137 514,122 529,112 544,101 561,94 582,91 603,88 624,86 647,86 L 674,86 674,0 78,0 Z"/>
<glyph unicode="G" horiz-adv-x="1324" d="M 821,-20 C 702,-20 599,-2 511,34 422,70 349,121 291,187 232,253 189,332 160,425 131,518 117,620 117,733 117,844 132,945 163,1037 193,1129 238,1208 299,1274 359,1340 434,1391 523,1428 612,1465 716,1483 834,1483 910,1483 976,1477 1033,1466 1089,1454 1136,1438 1174,1417 1211,1396 1239,1372 1258,1344 1277,1316 1286,1286 1286,1253 1286,1231 1281,1211 1271,1194 1261,1176 1248,1161 1231,1149 1214,1136 1194,1127 1171,1120 1148,1113 1123,1110 1096,1110 1096,1142 1091,1174 1082,1205 1073,1236 1057,1265 1036,1290 1015,1315 987,1335 952,1351 917,1366 875,1374 825,1374 738,1374 665,1360 604,1332 543,1303 493,1262 454,1208 415,1153 387,1086 370,1007 353,927 344,836 344,733 344,631 353,540 371,461 389,382 418,315 459,261 500,207 552,166 617,138 682,110 761,96 854,96 893,96 932,98 969,102 1006,106 1038,112 1067,121 L 1067,451 C 1067,481 1063,505 1054,524 1045,542 1032,556 1017,566 1002,575 984,582 964,585 943,588 922,590 899,590 L 891,590 891,676 1423,676 1423,590 1415,590 C 1396,590 1377,588 1360,585 1343,582 1328,575 1315,565 1302,554 1292,539 1285,520 1278,500 1274,474 1274,442 L 1274,74 C 1205,42 1135,18 1062,3 989,-12 909,-20 821,-20 Z"/> <glyph unicode="G" horiz-adv-x="1324" d="M 821,-20 C 702,-20 599,-2 511,34 422,70 349,121 291,187 232,253 189,332 160,425 131,518 117,620 117,733 117,844 132,945 163,1037 193,1129 238,1208 299,1274 359,1340 434,1391 523,1428 612,1465 716,1483 834,1483 910,1483 976,1477 1033,1466 1089,1454 1136,1438 1174,1417 1211,1396 1239,1372 1258,1344 1277,1316 1286,1286 1286,1253 1286,1231 1281,1211 1271,1194 1261,1176 1248,1161 1231,1149 1214,1136 1194,1127 1171,1120 1148,1113 1123,1110 1096,1110 1096,1142 1091,1174 1082,1205 1073,1236 1057,1265 1036,1290 1015,1315 987,1335 952,1351 917,1366 875,1374 825,1374 738,1374 665,1360 604,1332 543,1303 493,1262 454,1208 415,1153 387,1086 370,1007 353,927 344,836 344,733 344,631 353,540 371,461 389,382 418,315 459,261 500,207 552,166 617,138 682,110 761,96 854,96 893,96 932,98 969,102 1006,106 1038,112 1067,121 L 1067,451 C 1067,481 1063,505 1054,524 1045,542 1032,556 1017,566 1002,575 984,582 964,585 943,588 922,590 899,590 L 891,590 891,676 1423,676 1423,590 1415,590 C 1396,590 1377,588 1360,585 1343,582 1328,575 1315,565 1302,554 1292,539 1285,520 1278,500 1274,474 1274,442 L 1274,74 C 1205,42 1135,18 1062,3 989,-12 909,-20 821,-20 Z"/>
<glyph unicode="C" horiz-adv-x="1086" d="M 774,1483 C 844,1483 905,1477 957,1466 1008,1454 1051,1438 1086,1417 1120,1396 1146,1372 1163,1344 1180,1316 1188,1286 1188,1253 1188,1231 1184,1211 1175,1194 1166,1176 1153,1161 1137,1149 1121,1136 1102,1127 1081,1120 1059,1113 1035,1110 1010,1110 1010,1142 1006,1174 998,1205 989,1236 976,1265 957,1290 938,1315 913,1335 883,1351 852,1366 815,1374 770,1374 693,1374 627,1360 573,1332 519,1303 475,1262 441,1208 407,1153 382,1086 367,1007 352,927 344,836 344,733 344,642 352,558 368,482 384,406 409,341 444,287 479,232 523,190 577,160 630,130 695,115 770,115 820,115 864,120 903,130 941,139 975,152 1004,168 1033,184 1059,203 1082,224 1104,245 1124,266 1141,289 1152,282 1162,272 1169,259 1176,246 1180,230 1180,209 1180,183 1171,157 1154,130 1136,103 1109,78 1073,56 1037,34 991,16 936,2 881,-13 815,-20 739,-20 637,-20 547,-2 470,34 393,70 328,121 276,187 223,253 184,332 157,425 130,518 117,620 117,733 117,844 131,945 159,1037 187,1129 229,1208 284,1274 339,1340 407,1391 489,1428 571,1465 666,1483 774,1483 Z"/> <glyph unicode="C" horiz-adv-x="1086" d="M 774,1483 C 844,1483 905,1477 957,1466 1008,1454 1051,1438 1086,1417 1120,1396 1146,1372 1163,1344 1180,1316 1188,1286 1188,1253 1188,1231 1184,1211 1175,1194 1166,1176 1153,1161 1137,1149 1121,1136 1102,1127 1081,1120 1059,1113 1035,1110 1010,1110 1010,1142 1006,1174 998,1205 989,1236 976,1265 957,1290 938,1315 913,1335 883,1351 852,1366 815,1374 770,1374 693,1374 627,1360 573,1332 519,1303 475,1262 441,1208 407,1153 382,1086 367,1007 352,927 344,836 344,733 344,642 352,558 368,482 384,406 409,341 444,287 479,232 523,190 577,160 630,130 695,115 770,115 820,115 864,120 903,130 941,139 975,152 1004,168 1033,184 1059,203 1082,224 1104,245 1124,266 1141,289 1152,282 1162,272 1169,259 1176,246 1180,230 1180,209 1180,183 1171,157 1154,130 1136,103 1109,78 1073,56 1037,34 991,16 936,2 881,-13 815,-20 739,-20 637,-20 547,-2 470,34 393,70 328,121 276,187 223,253 184,332 157,425 130,518 117,620 117,733 117,844 131,945 159,1037 187,1129 229,1208 284,1274 339,1340 407,1391 489,1428 571,1465 666,1483 774,1483 Z"/>
<glyph unicode="B" horiz-adv-x="1165" d="M 1153,1096 C 1153,1050 1147,1010 1134,975 1121,940 1103,910 1080,884 1057,858 1031,836 1000,819 969,801 935,786 899,774 L 899,766 C 946,758 988,744 1027,725 1066,706 1099,681 1127,650 1154,619 1176,582 1191,540 1206,497 1214,449 1214,395 1214,263 1171,164 1084,99 997,33 865,0 688,0 L 78,0 78,86 104,86 C 127,86 149,88 170,91 190,94 208,101 223,112 238,122 250,137 259,156 268,175 272,201 272,233 L 272,1237 C 272,1267 268,1291 259,1310 250,1328 237,1342 222,1352 207,1362 189,1369 169,1372 148,1375 127,1376 104,1376 L 78,1376 78,1462 627,1462 C 804,1462 936,1433 1023,1374 1110,1315 1153,1222 1153,1096 Z M 479,102 L 678,102 C 736,102 785,107 825,118 865,129 898,146 923,170 948,193 967,224 978,262 989,300 995,346 995,401 995,454 990,501 980,540 970,579 953,612 929,638 904,664 872,683 832,696 792,709 742,715 682,715 L 479,715 479,102 Z M 479,817 L 621,817 C 681,817 731,822 771,833 811,844 843,860 867,883 891,905 908,934 919,969 929,1004 934,1047 934,1096 934,1146 928,1188 917,1222 906,1255 887,1282 862,1303 837,1324 804,1338 764,1347 723,1356 674,1360 616,1360 L 479,1360 479,817 Z"/>
<glyph unicode="A" horiz-adv-x="1456" d="M 414,489 L 336,274 C 330,258 325,242 322,227 319,211 317,197 317,186 317,151 328,126 351,110 373,94 407,86 453,86 L 500,86 500,0 0,0 0,86 39,86 C 59,86 76,88 90,93 104,97 117,105 128,117 139,129 150,145 161,166 171,187 182,213 195,246 L 649,1462 809,1462 1272,195 C 1280,174 1288,156 1297,142 1305,128 1315,117 1326,109 1337,100 1350,94 1365,91 1380,88 1397,86 1417,86 L 1444,86 1444,0 881,0 881,86 928,86 C 1010,86 1051,119 1051,184 1051,195 1050,207 1047,219 1044,231 1039,245 1034,260 L 952,489 414,489 Z M 788,950 C 767,1011 747,1068 730,1121 712,1174 697,1225 686,1274 681,1249 676,1226 670,1203 663,1180 656,1156 649,1132 642,1108 633,1083 624,1057 615,1030 604,1001 592,969 L 453,592 915,592 788,950 Z"/> <glyph unicode="A" horiz-adv-x="1456" d="M 414,489 L 336,274 C 330,258 325,242 322,227 319,211 317,197 317,186 317,151 328,126 351,110 373,94 407,86 453,86 L 500,86 500,0 0,0 0,86 39,86 C 59,86 76,88 90,93 104,97 117,105 128,117 139,129 150,145 161,166 171,187 182,213 195,246 L 649,1462 809,1462 1272,195 C 1280,174 1288,156 1297,142 1305,128 1315,117 1326,109 1337,100 1350,94 1365,91 1380,88 1397,86 1417,86 L 1444,86 1444,0 881,0 881,86 928,86 C 1010,86 1051,119 1051,184 1051,195 1050,207 1047,219 1044,231 1039,245 1034,260 L 952,489 414,489 Z M 788,950 C 767,1011 747,1068 730,1121 712,1174 697,1225 686,1274 681,1249 676,1226 670,1203 663,1180 656,1156 649,1132 642,1108 633,1083 624,1057 615,1030 604,1001 592,969 L 453,592 915,592 788,950 Z"/>
<glyph unicode="8" horiz-adv-x="980" d="M 94,367 C 94,416 102,460 118,497 133,534 155,567 183,596 211,625 244,651 282,674 320,697 361,720 406,741 367,763 332,787 299,813 266,838 238,866 215,897 192,928 174,961 161,998 148,1034 141,1073 141,1116 141,1163 149,1209 166,1253 183,1297 209,1336 244,1370 279,1404 325,1431 382,1452 438,1473 505,1483 584,1483 648,1483 705,1474 755,1457 805,1439 847,1414 882,1383 916,1352 942,1315 960,1272 978,1229 987,1181 987,1130 987,1085 980,1046 967,1012 954,978 935,948 911,921 887,894 858,869 824,848 790,826 752,805 711,784 762,760 808,734 850,707 891,680 927,650 957,618 987,586 1010,551 1027,514 1043,477 1051,436 1051,391 1051,326 1039,269 1016,218 993,167 960,123 917,88 874,53 823,26 762,8 701,-11 634,-20 559,-20 482,-20 415,-10 357,10 299,30 251,58 212,93 173,128 143,169 124,216 104,263 94,313 94,367 Z M 569,74 C 614,74 654,81 691,95 727,108 758,127 784,151 809,175 829,204 843,237 857,270 864,307 864,346 864,381 858,413 845,443 832,472 812,501 783,529 754,556 717,583 672,610 626,636 570,663 504,692 439,657 388,612 350,557 312,502 293,435 293,358 293,315 299,277 311,242 322,207 340,178 363,153 386,128 414,108 449,95 484,81 524,74 569,74 Z M 805,1135 C 805,1166 801,1197 793,1227 784,1257 771,1284 753,1308 734,1331 710,1350 681,1365 651,1379 614,1386 571,1386 533,1386 499,1380 470,1368 440,1355 415,1338 395,1317 375,1295 360,1269 350,1239 339,1209 334,1176 334,1141 334,1104 340,1071 352,1042 363,1013 381,986 404,962 427,938 455,916 490,895 525,874 565,852 610,831 649,850 681,870 706,891 731,911 750,933 765,958 780,982 790,1009 796,1038 802,1067 805,1099 805,1135 Z"/> <glyph unicode="8" horiz-adv-x="980" d="M 94,367 C 94,416 102,460 118,497 133,534 155,567 183,596 211,625 244,651 282,674 320,697 361,720 406,741 367,763 332,787 299,813 266,838 238,866 215,897 192,928 174,961 161,998 148,1034 141,1073 141,1116 141,1163 149,1209 166,1253 183,1297 209,1336 244,1370 279,1404 325,1431 382,1452 438,1473 505,1483 584,1483 648,1483 705,1474 755,1457 805,1439 847,1414 882,1383 916,1352 942,1315 960,1272 978,1229 987,1181 987,1130 987,1085 980,1046 967,1012 954,978 935,948 911,921 887,894 858,869 824,848 790,826 752,805 711,784 762,760 808,734 850,707 891,680 927,650 957,618 987,586 1010,551 1027,514 1043,477 1051,436 1051,391 1051,326 1039,269 1016,218 993,167 960,123 917,88 874,53 823,26 762,8 701,-11 634,-20 559,-20 482,-20 415,-10 357,10 299,30 251,58 212,93 173,128 143,169 124,216 104,263 94,313 94,367 Z M 569,74 C 614,74 654,81 691,95 727,108 758,127 784,151 809,175 829,204 843,237 857,270 864,307 864,346 864,381 858,413 845,443 832,472 812,501 783,529 754,556 717,583 672,610 626,636 570,663 504,692 439,657 388,612 350,557 312,502 293,435 293,358 293,315 299,277 311,242 322,207 340,178 363,153 386,128 414,108 449,95 484,81 524,74 569,74 Z M 805,1135 C 805,1166 801,1197 793,1227 784,1257 771,1284 753,1308 734,1331 710,1350 681,1365 651,1379 614,1386 571,1386 533,1386 499,1380 470,1368 440,1355 415,1338 395,1317 375,1295 360,1269 350,1239 339,1209 334,1176 334,1141 334,1104 340,1071 352,1042 363,1013 381,986 404,962 427,938 455,916 490,895 525,874 565,852 610,831 649,850 681,870 706,891 731,911 750,933 765,958 780,982 790,1009 796,1038 802,1067 805,1099 805,1135 Z"/>
<glyph unicode="6" horiz-adv-x="953" d="M 659,1384 C 556,1384 479,1334 426,1233 373,1132 343,981 336,782 353,796 371,810 392,823 412,836 434,847 458,857 482,866 508,874 537,880 565,886 596,889 629,889 692,889 748,879 799,860 850,841 893,813 929,777 964,741 992,697 1011,645 1030,593 1040,534 1040,469 1040,397 1030,331 1011,271 991,211 962,160 925,117 887,74 841,40 787,16 732,-8 670,-20 600,-20 532,-20 469,-6 411,23 353,51 303,96 261,157 218,218 185,296 161,392 137,487 125,602 125,737 125,802 130,865 140,927 149,989 164,1048 183,1103 202,1158 227,1209 256,1256 285,1303 320,1343 360,1377 399,1410 444,1436 494,1455 544,1474 599,1483 659,1483 716,1483 766,1477 809,1465 851,1453 886,1437 914,1417 942,1397 963,1374 977,1348 990,1322 997,1295 997,1268 997,1229 984,1198 957,1177 930,1156 891,1145 840,1145 840,1178 837,1210 831,1239 824,1268 814,1293 799,1315 784,1336 766,1353 743,1366 720,1378 692,1384 659,1384 Z M 588,784 C 561,784 536,781 511,774 486,767 463,758 442,747 420,736 400,723 382,709 364,694 348,680 334,666 335,563 343,476 356,403 369,330 387,271 410,225 433,179 462,145 495,124 528,103 565,92 606,92 679,92 735,121 774,178 812,235 831,325 831,449 831,566 810,651 769,704 727,757 667,784 588,784 Z"/> <glyph unicode="6" horiz-adv-x="953" d="M 659,1384 C 556,1384 479,1334 426,1233 373,1132 343,981 336,782 353,796 371,810 392,823 412,836 434,847 458,857 482,866 508,874 537,880 565,886 596,889 629,889 692,889 748,879 799,860 850,841 893,813 929,777 964,741 992,697 1011,645 1030,593 1040,534 1040,469 1040,397 1030,331 1011,271 991,211 962,160 925,117 887,74 841,40 787,16 732,-8 670,-20 600,-20 532,-20 469,-6 411,23 353,51 303,96 261,157 218,218 185,296 161,392 137,487 125,602 125,737 125,802 130,865 140,927 149,989 164,1048 183,1103 202,1158 227,1209 256,1256 285,1303 320,1343 360,1377 399,1410 444,1436 494,1455 544,1474 599,1483 659,1483 716,1483 766,1477 809,1465 851,1453 886,1437 914,1417 942,1397 963,1374 977,1348 990,1322 997,1295 997,1268 997,1229 984,1198 957,1177 930,1156 891,1145 840,1145 840,1178 837,1210 831,1239 824,1268 814,1293 799,1315 784,1336 766,1353 743,1366 720,1378 692,1384 659,1384 Z M 588,784 C 561,784 536,781 511,774 486,767 463,758 442,747 420,736 400,723 382,709 364,694 348,680 334,666 335,563 343,476 356,403 369,330 387,271 410,225 433,179 462,145 495,124 528,103 565,92 606,92 679,92 735,121 774,178 812,235 831,325 831,449 831,566 810,651 769,704 727,757 667,784 588,784 Z"/>
@ -120,7 +119,7 @@
</font> </font>
</defs> </defs>
<defs class="TextShapeIndex"> <defs class="TextShapeIndex">
<g ooo:slide="id1" ooo:id-list="id3 id4 id5 id6 id7 id8 id9 id10 id11 id12 id13 id14 id15 id16 id17 id18 id19 id20 id21 id22 id23 id24 id25 id26 id27 id28 id29 id30 id31 id32 id33 id34 id35 id36 id37 id38 id39 id40 id41 id42 id43 id44 id45"/> <g ooo:slide="id1" ooo:id-list="id3 id4 id5 id6 id7 id8 id9 id10 id11 id12 id13 id14 id15 id16 id17 id18 id19 id20 id21 id22 id23 id24 id25 id26 id27 id28 id29 id30 id31 id32 id33 id34 id35 id36 id37 id38 id39 id40 id41 id42 id43 id44 id45 id46 id47 id48 id49"/>
</defs> </defs>
<defs class="EmbeddedBulletChars"> <defs class="EmbeddedBulletChars">
<g id="bullet-char-template-57356" transform="scale(0.00048828125,-0.00048828125)"> <g id="bullet-char-template-57356" transform="scale(0.00048828125,-0.00048828125)">
@ -177,7 +176,7 @@
<rect class="BoundingBox" stroke="none" fill="none" x="1704" y="3500" width="5611" height="1213"/> <rect class="BoundingBox" stroke="none" fill="none" x="1704" y="3500" width="5611" height="1213"/>
<path fill="rgb(142,134,174)" stroke="none" d="M 4509,4685 L 1731,4685 1731,3527 7287,3527 7287,4685 4509,4685 Z"/> <path fill="rgb(142,134,174)" stroke="none" d="M 4509,4685 L 1731,4685 1731,3527 7287,3527 7287,4685 4509,4685 Z"/>
<path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 4509,4685 L 1731,4685 1731,3527 7287,3527 7287,4685 4509,4685 Z"/> <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 4509,4685 L 1731,4685 1731,3527 7287,3527 7287,4685 4509,4685 Z"/>
<text class="SVGTextShape"><tspan class="TextParagraph" font-family="Droid Serif, serif" font-size="493px" font-weight="400"><tspan class="TextPosition" x="2619" y="4277"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">Read module.p8</tspan></tspan></tspan></text> <text class="SVGTextShape"><tspan class="TextParagraph" font-family="Droid Serif, serif" font-size="494px" font-weight="400"><tspan class="TextPosition" x="2600" y="4277"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">Read module.p8</tspan></tspan></tspan></text>
</g> </g>
</g> </g>
<g class="com.sun.star.drawing.CustomShape"> <g class="com.sun.star.drawing.CustomShape">
@ -185,7 +184,7 @@
<rect class="BoundingBox" stroke="none" fill="none" x="1704" y="5583" width="5611" height="1212"/> <rect class="BoundingBox" stroke="none" fill="none" x="1704" y="5583" width="5611" height="1212"/>
<path fill="rgb(142,134,174)" stroke="none" d="M 4509,6767 L 1731,6767 1731,5610 7287,5610 7287,6767 4509,6767 Z"/> <path fill="rgb(142,134,174)" stroke="none" d="M 4509,6767 L 1731,6767 1731,5610 7287,5610 7287,6767 4509,6767 Z"/>
<path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 4509,6767 L 1731,6767 1731,5610 7287,5610 7287,6767 4509,6767 Z"/> <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 4509,6767 L 1731,6767 1731,5610 7287,5610 7287,6767 4509,6767 Z"/>
<text class="SVGTextShape"><tspan class="TextParagraph" font-family="Droid Serif, serif" font-size="493px" font-weight="400"><tspan class="TextPosition" x="3459" y="6360"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">Tokenize</tspan></tspan></tspan></text> <text class="SVGTextShape"><tspan class="TextParagraph" font-family="Droid Serif, serif" font-size="494px" font-weight="400"><tspan class="TextPosition" x="3451" y="6360"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">Tokenize</tspan></tspan></tspan></text>
</g> </g>
</g> </g>
<g class="com.sun.star.drawing.CustomShape"> <g class="com.sun.star.drawing.CustomShape">
@ -193,7 +192,7 @@
<rect class="BoundingBox" stroke="none" fill="none" x="1704" y="9750" width="5611" height="1213"/> <rect class="BoundingBox" stroke="none" fill="none" x="1704" y="9750" width="5611" height="1213"/>
<path fill="rgb(114,159,207)" stroke="none" d="M 4509,10935 L 1731,10935 1731,9777 7287,9777 7287,10935 4509,10935 Z"/> <path fill="rgb(114,159,207)" stroke="none" d="M 4509,10935 L 1731,10935 1731,9777 7287,9777 7287,10935 4509,10935 Z"/>
<path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 4509,10935 L 1731,10935 1731,9777 7287,9777 7287,10935 4509,10935 Z"/> <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 4509,10935 L 1731,10935 1731,9777 7287,9777 7287,10935 4509,10935 Z"/>
<text class="SVGTextShape"><tspan class="TextParagraph" font-family="Droid Serif, serif" font-size="493px" font-weight="400"><tspan class="TextPosition" x="2774" y="10527"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">Convert to AST</tspan></tspan></tspan></text> <text class="SVGTextShape"><tspan class="TextParagraph" font-family="Droid Serif, serif" font-size="494px" font-weight="400"><tspan class="TextPosition" x="2761" y="10527"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">Convert to AST</tspan></tspan></tspan></text>
</g> </g>
</g> </g>
<g class="com.sun.star.drawing.CustomShape"> <g class="com.sun.star.drawing.CustomShape">
@ -201,7 +200,7 @@
<rect class="BoundingBox" stroke="none" fill="none" x="1704" y="13916" width="5611" height="1444"/> <rect class="BoundingBox" stroke="none" fill="none" x="1704" y="13916" width="5611" height="1444"/>
<path fill="rgb(114,159,207)" stroke="none" d="M 4509,15332 L 1731,15332 1731,13943 7287,13943 7287,15332 4509,15332 Z"/> <path fill="rgb(114,159,207)" stroke="none" d="M 4509,15332 L 1731,15332 1731,13943 7287,13943 7287,15332 4509,15332 Z"/>
<path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 4509,15332 L 1731,15332 1731,13943 7287,13943 7287,15332 4509,15332 Z"/> <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 4509,15332 L 1731,15332 1731,13943 7287,13943 7287,15332 4509,15332 Z"/>
<text class="SVGTextShape"><tspan class="TextParagraph" font-family="Droid Serif, serif" font-size="493px" font-weight="400"><tspan class="TextPosition" x="2285" y="14523"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">Semantic and Type </tspan></tspan><tspan class="TextPosition" x="3468" y="15095"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">checking</tspan></tspan></tspan></text> <text class="SVGTextShape"><tspan class="TextParagraph" font-family="Droid Serif, serif" font-size="494px" font-weight="400"><tspan class="TextPosition" x="2263" y="14519"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">Semantic and Type </tspan></tspan><tspan class="TextPosition" x="3459" y="15099"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">checking</tspan></tspan></tspan></text>
</g> </g>
</g> </g>
<g class="com.sun.star.drawing.CustomShape"> <g class="com.sun.star.drawing.CustomShape">
@ -209,7 +208,7 @@
<rect class="BoundingBox" stroke="none" fill="none" x="1704" y="11833" width="5611" height="1212"/> <rect class="BoundingBox" stroke="none" fill="none" x="1704" y="11833" width="5611" height="1212"/>
<path fill="rgb(114,159,207)" stroke="none" d="M 4509,13017 L 1731,13017 1731,11860 7287,11860 7287,13017 4509,13017 Z"/> <path fill="rgb(114,159,207)" stroke="none" d="M 4509,13017 L 1731,13017 1731,11860 7287,11860 7287,13017 4509,13017 Z"/>
<path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 4509,13017 L 1731,13017 1731,11860 7287,11860 7287,13017 4509,13017 Z"/> <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 4509,13017 L 1731,13017 1731,11860 7287,11860 7287,13017 4509,13017 Z"/>
<text class="SVGTextShape"><tspan class="TextParagraph" font-family="Droid Serif, serif" font-size="493px" font-weight="400"><tspan class="TextPosition" x="2716" y="12610"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">Preprocess AST</tspan></tspan></tspan></text> <text class="SVGTextShape"><tspan class="TextParagraph" font-family="Droid Serif, serif" font-size="494px" font-weight="400"><tspan class="TextPosition" x="2697" y="12610"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">Preprocess AST</tspan></tspan></tspan></text>
</g> </g>
</g> </g>
<g class="com.sun.star.drawing.ConnectorShape"> <g class="com.sun.star.drawing.ConnectorShape">
@ -260,7 +259,7 @@
<rect class="BoundingBox" stroke="none" fill="none" x="5269" y="16315" width="3758" height="1212"/> <rect class="BoundingBox" stroke="none" fill="none" x="5269" y="16315" width="3758" height="1212"/>
<path fill="rgb(114,159,207)" stroke="none" d="M 7148,17499 L 5296,17499 5296,16342 8999,16342 8999,17499 7148,17499 Z"/> <path fill="rgb(114,159,207)" stroke="none" d="M 7148,17499 L 5296,17499 5296,16342 8999,16342 8999,17499 7148,17499 Z"/>
<path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 7148,17499 L 5296,17499 5296,16342 8999,16342 8999,17499 7148,17499 Z"/> <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 7148,17499 L 5296,17499 5296,16342 8999,16342 8999,17499 7148,17499 Z"/>
<text class="SVGTextShape"><tspan class="TextParagraph" font-family="Droid Serif, serif" font-size="493px" font-weight="400"><tspan class="TextPosition" x="6083" y="17092"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">Optimize</tspan></tspan></tspan></text> <text class="SVGTextShape"><tspan class="TextParagraph" font-family="Droid Serif, serif" font-size="494px" font-weight="400"><tspan class="TextPosition" x="6072" y="17092"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">Optimize</tspan></tspan></tspan></text>
</g> </g>
</g> </g>
<g class="com.sun.star.drawing.CustomShape"> <g class="com.sun.star.drawing.CustomShape">
@ -268,7 +267,7 @@
<rect class="BoundingBox" stroke="none" fill="none" x="1704" y="18315" width="5611" height="1213"/> <rect class="BoundingBox" stroke="none" fill="none" x="1704" y="18315" width="5611" height="1213"/>
<path fill="rgb(114,159,207)" stroke="none" d="M 4509,19500 L 1731,19500 1731,18342 7287,18342 7287,19500 4509,19500 Z"/> <path fill="rgb(114,159,207)" stroke="none" d="M 4509,19500 L 1731,19500 1731,18342 7287,18342 7287,19500 4509,19500 Z"/>
<path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 4509,19500 L 1731,19500 1731,18342 7287,18342 7287,19500 4509,19500 Z"/> <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 4509,19500 L 1731,19500 1731,18342 7287,18342 7287,19500 4509,19500 Z"/>
<text class="SVGTextShape"><tspan class="TextParagraph" font-family="Droid Serif, serif" font-size="493px" font-weight="400"><tspan class="TextPosition" x="2625" y="19092"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">Postprocess AST</tspan></tspan></tspan></text> <text class="SVGTextShape"><tspan class="TextParagraph" font-family="Droid Serif, serif" font-size="494px" font-weight="400"><tspan class="TextPosition" x="2604" y="19092"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">Postprocess AST</tspan></tspan></tspan></text>
</g> </g>
</g> </g>
<g class="com.sun.star.drawing.ConnectorShape"> <g class="com.sun.star.drawing.ConnectorShape">
@ -290,7 +289,7 @@
<rect class="BoundingBox" stroke="none" fill="none" x="1704" y="7665" width="5611" height="1213"/> <rect class="BoundingBox" stroke="none" fill="none" x="1704" y="7665" width="5611" height="1213"/>
<path fill="rgb(142,134,174)" stroke="none" d="M 4509,8850 L 1731,8850 1731,7692 7287,7692 7287,8850 4509,8850 Z"/> <path fill="rgb(142,134,174)" stroke="none" d="M 4509,8850 L 1731,8850 1731,7692 7287,7692 7287,8850 4509,8850 Z"/>
<path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 4509,8850 L 1731,8850 1731,7692 7287,7692 7287,8850 4509,8850 Z"/> <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 4509,8850 L 1731,8850 1731,7692 7287,7692 7287,8850 4509,8850 Z"/>
<text class="SVGTextShape"><tspan class="TextParagraph" font-family="Droid Serif, serif" font-size="493px" font-weight="400"><tspan class="TextPosition" x="3866" y="8442"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">Parse</tspan></tspan></tspan></text> <text class="SVGTextShape"><tspan class="TextParagraph" font-family="Droid Serif, serif" font-size="494px" font-weight="400"><tspan class="TextPosition" x="3857" y="8442"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">Parse</tspan></tspan></tspan></text>
</g> </g>
</g> </g>
<g class="com.sun.star.drawing.ConnectorShape"> <g class="com.sun.star.drawing.ConnectorShape">
@ -311,7 +310,7 @@
<rect class="BoundingBox" stroke="none" fill="none" x="13167" y="3306" width="5611" height="1213"/> <rect class="BoundingBox" stroke="none" fill="none" x="13167" y="3306" width="5611" height="1213"/>
<path fill="rgb(129,172,166)" stroke="none" d="M 15972,4491 L 13194,4491 13194,3333 18750,3333 18750,4491 15972,4491 Z"/> <path fill="rgb(129,172,166)" stroke="none" d="M 15972,4491 L 13194,4491 13194,3333 18750,3333 18750,4491 15972,4491 Z"/>
<path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 15972,4491 L 13194,4491 13194,3333 18750,3333 18750,4491 15972,4491 Z"/> <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 15972,4491 L 13194,4491 13194,3333 18750,3333 18750,4491 15972,4491 Z"/>
<text class="SVGTextShape"><tspan class="TextParagraph" font-family="Droid Serif, serif" font-size="493px" font-weight="400"><tspan class="TextPosition" x="14190" y="3797"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">Select CodeGen</tspan></tspan><tspan class="TextPosition" x="14869" y="4369"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">for target</tspan></tspan></tspan></text> <text class="SVGTextShape"><tspan class="TextParagraph" font-family="Droid Serif, serif" font-size="494px" font-weight="400"><tspan class="TextPosition" x="14169" y="3793"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">Select CodeGen</tspan></tspan><tspan class="TextPosition" x="14861" y="4373"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">for target</tspan></tspan></tspan></text>
</g> </g>
</g> </g>
<g class="com.sun.star.drawing.ConnectorShape"> <g class="com.sun.star.drawing.ConnectorShape">
@ -333,7 +332,7 @@
<rect class="BoundingBox" stroke="none" fill="none" x="14723" y="5621" width="4305" height="1907"/> <rect class="BoundingBox" stroke="none" fill="none" x="14723" y="5621" width="4305" height="1907"/>
<path fill="rgb(129,172,166)" stroke="none" d="M 16875,7500 L 14750,7500 14750,5648 19000,5648 19000,7500 16875,7500 Z"/> <path fill="rgb(129,172,166)" stroke="none" d="M 16875,7500 L 14750,7500 14750,5648 19000,5648 19000,7500 16875,7500 Z"/>
<path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 16875,7500 L 14750,7500 14750,5648 19000,5648 19000,7500 16875,7500 Z"/> <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 16875,7500 L 14750,7500 14750,5648 19000,5648 19000,7500 16875,7500 Z"/>
<text class="SVGTextShape"><tspan class="TextParagraph" font-family="Droid Serif, serif" font-size="493px" font-weight="400"><tspan class="TextPosition" x="16236" y="6173"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">Make </tspan></tspan><tspan class="TextPosition" x="15343" y="6745"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">Intermediate </tspan></tspan><tspan class="TextPosition" x="16422" y="7317"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">AST</tspan></tspan></tspan></text> <text class="SVGTextShape"><tspan class="TextParagraph" font-family="Droid Serif, serif" font-size="494px" font-weight="400"><tspan class="TextPosition" x="16230" y="6165"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">Make </tspan></tspan><tspan class="TextPosition" x="15330" y="6745"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">Intermediate </tspan></tspan><tspan class="TextPosition" x="16420" y="7325"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">AST</tspan></tspan></tspan></text>
</g> </g>
</g> </g>
<g class="com.sun.star.drawing.ConnectorShape"> <g class="com.sun.star.drawing.ConnectorShape">
@ -348,7 +347,7 @@
<rect class="BoundingBox" stroke="none" fill="none" x="14723" y="8472" width="4305" height="1444"/> <rect class="BoundingBox" stroke="none" fill="none" x="14723" y="8472" width="4305" height="1444"/>
<path fill="rgb(129,172,166)" stroke="none" d="M 16875,9888 L 14750,9888 14750,8499 19000,8499 19000,9888 16875,9888 Z"/> <path fill="rgb(129,172,166)" stroke="none" d="M 16875,9888 L 14750,9888 14750,8499 19000,8499 19000,9888 16875,9888 Z"/>
<path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 16875,9888 L 14750,9888 14750,8499 19000,8499 19000,9888 16875,9888 Z"/> <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 16875,9888 L 14750,9888 14750,8499 19000,8499 19000,9888 16875,9888 Z"/>
<text class="SVGTextShape"><tspan class="TextParagraph" font-family="Droid Serif, serif" font-size="493px" font-weight="400"><tspan class="TextPosition" x="15434" y="9365"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">Generate IR </tspan></tspan></tspan></text> <text class="SVGTextShape"><tspan class="TextParagraph" font-family="Droid Serif, serif" font-size="494px" font-weight="400"><tspan class="TextPosition" x="15419" y="9365"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">Generate IR </tspan></tspan></tspan></text>
</g> </g>
</g> </g>
<g class="com.sun.star.drawing.ConnectorShape"> <g class="com.sun.star.drawing.ConnectorShape">
@ -363,7 +362,7 @@
<rect class="BoundingBox" stroke="none" fill="none" x="14723" y="10972" width="4305" height="1444"/> <rect class="BoundingBox" stroke="none" fill="none" x="14723" y="10972" width="4305" height="1444"/>
<path fill="rgb(129,172,166)" stroke="none" d="M 16875,12388 L 14750,12388 14750,10999 19000,10999 19000,12388 16875,12388 Z"/> <path fill="rgb(129,172,166)" stroke="none" d="M 16875,12388 L 14750,12388 14750,10999 19000,10999 19000,12388 16875,12388 Z"/>
<path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 16875,12388 L 14750,12388 14750,10999 19000,10999 19000,12388 16875,12388 Z"/> <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 16875,12388 L 14750,12388 14750,10999 19000,10999 19000,12388 16875,12388 Z"/>
<text class="SVGTextShape"><tspan class="TextParagraph" font-family="Droid Serif, serif" font-size="493px" font-weight="400"><tspan class="TextPosition" x="15495" y="11865"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">Optimize IR</tspan></tspan></tspan></text> <text class="SVGTextShape"><tspan class="TextParagraph" font-family="Droid Serif, serif" font-size="494px" font-weight="400"><tspan class="TextPosition" x="15482" y="11865"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">Optimize IR</tspan></tspan></tspan></text>
</g> </g>
</g> </g>
<g class="com.sun.star.drawing.ConnectorShape"> <g class="com.sun.star.drawing.ConnectorShape">
@ -375,17 +374,17 @@
</g> </g>
<g class="com.sun.star.drawing.CustomShape"> <g class="com.sun.star.drawing.CustomShape">
<g id="id31"> <g id="id31">
<rect class="BoundingBox" stroke="none" fill="none" x="13157" y="14223" width="3620" height="1305"/> <rect class="BoundingBox" stroke="none" fill="none" x="13157" y="14223" width="3620" height="1457"/>
<path fill="rgb(255,166,166)" stroke="none" d="M 14967,15500 L 13184,15500 13184,14250 16749,14250 16749,15500 14967,15500 Z"/> <path fill="rgb(255,166,166)" stroke="none" d="M 14967,15652 L 13184,15652 13184,14250 16749,14250 16749,15652 14967,15652 Z"/>
<path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 14967,15500 L 13184,15500 13184,14250 16749,14250 16749,15500 14967,15500 Z"/> <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 14967,15652 L 13184,15652 13184,14250 16749,14250 16749,15652 14967,15652 Z"/>
<text class="SVGTextShape"><tspan class="TextParagraph" font-family="Droid Serif, serif" font-size="493px" font-weight="400"><tspan class="TextPosition" x="13904" y="14760"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">Generate </tspan></tspan><tspan class="TextPosition" x="13836" y="15332"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">6502 Asm</tspan></tspan></tspan></text> <text class="SVGTextShape"><tspan class="TextParagraph" font-family="Droid Serif, serif" font-size="494px" font-weight="400"><tspan class="TextPosition" x="13891" y="14832"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">Generate </tspan></tspan><tspan class="TextPosition" x="13832" y="15412"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">6502 Asm</tspan></tspan></tspan></text>
</g> </g>
</g> </g>
<g class="com.sun.star.drawing.ConnectorShape"> <g class="com.sun.star.drawing.ConnectorShape">
<g id="id32"> <g id="id32">
<rect class="BoundingBox" stroke="none" fill="none" x="12223" y="4464" width="3777" height="10552"/> <rect class="BoundingBox" stroke="none" fill="none" x="12223" y="4464" width="3777" height="10628"/>
<path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="miter" stroke-linecap="round" d="M 15972,4491 L 15972,5019 12250,5019 12250,14875 12925,14875"/> <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="miter" stroke-linecap="round" d="M 15972,4491 L 15972,5019 12250,5019 12250,14951 12925,14951"/>
<path fill="rgb(52,101,164)" stroke="none" d="M 12906,14736 L 13185,14875 12906,15015 12906,14736 Z"/> <path fill="rgb(52,101,164)" stroke="none" d="M 12906,14812 L 13185,14951 12906,15091 12906,14812 Z"/>
</g> </g>
</g> </g>
<g class="TextShape"> <g class="TextShape">
@ -399,13 +398,13 @@
<rect class="BoundingBox" stroke="none" fill="none" x="13167" y="16269" width="3611" height="1509"/> <rect class="BoundingBox" stroke="none" fill="none" x="13167" y="16269" width="3611" height="1509"/>
<path fill="rgb(255,166,166)" stroke="none" d="M 14972,17750 L 13194,17750 13194,16296 16750,16296 16750,17750 14972,17750 Z"/> <path fill="rgb(255,166,166)" stroke="none" d="M 14972,17750 L 13194,17750 13194,16296 16750,16296 16750,17750 14972,17750 Z"/>
<path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 14972,17750 L 13194,17750 13194,16296 16750,16296 16750,17750 14972,17750 Z"/> <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 14972,17750 L 13194,17750 13194,16296 16750,16296 16750,17750 14972,17750 Z"/>
<text class="SVGTextShape"><tspan class="TextParagraph" font-family="Droid Serif, serif" font-size="493px" font-weight="400"><tspan class="TextPosition" x="13857" y="16908"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">Assemble </tspan></tspan><tspan class="TextPosition" x="14083" y="17480"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">(64tass)</tspan></tspan></tspan></text> <text class="SVGTextShape"><tspan class="TextParagraph" font-family="Droid Serif, serif" font-size="494px" font-weight="400"><tspan class="TextPosition" x="13844" y="16904"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">Assemble </tspan></tspan><tspan class="TextPosition" x="14073" y="17484"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">(64tass)</tspan></tspan></tspan></text>
</g> </g>
</g> </g>
<g class="com.sun.star.drawing.ConnectorShape"> <g class="com.sun.star.drawing.ConnectorShape">
<g id="id35"> <g id="id35">
<rect class="BoundingBox" stroke="none" fill="none" x="14832" y="15473" width="281" height="824"/> <rect class="BoundingBox" stroke="none" fill="none" x="14832" y="15625" width="281" height="672"/>
<path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="miter" stroke-linecap="round" d="M 14967,15500 L 14967,15898 14972,15898 14972,16036"/> <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="miter" stroke-linecap="round" d="M 14967,15652 L 14967,15974 14972,15974 14972,16036"/>
<path fill="rgb(52,101,164)" stroke="none" d="M 15112,16017 L 14972,16296 14833,16017 15112,16017 Z"/> <path fill="rgb(52,101,164)" stroke="none" d="M 15112,16017 L 14972,16296 14833,16017 15112,16017 Z"/>
</g> </g>
</g> </g>
@ -439,49 +438,79 @@
<path fill="rgb(52,101,164)" stroke="none" d="M 15108,18332 L 14968,18611 14829,18332 15108,18332 Z"/> <path fill="rgb(52,101,164)" stroke="none" d="M 15108,18332 L 14968,18611 14829,18332 15108,18332 Z"/>
</g> </g>
</g> </g>
<g class="com.sun.star.drawing.CustomShape"> <g class="com.sun.star.drawing.ConnectorShape">
<g id="id40"> <g id="id40">
<rect class="BoundingBox" stroke="none" fill="none" x="17473" y="14222" width="3305" height="1212"/> <rect class="BoundingBox" stroke="none" fill="none" x="18809" y="17375" width="281" height="1028"/>
<path fill="rgb(255,166,166)" stroke="none" d="M 19125,15406 L 17500,15406 17500,14249 20750,14249 20750,15406 19125,15406 Z"/> <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="miter" stroke-linecap="round" d="M 18869,17402 L 18869,17902 18949,17902 18949,18142"/>
<path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 19125,15406 L 17500,15406 17500,14249 20750,14249 20750,15406 19125,15406 Z"/> <path fill="rgb(52,101,164)" stroke="none" d="M 19089,18123 L 18949,18402 18810,18123 19089,18123 Z"/>
<text class="SVGTextShape"><tspan class="TextParagraph" font-family="Droid Serif, serif" font-size="493px" font-weight="400"><tspan class="TextPosition" x="18063" y="14713"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">Generate </tspan></tspan><tspan class="TextPosition" x="18122" y="15285"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">VM code</tspan></tspan></tspan></text>
</g> </g>
</g> </g>
<g class="com.sun.star.drawing.CustomShape"> <g class="com.sun.star.drawing.ConnectorShape">
<g id="id41"> <g id="id41">
<rect class="BoundingBox" stroke="none" fill="none" x="17595" y="16498" width="3157" height="1393"/>
<path fill="rgb(255,245,206)" stroke="none" d="M 17596,16499 L 20750,16499 20750,17616 C 19501,17607 19543,17835 18416,17889 18013,17856 17866,17831 17596,17793 L 17596,16499 Z"/>
<path fill="none" stroke="rgb(52,101,164)" d="M 17596,16499 L 20750,16499 20750,17616 C 19501,17607 19543,17835 18416,17889 18013,17856 17866,17831 17596,17793 L 17596,16499 Z"/>
<text class="SVGTextShape"><tspan class="TextParagraph" font-family="Bitstream Vera Sans Mono, monospace" font-size="370px" font-weight="400"><tspan class="TextPosition" x="17852" y="17185"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">hello.p8virt</tspan></tspan></tspan></text>
</g>
</g>
<g class="com.sun.star.drawing.ConnectorShape">
<g id="id42">
<rect class="BoundingBox" stroke="none" fill="none" x="19034" y="15380" width="281" height="1121"/>
<path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="miter" stroke-linecap="round" d="M 19125,15407 L 19125,15966 19174,15966 19174,16240"/>
<path fill="rgb(52,101,164)" stroke="none" d="M 19314,16221 L 19174,16500 19035,16221 19314,16221 Z"/>
</g>
</g>
<g class="com.sun.star.drawing.ConnectorShape">
<g id="id43">
<rect class="BoundingBox" stroke="none" fill="none" x="14827" y="12362" width="2076" height="1889"/> <rect class="BoundingBox" stroke="none" fill="none" x="14827" y="12362" width="2076" height="1889"/>
<path fill="none" stroke="rgb(183,179,202)" stroke-width="53" stroke-linejoin="miter" stroke-linecap="round" d="M 16875,12389 L 16875,13319 14967,13319 14967,13990"/> <path fill="none" stroke="rgb(183,179,202)" stroke-width="53" stroke-linejoin="miter" stroke-linecap="round" d="M 16875,12389 L 16875,13319 14967,13319 14967,13990"/>
<path fill="rgb(183,179,202)" stroke="none" d="M 15107,13971 L 14967,14250 14828,13971 15107,13971 Z"/> <path fill="rgb(183,179,202)" stroke="none" d="M 15107,13971 L 14967,14250 14828,13971 15107,13971 Z"/>
</g> </g>
</g> </g>
<g class="com.sun.star.drawing.ConnectorShape"> <g class="com.sun.star.drawing.ConnectorShape">
<g id="id44"> <g id="id42">
<rect class="BoundingBox" stroke="none" fill="none" x="16848" y="12362" width="2418" height="1889"/> <rect class="BoundingBox" stroke="none" fill="none" x="16848" y="12362" width="2195" height="1652"/>
<path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="miter" stroke-linecap="round" d="M 16875,12389 L 16875,13319 19125,13319 19125,13990"/> <path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="miter" stroke-linecap="round" d="M 16875,12389 L 16875,13213 18902,13213 18902,13753"/>
<path fill="rgb(52,101,164)" stroke="none" d="M 19265,13971 L 19125,14250 18986,13971 19265,13971 Z"/> <path fill="rgb(52,101,164)" stroke="none" d="M 19042,13734 L 18902,14013 18763,13734 19042,13734 Z"/>
</g> </g>
</g> </g>
<g class="TextShape"> <g class="TextShape">
<g id="id45"> <g id="id43">
<rect class="BoundingBox" stroke="none" fill="none" x="13473" y="13084" width="2778" height="1167"/> <rect class="BoundingBox" stroke="none" fill="none" x="13473" y="13084" width="2778" height="1167"/>
<text class="SVGTextShape"><tspan class="TextParagraph" font-family="Liberation Serif, serif" font-size="388px" font-style="italic" font-weight="400"><tspan class="TextPosition" x="13723" y="13556"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">(future)</tspan></tspan></tspan></text> <text class="SVGTextShape"><tspan class="TextParagraph" font-family="Liberation Serif, serif" font-size="388px" font-style="italic" font-weight="400"><tspan class="TextPosition" x="13723" y="13556"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">(future)</tspan></tspan></tspan></text>
</g> </g>
</g> </g>
<g class="com.sun.star.drawing.CustomShape">
<g id="id44">
<rect class="BoundingBox" stroke="none" fill="none" x="17125" y="18375" width="3647" height="1806"/>
<path fill="rgb(255,166,166)" stroke="none" d="M 17729,20153 L 17729,20153 C 17628,20153 17528,20113 17441,20036 17353,19959 17280,19848 17229,19715 17179,19582 17152,19431 17152,19278 L 17152,19278 C 17152,19124 17179,18973 17229,18840 17280,18707 17353,18596 17441,18519 17528,18442 17628,18402 17729,18402 L 20166,18402 20167,18402 C 20268,18402 20368,18442 20455,18519 20543,18596 20616,18707 20667,18840 20717,18973 20744,19124 20744,19278 L 20744,19278 20744,19278 C 20744,19431 20717,19582 20667,19715 20616,19848 20543,19959 20455,20036 20368,20113 20268,20153 20167,20153 L 17729,20153 Z"/>
<path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 17729,20153 L 17729,20153 C 17628,20153 17528,20113 17441,20036 17353,19959 17280,19848 17229,19715 17179,19582 17152,19431 17152,19278 L 17152,19278 C 17152,19124 17179,18973 17229,18840 17280,18707 17353,18596 17441,18519 17528,18442 17628,18402 17729,18402 L 20166,18402 20167,18402 C 20268,18402 20368,18442 20455,18519 20543,18596 20616,18707 20667,18840 20717,18973 20744,19124 20744,19278 L 20744,19278 20744,19278 C 20744,19431 20717,19582 20667,19715 20616,19848 20543,19959 20455,20036 20368,20113 20268,20153 20167,20153 L 17729,20153 Z"/>
<text class="SVGTextShape"><tspan class="TextParagraph" font-family="Droid Serif, serif" font-size="423px" font-weight="400"><tspan class="TextPosition" x="18277" y="18927"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">Run in </tspan></tspan><tspan class="TextPosition" x="18266" y="19423"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">virtual </tspan></tspan><tspan class="TextPosition" x="18074" y="19919"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">machine</tspan></tspan></tspan></text>
</g>
</g>
<g class="com.sun.star.drawing.CustomShape">
<g id="id45">
<rect class="BoundingBox" stroke="none" fill="none" x="17217" y="15973" width="3305" height="1457"/>
<path fill="rgb(255,166,166)" stroke="none" d="M 18869,17402 L 17244,17402 17244,16000 20494,16000 20494,17402 18869,17402 Z"/>
<path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 18869,17402 L 17244,17402 17244,16000 20494,16000 20494,17402 18869,17402 Z"/>
<text class="SVGTextShape"><tspan class="TextParagraph" font-family="Droid Serif, serif" font-size="494px" font-weight="400"><tspan class="TextPosition" x="17781" y="16582"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">Build VM </tspan></tspan><tspan class="TextPosition" x="17836" y="17162"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">program</tspan></tspan></tspan></text>
</g>
</g>
<g class="com.sun.star.drawing.CustomShape">
<g id="id46">
<rect class="BoundingBox" stroke="none" fill="none" x="17307" y="14011" width="3189" height="1393"/>
<path fill="rgb(255,245,206)" stroke="none" d="M 17308,14012 L 20494,14012 20494,15129 C 19232,15120 19275,15348 18136,15402 17729,15369 17580,15344 17308,15306 L 17308,14012 Z"/>
<path fill="none" stroke="rgb(52,101,164)" d="M 17308,14012 L 20494,14012 20494,15129 C 19232,15120 19275,15348 18136,15402 17729,15369 17580,15344 17308,15306 L 17308,14012 Z"/>
<text class="SVGTextShape"><tspan class="TextParagraph" font-family="Bitstream Vera Sans Mono, monospace" font-size="370px" font-weight="400"><tspan class="TextPosition" x="17801" y="14698"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">hello.p8ir</tspan></tspan></tspan></text>
</g>
</g>
<g class="com.sun.star.drawing.ConnectorShape">
<g id="id47">
<rect class="BoundingBox" stroke="none" fill="none" x="18729" y="15293" width="281" height="708"/>
<path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="miter" stroke-linecap="round" d="M 18902,15320 L 18902,15688 18869,15688 18869,15740"/>
<path fill="rgb(52,101,164)" stroke="none" d="M 19009,15721 L 18869,16000 18730,15721 19009,15721 Z"/>
</g>
</g>
<g class="com.sun.star.drawing.CustomShape">
<g id="id48">
<rect class="BoundingBox" stroke="none" fill="none" x="13125" y="20625" width="3647" height="1806"/>
<path fill="rgb(255,166,166)" stroke="none" d="M 13729,22403 L 13729,22403 C 13628,22403 13528,22363 13441,22286 13353,22209 13280,22098 13229,21965 13179,21832 13152,21681 13152,21528 L 13152,21528 C 13152,21374 13179,21223 13229,21090 13280,20957 13353,20846 13441,20769 13528,20692 13628,20652 13729,20652 L 16166,20652 16167,20652 C 16268,20652 16368,20692 16455,20769 16543,20846 16616,20957 16667,21090 16717,21223 16744,21374 16744,21528 L 16744,21528 16744,21528 C 16744,21681 16717,21832 16667,21965 16616,22098 16543,22209 16455,22286 16368,22363 16268,22403 16167,22403 L 13729,22403 Z"/>
<path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 13729,22403 L 13729,22403 C 13628,22403 13528,22363 13441,22286 13353,22209 13280,22098 13229,21965 13179,21832 13152,21681 13152,21528 L 13152,21528 C 13152,21374 13179,21223 13229,21090 13280,20957 13353,20846 13441,20769 13528,20692 13628,20652 13729,20652 L 16166,20652 16167,20652 C 16268,20652 16368,20692 16455,20769 16543,20846 16616,20957 16667,21090 16717,21223 16744,21374 16744,21528 L 16744,21528 16744,21528 C 16744,21681 16717,21832 16667,21965 16616,22098 16543,22209 16455,22286 16368,22363 16268,22403 16167,22403 L 13729,22403 Z"/>
<text class="SVGTextShape"><tspan class="TextParagraph" font-family="Droid Serif, serif" font-size="423px" font-weight="400"><tspan class="TextPosition" x="14222" y="21177"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">Run on </tspan></tspan><tspan class="TextPosition" x="14353" y="21673"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">target </tspan></tspan><tspan class="TextPosition" x="14074" y="22169"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">machine</tspan></tspan></tspan></text>
</g>
</g>
<g class="com.sun.star.drawing.ConnectorShape">
<g id="id49">
<rect class="BoundingBox" stroke="none" fill="none" x="14809" y="19891" width="281" height="762"/>
<path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="miter" stroke-linecap="round" d="M 14968,19918 L 14968,20313 14949,20313 14949,20392"/>
<path fill="rgb(52,101,164)" stroke="none" d="M 15089,20373 L 14949,20652 14810,20373 15089,20373 Z"/>
</g>
</g>
</g> </g>
</g> </g>
</g> </g>

Before

Width:  |  Height:  |  Size: 92 KiB

After

Width:  |  Height:  |  Size: 97 KiB

View File

@ -107,10 +107,6 @@ The name of a block must be unique in your entire program.
Be careful when importing other modules; blocks in your own code cannot have Be careful when importing other modules; blocks in your own code cannot have
the same name as a block defined in an imported module or library. the same name as a block defined in an imported module or library.
If you omit both the name and address, the entire block is *ignored* by the compiler (and a warning is displayed).
This is a way to quickly "comment out" a piece of code that is unfinshed or may contain errors that you
want to work on later, because the contents of the ignored block are not fully parsed either.
The address can be used to place a block at a specific location in memory. The address can be used to place a block at a specific location in memory.
Usually it is omitted, and the compiler will automatically choose the location (usually immediately after Usually it is omitted, and the compiler will automatically choose the location (usually immediately after
the previous block in memory). the previous block in memory).
@ -201,12 +197,12 @@ Values will usually be part of an expression or assignment statement::
*putting a variable in zeropage:* *putting a variable in zeropage:*
If you add the ``@zp`` tag to the variable declaration, the compiler will prioritize this variable If you add the ``@zp`` tag to the variable declaration, the compiler will prioritize this variable
when selecting variables to put into zero page (but no guarantees). If there are enough free locations in the zeropage, when selecting variables to put into zeropage (but no guarantees). If there are enough free locations in the zeropage,
it will try to fill it with as much other variables as possible (before they will be put in regular memory pages). it will try to fill it with as much other variables as possible (before they will be put in regular memory pages).
Use ``@requirezp`` tag to *force* the variable into zeropage, but if there is no more free space the compilation will fail. Use ``@requirezp`` tag to *force* the variable into zeropage, but if there is no more free space the compilation will fail.
It's possible to put strings, arrays and floats into zeropage too, however because Zp space is really scarce It's possible to put strings, arrays and floats into zeropage too, however because Zp space is really scarce
this is not advised as they will eat up the available space very quickly. It's best to only put byte or word this is not advised as they will eat up the available space very quickly. It's best to only put byte or word
variables in Zeropage. variables in zeropage.
Example:: Example::
@ -230,7 +226,7 @@ Integers
Integers are 8 or 16 bit numbers and can be written in normal decimal notation, Integers are 8 or 16 bit numbers and can be written in normal decimal notation,
in hexadecimal and in binary notation. in hexadecimal and in binary notation.
A single character in single quotes such as ``'a'`` is translated into a byte integer, A single character in single quotes such as ``'a'`` is translated into a byte integer,
which is the Petscii value for that character. which is the PETSCII value for that character.
Unsigned integers are in the range 0-255 for unsigned byte types, and 0-65535 for unsigned word types. Unsigned integers are in the range 0-255 for unsigned byte types, and 0-65535 for unsigned word types.
The signed integers integers are in the range -128..127 for bytes, The signed integers integers are in the range -128..127 for bytes,
@ -260,9 +256,9 @@ 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. and currently all floating point operations are specific to the Commodore 64.
This is because routines in the C-64 BASIC and KERNAL ROMs are used for that. This is because routines in the C64 BASIC and Kernal ROMs are used for that.
So floating point operations will only work if the C-64 BASIC ROM (and KERNAL ROM) So floating point operations will only work if the C64 BASIC ROM (and Kernal ROM)
are banked in. are banked in.
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
@ -286,7 +282,7 @@ Here are some examples of arrays::
byte[10] array ; array of 10 bytes, initially set to 0 byte[10] array ; array of 10 bytes, initially set to 0
byte[] array = [1, 2, 3, 4] ; initialize the array, size taken from value byte[] array = [1, 2, 3, 4] ; initialize the array, size taken from value
byte[99] array = 255 ; initialize array with 99 times 255 [255, 255, 255, 255, ...] ubyte[99] array = 255 ; initialize array with 99 times 255 [255, 255, 255, 255, ...]
byte[] array = 100 to 199 ; initialize array with [100, 101, ..., 198, 199] byte[] array = 100 to 199 ; initialize array with [100, 101, ..., 198, 199]
str[] names = ["ally", "pete"] ; array of string pointers/addresses (equivalent to uword) str[] names = ["ally", "pete"] ; array of string pointers/addresses (equivalent to uword)
uword[] others = [names, array] ; array of pointers/addresses to other arrays uword[] others = [names, array] ; array of pointers/addresses to other arrays
@ -341,7 +337,7 @@ Strings (without encoding prefix) will be encoded (translated from ASCII/UTF-8)
Alternative encodings can be specified with a ``encodingname:`` prefix to the string or character literal. Alternative encodings can be specified with a ``encodingname:`` prefix to the string or character literal.
The following encodings are currently recognised: The following encodings are currently recognised:
- ``petscii`` Petscii, the default encoding on CBM machines (c64, c128, cx16) - ``petscii`` PETSCII, the default encoding on CBM machines (c64, c128, cx16)
- ``sc`` CBM-screencodes aka 'poke' codes (c64, c128, cx16) - ``sc`` CBM-screencodes aka 'poke' codes (c64, c128, cx16)
- ``iso`` iso-8859-15 text (supported on cx16) - ``iso`` iso-8859-15 text (supported on cx16)
@ -353,7 +349,7 @@ It can be correctly displayed on the screen only if a iso-8859-15 charset has be
You can concatenate two string literals using '+', which can be useful to You can concatenate two string literals using '+', which can be useful to
split long strings over separate lines. But remember that the length split long strings over separate lines. But remember that the length
of the total string still cannot exceed 255 characaters. of the total string still cannot exceed 255 characters.
A string literal can also be repeated a given number of times using '*', where the repeat number must be a constant value. A string literal can also be repeated a given number of times using '*', where the repeat number must be a constant value.
And a new string value can be assigned to another string, but no bounds check is done And a new string value can be assigned to another string, but no bounds check is done
so be sure the destination string is large enough to contain the new value (it is overwritten in memory):: so be sure the destination string is large enough to contain the new value (it is overwritten in memory)::
@ -370,7 +366,7 @@ as newlines, quote characters themselves, and so on. The ones used most often ar
``\\``, ``\"``, ``\n``, ``\r``. For a detailed description of all of them and what they mean, ``\\``, ``\"``, ``\n``, ``\r``. For a detailed description of all of them and what they mean,
read the syntax reference on strings. read the syntax reference on strings.
Using the ``in`` operator you can easily check if a characater is present in a string, Using the ``in`` operator you can easily check if a character is present in a string,
example: ``if '@' in email_address {....}`` (however this gives no clue about the location example: ``if '@' in email_address {....}`` (however this gives no clue about the location
in the string where the character is present, if you need that, use the ``string.find()`` in the string where the character is present, if you need that, use the ``string.find()``
library function instead) library function instead)
@ -412,10 +408,10 @@ for the constant itself). This is only valid for the simple numeric types (byte,
When using ``&`` (the address-of operator but now applied to a datatype), the variable will point to specific location in memory, When using ``&`` (the address-of operator but now applied to a datatype), the variable will point to specific location in memory,
rather than being newly allocated. The initial value (mandatory) must be a valid rather than being newly allocated. The initial value (mandatory) must be a valid
memory address. Reading the variable will read the given data type from the memory address. Reading the variable will read the given data type from the
address you specified, and setting the varible will directly modify that memory location(s):: address you specified, and setting the variable will directly modify that memory location(s)::
const byte max_age = 2000 - 1974 ; max_age will be the constant value 26 const byte max_age = 2000 - 1974 ; max_age will be the constant value 26
&word SCREENCOLORS = $d020 ; a 16-bit word at the addres $d020-$d021 &word SCREENCOLORS = $d020 ; a 16-bit word at the address $d020-$d021
.. _pointervars_programming: .. _pointervars_programming:
@ -452,9 +448,9 @@ Many type conversions are possible by just writing ``as <type>`` at the end of a
f = 56.777 f = 56.777
ub = f as ubyte ; ub will be 56 ub = f as ubyte ; ub will be 56
Sometimes it is a straight 'type cast' where the value is simply interpreted as being of the other type, Sometimes it is a straight reinterpretation of the given value as being of the other type,
sometimes an actual value conversion is done to convert it into the targe type. sometimes an actual value conversion is done to convert it into the other type.
Try to avoid type conversions as much as possible. Try to avoid those type conversions as much as possible.
Initial values across multiple runs of the program Initial values across multiple runs of the program
@ -511,19 +507,29 @@ Conditional Execution
if statements if statements
^^^^^^^^^^^^^ ^^^^^^^^^^^^^
Conditional execution means that the flow of execution changes based on certiain conditions, Conditional execution means that the flow of execution changes based on certain conditions,
rather than having fixed gotos or subroutine calls:: rather than having fixed gotos or subroutine calls::
if aa>4 goto overflow if xx==5 {
yy = 99
zz = 42
} else {
aa = 3
bb = 9
}
if xx==3 yy = 4 if xx==5
if xx==3 yy = 4 else aa = 2 yy = 42
else if xx==6
yy = 43
else
yy = 44
if xx==5 { if aa>4 goto some_label
yy = 99
} else { if xx==3 yy = 4
aa = 3
} if xx==3 yy = 4 else aa = 2
Conditional jumps (``if condition goto label``) are compiled using 6502's branching instructions (such as ``bne`` and ``bcc``) so Conditional jumps (``if condition goto label``) are compiled using 6502's branching instructions (such as ``bne`` and ``bcc``) so
@ -557,7 +563,7 @@ So ``if_cc goto target`` will directly translate into the single CPU instruction
.. caution:: .. caution::
These special ``if_XX`` branching statements are only useful in certain specific situations where you are *certain* These special ``if_XX`` branching statements are only useful in certain specific situations where you are *certain*
that the status register (still) contains the correct status bits. that the status register (still) contains the correct status bits.
This is not always the case after a fuction call or other operations! This is not always the case after a function call or other operations!
If in doubt, check the generated assembly code! If in doubt, check the generated assembly code!
.. note:: .. note::
@ -633,7 +639,7 @@ If possible, the expression is parsed and evaluated by the compiler itself at co
Expressions that cannot be compile-time evaluated will result in code that calculates them at runtime. Expressions that cannot be compile-time evaluated will result in code that calculates them at runtime.
Expressions can contain procedure and function calls. Expressions can contain procedure and function calls.
There are various built-in functions such as sin(), cos() that can be used in expressions (see :ref:`builtinfunctions`). There are various built-in functions such as sin(), cos() that can be used in expressions (see :ref:`builtinfunctions`).
You can also reference idendifiers defined elsewhere in your code. You can also reference identifiers defined elsewhere in your code.
Read the :ref:`syntaxreference` chapter for all details on the available operators and kinds of expressions you can write. Read the :ref:`syntaxreference` chapter for all details on the available operators and kinds of expressions you can write.
@ -670,7 +676,7 @@ Logical expressions are expressions that calculate a boolean result: true or fal
logical expressions will compile more efficiently than when you're using regular integer type operands logical expressions will compile more efficiently than when you're using regular integer type operands
(because these have to be converted to 0 or 1 every time) (because these have to be converted to 0 or 1 every time)
You can use parentheses to group parts of an expresion to change the precedence. You can use parentheses to group parts of an expression to change the precedence.
Usually the normal precedence rules apply (``*`` goes before ``+`` etc.) but subexpressions Usually the normal precedence rules apply (``*`` goes before ``+`` etc.) but subexpressions
within parentheses will be evaluated first. So ``(4 + 8) * 2`` is 24 and not 20, within parentheses will be evaluated first. So ``(4 + 8) * 2`` is 24 and not 20,
and ``(true or false) and false`` is false instead of true. and ``(true or false) and false`` is false instead of true.
@ -835,24 +841,18 @@ pokemon(address, value)
Doesn't have anything to do with a certain video game. Doesn't have anything to do with a certain video game.
push(value) push(value)
pushes a byte value on the CPU hardware stack. Lowlevel function that should normally not be used. pushes a byte value on the CPU hardware stack. Low-level function that should normally not be used.
pushw(value) pushw(value)
pushes a 16-bit word value on the CPU hardware stack. Lowlevel function that should normally not be used. pushes a 16-bit word value on the CPU hardware stack. Low-level function that should normally not be used.
pop(variable) pop(variable)
pops a byte value off the CPU hardware stack into the given variable. Only variables can be used. pops a byte value off the CPU hardware stack into the given variable. Only variables can be used.
Lowlevel function that should normally not be used. Low-level function that should normally not be used.
popw(value) popw(value)
pops a 16-bit word value off the CPU hardware stack into the given variable. Only variables can be used. pops a 16-bit word value off the CPU hardware stack into the given variable. Only variables can be used.
Lowlevel function that should normally not be used. Low-level function that should normally not be used.
rnd()
returns a pseudo-random byte from 0..255
rndw()
returns a pseudo-random word from 0..65535
rol(x) rol(x)
Rotate the bits in x (byte or word) one position to the left. Rotate the bits in x (byte or word) one position to the left.
@ -885,7 +885,7 @@ ror2(x)
sizeof(name) sizeof(name)
Number of bytes that the object 'name' occupies in memory. This is a constant determined by the data type of Number of bytes that the object 'name' occupies in memory. This is a constant determined by the data type of
the object. For instance, for a variable of type uword, the sizeof is 2. the object. For instance, for a variable of type uword, the sizeof is 2.
For an 10 element array of floats, it is 50 (on the C-64, where a float is 5 bytes). For an 10 element array of floats, it is 50 (on the C64, where a float is 5 bytes).
Note: usually you will be interested in the number of elements in an array, use len() for that. Note: usually you will be interested in the number of elements in an array, use len() for that.
memory(name, size, alignment) memory(name, size, alignment)
@ -903,7 +903,7 @@ memory(name, size, alignment)
You can only treat it as a pointer or use it in inline assembly. You can only treat it as a pointer or use it in inline assembly.
callfar(bank, address, argumentaddress) ; NOTE: specific to cx16 target for now callfar(bank, address, argumentaddress) ; NOTE: specific to cx16 target for now
Calls an assembly routine in another ram-bank on the CommanderX16 (using the ``jsrfar`` routine) Calls an assembly routine in another ram-bank on the Commander X16 (using the ``jsrfar`` routine)
The banked RAM is located in the address range $A000-$BFFF (8 kilobyte), but you can specify The banked RAM is located in the address range $A000-$BFFF (8 kilobyte), but you can specify
any address in system ram (why this can be useful is explained at the end of this paragraph) any address in system ram (why this can be useful is explained at the end of this paragraph)
The third argument can be used to designate the memory address The third argument can be used to designate the memory address
@ -917,7 +917,7 @@ callfar(bank, address, argumentaddress) ; NOTE: specific to cx16 target for
This is not very efficient though, so maybe you should write a small piece of inline assembly for this instead. This is not very efficient though, so maybe you should write a small piece of inline assembly for this instead.
callrom(bank, address, argumentaddress) ; NOTE: specific to cx16 target for now callrom(bank, address, argumentaddress) ; NOTE: specific to cx16 target for now
Calls an assembly routine in another rom-bank on the CommanderX16 Calls an assembly routine in another rom-bank on the Commander X16
The banked ROM is located in the address range $C000-$FFFF (16 kilobyte). The banked ROM is located in the address range $C000-$FFFF (16 kilobyte).
There are 32 banks (0 to 31). There are 32 banks (0 to 31).
The third argument can be used to designate the memory address The third argument can be used to designate the memory address
@ -931,7 +931,7 @@ callrom(bank, address, argumentaddress) ; NOTE: specific to cx16 target for
syscall(callnr), syscall1(callnr, arg), syscall2(callnr, arg1, arg2), syscall3(callnr, arg1, arg2, arg3) syscall(callnr), syscall1(callnr, arg), syscall2(callnr, arg1, arg2), syscall3(callnr, arg1, arg2, arg3)
Functions for doing a system call on targets that support this. Currently no actual target Functions for doing a system call on targets that support this. Currently no actual target
uses this though except, possibly, the experimental code generation target! uses this though except, possibly, the experimental code generation target!
The regular 6502 based compiler targets just use a subroutine call to asmsub kernal routines at The regular 6502 based compiler targets just use a subroutine call to asmsub Kernal routines at
specific memory locations. So these builtin function calls are not useful yet except for specific memory locations. So these builtin function calls are not useful yet except for
experimentation in new code generation targets. experimentation in new code generation targets.

View File

@ -8,7 +8,7 @@ Module file
----------- -----------
This is a file with the ``.p8`` suffix, containing *directives* and *code blocks*, described below. This is a file with the ``.p8`` suffix, containing *directives* and *code blocks*, described below.
The file is a text file wich can also contain: The file is a text file which can also contain:
Lines, whitespace, indentation Lines, whitespace, indentation
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -48,28 +48,28 @@ Directives
Global setting, selects the program launcher stub to use. Global setting, selects the program launcher stub to use.
Only relevant when using the ``prg`` output type. Defaults to ``basic``. Only relevant when using the ``prg`` output type. Defaults to ``basic``.
- type ``basic`` : add a tiny C64 BASIC program, whith a SYS statement calling into the machine code - type ``basic`` : add a tiny C64 BASIC program, with a SYS statement calling into the machine code
- type ``none`` : no launcher logic is added at all - type ``none`` : no launcher logic is added at all
.. data:: %zeropage <style> .. data:: %zeropage <style>
Level: module. Level: module.
Global setting, select ZeroPage handling style. Defaults to ``kernalsafe``. Global setting, select zeropage handling style. Defaults to ``kernalsafe``.
- style ``kernalsafe`` -- use the part of the ZP that is 'free' or only used by BASIC routines, - style ``kernalsafe`` -- use the part of the ZP that is 'free' or only used by BASIC routines,
and don't change anything else. This allows full use of KERNAL ROM routines (but not BASIC routines), and don't change anything else. This allows full use of Kernal ROM routines (but not BASIC routines),
including default IRQs during normal system operation. including default IRQs during normal system operation.
It's not possible to return cleanly to BASIC when the program exits. The only choice is It's not possible to return cleanly to BASIC when the program exits. The only choice is
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 ``floatsafe`` -- like the previous one but also reserves the addresses that - style ``floatsafe`` -- like the previous one but also reserves the addresses that
are required to perform floating point operations (from the BASIC kernal). No clean exit is possible. are required to perform floating point operations (from the BASIC Kernal). No clean exit is possible.
- style ``basicsafe`` -- the most restricted mode; only use the handful 'free' addresses in the ZP, and don't - style ``basicsafe`` -- the most restricted mode; only use the handful 'free' addresses in the ZP, and don't
touch change anything else. This allows full use of BASIC and KERNAL ROM routines including default IRQs touch change anything else. This allows full use of BASIC and Kernal ROM routines including default IRQs
during normal system operation. during normal system operation.
When the program exits, it simply returns to the BASIC ready prompt. When the program exits, it simply returns to the BASIC ready prompt.
- style ``full`` -- claim the whole ZP for variables for the program, overwriting everything, - style ``full`` -- claim the whole ZP for variables for the program, overwriting everything,
except the few addresses mentioned above that are used by the system's IRQ routine. except the few addresses mentioned above that are used by the system's IRQ routine.
Even though the default IRQ routine is still active, it is impossible to use most BASIC and KERNAL ROM routines. Even though the default IRQ routine is still active, it is impossible to use most BASIC and Kernal ROM routines.
This includes many floating point operations and several utility routines that do I/O, such as ``print``. This includes many floating point operations and several utility routines that do I/O, such as ``print``.
This option makes programs smaller and faster because even more variables can This option makes programs smaller and faster because even more variables can
be stored in the ZP (which allows for more efficient assembly code). be stored in the ZP (which allows for more efficient assembly code).
@ -84,9 +84,9 @@ Directives
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
(but not on the same locations). They are relocated automatically by the compiler. (but not on the same locations). They are relocated automatically by the compiler.
The other options need those locations for other things so those virtual registers have The other options need those locations for other things so those virtual registers have
to be put into memory elsewhere (outside of the zeropage). Trying to use them as zero page to be put into memory elsewhere (outside of the zeropage). Trying to use them as zeropage
variables or pointers etc. will be a lot slower in those cases! variables or pointers etc. will be a lot slower in those cases!
On the CommanderX16 the registers are always in zeropage. On other targets, for now, they On the Commander X16 the registers are always in zeropage. On other targets, for now, they
are always outside of the zeropage. are always outside of the zeropage.
.. data:: %zpreserved <fromaddress>,<toaddress> .. data:: %zpreserved <fromaddress>,<toaddress>
@ -100,8 +100,8 @@ Directives
Level: module. Level: module.
Global setting, set the program's start memory address. It's usually fixed at ``$0801`` because the Global setting, set the program's start memory address. It's usually fixed at ``$0801`` because the
default launcher type is a CBM-basic program. But you have to specify this address yourself when default launcher type is a CBM-BASIC program. But you have to specify this address yourself when
you don't use a CBM-basic launcher. you don't use a CBM-BASIC launcher.
.. data:: %import <name> .. data:: %import <name>
@ -119,7 +119,7 @@ Directives
Sets special compiler options. Sets special compiler options.
- ``enable_floats`` (module level) tells the compiler - ``enable_floats`` (module level) tells the compiler
to deal with floating point numbers (by using various subroutines from the Commodore-64 kernal). to deal with floating point numbers (by using various subroutines from the Commodore 64 Kernal).
Otherwise, floating point support is not enabled. Normally you don't have to use this yourself as Otherwise, floating point support is not enabled. Normally you don't have to use this yourself as
importing the ``floats`` library is required anyway and that will enable it for you automatically. importing the ``floats`` library is required anyway and that will enable it for you automatically.
- ``no_sysinit`` (module level) which cause the resulting program to *not* include - ``no_sysinit`` (module level) which cause the resulting program to *not* include
@ -150,7 +150,7 @@ Directives
The assembler will include the file as raw assembly source text at this point, The assembler will include the file as raw assembly source text at this point,
prog8 will not process this at all. Symbols defined in the included assembly can not be referenced prog8 will not process this at all. Symbols defined in the included assembly can not be referenced
from prog8 code. However they can be referenced from other assembly code if properly prefixed. from prog8 code. However they can be referenced from other assembly code if properly prefixed.
You can ofcourse use a label in your prog8 code just before the %asminclude directive, and reference You can of course use a label in your prog8 code just before the %asminclude directive, and reference
that particular label to get to (the start of) the included assembly. that particular label to get to (the start of) the included assembly.
Be careful: you risk symbol redefinitions or duplications if you include a piece of Be careful: you risk symbol redefinitions or duplications if you include a piece of
assembly into a prog8 block that already defines symbols itself. assembly into a prog8 block that already defines symbols itself.
@ -225,7 +225,7 @@ and after that, a combination of letters, numbers, or underscores. Examples of v
Code blocks Code blocks
----------- -----------
A named block of actual program code. Itefines a *scope* (also known as 'namespace') and A named block of actual program code. It defines a *scope* (also known as 'namespace') and
can only contain *directives*, *variable declarations*, *subroutines* or *inline assembly*:: can only contain *directives*, *variable declarations*, *subroutines* or *inline assembly*::
<blockname> [<address>] { <blockname> [<address>] {
@ -270,7 +270,7 @@ Variable declarations
Variables should be declared with their exact type and size so the compiler can allocate storage Variables should be declared with their exact type and size so the compiler can allocate storage
for them. You can give them an initial value as well. That value can be a simple literal value, for them. You can give them an initial value as well. That value can be a simple literal value,
or an expression. If you don't provide an intial value yourself, zero will be used. or an expression. If you don't provide an initial value yourself, zero will be used.
You can add a ``@zp`` zeropage-tag, to tell the compiler to prioritize it You can add a ``@zp`` zeropage-tag, to tell the compiler to prioritize it
when selecting variables to be put into zeropage (but no guarantees). If the ZP is full, when selecting variables to be put into zeropage (but no guarantees). If the ZP is full,
the variable will be allocated in normal memory elsewhere. the variable will be allocated in normal memory elsewhere.
@ -295,7 +295,7 @@ Various examples::
byte[5] values = 255 ; initialize with five 255 bytes byte[5] values = 255 ; initialize with five 255 bytes
word @zp zpword = 9999 ; prioritize this when selecting vars for zeropage storage word @zp zpword = 9999 ; prioritize this when selecting vars for zeropage storage
uword @requirezp zpaddr = $3000 ; we require this variable in Zeropage uword @requirezp zpaddr = $3000 ; we require this variable in zeropage
word @shared asmvar ; variable is used in assembly code but not elsewhere word @shared asmvar ; variable is used in assembly code but not elsewhere
@ -329,7 +329,7 @@ type identifier type storage size example var declara
``float[]`` floating-point array depends on value ``float[] myvar = [1.1, 2.2, 3.3, 4.4]`` ``float[]`` floating-point array depends on value ``float[] myvar = [1.1, 2.2, 3.3, 4.4]``
``bool[]`` boolean array depends on value ``bool[] myvar = [true, false, true]`` note: consider using bit flags in a byte or word instead to save space ``bool[]`` boolean array depends on value ``bool[] myvar = [true, false, true]`` note: consider using bit flags in a byte or word instead to save space
``str[]`` array with string ptrs 2*x bytes + strs ``str[] names = ["ally", "pete"]`` ``str[]`` array with string ptrs 2*x bytes + strs ``str[] names = ["ally", "pete"]``
``str`` string (petscii) varies ``str myvar = "hello."`` ``str`` string (PETSCII) varies ``str myvar = "hello."``
implicitly terminated by a 0-byte implicitly terminated by a 0-byte
=============== ======================= ================= ========================================= =============== ======================= ================= =========================================
@ -342,7 +342,7 @@ value is given, the array size in the declaration can be omitted.
Note that ``%`` is also the remainder operator so be careful: if you want to take the remainder Note that ``%`` is also the remainder operator so be careful: if you want to take the remainder
of something with an operand starting with 1 or 0, you'll have to add a space in between. of something with an operand starting with 1 or 0, you'll have to add a space in between.
**character values:** you can use a single character in quotes like this ``'a'`` for the Petscii byte value of that character. **character values:** you can use a single character in quotes like this ``'a'`` for the PETSCII byte value of that character.
**``byte`` versus ``word`` values:** **``byte`` versus ``word`` values:**
@ -461,7 +461,7 @@ There are several escape sequences available to put special characters into your
- ``\uHHHH`` - a unicode codepoint \u0000 - \uffff (16-bit hexadecimal) - ``\uHHHH`` - a unicode codepoint \u0000 - \uffff (16-bit hexadecimal)
- ``\xHH`` - 8-bit hex value that will be copied verbatim *without encoding* - ``\xHH`` - 8-bit hex value that will be copied verbatim *without encoding*
- String literals can contain many symbols directly if they have a petscii equivalent, such as "♠♥♣♦π▚●○╳". - String literals can contain many symbols directly if they have a PETSCII equivalent, such as "♠♥♣♦π▚●○╳".
Characters like ^, _, \\, {, } and | (that have no direct PETSCII counterpart) are still accepted and converted to the closest PETSCII equivalents. (Make sure you save the source file in UTF-8 encoding if you use this.) Characters like ^, _, \\, {, } and | (that have no direct PETSCII counterpart) are still accepted and converted to the closest PETSCII equivalents. (Make sure you save the source file in UTF-8 encoding if you use this.)
@ -505,7 +505,7 @@ logical: ``not`` ``and`` ``or`` ``xor``
the ``bool`` variable type instead, where this conversion doesn't need to occur. the ``bool`` variable type instead, where this conversion doesn't need to occur.
.. note:: .. note::
Unlike most other programming languages, there is no short-cirquit or McCarthy-evaluation Unlike most other programming languages, there is no short-circuit or McCarthy evaluation
for the logical ``and`` and ``or`` operators. This means that prog8 currently always evaluates for the logical ``and`` and ``or`` operators. This means that prog8 currently always evaluates
all operands from these logical expressions, even when one of them already determines the outcome! all operands from these logical expressions, even when one of them already determines the outcome!
@ -580,7 +580,7 @@ Multiple return values
^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^
Normal subroutines can only return zero or one return values. Normal subroutines can only return zero or one return values.
However, the special ``asmsub`` routines (implemented in assembly code) or ``romsub`` routines However, the special ``asmsub`` routines (implemented in assembly code) or ``romsub`` routines
(referencing a routine in kernal ROM) can return more than one return value. (referencing a routine in Kernal ROM) can return more than one return value.
For example a status in the carry bit and a number in A, or a 16-bit value in A/Y registers. For example a status in the carry bit and a number in A, or a 16-bit value in A/Y registers.
It is not possible to process the results of a call to these kind of routines It is not possible to process the results of a call to these kind of routines
directly from the language, because only single value assignments are possible. directly from the language, because only single value assignments are possible.
@ -648,21 +648,22 @@ the statement body of such a subroutine should consist of just an inline assembl
The ``@ <register>`` part is required for rom and assembly-subroutines, as it specifies for the compiler The ``@ <register>`` part is required for rom and assembly-subroutines, as it specifies for the compiler
what cpu registers should take the routine's arguments. You can use the regular set of registers what cpu registers should take the routine's arguments. You can use the regular set of registers
(A, X, Y), the special 16-bit register pairs to take word values (AX, AY and XY) and even a processor status (A, X, Y), special 16-bit register pairs to take word values (AX, AY and XY) and even a processor status
flag such as Carry (Pc). flag such as Carry (Pc).
It is not possible to use floating point arguments or return values in an asmsub.
.. note:: .. note::
Asmsubs can also be tagged as ``inline asmsub`` to make trivial pieces of assembly inserted Asmsubs can also be tagged as ``inline asmsub`` to make trivial pieces of assembly inserted
directly instead of a call to them. Note that it is literal copy-paste of code that is done, directly instead of a call to them. Note that it is literal copy-paste of code that is done,
so make sure the assembly is actually written to behave like such - which probably means you so make sure the assembly is actually written to behave like such - which probably means you
don't want a ``rts`` or ``jmp`` or ``bra`` in it! don't want a ``rts`` or ``jmp`` or ``bra`` in it!
.. note:: .. note::
The 'virtual' 16-bit registers from the Commander X16 can also be specified as ``R0`` .. ``R15`` . The 'virtual' 16-bit registers from the Commander X16 can also be specified as ``R0`` .. ``R15`` .
This means you don't have to set them up manually before calling a subroutine that takes This means you don't have to set them up manually before calling a subroutine that takes
one or more parameters in those 'registers'. You can just list the arguments directly. one or more parameters in those 'registers'. You can just list the arguments directly.
*This also works on the Commodore-64!* (however they are not as efficient there because they're not in zeropage) *This also works on the Commodore 64!* (however they are not as efficient there because they're not in zeropage)
In prog8 and assembly code these 'registers' are directly accessible too via In prog8 and assembly code these 'registers' are directly accessible too via
``cx16.r0`` .. ``cx16.r15`` (these are memory mapped uword values), ``cx16.r0`` .. ``cx16.r15`` (these are memory mapped uword values),
``cx16.r0s`` .. ``cx16.r15s`` (these are memory mapped word values), ``cx16.r0s`` .. ``cx16.r15s`` (these are memory mapped word values),
@ -715,7 +716,8 @@ And this is a loop over the values of the array ``fibonacci_numbers``::
uword number uword number
for number in fibonacci_numbers { for number in fibonacci_numbers {
; do something with number ; do something with number...
break ; break out of the loop early
} }
@ -765,7 +767,7 @@ Conditional Execution and Jumps
Unconditional jump: goto Unconditional jump: goto
^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^
To jump to another part of the program, you use a ``goto`` statement with an addres or the name To jump to another part of the program, you use a ``goto`` statement with an address or the name
of a label or subroutine:: of a label or subroutine::
goto $c000 ; address goto $c000 ; address
@ -783,31 +785,35 @@ Note: to do an indirect *JSR* to a routine with a varying address, you can use t
(which is not very efficient) or you have to write a small piece of inline assembly. (which is not very efficient) or you have to write a small piece of inline assembly.
Conditional execution if statements
^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^
With the 'if' / 'else' statement you can execute code depending on the value of a condition:: With the 'if' / 'else' statement you can execute code depending on the value of a condition::
if <expression> <statements> [else <statements> ] if <expression> <statements> [else <statements> ]
where <statements> can be just a single statement for instance just a ``goto``, or it can be a block such as this:: If <statements> is just a single statement, for instance just a ``goto`` or a single assignment,
it's possible to just write the statement without any curly braces.
However if <statements> is a block of multiple statements, you'll have to enclose it in curly braces::
if <expression> { if <expression> {
<statements> <statements>
} else if <expression> {
<statements>
} else { } else {
<alternative statements> <statements>
} }
**Special status register branch form:** **Special status register branch form:**
There is a special form of the if-statement that immediately translates into one of the 6502's branching instructions. There is a special form of the if-statement that immediately translates into one of the 6502's branching instructions.
It is almost the same as the regular if-statement but it lacks a contional expression part, because the if-statement It is almost the same as the regular if-statement but it lacks a conditional expression part, because the if-statement
itself defines on what status register bit it should branch on:: itself defines on what status register bit it should branch on::
if_XX <statements> [else <statements> ] if_XX <statements> [else <statements> ]
where <statements> can be just a single statement for instance just a ``goto``, or it can be a block such as this:: where <statements> can be just a single statement or a block again::
if_XX { if_XX {
<statements> <statements>
@ -822,7 +828,7 @@ It can also be one of the four aliases that are easier to read: ``if_z``, ``if_n
.. caution:: .. caution::
These special ``if_XX`` branching statements are only useful in certain specific situations where you are *certain* These special ``if_XX`` branching statements are only useful in certain specific situations where you are *certain*
that the status register (still) contains the correct status bits. that the status register (still) contains the correct status bits.
This is not always the case after a fuction call or other operations! This is not always the case after a function call or other operations!
If in doubt, check the generated assembly code! If in doubt, check the generated assembly code!

View File

@ -20,7 +20,7 @@ Currently these machines can be selected as a compilation target (via the ``-tar
This chapter explains some relevant system details of the c64 and cx16 machines. This chapter explains some relevant system details of the c64 and cx16 machines.
.. hint:: .. hint::
If you only use standard kernal and prog8 library routines, If you only use standard Kernal and prog8 library routines,
it is often possible to compile the *exact same program* for it is often possible to compile the *exact same program* for
different machines (just change the compilation target flag)! different machines (just change the compilation target flag)!
@ -39,7 +39,7 @@ This is a hard limit: there is no built-in support for RAM expansions or bank sw
====================== ================== ======== ====================== ================== ========
memory area type note memory area type note
====================== ================== ======== ====================== ================== ========
``$00``--``$ff`` ZeroPage contains many sensitive system variables ``$00``--``$ff`` zeropage contains many sensitive system variables
``$100``--``$1ff`` Hardware stack used by the CPU, normally not accessed directly ``$100``--``$1ff`` Hardware stack used by the CPU, normally not accessed directly
``$0200``--``$ffff`` Free RAM or ROM free to use memory area, often a mix of RAM and ROM ``$0200``--``$ffff`` Free RAM or ROM free to use memory area, often a mix of RAM and ROM
====================== ================== ======== ====================== ================== ========
@ -64,9 +64,9 @@ reserved address in use for
================== ======================= ================== =======================
The actual machine will often have many other special addresses as well, The actual machine will often have many other special addresses as well,
For example, the Commodore-64 has: For example, the Commodore 64 has:
- ROMs installed in the machine: BASIC, kernal and character roms. Occupying ``$a000``--``$bfff`` and ``$e000``--``$ffff``. - ROMs installed in the machine: BASIC, Kernal and character roms. Occupying ``$a000``--``$bfff`` and ``$e000``--``$ffff``.
- memory-mapped I/O registers, for the video and sound chips, and the CIA's. Occupying ``$d000``--``$dfff``. - memory-mapped I/O registers, for the video and sound chips, and the CIA's. Occupying ``$d000``--``$dfff``.
- RAM areas that are used for screen graphics and sprite data: usually at ``$0400``--``$07ff``. - RAM areas that are used for screen graphics and sprite data: usually at ``$0400``--``$07ff``.
@ -75,18 +75,18 @@ Prog8 programs can access all of those special memory locations but it will have
.. _zeropage: .. _zeropage:
ZeroPage ("ZP") Zeropage ("ZP")
--------------- ---------------
The ZeroPage memory block ``$02``--``$ff`` can be regarded as 254 CPU 'registers', because The zeropage memory block ``$02``--``$ff`` can be regarded as 254 CPU 'registers', because
they take less clock cycles to access and need fewer instruction bytes than accessing other memory locations outside of the ZP. they take less clock cycles to access and need fewer instruction bytes than accessing other memory locations outside of the ZP.
Theoretically they can all be used in a program, with the follwoing limitations: Theoretically they can all be used in a program, with the following limitations:
- several addresses (``$02``, ``$03``, ``$fb - $fc``, ``$fd - $fe``) are reserved for internal use - several addresses (``$02``, ``$03``, ``$fb - $fc``, ``$fd - $fe``) are reserved for internal use
- most other addresses will already be in use by the machine's operating system or kernal, - most other addresses will already be in use by the machine's operating system or Kernal,
and overwriting them will probably crash the machine. It is possible to use all of these and overwriting them will probably crash the machine. It is possible to use all of these
yourself, but only if the program takes over the entire system (and seizes control from the regular kernal). yourself, but only if the program takes over the entire system (and seizes control from the regular Kernal).
This means it can no longer use (most) BASIC and kernal routines from ROM. This means it can no longer use (most) BASIC and Kernal routines from ROM.
- it's more convenient and safe to let the compiler allocate these addresses for you and just - it's more convenient and safe to let the compiler allocate these addresses for you and just
use symbolic names in the program code. use symbolic names in the program code.
@ -95,19 +95,19 @@ It will use the free ZP addresses to place its ZP variables in,
until they're all used up. If instructed to output a program that takes over the entire until they're all used up. If instructed to output a program that takes over the entire
machine, (almost) all of the ZP addresses are suddenly available and will be used. machine, (almost) all of the ZP addresses are suddenly available and will be used.
**ZeroPage handling is configurable:** **zeropage handling is configurable:**
There's a global program directive to specify the way the compiler There's a global program directive to specify the way the compiler
treats the ZP for the program. The default is to be reasonably restrictive to use the treats the ZP for the program. The default is to be reasonably restrictive to use the
part of the ZP that is not used by the C64's kernal routines. part of the ZP that is not used by the C64's Kernal routines.
It's possible to claim the whole ZP as well (by disabling the operating system or kernal). It's possible to claim the whole ZP as well (by disabling the operating system or Kernal).
If you want, it's also possible to be more restricive and stay clear of the addresses used by BASIC routines too. If you want, it's also possible to be more restrictive and stay clear of the addresses used by BASIC routines too.
This allows the program to exit cleanly back to a BASIC ready prompt - something that is not possible in the other modes. This allows the program to exit cleanly back to a BASIC ready prompt - something that is not possible in the other modes.
IRQs and the ZeroPage IRQs and the zeropage
^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^
The normal IRQ routine in the C-64's kernal will read and write several addresses in the ZP The normal IRQ routine in the C64's Kernal will read and write several addresses in the ZP
(such as the system's software jiffy clock which sits in ``$a0 - $a2``): (such as the system's software jiffy clock which sits in ``$a0 - $a2``):
``$a0 - $a2``; ``$91``; ``$c0``; ``$c5``; ``$cb``; ``$f5 - $f6`` ``$a0 - $a2``; ``$91``; ``$c0``; ``$c5``; ``$cb``; ``$f5 - $f6``
@ -144,7 +144,7 @@ IRQ Handling
Normally, the system's default IRQ handling is not interfered with. Normally, the system's default IRQ handling is not interfered with.
You can however install your own IRQ handler (for clean separation, it is advised to define it inside its own block). You can however install your own IRQ handler (for clean separation, it is advised to define it inside its own block).
There are a few library routines available to make setting up C-64 60hz IRQs and Raster IRQs a lot easier (no assembly code required). There are a few library routines available to make setting up C64 60hz IRQs and Raster IRQs a lot easier (no assembly code required).
For the C64 these routines are:: For the C64 these routines are::
@ -155,7 +155,7 @@ For the C64 these routines are::
And for the Commander X16:: And for the Commander X16::
cx16.set_irq(uword handler_address, boolean useKernal) ; vsync irq cx16.set_irq(uword handler_address, boolean useKernal) ; vsync irq
cx16.set_rasterirq(uword handler_address, uword rasterline) ; note: disables kernal irq handler! sys.wait() won't work anymore cx16.set_rasterirq(uword handler_address, uword rasterline) ; note: disables Kernal irq handler! sys.wait() won't work anymore
cx16.restore_irq() ; set everything back to the systems default irq handler cx16.restore_irq() ; set everything back to the systems default irq handler
@ -163,5 +163,5 @@ The Commander X16 provides two additional routines that should be used *in your
cx16.push_vera_context() cx16.push_vera_context()
; ... do your work that uses vera here... ; ... do your work that uses vera here...
cx15.pop_vera_context() cx16.pop_vera_context()

View File

@ -7,7 +7,7 @@ All variables are static in memory
All variables are allocated statically, there is no concept of dynamic heap or stack frames. All variables are allocated statically, there is no concept of dynamic heap or stack frames.
Essentially all variables are global (but scoped) and can be accessed and modified anywhere, Essentially all variables are global (but scoped) and can be accessed and modified anywhere,
but care should be taken ofcourse to avoid unexpected side effects. but care should be taken of course to avoid unexpected side effects.
Especially when you're dealing with interrupts or re-entrant routines: don't modify variables Especially when you're dealing with interrupts or re-entrant routines: don't modify variables
that you not own or else you will break stuff. that you not own or else you will break stuff.
@ -24,7 +24,7 @@ directly into the target variable, register, or memory location.
The software stack is implemented as follows: The software stack is implemented as follows:
- 2 pages of memory are allocated for this, exact locations vary per machine target. - 2 pages of memory are allocated for this, exact locations vary per machine target.
For the C-64 they are set at $ce00 and $cf00 (so $ce00-$cfff is reserved). For the C64 they are set at $ce00 and $cf00 (so $ce00-$cfff is reserved).
For the Commander X16 they are set at $0400 and $0500 (so $0400-$05ff are reserved). For the Commander X16 they are set at $0400 and $0500 (so $0400-$05ff are reserved).
This default location can be overridden using the `-esa` command line option. This default location can be overridden using the `-esa` command line option.
- these are the high and low bytes of the values on the stack (it's a 'split 16 bit word stack') - these are the high and low bytes of the values on the stack (it's a 'split 16 bit word stack')
@ -42,7 +42,7 @@ Calling a subroutine requires three steps:
#. preparing the arguments (if any) and passing them to the routine #. preparing the arguments (if any) and passing them to the routine
#. calling the routine #. calling the routine
#. preparig the return value (if any) and returning that from the call. #. preparing the return value (if any) and returning that from the call.
Calling the routine is just a simple JSR instruction, but the other two work like this: Calling the routine is just a simple JSR instruction, but the other two work like this:
@ -51,7 +51,7 @@ Calling the routine is just a simple JSR instruction, but the other two work lik
``asmsub`` routines ``asmsub`` routines
^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^
These are usually declarations of kernal (ROM) routines or low-level assembly only routines, These are usually declarations of Kernal (ROM) routines or low-level assembly only routines,
that have their arguments solely passed into specific registers. that have their arguments solely passed into specific registers.
Sometimes even via a processor status flag such as the Carry flag. Sometimes even via a processor status flag such as the Carry flag.
Return values also via designated registers. Return values also via designated registers.
@ -73,7 +73,7 @@ regular subroutines
- the return value is passed back to the caller via cpu register(s): - the return value is passed back to the caller via cpu register(s):
Byte values will be put in ``A`` . Byte values will be put in ``A`` .
Word values will be put in ``A`` + ``Y`` register pair. Word values will be put in ``A`` + ``Y`` register pair.
Float values will be put in the ``FAC1`` float 'register' (Basic allocated this somewhere in ram). Float values will be put in the ``FAC1`` float 'register' (BASIC allocated this somewhere in ram).
Calls to builtin functions are treated in a special way: Calls to builtin functions are treated in a special way:
@ -83,7 +83,7 @@ Some builtin functions have a fully custom implementation.
The compiler will warn about routines that are called and that return a value, if you're not The compiler will warn about routines that are called and that return a value, if you're not
doing something with that returnvalue. This can be on purpuse if you're simply not interested in it. doing something with that returnvalue. This can be on purpose if you're simply not interested in it.
Use the ``void`` keyword in front of the subroutine call to get rid of the warning in that case. Use the ``void`` keyword in front of the subroutine call to get rid of the warning in that case.
@ -92,7 +92,7 @@ The 6502 CPU's X-register: off-limits
Prog8 uses the cpu's X-register as a pointer in its internal expression evaluation stack. Prog8 uses the cpu's X-register as a pointer in its internal expression evaluation stack.
When only writing code in Prog8, this is taken care of behind the scenes for you by the compiler. When only writing code in Prog8, this is taken care of behind the scenes for you by the compiler.
However when you are including or linking with assembly routines or kernal/ROM calls that *do* However when you are including or linking with assembly routines or Kernal/ROM calls that *do*
use the X register (either clobbering it internally, or using it as a parameter, or return value register), use the X register (either clobbering it internally, or using it as a parameter, or return value register),
those calls will destroy Prog8's stack pointer and this will result in invalid calculations. those calls will destroy Prog8's stack pointer and this will result in invalid calculations.
@ -138,8 +138,8 @@ Some notes and references into the compiler's source code modules:
Most notably, node type information is now baked in. (``codeCore`` module) Most notably, node type information is now baked in. (``codeCore`` module)
#. An *Intermediate Representation* has been defined that is generated from the intermediate AST. This IR #. An *Intermediate Representation* has been defined that is generated from the intermediate AST. This IR
is more or less a machine code language for a virtual machine - and indeed this is what the built-in is more or less a machine code language for a virtual machine - and indeed this is what the built-in
prog8 VM will execute if you use the 'virtual' compilaton target and use ``-emu`` to launch the VM. prog8 VM will execute if you use the 'virtual' compilation target and use ``-emu`` to launch the VM.
(``intermediate`` and ``codeGenIntermediate`` modules, and ``codeGenVirtual`` and ``virtualmachine`` module for the VM related stuff) (``intermediate`` and ``codeGenIntermediate`` modules, and ``virtualmachine`` module for the VM related stuff)
#. Currently the 6502 ASM code generator still works directly on the *Compiler AST*. A future version #. Currently the 6502 ASM code generator still works directly on the *Compiler AST*. A future version
should replace this by working on the IR code, and should be much smaller and simpler. should replace this by working on the IR code, and should be much smaller and simpler.
(``codeGenCpu6502`` module) (``codeGenCpu6502`` module)

View File

@ -3,6 +3,9 @@ TODO
For next release For next release
^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^
- ir: register allocation per data type a specific allocation, so we are certain when a reg is used it's just for one specific datatype
- ir: write addresses as hex into p8ir file
... ...
@ -17,12 +20,12 @@ Future Things and Ideas
^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^
Compiler: Compiler:
- vm/ir: all(), any(), reverse() and sort() still depend on a VM Syscall. Get rid of this. (maybe use a IR 'builtin' function?) - create BSS section in output program and put StStaticVariables in there with bss=true. Don't forget to add init code to zero out everything that was put in bss. If array in bss->only zero ONCE! So requires self-modifying code
- vm/ir: put variables and arrays in BSS section (unless -noreinit is specified) - ir: mechanism to determine for chunks which registers are getting input values from "outside"
- vm: Jumps go to a code block rather than a specific address(label) -> also helps future dead code elimination? - ir: mechanism to determine for chunks which registers are passing values out? (i.e. are used again in another chunk)
- vm: the above means that every label introduces a new code block. This eliminates the use of actual labels altogether. - ir: peephole opt: renumber registers in chunks to start with 1 again every time (but keep entry values in mind!)
- vm: add more optimizations in IRPeepholeOptimizer - ir peephole opt: reuse registers in chunks (but keep result registers in mind that pass values out!)
- vm: how to remove all unused subroutines? (the 6502 assembly codegen relies on 64tass solve this for us) - ir: add more optimizations in IRPeepholeOptimizer
- see if we can let for loops skip the loop if end<start, like other programming languages. Without adding a lot of code size/duplicating the loop condition. - see if we can let for loops skip the loop if end<start, like other programming languages. Without adding a lot of code size/duplicating the loop condition.
this is documented behavior to now loop around but it's too easy to forget about! this is documented behavior to now loop around but it's too easy to forget about!
Lot of work because of so many special cases in ForLoopsAsmgen..... Lot of work because of so many special cases in ForLoopsAsmgen.....
@ -51,18 +54,15 @@ Libraries:
- fix the problems in atari target, and flesh out its libraries. - fix the problems in atari target, and flesh out its libraries.
- 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 (16 color and 4 color)? - add modes 3 and perhaps even 2 to gfx2 (lores 16 color and 4 color)?
- add a flood fill routine to gfx2? - add a flood fill (span fill/scanline fill) routine to gfx2?
Expressions: Expressions:
- rethink the whole "isAugmentable" business. Because the way this is determined, should always also be exactly mirrorred in the AugmentableAssignmentAsmGen or you'll get a crash at code gen time.
note: the new Ast doesn't need this any more so maybe we can get rid of it altogether in the old AST - but it's still used for something in the UnusedCodeRemover.
- 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
- rewrite expression tree evaluation such that it doesn't use an eval stack but flatten the tree into linear code that uses a fixed number of predetermined value 'variables'? - rewrite expression tree evaluation such that it doesn't use an eval stack but flatten the tree into linear code
"Three address code" was mentioned. https://en.wikipedia.org/wiki/Three-address_code that, for instance, uses a fixed number of predetermined value 'variables'?
these variables have to be unique for each subroutine because they could otherwise be interfered with from irq routines etc.
The VM IL solves this already (by using unlimited registers) but that still lacks a translation to 6502. The VM IL solves this already (by using unlimited registers) but that still lacks a translation to 6502.
- this removes the need for the BinExprSplitter? (which is problematic and very limited now) - this removes the need for the BinExprSplitter? (which is problematic and very limited now)
and perhaps the assignment splitting in BeforeAsmAstChanger too and perhaps the assignment splitting in BeforeAsmAstChanger too

View File

@ -1,5 +1,6 @@
%import syslib %import syslib
%import textio %import textio
%import math
%import test_stack %import test_stack
%zeropage basicsafe %zeropage basicsafe
@ -41,7 +42,7 @@ main {
active_height-- active_height--
upwards = false upwards = false
} else { } else {
target_height = 8 + rnd() % 16 target_height = 8 + math.rnd() % 16
if upwards if upwards
mountain = 233 mountain = 233
else else
@ -56,7 +57,7 @@ main {
txt.scroll_left(true) txt.scroll_left(true)
; float the balloon ; float the balloon
if rnd() & %10000 if math.rnd() & %10000
c64.SPXY[1] ++ c64.SPXY[1] ++
else else
c64.SPXY[1] -- c64.SPXY[1] --
@ -70,10 +71,10 @@ main {
txt.setcc(39, yy, 160, 8) ; draw mountain txt.setcc(39, yy, 160, 8) ; draw mountain
} }
yy = rnd() yy = math.rnd()
if yy > 100 { if yy > 100 {
; draw a star ; draw a star
txt.setcc(39, yy % (active_height-1), '.', rnd()) txt.setcc(39, yy % (active_height-1), '.', math.rnd())
} }
if yy > 200 { if yy > 200 {
@ -84,7 +85,7 @@ main {
tree = 88 tree = 88
else if yy & %00100000 != 0 else if yy & %00100000 != 0
tree = 65 tree = 65
if rnd() > 130 if math.rnd() > 130
treecolor = 13 treecolor = 13
txt.setcc(39, active_height, tree, treecolor) txt.setcc(39, active_height, tree, treecolor)
} }

View File

@ -1,4 +1,5 @@
%import textio %import textio
%import math
%zeropage basicsafe %zeropage basicsafe
; Note: this program is compatible with C64 and CX16. ; Note: this program is compatible with C64 and CX16.
@ -22,11 +23,11 @@ main {
; Setup Starting Ball Positions ; Setup Starting Ball Positions
ubyte lp ubyte lp
for lp in 0 to ballCount-1 { for lp in 0 to ballCount-1 {
BX[lp] = rnd() % txt.DEFAULT_WIDTH BX[lp] = math.rnd() % txt.DEFAULT_WIDTH
BY[lp] = rnd() % txt.DEFAULT_HEIGHT BY[lp] = math.rnd() % txt.DEFAULT_HEIGHT
BC[lp] = rnd() & 15 BC[lp] = math.rnd() & 15
DX[lp] = rnd() & 1 DX[lp] = math.rnd() & 1
DY[lp] = rnd() & 1 DY[lp] = math.rnd() & 1
} }
; display balls ; display balls

Some files were not shown because too many files have changed in this diff Show More