mirror of
https://github.com/irmen/prog8.git
synced 2025-06-21 12:24:00 +00:00
Compare commits
85 Commits
Author | SHA1 | Date | |
---|---|---|---|
8acd94fc89 | |||
1436480eab | |||
448d176c24 | |||
fd269453a4 | |||
b3b380964c | |||
6e9025ebf2 | |||
3922691b3c | |||
0545b77cf4 | |||
6b3f39fa1a | |||
3114ab87dc | |||
00bc99cc7b | |||
540b3ae2f4 | |||
dbfe4140e1 | |||
d3675ec254 | |||
ded2483fc0 | |||
e62ea388e0 | |||
f20356e9be | |||
d282a2d846 | |||
4641ac46e7 | |||
ba9268a09e | |||
fb9902c536 | |||
5318ba6c6e | |||
fd5ebef488 | |||
d9e4f39ddc | |||
435b9d8973 | |||
0ea70ba656 | |||
92a07b87d2 | |||
c3c82282ba | |||
adc15c24ef | |||
dddf9a9396 | |||
9ca6860ffa | |||
f7dd388954 | |||
6012839f0e | |||
8e9cbab053 | |||
aaf375a57b | |||
3cce985f03 | |||
c59df6ec20 | |||
5c3f41f64d | |||
cf3523f49f | |||
db794752cb | |||
bceaebe856 | |||
3916de2921 | |||
9e0f8c1a97 | |||
0cbc56b82e | |||
b95608f68a | |||
b6e5dbd06c | |||
f09bcf3fcf | |||
75d486b124 | |||
4914609485 | |||
75bd66326a | |||
8f904f75bb | |||
ed68d604d6 | |||
f83752f43b | |||
86c22636eb | |||
30d20a453b | |||
fe29d8a23f | |||
694d088160 | |||
6aabbffc62 | |||
623329fb33 | |||
9f0074eef9 | |||
f117805129 | |||
c75b1581d2 | |||
109e118aba | |||
201b77d5b6 | |||
a5ca08f33d | |||
988a3e4446 | |||
0f5cd22bb7 | |||
2f5bed36b3 | |||
5b6534bb28 | |||
e31e5b2477 | |||
07d5fafe2e | |||
8a4979f44c | |||
e67464325f | |||
94c9b0d23b | |||
c78d1e3c39 | |||
e94319145f | |||
3f3b01b5f6 | |||
4e8ccf0ef3 | |||
48c9349ce9 | |||
117d848466 | |||
99c62aab36 | |||
a3c0c7c96f | |||
9b209823f6 | |||
b2cb125bd4 | |||
5e8f767642 |
@ -1,35 +0,0 @@
|
|||||||
#### Just a few remarks upfront:
|
|
||||||
* There is the (gradle/IDEA) module `parser`: that's the parser generated by ANTLR4, in Java. The only file to be edited here is the grammar, `prog8.g4`.
|
|
||||||
* Then we have the module `compilerAst` - in Kotlin - which uses `parser` and adds AST nodes. Here we put our additions to the generated thing, *including any tests of the parsing stage*.
|
|
||||||
- the name is a bit misleading, as this module isn't (or, resp. shouldn't be; see below) about *compiling*, only the parsing stage
|
|
||||||
- also, the tree that comes out isn't much of an *abstraction*, but rather still more or less a parse tree (this might very well change).
|
|
||||||
- **However, let's not *yet* rename the module.** We'll find a good name during refactoring.
|
|
||||||
|
|
||||||
#### Problems with `compilerAst`:
|
|
||||||
* `ModuleImporter.kt`, doing (Prog8-) module resolution. That's not the parser's job.
|
|
||||||
* `ParsingFailedError` (in `ModuleParsing.kt`): this exception (it is actually *not* a `java.lang.Error`...) is thrown in a number of places, where other exceptions would make more sense. For example: not finding a file should just yield a `NoSuchFileException`, not this one. The other problem with it is that it does not provide any additional information about the source of parsing error, in particular a `Position`.
|
|
||||||
* During parsing, character literals are turned into UBYTEs (since there is no basic type e.g. CHAR). That's bad because it depends on a specific character encoding (`IStringEncoding` in `compilerAst/src/prog8/ast/AstToplevel.kt`) of/for some target platform. Note that *strings* are indeed encoded later, in the `compiler` module.
|
|
||||||
* The same argument applies to `IMemSizer`, and - not entirely sure about that - `IBuiltinFunctions`.
|
|
||||||
|
|
||||||
#### Steps to take, in conceptual (!) order:
|
|
||||||
|
|
||||||
(note: all these steps have been implemented, rejected or otherwise solved now.)
|
|
||||||
|
|
||||||
1. introduce an abstraction `SourceCode` that encapsulates the origin and actual loading of Prog8 source code
|
|
||||||
- from the local file system (use case: user programs)
|
|
||||||
- from resources (prog8lib)
|
|
||||||
- from plain strings (for testing)
|
|
||||||
2. add subclass `ParseError : ParsingFailedError` which adds information about the *source of parsing error* (`SourceCode` and `Position`). We cannot just replace `ParsingFailedError` right away because it is so widely used (even in the `compiler` module). Therefore we'll just subclass for the time being, add more and more tests requiring the new one to be thrown (or, resp., NOT to be thrown), and gradually transition.
|
|
||||||
3. introduce a minimal interface to the outside, input: `SourceCode`, output: a tree with a `Module` node as the root
|
|
||||||
- this will be the Kotlin singleton `Prog8Parser` with the main method `parseModule`
|
|
||||||
- plus, optionally, method's for registering/unregistering a listener with the parser
|
|
||||||
- the *only* exception ever thrown / reported to listeners (TBD) will be `ParseError`
|
|
||||||
- anything related to the lexer, error strategies, character/token streams is hidden from the outside
|
|
||||||
- to make a clear distinction between the *generated* parser (and lexer) vs. `Prog8Parser`, and to discourage directly using the generated stuff, we'll rename the existing `prog8Parser`/`prog8Lexer` to `Prog8ANTLRParser` and `Prog8ANTLRLexer` and move them to package `prog8.parser.generated`
|
|
||||||
4. introduce AST node `CharLiteral` and keep them until after identifier resolution and type checking; insert there an AST transformation step that turns them in UBYTE constants (literals)
|
|
||||||
5. remove uses of `IStringEncoding` from module `compilerAst` - none should be necessary anymore
|
|
||||||
6. move `IStringEncoding` to module `compiler`
|
|
||||||
7. same with `ModuleImporter`, then rewrite that (addressing #46)
|
|
||||||
8. refactor AST nodes and grammar: less generated parse tree nodes (`XyzContext`), less intermediary stuff (private classes in `Antrl2Kotlin.kt`), more compact code. Also: nicer names such as simply `StringLiteral` instead of `StringLiteralValue`
|
|
||||||
9. re-think `IStringEncoding` to address #38
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
|||||||
package prog8.code
|
package prog8.code
|
||||||
|
|
||||||
|
import prog8.code.ast.PtNode
|
||||||
|
import prog8.code.ast.PtProgram
|
||||||
import prog8.code.core.*
|
import prog8.code.core.*
|
||||||
|
|
||||||
|
|
||||||
@ -7,7 +9,7 @@ import prog8.code.core.*
|
|||||||
* Tree structure containing all symbol definitions in the program
|
* Tree structure containing all symbol definitions in the program
|
||||||
* (blocks, subroutines, variables (all types), memoryslabs, and labels).
|
* (blocks, subroutines, variables (all types), memoryslabs, and labels).
|
||||||
*/
|
*/
|
||||||
class SymbolTable : StNode("", StNodeType.GLOBAL, Position.DUMMY) {
|
class SymbolTable(astProgram: PtProgram) : StNode(astProgram.name, StNodeType.GLOBAL, astProgram) {
|
||||||
/**
|
/**
|
||||||
* The table as a flat mapping of scoped names to the StNode.
|
* The table as a flat mapping of scoped names to the StNode.
|
||||||
* This gives the fastest lookup possible (no need to traverse tree nodes)
|
* This gives the fastest lookup possible (no need to traverse tree nodes)
|
||||||
@ -52,7 +54,17 @@ class SymbolTable : StNode("", StNodeType.GLOBAL, Position.DUMMY) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
val allMemorySlabs: Collection<StMemorySlab> by lazy {
|
val allMemorySlabs: Collection<StMemorySlab> by lazy {
|
||||||
children.mapNotNull { if (it.value.type == StNodeType.MEMORYSLAB) it.value as StMemorySlab else null }
|
val vars = mutableListOf<StMemorySlab>()
|
||||||
|
fun collect(node: StNode) {
|
||||||
|
for(child in node.children) {
|
||||||
|
if(child.value.type== StNodeType.MEMORYSLAB)
|
||||||
|
vars.add(child.value as StMemorySlab)
|
||||||
|
else
|
||||||
|
collect(child.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
collect(this)
|
||||||
|
vars
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun lookup(scopedName: String) = flat[scopedName]
|
override fun lookup(scopedName: String) = flat[scopedName]
|
||||||
@ -76,7 +88,7 @@ enum class StNodeType {
|
|||||||
|
|
||||||
open class StNode(val name: String,
|
open class StNode(val name: String,
|
||||||
val type: StNodeType,
|
val type: StNodeType,
|
||||||
val position: Position,
|
val astNode: PtNode,
|
||||||
val children: MutableMap<String, StNode> = mutableMapOf()
|
val children: MutableMap<String, StNode> = mutableMapOf()
|
||||||
) {
|
) {
|
||||||
|
|
||||||
@ -145,33 +157,31 @@ 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?,
|
||||||
val length: Int?, // for arrays: the number of elements, for strings: number of characters *including* the terminating 0-byte
|
val length: Int?, // for arrays: the number of elements, for strings: number of characters *including* the terminating 0-byte
|
||||||
val zpwish: ZeropageWish,
|
val zpwish: ZeropageWish, // used in the variable allocator
|
||||||
position: Position) : StNode(name, StNodeType.STATICVAR, position) {
|
astNode: PtNode) : StNode(name, StNodeType.STATICVAR, astNode) {
|
||||||
|
|
||||||
|
val uninitialized = onetimeInitializationArrayValue==null && onetimeInitializationStringValue==null && onetimeInitializationNumericValue==null
|
||||||
|
|
||||||
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(onetimeInitializationArrayValue.isEmpty() ||onetimeInitializationArrayValue.size==length)
|
require(onetimeInitializationArrayValue.isEmpty() ||onetimeInitializationArrayValue.size==length)
|
||||||
}
|
}
|
||||||
if(onetimeInitializationNumericValue!=null)
|
if(onetimeInitializationNumericValue!=null) {
|
||||||
require(dt in NumericDatatypes)
|
require(dt in NumericDatatypes)
|
||||||
if(onetimeInitializationArrayValue!=null)
|
require(onetimeInitializationNumericValue!=0.0) { "zero as init value should just remain uninitialized"}
|
||||||
|
}
|
||||||
|
if(onetimeInitializationArrayValue!=null) {
|
||||||
require(dt in ArrayDatatypes)
|
require(dt in ArrayDatatypes)
|
||||||
|
if(onetimeInitializationArrayValue.all { it.number!=null} ) {
|
||||||
|
require(onetimeInitializationArrayValue.any { it.number != 0.0 }) { "array of all zerors as init value should just remain uninitialized" }
|
||||||
|
}
|
||||||
|
}
|
||||||
if(onetimeInitializationStringValue!=null) {
|
if(onetimeInitializationStringValue!=null) {
|
||||||
require(dt == DataType.STR)
|
require(dt == DataType.STR)
|
||||||
require(length == onetimeInitializationStringValue.first.length+1)
|
require(length == onetimeInitializationStringValue.first.length+1)
|
||||||
@ -180,8 +190,8 @@ class StStaticVariable(name: String,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class StConstant(name: String, val dt: DataType, val value: Double, position: Position) :
|
class StConstant(name: String, val dt: DataType, val value: Double, astNode: PtNode) :
|
||||||
StNode(name, StNodeType.CONSTANT, position) {
|
StNode(name, StNodeType.CONSTANT, astNode) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -189,31 +199,31 @@ class StMemVar(name: String,
|
|||||||
val dt: DataType,
|
val dt: DataType,
|
||||||
val address: UInt,
|
val address: UInt,
|
||||||
val length: Int?, // for arrays: the number of elements, for strings: number of characters *including* the terminating 0-byte
|
val length: Int?, // for arrays: the number of elements, for strings: number of characters *including* the terminating 0-byte
|
||||||
position: Position) :
|
astNode: PtNode) :
|
||||||
StNode(name, StNodeType.MEMVAR, position) {
|
StNode(name, StNodeType.MEMVAR, astNode) {
|
||||||
}
|
}
|
||||||
|
|
||||||
class StMemorySlab(
|
class StMemorySlab(
|
||||||
name: String,
|
name: String,
|
||||||
val size: UInt,
|
val size: UInt,
|
||||||
val align: UInt,
|
val align: UInt,
|
||||||
position: Position
|
astNode: PtNode
|
||||||
):
|
):
|
||||||
StNode(name, StNodeType.MEMORYSLAB, position) {
|
StNode(name, StNodeType.MEMORYSLAB, astNode) {
|
||||||
}
|
}
|
||||||
|
|
||||||
class StSub(name: String, val parameters: List<StSubroutineParameter>, val returnType: DataType?, position: Position) :
|
class StSub(name: String, val parameters: List<StSubroutineParameter>, val returnType: DataType?, astNode: PtNode) :
|
||||||
StNode(name, StNodeType.SUBROUTINE, position) {
|
StNode(name, StNodeType.SUBROUTINE, astNode) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class StRomSub(name: String,
|
class StRomSub(name: String,
|
||||||
val address: UInt,
|
val address: UInt,
|
||||||
val parameters: List<StRomSubParameter>,
|
val parameters: List<StRomSubParameter>,
|
||||||
val returns: List<RegisterOrStatusflag>,
|
val returns: List<StRomSubParameter>,
|
||||||
position: Position) :
|
astNode: PtNode) :
|
||||||
StNode(name, StNodeType.ROMSUB, position) {
|
StNode(name, StNodeType.ROMSUB, astNode)
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class StSubroutineParameter(val name: String, val type: DataType)
|
class StSubroutineParameter(val name: String, val type: DataType)
|
||||||
|
207
codeCore/src/prog8/code/SymbolTableMaker.kt
Normal file
207
codeCore/src/prog8/code/SymbolTableMaker.kt
Normal file
@ -0,0 +1,207 @@
|
|||||||
|
package prog8.code
|
||||||
|
|
||||||
|
import prog8.code.ast.*
|
||||||
|
import prog8.code.core.*
|
||||||
|
import prog8.code.target.VMTarget
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
class SymbolTableMaker(private val program: PtProgram, private val options: CompilationOptions) {
|
||||||
|
fun make(): SymbolTable {
|
||||||
|
val st = SymbolTable(program)
|
||||||
|
|
||||||
|
BuiltinFunctions.forEach {
|
||||||
|
st.add(StNode(it.key, StNodeType.BUILTINFUNC, PtIdentifier(it.key, it.value.returnType ?: DataType.UNDEFINED, Position.DUMMY)))
|
||||||
|
}
|
||||||
|
|
||||||
|
val scopestack = Stack<StNode>()
|
||||||
|
scopestack.push(st)
|
||||||
|
program.children.forEach {
|
||||||
|
addToSt(it, scopestack)
|
||||||
|
}
|
||||||
|
require(scopestack.size==1)
|
||||||
|
|
||||||
|
if(options.compTarget.name != VMTarget.NAME) {
|
||||||
|
listOf(
|
||||||
|
PtMemMapped("P8ZP_SCRATCH_B1", DataType.UBYTE, options.compTarget.machine.zeropage.SCRATCH_B1, null, Position.DUMMY),
|
||||||
|
PtMemMapped("P8ZP_SCRATCH_REG", DataType.UBYTE, options.compTarget.machine.zeropage.SCRATCH_REG, null, Position.DUMMY),
|
||||||
|
PtMemMapped("P8ZP_SCRATCH_W1", DataType.UWORD, options.compTarget.machine.zeropage.SCRATCH_W1, null, Position.DUMMY),
|
||||||
|
PtMemMapped("P8ZP_SCRATCH_W2", DataType.UWORD, options.compTarget.machine.zeropage.SCRATCH_W2, null, Position.DUMMY),
|
||||||
|
PtMemMapped("P8ESTACK_LO", DataType.ARRAY_UB, options.compTarget.machine.ESTACK_LO, 256u, Position.DUMMY),
|
||||||
|
PtMemMapped("P8ESTACK_HI", DataType.ARRAY_UB, options.compTarget.machine.ESTACK_HI, 256u, Position.DUMMY)
|
||||||
|
).forEach {
|
||||||
|
it.parent = program
|
||||||
|
st.add(StMemVar(it.name, it.type, it.address, null, it))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return st
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun addToSt(node: PtNode, scope: Stack<StNode>) {
|
||||||
|
val stNode = when(node) {
|
||||||
|
is PtAsmSub -> {
|
||||||
|
if(node.address==null) {
|
||||||
|
val params = node.parameters.map { StSubroutineParameter(it.second.name, it.second.type) }
|
||||||
|
StSub(node.name, params, node.returns.singleOrNull()?.second, node)
|
||||||
|
} else {
|
||||||
|
val parameters = node.parameters.map { StRomSubParameter(it.first, it.second.type) }
|
||||||
|
val returns = node.returns.map { StRomSubParameter(it.first, it.second) }
|
||||||
|
StRomSub(node.name, node.address, parameters, returns, node)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is PtBlock -> {
|
||||||
|
StNode(node.name, StNodeType.BLOCK, node)
|
||||||
|
}
|
||||||
|
is PtConstant -> {
|
||||||
|
StConstant(node.name, node.type, node.value, node)
|
||||||
|
}
|
||||||
|
is PtLabel -> {
|
||||||
|
StNode(node.name, StNodeType.LABEL, node)
|
||||||
|
}
|
||||||
|
is PtMemMapped -> {
|
||||||
|
StMemVar(node.name, node.type, node.address, node.arraySize?.toInt(), node)
|
||||||
|
}
|
||||||
|
is PtSub -> {
|
||||||
|
val params = node.parameters.map {StSubroutineParameter(it.name, it.type) }
|
||||||
|
StSub(node.name, params, node.returntype, node)
|
||||||
|
}
|
||||||
|
is PtVariable -> {
|
||||||
|
val initialNumeric: Double?
|
||||||
|
val initialString: StString?
|
||||||
|
val initialArray: StArray?
|
||||||
|
val numElements: Int?
|
||||||
|
val value = node.value
|
||||||
|
if(value!=null) {
|
||||||
|
val number = (value as? PtNumber)?.number
|
||||||
|
initialNumeric = if(number==0.0) null else number // 0 as init value -> just uninitialized
|
||||||
|
when (value) {
|
||||||
|
is PtString -> {
|
||||||
|
initialString = StString(value.value, value.encoding)
|
||||||
|
initialArray = null
|
||||||
|
numElements = value.value.length + 1 // include the terminating 0-byte
|
||||||
|
}
|
||||||
|
is PtArray -> {
|
||||||
|
val array = makeInitialArray(value)
|
||||||
|
initialArray = if(array.all { it.number==0.0 }) null else array // all 0 as init value -> just uninitialized
|
||||||
|
initialString = null
|
||||||
|
numElements = array.size
|
||||||
|
require(node.arraySize?.toInt()==numElements)
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
initialString = null
|
||||||
|
initialArray = null
|
||||||
|
numElements = node.arraySize?.toInt()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
initialNumeric = null
|
||||||
|
initialArray = null
|
||||||
|
initialString = null
|
||||||
|
numElements = node.arraySize?.toInt()
|
||||||
|
}
|
||||||
|
StStaticVariable(node.name, node.type, initialNumeric, initialString, initialArray, numElements, node.zeropage, node)
|
||||||
|
}
|
||||||
|
is PtBuiltinFunctionCall -> {
|
||||||
|
if(node.name=="memory") {
|
||||||
|
// memory slab allocations are a builtin functioncall in the program, but end up named as well in the symboltable
|
||||||
|
require(node.name.all { it.isLetterOrDigit() || it=='_' }) {"memory name should be a valid symbol name"}
|
||||||
|
val slabname = (node.args[0] as PtString).value
|
||||||
|
val size = (node.args[1] as PtNumber).number.toUInt()
|
||||||
|
val align = (node.args[2] as PtNumber).number.toUInt()
|
||||||
|
// don't add memory slabs in nested scope, just put them in the top level of the ST
|
||||||
|
scope.firstElement().add(StMemorySlab("prog8_memoryslab_$slabname", size, align, node))
|
||||||
|
}
|
||||||
|
null
|
||||||
|
}
|
||||||
|
else -> null // node is not present in the ST
|
||||||
|
}
|
||||||
|
|
||||||
|
if(stNode!=null) {
|
||||||
|
scope.peek().add(stNode)
|
||||||
|
scope.push(stNode)
|
||||||
|
}
|
||||||
|
node.children.forEach {
|
||||||
|
addToSt(it, scope)
|
||||||
|
}
|
||||||
|
if(stNode!=null)
|
||||||
|
scope.pop()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun makeInitialArray(value: PtArray): List<StArrayElement> {
|
||||||
|
return value.children.map {
|
||||||
|
when(it) {
|
||||||
|
is PtAddressOf -> StArrayElement(null, it.identifier.name)
|
||||||
|
is PtIdentifier -> StArrayElement(null, it.name)
|
||||||
|
is PtNumber -> StArrayElement(it.number, null)
|
||||||
|
else -> throw AssemblyError("invalid array element $it")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// override fun visit(decl: VarDecl) {
|
||||||
|
// val node =
|
||||||
|
// when(decl.type) {
|
||||||
|
// VarDeclType.VAR -> {
|
||||||
|
// var initialNumeric = (decl.value as? NumericLiteral)?.number
|
||||||
|
// if(initialNumeric==0.0)
|
||||||
|
// initialNumeric=null // variable will go into BSS and this will be set to 0
|
||||||
|
// val initialStringLit = decl.value as? StringLiteral
|
||||||
|
// val initialString = if(initialStringLit==null) null else Pair(initialStringLit.value, initialStringLit.encoding)
|
||||||
|
// val initialArrayLit = decl.value as? ArrayLiteral
|
||||||
|
// val initialArray = makeInitialArray(initialArrayLit)
|
||||||
|
// if(decl.isArray && decl.datatype !in ArrayDatatypes)
|
||||||
|
// throw FatalAstException("array vardecl has mismatched dt ${decl.datatype}")
|
||||||
|
// val numElements =
|
||||||
|
// if(decl.isArray)
|
||||||
|
// decl.arraysize!!.constIndex()
|
||||||
|
// else if(initialStringLit!=null)
|
||||||
|
// initialStringLit.value.length+1 // include the terminating 0-byte
|
||||||
|
// else
|
||||||
|
// null
|
||||||
|
// val bss = if(decl.datatype==DataType.STR)
|
||||||
|
// false
|
||||||
|
// else if(decl.isArray)
|
||||||
|
// initialArray.isNullOrEmpty()
|
||||||
|
// else
|
||||||
|
// initialNumeric == null
|
||||||
|
// val astNode = PtVariable(decl.name, decl.datatype, null, null, decl.position)
|
||||||
|
// StStaticVariable(decl.name, decl.datatype, bss, initialNumeric, initialString, initialArray, numElements, decl.zeropage, astNode, decl.position)
|
||||||
|
// }
|
||||||
|
// VarDeclType.CONST -> {
|
||||||
|
// val astNode = PtVariable(decl.name, decl.datatype, null, null, decl.position)
|
||||||
|
// StConstant(decl.name, decl.datatype, (decl.value as NumericLiteral).number, astNode, decl.position)
|
||||||
|
// }
|
||||||
|
// VarDeclType.MEMORY -> {
|
||||||
|
// val numElements =
|
||||||
|
// if(decl.datatype in ArrayDatatypes)
|
||||||
|
// decl.arraysize!!.constIndex()
|
||||||
|
// else null
|
||||||
|
// val astNode = PtVariable(decl.name, decl.datatype, null, null, decl.position)
|
||||||
|
// StMemVar(decl.name, decl.datatype, (decl.value as NumericLiteral).number.toUInt(), numElements, astNode, decl.position)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// scopestack.peek().add(node)
|
||||||
|
// // st.origAstLinks[decl] = node
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// private fun makeInitialArray(arrayLit: ArrayLiteral?): StArray? {
|
||||||
|
// if(arrayLit==null)
|
||||||
|
// return null
|
||||||
|
// return arrayLit.value.map {
|
||||||
|
// when(it){
|
||||||
|
// is AddressOf -> {
|
||||||
|
// val scopedName = it.identifier.targetNameAndType(program).first
|
||||||
|
// StArrayElement(null, scopedName)
|
||||||
|
// }
|
||||||
|
// is IdentifierReference -> {
|
||||||
|
// val scopedName = it.targetNameAndType(program).first
|
||||||
|
// StArrayElement(null, scopedName)
|
||||||
|
// }
|
||||||
|
// is NumericLiteral -> StArrayElement(it.number, null)
|
||||||
|
// else -> throw FatalAstException("weird element dt in array literal")
|
||||||
|
// }
|
||||||
|
// }.toList()
|
||||||
|
// }
|
||||||
|
//
|
@ -3,6 +3,7 @@ package prog8.code.ast
|
|||||||
import prog8.code.core.IMemSizer
|
import prog8.code.core.IMemSizer
|
||||||
import prog8.code.core.IStringEncoding
|
import prog8.code.core.IStringEncoding
|
||||||
import prog8.code.core.Position
|
import prog8.code.core.Position
|
||||||
|
import prog8.code.core.SourceCode
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
|
|
||||||
// New simplified AST for the code generator.
|
// New simplified AST for the code generator.
|
||||||
@ -13,16 +14,6 @@ sealed class PtNode(val position: Position) {
|
|||||||
val children = mutableListOf<PtNode>()
|
val children = mutableListOf<PtNode>()
|
||||||
lateinit var parent: PtNode
|
lateinit var parent: PtNode
|
||||||
|
|
||||||
fun printIndented(indent: Int) {
|
|
||||||
print(" ".repeat(indent))
|
|
||||||
print("${this.javaClass.simpleName} ")
|
|
||||||
printProperties()
|
|
||||||
println()
|
|
||||||
children.forEach { it.printIndented(indent+1) }
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract fun printProperties()
|
|
||||||
|
|
||||||
fun add(child: PtNode) {
|
fun add(child: PtNode) {
|
||||||
children.add(child)
|
children.add(child)
|
||||||
child.parent = this
|
child.parent = this
|
||||||
@ -36,12 +27,11 @@ sealed class PtNode(val position: Position) {
|
|||||||
fun definingBlock() = findParentNode<PtBlock>(this)
|
fun definingBlock() = findParentNode<PtBlock>(this)
|
||||||
fun definingSub() = findParentNode<PtSub>(this)
|
fun definingSub() = findParentNode<PtSub>(this)
|
||||||
fun definingAsmSub() = findParentNode<PtAsmSub>(this)
|
fun definingAsmSub() = findParentNode<PtAsmSub>(this)
|
||||||
|
fun definingISub() = findParentNode<IPtSubroutine>(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class PtNodeGroup : PtNode(Position.DUMMY) {
|
class PtNodeGroup : PtNode(Position.DUMMY)
|
||||||
override fun printProperties() {}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
sealed class PtNamedNode(var name: String, position: Position): PtNode(position) {
|
sealed class PtNamedNode(var name: String, position: Position): PtNode(position) {
|
||||||
@ -65,10 +55,6 @@ class PtProgram(
|
|||||||
val memsizer: IMemSizer,
|
val memsizer: IMemSizer,
|
||||||
val encoding: IStringEncoding
|
val encoding: IStringEncoding
|
||||||
) : PtNode(Position.DUMMY) {
|
) : PtNode(Position.DUMMY) {
|
||||||
fun print() = printIndented(0)
|
|
||||||
override fun printProperties() {
|
|
||||||
print("'$name'")
|
|
||||||
}
|
|
||||||
|
|
||||||
// fun allModuleDirectives(): Sequence<PtDirective> =
|
// fun allModuleDirectives(): Sequence<PtDirective> =
|
||||||
// children.asSequence().flatMap { it.children }.filterIsInstance<PtDirective>().distinct()
|
// children.asSequence().flatMap { it.children }.filterIsInstance<PtDirective>().distinct()
|
||||||
@ -86,12 +72,9 @@ class PtBlock(name: String,
|
|||||||
val library: Boolean,
|
val library: Boolean,
|
||||||
val forceOutput: Boolean,
|
val forceOutput: Boolean,
|
||||||
val alignment: BlockAlignment,
|
val alignment: BlockAlignment,
|
||||||
|
val source: SourceCode, // taken from the module the block is defined in.
|
||||||
position: Position
|
position: Position
|
||||||
) : PtNamedNode(name, position) {
|
) : PtNamedNode(name, position) {
|
||||||
override fun printProperties() {
|
|
||||||
print("$name addr=$address library=$library forceOutput=$forceOutput alignment=$alignment")
|
|
||||||
}
|
|
||||||
|
|
||||||
enum class BlockAlignment {
|
enum class BlockAlignment {
|
||||||
NONE,
|
NONE,
|
||||||
WORD,
|
WORD,
|
||||||
@ -101,8 +84,6 @@ class PtBlock(name: String,
|
|||||||
|
|
||||||
|
|
||||||
class PtInlineAssembly(val assembly: String, val isIR: Boolean, position: Position) : PtNode(position) {
|
class PtInlineAssembly(val assembly: String, val isIR: Boolean, position: Position) : PtNode(position) {
|
||||||
override fun printProperties() {}
|
|
||||||
|
|
||||||
init {
|
init {
|
||||||
require(!assembly.startsWith('\n') && !assembly.startsWith('\r')) { "inline assembly should be trimmed" }
|
require(!assembly.startsWith('\n') && !assembly.startsWith('\r')) { "inline assembly should be trimmed" }
|
||||||
require(!assembly.endsWith('\n') && !assembly.endsWith('\r')) { "inline assembly should be trimmed" }
|
require(!assembly.endsWith('\n') && !assembly.endsWith('\r')) { "inline assembly should be trimmed" }
|
||||||
@ -110,28 +91,16 @@ class PtInlineAssembly(val assembly: String, val isIR: Boolean, position: Positi
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class PtLabel(name: String, position: Position) : PtNamedNode(name, position) {
|
class PtLabel(name: String, position: Position) : PtNamedNode(name, position)
|
||||||
override fun printProperties() {
|
|
||||||
print(name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class PtBreakpoint(position: Position): PtNode(position) {
|
class PtBreakpoint(position: Position): PtNode(position)
|
||||||
override fun printProperties() {}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class PtIncludeBinary(val file: Path, val offset: UInt?, val length: UInt?, position: Position) : PtNode(position) {
|
class PtIncludeBinary(val file: Path, val offset: UInt?, val length: UInt?, position: Position) : PtNode(position)
|
||||||
override fun printProperties() {
|
|
||||||
print("filename=$file offset=$offset length=$length")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class PtNop(position: Position): PtNode(position) {
|
class PtNop(position: Position): PtNode(position)
|
||||||
override fun printProperties() {}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// find the parent node of a specific type or interface
|
// find the parent node of a specific type or interface
|
||||||
|
@ -1,9 +1,8 @@
|
|||||||
package prog8.code.ast
|
package prog8.code.ast
|
||||||
|
|
||||||
import prog8.code.core.DataType
|
import prog8.code.core.*
|
||||||
import prog8.code.core.Encoding
|
|
||||||
import prog8.code.core.Position
|
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
import kotlin.math.abs
|
||||||
import kotlin.math.round
|
import kotlin.math.round
|
||||||
|
|
||||||
|
|
||||||
@ -23,10 +22,6 @@ sealed class PtExpression(val type: DataType, position: Position) : PtNode(posit
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun printProperties() {
|
|
||||||
print(type)
|
|
||||||
}
|
|
||||||
|
|
||||||
infix fun isSameAs(other: PtExpression): Boolean {
|
infix fun isSameAs(other: PtExpression): Boolean {
|
||||||
return when(this) {
|
return when(this) {
|
||||||
is PtAddressOf -> other is PtAddressOf && other.type==type && other.identifier isSameAs identifier
|
is PtAddressOf -> other is PtAddressOf && other.type==type && other.identifier isSameAs identifier
|
||||||
@ -43,6 +38,69 @@ sealed class PtExpression(val type: DataType, position: Position) : PtNode(posit
|
|||||||
else -> false
|
else -> false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
infix fun isSameAs(target: PtAssignTarget): Boolean {
|
||||||
|
return when {
|
||||||
|
target.memory != null && this is PtMemoryByte-> {
|
||||||
|
target.memory!!.address isSameAs this.address
|
||||||
|
}
|
||||||
|
target.identifier != null && this is PtIdentifier -> {
|
||||||
|
this.name == target.identifier!!.name
|
||||||
|
}
|
||||||
|
target.array != null && this is PtArrayIndexer -> {
|
||||||
|
this.variable.name == target.array!!.variable.name && this.index isSameAs target.array!!.index
|
||||||
|
}
|
||||||
|
else -> false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun asConstInteger(): Int? = (this as? PtNumber)?.number?.toInt()
|
||||||
|
|
||||||
|
fun isSimple(): Boolean {
|
||||||
|
return when(this) {
|
||||||
|
is PtAddressOf -> true
|
||||||
|
is PtArray -> true
|
||||||
|
is PtArrayIndexer -> index is PtNumber || index is PtIdentifier
|
||||||
|
is PtBinaryExpression -> false
|
||||||
|
is PtBuiltinFunctionCall -> name in arrayOf("msb", "lsb", "peek", "peekw", "mkword", "set_carry", "set_irqd", "clear_carry", "clear_irqd")
|
||||||
|
is PtContainmentCheck -> false
|
||||||
|
is PtFunctionCall -> false
|
||||||
|
is PtIdentifier -> true
|
||||||
|
is PtMachineRegister -> true
|
||||||
|
is PtMemoryByte -> address is PtNumber || address is PtIdentifier
|
||||||
|
is PtNumber -> true
|
||||||
|
is PtPrefix -> value.isSimple()
|
||||||
|
is PtRange -> true
|
||||||
|
is PtString -> true
|
||||||
|
is PtTypeCast -> value.isSimple()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
fun clone(): PtExpression {
|
||||||
|
fun withClonedChildrenFrom(orig: PtExpression, clone: PtExpression): PtExpression {
|
||||||
|
orig.children.forEach { clone.add((it as PtExpression).clone()) }
|
||||||
|
return clone
|
||||||
|
}
|
||||||
|
when(this) {
|
||||||
|
is PtAddressOf -> return withClonedChildrenFrom(this, PtAddressOf(position))
|
||||||
|
is PtArray -> return withClonedChildrenFrom(this, PtArray(type, position))
|
||||||
|
is PtArrayIndexer -> return withClonedChildrenFrom(this, PtArrayIndexer(type, position))
|
||||||
|
is PtBinaryExpression -> return withClonedChildrenFrom(this, PtBinaryExpression(operator, type, position))
|
||||||
|
is PtBuiltinFunctionCall -> return withClonedChildrenFrom(this, PtBuiltinFunctionCall(name, void, hasNoSideEffects, type, position))
|
||||||
|
is PtContainmentCheck -> return withClonedChildrenFrom(this, PtContainmentCheck(position))
|
||||||
|
is PtFunctionCall -> return withClonedChildrenFrom(this, PtFunctionCall(name, void, type, position))
|
||||||
|
is PtIdentifier -> return withClonedChildrenFrom(this, PtIdentifier(name, type, position))
|
||||||
|
is PtMachineRegister -> return withClonedChildrenFrom(this, PtMachineRegister(register, type, position))
|
||||||
|
is PtMemoryByte -> return withClonedChildrenFrom(this, PtMemoryByte(position))
|
||||||
|
is PtNumber -> return withClonedChildrenFrom(this, PtNumber(type, number, position))
|
||||||
|
is PtPrefix -> return withClonedChildrenFrom(this, PtPrefix(operator, type, position))
|
||||||
|
is PtRange -> return withClonedChildrenFrom(this, PtRange(type, position))
|
||||||
|
is PtString -> return withClonedChildrenFrom(this, PtString(value, encoding, position))
|
||||||
|
is PtTypeCast -> return withClonedChildrenFrom(this, PtTypeCast(type, position))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
class PtAddressOf(position: Position) : PtExpression(DataType.UWORD, position) {
|
class PtAddressOf(position: Position) : PtExpression(DataType.UWORD, position) {
|
||||||
@ -51,11 +109,15 @@ class PtAddressOf(position: Position) : PtExpression(DataType.UWORD, position) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class PtArrayIndexer(type: DataType, position: Position): PtExpression(type, position) {
|
class PtArrayIndexer(elementType: DataType, position: Position): PtExpression(elementType, position) {
|
||||||
val variable: PtIdentifier
|
val variable: PtIdentifier
|
||||||
get() = children[0] as PtIdentifier
|
get() = children[0] as PtIdentifier
|
||||||
val index: PtExpression
|
val index: PtExpression
|
||||||
get() = children[1] as PtExpression
|
get() = children[1] as PtExpression
|
||||||
|
|
||||||
|
init {
|
||||||
|
require(elementType in NumericDatatypes)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -66,6 +128,9 @@ class PtArray(type: DataType, position: Position): PtExpression(type, position)
|
|||||||
return false
|
return false
|
||||||
return type==other.type && children == other.children
|
return type==other.type && children == other.children
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val size: Int
|
||||||
|
get() = children.size
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -81,9 +146,6 @@ class PtBuiltinFunctionCall(val name: String,
|
|||||||
|
|
||||||
val args: List<PtExpression>
|
val args: List<PtExpression>
|
||||||
get() = children.map { it as PtExpression }
|
get() = children.map { it as PtExpression }
|
||||||
override fun printProperties() {
|
|
||||||
print("$name void=$void noSideFx=$hasNoSideEffects")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -93,10 +155,6 @@ class PtBinaryExpression(val operator: String, type: DataType, position: Positio
|
|||||||
get() = children[0] as PtExpression
|
get() = children[0] as PtExpression
|
||||||
val right: PtExpression
|
val right: PtExpression
|
||||||
get() = children[1] as PtExpression
|
get() = children[1] as PtExpression
|
||||||
|
|
||||||
override fun printProperties() {
|
|
||||||
print("$operator -> $type")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -119,28 +177,25 @@ class PtFunctionCall(val name: String,
|
|||||||
|
|
||||||
val args: List<PtExpression>
|
val args: List<PtExpression>
|
||||||
get() = children.map { it as PtExpression }
|
get() = children.map { it as PtExpression }
|
||||||
override fun printProperties() {
|
|
||||||
print("$name void=$void")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class PtIdentifier(val name: String, type: DataType, position: Position) : PtExpression(type, position) {
|
class PtIdentifier(val name: String, type: DataType, position: Position) : PtExpression(type, position)
|
||||||
override fun printProperties() {
|
|
||||||
print("$name $type")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class PtMemoryByte(position: Position) : PtExpression(DataType.UBYTE, position) {
|
class PtMemoryByte(position: Position) : PtExpression(DataType.UBYTE, position) {
|
||||||
val address: PtExpression
|
val address: PtExpression
|
||||||
get() = children.single() as PtExpression
|
get() = children.single() as PtExpression
|
||||||
override fun printProperties() {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class PtNumber(type: DataType, val number: Double, position: Position) : PtExpression(type, position) {
|
class PtNumber(type: DataType, val number: Double, position: Position) : PtExpression(type, position) {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun fromBoolean(bool: Boolean, position: Position): PtNumber =
|
||||||
|
PtNumber(DataType.UBYTE, if(bool) 1.0 else 0.0, position)
|
||||||
|
}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
if(type==DataType.BOOL)
|
if(type==DataType.BOOL)
|
||||||
throw IllegalArgumentException("bool should have become ubyte @$position")
|
throw IllegalArgumentException("bool should have become ubyte @$position")
|
||||||
@ -151,10 +206,6 @@ class PtNumber(type: DataType, val number: Double, position: Position) : PtExpre
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun printProperties() {
|
|
||||||
print("$number ($type)")
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun hashCode(): Int = Objects.hash(type, number)
|
override fun hashCode(): Int = Objects.hash(type, number)
|
||||||
|
|
||||||
override fun equals(other: Any?): Boolean {
|
override fun equals(other: Any?): Boolean {
|
||||||
@ -175,10 +226,6 @@ class PtPrefix(val operator: String, type: DataType, position: Position): PtExpr
|
|||||||
// 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
|
||||||
require(operator in setOf("+", "-", "~")) { "invalid prefix operator: $operator" }
|
require(operator in setOf("+", "-", "~")) { "invalid prefix operator: $operator" }
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun printProperties() {
|
|
||||||
print(operator)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -190,15 +237,36 @@ class PtRange(type: DataType, position: Position) : PtExpression(type, position)
|
|||||||
val step: PtNumber
|
val step: PtNumber
|
||||||
get() = children[2] as PtNumber
|
get() = children[2] as PtNumber
|
||||||
|
|
||||||
override fun printProperties() {}
|
fun toConstantIntegerRange(): IntProgression? {
|
||||||
|
fun makeRange(fromVal: Int, toVal: Int, stepVal: Int): IntProgression {
|
||||||
|
return when {
|
||||||
|
fromVal <= toVal -> when {
|
||||||
|
stepVal <= 0 -> IntRange.EMPTY
|
||||||
|
stepVal == 1 -> fromVal..toVal
|
||||||
|
else -> fromVal..toVal step stepVal
|
||||||
|
}
|
||||||
|
else -> when {
|
||||||
|
stepVal >= 0 -> IntRange.EMPTY
|
||||||
|
stepVal == -1 -> fromVal downTo toVal
|
||||||
|
else -> fromVal downTo toVal step abs(stepVal)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val fromLv = from as? PtNumber
|
||||||
|
val toLv = to as? PtNumber
|
||||||
|
val stepLv = step as? PtNumber
|
||||||
|
if(fromLv==null || toLv==null || stepLv==null)
|
||||||
|
return null
|
||||||
|
val fromVal = fromLv.number.toInt()
|
||||||
|
val toVal = toLv.number.toInt()
|
||||||
|
val stepVal = stepLv.number.toInt()
|
||||||
|
return makeRange(fromVal, toVal, stepVal)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class PtString(val value: String, val encoding: Encoding, position: Position) : PtExpression(DataType.STR, position) {
|
class PtString(val value: String, val encoding: Encoding, position: Position) : PtExpression(DataType.STR, position) {
|
||||||
override fun printProperties() {
|
|
||||||
print("$encoding:\"$value\"")
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun hashCode(): Int = Objects.hash(value, encoding)
|
override fun hashCode(): Int = Objects.hash(value, encoding)
|
||||||
override fun equals(other: Any?): Boolean {
|
override fun equals(other: Any?): Boolean {
|
||||||
if(other==null || other !is PtString)
|
if(other==null || other !is PtString)
|
||||||
@ -214,12 +282,8 @@ class PtTypeCast(type: DataType, position: Position) : PtExpression(type, positi
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// special node that isn't created from compiling user code, but used internally
|
// special node that isn't created from compiling user code, but used internally in the Intermediate Code
|
||||||
class PtMachineRegister(val register: Int, type: DataType, position: Position) : PtExpression(type, position) {
|
class PtMachineRegister(val register: Int, type: DataType, position: Position) : PtExpression(type, position)
|
||||||
override fun printProperties() {
|
|
||||||
print("reg=$register $type")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
fun constValue(expr: PtExpression): Double? = if(expr is PtNumber) expr.number else null
|
fun constValue(expr: PtExpression): Double? = if(expr is PtNumber) expr.number else null
|
||||||
|
@ -10,8 +10,9 @@ fun printAst(root: PtNode, output: (text: String) -> Unit) {
|
|||||||
fun type(dt: DataType) = "!${dt.name.lowercase()}!"
|
fun type(dt: DataType) = "!${dt.name.lowercase()}!"
|
||||||
fun txt(node: PtNode): String {
|
fun txt(node: PtNode): String {
|
||||||
return when(node) {
|
return when(node) {
|
||||||
is PtAssignTarget -> ""
|
is PtAssignTarget -> "<target>"
|
||||||
is PtAssignment -> "<assign>"
|
is PtAssignment -> "<assign>"
|
||||||
|
is PtAugmentedAssign -> "<inplace-assign> ${node.operator}"
|
||||||
is PtBreakpoint -> "%breakpoint"
|
is PtBreakpoint -> "%breakpoint"
|
||||||
is PtConditionalBranch -> "if_${node.condition.name.lowercase()}"
|
is PtConditionalBranch -> "if_${node.condition.name.lowercase()}"
|
||||||
is PtAddressOf -> "&"
|
is PtAddressOf -> "&"
|
||||||
@ -30,7 +31,10 @@ fun printAst(root: PtNode, output: (text: String) -> Unit) {
|
|||||||
is PtIdentifier -> "${node.name} ${type(node.type)}"
|
is PtIdentifier -> "${node.name} ${type(node.type)}"
|
||||||
is PtMachineRegister -> "VMREG#${node.register} ${type(node.type)}"
|
is PtMachineRegister -> "VMREG#${node.register} ${type(node.type)}"
|
||||||
is PtMemoryByte -> "@()"
|
is PtMemoryByte -> "@()"
|
||||||
is PtNumber -> "${node.number.toHex()} ${type(node.type)}"
|
is PtNumber -> {
|
||||||
|
val numstr = if(node.type == DataType.FLOAT) node.number.toString() else node.number.toHex()
|
||||||
|
"$numstr ${type(node.type)}"
|
||||||
|
}
|
||||||
is PtPrefix -> node.operator
|
is PtPrefix -> node.operator
|
||||||
is PtRange -> "<range>"
|
is PtRange -> "<range>"
|
||||||
is PtString -> "\"${node.value.escape()}\""
|
is PtString -> "\"${node.value.escape()}\""
|
||||||
@ -57,7 +61,7 @@ fun printAst(root: PtNode, output: (text: String) -> Unit) {
|
|||||||
is PtAsmSub -> {
|
is PtAsmSub -> {
|
||||||
val params = if (node.parameters.isEmpty()) "" else "...TODO ${node.parameters.size} PARAMS..."
|
val params = if (node.parameters.isEmpty()) "" else "...TODO ${node.parameters.size} PARAMS..."
|
||||||
val clobbers = if (node.clobbers.isEmpty()) "" else "clobbers ${node.clobbers}"
|
val clobbers = if (node.clobbers.isEmpty()) "" else "clobbers ${node.clobbers}"
|
||||||
val returns = if (node.returnTypes.isEmpty()) "" else (if (node.returnTypes.size == 1) "-> ${node.returnTypes[0].name.lowercase()}" else "-> ${node.returnTypes.map { it.name.lowercase() }}")
|
val returns = if (node.returns.isEmpty()) "" else (if (node.returns.size == 1) "-> ${node.returns[0].second.name.lowercase()}" else "-> ${node.returns.map { it.second.name.lowercase() }}")
|
||||||
val str = if (node.inline) "inline " else ""
|
val str = if (node.inline) "inline " else ""
|
||||||
if(node.address==null) {
|
if(node.address==null) {
|
||||||
str + "asmsub ${node.name}($params) $clobbers $returns"
|
str + "asmsub ${node.name}($params) $clobbers $returns"
|
||||||
@ -66,7 +70,7 @@ fun printAst(root: PtNode, output: (text: String) -> Unit) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
is PtBlock -> {
|
is PtBlock -> {
|
||||||
val addr = if(node.address==null) "" else "@${node.address?.toHex()}"
|
val addr = if(node.address==null) "" else "@${node.address.toHex()}"
|
||||||
val align = if(node.alignment==PtBlock.BlockAlignment.NONE) "" else "align=${node.alignment}"
|
val align = if(node.alignment==PtBlock.BlockAlignment.NONE) "" else "align=${node.alignment}"
|
||||||
"\nblock '${node.name}' $addr $align"
|
"\nblock '${node.name}' $addr $align"
|
||||||
}
|
}
|
||||||
@ -86,8 +90,7 @@ fun printAst(root: PtNode, output: (text: String) -> Unit) {
|
|||||||
}
|
}
|
||||||
is PtSub -> {
|
is PtSub -> {
|
||||||
val params = if (node.parameters.isEmpty()) "" else "...TODO ${node.parameters.size} PARAMS..."
|
val params = if (node.parameters.isEmpty()) "" else "...TODO ${node.parameters.size} PARAMS..."
|
||||||
var str = if(node.inline) "inline " else ""
|
var str = "sub ${node.name}($params) "
|
||||||
str += "sub ${node.name}($params) "
|
|
||||||
if(node.returntype!=null)
|
if(node.returntype!=null)
|
||||||
str += "-> ${node.returntype.name.lowercase()}"
|
str += "-> ${node.returntype.name.lowercase()}"
|
||||||
str
|
str
|
||||||
@ -104,7 +107,7 @@ fun printAst(root: PtNode, output: (text: String) -> Unit) {
|
|||||||
else
|
else
|
||||||
"${node.type.name.lowercase()} ${node.name}"
|
"${node.type.name.lowercase()} ${node.name}"
|
||||||
if(node.value!=null)
|
if(node.value!=null)
|
||||||
str + " = " + txt(node.value!!)
|
str + " = " + txt(node.value)
|
||||||
else
|
else
|
||||||
str
|
str
|
||||||
}
|
}
|
||||||
@ -135,6 +138,7 @@ fun printAst(root: PtNode, output: (text: String) -> Unit) {
|
|||||||
output(" ".repeat(depth) + txt(node))
|
output(" ".repeat(depth) + txt(node))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
println()
|
||||||
} else {
|
} else {
|
||||||
walkAst(root) { node, depth ->
|
walkAst(root) { node, depth ->
|
||||||
val txt = txt(node)
|
val txt = txt(node)
|
||||||
|
@ -3,48 +3,39 @@ package prog8.code.ast
|
|||||||
import prog8.code.core.*
|
import prog8.code.core.*
|
||||||
|
|
||||||
|
|
||||||
|
sealed interface IPtSubroutine {
|
||||||
|
val name: String
|
||||||
|
}
|
||||||
|
|
||||||
class PtAsmSub(
|
class PtAsmSub(
|
||||||
name: String,
|
name: String,
|
||||||
val address: UInt?,
|
val address: UInt?,
|
||||||
val clobbers: Set<CpuRegister>,
|
val clobbers: Set<CpuRegister>,
|
||||||
val parameters: List<Pair<PtSubroutineParameter, RegisterOrStatusflag>>,
|
val parameters: List<Pair<RegisterOrStatusflag, PtSubroutineParameter>>,
|
||||||
val returnTypes: List<DataType>, // TODO join with register as Pairs ?
|
val returns: List<Pair<RegisterOrStatusflag, DataType>>,
|
||||||
val retvalRegisters: List<RegisterOrStatusflag>,
|
|
||||||
val inline: Boolean,
|
val inline: Boolean,
|
||||||
position: Position
|
position: Position
|
||||||
) : PtNamedNode(name, position) {
|
) : PtNamedNode(name, position), IPtSubroutine
|
||||||
override fun printProperties() {
|
|
||||||
print("$name inline=$inline")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class PtSub(
|
class PtSub(
|
||||||
name: String,
|
name: String,
|
||||||
val parameters: List<PtSubroutineParameter>,
|
val parameters: List<PtSubroutineParameter>,
|
||||||
val returntype: DataType?,
|
val returntype: DataType?,
|
||||||
val inline: Boolean,
|
|
||||||
position: Position
|
position: Position
|
||||||
) : PtNamedNode(name, position) {
|
) : PtNamedNode(name, position), IPtSubroutine {
|
||||||
override fun printProperties() {
|
|
||||||
print(name)
|
|
||||||
}
|
|
||||||
|
|
||||||
init {
|
init {
|
||||||
// params and return value should not be str
|
// params and return value should not be str
|
||||||
if(parameters.any{ it.type !in NumericDatatypes })
|
if(parameters.any{ it.type !in NumericDatatypes })
|
||||||
throw AssemblyError("non-numeric parameter")
|
throw AssemblyError("non-numeric parameter")
|
||||||
if(returntype!=null && returntype !in NumericDatatypes)
|
if(returntype!=null && returntype !in NumericDatatypes)
|
||||||
throw AssemblyError("non-numeric returntype $returntype")
|
throw AssemblyError("non-numeric returntype $returntype")
|
||||||
|
parameters.forEach { it.parent=this }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class PtSubroutineParameter(val name: String, val type: DataType, position: Position): PtNode(position) {
|
class PtSubroutineParameter(name: String, val type: DataType, position: Position): PtNamedNode(name, position)
|
||||||
override fun printProperties() {
|
|
||||||
print("$type $name")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class PtAssignment(position: Position) : PtNode(position) {
|
class PtAssignment(position: Position) : PtNode(position) {
|
||||||
@ -52,38 +43,22 @@ class PtAssignment(position: Position) : PtNode(position) {
|
|||||||
get() = children[0] as PtAssignTarget
|
get() = children[0] as PtAssignTarget
|
||||||
val value: PtExpression
|
val value: PtExpression
|
||||||
get() = children[1] as PtExpression
|
get() = children[1] as PtExpression
|
||||||
|
}
|
||||||
|
|
||||||
override fun printProperties() { }
|
|
||||||
|
|
||||||
val isInplaceAssign: Boolean by lazy {
|
class PtAugmentedAssign(val operator: String, position: Position) : PtNode(position) {
|
||||||
val target = target.children.single() as PtExpression
|
val target: PtAssignTarget
|
||||||
when(val source = value) {
|
get() = children[0] as PtAssignTarget
|
||||||
is PtArrayIndexer -> {
|
val value: PtExpression
|
||||||
if(target is PtArrayIndexer && source.type==target.type) {
|
get() = children[1] as PtExpression
|
||||||
if(target.variable isSameAs source.variable) {
|
init {
|
||||||
target.index isSameAs source.index
|
require(operator.endsWith('=') || operator in PrefixOperators) {
|
||||||
}
|
"invalid augmented assign operator $operator"
|
||||||
}
|
|
||||||
false
|
|
||||||
}
|
|
||||||
is PtIdentifier -> target is PtIdentifier && target.type==source.type && target.name==source.name
|
|
||||||
is PtMachineRegister -> target is PtMachineRegister && target.register==source.register
|
|
||||||
is PtMemoryByte -> target is PtMemoryByte && target.address isSameAs source.address
|
|
||||||
is PtNumber -> target is PtNumber && target.type == source.type && target.number==source.number
|
|
||||||
is PtAddressOf -> target is PtAddressOf && target.identifier isSameAs source.identifier
|
|
||||||
is PtPrefix -> {
|
|
||||||
(target is PtPrefix && target.operator==source.operator && target.value isSameAs source.value)
|
|
||||||
||
|
|
||||||
(target is PtIdentifier && (source.value as? PtIdentifier)?.name==target.name)
|
|
||||||
}
|
|
||||||
is PtTypeCast -> target is PtTypeCast && target.type==source.type && target.value isSameAs source.value
|
|
||||||
is PtBinaryExpression ->
|
|
||||||
target isSameAs source.left
|
|
||||||
else -> false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class PtAssignTarget(position: Position) : PtNode(position) {
|
class PtAssignTarget(position: Position) : PtNode(position) {
|
||||||
val identifier: PtIdentifier?
|
val identifier: PtIdentifier?
|
||||||
get() = children.single() as? PtIdentifier
|
get() = children.single() as? PtIdentifier
|
||||||
@ -102,7 +77,7 @@ class PtAssignTarget(position: Position) : PtNode(position) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun printProperties() {}
|
infix fun isSameAs(expression: PtExpression): Boolean = expression.isSameAs(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -111,10 +86,6 @@ class PtConditionalBranch(val condition: BranchCondition, position: Position) :
|
|||||||
get() = children[0] as PtNodeGroup
|
get() = children[0] as PtNodeGroup
|
||||||
val falseScope: PtNodeGroup
|
val falseScope: PtNodeGroup
|
||||||
get() = children[1] as PtNodeGroup
|
get() = children[1] as PtNodeGroup
|
||||||
|
|
||||||
override fun printProperties() {
|
|
||||||
print(condition)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -125,8 +96,6 @@ class PtForLoop(position: Position) : PtNode(position) {
|
|||||||
get() = children[1] as PtExpression
|
get() = children[1] as PtExpression
|
||||||
val statements: PtNodeGroup
|
val statements: PtNodeGroup
|
||||||
get() = children[2] as PtNodeGroup
|
get() = children[2] as PtNodeGroup
|
||||||
|
|
||||||
override fun printProperties() {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -137,8 +106,6 @@ class PtIfElse(position: Position) : PtNode(position) {
|
|||||||
get() = children[1] as PtNodeGroup
|
get() = children[1] as PtNodeGroup
|
||||||
val elseScope: PtNodeGroup
|
val elseScope: PtNodeGroup
|
||||||
get() = children[2] as PtNodeGroup
|
get() = children[2] as PtNodeGroup
|
||||||
|
|
||||||
override fun printProperties() {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -146,10 +113,8 @@ class PtJump(val identifier: PtIdentifier?,
|
|||||||
val address: UInt?,
|
val address: UInt?,
|
||||||
val generatedLabel: String?,
|
val generatedLabel: String?,
|
||||||
position: Position) : PtNode(position) {
|
position: Position) : PtNode(position) {
|
||||||
override fun printProperties() {
|
init {
|
||||||
identifier?.printProperties()
|
identifier?.let {it.parent = this }
|
||||||
if(address!=null) print(address.toHex())
|
|
||||||
if(generatedLabel!=null) print(generatedLabel)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -157,10 +122,6 @@ class PtJump(val identifier: PtIdentifier?,
|
|||||||
class PtPostIncrDecr(val operator: String, position: Position) : PtNode(position) {
|
class PtPostIncrDecr(val operator: String, position: Position) : PtNode(position) {
|
||||||
val target: PtAssignTarget
|
val target: PtAssignTarget
|
||||||
get() = children.single() as PtAssignTarget
|
get() = children.single() as PtAssignTarget
|
||||||
|
|
||||||
override fun printProperties() {
|
|
||||||
print(operator)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -169,8 +130,6 @@ class PtRepeatLoop(position: Position) : PtNode(position) {
|
|||||||
get() = children[0] as PtExpression
|
get() = children[0] as PtExpression
|
||||||
val statements: PtNodeGroup
|
val statements: PtNodeGroup
|
||||||
get() = children[1] as PtNodeGroup
|
get() = children[1] as PtNodeGroup
|
||||||
|
|
||||||
override fun printProperties() {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -183,30 +142,26 @@ class PtReturn(position: Position) : PtNode(position) {
|
|||||||
else
|
else
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun printProperties() {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class PtVariable(name: String, val type: DataType, var value: PtExpression?, var arraySize: UInt?, position: Position) : PtNamedNode(name, position) {
|
sealed interface IPtVariable {
|
||||||
override fun printProperties() {
|
val name: String
|
||||||
print("$type $name")
|
val type: DataType
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class PtVariable(name: String, override val type: DataType, val zeropage: ZeropageWish, val value: PtExpression?, val arraySize: UInt?, position: Position) : PtNamedNode(name, position), IPtVariable {
|
||||||
|
init {
|
||||||
|
value?.let {it.parent=this}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class PtConstant(name: String, val type: DataType, val value: Double, position: Position) : PtNamedNode(name, position) {
|
class PtConstant(name: String, override val type: DataType, val value: Double, position: Position) : PtNamedNode(name, position), IPtVariable
|
||||||
override fun printProperties() {
|
|
||||||
print("$type $name = $value")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class PtMemMapped(name: String, val type: DataType, val address: UInt, val arraySize: UInt?, position: Position) : PtNamedNode(name, position) {
|
class PtMemMapped(name: String, override val type: DataType, val address: UInt, val arraySize: UInt?, position: Position) : PtNamedNode(name, position), IPtVariable
|
||||||
override fun printProperties() {
|
|
||||||
print("&$type $name = ${address.toHex()}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class PtWhen(position: Position) : PtNode(position) {
|
class PtWhen(position: Position) : PtNode(position) {
|
||||||
@ -214,8 +169,6 @@ class PtWhen(position: Position) : PtNode(position) {
|
|||||||
get() = children[0] as PtExpression
|
get() = children[0] as PtExpression
|
||||||
val choices: PtNodeGroup
|
val choices: PtNodeGroup
|
||||||
get() = children[1] as PtNodeGroup
|
get() = children[1] as PtNodeGroup
|
||||||
|
|
||||||
override fun printProperties() {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -224,5 +177,4 @@ class PtWhenChoice(val isElse: Boolean, position: Position) : PtNode(position) {
|
|||||||
get() = children[0] as PtNodeGroup
|
get() = children[0] as PtNodeGroup
|
||||||
val statements: PtNodeGroup
|
val statements: PtNodeGroup
|
||||||
get() = children[1] as PtNodeGroup
|
get() = children[1] as PtNodeGroup
|
||||||
override fun printProperties() {}
|
|
||||||
}
|
}
|
||||||
|
110
codeCore/src/prog8/code/core/BuiltinFunctions.kt
Normal file
110
codeCore/src/prog8/code/core/BuiltinFunctions.kt
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
package prog8.code.core
|
||||||
|
|
||||||
|
class ReturnConvention(val dt: DataType?, val reg: RegisterOrPair?, val floatFac1: Boolean)
|
||||||
|
class ParamConvention(val dt: DataType, val reg: RegisterOrPair?, val variable: Boolean)
|
||||||
|
class CallConvention(val params: List<ParamConvention>, val returns: ReturnConvention) {
|
||||||
|
override fun toString(): String {
|
||||||
|
val paramConvs = params.mapIndexed { index, it ->
|
||||||
|
when {
|
||||||
|
it.reg!=null -> "$index:${it.reg}"
|
||||||
|
it.variable -> "$index:variable"
|
||||||
|
else -> "$index:???"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val returnConv =
|
||||||
|
when {
|
||||||
|
returns.reg!=null -> returns.reg.toString()
|
||||||
|
returns.floatFac1 -> "floatFAC1"
|
||||||
|
else -> "<no returnvalue>"
|
||||||
|
}
|
||||||
|
return "CallConvention[" + paramConvs.joinToString() + " ; returns: $returnConv]"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class FParam(val name: String, val possibleDatatypes: Array<DataType>)
|
||||||
|
|
||||||
|
class FSignature(val pure: Boolean, // does it have side effects?
|
||||||
|
val parameters: List<FParam>,
|
||||||
|
val returnType: DataType?) {
|
||||||
|
|
||||||
|
fun callConvention(actualParamTypes: List<DataType>): CallConvention {
|
||||||
|
val returns: ReturnConvention = when (returnType) {
|
||||||
|
DataType.UBYTE, DataType.BYTE -> ReturnConvention(returnType, RegisterOrPair.A, false)
|
||||||
|
DataType.UWORD, DataType.WORD -> ReturnConvention(returnType, RegisterOrPair.AY, false)
|
||||||
|
DataType.FLOAT -> ReturnConvention(returnType, null, true)
|
||||||
|
in PassByReferenceDatatypes -> ReturnConvention(returnType!!, RegisterOrPair.AY, false)
|
||||||
|
null -> ReturnConvention(null, null, false)
|
||||||
|
else -> {
|
||||||
|
// return type depends on arg type
|
||||||
|
when (val paramType = actualParamTypes.first()) {
|
||||||
|
DataType.UBYTE, DataType.BYTE -> ReturnConvention(paramType, RegisterOrPair.A, false)
|
||||||
|
DataType.UWORD, DataType.WORD -> ReturnConvention(paramType, RegisterOrPair.AY, false)
|
||||||
|
DataType.FLOAT -> ReturnConvention(paramType, null, true)
|
||||||
|
in PassByReferenceDatatypes -> ReturnConvention(paramType, RegisterOrPair.AY, false)
|
||||||
|
else -> ReturnConvention(paramType, null, false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return when {
|
||||||
|
actualParamTypes.isEmpty() -> CallConvention(emptyList(), returns)
|
||||||
|
actualParamTypes.size==1 -> {
|
||||||
|
// one parameter goes via register/registerpair
|
||||||
|
val paramConv = when(val paramType = actualParamTypes[0]) {
|
||||||
|
DataType.UBYTE, DataType.BYTE -> ParamConvention(paramType, RegisterOrPair.A, false)
|
||||||
|
DataType.UWORD, DataType.WORD -> ParamConvention(paramType, RegisterOrPair.AY, false)
|
||||||
|
DataType.FLOAT -> ParamConvention(paramType, RegisterOrPair.AY, false)
|
||||||
|
in PassByReferenceDatatypes -> ParamConvention(paramType, RegisterOrPair.AY, false)
|
||||||
|
else -> ParamConvention(paramType, null, false)
|
||||||
|
}
|
||||||
|
CallConvention(listOf(paramConv), returns)
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
// multiple parameters go via variables
|
||||||
|
val paramConvs = actualParamTypes.map { ParamConvention(it, null, true) }
|
||||||
|
CallConvention(paramConvs, returns)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
val BuiltinFunctions: Map<String, FSignature> = mapOf(
|
||||||
|
// this set of function have no return value and operate in-place:
|
||||||
|
"rol" to FSignature(false, listOf(FParam("item", arrayOf(DataType.UBYTE, DataType.UWORD))), null),
|
||||||
|
"ror" to FSignature(false, listOf(FParam("item", arrayOf(DataType.UBYTE, DataType.UWORD))), null),
|
||||||
|
"rol2" to FSignature(false, listOf(FParam("item", arrayOf(DataType.UBYTE, DataType.UWORD))), null),
|
||||||
|
"ror2" to FSignature(false, listOf(FParam("item", arrayOf(DataType.UBYTE, DataType.UWORD))), null),
|
||||||
|
"sort" to FSignature(false, listOf(FParam("array", ArrayDatatypes)), null),
|
||||||
|
"reverse" to FSignature(false, listOf(FParam("array", ArrayDatatypes)), null),
|
||||||
|
// cmp returns a status in the carry flag, but not a proper return value
|
||||||
|
"cmp" to FSignature(false, listOf(FParam("value1", IntegerDatatypesNoBool), FParam("value2", NumericDatatypesNoBool)), null),
|
||||||
|
"abs" to FSignature(true, listOf(FParam("value", IntegerDatatypesNoBool)), DataType.UWORD),
|
||||||
|
"len" to FSignature(true, listOf(FParam("values", IterableDatatypes)), DataType.UWORD),
|
||||||
|
// normal functions follow:
|
||||||
|
"sizeof" to FSignature(true, listOf(FParam("object", DataType.values())), DataType.UBYTE),
|
||||||
|
"sgn" to FSignature(true, listOf(FParam("value", NumericDatatypesNoBool)), DataType.BYTE),
|
||||||
|
"sqrt16" to FSignature(true, listOf(FParam("value", arrayOf(DataType.UWORD))), DataType.UBYTE),
|
||||||
|
"any" to FSignature(true, listOf(FParam("values", ArrayDatatypes)), DataType.UBYTE),
|
||||||
|
"all" to FSignature(true, listOf(FParam("values", ArrayDatatypes)), DataType.UBYTE),
|
||||||
|
"lsb" to FSignature(true, listOf(FParam("value", arrayOf(DataType.UWORD, DataType.WORD))), DataType.UBYTE),
|
||||||
|
"msb" to FSignature(true, listOf(FParam("value", arrayOf(DataType.UWORD, DataType.WORD))), DataType.UBYTE),
|
||||||
|
"mkword" to FSignature(true, listOf(FParam("msb", arrayOf(DataType.UBYTE)), FParam("lsb", arrayOf(DataType.UBYTE))), DataType.UWORD),
|
||||||
|
"peek" to FSignature(true, listOf(FParam("address", arrayOf(DataType.UWORD))), DataType.UBYTE),
|
||||||
|
"peekw" to FSignature(true, listOf(FParam("address", arrayOf(DataType.UWORD))), DataType.UWORD),
|
||||||
|
"poke" to FSignature(false, listOf(FParam("address", arrayOf(DataType.UWORD)), FParam("value", arrayOf(DataType.UBYTE))), null),
|
||||||
|
"pokemon" to FSignature(false, listOf(FParam("address", arrayOf(DataType.UWORD)), FParam("value", arrayOf(DataType.UBYTE))), null),
|
||||||
|
"pokew" to FSignature(false, listOf(FParam("address", arrayOf(DataType.UWORD)), FParam("value", arrayOf(DataType.UWORD))), null),
|
||||||
|
"pop" to FSignature(false, listOf(FParam("target", ByteDatatypes)), null),
|
||||||
|
"popw" to FSignature(false, listOf(FParam("target", WordDatatypes)), null),
|
||||||
|
"push" to FSignature(false, listOf(FParam("value", ByteDatatypes)), null),
|
||||||
|
"pushw" to FSignature(false, listOf(FParam("value", WordDatatypes)), null),
|
||||||
|
"rsave" to FSignature(false, emptyList(), null),
|
||||||
|
"rsavex" to FSignature(false, emptyList(), null),
|
||||||
|
"rrestore" to FSignature(false, emptyList(), null),
|
||||||
|
"rrestorex" to FSignature(false, emptyList(), null),
|
||||||
|
"memory" to FSignature(true, listOf(FParam("name", arrayOf(DataType.STR)), FParam("size", arrayOf(DataType.UWORD)), FParam("alignment", arrayOf(DataType.UWORD))), DataType.UWORD),
|
||||||
|
"callfar" to FSignature(false, listOf(FParam("bank", arrayOf(DataType.UBYTE)), FParam("address", arrayOf(DataType.UWORD)), FParam("arg", arrayOf(DataType.UWORD))), DataType.UWORD),
|
||||||
|
)
|
||||||
|
|
||||||
|
val InplaceModifyingBuiltinFunctions = setOf("rol", "ror", "rol2", "ror2", "sort", "reverse")
|
@ -16,11 +16,15 @@ class CompilationOptions(val output: OutputType,
|
|||||||
var slowCodegenWarnings: Boolean = false,
|
var slowCodegenWarnings: Boolean = false,
|
||||||
var optimize: Boolean = false,
|
var optimize: Boolean = false,
|
||||||
var optimizeFloatExpressions: Boolean = false,
|
var optimizeFloatExpressions: Boolean = false,
|
||||||
var dontReinitGlobals: Boolean = false,
|
|
||||||
var asmQuiet: Boolean = false,
|
var asmQuiet: Boolean = false,
|
||||||
var asmListfile: Boolean = false,
|
var asmListfile: Boolean = false,
|
||||||
var experimentalCodegen: Boolean = false,
|
var experimentalCodegen: Boolean = false,
|
||||||
|
var varsHigh: Boolean = false,
|
||||||
var evalStackBaseAddress: UInt? = null,
|
var evalStackBaseAddress: UInt? = null,
|
||||||
var outputDir: Path = Path(""),
|
var outputDir: Path = Path(""),
|
||||||
var symbolDefs: Map<String, String> = emptyMap()
|
var symbolDefs: Map<String, String> = emptyMap()
|
||||||
)
|
) {
|
||||||
|
init {
|
||||||
|
compTarget.machine.initializeMemoryAreas(this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,12 +0,0 @@
|
|||||||
package prog8.code.core
|
|
||||||
|
|
||||||
interface IAssemblyGenerator {
|
|
||||||
fun compileToAssembly(): IAssemblyProgram?
|
|
||||||
}
|
|
||||||
|
|
||||||
interface IAssemblyProgram {
|
|
||||||
val name: String
|
|
||||||
fun assemble(options: CompilationOptions): Boolean
|
|
||||||
}
|
|
||||||
|
|
||||||
fun viceMonListName(baseFilename: String) = "$baseFilename.vice-mon-list"
|
|
19
codeCore/src/prog8/code/core/ICodeGeneratorBackend.kt
Normal file
19
codeCore/src/prog8/code/core/ICodeGeneratorBackend.kt
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
package prog8.code.core
|
||||||
|
|
||||||
|
import prog8.code.SymbolTable
|
||||||
|
import prog8.code.ast.PtProgram
|
||||||
|
|
||||||
|
interface ICodeGeneratorBackend {
|
||||||
|
fun generate(program: PtProgram,
|
||||||
|
symbolTable: SymbolTable,
|
||||||
|
options: CompilationOptions,
|
||||||
|
errors: IErrorReporter): IAssemblyProgram?
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
interface IAssemblyProgram {
|
||||||
|
val name: String
|
||||||
|
fun assemble(options: CompilationOptions): Boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
fun viceMonListName(baseFilename: String) = "$baseFilename.vice-mon-list"
|
@ -7,5 +7,5 @@ interface ICompilationTarget: IStringEncoding, IMemSizer {
|
|||||||
val defaultEncoding: Encoding
|
val defaultEncoding: Encoding
|
||||||
|
|
||||||
override fun encodeString(str: String, encoding: Encoding): List<UByte>
|
override fun encodeString(str: String, encoding: Encoding): List<UByte>
|
||||||
override fun decodeString(bytes: List<UByte>, encoding: Encoding): String
|
override fun decodeString(bytes: Iterable<UByte>, encoding: Encoding): String
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,8 @@ interface IMachineDefinition {
|
|||||||
var ESTACK_LO: UInt
|
var ESTACK_LO: UInt
|
||||||
var ESTACK_HI: UInt
|
var ESTACK_HI: UInt
|
||||||
val PROGRAM_LOAD_ADDRESS : UInt
|
val PROGRAM_LOAD_ADDRESS : UInt
|
||||||
|
val BSSHIGHRAM_START: UInt
|
||||||
|
val BSSHIGHRAM_END: UInt
|
||||||
|
|
||||||
val cpu: CpuType
|
val cpu: CpuType
|
||||||
var zeropage: Zeropage
|
var zeropage: Zeropage
|
||||||
|
@ -10,5 +10,5 @@ enum class Encoding(val prefix: String) {
|
|||||||
|
|
||||||
interface IStringEncoding {
|
interface IStringEncoding {
|
||||||
fun encodeString(str: String, encoding: Encoding): List<UByte>
|
fun encodeString(str: String, encoding: Encoding): List<UByte>
|
||||||
fun decodeString(bytes: List<UByte>, encoding: Encoding): String
|
fun decodeString(bytes: Iterable<UByte>, encoding: Encoding): String
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,7 @@ class MemAllocationError(message: String) : Exception(message)
|
|||||||
abstract class MemoryAllocator(protected val options: CompilationOptions) {
|
abstract class MemoryAllocator(protected val options: CompilationOptions) {
|
||||||
data class VarAllocation(val address: UInt, val dt: DataType, val size: Int)
|
data class VarAllocation(val address: UInt, val dt: DataType, val size: Int)
|
||||||
|
|
||||||
abstract fun allocate(name: List<String>,
|
abstract fun allocate(name: String,
|
||||||
datatype: DataType,
|
datatype: DataType,
|
||||||
numElements: Int?,
|
numElements: Int?,
|
||||||
position: Position?,
|
position: Position?,
|
||||||
@ -29,7 +29,7 @@ abstract class Zeropage(options: CompilationOptions): MemoryAllocator(options) {
|
|||||||
|
|
||||||
// the variables allocated into Zeropage.
|
// the variables allocated into Zeropage.
|
||||||
// name (scoped) ==> pair of address to (Datatype + bytesize)
|
// name (scoped) ==> pair of address to (Datatype + bytesize)
|
||||||
val allocatedVariables = mutableMapOf<List<String>, VarAllocation>()
|
val allocatedVariables = mutableMapOf<String, VarAllocation>()
|
||||||
|
|
||||||
val free = mutableListOf<UInt>() // subclasses must set this to the appropriate free locations.
|
val free = mutableListOf<UInt>() // subclasses must set this to the appropriate free locations.
|
||||||
|
|
||||||
@ -51,7 +51,7 @@ abstract class Zeropage(options: CompilationOptions): MemoryAllocator(options) {
|
|||||||
return free.windowed(2).any { it[0] == it[1] - 1u }
|
return free.windowed(2).any { it[0] == it[1] - 1u }
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun allocate(name: List<String>,
|
override fun allocate(name: String,
|
||||||
datatype: DataType,
|
datatype: DataType,
|
||||||
numElements: Int?,
|
numElements: Int?,
|
||||||
position: Position?,
|
position: Position?,
|
||||||
@ -107,7 +107,7 @@ abstract class Zeropage(options: CompilationOptions): MemoryAllocator(options) {
|
|||||||
|
|
||||||
private fun reserve(range: UIntRange) = free.removeAll(range)
|
private fun reserve(range: UIntRange) = free.removeAll(range)
|
||||||
|
|
||||||
private fun makeAllocation(address: UInt, size: Int, datatype: DataType, name: List<String>): UInt {
|
private fun makeAllocation(address: UInt, size: Int, datatype: DataType, name: String): UInt {
|
||||||
require(size>=0)
|
require(size>=0)
|
||||||
free.removeAll(address until address+size.toUInt())
|
free.removeAll(address until address+size.toUInt())
|
||||||
if(name.isNotEmpty()) {
|
if(name.isNotEmpty()) {
|
||||||
@ -135,7 +135,7 @@ class GoldenRam(options: CompilationOptions, val region: UIntRange): MemoryAlloc
|
|||||||
private var nextLocation: UInt = region.first
|
private var nextLocation: UInt = region.first
|
||||||
|
|
||||||
override fun allocate(
|
override fun allocate(
|
||||||
name: List<String>,
|
name: String,
|
||||||
datatype: DataType,
|
datatype: DataType,
|
||||||
numElements: Int?,
|
numElements: Int?,
|
||||||
position: Position?,
|
position: Position?,
|
||||||
|
@ -5,6 +5,7 @@ val ComparisonOperators = setOf("==", "!=", "<", ">", "<=", ">=")
|
|||||||
val LogicalOperators = setOf("and", "or", "xor", "not")
|
val LogicalOperators = setOf("and", "or", "xor", "not")
|
||||||
val AugmentAssignmentOperators = setOf("+", "-", "/", "*", "&", "|", "^", "<<", ">>", "%", "and", "or", "xor")
|
val AugmentAssignmentOperators = setOf("+", "-", "/", "*", "&", "|", "^", "<<", ">>", "%", "and", "or", "xor")
|
||||||
val BitwiseOperators = setOf("&", "|", "^", "~")
|
val BitwiseOperators = setOf("&", "|", "^", "~")
|
||||||
|
val PrefixOperators = setOf("+", "-", "~", "not")
|
||||||
// val InvalidOperatorsForBoolean = setOf("+", "-", "*", "/", "%", "<<", ">>") + BitwiseOperators
|
// val InvalidOperatorsForBoolean = setOf("+", "-", "*", "/", "%", "<<", ">>") + BitwiseOperators
|
||||||
|
|
||||||
fun invertedComparisonOperator(operator: String) =
|
fun invertedComparisonOperator(operator: String) =
|
||||||
|
@ -23,7 +23,7 @@ object Encoder: IStringEncoding {
|
|||||||
success = { it }
|
success = { it }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
override fun decodeString(bytes: List<UByte>, encoding: Encoding): String {
|
override fun decodeString(bytes: Iterable<UByte>, encoding: Encoding): String {
|
||||||
val decoded = when(encoding) {
|
val decoded = when(encoding) {
|
||||||
Encoding.PETSCII -> PetsciiEncoding.decodePetscii(bytes, true)
|
Encoding.PETSCII -> PetsciiEncoding.decodePetscii(bytes, true)
|
||||||
Encoding.SCREENCODES -> PetsciiEncoding.decodeScreencode(bytes, true)
|
Encoding.SCREENCODES -> PetsciiEncoding.decodeScreencode(bytes, true)
|
||||||
|
@ -13,9 +13,12 @@ class AtariMachineDefinition: IMachineDefinition {
|
|||||||
override val FLOAT_MEM_SIZE = 6
|
override val FLOAT_MEM_SIZE = 6
|
||||||
override val PROGRAM_LOAD_ADDRESS = 0x2000u
|
override val PROGRAM_LOAD_ADDRESS = 0x2000u
|
||||||
|
|
||||||
// the 2*256 byte evaluation stack (on which bytes, words, and even floats are stored during calculations)
|
// the 2*128 byte evaluation stack (1 page, on which bytes, words, and even floats are stored during calculations)
|
||||||
override var ESTACK_LO = 0x1a00u // $1a00-$1aff inclusive // TODO
|
override var ESTACK_LO = 0x1b00u // $1b00-$1b7f inclusive // TODO
|
||||||
override var ESTACK_HI = 0x1b00u // $1b00-$1bff inclusive // TODO
|
override var ESTACK_HI = 0x1b80u // $1b80-$1bff inclusive // TODO
|
||||||
|
|
||||||
|
override val BSSHIGHRAM_START = 0u // TODO
|
||||||
|
override val BSSHIGHRAM_END = 0u // TODO
|
||||||
|
|
||||||
override lateinit var zeropage: Zeropage
|
override lateinit var zeropage: Zeropage
|
||||||
override lateinit var golden: GoldenRam
|
override lateinit var golden: GoldenRam
|
||||||
|
@ -14,9 +14,13 @@ class C128MachineDefinition: IMachineDefinition {
|
|||||||
override val FLOAT_MEM_SIZE = Mflpt5.FLOAT_MEM_SIZE
|
override val FLOAT_MEM_SIZE = Mflpt5.FLOAT_MEM_SIZE
|
||||||
override val PROGRAM_LOAD_ADDRESS = 0x1c01u
|
override val PROGRAM_LOAD_ADDRESS = 0x1c01u
|
||||||
|
|
||||||
// the 2*256 byte evaluation stack (on which bytes, words, and even floats are stored during calculations)
|
// the 2*128 byte evaluation stack (1 page, on which bytes, words, and even floats are stored during calculations)
|
||||||
override var ESTACK_LO = 0x1a00u // $1a00-$1aff inclusive
|
override var ESTACK_LO = 0x1b00u // $1b00-$1b7f inclusive
|
||||||
override var ESTACK_HI = 0x1b00u // $1b00-$1bff inclusive
|
override var ESTACK_HI = 0x1b80u // $1b80-$1bff inclusive
|
||||||
|
|
||||||
|
override val BSSHIGHRAM_START = 0u // TODO
|
||||||
|
override val BSSHIGHRAM_END = 0u // TODO
|
||||||
|
|
||||||
override lateinit var zeropage: Zeropage
|
override lateinit var zeropage: Zeropage
|
||||||
override lateinit var golden: GoldenRam
|
override lateinit var golden: GoldenRam
|
||||||
|
|
||||||
|
@ -15,9 +15,13 @@ class C64MachineDefinition: IMachineDefinition {
|
|||||||
override val FLOAT_MEM_SIZE = Mflpt5.FLOAT_MEM_SIZE
|
override val FLOAT_MEM_SIZE = Mflpt5.FLOAT_MEM_SIZE
|
||||||
override val PROGRAM_LOAD_ADDRESS = 0x0801u
|
override val PROGRAM_LOAD_ADDRESS = 0x0801u
|
||||||
|
|
||||||
// the 2*256 byte evaluation stack (on which bytes, words, and even floats are stored during calculations)
|
// the 2*128 byte evaluation stack (1 page, on which bytes, words, and even floats are stored during calculations)
|
||||||
override var ESTACK_LO = 0xce00u // $ce00-$ceff inclusive
|
override var ESTACK_LO = 0xcf00u // $cf00-$cf7f inclusive
|
||||||
override var ESTACK_HI = 0xcf00u // $ce00-$ceff inclusive
|
override var ESTACK_HI = 0xcf80u // $cf80-$cfff inclusive
|
||||||
|
|
||||||
|
override val BSSHIGHRAM_START = 0xc000u
|
||||||
|
override val BSSHIGHRAM_END = ESTACK_LO
|
||||||
|
|
||||||
override lateinit var zeropage: Zeropage
|
override lateinit var zeropage: Zeropage
|
||||||
override lateinit var golden: GoldenRam
|
override lateinit var golden: GoldenRam
|
||||||
|
|
||||||
|
@ -83,12 +83,12 @@ class C64Zeropage(options: CompilationOptions) : Zeropage(options) {
|
|||||||
// This is important because the compiler sometimes treats ZP variables more efficiently (for example if it's a pointer)
|
// This is important because the compiler sometimes treats ZP variables more efficiently (for example if it's a pointer)
|
||||||
// The base addres is $04. Unfortunately it cannot be the same as on the Commander X16 ($02).
|
// The base addres is $04. Unfortunately it cannot be the same as on the Commander X16 ($02).
|
||||||
for(reg in 0..15) {
|
for(reg in 0..15) {
|
||||||
allocatedVariables[listOf("cx16", "r${reg}")] = VarAllocation((4+reg*2).toUInt(), DataType.UWORD, 2) // cx16.r0 .. cx16.r15
|
allocatedVariables["cx16.r${reg}"] = VarAllocation((4+reg*2).toUInt(), DataType.UWORD, 2) // cx16.r0 .. cx16.r15
|
||||||
allocatedVariables[listOf("cx16", "r${reg}s")] = VarAllocation((4+reg*2).toUInt(), DataType.WORD, 2) // cx16.r0s .. cx16.r15s
|
allocatedVariables["cx16.r${reg}s"] = VarAllocation((4+reg*2).toUInt(), DataType.WORD, 2) // cx16.r0s .. cx16.r15s
|
||||||
allocatedVariables[listOf("cx16", "r${reg}L")] = VarAllocation((4+reg*2).toUInt(), DataType.UBYTE, 1) // cx16.r0L .. cx16.r15L
|
allocatedVariables["cx16.r${reg}L"] = VarAllocation((4+reg*2).toUInt(), DataType.UBYTE, 1) // cx16.r0L .. cx16.r15L
|
||||||
allocatedVariables[listOf("cx16", "r${reg}H")] = VarAllocation((5+reg*2).toUInt(), DataType.UBYTE, 1) // cx16.r0H .. cx16.r15H
|
allocatedVariables["cx16.r${reg}H"] = VarAllocation((5+reg*2).toUInt(), DataType.UBYTE, 1) // cx16.r0H .. cx16.r15H
|
||||||
allocatedVariables[listOf("cx16", "r${reg}sL")] = VarAllocation((4+reg*2).toUInt(), DataType.BYTE, 1) // cx16.r0sL .. cx16.r15sL
|
allocatedVariables["cx16.r${reg}sL"] = VarAllocation((4+reg*2).toUInt(), DataType.BYTE, 1) // cx16.r0sL .. cx16.r15sL
|
||||||
allocatedVariables[listOf("cx16", "r${reg}sH")] = VarAllocation((5+reg*2).toUInt(), DataType.BYTE, 1) // cx16.r0sH .. cx16.r15sH
|
allocatedVariables["cx16.r${reg}sH"] = VarAllocation((5+reg*2).toUInt(), DataType.BYTE, 1) // cx16.r0sH .. cx16.r15sH
|
||||||
free.remove((4+reg*2).toUInt())
|
free.remove((4+reg*2).toUInt())
|
||||||
free.remove((5+reg*2).toUInt())
|
free.remove((5+reg*2).toUInt())
|
||||||
}
|
}
|
||||||
|
@ -208,7 +208,7 @@ object AtasciiEncoding {
|
|||||||
return Ok(mapped)
|
return Ok(mapped)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun decode(bytes: List<UByte>): Result<String, CharConversionException> {
|
fun decode(bytes: Iterable<UByte>): Result<String, CharConversionException> {
|
||||||
return Ok(bytes.map { decodeTable[it.toInt()] }.joinToString(""))
|
return Ok(bytes.map { decodeTable[it.toInt()] }.joinToString(""))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,7 +27,7 @@ object IsoEncoding {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun decode(bytes: List<UByte>): Result<String, CharConversionException> {
|
fun decode(bytes: Iterable<UByte>): Result<String, CharConversionException> {
|
||||||
return try {
|
return try {
|
||||||
Ok(String(bytes.map { it.toByte() }.toByteArray(), charset))
|
Ok(String(bytes.map { it.toByte() }.toByteArray(), charset))
|
||||||
} catch (ce: CharConversionException) {
|
} catch (ce: CharConversionException) {
|
||||||
|
@ -14,9 +14,13 @@ class CX16MachineDefinition: IMachineDefinition {
|
|||||||
override val FLOAT_MEM_SIZE = Mflpt5.FLOAT_MEM_SIZE
|
override val FLOAT_MEM_SIZE = Mflpt5.FLOAT_MEM_SIZE
|
||||||
override val PROGRAM_LOAD_ADDRESS = 0x0801u
|
override val PROGRAM_LOAD_ADDRESS = 0x0801u
|
||||||
|
|
||||||
// the 2*256 byte evaluation stack (on which bytes, words, and even floats are stored during calculations)
|
// the 2*128 byte evaluation stack (1 page, on which bytes, words, and even floats are stored during calculations)
|
||||||
override var ESTACK_LO = 0x0400u // $0400-$04ff inclusive
|
override var ESTACK_LO = 0x0700u // $0700-$077f inclusive
|
||||||
override var ESTACK_HI = 0x0500u // $0500-$05ff inclusive
|
override var ESTACK_HI = 0x0780u // $0780-$07ff inclusive
|
||||||
|
|
||||||
|
override val BSSHIGHRAM_START = 0xa000u // hiram bank 1, 8Kb, assumed to be active
|
||||||
|
override val BSSHIGHRAM_END = 0xc000u // rom starts here.
|
||||||
|
|
||||||
override lateinit var zeropage: Zeropage
|
override lateinit var zeropage: Zeropage
|
||||||
override lateinit var golden: GoldenRam
|
override lateinit var golden: GoldenRam
|
||||||
|
|
||||||
|
@ -58,12 +58,12 @@ class CX16Zeropage(options: CompilationOptions) : Zeropage(options) {
|
|||||||
// However, to be able for the compiler to "see" them as zero page variables, we have to register them here as well.
|
// However, to be able for the compiler to "see" them as zero page variables, we have to register them here as well.
|
||||||
// This is important because the compiler sometimes treats ZP variables more efficiently (for example if it's a pointer)
|
// This is important because the compiler sometimes treats ZP variables more efficiently (for example if it's a pointer)
|
||||||
for(reg in 0..15) {
|
for(reg in 0..15) {
|
||||||
allocatedVariables[listOf("cx16", "r${reg}")] = VarAllocation((2+reg*2).toUInt(), DataType.UWORD, 2) // cx16.r0 .. cx16.r15
|
allocatedVariables["cx16.r${reg}"] = VarAllocation((2+reg*2).toUInt(), DataType.UWORD, 2) // cx16.r0 .. cx16.r15
|
||||||
allocatedVariables[listOf("cx16", "r${reg}s")] = VarAllocation((2+reg*2).toUInt(), DataType.WORD, 2) // cx16.r0s .. cx16.r15s
|
allocatedVariables["cx16.r${reg}s"] = VarAllocation((2+reg*2).toUInt(), DataType.WORD, 2) // cx16.r0s .. cx16.r15s
|
||||||
allocatedVariables[listOf("cx16", "r${reg}L")] = VarAllocation((2+reg*2).toUInt(), DataType.UBYTE, 1) // cx16.r0L .. cx16.r15L
|
allocatedVariables["cx16.r${reg}L"] = VarAllocation((2+reg*2).toUInt(), DataType.UBYTE, 1) // cx16.r0L .. cx16.r15L
|
||||||
allocatedVariables[listOf("cx16", "r${reg}H")] = VarAllocation((3+reg*2).toUInt(), DataType.UBYTE, 1) // cx16.r0H .. cx16.r15H
|
allocatedVariables["cx16.r${reg}H"] = VarAllocation((3+reg*2).toUInt(), DataType.UBYTE, 1) // cx16.r0H .. cx16.r15H
|
||||||
allocatedVariables[listOf("cx16", "r${reg}sL")] = VarAllocation((2+reg*2).toUInt(), DataType.BYTE, 1) // cx16.r0sL .. cx16.r15sL
|
allocatedVariables["cx16.r${reg}sL"] = VarAllocation((2+reg*2).toUInt(), DataType.BYTE, 1) // cx16.r0sL .. cx16.r15sL
|
||||||
allocatedVariables[listOf("cx16", "r${reg}sH")] = VarAllocation((3+reg*2).toUInt(), DataType.BYTE, 1) // cx16.r0sH .. cx16.r15sH
|
allocatedVariables["cx16.r${reg}sH"] = VarAllocation((3+reg*2).toUInt(), DataType.BYTE, 1) // cx16.r0sH .. cx16.r15sH
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -17,10 +17,18 @@ class VirtualMachineDefinition: IMachineDefinition {
|
|||||||
|
|
||||||
override var ESTACK_LO = 0u // not actually used
|
override var ESTACK_LO = 0u // not actually used
|
||||||
override var ESTACK_HI = 0u // not actually used
|
override var ESTACK_HI = 0u // not actually used
|
||||||
|
override val BSSHIGHRAM_START = 0u // not actually used
|
||||||
|
override val BSSHIGHRAM_END = 0u // not actually used
|
||||||
override lateinit var zeropage: Zeropage // not actually used
|
override lateinit var zeropage: Zeropage // not actually used
|
||||||
override lateinit var golden: GoldenRam // not actually used
|
override lateinit var golden: GoldenRam // not actually used
|
||||||
|
|
||||||
override fun getFloatAsmBytes(num: Number) = TODO("float asm bytes from number")
|
override fun getFloatAsmBytes(num: Number): String {
|
||||||
|
// little endian binary representation
|
||||||
|
val bits = num.toFloat().toBits().toUInt()
|
||||||
|
val hexStr = bits.toString(16).padStart(8, '0')
|
||||||
|
val parts = hexStr.chunked(2).map { "\$" + it }
|
||||||
|
return parts.joinToString(", ")
|
||||||
|
}
|
||||||
|
|
||||||
override fun importLibs(compilerOptions: CompilationOptions, compilationTargetName: String): List<String> {
|
override fun importLibs(compilerOptions: CompilationOptions, compilationTargetName: String): List<String> {
|
||||||
return listOf("syslib")
|
return listOf("syslib")
|
||||||
@ -44,9 +52,24 @@ class VirtualMachineDefinition: IMachineDefinition {
|
|||||||
|
|
||||||
override fun isIOAddress(address: UInt): Boolean = false
|
override fun isIOAddress(address: UInt): Boolean = false
|
||||||
|
|
||||||
override fun initializeMemoryAreas(compilerOptions: CompilationOptions) {}
|
override fun initializeMemoryAreas(compilerOptions: CompilationOptions) {
|
||||||
|
zeropage = VirtualZeropage(compilerOptions)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IVirtualMachineRunner {
|
interface IVirtualMachineRunner {
|
||||||
fun runProgram(irSource: String)
|
fun runProgram(irSource: String)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class VirtualZeropage(options: CompilationOptions): Zeropage(options) {
|
||||||
|
override val SCRATCH_B1: UInt
|
||||||
|
get() = throw IllegalStateException("virtual shouldn't use this zeropage variable")
|
||||||
|
override val SCRATCH_REG: UInt
|
||||||
|
get() = throw IllegalStateException("virtual shouldn't use this zeropage variable")
|
||||||
|
override val SCRATCH_W1: UInt
|
||||||
|
get() = throw IllegalStateException("virtual shouldn't use this zeropage variable")
|
||||||
|
override val SCRATCH_W2: UInt
|
||||||
|
get() = throw IllegalStateException("virtual shouldn't use this zeropage variable")
|
||||||
|
|
||||||
|
override fun allocateCx16VirtualRegisters() { /* there is no actual zero page in this target to allocate thing in */ }
|
||||||
|
}
|
||||||
|
@ -3,6 +3,7 @@ plugins {
|
|||||||
id 'java'
|
id 'java'
|
||||||
id 'application'
|
id 'application'
|
||||||
id "org.jetbrains.kotlin.jvm"
|
id "org.jetbrains.kotlin.jvm"
|
||||||
|
id "io.kotest" version "0.3.9"
|
||||||
}
|
}
|
||||||
|
|
||||||
java {
|
java {
|
||||||
@ -25,11 +26,11 @@ compileTestKotlin {
|
|||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation project(':codeCore')
|
implementation project(':codeCore')
|
||||||
implementation project(':compilerAst')
|
|
||||||
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"
|
||||||
|
|
||||||
|
testImplementation 'io.kotest:kotest-runner-junit5-jvm:5.3.2'
|
||||||
}
|
}
|
||||||
|
|
||||||
sourceSets {
|
sourceSets {
|
||||||
@ -41,6 +42,22 @@ sourceSets {
|
|||||||
srcDirs = ["${project.projectDir}/res"]
|
srcDirs = ["${project.projectDir}/res"]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
test {
|
||||||
|
java {
|
||||||
|
srcDir "${project.projectDir}/test"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// note: there are no unit tests in this module!
|
test {
|
||||||
|
// Enable JUnit 5 (Gradle 4.6+).
|
||||||
|
useJUnitPlatform()
|
||||||
|
|
||||||
|
// Always run tests, even when nothing changed.
|
||||||
|
dependsOn 'cleanTest'
|
||||||
|
|
||||||
|
// Show test results.
|
||||||
|
testLogging {
|
||||||
|
events "skipped", "failed"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -4,13 +4,15 @@
|
|||||||
<exclude-output />
|
<exclude-output />
|
||||||
<content url="file://$MODULE_DIR$">
|
<content url="file://$MODULE_DIR$">
|
||||||
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/test" isTestSource="true" />
|
||||||
<excludeFolder url="file://$MODULE_DIR$/build" />
|
<excludeFolder url="file://$MODULE_DIR$/build" />
|
||||||
</content>
|
</content>
|
||||||
<orderEntry type="inheritedJdk" />
|
<orderEntry type="inheritedJdk" />
|
||||||
<orderEntry type="sourceFolder" forTests="false" />
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
<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="compilerAst" />
|
|
||||||
<orderEntry type="library" name="michael.bull.kotlin.result.jvm" 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" />
|
||||||
</component>
|
</component>
|
||||||
</module>
|
</module>
|
File diff suppressed because it is too large
Load Diff
@ -1,16 +1,15 @@
|
|||||||
package prog8.codegen.cpu6502
|
package prog8.codegen.cpu6502
|
||||||
|
|
||||||
import prog8.ast.Program
|
import prog8.code.StConstant
|
||||||
import prog8.ast.expressions.NumericLiteral
|
import prog8.code.StMemVar
|
||||||
import prog8.ast.statements.VarDecl
|
import prog8.code.SymbolTable
|
||||||
import prog8.ast.statements.VarDeclType
|
|
||||||
import prog8.code.core.IMachineDefinition
|
import prog8.code.core.IMachineDefinition
|
||||||
|
|
||||||
|
|
||||||
// note: see https://wiki.nesdev.org/w/index.php/6502_assembly_optimisations
|
// note: see https://wiki.nesdev.org/w/index.php/6502_assembly_optimisations
|
||||||
|
|
||||||
|
|
||||||
internal fun optimizeAssembly(lines: MutableList<String>, machine: IMachineDefinition, program: Program): Int {
|
internal fun optimizeAssembly(lines: MutableList<String>, machine: IMachineDefinition, symbolTable: SymbolTable): Int {
|
||||||
|
|
||||||
var numberOfOptimizations = 0
|
var numberOfOptimizations = 0
|
||||||
|
|
||||||
@ -37,7 +36,7 @@ internal fun optimizeAssembly(lines: MutableList<String>, machine: IMachineDefin
|
|||||||
numberOfOptimizations++
|
numberOfOptimizations++
|
||||||
}
|
}
|
||||||
|
|
||||||
mods = optimizeStoreLoadSame(linesByFour, machine, program)
|
mods = optimizeStoreLoadSame(linesByFour, machine, symbolTable)
|
||||||
if(mods.isNotEmpty()) {
|
if(mods.isNotEmpty()) {
|
||||||
apply(mods, lines)
|
apply(mods, lines)
|
||||||
linesByFour = getLinesBy(lines, 4)
|
linesByFour = getLinesBy(lines, 4)
|
||||||
@ -52,14 +51,14 @@ internal fun optimizeAssembly(lines: MutableList<String>, machine: IMachineDefin
|
|||||||
}
|
}
|
||||||
|
|
||||||
var linesByFourteen = getLinesBy(lines, 14)
|
var linesByFourteen = getLinesBy(lines, 14)
|
||||||
mods = optimizeSameAssignments(linesByFourteen, machine, program)
|
mods = optimizeSameAssignments(linesByFourteen, machine, symbolTable)
|
||||||
if(mods.isNotEmpty()) {
|
if(mods.isNotEmpty()) {
|
||||||
apply(mods, lines)
|
apply(mods, lines)
|
||||||
linesByFourteen = getLinesBy(lines, 14)
|
linesByFourteen = getLinesBy(lines, 14)
|
||||||
numberOfOptimizations++
|
numberOfOptimizations++
|
||||||
}
|
}
|
||||||
|
|
||||||
mods = optimizeSamePointerIndexing(linesByFourteen, machine, program)
|
mods = optimizeSamePointerIndexing(linesByFourteen)
|
||||||
if(mods.isNotEmpty()) {
|
if(mods.isNotEmpty()) {
|
||||||
apply(mods, lines)
|
apply(mods, lines)
|
||||||
linesByFourteen = getLinesBy(lines, 14)
|
linesByFourteen = getLinesBy(lines, 14)
|
||||||
@ -129,7 +128,11 @@ private fun optimizeUselessStackByteWrites(linesByFour: List<List<IndexedValue<S
|
|||||||
return mods
|
return mods
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun optimizeSameAssignments(linesByFourteen: List<List<IndexedValue<String>>>, machine: IMachineDefinition, program: Program): List<Modification> {
|
private fun optimizeSameAssignments(
|
||||||
|
linesByFourteen: List<List<IndexedValue<String>>>,
|
||||||
|
machine: IMachineDefinition,
|
||||||
|
symbolTable: SymbolTable
|
||||||
|
): List<Modification> {
|
||||||
|
|
||||||
// Optimize sequential assignments of the same value to various targets (bytes, words, floats)
|
// Optimize sequential assignments of the same value to various targets (bytes, words, floats)
|
||||||
// the float one is the one that requires 2*7=14 lines of code to check...
|
// the float one is the one that requires 2*7=14 lines of code to check...
|
||||||
@ -154,8 +157,8 @@ private fun optimizeSameAssignments(linesByFourteen: List<List<IndexedValue<Stri
|
|||||||
val fourthvalue = sixth.substring(4)
|
val fourthvalue = sixth.substring(4)
|
||||||
if(firstvalue==thirdvalue && secondvalue==fourthvalue) {
|
if(firstvalue==thirdvalue && secondvalue==fourthvalue) {
|
||||||
// lda/ldy sta/sty twice the same word --> remove second lda/ldy pair (fifth and sixth lines)
|
// lda/ldy sta/sty twice the same word --> remove second lda/ldy pair (fifth and sixth lines)
|
||||||
val address1 = getAddressArg(first, program)
|
val address1 = getAddressArg(first, symbolTable)
|
||||||
val address2 = getAddressArg(second, program)
|
val address2 = getAddressArg(second, symbolTable)
|
||||||
if(address1==null || address2==null || (!machine.isIOAddress(address1) && !machine.isIOAddress(address2))) {
|
if(address1==null || address2==null || (!machine.isIOAddress(address1) && !machine.isIOAddress(address2))) {
|
||||||
mods.add(Modification(lines[4].index, true, null))
|
mods.add(Modification(lines[4].index, true, null))
|
||||||
mods.add(Modification(lines[5].index, true, null))
|
mods.add(Modification(lines[5].index, true, null))
|
||||||
@ -168,7 +171,7 @@ private fun optimizeSameAssignments(linesByFourteen: List<List<IndexedValue<Stri
|
|||||||
val secondvalue = third.substring(4)
|
val secondvalue = third.substring(4)
|
||||||
if(firstvalue==secondvalue) {
|
if(firstvalue==secondvalue) {
|
||||||
// lda value / sta ? / lda same-value / sta ? -> remove second lda (third line)
|
// lda value / sta ? / lda same-value / sta ? -> remove second lda (third line)
|
||||||
val address = getAddressArg(first, program)
|
val address = getAddressArg(first, symbolTable)
|
||||||
if(address==null || !machine.isIOAddress(address))
|
if(address==null || !machine.isIOAddress(address))
|
||||||
mods.add(Modification(lines[2].index, true, null))
|
mods.add(Modification(lines[2].index, true, null))
|
||||||
}
|
}
|
||||||
@ -251,7 +254,7 @@ private fun optimizeSameAssignments(linesByFourteen: List<List<IndexedValue<Stri
|
|||||||
val thirdvalue = third.substring(4)
|
val thirdvalue = third.substring(4)
|
||||||
val fourthvalue = fourth.substring(4)
|
val fourthvalue = fourth.substring(4)
|
||||||
if(firstvalue==thirdvalue && secondvalue == fourthvalue) {
|
if(firstvalue==thirdvalue && secondvalue == fourthvalue) {
|
||||||
val address = getAddressArg(first, program)
|
val address = getAddressArg(first, symbolTable)
|
||||||
if(address==null || !machine.isIOAddress(address)) {
|
if(address==null || !machine.isIOAddress(address)) {
|
||||||
overlappingMods = true
|
overlappingMods = true
|
||||||
mods.add(Modification(lines[2].index, true, null))
|
mods.add(Modification(lines[2].index, true, null))
|
||||||
@ -275,7 +278,7 @@ private fun optimizeSameAssignments(linesByFourteen: List<List<IndexedValue<Stri
|
|||||||
val firstvalue = first.substring(4)
|
val firstvalue = first.substring(4)
|
||||||
val thirdvalue = third.substring(4)
|
val thirdvalue = third.substring(4)
|
||||||
if(firstvalue==thirdvalue) {
|
if(firstvalue==thirdvalue) {
|
||||||
val address = getAddressArg(first, program)
|
val address = getAddressArg(first, symbolTable)
|
||||||
if(address==null || !machine.isIOAddress(address)) {
|
if(address==null || !machine.isIOAddress(address)) {
|
||||||
overlappingMods = true
|
overlappingMods = true
|
||||||
mods.add(Modification(lines[2].index, true, null))
|
mods.add(Modification(lines[2].index, true, null))
|
||||||
@ -295,7 +298,7 @@ private fun optimizeSameAssignments(linesByFourteen: List<List<IndexedValue<Stri
|
|||||||
val secondvalue = second.substring(4)
|
val secondvalue = second.substring(4)
|
||||||
val thirdvalue = third.substring(4)
|
val thirdvalue = third.substring(4)
|
||||||
if(firstvalue==secondvalue && firstvalue==thirdvalue) {
|
if(firstvalue==secondvalue && firstvalue==thirdvalue) {
|
||||||
val address = getAddressArg(first, program)
|
val address = getAddressArg(first, symbolTable)
|
||||||
if(address==null || !machine.isIOAddress(address)) {
|
if(address==null || !machine.isIOAddress(address)) {
|
||||||
overlappingMods = true
|
overlappingMods = true
|
||||||
val reg2 = second[2]
|
val reg2 = second[2]
|
||||||
@ -314,7 +317,7 @@ private fun optimizeSameAssignments(linesByFourteen: List<List<IndexedValue<Stri
|
|||||||
val firstvalue = first.substring(4)
|
val firstvalue = first.substring(4)
|
||||||
val secondvalue = second.substring(4)
|
val secondvalue = second.substring(4)
|
||||||
if(firstvalue==secondvalue) {
|
if(firstvalue==secondvalue) {
|
||||||
val address = getAddressArg(first, program)
|
val address = getAddressArg(first, symbolTable)
|
||||||
if(address==null || !machine.isIOAddress(address)) {
|
if(address==null || !machine.isIOAddress(address)) {
|
||||||
overlappingMods = true
|
overlappingMods = true
|
||||||
mods.add(Modification(lines[0].index, true, null))
|
mods.add(Modification(lines[0].index, true, null))
|
||||||
@ -327,7 +330,7 @@ private fun optimizeSameAssignments(linesByFourteen: List<List<IndexedValue<Stri
|
|||||||
return mods
|
return mods
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun optimizeSamePointerIndexing(linesByFourteen: List<List<IndexedValue<String>>>, machine: IMachineDefinition, program: Program): List<Modification> {
|
private fun optimizeSamePointerIndexing(linesByFourteen: List<List<IndexedValue<String>>>): List<Modification> {
|
||||||
|
|
||||||
// Optimize same pointer indexing where for instance we load and store to the same ptr index in Y
|
// Optimize same pointer indexing where for instance we load and store to the same ptr index in Y
|
||||||
// if Y isn't modified in between we can omit the second LDY:
|
// if Y isn't modified in between we can omit the second LDY:
|
||||||
@ -369,7 +372,11 @@ private fun optimizeSamePointerIndexing(linesByFourteen: List<List<IndexedValue<
|
|||||||
return mods
|
return mods
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun optimizeStoreLoadSame(linesByFour: List<List<IndexedValue<String>>>, machine: IMachineDefinition, program: Program): List<Modification> {
|
private fun optimizeStoreLoadSame(
|
||||||
|
linesByFour: List<List<IndexedValue<String>>>,
|
||||||
|
machine: IMachineDefinition,
|
||||||
|
symbolTable: SymbolTable
|
||||||
|
): List<Modification> {
|
||||||
// sta X + lda X, sty X + ldy X, stx X + ldx X -> the second instruction can OFTEN be eliminated
|
// sta X + lda X, sty X + ldy X, stx X + ldx X -> the second instruction can OFTEN be eliminated
|
||||||
val mods = mutableListOf<Modification>()
|
val mods = mutableListOf<Modification>()
|
||||||
for (lines in linesByFour) {
|
for (lines in linesByFour) {
|
||||||
@ -397,7 +404,7 @@ private fun optimizeStoreLoadSame(linesByFour: List<List<IndexedValue<String>>>,
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// no branch instruction follows, we can remove the load instruction
|
// no branch instruction follows, we can remove the load instruction
|
||||||
val address = getAddressArg(lines[2].value, program)
|
val address = getAddressArg(lines[2].value, symbolTable)
|
||||||
address==null || !machine.isIOAddress(address)
|
address==null || !machine.isIOAddress(address)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -439,7 +446,8 @@ private fun optimizeStoreLoadSame(linesByFour: List<List<IndexedValue<String>>>,
|
|||||||
|
|
||||||
private val identifierRegex = Regex("""^([a-zA-Z_$][a-zA-Z\d_\.$]*)""")
|
private val identifierRegex = Regex("""^([a-zA-Z_$][a-zA-Z\d_\.$]*)""")
|
||||||
|
|
||||||
private fun getAddressArg(line: String, program: Program): UInt? {
|
private fun getAddressArg(line: String, symbolTable: SymbolTable): UInt? {
|
||||||
|
// try to get the constant value address, could return null if it's a symbol instead
|
||||||
val loadArg = line.trimStart().substring(3).trim()
|
val loadArg = line.trimStart().substring(3).trim()
|
||||||
return when {
|
return when {
|
||||||
loadArg.startsWith('$') -> loadArg.substring(1).toUIntOrNull(16)
|
loadArg.startsWith('$') -> loadArg.substring(1).toUIntOrNull(16)
|
||||||
@ -450,15 +458,11 @@ private fun getAddressArg(line: String, program: Program): UInt? {
|
|||||||
val identMatch = identifierRegex.find(loadArg)
|
val identMatch = identifierRegex.find(loadArg)
|
||||||
if(identMatch!=null) {
|
if(identMatch!=null) {
|
||||||
val identifier = identMatch.value
|
val identifier = identMatch.value
|
||||||
val decl = program.toplevelModule.lookup(identifier.split('.')) as? VarDecl
|
when (val symbol = symbolTable.flat[identifier]) {
|
||||||
if(decl!=null) {
|
is StConstant -> symbol.value.toUInt()
|
||||||
when(decl.type){
|
is StMemVar -> symbol.address
|
||||||
VarDeclType.VAR -> null
|
else -> null
|
||||||
VarDeclType.CONST,
|
|
||||||
VarDeclType.MEMORY -> (decl.value as NumericLiteral).number.toUInt()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else null
|
|
||||||
} else null
|
} else null
|
||||||
}
|
}
|
||||||
else -> loadArg.substring(1).toUIntOrNull()
|
else -> loadArg.substring(1).toUIntOrNull()
|
||||||
|
@ -1,15 +1,12 @@
|
|||||||
package prog8.codegen.cpu6502
|
package prog8.codegen.cpu6502
|
||||||
|
|
||||||
import prog8.ast.expressions.ArrayIndexedExpression
|
import prog8.code.ast.*
|
||||||
import prog8.ast.expressions.BuiltinFunctionCall
|
|
||||||
import prog8.ast.expressions.Expression
|
|
||||||
import prog8.ast.statements.Subroutine
|
|
||||||
import prog8.code.core.Cx16VirtualRegisters
|
import prog8.code.core.Cx16VirtualRegisters
|
||||||
import prog8.code.core.RegisterOrPair
|
import prog8.code.core.RegisterOrPair
|
||||||
import prog8.code.core.RegisterOrStatusflag
|
import prog8.code.core.RegisterOrStatusflag
|
||||||
|
|
||||||
|
|
||||||
fun asmsub6502ArgsEvalOrder(sub: Subroutine): List<Int> {
|
fun asmsub6502ArgsEvalOrder(sub: PtAsmSub): List<Int> {
|
||||||
val order = mutableListOf<Int>()
|
val order = mutableListOf<Int>()
|
||||||
// order is:
|
// order is:
|
||||||
// 1) cx16 virtual word registers,
|
// 1) cx16 virtual word registers,
|
||||||
@ -17,43 +14,45 @@ fun asmsub6502ArgsEvalOrder(sub: Subroutine): List<Int> {
|
|||||||
// 3) single CPU registers (X last), except A,
|
// 3) single CPU registers (X last), except A,
|
||||||
// 4) CPU Carry status flag
|
// 4) CPU Carry status flag
|
||||||
// 5) the A register itself last (so everything before it can use the accumulator without having to save its value)
|
// 5) the A register itself last (so everything before it can use the accumulator without having to save its value)
|
||||||
val args = sub.parameters.zip(sub.asmParameterRegisters).withIndex()
|
val args = sub.parameters.withIndex()
|
||||||
val (cx16regs, args2) = args.partition { it.value.second.registerOrPair in Cx16VirtualRegisters }
|
val (cx16regs, args2) = args.partition { it.value.first.registerOrPair in Cx16VirtualRegisters }
|
||||||
val pairedRegisters = arrayOf(RegisterOrPair.AX, RegisterOrPair.AY, RegisterOrPair.XY)
|
val pairedRegisters = arrayOf(RegisterOrPair.AX, RegisterOrPair.AY, RegisterOrPair.XY)
|
||||||
val (pairedRegs , args3) = args2.partition { it.value.second.registerOrPair in pairedRegisters }
|
val (pairedRegs , args3) = args2.partition { it.value.first.registerOrPair in pairedRegisters }
|
||||||
val (regsWithoutA, args4) = args3.partition { it.value.second.registerOrPair != RegisterOrPair.A }
|
val (regsWithoutA, args4) = args3.partition { it.value.first.registerOrPair != RegisterOrPair.A }
|
||||||
val (regA, rest) = args4.partition { it.value.second.registerOrPair != null }
|
val (regA, rest) = args4.partition { it.value.first.registerOrPair != null }
|
||||||
|
|
||||||
cx16regs.forEach { order += it.index }
|
cx16regs.forEach { order += it.index }
|
||||||
pairedRegs.forEach { order += it.index }
|
pairedRegs.forEach { order += it.index }
|
||||||
regsWithoutA.forEach {
|
regsWithoutA.forEach {
|
||||||
if(it.value.second.registerOrPair != RegisterOrPair.X)
|
if(it.value.first.registerOrPair != RegisterOrPair.X)
|
||||||
order += it.index
|
order += it.index
|
||||||
}
|
}
|
||||||
regsWithoutA.firstOrNull { it.value.second.registerOrPair==RegisterOrPair.X } ?.let { order += it.index}
|
regsWithoutA.firstOrNull { it.value.first.registerOrPair==RegisterOrPair.X } ?.let { order += it.index}
|
||||||
rest.forEach { order += it.index }
|
rest.forEach { order += it.index }
|
||||||
regA.forEach { order += it.index }
|
regA.forEach { order += it.index }
|
||||||
require(order.size==sub.parameters.size)
|
require(order.size==sub.parameters.size)
|
||||||
return order
|
return order
|
||||||
}
|
}
|
||||||
|
|
||||||
fun asmsub6502ArgsHaveRegisterClobberRisk(args: List<Expression>,
|
fun asmsub6502ArgsHaveRegisterClobberRisk(
|
||||||
paramRegisters: List<RegisterOrStatusflag>): Boolean {
|
args: List<PtExpression>,
|
||||||
fun isClobberRisk(expr: Expression): Boolean {
|
params: List<Pair<RegisterOrStatusflag, PtSubroutineParameter>>
|
||||||
|
): Boolean {
|
||||||
|
fun isClobberRisk(expr: PtExpression): Boolean {
|
||||||
when (expr) {
|
when (expr) {
|
||||||
is ArrayIndexedExpression -> {
|
is PtArrayIndexer -> {
|
||||||
return paramRegisters.any {
|
return params.any {
|
||||||
it.registerOrPair in listOf(RegisterOrPair.Y, RegisterOrPair.AY, RegisterOrPair.XY)
|
it.first.registerOrPair in listOf(RegisterOrPair.Y, RegisterOrPair.AY, RegisterOrPair.XY)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
is BuiltinFunctionCall -> {
|
is PtBuiltinFunctionCall -> {
|
||||||
if (expr.name == "lsb" || expr.name == "msb")
|
if (expr.name == "lsb" || expr.name == "msb")
|
||||||
return isClobberRisk(expr.args[0])
|
return isClobberRisk(expr.args[0])
|
||||||
if (expr.name == "mkword")
|
if (expr.name == "mkword")
|
||||||
return isClobberRisk(expr.args[0]) && isClobberRisk(expr.args[1])
|
return isClobberRisk(expr.args[0]) && isClobberRisk(expr.args[1])
|
||||||
return !expr.isSimple
|
return !expr.isSimple()
|
||||||
}
|
}
|
||||||
else -> return !expr.isSimple
|
else -> return !expr.isSimple()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,7 +3,6 @@ package prog8.codegen.cpu6502
|
|||||||
import com.github.michaelbull.result.Ok
|
import com.github.michaelbull.result.Ok
|
||||||
import com.github.michaelbull.result.Result
|
import com.github.michaelbull.result.Result
|
||||||
import com.github.michaelbull.result.mapError
|
import com.github.michaelbull.result.mapError
|
||||||
import prog8.ast.generatedLabelPrefix
|
|
||||||
import prog8.code.core.*
|
import prog8.code.core.*
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
@ -63,7 +62,7 @@ internal class AssemblyProgram(
|
|||||||
"atari" -> {
|
"atari" -> {
|
||||||
// Atari800XL .xex generation.
|
// Atari800XL .xex generation.
|
||||||
|
|
||||||
// TODO are these options okay?
|
// TODO are these options okay for atari?
|
||||||
val command = mutableListOf("64tass", "--ascii", "--case-sensitive", "--long-branch",
|
val command = mutableListOf("64tass", "--ascii", "--case-sensitive", "--long-branch",
|
||||||
"-Wall", "-Wno-strict-bool", "-Wno-shadow", // "-Werror",
|
"-Wall", "-Wno-strict-bool", "-Wno-shadow", // "-Werror",
|
||||||
"--no-monitor"
|
"--no-monitor"
|
||||||
@ -104,7 +103,7 @@ internal class AssemblyProgram(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun removeGeneratedLabelsFromMonlist() {
|
private fun removeGeneratedLabelsFromMonlist() {
|
||||||
val pattern = Regex("""al (\w+) \S+${generatedLabelPrefix}.+?""")
|
val pattern = Regex("""al (\w+) \S+prog8_label_.+?""")
|
||||||
val lines = viceMonListFile.toFile().readLines()
|
val lines = viceMonListFile.toFile().readLines()
|
||||||
viceMonListFile.toFile().outputStream().bufferedWriter().use {
|
viceMonListFile.toFile().outputStream().bufferedWriter().use {
|
||||||
for (line in lines) {
|
for (line in lines) {
|
||||||
|
@ -1,49 +1,39 @@
|
|||||||
package prog8.codegen.cpu6502
|
package prog8.codegen.cpu6502
|
||||||
|
|
||||||
import prog8.ast.IFunctionCall
|
import prog8.code.ast.*
|
||||||
import prog8.ast.Node
|
|
||||||
import prog8.ast.Program
|
|
||||||
import prog8.ast.expressions.*
|
|
||||||
import prog8.ast.statements.ArrayIndex
|
|
||||||
import prog8.ast.statements.BuiltinFunctionCallStatement
|
|
||||||
import prog8.ast.statements.Subroutine
|
|
||||||
import prog8.code.core.*
|
import prog8.code.core.*
|
||||||
import prog8.codegen.cpu6502.assignment.*
|
import prog8.codegen.cpu6502.assignment.*
|
||||||
import prog8.compiler.BuiltinFunctions
|
|
||||||
import prog8.compiler.FSignature
|
|
||||||
|
|
||||||
|
|
||||||
internal class BuiltinFunctionsAsmGen(private val program: Program,
|
internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||||
private val asmgen: AsmGen,
|
private val asmgen: AsmGen6502Internal,
|
||||||
private val assignAsmGen: AssignmentAsmGen) {
|
private val assignAsmGen: AssignmentAsmGen) {
|
||||||
|
|
||||||
internal fun translateFunctioncallExpression(fcall: BuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?) {
|
internal fun translateFunctioncallExpression(fcall: PtBuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?): DataType? {
|
||||||
val func = BuiltinFunctions.getValue(fcall.target.nameInSource.single())
|
return translateFunctioncall(fcall, discardResult = false, resultToStack = resultToStack, resultRegister = resultRegister)
|
||||||
translateFunctioncall(fcall, func, discardResult = false, resultToStack = resultToStack, resultRegister = resultRegister)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun translateFunctioncallStatement(fcall: BuiltinFunctionCallStatement) {
|
internal fun translateFunctioncallStatement(fcall: PtBuiltinFunctionCall) {
|
||||||
val func = BuiltinFunctions.getValue(fcall.name)
|
translateFunctioncall(fcall, discardResult = true, resultToStack = false, resultRegister = null)
|
||||||
translateFunctioncall(fcall, func, discardResult = true, resultToStack = false, resultRegister = null)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun translateFunctioncall(fcall: IFunctionCall, func: FSignature, discardResult: Boolean, resultToStack: Boolean, resultRegister: RegisterOrPair?) {
|
private fun translateFunctioncall(fcall: PtBuiltinFunctionCall, discardResult: Boolean, resultToStack: Boolean, resultRegister: RegisterOrPair?): DataType? {
|
||||||
if (discardResult && func.pure)
|
if (discardResult && fcall.hasNoSideEffects)
|
||||||
return // can just ignore the whole function call altogether
|
return null // can just ignore the whole function call altogether
|
||||||
|
|
||||||
if(discardResult && resultToStack)
|
if(discardResult && resultToStack)
|
||||||
throw AssemblyError("cannot both discard the result AND put it onto stack")
|
throw AssemblyError("cannot both discard the result AND put it onto stack")
|
||||||
|
|
||||||
val sscope = (fcall as Node).definingSubroutine
|
val sscope = fcall.definingISub()
|
||||||
|
|
||||||
when (func.name) {
|
when (fcall.name) {
|
||||||
"msb" -> funcMsb(fcall, resultToStack, resultRegister)
|
"msb" -> funcMsb(fcall, resultToStack, resultRegister)
|
||||||
"lsb" -> funcLsb(fcall, resultToStack, resultRegister)
|
"lsb" -> funcLsb(fcall, resultToStack, resultRegister)
|
||||||
"mkword" -> funcMkword(fcall, resultToStack, resultRegister)
|
"mkword" -> funcMkword(fcall, resultToStack, resultRegister)
|
||||||
"abs" -> funcAbs(fcall, func, resultToStack, resultRegister, sscope)
|
"abs" -> funcAbs(fcall, resultToStack, resultRegister, sscope)
|
||||||
"any", "all" -> funcAnyAll(fcall, func, resultToStack, resultRegister, sscope)
|
"any", "all" -> funcAnyAll(fcall, resultToStack, resultRegister, sscope)
|
||||||
"sgn" -> funcSgn(fcall, func, resultToStack, resultRegister, sscope)
|
"sgn" -> funcSgn(fcall, resultToStack, resultRegister, sscope)
|
||||||
"sqrt16" -> funcSqrt16(fcall, func, resultToStack, resultRegister, sscope)
|
"sqrt16" -> funcSqrt16(fcall, resultToStack, resultRegister, sscope)
|
||||||
"rol" -> funcRol(fcall)
|
"rol" -> funcRol(fcall)
|
||||||
"rol2" -> funcRol2(fcall)
|
"rol2" -> funcRol2(fcall)
|
||||||
"ror" -> funcRor(fcall)
|
"ror" -> funcRor(fcall)
|
||||||
@ -59,16 +49,20 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
|
|||||||
"push" -> asmgen.pushCpuStack(DataType.UBYTE, fcall.args[0])
|
"push" -> asmgen.pushCpuStack(DataType.UBYTE, fcall.args[0])
|
||||||
"pushw" -> asmgen.pushCpuStack(DataType.UWORD, fcall.args[0])
|
"pushw" -> asmgen.pushCpuStack(DataType.UWORD, fcall.args[0])
|
||||||
"pop" -> {
|
"pop" -> {
|
||||||
require(fcall.args[0] is IdentifierReference) {
|
require(fcall.args[0] is PtIdentifier) {
|
||||||
"attempt to pop a value into a differently typed variable, or in something else that isn't supported ${(fcall as Node).position}"
|
"attempt to pop a value into a differently typed variable, or in something else that isn't supported ${fcall.position}"
|
||||||
}
|
}
|
||||||
asmgen.popCpuStack(DataType.UBYTE, (fcall.args[0] as IdentifierReference).targetVarDecl(program)!!, (fcall as Node).definingSubroutine)
|
val symbol = asmgen.symbolTable.lookup((fcall.args[0] as PtIdentifier).name)
|
||||||
|
val target = symbol!!.astNode as IPtVariable
|
||||||
|
asmgen.popCpuStack(DataType.UBYTE, target, fcall.definingISub())
|
||||||
}
|
}
|
||||||
"popw" -> {
|
"popw" -> {
|
||||||
require(fcall.args[0] is IdentifierReference) {
|
require(fcall.args[0] is PtIdentifier) {
|
||||||
"attempt to pop a value into a differently typed variable, or in something else that isn't supported ${(fcall as Node).position}"
|
"attempt to pop a value into a differently typed variable, or in something else that isn't supported ${fcall.position}"
|
||||||
}
|
}
|
||||||
asmgen.popCpuStack(DataType.UWORD, (fcall.args[0] as IdentifierReference).targetVarDecl(program)!!, (fcall as Node).definingSubroutine)
|
val symbol = asmgen.symbolTable.lookup((fcall.args[0] as PtIdentifier).name)
|
||||||
|
val target = symbol!!.astNode as IPtVariable
|
||||||
|
asmgen.popCpuStack(DataType.UWORD, target, fcall.definingISub())
|
||||||
}
|
}
|
||||||
"rsave" -> funcRsave()
|
"rsave" -> funcRsave()
|
||||||
"rsavex" -> funcRsaveX()
|
"rsavex" -> funcRsaveX()
|
||||||
@ -76,9 +70,10 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
|
|||||||
"rrestorex" -> funcRrestoreX()
|
"rrestorex" -> funcRrestoreX()
|
||||||
"cmp" -> funcCmp(fcall)
|
"cmp" -> funcCmp(fcall)
|
||||||
"callfar" -> funcCallFar(fcall)
|
"callfar" -> funcCallFar(fcall)
|
||||||
"callrom" -> funcCallRom(fcall)
|
else -> throw AssemblyError("missing asmgen for builtin func ${fcall.name}")
|
||||||
else -> throw AssemblyError("missing asmgen for builtin func ${func.name}")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return BuiltinFunctions.getValue(fcall.name).returnType
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun funcRsave() {
|
private fun funcRsave() {
|
||||||
@ -132,147 +127,60 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
|
|||||||
asmgen.out(" sta P8ZP_SCRATCH_B1 | pla | tax | lda P8ZP_SCRATCH_B1")
|
asmgen.out(" sta P8ZP_SCRATCH_B1 | pla | tax | lda P8ZP_SCRATCH_B1")
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun funcCallFar(fcall: IFunctionCall) {
|
private fun funcCallFar(fcall: PtBuiltinFunctionCall) {
|
||||||
if(asmgen.options.compTarget.name != "cx16")
|
if(asmgen.options.compTarget.name != "cx16")
|
||||||
throw AssemblyError("callfar only works on cx16 target at this time")
|
throw AssemblyError("callfar only works on cx16 target at this time")
|
||||||
|
|
||||||
val bank = fcall.args[0].constValue(program)?.number?.toInt()
|
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.A) // bank
|
||||||
val address = fcall.args[1].constValue(program)?.number?.toInt() ?: 0
|
asmgen.out(" sta (++)+0")
|
||||||
val argAddrArg = fcall.args[2]
|
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.AY) // jump address
|
||||||
if(bank==null)
|
asmgen.out(" sta (+)+0 | sty (+)+1")
|
||||||
throw AssemblyError("callfar (jsrfar) bank has to be a constant")
|
asmgen.assignExpressionToRegister(fcall.args[2], RegisterOrPair.AY) // uword argument
|
||||||
if(fcall.args[1].constValue(program) == null) {
|
asmgen.out("""
|
||||||
assignAsmGen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.AY, false)
|
jsr cx16.jsrfar
|
||||||
asmgen.out(" sta (+)+0 | sty (+)+1 ; store jsrfar address word")
|
+ .word 0
|
||||||
}
|
+ .byte 0""")
|
||||||
|
// note that by convention the values in A+Y registers are now the return value of the call.
|
||||||
if(argAddrArg.constValue(program)?.number == 0.0) {
|
|
||||||
asmgen.out("""
|
|
||||||
jsr cx16.jsrfar
|
|
||||||
+ .word ${address.toHex()}
|
|
||||||
.byte ${bank.toHex()}""")
|
|
||||||
} else {
|
|
||||||
when(argAddrArg) {
|
|
||||||
is AddressOf -> {
|
|
||||||
if(argAddrArg.identifier.targetVarDecl(program)?.datatype != DataType.UBYTE)
|
|
||||||
throw AssemblyError("callfar done with 'arg' pointer to variable that's not UBYTE")
|
|
||||||
asmgen.out("""
|
|
||||||
lda ${asmgen.asmVariableName(argAddrArg.identifier)}
|
|
||||||
jsr cx16.jsrfar
|
|
||||||
+ .word ${address.toHex()}
|
|
||||||
.byte ${bank.toHex()}
|
|
||||||
sta ${asmgen.asmVariableName(argAddrArg.identifier)}""")
|
|
||||||
}
|
|
||||||
is NumericLiteral -> {
|
|
||||||
asmgen.out("""
|
|
||||||
lda ${argAddrArg.number.toHex()}
|
|
||||||
jsr cx16.jsrfar
|
|
||||||
+ .word ${address.toHex()}
|
|
||||||
.byte ${bank.toHex()}
|
|
||||||
sta ${argAddrArg.number.toHex()}""")
|
|
||||||
}
|
|
||||||
else -> throw AssemblyError("callfar only accepts pointer-of a (ubyte) variable or constant memory address for the 'arg' parameter")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun funcCallRom(fcall: IFunctionCall) {
|
private fun funcCmp(fcall: PtBuiltinFunctionCall) {
|
||||||
if(asmgen.options.compTarget.name != "cx16")
|
|
||||||
throw AssemblyError("callrom only works on cx16 target at this time")
|
|
||||||
|
|
||||||
val bank = fcall.args[0].constValue(program)?.number?.toInt()
|
|
||||||
val address = fcall.args[1].constValue(program)?.number?.toInt()
|
|
||||||
if(bank==null || address==null)
|
|
||||||
throw AssemblyError("callrom requires constant arguments")
|
|
||||||
|
|
||||||
if(address !in 0xc000..0xffff)
|
|
||||||
throw AssemblyError("callrom done on address outside of cx16 banked rom")
|
|
||||||
if(bank>=32)
|
|
||||||
throw AssemblyError("callrom bank must be <32")
|
|
||||||
|
|
||||||
val argAddrArg = fcall.args[2]
|
|
||||||
if(argAddrArg.constValue(program)?.number == 0.0) {
|
|
||||||
asmgen.out("""
|
|
||||||
lda $01
|
|
||||||
pha
|
|
||||||
lda #${bank}
|
|
||||||
sta $01
|
|
||||||
jsr ${address.toHex()}
|
|
||||||
pla
|
|
||||||
sta $01""")
|
|
||||||
} else {
|
|
||||||
when(argAddrArg) {
|
|
||||||
is AddressOf -> {
|
|
||||||
if(argAddrArg.identifier.targetVarDecl(program)?.datatype != DataType.UBYTE)
|
|
||||||
throw AssemblyError("callrom done with 'arg' pointer to variable that's not UBYTE")
|
|
||||||
asmgen.out("""
|
|
||||||
lda $01
|
|
||||||
pha
|
|
||||||
lda #${bank}
|
|
||||||
sta $01
|
|
||||||
lda ${asmgen.asmVariableName(argAddrArg.identifier)}
|
|
||||||
jsr ${address.toHex()}
|
|
||||||
sta ${asmgen.asmVariableName(argAddrArg.identifier)}
|
|
||||||
pla
|
|
||||||
sta $01""")
|
|
||||||
}
|
|
||||||
is NumericLiteral -> {
|
|
||||||
asmgen.out("""
|
|
||||||
lda $01
|
|
||||||
pha
|
|
||||||
lda #${bank}
|
|
||||||
sta $01
|
|
||||||
lda ${argAddrArg.number.toHex()}
|
|
||||||
jsr ${address.toHex()}
|
|
||||||
sta ${argAddrArg.number.toHex()}
|
|
||||||
pla
|
|
||||||
sta $01""")
|
|
||||||
}
|
|
||||||
else -> throw AssemblyError("callrom only accepts pointer-of a (ubyte) variable or constant memory address for the 'arg' parameter")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun funcCmp(fcall: IFunctionCall) {
|
|
||||||
val arg1 = fcall.args[0]
|
val arg1 = fcall.args[0]
|
||||||
val arg2 = fcall.args[1]
|
val arg2 = fcall.args[1]
|
||||||
val dt1 = arg1.inferType(program).getOrElse { throw AssemblyError("unknown dt") }
|
if(arg1.type in ByteDatatypes) {
|
||||||
val dt2 = arg2.inferType(program).getOrElse { throw AssemblyError("unknown dt") }
|
if(arg2.type in ByteDatatypes) {
|
||||||
if(dt1 in ByteDatatypes) {
|
|
||||||
if(dt2 in ByteDatatypes) {
|
|
||||||
when (arg2) {
|
when (arg2) {
|
||||||
is IdentifierReference -> {
|
is PtIdentifier -> {
|
||||||
asmgen.assignExpressionToRegister(arg1, RegisterOrPair.A)
|
asmgen.assignExpressionToRegister(arg1, RegisterOrPair.A)
|
||||||
asmgen.out(" cmp ${asmgen.asmVariableName(arg2)}")
|
asmgen.out(" cmp ${asmgen.asmVariableName(arg2)}")
|
||||||
}
|
}
|
||||||
is NumericLiteral -> {
|
is PtNumber -> {
|
||||||
asmgen.assignExpressionToRegister(arg1, RegisterOrPair.A)
|
asmgen.assignExpressionToRegister(arg1, RegisterOrPair.A)
|
||||||
asmgen.out(" cmp #${arg2.number.toInt()}")
|
asmgen.out(" cmp #${arg2.number.toInt()}")
|
||||||
}
|
}
|
||||||
is DirectMemoryRead -> {
|
is PtMemoryByte -> {
|
||||||
if(arg2.addressExpression is NumericLiteral) {
|
if(arg2.address is PtNumber) {
|
||||||
asmgen.assignExpressionToRegister(arg1, RegisterOrPair.A)
|
asmgen.assignExpressionToRegister(arg1, RegisterOrPair.A)
|
||||||
asmgen.out(" cmp ${arg2.addressExpression.constValue(program)!!.number.toHex()}")
|
asmgen.out(" cmp ${arg2.address.asConstInteger()!!.toHex()}")
|
||||||
} else {
|
} else {
|
||||||
if(arg1.isSimple) {
|
if(arg1.isSimple()) {
|
||||||
asmgen.assignExpressionToVariable(arg2, "P8ZP_SCRATCH_B1", DataType.UBYTE, (fcall as Node).definingSubroutine)
|
asmgen.assignExpressionToVariable(arg2, "P8ZP_SCRATCH_B1", DataType.UBYTE, fcall.definingISub())
|
||||||
asmgen.assignExpressionToRegister(arg1, RegisterOrPair.A)
|
asmgen.assignExpressionToRegister(arg1, RegisterOrPair.A)
|
||||||
asmgen.out(" cmp P8ZP_SCRATCH_B1")
|
asmgen.out(" cmp P8ZP_SCRATCH_B1")
|
||||||
} else {
|
} else {
|
||||||
asmgen.pushCpuStack(DataType.UBYTE, arg1)
|
asmgen.pushCpuStack(DataType.UBYTE, arg1)
|
||||||
asmgen.assignExpressionToVariable(arg2, "P8ZP_SCRATCH_B1", DataType.UBYTE, (fcall as Node).definingSubroutine)
|
asmgen.assignExpressionToVariable(arg2, "P8ZP_SCRATCH_B1", DataType.UBYTE, fcall.definingISub())
|
||||||
asmgen.out(" pla | cmp P8ZP_SCRATCH_B1")
|
asmgen.out(" pla | cmp P8ZP_SCRATCH_B1")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
if(arg1.isSimple) {
|
if(arg1.isSimple()) {
|
||||||
asmgen.assignExpressionToVariable(arg2, "P8ZP_SCRATCH_B1", DataType.UBYTE, (fcall as Node).definingSubroutine)
|
asmgen.assignExpressionToVariable(arg2, "P8ZP_SCRATCH_B1", DataType.UBYTE, fcall.definingISub())
|
||||||
asmgen.assignExpressionToRegister(arg1, RegisterOrPair.A)
|
asmgen.assignExpressionToRegister(arg1, RegisterOrPair.A)
|
||||||
asmgen.out(" cmp P8ZP_SCRATCH_B1")
|
asmgen.out(" cmp P8ZP_SCRATCH_B1")
|
||||||
} else {
|
} else {
|
||||||
asmgen.pushCpuStack(DataType.UBYTE, arg1)
|
asmgen.pushCpuStack(DataType.UBYTE, arg1)
|
||||||
asmgen.assignExpressionToVariable(arg2, "P8ZP_SCRATCH_B1", DataType.UBYTE, (fcall as Node).definingSubroutine)
|
asmgen.assignExpressionToVariable(arg2, "P8ZP_SCRATCH_B1", DataType.UBYTE, fcall.definingISub())
|
||||||
asmgen.out(" pla | cmp P8ZP_SCRATCH_B1")
|
asmgen.out(" pla | cmp P8ZP_SCRATCH_B1")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -280,10 +188,10 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
|
|||||||
} else
|
} else
|
||||||
throw AssemblyError("args for cmp() should have same dt")
|
throw AssemblyError("args for cmp() should have same dt")
|
||||||
} else {
|
} else {
|
||||||
// dt1 is a word
|
// arg1 is a word
|
||||||
if(dt2 in WordDatatypes) {
|
if(arg2.type in WordDatatypes) {
|
||||||
when (arg2) {
|
when (arg2) {
|
||||||
is IdentifierReference -> {
|
is PtIdentifier -> {
|
||||||
asmgen.assignExpressionToRegister(arg1, RegisterOrPair.AY)
|
asmgen.assignExpressionToRegister(arg1, RegisterOrPair.AY)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
cpy ${asmgen.asmVariableName(arg2)}+1
|
cpy ${asmgen.asmVariableName(arg2)}+1
|
||||||
@ -291,7 +199,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
|
|||||||
cmp ${asmgen.asmVariableName(arg2)}
|
cmp ${asmgen.asmVariableName(arg2)}
|
||||||
+""")
|
+""")
|
||||||
}
|
}
|
||||||
is NumericLiteral -> {
|
is PtNumber -> {
|
||||||
asmgen.assignExpressionToRegister(arg1, RegisterOrPair.AY)
|
asmgen.assignExpressionToRegister(arg1, RegisterOrPair.AY)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
cpy #>${arg2.number.toInt()}
|
cpy #>${arg2.number.toInt()}
|
||||||
@ -300,8 +208,8 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
|
|||||||
+""")
|
+""")
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
if(arg1.isSimple) {
|
if(arg1.isSimple()) {
|
||||||
asmgen.assignExpressionToVariable(arg2, "P8ZP_SCRATCH_W1", DataType.UWORD, (fcall as Node).definingSubroutine)
|
asmgen.assignExpressionToVariable(arg2, "P8ZP_SCRATCH_W1", DataType.UWORD, fcall.definingISub())
|
||||||
asmgen.assignExpressionToRegister(arg1, RegisterOrPair.AY)
|
asmgen.assignExpressionToRegister(arg1, RegisterOrPair.AY)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
cpy P8ZP_SCRATCH_W1+1
|
cpy P8ZP_SCRATCH_W1+1
|
||||||
@ -310,7 +218,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
|
|||||||
+""")
|
+""")
|
||||||
} else {
|
} else {
|
||||||
asmgen.pushCpuStack(DataType.UWORD, arg1)
|
asmgen.pushCpuStack(DataType.UWORD, arg1)
|
||||||
asmgen.assignExpressionToVariable(arg2, "P8ZP_SCRATCH_W1", DataType.UWORD, (fcall as Node).definingSubroutine)
|
asmgen.assignExpressionToVariable(arg2, "P8ZP_SCRATCH_W1", DataType.UWORD, fcall.definingISub())
|
||||||
asmgen.restoreRegisterStack(CpuRegister.Y, false)
|
asmgen.restoreRegisterStack(CpuRegister.Y, false)
|
||||||
asmgen.restoreRegisterStack(CpuRegister.A, false)
|
asmgen.restoreRegisterStack(CpuRegister.A, false)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
@ -326,25 +234,27 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun funcMemory(fcall: IFunctionCall, discardResult: Boolean, resultToStack: Boolean, resultRegister: RegisterOrPair?) {
|
private fun funcMemory(fcall: PtBuiltinFunctionCall, discardResult: Boolean, resultToStack: Boolean, resultRegister: RegisterOrPair?) {
|
||||||
if(discardResult || fcall !is BuiltinFunctionCall)
|
if(discardResult)
|
||||||
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 PtString).value
|
||||||
require(name.all { it.isLetterOrDigit() || it=='_' }) {"memory name should be a valid symbol name ${fcall.position}"}
|
require(name.all { it.isLetterOrDigit() || it=='_' }) {"memory name should be a valid symbol name ${fcall.position}"}
|
||||||
val slabname = IdentifierReference(listOf("prog8_slabs", "prog8_memoryslab_$name"), fcall.position)
|
val slabname = PtIdentifier("prog8_slabs.prog8_memoryslab_$name", DataType.UWORD, fcall.position)
|
||||||
slabname.linkParents(fcall)
|
val addressOf = PtAddressOf(fcall.position)
|
||||||
val src = AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, DataType.UWORD, expression = AddressOf(slabname, fcall.position))
|
addressOf.add(slabname)
|
||||||
|
addressOf.parent = fcall
|
||||||
|
val src = AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, DataType.UWORD, expression = addressOf)
|
||||||
val target =
|
val target =
|
||||||
if(resultToStack)
|
if(resultToStack)
|
||||||
AsmAssignTarget(TargetStorageKind.STACK, asmgen, DataType.UWORD, null)
|
AsmAssignTarget(TargetStorageKind.STACK, asmgen, DataType.UWORD, null)
|
||||||
else
|
else
|
||||||
AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, false, null, asmgen)
|
AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, false, null, asmgen)
|
||||||
val assign = AsmAssignment(src, target, false, program.memsizer, fcall.position)
|
val assign = AsmAssignment(src, target, program.memsizer, fcall.position)
|
||||||
asmgen.translateNormalAssignment(assign)
|
asmgen.translateNormalAssignment(assign)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun funcSqrt16(fcall: IFunctionCall, func: FSignature, resultToStack: Boolean, resultRegister: RegisterOrPair?, scope: Subroutine?) {
|
private fun funcSqrt16(fcall: PtBuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?, scope: IPtSubroutine?) {
|
||||||
translateArguments(fcall.args, func, scope)
|
translateArguments(fcall, scope)
|
||||||
if(resultToStack)
|
if(resultToStack)
|
||||||
asmgen.out(" jsr prog8_lib.func_sqrt16_stack")
|
asmgen.out(" jsr prog8_lib.func_sqrt16_stack")
|
||||||
else {
|
else {
|
||||||
@ -353,13 +263,18 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun funcReverse(fcall: IFunctionCall) {
|
private fun funcReverse(fcall: PtBuiltinFunctionCall) {
|
||||||
val variable = fcall.args.single()
|
val variable = fcall.args.single()
|
||||||
if (variable is IdentifierReference) {
|
if (variable is PtIdentifier) {
|
||||||
val decl = variable.targetVarDecl(program)!!
|
val symbol = asmgen.symbolTable.lookup(variable.name)
|
||||||
|
val decl = symbol!!.astNode as IPtVariable
|
||||||
|
val numElements = when(decl) {
|
||||||
|
is PtConstant -> throw AssemblyError("cannot reverse a constant")
|
||||||
|
is PtMemMapped -> decl.arraySize
|
||||||
|
is PtVariable -> decl.arraySize
|
||||||
|
}
|
||||||
val varName = asmgen.asmVariableName(variable)
|
val varName = asmgen.asmVariableName(variable)
|
||||||
val numElements = decl.arraysize!!.constIndex()
|
when (decl.type) {
|
||||||
when (decl.datatype) {
|
|
||||||
DataType.ARRAY_UB, DataType.ARRAY_B -> {
|
DataType.ARRAY_UB, DataType.ARRAY_B -> {
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda #<$varName
|
lda #<$varName
|
||||||
@ -392,13 +307,18 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun funcSort(fcall: IFunctionCall) {
|
private fun funcSort(fcall: PtBuiltinFunctionCall) {
|
||||||
val variable = fcall.args.single()
|
val variable = fcall.args.single()
|
||||||
if (variable is IdentifierReference) {
|
if (variable is PtIdentifier) {
|
||||||
val decl = variable.targetVarDecl(program)!!
|
val symbol = asmgen.symbolTable.lookup(variable.name)
|
||||||
|
val decl = symbol!!.astNode as IPtVariable
|
||||||
val varName = asmgen.asmVariableName(variable)
|
val varName = asmgen.asmVariableName(variable)
|
||||||
val numElements = decl.arraysize!!.constIndex()
|
val numElements = when(decl) {
|
||||||
when (decl.datatype) {
|
is PtConstant -> throw AssemblyError("cannot sort a constant")
|
||||||
|
is PtMemMapped -> decl.arraySize
|
||||||
|
is PtVariable -> decl.arraySize
|
||||||
|
}
|
||||||
|
when (decl.type) {
|
||||||
DataType.ARRAY_UB, DataType.ARRAY_B -> {
|
DataType.ARRAY_UB, DataType.ARRAY_B -> {
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda #<$varName
|
lda #<$varName
|
||||||
@ -406,7 +326,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
|
|||||||
sta P8ZP_SCRATCH_W1
|
sta P8ZP_SCRATCH_W1
|
||||||
sty P8ZP_SCRATCH_W1+1
|
sty P8ZP_SCRATCH_W1+1
|
||||||
lda #$numElements""")
|
lda #$numElements""")
|
||||||
asmgen.out(if (decl.datatype == DataType.ARRAY_UB) " jsr prog8_lib.func_sort_ub" else " jsr prog8_lib.func_sort_b")
|
asmgen.out(if (decl.type == DataType.ARRAY_UB) " jsr prog8_lib.func_sort_ub" else " jsr prog8_lib.func_sort_b")
|
||||||
}
|
}
|
||||||
DataType.ARRAY_UW, DataType.ARRAY_W -> {
|
DataType.ARRAY_UW, DataType.ARRAY_W -> {
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
@ -415,7 +335,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
|
|||||||
sta P8ZP_SCRATCH_W1
|
sta P8ZP_SCRATCH_W1
|
||||||
sty P8ZP_SCRATCH_W1+1
|
sty P8ZP_SCRATCH_W1+1
|
||||||
lda #$numElements""")
|
lda #$numElements""")
|
||||||
asmgen.out(if (decl.datatype == DataType.ARRAY_UW) " jsr prog8_lib.func_sort_uw" else " jsr prog8_lib.func_sort_w")
|
asmgen.out(if (decl.type == DataType.ARRAY_UW) " jsr prog8_lib.func_sort_uw" else " jsr prog8_lib.func_sort_w")
|
||||||
}
|
}
|
||||||
DataType.ARRAY_F -> throw AssemblyError("sorting of floating point array is not supported")
|
DataType.ARRAY_F -> throw AssemblyError("sorting of floating point array is not supported")
|
||||||
else -> throw AssemblyError("weird type")
|
else -> throw AssemblyError("weird type")
|
||||||
@ -424,26 +344,25 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
|
|||||||
throw AssemblyError("weird type")
|
throw AssemblyError("weird type")
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun funcRor2(fcall: IFunctionCall) {
|
private fun funcRor2(fcall: PtBuiltinFunctionCall) {
|
||||||
val what = fcall.args.single()
|
val what = fcall.args.single()
|
||||||
val dt = what.inferType(program)
|
when (what.type) {
|
||||||
when (dt.getOr(DataType.UNDEFINED)) {
|
|
||||||
DataType.UBYTE -> {
|
DataType.UBYTE -> {
|
||||||
when (what) {
|
when (what) {
|
||||||
is ArrayIndexedExpression -> {
|
is PtArrayIndexer -> {
|
||||||
translateRolRorArrayArgs(what.arrayvar, what.indexer, "ror2", 'b')
|
translateRolRorArrayArgs(what.variable, what, "ror2", 'b')
|
||||||
asmgen.out(" jsr prog8_lib.ror2_array_ub")
|
asmgen.out(" jsr prog8_lib.ror2_array_ub")
|
||||||
}
|
}
|
||||||
is DirectMemoryRead -> {
|
is PtMemoryByte -> {
|
||||||
if (what.addressExpression is NumericLiteral) {
|
if (what.address is PtNumber) {
|
||||||
val number = (what.addressExpression as NumericLiteral).number
|
val number = (what.address as PtNumber).number
|
||||||
asmgen.out(" lda ${number.toHex()} | lsr a | bcc + | ora #\$80 |+ | sta ${number.toHex()}")
|
asmgen.out(" lda ${number.toHex()} | lsr a | bcc + | ora #\$80 |+ | sta ${number.toHex()}")
|
||||||
} else {
|
} else {
|
||||||
asmgen.assignExpressionToRegister(what.addressExpression, RegisterOrPair.AY)
|
asmgen.assignExpressionToRegister(what.address, RegisterOrPair.AY)
|
||||||
asmgen.out(" jsr prog8_lib.ror2_mem_ub")
|
asmgen.out(" jsr prog8_lib.ror2_mem_ub")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
is IdentifierReference -> {
|
is PtIdentifier -> {
|
||||||
val variable = asmgen.asmVariableName(what)
|
val variable = asmgen.asmVariableName(what)
|
||||||
asmgen.out(" lda $variable | lsr a | bcc + | ora #\$80 |+ | sta $variable")
|
asmgen.out(" lda $variable | lsr a | bcc + | ora #\$80 |+ | sta $variable")
|
||||||
}
|
}
|
||||||
@ -452,11 +371,11 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
|
|||||||
}
|
}
|
||||||
DataType.UWORD -> {
|
DataType.UWORD -> {
|
||||||
when (what) {
|
when (what) {
|
||||||
is ArrayIndexedExpression -> {
|
is PtArrayIndexer -> {
|
||||||
translateRolRorArrayArgs(what.arrayvar, what.indexer, "ror2", 'w')
|
translateRolRorArrayArgs(what.variable, what, "ror2", 'w')
|
||||||
asmgen.out(" jsr prog8_lib.ror2_array_uw")
|
asmgen.out(" jsr prog8_lib.ror2_array_uw")
|
||||||
}
|
}
|
||||||
is IdentifierReference -> {
|
is PtIdentifier -> {
|
||||||
val variable = asmgen.asmVariableName(what)
|
val variable = asmgen.asmVariableName(what)
|
||||||
asmgen.out(" lsr $variable+1 | ror $variable | bcc + | lda $variable+1 | ora #\$80 | sta $variable+1 |+ ")
|
asmgen.out(" lsr $variable+1 | ror $variable | bcc + | lda $variable+1 | ora #\$80 | sta $variable+1 |+ ")
|
||||||
}
|
}
|
||||||
@ -467,26 +386,25 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun funcRor(fcall: IFunctionCall) {
|
private fun funcRor(fcall: PtBuiltinFunctionCall) {
|
||||||
val what = fcall.args.single()
|
val what = fcall.args.single()
|
||||||
val dt = what.inferType(program)
|
when (what.type) {
|
||||||
when (dt.getOr(DataType.UNDEFINED)) {
|
|
||||||
DataType.UBYTE -> {
|
DataType.UBYTE -> {
|
||||||
when (what) {
|
when (what) {
|
||||||
is ArrayIndexedExpression -> {
|
is PtArrayIndexer -> {
|
||||||
translateRolRorArrayArgs(what.arrayvar, what.indexer, "ror", 'b')
|
translateRolRorArrayArgs(what.variable, what, "ror", 'b')
|
||||||
asmgen.out(" jsr prog8_lib.ror_array_ub")
|
asmgen.out(" jsr prog8_lib.ror_array_ub")
|
||||||
}
|
}
|
||||||
is DirectMemoryRead -> {
|
is PtMemoryByte -> {
|
||||||
if (what.addressExpression is NumericLiteral) {
|
if (what.address is PtNumber) {
|
||||||
val number = (what.addressExpression as NumericLiteral).number
|
val number = (what.address as PtNumber).number
|
||||||
asmgen.out(" ror ${number.toHex()}")
|
asmgen.out(" ror ${number.toHex()}")
|
||||||
} else {
|
} else {
|
||||||
val ptrAndIndex = asmgen.pointerViaIndexRegisterPossible(what.addressExpression)
|
val ptrAndIndex = asmgen.pointerViaIndexRegisterPossible(what.address)
|
||||||
if(ptrAndIndex!=null) {
|
if(ptrAndIndex!=null) {
|
||||||
asmgen.saveRegisterLocal(CpuRegister.X, (fcall as Node).definingSubroutine!!)
|
asmgen.saveRegisterLocal(CpuRegister.X, fcall.definingISub()!!)
|
||||||
asmgen.assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.X)
|
asmgen.assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.X)
|
||||||
asmgen.saveRegisterLocal(CpuRegister.X, (fcall as Node).definingSubroutine!!)
|
asmgen.saveRegisterLocal(CpuRegister.X, fcall.definingISub()!!)
|
||||||
asmgen.assignExpressionToRegister(ptrAndIndex.first, RegisterOrPair.AY)
|
asmgen.assignExpressionToRegister(ptrAndIndex.first, RegisterOrPair.AY)
|
||||||
asmgen.restoreRegisterLocal(CpuRegister.X)
|
asmgen.restoreRegisterLocal(CpuRegister.X)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
@ -495,7 +413,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
|
|||||||
+ ror ${'$'}ffff,x ; modified""")
|
+ ror ${'$'}ffff,x ; modified""")
|
||||||
asmgen.restoreRegisterLocal(CpuRegister.X)
|
asmgen.restoreRegisterLocal(CpuRegister.X)
|
||||||
} else {
|
} else {
|
||||||
asmgen.assignExpressionToRegister(what.addressExpression, RegisterOrPair.AY)
|
asmgen.assignExpressionToRegister(what.address, RegisterOrPair.AY)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
sta (+) + 1
|
sta (+) + 1
|
||||||
sty (+) + 2
|
sty (+) + 2
|
||||||
@ -503,7 +421,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
is IdentifierReference -> {
|
is PtIdentifier -> {
|
||||||
val variable = asmgen.asmVariableName(what)
|
val variable = asmgen.asmVariableName(what)
|
||||||
asmgen.out(" ror $variable")
|
asmgen.out(" ror $variable")
|
||||||
}
|
}
|
||||||
@ -512,11 +430,11 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
|
|||||||
}
|
}
|
||||||
DataType.UWORD -> {
|
DataType.UWORD -> {
|
||||||
when (what) {
|
when (what) {
|
||||||
is ArrayIndexedExpression -> {
|
is PtArrayIndexer -> {
|
||||||
translateRolRorArrayArgs(what.arrayvar, what.indexer, "ror", 'w')
|
translateRolRorArrayArgs(what.variable, what, "ror", 'w')
|
||||||
asmgen.out(" jsr prog8_lib.ror_array_uw")
|
asmgen.out(" jsr prog8_lib.ror_array_uw")
|
||||||
}
|
}
|
||||||
is IdentifierReference -> {
|
is PtIdentifier -> {
|
||||||
val variable = asmgen.asmVariableName(what)
|
val variable = asmgen.asmVariableName(what)
|
||||||
asmgen.out(" ror $variable+1 | ror $variable")
|
asmgen.out(" ror $variable+1 | ror $variable")
|
||||||
}
|
}
|
||||||
@ -527,26 +445,25 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun funcRol2(fcall: IFunctionCall) {
|
private fun funcRol2(fcall: PtBuiltinFunctionCall) {
|
||||||
val what = fcall.args.single()
|
val what = fcall.args.single()
|
||||||
val dt = what.inferType(program)
|
when (what.type) {
|
||||||
when (dt.getOr(DataType.UNDEFINED)) {
|
|
||||||
DataType.UBYTE -> {
|
DataType.UBYTE -> {
|
||||||
when (what) {
|
when (what) {
|
||||||
is ArrayIndexedExpression -> {
|
is PtArrayIndexer -> {
|
||||||
translateRolRorArrayArgs(what.arrayvar, what.indexer, "rol2", 'b')
|
translateRolRorArrayArgs(what.variable, what, "rol2", 'b')
|
||||||
asmgen.out(" jsr prog8_lib.rol2_array_ub")
|
asmgen.out(" jsr prog8_lib.rol2_array_ub")
|
||||||
}
|
}
|
||||||
is DirectMemoryRead -> {
|
is PtMemoryByte -> {
|
||||||
if (what.addressExpression is NumericLiteral) {
|
if (what.address is PtNumber) {
|
||||||
val number = (what.addressExpression as NumericLiteral).number
|
val number = (what.address as PtNumber).number
|
||||||
asmgen.out(" lda ${number.toHex()} | cmp #\$80 | rol a | sta ${number.toHex()}")
|
asmgen.out(" lda ${number.toHex()} | cmp #\$80 | rol a | sta ${number.toHex()}")
|
||||||
} else {
|
} else {
|
||||||
asmgen.assignExpressionToRegister(what.addressExpression, RegisterOrPair.AY)
|
asmgen.assignExpressionToRegister(what.address, RegisterOrPair.AY)
|
||||||
asmgen.out(" jsr prog8_lib.rol2_mem_ub")
|
asmgen.out(" jsr prog8_lib.rol2_mem_ub")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
is IdentifierReference -> {
|
is PtIdentifier -> {
|
||||||
val variable = asmgen.asmVariableName(what)
|
val variable = asmgen.asmVariableName(what)
|
||||||
asmgen.out(" lda $variable | cmp #\$80 | rol a | sta $variable")
|
asmgen.out(" lda $variable | cmp #\$80 | rol a | sta $variable")
|
||||||
}
|
}
|
||||||
@ -555,11 +472,11 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
|
|||||||
}
|
}
|
||||||
DataType.UWORD -> {
|
DataType.UWORD -> {
|
||||||
when (what) {
|
when (what) {
|
||||||
is ArrayIndexedExpression -> {
|
is PtArrayIndexer -> {
|
||||||
translateRolRorArrayArgs(what.arrayvar, what.indexer, "rol2", 'w')
|
translateRolRorArrayArgs(what.variable, what, "rol2", 'w')
|
||||||
asmgen.out(" jsr prog8_lib.rol2_array_uw")
|
asmgen.out(" jsr prog8_lib.rol2_array_uw")
|
||||||
}
|
}
|
||||||
is IdentifierReference -> {
|
is PtIdentifier -> {
|
||||||
val variable = asmgen.asmVariableName(what)
|
val variable = asmgen.asmVariableName(what)
|
||||||
asmgen.out(" asl $variable | rol $variable+1 | bcc + | inc $variable |+ ")
|
asmgen.out(" asl $variable | rol $variable+1 | bcc + | inc $variable |+ ")
|
||||||
}
|
}
|
||||||
@ -570,26 +487,25 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun funcRol(fcall: IFunctionCall) {
|
private fun funcRol(fcall: PtBuiltinFunctionCall) {
|
||||||
val what = fcall.args.single()
|
val what = fcall.args.single()
|
||||||
val dt = what.inferType(program)
|
when (what.type) {
|
||||||
when (dt.getOr(DataType.UNDEFINED)) {
|
|
||||||
DataType.UBYTE -> {
|
DataType.UBYTE -> {
|
||||||
when (what) {
|
when (what) {
|
||||||
is ArrayIndexedExpression -> {
|
is PtArrayIndexer -> {
|
||||||
translateRolRorArrayArgs(what.arrayvar, what.indexer, "rol", 'b')
|
translateRolRorArrayArgs(what.variable, what, "rol", 'b')
|
||||||
asmgen.out(" jsr prog8_lib.rol_array_ub")
|
asmgen.out(" jsr prog8_lib.rol_array_ub")
|
||||||
}
|
}
|
||||||
is DirectMemoryRead -> {
|
is PtMemoryByte -> {
|
||||||
if (what.addressExpression is NumericLiteral) {
|
if (what.address is PtNumber) {
|
||||||
val number = (what.addressExpression as NumericLiteral).number
|
val number = (what.address as PtNumber).number
|
||||||
asmgen.out(" rol ${number.toHex()}")
|
asmgen.out(" rol ${number.toHex()}")
|
||||||
} else {
|
} else {
|
||||||
val ptrAndIndex = asmgen.pointerViaIndexRegisterPossible(what.addressExpression)
|
val ptrAndIndex = asmgen.pointerViaIndexRegisterPossible(what.address)
|
||||||
if(ptrAndIndex!=null) {
|
if(ptrAndIndex!=null) {
|
||||||
asmgen.saveRegisterLocal(CpuRegister.X, (fcall as Node).definingSubroutine!!)
|
asmgen.saveRegisterLocal(CpuRegister.X, fcall.definingISub()!!)
|
||||||
asmgen.assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.X)
|
asmgen.assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.X)
|
||||||
asmgen.saveRegisterLocal(CpuRegister.X, (fcall as Node).definingSubroutine!!)
|
asmgen.saveRegisterLocal(CpuRegister.X, fcall.definingISub()!!)
|
||||||
asmgen.assignExpressionToRegister(ptrAndIndex.first, RegisterOrPair.AY)
|
asmgen.assignExpressionToRegister(ptrAndIndex.first, RegisterOrPair.AY)
|
||||||
asmgen.restoreRegisterLocal(CpuRegister.X)
|
asmgen.restoreRegisterLocal(CpuRegister.X)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
@ -598,7 +514,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
|
|||||||
+ rol ${'$'}ffff,x ; modified""")
|
+ rol ${'$'}ffff,x ; modified""")
|
||||||
asmgen.restoreRegisterLocal(CpuRegister.X)
|
asmgen.restoreRegisterLocal(CpuRegister.X)
|
||||||
} else {
|
} else {
|
||||||
asmgen.assignExpressionToRegister(what.addressExpression, RegisterOrPair.AY)
|
asmgen.assignExpressionToRegister(what.address, RegisterOrPair.AY)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
sta (+) + 1
|
sta (+) + 1
|
||||||
sty (+) + 2
|
sty (+) + 2
|
||||||
@ -606,7 +522,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
is IdentifierReference -> {
|
is PtIdentifier -> {
|
||||||
val variable = asmgen.asmVariableName(what)
|
val variable = asmgen.asmVariableName(what)
|
||||||
asmgen.out(" rol $variable")
|
asmgen.out(" rol $variable")
|
||||||
}
|
}
|
||||||
@ -615,11 +531,11 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
|
|||||||
}
|
}
|
||||||
DataType.UWORD -> {
|
DataType.UWORD -> {
|
||||||
when (what) {
|
when (what) {
|
||||||
is ArrayIndexedExpression -> {
|
is PtArrayIndexer -> {
|
||||||
translateRolRorArrayArgs(what.arrayvar, what.indexer, "rol", 'w')
|
translateRolRorArrayArgs(what.variable, what, "rol", 'w')
|
||||||
asmgen.out(" jsr prog8_lib.rol_array_uw")
|
asmgen.out(" jsr prog8_lib.rol_array_uw")
|
||||||
}
|
}
|
||||||
is IdentifierReference -> {
|
is PtIdentifier -> {
|
||||||
val variable = asmgen.asmVariableName(what)
|
val variable = asmgen.asmVariableName(what)
|
||||||
asmgen.out(" rol $variable | rol $variable+1")
|
asmgen.out(" rol $variable | rol $variable+1")
|
||||||
}
|
}
|
||||||
@ -630,22 +546,25 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun translateRolRorArrayArgs(arrayvar: IdentifierReference, indexer: ArrayIndex, operation: String, dt: Char) {
|
private fun translateRolRorArrayArgs(arrayvar: PtIdentifier, indexer: PtArrayIndexer, operation: String, dt: Char) {
|
||||||
if(arrayvar.targetVarDecl(program)!!.datatype==DataType.UWORD) {
|
if(arrayvar.type==DataType.UWORD) {
|
||||||
if(dt!='b')
|
if(dt!='b')
|
||||||
throw AssemblyError("non-array var indexing requires bytes dt")
|
throw AssemblyError("non-array var indexing requires bytes dt")
|
||||||
asmgen.assignExpressionToVariable(arrayvar, "prog8_lib.${operation}_array_u${dt}._arg_target", DataType.UWORD, null)
|
asmgen.assignExpressionToVariable(arrayvar, "prog8_lib.${operation}_array_u${dt}._arg_target", DataType.UWORD, null)
|
||||||
} else {
|
} else {
|
||||||
asmgen.assignExpressionToVariable(AddressOf(arrayvar, arrayvar.position), "prog8_lib.${operation}_array_u${dt}._arg_target", DataType.UWORD, null)
|
val addressOf = PtAddressOf(arrayvar.position)
|
||||||
|
addressOf.add(arrayvar)
|
||||||
|
addressOf.parent = arrayvar.parent.parent
|
||||||
|
asmgen.assignExpressionToVariable(addressOf, "prog8_lib.${operation}_array_u${dt}._arg_target", DataType.UWORD, null)
|
||||||
}
|
}
|
||||||
asmgen.assignExpressionToVariable(indexer.indexExpr, "prog8_lib.${operation}_array_u${dt}._arg_index", DataType.UBYTE, null)
|
asmgen.assignExpressionToVariable(indexer.index, "prog8_lib.${operation}_array_u${dt}._arg_index", DataType.UBYTE, null)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun funcSgn(fcall: IFunctionCall, func: FSignature, resultToStack: Boolean, resultRegister: RegisterOrPair?, scope: Subroutine?) {
|
private fun funcSgn(fcall: PtBuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?, scope: IPtSubroutine?) {
|
||||||
translateArguments(fcall.args, func, scope)
|
translateArguments(fcall, scope)
|
||||||
val dt = fcall.args.single().inferType(program)
|
val dt = fcall.args.single().type
|
||||||
if(resultToStack) {
|
if(resultToStack) {
|
||||||
when (dt.getOr(DataType.UNDEFINED)) {
|
when (dt) {
|
||||||
DataType.UBYTE -> asmgen.out(" jsr prog8_lib.func_sign_ub_stack")
|
DataType.UBYTE -> asmgen.out(" jsr prog8_lib.func_sign_ub_stack")
|
||||||
DataType.BYTE -> asmgen.out(" jsr prog8_lib.func_sign_b_stack")
|
DataType.BYTE -> asmgen.out(" jsr prog8_lib.func_sign_b_stack")
|
||||||
DataType.UWORD -> asmgen.out(" jsr prog8_lib.func_sign_uw_stack")
|
DataType.UWORD -> asmgen.out(" jsr prog8_lib.func_sign_uw_stack")
|
||||||
@ -654,7 +573,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
|
|||||||
else -> throw AssemblyError("weird type $dt")
|
else -> throw AssemblyError("weird type $dt")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
when (dt.getOr(DataType.UNDEFINED)) {
|
when (dt) {
|
||||||
DataType.UBYTE -> asmgen.out(" jsr prog8_lib.func_sign_ub_into_A")
|
DataType.UBYTE -> asmgen.out(" jsr prog8_lib.func_sign_ub_into_A")
|
||||||
DataType.BYTE -> asmgen.out(" jsr prog8_lib.func_sign_b_into_A")
|
DataType.BYTE -> asmgen.out(" jsr prog8_lib.func_sign_b_into_A")
|
||||||
DataType.UWORD -> asmgen.out(" jsr prog8_lib.func_sign_uw_into_A")
|
DataType.UWORD -> asmgen.out(" jsr prog8_lib.func_sign_uw_into_A")
|
||||||
@ -666,30 +585,30 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun funcAnyAll(fcall: IFunctionCall, function: FSignature, resultToStack: Boolean, resultRegister: RegisterOrPair?, scope: Subroutine?) {
|
private fun funcAnyAll(fcall: PtBuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?, scope: IPtSubroutine?) {
|
||||||
outputAddressAndLenghtOfArray(fcall.args[0])
|
outputAddressAndLenghtOfArray(fcall.args[0])
|
||||||
val dt = fcall.args.single().inferType(program)
|
val dt = fcall.args.single().type
|
||||||
if(resultToStack) {
|
if(resultToStack) {
|
||||||
when (dt.getOr(DataType.UNDEFINED)) {
|
when (dt) {
|
||||||
DataType.ARRAY_B, DataType.ARRAY_UB, DataType.STR -> asmgen.out(" jsr prog8_lib.func_${function.name}_b_stack")
|
DataType.ARRAY_B, DataType.ARRAY_UB, DataType.STR -> asmgen.out(" jsr prog8_lib.func_${fcall.name}_b_stack")
|
||||||
DataType.ARRAY_UW, DataType.ARRAY_W -> asmgen.out(" jsr prog8_lib.func_${function.name}_w_stack")
|
DataType.ARRAY_UW, DataType.ARRAY_W -> asmgen.out(" jsr prog8_lib.func_${fcall.name}_w_stack")
|
||||||
DataType.ARRAY_F -> asmgen.out(" jsr floats.func_${function.name}_f_stack")
|
DataType.ARRAY_F -> asmgen.out(" jsr floats.func_${fcall.name}_f_stack")
|
||||||
else -> throw AssemblyError("weird type $dt")
|
else -> throw AssemblyError("weird type $dt")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
when (dt.getOr(DataType.UNDEFINED)) {
|
when (dt) {
|
||||||
DataType.ARRAY_B, DataType.ARRAY_UB, DataType.STR -> asmgen.out(" jsr prog8_lib.func_${function.name}_b_into_A | ldy #0")
|
DataType.ARRAY_B, DataType.ARRAY_UB, DataType.STR -> asmgen.out(" jsr prog8_lib.func_${fcall.name}_b_into_A | ldy #0")
|
||||||
DataType.ARRAY_UW, DataType.ARRAY_W -> asmgen.out(" jsr prog8_lib.func_${function.name}_w_into_A | ldy #0")
|
DataType.ARRAY_UW, DataType.ARRAY_W -> asmgen.out(" jsr prog8_lib.func_${fcall.name}_w_into_A | ldy #0")
|
||||||
DataType.ARRAY_F -> asmgen.out(" jsr floats.func_${function.name}_f_into_A | ldy #0")
|
DataType.ARRAY_F -> asmgen.out(" jsr floats.func_${fcall.name}_f_into_A | ldy #0")
|
||||||
else -> throw AssemblyError("weird type $dt")
|
else -> throw AssemblyError("weird type $dt")
|
||||||
}
|
}
|
||||||
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, false, scope, asmgen), CpuRegister.A)
|
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, false, scope, asmgen), CpuRegister.A)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun funcAbs(fcall: IFunctionCall, func: FSignature, resultToStack: Boolean, resultRegister: RegisterOrPair?, scope: Subroutine?) {
|
private fun funcAbs(fcall: PtBuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?, scope: IPtSubroutine?) {
|
||||||
translateArguments(fcall.args, func, scope)
|
translateArguments(fcall, scope)
|
||||||
val dt = fcall.args.single().inferType(program).getOr(DataType.UNDEFINED)
|
val dt = fcall.args.single().type
|
||||||
if(resultToStack) {
|
if(resultToStack) {
|
||||||
when (dt) {
|
when (dt) {
|
||||||
DataType.UBYTE -> asmgen.out(" ldy #0")
|
DataType.UBYTE -> asmgen.out(" ldy #0")
|
||||||
@ -710,19 +629,19 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun funcPokeW(fcall: IFunctionCall) {
|
private fun funcPokeW(fcall: PtBuiltinFunctionCall) {
|
||||||
when(val addrExpr = fcall.args[0]) {
|
when(val addrExpr = fcall.args[0]) {
|
||||||
is NumericLiteral -> {
|
is PtNumber -> {
|
||||||
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.AY)
|
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.AY)
|
||||||
val addr = addrExpr.number.toHex()
|
val addr = addrExpr.number.toHex()
|
||||||
asmgen.out(" sta $addr | sty ${addr}+1")
|
asmgen.out(" sta $addr | sty ${addr}+1")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
is IdentifierReference -> {
|
is PtIdentifier -> {
|
||||||
val varname = asmgen.asmVariableName(addrExpr)
|
val varname = asmgen.asmVariableName(addrExpr)
|
||||||
if(asmgen.isZpVar(addrExpr)) {
|
if(asmgen.isZpVar(addrExpr)) {
|
||||||
// pointervar is already in the zero page, no need to copy
|
// pointervar is already in the zero page, no need to copy
|
||||||
asmgen.saveRegisterLocal(CpuRegister.X, (fcall as Node).definingSubroutine!!)
|
asmgen.saveRegisterLocal(CpuRegister.X, fcall.definingISub()!!)
|
||||||
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.AX)
|
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.AX)
|
||||||
if (asmgen.isTargetCpu(CpuType.CPU65c02)) {
|
if (asmgen.isTargetCpu(CpuType.CPU65c02)) {
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
@ -742,14 +661,14 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
is BinaryExpression -> {
|
is PtBinaryExpression -> {
|
||||||
if(addrExpr.operator=="+" && addrExpr.left is IdentifierReference && addrExpr.right is NumericLiteral) {
|
if(addrExpr.operator=="+" && addrExpr.left is PtIdentifier && addrExpr.right is PtNumber) {
|
||||||
val varname = asmgen.asmVariableName(addrExpr.left as IdentifierReference)
|
val varname = asmgen.asmVariableName(addrExpr.left as PtIdentifier)
|
||||||
if(asmgen.isZpVar(addrExpr.left as IdentifierReference)) {
|
if(asmgen.isZpVar(addrExpr.left as PtIdentifier)) {
|
||||||
// pointervar is already in the zero page, no need to copy
|
// pointervar is already in the zero page, no need to copy
|
||||||
asmgen.saveRegisterLocal(CpuRegister.X, (fcall as Node).definingSubroutine!!)
|
asmgen.saveRegisterLocal(CpuRegister.X, fcall.definingISub()!!)
|
||||||
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.AX)
|
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.AX)
|
||||||
val index = (addrExpr.right as NumericLiteral).number.toHex()
|
val index = (addrExpr.right as PtNumber).number.toHex()
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
ldy #$index
|
ldy #$index
|
||||||
sta ($varname),y
|
sta ($varname),y
|
||||||
@ -769,13 +688,13 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
|
|||||||
asmgen.out(" jsr prog8_lib.func_pokew")
|
asmgen.out(" jsr prog8_lib.func_pokew")
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun funcPeekW(fcall: IFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?) {
|
private fun funcPeekW(fcall: PtBuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?) {
|
||||||
when(val addrExpr = fcall.args[0]) {
|
when(val addrExpr = fcall.args[0]) {
|
||||||
is NumericLiteral -> {
|
is PtNumber -> {
|
||||||
val addr = addrExpr.number.toHex()
|
val addr = addrExpr.number.toHex()
|
||||||
asmgen.out(" lda $addr | ldy ${addr}+1")
|
asmgen.out(" lda $addr | ldy ${addr}+1")
|
||||||
}
|
}
|
||||||
is IdentifierReference -> {
|
is PtIdentifier -> {
|
||||||
val varname = asmgen.asmVariableName(addrExpr)
|
val varname = asmgen.asmVariableName(addrExpr)
|
||||||
if(asmgen.isZpVar(addrExpr)) {
|
if(asmgen.isZpVar(addrExpr)) {
|
||||||
// pointervar is already in the zero page, no need to copy
|
// pointervar is already in the zero page, no need to copy
|
||||||
@ -800,12 +719,12 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
|
|||||||
asmgen.out(" jsr prog8_lib.func_peekw")
|
asmgen.out(" jsr prog8_lib.func_peekw")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
is BinaryExpression -> {
|
is PtBinaryExpression -> {
|
||||||
if(addrExpr.operator=="+" && addrExpr.left is IdentifierReference && addrExpr.right is NumericLiteral) {
|
if(addrExpr.operator=="+" && addrExpr.left is PtIdentifier && addrExpr.right is PtNumber) {
|
||||||
val varname = asmgen.asmVariableName(addrExpr.left as IdentifierReference)
|
val varname = asmgen.asmVariableName(addrExpr.left as PtIdentifier)
|
||||||
if(asmgen.isZpVar(addrExpr.left as IdentifierReference)) {
|
if(asmgen.isZpVar(addrExpr.left as PtIdentifier)) {
|
||||||
// pointervar is already in the zero page, no need to copy
|
// pointervar is already in the zero page, no need to copy
|
||||||
val index = (addrExpr.right as NumericLiteral).number.toHex()
|
val index = (addrExpr.right as PtNumber).number.toHex()
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
ldy #$index
|
ldy #$index
|
||||||
lda ($varname),y
|
lda ($varname),y
|
||||||
@ -845,7 +764,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun funcMkword(fcall: IFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?) {
|
private fun funcMkword(fcall: PtBuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?) {
|
||||||
if(resultToStack) {
|
if(resultToStack) {
|
||||||
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.Y) // msb
|
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.Y) // msb
|
||||||
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.A) // lsb
|
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.A) // lsb
|
||||||
@ -854,12 +773,12 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
|
|||||||
val reg = resultRegister ?: RegisterOrPair.AY
|
val reg = resultRegister ?: RegisterOrPair.AY
|
||||||
var needAsave = asmgen.needAsaveForExpr(fcall.args[0])
|
var needAsave = asmgen.needAsaveForExpr(fcall.args[0])
|
||||||
if(!needAsave) {
|
if(!needAsave) {
|
||||||
val mr0 = fcall.args[0] as? DirectMemoryRead
|
val mr0 = fcall.args[0] as? PtMemoryByte
|
||||||
val mr1 = fcall.args[1] as? DirectMemoryRead
|
val mr1 = fcall.args[1] as? PtMemoryByte
|
||||||
if (mr0 != null)
|
if (mr0 != null)
|
||||||
needAsave = mr0.addressExpression !is NumericLiteral
|
needAsave = mr0.address !is PtNumber
|
||||||
if (mr1 != null)
|
if (mr1 != null)
|
||||||
needAsave = needAsave or (mr1.addressExpression !is NumericLiteral)
|
needAsave = needAsave or (mr1.address !is PtNumber)
|
||||||
}
|
}
|
||||||
when(reg) {
|
when(reg) {
|
||||||
RegisterOrPair.AX -> {
|
RegisterOrPair.AX -> {
|
||||||
@ -898,13 +817,13 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun funcMsb(fcall: IFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?) {
|
private fun funcMsb(fcall: PtBuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?) {
|
||||||
val arg = fcall.args.single()
|
val arg = fcall.args.single()
|
||||||
if (!arg.inferType(program).isWords)
|
if (arg.type !in WordDatatypes)
|
||||||
throw AssemblyError("msb required word argument")
|
throw AssemblyError("msb required word argument")
|
||||||
if (arg is NumericLiteral)
|
if (arg is PtNumber)
|
||||||
throw AssemblyError("msb(const) should have been const-folded away")
|
throw AssemblyError("msb(const) should have been const-folded away")
|
||||||
if (arg is IdentifierReference) {
|
if (arg is PtIdentifier) {
|
||||||
val sourceName = asmgen.asmVariableName(arg)
|
val sourceName = asmgen.asmVariableName(arg)
|
||||||
if(resultToStack) {
|
if(resultToStack) {
|
||||||
asmgen.out(" lda $sourceName+1 | sta P8ESTACK_LO,x | dex")
|
asmgen.out(" lda $sourceName+1 | sta P8ESTACK_LO,x | dex")
|
||||||
@ -952,14 +871,14 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun funcLsb(fcall: IFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?) {
|
private fun funcLsb(fcall: PtBuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?) {
|
||||||
val arg = fcall.args.single()
|
val arg = fcall.args.single()
|
||||||
if (!arg.inferType(program).isWords)
|
if (arg.type !in WordDatatypes)
|
||||||
throw AssemblyError("lsb required word argument")
|
throw AssemblyError("lsb required word argument")
|
||||||
if (arg is NumericLiteral)
|
if (arg is PtNumber)
|
||||||
throw AssemblyError("lsb(const) should have been const-folded away")
|
throw AssemblyError("lsb(const) should have been const-folded away")
|
||||||
|
|
||||||
if (arg is IdentifierReference) {
|
if (arg is PtIdentifier) {
|
||||||
val sourceName = asmgen.asmVariableName(arg)
|
val sourceName = asmgen.asmVariableName(arg)
|
||||||
if(resultToStack) {
|
if(resultToStack) {
|
||||||
asmgen.out(" lda $sourceName | sta P8ESTACK_LO,x | dex")
|
asmgen.out(" lda $sourceName | sta P8ESTACK_LO,x | dex")
|
||||||
@ -1013,35 +932,39 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun outputAddressAndLenghtOfArray(arg: Expression) {
|
private fun outputAddressAndLenghtOfArray(arg: PtExpression) {
|
||||||
// address in P8ZP_SCRATCH_W1, number of elements in A
|
// address in P8ZP_SCRATCH_W1, number of elements in A
|
||||||
arg as IdentifierReference
|
arg as PtIdentifier
|
||||||
val arrayVar = arg.targetVarDecl(program)!!
|
val symbol = asmgen.symbolTable.lookup(arg.name)
|
||||||
if(!arrayVar.isArray)
|
val arrayVar = symbol!!.astNode as IPtVariable
|
||||||
throw AssemblyError("length of non-array requested")
|
val numElements = when(arrayVar) {
|
||||||
val size = arrayVar.arraysize!!.constIndex()!!
|
is PtConstant -> null
|
||||||
|
is PtMemMapped -> arrayVar.arraySize
|
||||||
|
is PtVariable -> arrayVar.arraySize
|
||||||
|
} ?: throw AssemblyError("length of non-array requested")
|
||||||
val identifierName = asmgen.asmVariableName(arg)
|
val identifierName = asmgen.asmVariableName(arg)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda #<$identifierName
|
lda #<$identifierName
|
||||||
ldy #>$identifierName
|
ldy #>$identifierName
|
||||||
sta P8ZP_SCRATCH_W1
|
sta P8ZP_SCRATCH_W1
|
||||||
sty P8ZP_SCRATCH_W1+1
|
sty P8ZP_SCRATCH_W1+1
|
||||||
lda #$size
|
lda #$numElements
|
||||||
""")
|
""")
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun translateArguments(args: MutableList<Expression>, signature: FSignature, scope: Subroutine?) {
|
private fun translateArguments(call: PtBuiltinFunctionCall, scope: IPtSubroutine?) {
|
||||||
val callConv = signature.callConvention(args.map {
|
val signature = BuiltinFunctions.getValue(call.name)
|
||||||
it.inferType(program).getOrElse { throw AssemblyError("unknown dt") }
|
val callConv = signature.callConvention(call.args.map { it.type})
|
||||||
})
|
|
||||||
|
|
||||||
fun getSourceForFloat(value: Expression): AsmAssignSource {
|
fun getSourceForFloat(value: PtExpression): AsmAssignSource {
|
||||||
return when (value) {
|
return when (value) {
|
||||||
is IdentifierReference -> {
|
is PtIdentifier -> {
|
||||||
val addr = AddressOf(value, value.position)
|
val addr = PtAddressOf(value.position)
|
||||||
|
addr.add(value)
|
||||||
|
addr.parent = call
|
||||||
AsmAssignSource.fromAstSource(addr, program, asmgen)
|
AsmAssignSource.fromAstSource(addr, program, asmgen)
|
||||||
}
|
}
|
||||||
is NumericLiteral -> {
|
is PtNumber -> {
|
||||||
throw AssemblyError("float literals should have been converted into autovar")
|
throw AssemblyError("float literals should have been converted into autovar")
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
@ -1049,27 +972,30 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
|
|||||||
throw AssemblyError("cannot use float arguments outside of a subroutine scope")
|
throw AssemblyError("cannot use float arguments outside of a subroutine scope")
|
||||||
|
|
||||||
asmgen.subroutineExtra(scope).usedFloatEvalResultVar2 = true
|
asmgen.subroutineExtra(scope).usedFloatEvalResultVar2 = true
|
||||||
val variable = IdentifierReference(listOf(subroutineFloatEvalResultVar2), value.position)
|
val variable = PtIdentifier(subroutineFloatEvalResultVar2, DataType.FLOAT, value.position)
|
||||||
val addr = AddressOf(variable, value.position)
|
val addr = PtAddressOf(value.position)
|
||||||
addr.linkParents(value)
|
addr.add(variable)
|
||||||
|
addr.parent = call
|
||||||
asmgen.assignExpressionToVariable(value, asmgen.asmVariableName(variable), DataType.FLOAT, scope)
|
asmgen.assignExpressionToVariable(value, asmgen.asmVariableName(variable), DataType.FLOAT, scope)
|
||||||
AsmAssignSource.fromAstSource(addr, program, asmgen)
|
AsmAssignSource.fromAstSource(addr, program, asmgen)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
args.zip(callConv.params).zip(signature.parameters).forEach {
|
call.args.zip(callConv.params).zip(signature.parameters).forEach {
|
||||||
val paramName = it.second.name
|
val paramName = it.second.name
|
||||||
val conv = it.first.second
|
val conv = it.first.second
|
||||||
val value = it.first.first
|
val value = it.first.first
|
||||||
when {
|
when {
|
||||||
conv.variable -> {
|
conv.variable -> {
|
||||||
val varname = "prog8_lib.func_${signature.name}._arg_${paramName}"
|
val varname = "prog8_lib.func_${call.name}._arg_${paramName}"
|
||||||
val src = when (conv.dt) {
|
val src = when (conv.dt) {
|
||||||
DataType.FLOAT -> getSourceForFloat(value)
|
DataType.FLOAT -> getSourceForFloat(value)
|
||||||
in PassByReferenceDatatypes -> {
|
in PassByReferenceDatatypes -> {
|
||||||
// put the address of the argument in AY
|
// put the address of the argument in AY
|
||||||
val addr = AddressOf(value as IdentifierReference, value.position)
|
val addr = PtAddressOf(value.position)
|
||||||
|
addr.add(value)
|
||||||
|
addr.parent = call
|
||||||
AsmAssignSource.fromAstSource(addr, program, asmgen)
|
AsmAssignSource.fromAstSource(addr, program, asmgen)
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
@ -1077,7 +1003,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
val tgt = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, conv.dt, null, variableAsmName = varname)
|
val tgt = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, conv.dt, null, variableAsmName = varname)
|
||||||
val assign = AsmAssignment(src, tgt, false, program.memsizer, value.position)
|
val assign = AsmAssignment(src, tgt, program.memsizer, value.position)
|
||||||
asmgen.translateNormalAssignment(assign)
|
asmgen.translateNormalAssignment(assign)
|
||||||
}
|
}
|
||||||
conv.reg != null -> {
|
conv.reg != null -> {
|
||||||
@ -1085,7 +1011,9 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
|
|||||||
DataType.FLOAT -> getSourceForFloat(value)
|
DataType.FLOAT -> getSourceForFloat(value)
|
||||||
in PassByReferenceDatatypes -> {
|
in PassByReferenceDatatypes -> {
|
||||||
// put the address of the argument in AY
|
// put the address of the argument in AY
|
||||||
val addr = AddressOf(value as IdentifierReference, value.position)
|
val addr = PtAddressOf(value.position)
|
||||||
|
addr.add(value)
|
||||||
|
addr.parent = call
|
||||||
AsmAssignSource.fromAstSource(addr, program, asmgen)
|
AsmAssignSource.fromAstSource(addr, program, asmgen)
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
@ -1093,7 +1021,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
val tgt = AsmAssignTarget.fromRegisters(conv.reg!!, false, null, asmgen)
|
val tgt = AsmAssignTarget.fromRegisters(conv.reg!!, false, null, asmgen)
|
||||||
val assign = AsmAssignment(src, tgt, false, program.memsizer, value.position)
|
val assign = AsmAssignment(src, tgt, program.memsizer, value.position)
|
||||||
asmgen.translateNormalAssignment(assign)
|
asmgen.translateNormalAssignment(assign)
|
||||||
}
|
}
|
||||||
else -> throw AssemblyError("callconv")
|
else -> throw AssemblyError("callconv")
|
||||||
|
@ -1,18 +1,17 @@
|
|||||||
package prog8.codegen.cpu6502
|
package prog8.codegen.cpu6502
|
||||||
|
|
||||||
import prog8.ast.Program
|
import prog8.code.ast.*
|
||||||
import prog8.ast.expressions.*
|
|
||||||
import prog8.code.core.*
|
import prog8.code.core.*
|
||||||
import kotlin.math.absoluteValue
|
import kotlin.math.absoluteValue
|
||||||
|
|
||||||
internal class ExpressionsAsmGen(private val program: Program,
|
internal class ExpressionsAsmGen(private val program: PtProgram,
|
||||||
private val asmgen: AsmGen,
|
private val asmgen: AsmGen6502Internal,
|
||||||
private val allocator: VariableAllocator) {
|
private val allocator: VariableAllocator) {
|
||||||
|
|
||||||
@Deprecated("avoid calling this as it generates slow evalstack based code")
|
@Deprecated("avoid calling this as it generates slow evalstack based code")
|
||||||
internal fun translateExpression(expression:Expression) {
|
internal fun translateExpression(expression: PtExpression) {
|
||||||
if (this.asmgen.options.slowCodegenWarnings) {
|
if (this.asmgen.options.slowCodegenWarnings) {
|
||||||
asmgen.errors.warn("slow stack evaluation used for expression $expression", expression.position)
|
asmgen.errors.warn("slow stack evaluation used for expression", expression.position)
|
||||||
}
|
}
|
||||||
translateExpressionInternal(expression)
|
translateExpressionInternal(expression)
|
||||||
}
|
}
|
||||||
@ -21,41 +20,42 @@ internal class ExpressionsAsmGen(private val program: Program,
|
|||||||
// the rest of the methods are all PRIVATE
|
// the rest of the methods are all PRIVATE
|
||||||
|
|
||||||
|
|
||||||
private fun translateExpressionInternal(expression: Expression) {
|
private fun translateExpressionInternal(expression: PtExpression) {
|
||||||
|
|
||||||
when(expression) {
|
when(expression) {
|
||||||
is PrefixExpression -> translateExpression(expression)
|
is PtPrefix -> translateExpression(expression)
|
||||||
is BinaryExpression -> translateExpression(expression)
|
is PtBinaryExpression -> translateExpression(expression)
|
||||||
is ArrayIndexedExpression -> translateExpression(expression)
|
is PtArrayIndexer -> translateExpression(expression)
|
||||||
is TypecastExpression -> translateExpression(expression)
|
is PtTypeCast -> translateExpression(expression)
|
||||||
is AddressOf -> translateExpression(expression)
|
is PtAddressOf -> translateExpression(expression)
|
||||||
is DirectMemoryRead -> asmgen.translateDirectMemReadExpressionToRegAorStack(expression, true)
|
is PtMemoryByte -> asmgen.translateDirectMemReadExpressionToRegAorStack(expression, true)
|
||||||
is NumericLiteral -> translateExpression(expression)
|
is PtNumber -> translateExpression(expression)
|
||||||
is IdentifierReference -> translateExpression(expression)
|
is PtIdentifier -> translateExpression(expression)
|
||||||
is FunctionCallExpression -> translateFunctionCallResultOntoStack(expression)
|
is PtFunctionCall -> translateFunctionCallResultOntoStack(expression)
|
||||||
is BuiltinFunctionCall -> asmgen.translateBuiltinFunctionCallExpression(expression, true, null)
|
is PtBuiltinFunctionCall -> asmgen.translateBuiltinFunctionCallExpression(expression, true, null)
|
||||||
is ContainmentCheck -> throw AssemblyError("containment check as complex expression value is not supported")
|
is PtContainmentCheck -> throw AssemblyError("containment check as complex expression value is not supported")
|
||||||
is ArrayLiteral, is StringLiteral -> throw AssemblyError("no asm gen for string/array literal value assignment - should have been replaced by a variable")
|
is PtArray, is PtString -> throw AssemblyError("no asm gen for string/array literal value assignment - should have been replaced by a variable")
|
||||||
is RangeExpression -> throw AssemblyError("range expression should have been changed into array values")
|
is PtRange -> throw AssemblyError("range expression should have been changed into array values")
|
||||||
is CharLiteral -> throw AssemblyError("charliteral should have been replaced by ubyte using certain encoding")
|
is PtMachineRegister -> throw AssemblyError("machine register ast node should not occur in 6502 codegen it is for IR code")
|
||||||
else -> TODO("missing expression asmgen for $expression")
|
else -> TODO("missing expression asmgen for $expression")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun translateFunctionCallResultOntoStack(call: FunctionCallExpression) {
|
private fun translateFunctionCallResultOntoStack(call: PtFunctionCall) {
|
||||||
// only for use in nested expression evaluation
|
// only for use in nested expression evaluation
|
||||||
|
|
||||||
val sub = call.target.targetSubroutine(program)!!
|
val symbol = asmgen.symbolTable.lookup(call.name)
|
||||||
|
val sub = symbol!!.astNode as IPtSubroutine
|
||||||
asmgen.saveXbeforeCall(call)
|
asmgen.saveXbeforeCall(call)
|
||||||
asmgen.translateFunctionCall(call, true)
|
asmgen.translateFunctionCall(call)
|
||||||
if(sub.regXasResult()) {
|
if(sub.regXasResult()) {
|
||||||
// store the return value in X somewhere that we can access again below
|
// store the return value in X somewhere that we can access again below
|
||||||
asmgen.out(" stx P8ZP_SCRATCH_REG")
|
asmgen.out(" stx P8ZP_SCRATCH_REG")
|
||||||
}
|
}
|
||||||
asmgen.restoreXafterCall(call)
|
asmgen.restoreXafterCall(call)
|
||||||
|
|
||||||
val returns = sub.returntypes.zip(sub.asmReturnvaluesRegisters)
|
val returns: List<Pair<RegisterOrStatusflag, DataType>> = sub.returnsWhatWhere()
|
||||||
for ((_, reg) in returns) {
|
for ((reg, _) in returns) {
|
||||||
// result value is in cpu or status registers, put it on the stack instead (as we're evaluating an expression tree)
|
// result value is in cpu or status registers, put it on the stack instead (as we're evaluating an expression tree)
|
||||||
if (reg.registerOrPair != null) {
|
if (reg.registerOrPair != null) {
|
||||||
when (reg.registerOrPair!!) {
|
when (reg.registerOrPair!!) {
|
||||||
@ -133,9 +133,9 @@ internal class ExpressionsAsmGen(private val program: Program,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun translateExpression(typecast: TypecastExpression) {
|
private fun translateExpression(typecast: PtTypeCast) {
|
||||||
translateExpressionInternal(typecast.expression)
|
translateExpressionInternal(typecast.value)
|
||||||
when(typecast.expression.inferType(program).getOr(DataType.UNDEFINED)) {
|
when(typecast.value.type) {
|
||||||
DataType.UBYTE, DataType.BOOL -> {
|
DataType.UBYTE, DataType.BOOL -> {
|
||||||
when(typecast.type) {
|
when(typecast.type) {
|
||||||
DataType.UBYTE, DataType.BYTE -> {}
|
DataType.UBYTE, DataType.BYTE -> {}
|
||||||
@ -197,12 +197,12 @@ internal class ExpressionsAsmGen(private val program: Program,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun translateExpression(expr: AddressOf) {
|
private fun translateExpression(expr: PtAddressOf) {
|
||||||
val name = asmgen.asmVariableName(expr.identifier)
|
val name = asmgen.asmVariableName(expr.identifier)
|
||||||
asmgen.out(" lda #<$name | sta P8ESTACK_LO,x | lda #>$name | sta P8ESTACK_HI,x | dex")
|
asmgen.out(" lda #<$name | sta P8ESTACK_LO,x | lda #>$name | sta P8ESTACK_HI,x | dex")
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun translateExpression(expr: NumericLiteral) {
|
private fun translateExpression(expr: PtNumber) {
|
||||||
when(expr.type) {
|
when(expr.type) {
|
||||||
DataType.UBYTE, DataType.BYTE -> asmgen.out(" lda #${expr.number.toHex()} | sta P8ESTACK_LO,x | dex")
|
DataType.UBYTE, DataType.BYTE -> asmgen.out(" lda #${expr.number.toHex()} | sta P8ESTACK_LO,x | dex")
|
||||||
DataType.UWORD, DataType.WORD -> asmgen.out("""
|
DataType.UWORD, DataType.WORD -> asmgen.out("""
|
||||||
@ -220,9 +220,9 @@ internal class ExpressionsAsmGen(private val program: Program,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun translateExpression(expr: IdentifierReference) {
|
private fun translateExpression(expr: PtIdentifier) {
|
||||||
val varname = asmgen.asmVariableName(expr)
|
val varname = asmgen.asmVariableName(expr)
|
||||||
when(expr.inferType(program).getOr(DataType.UNDEFINED)) {
|
when(expr.type) {
|
||||||
DataType.UBYTE, DataType.BYTE -> {
|
DataType.UBYTE, DataType.BYTE -> {
|
||||||
asmgen.out(" lda $varname | sta P8ESTACK_LO,x | dex")
|
asmgen.out(" lda $varname | sta P8ESTACK_LO,x | dex")
|
||||||
}
|
}
|
||||||
@ -239,22 +239,16 @@ internal class ExpressionsAsmGen(private val program: Program,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun translateExpression(expr: BinaryExpression) {
|
private fun translateExpression(expr: PtBinaryExpression) {
|
||||||
// Uses evalstack to evaluate the given expression.
|
// Uses evalstack to evaluate the given expression. THIS IS SLOW AND SHOULD BE AVOIDED!
|
||||||
// TODO we're slowly reducing the number of places where this is called and instead replace that by more efficient assignment-form code (using temp var or register for instance).
|
val leftDt = expr.left.type
|
||||||
val leftIDt = expr.left.inferType(program)
|
val rightDt = expr.right.type
|
||||||
val rightIDt = expr.right.inferType(program)
|
// see if we can apply some optimized routines still
|
||||||
if(!leftIDt.isKnown || !rightIDt.isKnown)
|
|
||||||
throw AssemblyError("can't infer type of both expression operands")
|
|
||||||
|
|
||||||
val leftDt = leftIDt.getOrElse { throw AssemblyError("unknown dt") }
|
|
||||||
val rightDt = rightIDt.getOrElse { throw AssemblyError("unknown dt") }
|
|
||||||
// see if we can apply some optimized routines
|
|
||||||
when(expr.operator) {
|
when(expr.operator) {
|
||||||
"+" -> {
|
"+" -> {
|
||||||
if(leftDt in IntegerDatatypes && rightDt in IntegerDatatypes) {
|
if(leftDt in IntegerDatatypes && rightDt in IntegerDatatypes) {
|
||||||
val leftVal = expr.left.constValue(program)?.number?.toInt()
|
val leftVal = expr.left.asConstInteger()
|
||||||
val rightVal = expr.right.constValue(program)?.number?.toInt()
|
val rightVal = expr.right.asConstInteger()
|
||||||
if (leftVal!=null && leftVal in -4..4) {
|
if (leftVal!=null && leftVal in -4..4) {
|
||||||
translateExpressionInternal(expr.right)
|
translateExpressionInternal(expr.right)
|
||||||
if(rightDt in ByteDatatypes) {
|
if(rightDt in ByteDatatypes) {
|
||||||
@ -318,7 +312,7 @@ internal class ExpressionsAsmGen(private val program: Program,
|
|||||||
}
|
}
|
||||||
"-" -> {
|
"-" -> {
|
||||||
if(leftDt in IntegerDatatypes && rightDt in IntegerDatatypes) {
|
if(leftDt in IntegerDatatypes && rightDt in IntegerDatatypes) {
|
||||||
val rightVal = expr.right.constValue(program)?.number?.toInt()
|
val rightVal = expr.right.asConstInteger()
|
||||||
if (rightVal!=null && rightVal in -4..4)
|
if (rightVal!=null && rightVal in -4..4)
|
||||||
{
|
{
|
||||||
translateExpressionInternal(expr.left)
|
translateExpressionInternal(expr.left)
|
||||||
@ -352,7 +346,7 @@ internal class ExpressionsAsmGen(private val program: Program,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
">>" -> {
|
">>" -> {
|
||||||
val amount = expr.right.constValue(program)?.number?.toInt()
|
val amount = expr.right.asConstInteger()
|
||||||
if(amount!=null) {
|
if(amount!=null) {
|
||||||
translateExpressionInternal(expr.left)
|
translateExpressionInternal(expr.left)
|
||||||
when (leftDt) {
|
when (leftDt) {
|
||||||
@ -423,7 +417,7 @@ internal class ExpressionsAsmGen(private val program: Program,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
"<<" -> {
|
"<<" -> {
|
||||||
val amount = expr.right.constValue(program)?.number?.toInt()
|
val amount = expr.right.asConstInteger()
|
||||||
if(amount!=null) {
|
if(amount!=null) {
|
||||||
translateExpressionInternal(expr.left)
|
translateExpressionInternal(expr.left)
|
||||||
if (leftDt in ByteDatatypes) {
|
if (leftDt in ByteDatatypes) {
|
||||||
@ -450,13 +444,13 @@ internal class ExpressionsAsmGen(private val program: Program,
|
|||||||
}
|
}
|
||||||
"*" -> {
|
"*" -> {
|
||||||
if(leftDt in IntegerDatatypes && rightDt in IntegerDatatypes) {
|
if(leftDt in IntegerDatatypes && rightDt in IntegerDatatypes) {
|
||||||
val leftVar = expr.left as? IdentifierReference
|
val leftVar = expr.left as? PtIdentifier
|
||||||
val rightVar = expr.right as? IdentifierReference
|
val rightVar = expr.right as? PtIdentifier
|
||||||
if(leftVar!=null && rightVar!=null && leftVar==rightVar)
|
if(leftVar!=null && rightVar!=null && leftVar==rightVar)
|
||||||
return translateSquared(leftVar, leftDt)
|
return translateSquared(leftVar, leftDt)
|
||||||
}
|
}
|
||||||
|
|
||||||
val value = expr.right.constValue(program)
|
val value = expr.right as? PtNumber
|
||||||
if(value!=null) {
|
if(value!=null) {
|
||||||
if(rightDt in IntegerDatatypes) {
|
if(rightDt in IntegerDatatypes) {
|
||||||
val amount = value.number.toInt()
|
val amount = value.number.toInt()
|
||||||
@ -516,7 +510,7 @@ internal class ExpressionsAsmGen(private val program: Program,
|
|||||||
}
|
}
|
||||||
"/" -> {
|
"/" -> {
|
||||||
if(leftDt in IntegerDatatypes && rightDt in IntegerDatatypes) {
|
if(leftDt in IntegerDatatypes && rightDt in IntegerDatatypes) {
|
||||||
val rightVal = expr.right.constValue(program)?.number?.toInt()
|
val rightVal = expr.right.asConstInteger()
|
||||||
if(rightVal!=null && rightVal==2) {
|
if(rightVal!=null && rightVal==2) {
|
||||||
translateExpressionInternal(expr.left)
|
translateExpressionInternal(expr.left)
|
||||||
when (leftDt) {
|
when (leftDt) {
|
||||||
@ -557,8 +551,8 @@ internal class ExpressionsAsmGen(private val program: Program,
|
|||||||
}
|
}
|
||||||
in ComparisonOperators -> {
|
in ComparisonOperators -> {
|
||||||
if(leftDt in NumericDatatypes && rightDt in NumericDatatypes) {
|
if(leftDt in NumericDatatypes && rightDt in NumericDatatypes) {
|
||||||
val rightVal = expr.right.constValue(program)?.number
|
val rightVal = expr.right.asConstInteger()
|
||||||
if(rightVal==0.0)
|
if(rightVal==0)
|
||||||
return translateComparisonWithZero(expr.left, leftDt, expr.operator)
|
return translateComparisonWithZero(expr.left, leftDt, expr.operator)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -572,7 +566,8 @@ internal class ExpressionsAsmGen(private val program: Program,
|
|||||||
translateCompareStrings(expr.left, expr.operator, expr.right)
|
translateCompareStrings(expr.left, expr.operator, expr.right)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// the general, non-optimized cases TODO optimize more cases.... (or one day just don't use the evalstack at all anymore)
|
// the general, non-optimized cases
|
||||||
|
// TODO optimize more cases.... (or one day just don't use the evalstack at all anymore)
|
||||||
translateExpressionInternal(expr.left)
|
translateExpressionInternal(expr.left)
|
||||||
translateExpressionInternal(expr.right)
|
translateExpressionInternal(expr.right)
|
||||||
when (leftDt) {
|
when (leftDt) {
|
||||||
@ -584,8 +579,8 @@ internal class ExpressionsAsmGen(private val program: Program,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun translateComparisonWithZero(expr: Expression, dt: DataType, operator: String) {
|
private fun translateComparisonWithZero(expr: PtExpression, dt: DataType, operator: String) {
|
||||||
if(expr.isSimple) {
|
if(expr.isSimple()) {
|
||||||
if(operator=="!=") {
|
if(operator=="!=") {
|
||||||
when (dt) {
|
when (dt) {
|
||||||
in ByteDatatypes -> {
|
in ByteDatatypes -> {
|
||||||
@ -641,7 +636,7 @@ internal class ExpressionsAsmGen(private val program: Program,
|
|||||||
}
|
}
|
||||||
"<" -> {
|
"<" -> {
|
||||||
if(dt==DataType.UBYTE || dt==DataType.UWORD)
|
if(dt==DataType.UBYTE || dt==DataType.UWORD)
|
||||||
return translateExpressionInternal(NumericLiteral.fromBoolean(false, expr.position))
|
return translateExpressionInternal(PtNumber.fromBoolean(false, expr.position))
|
||||||
when(dt) {
|
when(dt) {
|
||||||
DataType.BYTE -> asmgen.out(" jsr prog8_lib.lesszero_b")
|
DataType.BYTE -> asmgen.out(" jsr prog8_lib.lesszero_b")
|
||||||
DataType.WORD -> asmgen.out(" jsr prog8_lib.lesszero_w")
|
DataType.WORD -> asmgen.out(" jsr prog8_lib.lesszero_w")
|
||||||
@ -671,7 +666,7 @@ internal class ExpressionsAsmGen(private val program: Program,
|
|||||||
}
|
}
|
||||||
">=" -> {
|
">=" -> {
|
||||||
if(dt==DataType.UBYTE || dt==DataType.UWORD)
|
if(dt==DataType.UBYTE || dt==DataType.UWORD)
|
||||||
return translateExpressionInternal(NumericLiteral.fromBoolean(true, expr.position))
|
return translateExpressionInternal(PtNumber.fromBoolean(true, expr.position))
|
||||||
when(dt) {
|
when(dt) {
|
||||||
DataType.BYTE -> asmgen.out(" jsr prog8_lib.greaterequalzero_sb")
|
DataType.BYTE -> asmgen.out(" jsr prog8_lib.greaterequalzero_sb")
|
||||||
DataType.WORD -> asmgen.out(" jsr prog8_lib.greaterequalzero_sw")
|
DataType.WORD -> asmgen.out(" jsr prog8_lib.greaterequalzero_sw")
|
||||||
@ -683,7 +678,7 @@ internal class ExpressionsAsmGen(private val program: Program,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun translateSquared(variable: IdentifierReference, dt: DataType) {
|
private fun translateSquared(variable: PtIdentifier, dt: DataType) {
|
||||||
val asmVar = asmgen.asmVariableName(variable)
|
val asmVar = asmgen.asmVariableName(variable)
|
||||||
when(dt) {
|
when(dt) {
|
||||||
DataType.BYTE, DataType.UBYTE -> {
|
DataType.BYTE, DataType.UBYTE -> {
|
||||||
@ -699,14 +694,12 @@ internal class ExpressionsAsmGen(private val program: Program,
|
|||||||
asmgen.out(" sta P8ESTACK_LO,x | tya | sta P8ESTACK_HI,x | dex")
|
asmgen.out(" sta P8ESTACK_LO,x | tya | sta P8ESTACK_HI,x | dex")
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun translateExpression(expr: PrefixExpression) {
|
private fun translateExpression(expr: PtPrefix) {
|
||||||
translateExpressionInternal(expr.expression)
|
translateExpressionInternal(expr.value)
|
||||||
val itype = expr.inferType(program)
|
|
||||||
val type = itype.getOrElse { throw AssemblyError("unknown dt") }
|
|
||||||
when(expr.operator) {
|
when(expr.operator) {
|
||||||
"+" -> {}
|
"+" -> {}
|
||||||
"-" -> {
|
"-" -> {
|
||||||
when(type) {
|
when(expr.type) {
|
||||||
in ByteDatatypes -> asmgen.out(" jsr prog8_lib.neg_b")
|
in ByteDatatypes -> asmgen.out(" jsr prog8_lib.neg_b")
|
||||||
in WordDatatypes -> asmgen.out(" jsr prog8_lib.neg_w")
|
in WordDatatypes -> asmgen.out(" jsr prog8_lib.neg_w")
|
||||||
DataType.FLOAT -> asmgen.out(" jsr floats.neg_f")
|
DataType.FLOAT -> asmgen.out(" jsr floats.neg_f")
|
||||||
@ -714,7 +707,7 @@ internal class ExpressionsAsmGen(private val program: Program,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
"~" -> {
|
"~" -> {
|
||||||
when(type) {
|
when(expr.type) {
|
||||||
in ByteDatatypes ->
|
in ByteDatatypes ->
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda P8ESTACK_LO+1,x
|
lda P8ESTACK_LO+1,x
|
||||||
@ -729,22 +722,18 @@ internal class ExpressionsAsmGen(private val program: Program,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun translateExpression(arrayExpr: ArrayIndexedExpression) {
|
private fun translateExpression(arrayExpr: PtArrayIndexer) {
|
||||||
val elementIDt = arrayExpr.inferType(program)
|
val elementDt = arrayExpr.type
|
||||||
if(!elementIDt.isKnown)
|
val arrayVarName = asmgen.asmVariableName(arrayExpr.variable)
|
||||||
throw AssemblyError("unknown dt")
|
|
||||||
val elementDt = elementIDt.getOr(DataType.UNDEFINED)
|
|
||||||
val arrayVarName = asmgen.asmVariableName(arrayExpr.arrayvar)
|
|
||||||
|
|
||||||
val arrayVarDecl = arrayExpr.arrayvar.targetVarDecl(program)!!
|
if(arrayExpr.variable.type==DataType.UWORD) {
|
||||||
if(arrayVarDecl.datatype==DataType.UWORD) {
|
|
||||||
// indexing a pointer var instead of a real array or string
|
// indexing a pointer var instead of a real array or string
|
||||||
if(elementDt !in ByteDatatypes)
|
if(elementDt !in ByteDatatypes)
|
||||||
throw AssemblyError("non-array var indexing requires bytes dt")
|
throw AssemblyError("non-array var indexing requires bytes dt")
|
||||||
if(arrayExpr.inferType(program) isnot DataType.UBYTE)
|
if(arrayExpr.index.type != DataType.UBYTE)
|
||||||
throw AssemblyError("non-array var indexing requires bytes index")
|
throw AssemblyError("non-array var indexing requires bytes index")
|
||||||
asmgen.loadScaledArrayIndexIntoRegister(arrayExpr, elementDt, CpuRegister.Y)
|
asmgen.loadScaledArrayIndexIntoRegister(arrayExpr, elementDt, CpuRegister.Y)
|
||||||
if(asmgen.isZpVar(arrayExpr.arrayvar)) {
|
if(asmgen.isZpVar(arrayExpr.variable)) {
|
||||||
asmgen.out(" lda ($arrayVarName),y")
|
asmgen.out(" lda ($arrayVarName),y")
|
||||||
} else {
|
} else {
|
||||||
asmgen.out(" lda $arrayVarName | sta P8ZP_SCRATCH_W1 | lda $arrayVarName+1 | sta P8ZP_SCRATCH_W1+1")
|
asmgen.out(" lda $arrayVarName | sta P8ZP_SCRATCH_W1 | lda $arrayVarName+1 | sta P8ZP_SCRATCH_W1+1")
|
||||||
@ -754,7 +743,7 @@ internal class ExpressionsAsmGen(private val program: Program,
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
val constIndexNum = arrayExpr.indexer.constIndex()
|
val constIndexNum = arrayExpr.index.asConstInteger()
|
||||||
if(constIndexNum!=null) {
|
if(constIndexNum!=null) {
|
||||||
val indexValue = constIndexNum * program.memsizer.memorySize(elementDt)
|
val indexValue = constIndexNum * program.memsizer.memorySize(elementDt)
|
||||||
when(elementDt) {
|
when(elementDt) {
|
||||||
@ -881,7 +870,7 @@ internal class ExpressionsAsmGen(private val program: Program,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun translateCompareStrings(s1: Expression, operator: String, s2: Expression) {
|
private fun translateCompareStrings(s1: PtExpression, operator: String, s2: PtExpression) {
|
||||||
asmgen.assignExpressionToVariable(s1, "prog8_lib.strcmp_expression._arg_s1", DataType.UWORD, null)
|
asmgen.assignExpressionToVariable(s1, "prog8_lib.strcmp_expression._arg_s1", DataType.UWORD, null)
|
||||||
asmgen.assignExpressionToVariable(s2, "prog8_lib.strcmp_expression._arg_s2", DataType.UWORD, null)
|
asmgen.assignExpressionToVariable(s2, "prog8_lib.strcmp_expression._arg_s2", DataType.UWORD, null)
|
||||||
asmgen.out(" jsr prog8_lib.strcmp_expression") // result of compare is in A
|
asmgen.out(" jsr prog8_lib.strcmp_expression") // result of compare is in A
|
||||||
|
60
codeGenCpu6502/src/prog8/codegen/cpu6502/Extensions.kt
Normal file
60
codeGenCpu6502/src/prog8/codegen/cpu6502/Extensions.kt
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
package prog8.codegen.cpu6502
|
||||||
|
|
||||||
|
import prog8.code.ast.IPtSubroutine
|
||||||
|
import prog8.code.ast.PtAsmSub
|
||||||
|
import prog8.code.ast.PtSub
|
||||||
|
import prog8.code.core.*
|
||||||
|
|
||||||
|
|
||||||
|
internal fun IPtSubroutine.regXasResult(): Boolean =
|
||||||
|
(this is PtAsmSub) && this.returns.any { it.first.registerOrPair in arrayOf(RegisterOrPair.X, RegisterOrPair.AX, RegisterOrPair.XY) }
|
||||||
|
|
||||||
|
internal fun IPtSubroutine.shouldSaveX(): Boolean =
|
||||||
|
this.regXasResult() || (this is PtAsmSub && (CpuRegister.X in this.clobbers || regXasParam()))
|
||||||
|
|
||||||
|
internal fun PtAsmSub.regXasParam(): Boolean =
|
||||||
|
parameters.any { it.first.registerOrPair in arrayOf(RegisterOrPair.X, RegisterOrPair.AX, RegisterOrPair.XY) }
|
||||||
|
|
||||||
|
internal class KeepAresult(val saveOnEntry: Boolean, val saveOnReturn: Boolean)
|
||||||
|
|
||||||
|
internal fun PtAsmSub.shouldKeepA(): KeepAresult {
|
||||||
|
// determine if A's value should be kept when preparing for calling the subroutine, and when returning from it
|
||||||
|
|
||||||
|
// it seems that we never have to save A when calling? will be loaded correctly after setup.
|
||||||
|
// but on return it depends on wether the routine returns something in A.
|
||||||
|
val saveAonReturn = returns.any { it.first.registerOrPair==RegisterOrPair.A || it.first.registerOrPair==RegisterOrPair.AY || it.first.registerOrPair==RegisterOrPair.AX }
|
||||||
|
return KeepAresult(false, saveAonReturn)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun IPtSubroutine.returnsWhatWhere(): List<Pair<RegisterOrStatusflag, DataType>> {
|
||||||
|
when(this) {
|
||||||
|
is PtAsmSub -> {
|
||||||
|
return returns
|
||||||
|
}
|
||||||
|
is PtSub -> {
|
||||||
|
// for non-asm subroutines, determine the return registers based on the type of the return value
|
||||||
|
return if(returntype==null)
|
||||||
|
emptyList()
|
||||||
|
else {
|
||||||
|
val register = when (returntype!!) {
|
||||||
|
in ByteDatatypes -> RegisterOrStatusflag(RegisterOrPair.A, null)
|
||||||
|
in WordDatatypes -> RegisterOrStatusflag(RegisterOrPair.AY, null)
|
||||||
|
DataType.FLOAT -> RegisterOrStatusflag(RegisterOrPair.FAC1, null)
|
||||||
|
else -> RegisterOrStatusflag(RegisterOrPair.AY, null)
|
||||||
|
}
|
||||||
|
listOf(Pair(register, returntype!!))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
internal fun PtSub.returnRegister(): RegisterOrStatusflag? {
|
||||||
|
return when(returntype) {
|
||||||
|
in ByteDatatypes -> RegisterOrStatusflag(RegisterOrPair.A, null)
|
||||||
|
in WordDatatypes -> RegisterOrStatusflag(RegisterOrPair.AY, null)
|
||||||
|
DataType.FLOAT -> RegisterOrStatusflag(RegisterOrPair.FAC1, null)
|
||||||
|
null -> null
|
||||||
|
else -> RegisterOrStatusflag(RegisterOrPair.AY, null)
|
||||||
|
}
|
||||||
|
}
|
@ -1,46 +1,43 @@
|
|||||||
package prog8.codegen.cpu6502
|
package prog8.codegen.cpu6502
|
||||||
|
|
||||||
import com.github.michaelbull.result.fold
|
import com.github.michaelbull.result.fold
|
||||||
import prog8.ast.Program
|
import prog8.code.ast.*
|
||||||
import prog8.ast.expressions.IdentifierReference
|
|
||||||
import prog8.ast.expressions.RangeExpression
|
|
||||||
import prog8.ast.statements.ForLoop
|
|
||||||
import prog8.code.core.*
|
import prog8.code.core.*
|
||||||
import kotlin.math.absoluteValue
|
import kotlin.math.absoluteValue
|
||||||
|
|
||||||
internal class ForLoopsAsmGen(private val program: Program, private val asmgen: AsmGen, private val zeropage: Zeropage) {
|
internal class ForLoopsAsmGen(private val program: PtProgram,
|
||||||
|
private val asmgen: AsmGen6502Internal,
|
||||||
|
private val zeropage: Zeropage) {
|
||||||
|
|
||||||
internal fun translate(stmt: ForLoop) {
|
internal fun translate(stmt: PtForLoop) {
|
||||||
val iterableDt = stmt.iterable.inferType(program)
|
val iterableDt = stmt.iterable.type
|
||||||
if(!iterableDt.isKnown)
|
|
||||||
throw AssemblyError("unknown dt")
|
|
||||||
when(stmt.iterable) {
|
when(stmt.iterable) {
|
||||||
is RangeExpression -> {
|
is PtRange -> {
|
||||||
val range = (stmt.iterable as RangeExpression).toConstantIntegerRange()
|
val range = (stmt.iterable as PtRange).toConstantIntegerRange()
|
||||||
if(range==null) {
|
if(range==null) {
|
||||||
translateForOverNonconstRange(stmt, iterableDt.getOrElse { throw AssemblyError("unknown dt") }, stmt.iterable as RangeExpression)
|
translateForOverNonconstRange(stmt, iterableDt, stmt.iterable as PtRange)
|
||||||
} else {
|
} else {
|
||||||
translateForOverConstRange(stmt, iterableDt.getOrElse { throw AssemblyError("unknown dt") }, range)
|
translateForOverConstRange(stmt, iterableDt, range)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
is IdentifierReference -> {
|
is PtIdentifier -> {
|
||||||
translateForOverIterableVar(stmt, iterableDt.getOrElse { throw AssemblyError("unknown dt") }, stmt.iterable as IdentifierReference)
|
translateForOverIterableVar(stmt, iterableDt, stmt.iterable as PtIdentifier)
|
||||||
}
|
}
|
||||||
else -> throw AssemblyError("can't iterate over ${stmt.iterable.javaClass} - should have been replaced by a variable")
|
else -> throw AssemblyError("can't iterate over ${stmt.iterable.javaClass} - should have been replaced by a variable")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun translateForOverNonconstRange(stmt: ForLoop, iterableDt: DataType, range: RangeExpression) {
|
private fun translateForOverNonconstRange(stmt: PtForLoop, iterableDt: DataType, range: PtRange) {
|
||||||
val loopLabel = program.makeLabel("for_loop")
|
val loopLabel = asmgen.makeLabel("for_loop")
|
||||||
val endLabel = program.makeLabel("for_end")
|
val endLabel = asmgen.makeLabel("for_end")
|
||||||
val modifiedLabel = program.makeLabel("for_modified")
|
val modifiedLabel = asmgen.makeLabel("for_modified")
|
||||||
val modifiedLabel2 = program.makeLabel("for_modifiedb")
|
val modifiedLabel2 = asmgen.makeLabel("for_modifiedb")
|
||||||
asmgen.loopEndLabels.push(endLabel)
|
asmgen.loopEndLabels.push(endLabel)
|
||||||
val stepsize=range.step.constValue(program)!!.number.toInt()
|
val stepsize=range.step.asConstInteger()!!
|
||||||
|
|
||||||
if(stepsize < -1) {
|
if(stepsize < -1) {
|
||||||
val limit = range.to.constValue(program)?.number
|
val limit = range.to.asConstInteger()
|
||||||
if(limit==0.0)
|
if(limit==0)
|
||||||
throw AssemblyError("for unsigned loop variable it's not possible to count down with step != -1 from a non-const value to exactly zero due to value wrapping")
|
throw AssemblyError("for unsigned loop variable it's not possible to count down with step != -1 from a non-const value to exactly zero due to value wrapping")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -52,11 +49,11 @@ internal class ForLoopsAsmGen(private val program: Program, private val asmgen:
|
|||||||
|
|
||||||
val incdec = if(stepsize==1) "inc" else "dec"
|
val incdec = if(stepsize==1) "inc" else "dec"
|
||||||
// loop over byte range via loopvar
|
// loop over byte range via loopvar
|
||||||
val varname = asmgen.asmVariableName(stmt.loopVar)
|
val varname = asmgen.asmVariableName(stmt.variable)
|
||||||
asmgen.assignExpressionToVariable(range.from, varname, ArrayToElementTypes.getValue(iterableDt), null)
|
asmgen.assignExpressionToVariable(range.from, varname, ArrayToElementTypes.getValue(iterableDt), null)
|
||||||
asmgen.assignExpressionToVariable(range.to, "$modifiedLabel+1", ArrayToElementTypes.getValue(iterableDt), null)
|
asmgen.assignExpressionToVariable(range.to, "$modifiedLabel+1", ArrayToElementTypes.getValue(iterableDt), null)
|
||||||
asmgen.out(loopLabel)
|
asmgen.out(loopLabel)
|
||||||
asmgen.translate(stmt.body)
|
asmgen.translate(stmt.statements)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda $varname
|
lda $varname
|
||||||
$modifiedLabel cmp #0 ; modified
|
$modifiedLabel cmp #0 ; modified
|
||||||
@ -70,11 +67,11 @@ $modifiedLabel cmp #0 ; modified
|
|||||||
// bytes, step >= 2 or <= -2
|
// bytes, step >= 2 or <= -2
|
||||||
|
|
||||||
// loop over byte range via loopvar
|
// loop over byte range via loopvar
|
||||||
val varname = asmgen.asmVariableName(stmt.loopVar)
|
val varname = asmgen.asmVariableName(stmt.variable)
|
||||||
asmgen.assignExpressionToVariable(range.from, varname, ArrayToElementTypes.getValue(iterableDt), null)
|
asmgen.assignExpressionToVariable(range.from, varname, ArrayToElementTypes.getValue(iterableDt), null)
|
||||||
asmgen.assignExpressionToVariable(range.to, "$modifiedLabel+1", ArrayToElementTypes.getValue(iterableDt), null)
|
asmgen.assignExpressionToVariable(range.to, "$modifiedLabel+1", ArrayToElementTypes.getValue(iterableDt), null)
|
||||||
asmgen.out(loopLabel)
|
asmgen.out(loopLabel)
|
||||||
asmgen.translate(stmt.body)
|
asmgen.translate(stmt.statements)
|
||||||
if(stepsize>0) {
|
if(stepsize>0) {
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda $varname
|
lda $varname
|
||||||
@ -102,14 +99,14 @@ $modifiedLabel cmp #0 ; modified
|
|||||||
// words, step 1 or -1
|
// words, step 1 or -1
|
||||||
|
|
||||||
stepsize == 1 || stepsize == -1 -> {
|
stepsize == 1 || stepsize == -1 -> {
|
||||||
val varname = asmgen.asmVariableName(stmt.loopVar)
|
val varname = asmgen.asmVariableName(stmt.variable)
|
||||||
assignLoopvar(stmt, range)
|
assignLoopvar(stmt, range)
|
||||||
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.AY)
|
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.AY)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
sty $modifiedLabel+1
|
sty $modifiedLabel+1
|
||||||
sta $modifiedLabel2+1
|
sta $modifiedLabel2+1
|
||||||
$loopLabel""")
|
$loopLabel""")
|
||||||
asmgen.translate(stmt.body)
|
asmgen.translate(stmt.statements)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda $varname+1
|
lda $varname+1
|
||||||
$modifiedLabel cmp #0 ; modified
|
$modifiedLabel cmp #0 ; modified
|
||||||
@ -136,14 +133,14 @@ $modifiedLabel2 cmp #0 ; modified
|
|||||||
stepsize > 0 -> {
|
stepsize > 0 -> {
|
||||||
|
|
||||||
// (u)words, step >= 2
|
// (u)words, step >= 2
|
||||||
val varname = asmgen.asmVariableName(stmt.loopVar)
|
val varname = asmgen.asmVariableName(stmt.variable)
|
||||||
assignLoopvar(stmt, range)
|
assignLoopvar(stmt, range)
|
||||||
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.AY)
|
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.AY)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
sty $modifiedLabel+1
|
sty $modifiedLabel+1
|
||||||
sta $modifiedLabel2+1
|
sta $modifiedLabel2+1
|
||||||
$loopLabel""")
|
$loopLabel""")
|
||||||
asmgen.translate(stmt.body)
|
asmgen.translate(stmt.statements)
|
||||||
|
|
||||||
if (iterableDt == DataType.ARRAY_UW) {
|
if (iterableDt == DataType.ARRAY_UW) {
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
@ -184,14 +181,14 @@ $endLabel""")
|
|||||||
else -> {
|
else -> {
|
||||||
|
|
||||||
// (u)words, step <= -2
|
// (u)words, step <= -2
|
||||||
val varname = asmgen.asmVariableName(stmt.loopVar)
|
val varname = asmgen.asmVariableName(stmt.variable)
|
||||||
assignLoopvar(stmt, range)
|
assignLoopvar(stmt, range)
|
||||||
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.AY)
|
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.AY)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
sty $modifiedLabel+1
|
sty $modifiedLabel+1
|
||||||
sta $modifiedLabel2+1
|
sta $modifiedLabel2+1
|
||||||
$loopLabel""")
|
$loopLabel""")
|
||||||
asmgen.translate(stmt.body)
|
asmgen.translate(stmt.statements)
|
||||||
|
|
||||||
if(iterableDt==DataType.ARRAY_UW) {
|
if(iterableDt==DataType.ARRAY_UW) {
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
@ -237,12 +234,18 @@ $endLabel""")
|
|||||||
asmgen.loopEndLabels.pop()
|
asmgen.loopEndLabels.pop()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun translateForOverIterableVar(stmt: ForLoop, iterableDt: DataType, ident: IdentifierReference) {
|
private fun translateForOverIterableVar(stmt: PtForLoop, iterableDt: DataType, ident: PtIdentifier) {
|
||||||
val loopLabel = program.makeLabel("for_loop")
|
val loopLabel = asmgen.makeLabel("for_loop")
|
||||||
val endLabel = program.makeLabel("for_end")
|
val endLabel = asmgen.makeLabel("for_end")
|
||||||
asmgen.loopEndLabels.push(endLabel)
|
asmgen.loopEndLabels.push(endLabel)
|
||||||
val iterableName = asmgen.asmVariableName(ident)
|
val iterableName = asmgen.asmVariableName(ident)
|
||||||
val decl = ident.targetVarDecl(program)!!
|
val symbol = asmgen.symbolTable.lookup(ident.name)
|
||||||
|
val decl = symbol!!.astNode as IPtVariable
|
||||||
|
val numElements = when(decl) {
|
||||||
|
is PtConstant -> throw AssemblyError("length of non-array requested")
|
||||||
|
is PtMemMapped -> decl.arraySize
|
||||||
|
is PtVariable -> decl.arraySize
|
||||||
|
}
|
||||||
when(iterableDt) {
|
when(iterableDt) {
|
||||||
DataType.STR -> {
|
DataType.STR -> {
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
@ -252,8 +255,8 @@ $endLabel""")
|
|||||||
sty $loopLabel+2
|
sty $loopLabel+2
|
||||||
$loopLabel lda ${65535.toHex()} ; modified
|
$loopLabel lda ${65535.toHex()} ; modified
|
||||||
beq $endLabel
|
beq $endLabel
|
||||||
sta ${asmgen.asmVariableName(stmt.loopVar)}""")
|
sta ${asmgen.asmVariableName(stmt.variable)}""")
|
||||||
asmgen.translate(stmt.body)
|
asmgen.translate(stmt.statements)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
inc $loopLabel+1
|
inc $loopLabel+1
|
||||||
bne $loopLabel
|
bne $loopLabel
|
||||||
@ -262,19 +265,18 @@ $loopLabel lda ${65535.toHex()} ; modified
|
|||||||
$endLabel""")
|
$endLabel""")
|
||||||
}
|
}
|
||||||
DataType.ARRAY_UB, DataType.ARRAY_B -> {
|
DataType.ARRAY_UB, DataType.ARRAY_B -> {
|
||||||
val length = decl.arraysize!!.constIndex()!!
|
val indexVar = asmgen.makeLabel("for_index")
|
||||||
val indexVar = program.makeLabel("for_index")
|
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
ldy #0
|
ldy #0
|
||||||
$loopLabel sty $indexVar
|
$loopLabel sty $indexVar
|
||||||
lda $iterableName,y
|
lda $iterableName,y
|
||||||
sta ${asmgen.asmVariableName(stmt.loopVar)}""")
|
sta ${asmgen.asmVariableName(stmt.variable)}""")
|
||||||
asmgen.translate(stmt.body)
|
asmgen.translate(stmt.statements)
|
||||||
if(length<=255) {
|
if(numElements!!<=255u) {
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
ldy $indexVar
|
ldy $indexVar
|
||||||
iny
|
iny
|
||||||
cpy #$length
|
cpy #$numElements
|
||||||
beq $endLabel
|
beq $endLabel
|
||||||
bne $loopLabel""")
|
bne $loopLabel""")
|
||||||
} else {
|
} else {
|
||||||
@ -285,9 +287,9 @@ $loopLabel sty $indexVar
|
|||||||
bne $loopLabel
|
bne $loopLabel
|
||||||
beq $endLabel""")
|
beq $endLabel""")
|
||||||
}
|
}
|
||||||
if(length>=16) {
|
if(numElements>=16u) {
|
||||||
// allocate index var on ZP if possible
|
// allocate index var on ZP if possible
|
||||||
val result = zeropage.allocate(listOf(indexVar), DataType.UBYTE, null, stmt.position, asmgen.errors)
|
val result = zeropage.allocate(indexVar, DataType.UBYTE, null, stmt.position, asmgen.errors)
|
||||||
result.fold(
|
result.fold(
|
||||||
success = { (address,_)-> asmgen.out("""$indexVar = $address ; auto zp UBYTE""") },
|
success = { (address,_)-> asmgen.out("""$indexVar = $address ; auto zp UBYTE""") },
|
||||||
failure = { asmgen.out("$indexVar .byte 0") }
|
failure = { asmgen.out("$indexVar .byte 0") }
|
||||||
@ -298,9 +300,9 @@ $loopLabel sty $indexVar
|
|||||||
asmgen.out(endLabel)
|
asmgen.out(endLabel)
|
||||||
}
|
}
|
||||||
DataType.ARRAY_W, DataType.ARRAY_UW -> {
|
DataType.ARRAY_W, DataType.ARRAY_UW -> {
|
||||||
val length = decl.arraysize!!.constIndex()!! * 2
|
val length = numElements!! * 2u
|
||||||
val indexVar = program.makeLabel("for_index")
|
val indexVar = asmgen.makeLabel("for_index")
|
||||||
val loopvarName = asmgen.asmVariableName(stmt.loopVar)
|
val loopvarName = asmgen.asmVariableName(stmt.variable)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
ldy #0
|
ldy #0
|
||||||
$loopLabel sty $indexVar
|
$loopLabel sty $indexVar
|
||||||
@ -308,8 +310,8 @@ $loopLabel sty $indexVar
|
|||||||
sta $loopvarName
|
sta $loopvarName
|
||||||
lda $iterableName+1,y
|
lda $iterableName+1,y
|
||||||
sta $loopvarName+1""")
|
sta $loopvarName+1""")
|
||||||
asmgen.translate(stmt.body)
|
asmgen.translate(stmt.statements)
|
||||||
if(length<=127) {
|
if(length<=127u) {
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
ldy $indexVar
|
ldy $indexVar
|
||||||
iny
|
iny
|
||||||
@ -326,9 +328,9 @@ $loopLabel sty $indexVar
|
|||||||
bne $loopLabel
|
bne $loopLabel
|
||||||
beq $endLabel""")
|
beq $endLabel""")
|
||||||
}
|
}
|
||||||
if(length>=16) {
|
if(length>=16u) {
|
||||||
// allocate index var on ZP if possible
|
// allocate index var on ZP if possible
|
||||||
val result = zeropage.allocate(listOf(indexVar), DataType.UBYTE, null, stmt.position, asmgen.errors)
|
val result = zeropage.allocate(indexVar, DataType.UBYTE, null, stmt.position, asmgen.errors)
|
||||||
result.fold(
|
result.fold(
|
||||||
success = { (address,_)-> asmgen.out("""$indexVar = $address ; auto zp UBYTE""") },
|
success = { (address,_)-> asmgen.out("""$indexVar = $address ; auto zp UBYTE""") },
|
||||||
failure = { asmgen.out("$indexVar .byte 0") }
|
failure = { asmgen.out("$indexVar .byte 0") }
|
||||||
@ -346,7 +348,7 @@ $loopLabel sty $indexVar
|
|||||||
asmgen.loopEndLabels.pop()
|
asmgen.loopEndLabels.pop()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun translateForOverConstRange(stmt: ForLoop, iterableDt: DataType, range: IntProgression) {
|
private fun translateForOverConstRange(stmt: PtForLoop, iterableDt: DataType, range: IntProgression) {
|
||||||
if (range.isEmpty() || range.step==0)
|
if (range.isEmpty() || range.step==0)
|
||||||
throw AssemblyError("empty range or step 0")
|
throw AssemblyError("empty range or step 0")
|
||||||
if(iterableDt==DataType.ARRAY_B || iterableDt==DataType.ARRAY_UB) {
|
if(iterableDt==DataType.ARRAY_B || iterableDt==DataType.ARRAY_UB) {
|
||||||
@ -359,18 +361,18 @@ $loopLabel sty $indexVar
|
|||||||
}
|
}
|
||||||
|
|
||||||
// not one of the easy cases, generate more complex code...
|
// not one of the easy cases, generate more complex code...
|
||||||
val loopLabel = program.makeLabel("for_loop")
|
val loopLabel = asmgen.makeLabel("for_loop")
|
||||||
val endLabel = program.makeLabel("for_end")
|
val endLabel = asmgen.makeLabel("for_end")
|
||||||
asmgen.loopEndLabels.push(endLabel)
|
asmgen.loopEndLabels.push(endLabel)
|
||||||
when(iterableDt) {
|
when(iterableDt) {
|
||||||
DataType.ARRAY_B, DataType.ARRAY_UB -> {
|
DataType.ARRAY_B, DataType.ARRAY_UB -> {
|
||||||
// loop over byte range via loopvar, step >= 2 or <= -2
|
// loop over byte range via loopvar, step >= 2 or <= -2
|
||||||
val varname = asmgen.asmVariableName(stmt.loopVar)
|
val varname = asmgen.asmVariableName(stmt.variable)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda #${range.first}
|
lda #${range.first}
|
||||||
sta $varname
|
sta $varname
|
||||||
$loopLabel""")
|
$loopLabel""")
|
||||||
asmgen.translate(stmt.body)
|
asmgen.translate(stmt.statements)
|
||||||
when (range.step) {
|
when (range.step) {
|
||||||
0, 1, -1 -> {
|
0, 1, -1 -> {
|
||||||
throw AssemblyError("step 0, 1 and -1 should have been handled specifically $stmt")
|
throw AssemblyError("step 0, 1 and -1 should have been handled specifically $stmt")
|
||||||
@ -430,7 +432,7 @@ $loopLabel""")
|
|||||||
}
|
}
|
||||||
DataType.ARRAY_W, DataType.ARRAY_UW -> {
|
DataType.ARRAY_W, DataType.ARRAY_UW -> {
|
||||||
// loop over word range via loopvar, step >= 2 or <= -2
|
// loop over word range via loopvar, step >= 2 or <= -2
|
||||||
val varname = asmgen.asmVariableName(stmt.loopVar)
|
val varname = asmgen.asmVariableName(stmt.variable)
|
||||||
when (range.step) {
|
when (range.step) {
|
||||||
0, 1, -1 -> {
|
0, 1, -1 -> {
|
||||||
throw AssemblyError("step 0, 1 and -1 should have been handled specifically $stmt")
|
throw AssemblyError("step 0, 1 and -1 should have been handled specifically $stmt")
|
||||||
@ -444,7 +446,7 @@ $loopLabel""")
|
|||||||
sta $varname
|
sta $varname
|
||||||
sty $varname+1
|
sty $varname+1
|
||||||
$loopLabel""")
|
$loopLabel""")
|
||||||
asmgen.translate(stmt.body)
|
asmgen.translate(stmt.statements)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda $varname
|
lda $varname
|
||||||
cmp #<${range.last}
|
cmp #<${range.last}
|
||||||
@ -470,16 +472,16 @@ $loopLabel""")
|
|||||||
asmgen.loopEndLabels.pop()
|
asmgen.loopEndLabels.pop()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun translateForSimpleByteRangeAsc(stmt: ForLoop, range: IntProgression) {
|
private fun translateForSimpleByteRangeAsc(stmt: PtForLoop, range: IntProgression) {
|
||||||
val loopLabel = program.makeLabel("for_loop")
|
val loopLabel = asmgen.makeLabel("for_loop")
|
||||||
val endLabel = program.makeLabel("for_end")
|
val endLabel = asmgen.makeLabel("for_end")
|
||||||
asmgen.loopEndLabels.push(endLabel)
|
asmgen.loopEndLabels.push(endLabel)
|
||||||
val varname = asmgen.asmVariableName(stmt.loopVar)
|
val varname = asmgen.asmVariableName(stmt.variable)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda #${range.first}
|
lda #${range.first}
|
||||||
sta $varname
|
sta $varname
|
||||||
$loopLabel""")
|
$loopLabel""")
|
||||||
asmgen.translate(stmt.body)
|
asmgen.translate(stmt.statements)
|
||||||
if (range.last == 255) {
|
if (range.last == 255) {
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
inc $varname
|
inc $varname
|
||||||
@ -496,16 +498,16 @@ $endLabel""")
|
|||||||
asmgen.loopEndLabels.pop()
|
asmgen.loopEndLabels.pop()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun translateForSimpleByteRangeDesc(stmt: ForLoop, range: IntProgression) {
|
private fun translateForSimpleByteRangeDesc(stmt: PtForLoop, range: IntProgression) {
|
||||||
val loopLabel = program.makeLabel("for_loop")
|
val loopLabel = asmgen.makeLabel("for_loop")
|
||||||
val endLabel = program.makeLabel("for_end")
|
val endLabel = asmgen.makeLabel("for_end")
|
||||||
asmgen.loopEndLabels.push(endLabel)
|
asmgen.loopEndLabels.push(endLabel)
|
||||||
val varname = asmgen.asmVariableName(stmt.loopVar)
|
val varname = asmgen.asmVariableName(stmt.variable)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda #${range.first}
|
lda #${range.first}
|
||||||
sta $varname
|
sta $varname
|
||||||
$loopLabel""")
|
$loopLabel""")
|
||||||
asmgen.translate(stmt.body)
|
asmgen.translate(stmt.statements)
|
||||||
when (range.last) {
|
when (range.last) {
|
||||||
0 -> {
|
0 -> {
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
@ -533,18 +535,18 @@ $endLabel""")
|
|||||||
asmgen.loopEndLabels.pop()
|
asmgen.loopEndLabels.pop()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun translateForSimpleWordRangeAsc(stmt: ForLoop, range: IntProgression) {
|
private fun translateForSimpleWordRangeAsc(stmt: PtForLoop, range: IntProgression) {
|
||||||
val loopLabel = program.makeLabel("for_loop")
|
val loopLabel = asmgen.makeLabel("for_loop")
|
||||||
val endLabel = program.makeLabel("for_end")
|
val endLabel = asmgen.makeLabel("for_end")
|
||||||
asmgen.loopEndLabels.push(endLabel)
|
asmgen.loopEndLabels.push(endLabel)
|
||||||
val varname = asmgen.asmVariableName(stmt.loopVar)
|
val varname = asmgen.asmVariableName(stmt.variable)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda #<${range.first}
|
lda #<${range.first}
|
||||||
ldy #>${range.first}
|
ldy #>${range.first}
|
||||||
sta $varname
|
sta $varname
|
||||||
sty $varname+1
|
sty $varname+1
|
||||||
$loopLabel""")
|
$loopLabel""")
|
||||||
asmgen.translate(stmt.body)
|
asmgen.translate(stmt.statements)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda $varname
|
lda $varname
|
||||||
cmp #<${range.last}
|
cmp #<${range.last}
|
||||||
@ -560,18 +562,18 @@ $loopLabel""")
|
|||||||
asmgen.loopEndLabels.pop()
|
asmgen.loopEndLabels.pop()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun translateForSimpleWordRangeDesc(stmt: ForLoop, range: IntProgression) {
|
private fun translateForSimpleWordRangeDesc(stmt: PtForLoop, range: IntProgression) {
|
||||||
val loopLabel = program.makeLabel("for_loop")
|
val loopLabel = asmgen.makeLabel("for_loop")
|
||||||
val endLabel = program.makeLabel("for_end")
|
val endLabel = asmgen.makeLabel("for_end")
|
||||||
asmgen.loopEndLabels.push(endLabel)
|
asmgen.loopEndLabels.push(endLabel)
|
||||||
val varname = asmgen.asmVariableName(stmt.loopVar)
|
val varname = asmgen.asmVariableName(stmt.variable)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda #<${range.first}
|
lda #<${range.first}
|
||||||
ldy #>${range.first}
|
ldy #>${range.first}
|
||||||
sta $varname
|
sta $varname
|
||||||
sty $varname+1
|
sty $varname+1
|
||||||
$loopLabel""")
|
$loopLabel""")
|
||||||
asmgen.translate(stmt.body)
|
asmgen.translate(stmt.statements)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda $varname
|
lda $varname
|
||||||
cmp #<${range.last}
|
cmp #<${range.last}
|
||||||
@ -588,10 +590,10 @@ $loopLabel""")
|
|||||||
asmgen.loopEndLabels.pop()
|
asmgen.loopEndLabels.pop()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun assignLoopvar(stmt: ForLoop, range: RangeExpression) =
|
private fun assignLoopvar(stmt: PtForLoop, range: PtRange) =
|
||||||
asmgen.assignExpressionToVariable(
|
asmgen.assignExpressionToVariable(
|
||||||
range.from,
|
range.from,
|
||||||
asmgen.asmVariableName(stmt.loopVar),
|
asmgen.asmVariableName(stmt.variable),
|
||||||
stmt.loopVarDt(program).getOrElse { throw AssemblyError("unknown dt") },
|
stmt.variable.type,
|
||||||
stmt.definingSubroutine)
|
stmt.definingISub())
|
||||||
}
|
}
|
||||||
|
@ -1,16 +1,6 @@
|
|||||||
package prog8.codegen.cpu6502
|
package prog8.codegen.cpu6502
|
||||||
|
|
||||||
import prog8.ast.IFunctionCall
|
import prog8.code.ast.*
|
||||||
import prog8.ast.Node
|
|
||||||
import prog8.ast.Program
|
|
||||||
import prog8.ast.expressions.AddressOf
|
|
||||||
import prog8.ast.expressions.Expression
|
|
||||||
import prog8.ast.expressions.IdentifierReference
|
|
||||||
import prog8.ast.expressions.NumericLiteral
|
|
||||||
import prog8.ast.statements.FunctionCallStatement
|
|
||||||
import prog8.ast.statements.InlineAssembly
|
|
||||||
import prog8.ast.statements.Subroutine
|
|
||||||
import prog8.ast.statements.SubroutineParameter
|
|
||||||
import prog8.code.core.*
|
import prog8.code.core.*
|
||||||
import prog8.codegen.cpu6502.assignment.AsmAssignSource
|
import prog8.codegen.cpu6502.assignment.AsmAssignSource
|
||||||
import prog8.codegen.cpu6502.assignment.AsmAssignTarget
|
import prog8.codegen.cpu6502.assignment.AsmAssignTarget
|
||||||
@ -18,51 +8,60 @@ import prog8.codegen.cpu6502.assignment.AsmAssignment
|
|||||||
import prog8.codegen.cpu6502.assignment.TargetStorageKind
|
import prog8.codegen.cpu6502.assignment.TargetStorageKind
|
||||||
|
|
||||||
|
|
||||||
internal class FunctionCallAsmGen(private val program: Program, private val asmgen: AsmGen) {
|
internal class FunctionCallAsmGen(private val program: PtProgram, private val asmgen: AsmGen6502Internal) {
|
||||||
|
|
||||||
internal fun translateFunctionCallStatement(stmt: FunctionCallStatement) {
|
internal fun translateFunctionCallStatement(stmt: PtFunctionCall) {
|
||||||
saveXbeforeCall(stmt)
|
saveXbeforeCall(stmt)
|
||||||
translateFunctionCall(stmt, false)
|
translateFunctionCall(stmt)
|
||||||
restoreXafterCall(stmt)
|
restoreXafterCall(stmt)
|
||||||
// just ignore any result values from the function call.
|
// just ignore any result values from the function call.
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun saveXbeforeCall(stmt: IFunctionCall) {
|
internal fun saveXbeforeCall(stmt: PtFunctionCall) {
|
||||||
val sub = stmt.target.targetSubroutine(program) ?: throw AssemblyError("undefined subroutine ${stmt.target}")
|
val symbol = asmgen.symbolTable.lookup(stmt.name)
|
||||||
|
val sub = symbol!!.astNode as IPtSubroutine
|
||||||
if(sub.shouldSaveX()) {
|
if(sub.shouldSaveX()) {
|
||||||
val regSaveOnStack = sub.asmAddress==null // rom-routines don't require registers to be saved on stack, normal subroutines do because they can contain nested calls
|
if(sub is PtAsmSub) {
|
||||||
if(regSaveOnStack)
|
val regSaveOnStack = sub.address == null // rom-routines don't require registers to be saved on stack, normal subroutines do because they can contain nested calls
|
||||||
asmgen.saveRegisterStack(CpuRegister.X, sub.shouldKeepA().saveOnEntry)
|
if (regSaveOnStack)
|
||||||
else
|
asmgen.saveRegisterStack(CpuRegister.X, sub.shouldKeepA().saveOnEntry)
|
||||||
asmgen.saveRegisterLocal(CpuRegister.X, (stmt as Node).definingSubroutine!!)
|
else
|
||||||
|
asmgen.saveRegisterLocal(CpuRegister.X, stmt.definingISub()!!)
|
||||||
|
} else
|
||||||
|
asmgen.saveRegisterLocal(CpuRegister.X, stmt.definingISub()!!)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun restoreXafterCall(stmt: IFunctionCall) {
|
internal fun restoreXafterCall(stmt: PtFunctionCall) {
|
||||||
val sub = stmt.target.targetSubroutine(program) ?: throw AssemblyError("undefined subroutine ${stmt.target}")
|
val symbol = asmgen.symbolTable.lookup(stmt.name)
|
||||||
|
val sub = symbol!!.astNode as IPtSubroutine
|
||||||
if(sub.shouldSaveX()) {
|
if(sub.shouldSaveX()) {
|
||||||
val regSaveOnStack = sub.asmAddress==null // rom-routines don't require registers to be saved on stack, normal subroutines do because they can contain nested calls
|
if(sub is PtAsmSub) {
|
||||||
if(regSaveOnStack)
|
val regSaveOnStack = sub.address == null // rom-routines don't require registers to be saved on stack, normal subroutines do because they can contain nested calls
|
||||||
asmgen.restoreRegisterStack(CpuRegister.X, sub.shouldKeepA().saveOnReturn)
|
if (regSaveOnStack)
|
||||||
else
|
asmgen.restoreRegisterStack(CpuRegister.X, sub.shouldKeepA().saveOnReturn)
|
||||||
|
else
|
||||||
|
asmgen.restoreRegisterLocal(CpuRegister.X)
|
||||||
|
} else
|
||||||
asmgen.restoreRegisterLocal(CpuRegister.X)
|
asmgen.restoreRegisterLocal(CpuRegister.X)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun optimizeIntArgsViaRegisters(sub: Subroutine) =
|
internal fun optimizeIntArgsViaRegisters(sub: PtSub) =
|
||||||
(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) { // TODO remove isExpression unused parameter
|
internal fun translateFunctionCall(call: PtFunctionCall) {
|
||||||
// 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!!
|
||||||
// (you can use subroutine.shouldSaveX() and saveX()/restoreX() routines as a help for this)
|
// (you can use subroutine.shouldSaveX() and saveX()/restoreX() routines as a help for this)
|
||||||
|
|
||||||
val sub = call.target.targetSubroutine(program) ?: throw AssemblyError("undefined subroutine ${call.target}")
|
val symbol = asmgen.symbolTable.lookup(call.name)
|
||||||
val subAsmName = asmgen.asmSymbolName(call.target)
|
val sub = symbol!!.astNode as IPtSubroutine
|
||||||
|
val subAsmName = asmgen.asmSymbolName(call.name)
|
||||||
|
|
||||||
if(sub.isAsmSubroutine) {
|
if(sub is PtAsmSub) {
|
||||||
argumentsViaRegisters(sub, call)
|
argumentsViaRegisters(sub, call)
|
||||||
if (sub.inline && asmgen.options.optimize) {
|
if (sub.inline && asmgen.options.optimize) {
|
||||||
// inline the subroutine.
|
// inline the subroutine.
|
||||||
@ -70,16 +69,13 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
|
|||||||
// NOTE: *if* there is a return statement, it will be the only one, and the very last statement of the subroutine
|
// NOTE: *if* there is a return statement, it will be the only one, and the very last statement of the subroutine
|
||||||
// (this condition has been enforced by an ast check earlier)
|
// (this condition has been enforced by an ast check earlier)
|
||||||
asmgen.out(" \t; inlined routine follows: ${sub.name}")
|
asmgen.out(" \t; inlined routine follows: ${sub.name}")
|
||||||
sub.statements.forEach { asmgen.translate(it as InlineAssembly) }
|
sub.children.forEach { asmgen.translate(it as PtInlineAssembly) }
|
||||||
asmgen.out(" \t; inlined routine end: ${sub.name}")
|
asmgen.out(" \t; inlined routine end: ${sub.name}")
|
||||||
} else {
|
} else {
|
||||||
asmgen.out(" jsr $subAsmName")
|
asmgen.out(" jsr $subAsmName")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else if(sub is PtSub) {
|
||||||
if(sub.inline)
|
|
||||||
throw AssemblyError("can only reliably inline asmsub routines at this time")
|
|
||||||
|
|
||||||
if(optimizeIntArgsViaRegisters(sub)) {
|
if(optimizeIntArgsViaRegisters(sub)) {
|
||||||
if(sub.parameters.size==1) {
|
if(sub.parameters.size==1) {
|
||||||
val register = if (sub.parameters[0].type in ByteDatatypes) RegisterOrPair.A else RegisterOrPair.AY
|
val register = if (sub.parameters[0].type in ByteDatatypes) RegisterOrPair.A else RegisterOrPair.AY
|
||||||
@ -100,84 +96,82 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
|
|||||||
}
|
}
|
||||||
asmgen.out(" jsr $subAsmName")
|
asmgen.out(" jsr $subAsmName")
|
||||||
}
|
}
|
||||||
|
else throw AssemblyError("invalid sub type")
|
||||||
|
|
||||||
// remember: dealing with the X register and/or dealing with return values is the responsibility of the caller
|
// remember: dealing with the X register and/or dealing with return values is the responsibility of the caller
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun argumentsViaRegisters(sub: Subroutine, call: IFunctionCall) {
|
private fun argumentsViaRegisters(sub: PtAsmSub, call: PtFunctionCall) {
|
||||||
if(sub.parameters.size==1) {
|
if(sub.parameters.size==1) {
|
||||||
argumentViaRegister(sub, IndexedValue(0, sub.parameters.single()), call.args[0])
|
argumentViaRegister(sub, IndexedValue(0, sub.parameters.single().second), call.args[0])
|
||||||
} else {
|
} else {
|
||||||
if(asmsub6502ArgsHaveRegisterClobberRisk(call.args, sub.asmParameterRegisters)) {
|
if(asmsub6502ArgsHaveRegisterClobberRisk(call.args, sub.parameters)) {
|
||||||
registerArgsViaCpuStackEvaluation(call, sub)
|
registerArgsViaCpuStackEvaluation(call, sub)
|
||||||
} else {
|
} else {
|
||||||
asmsub6502ArgsEvalOrder(sub).forEach {
|
asmsub6502ArgsEvalOrder(sub).forEach {
|
||||||
val param = sub.parameters[it]
|
val param = sub.parameters[it]
|
||||||
val arg = call.args[it]
|
val arg = call.args[it]
|
||||||
argumentViaRegister(sub, IndexedValue(it, param), arg)
|
argumentViaRegister(sub, IndexedValue(it, param.second), arg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun registerArgsViaCpuStackEvaluation(call: IFunctionCall, callee: Subroutine) {
|
private fun registerArgsViaCpuStackEvaluation(call: PtFunctionCall, callee: PtAsmSub) {
|
||||||
// this is called when one or more of the arguments are 'complex' and
|
// this is called when one or more of the arguments are 'complex' and
|
||||||
// cannot be assigned to a register easily or risk clobbering other registers.
|
// cannot be assigned to a register easily or risk clobbering other registers.
|
||||||
|
|
||||||
require(callee.isAsmSubroutine) { "register args only for asm subroutine ${callee.position}" }
|
|
||||||
if(callee.parameters.isEmpty())
|
if(callee.parameters.isEmpty())
|
||||||
return
|
return
|
||||||
|
|
||||||
// use the cpu hardware stack as intermediate storage for the arguments.
|
// use the cpu hardware stack as intermediate storage for the arguments.
|
||||||
val argOrder = asmsub6502ArgsEvalOrder(callee)
|
val argOrder = asmsub6502ArgsEvalOrder(callee)
|
||||||
argOrder.reversed().forEach {
|
argOrder.reversed().forEach {
|
||||||
asmgen.pushCpuStack(callee.parameters[it].type, call.args[it])
|
asmgen.pushCpuStack(callee.parameters[it].second.type, call.args[it])
|
||||||
}
|
}
|
||||||
argOrder.forEach {
|
argOrder.forEach {
|
||||||
val param = callee.parameters[it]
|
val param = callee.parameters[it]
|
||||||
val targetVar = callee.searchParameter(param.name)!!
|
asmgen.popCpuStack(callee, param.second, param.first)
|
||||||
asmgen.popCpuStack(param.type, targetVar, (call as Node).definingSubroutine)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun argumentViaVariable(sub: Subroutine, parameter: SubroutineParameter, value: Expression) {
|
private fun argumentViaVariable(sub: PtSub, parameter: PtSubroutineParameter, value: PtExpression) {
|
||||||
// pass parameter via a regular variable (not via registers)
|
// pass parameter via a regular variable (not via registers)
|
||||||
val valueIDt = value.inferType(program)
|
if(!isArgumentTypeCompatible(value.type, parameter.type))
|
||||||
val valueDt = valueIDt.getOrElse { throw AssemblyError("unknown dt") }
|
|
||||||
if(!isArgumentTypeCompatible(valueDt, parameter.type))
|
|
||||||
throw AssemblyError("argument type incompatible")
|
throw AssemblyError("argument type incompatible")
|
||||||
|
|
||||||
val varName = asmgen.asmVariableName(sub.scopedName + parameter.name)
|
val varName = asmgen.asmVariableName(sub.scopedName + "." + parameter.name)
|
||||||
asmgen.assignExpressionToVariable(value, varName, parameter.type, sub)
|
asmgen.assignExpressionToVariable(value, varName, parameter.type, sub)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun argumentViaRegister(sub: Subroutine, parameter: IndexedValue<SubroutineParameter>, value: Expression, registerOverride: RegisterOrPair? = null) {
|
private fun argumentViaRegister(sub: IPtSubroutine, parameter: IndexedValue<PtSubroutineParameter>, value: PtExpression, registerOverride: RegisterOrPair? = null) {
|
||||||
// pass argument via a register parameter
|
// pass argument via a register parameter
|
||||||
val valueIDt = value.inferType(program)
|
if(!isArgumentTypeCompatible(value.type, parameter.value.type))
|
||||||
val valueDt = valueIDt.getOrElse { throw AssemblyError("unknown dt") }
|
|
||||||
if(!isArgumentTypeCompatible(valueDt, parameter.value.type))
|
|
||||||
throw AssemblyError("argument type incompatible")
|
throw AssemblyError("argument type incompatible")
|
||||||
|
|
||||||
val paramRegister = if(registerOverride==null) sub.asmParameterRegisters[parameter.index] else RegisterOrStatusflag(registerOverride, null)
|
val paramRegister: RegisterOrStatusflag = when(sub) {
|
||||||
|
is PtAsmSub -> if(registerOverride==null) sub.parameters[parameter.index].first else RegisterOrStatusflag(registerOverride, null)
|
||||||
|
is PtSub -> RegisterOrStatusflag(registerOverride!!, null)
|
||||||
|
}
|
||||||
val statusflag = paramRegister.statusflag
|
val statusflag = paramRegister.statusflag
|
||||||
val register = paramRegister.registerOrPair
|
val register = paramRegister.registerOrPair
|
||||||
val requiredDt = parameter.value.type
|
val requiredDt = parameter.value.type
|
||||||
if(requiredDt!=valueDt) {
|
if(requiredDt!=value.type) {
|
||||||
if(valueDt largerThan requiredDt)
|
if(value.type largerThan requiredDt)
|
||||||
throw AssemblyError("can only convert byte values to word param types")
|
throw AssemblyError("can only convert byte values to word param types")
|
||||||
}
|
}
|
||||||
if (statusflag!=null) {
|
if (statusflag!=null) {
|
||||||
if(requiredDt!=valueDt)
|
if(requiredDt!=value.type)
|
||||||
throw AssemblyError("for statusflag, byte value is required")
|
throw AssemblyError("for statusflag, byte value is required")
|
||||||
if (statusflag == Statusflag.Pc) {
|
if (statusflag == Statusflag.Pc) {
|
||||||
// this param needs to be set last, right before the jsr
|
// this param needs to be set last, right before the jsr
|
||||||
// for now, this is already enforced on the subroutine definition by the Ast Checker
|
// for now, this is already enforced on the subroutine definition by the Ast Checker
|
||||||
when(value) {
|
when(value) {
|
||||||
is NumericLiteral -> {
|
is PtNumber -> {
|
||||||
val carrySet = value.number.toInt() != 0
|
val carrySet = value.number.toInt() != 0
|
||||||
asmgen.out(if(carrySet) " sec" else " clc")
|
asmgen.out(if(carrySet) " sec" else " clc")
|
||||||
}
|
}
|
||||||
is IdentifierReference -> {
|
is PtIdentifier -> {
|
||||||
val sourceName = asmgen.asmVariableName(value)
|
val sourceName = asmgen.asmVariableName(value)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
pha
|
pha
|
||||||
@ -202,10 +196,10 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
|
|||||||
else {
|
else {
|
||||||
// via register or register pair
|
// via register or register pair
|
||||||
register!!
|
register!!
|
||||||
if(requiredDt largerThan valueDt) {
|
if(requiredDt largerThan value.type) {
|
||||||
// we need to sign extend the source, do this via temporary word variable
|
// we need to sign extend the source, do this via temporary word variable
|
||||||
asmgen.assignExpressionToVariable(value, "P8ZP_SCRATCH_W1", DataType.UBYTE, sub)
|
asmgen.assignExpressionToVariable(value, "P8ZP_SCRATCH_W1", DataType.UBYTE, sub)
|
||||||
asmgen.signExtendVariableLsb("P8ZP_SCRATCH_W1", valueDt)
|
asmgen.signExtendVariableLsb("P8ZP_SCRATCH_W1", value.type)
|
||||||
asmgen.assignVariableToRegister("P8ZP_SCRATCH_W1", register)
|
asmgen.assignVariableToRegister("P8ZP_SCRATCH_W1", register)
|
||||||
} else {
|
} else {
|
||||||
val target: AsmAssignTarget =
|
val target: AsmAssignTarget =
|
||||||
@ -215,9 +209,11 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
|
|||||||
val signed = parameter.value.type == DataType.BYTE || parameter.value.type == DataType.WORD
|
val signed = parameter.value.type == DataType.BYTE || parameter.value.type == DataType.WORD
|
||||||
AsmAssignTarget.fromRegisters(register, signed, sub, asmgen)
|
AsmAssignTarget.fromRegisters(register, signed, sub, asmgen)
|
||||||
}
|
}
|
||||||
val src = if(valueDt in PassByReferenceDatatypes) {
|
val src = if(value.type in PassByReferenceDatatypes) {
|
||||||
if(value is IdentifierReference) {
|
if(value is PtIdentifier) {
|
||||||
val addr = AddressOf(value, Position.DUMMY)
|
val addr = PtAddressOf(Position.DUMMY)
|
||||||
|
addr.add(value)
|
||||||
|
addr.parent = sub as PtNode
|
||||||
AsmAssignSource.fromAstSource(addr, program, asmgen).adjustSignedUnsigned(target)
|
AsmAssignSource.fromAstSource(addr, program, asmgen).adjustSignedUnsigned(target)
|
||||||
} else {
|
} else {
|
||||||
AsmAssignSource.fromAstSource(value, program, asmgen).adjustSignedUnsigned(target)
|
AsmAssignSource.fromAstSource(value, program, asmgen).adjustSignedUnsigned(target)
|
||||||
@ -225,7 +221,7 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
|
|||||||
} else {
|
} else {
|
||||||
AsmAssignSource.fromAstSource(value, program, asmgen).adjustSignedUnsigned(target)
|
AsmAssignSource.fromAstSource(value, program, asmgen).adjustSignedUnsigned(target)
|
||||||
}
|
}
|
||||||
asmgen.translateNormalAssignment(AsmAssignment(src, target, false, program.memsizer, Position.DUMMY))
|
asmgen.translateNormalAssignment(AsmAssignment(src, target, program.memsizer, Position.DUMMY))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,23 +1,23 @@
|
|||||||
package prog8.codegen.cpu6502
|
package prog8.codegen.cpu6502
|
||||||
|
|
||||||
import prog8.ast.Program
|
import prog8.code.ast.PtIdentifier
|
||||||
import prog8.ast.expressions.IdentifierReference
|
import prog8.code.ast.PtNumber
|
||||||
import prog8.ast.expressions.NumericLiteral
|
import prog8.code.ast.PtPostIncrDecr
|
||||||
import prog8.ast.statements.PostIncrDecr
|
import prog8.code.ast.PtProgram
|
||||||
import prog8.code.core.*
|
import prog8.code.core.*
|
||||||
|
|
||||||
|
|
||||||
internal class PostIncrDecrAsmGen(private val program: Program, private val asmgen: AsmGen) {
|
internal class PostIncrDecrAsmGen(private val program: PtProgram, private val asmgen: AsmGen6502Internal) {
|
||||||
internal fun translate(stmt: PostIncrDecr) {
|
internal fun translate(stmt: PtPostIncrDecr) {
|
||||||
val incr = stmt.operator=="++"
|
val incr = stmt.operator=="++"
|
||||||
val targetIdent = stmt.target.identifier
|
val targetIdent = stmt.target.identifier
|
||||||
val targetMemory = stmt.target.memoryAddress
|
val targetMemory = stmt.target.memory
|
||||||
val targetArrayIdx = stmt.target.arrayindexed
|
val targetArrayIdx = stmt.target.array
|
||||||
val scope = stmt.definingSubroutine
|
val scope = stmt.definingISub()
|
||||||
when {
|
when {
|
||||||
targetIdent!=null -> {
|
targetIdent!=null -> {
|
||||||
val what = asmgen.asmVariableName(targetIdent)
|
val what = asmgen.asmVariableName(targetIdent)
|
||||||
when (stmt.target.inferType(program).getOr(DataType.UNDEFINED)) {
|
when (stmt.target.type) {
|
||||||
in ByteDatatypes -> asmgen.out(if (incr) " inc $what" else " dec $what")
|
in ByteDatatypes -> asmgen.out(if (incr) " inc $what" else " dec $what")
|
||||||
in WordDatatypes -> {
|
in WordDatatypes -> {
|
||||||
if(incr)
|
if(incr)
|
||||||
@ -38,12 +38,12 @@ internal class PostIncrDecrAsmGen(private val program: Program, private val asmg
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
targetMemory!=null -> {
|
targetMemory!=null -> {
|
||||||
when (val addressExpr = targetMemory.addressExpression) {
|
when (val addressExpr = targetMemory.address) {
|
||||||
is NumericLiteral -> {
|
is PtNumber -> {
|
||||||
val what = addressExpr.number.toHex()
|
val what = addressExpr.number.toHex()
|
||||||
asmgen.out(if(incr) " inc $what" else " dec $what")
|
asmgen.out(if(incr) " inc $what" else " dec $what")
|
||||||
}
|
}
|
||||||
is IdentifierReference -> {
|
is PtIdentifier -> {
|
||||||
val what = asmgen.asmVariableName(addressExpr)
|
val what = asmgen.asmVariableName(addressExpr)
|
||||||
asmgen.out(" lda $what | sta (+) +1 | lda $what+1 | sta (+) +2")
|
asmgen.out(" lda $what | sta (+) +1 | lda $what+1 | sta (+) +2")
|
||||||
if(incr)
|
if(incr)
|
||||||
@ -62,9 +62,9 @@ internal class PostIncrDecrAsmGen(private val program: Program, private val asmg
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
targetArrayIdx!=null -> {
|
targetArrayIdx!=null -> {
|
||||||
val asmArrayvarname = asmgen.asmVariableName(targetArrayIdx.arrayvar)
|
val asmArrayvarname = asmgen.asmVariableName(targetArrayIdx.variable)
|
||||||
val elementDt = targetArrayIdx.inferType(program).getOr(DataType.UNDEFINED)
|
val elementDt = targetArrayIdx.type
|
||||||
val constIndex = targetArrayIdx.indexer.constIndex()
|
val constIndex = targetArrayIdx.index.asConstInteger()
|
||||||
if(constIndex!=null) {
|
if(constIndex!=null) {
|
||||||
val indexValue = constIndex * program.memsizer.memorySize(elementDt)
|
val indexValue = constIndex * program.memsizer.memorySize(elementDt)
|
||||||
when(elementDt) {
|
when(elementDt) {
|
||||||
|
@ -1,12 +1,10 @@
|
|||||||
package prog8.codegen.cpu6502
|
package prog8.codegen.cpu6502
|
||||||
|
|
||||||
import prog8.ast.Program
|
|
||||||
import prog8.ast.statements.*
|
|
||||||
import prog8.code.*
|
import prog8.code.*
|
||||||
|
import prog8.code.ast.*
|
||||||
import prog8.code.core.*
|
import prog8.code.core.*
|
||||||
import prog8.codegen.cpu6502.assignment.AsmAssignTarget
|
import prog8.codegen.cpu6502.assignment.AsmAssignTarget
|
||||||
import prog8.codegen.cpu6502.assignment.TargetStorageKind
|
import prog8.codegen.cpu6502.assignment.TargetStorageKind
|
||||||
import prog8.compiler.CallGraph
|
|
||||||
import java.time.LocalDate
|
import java.time.LocalDate
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
import kotlin.math.absoluteValue
|
import kotlin.math.absoluteValue
|
||||||
@ -20,30 +18,26 @@ import kotlin.math.absoluteValue
|
|||||||
* - all variables (note: VarDecl ast nodes are *NOT* used anymore for this! now uses IVariablesAndConsts data tables!)
|
* - all variables (note: VarDecl ast nodes are *NOT* used anymore for this! now uses IVariablesAndConsts data tables!)
|
||||||
*/
|
*/
|
||||||
internal class ProgramAndVarsGen(
|
internal class ProgramAndVarsGen(
|
||||||
val program: Program,
|
val program: PtProgram,
|
||||||
val options: CompilationOptions,
|
val options: CompilationOptions,
|
||||||
val errors: IErrorReporter,
|
val errors: IErrorReporter,
|
||||||
private val symboltable: SymbolTable,
|
private val symboltable: SymbolTable,
|
||||||
private val functioncallAsmGen: FunctionCallAsmGen,
|
private val functioncallAsmGen: FunctionCallAsmGen,
|
||||||
private val asmgen: AsmGen,
|
private val asmgen: AsmGen6502Internal,
|
||||||
private val allocator: VariableAllocator,
|
private val allocator: VariableAllocator,
|
||||||
private val zeropage: Zeropage
|
private val zeropage: Zeropage
|
||||||
) {
|
) {
|
||||||
private val compTarget = options.compTarget
|
private val compTarget = options.compTarget
|
||||||
private val callGraph = CallGraph(program, true)
|
private val blockVariableInitializers = program.allBlocks().associateWith { it.children.filterIsInstance<PtAssignment>() }
|
||||||
private val blockVariableInitializers = program.allBlocks.associateWith { it.statements.filterIsInstance<Assignment>() }
|
|
||||||
|
|
||||||
internal fun generate() {
|
internal fun generate() {
|
||||||
val allInitializers = blockVariableInitializers.asSequence().flatMap { it.value }
|
|
||||||
require(allInitializers.all { it.origin==AssignmentOrigin.VARINIT }) {"all block-level assignments must be a variable initializer"}
|
|
||||||
|
|
||||||
header()
|
header()
|
||||||
val allBlocks = program.allBlocks
|
val allBlocks = program.allBlocks()
|
||||||
if(allBlocks.first().name != "main")
|
if(allBlocks.first().name != "main")
|
||||||
throw AssemblyError("first block should be 'main'")
|
throw AssemblyError("first block should be 'main'")
|
||||||
|
|
||||||
if(errors.noErrors()) {
|
if(errors.noErrors()) {
|
||||||
program.allBlocks.forEach { block2asm(it) }
|
program.allBlocks().forEach { block2asm(it) }
|
||||||
|
|
||||||
// the global list of all floating point constants for the whole program
|
// the global list of all floating point constants for the whole program
|
||||||
asmgen.out("; global float constants")
|
asmgen.out("; global float constants")
|
||||||
@ -71,7 +65,7 @@ internal class ProgramAndVarsGen(
|
|||||||
asmgen.out("; assembler syntax is for the 64tasm cross-assembler")
|
asmgen.out("; assembler syntax is for the 64tasm cross-assembler")
|
||||||
asmgen.out("; output options: output=${options.output} launcher=${options.launcher} zp=${options.zeropage}")
|
asmgen.out("; output options: output=${options.output} launcher=${options.launcher} zp=${options.zeropage}")
|
||||||
asmgen.out("")
|
asmgen.out("")
|
||||||
asmgen.out(".cpu '$cpu'\n.enc 'none'\n")
|
asmgen.out(".cpu '$cpu'\n.enc 'none'")
|
||||||
|
|
||||||
// the global prog8 variables needed
|
// the global prog8 variables needed
|
||||||
val zp = zeropage
|
val zp = zeropage
|
||||||
@ -94,13 +88,13 @@ internal class ProgramAndVarsGen(
|
|||||||
when(options.output) {
|
when(options.output) {
|
||||||
OutputType.RAW -> {
|
OutputType.RAW -> {
|
||||||
asmgen.out("; ---- raw assembler program ----")
|
asmgen.out("; ---- raw assembler program ----")
|
||||||
asmgen.out("* = ${options.loadAddress.toHex()}\n")
|
asmgen.out("* = ${options.loadAddress.toHex()}")
|
||||||
}
|
}
|
||||||
OutputType.PRG -> {
|
OutputType.PRG -> {
|
||||||
when(options.launcher) {
|
when(options.launcher) {
|
||||||
CbmPrgLauncherType.BASIC -> {
|
CbmPrgLauncherType.BASIC -> {
|
||||||
if (options.loadAddress != options.compTarget.machine.PROGRAM_LOAD_ADDRESS) {
|
if (options.loadAddress != options.compTarget.machine.PROGRAM_LOAD_ADDRESS) {
|
||||||
errors.err("BASIC output must have load address ${options.compTarget.machine.PROGRAM_LOAD_ADDRESS.toHex()}", program.toplevelModule.position)
|
errors.err("BASIC output must have load address ${options.compTarget.machine.PROGRAM_LOAD_ADDRESS.toHex()}", program.position)
|
||||||
}
|
}
|
||||||
asmgen.out("; ---- basic program with sys call ----")
|
asmgen.out("; ---- basic program with sys call ----")
|
||||||
asmgen.out("* = ${options.loadAddress.toHex()}")
|
asmgen.out("* = ${options.loadAddress.toHex()}")
|
||||||
@ -108,14 +102,14 @@ internal class ProgramAndVarsGen(
|
|||||||
asmgen.out(" .word (+), $year")
|
asmgen.out(" .word (+), $year")
|
||||||
asmgen.out(" .null $9e, format(' %d ', prog8_entrypoint), $3a, $8f, ' prog8'")
|
asmgen.out(" .null $9e, format(' %d ', prog8_entrypoint), $3a, $8f, ' prog8'")
|
||||||
asmgen.out("+\t.word 0")
|
asmgen.out("+\t.word 0")
|
||||||
asmgen.out("prog8_entrypoint\t; assembly code starts here\n")
|
asmgen.out("prog8_entrypoint\t; assembly code starts here")
|
||||||
if(!options.noSysInit)
|
if(!options.noSysInit)
|
||||||
asmgen.out(" jsr ${compTarget.name}.init_system")
|
asmgen.out(" jsr ${compTarget.name}.init_system")
|
||||||
asmgen.out(" jsr ${compTarget.name}.init_system_phase2")
|
asmgen.out(" jsr ${compTarget.name}.init_system_phase2")
|
||||||
}
|
}
|
||||||
CbmPrgLauncherType.NONE -> {
|
CbmPrgLauncherType.NONE -> {
|
||||||
asmgen.out("; ---- program without basic sys call ----")
|
asmgen.out("; ---- program without basic sys call ----")
|
||||||
asmgen.out("* = ${options.loadAddress.toHex()}\n")
|
asmgen.out("* = ${options.loadAddress.toHex()}")
|
||||||
if(!options.noSysInit)
|
if(!options.noSysInit)
|
||||||
asmgen.out(" jsr ${compTarget.name}.init_system")
|
asmgen.out(" jsr ${compTarget.name}.init_system")
|
||||||
asmgen.out(" jsr ${compTarget.name}.init_system_phase2")
|
asmgen.out(" jsr ${compTarget.name}.init_system_phase2")
|
||||||
@ -124,7 +118,7 @@ internal class ProgramAndVarsGen(
|
|||||||
}
|
}
|
||||||
OutputType.XEX -> {
|
OutputType.XEX -> {
|
||||||
asmgen.out("; ---- atari xex program ----")
|
asmgen.out("; ---- atari xex program ----")
|
||||||
asmgen.out("* = ${options.loadAddress.toHex()}\n")
|
asmgen.out("* = ${options.loadAddress.toHex()}")
|
||||||
if(!options.noSysInit)
|
if(!options.noSysInit)
|
||||||
asmgen.out(" jsr ${compTarget.name}.init_system")
|
asmgen.out(" jsr ${compTarget.name}.init_system")
|
||||||
asmgen.out(" jsr ${compTarget.name}.init_system_phase2")
|
asmgen.out(" jsr ${compTarget.name}.init_system_phase2")
|
||||||
@ -168,61 +162,88 @@ internal class ProgramAndVarsGen(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun memorySlabs() {
|
private fun memorySlabs() {
|
||||||
asmgen.out("; memory slabs")
|
if(symboltable.allMemorySlabs.isNotEmpty()) {
|
||||||
asmgen.out("prog8_slabs\t.block")
|
asmgen.out("; memory slabs\n .section slabs_BSS")
|
||||||
for(slab in symboltable.allMemorySlabs) {
|
asmgen.out("prog8_slabs\t.block")
|
||||||
if(slab.align>1u)
|
for (slab in symboltable.allMemorySlabs) {
|
||||||
asmgen.out("\t.align ${slab.align.toHex()}")
|
if (slab.align > 1u)
|
||||||
asmgen.out("${slab.name}\t.fill ${slab.size}")
|
asmgen.out("\t.align ${slab.align.toHex()}")
|
||||||
|
asmgen.out("${slab.name}\t.fill ${slab.size}")
|
||||||
|
}
|
||||||
|
asmgen.out("\t.bend\n .send slabs_BSS")
|
||||||
}
|
}
|
||||||
asmgen.out("\t.bend")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun footer() {
|
private fun footer() {
|
||||||
// program end
|
asmgen.out("; bss sections")
|
||||||
asmgen.out("prog8_program_end\t; end of program label for progend()")
|
if(options.varsHigh) {
|
||||||
|
if(options.compTarget.machine.BSSHIGHRAM_START == 0u || options.compTarget.machine.BSSHIGHRAM_END==0u) {
|
||||||
|
throw AssemblyError("current compilation target hasn't got the high ram area properly defined")
|
||||||
|
}
|
||||||
|
// BSS vars in high ram area, memory() slabs just concatenated at the end of the program.
|
||||||
|
if(symboltable.allMemorySlabs.isNotEmpty()) {
|
||||||
|
asmgen.out(" .dsection slabs_BSS")
|
||||||
|
}
|
||||||
|
asmgen.out("prog8_program_end\t; end of program label for progend()")
|
||||||
|
asmgen.out(" * = ${options.compTarget.machine.BSSHIGHRAM_START.toHex()}")
|
||||||
|
asmgen.out("prog8_bss_section_start")
|
||||||
|
asmgen.out(" .dsection BSS")
|
||||||
|
asmgen.out(" .cerror * >= ${options.compTarget.machine.BSSHIGHRAM_END.toHex()}, \"too many variables for BSS section\"")
|
||||||
|
asmgen.out("prog8_bss_section_size = * - prog8_bss_section_start")
|
||||||
|
} else {
|
||||||
|
// BSS vars followed by memory() slabs, concatenated at the end of the program.
|
||||||
|
asmgen.out("prog8_bss_section_start")
|
||||||
|
asmgen.out(" .dsection BSS")
|
||||||
|
asmgen.out("prog8_bss_section_size = * - prog8_bss_section_start")
|
||||||
|
if(symboltable.allMemorySlabs.isNotEmpty()) {
|
||||||
|
asmgen.out(" .dsection slabs_BSS")
|
||||||
|
}
|
||||||
|
asmgen.out("prog8_program_end\t; end of program label for progend()")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun block2asm(block: Block) {
|
private fun block2asm(block: PtBlock) {
|
||||||
asmgen.out("")
|
asmgen.out("")
|
||||||
asmgen.out("; ---- block: '${block.name}' ----")
|
asmgen.out("; ---- block: '${block.name}' ----")
|
||||||
if(block.address!=null)
|
if(block.address!=null)
|
||||||
asmgen.out("* = ${block.address!!.toHex()}")
|
asmgen.out("* = ${block.address!!.toHex()}")
|
||||||
else {
|
else {
|
||||||
if("align_word" in block.options())
|
if(block.alignment==PtBlock.BlockAlignment.WORD)
|
||||||
asmgen.out("\t.align 2")
|
asmgen.out("\t.align 2")
|
||||||
else if("align_page" in block.options())
|
else if(block.alignment==PtBlock.BlockAlignment.PAGE)
|
||||||
asmgen.out("\t.align $100")
|
asmgen.out("\t.align $100")
|
||||||
}
|
}
|
||||||
|
|
||||||
asmgen.out("${block.name}\t" + (if("force_output" in block.options()) ".block\n" else ".proc\n"))
|
asmgen.out("${block.name}\t" + (if(block.forceOutput) ".block" else ".proc"))
|
||||||
asmgen.outputSourceLine(block)
|
asmgen.outputSourceLine(block)
|
||||||
|
|
||||||
createBlockVariables(block)
|
createBlockVariables(block)
|
||||||
asmsubs2asm(block.statements)
|
asmsubs2asm(block.children)
|
||||||
|
|
||||||
asmgen.out("")
|
asmgen.out("")
|
||||||
|
|
||||||
val initializers = blockVariableInitializers.getValue(block)
|
val initializers = blockVariableInitializers.getValue(block)
|
||||||
val notInitializers = block.statements.filterNot { it in initializers }
|
val notInitializers = block.children.filterNot { it in initializers }
|
||||||
notInitializers.forEach { asmgen.translate(it) }
|
notInitializers.forEach { asmgen.translate(it) }
|
||||||
|
|
||||||
if(!options.dontReinitGlobals) {
|
// generate subroutine to initialize block-level (global) variables
|
||||||
// generate subroutine to initialize block-level (global) variables
|
if (initializers.isNotEmpty()) {
|
||||||
if (initializers.isNotEmpty()) {
|
asmgen.out("prog8_init_vars\t.block")
|
||||||
asmgen.out("prog8_init_vars\t.block\n")
|
initializers.forEach { assign ->
|
||||||
initializers.forEach { assign -> asmgen.translate(assign) }
|
if((assign.value as? PtNumber)?.number != 0.0 || allocator.isZpVar(assign.target.identifier!!.name))
|
||||||
asmgen.out(" rts\n .bend")
|
asmgen.translate(assign)
|
||||||
|
// the other variables that should be set to zero are done so as part of the BSS section.
|
||||||
}
|
}
|
||||||
|
asmgen.out(" rts\n .bend")
|
||||||
}
|
}
|
||||||
|
|
||||||
asmgen.out(if("force_output" in block.options()) "\n\t.bend\n" else "\n\t.pend\n")
|
asmgen.out(if(block.forceOutput) "\n\t.bend" else "\n\t.pend")
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getVars(scope: StNode): Map<String, StNode> =
|
private fun getVars(scope: StNode): Map<String, StNode> =
|
||||||
scope.children.filter { it.value.type in arrayOf(StNodeType.STATICVAR, StNodeType.CONSTANT, StNodeType.MEMVAR) }
|
scope.children.filter { it.value.type in arrayOf(StNodeType.STATICVAR, StNodeType.CONSTANT, StNodeType.MEMVAR) }
|
||||||
|
|
||||||
private fun createBlockVariables(block: Block) {
|
private fun createBlockVariables(block: PtBlock) {
|
||||||
val scope = symboltable.lookupUnscopedOrElse(block.name) { throw AssemblyError("lookup") }
|
val scope = symboltable.lookupUnscopedOrElse(block.name) { throw AssemblyError("lookup") }
|
||||||
require(scope.type==StNodeType.BLOCK)
|
require(scope.type==StNodeType.BLOCK)
|
||||||
val varsInBlock = getVars(scope)
|
val varsInBlock = getVars(scope)
|
||||||
@ -242,23 +263,15 @@ internal class ProgramAndVarsGen(
|
|||||||
|
|
||||||
// normal statically allocated variables
|
// normal statically allocated variables
|
||||||
val variables = varsInBlock
|
val variables = varsInBlock
|
||||||
.filter { it.value.type==StNodeType.STATICVAR && !allocator.isZpVar(it.value.scopedName.split('.')) }
|
.filter { it.value.type==StNodeType.STATICVAR && !allocator.isZpVar(it.value.scopedName) }
|
||||||
.map { it.value as StStaticVariable }
|
.map { it.value as StStaticVariable }
|
||||||
nonZpVariables2asm(variables)
|
nonZpVariables2asm(variables)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun translateSubroutine(sub: Subroutine) {
|
internal fun translateAsmSubroutine(sub: PtAsmSub) {
|
||||||
var onlyVariables = false
|
|
||||||
|
|
||||||
if(sub.inline) {
|
if(sub.inline) {
|
||||||
if(options.optimize) {
|
if(options.optimize) {
|
||||||
if(sub.isAsmSubroutine || callGraph.unused(sub))
|
return
|
||||||
return
|
|
||||||
|
|
||||||
// from an inlined subroutine only the local variables are generated,
|
|
||||||
// all other code statements are omitted in the subroutine itself
|
|
||||||
// (they've been inlined at the call site, remember?)
|
|
||||||
onlyVariables = true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -266,7 +279,7 @@ internal class ProgramAndVarsGen(
|
|||||||
|
|
||||||
val asmStartScope: String
|
val asmStartScope: String
|
||||||
val asmEndScope: String
|
val asmEndScope: String
|
||||||
if(sub.definingBlock.options().contains("force_output")) {
|
if(sub.definingBlock()!!.forceOutput) {
|
||||||
asmStartScope = ".block"
|
asmStartScope = ".block"
|
||||||
asmEndScope = ".bend"
|
asmEndScope = ".bend"
|
||||||
} else {
|
} else {
|
||||||
@ -274,105 +287,127 @@ internal class ProgramAndVarsGen(
|
|||||||
asmEndScope = ".pend"
|
asmEndScope = ".pend"
|
||||||
}
|
}
|
||||||
|
|
||||||
if(sub.isAsmSubroutine) {
|
if(sub.address!=null)
|
||||||
if(sub.asmAddress!=null)
|
return // already done at the memvars section
|
||||||
return // already done at the memvars section
|
|
||||||
|
|
||||||
// asmsub with most likely just an inline asm in it
|
// asmsub with most likely just an inline asm in it
|
||||||
asmgen.out("${sub.name}\t$asmStartScope")
|
asmgen.out("${sub.name}\t$asmStartScope")
|
||||||
sub.statements.forEach { asmgen.translate(it) }
|
sub.children.forEach { asmgen.translate(it) }
|
||||||
asmgen.out(" $asmEndScope\n")
|
asmgen.out(" $asmEndScope")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
internal fun translateSubroutine(sub: PtSub) {
|
||||||
|
asmgen.out("")
|
||||||
|
|
||||||
|
val asmStartScope: String
|
||||||
|
val asmEndScope: String
|
||||||
|
if(sub.definingBlock()!!.forceOutput) {
|
||||||
|
asmStartScope = ".block"
|
||||||
|
asmEndScope = ".bend"
|
||||||
} else {
|
} else {
|
||||||
// regular subroutine
|
asmStartScope = ".proc"
|
||||||
asmgen.out("${sub.name}\t$asmStartScope")
|
asmEndScope = ".pend"
|
||||||
|
|
||||||
val scope = symboltable.lookupOrElse(sub.scopedName.joinToString(".")) { throw AssemblyError("lookup") }
|
|
||||||
require(scope.type==StNodeType.SUBROUTINE)
|
|
||||||
val varsInSubroutine = getVars(scope)
|
|
||||||
|
|
||||||
// Zeropage Variables
|
|
||||||
val varnames = varsInSubroutine.filter { it.value.type==StNodeType.STATICVAR }.map { it.value.scopedName }.toSet()
|
|
||||||
zeropagevars2asm(varnames)
|
|
||||||
|
|
||||||
// MemDefs and Consts
|
|
||||||
val mvs = varsInSubroutine
|
|
||||||
.filter { it.value.type==StNodeType.MEMVAR }
|
|
||||||
.map { it.value as StMemVar }
|
|
||||||
val consts = varsInSubroutine
|
|
||||||
.filter { it.value.type==StNodeType.CONSTANT }
|
|
||||||
.map { it.value as StConstant }
|
|
||||||
memdefsAndConsts2asm(mvs, consts)
|
|
||||||
|
|
||||||
asmsubs2asm(sub.statements)
|
|
||||||
|
|
||||||
// the main.start subroutine is the program's entrypoint and should perform some initialization logic
|
|
||||||
if(sub.name=="start" && sub.definingBlock.name=="main")
|
|
||||||
entrypointInitialization()
|
|
||||||
|
|
||||||
if(functioncallAsmGen.optimizeIntArgsViaRegisters(sub)) {
|
|
||||||
asmgen.out("; simple int arg(s) passed via register(s)")
|
|
||||||
if(sub.parameters.size==1) {
|
|
||||||
val dt = sub.parameters[0].type
|
|
||||||
val target = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, dt, sub, variableAsmName = sub.parameters[0].name)
|
|
||||||
if(dt in ByteDatatypes)
|
|
||||||
asmgen.assignRegister(RegisterOrPair.A, target)
|
|
||||||
else
|
|
||||||
asmgen.assignRegister(RegisterOrPair.AY, target)
|
|
||||||
} else {
|
|
||||||
require(sub.parameters.size==2)
|
|
||||||
// 2 simple byte args, first in A, second in Y
|
|
||||||
val target1 = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, sub.parameters[0].type, sub, variableAsmName = sub.parameters[0].name)
|
|
||||||
val target2 = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, sub.parameters[1].type, sub, variableAsmName = sub.parameters[1].name)
|
|
||||||
asmgen.assignRegister(RegisterOrPair.A, target1)
|
|
||||||
asmgen.assignRegister(RegisterOrPair.Y, target2)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!onlyVariables) {
|
|
||||||
asmgen.out("; statements")
|
|
||||||
sub.statements.forEach { asmgen.translate(it) }
|
|
||||||
}
|
|
||||||
|
|
||||||
asmgen.out("; variables")
|
|
||||||
val asmGenInfo = asmgen.subroutineExtra(sub)
|
|
||||||
for((dt, name, addr) in asmGenInfo.extraVars) {
|
|
||||||
if(addr!=null)
|
|
||||||
asmgen.out("$name = $addr")
|
|
||||||
else when(dt) {
|
|
||||||
DataType.UBYTE -> asmgen.out("$name .byte 0")
|
|
||||||
DataType.UWORD -> asmgen.out("$name .word 0")
|
|
||||||
else -> throw AssemblyError("weird dt")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(asmGenInfo.usedRegsaveA) // will probably never occur
|
|
||||||
asmgen.out("prog8_regsaveA .byte 0")
|
|
||||||
if(asmGenInfo.usedRegsaveX)
|
|
||||||
asmgen.out("prog8_regsaveX .byte 0")
|
|
||||||
if(asmGenInfo.usedRegsaveY)
|
|
||||||
asmgen.out("prog8_regsaveY .byte 0")
|
|
||||||
if(asmGenInfo.usedFloatEvalResultVar1)
|
|
||||||
asmgen.out("$subroutineFloatEvalResultVar1 .byte 0,0,0,0,0")
|
|
||||||
if(asmGenInfo.usedFloatEvalResultVar2)
|
|
||||||
asmgen.out("$subroutineFloatEvalResultVar2 .byte 0,0,0,0,0")
|
|
||||||
|
|
||||||
// normal statically allocated variables
|
|
||||||
val variables = varsInSubroutine
|
|
||||||
.filter { it.value.type==StNodeType.STATICVAR && !allocator.isZpVar(it.value.scopedName.split('.')) }
|
|
||||||
.map { it.value as StStaticVariable }
|
|
||||||
nonZpVariables2asm(variables)
|
|
||||||
|
|
||||||
asmgen.out(" $asmEndScope\n")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
asmgen.out("${sub.name}\t$asmStartScope")
|
||||||
|
|
||||||
|
val scope = symboltable.lookupOrElse(sub.scopedName) { throw AssemblyError("lookup") }
|
||||||
|
require(scope.type==StNodeType.SUBROUTINE)
|
||||||
|
val varsInSubroutine = getVars(scope)
|
||||||
|
|
||||||
|
// Zeropage Variables
|
||||||
|
val varnames = varsInSubroutine.filter { it.value.type==StNodeType.STATICVAR }.map { it.value.scopedName }.toSet()
|
||||||
|
zeropagevars2asm(varnames)
|
||||||
|
|
||||||
|
// MemDefs and Consts
|
||||||
|
val mvs = varsInSubroutine
|
||||||
|
.filter { it.value.type==StNodeType.MEMVAR }
|
||||||
|
.map { it.value as StMemVar }
|
||||||
|
val consts = varsInSubroutine
|
||||||
|
.filter { it.value.type==StNodeType.CONSTANT }
|
||||||
|
.map { it.value as StConstant }
|
||||||
|
memdefsAndConsts2asm(mvs, consts)
|
||||||
|
|
||||||
|
asmsubs2asm(sub.children)
|
||||||
|
|
||||||
|
// the main.start subroutine is the program's entrypoint and should perform some initialization logic
|
||||||
|
if(sub.name=="start" && sub.definingBlock()!!.name=="main")
|
||||||
|
entrypointInitialization()
|
||||||
|
|
||||||
|
if(functioncallAsmGen.optimizeIntArgsViaRegisters(sub)) {
|
||||||
|
asmgen.out("; simple int arg(s) passed via register(s)")
|
||||||
|
if(sub.parameters.size==1) {
|
||||||
|
val dt = sub.parameters[0].type
|
||||||
|
val target = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, dt, sub, variableAsmName = sub.parameters[0].name)
|
||||||
|
if(dt in ByteDatatypes)
|
||||||
|
asmgen.assignRegister(RegisterOrPair.A, target)
|
||||||
|
else
|
||||||
|
asmgen.assignRegister(RegisterOrPair.AY, target)
|
||||||
|
} else {
|
||||||
|
require(sub.parameters.size==2)
|
||||||
|
// 2 simple byte args, first in A, second in Y
|
||||||
|
val target1 = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, sub.parameters[0].type, sub, variableAsmName = sub.parameters[0].name)
|
||||||
|
val target2 = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, sub.parameters[1].type, sub, variableAsmName = sub.parameters[1].name)
|
||||||
|
asmgen.assignRegister(RegisterOrPair.A, target1)
|
||||||
|
asmgen.assignRegister(RegisterOrPair.Y, target2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
asmgen.out("; statements")
|
||||||
|
sub.children.forEach { asmgen.translate(it) }
|
||||||
|
|
||||||
|
asmgen.out("; variables")
|
||||||
|
val asmGenInfo = asmgen.subroutineExtra(sub)
|
||||||
|
for((dt, name, addr) in asmGenInfo.extraVars) {
|
||||||
|
if(addr!=null)
|
||||||
|
asmgen.out("$name = $addr")
|
||||||
|
else when(dt) {
|
||||||
|
DataType.UBYTE -> asmgen.out("$name .byte 0")
|
||||||
|
DataType.UWORD -> asmgen.out("$name .word 0")
|
||||||
|
else -> throw AssemblyError("weird dt")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(asmGenInfo.usedRegsaveA) // will probably never occur
|
||||||
|
asmgen.out("prog8_regsaveA .byte 0")
|
||||||
|
if(asmGenInfo.usedRegsaveX)
|
||||||
|
asmgen.out("prog8_regsaveX .byte 0")
|
||||||
|
if(asmGenInfo.usedRegsaveY)
|
||||||
|
asmgen.out("prog8_regsaveY .byte 0")
|
||||||
|
if(asmGenInfo.usedFloatEvalResultVar1)
|
||||||
|
asmgen.out("$subroutineFloatEvalResultVar1 .byte 0,0,0,0,0")
|
||||||
|
if(asmGenInfo.usedFloatEvalResultVar2)
|
||||||
|
asmgen.out("$subroutineFloatEvalResultVar2 .byte 0,0,0,0,0")
|
||||||
|
|
||||||
|
// normal statically allocated variables
|
||||||
|
val variables = varsInSubroutine
|
||||||
|
.filter { it.value.type==StNodeType.STATICVAR && !allocator.isZpVar(it.value.scopedName) }
|
||||||
|
.map { it.value as StStaticVariable }
|
||||||
|
nonZpVariables2asm(variables)
|
||||||
|
|
||||||
|
asmgen.out(" $asmEndScope")
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun entrypointInitialization() {
|
private fun entrypointInitialization() {
|
||||||
asmgen.out("; program startup initialization")
|
asmgen.out("; program startup initialization")
|
||||||
asmgen.out(" cld")
|
asmgen.out(" cld | tsx | stx prog8_lib.orig_stackpointer ; required for sys.exit()")
|
||||||
if(!options.dontReinitGlobals) {
|
// set full BSS area to zero
|
||||||
blockVariableInitializers.forEach {
|
asmgen.out("""
|
||||||
if (it.value.isNotEmpty())
|
.if prog8_bss_section_size>0
|
||||||
asmgen.out(" jsr ${it.key.name}.prog8_init_vars")
|
; reset all variables in BSS section to zero
|
||||||
}
|
lda #<prog8_bss_section_start
|
||||||
|
ldy #>prog8_bss_section_start
|
||||||
|
sta P8ZP_SCRATCH_W1
|
||||||
|
sty P8ZP_SCRATCH_W1+1
|
||||||
|
ldx #<prog8_bss_section_size
|
||||||
|
ldy #>prog8_bss_section_size
|
||||||
|
lda #0
|
||||||
|
jsr prog8_lib.memset
|
||||||
|
.endif""")
|
||||||
|
|
||||||
|
blockVariableInitializers.forEach {
|
||||||
|
if (it.value.isNotEmpty())
|
||||||
|
asmgen.out(" jsr ${it.key.name}.prog8_init_vars")
|
||||||
}
|
}
|
||||||
|
|
||||||
// string and array variables in zeropage that have initializer value, should be initialized
|
// string and array variables in zeropage that have initializer value, should be initialized
|
||||||
@ -420,9 +455,8 @@ internal class ProgramAndVarsGen(
|
|||||||
arrayVariable2asm(varname, it.alloc.dt, it.value, null)
|
arrayVariable2asm(varname, it.alloc.dt, it.value, null)
|
||||||
}
|
}
|
||||||
|
|
||||||
asmgen.out("""+ tsx
|
asmgen.out("""+
|
||||||
stx prog8_lib.orig_stackpointer ; required for sys.exit()
|
ldx #127 ; init estack ptr (half page)
|
||||||
ldx #255 ; init estack ptr
|
|
||||||
clv
|
clv
|
||||||
clc""")
|
clc""")
|
||||||
}
|
}
|
||||||
@ -443,7 +477,7 @@ internal class ProgramAndVarsGen(
|
|||||||
val result = mutableListOf<ZpStringWithInitial>()
|
val result = mutableListOf<ZpStringWithInitial>()
|
||||||
val vars = allocator.zeropageVars.filter { it.value.dt==DataType.STR }
|
val vars = allocator.zeropageVars.filter { it.value.dt==DataType.STR }
|
||||||
for (variable in vars) {
|
for (variable in vars) {
|
||||||
val scopedName = variable.key.joinToString(".")
|
val scopedName = variable.key
|
||||||
val svar = symboltable.flat.getValue(scopedName) as StStaticVariable
|
val svar = symboltable.flat.getValue(scopedName) as StStaticVariable
|
||||||
if(svar.onetimeInitializationStringValue!=null)
|
if(svar.onetimeInitializationStringValue!=null)
|
||||||
result.add(ZpStringWithInitial(scopedName, variable.value, svar.onetimeInitializationStringValue!!))
|
result.add(ZpStringWithInitial(scopedName, variable.value, svar.onetimeInitializationStringValue!!))
|
||||||
@ -455,7 +489,7 @@ internal class ProgramAndVarsGen(
|
|||||||
val result = mutableListOf<ZpArrayWithInitial>()
|
val result = mutableListOf<ZpArrayWithInitial>()
|
||||||
val vars = allocator.zeropageVars.filter { it.value.dt in ArrayDatatypes }
|
val vars = allocator.zeropageVars.filter { it.value.dt in ArrayDatatypes }
|
||||||
for (variable in vars) {
|
for (variable in vars) {
|
||||||
val scopedName = variable.key.joinToString(".")
|
val scopedName = variable.key
|
||||||
val svar = symboltable.flat.getValue(scopedName) as StStaticVariable
|
val svar = symboltable.flat.getValue(scopedName) as StStaticVariable
|
||||||
if(svar.onetimeInitializationArrayValue!=null)
|
if(svar.onetimeInitializationArrayValue!=null)
|
||||||
result.add(ZpArrayWithInitial(scopedName, variable.value, svar.onetimeInitializationArrayValue!!))
|
result.add(ZpArrayWithInitial(scopedName, variable.value, svar.onetimeInitializationArrayValue!!))
|
||||||
@ -464,29 +498,60 @@ internal class ProgramAndVarsGen(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun zeropagevars2asm(varNames: Set<String>) {
|
private fun zeropagevars2asm(varNames: Set<String>) {
|
||||||
val namesLists = varNames.map { it.split('.') }.toSet()
|
val zpVariables = allocator.zeropageVars.filter { it.key in varNames }.toList().sortedBy { it.second.address }
|
||||||
val zpVariables = allocator.zeropageVars.filter { it.key in namesLists }.toList().sortedBy { it.second.address }
|
|
||||||
for ((scopedName, zpvar) in zpVariables) {
|
for ((scopedName, zpvar) in zpVariables) {
|
||||||
if (scopedName.size == 2 && scopedName[0] == "cx16" && scopedName[1][0] == 'r' && scopedName[1][1].isDigit())
|
if (scopedName.startsWith("cx16.r"))
|
||||||
continue // The 16 virtual registers of the cx16 are not actual variables in zp, they're memory mapped
|
continue // The 16 virtual registers of the cx16 are not actual variables in zp, they're memory mapped
|
||||||
asmgen.out("${scopedName.last()} \t= ${zpvar.address} \t; zp ${zpvar.dt}")
|
asmgen.out("${scopedName.substringAfterLast('.')} \t= ${zpvar.address} \t; zp ${zpvar.dt}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun nonZpVariables2asm(variables: List<StStaticVariable>) {
|
private fun nonZpVariables2asm(variables: List<StStaticVariable>) {
|
||||||
asmgen.out("")
|
asmgen.out("")
|
||||||
asmgen.out("; non-zeropage variables")
|
val (varsNoInit, varsWithInit) = variables.partition { it.uninitialized }
|
||||||
val (stringvars, othervars) = variables.sortedBy { it.name }.partition { it.dt==DataType.STR }
|
if(varsNoInit.isNotEmpty()) {
|
||||||
stringvars.forEach {
|
asmgen.out("; non-zeropage variables without initialization value")
|
||||||
outputStringvar(it.name, it.onetimeInitializationStringValue!!.second, it.onetimeInitializationStringValue!!.first)
|
asmgen.out(" .section BSS")
|
||||||
|
varsNoInit.sortedWith(compareBy<StStaticVariable> { it.name }.thenBy { it.dt }).forEach {
|
||||||
|
uninitializedVariable2asm(it)
|
||||||
|
}
|
||||||
|
asmgen.out(" .send BSS")
|
||||||
}
|
}
|
||||||
othervars.sortedBy { it.type }.forEach {
|
|
||||||
staticVariable2asm(it)
|
if(varsWithInit.isNotEmpty()) {
|
||||||
|
asmgen.out("; non-zeropage variables")
|
||||||
|
val (stringvars, othervars) = varsWithInit.sortedBy { it.name }.partition { it.dt == DataType.STR }
|
||||||
|
stringvars.forEach {
|
||||||
|
outputStringvar(
|
||||||
|
it.name,
|
||||||
|
it.onetimeInitializationStringValue!!.second,
|
||||||
|
it.onetimeInitializationStringValue!!.first
|
||||||
|
)
|
||||||
|
}
|
||||||
|
othervars.sortedBy { it.type }.forEach {
|
||||||
|
staticVariable2asm(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun uninitializedVariable2asm(variable: StStaticVariable) {
|
||||||
|
when (variable.dt) {
|
||||||
|
DataType.UBYTE -> asmgen.out("${variable.name}\t.byte ?")
|
||||||
|
DataType.BYTE -> asmgen.out("${variable.name}\t.char ?")
|
||||||
|
DataType.UWORD -> asmgen.out("${variable.name}\t.word ?")
|
||||||
|
DataType.WORD -> asmgen.out("${variable.name}\t.sint ?")
|
||||||
|
DataType.FLOAT -> asmgen.out("${variable.name}\t.fill ${compTarget.machine.FLOAT_MEM_SIZE}")
|
||||||
|
in ArrayDatatypes -> {
|
||||||
|
val numbytes = compTarget.memorySize(variable.dt, variable.length!!)
|
||||||
|
asmgen.out("${variable.name}\t.fill $numbytes")
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
throw AssemblyError("weird dt")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun staticVariable2asm(variable: StStaticVariable) {
|
private fun staticVariable2asm(variable: StStaticVariable) {
|
||||||
val name = variable.name
|
|
||||||
val initialValue: Number =
|
val initialValue: Number =
|
||||||
if(variable.onetimeInitializationNumericValue!=null) {
|
if(variable.onetimeInitializationNumericValue!=null) {
|
||||||
if(variable.dt== DataType.FLOAT)
|
if(variable.dt== DataType.FLOAT)
|
||||||
@ -496,22 +561,22 @@ internal class ProgramAndVarsGen(
|
|||||||
} else 0
|
} else 0
|
||||||
|
|
||||||
when (variable.dt) {
|
when (variable.dt) {
|
||||||
DataType.UBYTE -> asmgen.out("$name\t.byte ${initialValue.toHex()}")
|
DataType.UBYTE -> asmgen.out("${variable.name}\t.byte ${initialValue.toHex()}")
|
||||||
DataType.BYTE -> asmgen.out("$name\t.char $initialValue")
|
DataType.BYTE -> asmgen.out("${variable.name}\t.char $initialValue")
|
||||||
DataType.UWORD -> asmgen.out("$name\t.word ${initialValue.toHex()}")
|
DataType.UWORD -> asmgen.out("${variable.name}\t.word ${initialValue.toHex()}")
|
||||||
DataType.WORD -> asmgen.out("$name\t.sint $initialValue")
|
DataType.WORD -> asmgen.out("${variable.name}\t.sint $initialValue")
|
||||||
DataType.FLOAT -> {
|
DataType.FLOAT -> {
|
||||||
if(initialValue==0) {
|
if(initialValue==0) {
|
||||||
asmgen.out("$name\t.byte 0,0,0,0,0 ; float")
|
asmgen.out("${variable.name}\t.byte 0,0,0,0,0 ; float")
|
||||||
} else {
|
} else {
|
||||||
val floatFill = compTarget.machine.getFloatAsmBytes(initialValue)
|
val floatFill = compTarget.machine.getFloatAsmBytes(initialValue)
|
||||||
asmgen.out("$name\t.byte $floatFill ; float $initialValue")
|
asmgen.out("${variable.name}\t.byte $floatFill ; float $initialValue")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DataType.STR -> {
|
DataType.STR -> {
|
||||||
throw AssemblyError("all string vars should have been interned into prog")
|
throw AssemblyError("all string vars should have been interned into prog")
|
||||||
}
|
}
|
||||||
in ArrayDatatypes -> arrayVariable2asm(name, variable.dt, variable.onetimeInitializationArrayValue, variable.length)
|
in ArrayDatatypes -> arrayVariable2asm(variable.name, variable.dt, variable.onetimeInitializationArrayValue, variable.length)
|
||||||
else -> {
|
else -> {
|
||||||
throw AssemblyError("weird dt")
|
throw AssemblyError("weird dt")
|
||||||
}
|
}
|
||||||
@ -593,12 +658,12 @@ internal class ProgramAndVarsGen(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun asmsubs2asm(statements: List<Statement>) {
|
private fun asmsubs2asm(statements: List<PtNode>) {
|
||||||
statements
|
statements
|
||||||
.filter { it is Subroutine && it.isAsmSubroutine && it.asmAddress!=null }
|
.filter { it is PtAsmSub && it.address!=null }
|
||||||
.forEach { asmsub ->
|
.forEach { asmsub ->
|
||||||
asmsub as Subroutine
|
asmsub as PtAsmSub
|
||||||
asmgen.out(" ${asmsub.name} = ${asmsub.asmAddress!!.toHex()}")
|
asmgen.out(" ${asmsub.name} = ${asmsub.address!!.toHex()}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,13 +16,14 @@ internal class VariableAllocator(private val symboltable: SymbolTable,
|
|||||||
|
|
||||||
private val zeropage = options.compTarget.machine.zeropage
|
private val zeropage = options.compTarget.machine.zeropage
|
||||||
internal val globalFloatConsts = mutableMapOf<Double, String>() // all float values in the entire program (value -> varname)
|
internal val globalFloatConsts = mutableMapOf<Double, String>() // all float values in the entire program (value -> varname)
|
||||||
internal val zeropageVars: Map<List<String>, MemoryAllocator.VarAllocation> = zeropage.allocatedVariables
|
internal val zeropageVars: Map<String, MemoryAllocator.VarAllocation>
|
||||||
|
|
||||||
init {
|
init {
|
||||||
allocateZeropageVariables()
|
allocateZeropageVariables()
|
||||||
|
zeropageVars = zeropage.allocatedVariables
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun isZpVar(scopedName: List<String>) = scopedName in zeropageVars
|
internal fun isZpVar(scopedName: String) = scopedName in zeropageVars
|
||||||
|
|
||||||
internal fun getFloatAsmConst(number: Double): String {
|
internal fun getFloatAsmConst(number: Double): String {
|
||||||
val asmName = globalFloatConsts[number]
|
val asmName = globalFloatConsts[number]
|
||||||
@ -56,10 +57,10 @@ internal class VariableAllocator(private val symboltable: SymbolTable,
|
|||||||
|
|
||||||
varsRequiringZp.forEach { variable ->
|
varsRequiringZp.forEach { variable ->
|
||||||
val result = zeropage.allocate(
|
val result = zeropage.allocate(
|
||||||
variable.scopedName.split('.'),
|
variable.scopedName,
|
||||||
variable.dt,
|
variable.dt,
|
||||||
variable.length,
|
variable.length,
|
||||||
variable.position,
|
variable.astNode.position,
|
||||||
errors
|
errors
|
||||||
)
|
)
|
||||||
result.fold(
|
result.fold(
|
||||||
@ -67,7 +68,7 @@ internal class VariableAllocator(private val symboltable: SymbolTable,
|
|||||||
numVariablesAllocatedInZP++
|
numVariablesAllocatedInZP++
|
||||||
},
|
},
|
||||||
failure = {
|
failure = {
|
||||||
errors.err(it.message!!, variable.position)
|
errors.err(it.message!!, variable.astNode.position)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -75,10 +76,10 @@ internal class VariableAllocator(private val symboltable: SymbolTable,
|
|||||||
if(errors.noErrors()) {
|
if(errors.noErrors()) {
|
||||||
varsPreferringZp.forEach { variable ->
|
varsPreferringZp.forEach { variable ->
|
||||||
val result = zeropage.allocate(
|
val result = zeropage.allocate(
|
||||||
variable.scopedName.split('.'),
|
variable.scopedName,
|
||||||
variable.dt,
|
variable.dt,
|
||||||
variable.length,
|
variable.length,
|
||||||
variable.position,
|
variable.astNode.position,
|
||||||
errors
|
errors
|
||||||
)
|
)
|
||||||
result.onSuccess { numVariablesAllocatedInZP++ }
|
result.onSuccess { numVariablesAllocatedInZP++ }
|
||||||
@ -95,10 +96,10 @@ internal class VariableAllocator(private val symboltable: SymbolTable,
|
|||||||
break
|
break
|
||||||
} else {
|
} else {
|
||||||
val result = zeropage.allocate(
|
val result = zeropage.allocate(
|
||||||
variable.scopedName.split('.'),
|
variable.scopedName,
|
||||||
variable.dt,
|
variable.dt,
|
||||||
variable.length,
|
variable.length,
|
||||||
variable.position,
|
variable.astNode.position,
|
||||||
errors
|
errors
|
||||||
)
|
)
|
||||||
result.onSuccess { numVariablesAllocatedInZP++ }
|
result.onSuccess { numVariablesAllocatedInZP++ }
|
||||||
|
@ -1,13 +1,9 @@
|
|||||||
package prog8.codegen.cpu6502.assignment
|
package prog8.codegen.cpu6502.assignment
|
||||||
|
|
||||||
import prog8.ast.Program
|
import prog8.code.ast.*
|
||||||
import prog8.ast.expressions.*
|
|
||||||
import prog8.ast.statements.AssignTarget
|
|
||||||
import prog8.ast.statements.Assignment
|
|
||||||
import prog8.ast.statements.DirectMemoryWrite
|
|
||||||
import prog8.ast.statements.Subroutine
|
|
||||||
import prog8.code.core.*
|
import prog8.code.core.*
|
||||||
import prog8.codegen.cpu6502.AsmGen
|
import prog8.codegen.cpu6502.AsmGen6502Internal
|
||||||
|
import prog8.codegen.cpu6502.returnsWhatWhere
|
||||||
|
|
||||||
|
|
||||||
internal enum class TargetStorageKind {
|
internal enum class TargetStorageKind {
|
||||||
@ -29,25 +25,25 @@ internal enum class SourceStorageKind {
|
|||||||
}
|
}
|
||||||
|
|
||||||
internal class AsmAssignTarget(val kind: TargetStorageKind,
|
internal class AsmAssignTarget(val kind: TargetStorageKind,
|
||||||
private val asmgen: AsmGen,
|
private val asmgen: AsmGen6502Internal,
|
||||||
val datatype: DataType,
|
val datatype: DataType,
|
||||||
val scope: Subroutine?,
|
val scope: IPtSubroutine?,
|
||||||
private val variableAsmName: String? = null,
|
private val variableAsmName: String? = null,
|
||||||
val array: ArrayIndexedExpression? = null,
|
val array: PtArrayIndexer? = null,
|
||||||
val memory: DirectMemoryWrite? = null,
|
val memory: PtMemoryByte? = null,
|
||||||
val register: RegisterOrPair? = null,
|
val register: RegisterOrPair? = null,
|
||||||
val origAstTarget: AssignTarget? = null
|
val origAstTarget: PtAssignTarget? = null
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
val constArrayIndexValue by lazy { array?.indexer?.constIndex()?.toUInt() }
|
val constArrayIndexValue by lazy { array?.index?.asConstInteger()?.toUInt() }
|
||||||
val asmVarname: String by lazy {
|
val asmVarname: String by lazy {
|
||||||
if (array == null)
|
if (array == null)
|
||||||
variableAsmName!!
|
variableAsmName!!
|
||||||
else
|
else
|
||||||
asmgen.asmVariableName(array.arrayvar)
|
asmgen.asmVariableName(array.variable)
|
||||||
}
|
}
|
||||||
|
|
||||||
lateinit var origAssign: AsmAssignment
|
lateinit var origAssign: AsmAssignmentBase
|
||||||
|
|
||||||
init {
|
init {
|
||||||
if(register!=null && datatype !in NumericDatatypes)
|
if(register!=null && datatype !in NumericDatatypes)
|
||||||
@ -55,33 +51,31 @@ internal class AsmAssignTarget(val kind: TargetStorageKind,
|
|||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun fromAstAssignment(assign: Assignment, program: Program, asmgen: AsmGen): AsmAssignTarget {
|
fun fromAstAssignment(target: PtAssignTarget, definingSub: IPtSubroutine?, asmgen: AsmGen6502Internal): AsmAssignTarget {
|
||||||
with(assign.target) {
|
with(target) {
|
||||||
val idt = inferType(program)
|
|
||||||
val dt = idt.getOrElse { throw AssemblyError("unknown dt") }
|
|
||||||
when {
|
when {
|
||||||
identifier != null -> {
|
identifier != null -> {
|
||||||
val parameter = identifier!!.targetVarDecl(program)?.subroutineParameter
|
val parameter = asmgen.findSubroutineParameter(identifier!!.name, asmgen)
|
||||||
if (parameter!=null) {
|
if (parameter!=null) {
|
||||||
val sub = parameter.definingSubroutine!!
|
val sub = parameter.definingAsmSub()
|
||||||
if (sub.isAsmSubroutine) {
|
if (sub!=null) {
|
||||||
val reg = sub.asmParameterRegisters[sub.parameters.indexOf(parameter)]
|
val reg = sub.parameters.single { it.second===parameter }.first
|
||||||
if(reg.statusflag!=null)
|
if(reg.statusflag!=null)
|
||||||
throw AssemblyError("can't assign value to processor statusflag directly")
|
throw AssemblyError("can't assign value to processor statusflag directly")
|
||||||
else
|
else
|
||||||
return AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, dt, assign.definingSubroutine, register=reg.registerOrPair, origAstTarget = this)
|
return AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, type, definingSub, register=reg.registerOrPair, origAstTarget = this)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, dt, assign.definingSubroutine, variableAsmName = asmgen.asmVariableName(identifier!!), origAstTarget = this)
|
return AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, type, definingSub, variableAsmName = asmgen.asmVariableName(identifier!!), origAstTarget = this)
|
||||||
}
|
}
|
||||||
arrayindexed != null -> return AsmAssignTarget(TargetStorageKind.ARRAY, asmgen, dt, assign.definingSubroutine, array = arrayindexed, origAstTarget = this)
|
array != null -> return AsmAssignTarget(TargetStorageKind.ARRAY, asmgen, type, definingSub, array = array, origAstTarget = this)
|
||||||
memoryAddress != null -> return AsmAssignTarget(TargetStorageKind.MEMORY, asmgen, dt, assign.definingSubroutine, memory = memoryAddress, origAstTarget = this)
|
memory != null -> return AsmAssignTarget(TargetStorageKind.MEMORY, asmgen, type, definingSub, memory = memory, origAstTarget = this)
|
||||||
else -> throw AssemblyError("weird target")
|
else -> throw AssemblyError("weird target")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun fromRegisters(registers: RegisterOrPair, signed: Boolean, scope: Subroutine?, asmgen: AsmGen): AsmAssignTarget =
|
fun fromRegisters(registers: RegisterOrPair, signed: Boolean, scope: IPtSubroutine?, asmgen: AsmGen6502Internal): AsmAssignTarget =
|
||||||
when(registers) {
|
when(registers) {
|
||||||
RegisterOrPair.A,
|
RegisterOrPair.A,
|
||||||
RegisterOrPair.X,
|
RegisterOrPair.X,
|
||||||
@ -112,69 +106,66 @@ internal class AsmAssignTarget(val kind: TargetStorageKind,
|
|||||||
}
|
}
|
||||||
|
|
||||||
internal class AsmAssignSource(val kind: SourceStorageKind,
|
internal class AsmAssignSource(val kind: SourceStorageKind,
|
||||||
private val program: Program,
|
private val program: PtProgram,
|
||||||
private val asmgen: AsmGen,
|
private val asmgen: AsmGen6502Internal,
|
||||||
val datatype: DataType,
|
val datatype: DataType,
|
||||||
private val variableAsmName: String? = null,
|
private val variableAsmName: String? = null,
|
||||||
val array: ArrayIndexedExpression? = null,
|
val array: PtArrayIndexer? = null,
|
||||||
val memory: DirectMemoryRead? = null,
|
val memory: PtMemoryByte? = null,
|
||||||
val register: RegisterOrPair? = null,
|
val register: RegisterOrPair? = null,
|
||||||
val number: NumericLiteral? = null,
|
val number: PtNumber? = null,
|
||||||
val expression: Expression? = null
|
val expression: PtExpression? = null
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
val asmVarname: String
|
val asmVarname: String
|
||||||
get() = if(array==null)
|
get() = if(array==null)
|
||||||
variableAsmName!!
|
variableAsmName!!
|
||||||
else
|
else
|
||||||
asmgen.asmVariableName(array.arrayvar)
|
asmgen.asmVariableName(array.variable)
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun fromAstSource(value: Expression, program: Program, asmgen: AsmGen): AsmAssignSource {
|
fun fromAstSource(value: PtExpression, program: PtProgram, asmgen: AsmGen6502Internal): AsmAssignSource {
|
||||||
val cv = value.constValue(program)
|
val cv = value as? PtNumber
|
||||||
if(cv!=null)
|
if(cv!=null)
|
||||||
return AsmAssignSource(SourceStorageKind.LITERALNUMBER, program, asmgen, cv.type, number = cv)
|
return AsmAssignSource(SourceStorageKind.LITERALNUMBER, program, asmgen, cv.type, number = cv)
|
||||||
|
|
||||||
return when(value) {
|
return when(value) {
|
||||||
is NumericLiteral -> throw AssemblyError("should have been constant value")
|
// checked above: is PtNumber -> throw AssemblyError("should have been constant value")
|
||||||
is StringLiteral -> throw AssemblyError("string literal value should not occur anymore for asm generation")
|
is PtString -> throw AssemblyError("string literal value should not occur anymore for asm generation")
|
||||||
is ArrayLiteral -> throw AssemblyError("array literal value should not occur anymore for asm generation")
|
is PtArray -> throw AssemblyError("array literal value should not occur anymore for asm generation")
|
||||||
is IdentifierReference -> {
|
is PtIdentifier -> {
|
||||||
val parameter = value.targetVarDecl(program)?.subroutineParameter
|
val parameter = asmgen.findSubroutineParameter(value.name, asmgen)
|
||||||
if(parameter!=null && parameter.definingSubroutine!!.isAsmSubroutine)
|
if(parameter?.definingAsmSub() != null)
|
||||||
throw AssemblyError("can't assign from a asmsub register parameter $value ${value.position}")
|
throw AssemblyError("can't assign from a asmsub register parameter $value ${value.position}")
|
||||||
val dt = value.inferType(program).getOr(DataType.UNDEFINED)
|
|
||||||
val varName=asmgen.asmVariableName(value)
|
val varName=asmgen.asmVariableName(value)
|
||||||
// special case: "cx16.r[0-15]" are 16-bits virtual registers of the commander X16 system
|
// special case: "cx16.r[0-15]" are 16-bits virtual registers of the commander X16 system
|
||||||
if(dt == DataType.UWORD && varName.lowercase().startsWith("cx16.r")) {
|
if(value.type == DataType.UWORD && varName.lowercase().startsWith("cx16.r")) {
|
||||||
val regStr = varName.lowercase().substring(5)
|
val regStr = varName.lowercase().substring(5)
|
||||||
val reg = RegisterOrPair.valueOf(regStr.uppercase())
|
val reg = RegisterOrPair.valueOf(regStr.uppercase())
|
||||||
AsmAssignSource(SourceStorageKind.REGISTER, program, asmgen, dt, register = reg)
|
AsmAssignSource(SourceStorageKind.REGISTER, program, asmgen, value.type, register = reg)
|
||||||
} else {
|
} else {
|
||||||
AsmAssignSource(SourceStorageKind.VARIABLE, program, asmgen, dt, variableAsmName = varName)
|
AsmAssignSource(SourceStorageKind.VARIABLE, program, asmgen, value.type, variableAsmName = varName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
is DirectMemoryRead -> {
|
is PtMemoryByte -> {
|
||||||
AsmAssignSource(SourceStorageKind.MEMORY, program, asmgen, DataType.UBYTE, memory = value)
|
AsmAssignSource(SourceStorageKind.MEMORY, program, asmgen, DataType.UBYTE, memory = value)
|
||||||
}
|
}
|
||||||
is ArrayIndexedExpression -> {
|
is PtArrayIndexer -> {
|
||||||
val dt = value.inferType(program).getOrElse { throw AssemblyError("unknown dt") }
|
AsmAssignSource(SourceStorageKind.ARRAY, program, asmgen, value.type, array = value)
|
||||||
AsmAssignSource(SourceStorageKind.ARRAY, program, asmgen, dt, array = value)
|
|
||||||
}
|
}
|
||||||
is BuiltinFunctionCall -> {
|
is PtBuiltinFunctionCall -> {
|
||||||
val returnType = value.inferType(program)
|
AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, value.type, expression = value)
|
||||||
AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, returnType.getOrElse { throw AssemblyError("unknown dt") }, expression = value)
|
|
||||||
}
|
}
|
||||||
is FunctionCallExpression -> {
|
is PtFunctionCall -> {
|
||||||
val sub = value.target.targetSubroutine(program)!!
|
val symbol = asmgen.symbolTable.lookup(value.name)
|
||||||
val returnType = sub.returntypes.zip(sub.asmReturnvaluesRegisters).firstOrNull { rr -> rr.second.registerOrPair != null || rr.second.statusflag!=null }?.first
|
val sub = symbol!!.astNode as IPtSubroutine
|
||||||
|
val returnType = sub.returnsWhatWhere().firstOrNull { rr -> rr.first.registerOrPair != null || rr.first.statusflag!=null }?.second
|
||||||
?: throw AssemblyError("can't translate zero return values in assignment")
|
?: throw AssemblyError("can't translate zero return values in assignment")
|
||||||
|
|
||||||
AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, returnType, expression = value)
|
AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, returnType, expression = value)
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
val returnType = value.inferType(program)
|
AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, value.type, expression = value)
|
||||||
AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, returnType.getOrElse { throw AssemblyError("unknown dt") }, expression = value)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -199,17 +190,27 @@ internal class AsmAssignSource(val kind: SourceStorageKind,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
internal class AsmAssignment(val source: AsmAssignSource,
|
internal sealed class AsmAssignmentBase(val source: AsmAssignSource,
|
||||||
val target: AsmAssignTarget,
|
val target: AsmAssignTarget,
|
||||||
val isAugmentable: Boolean,
|
val memsizer: IMemSizer,
|
||||||
memsizer: IMemSizer,
|
val position: Position) {
|
||||||
val position: Position) {
|
|
||||||
|
|
||||||
init {
|
init {
|
||||||
if(target.register !in arrayOf(RegisterOrPair.XY, RegisterOrPair.AX, RegisterOrPair.AY))
|
if(target.register !in arrayOf(RegisterOrPair.XY, RegisterOrPair.AX, RegisterOrPair.AY))
|
||||||
require(source.datatype != DataType.UNDEFINED) { "must not be placeholder/undefined datatype at $position" }
|
require(source.datatype != DataType.UNDEFINED) { "must not be placeholder/undefined datatype at $position" }
|
||||||
require(memsizer.memorySize(source.datatype) <= memsizer.memorySize(target.datatype)) {
|
require(memsizer.memorySize(source.datatype) <= memsizer.memorySize(target.datatype)) {
|
||||||
"source dt size must be less or equal to target dt size at $position"
|
"source dt size must be less or equal to target dt size at $position srcdt=${source.datatype} targetdt=${target.datatype}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal class AsmAssignment(source: AsmAssignSource,
|
||||||
|
target: AsmAssignTarget,
|
||||||
|
memsizer: IMemSizer,
|
||||||
|
position: Position): AsmAssignmentBase(source, target, memsizer, position)
|
||||||
|
|
||||||
|
internal class AsmAugmentedAssignment(source: AsmAssignSource,
|
||||||
|
val operator: String,
|
||||||
|
target: AsmAssignTarget,
|
||||||
|
memsizer: IMemSizer,
|
||||||
|
position: Position): AsmAssignmentBase(source, target, memsizer, position)
|
||||||
|
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -1,166 +1,71 @@
|
|||||||
package prog8.codegen.cpu6502.assignment
|
package prog8.codegen.cpu6502.assignment
|
||||||
|
|
||||||
import prog8.ast.Program
|
import prog8.code.ast.*
|
||||||
import prog8.ast.base.FatalAstException
|
|
||||||
import prog8.ast.expressions.*
|
|
||||||
import prog8.ast.statements.Subroutine
|
|
||||||
import prog8.code.core.*
|
import prog8.code.core.*
|
||||||
import prog8.codegen.cpu6502.AsmGen
|
import prog8.codegen.cpu6502.AsmGen6502Internal
|
||||||
import prog8.codegen.cpu6502.VariableAllocator
|
import prog8.codegen.cpu6502.VariableAllocator
|
||||||
|
|
||||||
|
|
||||||
internal class AugmentableAssignmentAsmGen(private val program: Program,
|
internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||||
private val assignmentAsmGen: AssignmentAsmGen,
|
private val assignmentAsmGen: AssignmentAsmGen,
|
||||||
private val asmgen: AsmGen,
|
private val asmgen: AsmGen6502Internal,
|
||||||
private val allocator: VariableAllocator
|
private val allocator: VariableAllocator
|
||||||
) {
|
) {
|
||||||
fun translate(assign: AsmAssignment) {
|
fun translate(assign: AsmAugmentedAssignment) {
|
||||||
require(assign.isAugmentable)
|
|
||||||
require(assign.source.kind == SourceStorageKind.EXPRESSION) {
|
|
||||||
"non-expression assign value should be handled elsewhere ${assign.position}"
|
|
||||||
}
|
|
||||||
|
|
||||||
when (val value = assign.source.expression!!) {
|
when(assign.operator) {
|
||||||
is PrefixExpression -> {
|
"-" -> {
|
||||||
// A = -A , A = +A, A = ~A, A = not A
|
val a2 = AsmAssignment(assign.source, assign.target, assign.memsizer, assign.position)
|
||||||
when (value.operator) {
|
assignmentAsmGen.inplaceNegate(a2, false)
|
||||||
"+" -> {}
|
}
|
||||||
"-" -> inplaceNegate(assign, false)
|
"~" -> {
|
||||||
"~" -> inplaceInvert(assign)
|
val a2 = AsmAssignment(assign.source, assign.target, assign.memsizer, assign.position)
|
||||||
else -> throw AssemblyError("invalid prefix operator")
|
assignmentAsmGen.inplaceInvert(a2)
|
||||||
}
|
}
|
||||||
|
"+" -> { /* is a nop */ }
|
||||||
|
else -> {
|
||||||
|
if(assign.operator.endsWith('='))
|
||||||
|
augmentedAssignExpr(assign)
|
||||||
|
else
|
||||||
|
throw AssemblyError("invalid augmented assign operator ${assign.operator}")
|
||||||
}
|
}
|
||||||
is TypecastExpression -> inplaceCast(assign.target, value, assign.position)
|
|
||||||
is BinaryExpression -> inplaceBinary(assign.target, value)
|
|
||||||
else -> throw AssemblyError("invalid aug assign value type")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun inplaceBinary(target: AsmAssignTarget, binExpr: BinaryExpression) {
|
private fun augmentedAssignExpr(assign: AsmAugmentedAssignment) {
|
||||||
val astTarget = target.origAstTarget!!
|
val srcValue = assign.source.toAstExpression(assign.target.scope as PtNamedNode)
|
||||||
if (binExpr.left isSameAs astTarget) {
|
when (assign.operator) {
|
||||||
// A = A <operator> Something
|
"+=" -> inplaceModification(assign.target, "+", srcValue)
|
||||||
return inplaceModification(target, binExpr.operator, binExpr.right)
|
"-=" -> inplaceModification(assign.target, "-", srcValue)
|
||||||
|
"*=" -> inplaceModification(assign.target, "*", srcValue)
|
||||||
|
"/=" -> inplaceModification(assign.target, "/", srcValue)
|
||||||
|
"|=" -> inplaceModification(assign.target, "|", srcValue)
|
||||||
|
"&=" -> inplaceModification(assign.target, "&", srcValue)
|
||||||
|
"^=" -> inplaceModification(assign.target, "^", srcValue)
|
||||||
|
"<<=" -> inplaceModification(assign.target, "<<", srcValue)
|
||||||
|
">>=" -> inplaceModification(assign.target, ">>", srcValue)
|
||||||
|
else -> throw AssemblyError("invalid augmented assign operator ${assign.operator}")
|
||||||
}
|
}
|
||||||
|
|
||||||
if (binExpr.operator in AssociativeOperators) {
|
|
||||||
if (binExpr.right isSameAs astTarget) {
|
|
||||||
// A = 5 <operator> A
|
|
||||||
return inplaceModification(target, binExpr.operator, binExpr.left)
|
|
||||||
}
|
|
||||||
|
|
||||||
val leftBinExpr = binExpr.left as? BinaryExpression
|
|
||||||
if (leftBinExpr?.operator == binExpr.operator) {
|
|
||||||
// TODO better optimize the chained asm to avoid intermediate stores/loads?
|
|
||||||
when {
|
|
||||||
binExpr.right isSameAs astTarget -> {
|
|
||||||
// A = (x <associative-operator> y) <same-operator> A
|
|
||||||
inplaceModification(target, binExpr.operator, leftBinExpr.left)
|
|
||||||
inplaceModification(target, binExpr.operator, leftBinExpr.right)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
leftBinExpr.left isSameAs astTarget -> {
|
|
||||||
// A = (A <associative-operator> x) <same-operator> y
|
|
||||||
inplaceModification(target, binExpr.operator, leftBinExpr.right)
|
|
||||||
inplaceModification(target, binExpr.operator, binExpr.right)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
leftBinExpr.right isSameAs astTarget -> {
|
|
||||||
// A = (x <associative-operator> A) <same-operator> y
|
|
||||||
inplaceModification(target, binExpr.operator, leftBinExpr.left)
|
|
||||||
inplaceModification(target, binExpr.operator, binExpr.right)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
val rightBinExpr = binExpr.right as? BinaryExpression
|
|
||||||
if (rightBinExpr?.operator == binExpr.operator) {
|
|
||||||
when {
|
|
||||||
binExpr.left isSameAs astTarget -> {
|
|
||||||
// A = A <associative-operator> (x <same-operator> y)
|
|
||||||
inplaceModification(target, binExpr.operator, rightBinExpr.left)
|
|
||||||
inplaceModification(target, binExpr.operator, rightBinExpr.right)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
rightBinExpr.left isSameAs astTarget -> {
|
|
||||||
// A = y <associative-operator> (A <same-operator> x)
|
|
||||||
inplaceModification(target, binExpr.operator, binExpr.left)
|
|
||||||
inplaceModification(target, binExpr.operator, rightBinExpr.right)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
rightBinExpr.right isSameAs astTarget -> {
|
|
||||||
// A = y <associative-operator> (x <same-operator> y)
|
|
||||||
inplaceModification(target, binExpr.operator, binExpr.left)
|
|
||||||
inplaceModification(target, binExpr.operator, rightBinExpr.left)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val leftBinExpr = binExpr.left as? BinaryExpression
|
|
||||||
val rightBinExpr = binExpr.right as? BinaryExpression
|
|
||||||
if(leftBinExpr!=null && rightBinExpr==null) {
|
|
||||||
if(leftBinExpr.left isSameAs astTarget) {
|
|
||||||
// X = (X <oper> Right) <oper> Something
|
|
||||||
inplaceModification(target, leftBinExpr.operator, leftBinExpr.right)
|
|
||||||
inplaceModification(target, binExpr.operator, binExpr.right)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if(leftBinExpr.right isSameAs astTarget) {
|
|
||||||
// X = (Left <oper> X) <oper> Something
|
|
||||||
if(leftBinExpr.operator in AssociativeOperators) {
|
|
||||||
inplaceModification(target, leftBinExpr.operator, leftBinExpr.left)
|
|
||||||
inplaceModification(target, binExpr.operator, binExpr.right)
|
|
||||||
return
|
|
||||||
} else {
|
|
||||||
throw AssemblyError("operands in wrong order for non-associative operator")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(leftBinExpr==null && rightBinExpr!=null) {
|
|
||||||
if(rightBinExpr.left isSameAs astTarget) {
|
|
||||||
// X = Something <oper> (X <oper> Right)
|
|
||||||
if(binExpr.operator in AssociativeOperators) {
|
|
||||||
inplaceModification(target, rightBinExpr.operator, rightBinExpr.right)
|
|
||||||
inplaceModification(target, binExpr.operator, binExpr.left)
|
|
||||||
return
|
|
||||||
} else {
|
|
||||||
throw AssemblyError("operands in wrong order for non-associative operator")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(rightBinExpr.right isSameAs astTarget) {
|
|
||||||
// X = Something <oper> (Left <oper> X)
|
|
||||||
if(binExpr.operator in AssociativeOperators && rightBinExpr.operator in AssociativeOperators) {
|
|
||||||
inplaceModification(target, rightBinExpr.operator, rightBinExpr.left)
|
|
||||||
inplaceModification(target, binExpr.operator, binExpr.left)
|
|
||||||
return
|
|
||||||
} else {
|
|
||||||
throw AssemblyError("operands in wrong order for non-associative operator")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
throw FatalAstException("assignment should follow augmentable rules $binExpr")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun inplaceModification(target: AsmAssignTarget, operator: String, origValue: Expression) {
|
private fun inplaceModification(target: AsmAssignTarget, operator: String, origValue: PtExpression) {
|
||||||
|
|
||||||
// the asm-gen code can deal with situations where you want to assign a byte into a word.
|
// the asm-gen code can deal with situations where you want to assign a byte into a word.
|
||||||
// it will create the most optimized code to do this (so it type-extends for us).
|
// it will create the most optimized code to do this (so it type-extends for us).
|
||||||
// But we can't deal with writing a word into a byte - explicit typeconversion is required
|
// But we can't deal with writing a word into a byte - explicit typeconversion is required
|
||||||
val value = if(program.memsizer.memorySize(origValue.inferType(program).getOrElse { throw AssemblyError("unknown dt") }) > program.memsizer.memorySize(target.datatype)) {
|
val value = if(program.memsizer.memorySize(origValue.type) > program.memsizer.memorySize(target.datatype)) {
|
||||||
val typecast = TypecastExpression(origValue, target.datatype, true, origValue.position)
|
val typecast = PtTypeCast(target.datatype, origValue.position)
|
||||||
typecast.linkParents(origValue.parent)
|
typecast.add(origValue)
|
||||||
|
require(typecast.type!=origValue.type)
|
||||||
typecast
|
typecast
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
origValue
|
origValue
|
||||||
}
|
}
|
||||||
|
|
||||||
val valueLv = (value as? NumericLiteral)?.number
|
val valueLv = (value as? PtNumber)?.number
|
||||||
val ident = value as? IdentifierReference
|
val ident = value as? PtIdentifier
|
||||||
val memread = value as? DirectMemoryRead
|
val memread = value as? PtMemoryByte
|
||||||
|
|
||||||
when (target.kind) {
|
when (target.kind) {
|
||||||
TargetStorageKind.VARIABLE -> {
|
TargetStorageKind.VARIABLE -> {
|
||||||
@ -170,7 +75,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
valueLv != null -> inplaceModification_byte_litval_to_variable(target.asmVarname, target.datatype, operator, valueLv.toInt())
|
valueLv != null -> inplaceModification_byte_litval_to_variable(target.asmVarname, target.datatype, operator, valueLv.toInt())
|
||||||
ident != null -> inplaceModification_byte_variable_to_variable(target.asmVarname, target.datatype, operator, ident)
|
ident != null -> inplaceModification_byte_variable_to_variable(target.asmVarname, target.datatype, operator, ident)
|
||||||
memread != null -> inplaceModification_byte_memread_to_variable(target.asmVarname, target.datatype, operator, memread)
|
memread != null -> inplaceModification_byte_memread_to_variable(target.asmVarname, target.datatype, operator, memread)
|
||||||
value is TypecastExpression -> {
|
value is PtTypeCast -> {
|
||||||
if (tryInplaceModifyWithRemovedRedundantCast(value, target, operator)) return
|
if (tryInplaceModifyWithRemovedRedundantCast(value, target, operator)) return
|
||||||
inplaceModification_byte_value_to_variable(target.asmVarname, target.datatype, operator, value)
|
inplaceModification_byte_value_to_variable(target.asmVarname, target.datatype, operator, value)
|
||||||
}
|
}
|
||||||
@ -182,7 +87,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
valueLv != null -> inplaceModification_word_litval_to_variable(target.asmVarname, target.datatype, operator, valueLv.toInt())
|
valueLv != null -> inplaceModification_word_litval_to_variable(target.asmVarname, target.datatype, operator, valueLv.toInt())
|
||||||
ident != null -> inplaceModification_word_variable_to_variable(target.asmVarname, target.datatype, operator, ident)
|
ident != null -> inplaceModification_word_variable_to_variable(target.asmVarname, target.datatype, operator, ident)
|
||||||
memread != null -> inplaceModification_word_memread_to_variable(target.asmVarname, target.datatype, operator, memread)
|
memread != null -> inplaceModification_word_memread_to_variable(target.asmVarname, target.datatype, operator, memread)
|
||||||
value is TypecastExpression -> {
|
value is PtTypeCast -> {
|
||||||
if (tryInplaceModifyWithRemovedRedundantCast(value, target, operator))
|
if (tryInplaceModifyWithRemovedRedundantCast(value, target, operator))
|
||||||
return
|
return
|
||||||
inplaceModification_word_value_to_variable(target.asmVarname, target.datatype, operator, value)
|
inplaceModification_word_value_to_variable(target.asmVarname, target.datatype, operator, value)
|
||||||
@ -194,7 +99,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
when {
|
when {
|
||||||
valueLv != null -> inplaceModification_float_litval_to_variable(target.asmVarname, operator, valueLv.toDouble(), target.scope!!)
|
valueLv != null -> inplaceModification_float_litval_to_variable(target.asmVarname, operator, valueLv.toDouble(), target.scope!!)
|
||||||
ident != null -> inplaceModification_float_variable_to_variable(target.asmVarname, operator, ident, target.scope!!)
|
ident != null -> inplaceModification_float_variable_to_variable(target.asmVarname, operator, ident, target.scope!!)
|
||||||
value is TypecastExpression -> {
|
value is PtTypeCast -> {
|
||||||
if (tryInplaceModifyWithRemovedRedundantCast(value, target, operator)) return
|
if (tryInplaceModifyWithRemovedRedundantCast(value, target, operator)) return
|
||||||
inplaceModification_float_value_to_variable(target.asmVarname, operator, value, target.scope!!)
|
inplaceModification_float_value_to_variable(target.asmVarname, operator, value, target.scope!!)
|
||||||
}
|
}
|
||||||
@ -206,27 +111,27 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
}
|
}
|
||||||
TargetStorageKind.MEMORY -> {
|
TargetStorageKind.MEMORY -> {
|
||||||
val memory = target.memory!!
|
val memory = target.memory!!
|
||||||
when (memory.addressExpression) {
|
when (memory.address) {
|
||||||
is NumericLiteral -> {
|
is PtNumber -> {
|
||||||
val addr = (memory.addressExpression as NumericLiteral).number.toInt()
|
val addr = (memory.address as PtNumber).number.toInt()
|
||||||
// re-use code to assign a variable, instead this time, use a direct memory address
|
// re-use code to assign a variable, instead this time, use a direct memory address
|
||||||
when {
|
when {
|
||||||
valueLv != null -> inplaceModification_byte_litval_to_variable(addr.toHex(), DataType.UBYTE, operator, valueLv.toInt())
|
valueLv != null -> inplaceModification_byte_litval_to_variable(addr.toHex(), DataType.UBYTE, operator, valueLv.toInt())
|
||||||
ident != null -> inplaceModification_byte_variable_to_variable(addr.toHex(), DataType.UBYTE, operator, ident)
|
ident != null -> inplaceModification_byte_variable_to_variable(addr.toHex(), DataType.UBYTE, operator, ident)
|
||||||
memread != null -> inplaceModification_byte_memread_to_variable(addr.toHex(), DataType.UBYTE, operator, value)
|
memread != null -> inplaceModification_byte_memread_to_variable(addr.toHex(), DataType.UBYTE, operator, value)
|
||||||
value is TypecastExpression -> {
|
value is PtTypeCast -> {
|
||||||
if (tryInplaceModifyWithRemovedRedundantCast(value, target, operator)) return
|
if (tryInplaceModifyWithRemovedRedundantCast(value, target, operator)) return
|
||||||
inplaceModification_byte_value_to_variable(addr.toHex(), DataType.UBYTE, operator, value)
|
inplaceModification_byte_value_to_variable(addr.toHex(), DataType.UBYTE, operator, value)
|
||||||
}
|
}
|
||||||
else -> inplaceModification_byte_value_to_variable(addr.toHex(), DataType.UBYTE, operator, value)
|
else -> inplaceModification_byte_value_to_variable(addr.toHex(), DataType.UBYTE, operator, value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
is IdentifierReference -> {
|
is PtIdentifier -> {
|
||||||
val pointer = memory.addressExpression as IdentifierReference
|
val pointer = memory.address as PtIdentifier
|
||||||
when {
|
when {
|
||||||
valueLv != null -> inplaceModification_byte_litval_to_pointer(pointer, operator, valueLv.toInt())
|
valueLv != null -> inplaceModification_byte_litval_to_pointer(pointer, operator, valueLv.toInt())
|
||||||
ident != null -> inplaceModification_byte_variable_to_pointer(pointer, operator, ident)
|
ident != null -> inplaceModification_byte_variable_to_pointer(pointer, operator, ident)
|
||||||
value is TypecastExpression -> {
|
value is PtTypeCast -> {
|
||||||
if (tryInplaceModifyWithRemovedRedundantCast(value, target, operator)) return
|
if (tryInplaceModifyWithRemovedRedundantCast(value, target, operator)) return
|
||||||
inplaceModification_byte_value_to_pointer(pointer, operator, value)
|
inplaceModification_byte_value_to_pointer(pointer, operator, value)
|
||||||
}
|
}
|
||||||
@ -235,13 +140,13 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
// TODO use some other evaluation here; don't use the estack to transfer the address to read/write from
|
// TODO use some other evaluation here; don't use the estack to transfer the address to read/write from
|
||||||
asmgen.assignExpressionTo(memory.addressExpression, AsmAssignTarget(TargetStorageKind.STACK, asmgen, DataType.UWORD, memory.definingSubroutine))
|
asmgen.assignExpressionTo(memory.address, AsmAssignTarget(TargetStorageKind.STACK, asmgen, DataType.UWORD, memory.definingISub()))
|
||||||
asmgen.out(" jsr prog8_lib.read_byte_from_address_on_stack | sta P8ZP_SCRATCH_B1")
|
asmgen.out(" jsr prog8_lib.read_byte_from_address_on_stack | sta P8ZP_SCRATCH_B1")
|
||||||
when {
|
when {
|
||||||
valueLv != null -> inplaceModification_byte_litval_to_variable("P8ZP_SCRATCH_B1", DataType.UBYTE, operator, valueLv.toInt())
|
valueLv != null -> inplaceModification_byte_litval_to_variable("P8ZP_SCRATCH_B1", DataType.UBYTE, operator, valueLv.toInt())
|
||||||
ident != null -> inplaceModification_byte_variable_to_variable("P8ZP_SCRATCH_B1", DataType.UBYTE, operator, ident)
|
ident != null -> inplaceModification_byte_variable_to_variable("P8ZP_SCRATCH_B1", DataType.UBYTE, operator, ident)
|
||||||
memread != null -> inplaceModification_byte_memread_to_variable("P8ZP_SCRATCH_B1", DataType.UBYTE, operator, memread)
|
memread != null -> inplaceModification_byte_memread_to_variable("P8ZP_SCRATCH_B1", DataType.UBYTE, operator, memread)
|
||||||
value is TypecastExpression -> {
|
value is PtTypeCast -> {
|
||||||
if (tryInplaceModifyWithRemovedRedundantCast(value, target, operator)) return
|
if (tryInplaceModifyWithRemovedRedundantCast(value, target, operator)) return
|
||||||
inplaceModification_byte_value_to_variable("P8ZP_SCRATCH_B1", DataType.UBYTE, operator, value)
|
inplaceModification_byte_value_to_variable("P8ZP_SCRATCH_B1", DataType.UBYTE, operator, value)
|
||||||
}
|
}
|
||||||
@ -252,91 +157,89 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
TargetStorageKind.ARRAY -> {
|
TargetStorageKind.ARRAY -> {
|
||||||
with(target.array!!.indexer) {
|
val indexNum = target.array!!.index as? PtNumber
|
||||||
val indexNum = indexExpr as? NumericLiteral
|
val indexVar = target.array.index as? PtIdentifier
|
||||||
val indexVar = indexExpr as? IdentifierReference
|
when {
|
||||||
when {
|
indexNum!=null -> {
|
||||||
indexNum!=null -> {
|
val targetVarName = "${target.asmVarname} + ${indexNum.number.toInt()*program.memsizer.memorySize(target.datatype)}"
|
||||||
val targetVarName = "${target.asmVarname} + ${indexNum.number.toInt()*program.memsizer.memorySize(target.datatype)}"
|
when (target.datatype) {
|
||||||
when (target.datatype) {
|
in ByteDatatypes -> {
|
||||||
in ByteDatatypes -> {
|
when {
|
||||||
when {
|
valueLv != null -> inplaceModification_byte_litval_to_variable(targetVarName, target.datatype, operator, valueLv.toInt())
|
||||||
valueLv != null -> inplaceModification_byte_litval_to_variable(targetVarName, target.datatype, operator, valueLv.toInt())
|
ident != null -> inplaceModification_byte_variable_to_variable(targetVarName, target.datatype, operator, ident)
|
||||||
ident != null -> inplaceModification_byte_variable_to_variable(targetVarName, target.datatype, operator, ident)
|
memread != null -> inplaceModification_byte_memread_to_variable(targetVarName, target.datatype, operator, memread)
|
||||||
memread != null -> inplaceModification_byte_memread_to_variable(targetVarName, target.datatype, operator, memread)
|
value is PtTypeCast -> {
|
||||||
value is TypecastExpression -> {
|
if (tryInplaceModifyWithRemovedRedundantCast(value, target, operator)) return
|
||||||
if (tryInplaceModifyWithRemovedRedundantCast(value, target, operator)) return
|
inplaceModification_byte_value_to_variable(targetVarName, target.datatype, operator, value)
|
||||||
inplaceModification_byte_value_to_variable(targetVarName, target.datatype, operator, value)
|
|
||||||
}
|
|
||||||
else -> inplaceModification_byte_value_to_variable(targetVarName, target.datatype, operator, value)
|
|
||||||
}
|
}
|
||||||
|
else -> inplaceModification_byte_value_to_variable(targetVarName, target.datatype, operator, value)
|
||||||
}
|
}
|
||||||
in WordDatatypes -> {
|
|
||||||
when {
|
|
||||||
valueLv != null -> inplaceModification_word_litval_to_variable(targetVarName, target.datatype, operator, valueLv.toInt())
|
|
||||||
ident != null -> inplaceModification_word_variable_to_variable(targetVarName, target.datatype, operator, ident)
|
|
||||||
memread != null -> inplaceModification_word_memread_to_variable(targetVarName, target.datatype, operator, memread)
|
|
||||||
value is TypecastExpression -> {
|
|
||||||
if (tryInplaceModifyWithRemovedRedundantCast(value, target, operator)) return
|
|
||||||
inplaceModification_word_value_to_variable(targetVarName, target.datatype, operator, value)
|
|
||||||
}
|
|
||||||
else -> inplaceModification_word_value_to_variable(targetVarName, target.datatype, operator, value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DataType.FLOAT -> {
|
|
||||||
when {
|
|
||||||
valueLv != null -> inplaceModification_float_litval_to_variable(targetVarName, operator, valueLv.toDouble(), target.scope!!)
|
|
||||||
ident != null -> inplaceModification_float_variable_to_variable(targetVarName, operator, ident, target.scope!!)
|
|
||||||
value is TypecastExpression -> {
|
|
||||||
if (tryInplaceModifyWithRemovedRedundantCast(value, target, operator)) return
|
|
||||||
inplaceModification_float_value_to_variable(targetVarName, operator, value, target.scope!!)
|
|
||||||
}
|
|
||||||
else -> inplaceModification_float_value_to_variable(targetVarName, operator, value, target.scope!!)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else -> throw AssemblyError("weird type to do in-place modification on ${target.datatype}")
|
|
||||||
}
|
}
|
||||||
}
|
in WordDatatypes -> {
|
||||||
indexVar!=null -> {
|
when {
|
||||||
when (target.datatype) {
|
valueLv != null -> inplaceModification_word_litval_to_variable(targetVarName, target.datatype, operator, valueLv.toInt())
|
||||||
in ByteDatatypes -> {
|
ident != null -> inplaceModification_word_variable_to_variable(targetVarName, target.datatype, operator, ident)
|
||||||
val tgt =
|
memread != null -> inplaceModification_word_memread_to_variable(targetVarName, target.datatype, operator, memread)
|
||||||
AsmAssignTarget.fromRegisters(
|
value is PtTypeCast -> {
|
||||||
RegisterOrPair.A,
|
if (tryInplaceModifyWithRemovedRedundantCast(value, target, operator)) return
|
||||||
target.datatype == DataType.BYTE, null,
|
inplaceModification_word_value_to_variable(targetVarName, target.datatype, operator, value)
|
||||||
asmgen
|
}
|
||||||
)
|
else -> inplaceModification_word_value_to_variable(targetVarName, target.datatype, operator, value)
|
||||||
val assign = AsmAssignment(target.origAssign.source, tgt, false, program.memsizer, value.position)
|
|
||||||
assignmentAsmGen.translateNormalAssignment(assign)
|
|
||||||
assignmentAsmGen.assignRegisterByte(target, CpuRegister.A)
|
|
||||||
}
|
}
|
||||||
in WordDatatypes -> {
|
|
||||||
val tgt =
|
|
||||||
AsmAssignTarget.fromRegisters(
|
|
||||||
RegisterOrPair.AY,
|
|
||||||
target.datatype == DataType.WORD, null,
|
|
||||||
asmgen
|
|
||||||
)
|
|
||||||
val assign = AsmAssignment(target.origAssign.source, tgt, false, program.memsizer, value.position)
|
|
||||||
assignmentAsmGen.translateNormalAssignment(assign)
|
|
||||||
assignmentAsmGen.assignRegisterpairWord(target, RegisterOrPair.AY)
|
|
||||||
}
|
|
||||||
DataType.FLOAT -> {
|
|
||||||
val tgt =
|
|
||||||
AsmAssignTarget.fromRegisters(
|
|
||||||
RegisterOrPair.FAC1,
|
|
||||||
true, null,
|
|
||||||
asmgen
|
|
||||||
)
|
|
||||||
val assign = AsmAssignment(target.origAssign.source, tgt, false, program.memsizer, value.position)
|
|
||||||
assignmentAsmGen.translateNormalAssignment(assign)
|
|
||||||
assignmentAsmGen.assignFAC1float(target)
|
|
||||||
}
|
|
||||||
else -> throw AssemblyError("weird type to do in-place modification on ${target.datatype}")
|
|
||||||
}
|
}
|
||||||
|
DataType.FLOAT -> {
|
||||||
|
when {
|
||||||
|
valueLv != null -> inplaceModification_float_litval_to_variable(targetVarName, operator, valueLv.toDouble(), target.scope!!)
|
||||||
|
ident != null -> inplaceModification_float_variable_to_variable(targetVarName, operator, ident, target.scope!!)
|
||||||
|
value is PtTypeCast -> {
|
||||||
|
if (tryInplaceModifyWithRemovedRedundantCast(value, target, operator)) return
|
||||||
|
inplaceModification_float_value_to_variable(targetVarName, operator, value, target.scope!!)
|
||||||
|
}
|
||||||
|
else -> inplaceModification_float_value_to_variable(targetVarName, operator, value, target.scope!!)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("weird type to do in-place modification on ${target.datatype}")
|
||||||
}
|
}
|
||||||
else -> throw AssemblyError("indexer expression should have been replaced by auto indexer var")
|
|
||||||
}
|
}
|
||||||
|
indexVar!=null -> {
|
||||||
|
when (target.datatype) {
|
||||||
|
in ByteDatatypes -> {
|
||||||
|
val tgt =
|
||||||
|
AsmAssignTarget.fromRegisters(
|
||||||
|
RegisterOrPair.A,
|
||||||
|
target.datatype == DataType.BYTE, null,
|
||||||
|
asmgen
|
||||||
|
)
|
||||||
|
val assign = AsmAssignment(target.origAssign.source, tgt, program.memsizer, value.position)
|
||||||
|
assignmentAsmGen.translateNormalAssignment(assign)
|
||||||
|
assignmentAsmGen.assignRegisterByte(target, CpuRegister.A)
|
||||||
|
}
|
||||||
|
in WordDatatypes -> {
|
||||||
|
val tgt =
|
||||||
|
AsmAssignTarget.fromRegisters(
|
||||||
|
RegisterOrPair.AY,
|
||||||
|
target.datatype == DataType.WORD, null,
|
||||||
|
asmgen
|
||||||
|
)
|
||||||
|
val assign = AsmAssignment(target.origAssign.source, tgt, program.memsizer, value.position)
|
||||||
|
assignmentAsmGen.translateNormalAssignment(assign)
|
||||||
|
assignmentAsmGen.assignRegisterpairWord(target, RegisterOrPair.AY)
|
||||||
|
}
|
||||||
|
DataType.FLOAT -> {
|
||||||
|
val tgt =
|
||||||
|
AsmAssignTarget.fromRegisters(
|
||||||
|
RegisterOrPair.FAC1,
|
||||||
|
true, null,
|
||||||
|
asmgen
|
||||||
|
)
|
||||||
|
val assign = AsmAssignment(target.origAssign.source, tgt, program.memsizer, value.position)
|
||||||
|
assignmentAsmGen.translateNormalAssignment(assign)
|
||||||
|
assignmentAsmGen.assignFAC1float(target)
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("weird type to do in-place modification on ${target.datatype}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("indexer expression should have been replaced by auto indexer var")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
TargetStorageKind.REGISTER -> throw AssemblyError("no asm gen for reg in-place modification")
|
TargetStorageKind.REGISTER -> throw AssemblyError("no asm gen for reg in-place modification")
|
||||||
@ -344,21 +247,20 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun tryInplaceModifyWithRemovedRedundantCast(value: TypecastExpression, target: AsmAssignTarget, operator: String): Boolean {
|
private fun tryInplaceModifyWithRemovedRedundantCast(value: PtTypeCast, target: AsmAssignTarget, operator: String): Boolean {
|
||||||
if (target.datatype == value.type) {
|
if (target.datatype == value.type) {
|
||||||
val childIDt = value.expression.inferType(program)
|
val childDt = value.value.type
|
||||||
val childDt = childIDt.getOrElse { throw AssemblyError("unknown dt") }
|
|
||||||
if (value.type!=DataType.FLOAT && (value.type.equalsSize(childDt) || value.type.largerThan(childDt))) {
|
if (value.type!=DataType.FLOAT && (value.type.equalsSize(childDt) || value.type.largerThan(childDt))) {
|
||||||
// this typecast is redundant here; the rest of the code knows how to deal with the uncasted value.
|
// this typecast is redundant here; the rest of the code knows how to deal with the uncasted value.
|
||||||
// (works for integer types, not for float.)
|
// (works for integer types, not for float.)
|
||||||
inplaceModification(target, operator, value.expression)
|
inplaceModification(target, operator, value.value)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun inplaceModification_byte_value_to_pointer(pointervar: IdentifierReference, operator: String, value: Expression) {
|
private fun inplaceModification_byte_value_to_pointer(pointervar: PtIdentifier, operator: String, value: PtExpression) {
|
||||||
asmgen.assignExpressionToVariable(value, "P8ZP_SCRATCH_B1", DataType.UBYTE, null)
|
asmgen.assignExpressionToVariable(value, "P8ZP_SCRATCH_B1", DataType.UBYTE, null)
|
||||||
val sourceName = asmgen.loadByteFromPointerIntoA(pointervar)
|
val sourceName = asmgen.loadByteFromPointerIntoA(pointervar)
|
||||||
when (operator) {
|
when (operator) {
|
||||||
@ -394,7 +296,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
asmgen.storeAIntoZpPointerVar(sourceName)
|
asmgen.storeAIntoZpPointerVar(sourceName)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun inplaceModification_byte_variable_to_pointer(pointervar: IdentifierReference, operator: String, value: IdentifierReference) {
|
private fun inplaceModification_byte_variable_to_pointer(pointervar: PtIdentifier, operator: String, value: PtIdentifier) {
|
||||||
val otherName = asmgen.asmVariableName(value)
|
val otherName = asmgen.asmVariableName(value)
|
||||||
val sourceName = asmgen.loadByteFromPointerIntoA(pointervar)
|
val sourceName = asmgen.loadByteFromPointerIntoA(pointervar)
|
||||||
|
|
||||||
@ -431,7 +333,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
asmgen.storeAIntoZpPointerVar(sourceName)
|
asmgen.storeAIntoZpPointerVar(sourceName)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun inplaceModification_byte_litval_to_pointer(pointervar: IdentifierReference, operator: String, value: Int) {
|
private fun inplaceModification_byte_litval_to_pointer(pointervar: PtIdentifier, operator: String, value: Int) {
|
||||||
when (operator) {
|
when (operator) {
|
||||||
// note: ** (power) operator requires floats.
|
// note: ** (power) operator requires floats.
|
||||||
"+" -> {
|
"+" -> {
|
||||||
@ -499,7 +401,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun inplaceModification_byte_value_to_variable(name: String, dt: DataType, operator: String, value: Expression) {
|
private fun inplaceModification_byte_value_to_variable(name: String, dt: DataType, operator: String, value: PtExpression) {
|
||||||
// this should be the last resort for code generation for this,
|
// this should be the last resort for code generation for this,
|
||||||
// because the value is evaluated onto the eval stack (=slow).
|
// because the value is evaluated onto the eval stack (=slow).
|
||||||
when (operator) {
|
when (operator) {
|
||||||
@ -594,7 +496,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun inplaceModification_byte_variable_to_variable(name: String, dt: DataType, operator: String, ident: IdentifierReference) {
|
private fun inplaceModification_byte_variable_to_variable(name: String, dt: DataType, operator: String, ident: PtIdentifier) {
|
||||||
val otherName = asmgen.asmVariableName(ident)
|
val otherName = asmgen.asmVariableName(ident)
|
||||||
when (operator) {
|
when (operator) {
|
||||||
// note: ** (power) operator requires floats.
|
// note: ** (power) operator requires floats.
|
||||||
@ -762,7 +664,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun inplaceModification_byte_memread_to_variable(name: String, dt: DataType, operator: String, memread: DirectMemoryRead) {
|
private fun inplaceModification_byte_memread_to_variable(name: String, dt: DataType, operator: String, memread: PtMemoryByte) {
|
||||||
when (operator) {
|
when (operator) {
|
||||||
"+" -> {
|
"+" -> {
|
||||||
asmgen.translateDirectMemReadExpressionToRegAorStack(memread, false)
|
asmgen.translateDirectMemReadExpressionToRegAorStack(memread, false)
|
||||||
@ -799,7 +701,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun inplaceModification_word_memread_to_variable(name: String, dt: DataType, operator: String, memread: DirectMemoryRead) {
|
private fun inplaceModification_word_memread_to_variable(name: String, dt: DataType, operator: String, memread: PtMemoryByte) {
|
||||||
when (operator) {
|
when (operator) {
|
||||||
"+" -> {
|
"+" -> {
|
||||||
asmgen.translateDirectMemReadExpressionToRegAorStack(memread, false)
|
asmgen.translateDirectMemReadExpressionToRegAorStack(memread, false)
|
||||||
@ -1111,9 +1013,9 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun inplaceModification_word_variable_to_variable(name: String, dt: DataType, operator: String, ident: IdentifierReference) {
|
private fun inplaceModification_word_variable_to_variable(name: String, dt: DataType, operator: String, ident: PtIdentifier) {
|
||||||
val otherName = asmgen.asmVariableName(ident)
|
val otherName = asmgen.asmVariableName(ident)
|
||||||
when (val valueDt = ident.targetVarDecl(program)!!.datatype) {
|
when (val valueDt = ident.type) {
|
||||||
in ByteDatatypes -> {
|
in ByteDatatypes -> {
|
||||||
// the other variable is a BYTE type so optimize for that
|
// the other variable is a BYTE type so optimize for that
|
||||||
when (operator) {
|
when (operator) {
|
||||||
@ -1356,12 +1258,10 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun inplaceModification_word_value_to_variable(name: String, dt: DataType, operator: String, value: Expression) {
|
private fun inplaceModification_word_value_to_variable(name: String, dt: DataType, operator: String, value: PtExpression) {
|
||||||
// this should be the last resort for code generation for this,
|
// this should be the last resort for code generation for this,
|
||||||
// because the value is evaluated onto the eval stack (=slow).
|
// because the value is evaluated onto the eval stack (=slow).
|
||||||
|
|
||||||
val valueiDt = value.inferType(program)
|
|
||||||
val valueDt = valueiDt.getOrElse { throw AssemblyError("unknown dt") }
|
|
||||||
|
|
||||||
fun multiplyVarByWordInAY() {
|
fun multiplyVarByWordInAY() {
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
@ -1410,7 +1310,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
""")
|
""")
|
||||||
}
|
}
|
||||||
|
|
||||||
when (valueDt) {
|
when (val valueDt = value.type) {
|
||||||
in ByteDatatypes -> {
|
in ByteDatatypes -> {
|
||||||
// the other variable is a BYTE type so optimize for that
|
// the other variable is a BYTE type so optimize for that
|
||||||
when (operator) {
|
when (operator) {
|
||||||
@ -1581,7 +1481,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun inplaceModification_float_value_to_variable(name: String, operator: String, value: Expression, scope: Subroutine) {
|
private fun inplaceModification_float_value_to_variable(name: String, operator: String, value: PtExpression, scope: IPtSubroutine) {
|
||||||
asmgen.assignExpressionToRegister(value, RegisterOrPair.FAC1)
|
asmgen.assignExpressionToRegister(value, RegisterOrPair.FAC1)
|
||||||
asmgen.saveRegisterLocal(CpuRegister.X, scope)
|
asmgen.saveRegisterLocal(CpuRegister.X, scope)
|
||||||
when (operator) {
|
when (operator) {
|
||||||
@ -1623,8 +1523,8 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
asmgen.restoreRegisterLocal(CpuRegister.X)
|
asmgen.restoreRegisterLocal(CpuRegister.X)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun inplaceModification_float_variable_to_variable(name: String, operator: String, ident: IdentifierReference, scope: Subroutine) {
|
private fun inplaceModification_float_variable_to_variable(name: String, operator: String, ident: PtIdentifier, scope: IPtSubroutine) {
|
||||||
val valueDt = ident.targetVarDecl(program)!!.datatype
|
val valueDt = ident.type
|
||||||
if(valueDt != DataType.FLOAT)
|
if(valueDt != DataType.FLOAT)
|
||||||
throw AssemblyError("float variable expected")
|
throw AssemblyError("float variable expected")
|
||||||
|
|
||||||
@ -1682,7 +1582,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
asmgen.restoreRegisterLocal(CpuRegister.X)
|
asmgen.restoreRegisterLocal(CpuRegister.X)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun inplaceModification_float_litval_to_variable(name: String, operator: String, value: Double, scope: Subroutine) {
|
private fun inplaceModification_float_litval_to_variable(name: String, operator: String, value: Double, scope: IPtSubroutine) {
|
||||||
val constValueName = allocator.getFloatAsmConst(value)
|
val constValueName = allocator.getFloatAsmConst(value)
|
||||||
asmgen.saveRegisterLocal(CpuRegister.X, scope)
|
asmgen.saveRegisterLocal(CpuRegister.X, scope)
|
||||||
when (operator) {
|
when (operator) {
|
||||||
@ -1743,259 +1643,50 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
""")
|
""")
|
||||||
asmgen.restoreRegisterLocal(CpuRegister.X)
|
asmgen.restoreRegisterLocal(CpuRegister.X)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
private fun inplaceCast(target: AsmAssignTarget, cast: TypecastExpression, position: Position) {
|
|
||||||
val outerCastDt = cast.type
|
private fun AsmAssignSource.toAstExpression(scope: PtNamedNode): PtExpression {
|
||||||
val innerCastDt = (cast.expression as? TypecastExpression)?.type
|
return when(kind) {
|
||||||
if (innerCastDt == null) {
|
SourceStorageKind.LITERALNUMBER -> this.number!!
|
||||||
// simple typecast where the value is the target
|
SourceStorageKind.VARIABLE -> {
|
||||||
when (target.datatype) {
|
val ident = PtIdentifier(scope.scopedName + '.' + asmVarname, datatype, Position.DUMMY)
|
||||||
DataType.UBYTE, DataType.BYTE -> { /* byte target can't be typecasted to anything else at all */ }
|
ident.parent = scope
|
||||||
DataType.UWORD, DataType.WORD -> {
|
ident
|
||||||
when (outerCastDt) {
|
}
|
||||||
DataType.UBYTE, DataType.BYTE -> {
|
SourceStorageKind.ARRAY -> this.array!!
|
||||||
when (target.kind) {
|
SourceStorageKind.MEMORY -> this.memory!!
|
||||||
TargetStorageKind.VARIABLE -> {
|
SourceStorageKind.EXPRESSION -> this.expression!!
|
||||||
if(asmgen.isTargetCpu(CpuType.CPU65c02))
|
SourceStorageKind.REGISTER -> {
|
||||||
asmgen.out(" stz ${target.asmVarname}+1")
|
if(register in Cx16VirtualRegisters) {
|
||||||
else
|
val ident = PtIdentifier("cx16.${register!!.name.lowercase()}", DataType.UWORD, position = scope.position)
|
||||||
asmgen.out(" lda #0 | sta ${target.asmVarname}+1")
|
ident.parent = scope
|
||||||
}
|
ident
|
||||||
TargetStorageKind.ARRAY -> {
|
} else {
|
||||||
asmgen.loadScaledArrayIndexIntoRegister(target.array!!, target.datatype, CpuRegister.Y, true)
|
throw AssemblyError("no ast expr possible for source register $register")
|
||||||
asmgen.out(" lda #0 | sta ${target.asmVarname},y")
|
}
|
||||||
}
|
}
|
||||||
TargetStorageKind.STACK -> {
|
else -> throw AssemblyError("invalid assign source kind $kind")
|
||||||
if(asmgen.isTargetCpu(CpuType.CPU65c02))
|
}
|
||||||
asmgen.out(" stz P8ESTACK_HI+1,x")
|
}
|
||||||
else
|
|
||||||
asmgen.out(" lda #0 | sta P8ESTACK_HI+1,x")
|
private fun AsmAssignTarget.toAstExpression(): PtExpression {
|
||||||
}
|
return when(kind) {
|
||||||
else -> throw AssemblyError("weird target")
|
TargetStorageKind.VARIABLE -> {
|
||||||
}
|
val ident = PtIdentifier((this.scope as PtNamedNode).scopedName + '.' + asmVarname, datatype, origAstTarget?.position ?: Position.DUMMY)
|
||||||
}
|
ident.parent = this.scope
|
||||||
DataType.UWORD, DataType.WORD, in IterableDatatypes -> {}
|
ident
|
||||||
DataType.FLOAT -> throw AssemblyError("can't cast float in-place")
|
}
|
||||||
else -> throw AssemblyError("weird cast type")
|
TargetStorageKind.ARRAY -> this.array!!
|
||||||
}
|
TargetStorageKind.MEMORY -> this.memory!!
|
||||||
}
|
TargetStorageKind.REGISTER -> {
|
||||||
DataType.FLOAT -> {
|
if(register in Cx16VirtualRegisters) {
|
||||||
if (outerCastDt != DataType.FLOAT)
|
val ident = PtIdentifier("cx16.${register!!.name.lowercase()}", DataType.UWORD, position = this.origAssign.position)
|
||||||
throw AssemblyError("in-place cast of a float makes no sense")
|
ident.parent = (this.scope as? PtNamedNode) ?: this.origAstTarget!!
|
||||||
}
|
ident
|
||||||
else -> throw AssemblyError("invalid cast target type")
|
} else {
|
||||||
}
|
throw AssemblyError("no ast expr possible for target register $register")
|
||||||
} else {
|
}
|
||||||
// typecast with nested typecast, that has the target as a value
|
}
|
||||||
// calculate singular cast that is required
|
else -> throw AssemblyError("invalid assign target kind $kind")
|
||||||
val castDt = if (outerCastDt largerThan innerCastDt) innerCastDt else outerCastDt
|
}
|
||||||
val value = (cast.expression as TypecastExpression).expression
|
|
||||||
val resultingCast = TypecastExpression(value, castDt, false, position)
|
|
||||||
inplaceCast(target, resultingCast, position)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal fun inplaceInvert(assign: AsmAssignment) {
|
|
||||||
val target = assign.target
|
|
||||||
when (assign.target.datatype) {
|
|
||||||
DataType.UBYTE -> {
|
|
||||||
when (target.kind) {
|
|
||||||
TargetStorageKind.VARIABLE -> {
|
|
||||||
asmgen.out("""
|
|
||||||
lda ${target.asmVarname}
|
|
||||||
eor #255
|
|
||||||
sta ${target.asmVarname}""")
|
|
||||||
}
|
|
||||||
TargetStorageKind.MEMORY -> {
|
|
||||||
val memory = target.memory!!
|
|
||||||
when (memory.addressExpression) {
|
|
||||||
is NumericLiteral -> {
|
|
||||||
val addr = (memory.addressExpression as NumericLiteral).number.toHex()
|
|
||||||
asmgen.out("""
|
|
||||||
lda $addr
|
|
||||||
eor #255
|
|
||||||
sta $addr""")
|
|
||||||
}
|
|
||||||
is IdentifierReference -> {
|
|
||||||
val sourceName = asmgen.loadByteFromPointerIntoA(memory.addressExpression as IdentifierReference)
|
|
||||||
asmgen.out(" eor #255")
|
|
||||||
asmgen.out(" sta ($sourceName),y")
|
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
asmgen.assignExpressionToVariable(memory.addressExpression, "P8ZP_SCRATCH_W2", DataType.UWORD, target.scope)
|
|
||||||
asmgen.out("""
|
|
||||||
ldy #0
|
|
||||||
lda (P8ZP_SCRATCH_W2),y
|
|
||||||
eor #255""")
|
|
||||||
asmgen.storeAIntoZpPointerVar("P8ZP_SCRATCH_W2")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
TargetStorageKind.REGISTER -> {
|
|
||||||
when(target.register!!) {
|
|
||||||
RegisterOrPair.A -> asmgen.out(" eor #255")
|
|
||||||
RegisterOrPair.X -> asmgen.out(" txa | eor #255 | tax")
|
|
||||||
RegisterOrPair.Y -> asmgen.out(" tya | eor #255 | tay")
|
|
||||||
else -> throw AssemblyError("invalid reg dt for byte invert")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
TargetStorageKind.STACK -> TODO("no asm gen for byte stack invert")
|
|
||||||
TargetStorageKind.ARRAY -> assignmentAsmGen.assignPrefixedExpressionToArrayElt(assign)
|
|
||||||
else -> throw AssemblyError("weird target")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DataType.UWORD -> {
|
|
||||||
when (target.kind) {
|
|
||||||
TargetStorageKind.VARIABLE -> {
|
|
||||||
asmgen.out("""
|
|
||||||
lda ${target.asmVarname}
|
|
||||||
eor #255
|
|
||||||
sta ${target.asmVarname}
|
|
||||||
lda ${target.asmVarname}+1
|
|
||||||
eor #255
|
|
||||||
sta ${target.asmVarname}+1""")
|
|
||||||
}
|
|
||||||
TargetStorageKind.REGISTER -> {
|
|
||||||
when(target.register!!) {
|
|
||||||
RegisterOrPair.AX -> asmgen.out(" pha | txa | eor #255 | tax | pla | eor #255")
|
|
||||||
RegisterOrPair.AY -> asmgen.out(" pha | tya | eor #255 | tay | pla | eor #255")
|
|
||||||
RegisterOrPair.XY -> asmgen.out(" txa | eor #255 | tax | tya | eor #255 | tay")
|
|
||||||
in Cx16VirtualRegisters -> throw AssemblyError("cx16 virtual regs should be variables, not real registers")
|
|
||||||
else -> throw AssemblyError("invalid reg dt for word invert")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
TargetStorageKind.STACK -> TODO("no asm gen for word stack invert")
|
|
||||||
TargetStorageKind.ARRAY -> assignmentAsmGen.assignPrefixedExpressionToArrayElt(assign)
|
|
||||||
else -> throw AssemblyError("weird target")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else -> throw AssemblyError("invert of invalid type")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal fun inplaceNegate(assign: AsmAssignment, ignoreDatatype: Boolean) {
|
|
||||||
val target = assign.target
|
|
||||||
val datatype = if(ignoreDatatype) {
|
|
||||||
when(target.datatype) {
|
|
||||||
DataType.UBYTE, DataType.BYTE -> DataType.BYTE
|
|
||||||
DataType.UWORD, DataType.WORD -> DataType.WORD
|
|
||||||
else -> target.datatype
|
|
||||||
}
|
|
||||||
} else target.datatype
|
|
||||||
when (datatype) {
|
|
||||||
DataType.BYTE -> {
|
|
||||||
when (target.kind) {
|
|
||||||
TargetStorageKind.VARIABLE -> {
|
|
||||||
asmgen.out("""
|
|
||||||
lda #0
|
|
||||||
sec
|
|
||||||
sbc ${target.asmVarname}
|
|
||||||
sta ${target.asmVarname}""")
|
|
||||||
}
|
|
||||||
TargetStorageKind.REGISTER -> {
|
|
||||||
when(target.register!!) {
|
|
||||||
RegisterOrPair.A -> {
|
|
||||||
if(asmgen.isTargetCpu(CpuType.CPU65c02))
|
|
||||||
asmgen.out(" eor #255 | ina")
|
|
||||||
else
|
|
||||||
asmgen.out(" eor #255 | clc | adc #1")
|
|
||||||
|
|
||||||
}
|
|
||||||
RegisterOrPair.X -> asmgen.out(" txa | eor #255 | tax | inx")
|
|
||||||
RegisterOrPair.Y -> asmgen.out(" tya | eor #255 | tay | iny")
|
|
||||||
else -> throw AssemblyError("invalid reg dt for byte negate")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
TargetStorageKind.MEMORY -> throw AssemblyError("memory is ubyte, can't negate that")
|
|
||||||
TargetStorageKind.STACK -> TODO("no asm gen for byte stack negate")
|
|
||||||
TargetStorageKind.ARRAY -> assignmentAsmGen.assignPrefixedExpressionToArrayElt(assign)
|
|
||||||
else -> throw AssemblyError("weird target")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DataType.WORD -> {
|
|
||||||
when (target.kind) {
|
|
||||||
TargetStorageKind.VARIABLE -> {
|
|
||||||
asmgen.out("""
|
|
||||||
lda #0
|
|
||||||
sec
|
|
||||||
sbc ${target.asmVarname}
|
|
||||||
sta ${target.asmVarname}
|
|
||||||
lda #0
|
|
||||||
sbc ${target.asmVarname}+1
|
|
||||||
sta ${target.asmVarname}+1""")
|
|
||||||
}
|
|
||||||
TargetStorageKind.REGISTER -> {
|
|
||||||
when(target.register!!) { //P8ZP_SCRATCH_REG
|
|
||||||
RegisterOrPair.AX -> {
|
|
||||||
asmgen.out("""
|
|
||||||
sec
|
|
||||||
eor #255
|
|
||||||
adc #0
|
|
||||||
pha
|
|
||||||
txa
|
|
||||||
eor #255
|
|
||||||
adc #0
|
|
||||||
tax
|
|
||||||
pla""")
|
|
||||||
}
|
|
||||||
RegisterOrPair.AY -> {
|
|
||||||
asmgen.out("""
|
|
||||||
sec
|
|
||||||
eor #255
|
|
||||||
adc #0
|
|
||||||
pha
|
|
||||||
tya
|
|
||||||
eor #255
|
|
||||||
adc #0
|
|
||||||
tay
|
|
||||||
pla""")
|
|
||||||
}
|
|
||||||
RegisterOrPair.XY -> {
|
|
||||||
asmgen.out("""
|
|
||||||
sec
|
|
||||||
txa
|
|
||||||
eor #255
|
|
||||||
adc #0
|
|
||||||
tax
|
|
||||||
tya
|
|
||||||
eor #255
|
|
||||||
adc #0
|
|
||||||
tay""")
|
|
||||||
}
|
|
||||||
in Cx16VirtualRegisters -> throw AssemblyError("cx16 virtual regs should be variables, not real registers")
|
|
||||||
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.ARRAY -> assignmentAsmGen.assignPrefixedExpressionToArrayElt(assign)
|
|
||||||
else -> throw AssemblyError("weird target")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DataType.FLOAT -> {
|
|
||||||
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 -> {
|
|
||||||
// simply flip the sign bit in the float
|
|
||||||
asmgen.out("""
|
|
||||||
lda ${target.asmVarname}+1
|
|
||||||
eor #$80
|
|
||||||
sta ${target.asmVarname}+1
|
|
||||||
""")
|
|
||||||
}
|
|
||||||
TargetStorageKind.STACK -> TODO("no asm gen for float stack negate")
|
|
||||||
TargetStorageKind.ARRAY -> assignmentAsmGen.assignPrefixedExpressionToArrayElt(assign)
|
|
||||||
else -> throw AssemblyError("weird target for in-place float negation")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else -> throw AssemblyError("negate of invalid type")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
60
codeGenCpu6502/test/Dummies.kt
Normal file
60
codeGenCpu6502/test/Dummies.kt
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
package prog8tests.codegencpu6502
|
||||||
|
|
||||||
|
import prog8.code.core.*
|
||||||
|
|
||||||
|
|
||||||
|
internal object DummyMemsizer : IMemSizer {
|
||||||
|
override fun memorySize(dt: DataType) = when(dt) {
|
||||||
|
in ByteDatatypes -> 1
|
||||||
|
DataType.FLOAT -> 5
|
||||||
|
else -> 2
|
||||||
|
}
|
||||||
|
override fun memorySize(arrayDt: DataType, numElements: Int) = when(arrayDt) {
|
||||||
|
DataType.ARRAY_UW -> numElements*2
|
||||||
|
DataType.ARRAY_W -> numElements*2
|
||||||
|
DataType.ARRAY_F -> numElements*5
|
||||||
|
else -> numElements
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal object DummyStringEncoder : IStringEncoding {
|
||||||
|
override fun encodeString(str: String, encoding: Encoding): List<UByte> {
|
||||||
|
return emptyList()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun decodeString(bytes: Iterable<UByte>, encoding: Encoding): String {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class ErrorReporterForTests(private val throwExceptionAtReportIfErrors: Boolean=true, private val keepMessagesAfterReporting: Boolean=false):
|
||||||
|
IErrorReporter {
|
||||||
|
|
||||||
|
val errors = mutableListOf<String>()
|
||||||
|
val warnings = mutableListOf<String>()
|
||||||
|
|
||||||
|
override fun err(msg: String, position: Position) {
|
||||||
|
errors.add("${position.toClickableStr()} $msg")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun warn(msg: String, position: Position) {
|
||||||
|
warnings.add("${position.toClickableStr()} $msg")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun noErrors(): Boolean = errors.isEmpty()
|
||||||
|
|
||||||
|
override fun report() {
|
||||||
|
warnings.forEach { println("UNITTEST COMPILATION REPORT: WARNING: $it") }
|
||||||
|
errors.forEach { println("UNITTEST COMPILATION REPORT: ERROR: $it") }
|
||||||
|
if(throwExceptionAtReportIfErrors)
|
||||||
|
finalizeNumErrors(errors.size, warnings.size)
|
||||||
|
if(!keepMessagesAfterReporting) {
|
||||||
|
clear()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun clear() {
|
||||||
|
errors.clear()
|
||||||
|
warnings.clear()
|
||||||
|
}
|
||||||
|
}
|
106
codeGenCpu6502/test/TestCodegen.kt
Normal file
106
codeGenCpu6502/test/TestCodegen.kt
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
package prog8tests.codegencpu6502
|
||||||
|
|
||||||
|
import io.kotest.core.spec.style.FunSpec
|
||||||
|
import io.kotest.matchers.shouldBe
|
||||||
|
import prog8.code.SymbolTableMaker
|
||||||
|
import prog8.code.ast.*
|
||||||
|
import prog8.code.core.*
|
||||||
|
import prog8.code.target.C64Target
|
||||||
|
import prog8.codegen.cpu6502.AsmGen6502
|
||||||
|
import java.nio.file.Files
|
||||||
|
import kotlin.io.path.Path
|
||||||
|
|
||||||
|
class TestCodegen: FunSpec({
|
||||||
|
|
||||||
|
fun getTestOptions(): CompilationOptions {
|
||||||
|
val target = C64Target()
|
||||||
|
return CompilationOptions(
|
||||||
|
OutputType.RAW,
|
||||||
|
CbmPrgLauncherType.NONE,
|
||||||
|
ZeropageType.DONTUSE,
|
||||||
|
zpReserved = emptyList(),
|
||||||
|
floats = true,
|
||||||
|
noSysInit = false,
|
||||||
|
compTarget = target,
|
||||||
|
loadAddress = target.machine.PROGRAM_LOAD_ADDRESS
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
test("augmented assign on arrays") {
|
||||||
|
//main {
|
||||||
|
// sub start() {
|
||||||
|
// ubyte[] particleX = [1,2,3]
|
||||||
|
// ubyte[] particleDX = [1,2,3]
|
||||||
|
// particleX[2] += particleDX[2]
|
||||||
|
//
|
||||||
|
// word @shared xx = 1
|
||||||
|
// xx = -xx
|
||||||
|
// xx += 42
|
||||||
|
// xx += cx16.r0
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
val codegen = AsmGen6502()
|
||||||
|
val program = PtProgram("test", DummyMemsizer, DummyStringEncoder)
|
||||||
|
val block = PtBlock("main", null, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY)
|
||||||
|
val sub = PtSub("start", emptyList(), null, Position.DUMMY)
|
||||||
|
sub.add(PtVariable("pi", DataType.UBYTE, ZeropageWish.DONTCARE, PtNumber(DataType.UBYTE, 0.0, Position.DUMMY), null, Position.DUMMY))
|
||||||
|
sub.add(PtVariable("particleX", DataType.ARRAY_UB, ZeropageWish.DONTCARE, null, 3u, Position.DUMMY))
|
||||||
|
sub.add(PtVariable("particleDX", DataType.ARRAY_UB, ZeropageWish.DONTCARE, null, 3u, Position.DUMMY))
|
||||||
|
sub.add(PtVariable("xx", DataType.WORD, ZeropageWish.DONTCARE, PtNumber(DataType.WORD, 1.0, Position.DUMMY), null, Position.DUMMY))
|
||||||
|
|
||||||
|
val assign = PtAugmentedAssign("+=", Position.DUMMY)
|
||||||
|
val target = PtAssignTarget(Position.DUMMY).also {
|
||||||
|
val targetIdx = PtArrayIndexer(DataType.UBYTE, Position.DUMMY).also { idx ->
|
||||||
|
idx.add(PtIdentifier("main.start.particleX", DataType.ARRAY_UB, Position.DUMMY))
|
||||||
|
idx.add(PtNumber(DataType.UBYTE, 2.0, Position.DUMMY))
|
||||||
|
}
|
||||||
|
it.add(targetIdx)
|
||||||
|
}
|
||||||
|
val value = PtArrayIndexer(DataType.UBYTE, Position.DUMMY)
|
||||||
|
value.add(PtIdentifier("main.start.particleDX", DataType.ARRAY_UB, Position.DUMMY))
|
||||||
|
value.add(PtNumber(DataType.UBYTE, 2.0, Position.DUMMY))
|
||||||
|
assign.add(target)
|
||||||
|
assign.add(value)
|
||||||
|
sub.add(assign)
|
||||||
|
|
||||||
|
val prefixAssign = PtAugmentedAssign("-", Position.DUMMY)
|
||||||
|
val prefixTarget = PtAssignTarget(Position.DUMMY).also {
|
||||||
|
it.add(PtIdentifier("main.start.xx", DataType.WORD, Position.DUMMY))
|
||||||
|
}
|
||||||
|
prefixAssign.add(prefixTarget)
|
||||||
|
prefixAssign.add(PtIdentifier("main.start.xx", DataType.WORD, Position.DUMMY))
|
||||||
|
sub.add(prefixAssign)
|
||||||
|
|
||||||
|
val numberAssign = PtAugmentedAssign("-=", Position.DUMMY)
|
||||||
|
val numberAssignTarget = PtAssignTarget(Position.DUMMY).also {
|
||||||
|
it.add(PtIdentifier("main.start.xx", DataType.WORD, Position.DUMMY))
|
||||||
|
}
|
||||||
|
numberAssign.add(numberAssignTarget)
|
||||||
|
numberAssign.add(PtNumber(DataType.WORD, 42.0, Position.DUMMY))
|
||||||
|
sub.add(numberAssign)
|
||||||
|
|
||||||
|
val cxregAssign = PtAugmentedAssign("+=", Position.DUMMY)
|
||||||
|
val cxregAssignTarget = PtAssignTarget(Position.DUMMY).also {
|
||||||
|
it.add(PtIdentifier("main.start.xx", DataType.WORD, Position.DUMMY))
|
||||||
|
}
|
||||||
|
cxregAssign.add(cxregAssignTarget)
|
||||||
|
cxregAssign.add(PtIdentifier("cx16.r0", DataType.UWORD, Position.DUMMY))
|
||||||
|
sub.add(cxregAssign)
|
||||||
|
|
||||||
|
block.add(sub)
|
||||||
|
program.add(block)
|
||||||
|
|
||||||
|
// define the "cx16.r0" virtual register
|
||||||
|
val cx16block = PtBlock("cx16", null, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY)
|
||||||
|
cx16block.add(PtMemMapped("r0", DataType.UWORD, 100u, null, Position.DUMMY))
|
||||||
|
program.add(cx16block)
|
||||||
|
|
||||||
|
val options = getTestOptions()
|
||||||
|
val st = SymbolTableMaker(program, options).make()
|
||||||
|
val errors = ErrorReporterForTests()
|
||||||
|
val result = codegen.generate(program, st, options, errors)!!
|
||||||
|
result.name shouldBe "test"
|
||||||
|
Files.deleteIfExists(Path("${result.name}.asm"))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
@ -3,19 +3,19 @@ package prog8.codegen.experimental
|
|||||||
import prog8.code.SymbolTable
|
import prog8.code.SymbolTable
|
||||||
import prog8.code.ast.PtProgram
|
import prog8.code.ast.PtProgram
|
||||||
import prog8.code.core.CompilationOptions
|
import prog8.code.core.CompilationOptions
|
||||||
import prog8.code.core.IAssemblyGenerator
|
|
||||||
import prog8.code.core.IAssemblyProgram
|
import prog8.code.core.IAssemblyProgram
|
||||||
|
import prog8.code.core.ICodeGeneratorBackend
|
||||||
import prog8.code.core.IErrorReporter
|
import prog8.code.core.IErrorReporter
|
||||||
import prog8.codegen.intermediate.IRCodeGen
|
import prog8.codegen.intermediate.IRCodeGen
|
||||||
import prog8.intermediate.IRFileWriter
|
import prog8.intermediate.IRFileWriter
|
||||||
|
|
||||||
class CodeGen(private val program: PtProgram,
|
class ExperiCodeGen: ICodeGeneratorBackend {
|
||||||
private val symbolTable: SymbolTable,
|
override fun generate(
|
||||||
private val options: CompilationOptions,
|
program: PtProgram,
|
||||||
private val errors: IErrorReporter
|
symbolTable: SymbolTable,
|
||||||
): IAssemblyGenerator {
|
options: CompilationOptions,
|
||||||
override fun compileToAssembly(): IAssemblyProgram? {
|
errors: IErrorReporter
|
||||||
|
): IAssemblyProgram? {
|
||||||
// you could write a code generator directly on the PtProgram AST,
|
// you could write a code generator directly on the PtProgram AST,
|
||||||
// but you can also use the Intermediate Representation to build a codegen on:
|
// but you can also use the Intermediate Representation to build a codegen on:
|
||||||
val irCodeGen = IRCodeGen(program, symbolTable, options, errors)
|
val irCodeGen = IRCodeGen(program, symbolTable, options, errors)
|
@ -3,6 +3,7 @@ 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.PrefixOperators
|
||||||
import prog8.code.core.SignedDatatypes
|
import prog8.code.core.SignedDatatypes
|
||||||
import prog8.intermediate.*
|
import prog8.intermediate.*
|
||||||
|
|
||||||
@ -12,22 +13,26 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
|||||||
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")
|
||||||
|
|
||||||
return if (assignment.isInplaceAssign)
|
return translateRegularAssign(assignment)
|
||||||
translateInplaceAssign(assignment)
|
|
||||||
else
|
|
||||||
translateRegularAssign(assignment)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun translateInplaceAssign(assignment: PtAssignment): IRCodeChunks {
|
internal fun translate(augmentedAssign: PtAugmentedAssign): IRCodeChunks {
|
||||||
|
if(augmentedAssign.target.children.single() is PtMachineRegister)
|
||||||
|
throw AssemblyError("assigning to a register should be done by just evaluating the expression into resultregister")
|
||||||
|
|
||||||
|
return translateInplaceAssign(augmentedAssign)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun translateInplaceAssign(assignment: PtAugmentedAssign): 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
|
||||||
|
|
||||||
return if(ident!=null) {
|
return if(ident!=null) {
|
||||||
assignSelfInMemory(ident.name, assignment.value, assignment)
|
assignVarAugmented(ident.name, assignment)
|
||||||
} else if(memory != null) {
|
} else if(memory != null) {
|
||||||
if(memory.address is PtNumber)
|
if(memory.address is PtNumber)
|
||||||
assignSelfInMemoryKnownAddress((memory.address as PtNumber).number.toInt(), assignment.value, assignment)
|
assignMemoryAugmented((memory.address as PtNumber).number.toInt(), assignment)
|
||||||
else
|
else
|
||||||
fallbackAssign(assignment)
|
fallbackAssign(assignment)
|
||||||
} else if(array!=null) {
|
} else if(array!=null) {
|
||||||
@ -40,120 +45,83 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun assignSelfInMemoryKnownAddress(
|
private fun assignMemoryAugmented(
|
||||||
address: Int,
|
address: Int,
|
||||||
value: PtExpression,
|
assignment: PtAugmentedAssign
|
||||||
origAssign: PtAssignment
|
|
||||||
): IRCodeChunks {
|
): IRCodeChunks {
|
||||||
|
val value = assignment.value
|
||||||
val vmDt = codeGen.irType(value.type)
|
val vmDt = codeGen.irType(value.type)
|
||||||
when(value) {
|
return when(assignment.operator) {
|
||||||
is PtIdentifier -> return emptyList() // do nothing, x=x null assignment.
|
"+" -> expressionEval.operatorPlusInplace(address, null, vmDt, value)
|
||||||
is PtMachineRegister -> return emptyList() // do nothing, reg=reg null assignment
|
"-" -> expressionEval.operatorMinusInplace(address, null, vmDt, value)
|
||||||
is PtPrefix -> return inplacePrefix(value.operator, vmDt, address, null)
|
"*" -> expressionEval.operatorMultiplyInplace(address, null, vmDt, value)
|
||||||
is PtBinaryExpression -> return inplaceBinexpr(value.operator, value.right, vmDt, value.type in SignedDatatypes, address, null, origAssign)
|
"/" -> expressionEval.operatorDivideInplace(address, null, vmDt, value.type in SignedDatatypes, value)
|
||||||
is PtMemoryByte -> {
|
"|" -> expressionEval.operatorOrInplace(address, null, vmDt, value)
|
||||||
return if (!codeGen.options.compTarget.machine.isIOAddress(address.toUInt()))
|
"&" -> expressionEval.operatorAndInplace(address, null, vmDt, value)
|
||||||
emptyList() // do nothing, mem=mem null assignment.
|
"^" -> expressionEval.operatorXorInplace(address, null, vmDt, value)
|
||||||
else {
|
"<<" -> expressionEval.operatorShiftLeftInplace(address, null, vmDt, value)
|
||||||
// read and write a (i/o) memory location to itself.
|
">>" -> expressionEval.operatorShiftRightInplace(address, null, vmDt, value.type in SignedDatatypes, value)
|
||||||
val tempReg = codeGen.registers.nextFree()
|
in PrefixOperators -> inplacePrefix(assignment.operator, vmDt, address, null)
|
||||||
val code = IRCodeChunk(null, null)
|
else -> throw AssemblyError("invalid augmented assign operator ${assignment.operator}")
|
||||||
code += IRInstruction(Opcode.LOADM, vmDt, reg1 = tempReg, value = address)
|
|
||||||
code += IRInstruction(Opcode.STOREM, vmDt, reg1 = tempReg, value = address)
|
|
||||||
listOf(code)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else -> return fallbackAssign(origAssign)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun assignSelfInMemory(
|
private fun assignVarAugmented(symbol: String, assignment: PtAugmentedAssign): IRCodeChunks {
|
||||||
symbol: String,
|
val value = assignment.value
|
||||||
value: PtExpression,
|
val valueVmDt = codeGen.irType(value.type)
|
||||||
origAssign: PtAssignment
|
return when (assignment.operator) {
|
||||||
): IRCodeChunks {
|
"+=" -> expressionEval.operatorPlusInplace(null, symbol, valueVmDt, value)
|
||||||
val vmDt = codeGen.irType(value.type)
|
"-=" -> expressionEval.operatorMinusInplace(null, symbol, valueVmDt, value)
|
||||||
return when(value) {
|
"*=" -> expressionEval.operatorMultiplyInplace(null, symbol, valueVmDt, value)
|
||||||
is PtIdentifier -> emptyList() // do nothing, x=x null assignment.
|
"/=" -> expressionEval.operatorDivideInplace(null, symbol, valueVmDt, value.type in SignedDatatypes, value)
|
||||||
is PtMachineRegister -> emptyList() // do nothing, reg=reg null assignment
|
"|=" -> expressionEval.operatorOrInplace(null, symbol, valueVmDt, value)
|
||||||
is PtPrefix -> inplacePrefix(value.operator, vmDt, null, symbol)
|
"&=" -> expressionEval.operatorAndInplace(null, symbol, valueVmDt, value)
|
||||||
is PtBinaryExpression -> inplaceBinexpr(value.operator, value.right, vmDt, value.type in SignedDatatypes, null, symbol, origAssign)
|
"^=" -> expressionEval.operatorXorInplace(null, symbol, valueVmDt, value)
|
||||||
is PtMemoryByte -> {
|
"<<=" -> expressionEval.operatorShiftLeftInplace(null, symbol, valueVmDt, value)
|
||||||
val code = IRCodeChunk(null, null)
|
">>=" -> expressionEval.operatorShiftRightInplace(null, symbol, valueVmDt, value.type in SignedDatatypes, value)
|
||||||
val tempReg = codeGen.registers.nextFree()
|
in PrefixOperators -> inplacePrefix(assignment.operator, valueVmDt, null, symbol)
|
||||||
code += IRInstruction(Opcode.LOADM, vmDt, reg1 = tempReg, labelSymbol = symbol)
|
else -> throw AssemblyError("invalid augmented assign operator ${assignment.operator}")
|
||||||
code += IRInstruction(Opcode.STOREM, vmDt, reg1 = tempReg, labelSymbol = symbol)
|
|
||||||
listOf(code)
|
|
||||||
}
|
|
||||||
|
|
||||||
else -> fallbackAssign(origAssign)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun fallbackAssign(origAssign: PtAssignment): IRCodeChunks {
|
private fun fallbackAssign(origAssign: PtAugmentedAssign): 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)
|
val normalAssign = PtAssignment(origAssign.position)
|
||||||
}
|
normalAssign.add(origAssign.target)
|
||||||
|
val value: PtExpression
|
||||||
private fun inplaceBinexpr(
|
if(origAssign.operator in PrefixOperators) {
|
||||||
operator: String,
|
value = PtPrefix(origAssign.operator, origAssign.value.type, origAssign.value.position)
|
||||||
operand: PtExpression,
|
value.add(origAssign.value)
|
||||||
vmDt: IRDataType,
|
|
||||||
signed: Boolean,
|
|
||||||
knownAddress: Int?,
|
|
||||||
symbol: String?,
|
|
||||||
origAssign: PtAssignment
|
|
||||||
): IRCodeChunks {
|
|
||||||
if(knownAddress!=null) {
|
|
||||||
when (operator) {
|
|
||||||
"+" -> return expressionEval.operatorPlusInplace(knownAddress, null, vmDt, operand)
|
|
||||||
"-" -> return expressionEval.operatorMinusInplace(knownAddress, null, vmDt, operand)
|
|
||||||
"*" -> return expressionEval.operatorMultiplyInplace(knownAddress, null, vmDt, operand)
|
|
||||||
"/" -> return expressionEval.operatorDivideInplace(knownAddress, null, vmDt, signed, operand)
|
|
||||||
"|" -> return expressionEval.operatorOrInplace(knownAddress, null, vmDt, operand)
|
|
||||||
"&" -> return expressionEval.operatorAndInplace(knownAddress, null, vmDt, operand)
|
|
||||||
"^" -> return expressionEval.operatorXorInplace(knownAddress, null, vmDt, operand)
|
|
||||||
"<<" -> return expressionEval.operatorShiftLeftInplace(knownAddress, null, vmDt, operand)
|
|
||||||
">>" -> return expressionEval.operatorShiftRightInplace(knownAddress, null, vmDt, signed, operand)
|
|
||||||
else -> {}
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
symbol!!
|
require(origAssign.operator.endsWith('='))
|
||||||
when (operator) {
|
value = PtBinaryExpression(origAssign.operator.dropLast(1), origAssign.value.type, origAssign.value.position)
|
||||||
"+" -> return expressionEval.operatorPlusInplace(null, symbol, vmDt, operand)
|
val left: PtExpression = origAssign.target.children.single() as PtExpression
|
||||||
"-" -> return expressionEval.operatorMinusInplace(null, symbol, vmDt, operand)
|
value.add(left)
|
||||||
"*" -> return expressionEval.operatorMultiplyInplace(null, symbol, vmDt, operand)
|
value.add(origAssign.value)
|
||||||
"/" -> return expressionEval.operatorDivideInplace(null, symbol, vmDt, signed, operand)
|
|
||||||
"|" -> return expressionEval.operatorOrInplace(null, symbol, vmDt, operand)
|
|
||||||
"&" -> return expressionEval.operatorAndInplace(null, symbol, vmDt, operand)
|
|
||||||
"^" -> return expressionEval.operatorXorInplace(null, symbol, vmDt, operand)
|
|
||||||
"<<" -> return expressionEval.operatorShiftLeftInplace(null, symbol, vmDt, operand)
|
|
||||||
">>" -> return expressionEval.operatorShiftRightInplace(null, symbol, vmDt, signed, operand)
|
|
||||||
else -> {}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return fallbackAssign(origAssign)
|
normalAssign.add(value)
|
||||||
|
return translateRegularAssign(normalAssign)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun inplacePrefix(operator: String, vmDt: IRDataType, knownAddress: Int?, addressSymbol: String?): IRCodeChunks {
|
private fun inplacePrefix(operator: String, vmDt: IRDataType, address: Int?, symbol: String?): IRCodeChunks {
|
||||||
val code= IRCodeChunk(null, null)
|
val code= IRCodeChunk(null, null)
|
||||||
when(operator) {
|
when(operator) {
|
||||||
"+" -> { }
|
"+" -> { }
|
||||||
"-" -> {
|
"-" -> {
|
||||||
code += if(knownAddress!=null)
|
code += if(address!=null)
|
||||||
IRInstruction(Opcode.NEGM, vmDt, value = knownAddress)
|
IRInstruction(Opcode.NEGM, vmDt, value = address)
|
||||||
else
|
else
|
||||||
IRInstruction(Opcode.NEGM, vmDt, labelSymbol = addressSymbol)
|
IRInstruction(Opcode.NEGM, vmDt, labelSymbol = symbol)
|
||||||
}
|
}
|
||||||
"~" -> {
|
"~" -> {
|
||||||
val regMask = codeGen.registers.nextFree()
|
val regMask = codeGen.registers.nextFree()
|
||||||
val mask = if(vmDt==IRDataType.BYTE) 0x00ff else 0xffff
|
val mask = if(vmDt==IRDataType.BYTE) 0x00ff else 0xffff
|
||||||
code += IRInstruction(Opcode.LOAD, vmDt, reg1=regMask, value = mask)
|
code += IRInstruction(Opcode.LOAD, vmDt, reg1=regMask, value = mask)
|
||||||
code += if(knownAddress!=null)
|
code += if(address!=null)
|
||||||
IRInstruction(Opcode.XORM, vmDt, reg1=regMask, value = knownAddress)
|
IRInstruction(Opcode.XORM, vmDt, reg1=regMask, value = address)
|
||||||
else
|
else
|
||||||
IRInstruction(Opcode.XORM, vmDt, reg1=regMask, labelSymbol = addressSymbol)
|
IRInstruction(Opcode.XORM, vmDt, reg1=regMask, labelSymbol = symbol)
|
||||||
}
|
}
|
||||||
else -> throw AssemblyError("weird prefix operator")
|
else -> throw AssemblyError("weird prefix operator")
|
||||||
}
|
}
|
||||||
|
@ -26,7 +26,6 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
|||||||
"rrestore",
|
"rrestore",
|
||||||
"rrestorex" -> emptyList() // vm doesn't have registers to save/restore
|
"rrestorex" -> emptyList() // vm doesn't have registers to save/restore
|
||||||
"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")
|
|
||||||
"msb" -> funcMsb(call, resultRegister)
|
"msb" -> funcMsb(call, resultRegister)
|
||||||
"lsb" -> funcLsb(call, resultRegister)
|
"lsb" -> funcLsb(call, resultRegister)
|
||||||
"memory" -> funcMemory(call, resultRegister)
|
"memory" -> funcMemory(call, resultRegister)
|
||||||
|
@ -988,20 +988,20 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
|||||||
if(fcall.type==DataType.FLOAT)
|
if(fcall.type==DataType.FLOAT)
|
||||||
throw AssemblyError("doesn't support float register result in asm romsub")
|
throw AssemblyError("doesn't support float register result in asm romsub")
|
||||||
val returns = callTarget.returns.single()
|
val returns = callTarget.returns.single()
|
||||||
val regStr = if(returns.registerOrPair!=null) returns.registerOrPair.toString() else returns.statusflag.toString()
|
val regStr = if(returns.register.registerOrPair!=null) returns.register.registerOrPair.toString() else returns.register.statusflag.toString()
|
||||||
addInstr(result, IRInstruction(Opcode.LOADCPU, codeGen.irType(fcall.type), reg1=resultRegister, labelSymbol = regStr), null)
|
addInstr(result, IRInstruction(Opcode.LOADCPU, codeGen.irType(fcall.type), reg1=resultRegister, labelSymbol = regStr), null)
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
val returnRegister = callTarget.returns.singleOrNull{ it.registerOrPair!=null }
|
val returnRegister = callTarget.returns.singleOrNull{ it.register.registerOrPair!=null }
|
||||||
if(returnRegister!=null) {
|
if(returnRegister!=null) {
|
||||||
// we skip the other values returned in the status flags.
|
// we skip the other values returned in the status flags.
|
||||||
val regStr = returnRegister.registerOrPair.toString()
|
val regStr = returnRegister.register.registerOrPair.toString()
|
||||||
addInstr(result, IRInstruction(Opcode.LOADCPU, codeGen.irType(fcall.type), reg1=resultRegister, labelSymbol = regStr), null)
|
addInstr(result, IRInstruction(Opcode.LOADCPU, codeGen.irType(fcall.type), reg1=resultRegister, labelSymbol = regStr), null)
|
||||||
} else {
|
} else {
|
||||||
val firstReturnRegister = callTarget.returns.firstOrNull{ it.registerOrPair!=null }
|
val firstReturnRegister = callTarget.returns.firstOrNull{ it.register.registerOrPair!=null }
|
||||||
if(firstReturnRegister!=null) {
|
if(firstReturnRegister!=null) {
|
||||||
// we just take the first register return value and ignore the rest.
|
// we just take the first register return value and ignore the rest.
|
||||||
val regStr = firstReturnRegister.registerOrPair.toString()
|
val regStr = firstReturnRegister.register.registerOrPair.toString()
|
||||||
addInstr(result, IRInstruction(Opcode.LOADCPU, codeGen.irType(fcall.type), reg1=resultRegister, labelSymbol = regStr), null)
|
addInstr(result, IRInstruction(Opcode.LOADCPU, codeGen.irType(fcall.type), reg1=resultRegister, labelSymbol = regStr), null)
|
||||||
} else {
|
} else {
|
||||||
throw AssemblyError("invalid number of return values from call")
|
throw AssemblyError("invalid number of return values from call")
|
||||||
|
@ -33,15 +33,13 @@ class IRCodeGen(
|
|||||||
if(options.evalStackBaseAddress!=null)
|
if(options.evalStackBaseAddress!=null)
|
||||||
throw AssemblyError("IR doesn't use eval-stack")
|
throw AssemblyError("IR doesn't use eval-stack")
|
||||||
|
|
||||||
if(!options.dontReinitGlobals) {
|
// collect global variables initializers
|
||||||
// collect global variables initializers
|
program.allBlocks().forEach {
|
||||||
program.allBlocks().forEach {
|
val result = mutableListOf<IRCodeChunkBase>()
|
||||||
val result = mutableListOf<IRCodeChunkBase>()
|
it.children.filterIsInstance<PtAssignment>().forEach { assign -> result += assignmentGen.translate(assign) }
|
||||||
it.children.filterIsInstance<PtAssignment>().forEach { assign -> result += assignmentGen.translate(assign) }
|
result.forEach { chunk ->
|
||||||
result.forEach { chunk ->
|
if (chunk is IRCodeChunk) irProg.addGlobalInits(chunk)
|
||||||
if (chunk is IRCodeChunk) irProg.addGlobalInits(chunk)
|
else throw AssemblyError("only expect code chunk for global inits")
|
||||||
else throw AssemblyError("only expect code chunk for global inits")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -238,6 +236,7 @@ class IRCodeGen(
|
|||||||
is PtMemMapped -> emptyList() // memmapped var should be looked up via symbol table
|
is PtMemMapped -> emptyList() // memmapped var should be looked up via symbol table
|
||||||
is PtConstant -> emptyList() // constants have all been folded into the code
|
is PtConstant -> emptyList() // constants have all been folded into the code
|
||||||
is PtAssignment -> assignmentGen.translate(node)
|
is PtAssignment -> assignmentGen.translate(node)
|
||||||
|
is PtAugmentedAssign -> assignmentGen.translate(node)
|
||||||
is PtNodeGroup -> translateGroup(node.children)
|
is PtNodeGroup -> translateGroup(node.children)
|
||||||
is PtBuiltinFunctionCall -> translateBuiltinFunc(node, 0)
|
is PtBuiltinFunctionCall -> translateBuiltinFunc(node, 0)
|
||||||
is PtFunctionCall -> expressionEval.translate(node, 0, 0)
|
is PtFunctionCall -> expressionEval.translate(node, 0, 0)
|
||||||
@ -1178,7 +1177,7 @@ class IRCodeGen(
|
|||||||
for (child in block.children) {
|
for (child in block.children) {
|
||||||
when(child) {
|
when(child) {
|
||||||
is PtNop -> { /* nothing */ }
|
is PtNop -> { /* nothing */ }
|
||||||
is PtAssignment -> { /* global variable initialization is done elsewhere */ }
|
is PtAssignment, is PtAugmentedAssign -> { /* global variable initialization is done elsewhere */ }
|
||||||
is PtVariable, is PtConstant, is PtMemMapped -> { /* vars should be looked up via symbol table */ }
|
is PtVariable, is PtConstant, is PtMemMapped -> { /* vars should be looked up via symbol table */ }
|
||||||
is PtSub -> {
|
is PtSub -> {
|
||||||
val sub = IRSubroutine(child.name, translate(child.parameters), child.returntype, child.position)
|
val sub = IRSubroutine(child.name, translate(child.parameters), child.returntype, child.position)
|
||||||
@ -1203,8 +1202,8 @@ class IRCodeGen(
|
|||||||
child.name,
|
child.name,
|
||||||
child.address,
|
child.address,
|
||||||
child.clobbers,
|
child.clobbers,
|
||||||
child.parameters.map { IRAsmSubroutine.IRAsmParam(it.second, it.first.type) }, // note: the name of the asmsub param is not used anymore.
|
child.parameters.map { IRAsmSubroutine.IRAsmParam(it.first, it.second.type) }, // note: the name of the asmsub param is not used here anymore
|
||||||
child.returnTypes.zip(child.retvalRegisters).map { IRAsmSubroutine.IRAsmParam(it.second, it.first) },
|
child.returns.map { IRAsmSubroutine.IRAsmParam(it.first, it.second)},
|
||||||
asmChunk,
|
asmChunk,
|
||||||
child.position
|
child.position
|
||||||
)
|
)
|
||||||
|
@ -3,29 +3,28 @@ package prog8.codegen.vm
|
|||||||
import prog8.code.SymbolTable
|
import prog8.code.SymbolTable
|
||||||
import prog8.code.ast.PtProgram
|
import prog8.code.ast.PtProgram
|
||||||
import prog8.code.core.CompilationOptions
|
import prog8.code.core.CompilationOptions
|
||||||
import prog8.code.core.IAssemblyGenerator
|
|
||||||
import prog8.code.core.IAssemblyProgram
|
import prog8.code.core.IAssemblyProgram
|
||||||
|
import prog8.code.core.ICodeGeneratorBackend
|
||||||
import prog8.code.core.IErrorReporter
|
import prog8.code.core.IErrorReporter
|
||||||
import prog8.codegen.intermediate.IRCodeGen
|
import prog8.codegen.intermediate.IRCodeGen
|
||||||
import prog8.intermediate.IRFileWriter
|
import prog8.intermediate.IRFileWriter
|
||||||
import prog8.intermediate.IRProgram
|
import prog8.intermediate.IRProgram
|
||||||
|
|
||||||
class VmCodeGen(private val program: PtProgram,
|
class VmCodeGen: ICodeGeneratorBackend {
|
||||||
private val symbolTable: SymbolTable,
|
override fun generate(
|
||||||
private val options: CompilationOptions,
|
program: PtProgram,
|
||||||
private val errors: IErrorReporter
|
symbolTable: SymbolTable,
|
||||||
): IAssemblyGenerator {
|
options: CompilationOptions,
|
||||||
override fun compileToAssembly(): IAssemblyProgram? {
|
errors: IErrorReporter
|
||||||
|
): IAssemblyProgram? {
|
||||||
val irCodeGen = IRCodeGen(program, symbolTable, options, errors)
|
val irCodeGen = IRCodeGen(program, symbolTable, options, errors)
|
||||||
val irProgram = irCodeGen.generate()
|
val irProgram = irCodeGen.generate()
|
||||||
|
|
||||||
return VmAssemblyProgram(irProgram.name, irProgram)
|
return VmAssemblyProgram(irProgram.name, irProgram)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
internal class VmAssemblyProgram(override val name: String, private val irProgram: IRProgram): IAssemblyProgram {
|
internal class VmAssemblyProgram(override val name: String, internal val irProgram: IRProgram): IAssemblyProgram {
|
||||||
|
|
||||||
override fun assemble(options: CompilationOptions): Boolean {
|
override fun assemble(options: CompilationOptions): Boolean {
|
||||||
// the VM reads the IR file from disk.
|
// the VM reads the IR file from disk.
|
||||||
|
@ -1,12 +1,18 @@
|
|||||||
import prog8.code.core.DataType
|
import prog8.code.core.*
|
||||||
import prog8.code.core.Encoding
|
|
||||||
import prog8.code.core.IMemSizer
|
|
||||||
import prog8.code.core.IStringEncoding
|
|
||||||
|
|
||||||
|
|
||||||
internal object DummyMemsizer : IMemSizer {
|
internal object DummyMemsizer : IMemSizer {
|
||||||
override fun memorySize(dt: DataType) = 0
|
override fun memorySize(dt: DataType) = when(dt) {
|
||||||
override fun memorySize(arrayDt: DataType, numElements: Int) = 0
|
in ByteDatatypes -> 1
|
||||||
|
DataType.FLOAT -> 5
|
||||||
|
else -> 2
|
||||||
|
}
|
||||||
|
override fun memorySize(arrayDt: DataType, numElements: Int) = when(arrayDt) {
|
||||||
|
DataType.ARRAY_UW -> numElements*2
|
||||||
|
DataType.ARRAY_W -> numElements*2
|
||||||
|
DataType.ARRAY_F -> numElements*5
|
||||||
|
else -> numElements
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal object DummyStringEncoder : IStringEncoding {
|
internal object DummyStringEncoder : IStringEncoding {
|
||||||
@ -14,7 +20,39 @@ internal object DummyStringEncoder : IStringEncoding {
|
|||||||
return emptyList()
|
return emptyList()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun decodeString(bytes: List<UByte>, encoding: Encoding): String {
|
override fun decodeString(bytes: Iterable<UByte>, encoding: Encoding): String {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal class ErrorReporterForTests(private val throwExceptionAtReportIfErrors: Boolean=true, private val keepMessagesAfterReporting: Boolean=false):
|
||||||
|
IErrorReporter {
|
||||||
|
|
||||||
|
val errors = mutableListOf<String>()
|
||||||
|
val warnings = mutableListOf<String>()
|
||||||
|
|
||||||
|
override fun err(msg: String, position: Position) {
|
||||||
|
errors.add("${position.toClickableStr()} $msg")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun warn(msg: String, position: Position) {
|
||||||
|
warnings.add("${position.toClickableStr()} $msg")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun noErrors(): Boolean = errors.isEmpty()
|
||||||
|
|
||||||
|
override fun report() {
|
||||||
|
warnings.forEach { println("UNITTEST COMPILATION REPORT: WARNING: $it") }
|
||||||
|
errors.forEach { println("UNITTEST COMPILATION REPORT: ERROR: $it") }
|
||||||
|
if(throwExceptionAtReportIfErrors)
|
||||||
|
finalizeNumErrors(errors.size, warnings.size)
|
||||||
|
if(!keepMessagesAfterReporting) {
|
||||||
|
clear()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun clear() {
|
||||||
|
errors.clear()
|
||||||
|
warnings.clear()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
103
codeGenIntermediate/test/TestVmCodeGen.kt
Normal file
103
codeGenIntermediate/test/TestVmCodeGen.kt
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
import io.kotest.core.spec.style.FunSpec
|
||||||
|
import io.kotest.matchers.ints.shouldBeGreaterThan
|
||||||
|
import prog8.code.SymbolTableMaker
|
||||||
|
import prog8.code.ast.*
|
||||||
|
import prog8.code.core.*
|
||||||
|
import prog8.code.target.VMTarget
|
||||||
|
import prog8.codegen.vm.VmAssemblyProgram
|
||||||
|
import prog8.codegen.vm.VmCodeGen
|
||||||
|
import prog8.intermediate.IRSubroutine
|
||||||
|
|
||||||
|
class TestVmCodeGen: FunSpec({
|
||||||
|
|
||||||
|
fun getTestOptions(): CompilationOptions {
|
||||||
|
val target = VMTarget()
|
||||||
|
return CompilationOptions(
|
||||||
|
OutputType.RAW,
|
||||||
|
CbmPrgLauncherType.NONE,
|
||||||
|
ZeropageType.DONTUSE,
|
||||||
|
zpReserved = emptyList(),
|
||||||
|
floats = true,
|
||||||
|
noSysInit = false,
|
||||||
|
compTarget = target,
|
||||||
|
loadAddress = target.machine.PROGRAM_LOAD_ADDRESS
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
test("augmented assigns") {
|
||||||
|
//main {
|
||||||
|
// sub start() {
|
||||||
|
// ubyte[] particleX = [1,2,3]
|
||||||
|
// ubyte[] particleDX = [1,2,3]
|
||||||
|
// particleX[2] += particleDX[2]
|
||||||
|
//
|
||||||
|
// word @shared xx = 1
|
||||||
|
// xx = -xx
|
||||||
|
// xx += 42
|
||||||
|
// xx += cx16.r0
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
val codegen = VmCodeGen()
|
||||||
|
val program = PtProgram("test", DummyMemsizer, DummyStringEncoder)
|
||||||
|
val block = PtBlock("main", null, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY)
|
||||||
|
val sub = PtSub("start", emptyList(), null, Position.DUMMY)
|
||||||
|
sub.add(PtVariable("pi", DataType.UBYTE, ZeropageWish.DONTCARE, PtNumber(DataType.UBYTE, 0.0, Position.DUMMY), null, Position.DUMMY))
|
||||||
|
sub.add(PtVariable("particleX", DataType.ARRAY_UB, ZeropageWish.DONTCARE, null, 3u, Position.DUMMY))
|
||||||
|
sub.add(PtVariable("particleDX", DataType.ARRAY_UB, ZeropageWish.DONTCARE, null, 3u, Position.DUMMY))
|
||||||
|
sub.add(PtVariable("xx", DataType.WORD, ZeropageWish.DONTCARE, PtNumber(DataType.WORD, 1.0, Position.DUMMY), null, Position.DUMMY))
|
||||||
|
|
||||||
|
val assign = PtAugmentedAssign("+=", Position.DUMMY)
|
||||||
|
val target = PtAssignTarget(Position.DUMMY).also {
|
||||||
|
val targetIdx = PtArrayIndexer(DataType.UBYTE, Position.DUMMY).also { idx ->
|
||||||
|
idx.add(PtIdentifier("main.start.particleX", DataType.ARRAY_UB, Position.DUMMY))
|
||||||
|
idx.add(PtNumber(DataType.UBYTE, 2.0, Position.DUMMY))
|
||||||
|
}
|
||||||
|
it.add(targetIdx)
|
||||||
|
}
|
||||||
|
val value = PtArrayIndexer(DataType.UBYTE, Position.DUMMY)
|
||||||
|
value.add(PtIdentifier("main.start.particleDX", DataType.ARRAY_UB, Position.DUMMY))
|
||||||
|
value.add(PtNumber(DataType.UBYTE, 2.0, Position.DUMMY))
|
||||||
|
assign.add(target)
|
||||||
|
assign.add(value)
|
||||||
|
sub.add(assign)
|
||||||
|
|
||||||
|
val prefixAssign = PtAugmentedAssign("-", Position.DUMMY)
|
||||||
|
val prefixTarget = PtAssignTarget(Position.DUMMY).also {
|
||||||
|
it.add(PtIdentifier("main.start.xx", DataType.WORD, Position.DUMMY))
|
||||||
|
}
|
||||||
|
prefixAssign.add(prefixTarget)
|
||||||
|
prefixAssign.add(PtIdentifier("main.start.xx", DataType.WORD, Position.DUMMY))
|
||||||
|
sub.add(prefixAssign)
|
||||||
|
|
||||||
|
val numberAssign = PtAugmentedAssign("+=", Position.DUMMY)
|
||||||
|
val numberAssignTarget = PtAssignTarget(Position.DUMMY).also {
|
||||||
|
it.add(PtIdentifier("main.start.xx", DataType.WORD, Position.DUMMY))
|
||||||
|
}
|
||||||
|
numberAssign.add(numberAssignTarget)
|
||||||
|
numberAssign.add(PtNumber(DataType.WORD, 42.0, Position.DUMMY))
|
||||||
|
sub.add(numberAssign)
|
||||||
|
|
||||||
|
val cxregAssign = PtAugmentedAssign("+=", Position.DUMMY)
|
||||||
|
val cxregAssignTarget = PtAssignTarget(Position.DUMMY).also {
|
||||||
|
it.add(PtIdentifier("main.start.xx", DataType.WORD, Position.DUMMY))
|
||||||
|
}
|
||||||
|
cxregAssign.add(cxregAssignTarget)
|
||||||
|
cxregAssign.add(PtIdentifier("cx16.r0", DataType.UWORD, Position.DUMMY))
|
||||||
|
sub.add(cxregAssign)
|
||||||
|
|
||||||
|
block.add(sub)
|
||||||
|
program.add(block)
|
||||||
|
|
||||||
|
// define the "cx16.r0" virtual register
|
||||||
|
val cx16block = PtBlock("cx16", null, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY)
|
||||||
|
cx16block.add(PtMemMapped("r0", DataType.UWORD, 100u, null, Position.DUMMY))
|
||||||
|
program.add(cx16block)
|
||||||
|
|
||||||
|
val options = getTestOptions()
|
||||||
|
val st = SymbolTableMaker(program, options).make()
|
||||||
|
val errors = ErrorReporterForTests()
|
||||||
|
val result = codegen.generate(program, st, options, errors) as VmAssemblyProgram
|
||||||
|
val irChunks = (result.irProgram.blocks.first().children.single() as IRSubroutine).chunks
|
||||||
|
irChunks.size shouldBeGreaterThan 4
|
||||||
|
}
|
||||||
|
})
|
@ -4,9 +4,6 @@ import prog8.ast.IStatementContainer
|
|||||||
import prog8.ast.Node
|
import prog8.ast.Node
|
||||||
import prog8.ast.Program
|
import prog8.ast.Program
|
||||||
import prog8.ast.expressions.BinaryExpression
|
import prog8.ast.expressions.BinaryExpression
|
||||||
import prog8.ast.expressions.IdentifierReference
|
|
||||||
import prog8.ast.expressions.TypecastExpression
|
|
||||||
import prog8.ast.getTempVar
|
|
||||||
import prog8.ast.statements.AssignTarget
|
import prog8.ast.statements.AssignTarget
|
||||||
import prog8.ast.statements.Assignment
|
import prog8.ast.statements.Assignment
|
||||||
import prog8.ast.statements.AssignmentOrigin
|
import prog8.ast.statements.AssignmentOrigin
|
||||||
@ -31,23 +28,6 @@ class BinExprSplitter(private val program: Program, private val options: Compila
|
|||||||
val binExpr = assignment.value as? BinaryExpression
|
val binExpr = assignment.value as? BinaryExpression
|
||||||
if (binExpr != null) {
|
if (binExpr != null) {
|
||||||
|
|
||||||
/*
|
|
||||||
|
|
||||||
Reduce the complexity of a (binary) expression that has to be evaluated on the eval stack,
|
|
||||||
by attempting to splitting it up into individual simple steps.
|
|
||||||
We only consider a binary expression *one* level deep (so the operands must not be a combined expression)
|
|
||||||
|
|
||||||
|
|
||||||
X = BinExpr X = LeftExpr
|
|
||||||
<operator> followed by
|
|
||||||
/ \ IF 'X' not used X = BinExpr
|
|
||||||
/ \ IN expression ==> <operator>
|
|
||||||
/ \ / \
|
|
||||||
LeftExpr. RightExpr. / \
|
|
||||||
X RightExpr.
|
|
||||||
|
|
||||||
|
|
||||||
*/
|
|
||||||
if(binExpr.operator in AugmentAssignmentOperators && isSimpleTarget(assignment.target)) {
|
if(binExpr.operator in AugmentAssignmentOperators && isSimpleTarget(assignment.target)) {
|
||||||
if(assignment.target isSameAs binExpr.right)
|
if(assignment.target isSameAs binExpr.right)
|
||||||
return noModifications
|
return noModifications
|
||||||
@ -60,20 +40,6 @@ X = BinExpr X = LeftExpr
|
|||||||
val rightBx = binExpr.right as? BinaryExpression
|
val rightBx = binExpr.right as? BinaryExpression
|
||||||
if(rightBx!=null && (!rightBx.left.isSimple || !rightBx.right.isSimple))
|
if(rightBx!=null && (!rightBx.left.isSimple || !rightBx.right.isSimple))
|
||||||
return noModifications
|
return noModifications
|
||||||
|
|
||||||
// TODO below attempts to remove stack-based evaluated expressions, but often the resulting code is BIGGER, and SLOWER.
|
|
||||||
// val dt = assignment.target.inferType(program)
|
|
||||||
// if(!dt.isInteger)
|
|
||||||
// return noModifications
|
|
||||||
// val tempVar = IdentifierReference(getTempVarName(dt), binExpr.right.position)
|
|
||||||
// val assignTempVar = Assignment(
|
|
||||||
// AssignTarget(tempVar, null, null, binExpr.right.position),
|
|
||||||
// binExpr.right, binExpr.right.position
|
|
||||||
// )
|
|
||||||
// return listOf(
|
|
||||||
// IAstModification.InsertBefore(assignment, assignTempVar, assignment.parent as IStatementContainer),
|
|
||||||
// IAstModification.ReplaceNode(binExpr.right, tempVar.copy(), binExpr)
|
|
||||||
// )
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(binExpr.right.isSimple) {
|
if(binExpr.right.isSimple) {
|
||||||
@ -87,30 +53,10 @@ X = BinExpr X = LeftExpr
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO further unraveling of binary expression trees into flat statements.
|
// Further unraveling of binary expressions is really complicated here and
|
||||||
// however this should probably be done in a more generic way to also work on
|
// often results in much bigger code, thereby defeating the purpose a bit.
|
||||||
// the expressiontrees that are not used in an assignment statement...
|
// All in all this should probably be fixed in a better code generation backend
|
||||||
}
|
// that doesn't require this at all.
|
||||||
|
|
||||||
val typecast = assignment.value as? TypecastExpression
|
|
||||||
if(typecast!=null) {
|
|
||||||
val origExpr = typecast.expression as? BinaryExpression
|
|
||||||
if(origExpr!=null && options.compTarget.name!=VMTarget.NAME) {
|
|
||||||
// it's a typecast of a binary expression.
|
|
||||||
// we can see if we can unwrap the binary expression by working on a new temporary variable
|
|
||||||
// (that has the type of the expression), and then finally doing the typecast.
|
|
||||||
// Once it's outside the typecast, the regular splitting can commence.
|
|
||||||
val tempvarDt = origExpr.inferType(program).getOr(DataType.UNDEFINED)
|
|
||||||
val (tempVarName, _) = program.getTempVar(tempvarDt)
|
|
||||||
val assignTempVar = Assignment(
|
|
||||||
AssignTarget(IdentifierReference(tempVarName, typecast.position), null, null, typecast.position),
|
|
||||||
typecast.expression, AssignmentOrigin.OPTIMIZER, typecast.position
|
|
||||||
)
|
|
||||||
return listOf(
|
|
||||||
IAstModification.InsertBefore(assignment, assignTempVar, parent as IStatementContainer),
|
|
||||||
IAstModification.ReplaceNode(typecast.expression, IdentifierReference(tempVarName, typecast.position), typecast)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return noModifications
|
return noModifications
|
||||||
|
@ -354,9 +354,11 @@ class ConstantFoldingOptimizer(private val program: Program) : AstWalker() {
|
|||||||
if(decl.type== VarDeclType.CONST && numval!=null) {
|
if(decl.type== VarDeclType.CONST && numval!=null) {
|
||||||
val valueDt = numval.inferType(program)
|
val valueDt = numval.inferType(program)
|
||||||
if(valueDt isnot decl.datatype) {
|
if(valueDt isnot decl.datatype) {
|
||||||
val cast = numval.cast(decl.datatype)
|
if(decl.datatype!=DataType.BOOL || valueDt.isnot(DataType.UBYTE)) {
|
||||||
if(cast.isValid)
|
val cast = numval.cast(decl.datatype)
|
||||||
return listOf(IAstModification.ReplaceNode(numval, cast.valueOrZero(), decl))
|
if (cast.isValid)
|
||||||
|
return listOf(IAstModification.ReplaceNode(numval, cast.valueOrZero(), decl))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return noModifications
|
return noModifications
|
||||||
@ -386,9 +388,9 @@ class ConstantFoldingOptimizer(private val program: Program) : AstWalker() {
|
|||||||
subleftIsConst: Boolean,
|
subleftIsConst: Boolean,
|
||||||
subrightIsConst: Boolean): IAstModification?
|
subrightIsConst: Boolean): IAstModification?
|
||||||
{
|
{
|
||||||
// NOTE: THIS IS ONLY VALID ON FLOATING POINT CONSTANTS
|
// NOTE: THESE REORDERINGS ARE ONLY VALID FOR FLOATING POINT CONSTANTS
|
||||||
|
// TODO: this implements only a small set of possible reorderings at this time, we could perhaps add more
|
||||||
|
|
||||||
// TODO: this implements only a small set of possible reorderings at this time, we could think of more
|
|
||||||
if(expr.operator==subExpr.operator) {
|
if(expr.operator==subExpr.operator) {
|
||||||
// both operators are the same.
|
// both operators are the same.
|
||||||
|
|
||||||
|
@ -18,7 +18,7 @@ import kotlin.math.abs
|
|||||||
import kotlin.math.log2
|
import kotlin.math.log2
|
||||||
import kotlin.math.pow
|
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
|
||||||
|
|
||||||
class ExpressionSimplifier(private val program: Program,
|
class ExpressionSimplifier(private val program: Program,
|
||||||
private val errors: IErrorReporter,
|
private val errors: IErrorReporter,
|
||||||
@ -187,12 +187,37 @@ class ExpressionSimplifier(private val program: Program,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// X <= Y-1 ---> X<Y , X >= Y+1 ---> X>Y
|
||||||
|
if(leftDt in IntegerDatatypes && rightDt in IntegerDatatypes) {
|
||||||
|
val rightExpr = expr.right as? BinaryExpression
|
||||||
|
if(rightExpr!=null && rightExpr.right.constValue(program)?.number==1.0) {
|
||||||
|
if (expr.operator == "<=" && rightExpr.operator == "-") {
|
||||||
|
expr.operator = "<"
|
||||||
|
return listOf(IAstModification.ReplaceNode(rightExpr, rightExpr.left, expr))
|
||||||
|
} else if (expr.operator == ">=" && rightExpr.operator == "+") {
|
||||||
|
expr.operator = ">"
|
||||||
|
return listOf(IAstModification.ReplaceNode(rightExpr, rightExpr.left, expr))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if(leftDt!=DataType.FLOAT && expr.operator == ">=" && rightVal?.number == 1.0) {
|
if(leftDt!=DataType.FLOAT && expr.operator == ">=" && rightVal?.number == 1.0) {
|
||||||
// for integers: x >= 1 --> x > 0
|
// for integers: x >= 1 --> x > 0
|
||||||
expr.operator = ">"
|
expr.operator = ">"
|
||||||
return listOf(IAstModification.ReplaceNode(expr.right, NumericLiteral.optimalInteger(0, expr.right.position), expr))
|
return listOf(IAstModification.ReplaceNode(expr.right, NumericLiteral.optimalInteger(0, expr.right.position), expr))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// for signed integers: X <= -1 => X<0 , X > -1 => X>=0
|
||||||
|
if(leftDt in SignedDatatypes && leftDt!=DataType.FLOAT && rightVal?.number==-1.0) {
|
||||||
|
if(expr.operator=="<=") {
|
||||||
|
expr.operator = "<"
|
||||||
|
return listOf(IAstModification.ReplaceNode(expr.right, NumericLiteral(rightDt, 0.0, expr.right.position), expr))
|
||||||
|
} else if(expr.operator==">") {
|
||||||
|
expr.operator = ">="
|
||||||
|
return listOf(IAstModification.ReplaceNode(expr.right, NumericLiteral(rightDt, 0.0, expr.right.position), expr))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (leftDt == DataType.UBYTE || leftDt == DataType.UWORD) {
|
if (leftDt == DataType.UBYTE || leftDt == DataType.UWORD) {
|
||||||
if(expr.operator == ">=" && rightVal?.number == 0.0) {
|
if(expr.operator == ">=" && rightVal?.number == 0.0) {
|
||||||
// unsigned >= 0 --> true
|
// unsigned >= 0 --> true
|
||||||
|
@ -118,9 +118,11 @@ class Inliner(val program: Program): AstWalker() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun makeFullyScoped(identifier: IdentifierReference) {
|
private fun makeFullyScoped(identifier: IdentifierReference) {
|
||||||
val scoped = (identifier.targetStatement(program)!! as INamedStatement).scopedName
|
identifier.targetStatement(program)?.let { target ->
|
||||||
val scopedIdent = IdentifierReference(scoped, identifier.position)
|
val scoped = (target as INamedStatement).scopedName
|
||||||
modifications += IAstModification.ReplaceNode(identifier, scopedIdent, identifier.parent)
|
val scopedIdent = IdentifierReference(scoped, identifier.position)
|
||||||
|
modifications += IAstModification.ReplaceNode(identifier, scopedIdent, identifier.parent)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun makeFullyScoped(call: BuiltinFunctionCallStatement) {
|
private fun makeFullyScoped(call: BuiltinFunctionCallStatement) {
|
||||||
@ -130,27 +132,30 @@ class Inliner(val program: Program): AstWalker() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun makeFullyScoped(call: FunctionCallStatement) {
|
private fun makeFullyScoped(call: FunctionCallStatement) {
|
||||||
val sub = call.target.targetSubroutine(program)!!
|
call.target.targetSubroutine(program)?.let { sub ->
|
||||||
val scopedName = IdentifierReference(sub.scopedName, call.target.position)
|
val scopedName = IdentifierReference(sub.scopedName, call.target.position)
|
||||||
val scopedArgs = makeScopedArgs(call.args)
|
val scopedArgs = makeScopedArgs(call.args)
|
||||||
val scopedCall = FunctionCallStatement(scopedName, scopedArgs.toMutableList(), call.void, call.position)
|
val scopedCall = FunctionCallStatement(scopedName, scopedArgs.toMutableList(), call.void, call.position)
|
||||||
modifications += IAstModification.ReplaceNode(call, scopedCall, call.parent)
|
modifications += IAstModification.ReplaceNode(call, scopedCall, call.parent)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun makeFullyScoped(call: BuiltinFunctionCall) {
|
private fun makeFullyScoped(call: BuiltinFunctionCall) {
|
||||||
val sub = call.target.targetSubroutine(program)!!
|
call.target.targetSubroutine(program)?.let { sub ->
|
||||||
val scopedName = IdentifierReference(sub.scopedName, call.target.position)
|
val scopedName = IdentifierReference(sub.scopedName, call.target.position)
|
||||||
val scopedArgs = makeScopedArgs(call.args)
|
val scopedArgs = makeScopedArgs(call.args)
|
||||||
val scopedCall = BuiltinFunctionCall(scopedName, scopedArgs.toMutableList(), call.position)
|
val scopedCall = BuiltinFunctionCall(scopedName, scopedArgs.toMutableList(), call.position)
|
||||||
modifications += IAstModification.ReplaceNode(call, scopedCall, call.parent)
|
modifications += IAstModification.ReplaceNode(call, scopedCall, call.parent)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun makeFullyScoped(call: FunctionCallExpression) {
|
private fun makeFullyScoped(call: FunctionCallExpression) {
|
||||||
val sub = call.target.targetSubroutine(program)!!
|
call.target.targetSubroutine(program)?.let { sub ->
|
||||||
val scopedName = IdentifierReference(sub.scopedName, call.target.position)
|
val scopedName = IdentifierReference(sub.scopedName, call.target.position)
|
||||||
val scopedArgs = makeScopedArgs(call.args)
|
val scopedArgs = makeScopedArgs(call.args)
|
||||||
val scopedCall = FunctionCallExpression(scopedName, scopedArgs.toMutableList(), call.position)
|
val scopedCall = FunctionCallExpression(scopedName, scopedArgs.toMutableList(), call.position)
|
||||||
modifications += IAstModification.ReplaceNode(call, scopedCall, call.parent)
|
modifications += IAstModification.ReplaceNode(call, scopedCall, call.parent)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun makeScopedArgs(args: List<Expression>): List<Expression> {
|
private fun makeScopedArgs(args: List<Expression>): List<Expression> {
|
||||||
|
@ -120,17 +120,6 @@ asmsub GIVAYFAY (uword value @ AY) clobbers(A,X,Y) {
|
|||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
asmsub FTOSWRDAY () clobbers(X) -> uword @ AY {
|
|
||||||
; ---- fac1 to signed word in A/Y
|
|
||||||
%asm {{
|
|
||||||
jsr FTOSWORDYA ; note the inverse Y/A order
|
|
||||||
sta P8ZP_SCRATCH_B1
|
|
||||||
tya
|
|
||||||
ldy P8ZP_SCRATCH_B1
|
|
||||||
rts
|
|
||||||
}}
|
|
||||||
}
|
|
||||||
|
|
||||||
asmsub GETADRAY () clobbers(X) -> uword @ AY {
|
asmsub GETADRAY () clobbers(X) -> uword @ AY {
|
||||||
; ---- fac1 to unsigned word in A/Y
|
; ---- fac1 to unsigned word in A/Y
|
||||||
%asm {{
|
%asm {{
|
||||||
|
@ -28,10 +28,6 @@ romsub $bc0c = MOVAF() clobbers(A,X) ; copy fac1 to fac2
|
|||||||
romsub $bc0f = MOVEF() clobbers(A,X) ; copy fac1 to fac2
|
romsub $bc0f = MOVEF() clobbers(A,X) ; copy fac1 to fac2
|
||||||
romsub $bbd4 = MOVMF(uword mflpt @ XY) clobbers(A,Y) ; store fac1 to memory X/Y as 5-byte mflpt
|
romsub $bbd4 = MOVMF(uword mflpt @ XY) clobbers(A,Y) ; store fac1 to memory X/Y as 5-byte mflpt
|
||||||
|
|
||||||
; fac1-> signed word in Y/A (might throw ILLEGAL QUANTITY)
|
|
||||||
; (tip: use floats.FTOSWRDAY to get A/Y output; lo/hi switched to normal little endian order)
|
|
||||||
romsub $b1aa = FTOSWORDYA() clobbers(X) -> ubyte @ Y, ubyte @ A ; note: calls AYINT.
|
|
||||||
|
|
||||||
; fac1 -> unsigned word in Y/A (might throw ILLEGAL QUANTITY) (result also in $14/15)
|
; fac1 -> unsigned word in Y/A (might throw ILLEGAL QUANTITY) (result also in $14/15)
|
||||||
; (tip: use floats.GETADRAY to get A/Y output; lo/hi switched to normal little endian order)
|
; (tip: use floats.GETADRAY to get A/Y output; lo/hi switched to normal little endian order)
|
||||||
romsub $b7f7 = GETADR() clobbers(X) -> ubyte @ Y, ubyte @ A
|
romsub $b7f7 = GETADR() clobbers(X) -> ubyte @ Y, ubyte @ A
|
||||||
@ -147,17 +143,6 @@ asmsub GIVAYFAY (uword value @ AY) clobbers(A,X,Y) {
|
|||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
asmsub FTOSWRDAY () clobbers(X) -> uword @ AY {
|
|
||||||
; ---- fac1 to signed word in A/Y
|
|
||||||
%asm {{
|
|
||||||
jsr FTOSWORDYA ; note the inverse Y/A order
|
|
||||||
sta P8ZP_SCRATCH_REG
|
|
||||||
tya
|
|
||||||
ldy P8ZP_SCRATCH_REG
|
|
||||||
rts
|
|
||||||
}}
|
|
||||||
}
|
|
||||||
|
|
||||||
asmsub GETADRAY () clobbers(X) -> uword @ AY {
|
asmsub GETADRAY () clobbers(X) -> uword @ AY {
|
||||||
; ---- fac1 to unsigned word in A/Y
|
; ---- fac1 to unsigned word in A/Y
|
||||||
%asm {{
|
%asm {{
|
||||||
|
@ -521,7 +521,7 @@ asmsub uword2decimal (uword value @AY) -> ubyte @Y, ubyte @A, ubyte @X {
|
|||||||
|
|
||||||
;Convert 16 bit Hex to Decimal (0-65535) Rev 2
|
;Convert 16 bit Hex to Decimal (0-65535) Rev 2
|
||||||
;By Omegamatrix Further optimizations by tepples
|
;By Omegamatrix Further optimizations by tepples
|
||||||
; routine from https://forums.nesdev.org/viewtopic.php?f=2&t=11341&start=15
|
; routine from https://forums.nesdev.org/viewtopic.php?p=130363&sid=1944ba8bac4d6afa9c02e3cc42304e6b#p130363
|
||||||
|
|
||||||
;HexToDec99
|
;HexToDec99
|
||||||
; start in A
|
; start in A
|
||||||
|
@ -119,17 +119,6 @@ asmsub GIVAYFAY (uword value @ AY) clobbers(A,X,Y) {
|
|||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
asmsub FTOSWRDAY () clobbers(X) -> uword @ AY {
|
|
||||||
; ---- fac1 to signed word in A/Y
|
|
||||||
%asm {{
|
|
||||||
jsr FTOSWORDYA ; note the inverse Y/A order
|
|
||||||
sta P8ZP_SCRATCH_B1
|
|
||||||
tya
|
|
||||||
ldy P8ZP_SCRATCH_B1
|
|
||||||
rts
|
|
||||||
}}
|
|
||||||
}
|
|
||||||
|
|
||||||
asmsub GETADRAY () clobbers(X) -> uword @ AY {
|
asmsub GETADRAY () clobbers(X) -> uword @ AY {
|
||||||
; ---- fac1 to unsigned word in A/Y
|
; ---- fac1 to unsigned word in A/Y
|
||||||
%asm {{
|
%asm {{
|
||||||
|
@ -454,20 +454,19 @@ inline asmsub getrombank() -> ubyte @A {
|
|||||||
}
|
}
|
||||||
|
|
||||||
inline asmsub getrambank() -> ubyte @A {
|
inline asmsub getrambank() -> ubyte @A {
|
||||||
; -- get the current ram bank
|
; -- get the current RAM bank
|
||||||
%asm {{
|
%asm {{
|
||||||
lda $00
|
lda $00
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
asmsub numbanks() -> uword @AY {
|
asmsub numbanks() -> uword @AY {
|
||||||
; -- uses MEMTOP's cx16 extension to query the number of available RAM banks. (each is 8 Kb)
|
; -- Returns the number of available RAM banks according to the kernal (each bank is 8 Kb).
|
||||||
; Note that the number of such banks can be bigger than 255 so a word is returned, but mostly
|
; Note that the number of such banks can be 256 so a word is returned.
|
||||||
; the A register could suffice as the lsb.
|
; But just looking at the A register (the LSB of the result word) could suffice if you know that A=0 means 256 banks:
|
||||||
; The maximum number of banks is 256 = 2 Megabytes of banked Ram aka Hiram. (Y=1 and A=0 in this case).
|
; The maximum number of RAM banks in the X16 is currently 256 (2 Megabytes of banked RAM).
|
||||||
; MEMTOP itself reports 0 in this case which we change into 256 for convenience.
|
; Kernal's MEMTOP routine reports 0 in this case but that doesn't mean 'zero banks', instead it means 256 banks,
|
||||||
; It reporting 0 doesn't mean 'zero banks', instead it means 256 banks (=2Mb banked RAM),
|
; as there is no X16 without at least 1 page of banked RAM. So this routine returns 256 instead of 0.
|
||||||
; as there is no X16 without at least 1 page of banked RAM.
|
|
||||||
%asm {{
|
%asm {{
|
||||||
phx
|
phx
|
||||||
sec
|
sec
|
||||||
@ -634,13 +633,17 @@ asmsub init_system() {
|
|||||||
; Called automatically by the loader program logic.
|
; Called automatically by the loader program logic.
|
||||||
%asm {{
|
%asm {{
|
||||||
sei
|
sei
|
||||||
|
lda #0
|
||||||
|
tax
|
||||||
|
tay
|
||||||
|
jsr cx16.mouse_config ; disable mouse
|
||||||
cld
|
cld
|
||||||
lda VERA_DC_VIDEO
|
lda VERA_DC_VIDEO
|
||||||
and #%00000111 ; retain chroma + output mode
|
and #%00000111 ; retain chroma + output mode
|
||||||
sta P8ZP_SCRATCH_REG
|
sta P8ZP_SCRATCH_REG
|
||||||
lda #$80
|
lda #$80
|
||||||
sta VERA_CTRL ; reset vera
|
sta VERA_CTRL ; reset vera
|
||||||
stz $01 ; select rom bank 0 (enable kernal)
|
stz $01 ; rom bank 0 (kernal)
|
||||||
jsr c64.IOINIT
|
jsr c64.IOINIT
|
||||||
jsr c64.RESTOR
|
jsr c64.RESTOR
|
||||||
jsr c64.CINT
|
jsr c64.CINT
|
||||||
@ -650,8 +653,9 @@ asmsub init_system() {
|
|||||||
sta VERA_DC_VIDEO ; keep old output mode
|
sta VERA_DC_VIDEO ; keep old output mode
|
||||||
lda #$90 ; black
|
lda #$90 ; black
|
||||||
jsr c64.CHROUT
|
jsr c64.CHROUT
|
||||||
lda #1 ; swap fg/bg
|
lda #1
|
||||||
jsr c64.CHROUT
|
sta $00 ; select ram bank 1
|
||||||
|
jsr c64.CHROUT ; swap fg/bg
|
||||||
lda #$9e ; yellow
|
lda #$9e ; yellow
|
||||||
jsr c64.CHROUT
|
jsr c64.CHROUT
|
||||||
lda #147 ; clear screen
|
lda #147 ; clear screen
|
||||||
@ -684,7 +688,7 @@ asmsub cleanup_at_exit() {
|
|||||||
lda #1
|
lda #1
|
||||||
sta $00 ; ram bank 1
|
sta $00 ; ram bank 1
|
||||||
lda #4
|
lda #4
|
||||||
sta $01 ; rom bank 4 (kernal)
|
sta $01 ; rom bank 4 (basic)
|
||||||
stz $2d ; hack to reset machine code monitor bank to 0
|
stz $2d ; hack to reset machine code monitor bank to 0
|
||||||
rts
|
rts
|
||||||
}}
|
}}
|
||||||
@ -714,8 +718,7 @@ _modified jsr $ffff ; modified
|
|||||||
lda _use_kernal
|
lda _use_kernal
|
||||||
bne +
|
bne +
|
||||||
; end irq processing - don't use kernal's irq handling
|
; end irq processing - don't use kernal's irq handling
|
||||||
lda cx16.VERA_ISR
|
lda #1
|
||||||
ora #1
|
|
||||||
sta cx16.VERA_ISR ; clear Vera Vsync irq status
|
sta cx16.VERA_ISR ; clear Vera Vsync irq status
|
||||||
ply
|
ply
|
||||||
plx
|
plx
|
||||||
|
@ -1 +1 @@
|
|||||||
8.9
|
8.10
|
||||||
|
@ -43,7 +43,6 @@ private fun compileMain(args: Array<String>): Boolean {
|
|||||||
val experimentalCodegen by cli.option(ArgType.Boolean, fullName = "expericodegen", description = "use experimental/alternative codegen")
|
val experimentalCodegen by cli.option(ArgType.Boolean, fullName = "expericodegen", description = "use experimental/alternative codegen")
|
||||||
val dontWriteAssembly by cli.option(ArgType.Boolean, fullName = "noasm", description="don't create assembly code")
|
val dontWriteAssembly by cli.option(ArgType.Boolean, fullName = "noasm", description="don't create assembly code")
|
||||||
val dontOptimize by cli.option(ArgType.Boolean, fullName = "noopt", description = "don't perform any optimizations")
|
val dontOptimize by cli.option(ArgType.Boolean, fullName = "noopt", description = "don't perform any optimizations")
|
||||||
val dontReinitGlobals by cli.option(ArgType.Boolean, fullName = "noreinit", description = "don't create code to reinitialize globals on multiple runs of the program (experimental)")
|
|
||||||
val outputDir by cli.option(ArgType.String, fullName = "out", description = "directory for output files instead of current directory").default(".")
|
val outputDir by cli.option(ArgType.String, fullName = "out", description = "directory for output files instead of current directory").default(".")
|
||||||
val optimizeFloatExpressions by cli.option(ArgType.Boolean, fullName = "optfloatx", description = "optimize float expressions (warning: can increase program size)")
|
val optimizeFloatExpressions by cli.option(ArgType.Boolean, fullName = "optfloatx", description = "optimize float expressions (warning: can increase program size)")
|
||||||
val quietAssembler by cli.option(ArgType.Boolean, fullName = "quietasm", description = "don't print assembler output results")
|
val quietAssembler by cli.option(ArgType.Boolean, fullName = "quietasm", description = "don't print assembler output results")
|
||||||
@ -52,6 +51,7 @@ private fun compileMain(args: Array<String>): Boolean {
|
|||||||
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 .p8ir IR source file in the VM")
|
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 varsHigh by cli.option(ArgType.Boolean, fullName = "varshigh", description = "put uninitialized variables in high memory area instead of at the end of the program")
|
||||||
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)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -120,12 +120,12 @@ private fun compileMain(args: Array<String>): Boolean {
|
|||||||
filepath,
|
filepath,
|
||||||
dontOptimize != true,
|
dontOptimize != true,
|
||||||
optimizeFloatExpressions == true,
|
optimizeFloatExpressions == true,
|
||||||
dontReinitGlobals == true,
|
|
||||||
dontWriteAssembly != true,
|
dontWriteAssembly != true,
|
||||||
slowCodegenWarnings == true,
|
slowCodegenWarnings == true,
|
||||||
quietAssembler == true,
|
quietAssembler == true,
|
||||||
asmListfile == true,
|
asmListfile == true,
|
||||||
experimentalCodegen == true,
|
experimentalCodegen == true,
|
||||||
|
varsHigh == true,
|
||||||
compilationTarget,
|
compilationTarget,
|
||||||
evalStackAddr,
|
evalStackAddr,
|
||||||
processedSymbols,
|
processedSymbols,
|
||||||
@ -184,12 +184,12 @@ private fun compileMain(args: Array<String>): Boolean {
|
|||||||
filepath,
|
filepath,
|
||||||
dontOptimize != true,
|
dontOptimize != true,
|
||||||
optimizeFloatExpressions == true,
|
optimizeFloatExpressions == true,
|
||||||
dontReinitGlobals == true,
|
|
||||||
dontWriteAssembly != true,
|
dontWriteAssembly != true,
|
||||||
slowCodegenWarnings == true,
|
slowCodegenWarnings == true,
|
||||||
quietAssembler == true,
|
quietAssembler == true,
|
||||||
asmListfile == true,
|
asmListfile == true,
|
||||||
experimentalCodegen == true,
|
experimentalCodegen == true,
|
||||||
|
varsHigh == true,
|
||||||
compilationTarget,
|
compilationTarget,
|
||||||
evalStackAddr,
|
evalStackAddr,
|
||||||
processedSymbols,
|
processedSymbols,
|
||||||
@ -206,13 +206,13 @@ private fun compileMain(args: Array<String>): Boolean {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(startEmulator1==true || startEmulator2==true) {
|
if(startEmulator1==true || startEmulator2==true) {
|
||||||
if (compilationResult.program.name.isEmpty()) {
|
if (compilationResult.compilerAst.name.isEmpty()) {
|
||||||
println("\nCan't start emulator because no program was assembled.")
|
println("\nCan't start emulator because no program was assembled.")
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val programNameInPath = outputPath.resolve(compilationResult.program.name)
|
val programNameInPath = outputPath.resolve(compilationResult.compilerAst.name)
|
||||||
|
|
||||||
if(startEmulator1==true || startEmulator2==true) {
|
if(startEmulator1==true || startEmulator2==true) {
|
||||||
if (compilationResult.compilationOptions.launcher != CbmPrgLauncherType.NONE || compilationTarget=="atari") {
|
if (compilationResult.compilationOptions.launcher != CbmPrgLauncherType.NONE || compilationTarget=="atari") {
|
||||||
|
158
compiler/src/prog8/compiler/BuiltinFunctions.kt
Normal file
158
compiler/src/prog8/compiler/BuiltinFunctions.kt
Normal file
@ -0,0 +1,158 @@
|
|||||||
|
package prog8.compiler
|
||||||
|
|
||||||
|
import prog8.ast.Program
|
||||||
|
import prog8.ast.base.AstException
|
||||||
|
import prog8.ast.base.FatalAstException
|
||||||
|
import prog8.ast.base.SyntaxError
|
||||||
|
import prog8.ast.expressions.*
|
||||||
|
import prog8.ast.statements.VarDecl
|
||||||
|
import prog8.code.core.*
|
||||||
|
import kotlin.math.abs
|
||||||
|
import kotlin.math.sign
|
||||||
|
import kotlin.math.sqrt
|
||||||
|
|
||||||
|
|
||||||
|
private typealias ConstExpressionCaller = (args: List<Expression>, position: Position, program: Program) -> NumericLiteral
|
||||||
|
|
||||||
|
internal val constEvaluatorsForBuiltinFuncs: Map<String, ConstExpressionCaller> = mapOf(
|
||||||
|
"abs" to ::builtinAbs,
|
||||||
|
"len" to ::builtinLen,
|
||||||
|
"sizeof" to ::builtinSizeof,
|
||||||
|
"sgn" to ::builtinSgn,
|
||||||
|
"sqrt16" to { a, p, prg -> oneIntArgOutputInt(a, p, prg) { sqrt(it.toDouble()) } },
|
||||||
|
"any" to { a, p, prg -> collectionArg(a, p, prg, ::builtinAny) },
|
||||||
|
"all" to { a, p, prg -> collectionArg(a, p, prg, ::builtinAll) },
|
||||||
|
"lsb" to { a, p, prg -> oneIntArgOutputInt(a, p, prg) { x: Int -> (x and 255).toDouble() } },
|
||||||
|
"msb" to { a, p, prg -> oneIntArgOutputInt(a, p, prg) { x: Int -> (x ushr 8 and 255).toDouble()} },
|
||||||
|
"mkword" to ::builtinMkword
|
||||||
|
)
|
||||||
|
|
||||||
|
private fun builtinAny(array: List<Double>): Double = if(array.any { it!=0.0 }) 1.0 else 0.0
|
||||||
|
|
||||||
|
private fun builtinAll(array: List<Double>): Double = if(array.all { it!=0.0 }) 1.0 else 0.0
|
||||||
|
|
||||||
|
internal fun builtinFunctionReturnType(function: String): InferredTypes.InferredType {
|
||||||
|
if(function in arrayOf("set_carry", "set_irqd", "clear_carry", "clear_irqd"))
|
||||||
|
return InferredTypes.InferredType.void()
|
||||||
|
|
||||||
|
val func = BuiltinFunctions.getValue(function)
|
||||||
|
val returnType = func.returnType
|
||||||
|
return if(returnType==null)
|
||||||
|
InferredTypes.InferredType.void()
|
||||||
|
else
|
||||||
|
InferredTypes.knownFor(returnType)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
internal class NotConstArgumentException: AstException("not a const argument to a built-in function")
|
||||||
|
internal class CannotEvaluateException(func:String, msg: String): FatalAstException("cannot evaluate built-in function $func: $msg")
|
||||||
|
|
||||||
|
|
||||||
|
private fun oneIntArgOutputInt(args: List<Expression>, position: Position, program: Program, function: (arg: Int)->Double): NumericLiteral {
|
||||||
|
if(args.size!=1)
|
||||||
|
throw SyntaxError("built-in function requires one integer argument", position)
|
||||||
|
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
|
||||||
|
if(constval.type != DataType.UBYTE && constval.type!= DataType.UWORD)
|
||||||
|
throw SyntaxError("built-in function requires one integer argument", position)
|
||||||
|
|
||||||
|
val integer = constval.number.toInt()
|
||||||
|
return NumericLiteral.optimalInteger(function(integer).toInt(), args[0].position)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun collectionArg(args: List<Expression>, position: Position, program: Program, function: (arg: List<Double>)->Double): NumericLiteral {
|
||||||
|
if(args.size!=1)
|
||||||
|
throw SyntaxError("builtin function requires one non-scalar argument", position)
|
||||||
|
|
||||||
|
val array= args[0] as? ArrayLiteral ?: throw NotConstArgumentException()
|
||||||
|
val constElements = array.value.map{it.constValue(program)?.number}
|
||||||
|
if(constElements.contains(null))
|
||||||
|
throw NotConstArgumentException()
|
||||||
|
|
||||||
|
return NumericLiteral.optimalNumeric(function(constElements.mapNotNull { it }), args[0].position)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun builtinAbs(args: List<Expression>, position: Position, program: Program): NumericLiteral {
|
||||||
|
// 1 arg, type = int, result type= uword
|
||||||
|
if(args.size!=1)
|
||||||
|
throw SyntaxError("abs requires one integer argument", position)
|
||||||
|
|
||||||
|
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
|
||||||
|
return when (constval.type) {
|
||||||
|
in IntegerDatatypesNoBool -> NumericLiteral.optimalInteger(abs(constval.number.toInt()), args[0].position)
|
||||||
|
else -> throw SyntaxError("abs requires one integer argument", position)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun builtinSizeof(args: List<Expression>, position: Position, program: Program): NumericLiteral {
|
||||||
|
// 1 arg, type = anything, result type = ubyte
|
||||||
|
if(args.size!=1)
|
||||||
|
throw SyntaxError("sizeof requires one argument", position)
|
||||||
|
if(args[0] !is IdentifierReference)
|
||||||
|
throw SyntaxError("sizeof argument should be an identifier", position)
|
||||||
|
|
||||||
|
val dt = args[0].inferType(program)
|
||||||
|
if(dt.isKnown) {
|
||||||
|
val target = (args[0] as IdentifierReference).targetStatement(program)
|
||||||
|
?: throw CannotEvaluateException("sizeof", "no target")
|
||||||
|
|
||||||
|
return when {
|
||||||
|
dt.isArray -> {
|
||||||
|
val length = (target as VarDecl).arraysize!!.constIndex() ?: throw CannotEvaluateException("sizeof", "unknown array size")
|
||||||
|
val elementDt = ArrayToElementTypes.getValue(dt.getOr(DataType.UNDEFINED))
|
||||||
|
NumericLiteral.optimalInteger(program.memsizer.memorySize(elementDt) * length, position)
|
||||||
|
}
|
||||||
|
dt istype DataType.STR -> throw SyntaxError("sizeof str is undefined, did you mean len?", position)
|
||||||
|
else -> NumericLiteral(DataType.UBYTE, program.memsizer.memorySize(dt.getOr(DataType.UNDEFINED)).toDouble(), position)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw SyntaxError("sizeof invalid argument type", position)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun builtinLen(args: List<Expression>, position: Position, program: Program): NumericLiteral {
|
||||||
|
// note: in some cases the length is > 255, and then we have to return a UWORD type instead of a UBYTE.
|
||||||
|
if(args.size!=1)
|
||||||
|
throw SyntaxError("len requires one argument", position)
|
||||||
|
|
||||||
|
val directMemVar = ((args[0] as? DirectMemoryRead)?.addressExpression as? IdentifierReference)?.targetVarDecl(program)
|
||||||
|
var arraySize = directMemVar?.arraysize?.constIndex()
|
||||||
|
if(arraySize != null)
|
||||||
|
return NumericLiteral.optimalInteger(arraySize, position)
|
||||||
|
if(args[0] is ArrayLiteral)
|
||||||
|
return NumericLiteral.optimalInteger((args[0] as ArrayLiteral).value.size, position)
|
||||||
|
if(args[0] !is IdentifierReference)
|
||||||
|
throw SyntaxError("len argument should be an identifier", position)
|
||||||
|
val target = (args[0] as IdentifierReference).targetVarDecl(program)
|
||||||
|
?: throw CannotEvaluateException("len", "no target vardecl")
|
||||||
|
|
||||||
|
return when(target.datatype) {
|
||||||
|
in ArrayDatatypes -> {
|
||||||
|
arraySize = target.arraysize?.constIndex()
|
||||||
|
if(arraySize==null)
|
||||||
|
throw CannotEvaluateException("len", "arraysize unknown")
|
||||||
|
NumericLiteral.optimalInteger(arraySize, args[0].position)
|
||||||
|
}
|
||||||
|
DataType.STR -> {
|
||||||
|
val refLv = target.value as? StringLiteral ?: throw CannotEvaluateException("len", "stringsize unknown")
|
||||||
|
NumericLiteral.optimalInteger(refLv.value.length, args[0].position)
|
||||||
|
}
|
||||||
|
in NumericDatatypes -> throw SyntaxError("cannot use len on numeric value, did you mean sizeof?", args[0].position)
|
||||||
|
else -> throw InternalCompilerException("weird datatype")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun builtinMkword(args: List<Expression>, position: Position, program: Program): NumericLiteral {
|
||||||
|
if (args.size != 2)
|
||||||
|
throw SyntaxError("mkword requires msb and lsb arguments", position)
|
||||||
|
val constMsb = args[0].constValue(program) ?: throw NotConstArgumentException()
|
||||||
|
val constLsb = args[1].constValue(program) ?: throw NotConstArgumentException()
|
||||||
|
val result = (constMsb.number.toInt() shl 8) or constLsb.number.toInt()
|
||||||
|
return NumericLiteral(DataType.UWORD, result.toDouble(), position)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun builtinSgn(args: List<Expression>, position: Position, program: Program): NumericLiteral {
|
||||||
|
if (args.size != 1)
|
||||||
|
throw SyntaxError("sgn requires one argument", position)
|
||||||
|
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
|
||||||
|
return NumericLiteral(DataType.BYTE, constval.number.sign, position)
|
||||||
|
}
|
@ -1,17 +1,14 @@
|
|||||||
package prog8.compiler
|
package prog8.compiler
|
||||||
|
|
||||||
import com.github.michaelbull.result.onFailure
|
import com.github.michaelbull.result.onFailure
|
||||||
import prog8.ast.AstToSourceTextConverter
|
|
||||||
import prog8.ast.IBuiltinFunctions
|
import prog8.ast.IBuiltinFunctions
|
||||||
import prog8.ast.IStatementContainer
|
|
||||||
import prog8.ast.Program
|
import prog8.ast.Program
|
||||||
import prog8.ast.base.AstException
|
import prog8.ast.base.AstException
|
||||||
import prog8.ast.expressions.Expression
|
import prog8.ast.expressions.Expression
|
||||||
import prog8.ast.expressions.NumericLiteral
|
import prog8.ast.expressions.NumericLiteral
|
||||||
import prog8.ast.statements.Directive
|
import prog8.ast.statements.Directive
|
||||||
import prog8.ast.statements.VarDecl
|
import prog8.code.SymbolTableMaker
|
||||||
import prog8.ast.walk.IAstVisitor
|
import prog8.code.ast.PtProgram
|
||||||
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.codegen.vm.VmCodeGen
|
||||||
@ -25,19 +22,20 @@ import kotlin.math.round
|
|||||||
import kotlin.system.measureTimeMillis
|
import kotlin.system.measureTimeMillis
|
||||||
|
|
||||||
|
|
||||||
class CompilationResult(val program: Program,
|
class CompilationResult(val compilerAst: Program, // deprecated, use codegenAst instead
|
||||||
|
val codegenAst: PtProgram?,
|
||||||
val compilationOptions: CompilationOptions,
|
val compilationOptions: CompilationOptions,
|
||||||
val importedFiles: List<Path>)
|
val importedFiles: List<Path>)
|
||||||
|
|
||||||
class CompilerArguments(val filepath: Path,
|
class CompilerArguments(val filepath: Path,
|
||||||
val optimize: Boolean,
|
val optimize: Boolean,
|
||||||
val optimizeFloatExpressions: Boolean,
|
val optimizeFloatExpressions: Boolean,
|
||||||
val dontReinitGlobals: Boolean,
|
|
||||||
val writeAssembly: Boolean,
|
val writeAssembly: Boolean,
|
||||||
val slowCodegenWarnings: Boolean,
|
val slowCodegenWarnings: Boolean,
|
||||||
val quietAssembler: Boolean,
|
val quietAssembler: Boolean,
|
||||||
val asmListfile: Boolean,
|
val asmListfile: Boolean,
|
||||||
val experimentalCodegen: Boolean,
|
val experimentalCodegen: Boolean,
|
||||||
|
val varsHigh: Boolean,
|
||||||
val compilationTarget: String,
|
val compilationTarget: String,
|
||||||
val evalStackBaseAddress: UInt?,
|
val evalStackBaseAddress: UInt?,
|
||||||
val symbolDefs: Map<String, String>,
|
val symbolDefs: Map<String, String>,
|
||||||
@ -63,23 +61,21 @@ fun compileProgram(args: CompilerArguments): CompilationResult? {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var compilationOptions: CompilationOptions
|
var compilationOptions: CompilationOptions
|
||||||
|
var ast: PtProgram? = null
|
||||||
|
|
||||||
try {
|
try {
|
||||||
val totalTime = measureTimeMillis {
|
val totalTime = measureTimeMillis {
|
||||||
// import main module and everything it needs
|
val (programresult, options, imported) = parseMainModule(args.filepath, args.errors, compTarget, args.sourceDirs)
|
||||||
val (programresult, options, imported) = parseImports(args.filepath, args.errors, compTarget, args.sourceDirs)
|
|
||||||
compilationOptions = options
|
compilationOptions = options
|
||||||
print("Parsed ${args.filepath}")
|
|
||||||
ModuleImporter.ansiEraseRestOfLine(true)
|
|
||||||
|
|
||||||
with(compilationOptions) {
|
with(compilationOptions) {
|
||||||
slowCodegenWarnings = args.slowCodegenWarnings
|
slowCodegenWarnings = args.slowCodegenWarnings
|
||||||
optimize = args.optimize
|
optimize = args.optimize
|
||||||
optimizeFloatExpressions = optimizeFloatExpr
|
optimizeFloatExpressions = optimizeFloatExpr
|
||||||
dontReinitGlobals = args.dontReinitGlobals
|
|
||||||
asmQuiet = args.quietAssembler
|
asmQuiet = args.quietAssembler
|
||||||
asmListfile = args.asmListfile
|
asmListfile = args.asmListfile
|
||||||
experimentalCodegen = args.experimentalCodegen
|
experimentalCodegen = args.experimentalCodegen
|
||||||
|
varsHigh = args.varsHigh
|
||||||
evalStackBaseAddress = args.evalStackBaseAddress
|
evalStackBaseAddress = args.evalStackBaseAddress
|
||||||
outputDir = args.outputDir.normalize()
|
outputDir = args.outputDir.normalize()
|
||||||
symbolDefs = args.symbolDefs
|
symbolDefs = args.symbolDefs
|
||||||
@ -113,10 +109,22 @@ fun compileProgram(args: CompilerArguments): CompilationResult? {
|
|||||||
args.errors.report()
|
args.errors.report()
|
||||||
|
|
||||||
if (args.writeAssembly) {
|
if (args.writeAssembly) {
|
||||||
if(!createAssemblyAndAssemble(program, args.errors, compilationOptions)) {
|
|
||||||
|
compilationOptions.compTarget.machine.initializeMemoryAreas(compilationOptions)
|
||||||
|
program.processAstBeforeAsmGeneration(compilationOptions, args.errors)
|
||||||
|
args.errors.report()
|
||||||
|
|
||||||
|
val intermediateAst = IntermediateAstMaker(program, compilationOptions).transform()
|
||||||
|
// println("*********** COMPILER AST RIGHT BEFORE ASM GENERATION *************")
|
||||||
|
// printProgram(program)
|
||||||
|
// println("*********** AST RIGHT BEFORE ASM GENERATION *************")
|
||||||
|
// printAst(intermediateAst, ::println)
|
||||||
|
|
||||||
|
if(!createAssemblyAndAssemble(intermediateAst, args.errors, compilationOptions)) {
|
||||||
System.err.println("Error in codegeneration or assembler")
|
System.err.println("Error in codegeneration or assembler")
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
ast = intermediateAst
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -124,7 +132,7 @@ fun compileProgram(args: CompilerArguments): CompilationResult? {
|
|||||||
System.err.flush()
|
System.err.flush()
|
||||||
val seconds = totalTime/1000.0
|
val seconds = totalTime/1000.0
|
||||||
println("\nTotal compilation+assemble time: ${round(seconds*100.0)/100.0} sec.")
|
println("\nTotal compilation+assemble time: ${round(seconds*100.0)/100.0} sec.")
|
||||||
return CompilationResult(program, compilationOptions, importedFiles)
|
return CompilationResult(program, ast, compilationOptions, importedFiles)
|
||||||
} catch (px: ParseError) {
|
} catch (px: ParseError) {
|
||||||
System.err.print("\n\u001b[91m") // bright red
|
System.err.print("\n\u001b[91m") // bright red
|
||||||
System.err.println("${px.position.toClickableStr()} parse error: ${px.message}".trim())
|
System.err.println("${px.position.toClickableStr()} parse error: ${px.message}".trim())
|
||||||
@ -208,7 +216,7 @@ private class BuiltinFunctionsFacade(functions: Map<String, FSignature>): IBuilt
|
|||||||
override fun constValue(funcName: String, args: List<Expression>, position: Position): NumericLiteral? {
|
override fun constValue(funcName: String, args: List<Expression>, position: Position): NumericLiteral? {
|
||||||
val func = BuiltinFunctions[funcName]
|
val func = BuiltinFunctions[funcName]
|
||||||
if(func!=null) {
|
if(func!=null) {
|
||||||
val exprfunc = func.constExpressionFunc
|
val exprfunc = constEvaluatorsForBuiltinFuncs[funcName]
|
||||||
if(exprfunc!=null) {
|
if(exprfunc!=null) {
|
||||||
return try {
|
return try {
|
||||||
exprfunc(args, position, program)
|
exprfunc(args, position, program)
|
||||||
@ -226,17 +234,16 @@ private class BuiltinFunctionsFacade(functions: Map<String, FSignature>): IBuilt
|
|||||||
override fun returnType(funcName: String) = builtinFunctionReturnType(funcName)
|
override fun returnType(funcName: String) = builtinFunctionReturnType(funcName)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun parseImports(filepath: Path,
|
fun parseMainModule(filepath: Path,
|
||||||
errors: IErrorReporter,
|
errors: IErrorReporter,
|
||||||
compTarget: ICompilationTarget,
|
compTarget: ICompilationTarget,
|
||||||
sourceDirs: List<String>): Triple<Program, CompilationOptions, List<Path>> {
|
sourceDirs: List<String>): Triple<Program, CompilationOptions, List<Path>> {
|
||||||
println("Compilation target: ${compTarget.name}")
|
|
||||||
val bf = BuiltinFunctionsFacade(BuiltinFunctions)
|
val bf = BuiltinFunctionsFacade(BuiltinFunctions)
|
||||||
val program = Program(filepath.nameWithoutExtension, bf, compTarget, compTarget)
|
val program = Program(filepath.nameWithoutExtension, bf, compTarget, compTarget)
|
||||||
bf.program = program
|
bf.program = program
|
||||||
|
|
||||||
val importer = ModuleImporter(program, compTarget.name, errors, sourceDirs)
|
val importer = ModuleImporter(program, compTarget.name, errors, sourceDirs)
|
||||||
val importedModuleResult = importer.importModule(filepath)
|
val importedModuleResult = importer.importMainModule(filepath)
|
||||||
importedModuleResult.onFailure { throw it }
|
importedModuleResult.onFailure { throw it }
|
||||||
errors.report()
|
errors.report()
|
||||||
|
|
||||||
@ -246,11 +253,11 @@ fun parseImports(filepath: Path,
|
|||||||
val compilerOptions = determineCompilationOptions(program, compTarget)
|
val compilerOptions = determineCompilationOptions(program, compTarget)
|
||||||
// depending on the machine and compiler options we may have to include some libraries
|
// depending on the machine and compiler options we may have to include some libraries
|
||||||
for(lib in compTarget.machine.importLibs(compilerOptions, compTarget.name))
|
for(lib in compTarget.machine.importLibs(compilerOptions, compTarget.name))
|
||||||
importer.importLibraryModule(lib)
|
importer.importImplicitLibraryModule(lib)
|
||||||
|
|
||||||
// always import prog8_lib and math
|
// always import prog8_lib and math
|
||||||
importer.importLibraryModule("math")
|
importer.importImplicitLibraryModule("math")
|
||||||
importer.importLibraryModule("prog8_lib")
|
importer.importImplicitLibraryModule("prog8_lib")
|
||||||
|
|
||||||
if (compilerOptions.launcher == CbmPrgLauncherType.BASIC && compilerOptions.output != OutputType.PRG)
|
if (compilerOptions.launcher == CbmPrgLauncherType.BASIC && compilerOptions.output != OutputType.PRG)
|
||||||
errors.err("BASIC launcher requires output type PRG", program.toplevelModule.position)
|
errors.err("BASIC launcher requires output type PRG", program.toplevelModule.position)
|
||||||
@ -328,7 +335,6 @@ fun determineCompilationOptions(program: Program, compTarget: ICompilationTarget
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun processAst(program: Program, errors: IErrorReporter, compilerOptions: CompilationOptions) {
|
private fun processAst(program: Program, errors: IErrorReporter, compilerOptions: CompilationOptions) {
|
||||||
println("Analyzing code...")
|
|
||||||
program.preprocessAst(errors, compilerOptions)
|
program.preprocessAst(errors, compilerOptions)
|
||||||
program.checkIdentifiers(errors, compilerOptions)
|
program.checkIdentifiers(errors, compilerOptions)
|
||||||
errors.report()
|
errors.report()
|
||||||
@ -353,7 +359,6 @@ private fun processAst(program: Program, errors: IErrorReporter, compilerOptions
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun optimizeAst(program: Program, compilerOptions: CompilationOptions, errors: IErrorReporter, functions: IBuiltinFunctions, compTarget: ICompilationTarget) {
|
private fun optimizeAst(program: Program, compilerOptions: CompilationOptions, errors: IErrorReporter, functions: IBuiltinFunctions, compTarget: ICompilationTarget) {
|
||||||
println("Optimizing...")
|
|
||||||
val remover = UnusedCodeRemover(program, errors, compTarget)
|
val remover = UnusedCodeRemover(program, errors, compTarget)
|
||||||
remover.visit(program)
|
remover.visit(program)
|
||||||
remover.applyModifications()
|
remover.applyModifications()
|
||||||
@ -385,25 +390,23 @@ private fun postprocessAst(program: Program, errors: IErrorReporter, compilerOpt
|
|||||||
errors.report()
|
errors.report()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createAssemblyAndAssemble(program: Program,
|
private fun createAssemblyAndAssemble(program: PtProgram,
|
||||||
errors: IErrorReporter,
|
errors: IErrorReporter,
|
||||||
compilerOptions: CompilationOptions
|
compilerOptions: CompilationOptions
|
||||||
): Boolean {
|
): Boolean {
|
||||||
compilerOptions.compTarget.machine.initializeMemoryAreas(compilerOptions)
|
|
||||||
program.processAstBeforeAsmGeneration(compilerOptions, errors)
|
|
||||||
errors.report()
|
|
||||||
val symbolTable = SymbolTableMaker(program, compilerOptions).make()
|
|
||||||
|
|
||||||
// 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,
|
val asmgen = if(compilerOptions.experimentalCodegen)
|
||||||
// or don't use inferType at all anymore and "bake the type information" into the Ast somehow.
|
prog8.codegen.experimental.ExperiCodeGen()
|
||||||
// Note: we don't actually *need* to remove the VarDecl nodes, but it is nice as a temporary measure
|
else if (compilerOptions.compTarget.machine.cpu in arrayOf(CpuType.CPU6502, CpuType.CPU65c02))
|
||||||
// to help clean up the code that still depends on them.
|
prog8.codegen.cpu6502.AsmGen6502()
|
||||||
// removeAllVardeclsFromAst(program)
|
else if (compilerOptions.compTarget.name == VMTarget.NAME)
|
||||||
|
VmCodeGen()
|
||||||
|
else
|
||||||
|
throw NotImplementedError("no asm generator for cpu ${compilerOptions.compTarget.machine.cpu}")
|
||||||
|
|
||||||
// println("*********** COMPILER AST RIGHT BEFORE ASM GENERATION *************")
|
val stMaker = SymbolTableMaker(program, compilerOptions)
|
||||||
// printProgram(program)
|
val symbolTable = stMaker.make()
|
||||||
|
val assembly = asmgen.generate(program, symbolTable, compilerOptions, errors)
|
||||||
val assembly = asmGeneratorFor(program, errors, symbolTable, compilerOptions).compileToAssembly()
|
|
||||||
errors.report()
|
errors.report()
|
||||||
|
|
||||||
return if(assembly!=null && errors.noErrors()) {
|
return if(assembly!=null && errors.noErrors()) {
|
||||||
@ -412,51 +415,3 @@ private fun createAssemblyAndAssemble(program: Program,
|
|||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun removeAllVardeclsFromAst(program: Program) {
|
|
||||||
// remove all VarDecl nodes from the AST.
|
|
||||||
// code generation doesn't require them anymore, it operates only on the 'variables' collection.
|
|
||||||
|
|
||||||
class SearchAndRemove: IAstVisitor {
|
|
||||||
private val allVars = mutableListOf<VarDecl>()
|
|
||||||
init {
|
|
||||||
visit(program)
|
|
||||||
for (it in allVars) {
|
|
||||||
require((it.parent as IStatementContainer).statements.remove(it))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
override fun visit(decl: VarDecl) {
|
|
||||||
allVars.add(decl)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SearchAndRemove()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun printProgram(program: Program) {
|
|
||||||
println()
|
|
||||||
val printer = AstToSourceTextConverter(::print, program)
|
|
||||||
printer.visit(program)
|
|
||||||
println()
|
|
||||||
}
|
|
||||||
|
|
||||||
internal fun asmGeneratorFor(program: Program,
|
|
||||||
errors: IErrorReporter,
|
|
||||||
symbolTable: SymbolTable,
|
|
||||||
options: CompilationOptions): IAssemblyGenerator
|
|
||||||
{
|
|
||||||
if(options.experimentalCodegen) {
|
|
||||||
val intermediateAst = IntermediateAstMaker(program, symbolTable, options).transform()
|
|
||||||
return prog8.codegen.experimental.CodeGen(intermediateAst, symbolTable, options, errors)
|
|
||||||
} else {
|
|
||||||
if (options.compTarget.machine.cpu in arrayOf(CpuType.CPU6502, CpuType.CPU65c02))
|
|
||||||
// TODO rewrite 6502 codegen on new Intermediary Ast or on new Intermediate Representation
|
|
||||||
return prog8.codegen.cpu6502.AsmGen(program, symbolTable, options, errors)
|
|
||||||
if (options.compTarget.name == VMTarget.NAME) {
|
|
||||||
val intermediateAst = IntermediateAstMaker(program, symbolTable, options).transform()
|
|
||||||
return VmCodeGen(intermediateAst, symbolTable, options, errors)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
throw NotImplementedError("no asm generator for cpu ${options.compTarget.machine.cpu}")
|
|
||||||
}
|
|
||||||
|
@ -13,7 +13,9 @@ import prog8.code.core.SourceCode
|
|||||||
import prog8.parser.Prog8Parser
|
import prog8.parser.Prog8Parser
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
import kotlin.io.path.*
|
import kotlin.io.path.Path
|
||||||
|
import kotlin.io.path.absolute
|
||||||
|
import kotlin.io.path.exists
|
||||||
|
|
||||||
|
|
||||||
class ModuleImporter(private val program: Program,
|
class ModuleImporter(private val program: Program,
|
||||||
@ -21,30 +23,25 @@ class ModuleImporter(private val program: Program,
|
|||||||
val errors: IErrorReporter,
|
val errors: IErrorReporter,
|
||||||
sourceDirs: List<String>) {
|
sourceDirs: List<String>) {
|
||||||
|
|
||||||
private val sourcePaths: List<Path> = sourceDirs.map { Path(it) }
|
private val sourcePaths: List<Path> = sourceDirs.map { Path(it).absolute().normalize() }.toSortedSet().toList()
|
||||||
|
|
||||||
fun importModule(filePath: Path): Result<Module, NoSuchFileException> {
|
fun importMainModule(filePath: Path): Result<Module, NoSuchFileException> {
|
||||||
val currentDir = Path("").absolute()
|
val searchIn = (listOf(Path("").absolute()) + sourcePaths).toSortedSet()
|
||||||
val searchIn = listOf(currentDir) + sourcePaths
|
val normalizedFilePath = filePath.normalize()
|
||||||
val candidates = searchIn
|
for(path in searchIn) {
|
||||||
.map { it.absolute().div(filePath).normalize().absolute() }
|
val programPath = path.resolve(normalizedFilePath)
|
||||||
.filter { it.exists() }
|
if(programPath.exists()) {
|
||||||
.map { currentDir.relativize(it) }
|
println("Compiling program ${Path("").absolute().relativize(programPath)}")
|
||||||
.map { if (it.isAbsolute) it else Path(".", "$it") }
|
val source = SourceCode.File(programPath)
|
||||||
|
return Ok(importModule(source))
|
||||||
val srcPath = when (candidates.size) {
|
}
|
||||||
0 -> return Err(NoSuchFileException(
|
|
||||||
file = filePath.normalize().toFile(),
|
|
||||||
reason = "Searched in $searchIn"))
|
|
||||||
1 -> candidates.first()
|
|
||||||
else -> candidates.first() // when more candiates, pick the one from the first location
|
|
||||||
}
|
}
|
||||||
|
return Err(NoSuchFileException(
|
||||||
val source = SourceCode.File(srcPath)
|
file = normalizedFilePath.toFile(),
|
||||||
return Ok(importModule(source))
|
reason = "Searched in $searchIn"))
|
||||||
}
|
}
|
||||||
|
|
||||||
fun importLibraryModule(name: String): Module? {
|
fun importImplicitLibraryModule(name: String): Module? {
|
||||||
val import = Directive("%import", listOf(
|
val import = Directive("%import", listOf(
|
||||||
DirectiveArg("", name, 42u, position = Position("<<<implicit-import>>>", 0, 0, 0))
|
DirectiveArg("", name, 42u, position = Position("<<<implicit-import>>>", 0, 0, 0))
|
||||||
), Position("<<<implicit-import>>>", 0, 0, 0))
|
), Position("<<<implicit-import>>>", 0, 0, 0))
|
||||||
@ -52,7 +49,6 @@ class ModuleImporter(private val program: Program,
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun importModule(src: SourceCode) : Module {
|
private fun importModule(src: SourceCode) : Module {
|
||||||
printImportingMessage(src.name, src.origin)
|
|
||||||
val moduleAst = Prog8Parser.parseModule(src)
|
val moduleAst = Prog8Parser.parseModule(src)
|
||||||
program.addModule(moduleAst)
|
program.addModule(moduleAst)
|
||||||
|
|
||||||
@ -146,10 +142,8 @@ class ModuleImporter(private val program: Program,
|
|||||||
if (importingModule == null) { // <=> imported from library module
|
if (importingModule == null) { // <=> imported from library module
|
||||||
sourcePaths
|
sourcePaths
|
||||||
} else {
|
} else {
|
||||||
val dropCurDir = if(sourcePaths.isNotEmpty() && sourcePaths[0].name == ".") 1 else 0
|
val pathFromImportingModule = (Path(importingModule.position.file).parent ?: Path("")).absolute()
|
||||||
sourcePaths.drop(dropCurDir) +
|
listOf(pathFromImportingModule) + sourcePaths
|
||||||
listOf(Path(importingModule.position.file).parent ?: Path("")) +
|
|
||||||
listOf(Path(".", "prog8lib"))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
locations.forEach {
|
locations.forEach {
|
||||||
@ -161,18 +155,4 @@ class ModuleImporter(private val program: Program,
|
|||||||
|
|
||||||
return Err(NoSuchFileException(File("name")))
|
return Err(NoSuchFileException(File("name")))
|
||||||
}
|
}
|
||||||
|
|
||||||
fun printImportingMessage(module: String, origin: String) {
|
|
||||||
print(" importing '$module' (from ${origin})")
|
|
||||||
ansiEraseRestOfLine(false)
|
|
||||||
print("\r")
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
fun ansiEraseRestOfLine(newline: Boolean) {
|
|
||||||
print("\u001b[0K")
|
|
||||||
if(newline)
|
|
||||||
println()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -8,8 +8,6 @@ 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.code.target.VMTarget
|
||||||
import prog8.compiler.BuiltinFunctions
|
|
||||||
import prog8.compiler.InplaceModifyingBuiltinFunctions
|
|
||||||
import prog8.compiler.builtinFunctionReturnType
|
import prog8.compiler.builtinFunctionReturnType
|
||||||
import java.io.CharConversionException
|
import java.io.CharConversionException
|
||||||
import java.io.File
|
import java.io.File
|
||||||
@ -62,10 +60,12 @@ internal class AstChecker(private val program: Program,
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(identifier: IdentifierReference) {
|
override fun visit(identifier: IdentifierReference) {
|
||||||
val targetParam = identifier.targetVarDecl(program)?.subroutineParameter
|
val target = identifier.targetVarDecl(program)
|
||||||
if(targetParam!=null) {
|
if(target != null && target.origin==VarDeclOrigin.SUBROUTINEPARAM) {
|
||||||
if((targetParam.parent as Subroutine).isAsmSubroutine)
|
if(target.definingSubroutine!!.isAsmSubroutine) {
|
||||||
errors.err("cannot refer to parameter of asmsub by name", identifier.position)
|
if(target.definingSubroutine!!.parameters.any { it.name == identifier.nameInSource.last() })
|
||||||
|
errors.err("cannot refer to parameter of asmsub by name", identifier.position)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -664,8 +664,12 @@ internal class AstChecker(private val program: Program,
|
|||||||
err("string var must be initialized with a string literal")
|
err("string var must be initialized with a string literal")
|
||||||
}
|
}
|
||||||
|
|
||||||
if(decl.value !is StringLiteral)
|
if(decl.value !is StringLiteral) {
|
||||||
err("string var must be initialized with a string literal")
|
if(decl.type==VarDeclType.MEMORY)
|
||||||
|
err("strings can't be memory mapped")
|
||||||
|
else
|
||||||
|
err("string var must be initialized with a string literal")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(compilerOptions.zeropage==ZeropageType.DONTUSE && decl.zeropage == ZeropageWish.REQUIRE_ZEROPAGE)
|
if(compilerOptions.zeropage==ZeropageType.DONTUSE && decl.zeropage == ZeropageWish.REQUIRE_ZEROPAGE)
|
||||||
|
@ -136,12 +136,8 @@ internal fun Program.checkIdentifiers(errors: IErrorReporter, options: Compilati
|
|||||||
internal fun Program.variousCleanups(errors: IErrorReporter, options: CompilationOptions) {
|
internal fun Program.variousCleanups(errors: IErrorReporter, options: CompilationOptions) {
|
||||||
val process = VariousCleanups(this, errors, options)
|
val process = VariousCleanups(this, errors, options)
|
||||||
process.visit(this)
|
process.visit(this)
|
||||||
if(errors.noErrors()) {
|
while(errors.noErrors() && process.applyModifications()>0) {
|
||||||
if(process.applyModifications()>0) {
|
process.visit(this)
|
||||||
process.visit(this)
|
|
||||||
if(errors.noErrors())
|
|
||||||
process.applyModifications()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,11 +7,11 @@ import prog8.ast.expressions.FunctionCallExpression
|
|||||||
import prog8.ast.expressions.StringLiteral
|
import prog8.ast.expressions.StringLiteral
|
||||||
import prog8.ast.statements.*
|
import prog8.ast.statements.*
|
||||||
import prog8.ast.walk.IAstVisitor
|
import prog8.ast.walk.IAstVisitor
|
||||||
|
import prog8.code.core.BuiltinFunctions
|
||||||
import prog8.code.core.ICompilationTarget
|
import prog8.code.core.ICompilationTarget
|
||||||
import prog8.code.core.IErrorReporter
|
import prog8.code.core.IErrorReporter
|
||||||
import prog8.code.core.Position
|
import prog8.code.core.Position
|
||||||
import prog8.code.target.VMTarget
|
import prog8.code.target.VMTarget
|
||||||
import prog8.compiler.BuiltinFunctions
|
|
||||||
|
|
||||||
|
|
||||||
internal class AstIdentifiersChecker(private val errors: IErrorReporter,
|
internal class AstIdentifiersChecker(private val errors: IErrorReporter,
|
||||||
@ -92,7 +92,7 @@ internal class AstIdentifiersChecker(private val errors: IErrorReporter,
|
|||||||
val paramsToCheck = paramNames.intersect(namesInSub)
|
val paramsToCheck = paramNames.intersect(namesInSub)
|
||||||
for(name in paramsToCheck) {
|
for(name in paramsToCheck) {
|
||||||
val symbol = subroutine.searchSymbol(name)
|
val symbol = subroutine.searchSymbol(name)
|
||||||
if(symbol!=null && (symbol as? VarDecl)?.subroutineParameter==null)
|
if(symbol!=null && (symbol as? VarDecl)?.origin!=VarDeclOrigin.SUBROUTINEPARAM)
|
||||||
nameError(name, symbol.position, subroutine)
|
nameError(name, symbol.position, subroutine)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -163,7 +163,7 @@ internal class AstIdentifiersChecker(private val errors: IErrorReporter,
|
|||||||
val pos = (if(call.args.any()) call.args[0] else (call as Node)).position
|
val pos = (if(call.args.any()) call.args[0] else (call as Node)).position
|
||||||
errors.err("invalid number of arguments", pos)
|
errors.err("invalid number of arguments", pos)
|
||||||
}
|
}
|
||||||
if(func.name=="memory") {
|
if(target.name=="memory") {
|
||||||
val name = call.args[0] as? StringLiteral
|
val name = call.args[0] as? StringLiteral
|
||||||
if(name!=null) {
|
if(name!=null) {
|
||||||
val processed = name.value.map {
|
val processed = name.value.map {
|
||||||
|
@ -31,7 +31,7 @@ internal class AstOnetimeTransforms(private val program: Program, private val op
|
|||||||
// note: The CodeDesugarer already does something similar, but that is meant ONLY to take
|
// note: The CodeDesugarer already does something similar, but that is meant ONLY to take
|
||||||
// into account the case where the index value is a word type.
|
// into account the case where the index value is a word type.
|
||||||
// The replacement here is to fix missing cases in the 6502 codegen.
|
// 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
|
// TODO make the 6502 codegen better so this workaround 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) {
|
||||||
|
@ -36,27 +36,9 @@ internal class BeforeAsmAstChanger(val program: Program,
|
|||||||
return noModifications
|
return noModifications
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun before(block: Block, parent: Node): Iterable<IAstModification> {
|
|
||||||
// adjust global variables initialization
|
|
||||||
if(options.dontReinitGlobals) {
|
|
||||||
block.statements.asSequence().filterIsInstance<VarDecl>().forEach {
|
|
||||||
if(it.type==VarDeclType.VAR) {
|
|
||||||
it.zeropage = ZeropageWish.NOT_IN_ZEROPAGE
|
|
||||||
it.findInitializer(program)?.let { initializer ->
|
|
||||||
it.value = initializer.value // put the init value back into the vardecl
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return noModifications
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> {
|
override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> {
|
||||||
if(!options.dontReinitGlobals) {
|
if (decl.type == VarDeclType.VAR && decl.value != null && decl.datatype in NumericDatatypes)
|
||||||
if (decl.type == VarDeclType.VAR && decl.value != null && decl.datatype in NumericDatatypes)
|
throw InternalCompilerException("vardecls for variables, with initial numerical value, should have been rewritten as plain vardecl + assignment $decl")
|
||||||
throw InternalCompilerException("vardecls for variables, with initial numerical value, should have been rewritten as plain vardecl + assignment $decl")
|
|
||||||
}
|
|
||||||
|
|
||||||
return noModifications
|
return noModifications
|
||||||
}
|
}
|
||||||
@ -90,7 +72,7 @@ internal class BeforeAsmAstChanger(val program: Program,
|
|||||||
"unknown dt"
|
"unknown dt"
|
||||||
)
|
)
|
||||||
}, sourceDt, implicit=true)
|
}, sourceDt, implicit=true)
|
||||||
val assignRight = Assignment(assignment.target, right, AssignmentOrigin.BEFOREASMGEN, assignment.position)
|
val assignRight = Assignment(assignment.target, right, AssignmentOrigin.ASMGEN, assignment.position)
|
||||||
return listOf(
|
return listOf(
|
||||||
IAstModification.InsertBefore(assignment, assignRight, parent as IStatementContainer),
|
IAstModification.InsertBefore(assignment, assignRight, parent as IStatementContainer),
|
||||||
IAstModification.ReplaceNode(binExpr.right, binExpr.left, binExpr),
|
IAstModification.ReplaceNode(binExpr.right, binExpr.left, binExpr),
|
||||||
@ -103,7 +85,7 @@ internal class BeforeAsmAstChanger(val program: Program,
|
|||||||
"unknown dt"
|
"unknown dt"
|
||||||
)
|
)
|
||||||
}, sourceDt, implicit=true)
|
}, sourceDt, implicit=true)
|
||||||
val assignLeft = Assignment(assignment.target, left, AssignmentOrigin.BEFOREASMGEN, assignment.position)
|
val assignLeft = Assignment(assignment.target, left, AssignmentOrigin.ASMGEN, assignment.position)
|
||||||
return listOf(
|
return listOf(
|
||||||
IAstModification.InsertBefore(assignment, assignLeft, parent as IStatementContainer),
|
IAstModification.InsertBefore(assignment, assignLeft, parent as IStatementContainer),
|
||||||
IAstModification.ReplaceNode(binExpr.left, assignment.target.toExpression(), binExpr)
|
IAstModification.ReplaceNode(binExpr.left, assignment.target.toExpression(), binExpr)
|
||||||
@ -293,7 +275,7 @@ internal class BeforeAsmAstChanger(val program: Program,
|
|||||||
val dt = expr.indexer.indexExpr.inferType(program)
|
val dt = expr.indexer.indexExpr.inferType(program)
|
||||||
val (tempVarName, _) = program.getTempVar(dt.getOrElse { throw FatalAstException("invalid dt") })
|
val (tempVarName, _) = program.getTempVar(dt.getOrElse { throw FatalAstException("invalid dt") })
|
||||||
val target = AssignTarget(IdentifierReference(tempVarName, expr.indexer.position), null, null, expr.indexer.position)
|
val target = AssignTarget(IdentifierReference(tempVarName, expr.indexer.position), null, null, expr.indexer.position)
|
||||||
val assign = Assignment(target, expr.indexer.indexExpr, AssignmentOrigin.BEFOREASMGEN, expr.indexer.position)
|
val assign = Assignment(target, expr.indexer.indexExpr, AssignmentOrigin.ASMGEN, expr.indexer.position)
|
||||||
modifications.add(IAstModification.InsertBefore(statement, assign, statement.parent as IStatementContainer))
|
modifications.add(IAstModification.InsertBefore(statement, assign, statement.parent as IStatementContainer))
|
||||||
modifications.add(
|
modifications.add(
|
||||||
IAstModification.ReplaceNode(
|
IAstModification.ReplaceNode(
|
||||||
|
@ -30,7 +30,7 @@ internal class BoolRemover(val program: Program) : AstWalker() {
|
|||||||
newvalue = NumericLiteral(DataType.UBYTE, 1.0, newvalue.position)
|
newvalue = NumericLiteral(DataType.UBYTE, 1.0, newvalue.position)
|
||||||
}
|
}
|
||||||
val ubyteDecl = VarDecl(decl.type, decl.origin, DataType.UBYTE, decl.zeropage, decl.arraysize, decl.name,
|
val ubyteDecl = VarDecl(decl.type, decl.origin, DataType.UBYTE, decl.zeropage, decl.arraysize, decl.name,
|
||||||
newvalue, decl.isArray, decl.sharedWithAsm, decl.subroutineParameter, decl.position)
|
newvalue, decl.isArray, decl.sharedWithAsm, decl.position)
|
||||||
return listOf(IAstModification.ReplaceNode(decl, ubyteDecl, parent))
|
return listOf(IAstModification.ReplaceNode(decl, ubyteDecl, parent))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -47,7 +47,7 @@ internal class BoolRemover(val program: Program) : AstWalker() {
|
|||||||
newarray = ArrayLiteral(InferredTypes.InferredType.known(DataType.ARRAY_UB), convertedArray, decl.position)
|
newarray = ArrayLiteral(InferredTypes.InferredType.known(DataType.ARRAY_UB), convertedArray, decl.position)
|
||||||
}
|
}
|
||||||
val ubyteArrayDecl = VarDecl(decl.type, decl.origin, DataType.ARRAY_UB, decl.zeropage, decl.arraysize, decl.name,
|
val ubyteArrayDecl = VarDecl(decl.type, decl.origin, DataType.ARRAY_UB, decl.zeropage, decl.arraysize, decl.name,
|
||||||
newarray, true, decl.sharedWithAsm, decl.subroutineParameter, decl.position)
|
newarray, true, decl.sharedWithAsm, decl.position)
|
||||||
return listOf(IAstModification.ReplaceNode(decl, ubyteArrayDecl, parent))
|
return listOf(IAstModification.ReplaceNode(decl, ubyteArrayDecl, parent))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,12 +8,11 @@ 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.SymbolTable
|
|
||||||
import prog8.code.ast.*
|
import prog8.code.ast.*
|
||||||
|
import prog8.code.core.BuiltinFunctions
|
||||||
import prog8.code.core.CompilationOptions
|
import prog8.code.core.CompilationOptions
|
||||||
import prog8.code.core.DataType
|
import prog8.code.core.DataType
|
||||||
import prog8.code.core.SourceCode
|
import prog8.code.core.SourceCode
|
||||||
import prog8.compiler.BuiltinFunctions
|
|
||||||
import prog8.compiler.builtinFunctionReturnType
|
import prog8.compiler.builtinFunctionReturnType
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import kotlin.io.path.Path
|
import kotlin.io.path.Path
|
||||||
@ -23,7 +22,7 @@ import kotlin.io.path.isRegularFile
|
|||||||
/**
|
/**
|
||||||
* Convert 'old' compiler-AST into the 'new' simplified AST with baked types.
|
* Convert 'old' compiler-AST into the 'new' simplified AST with baked types.
|
||||||
*/
|
*/
|
||||||
class IntermediateAstMaker(private val program: Program, private val symbolTable: SymbolTable, private val options: CompilationOptions) {
|
class IntermediateAstMaker(private val program: Program, private val options: CompilationOptions) {
|
||||||
fun transform(): PtProgram {
|
fun transform(): PtProgram {
|
||||||
val ptProgram = PtProgram(
|
val ptProgram = PtProgram(
|
||||||
program.name,
|
program.name,
|
||||||
@ -91,6 +90,54 @@ class IntermediateAstMaker(private val program: Program, private val symbolTable
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun transform(srcAssign: Assignment): PtNode {
|
private fun transform(srcAssign: Assignment): PtNode {
|
||||||
|
if(srcAssign.isAugmentable) {
|
||||||
|
val srcExpr = srcAssign.value
|
||||||
|
val (operator: String, augmentedValue: Expression?) = when(srcExpr) {
|
||||||
|
is BinaryExpression -> {
|
||||||
|
if(srcExpr.operator=="==" || srcExpr.operator=="%") {
|
||||||
|
// no special code possible for 'in-place comparison and result as boolean' or 'remainder'
|
||||||
|
Pair("", null)
|
||||||
|
}
|
||||||
|
else if(srcExpr.left isSameAs srcAssign.target) {
|
||||||
|
Pair(srcExpr.operator+'=', srcExpr.right)
|
||||||
|
} else if(srcExpr.right isSameAs srcAssign.target) {
|
||||||
|
Pair(srcExpr.operator+'=', srcExpr.left)
|
||||||
|
} else {
|
||||||
|
// either left or right is same as target, other combinations are not supported here
|
||||||
|
Pair("", null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is PrefixExpression -> {
|
||||||
|
require(srcExpr.expression isSameAs srcAssign.target)
|
||||||
|
Pair(srcExpr.operator, srcExpr.expression)
|
||||||
|
}
|
||||||
|
is TypecastExpression -> {
|
||||||
|
// At this time, there are no special optimized instructions to do an in-place type conversion.
|
||||||
|
// so we simply revert to a regular type converting assignment.
|
||||||
|
// Also, an in-place type cast is very uncommon so probably not worth optimizing anyway.
|
||||||
|
Pair("", null)
|
||||||
|
// the following is what *could* be used here if such instructions *were* available:
|
||||||
|
// if(srcExpr.expression isSameAs srcAssign.target)
|
||||||
|
// Pair("cast", srcExpr.expression)
|
||||||
|
// else {
|
||||||
|
// val subTypeCast = srcExpr.expression as? TypecastExpression
|
||||||
|
// val targetDt = srcAssign.target.inferType(program).getOrElse { DataType.UNDEFINED }
|
||||||
|
// if (subTypeCast!=null && srcExpr.type==targetDt && subTypeCast.expression isSameAs srcAssign.target) {
|
||||||
|
// Pair("cast", subTypeCast)
|
||||||
|
// } else
|
||||||
|
// Pair("", null)
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
else -> Pair("", null)
|
||||||
|
}
|
||||||
|
if(augmentedValue!=null) {
|
||||||
|
val assign = PtAugmentedAssign(operator, srcAssign.position)
|
||||||
|
assign.add(transform(srcAssign.target))
|
||||||
|
assign.add(transformExpression(augmentedValue))
|
||||||
|
return assign
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val assign = PtAssignment(srcAssign.position)
|
val assign = PtAssignment(srcAssign.position)
|
||||||
assign.add(transform(srcAssign.target))
|
assign.add(transform(srcAssign.target))
|
||||||
assign.add(transformExpression(srcAssign.value))
|
assign.add(transformExpression(srcAssign.value))
|
||||||
@ -130,7 +177,8 @@ class IntermediateAstMaker(private val program: Program, private val symbolTable
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
val (vardecls, statements) = srcBlock.statements.partition { it is VarDecl }
|
val (vardecls, statements) = srcBlock.statements.partition { it is VarDecl }
|
||||||
val block = PtBlock(srcBlock.name, srcBlock.address, srcBlock.isInLibrary, forceOutput, alignment, srcBlock.position)
|
val src = srcBlock.definingModule.source
|
||||||
|
val block = PtBlock(srcBlock.name, srcBlock.address, srcBlock.isInLibrary, forceOutput, alignment, src, srcBlock.position)
|
||||||
makeScopeVarsDecls(vardecls).forEach { block.add(it) }
|
makeScopeVarsDecls(vardecls).forEach { block.add(it) }
|
||||||
for (stmt in statements)
|
for (stmt in statements)
|
||||||
block.add(transformStatement(stmt))
|
block.add(transformStatement(stmt))
|
||||||
@ -285,18 +333,15 @@ class IntermediateAstMaker(private val program: Program, private val symbolTable
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun transformAsmSub(srcSub: Subroutine): PtAsmSub {
|
private fun transformAsmSub(srcSub: Subroutine): PtAsmSub {
|
||||||
val params = srcSub.parameters
|
val params = srcSub.asmParameterRegisters.zip(srcSub.parameters.map { PtSubroutineParameter(it.name, it.type, it.position) })
|
||||||
.map { PtSubroutineParameter(it.name, it.type, it.position) }
|
|
||||||
.zip(srcSub.asmParameterRegisters)
|
|
||||||
val sub = PtAsmSub(srcSub.name,
|
val sub = PtAsmSub(srcSub.name,
|
||||||
srcSub.asmAddress,
|
srcSub.asmAddress,
|
||||||
srcSub.asmClobbers,
|
srcSub.asmClobbers,
|
||||||
params,
|
params,
|
||||||
srcSub.returntypes,
|
srcSub.asmReturnvaluesRegisters.zip(srcSub.returntypes),
|
||||||
srcSub.asmReturnvaluesRegisters,
|
|
||||||
srcSub.inline,
|
srcSub.inline,
|
||||||
srcSub.position)
|
srcSub.position)
|
||||||
sub.parameters.forEach { it.first.parent=sub }
|
sub.parameters.forEach { it.second.parent=sub }
|
||||||
|
|
||||||
if(srcSub.asmAddress==null) {
|
if(srcSub.asmAddress==null) {
|
||||||
var combinedTrueAsm = ""
|
var combinedTrueAsm = ""
|
||||||
@ -329,10 +374,11 @@ class IntermediateAstMaker(private val program: Program, private val symbolTable
|
|||||||
var returntype = srcSub.returntypes.singleOrNull()
|
var returntype = srcSub.returntypes.singleOrNull()
|
||||||
if(returntype==DataType.STR)
|
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.
|
returntype=DataType.UWORD // if a sub returns 'str', replace with uword. Intermediate AST and I.R. don't contain 'str' datatype anymore.
|
||||||
|
|
||||||
|
// do not bother about the 'inline' hint of the source subroutine.
|
||||||
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) },
|
||||||
returntype,
|
returntype,
|
||||||
srcSub.inline,
|
|
||||||
srcSub.position)
|
srcSub.position)
|
||||||
sub.parameters.forEach { it.parent=sub }
|
sub.parameters.forEach { it.parent=sub }
|
||||||
makeScopeVarsDecls(vardecls).forEach { sub.add(it) }
|
makeScopeVarsDecls(vardecls).forEach { sub.add(it) }
|
||||||
@ -346,7 +392,7 @@ class IntermediateAstMaker(private val program: Program, private val symbolTable
|
|||||||
return when(srcVar.type) {
|
return when(srcVar.type) {
|
||||||
VarDeclType.VAR -> {
|
VarDeclType.VAR -> {
|
||||||
val value = if(srcVar.value!=null) transformExpression(srcVar.value!!) else null
|
val value = if(srcVar.value!=null) transformExpression(srcVar.value!!) else null
|
||||||
PtVariable(srcVar.name, srcVar.datatype, value, srcVar.arraysize?.constIndex()?.toUInt(), srcVar.position)
|
PtVariable(srcVar.name, srcVar.datatype, srcVar.zeropage, value, srcVar.arraysize?.constIndex()?.toUInt(), srcVar.position)
|
||||||
}
|
}
|
||||||
VarDeclType.CONST -> PtConstant(srcVar.name, srcVar.datatype, (srcVar.value as NumericLiteral).number, srcVar.position)
|
VarDeclType.CONST -> PtConstant(srcVar.name, srcVar.datatype, (srcVar.value as NumericLiteral).number, srcVar.position)
|
||||||
VarDeclType.MEMORY -> PtMemMapped(srcVar.name, srcVar.datatype, (srcVar.value as NumericLiteral).number.toUInt(), srcVar.arraysize?.constIndex()?.toUInt(), srcVar.position)
|
VarDeclType.MEMORY -> PtMemMapped(srcVar.name, srcVar.datatype, (srcVar.value as NumericLiteral).number.toUInt(), srcVar.arraysize?.constIndex()?.toUInt(), srcVar.position)
|
||||||
@ -385,8 +431,8 @@ class IntermediateAstMaker(private val program: Program, private val symbolTable
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun transform(srcArr: ArrayIndexedExpression): PtArrayIndexer {
|
private fun transform(srcArr: ArrayIndexedExpression): PtArrayIndexer {
|
||||||
val type = srcArr.inferType(program).getOrElse { throw FatalAstException("unknown dt") }
|
val arrayVarType = srcArr.inferType(program).getOrElse { throw FatalAstException("unknown dt") }
|
||||||
val array = PtArrayIndexer(type, srcArr.position)
|
val array = PtArrayIndexer(arrayVarType, srcArr.position)
|
||||||
array.add(transform(srcArr.arrayvar))
|
array.add(transform(srcArr.arrayvar))
|
||||||
array.add(transformExpression(srcArr.indexer.indexExpr))
|
array.add(transformExpression(srcArr.indexer.indexExpr))
|
||||||
return array
|
return array
|
||||||
@ -464,6 +510,7 @@ class IntermediateAstMaker(private val program: Program, private val symbolTable
|
|||||||
private fun transform(srcCast: TypecastExpression): PtTypeCast {
|
private fun transform(srcCast: TypecastExpression): PtTypeCast {
|
||||||
val cast = PtTypeCast(srcCast.type, srcCast.position)
|
val cast = PtTypeCast(srcCast.type, srcCast.position)
|
||||||
cast.add(transformExpression(srcCast.expression))
|
cast.add(transformExpression(srcCast.expression))
|
||||||
|
require(cast.type!=cast.value.type)
|
||||||
return cast
|
return cast
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -153,17 +153,6 @@ internal class NotExpressionAndIfComparisonExprChanger(val program: Program, val
|
|||||||
if(compTarget.name == VMTarget.NAME) // don't apply this optimization for Vm target
|
if(compTarget.name == VMTarget.NAME) // don't apply this optimization for Vm target
|
||||||
return CondExprSimplificationResult(null, null, null, null)
|
return CondExprSimplificationResult(null, null, null, null)
|
||||||
|
|
||||||
var leftAssignment: Assignment? = null
|
|
||||||
var leftOperandReplacement: Expression? = null
|
|
||||||
var rightAssignment: Assignment? = null
|
|
||||||
var rightOperandReplacement: Expression? = null
|
|
||||||
|
|
||||||
val separateLeftExpr = !expr.left.isSimple
|
|
||||||
&& expr.left !is IFunctionCall
|
|
||||||
&& expr.left !is ContainmentCheck
|
|
||||||
val separateRightExpr = !expr.right.isSimple
|
|
||||||
&& expr.right !is IFunctionCall
|
|
||||||
&& expr.right !is ContainmentCheck
|
|
||||||
val leftDt = expr.left.inferType(program)
|
val leftDt = expr.left.inferType(program)
|
||||||
val rightDt = expr.right.inferType(program)
|
val rightDt = expr.right.inferType(program)
|
||||||
|
|
||||||
@ -172,13 +161,24 @@ internal class NotExpressionAndIfComparisonExprChanger(val program: Program, val
|
|||||||
return CondExprSimplificationResult(null, null, null, null)
|
return CondExprSimplificationResult(null, null, null, null)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var leftAssignment: Assignment? = null
|
||||||
|
var leftOperandReplacement: Expression? = null
|
||||||
|
var rightAssignment: Assignment? = null
|
||||||
|
var rightOperandReplacement: Expression? = null
|
||||||
|
val separateLeftExpr = !expr.left.isSimple
|
||||||
|
&& expr.left !is IFunctionCall
|
||||||
|
&& expr.left !is ContainmentCheck
|
||||||
|
val separateRightExpr = !expr.right.isSimple
|
||||||
|
&& expr.right !is IFunctionCall
|
||||||
|
&& expr.right !is ContainmentCheck
|
||||||
|
|
||||||
if(separateLeftExpr) {
|
if(separateLeftExpr) {
|
||||||
val name = getTempRegisterName(leftDt)
|
val name = getTempRegisterName(leftDt)
|
||||||
leftOperandReplacement = IdentifierReference(name, expr.position)
|
leftOperandReplacement = IdentifierReference(name, expr.position)
|
||||||
leftAssignment = Assignment(
|
leftAssignment = Assignment(
|
||||||
AssignTarget(IdentifierReference(name, expr.position), null, null, expr.position),
|
AssignTarget(IdentifierReference(name, expr.position), null, null, expr.position),
|
||||||
expr.left.copy(),
|
expr.left.copy(),
|
||||||
AssignmentOrigin.BEFOREASMGEN, expr.position
|
AssignmentOrigin.ASMGEN, expr.position
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
if(separateRightExpr) {
|
if(separateRightExpr) {
|
||||||
@ -187,7 +187,7 @@ internal class NotExpressionAndIfComparisonExprChanger(val program: Program, val
|
|||||||
rightAssignment = Assignment(
|
rightAssignment = Assignment(
|
||||||
AssignTarget(IdentifierReference(tempVarName, expr.position), null, null, expr.position),
|
AssignTarget(IdentifierReference(tempVarName, expr.position), null, null, expr.position),
|
||||||
expr.right.copy(),
|
expr.right.copy(),
|
||||||
AssignmentOrigin.BEFOREASMGEN, expr.position
|
AssignmentOrigin.ASMGEN, expr.position
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
return CondExprSimplificationResult(
|
return CondExprSimplificationResult(
|
||||||
|
@ -153,7 +153,7 @@ internal class StatementReorderer(val program: Program,
|
|||||||
subroutine.statements
|
subroutine.statements
|
||||||
.asSequence()
|
.asSequence()
|
||||||
.filterIsInstance<VarDecl>()
|
.filterIsInstance<VarDecl>()
|
||||||
.filter { it.subroutineParameter!=null && it.name in stringParamsByNames }
|
.filter { it.origin==VarDeclOrigin.SUBROUTINEPARAM && it.name in stringParamsByNames }
|
||||||
.map {
|
.map {
|
||||||
val newvar = VarDecl(it.type, it.origin, DataType.UWORD,
|
val newvar = VarDecl(it.type, it.origin, DataType.UWORD,
|
||||||
it.zeropage,
|
it.zeropage,
|
||||||
@ -162,7 +162,6 @@ internal class StatementReorderer(val program: Program,
|
|||||||
null,
|
null,
|
||||||
false,
|
false,
|
||||||
it.sharedWithAsm,
|
it.sharedWithAsm,
|
||||||
stringParamsByNames.getValue(it.name),
|
|
||||||
it.position
|
it.position
|
||||||
)
|
)
|
||||||
IAstModification.ReplaceNode(it, newvar, subroutine)
|
IAstModification.ReplaceNode(it, newvar, subroutine)
|
||||||
|
@ -1,138 +0,0 @@
|
|||||||
package prog8.compiler.astprocessing
|
|
||||||
|
|
||||||
import prog8.ast.Program
|
|
||||||
import prog8.ast.base.FatalAstException
|
|
||||||
import prog8.ast.expressions.*
|
|
||||||
import prog8.ast.statements.*
|
|
||||||
import prog8.ast.walk.IAstVisitor
|
|
||||||
import prog8.code.*
|
|
||||||
import prog8.code.core.ArrayDatatypes
|
|
||||||
import prog8.code.core.CompilationOptions
|
|
||||||
import prog8.code.core.DataType
|
|
||||||
import prog8.code.core.Position
|
|
||||||
import java.util.*
|
|
||||||
|
|
||||||
internal class SymbolTableMaker(private val program: Program, private val options: CompilationOptions): IAstVisitor {
|
|
||||||
|
|
||||||
private val st = SymbolTable()
|
|
||||||
private val scopestack = Stack<StNode>()
|
|
||||||
private var dontReinitGlobals = false
|
|
||||||
|
|
||||||
fun make(): SymbolTable {
|
|
||||||
scopestack.clear()
|
|
||||||
st.children.clear()
|
|
||||||
dontReinitGlobals = options.dontReinitGlobals
|
|
||||||
this.visit(program)
|
|
||||||
program.builtinFunctions.names.forEach {
|
|
||||||
val node = StNode(it, StNodeType.BUILTINFUNC, Position.DUMMY)
|
|
||||||
st.add(node)
|
|
||||||
}
|
|
||||||
require(scopestack.isEmpty())
|
|
||||||
return st
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun visit(block: Block) {
|
|
||||||
val node = StNode(block.name, StNodeType.BLOCK, block.position)
|
|
||||||
st.add(node)
|
|
||||||
scopestack.push(node)
|
|
||||||
super.visit(block)
|
|
||||||
scopestack.pop()
|
|
||||||
// st.origAstLinks[block] = node
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun visit(subroutine: Subroutine) {
|
|
||||||
if(subroutine.asmAddress!=null) {
|
|
||||||
val parameters = subroutine.parameters.zip(subroutine.asmParameterRegisters).map { StRomSubParameter(it.second, it.first.type) }
|
|
||||||
val node = StRomSub(subroutine.name, subroutine.asmAddress!!, parameters, subroutine.asmReturnvaluesRegisters, subroutine.position)
|
|
||||||
scopestack.peek().add(node)
|
|
||||||
// st.origAstLinks[subroutine] = node
|
|
||||||
} else {
|
|
||||||
val parameters = subroutine.parameters.map { StSubroutineParameter(it.name, it.type) }
|
|
||||||
val returnType = if(subroutine.returntypes.isEmpty()) null else subroutine.returntypes.first()
|
|
||||||
val node = StSub(subroutine.name, parameters, returnType, subroutine.position)
|
|
||||||
scopestack.peek().add(node)
|
|
||||||
scopestack.push(node)
|
|
||||||
super.visit(subroutine)
|
|
||||||
scopestack.pop()
|
|
||||||
// st.origAstLinks[subroutine] = node
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun visit(decl: VarDecl) {
|
|
||||||
val node =
|
|
||||||
when(decl.type) {
|
|
||||||
VarDeclType.VAR -> {
|
|
||||||
var initialNumeric = (decl.value as? NumericLiteral)?.number
|
|
||||||
if(initialNumeric==0.0)
|
|
||||||
initialNumeric=null // variable will go into BSS and this will be set to 0
|
|
||||||
val initialStringLit = decl.value as? StringLiteral
|
|
||||||
val initialString = if(initialStringLit==null) null else Pair(initialStringLit.value, initialStringLit.encoding)
|
|
||||||
val initialArrayLit = decl.value as? ArrayLiteral
|
|
||||||
val initialArray = makeInitialArray(initialArrayLit)
|
|
||||||
if(decl.isArray && decl.datatype !in ArrayDatatypes)
|
|
||||||
throw FatalAstException("array vardecl has mismatched dt ${decl.datatype}")
|
|
||||||
val numElements =
|
|
||||||
if(decl.isArray)
|
|
||||||
decl.arraysize!!.constIndex()
|
|
||||||
else if(initialStringLit!=null)
|
|
||||||
initialStringLit.value.length+1 // include the terminating 0-byte
|
|
||||||
else
|
|
||||||
null
|
|
||||||
val bss = if(decl.datatype==DataType.STR)
|
|
||||||
false
|
|
||||||
else if(decl.isArray)
|
|
||||||
initialArray.isNullOrEmpty()
|
|
||||||
else
|
|
||||||
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.MEMORY -> {
|
|
||||||
val numElements =
|
|
||||||
if(decl.datatype in ArrayDatatypes)
|
|
||||||
decl.arraysize!!.constIndex()
|
|
||||||
else null
|
|
||||||
StMemVar(decl.name, decl.datatype, (decl.value as NumericLiteral).number.toUInt(), numElements, decl.position)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
scopestack.peek().add(node)
|
|
||||||
// st.origAstLinks[decl] = node
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun makeInitialArray(arrayLit: ArrayLiteral?): StArray? {
|
|
||||||
if(arrayLit==null)
|
|
||||||
return null
|
|
||||||
return arrayLit.value.map {
|
|
||||||
when(it){
|
|
||||||
is AddressOf -> {
|
|
||||||
val scopedName = it.identifier.targetNameAndType(program).first
|
|
||||||
StArrayElement(null, scopedName)
|
|
||||||
}
|
|
||||||
is IdentifierReference -> {
|
|
||||||
val scopedName = it.targetNameAndType(program).first
|
|
||||||
StArrayElement(null, scopedName)
|
|
||||||
}
|
|
||||||
is NumericLiteral -> StArrayElement(it.number, null)
|
|
||||||
else -> throw FatalAstException("weird element dt in array literal")
|
|
||||||
}
|
|
||||||
}.toList()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun visit(label: Label) {
|
|
||||||
val node = StNode(label.name, StNodeType.LABEL, label.position)
|
|
||||||
scopestack.peek().add(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)
|
|
||||||
}
|
|
||||||
}
|
|
@ -9,7 +9,6 @@ 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
|
|
||||||
|
|
||||||
|
|
||||||
class TypecastsAdder(val program: Program, val options: CompilationOptions, val errors: IErrorReporter) : AstWalker() {
|
class TypecastsAdder(val program: Program, val options: CompilationOptions, val errors: IErrorReporter) : AstWalker() {
|
||||||
|
@ -40,7 +40,7 @@ internal class VariousCleanups(val program: Program, val errors: IErrorReporter,
|
|||||||
}
|
}
|
||||||
|
|
||||||
val sourceDt = typecast.expression.inferType(program)
|
val sourceDt = typecast.expression.inferType(program)
|
||||||
if(sourceDt istype typecast.type)
|
if(sourceDt istype typecast.type || (sourceDt istype DataType.BOOL && typecast.type==DataType.UBYTE))
|
||||||
return listOf(IAstModification.ReplaceNode(typecast, typecast.expression, parent))
|
return listOf(IAstModification.ReplaceNode(typecast, typecast.expression, parent))
|
||||||
|
|
||||||
if(parent is Assignment) {
|
if(parent is Assignment) {
|
||||||
@ -62,6 +62,11 @@ internal class VariousCleanups(val program: Program, val errors: IErrorReporter,
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun after(assignment: Assignment, parent: Node): Iterable<IAstModification> {
|
override fun after(assignment: Assignment, parent: Node): Iterable<IAstModification> {
|
||||||
|
if(assignment.target isSameAs assignment.value) {
|
||||||
|
// remove assignment to self
|
||||||
|
return listOf(IAstModification.Remove(assignment, parent as IStatementContainer))
|
||||||
|
}
|
||||||
|
|
||||||
// remove duplicated assignments, but not if it's a memory mapped IO register
|
// remove duplicated assignments, but not if it's a memory mapped IO register
|
||||||
val isIO = try {
|
val isIO = try {
|
||||||
assignment.target.isIOAddress(options.compTarget.machine)
|
assignment.target.isIOAddress(options.compTarget.machine)
|
||||||
|
@ -5,11 +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.*
|
||||||
import prog8.code.core.DataType
|
|
||||||
import prog8.code.core.IErrorReporter
|
|
||||||
import prog8.code.core.Position
|
|
||||||
import prog8.compiler.BuiltinFunctions
|
|
||||||
|
|
||||||
internal class VerifyFunctionArgTypes(val program: Program, val errors: IErrorReporter) : IAstVisitor {
|
internal class VerifyFunctionArgTypes(val program: Program, val errors: IErrorReporter) : IAstVisitor {
|
||||||
|
|
||||||
|
@ -43,7 +43,7 @@ class TestModuleImporter: FunSpec({
|
|||||||
val importer = makeImporter(null, dirRel.invariantSeparatorsPathString)
|
val importer = makeImporter(null, dirRel.invariantSeparatorsPathString)
|
||||||
val srcPathRel = assumeNotExists(dirRel, "i_do_not_exist")
|
val srcPathRel = assumeNotExists(dirRel, "i_do_not_exist")
|
||||||
val srcPathAbs = srcPathRel.absolute()
|
val srcPathAbs = srcPathRel.absolute()
|
||||||
val error1 = importer.importModule(srcPathRel).getErrorOrElse { fail("should have import error") }
|
val error1 = importer.importMainModule(srcPathRel).getErrorOrElse { fail("should have import error") }
|
||||||
withClue(".file should be normalized") {
|
withClue(".file should be normalized") {
|
||||||
"${error1.file}" shouldBe "${error1.file.normalize()}"
|
"${error1.file}" shouldBe "${error1.file.normalize()}"
|
||||||
}
|
}
|
||||||
@ -51,7 +51,7 @@ class TestModuleImporter: FunSpec({
|
|||||||
error1.file.absolutePath shouldBe "${srcPathAbs.normalize()}"
|
error1.file.absolutePath shouldBe "${srcPathAbs.normalize()}"
|
||||||
}
|
}
|
||||||
program.modules.size shouldBe 1
|
program.modules.size shouldBe 1
|
||||||
val error2 = importer.importModule(srcPathAbs).getErrorOrElse { fail("should have import error") }
|
val error2 = importer.importMainModule(srcPathAbs).getErrorOrElse { fail("should have import error") }
|
||||||
withClue(".file should be normalized") {
|
withClue(".file should be normalized") {
|
||||||
"${error2.file}" shouldBe "${error2.file.normalize()}"
|
"${error2.file}" shouldBe "${error2.file.normalize()}"
|
||||||
}
|
}
|
||||||
@ -67,7 +67,7 @@ class TestModuleImporter: FunSpec({
|
|||||||
val searchIn = Path(".", "$srcPathRel").invariantSeparatorsPathString
|
val searchIn = Path(".", "$srcPathRel").invariantSeparatorsPathString
|
||||||
val importer = makeImporter(null, searchIn)
|
val importer = makeImporter(null, searchIn)
|
||||||
|
|
||||||
shouldThrow<FileSystemException> { importer.importModule(srcPathRel) }
|
shouldThrow<FileSystemException> { importer.importMainModule(srcPathRel) }
|
||||||
.let {
|
.let {
|
||||||
withClue(".file should be normalized") {
|
withClue(".file should be normalized") {
|
||||||
"${it.file}" shouldBe "${it.file.normalize()}"
|
"${it.file}" shouldBe "${it.file.normalize()}"
|
||||||
@ -78,7 +78,7 @@ class TestModuleImporter: FunSpec({
|
|||||||
}
|
}
|
||||||
program.modules.size shouldBe 1
|
program.modules.size shouldBe 1
|
||||||
|
|
||||||
shouldThrow<FileSystemException> { importer.importModule(srcPathAbs) }
|
shouldThrow<FileSystemException> { importer.importMainModule(srcPathAbs) }
|
||||||
.let {
|
.let {
|
||||||
withClue(".file should be normalized") {
|
withClue(".file should be normalized") {
|
||||||
"${it.file}" shouldBe "${it.file.normalize()}"
|
"${it.file}" shouldBe "${it.file.normalize()}"
|
||||||
@ -101,7 +101,7 @@ class TestModuleImporter: FunSpec({
|
|||||||
val fileName = "ast_simple_main.p8"
|
val fileName = "ast_simple_main.p8"
|
||||||
val path = assumeReadableFile(searchIn[0], fileName)
|
val path = assumeReadableFile(searchIn[0], fileName)
|
||||||
|
|
||||||
val module = importer.importModule(path.absolute()).getOrElse { throw it }
|
val module = importer.importMainModule(path.absolute()).getOrElse { throw it }
|
||||||
program.modules.size shouldBe 2
|
program.modules.size shouldBe 2
|
||||||
module shouldBeIn program.modules
|
module shouldBeIn program.modules
|
||||||
module.program shouldBe program
|
module.program shouldBe program
|
||||||
@ -118,7 +118,7 @@ class TestModuleImporter: FunSpec({
|
|||||||
path.isAbsolute shouldBe false
|
path.isAbsolute shouldBe false
|
||||||
}
|
}
|
||||||
|
|
||||||
val module = importer.importModule(path).getOrElse { throw it }
|
val module = importer.importMainModule(path).getOrElse { throw it }
|
||||||
program.modules.size shouldBe 2
|
program.modules.size shouldBe 2
|
||||||
module shouldBeIn program.modules
|
module shouldBeIn program.modules
|
||||||
module.program shouldBe program
|
module.program shouldBe program
|
||||||
@ -133,7 +133,7 @@ class TestModuleImporter: FunSpec({
|
|||||||
val path = Path(".", fileName)
|
val path = Path(".", fileName)
|
||||||
assumeReadableFile(searchIn, path)
|
assumeReadableFile(searchIn, path)
|
||||||
|
|
||||||
val module = importer.importModule(path).getOrElse { throw it }
|
val module = importer.importMainModule(path).getOrElse { throw it }
|
||||||
program.modules.size shouldBe 2
|
program.modules.size shouldBe 2
|
||||||
module shouldBeIn program.modules
|
module shouldBeIn program.modules
|
||||||
module.program shouldBe program
|
module.program shouldBe program
|
||||||
@ -145,7 +145,7 @@ class TestModuleImporter: FunSpec({
|
|||||||
val importer = makeImporter(null, searchIn.invariantSeparatorsPathString)
|
val importer = makeImporter(null, searchIn.invariantSeparatorsPathString)
|
||||||
val srcPath = assumeReadableFile(fixturesDir, "ast_file_with_syntax_error.p8")
|
val srcPath = assumeReadableFile(fixturesDir, "ast_file_with_syntax_error.p8")
|
||||||
|
|
||||||
val act = { importer.importModule(srcPath) }
|
val act = { importer.importMainModule(srcPath) }
|
||||||
|
|
||||||
repeat(2) { n -> withClue(count[n] + " call") {
|
repeat(2) { n -> withClue(count[n] + " call") {
|
||||||
shouldThrow<ParseError> { act() }.let {
|
shouldThrow<ParseError> { act() }.let {
|
||||||
@ -165,7 +165,7 @@ class TestModuleImporter: FunSpec({
|
|||||||
val importing = assumeReadableFile(fixturesDir, "import_file_with_syntax_error.p8")
|
val importing = assumeReadableFile(fixturesDir, "import_file_with_syntax_error.p8")
|
||||||
val imported = assumeReadableFile(fixturesDir, "file_with_syntax_error.p8")
|
val imported = assumeReadableFile(fixturesDir, "file_with_syntax_error.p8")
|
||||||
|
|
||||||
val act = { importer.importModule(importing) }
|
val act = { importer.importMainModule(importing) }
|
||||||
|
|
||||||
repeat(repetitions) { n -> withClue(count[n] + " call") {
|
repeat(repetitions) { n -> withClue(count[n] + " call") {
|
||||||
shouldThrow<ParseError> { act() }.let {
|
shouldThrow<ParseError> { act() }.let {
|
||||||
@ -201,14 +201,14 @@ class TestModuleImporter: FunSpec({
|
|||||||
val filenameWithExt = assumeNotExists(fixturesDir, "i_do_not_exist.p8").name
|
val filenameWithExt = assumeNotExists(fixturesDir, "i_do_not_exist.p8").name
|
||||||
|
|
||||||
repeat(2) { n ->
|
repeat(2) { n ->
|
||||||
val result = importer.importLibraryModule(filenameNoExt)
|
val result = importer.importImplicitLibraryModule(filenameNoExt)
|
||||||
withClue(count[n] + " call / NO .p8 extension") { result shouldBe null }
|
withClue(count[n] + " call / NO .p8 extension") { result shouldBe null }
|
||||||
withClue(count[n] + " call / NO .p8 extension") { errors.noErrors() shouldBe false }
|
withClue(count[n] + " call / NO .p8 extension") { errors.noErrors() shouldBe false }
|
||||||
errors.errors.single() shouldContain "0:0: no module found with name i_do_not_exist"
|
errors.errors.single() shouldContain "0:0: no module found with name i_do_not_exist"
|
||||||
errors.report()
|
errors.report()
|
||||||
program.modules.size shouldBe 1
|
program.modules.size shouldBe 1
|
||||||
|
|
||||||
val result2 = importer.importLibraryModule(filenameWithExt)
|
val result2 = importer.importImplicitLibraryModule(filenameWithExt)
|
||||||
withClue(count[n] + " call / with .p8 extension") { result2 shouldBe null }
|
withClue(count[n] + " call / with .p8 extension") { result2 shouldBe null }
|
||||||
withClue(count[n] + " call / with .p8 extension") { importer.errors.noErrors() shouldBe false }
|
withClue(count[n] + " call / with .p8 extension") { importer.errors.noErrors() shouldBe false }
|
||||||
errors.errors.single() shouldContain "0:0: no module found with name i_do_not_exist.p8"
|
errors.errors.single() shouldContain "0:0: no module found with name i_do_not_exist.p8"
|
||||||
@ -228,7 +228,7 @@ class TestModuleImporter: FunSpec({
|
|||||||
repeat(2) { n -> withClue(count[n] + " call") {
|
repeat(2) { n -> withClue(count[n] + " call") {
|
||||||
shouldThrow<ParseError>()
|
shouldThrow<ParseError>()
|
||||||
{
|
{
|
||||||
importer.importLibraryModule(srcPath.nameWithoutExtension) }.let {
|
importer.importImplicitLibraryModule(srcPath.nameWithoutExtension) }.let {
|
||||||
it.position.file shouldBe SourceCode.relative(srcPath).toString()
|
it.position.file shouldBe SourceCode.relative(srcPath).toString()
|
||||||
withClue("line; should be 1-based") { it.position.line shouldBe 2 }
|
withClue("line; should be 1-based") { it.position.line shouldBe 2 }
|
||||||
withClue("startCol; should be 0-based") { it.position.startCol shouldBe 6 }
|
withClue("startCol; should be 0-based") { it.position.startCol shouldBe 6 }
|
||||||
@ -246,7 +246,7 @@ class TestModuleImporter: FunSpec({
|
|||||||
val importing = assumeReadableFile(fixturesDir, "import_file_with_syntax_error.p8")
|
val importing = assumeReadableFile(fixturesDir, "import_file_with_syntax_error.p8")
|
||||||
val imported = assumeReadableFile(fixturesDir, "file_with_syntax_error.p8")
|
val imported = assumeReadableFile(fixturesDir, "file_with_syntax_error.p8")
|
||||||
|
|
||||||
val act = { importer.importLibraryModule(importing.nameWithoutExtension) }
|
val act = { importer.importImplicitLibraryModule(importing.nameWithoutExtension) }
|
||||||
|
|
||||||
repeat(repetitions) { n -> withClue(count[n] + " call") {
|
repeat(repetitions) { n -> withClue(count[n] + " call") {
|
||||||
shouldThrow<ParseError> {
|
shouldThrow<ParseError> {
|
||||||
|
@ -3,31 +3,19 @@ package prog8tests
|
|||||||
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 io.kotest.matchers.shouldNotBe
|
import io.kotest.matchers.shouldNotBe
|
||||||
|
import prog8.ast.expressions.NumericLiteral
|
||||||
|
import prog8.ast.statements.Assignment
|
||||||
|
import prog8.code.core.BuiltinFunctions
|
||||||
import prog8.code.core.DataType
|
import prog8.code.core.DataType
|
||||||
import prog8.code.core.NumericDatatypesNoBool
|
import prog8.code.core.NumericDatatypesNoBool
|
||||||
import prog8.code.core.RegisterOrPair
|
import prog8.code.core.RegisterOrPair
|
||||||
import prog8.code.target.Cx16Target
|
import prog8.code.target.Cx16Target
|
||||||
import prog8.compiler.BuiltinFunctions
|
|
||||||
import prog8tests.helpers.compileText
|
import prog8tests.helpers.compileText
|
||||||
|
|
||||||
class TestBuiltinFunctions: FunSpec({
|
class TestBuiltinFunctions: FunSpec({
|
||||||
|
|
||||||
test("push pop") {
|
|
||||||
val src="""
|
|
||||||
main {
|
|
||||||
sub start () {
|
|
||||||
pushw(cx16.r0)
|
|
||||||
push(cx16.r1L)
|
|
||||||
pop(cx16.r1L)
|
|
||||||
popw(cx16.r0)
|
|
||||||
}
|
|
||||||
}"""
|
|
||||||
compileText(Cx16Target(), false, src, writeAssembly = true) shouldNotBe null
|
|
||||||
}
|
|
||||||
|
|
||||||
test("pure func with fixed type") {
|
test("pure func with fixed type") {
|
||||||
val func = BuiltinFunctions.getValue("sgn")
|
val func = BuiltinFunctions.getValue("sgn")
|
||||||
func.name shouldBe "sgn"
|
|
||||||
func.parameters.size shouldBe 1
|
func.parameters.size shouldBe 1
|
||||||
func.parameters[0].name shouldBe "value"
|
func.parameters[0].name shouldBe "value"
|
||||||
func.parameters[0].possibleDatatypes shouldBe NumericDatatypesNoBool
|
func.parameters[0].possibleDatatypes shouldBe NumericDatatypesNoBool
|
||||||
@ -46,7 +34,6 @@ class TestBuiltinFunctions: FunSpec({
|
|||||||
|
|
||||||
test("not-pure func with varying result value type") {
|
test("not-pure func with varying result value type") {
|
||||||
val func = BuiltinFunctions.getValue("cmp")
|
val func = BuiltinFunctions.getValue("cmp")
|
||||||
func.name shouldBe "cmp"
|
|
||||||
func.parameters.size shouldBe 2
|
func.parameters.size shouldBe 2
|
||||||
func.pure shouldBe false
|
func.pure shouldBe false
|
||||||
func.returnType shouldBe null
|
func.returnType shouldBe null
|
||||||
@ -60,7 +47,6 @@ class TestBuiltinFunctions: FunSpec({
|
|||||||
|
|
||||||
test("func without return type") {
|
test("func without return type") {
|
||||||
val func = BuiltinFunctions.getValue("poke")
|
val func = BuiltinFunctions.getValue("poke")
|
||||||
func.name shouldBe "poke"
|
|
||||||
func.parameters.size shouldBe 2
|
func.parameters.size shouldBe 2
|
||||||
func.parameters[0].name shouldBe "address"
|
func.parameters[0].name shouldBe "address"
|
||||||
func.parameters[0].possibleDatatypes shouldBe arrayOf(DataType.UWORD)
|
func.parameters[0].possibleDatatypes shouldBe arrayOf(DataType.UWORD)
|
||||||
@ -81,5 +67,44 @@ class TestBuiltinFunctions: FunSpec({
|
|||||||
conv.returns.floatFac1 shouldBe false
|
conv.returns.floatFac1 shouldBe false
|
||||||
conv.returns.reg shouldBe null
|
conv.returns.reg shouldBe null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test("push pop") {
|
||||||
|
val src="""
|
||||||
|
main {
|
||||||
|
sub start () {
|
||||||
|
pushw(cx16.r0)
|
||||||
|
push(cx16.r1L)
|
||||||
|
pop(cx16.r1L)
|
||||||
|
popw(cx16.r0)
|
||||||
|
}
|
||||||
|
}"""
|
||||||
|
compileText(Cx16Target(), false, src, writeAssembly = true) shouldNotBe null
|
||||||
|
}
|
||||||
|
|
||||||
|
test("certain builtin functions should be compile time evaluated") {
|
||||||
|
val src="""
|
||||||
|
main {
|
||||||
|
sub start() {
|
||||||
|
|
||||||
|
uword[] array = [1,2,3]
|
||||||
|
str name = "hello"
|
||||||
|
cx16.r0L = len(array)
|
||||||
|
cx16.r0L = len(name)
|
||||||
|
cx16.r0L = sizeof(array)
|
||||||
|
cx16.r0 = mkword(200,100)
|
||||||
|
}
|
||||||
|
}"""
|
||||||
|
val result = compileText(Cx16Target(), false, src, writeAssembly = false)
|
||||||
|
val statements = result!!.compilerAst.entrypoint.statements
|
||||||
|
statements.size shouldBe 6
|
||||||
|
val a1 = statements[2] as Assignment
|
||||||
|
val a2 = statements[3] as Assignment
|
||||||
|
val a3 = statements[4] as Assignment
|
||||||
|
val a4 = statements[5] as Assignment
|
||||||
|
(a1.value as NumericLiteral).number shouldBe 3.0
|
||||||
|
(a2.value as NumericLiteral).number shouldBe 5.0
|
||||||
|
(a3.value as NumericLiteral).number shouldBe 6.0
|
||||||
|
(a4.value as NumericLiteral).number shouldBe 200*256+100
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -27,11 +27,11 @@ class TestCallgraph: FunSpec({
|
|||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
val result = compileText(C64Target(), false, sourcecode)!!
|
val result = compileText(C64Target(), false, sourcecode)!!
|
||||||
val graph = CallGraph(result.program)
|
val graph = CallGraph(result.compilerAst)
|
||||||
|
|
||||||
graph.imports.size shouldBe 1
|
graph.imports.size shouldBe 1
|
||||||
graph.importedBy.size shouldBe 1
|
graph.importedBy.size shouldBe 1
|
||||||
val toplevelModule = result.program.toplevelModule
|
val toplevelModule = result.compilerAst.toplevelModule
|
||||||
val importedModule = graph.imports.getValue(toplevelModule).single()
|
val importedModule = graph.imports.getValue(toplevelModule).single()
|
||||||
importedModule.name shouldBe "string"
|
importedModule.name shouldBe "string"
|
||||||
val importedBy = graph.importedBy.getValue(importedModule).single()
|
val importedBy = graph.importedBy.getValue(importedModule).single()
|
||||||
@ -46,7 +46,7 @@ class TestCallgraph: FunSpec({
|
|||||||
graph.calls shouldNotContainKey sub
|
graph.calls shouldNotContainKey sub
|
||||||
graph.calledBy shouldNotContainKey sub
|
graph.calledBy shouldNotContainKey sub
|
||||||
|
|
||||||
if(sub === result.program.entrypoint)
|
if(sub === result.compilerAst.entrypoint)
|
||||||
withClue("start() should always be marked as used to avoid having it removed") {
|
withClue("start() should always be marked as used to avoid having it removed") {
|
||||||
graph.unused(sub) shouldBe false
|
graph.unused(sub) shouldBe false
|
||||||
}
|
}
|
||||||
@ -68,11 +68,11 @@ class TestCallgraph: FunSpec({
|
|||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
val result = compileText(C64Target(), false, sourcecode)!!
|
val result = compileText(C64Target(), false, sourcecode)!!
|
||||||
val graph = CallGraph(result.program)
|
val graph = CallGraph(result.compilerAst)
|
||||||
|
|
||||||
graph.imports.size shouldBe 1
|
graph.imports.size shouldBe 1
|
||||||
graph.importedBy.size shouldBe 1
|
graph.importedBy.size shouldBe 1
|
||||||
val toplevelModule = result.program.toplevelModule
|
val toplevelModule = result.compilerAst.toplevelModule
|
||||||
val importedModule = graph.imports.getValue(toplevelModule).single()
|
val importedModule = graph.imports.getValue(toplevelModule).single()
|
||||||
importedModule.name shouldBe "string"
|
importedModule.name shouldBe "string"
|
||||||
val importedBy = graph.importedBy.getValue(importedModule).single()
|
val importedBy = graph.importedBy.getValue(importedModule).single()
|
||||||
@ -111,7 +111,7 @@ class TestCallgraph: FunSpec({
|
|||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
val result = compileText(C64Target(), false, sourcecode)!!
|
val result = compileText(C64Target(), false, sourcecode)!!
|
||||||
val graph = CallGraph(result.program)
|
val graph = CallGraph(result.compilerAst)
|
||||||
graph.allIdentifiers.size shouldBeGreaterThanOrEqual 5
|
graph.allIdentifiers.size shouldBeGreaterThanOrEqual 5
|
||||||
val empties = graph.allIdentifiers.keys.filter { it.nameInSource==listOf("empty") }
|
val empties = graph.allIdentifiers.keys.filter { it.nameInSource==listOf("empty") }
|
||||||
empties.size shouldBe 3
|
empties.size shouldBe 3
|
||||||
|
@ -33,7 +33,7 @@ class TestCompilerOnCharLit: FunSpec({
|
|||||||
}
|
}
|
||||||
""")!!
|
""")!!
|
||||||
|
|
||||||
val program = result.program
|
val program = result.compilerAst
|
||||||
val startSub = program.entrypoint
|
val startSub = program.entrypoint
|
||||||
val funCall = startSub.statements.filterIsInstance<IFunctionCall>()[0]
|
val funCall = startSub.statements.filterIsInstance<IFunctionCall>()[0]
|
||||||
|
|
||||||
@ -57,7 +57,7 @@ class TestCompilerOnCharLit: FunSpec({
|
|||||||
}
|
}
|
||||||
""")!!
|
""")!!
|
||||||
|
|
||||||
val program = result.program
|
val program = result.compilerAst
|
||||||
val startSub = program.entrypoint
|
val startSub = program.entrypoint
|
||||||
val funCall = startSub.statements.filterIsInstance<IFunctionCall>()[0]
|
val funCall = startSub.statements.filterIsInstance<IFunctionCall>()[0]
|
||||||
|
|
||||||
@ -92,7 +92,7 @@ class TestCompilerOnCharLit: FunSpec({
|
|||||||
}
|
}
|
||||||
""")!!
|
""")!!
|
||||||
|
|
||||||
val program = result.program
|
val program = result.compilerAst
|
||||||
val startSub = program.entrypoint
|
val startSub = program.entrypoint
|
||||||
val funCall = startSub.statements.filterIsInstance<IFunctionCall>()[0]
|
val funCall = startSub.statements.filterIsInstance<IFunctionCall>()[0]
|
||||||
|
|
||||||
|
@ -27,12 +27,12 @@ private fun compileTheThing(filepath: Path, optimize: Boolean, target: ICompilat
|
|||||||
filepath,
|
filepath,
|
||||||
optimize,
|
optimize,
|
||||||
optimizeFloatExpressions = true,
|
optimizeFloatExpressions = true,
|
||||||
dontReinitGlobals = false,
|
|
||||||
writeAssembly = true,
|
writeAssembly = true,
|
||||||
slowCodegenWarnings = false,
|
slowCodegenWarnings = false,
|
||||||
quietAssembler = true,
|
quietAssembler = true,
|
||||||
asmListfile = false,
|
asmListfile = false,
|
||||||
experimentalCodegen = false,
|
experimentalCodegen = false,
|
||||||
|
varsHigh = false,
|
||||||
compilationTarget = target.name,
|
compilationTarget = target.name,
|
||||||
evalStackBaseAddress = null,
|
evalStackBaseAddress = null,
|
||||||
symbolDefs = emptyMap(),
|
symbolDefs = emptyMap(),
|
||||||
@ -188,8 +188,8 @@ class TestCompilerOnExamplesVirtual: FunSpec({
|
|||||||
val (displayName, filepath) = prepareTestFiles(it, false, target)
|
val (displayName, filepath) = prepareTestFiles(it, false, target)
|
||||||
test(displayName) {
|
test(displayName) {
|
||||||
val src = filepath.readText()
|
val src = filepath.readText()
|
||||||
compileText(target, false, src, writeAssembly = true) shouldNotBe null
|
compileText(target, true, src, writeAssembly = true) shouldNotBe null
|
||||||
compileText(target, false, src, writeAssembly = true) shouldNotBe null
|
compileText(target, true, src, writeAssembly = true) shouldNotBe null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -30,7 +30,7 @@ class TestCompilerOnImportsAndIncludes: FunSpec({
|
|||||||
val platform = Cx16Target()
|
val platform = Cx16Target()
|
||||||
val result = compileFile(platform, optimize = false, fixturesDir, filepath.name)!!
|
val result = compileFile(platform, optimize = false, fixturesDir, filepath.name)!!
|
||||||
|
|
||||||
val program = result.program
|
val program = result.compilerAst
|
||||||
val startSub = program.entrypoint
|
val startSub = program.entrypoint
|
||||||
val strLits = startSub.statements
|
val strLits = startSub.statements
|
||||||
.filterIsInstance<FunctionCallStatement>()
|
.filterIsInstance<FunctionCallStatement>()
|
||||||
@ -52,7 +52,7 @@ class TestCompilerOnImportsAndIncludes: FunSpec({
|
|||||||
val platform = Cx16Target()
|
val platform = Cx16Target()
|
||||||
val result = compileFile(platform, optimize = false, fixturesDir, filepath.name)!!
|
val result = compileFile(platform, optimize = false, fixturesDir, filepath.name)!!
|
||||||
|
|
||||||
val program = result.program
|
val program = result.compilerAst
|
||||||
val startSub = program.entrypoint
|
val startSub = program.entrypoint
|
||||||
val args = startSub.statements
|
val args = startSub.statements
|
||||||
.filterIsInstance<FunctionCallStatement>()
|
.filterIsInstance<FunctionCallStatement>()
|
||||||
|
@ -40,7 +40,7 @@ class TestCompilerOnRanges: FunSpec({
|
|||||||
}
|
}
|
||||||
""")!!
|
""")!!
|
||||||
|
|
||||||
val program = result.program
|
val program = result.compilerAst
|
||||||
val startSub = program.entrypoint
|
val startSub = program.entrypoint
|
||||||
val decl = startSub
|
val decl = startSub
|
||||||
.statements.filterIsInstance<VarDecl>()[0]
|
.statements.filterIsInstance<VarDecl>()[0]
|
||||||
@ -72,7 +72,7 @@ class TestCompilerOnRanges: FunSpec({
|
|||||||
}
|
}
|
||||||
""")!!
|
""")!!
|
||||||
|
|
||||||
val program = result.program
|
val program = result.compilerAst
|
||||||
val startSub = program.entrypoint
|
val startSub = program.entrypoint
|
||||||
val decl = startSub
|
val decl = startSub
|
||||||
.statements.filterIsInstance<VarDecl>()[0]
|
.statements.filterIsInstance<VarDecl>()[0]
|
||||||
@ -143,7 +143,7 @@ class TestCompilerOnRanges: FunSpec({
|
|||||||
}
|
}
|
||||||
""")!!
|
""")!!
|
||||||
|
|
||||||
val program = result.program
|
val program = result.compilerAst
|
||||||
val startSub = program.entrypoint
|
val startSub = program.entrypoint
|
||||||
val iterable = startSub
|
val iterable = startSub
|
||||||
.statements.filterIsInstance<ForLoop>()
|
.statements.filterIsInstance<ForLoop>()
|
||||||
@ -177,7 +177,7 @@ class TestCompilerOnRanges: FunSpec({
|
|||||||
}
|
}
|
||||||
""")!!
|
""")!!
|
||||||
|
|
||||||
val program = result.program
|
val program = result.compilerAst
|
||||||
val startSub = program.entrypoint
|
val startSub = program.entrypoint
|
||||||
val rangeExpr = startSub
|
val rangeExpr = startSub
|
||||||
.statements.filterIsInstance<ForLoop>()
|
.statements.filterIsInstance<ForLoop>()
|
||||||
@ -203,7 +203,7 @@ class TestCompilerOnRanges: FunSpec({
|
|||||||
}
|
}
|
||||||
""")!!
|
""")!!
|
||||||
|
|
||||||
val program = result.program
|
val program = result.compilerAst
|
||||||
val startSub = program.entrypoint
|
val startSub = program.entrypoint
|
||||||
val rangeExpr = startSub
|
val rangeExpr = startSub
|
||||||
.statements.filterIsInstance<ForLoop>()
|
.statements.filterIsInstance<ForLoop>()
|
||||||
@ -247,7 +247,7 @@ class TestCompilerOnRanges: FunSpec({
|
|||||||
}
|
}
|
||||||
""")!!
|
""")!!
|
||||||
|
|
||||||
val program = result.program
|
val program = result.compilerAst
|
||||||
val startSub = program.entrypoint
|
val startSub = program.entrypoint
|
||||||
val iterable = startSub
|
val iterable = startSub
|
||||||
.statements.filterIsInstance<ForLoop>()
|
.statements.filterIsInstance<ForLoop>()
|
||||||
@ -279,7 +279,7 @@ class TestCompilerOnRanges: FunSpec({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
""")!!
|
""")!!
|
||||||
val statements = result.program.entrypoint.statements
|
val statements = result.compilerAst.entrypoint.statements
|
||||||
val array = (statements[0] as VarDecl).value
|
val array = (statements[0] as VarDecl).value
|
||||||
array shouldBe instanceOf<ArrayLiteral>()
|
array shouldBe instanceOf<ArrayLiteral>()
|
||||||
(array as ArrayLiteral).value.size shouldBe 26
|
(array as ArrayLiteral).value.size shouldBe 26
|
||||||
@ -301,7 +301,7 @@ class TestCompilerOnRanges: FunSpec({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
""")!!
|
""")!!
|
||||||
val statements = result.program.entrypoint.statements
|
val statements = result.compilerAst.entrypoint.statements
|
||||||
val forloop = (statements.dropLast(1).last() as ForLoop)
|
val forloop = (statements.dropLast(1).last() as ForLoop)
|
||||||
forloop.iterable shouldBe instanceOf<RangeExpression>()
|
forloop.iterable shouldBe instanceOf<RangeExpression>()
|
||||||
(forloop.iterable as RangeExpression).step shouldBe NumericLiteral(DataType.UBYTE, -2.0, Position.DUMMY)
|
(forloop.iterable as RangeExpression).step shouldBe NumericLiteral(DataType.UBYTE, -2.0, Position.DUMMY)
|
||||||
|
@ -44,12 +44,12 @@ class TestCompilerOptionSourcedirs: FunSpec({
|
|||||||
filepath = filePath,
|
filepath = filePath,
|
||||||
optimize = false,
|
optimize = false,
|
||||||
optimizeFloatExpressions = false,
|
optimizeFloatExpressions = false,
|
||||||
dontReinitGlobals = false,
|
|
||||||
writeAssembly = true,
|
writeAssembly = true,
|
||||||
slowCodegenWarnings = false,
|
slowCodegenWarnings = false,
|
||||||
quietAssembler = true,
|
quietAssembler = true,
|
||||||
asmListfile = false,
|
asmListfile = false,
|
||||||
experimentalCodegen = false,
|
experimentalCodegen = false,
|
||||||
|
varsHigh = false,
|
||||||
compilationTarget = Cx16Target.NAME,
|
compilationTarget = Cx16Target.NAME,
|
||||||
evalStackBaseAddress = null,
|
evalStackBaseAddress = null,
|
||||||
symbolDefs = emptyMap(),
|
symbolDefs = emptyMap(),
|
||||||
|
@ -25,7 +25,7 @@ class TestGoldenRam: FunSpec({
|
|||||||
test("empty golden ram allocations") {
|
test("empty golden ram allocations") {
|
||||||
val errors = ErrorReporterForTests()
|
val errors = ErrorReporterForTests()
|
||||||
val golden = GoldenRam(options, UIntRange.EMPTY)
|
val golden = GoldenRam(options, UIntRange.EMPTY)
|
||||||
val result = golden.allocate(listOf("test"), DataType.UBYTE, null, null, errors)
|
val result = golden.allocate("test", DataType.UBYTE, null, null, errors)
|
||||||
result.expectError { "should not be able to allocate anything" }
|
result.expectError { "should not be able to allocate anything" }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -33,28 +33,28 @@ class TestGoldenRam: FunSpec({
|
|||||||
val errors = ErrorReporterForTests()
|
val errors = ErrorReporterForTests()
|
||||||
val golden = GoldenRam(options, 0x400u until 0x800u)
|
val golden = GoldenRam(options, 0x400u until 0x800u)
|
||||||
|
|
||||||
var result = golden.allocate(listOf("test"), DataType.UBYTE, null, null, errors)
|
var result = golden.allocate("test", DataType.UBYTE, null, null, errors)
|
||||||
var alloc = result.getOrThrow()
|
var alloc = result.getOrThrow()
|
||||||
alloc.size shouldBe 1
|
alloc.size shouldBe 1
|
||||||
alloc.address shouldBe 0x400u
|
alloc.address shouldBe 0x400u
|
||||||
result = golden.allocate(listOf("test"), DataType.STR, 100, null, errors)
|
result = golden.allocate("test", DataType.STR, 100, null, errors)
|
||||||
alloc = result.getOrThrow()
|
alloc = result.getOrThrow()
|
||||||
alloc.size shouldBe 100
|
alloc.size shouldBe 100
|
||||||
alloc.address shouldBe 0x401u
|
alloc.address shouldBe 0x401u
|
||||||
|
|
||||||
repeat(461) {
|
repeat(461) {
|
||||||
result = golden.allocate(listOf("test"), DataType.UWORD, null, null, errors)
|
result = golden.allocate("test", DataType.UWORD, null, null, errors)
|
||||||
alloc = result.getOrThrow()
|
alloc = result.getOrThrow()
|
||||||
alloc.size shouldBe 2
|
alloc.size shouldBe 2
|
||||||
}
|
}
|
||||||
|
|
||||||
result = golden.allocate(listOf("test"), DataType.UWORD, null, null, errors)
|
result = golden.allocate("test", DataType.UWORD, null, null, errors)
|
||||||
result.expectError { "just 1 more byte available" }
|
result.expectError { "just 1 more byte available" }
|
||||||
result = golden.allocate(listOf("test"), DataType.UBYTE, null, null, errors)
|
result = golden.allocate("test", DataType.UBYTE, null, null, errors)
|
||||||
alloc = result.getOrThrow()
|
alloc = result.getOrThrow()
|
||||||
alloc.size shouldBe 1
|
alloc.size shouldBe 1
|
||||||
alloc.address shouldBe golden.region.last
|
alloc.address shouldBe golden.region.last
|
||||||
result = golden.allocate(listOf("test"), DataType.UBYTE, null, null, errors)
|
result = golden.allocate("test", DataType.UBYTE, null, null, errors)
|
||||||
result.expectError { "nothing more available" }
|
result.expectError { "nothing more available" }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@ import prog8.code.core.ZeropageType
|
|||||||
import prog8.code.core.internedStringsModuleName
|
import prog8.code.core.internedStringsModuleName
|
||||||
import prog8.code.target.C64Target
|
import prog8.code.target.C64Target
|
||||||
import prog8.compiler.determineCompilationOptions
|
import prog8.compiler.determineCompilationOptions
|
||||||
import prog8.compiler.parseImports
|
import prog8.compiler.parseMainModule
|
||||||
import prog8tests.helpers.ErrorReporterForTests
|
import prog8tests.helpers.ErrorReporterForTests
|
||||||
import prog8tests.helpers.compileText
|
import prog8tests.helpers.compileText
|
||||||
import prog8tests.helpers.outputDir
|
import prog8tests.helpers.outputDir
|
||||||
@ -28,9 +28,9 @@ main {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
""")!!
|
""")!!
|
||||||
result.program.toplevelModule.name shouldStartWith "on_the_fly_test"
|
result.compilerAst.toplevelModule.name shouldStartWith "on_the_fly_test"
|
||||||
|
|
||||||
val moduleNames = result.program.modules.map { it.name }
|
val moduleNames = result.compilerAst.modules.map { it.name }
|
||||||
withClue("main module must be first") {
|
withClue("main module must be first") {
|
||||||
moduleNames[0] shouldStartWith "on_the_fly_test"
|
moduleNames[0] shouldStartWith "on_the_fly_test"
|
||||||
}
|
}
|
||||||
@ -46,7 +46,7 @@ main {
|
|||||||
"prog8_lib"
|
"prog8_lib"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
result.program.toplevelModule.name shouldStartWith "on_the_fly_test"
|
result.compilerAst.toplevelModule.name shouldStartWith "on_the_fly_test"
|
||||||
}
|
}
|
||||||
|
|
||||||
test("testCompilationOptionsCorrectFromMain") {
|
test("testCompilationOptionsCorrectFromMain") {
|
||||||
@ -63,8 +63,8 @@ main {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
""")!!
|
""")!!
|
||||||
result.program.toplevelModule.name shouldStartWith "on_the_fly_test"
|
result.compilerAst.toplevelModule.name shouldStartWith "on_the_fly_test"
|
||||||
val options = determineCompilationOptions(result.program, C64Target())
|
val options = determineCompilationOptions(result.compilerAst, C64Target())
|
||||||
options.floats shouldBe true
|
options.floats shouldBe true
|
||||||
options.zeropage shouldBe ZeropageType.DONTUSE
|
options.zeropage shouldBe ZeropageType.DONTUSE
|
||||||
options.noSysInit shouldBe true
|
options.noSysInit shouldBe true
|
||||||
@ -87,7 +87,7 @@ main {
|
|||||||
val filenameBase = "on_the_fly_test_" + sourceText.hashCode().toUInt().toString(16)
|
val filenameBase = "on_the_fly_test_" + sourceText.hashCode().toUInt().toString(16)
|
||||||
val filepath = outputDir.resolve("$filenameBase.p8")
|
val filepath = outputDir.resolve("$filenameBase.p8")
|
||||||
filepath.toFile().writeText(sourceText)
|
filepath.toFile().writeText(sourceText)
|
||||||
val (program, options, importedfiles) = parseImports(filepath, errors, C64Target(), emptyList())
|
val (program, options, importedfiles) = parseMainModule(filepath, errors, C64Target(), emptyList())
|
||||||
|
|
||||||
program.toplevelModule.name shouldBe filenameBase
|
program.toplevelModule.name shouldBe filenameBase
|
||||||
withClue("all imports other than the test source must have been internal resources library files") {
|
withClue("all imports other than the test source must have been internal resources library files") {
|
||||||
|
@ -19,10 +19,10 @@ class TestLaunchEmu: FunSpec({
|
|||||||
<ASMSYMBOLS>
|
<ASMSYMBOLS>
|
||||||
</ASMSYMBOLS>
|
</ASMSYMBOLS>
|
||||||
|
|
||||||
<BSS>
|
<VARIABLESNOINIT>
|
||||||
</BSS>
|
</VARIABLESNOINIT>
|
||||||
<VARIABLES>
|
<VARIABLESWITHINIT>
|
||||||
</VARIABLES>
|
</VARIABLESWITHINIT>
|
||||||
|
|
||||||
<MEMORYMAPPEDVARIABLES>
|
<MEMORYMAPPEDVARIABLES>
|
||||||
</MEMORYMAPPEDVARIABLES>
|
</MEMORYMAPPEDVARIABLES>
|
||||||
|
@ -15,7 +15,6 @@ import prog8.code.core.Position
|
|||||||
import prog8.code.core.SourceCode
|
import prog8.code.core.SourceCode
|
||||||
import prog8.code.core.ZeropageWish
|
import prog8.code.core.ZeropageWish
|
||||||
import prog8.code.target.C64Target
|
import prog8.code.target.C64Target
|
||||||
import prog8.compiler.printProgram
|
|
||||||
import prog8tests.helpers.DummyFunctions
|
import prog8tests.helpers.DummyFunctions
|
||||||
import prog8tests.helpers.DummyMemsizer
|
import prog8tests.helpers.DummyMemsizer
|
||||||
import prog8tests.helpers.DummyStringEncoder
|
import prog8tests.helpers.DummyStringEncoder
|
||||||
@ -28,7 +27,7 @@ class TestMemory: FunSpec({
|
|||||||
|
|
||||||
fun wrapWithProgram(statements: List<Statement>): Program {
|
fun wrapWithProgram(statements: List<Statement>): Program {
|
||||||
val program = Program("test", DummyFunctions, DummyMemsizer, DummyStringEncoder)
|
val program = Program("test", DummyFunctions, DummyMemsizer, DummyStringEncoder)
|
||||||
val subroutine = Subroutine("test", mutableListOf(), emptyList(), statements.toMutableList(), false, Position.DUMMY)
|
val subroutine = Subroutine("test", mutableListOf(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, false, statements.toMutableList(), Position.DUMMY)
|
||||||
val module = Module(mutableListOf(subroutine), Position.DUMMY, SourceCode.Generated("test"))
|
val module = Module(mutableListOf(subroutine), Position.DUMMY, SourceCode.Generated("test"))
|
||||||
program.addModule(module)
|
program.addModule(module)
|
||||||
return program
|
return program
|
||||||
@ -113,7 +112,7 @@ class TestMemory: FunSpec({
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun createTestProgramForMemoryRefViaVar(address: UInt, vartype: VarDeclType): AssignTarget {
|
fun createTestProgramForMemoryRefViaVar(address: UInt, vartype: VarDeclType): AssignTarget {
|
||||||
val decl = VarDecl(vartype, VarDeclOrigin.USERCODE, DataType.BYTE, ZeropageWish.DONTCARE, null, "address", NumericLiteral.optimalInteger(address, Position.DUMMY), false, false, null, Position.DUMMY)
|
val decl = VarDecl(vartype, VarDeclOrigin.USERCODE, DataType.BYTE, ZeropageWish.DONTCARE, null, "address", NumericLiteral.optimalInteger(address, Position.DUMMY), false, false, Position.DUMMY)
|
||||||
val memexpr = IdentifierReference(listOf("address"), Position.DUMMY)
|
val memexpr = IdentifierReference(listOf("address"), Position.DUMMY)
|
||||||
val target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY)
|
val target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY)
|
||||||
val assignment = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
|
val assignment = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
|
||||||
@ -147,12 +146,11 @@ class TestMemory: FunSpec({
|
|||||||
target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY)
|
target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY)
|
||||||
assign = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
|
assign = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
|
||||||
wrapWithProgram(listOf(assign))
|
wrapWithProgram(listOf(assign))
|
||||||
printProgram(target.definingModule.program)
|
|
||||||
target.isIOAddress(c64target.machine) shouldBe true
|
target.isIOAddress(c64target.machine) shouldBe true
|
||||||
}
|
}
|
||||||
|
|
||||||
test("regular variable not in mapped IO ram on C64") {
|
test("regular variable not in mapped IO ram on C64") {
|
||||||
val decl = VarDecl(VarDeclType.VAR, VarDeclOrigin.USERCODE, DataType.BYTE, ZeropageWish.DONTCARE, null, "address", null, false, false, null, Position.DUMMY)
|
val decl = VarDecl(VarDeclType.VAR, VarDeclOrigin.USERCODE, DataType.BYTE, ZeropageWish.DONTCARE, null, "address", null, false, false, Position.DUMMY)
|
||||||
val target = AssignTarget(IdentifierReference(listOf("address"), Position.DUMMY), null, null, Position.DUMMY)
|
val target = AssignTarget(IdentifierReference(listOf("address"), Position.DUMMY), null, null, Position.DUMMY)
|
||||||
val assignment = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
|
val assignment = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
|
||||||
val subroutine = Subroutine("test", mutableListOf(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, false, mutableListOf(decl, assignment), Position.DUMMY)
|
val subroutine = Subroutine("test", mutableListOf(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, false, mutableListOf(decl, assignment), Position.DUMMY)
|
||||||
@ -164,7 +162,7 @@ class TestMemory: FunSpec({
|
|||||||
|
|
||||||
test("memory mapped variable not in mapped IO ram on C64") {
|
test("memory mapped variable not in mapped IO ram on C64") {
|
||||||
val address = 0x1000u
|
val address = 0x1000u
|
||||||
val decl = VarDecl(VarDeclType.MEMORY, VarDeclOrigin.USERCODE, DataType.UBYTE, ZeropageWish.DONTCARE, null, "address", NumericLiteral.optimalInteger(address, Position.DUMMY), false, false, null, Position.DUMMY)
|
val decl = VarDecl(VarDeclType.MEMORY, VarDeclOrigin.USERCODE, DataType.UBYTE, ZeropageWish.DONTCARE, null, "address", NumericLiteral.optimalInteger(address, Position.DUMMY), false, false, Position.DUMMY)
|
||||||
val target = AssignTarget(IdentifierReference(listOf("address"), Position.DUMMY), null, null, Position.DUMMY)
|
val target = AssignTarget(IdentifierReference(listOf("address"), Position.DUMMY), null, null, Position.DUMMY)
|
||||||
val assignment = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
|
val assignment = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
|
||||||
val subroutine = Subroutine("test", mutableListOf(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, false, mutableListOf(decl, assignment), Position.DUMMY)
|
val subroutine = Subroutine("test", mutableListOf(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, false, mutableListOf(decl, assignment), Position.DUMMY)
|
||||||
@ -176,7 +174,7 @@ class TestMemory: FunSpec({
|
|||||||
|
|
||||||
test("memory mapped variable in mapped IO ram on C64") {
|
test("memory mapped variable in mapped IO ram on C64") {
|
||||||
val address = 0xd020u
|
val address = 0xd020u
|
||||||
val decl = VarDecl(VarDeclType.MEMORY, VarDeclOrigin.USERCODE, DataType.UBYTE, ZeropageWish.DONTCARE, null, "address", NumericLiteral.optimalInteger(address, Position.DUMMY), false, false, null, Position.DUMMY)
|
val decl = VarDecl(VarDeclType.MEMORY, VarDeclOrigin.USERCODE, DataType.UBYTE, ZeropageWish.DONTCARE, null, "address", NumericLiteral.optimalInteger(address, Position.DUMMY), false, false, Position.DUMMY)
|
||||||
val target = AssignTarget(IdentifierReference(listOf("address"), Position.DUMMY), null, null, Position.DUMMY)
|
val target = AssignTarget(IdentifierReference(listOf("address"), Position.DUMMY), null, null, Position.DUMMY)
|
||||||
val assignment = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
|
val assignment = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
|
||||||
val subroutine = Subroutine("test", mutableListOf(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, false, mutableListOf(decl, assignment), Position.DUMMY)
|
val subroutine = Subroutine("test", mutableListOf(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, false, mutableListOf(decl, assignment), Position.DUMMY)
|
||||||
@ -187,7 +185,7 @@ class TestMemory: FunSpec({
|
|||||||
}
|
}
|
||||||
|
|
||||||
test("array not in mapped IO ram") {
|
test("array not in mapped IO ram") {
|
||||||
val decl = VarDecl(VarDeclType.VAR, VarDeclOrigin.USERCODE, DataType.ARRAY_UB, ZeropageWish.DONTCARE, null, "address", null, false, false, null, Position.DUMMY)
|
val decl = VarDecl(VarDeclType.VAR, VarDeclOrigin.USERCODE, DataType.ARRAY_UB, ZeropageWish.DONTCARE, null, "address", null, false, false, Position.DUMMY)
|
||||||
val arrayindexed = ArrayIndexedExpression(IdentifierReference(listOf("address"), Position.DUMMY), ArrayIndex(NumericLiteral.optimalInteger(1, Position.DUMMY), Position.DUMMY), Position.DUMMY)
|
val arrayindexed = ArrayIndexedExpression(IdentifierReference(listOf("address"), Position.DUMMY), ArrayIndex(NumericLiteral.optimalInteger(1, Position.DUMMY), Position.DUMMY), Position.DUMMY)
|
||||||
val target = AssignTarget(null, arrayindexed, null, Position.DUMMY)
|
val target = AssignTarget(null, arrayindexed, null, Position.DUMMY)
|
||||||
val assignment = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
|
val assignment = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
|
||||||
@ -200,7 +198,7 @@ class TestMemory: FunSpec({
|
|||||||
|
|
||||||
test("memory mapped array not in mapped IO ram") {
|
test("memory mapped array not in mapped IO ram") {
|
||||||
val address = 0x1000u
|
val address = 0x1000u
|
||||||
val decl = VarDecl(VarDeclType.MEMORY, VarDeclOrigin.USERCODE, DataType.ARRAY_UB, ZeropageWish.DONTCARE, null, "address", NumericLiteral.optimalInteger(address, Position.DUMMY), false, false, null, Position.DUMMY)
|
val decl = VarDecl(VarDeclType.MEMORY, VarDeclOrigin.USERCODE, DataType.ARRAY_UB, ZeropageWish.DONTCARE, null, "address", NumericLiteral.optimalInteger(address, Position.DUMMY), false, false, Position.DUMMY)
|
||||||
val arrayindexed = ArrayIndexedExpression(IdentifierReference(listOf("address"), Position.DUMMY), ArrayIndex(NumericLiteral.optimalInteger(1, Position.DUMMY), Position.DUMMY), Position.DUMMY)
|
val arrayindexed = ArrayIndexedExpression(IdentifierReference(listOf("address"), Position.DUMMY), ArrayIndex(NumericLiteral.optimalInteger(1, Position.DUMMY), Position.DUMMY), Position.DUMMY)
|
||||||
val target = AssignTarget(null, arrayindexed, null, Position.DUMMY)
|
val target = AssignTarget(null, arrayindexed, null, Position.DUMMY)
|
||||||
val assignment = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
|
val assignment = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
|
||||||
@ -213,7 +211,7 @@ class TestMemory: FunSpec({
|
|||||||
|
|
||||||
test("memory mapped array in mapped IO ram") {
|
test("memory mapped array in mapped IO ram") {
|
||||||
val address = 0xd800u
|
val address = 0xd800u
|
||||||
val decl = VarDecl(VarDeclType.MEMORY, VarDeclOrigin.USERCODE, DataType.ARRAY_UB, ZeropageWish.DONTCARE, null, "address", NumericLiteral.optimalInteger(address, Position.DUMMY), false, false, null, Position.DUMMY)
|
val decl = VarDecl(VarDeclType.MEMORY, VarDeclOrigin.USERCODE, DataType.ARRAY_UB, ZeropageWish.DONTCARE, null, "address", NumericLiteral.optimalInteger(address, Position.DUMMY), false, false, Position.DUMMY)
|
||||||
val arrayindexed = ArrayIndexedExpression(IdentifierReference(listOf("address"), Position.DUMMY), ArrayIndex(NumericLiteral.optimalInteger(1, Position.DUMMY), Position.DUMMY), Position.DUMMY)
|
val arrayindexed = ArrayIndexedExpression(IdentifierReference(listOf("address"), Position.DUMMY), ArrayIndex(NumericLiteral.optimalInteger(1, Position.DUMMY), Position.DUMMY), Position.DUMMY)
|
||||||
val target = AssignTarget(null, arrayindexed, null, Position.DUMMY)
|
val target = AssignTarget(null, arrayindexed, null, Position.DUMMY)
|
||||||
val assignment = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
|
val assignment = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
|
||||||
|
@ -14,7 +14,6 @@ import prog8.code.core.Position
|
|||||||
import prog8.code.core.toHex
|
import prog8.code.core.toHex
|
||||||
import prog8.code.target.C64Target
|
import prog8.code.target.C64Target
|
||||||
import prog8.code.target.cbm.Mflpt5
|
import prog8.code.target.cbm.Mflpt5
|
||||||
import prog8.compiler.printProgram
|
|
||||||
import prog8tests.helpers.ErrorReporterForTests
|
import prog8tests.helpers.ErrorReporterForTests
|
||||||
import prog8tests.helpers.compileText
|
import prog8tests.helpers.compileText
|
||||||
|
|
||||||
@ -199,9 +198,8 @@ class TestNumbers: FunSpec({
|
|||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
val result = compileText(C64Target(), false, src, writeAssembly = false)!!
|
val result = compileText(C64Target(), false, src, writeAssembly = false)!!
|
||||||
val statements = result.program.entrypoint.statements
|
val statements = result.compilerAst.entrypoint.statements
|
||||||
statements.size shouldBe 8
|
statements.size shouldBe 8
|
||||||
printProgram(result.program)
|
|
||||||
(statements[1] as Assignment).value shouldBe NumericLiteral(DataType.UWORD, 32768.0, Position.DUMMY)
|
(statements[1] as Assignment).value shouldBe NumericLiteral(DataType.UWORD, 32768.0, Position.DUMMY)
|
||||||
(statements[3] as Assignment).value shouldBe NumericLiteral(DataType.UWORD, 65535.0, Position.DUMMY)
|
(statements[3] as Assignment).value shouldBe NumericLiteral(DataType.UWORD, 65535.0, Position.DUMMY)
|
||||||
(statements[5] as Assignment).value shouldBe NumericLiteral(DataType.UBYTE, 255.0, Position.DUMMY)
|
(statements[5] as Assignment).value shouldBe NumericLiteral(DataType.UBYTE, 255.0, Position.DUMMY)
|
||||||
|
@ -16,7 +16,6 @@ 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.Cx16Target
|
import prog8.code.target.Cx16Target
|
||||||
import prog8.compiler.printProgram
|
|
||||||
import prog8tests.helpers.*
|
import prog8tests.helpers.*
|
||||||
|
|
||||||
|
|
||||||
@ -32,10 +31,10 @@ class TestOptimization: FunSpec({
|
|||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
val result = compileText(C64Target(), true, sourcecode)!!
|
val result = compileText(C64Target(), true, sourcecode)!!
|
||||||
val toplevelModule = result.program.toplevelModule
|
val toplevelModule = result.compilerAst.toplevelModule
|
||||||
val mainBlock = toplevelModule.statements.single() as Block
|
val mainBlock = toplevelModule.statements.single() as Block
|
||||||
val startSub = mainBlock.statements.single() as Subroutine
|
val startSub = mainBlock.statements.single() as Subroutine
|
||||||
result.program.entrypoint shouldBeSameInstanceAs startSub
|
result.compilerAst.entrypoint shouldBeSameInstanceAs startSub
|
||||||
withClue("only start sub should remain") {
|
withClue("only start sub should remain") {
|
||||||
startSub.name shouldBe "start"
|
startSub.name shouldBe "start"
|
||||||
}
|
}
|
||||||
@ -57,11 +56,11 @@ class TestOptimization: FunSpec({
|
|||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
val result = compileText(C64Target(), true, sourcecode)!!
|
val result = compileText(C64Target(), true, sourcecode)!!
|
||||||
val toplevelModule = result.program.toplevelModule
|
val toplevelModule = result.compilerAst.toplevelModule
|
||||||
val mainBlock = toplevelModule.statements.single() as Block
|
val mainBlock = toplevelModule.statements.single() as Block
|
||||||
val startSub = mainBlock.statements[0] as Subroutine
|
val startSub = mainBlock.statements[0] as Subroutine
|
||||||
val emptySub = mainBlock.statements[1] as Subroutine
|
val emptySub = mainBlock.statements[1] as Subroutine
|
||||||
result.program.entrypoint shouldBeSameInstanceAs startSub
|
result.compilerAst.entrypoint shouldBeSameInstanceAs startSub
|
||||||
startSub.name shouldBe "start"
|
startSub.name shouldBe "start"
|
||||||
emptySub.name shouldBe "empty"
|
emptySub.name shouldBe "empty"
|
||||||
withClue("compiler has inserted return in empty subroutines") {
|
withClue("compiler has inserted return in empty subroutines") {
|
||||||
@ -131,7 +130,7 @@ class TestOptimization: FunSpec({
|
|||||||
// cx16.r5s -= 1899
|
// cx16.r5s -= 1899
|
||||||
// cx16.r7s = llw
|
// cx16.r7s = llw
|
||||||
// cx16.r7s += 99
|
// cx16.r7s += 99
|
||||||
val stmts = result.program.entrypoint.statements
|
val stmts = result.compilerAst.entrypoint.statements
|
||||||
stmts.size shouldBe 14
|
stmts.size shouldBe 14
|
||||||
|
|
||||||
val addR0value = (stmts[5] as Assignment).value
|
val addR0value = (stmts[5] as Assignment).value
|
||||||
@ -183,7 +182,7 @@ class TestOptimization: FunSpec({
|
|||||||
// cx16.r4s = llw
|
// cx16.r4s = llw
|
||||||
// cx16.r4s *= 90
|
// cx16.r4s *= 90
|
||||||
// cx16.r4s /= 5
|
// cx16.r4s /= 5
|
||||||
val stmts = result.program.entrypoint.statements
|
val stmts = result.compilerAst.entrypoint.statements
|
||||||
stmts.size shouldBe 13
|
stmts.size shouldBe 13
|
||||||
|
|
||||||
val mulR0Value = (stmts[3] as Assignment).value
|
val mulR0Value = (stmts[3] as Assignment).value
|
||||||
@ -225,7 +224,7 @@ class TestOptimization: FunSpec({
|
|||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
val result = compileText(C64Target(), false, sourcecode)!!
|
val result = compileText(C64Target(), false, sourcecode)!!
|
||||||
val mainsub = result.program.entrypoint
|
val mainsub = result.compilerAst.entrypoint
|
||||||
mainsub.statements.size shouldBe 10
|
mainsub.statements.size shouldBe 10
|
||||||
val declTest = mainsub.statements[0] as VarDecl
|
val declTest = mainsub.statements[0] as VarDecl
|
||||||
val declX1 = mainsub.statements[1] as VarDecl
|
val declX1 = mainsub.statements[1] as VarDecl
|
||||||
@ -265,8 +264,7 @@ class TestOptimization: FunSpec({
|
|||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
val result = compileText(C64Target(), false, src, writeAssembly = true)!!
|
val result = compileText(C64Target(), false, src, writeAssembly = true)!!
|
||||||
printProgram(result.program)
|
val stmts = result.compilerAst.entrypoint.statements
|
||||||
val stmts = result.program.entrypoint.statements
|
|
||||||
stmts.size shouldBe 8
|
stmts.size shouldBe 8
|
||||||
|
|
||||||
val value1 = (stmts[4] as Assignment).value as BinaryExpression
|
val value1 = (stmts[4] as Assignment).value as BinaryExpression
|
||||||
@ -278,39 +276,6 @@ class TestOptimization: FunSpec({
|
|||||||
value3.operator shouldBe "&"
|
value3.operator shouldBe "&"
|
||||||
}
|
}
|
||||||
|
|
||||||
test("intermediate assignment steps generated for typecasted expression") {
|
|
||||||
val src = """
|
|
||||||
main {
|
|
||||||
sub start() {
|
|
||||||
ubyte r
|
|
||||||
ubyte @shared bb = (sgn(r)*2 + 100) as ubyte
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"""
|
|
||||||
val result = compileText(C64Target(), true, src, writeAssembly = true)!!
|
|
||||||
/* turned into:
|
|
||||||
ubyte r
|
|
||||||
r = 0
|
|
||||||
ubyte bb
|
|
||||||
prog8_lib.retval_interm_b = sgn(r)
|
|
||||||
prog8_lib.retval_interm_b <<= 1
|
|
||||||
prog8_lib.retval_interm_b += 100
|
|
||||||
bb = prog8_lib.retval_interm_b
|
|
||||||
return
|
|
||||||
*/
|
|
||||||
val st = result.program.entrypoint.statements
|
|
||||||
st.size shouldBe 8
|
|
||||||
st.last() shouldBe instanceOf<Return>()
|
|
||||||
var assign = st[3] as Assignment
|
|
||||||
assign.target.identifier!!.nameInSource shouldBe listOf("prog8_lib","tempvar_b")
|
|
||||||
assign = st[4] as Assignment
|
|
||||||
assign.target.identifier!!.nameInSource shouldBe listOf("prog8_lib","tempvar_b")
|
|
||||||
assign = st[5] as Assignment
|
|
||||||
assign.target.identifier!!.nameInSource shouldBe listOf("prog8_lib","tempvar_b")
|
|
||||||
assign = st[6] as Assignment
|
|
||||||
assign.target.identifier!!.nameInSource shouldBe listOf("bb")
|
|
||||||
}
|
|
||||||
|
|
||||||
test("asmgen correctly deals with float typecasting in augmented assignment") {
|
test("asmgen correctly deals with float typecasting in augmented assignment") {
|
||||||
val src="""
|
val src="""
|
||||||
%import floats
|
%import floats
|
||||||
@ -324,7 +289,7 @@ class TestOptimization: FunSpec({
|
|||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
val result = compileText(C64Target(), optimize=false, src, writeAssembly = false)!!
|
val result = compileText(C64Target(), optimize=false, src, writeAssembly = false)!!
|
||||||
val assignFF = result.program.entrypoint.statements.last() as Assignment
|
val assignFF = result.compilerAst.entrypoint.statements.last() as Assignment
|
||||||
assignFF.isAugmentable shouldBe true
|
assignFF.isAugmentable shouldBe true
|
||||||
assignFF.target.identifier!!.nameInSource shouldBe listOf("ff")
|
assignFF.target.identifier!!.nameInSource shouldBe listOf("ff")
|
||||||
val value = assignFF.value as BinaryExpression
|
val value = assignFF.value as BinaryExpression
|
||||||
@ -350,8 +315,8 @@ class TestOptimization: FunSpec({
|
|||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
val result = compileText(C64Target(), optimize=true, src, writeAssembly=false)!!
|
val result = compileText(C64Target(), optimize=true, src, writeAssembly=false)!!
|
||||||
result.program.entrypoint.statements.size shouldBe 7
|
result.compilerAst.entrypoint.statements.size shouldBe 7
|
||||||
val alldecls = result.program.entrypoint.allDefinedSymbols.toList()
|
val alldecls = result.compilerAst.entrypoint.allDefinedSymbols.toList()
|
||||||
alldecls.map { it.first } shouldBe listOf("unused_but_shared", "usedvar_only_written", "usedvar")
|
alldecls.map { it.first } shouldBe listOf("unused_but_shared", "usedvar_only_written", "usedvar")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -373,11 +338,11 @@ class TestOptimization: FunSpec({
|
|||||||
}
|
}
|
||||||
}"""
|
}"""
|
||||||
val result = compileText(C64Target(), optimize=true, src, writeAssembly=false)!!
|
val result = compileText(C64Target(), optimize=true, src, writeAssembly=false)!!
|
||||||
result.program.entrypoint.statements.size shouldBe 3
|
result.compilerAst.entrypoint.statements.size shouldBe 3
|
||||||
val ifstmt = result.program.entrypoint.statements[0] as IfElse
|
val ifstmt = result.compilerAst.entrypoint.statements[0] as IfElse
|
||||||
ifstmt.truepart.statements.size shouldBe 1
|
ifstmt.truepart.statements.size shouldBe 1
|
||||||
(ifstmt.truepart.statements[0] as Assignment).target.identifier!!.nameInSource shouldBe listOf("cx16", "r0")
|
(ifstmt.truepart.statements[0] as Assignment).target.identifier!!.nameInSource shouldBe listOf("cx16", "r0")
|
||||||
val func2 = result.program.entrypoint.statements[2] as Subroutine
|
val func2 = result.compilerAst.entrypoint.statements[2] as Subroutine
|
||||||
func2.statements.size shouldBe 1
|
func2.statements.size shouldBe 1
|
||||||
(func2.statements[0] as Assignment).target.identifier!!.nameInSource shouldBe listOf("cx16", "r0")
|
(func2.statements[0] as Assignment).target.identifier!!.nameInSource shouldBe listOf("cx16", "r0")
|
||||||
}
|
}
|
||||||
@ -401,7 +366,6 @@ class TestOptimization: FunSpec({
|
|||||||
}
|
}
|
||||||
}"""
|
}"""
|
||||||
val result = compileText(C64Target(), optimize=true, src, writeAssembly=false)!!
|
val result = compileText(C64Target(), optimize=true, src, writeAssembly=false)!!
|
||||||
printProgram(result.program)
|
|
||||||
/* expected:
|
/* expected:
|
||||||
ubyte z1
|
ubyte z1
|
||||||
z1 = 10
|
z1 = 10
|
||||||
@ -418,7 +382,7 @@ class TestOptimization: FunSpec({
|
|||||||
z6 = z1
|
z6 = z1
|
||||||
z6 -= 5
|
z6 -= 5
|
||||||
*/
|
*/
|
||||||
val statements = result.program.entrypoint.statements
|
val statements = result.compilerAst.entrypoint.statements
|
||||||
statements.size shouldBe 14
|
statements.size shouldBe 14
|
||||||
val z1decl = statements[0] as VarDecl
|
val z1decl = statements[0] as VarDecl
|
||||||
val z1init = statements[1] as Assignment
|
val z1init = statements[1] as Assignment
|
||||||
@ -469,7 +433,7 @@ class TestOptimization: FunSpec({
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
val result = compileText(C64Target(), optimize=true, src, writeAssembly=false)!!
|
val result = compileText(C64Target(), optimize=true, src, writeAssembly=false)!!
|
||||||
val stmts = result.program.entrypoint.statements
|
val stmts = result.compilerAst.entrypoint.statements
|
||||||
stmts.size shouldBe 6
|
stmts.size shouldBe 6
|
||||||
val assign=stmts.last() as Assignment
|
val assign=stmts.last() as Assignment
|
||||||
(assign.target.memoryAddress?.addressExpression as IdentifierReference).nameInSource shouldBe listOf("aa")
|
(assign.target.memoryAddress?.addressExpression as IdentifierReference).nameInSource shouldBe listOf("aa")
|
||||||
@ -486,7 +450,7 @@ class TestOptimization: FunSpec({
|
|||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
val result = compileText(C64Target(), optimize=true, src, writeAssembly=false)!!
|
val result = compileText(C64Target(), optimize=true, src, writeAssembly=false)!!
|
||||||
val stmts = result.program.entrypoint.statements
|
val stmts = result.compilerAst.entrypoint.statements
|
||||||
stmts.size shouldBe 6
|
stmts.size shouldBe 6
|
||||||
val assign=stmts.last() as Assignment
|
val assign=stmts.last() as Assignment
|
||||||
(assign.target.memoryAddress?.addressExpression as IdentifierReference).nameInSource shouldBe listOf("aa")
|
(assign.target.memoryAddress?.addressExpression as IdentifierReference).nameInSource shouldBe listOf("aa")
|
||||||
@ -505,7 +469,7 @@ class TestOptimization: FunSpec({
|
|||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
val result = compileText(C64Target(), optimize=true, src, writeAssembly=false)!!
|
val result = compileText(C64Target(), optimize=true, src, writeAssembly=false)!!
|
||||||
val stmts = result.program.entrypoint.statements
|
val stmts = result.compilerAst.entrypoint.statements
|
||||||
stmts.size shouldBe 10
|
stmts.size shouldBe 10
|
||||||
stmts.filterIsInstance<VarDecl>().size shouldBe 5
|
stmts.filterIsInstance<VarDecl>().size shouldBe 5
|
||||||
stmts.filterIsInstance<Assignment>().size shouldBe 5
|
stmts.filterIsInstance<Assignment>().size shouldBe 5
|
||||||
@ -554,8 +518,8 @@ class TestOptimization: FunSpec({
|
|||||||
}
|
}
|
||||||
}"""
|
}"""
|
||||||
val result = compileText(C64Target(), optimize=false, src, writeAssembly=true)!!
|
val result = compileText(C64Target(), optimize=false, src, writeAssembly=true)!!
|
||||||
result.program.entrypoint.statements.size shouldBe 11
|
result.compilerAst.entrypoint.statements.size shouldBe 11
|
||||||
result.program.entrypoint.statements.last() shouldBe instanceOf<Return>()
|
result.compilerAst.entrypoint.statements.last() shouldBe instanceOf<Return>()
|
||||||
}
|
}
|
||||||
|
|
||||||
test("keep the value initializer assignment if the next one depends on it") {
|
test("keep the value initializer assignment if the next one depends on it") {
|
||||||
@ -582,7 +546,7 @@ class TestOptimization: FunSpec({
|
|||||||
xx = abs(xx)
|
xx = abs(xx)
|
||||||
xx += 6
|
xx += 6
|
||||||
*/
|
*/
|
||||||
val stmts = result.program.entrypoint.statements
|
val stmts = result.compilerAst.entrypoint.statements
|
||||||
stmts.size shouldBe 8
|
stmts.size shouldBe 8
|
||||||
stmts.filterIsInstance<VarDecl>().size shouldBe 3
|
stmts.filterIsInstance<VarDecl>().size shouldBe 3
|
||||||
stmts.filterIsInstance<Assignment>().size shouldBe 5
|
stmts.filterIsInstance<Assignment>().size shouldBe 5
|
||||||
@ -611,7 +575,7 @@ class TestOptimization: FunSpec({
|
|||||||
yy = 0
|
yy = 0
|
||||||
xx += 10
|
xx += 10
|
||||||
*/
|
*/
|
||||||
val stmts = result.program.entrypoint.statements
|
val stmts = result.compilerAst.entrypoint.statements
|
||||||
stmts.size shouldBe 7
|
stmts.size shouldBe 7
|
||||||
stmts.filterIsInstance<VarDecl>().size shouldBe 2
|
stmts.filterIsInstance<VarDecl>().size shouldBe 2
|
||||||
stmts.filterIsInstance<Assignment>().size shouldBe 5
|
stmts.filterIsInstance<Assignment>().size shouldBe 5
|
||||||
@ -638,7 +602,6 @@ class TestOptimization: FunSpec({
|
|||||||
}
|
}
|
||||||
}"""
|
}"""
|
||||||
val result = compileText(C64Target(), optimize=true, src, writeAssembly=false)!!
|
val result = compileText(C64Target(), optimize=true, src, writeAssembly=false)!!
|
||||||
printProgram(result.program)
|
|
||||||
/*
|
/*
|
||||||
expected result:
|
expected result:
|
||||||
ubyte[] auto_heap_var = [1,4,99,3]
|
ubyte[] auto_heap_var = [1,4,99,3]
|
||||||
@ -649,7 +612,7 @@ class TestOptimization: FunSpec({
|
|||||||
if source in auto_heap_var
|
if source in auto_heap_var
|
||||||
thingy++
|
thingy++
|
||||||
*/
|
*/
|
||||||
val stmts = result.program.entrypoint.statements
|
val stmts = result.compilerAst.entrypoint.statements
|
||||||
stmts.size shouldBe 6
|
stmts.size shouldBe 6
|
||||||
val ifStmt = stmts[5] as IfElse
|
val ifStmt = stmts[5] as IfElse
|
||||||
val containment = ifStmt.condition as ContainmentCheck
|
val containment = ifStmt.condition as ContainmentCheck
|
||||||
@ -679,8 +642,7 @@ class TestOptimization: FunSpec({
|
|||||||
}
|
}
|
||||||
}"""
|
}"""
|
||||||
val result = compileText(C64Target(), optimize=true, src, writeAssembly=false)!!
|
val result = compileText(C64Target(), optimize=true, src, writeAssembly=false)!!
|
||||||
printProgram(result.program)
|
val stmts = result.compilerAst.entrypoint.statements
|
||||||
val stmts = result.program.entrypoint.statements
|
|
||||||
stmts.size shouldBe 5
|
stmts.size shouldBe 5
|
||||||
val ifStmt = stmts[4] as IfElse
|
val ifStmt = stmts[4] as IfElse
|
||||||
ifStmt.condition shouldBe instanceOf<BinaryExpression>()
|
ifStmt.condition shouldBe instanceOf<BinaryExpression>()
|
||||||
@ -698,8 +660,7 @@ class TestOptimization: FunSpec({
|
|||||||
}
|
}
|
||||||
}"""
|
}"""
|
||||||
val result = compileText(C64Target(), optimize=true, src, writeAssembly=false)!!
|
val result = compileText(C64Target(), optimize=true, src, writeAssembly=false)!!
|
||||||
printProgram(result.program)
|
val stmts = result.compilerAst.entrypoint.statements
|
||||||
val stmts = result.program.entrypoint.statements
|
|
||||||
stmts.size shouldBe 5
|
stmts.size shouldBe 5
|
||||||
val ifStmt = stmts[4] as IfElse
|
val ifStmt = stmts[4] as IfElse
|
||||||
ifStmt.condition shouldBe instanceOf<BinaryExpression>()
|
ifStmt.condition shouldBe instanceOf<BinaryExpression>()
|
||||||
@ -717,7 +678,7 @@ class TestOptimization: FunSpec({
|
|||||||
}
|
}
|
||||||
}"""
|
}"""
|
||||||
val result = compileText(C64Target(), optimize=true, src, writeAssembly=false)!!
|
val result = compileText(C64Target(), optimize=true, src, writeAssembly=false)!!
|
||||||
val stmts = result.program.entrypoint.statements
|
val stmts = result.compilerAst.entrypoint.statements
|
||||||
stmts.size shouldBe 5
|
stmts.size shouldBe 5
|
||||||
val ifStmt = stmts[4] as IfElse
|
val ifStmt = stmts[4] as IfElse
|
||||||
ifStmt.condition shouldBe instanceOf<BinaryExpression>()
|
ifStmt.condition shouldBe instanceOf<BinaryExpression>()
|
||||||
@ -734,7 +695,7 @@ class TestOptimization: FunSpec({
|
|||||||
}
|
}
|
||||||
}"""
|
}"""
|
||||||
val result = compileText(C64Target(), optimize=true, src, writeAssembly=false)!!
|
val result = compileText(C64Target(), optimize=true, src, writeAssembly=false)!!
|
||||||
val stmts = result.program.entrypoint.statements
|
val stmts = result.compilerAst.entrypoint.statements
|
||||||
stmts.size shouldBe 3
|
stmts.size shouldBe 3
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -756,15 +717,15 @@ main {
|
|||||||
}
|
}
|
||||||
}"""
|
}"""
|
||||||
var result = compileText(Cx16Target(), true, srcX16, writeAssembly = true)!!
|
var result = compileText(Cx16Target(), true, srcX16, writeAssembly = true)!!
|
||||||
var statements = result.program.entrypoint.statements
|
var statements = result.compilerAst.entrypoint.statements
|
||||||
statements.size shouldBe 9
|
statements.size shouldBe 9
|
||||||
(statements[1] as Assignment).target.identifier!!.nameInSource shouldBe listOf("xx")
|
(statements[1] as Assignment).target.identifier!!.nameInSource shouldBe listOf("xx")
|
||||||
(statements[2] as Assignment).target.identifier!!.nameInSource shouldBe listOf("cx16", "VERA_DATA0")
|
(statements[2] as Assignment).target.identifier!!.nameInSource shouldBe listOf("cx16", "VERA_DATA0")
|
||||||
(statements[3] as Assignment).target.identifier!!.nameInSource shouldBe listOf("cx16", "VERA_DATA0")
|
(statements[3] as Assignment).target.identifier!!.nameInSource shouldBe listOf("cx16", "VERA_DATA0")
|
||||||
(statements[4] as Assignment).target.identifier!!.nameInSource shouldBe listOf("cx16", "VERA_DATA0")
|
(statements[4] as Assignment).target.identifier!!.nameInSource shouldBe listOf("cx16", "VERA_DATA0")
|
||||||
(statements[5] as Assignment).target.memoryAddress!!.addressExpression.constValue(result.program)!!.number shouldBe 0x9fff
|
(statements[5] as Assignment).target.memoryAddress!!.addressExpression.constValue(result.compilerAst)!!.number shouldBe 0x9fff
|
||||||
(statements[6] as Assignment).target.memoryAddress!!.addressExpression.constValue(result.program)!!.number shouldBe 0x9fff
|
(statements[6] as Assignment).target.memoryAddress!!.addressExpression.constValue(result.compilerAst)!!.number shouldBe 0x9fff
|
||||||
(statements[7] as Assignment).target.memoryAddress!!.addressExpression.constValue(result.program)!!.number shouldBe 0x9fff
|
(statements[7] as Assignment).target.memoryAddress!!.addressExpression.constValue(result.compilerAst)!!.number shouldBe 0x9fff
|
||||||
|
|
||||||
val srcC64="""
|
val srcC64="""
|
||||||
main {
|
main {
|
||||||
@ -783,14 +744,26 @@ main {
|
|||||||
}
|
}
|
||||||
}"""
|
}"""
|
||||||
result = compileText(C64Target(), true, srcC64, writeAssembly = true)!!
|
result = compileText(C64Target(), true, srcC64, writeAssembly = true)!!
|
||||||
statements = result.program.entrypoint.statements
|
statements = result.compilerAst.entrypoint.statements
|
||||||
statements.size shouldBe 9
|
statements.size shouldBe 9
|
||||||
(statements[1] as Assignment).target.identifier!!.nameInSource shouldBe listOf("xx")
|
(statements[1] as Assignment).target.identifier!!.nameInSource shouldBe listOf("xx")
|
||||||
(statements[2] as Assignment).target.identifier!!.nameInSource shouldBe listOf("c64", "EXTCOL")
|
(statements[2] as Assignment).target.identifier!!.nameInSource shouldBe listOf("c64", "EXTCOL")
|
||||||
(statements[3] as Assignment).target.identifier!!.nameInSource shouldBe listOf("c64", "EXTCOL")
|
(statements[3] as Assignment).target.identifier!!.nameInSource shouldBe listOf("c64", "EXTCOL")
|
||||||
(statements[4] as Assignment).target.identifier!!.nameInSource shouldBe listOf("c64", "EXTCOL")
|
(statements[4] as Assignment).target.identifier!!.nameInSource shouldBe listOf("c64", "EXTCOL")
|
||||||
(statements[5] as Assignment).target.memoryAddress!!.addressExpression.constValue(result.program)!!.number shouldBe 53281.0
|
(statements[5] as Assignment).target.memoryAddress!!.addressExpression.constValue(result.compilerAst)!!.number shouldBe 53281.0
|
||||||
(statements[6] as Assignment).target.memoryAddress!!.addressExpression.constValue(result.program)!!.number shouldBe 53281.0
|
(statements[6] as Assignment).target.memoryAddress!!.addressExpression.constValue(result.compilerAst)!!.number shouldBe 53281.0
|
||||||
(statements[7] as Assignment).target.memoryAddress!!.addressExpression.constValue(result.program)!!.number shouldBe 53281.0
|
(statements[7] as Assignment).target.memoryAddress!!.addressExpression.constValue(result.compilerAst)!!.number shouldBe 53281.0
|
||||||
|
}
|
||||||
|
|
||||||
|
test("no crash on sorting unused array") {
|
||||||
|
val text="""
|
||||||
|
main {
|
||||||
|
ubyte[5] cards = [ 14, 6, 29, 16, 3 ]
|
||||||
|
|
||||||
|
sub start() {
|
||||||
|
sort(cards)
|
||||||
|
}
|
||||||
|
}"""
|
||||||
|
compileText(C64Target(), true, text, writeAssembly = false) shouldNotBe null
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -27,9 +27,9 @@ class TestScoping: FunSpec({
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
val result = compileText(C64Target(), false, src, writeAssembly = false)!!
|
val result = compileText(C64Target(), false, src, writeAssembly = false)!!
|
||||||
val module = result.program.toplevelModule
|
val module = result.compilerAst.toplevelModule
|
||||||
module.parent shouldBe instanceOf<GlobalNamespace>()
|
module.parent shouldBe instanceOf<GlobalNamespace>()
|
||||||
module.program shouldBeSameInstanceAs result.program
|
module.program shouldBeSameInstanceAs result.compilerAst
|
||||||
module.parent.parent shouldBe instanceOf<ParentSentinel>()
|
module.parent.parent shouldBe instanceOf<ParentSentinel>()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -46,7 +46,7 @@ class TestScoping: FunSpec({
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
val result = compileText(C64Target(), false, src, writeAssembly = false)!!
|
val result = compileText(C64Target(), false, src, writeAssembly = false)!!
|
||||||
val module = result.program.toplevelModule
|
val module = result.compilerAst.toplevelModule
|
||||||
val mainBlock = module.statements.single() as Block
|
val mainBlock = module.statements.single() as Block
|
||||||
val start = mainBlock.statements.single() as Subroutine
|
val start = mainBlock.statements.single() as Subroutine
|
||||||
val repeatbody = start.statements.filterIsInstance<RepeatLoop>().single().body
|
val repeatbody = start.statements.filterIsInstance<RepeatLoop>().single().body
|
||||||
@ -120,7 +120,7 @@ class TestScoping: FunSpec({
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
val result = compileText(C64Target(), false, src, writeAssembly = true)!!
|
val result = compileText(C64Target(), false, src, writeAssembly = true)!!
|
||||||
val module = result.program.toplevelModule
|
val module = result.compilerAst.toplevelModule
|
||||||
val mainBlock = module.statements.single() as Block
|
val mainBlock = module.statements.single() as Block
|
||||||
val start = mainBlock.statements.single() as Subroutine
|
val start = mainBlock.statements.single() as Subroutine
|
||||||
val labels = start.statements.filterIsInstance<Label>()
|
val labels = start.statements.filterIsInstance<Label>()
|
||||||
|
@ -60,7 +60,7 @@ class TestSubroutines: FunSpec({
|
|||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
val result = compileText(C64Target(), false, text, writeAssembly = false)!!
|
val result = compileText(C64Target(), false, text, writeAssembly = false)!!
|
||||||
val module = result.program.toplevelModule
|
val module = result.compilerAst.toplevelModule
|
||||||
val mainBlock = module.statements.single() as Block
|
val mainBlock = module.statements.single() as Block
|
||||||
val asmfunc = mainBlock.statements.filterIsInstance<Subroutine>().single { it.name=="asmfunc"}
|
val asmfunc = mainBlock.statements.filterIsInstance<Subroutine>().single { it.name=="asmfunc"}
|
||||||
val func = mainBlock.statements.filterIsInstance<Subroutine>().single { it.name=="func"}
|
val func = mainBlock.statements.filterIsInstance<Subroutine>().single { it.name=="func"}
|
||||||
@ -116,7 +116,7 @@ class TestSubroutines: FunSpec({
|
|||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
val result = compileText(C64Target(), false, text, writeAssembly = true)!!
|
val result = compileText(C64Target(), false, text, writeAssembly = true)!!
|
||||||
val module = result.program.toplevelModule
|
val module = result.compilerAst.toplevelModule
|
||||||
val mainBlock = module.statements.single() as Block
|
val mainBlock = module.statements.single() as Block
|
||||||
val asmfunc = mainBlock.statements.filterIsInstance<Subroutine>().single { it.name=="asmfunc"}
|
val asmfunc = mainBlock.statements.filterIsInstance<Subroutine>().single { it.name=="asmfunc"}
|
||||||
val func = mainBlock.statements.filterIsInstance<Subroutine>().single { it.name=="func"}
|
val func = mainBlock.statements.filterIsInstance<Subroutine>().single { it.name=="func"}
|
||||||
@ -178,7 +178,7 @@ class TestSubroutines: FunSpec({
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
val result = compileText(C64Target(), false, text, writeAssembly = false)!!
|
val result = compileText(C64Target(), false, text, writeAssembly = false)!!
|
||||||
val module = result.program.toplevelModule
|
val module = result.compilerAst.toplevelModule
|
||||||
val mainBlock = module.statements.single() as Block
|
val mainBlock = module.statements.single() as Block
|
||||||
val asmfunc = mainBlock.statements.filterIsInstance<Subroutine>().single { it.name=="asmfunc"}
|
val asmfunc = mainBlock.statements.filterIsInstance<Subroutine>().single { it.name=="asmfunc"}
|
||||||
val func = mainBlock.statements.filterIsInstance<Subroutine>().single { it.name=="func"}
|
val func = mainBlock.statements.filterIsInstance<Subroutine>().single { it.name=="func"}
|
||||||
@ -284,7 +284,7 @@ class TestSubroutines: FunSpec({
|
|||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
val result = compileText(C64Target(), false, text, writeAssembly = true)!!
|
val result = compileText(C64Target(), false, text, writeAssembly = true)!!
|
||||||
val stmts = result.program.entrypoint.statements
|
val stmts = result.compilerAst.entrypoint.statements
|
||||||
|
|
||||||
stmts.last() shouldBe instanceOf<Subroutine>()
|
stmts.last() shouldBe instanceOf<Subroutine>()
|
||||||
stmts.dropLast(1).last() shouldBe instanceOf<Return>() // this prevents the fallthrough
|
stmts.dropLast(1).last() shouldBe instanceOf<Return>() // this prevents the fallthrough
|
||||||
|
@ -4,19 +4,26 @@ import io.kotest.assertions.fail
|
|||||||
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 io.kotest.matchers.shouldNotBe
|
import io.kotest.matchers.shouldNotBe
|
||||||
|
import io.kotest.matchers.types.shouldBeSameInstanceAs
|
||||||
import prog8.code.*
|
import prog8.code.*
|
||||||
|
import prog8.code.ast.*
|
||||||
import prog8.code.core.DataType
|
import prog8.code.core.DataType
|
||||||
import prog8.code.core.Position
|
import prog8.code.core.Position
|
||||||
|
import prog8.code.core.SourceCode
|
||||||
import prog8.code.core.ZeropageWish
|
import prog8.code.core.ZeropageWish
|
||||||
|
import prog8tests.helpers.DummyMemsizer
|
||||||
|
import prog8tests.helpers.DummyStringEncoder
|
||||||
|
|
||||||
|
|
||||||
class TestSymbolTable: FunSpec({
|
class TestSymbolTable: FunSpec({
|
||||||
test("empty symboltable") {
|
test("empty symboltable") {
|
||||||
val st = SymbolTable()
|
val astNode = PtProgram("test", DummyMemsizer, DummyStringEncoder)
|
||||||
st.scopedName shouldBe ""
|
val st = SymbolTable(astNode)
|
||||||
st.name shouldBe ""
|
st.name shouldBe "test"
|
||||||
st.type shouldBe StNodeType.GLOBAL
|
st.type shouldBe StNodeType.GLOBAL
|
||||||
st.children shouldBe mutableMapOf()
|
st.children shouldBe mutableMapOf()
|
||||||
st.position shouldBe Position.DUMMY
|
st.astNode shouldBeSameInstanceAs astNode
|
||||||
|
st.astNode.position shouldBe Position.DUMMY
|
||||||
}
|
}
|
||||||
|
|
||||||
test("symboltable flatten") {
|
test("symboltable flatten") {
|
||||||
@ -33,9 +40,9 @@ class TestSymbolTable: FunSpec({
|
|||||||
st.lookupUnscoped("undefined") shouldBe null
|
st.lookupUnscoped("undefined") shouldBe null
|
||||||
st.lookup("undefined") shouldBe null
|
st.lookup("undefined") shouldBe null
|
||||||
st.lookup("undefined.undefined") shouldBe null
|
st.lookup("undefined.undefined") shouldBe null
|
||||||
var default = st.lookupUnscopedOrElse("undefined") { StNode("default", StNodeType.LABEL, Position.DUMMY) }
|
var default = st.lookupUnscopedOrElse("undefined") { StNode("default", StNodeType.LABEL, PtIdentifier("default", DataType.BYTE, Position.DUMMY)) }
|
||||||
default.name shouldBe "default"
|
default.name shouldBe "default"
|
||||||
default = st.lookupUnscopedOrElse("undefined") { StNode("default", StNodeType.LABEL, Position.DUMMY) }
|
default = st.lookupUnscopedOrElse("undefined") { StNode("default", StNodeType.LABEL, PtIdentifier("default", DataType.BYTE, Position.DUMMY)) }
|
||||||
default.name shouldBe "default"
|
default.name shouldBe "default"
|
||||||
|
|
||||||
val msbFunc = st.lookupUnscopedOrElse("msb") { fail("msb must be found") }
|
val msbFunc = st.lookupUnscopedOrElse("msb") { fail("msb must be found") }
|
||||||
@ -52,7 +59,7 @@ class TestSymbolTable: FunSpec({
|
|||||||
sub1.name shouldBe "sub1"
|
sub1.name shouldBe "sub1"
|
||||||
sub1.scopedName shouldBe "block1.sub1"
|
sub1.scopedName shouldBe "block1.sub1"
|
||||||
sub1.type shouldBe StNodeType.SUBROUTINE
|
sub1.type shouldBe StNodeType.SUBROUTINE
|
||||||
sub1.children.size shouldBe 2
|
sub1.children.size shouldBe 4
|
||||||
|
|
||||||
val v1 = sub1.lookupUnscopedOrElse("v1") { fail("v1 must be found") } as StStaticVariable
|
val v1 = sub1.lookupUnscopedOrElse("v1") { fail("v1 must be found") } as StStaticVariable
|
||||||
v1.type shouldBe StNodeType.STATICVAR
|
v1.type shouldBe StNodeType.STATICVAR
|
||||||
@ -67,35 +74,84 @@ class TestSymbolTable: FunSpec({
|
|||||||
subsub.lookupUnscoped("blockc") shouldBe null
|
subsub.lookupUnscoped("blockc") shouldBe null
|
||||||
subsub.lookupUnscoped("label") shouldNotBe null
|
subsub.lookupUnscoped("label") shouldNotBe null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test("symboltable collections") {
|
||||||
|
val st= makeSt()
|
||||||
|
|
||||||
|
st.allVariables.size shouldBe 4
|
||||||
|
st.allMemMappedVariables.single().scopedName shouldBe "block1.sub1.v3"
|
||||||
|
st.allMemorySlabs.single().scopedName shouldBe "block1.sub1.slab1"
|
||||||
|
}
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
private fun makeSt(): SymbolTable {
|
private fun makeSt(): SymbolTable {
|
||||||
val st = SymbolTable()
|
|
||||||
val block1 = StNode("block1", StNodeType.BLOCK, Position.DUMMY)
|
// first build the AST
|
||||||
val sub11 = StNode("sub1", StNodeType.SUBROUTINE, Position.DUMMY)
|
val astProgram = PtProgram("test", DummyMemsizer, DummyStringEncoder)
|
||||||
val sub12 = StNode("sub2", StNodeType.SUBROUTINE, Position.DUMMY)
|
val astBlock1 = PtBlock("block1", null, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("block1"), Position.DUMMY)
|
||||||
|
val astConstant1 = PtConstant("c1", DataType.UWORD, 12345.0, Position.DUMMY)
|
||||||
|
val astConstant2 = PtConstant("blockc", DataType.UWORD, 999.0, Position.DUMMY)
|
||||||
|
astBlock1.add(astConstant1)
|
||||||
|
astBlock1.add(astConstant2)
|
||||||
|
val astSub1 = PtSub("sub1", emptyList(), null, Position.DUMMY)
|
||||||
|
val astSub2 = PtSub("sub2", emptyList(), null, Position.DUMMY)
|
||||||
|
val astSub1v1 = PtVariable("v1", DataType.BYTE, ZeropageWish.DONTCARE, null, null, Position.DUMMY)
|
||||||
|
val astSub1v2 = PtVariable("v2", DataType.BYTE, ZeropageWish.DONTCARE,null, null, Position.DUMMY)
|
||||||
|
val astSub1v3 = PtVariable("v3", DataType.FLOAT, ZeropageWish.DONTCARE,null, null, Position.DUMMY)
|
||||||
|
val astSub1v4 = PtVariable("slab1", DataType.UWORD, ZeropageWish.DONTCARE,null, null, Position.DUMMY)
|
||||||
|
val astSub2v1 = PtVariable("v1", DataType.BYTE, ZeropageWish.DONTCARE,null, null, Position.DUMMY)
|
||||||
|
val astSub2v2 = PtVariable("v2", DataType.BYTE, ZeropageWish.DONTCARE,null, null, Position.DUMMY)
|
||||||
|
astSub1.add(astSub1v1)
|
||||||
|
astSub1.add(astSub1v2)
|
||||||
|
astSub1.add(astSub1v3)
|
||||||
|
astSub1.add(astSub1v4)
|
||||||
|
astSub2.add(astSub2v2)
|
||||||
|
astSub2.add(astSub2v2)
|
||||||
|
astBlock1.add(astSub1)
|
||||||
|
astBlock1.add(astSub2)
|
||||||
|
val astBfunc = PtIdentifier("msb", DataType.UBYTE, Position.DUMMY)
|
||||||
|
astBlock1.add(astBfunc)
|
||||||
|
val astBlock2 = PtBlock("block2", null, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("block2"), Position.DUMMY)
|
||||||
|
val astSub21 = PtSub("sub1", emptyList(), null, Position.DUMMY)
|
||||||
|
val astSub22 = PtSub("sub2", emptyList(), null, Position.DUMMY)
|
||||||
|
val astSub221 = PtSub("subsub", emptyList(), null, Position.DUMMY)
|
||||||
|
val astLabel = PtLabel("label", Position.DUMMY)
|
||||||
|
astSub221.add(astLabel)
|
||||||
|
astSub22.add(astSub221)
|
||||||
|
astBlock2.add(astSub21)
|
||||||
|
astBlock2.add(astSub22)
|
||||||
|
astProgram.add(astBlock1)
|
||||||
|
astProgram.add(astBlock2)
|
||||||
|
|
||||||
|
// now hook up the SymbolTable on that AST
|
||||||
|
val st = SymbolTable(astProgram)
|
||||||
|
val block1 = StNode("block1", StNodeType.BLOCK, astBlock1)
|
||||||
|
val sub11 = StNode("sub1", StNodeType.SUBROUTINE, astSub1)
|
||||||
|
val sub12 = StNode("sub2", StNodeType.SUBROUTINE, astSub2)
|
||||||
block1.add(sub11)
|
block1.add(sub11)
|
||||||
block1.add(sub12)
|
block1.add(sub12)
|
||||||
block1.add(StConstant("c1", DataType.UWORD, 12345.0, Position.DUMMY))
|
block1.add(StConstant("c1", DataType.UWORD, 12345.0, astConstant1))
|
||||||
block1.add(StConstant("blockc", DataType.UWORD, 999.0, Position.DUMMY))
|
block1.add(StConstant("blockc", DataType.UWORD, 999.0, astConstant2))
|
||||||
sub11.add(StStaticVariable("v1", DataType.BYTE, true, null, null, null, null, ZeropageWish.DONTCARE, Position.DUMMY))
|
sub11.add(StStaticVariable("v1", DataType.BYTE, null, null, null, null, ZeropageWish.DONTCARE, astSub1v1))
|
||||||
sub11.add(StStaticVariable("v2", DataType.BYTE, true, null, null, null, null, ZeropageWish.DONTCARE, Position.DUMMY))
|
sub11.add(StStaticVariable("v2", DataType.BYTE, null, null, null, null, ZeropageWish.DONTCARE, astSub1v2))
|
||||||
sub12.add(StStaticVariable("v1", DataType.BYTE, true, null, null, null, null, ZeropageWish.DONTCARE, Position.DUMMY))
|
sub11.add(StMemVar("v3", DataType.FLOAT, 12345u, null, astSub1v3))
|
||||||
sub12.add(StStaticVariable("v2", DataType.BYTE, true, null, null, null, null, ZeropageWish.DONTCARE, Position.DUMMY))
|
sub11.add(StMemorySlab("slab1", 200u, 64u, astSub1v4))
|
||||||
|
sub12.add(StStaticVariable("v1", DataType.BYTE, null, null, null, null, ZeropageWish.DONTCARE, astSub2v1))
|
||||||
val block2 = StNode("block2", StNodeType.BLOCK, Position.DUMMY)
|
sub12.add(StStaticVariable("v2", DataType.BYTE, null, null, null, null, ZeropageWish.DONTCARE, astSub2v2))
|
||||||
val sub21 = StNode("sub1", StNodeType.SUBROUTINE, Position.DUMMY)
|
val block2 = StNode("block2", StNodeType.BLOCK, astBlock2)
|
||||||
val sub22 = StNode("sub2", StNodeType.SUBROUTINE, Position.DUMMY)
|
val sub21 = StNode("sub1", StNodeType.SUBROUTINE, astSub21)
|
||||||
|
val sub22 = StNode("sub2", StNodeType.SUBROUTINE, astSub22)
|
||||||
block2.add(sub21)
|
block2.add(sub21)
|
||||||
block2.add(sub22)
|
block2.add(sub22)
|
||||||
val sub221 = StNode("subsub", StNodeType.SUBROUTINE, Position.DUMMY)
|
val sub221 = StNode("subsub", StNodeType.SUBROUTINE, astSub221)
|
||||||
sub221.add(StNode("label", StNodeType.LABEL, Position.DUMMY))
|
sub221.add(StNode("label", StNodeType.LABEL, astLabel))
|
||||||
sub22.add(sub221)
|
sub22.add(sub221)
|
||||||
|
|
||||||
val builtinfunc = StNode("msb", StNodeType.BUILTINFUNC, Position.DUMMY)
|
val builtinfunc = StNode("msb", StNodeType.BUILTINFUNC, astBfunc)
|
||||||
st.add(block1)
|
st.add(block1)
|
||||||
st.add(block2)
|
st.add(block2)
|
||||||
st.add(builtinfunc)
|
st.add(builtinfunc)
|
||||||
return st
|
return st
|
||||||
}
|
}
|
||||||
|
@ -49,7 +49,7 @@ class TestTypecasts: FunSpec({
|
|||||||
}
|
}
|
||||||
}"""
|
}"""
|
||||||
val result = compileText(C64Target(), false, text, writeAssembly = false)!!
|
val result = compileText(C64Target(), false, text, writeAssembly = false)!!
|
||||||
val stmts = result.program.entrypoint.statements
|
val stmts = result.compilerAst.entrypoint.statements
|
||||||
stmts.size shouldBe 4
|
stmts.size shouldBe 4
|
||||||
val expr = (stmts[3] as Assignment).value as BinaryExpression
|
val expr = (stmts[3] as Assignment).value as BinaryExpression
|
||||||
expr.operator shouldBe "and"
|
expr.operator shouldBe "and"
|
||||||
@ -57,7 +57,7 @@ class TestTypecasts: FunSpec({
|
|||||||
(expr.left as IdentifierReference).nameInSource shouldBe listOf("bb2") // no cast
|
(expr.left as IdentifierReference).nameInSource shouldBe listOf("bb2") // no cast
|
||||||
|
|
||||||
val result2 = compileText(C64Target(), true, text, writeAssembly = true)!!
|
val result2 = compileText(C64Target(), true, text, writeAssembly = true)!!
|
||||||
val stmts2 = result2.program.entrypoint.statements
|
val stmts2 = result2.compilerAst.entrypoint.statements
|
||||||
stmts2.size shouldBe 6
|
stmts2.size shouldBe 6
|
||||||
val expr2 = (stmts2[4] as Assignment).value as BinaryExpression
|
val expr2 = (stmts2[4] as Assignment).value as BinaryExpression
|
||||||
expr2.operator shouldBe "&"
|
expr2.operator shouldBe "&"
|
||||||
@ -86,7 +86,7 @@ main {
|
|||||||
}
|
}
|
||||||
}"""
|
}"""
|
||||||
val result = compileText(C64Target(), true, text, writeAssembly = true)!!
|
val result = compileText(C64Target(), true, text, writeAssembly = true)!!
|
||||||
val stmts = result.program.entrypoint.statements
|
val stmts = result.compilerAst.entrypoint.statements
|
||||||
/*
|
/*
|
||||||
ubyte ub1
|
ubyte ub1
|
||||||
ub1 = 1
|
ub1 = 1
|
||||||
@ -120,6 +120,42 @@ main {
|
|||||||
right3.right shouldBe NumericLiteral(DataType.UBYTE, 0.0, Position.DUMMY)
|
right3.right shouldBe NumericLiteral(DataType.UBYTE, 0.0, Position.DUMMY)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test("simple logical with bool no typecast") {
|
||||||
|
val text="""
|
||||||
|
main {
|
||||||
|
bool bb
|
||||||
|
|
||||||
|
sub start() {
|
||||||
|
bb = bb and 123
|
||||||
|
}
|
||||||
|
}"""
|
||||||
|
val result = compileText(C64Target(), true, text, writeAssembly = true)!!
|
||||||
|
val stmts = result.compilerAst.entrypoint.statements
|
||||||
|
stmts.size shouldBe 2
|
||||||
|
val assignValue = (stmts[0] as Assignment).value as BinaryExpression
|
||||||
|
assignValue.left shouldBe instanceOf<IdentifierReference>()
|
||||||
|
assignValue.operator shouldBe "&"
|
||||||
|
(assignValue.right as NumericLiteral).number shouldBe 1.0
|
||||||
|
}
|
||||||
|
|
||||||
|
test("simple logical with byte instead of bool ok with typecasting") {
|
||||||
|
val text="""
|
||||||
|
main {
|
||||||
|
ubyte ubb
|
||||||
|
|
||||||
|
sub start() {
|
||||||
|
ubb = ubb and 123
|
||||||
|
}
|
||||||
|
}"""
|
||||||
|
val result = compileText(C64Target(), true, text, writeAssembly = true)!!
|
||||||
|
val stmts = result.compilerAst.entrypoint.statements
|
||||||
|
stmts.size shouldBe 2
|
||||||
|
val assignValue = (stmts[0] as Assignment).value as BinaryExpression
|
||||||
|
assignValue.left shouldBe instanceOf<BinaryExpression>() // as a result of the cast to boolean
|
||||||
|
assignValue.operator shouldBe "&"
|
||||||
|
(assignValue.right as NumericLiteral).number shouldBe 1.0
|
||||||
|
}
|
||||||
|
|
||||||
test("logical with byte instead of bool") {
|
test("logical with byte instead of bool") {
|
||||||
val text="""
|
val text="""
|
||||||
%import textio
|
%import textio
|
||||||
@ -329,7 +365,7 @@ main {
|
|||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
val result = compileText(C64Target(), true, text, writeAssembly = true)!!
|
val result = compileText(C64Target(), true, text, writeAssembly = true)!!
|
||||||
val stmts = result.program.entrypoint.statements
|
val stmts = result.compilerAst.entrypoint.statements
|
||||||
stmts.size shouldBeGreaterThan 10
|
stmts.size shouldBeGreaterThan 10
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -542,7 +578,7 @@ main {
|
|||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
val result = compileText(C64Target(), true, text, writeAssembly = true)!!
|
val result = compileText(C64Target(), true, text, writeAssembly = true)!!
|
||||||
val stmts = result.program.entrypoint.statements
|
val stmts = result.compilerAst.entrypoint.statements
|
||||||
stmts.size shouldBeGreaterThan 10
|
stmts.size shouldBeGreaterThan 10
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -561,7 +597,7 @@ main {
|
|||||||
}
|
}
|
||||||
}"""
|
}"""
|
||||||
val result = compileText(C64Target(), false, text, writeAssembly = false)!!
|
val result = compileText(C64Target(), false, text, writeAssembly = false)!!
|
||||||
val stmts = result.program.entrypoint.statements
|
val stmts = result.compilerAst.entrypoint.statements
|
||||||
stmts.size shouldBe 6
|
stmts.size shouldBe 6
|
||||||
val arraydecl = stmts[0] as VarDecl
|
val arraydecl = stmts[0] as VarDecl
|
||||||
arraydecl.datatype shouldBe DataType.ARRAY_BOOL
|
arraydecl.datatype shouldBe DataType.ARRAY_BOOL
|
||||||
@ -595,7 +631,7 @@ main {
|
|||||||
}
|
}
|
||||||
}"""
|
}"""
|
||||||
val result = compileText(C64Target(), false, text, writeAssembly = false)!!
|
val result = compileText(C64Target(), false, text, writeAssembly = false)!!
|
||||||
val stmts = result.program.entrypoint.statements
|
val stmts = result.compilerAst.entrypoint.statements
|
||||||
stmts.size shouldBe 9
|
stmts.size shouldBe 9
|
||||||
val fcall1 = ((stmts[6] as Assignment).value as IFunctionCall)
|
val fcall1 = ((stmts[6] as Assignment).value as IFunctionCall)
|
||||||
fcall1.args[0] shouldBe NumericLiteral(DataType.UBYTE, 1.0, Position.DUMMY)
|
fcall1.args[0] shouldBe NumericLiteral(DataType.UBYTE, 1.0, Position.DUMMY)
|
||||||
@ -628,7 +664,7 @@ main {
|
|||||||
}
|
}
|
||||||
}"""
|
}"""
|
||||||
val result = compileText(C64Target(), false, text, writeAssembly = true)!!
|
val result = compileText(C64Target(), false, text, writeAssembly = true)!!
|
||||||
val stmts = result.program.entrypoint.statements
|
val stmts = result.compilerAst.entrypoint.statements
|
||||||
stmts.size shouldBeGreaterThan 10
|
stmts.size shouldBeGreaterThan 10
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -647,7 +683,7 @@ main {
|
|||||||
}
|
}
|
||||||
}"""
|
}"""
|
||||||
val result = compileText(C64Target(), false, text, writeAssembly = false)!!
|
val result = compileText(C64Target(), false, text, writeAssembly = false)!!
|
||||||
val stmts = result.program.entrypoint.statements
|
val stmts = result.compilerAst.entrypoint.statements
|
||||||
stmts.size shouldBe 3
|
stmts.size shouldBe 3
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -670,7 +706,7 @@ main {
|
|||||||
}
|
}
|
||||||
}"""
|
}"""
|
||||||
val result = compileText(C64Target(), false, text, writeAssembly = false)!!
|
val result = compileText(C64Target(), false, text, writeAssembly = false)!!
|
||||||
val stmts = result.program.entrypoint.statements
|
val stmts = result.compilerAst.entrypoint.statements
|
||||||
stmts.size shouldBe 8
|
stmts.size shouldBe 8
|
||||||
val arg1 = (stmts[2] as IFunctionCall).args.single()
|
val arg1 = (stmts[2] as IFunctionCall).args.single()
|
||||||
val arg2 = (stmts[3] as IFunctionCall).args.single()
|
val arg2 = (stmts[3] as IFunctionCall).args.single()
|
||||||
@ -707,7 +743,7 @@ main {
|
|||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
val result = compileText(C64Target(), false, text, writeAssembly = true)!!
|
val result = compileText(C64Target(), false, text, writeAssembly = true)!!
|
||||||
result.program.entrypoint.statements.size shouldBe 15
|
result.compilerAst.entrypoint.statements.size shouldBe 15
|
||||||
}
|
}
|
||||||
|
|
||||||
test("invalid typecasts of numbers") {
|
test("invalid typecasts of numbers") {
|
||||||
@ -827,7 +863,7 @@ main {
|
|||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
val result = compileText(C64Target(), false, text, writeAssembly = true)!!
|
val result = compileText(C64Target(), false, text, writeAssembly = true)!!
|
||||||
val statements = result.program.entrypoint.statements
|
val statements = result.compilerAst.entrypoint.statements
|
||||||
statements.size shouldBeGreaterThan 10
|
statements.size shouldBeGreaterThan 10
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -854,7 +890,7 @@ main {
|
|||||||
}
|
}
|
||||||
}"""
|
}"""
|
||||||
val result = compileText(C64Target(), false, text, writeAssembly = true)!!
|
val result = compileText(C64Target(), false, text, writeAssembly = true)!!
|
||||||
val statements = result.program.entrypoint.statements
|
val statements = result.compilerAst.entrypoint.statements
|
||||||
statements.size shouldBeGreaterThan 10
|
statements.size shouldBeGreaterThan 10
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -71,26 +71,26 @@ class TestC64Zeropage: FunSpec({
|
|||||||
compTarget = c64target, loadAddress = 999u
|
compTarget = c64target, loadAddress = 999u
|
||||||
))
|
))
|
||||||
|
|
||||||
var result = zp.allocate(emptyList(), DataType.UBYTE, null, null, errors)
|
var result = zp.allocate("", DataType.UBYTE, null, null, errors)
|
||||||
result.onFailure { fail(it.toString()) }
|
result.onFailure { fail(it.toString()) }
|
||||||
result = zp.allocate(emptyList(), DataType.UBYTE, null, null, errors)
|
result = zp.allocate("", DataType.UBYTE, null, null, errors)
|
||||||
result.onFailure { fail(it.toString()) }
|
result.onFailure { fail(it.toString()) }
|
||||||
result = zp.allocate(listOf("varname"), DataType.UBYTE, null, null, errors)
|
result = zp.allocate("varname", DataType.UBYTE, null, null, errors)
|
||||||
result.onFailure { fail(it.toString()) }
|
result.onFailure { fail(it.toString()) }
|
||||||
shouldThrow<IllegalArgumentException> { zp.allocate(listOf("varname"), DataType.UBYTE,null, null, errors) }
|
shouldThrow<IllegalArgumentException> { zp.allocate("varname", DataType.UBYTE,null, null, errors) }
|
||||||
result = zp.allocate(listOf("varname2"), DataType.UBYTE, null, null, errors)
|
result = zp.allocate("varname2", DataType.UBYTE, null, null, errors)
|
||||||
result.onFailure { fail(it.toString()) }
|
result.onFailure { fail(it.toString()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
test("testZpFloatEnable") {
|
test("testZpFloatEnable") {
|
||||||
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FULL, emptyList(), false, false, c64target, 999u))
|
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FULL, emptyList(), false, false, c64target, 999u))
|
||||||
var result = zp.allocate(emptyList(), DataType.FLOAT, null, null, errors)
|
var result = zp.allocate("", DataType.FLOAT, null, null, errors)
|
||||||
result.expectError { "should be allocation error due to disabled floats" }
|
result.expectError { "should be allocation error due to disabled floats" }
|
||||||
val zp2 = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.DONTUSE, emptyList(), true, false, c64target, 999u))
|
val zp2 = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.DONTUSE, emptyList(), true, false, c64target, 999u))
|
||||||
result = zp2.allocate(emptyList(), DataType.FLOAT, null, null, errors)
|
result = zp2.allocate("", DataType.FLOAT, null, null, errors)
|
||||||
result.expectError { "should be allocation error due to disabled ZP use" }
|
result.expectError { "should be allocation error due to disabled ZP use" }
|
||||||
val zp3 = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), true, false, c64target, 999u))
|
val zp3 = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), true, false, c64target, 999u))
|
||||||
zp3.allocate(emptyList(), DataType.FLOAT, null, null, errors)
|
zp3.allocate("", DataType.FLOAT, null, null, errors)
|
||||||
}
|
}
|
||||||
|
|
||||||
test("testZpModesWithFloats") {
|
test("testZpModesWithFloats") {
|
||||||
@ -112,7 +112,7 @@ class TestC64Zeropage: FunSpec({
|
|||||||
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.DONTUSE, emptyList(), false, false, c64target, 999u))
|
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.DONTUSE, emptyList(), false, false, c64target, 999u))
|
||||||
println(zp.free)
|
println(zp.free)
|
||||||
zp.availableBytes() shouldBe 0
|
zp.availableBytes() shouldBe 0
|
||||||
val result = zp.allocate(emptyList(), DataType.BYTE, null, null, errors)
|
val result = zp.allocate("", DataType.BYTE, null, null, errors)
|
||||||
result.expectError { "expected error due to disabled ZP use" }
|
result.expectError { "expected error due to disabled ZP use" }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -125,9 +125,9 @@ class TestC64Zeropage: FunSpec({
|
|||||||
zp3.availableBytes() shouldBe 97
|
zp3.availableBytes() shouldBe 97
|
||||||
val zp4 = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FULL, emptyList(), false, false, c64target, 999u))
|
val zp4 = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FULL, emptyList(), false, false, c64target, 999u))
|
||||||
zp4.availableBytes() shouldBe 207
|
zp4.availableBytes() shouldBe 207
|
||||||
zp4.allocate(listOf("test"), DataType.UBYTE, null, null, errors)
|
zp4.allocate("test", DataType.UBYTE, null, null, errors)
|
||||||
zp4.availableBytes() shouldBe 206
|
zp4.availableBytes() shouldBe 206
|
||||||
zp4.allocate(listOf("test2"), DataType.UBYTE, null, null, errors)
|
zp4.allocate("test2", DataType.UBYTE, null, null, errors)
|
||||||
zp4.availableBytes() shouldBe 205
|
zp4.availableBytes() shouldBe 205
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -166,19 +166,19 @@ class TestC64Zeropage: FunSpec({
|
|||||||
zp.hasByteAvailable() shouldBe true
|
zp.hasByteAvailable() shouldBe true
|
||||||
zp.hasWordAvailable() shouldBe true
|
zp.hasWordAvailable() shouldBe true
|
||||||
|
|
||||||
var result = zp.allocate(emptyList(), DataType.FLOAT, null, null, errors)
|
var result = zp.allocate("", DataType.FLOAT, null, null, errors)
|
||||||
result.expectError { "expect allocation error: in regular zp there aren't 5 sequential bytes free" }
|
result.expectError { "expect allocation error: in regular zp there aren't 5 sequential bytes free" }
|
||||||
|
|
||||||
for (i in 0 until zp.availableBytes()) {
|
for (i in 0 until zp.availableBytes()) {
|
||||||
val alloc = zp.allocate(emptyList(), DataType.UBYTE, null, null, errors)
|
val alloc = zp.allocate("", DataType.UBYTE, null, null, errors)
|
||||||
alloc.getOrElse { throw it }
|
alloc.getOrElse { throw it }
|
||||||
}
|
}
|
||||||
zp.availableBytes() shouldBe 0
|
zp.availableBytes() shouldBe 0
|
||||||
zp.hasByteAvailable() shouldBe false
|
zp.hasByteAvailable() shouldBe false
|
||||||
zp.hasWordAvailable() shouldBe false
|
zp.hasWordAvailable() shouldBe false
|
||||||
result = zp.allocate(emptyList(), DataType.UBYTE, null, null, errors)
|
result = zp.allocate("", DataType.UBYTE, null, null, errors)
|
||||||
result.expectError { "expected allocation error" }
|
result.expectError { "expected allocation error" }
|
||||||
result = zp.allocate(emptyList(), DataType.UWORD, null, null, errors)
|
result = zp.allocate("", DataType.UWORD, null, null, errors)
|
||||||
result.expectError { "expected allocation error" }
|
result.expectError { "expected allocation error" }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -187,47 +187,47 @@ class TestC64Zeropage: FunSpec({
|
|||||||
zp.availableBytes() shouldBe 207
|
zp.availableBytes() shouldBe 207
|
||||||
zp.hasByteAvailable() shouldBe true
|
zp.hasByteAvailable() shouldBe true
|
||||||
zp.hasWordAvailable() shouldBe true
|
zp.hasWordAvailable() shouldBe true
|
||||||
var result = zp.allocate(emptyList(), DataType.UWORD, null, null, errors)
|
var result = zp.allocate("", DataType.UWORD, null, null, errors)
|
||||||
val loc = result.getOrElse { throw it } .address
|
val loc = result.getOrElse { throw it } .address
|
||||||
loc shouldBeGreaterThan 3u
|
loc shouldBeGreaterThan 3u
|
||||||
loc shouldNotBeIn zp.free
|
loc shouldNotBeIn zp.free
|
||||||
val num = zp.availableBytes() / 2
|
val num = zp.availableBytes() / 2
|
||||||
|
|
||||||
for(i in 0..num-3) {
|
for(i in 0..num-3) {
|
||||||
zp.allocate(emptyList(), DataType.UWORD, null, null, errors)
|
zp.allocate("", DataType.UWORD, null, null, errors)
|
||||||
}
|
}
|
||||||
zp.availableBytes() shouldBe 5
|
zp.availableBytes() shouldBe 5
|
||||||
|
|
||||||
// can't allocate because no more sequential bytes, only fragmented
|
// can't allocate because no more sequential bytes, only fragmented
|
||||||
result = zp.allocate(emptyList(), DataType.UWORD, null, null, errors)
|
result = zp.allocate("", DataType.UWORD, null, null, errors)
|
||||||
result.expectError { "should give allocation error" }
|
result.expectError { "should give allocation error" }
|
||||||
|
|
||||||
for(i in 0..4) {
|
for(i in 0..4) {
|
||||||
zp.allocate(emptyList(), DataType.UBYTE, null, null, errors)
|
zp.allocate("", DataType.UBYTE, null, null, errors)
|
||||||
}
|
}
|
||||||
|
|
||||||
zp.availableBytes() shouldBe 0
|
zp.availableBytes() shouldBe 0
|
||||||
zp.hasByteAvailable() shouldBe false
|
zp.hasByteAvailable() shouldBe false
|
||||||
zp.hasWordAvailable() shouldBe false
|
zp.hasWordAvailable() shouldBe false
|
||||||
result = zp.allocate(emptyList(), DataType.UBYTE, null, null, errors)
|
result = zp.allocate("", DataType.UBYTE, null, null, errors)
|
||||||
result.expectError { "should give allocation error" }
|
result.expectError { "should give allocation error" }
|
||||||
}
|
}
|
||||||
|
|
||||||
test("testEfficientAllocation") {
|
test("testEfficientAllocation") {
|
||||||
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true, false, c64target, 999u))
|
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true, false, c64target, 999u))
|
||||||
zp.availableBytes() shouldBe 18
|
zp.availableBytes() shouldBe 18
|
||||||
zp.allocate(emptyList(), DataType.WORD, null, null, errors).getOrElse{throw it}.address shouldBe 0x04u
|
zp.allocate("", DataType.WORD, null, null, errors).getOrElse{throw it}.address shouldBe 0x04u
|
||||||
zp.allocate(emptyList(), DataType.UBYTE, null, null, errors).getOrElse{throw it}.address shouldBe 0x06u
|
zp.allocate("", DataType.UBYTE, null, null, errors).getOrElse{throw it}.address shouldBe 0x06u
|
||||||
zp.allocate(emptyList(), DataType.UBYTE, null, null, errors).getOrElse{throw it}.address shouldBe 0x0au
|
zp.allocate("", DataType.UBYTE, null, null, errors).getOrElse{throw it}.address shouldBe 0x0au
|
||||||
zp.allocate(emptyList(), DataType.UWORD, null, null, errors).getOrElse{throw it}.address shouldBe 0x9bu
|
zp.allocate("", DataType.UWORD, null, null, errors).getOrElse{throw it}.address shouldBe 0x9bu
|
||||||
zp.allocate(emptyList(), DataType.UWORD, null, null, errors).getOrElse{throw it}.address shouldBe 0x9eu
|
zp.allocate("", DataType.UWORD, null, null, errors).getOrElse{throw it}.address shouldBe 0x9eu
|
||||||
zp.allocate(emptyList(), DataType.UWORD, null, null, errors).getOrElse{throw it}.address shouldBe 0xa5u
|
zp.allocate("", DataType.UWORD, null, null, errors).getOrElse{throw it}.address shouldBe 0xa5u
|
||||||
zp.allocate(emptyList(), DataType.UWORD, null, null, errors).getOrElse{throw it}.address shouldBe 0xb0u
|
zp.allocate("", DataType.UWORD, null, null, errors).getOrElse{throw it}.address shouldBe 0xb0u
|
||||||
zp.allocate(emptyList(), DataType.UWORD, null, null, errors).getOrElse{throw it}.address shouldBe 0xbeu
|
zp.allocate("", DataType.UWORD, null, null, errors).getOrElse{throw it}.address shouldBe 0xbeu
|
||||||
zp.allocate(emptyList(), DataType.UBYTE, null, null, errors).getOrElse{throw it}.address shouldBe 0x0eu
|
zp.allocate("", DataType.UBYTE, null, null, errors).getOrElse{throw it}.address shouldBe 0x0eu
|
||||||
zp.allocate(emptyList(), DataType.UBYTE, null, null, errors).getOrElse{throw it}.address shouldBe 0x92u
|
zp.allocate("", DataType.UBYTE, null, null, errors).getOrElse{throw it}.address shouldBe 0x92u
|
||||||
zp.allocate(emptyList(), DataType.UBYTE, null, null, errors).getOrElse{throw it}.address shouldBe 0x96u
|
zp.allocate("", DataType.UBYTE, null, null, errors).getOrElse{throw it}.address shouldBe 0x96u
|
||||||
zp.allocate(emptyList(), DataType.UBYTE, null, null, errors).getOrElse{throw it}.address shouldBe 0xf9u
|
zp.allocate("", DataType.UBYTE, null, null, errors).getOrElse{throw it}.address shouldBe 0xf9u
|
||||||
zp.availableBytes() shouldBe 0
|
zp.availableBytes() shouldBe 0
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -258,9 +258,9 @@ class TestCx16Zeropage: FunSpec({
|
|||||||
zp2.availableBytes() shouldBe 175
|
zp2.availableBytes() shouldBe 175
|
||||||
val zp3 = CX16Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FULL, emptyList(), false, false, cx16target, 999u))
|
val zp3 = CX16Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FULL, emptyList(), false, false, cx16target, 999u))
|
||||||
zp3.availableBytes() shouldBe 216
|
zp3.availableBytes() shouldBe 216
|
||||||
zp3.allocate(listOf("test"), DataType.UBYTE, null, null, errors)
|
zp3.allocate("test", DataType.UBYTE, null, null, errors)
|
||||||
zp3.availableBytes() shouldBe 215
|
zp3.availableBytes() shouldBe 215
|
||||||
zp3.allocate(listOf("test2"), DataType.UBYTE, null, null, errors)
|
zp3.allocate("test2", DataType.UBYTE, null, null, errors)
|
||||||
zp3.availableBytes() shouldBe 214
|
zp3.availableBytes() shouldBe 214
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -276,12 +276,12 @@ class TestCx16Zeropage: FunSpec({
|
|||||||
|
|
||||||
test("preallocated zp vars") {
|
test("preallocated zp vars") {
|
||||||
val zp1 = CX16Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FULL, emptyList(), false, false, cx16target, 999u))
|
val zp1 = CX16Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FULL, emptyList(), false, false, cx16target, 999u))
|
||||||
zp1.allocatedVariables[listOf("test")] shouldBe null
|
zp1.allocatedVariables["test"] shouldBe null
|
||||||
zp1.allocatedVariables[listOf("cx16", "r0")] shouldNotBe null
|
zp1.allocatedVariables["cx16.r0"] shouldNotBe null
|
||||||
zp1.allocatedVariables[listOf("cx16", "r15")] shouldNotBe null
|
zp1.allocatedVariables["cx16.r15"] shouldNotBe null
|
||||||
zp1.allocatedVariables[listOf("cx16", "r0L")] shouldNotBe null
|
zp1.allocatedVariables["cx16.r0L"] shouldNotBe null
|
||||||
zp1.allocatedVariables[listOf("cx16", "r15L")] shouldNotBe null
|
zp1.allocatedVariables["cx16.r15L"] shouldNotBe null
|
||||||
zp1.allocatedVariables[listOf("cx16", "r0sH")] shouldNotBe null
|
zp1.allocatedVariables["cx16.r0sH"] shouldNotBe null
|
||||||
zp1.allocatedVariables[listOf("cx16", "r15sH")] shouldNotBe null
|
zp1.allocatedVariables["cx16.r15sH"] shouldNotBe null
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -8,7 +8,6 @@ import prog8.code.ast.*
|
|||||||
import prog8.code.core.*
|
import prog8.code.core.*
|
||||||
import prog8.code.target.C64Target
|
import prog8.code.target.C64Target
|
||||||
import prog8.compiler.astprocessing.IntermediateAstMaker
|
import prog8.compiler.astprocessing.IntermediateAstMaker
|
||||||
import prog8.compiler.astprocessing.SymbolTableMaker
|
|
||||||
import prog8tests.helpers.compileText
|
import prog8tests.helpers.compileText
|
||||||
|
|
||||||
class TestIntermediateAst: FunSpec({
|
class TestIntermediateAst: FunSpec({
|
||||||
@ -38,9 +37,8 @@ class TestIntermediateAst: FunSpec({
|
|||||||
loadAddress = target.machine.PROGRAM_LOAD_ADDRESS
|
loadAddress = target.machine.PROGRAM_LOAD_ADDRESS
|
||||||
)
|
)
|
||||||
val result = compileText(target, false, text, writeAssembly = false)!!
|
val result = compileText(target, false, text, writeAssembly = false)!!
|
||||||
val st = SymbolTableMaker(result.program, options).make()
|
val ast = IntermediateAstMaker(result.compilerAst, options).transform()
|
||||||
val ast = IntermediateAstMaker(result.program, st, options).transform()
|
ast.name shouldBe result.compilerAst.name
|
||||||
ast.name shouldBe result.program.name
|
|
||||||
ast.allBlocks().any() shouldBe true
|
ast.allBlocks().any() shouldBe true
|
||||||
val entry = ast.entrypoint() ?: fail("no main.start() found")
|
val entry = ast.entrypoint() ?: fail("no main.start() found")
|
||||||
entry.children.size shouldBe 5
|
entry.children.size shouldBe 5
|
||||||
@ -59,18 +57,16 @@ class TestIntermediateAst: FunSpec({
|
|||||||
ccdecl.name shouldBe "cc"
|
ccdecl.name shouldBe "cc"
|
||||||
ccdecl.scopedName shouldBe "main.start.cc"
|
ccdecl.scopedName shouldBe "main.start.cc"
|
||||||
ccdecl.type shouldBe DataType.UBYTE
|
ccdecl.type shouldBe DataType.UBYTE
|
||||||
val arraydecl = entry.children[1] as PtVariable
|
val arraydecl = entry.children[1] as IPtVariable
|
||||||
arraydecl.name shouldBe "array"
|
arraydecl.name shouldBe "array"
|
||||||
arraydecl.type shouldBe DataType.ARRAY_UB
|
arraydecl.type shouldBe DataType.ARRAY_UB
|
||||||
|
|
||||||
val containmentCast = (entry.children[3] as PtAssignment).value as PtTypeCast
|
val containment = (entry.children[3] as PtAssignment).value as PtContainmentCheck
|
||||||
containmentCast.type shouldBe DataType.UBYTE
|
|
||||||
val containment = containmentCast.value as PtContainmentCheck
|
|
||||||
(containment.element as PtNumber).number shouldBe 11.0
|
(containment.element as PtNumber).number shouldBe 11.0
|
||||||
val fcall = (entry.children[4] as PtAssignment).value as PtFunctionCall
|
val fcall = (entry.children[4] as PtAssignment).value as PtFunctionCall
|
||||||
fcall.void shouldBe false
|
fcall.void shouldBe false
|
||||||
fcall.type shouldBe DataType.UBYTE
|
fcall.type shouldBe DataType.UBYTE
|
||||||
ast.print()
|
printAst(ast, ::println)
|
||||||
}
|
}
|
||||||
|
|
||||||
})
|
})
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user