mirror of
https://github.com/irmen/prog8.git
synced 2025-06-16 09:23:43 +00:00
Compare commits
14 Commits
Author | SHA1 | Date | |
---|---|---|---|
80113f9208 | |||
27f987f0ae | |||
3ae2597261 | |||
248e7b808c | |||
a983a896f2 | |||
68df1730f5 | |||
d62ab93b24 | |||
47297f7e31 | |||
b64d611e02 | |||
9fb9bcfebd | |||
dff9c5f53e | |||
d4a77321d2 | |||
2665618fa6 | |||
b5c5560af8 |
@ -1,11 +1,11 @@
|
|||||||
language: java
|
language: java
|
||||||
|
sudo: false
|
||||||
# jdk: openjdk8
|
# jdk: openjdk8
|
||||||
# dist: xenial
|
# dist: xenial
|
||||||
# sudo: false
|
|
||||||
|
|
||||||
before_install:
|
before_install:
|
||||||
- chmod +x gradlew
|
- chmod +x gradlew
|
||||||
|
|
||||||
script:
|
script:
|
||||||
- ./gradlew test
|
- gradle test
|
||||||
|
|
||||||
|
@ -484,8 +484,8 @@ internal class Compiler(private val program: Program) {
|
|||||||
translatePrefixOperator(expr.operator, expr.expression.inferType(program))
|
translatePrefixOperator(expr.operator, expr.expression.inferType(program))
|
||||||
}
|
}
|
||||||
is BinaryExpression -> {
|
is BinaryExpression -> {
|
||||||
val leftDt = expr.left.inferType(program)!!
|
val leftDt = expr.left.inferType(program)
|
||||||
val rightDt = expr.right.inferType(program)!!
|
val rightDt = expr.right.inferType(program)
|
||||||
val (commonDt, _) = expr.commonDatatype(leftDt, rightDt, expr.left, expr.right)
|
val (commonDt, _) = expr.commonDatatype(leftDt, rightDt, expr.left, expr.right)
|
||||||
translate(expr.left)
|
translate(expr.left)
|
||||||
if(leftDt!=commonDt)
|
if(leftDt!=commonDt)
|
||||||
@ -654,7 +654,7 @@ internal class Compiler(private val program: Program) {
|
|||||||
// cast type if needed
|
// cast type if needed
|
||||||
if(builtinFuncParams!=null) {
|
if(builtinFuncParams!=null) {
|
||||||
val paramDts = builtinFuncParams[index].possibleDatatypes
|
val paramDts = builtinFuncParams[index].possibleDatatypes
|
||||||
val argDt = arg.inferType(program)!!
|
val argDt = arg.inferType(program)
|
||||||
if(argDt !in paramDts) {
|
if(argDt !in paramDts) {
|
||||||
for(paramDt in paramDts.sorted())
|
for(paramDt in paramDts.sorted())
|
||||||
if(tryConvertType(argDt, paramDt))
|
if(tryConvertType(argDt, paramDt))
|
||||||
@ -827,8 +827,8 @@ internal class Compiler(private val program: Program) {
|
|||||||
// swap(x,y) is treated differently, it's not a normal function call
|
// swap(x,y) is treated differently, it's not a normal function call
|
||||||
if (args.size != 2)
|
if (args.size != 2)
|
||||||
throw AstException("swap requires 2 arguments")
|
throw AstException("swap requires 2 arguments")
|
||||||
val dt1 = args[0].inferType(program)!!
|
val dt1 = args[0].inferType(program)
|
||||||
val dt2 = args[1].inferType(program)!!
|
val dt2 = args[1].inferType(program)
|
||||||
if (dt1 != dt2)
|
if (dt1 != dt2)
|
||||||
throw AstException("swap requires 2 args of identical type")
|
throw AstException("swap requires 2 args of identical type")
|
||||||
if (args[0].constValue(program) != null || args[1].constValue(program) != null)
|
if (args[0].constValue(program) != null || args[1].constValue(program) != null)
|
||||||
@ -861,7 +861,7 @@ internal class Compiler(private val program: Program) {
|
|||||||
// (subroutine arguments are not passed via the stack!)
|
// (subroutine arguments are not passed via the stack!)
|
||||||
for (arg in arguments.zip(subroutine.parameters)) {
|
for (arg in arguments.zip(subroutine.parameters)) {
|
||||||
translate(arg.first)
|
translate(arg.first)
|
||||||
convertType(arg.first.inferType(program)!!, arg.second.type) // convert types of arguments to required parameter type
|
convertType(arg.first.inferType(program), arg.second.type) // convert types of arguments to required parameter type
|
||||||
val opcode = opcodePopvar(arg.second.type)
|
val opcode = opcodePopvar(arg.second.type)
|
||||||
prog.instr(opcode, callLabel = subroutine.scopedname + "." + arg.second.name)
|
prog.instr(opcode, callLabel = subroutine.scopedname + "." + arg.second.name)
|
||||||
}
|
}
|
||||||
|
21
README.md
21
README.md
@ -14,32 +14,29 @@ as used in many home computers from that era. It is a medium to low level progra
|
|||||||
which aims to provide many conveniences over raw assembly code (even when using a macro assembler):
|
which aims to provide many conveniences over raw assembly code (even when using a macro assembler):
|
||||||
|
|
||||||
- reduction of source code length
|
- reduction of source code length
|
||||||
- easier program understanding (because it's higher level, and way more compact)
|
|
||||||
- modularity, symbol scoping, subroutines
|
- modularity, symbol scoping, subroutines
|
||||||
- subroutines have enforced input- and output parameter definitions
|
|
||||||
- various data types other than just bytes (16-bit words, floats, strings)
|
- various data types other than just bytes (16-bit words, floats, strings)
|
||||||
- automatic variable allocations, automatic string variables and string sharing
|
- automatic variable allocations, automatic string and array variables and string sharing
|
||||||
- constant folding in expressions (compile-time evaluation)
|
- subroutines with a input- and output parameter signature
|
||||||
|
- constant folding in expressions
|
||||||
- conditional branches
|
- conditional branches
|
||||||
- when statement to provide a 'jump table' alternative to if/elseif chains
|
- 'when' statement to provide a concise jump table alternative to if/elseif chains
|
||||||
- structs to group together sets of variables and manipulate them at once
|
- structs to group together sets of variables and manipulate them at once
|
||||||
- automatic type conversions
|
- floating point operations (requires the C64 Basic ROM routines for this)
|
||||||
- floating point operations (uses the C64 Basic ROM routines for this)
|
|
||||||
- abstracting away low level aspects such as ZeroPage handling, program startup, explicit memory addresses
|
- abstracting away low level aspects such as ZeroPage handling, program startup, explicit memory addresses
|
||||||
- various code optimizations (code structure, logical and numerical expressions, unused code removal...)
|
- various code optimizations (code structure, logical and numerical expressions, unused code removal...)
|
||||||
- inline assembly allows you to have full control when every cycle or byte matters
|
- inline assembly allows you to have full control when every cycle or byte matters
|
||||||
|
- many built-in functions such as ``sin``, ``cos``, ``rnd``, ``abs``, ``min``, ``max``, ``sqrt``, ``msb``, ``rol``, ``ror``, ``swap``, ``memset``, ``memcopy``
|
||||||
|
|
||||||
Rapid edit-compile-run-debug cycle:
|
Rapid edit-compile-run-debug cycle:
|
||||||
|
|
||||||
- use modern PC to work on
|
- use modern PC to work on
|
||||||
- quick compilation times (couple of seconds, and less than a second when using the continuous compilation mode)
|
- quick compilation times (seconds)
|
||||||
- option to automatically run the program in the Vice emulator
|
- option to automatically run the program in the Vice emulator
|
||||||
- breakpoints, that let the Vice emulator drop into the monitor if execution hits them
|
- breakpoints, that let the Vice emulator drop into the monitor if execution hits them
|
||||||
- source code labels automatically loaded in Vice emulator so it can show them in disassembly
|
- source code labels automatically loaded in Vice emulator so it can show them in disassembly
|
||||||
- the compiler includes a virtual machine that can execute compiled code directy on the
|
- virtual machine that can execute compiled code directy on the host system,
|
||||||
host system without having to actually convert it to assembly to run on a real 6502.
|
without having to actually convert it to assembly to run on a real 6502
|
||||||
This allows for very quick experimentation and debugging
|
|
||||||
|
|
||||||
It is mainly targeted at the Commodore-64 machine at this time.
|
It is mainly targeted at the Commodore-64 machine at this time.
|
||||||
Contributions to add support for other 8-bit (or other?!) machines are welcome.
|
Contributions to add support for other 8-bit (or other?!) machines are welcome.
|
||||||
|
@ -22,8 +22,6 @@ repositories {
|
|||||||
jcenter()
|
jcenter()
|
||||||
}
|
}
|
||||||
|
|
||||||
sourceCompatibility = 1.8
|
|
||||||
|
|
||||||
def prog8version = rootProject.file('compiler/res/version.txt').text.trim()
|
def prog8version = rootProject.file('compiler/res/version.txt').text.trim()
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
@ -49,6 +47,12 @@ compileKotlin {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
compileTestKotlin {
|
||||||
|
kotlinOptions {
|
||||||
|
jvmTarget = "1.8"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
sourceSets {
|
sourceSets {
|
||||||
main {
|
main {
|
||||||
java {
|
java {
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
<sourceFolder url="file://$MODULE_DIR$/test" isTestSource="true" />
|
<sourceFolder url="file://$MODULE_DIR$/test" isTestSource="true" />
|
||||||
<excludeFolder url="file://$MODULE_DIR$/build" />
|
<excludeFolder url="file://$MODULE_DIR$/build" />
|
||||||
</content>
|
</content>
|
||||||
<orderEntry type="jdk" jdkName="1.8" jdkType="JavaSDK" />
|
<orderEntry type="jdk" jdkName="openjdk-11" jdkType="JavaSDK" />
|
||||||
<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="library" name="antlr-runtime-4.7.2" level="project" />
|
<orderEntry type="library" name="antlr-runtime-4.7.2" level="project" />
|
||||||
|
@ -954,6 +954,16 @@ func_sum_f .proc
|
|||||||
bne -
|
bne -
|
||||||
+ jmp push_fac1_as_result
|
+ jmp push_fac1_as_result
|
||||||
.pend
|
.pend
|
||||||
|
|
||||||
|
sign_f .proc
|
||||||
|
jsr pop_float_fac1
|
||||||
|
jsr SIGN
|
||||||
|
sta c64.ESTACK_LO,x
|
||||||
|
dex
|
||||||
|
rts
|
||||||
|
.pend
|
||||||
|
|
||||||
|
|
||||||
}}
|
}}
|
||||||
|
|
||||||
} ; ------ end of block c64flt
|
} ; ------ end of block c64flt
|
||||||
|
@ -643,3 +643,40 @@ mul_word_40 .proc
|
|||||||
sta c64.ESTACK_HI+1,x
|
sta c64.ESTACK_HI+1,x
|
||||||
rts
|
rts
|
||||||
.pend
|
.pend
|
||||||
|
|
||||||
|
sign_b .proc
|
||||||
|
lda c64.ESTACK_LO+1,x
|
||||||
|
beq _sign_zero
|
||||||
|
bmi _sign_neg
|
||||||
|
_sign_pos lda #1
|
||||||
|
sta c64.ESTACK_LO+1,x
|
||||||
|
rts
|
||||||
|
_sign_neg lda #-1
|
||||||
|
_sign_zero sta c64.ESTACK_LO+1,x
|
||||||
|
rts
|
||||||
|
.pend
|
||||||
|
|
||||||
|
sign_ub .proc
|
||||||
|
lda c64.ESTACK_LO+1,x
|
||||||
|
beq sign_b._sign_zero
|
||||||
|
bne sign_b._sign_pos
|
||||||
|
.pend
|
||||||
|
|
||||||
|
sign_w .proc
|
||||||
|
lda c64.ESTACK_HI+1,x
|
||||||
|
bmi sign_b._sign_neg
|
||||||
|
beq sign_ub
|
||||||
|
bne sign_b._sign_pos
|
||||||
|
.pend
|
||||||
|
|
||||||
|
sign_uw .proc
|
||||||
|
lda c64.ESTACK_HI+1,x
|
||||||
|
beq _sign_possibly_zero
|
||||||
|
_sign_pos lda #1
|
||||||
|
sta c64.ESTACK_LO+1,x
|
||||||
|
rts
|
||||||
|
_sign_possibly_zero lda c64.ESTACK_LO+1,x
|
||||||
|
bne _sign_pos
|
||||||
|
sta c64.ESTACK_LO+1,x
|
||||||
|
rts
|
||||||
|
.pend
|
||||||
|
@ -1 +1 @@
|
|||||||
1.51
|
1.52
|
||||||
|
@ -77,7 +77,7 @@ private fun compileMain(args: Array<String>) {
|
|||||||
val event = watchservice.take()
|
val event = watchservice.take()
|
||||||
for(changed in event.pollEvents()) {
|
for(changed in event.pollEvents()) {
|
||||||
val changedPath = changed.context() as Path
|
val changedPath = changed.context() as Path
|
||||||
println(" change detected: ${changedPath}")
|
println(" change detected: $changedPath")
|
||||||
}
|
}
|
||||||
event.reset()
|
event.reset()
|
||||||
println("\u001b[H\u001b[2J") // clear the screen
|
println("\u001b[H\u001b[2J") // clear the screen
|
||||||
|
@ -132,7 +132,7 @@ class AstToSourceCode(val output: (text: String) -> Unit, val program: Program):
|
|||||||
for(param in subroutine.parameters.zip(subroutine.asmParameterRegisters)) {
|
for(param in subroutine.parameters.zip(subroutine.asmParameterRegisters)) {
|
||||||
val reg =
|
val reg =
|
||||||
when {
|
when {
|
||||||
true==param.second.stack -> "stack"
|
param.second.stack -> "stack"
|
||||||
param.second.registerOrPair!=null -> param.second.registerOrPair.toString()
|
param.second.registerOrPair!=null -> param.second.registerOrPair.toString()
|
||||||
param.second.statusflag!=null -> param.second.statusflag.toString()
|
param.second.statusflag!=null -> param.second.statusflag.toString()
|
||||||
else -> "?????1"
|
else -> "?????1"
|
||||||
@ -256,15 +256,12 @@ class AstToSourceCode(val output: (text: String) -> Unit, val program: Program):
|
|||||||
output(numLiteral.number.toString())
|
output(numLiteral.number.toString())
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(refLiteral: ReferenceLiteralValue) {
|
override fun visit(string: StringLiteralValue) {
|
||||||
when {
|
output("\"${escape(string.value)}\"")
|
||||||
refLiteral.isString -> output("\"${escape(refLiteral.str!!)}\"")
|
}
|
||||||
refLiteral.isArray -> {
|
|
||||||
if(refLiteral.array!=null) {
|
override fun visit(array: ArrayLiteralValue) {
|
||||||
outputListMembers(refLiteral.array.asSequence(), '[', ']')
|
outputListMembers(array.value.asSequence(), '[', ']')
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun outputListMembers(array: Sequence<Expression>, openchar: Char, closechar: Char) {
|
private fun outputListMembers(array: Sequence<Expression>, openchar: Char, closechar: Char) {
|
||||||
|
@ -429,7 +429,7 @@ private fun prog8Parser.ExpressionContext.toAst() : Expression {
|
|||||||
else -> throw FatalAstException("invalid datatype for numeric literal")
|
else -> throw FatalAstException("invalid datatype for numeric literal")
|
||||||
}
|
}
|
||||||
litval.floatliteral()!=null -> NumericLiteralValue(DataType.FLOAT, litval.floatliteral().toAst(), litval.toPosition())
|
litval.floatliteral()!=null -> NumericLiteralValue(DataType.FLOAT, litval.floatliteral().toAst(), litval.toPosition())
|
||||||
litval.stringliteral()!=null -> ReferenceLiteralValue(DataType.STR, unescape(litval.stringliteral().text, litval.toPosition()), position = litval.toPosition())
|
litval.stringliteral()!=null -> StringLiteralValue(DataType.STR, unescape(litval.stringliteral().text, litval.toPosition()), position = litval.toPosition())
|
||||||
litval.charliteral()!=null -> {
|
litval.charliteral()!=null -> {
|
||||||
try {
|
try {
|
||||||
NumericLiteralValue(DataType.UBYTE, Petscii.encodePetscii(unescape(litval.charliteral().text, litval.toPosition()), true)[0], litval.toPosition())
|
NumericLiteralValue(DataType.UBYTE, Petscii.encodePetscii(unescape(litval.charliteral().text, litval.toPosition()), true)[0], litval.toPosition())
|
||||||
@ -438,10 +438,10 @@ private fun prog8Parser.ExpressionContext.toAst() : Expression {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
litval.arrayliteral()!=null -> {
|
litval.arrayliteral()!=null -> {
|
||||||
val array = litval.arrayliteral()?.toAst()
|
val array = litval.arrayliteral().toAst()
|
||||||
// the actual type of the arraysize can not yet be determined here (missing namespace & heap)
|
// the actual type of the arraysize can not yet be determined here (missing namespace & heap)
|
||||||
// the ConstantFold takes care of that and converts the type if needed.
|
// the ConstantFold takes care of that and converts the type if needed.
|
||||||
ReferenceLiteralValue(DataType.ARRAY_UB, array = array, position = litval.toPosition())
|
ArrayLiteralValue(DataType.ARRAY_UB, array, position = litval.toPosition())
|
||||||
}
|
}
|
||||||
litval.structliteral()!=null -> {
|
litval.structliteral()!=null -> {
|
||||||
val values = litval.structliteral().expression().map { it.toAst() }
|
val values = litval.structliteral().expression().map { it.toAst() }
|
||||||
@ -596,10 +596,10 @@ private fun prog8Parser.WhenstmtContext.toAst(): WhenStatement {
|
|||||||
private fun prog8Parser.When_choiceContext.toAst(): WhenChoice {
|
private fun prog8Parser.When_choiceContext.toAst(): WhenChoice {
|
||||||
val values = expression_list()?.toAst()
|
val values = expression_list()?.toAst()
|
||||||
val stmt = statement()?.toAst()
|
val stmt = statement()?.toAst()
|
||||||
val stmt_block = statement_block()?.toAst()?.toMutableList() ?: mutableListOf()
|
val stmtBlock = statement_block()?.toAst()?.toMutableList() ?: mutableListOf()
|
||||||
if(stmt!=null)
|
if(stmt!=null)
|
||||||
stmt_block.add(stmt)
|
stmtBlock.add(stmt)
|
||||||
val scope = AnonymousScope(stmt_block, toPosition())
|
val scope = AnonymousScope(stmtBlock, toPosition())
|
||||||
return WhenChoice(values, scope, toPosition())
|
return WhenChoice(values, scope, toPosition())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@ import prog8.ast.Module
|
|||||||
import prog8.ast.Program
|
import prog8.ast.Program
|
||||||
import prog8.ast.processing.*
|
import prog8.ast.processing.*
|
||||||
import prog8.compiler.CompilationOptions
|
import prog8.compiler.CompilationOptions
|
||||||
import prog8.compiler.target.c64.codegen2.AnonymousScopeVarsCleanup
|
import prog8.compiler.target.c64.codegen.AnonymousScopeVarsCleanup
|
||||||
import prog8.optimizer.FlattenAnonymousScopesAndRemoveNops
|
import prog8.optimizer.FlattenAnonymousScopesAndRemoveNops
|
||||||
|
|
||||||
|
|
||||||
@ -40,6 +40,11 @@ internal fun Program.reorderStatements() {
|
|||||||
checker.visit(this)
|
checker.visit(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal fun Program.addTypecasts() {
|
||||||
|
val caster = TypecastsAdder(this)
|
||||||
|
caster.visit(this)
|
||||||
|
}
|
||||||
|
|
||||||
internal fun Module.checkImportedValid() {
|
internal fun Module.checkImportedValid() {
|
||||||
val checker = ImportedModuleDirectiveRemover()
|
val checker = ImportedModuleDirectiveRemover()
|
||||||
checker.visit(this)
|
checker.visit(this)
|
||||||
|
@ -30,7 +30,7 @@ sealed class Expression: Node {
|
|||||||
abstract fun accept(visitor: IAstModifyingVisitor): Expression
|
abstract fun accept(visitor: IAstModifyingVisitor): Expression
|
||||||
abstract fun accept(visitor: IAstVisitor)
|
abstract fun accept(visitor: IAstVisitor)
|
||||||
abstract fun referencesIdentifiers(vararg name: String): Boolean // todo: remove this and add identifier usage tracking into CallGraph instead
|
abstract fun referencesIdentifiers(vararg name: String): Boolean // todo: remove this and add identifier usage tracking into CallGraph instead
|
||||||
abstract fun inferType(program: Program): DataType?
|
abstract fun inferType(program: Program): InferredTypes.InferredType
|
||||||
|
|
||||||
infix fun isSameAs(other: Expression): Boolean {
|
infix fun isSameAs(other: Expression): Boolean {
|
||||||
if(this===other)
|
if(this===other)
|
||||||
@ -68,7 +68,7 @@ class PrefixExpression(val operator: String, var expression: Expression, overrid
|
|||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
override fun referencesIdentifiers(vararg name: String) = expression.referencesIdentifiers(*name)
|
override fun referencesIdentifiers(vararg name: String) = expression.referencesIdentifiers(*name)
|
||||||
override fun inferType(program: Program): DataType? = expression.inferType(program)
|
override fun inferType(program: Program): InferredTypes.InferredType = expression.inferType(program)
|
||||||
|
|
||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
return "Prefix($operator $expression)"
|
return "Prefix($operator $expression)"
|
||||||
@ -94,15 +94,22 @@ class BinaryExpression(var left: Expression, var operator: String, var right: Ex
|
|||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
override fun referencesIdentifiers(vararg name: String) = left.referencesIdentifiers(*name) || right.referencesIdentifiers(*name)
|
override fun referencesIdentifiers(vararg name: String) = left.referencesIdentifiers(*name) || right.referencesIdentifiers(*name)
|
||||||
override fun inferType(program: Program): DataType? {
|
override fun inferType(program: Program): InferredTypes.InferredType {
|
||||||
val leftDt = left.inferType(program)
|
val leftDt = left.inferType(program)
|
||||||
val rightDt = right.inferType(program)
|
val rightDt = right.inferType(program)
|
||||||
return when (operator) {
|
return when (operator) {
|
||||||
"+", "-", "*", "**", "%", "/" -> if (leftDt == null || rightDt == null) null else {
|
"+", "-", "*", "**", "%", "/" -> {
|
||||||
try {
|
if (!leftDt.isKnown || !rightDt.isKnown)
|
||||||
commonDatatype(leftDt, rightDt, null, null).first
|
InferredTypes.unknown()
|
||||||
} catch (x: FatalAstException) {
|
else {
|
||||||
null
|
try {
|
||||||
|
InferredTypes.knownFor(commonDatatype(
|
||||||
|
leftDt.typeOrElse(DataType.BYTE),
|
||||||
|
rightDt.typeOrElse(DataType.BYTE),
|
||||||
|
null, null).first)
|
||||||
|
} catch (x: FatalAstException) {
|
||||||
|
InferredTypes.unknown()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"&" -> leftDt
|
"&" -> leftDt
|
||||||
@ -111,7 +118,7 @@ class BinaryExpression(var left: Expression, var operator: String, var right: Ex
|
|||||||
"and", "or", "xor",
|
"and", "or", "xor",
|
||||||
"<", ">",
|
"<", ">",
|
||||||
"<=", ">=",
|
"<=", ">=",
|
||||||
"==", "!=" -> DataType.UBYTE
|
"==", "!=" -> InferredTypes.knownFor(DataType.UBYTE)
|
||||||
"<<", ">>" -> leftDt
|
"<<", ">>" -> leftDt
|
||||||
else -> throw FatalAstException("resulting datatype check for invalid operator $operator")
|
else -> throw FatalAstException("resulting datatype check for invalid operator $operator")
|
||||||
}
|
}
|
||||||
@ -191,17 +198,16 @@ class ArrayIndexedExpression(var identifier: IdentifierReference,
|
|||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
override fun referencesIdentifiers(vararg name: String) = identifier.referencesIdentifiers(*name)
|
override fun referencesIdentifiers(vararg name: String) = identifier.referencesIdentifiers(*name)
|
||||||
|
|
||||||
override fun inferType(program: Program): DataType? {
|
override fun inferType(program: Program): InferredTypes.InferredType {
|
||||||
val target = identifier.targetStatement(program.namespace)
|
val target = identifier.targetStatement(program.namespace)
|
||||||
if (target is VarDecl) {
|
if (target is VarDecl) {
|
||||||
return when (target.datatype) {
|
return when (target.datatype) {
|
||||||
in NumericDatatypes -> null
|
in StringDatatypes -> InferredTypes.knownFor(DataType.UBYTE)
|
||||||
in StringDatatypes -> DataType.UBYTE
|
in ArrayDatatypes -> InferredTypes.knownFor(ArrayElementTypes.getValue(target.datatype))
|
||||||
in ArrayDatatypes -> ArrayElementTypes[target.datatype]
|
else -> InferredTypes.unknown()
|
||||||
else -> throw FatalAstException("invalid dt")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null
|
return InferredTypes.unknown()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
@ -220,7 +226,7 @@ class TypecastExpression(var expression: Expression, var type: DataType, val imp
|
|||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
override fun referencesIdentifiers(vararg name: String) = expression.referencesIdentifiers(*name)
|
override fun referencesIdentifiers(vararg name: String) = expression.referencesIdentifiers(*name)
|
||||||
override fun inferType(program: Program): DataType? = type
|
override fun inferType(program: Program): InferredTypes.InferredType = InferredTypes.knownFor(type)
|
||||||
override fun constValue(program: Program): NumericLiteralValue? {
|
override fun constValue(program: Program): NumericLiteralValue? {
|
||||||
val cv = expression.constValue(program) ?: return null
|
val cv = expression.constValue(program) ?: return null
|
||||||
return cv.cast(type)
|
return cv.cast(type)
|
||||||
@ -243,7 +249,7 @@ data class AddressOf(var identifier: IdentifierReference, override val position:
|
|||||||
|
|
||||||
override fun constValue(program: Program): NumericLiteralValue? = null
|
override fun constValue(program: Program): NumericLiteralValue? = null
|
||||||
override fun referencesIdentifiers(vararg name: String) = false
|
override fun referencesIdentifiers(vararg name: String) = false
|
||||||
override fun inferType(program: Program) = DataType.UWORD
|
override fun inferType(program: Program): InferredTypes.InferredType = InferredTypes.knownFor(DataType.UWORD)
|
||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
}
|
}
|
||||||
@ -259,7 +265,7 @@ class DirectMemoryRead(var addressExpression: Expression, override val position:
|
|||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
override fun referencesIdentifiers(vararg name: String) = false
|
override fun referencesIdentifiers(vararg name: String) = false
|
||||||
override fun inferType(program: Program): DataType? = DataType.UBYTE
|
override fun inferType(program: Program): InferredTypes.InferredType = InferredTypes.knownFor(DataType.UBYTE)
|
||||||
override fun constValue(program: Program): NumericLiteralValue? = null
|
override fun constValue(program: Program): NumericLiteralValue? = null
|
||||||
|
|
||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
@ -315,7 +321,7 @@ class NumericLiteralValue(val type: DataType, // only numerical types allowed
|
|||||||
|
|
||||||
override fun toString(): String = "NumericLiteral(${type.name}:$number)"
|
override fun toString(): String = "NumericLiteral(${type.name}:$number)"
|
||||||
|
|
||||||
override fun inferType(program: Program) = type
|
override fun inferType(program: Program): InferredTypes.InferredType = InferredTypes.knownFor(type)
|
||||||
|
|
||||||
override fun hashCode(): Int = Objects.hash(type, number)
|
override fun hashCode(): Int = Objects.hash(type, number)
|
||||||
|
|
||||||
@ -399,123 +405,105 @@ class StructLiteralValue(var values: List<Expression>,
|
|||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
override fun referencesIdentifiers(vararg name: String) = values.any { it.referencesIdentifiers(*name) }
|
override fun referencesIdentifiers(vararg name: String) = values.any { it.referencesIdentifiers(*name) }
|
||||||
override fun inferType(program: Program) = DataType.STRUCT
|
override fun inferType(program: Program): InferredTypes.InferredType = InferredTypes.knownFor(DataType.STRUCT)
|
||||||
|
|
||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
return "struct{ ${values.joinToString(", ")} }"
|
return "struct{ ${values.joinToString(", ")} }"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ReferenceLiteralValue(val type: DataType, // only reference types allowed here
|
class StringLiteralValue(val type: DataType, // only string types
|
||||||
val str: String? = null,
|
val value: String,
|
||||||
val array: Array<Expression>? = null,
|
initHeapId: Int? =null,
|
||||||
// actually, at the moment, we don't have struct literals in the language
|
override val position: Position) : Expression() {
|
||||||
initHeapId: Int? =null,
|
|
||||||
override val position: Position) : Expression() {
|
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
|
|
||||||
override fun referencesIdentifiers(vararg name: String) = array?.any { it.referencesIdentifiers(*name) } ?: false
|
|
||||||
|
|
||||||
val isString = type in StringDatatypes
|
|
||||||
val isArray = type in ArrayDatatypes
|
|
||||||
var heapId = initHeapId
|
var heapId = initHeapId
|
||||||
private set
|
private set
|
||||||
|
|
||||||
init {
|
override fun linkParents(parent: Node) {
|
||||||
when(type){
|
this.parent = parent
|
||||||
in StringDatatypes ->
|
|
||||||
if(str==null) throw FatalAstException("literal value missing strvalue/heapId")
|
|
||||||
in ArrayDatatypes ->
|
|
||||||
if(array==null) throw FatalAstException("literal value missing arrayvalue/heapId")
|
|
||||||
else -> throw FatalAstException("invalid type $type")
|
|
||||||
}
|
|
||||||
if(array==null && str==null)
|
|
||||||
throw FatalAstException("literal ref value without actual value")
|
|
||||||
}
|
}
|
||||||
|
override fun referencesIdentifiers(vararg name: String) = false
|
||||||
|
override fun constValue(program: Program): NumericLiteralValue? = null
|
||||||
|
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
||||||
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
|
override fun toString(): String = "'${escape(value)}'"
|
||||||
|
override fun inferType(program: Program): InferredTypes.InferredType = InferredTypes.knownFor(type)
|
||||||
|
operator fun compareTo(other: StringLiteralValue): Int = value.compareTo(other.value)
|
||||||
|
override fun hashCode(): Int = Objects.hash(value, type)
|
||||||
|
override fun equals(other: Any?): Boolean {
|
||||||
|
if(other==null || other !is StringLiteralValue)
|
||||||
|
return false
|
||||||
|
return value==other.value && type==other.type
|
||||||
|
}
|
||||||
|
|
||||||
|
fun addToHeap(heap: HeapValues) {
|
||||||
|
if (heapId != null)
|
||||||
|
return
|
||||||
|
else
|
||||||
|
heapId = heap.addString(type, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ArrayLiteralValue(val type: DataType, // only array types
|
||||||
|
val value: Array<Expression>,
|
||||||
|
initHeapId: Int? =null,
|
||||||
|
override val position: Position) : Expression() {
|
||||||
|
override lateinit var parent: Node
|
||||||
|
|
||||||
|
var heapId = initHeapId
|
||||||
|
private set
|
||||||
|
|
||||||
override fun linkParents(parent: Node) {
|
override fun linkParents(parent: Node) {
|
||||||
this.parent = parent
|
this.parent = parent
|
||||||
array?.forEach {it.linkParents(this)}
|
value.forEach {it.linkParents(this)}
|
||||||
}
|
}
|
||||||
|
override fun referencesIdentifiers(vararg name: String) = value.any { it.referencesIdentifiers(*name) }
|
||||||
override fun constValue(program: Program): NumericLiteralValue? {
|
override fun constValue(program: Program): NumericLiteralValue? = null
|
||||||
// note that we can't handle arrays that only contain constant numbers here anymore
|
|
||||||
// so they're not treated as constants anymore
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
|
override fun toString(): String = "$value"
|
||||||
override fun toString(): String {
|
override fun inferType(program: Program): InferredTypes.InferredType = InferredTypes.knownFor(type)
|
||||||
val valueStr = when(type) {
|
operator fun compareTo(other: ArrayLiteralValue): Int = throw ExpressionError("cannot order compare arrays", position)
|
||||||
in StringDatatypes -> "'${escape(str!!)}'"
|
override fun hashCode(): Int = Objects.hash(value, type)
|
||||||
in ArrayDatatypes -> "$array"
|
|
||||||
else -> throw FatalAstException("weird ref type")
|
|
||||||
}
|
|
||||||
return "RefValueLit($type, $valueStr)"
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun inferType(program: Program) = type
|
|
||||||
|
|
||||||
override fun hashCode(): Int = Objects.hash(str, array, type)
|
|
||||||
|
|
||||||
override fun equals(other: Any?): Boolean {
|
override fun equals(other: Any?): Boolean {
|
||||||
if(other==null || other !is ReferenceLiteralValue)
|
if(other==null || other !is ArrayLiteralValue)
|
||||||
return false
|
return false
|
||||||
if(isArray && other.isArray)
|
return type==other.type && value.contentEquals(other.value)
|
||||||
return array!!.contentEquals(other.array!!) && heapId==other.heapId
|
|
||||||
if(isString && other.isString)
|
|
||||||
return str==other.str && heapId==other.heapId
|
|
||||||
|
|
||||||
if(type!=other.type)
|
|
||||||
return false
|
|
||||||
|
|
||||||
return compareTo(other) == 0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
operator fun compareTo(other: ReferenceLiteralValue): Int {
|
fun cast(targettype: DataType): ArrayLiteralValue? {
|
||||||
throw ExpressionError("cannot order compare type $type with ${other.type}", other.position)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun cast(targettype: DataType): ReferenceLiteralValue? {
|
|
||||||
if(type==targettype)
|
if(type==targettype)
|
||||||
return this
|
return this
|
||||||
when(type) {
|
if(targettype in ArrayDatatypes) {
|
||||||
in StringDatatypes -> {
|
val elementType = ArrayElementTypes.getValue(targettype)
|
||||||
if(targettype in StringDatatypes)
|
val castArray = value.map{
|
||||||
return ReferenceLiteralValue(targettype, str, position = position)
|
val num = it as? NumericLiteralValue
|
||||||
}
|
if(num==null) {
|
||||||
in ArrayDatatypes -> {
|
// an array of UWORDs could possibly also contain AddressOfs
|
||||||
if(targettype in ArrayDatatypes) {
|
if (elementType != DataType.UWORD || it !is AddressOf)
|
||||||
val elementType = ArrayElementTypes.getValue(targettype)
|
throw FatalAstException("weird array element $it")
|
||||||
val castArray = array!!.map{
|
it
|
||||||
val num = it as? NumericLiteralValue
|
} else {
|
||||||
if(num==null) {
|
try {
|
||||||
// an array of UWORDs could possibly also contain AddressOfs
|
num.cast(elementType)
|
||||||
if (elementType != DataType.UWORD || it !is AddressOf)
|
} catch(x: ExpressionError) {
|
||||||
throw FatalAstException("weird array element $it")
|
return null
|
||||||
it
|
}
|
||||||
} else {
|
|
||||||
num.cast(elementType) // TODO this can throw an exception
|
|
||||||
}
|
|
||||||
}.toTypedArray()
|
|
||||||
return ReferenceLiteralValue(targettype, null, array=castArray, position = position)
|
|
||||||
}
|
}
|
||||||
}
|
}.toTypedArray()
|
||||||
else -> {}
|
return ArrayLiteralValue(targettype, castArray, position = position)
|
||||||
}
|
}
|
||||||
return null // invalid type conversion from $this to $targettype
|
return null // invalid type conversion from $this to $targettype
|
||||||
}
|
}
|
||||||
|
|
||||||
fun addToHeap(heap: HeapValues) {
|
fun addToHeap(heap: HeapValues) {
|
||||||
if (heapId != null) return
|
if (heapId != null)
|
||||||
if (str != null) {
|
return
|
||||||
heapId = heap.addString(type, str)
|
else {
|
||||||
}
|
if(value.any {it is AddressOf }) {
|
||||||
else if (array!=null) {
|
val intArrayWithAddressOfs = value.map {
|
||||||
if(array.any {it is AddressOf }) {
|
|
||||||
val intArrayWithAddressOfs = array.map {
|
|
||||||
when (it) {
|
when (it) {
|
||||||
is AddressOf -> IntegerOrAddressOf(null, it)
|
is AddressOf -> IntegerOrAddressOf(null, it)
|
||||||
is NumericLiteralValue -> IntegerOrAddressOf(it.number.toInt(), null)
|
is NumericLiteralValue -> IntegerOrAddressOf(it.number.toInt(), null)
|
||||||
@ -524,7 +512,7 @@ class ReferenceLiteralValue(val type: DataType, // only reference types allo
|
|||||||
}
|
}
|
||||||
heapId = heap.addIntegerArray(type, intArrayWithAddressOfs.toTypedArray())
|
heapId = heap.addIntegerArray(type, intArrayWithAddressOfs.toTypedArray())
|
||||||
} else {
|
} else {
|
||||||
val valuesInArray = array.map { (it as? NumericLiteralValue)?.number }
|
val valuesInArray = value.map { (it as? NumericLiteralValue)?.number }
|
||||||
if(null !in valuesInArray) {
|
if(null !in valuesInArray) {
|
||||||
heapId = if (type == DataType.ARRAY_F) {
|
heapId = if (type == DataType.ARRAY_F) {
|
||||||
val doubleArray = valuesInArray.map { it!!.toDouble() }.toDoubleArray()
|
val doubleArray = valuesInArray.map { it!!.toDouble() }.toDoubleArray()
|
||||||
@ -556,18 +544,18 @@ class RangeExpr(var from: Expression,
|
|||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
override fun referencesIdentifiers(vararg name: String): Boolean = from.referencesIdentifiers(*name) || to.referencesIdentifiers(*name)
|
override fun referencesIdentifiers(vararg name: String): Boolean = from.referencesIdentifiers(*name) || to.referencesIdentifiers(*name)
|
||||||
override fun inferType(program: Program): DataType? {
|
override fun inferType(program: Program): InferredTypes.InferredType {
|
||||||
val fromDt=from.inferType(program)
|
val fromDt=from.inferType(program)
|
||||||
val toDt=to.inferType(program)
|
val toDt=to.inferType(program)
|
||||||
return when {
|
return when {
|
||||||
fromDt==null || toDt==null -> null
|
!fromDt.isKnown || !toDt.isKnown -> InferredTypes.unknown()
|
||||||
fromDt== DataType.UBYTE && toDt== DataType.UBYTE -> DataType.ARRAY_UB
|
fromDt istype DataType.UBYTE && toDt istype DataType.UBYTE -> InferredTypes.knownFor(DataType.ARRAY_UB)
|
||||||
fromDt== DataType.UWORD && toDt== DataType.UWORD -> DataType.ARRAY_UW
|
fromDt istype DataType.UWORD && toDt istype DataType.UWORD -> InferredTypes.knownFor(DataType.ARRAY_UW)
|
||||||
fromDt== DataType.STR && toDt== DataType.STR -> DataType.STR
|
fromDt istype DataType.STR && toDt istype DataType.STR -> InferredTypes.knownFor(DataType.STR)
|
||||||
fromDt== DataType.STR_S && toDt== DataType.STR_S -> DataType.STR_S
|
fromDt istype DataType.STR_S && toDt istype DataType.STR_S -> InferredTypes.knownFor(DataType.STR_S)
|
||||||
fromDt== DataType.WORD || toDt== DataType.WORD -> DataType.ARRAY_W
|
fromDt istype DataType.WORD || toDt istype DataType.WORD -> InferredTypes.knownFor(DataType.ARRAY_W)
|
||||||
fromDt== DataType.BYTE || toDt== DataType.BYTE -> DataType.ARRAY_B
|
fromDt istype DataType.BYTE || toDt istype DataType.BYTE -> InferredTypes.knownFor(DataType.ARRAY_B)
|
||||||
else -> DataType.ARRAY_UB
|
else -> InferredTypes.knownFor(DataType.ARRAY_UB)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
@ -585,12 +573,12 @@ class RangeExpr(var from: Expression,
|
|||||||
fun toConstantIntegerRange(): IntProgression? {
|
fun toConstantIntegerRange(): IntProgression? {
|
||||||
val fromVal: Int
|
val fromVal: Int
|
||||||
val toVal: Int
|
val toVal: Int
|
||||||
val fromRlv = from as? ReferenceLiteralValue
|
val fromString = from as? StringLiteralValue
|
||||||
val toRlv = to as? ReferenceLiteralValue
|
val toString = to as? StringLiteralValue
|
||||||
if(fromRlv!=null && fromRlv.isString && toRlv!=null && toRlv.isString) {
|
if(fromString!=null && toString!=null ) {
|
||||||
// string range -> int range over petscii values
|
// string range -> int range over petscii values
|
||||||
fromVal = Petscii.encodePetscii(fromRlv.str!!, true)[0].toInt()
|
fromVal = Petscii.encodePetscii(fromString.value, true)[0].toInt()
|
||||||
toVal = Petscii.encodePetscii(toRlv.str!!, true)[0].toInt()
|
toVal = Petscii.encodePetscii(toString.value, true)[0].toInt()
|
||||||
} else {
|
} else {
|
||||||
val fromLv = from as? NumericLiteralValue
|
val fromLv = from as? NumericLiteralValue
|
||||||
val toLv = to as? NumericLiteralValue
|
val toLv = to as? NumericLiteralValue
|
||||||
@ -601,17 +589,21 @@ class RangeExpr(var from: Expression,
|
|||||||
toVal = toLv.number.toInt()
|
toVal = toLv.number.toInt()
|
||||||
}
|
}
|
||||||
val stepVal = (step as? NumericLiteralValue)?.number?.toInt() ?: 1
|
val stepVal = (step as? NumericLiteralValue)?.number?.toInt() ?: 1
|
||||||
return when {
|
return makeRange(fromVal, toVal, stepVal)
|
||||||
fromVal <= toVal -> when {
|
}
|
||||||
stepVal <= 0 -> IntRange.EMPTY
|
}
|
||||||
stepVal == 1 -> fromVal..toVal
|
|
||||||
else -> fromVal..toVal step stepVal
|
internal fun makeRange(fromVal: Int, toVal: Int, stepVal: Int): IntProgression {
|
||||||
}
|
return when {
|
||||||
else -> when {
|
fromVal <= toVal -> when {
|
||||||
stepVal >= 0 -> IntRange.EMPTY
|
stepVal <= 0 -> IntRange.EMPTY
|
||||||
stepVal == -1 -> fromVal downTo toVal
|
stepVal == 1 -> fromVal..toVal
|
||||||
else -> fromVal downTo toVal step abs(stepVal)
|
else -> fromVal..toVal step stepVal
|
||||||
}
|
}
|
||||||
|
else -> when {
|
||||||
|
stepVal >= 0 -> IntRange.EMPTY
|
||||||
|
stepVal == -1 -> fromVal downTo toVal
|
||||||
|
else -> fromVal downTo toVal step abs(stepVal)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -631,7 +623,7 @@ class RegisterExpr(val register: Register, override val position: Position) : Ex
|
|||||||
return "RegisterExpr(register=$register, pos=$position)"
|
return "RegisterExpr(register=$register, pos=$position)"
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun inferType(program: Program) = DataType.UBYTE
|
override fun inferType(program: Program): InferredTypes.InferredType = InferredTypes.knownFor(DataType.UBYTE)
|
||||||
}
|
}
|
||||||
|
|
||||||
data class IdentifierReference(val nameInSource: List<String>, override val position: Position) : Expression() {
|
data class IdentifierReference(val nameInSource: List<String>, override val position: Position) : Expression() {
|
||||||
@ -668,12 +660,12 @@ data class IdentifierReference(val nameInSource: List<String>, override val posi
|
|||||||
|
|
||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
override fun referencesIdentifiers(vararg name: String): Boolean = nameInSource.last() in name // @todo is this correct all the time?
|
override fun referencesIdentifiers(vararg name: String): Boolean = nameInSource.last() in name
|
||||||
|
|
||||||
override fun inferType(program: Program): DataType? {
|
override fun inferType(program: Program): InferredTypes.InferredType {
|
||||||
val targetStmt = targetStatement(program.namespace)
|
val targetStmt = targetStatement(program.namespace)
|
||||||
if(targetStmt is VarDecl) {
|
if(targetStmt is VarDecl) {
|
||||||
return targetStmt.datatype
|
return InferredTypes.knownFor(targetStmt.datatype)
|
||||||
} else {
|
} else {
|
||||||
throw FatalAstException("cannot get datatype from identifier reference ${this}, pos=$position")
|
throw FatalAstException("cannot get datatype from identifier reference ${this}, pos=$position")
|
||||||
}
|
}
|
||||||
@ -686,17 +678,11 @@ data class IdentifierReference(val nameInSource: List<String>, override val posi
|
|||||||
val value = (node as? VarDecl)?.value ?: throw FatalAstException("requires a reference value")
|
val value = (node as? VarDecl)?.value ?: throw FatalAstException("requires a reference value")
|
||||||
return when (value) {
|
return when (value) {
|
||||||
is IdentifierReference -> value.heapId(namespace)
|
is IdentifierReference -> value.heapId(namespace)
|
||||||
is ReferenceLiteralValue -> value.heapId ?: throw FatalAstException("refLv is not on the heap: $value")
|
is StringLiteralValue -> value.heapId ?: throw FatalAstException("string is not on the heap: $value")
|
||||||
|
is ArrayLiteralValue -> value.heapId ?: throw FatalAstException("array is not on the heap: $value")
|
||||||
else -> throw FatalAstException("requires a reference value")
|
else -> throw FatalAstException("requires a reference value")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun withPrefixedName(nameprefix: String): IdentifierReference {
|
|
||||||
val prefixed = nameInSource.dropLast(1) + listOf(nameprefix+nameInSource.last())
|
|
||||||
val new = IdentifierReference(prefixed, position)
|
|
||||||
new.parent = parent
|
|
||||||
return new
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class FunctionCall(override var target: IdentifierReference,
|
class FunctionCall(override var target: IdentifierReference,
|
||||||
@ -729,7 +715,7 @@ class FunctionCall(override var target: IdentifierReference,
|
|||||||
|
|
||||||
if(withDatatypeCheck) {
|
if(withDatatypeCheck) {
|
||||||
val resultDt = this.inferType(program)
|
val resultDt = this.inferType(program)
|
||||||
if(resultValue==null || resultDt == resultValue.type)
|
if(resultValue==null || resultDt istype resultValue.type)
|
||||||
return resultValue
|
return resultValue
|
||||||
throw FatalAstException("evaluated const expression result value doesn't match expected datatype $resultDt, pos=$position")
|
throw FatalAstException("evaluated const expression result value doesn't match expected datatype $resultDt, pos=$position")
|
||||||
} else {
|
} else {
|
||||||
@ -750,27 +736,27 @@ class FunctionCall(override var target: IdentifierReference,
|
|||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
override fun referencesIdentifiers(vararg name: String): Boolean = target.referencesIdentifiers(*name) || arglist.any{it.referencesIdentifiers(*name)}
|
override fun referencesIdentifiers(vararg name: String): Boolean = target.referencesIdentifiers(*name) || arglist.any{it.referencesIdentifiers(*name)}
|
||||||
|
|
||||||
override fun inferType(program: Program): DataType? {
|
override fun inferType(program: Program): InferredTypes.InferredType {
|
||||||
val constVal = constValue(program ,false)
|
val constVal = constValue(program ,false)
|
||||||
if(constVal!=null)
|
if(constVal!=null)
|
||||||
return constVal.type
|
return InferredTypes.knownFor(constVal.type)
|
||||||
val stmt = target.targetStatement(program.namespace) ?: return null
|
val stmt = target.targetStatement(program.namespace) ?: return InferredTypes.unknown()
|
||||||
when (stmt) {
|
when (stmt) {
|
||||||
is BuiltinFunctionStatementPlaceholder -> {
|
is BuiltinFunctionStatementPlaceholder -> {
|
||||||
if(target.nameInSource[0] == "set_carry" || target.nameInSource[0]=="set_irqd" ||
|
if(target.nameInSource[0] == "set_carry" || target.nameInSource[0]=="set_irqd" ||
|
||||||
target.nameInSource[0] == "clear_carry" || target.nameInSource[0]=="clear_irqd") {
|
target.nameInSource[0] == "clear_carry" || target.nameInSource[0]=="clear_irqd") {
|
||||||
return null // these have no return value
|
return InferredTypes.void() // these have no return value
|
||||||
}
|
}
|
||||||
return builtinFunctionReturnType(target.nameInSource[0], this.arglist, program)
|
return builtinFunctionReturnType(target.nameInSource[0], this.arglist, program)
|
||||||
}
|
}
|
||||||
is Subroutine -> {
|
is Subroutine -> {
|
||||||
if(stmt.returntypes.isEmpty())
|
if(stmt.returntypes.isEmpty())
|
||||||
return null // no return value
|
return InferredTypes.void() // no return value
|
||||||
if(stmt.returntypes.size==1)
|
if(stmt.returntypes.size==1)
|
||||||
return stmt.returntypes[0]
|
return InferredTypes.knownFor(stmt.returntypes[0])
|
||||||
return null // has multiple return types... so not a single resulting datatype possible
|
return InferredTypes.unknown() // has multiple return types... so not a single resulting datatype possible
|
||||||
}
|
}
|
||||||
else -> return null
|
else -> return InferredTypes.unknown()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
50
compiler/src/prog8/ast/expressions/InferredTypes.kt
Normal file
50
compiler/src/prog8/ast/expressions/InferredTypes.kt
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
package prog8.ast.expressions
|
||||||
|
|
||||||
|
import prog8.ast.base.DataType
|
||||||
|
|
||||||
|
object InferredTypes {
|
||||||
|
class InferredType private constructor(val isUnknown: Boolean, val isVoid: Boolean, private var datatype: DataType?) {
|
||||||
|
init {
|
||||||
|
if(datatype!=null && (isUnknown || isVoid))
|
||||||
|
throw IllegalArgumentException("invalid combination of args")
|
||||||
|
}
|
||||||
|
|
||||||
|
val isKnown = datatype!=null
|
||||||
|
fun typeOrElse(alternative: DataType) = if(isUnknown || isVoid) alternative else datatype!!
|
||||||
|
infix fun istype(type: DataType): Boolean = if(isUnknown || isVoid) false else this.datatype==type
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun unknown() = InferredType(isUnknown = true, isVoid = false, datatype = null)
|
||||||
|
fun void() = InferredType(isUnknown = false, isVoid = true, datatype = null)
|
||||||
|
fun known(type: DataType) = InferredType(isUnknown = false, isVoid = false, datatype = type)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun equals(other: Any?): Boolean {
|
||||||
|
if(other !is InferredType)
|
||||||
|
return false
|
||||||
|
return isVoid==other.isVoid && datatype==other.datatype
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val unknownInstance = InferredType.unknown()
|
||||||
|
private val voidInstance = InferredType.void()
|
||||||
|
private val knownInstances = mapOf(
|
||||||
|
DataType.UBYTE to InferredType.known(DataType.UBYTE),
|
||||||
|
DataType.BYTE to InferredType.known(DataType.BYTE),
|
||||||
|
DataType.UWORD to InferredType.known(DataType.UWORD),
|
||||||
|
DataType.WORD to InferredType.known(DataType.WORD),
|
||||||
|
DataType.FLOAT to InferredType.known(DataType.FLOAT),
|
||||||
|
DataType.STR to InferredType.known(DataType.STR),
|
||||||
|
DataType.STR_S to InferredType.known(DataType.STR_S),
|
||||||
|
DataType.ARRAY_UB to InferredType.known(DataType.ARRAY_UB),
|
||||||
|
DataType.ARRAY_B to InferredType.known(DataType.ARRAY_B),
|
||||||
|
DataType.ARRAY_UW to InferredType.known(DataType.ARRAY_UW),
|
||||||
|
DataType.ARRAY_W to InferredType.known(DataType.ARRAY_W),
|
||||||
|
DataType.ARRAY_F to InferredType.known(DataType.ARRAY_F),
|
||||||
|
DataType.STRUCT to InferredType.known(DataType.STRUCT)
|
||||||
|
)
|
||||||
|
|
||||||
|
fun void() = voidInstance
|
||||||
|
fun unknown() = unknownInstance
|
||||||
|
fun knownFor(type: DataType) = knownInstances.getValue(type)
|
||||||
|
}
|
@ -7,7 +7,6 @@ import prog8.ast.base.*
|
|||||||
import prog8.ast.expressions.*
|
import prog8.ast.expressions.*
|
||||||
import prog8.ast.statements.*
|
import prog8.ast.statements.*
|
||||||
import prog8.compiler.CompilationOptions
|
import prog8.compiler.CompilationOptions
|
||||||
import prog8.compiler.HeapValues
|
|
||||||
import prog8.compiler.target.c64.MachineDefinition.FLOAT_MAX_NEGATIVE
|
import prog8.compiler.target.c64.MachineDefinition.FLOAT_MAX_NEGATIVE
|
||||||
import prog8.compiler.target.c64.MachineDefinition.FLOAT_MAX_POSITIVE
|
import prog8.compiler.target.c64.MachineDefinition.FLOAT_MAX_POSITIVE
|
||||||
import prog8.functions.BuiltinFunctions
|
import prog8.functions.BuiltinFunctions
|
||||||
@ -98,14 +97,18 @@ internal class AstChecker(private val program: Program,
|
|||||||
}
|
}
|
||||||
if(expectedReturnValues.size==1 && returnStmt.value!=null) {
|
if(expectedReturnValues.size==1 && returnStmt.value!=null) {
|
||||||
val valueDt = returnStmt.value!!.inferType(program)
|
val valueDt = returnStmt.value!!.inferType(program)
|
||||||
if(expectedReturnValues[0]!=valueDt)
|
if(!valueDt.isKnown) {
|
||||||
checkResult.add(ExpressionError("type $valueDt of return value doesn't match subroutine's return type", returnStmt.value!!.position))
|
checkResult.add(ExpressionError("return value type mismatch", returnStmt.value!!.position))
|
||||||
|
} else {
|
||||||
|
if (expectedReturnValues[0] != valueDt.typeOrElse(DataType.STRUCT))
|
||||||
|
checkResult.add(ExpressionError("type $valueDt of return value doesn't match subroutine's return type", returnStmt.value!!.position))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
super.visit(returnStmt)
|
super.visit(returnStmt)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(ifStatement: IfStatement) {
|
override fun visit(ifStatement: IfStatement) {
|
||||||
if(ifStatement.condition.inferType(program) !in IntegerDatatypes)
|
if(ifStatement.condition.inferType(program).typeOrElse(DataType.STRUCT) !in IntegerDatatypes)
|
||||||
checkResult.add(ExpressionError("condition value should be an integer type", ifStatement.condition.position))
|
checkResult.add(ExpressionError("condition value should be an integer type", ifStatement.condition.position))
|
||||||
super.visit(ifStatement)
|
super.visit(ifStatement)
|
||||||
}
|
}
|
||||||
@ -114,7 +117,7 @@ internal class AstChecker(private val program: Program,
|
|||||||
if(forLoop.body.containsNoCodeNorVars())
|
if(forLoop.body.containsNoCodeNorVars())
|
||||||
printWarning("for loop body is empty", forLoop.position)
|
printWarning("for loop body is empty", forLoop.position)
|
||||||
|
|
||||||
val iterableDt = forLoop.iterable.inferType(program)
|
val iterableDt = forLoop.iterable.inferType(program).typeOrElse(DataType.BYTE)
|
||||||
if(iterableDt !in IterableDatatypes && forLoop.iterable !is RangeExpr) {
|
if(iterableDt !in IterableDatatypes && forLoop.iterable !is RangeExpr) {
|
||||||
checkResult.add(ExpressionError("can only loop over an iterable type", forLoop.position))
|
checkResult.add(ExpressionError("can only loop over an iterable type", forLoop.position))
|
||||||
} else {
|
} else {
|
||||||
@ -329,7 +332,7 @@ internal class AstChecker(private val program: Program,
|
|||||||
override fun visit(repeatLoop: RepeatLoop) {
|
override fun visit(repeatLoop: RepeatLoop) {
|
||||||
if(repeatLoop.untilCondition.referencesIdentifiers("A", "X", "Y"))
|
if(repeatLoop.untilCondition.referencesIdentifiers("A", "X", "Y"))
|
||||||
printWarning("using a register in the loop condition is risky (it could get clobbered)", repeatLoop.untilCondition.position)
|
printWarning("using a register in the loop condition is risky (it could get clobbered)", repeatLoop.untilCondition.position)
|
||||||
if(repeatLoop.untilCondition.inferType(program) !in IntegerDatatypes)
|
if(repeatLoop.untilCondition.inferType(program).typeOrElse(DataType.STRUCT) !in IntegerDatatypes)
|
||||||
checkResult.add(ExpressionError("condition value should be an integer type", repeatLoop.untilCondition.position))
|
checkResult.add(ExpressionError("condition value should be an integer type", repeatLoop.untilCondition.position))
|
||||||
super.visit(repeatLoop)
|
super.visit(repeatLoop)
|
||||||
}
|
}
|
||||||
@ -337,7 +340,7 @@ internal class AstChecker(private val program: Program,
|
|||||||
override fun visit(whileLoop: WhileLoop) {
|
override fun visit(whileLoop: WhileLoop) {
|
||||||
if(whileLoop.condition.referencesIdentifiers("A", "X", "Y"))
|
if(whileLoop.condition.referencesIdentifiers("A", "X", "Y"))
|
||||||
printWarning("using a register in the loop condition is risky (it could get clobbered)", whileLoop.condition.position)
|
printWarning("using a register in the loop condition is risky (it could get clobbered)", whileLoop.condition.position)
|
||||||
if(whileLoop.condition.inferType(program) !in IntegerDatatypes)
|
if(whileLoop.condition.inferType(program).typeOrElse(DataType.STRUCT) !in IntegerDatatypes)
|
||||||
checkResult.add(ExpressionError("condition value should be an integer type", whileLoop.condition.position))
|
checkResult.add(ExpressionError("condition value should be an integer type", whileLoop.condition.position))
|
||||||
super.visit(whileLoop)
|
super.visit(whileLoop)
|
||||||
}
|
}
|
||||||
@ -351,7 +354,8 @@ internal class AstChecker(private val program: Program,
|
|||||||
if(stmt.returntypes.size>1)
|
if(stmt.returntypes.size>1)
|
||||||
checkResult.add(ExpressionError("It's not possible to store the multiple results of this asmsub call; you should use a small block of custom inline assembly for this.", assignment.value.position))
|
checkResult.add(ExpressionError("It's not possible to store the multiple results of this asmsub call; you should use a small block of custom inline assembly for this.", assignment.value.position))
|
||||||
else {
|
else {
|
||||||
if(stmt.returntypes.single()!=assignment.target.inferType(program, assignment)) {
|
val idt = assignment.target.inferType(program, assignment)
|
||||||
|
if(!idt.isKnown || stmt.returntypes.single()!=idt.typeOrElse(DataType.BYTE)) {
|
||||||
checkResult.add(ExpressionError("return type mismatch", assignment.value.position))
|
checkResult.add(ExpressionError("return type mismatch", assignment.value.position))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -403,7 +407,7 @@ internal class AstChecker(private val program: Program,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val targetDt = assignTarget.inferType(program, assignment)
|
val targetDt = assignTarget.inferType(program, assignment).typeOrElse(DataType.STR)
|
||||||
if(targetDt in StringDatatypes || targetDt in ArrayDatatypes)
|
if(targetDt in StringDatatypes || targetDt in ArrayDatatypes)
|
||||||
checkResult.add(SyntaxError("cannot assign to a string or array type", assignTarget.position))
|
checkResult.add(SyntaxError("cannot assign to a string or array type", assignTarget.position))
|
||||||
|
|
||||||
@ -413,13 +417,13 @@ internal class AstChecker(private val program: Program,
|
|||||||
throw FatalAstException("augmented assignment should have been converted into normal assignment")
|
throw FatalAstException("augmented assignment should have been converted into normal assignment")
|
||||||
|
|
||||||
val targetDatatype = assignTarget.inferType(program, assignment)
|
val targetDatatype = assignTarget.inferType(program, assignment)
|
||||||
if (targetDatatype != null) {
|
if (targetDatatype.isKnown) {
|
||||||
val constVal = assignment.value.constValue(program)
|
val constVal = assignment.value.constValue(program)
|
||||||
if (constVal != null) {
|
if (constVal != null) {
|
||||||
checkValueTypeAndRange(targetDatatype, constVal)
|
checkValueTypeAndRange(targetDatatype.typeOrElse(DataType.BYTE), constVal)
|
||||||
} else {
|
} else {
|
||||||
val sourceDatatype: DataType? = assignment.value.inferType(program)
|
val sourceDatatype = assignment.value.inferType(program)
|
||||||
if (sourceDatatype == null) {
|
if (!sourceDatatype.isKnown) {
|
||||||
if (assignment.value is FunctionCall) {
|
if (assignment.value is FunctionCall) {
|
||||||
val targetStmt = (assignment.value as FunctionCall).target.targetStatement(program.namespace)
|
val targetStmt = (assignment.value as FunctionCall).target.targetStatement(program.namespace)
|
||||||
if (targetStmt != null)
|
if (targetStmt != null)
|
||||||
@ -427,7 +431,8 @@ internal class AstChecker(private val program: Program,
|
|||||||
} else
|
} else
|
||||||
checkResult.add(ExpressionError("assignment value is invalid or has no proper datatype", assignment.value.position))
|
checkResult.add(ExpressionError("assignment value is invalid or has no proper datatype", assignment.value.position))
|
||||||
} else {
|
} else {
|
||||||
checkAssignmentCompatible(targetDatatype, assignTarget, sourceDatatype, assignment.value, assignment.position)
|
checkAssignmentCompatible(targetDatatype.typeOrElse(DataType.BYTE), assignTarget,
|
||||||
|
sourceDatatype.typeOrElse(DataType.BYTE), assignment.value, assignment.position)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -526,14 +531,12 @@ internal class AstChecker(private val program: Program,
|
|||||||
}
|
}
|
||||||
when(decl.value) {
|
when(decl.value) {
|
||||||
is RangeExpr -> throw FatalAstException("range expression should have been converted to a true array value")
|
is RangeExpr -> throw FatalAstException("range expression should have been converted to a true array value")
|
||||||
is ReferenceLiteralValue -> {
|
is StringLiteralValue -> {
|
||||||
val arraySpec = decl.arraysize ?: (
|
checkValueTypeAndRangeString(decl.datatype, decl.value as StringLiteralValue)
|
||||||
if((decl.value as ReferenceLiteralValue).isArray)
|
}
|
||||||
ArrayIndex.forArray(decl.value as ReferenceLiteralValue, program.heap)
|
is ArrayLiteralValue -> {
|
||||||
else
|
val arraySpec = decl.arraysize ?: ArrayIndex.forArray(decl.value as ArrayLiteralValue)
|
||||||
ArrayIndex(NumericLiteralValue.optimalInteger(-2, decl.position), decl.position)
|
checkValueTypeAndRangeArray(decl.datatype, decl.struct, arraySpec, decl.value as ArrayLiteralValue)
|
||||||
)
|
|
||||||
checkValueTypeAndRange(decl.datatype, decl.struct, arraySpec, decl.value as ReferenceLiteralValue, program.heap)
|
|
||||||
}
|
}
|
||||||
is NumericLiteralValue -> {
|
is NumericLiteralValue -> {
|
||||||
checkValueTypeAndRange(decl.datatype, decl.value as NumericLiteralValue)
|
checkValueTypeAndRange(decl.datatype, decl.value as NumericLiteralValue)
|
||||||
@ -682,35 +685,29 @@ internal class AstChecker(private val program: Program,
|
|||||||
checkResult.add(NameError("included file not found: $filename", directive.position))
|
checkResult.add(NameError("included file not found: $filename", directive.position))
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(refLiteral: ReferenceLiteralValue) {
|
override fun visit(array: ArrayLiteralValue) {
|
||||||
if(!compilerOptions.floats && refLiteral.type in setOf(DataType.FLOAT, DataType.ARRAY_F)) {
|
if(!compilerOptions.floats && array.type in setOf(DataType.FLOAT, DataType.ARRAY_F)) {
|
||||||
checkResult.add(SyntaxError("floating point used, but that is not enabled via options", refLiteral.position))
|
checkResult.add(SyntaxError("floating point used, but that is not enabled via options", array.position))
|
||||||
}
|
}
|
||||||
val arrayspec =
|
val arrayspec = ArrayIndex.forArray(array)
|
||||||
if(refLiteral.isArray)
|
checkValueTypeAndRangeArray(array.type, null, arrayspec, array)
|
||||||
ArrayIndex.forArray(refLiteral, program.heap)
|
|
||||||
else
|
|
||||||
ArrayIndex(NumericLiteralValue.optimalInteger(-3, refLiteral.position), refLiteral.position)
|
|
||||||
checkValueTypeAndRange(refLiteral.type, null, arrayspec, refLiteral, program.heap)
|
|
||||||
|
|
||||||
super.visit(refLiteral)
|
super.visit(array)
|
||||||
|
|
||||||
when(refLiteral.type) {
|
if(array.heapId==null)
|
||||||
in StringDatatypes -> {
|
throw FatalAstException("array should have been moved to heap at ${array.position}")
|
||||||
if(refLiteral.heapId==null)
|
}
|
||||||
throw FatalAstException("string should have been moved to heap at ${refLiteral.position}")
|
|
||||||
}
|
override fun visit(string: StringLiteralValue) {
|
||||||
in ArrayDatatypes -> {
|
checkValueTypeAndRangeString(string.type, string)
|
||||||
if(refLiteral.heapId==null)
|
super.visit(string)
|
||||||
throw FatalAstException("array should have been moved to heap at ${refLiteral.position}")
|
if(string.heapId==null)
|
||||||
}
|
throw FatalAstException("string should have been moved to heap at ${string.position}")
|
||||||
else -> {}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(expr: PrefixExpression) {
|
override fun visit(expr: PrefixExpression) {
|
||||||
if(expr.operator=="-") {
|
if(expr.operator=="-") {
|
||||||
val dt = expr.inferType(program)
|
val dt = expr.inferType(program).typeOrElse(DataType.STRUCT)
|
||||||
if (dt != DataType.BYTE && dt != DataType.WORD && dt != DataType.FLOAT) {
|
if (dt != DataType.BYTE && dt != DataType.WORD && dt != DataType.FLOAT) {
|
||||||
checkResult.add(ExpressionError("can only take negative of a signed number type", expr.position))
|
checkResult.add(ExpressionError("can only take negative of a signed number type", expr.position))
|
||||||
}
|
}
|
||||||
@ -719,8 +716,13 @@ internal class AstChecker(private val program: Program,
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(expr: BinaryExpression) {
|
override fun visit(expr: BinaryExpression) {
|
||||||
val leftDt = expr.left.inferType(program)
|
val leftIDt = expr.left.inferType(program)
|
||||||
val rightDt = expr.right.inferType(program)
|
val rightIDt = expr.right.inferType(program)
|
||||||
|
if(!leftIDt.isKnown || !rightIDt.isKnown) {
|
||||||
|
throw FatalAstException("can't determine datatype of both expression operands $expr")
|
||||||
|
}
|
||||||
|
val leftDt = leftIDt.typeOrElse(DataType.STRUCT)
|
||||||
|
val rightDt = rightIDt.typeOrElse(DataType.STRUCT)
|
||||||
|
|
||||||
when(expr.operator){
|
when(expr.operator){
|
||||||
"/", "%" -> {
|
"/", "%" -> {
|
||||||
@ -851,30 +853,30 @@ internal class AstChecker(private val program: Program,
|
|||||||
val paramTypesForAddressOf = PassByReferenceDatatypes + DataType.UWORD
|
val paramTypesForAddressOf = PassByReferenceDatatypes + DataType.UWORD
|
||||||
for (arg in args.withIndex().zip(func.parameters)) {
|
for (arg in args.withIndex().zip(func.parameters)) {
|
||||||
val argDt=arg.first.value.inferType(program)
|
val argDt=arg.first.value.inferType(program)
|
||||||
if (argDt != null
|
if (argDt.isKnown
|
||||||
&& !(argDt isAssignableTo arg.second.possibleDatatypes)
|
&& !(argDt.typeOrElse(DataType.STRUCT) isAssignableTo arg.second.possibleDatatypes)
|
||||||
&& (argDt != DataType.UWORD || arg.second.possibleDatatypes.intersect(paramTypesForAddressOf).isEmpty())) {
|
&& (argDt.typeOrElse(DataType.STRUCT) != DataType.UWORD || arg.second.possibleDatatypes.intersect(paramTypesForAddressOf).isEmpty())) {
|
||||||
checkResult.add(ExpressionError("builtin function '${target.name}' argument ${arg.first.index + 1} has invalid type $argDt, expected ${arg.second.possibleDatatypes}", position))
|
checkResult.add(ExpressionError("builtin function '${target.name}' argument ${arg.first.index + 1} has invalid type $argDt, expected ${arg.second.possibleDatatypes}", position))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(target.name=="swap") {
|
if(target.name=="swap") {
|
||||||
// swap() is a bit weird because this one is translated into a operations directly, instead of being a function call
|
// swap() is a bit weird because this one is translated into a operations directly, instead of being a function call
|
||||||
val dt1 = args[0].inferType(program)!!
|
val dt1 = args[0].inferType(program)
|
||||||
val dt2 = args[1].inferType(program)!!
|
val dt2 = args[1].inferType(program)
|
||||||
if (dt1 != dt2)
|
if (dt1 != dt2)
|
||||||
checkResult.add(ExpressionError("swap requires 2 args of identical type", position))
|
checkResult.add(ExpressionError("swap requires 2 args of identical type", position))
|
||||||
else if (args[0].constValue(program) != null || args[1].constValue(program) != null)
|
else if (args[0].constValue(program) != null || args[1].constValue(program) != null)
|
||||||
checkResult.add(ExpressionError("swap requires 2 variables, not constant value(s)", position))
|
checkResult.add(ExpressionError("swap requires 2 variables, not constant value(s)", position))
|
||||||
else if(args[0] isSameAs args[1])
|
else if(args[0] isSameAs args[1])
|
||||||
checkResult.add(ExpressionError("swap should have 2 different args", position))
|
checkResult.add(ExpressionError("swap should have 2 different args", position))
|
||||||
else if(dt1 !in NumericDatatypes)
|
else if(dt1.typeOrElse(DataType.STRUCT) !in NumericDatatypes)
|
||||||
checkResult.add(ExpressionError("swap requires args of numerical type", position))
|
checkResult.add(ExpressionError("swap requires args of numerical type", position))
|
||||||
}
|
}
|
||||||
else if(target.name=="all" || target.name=="any") {
|
else if(target.name=="all" || target.name=="any") {
|
||||||
if((args[0] as? AddressOf)?.identifier?.targetVarDecl(program.namespace)?.datatype in StringDatatypes) {
|
if((args[0] as? AddressOf)?.identifier?.targetVarDecl(program.namespace)?.datatype in StringDatatypes) {
|
||||||
checkResult.add(ExpressionError("any/all on a string is useless (is always true unless the string is empty)", position))
|
checkResult.add(ExpressionError("any/all on a string is useless (is always true unless the string is empty)", position))
|
||||||
}
|
}
|
||||||
if(args[0].inferType(program) in StringDatatypes) {
|
if(args[0].inferType(program).typeOrElse(DataType.STR) in StringDatatypes) {
|
||||||
checkResult.add(ExpressionError("any/all on a string is useless (is always true unless the string is empty)", position))
|
checkResult.add(ExpressionError("any/all on a string is useless (is always true unless the string is empty)", position))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -884,8 +886,12 @@ internal class AstChecker(private val program: Program,
|
|||||||
checkResult.add(SyntaxError("invalid number of arguments", position))
|
checkResult.add(SyntaxError("invalid number of arguments", position))
|
||||||
else {
|
else {
|
||||||
for (arg in args.withIndex().zip(target.parameters)) {
|
for (arg in args.withIndex().zip(target.parameters)) {
|
||||||
val argDt = arg.first.value.inferType(program)
|
val argIDt = arg.first.value.inferType(program)
|
||||||
if(argDt!=null && !(argDt isAssignableTo arg.second.type)) {
|
if(!argIDt.isKnown) {
|
||||||
|
throw FatalAstException("can't determine arg dt ${arg.first.value}")
|
||||||
|
}
|
||||||
|
val argDt=argIDt.typeOrElse(DataType.STRUCT)
|
||||||
|
if(!(argDt isAssignableTo arg.second.type)) {
|
||||||
// for asm subroutines having STR param it's okay to provide a UWORD (address value)
|
// for asm subroutines having STR param it's okay to provide a UWORD (address value)
|
||||||
if(!(target.isAsmSubroutine && arg.second.type in StringDatatypes && argDt == DataType.UWORD))
|
if(!(target.isAsmSubroutine && arg.second.type in StringDatatypes && argDt == DataType.UWORD))
|
||||||
checkResult.add(ExpressionError("subroutine '${target.name}' argument ${arg.first.index + 1} has invalid type $argDt, expected ${arg.second.type}", position))
|
checkResult.add(ExpressionError("subroutine '${target.name}' argument ${arg.first.index + 1} has invalid type $argDt, expected ${arg.second.type}", position))
|
||||||
@ -956,9 +962,9 @@ internal class AstChecker(private val program: Program,
|
|||||||
if(index!=null && (index<0 || index>=arraysize))
|
if(index!=null && (index<0 || index>=arraysize))
|
||||||
checkResult.add(ExpressionError("array index out of bounds", arrayIndexedExpression.arrayspec.position))
|
checkResult.add(ExpressionError("array index out of bounds", arrayIndexedExpression.arrayspec.position))
|
||||||
} else if(target.datatype in StringDatatypes) {
|
} else if(target.datatype in StringDatatypes) {
|
||||||
if(target.value is ReferenceLiteralValue) {
|
if(target.value is StringLiteralValue) {
|
||||||
// check string lengths for non-memory mapped strings
|
// check string lengths for non-memory mapped strings
|
||||||
val heapId = (target.value as ReferenceLiteralValue).heapId!!
|
val heapId = (target.value as StringLiteralValue).heapId!!
|
||||||
val stringLen = program.heap.get(heapId).str!!.length
|
val stringLen = program.heap.get(heapId).str!!.length
|
||||||
val index = (arrayIndexedExpression.arrayspec.index as? NumericLiteralValue)?.number?.toInt()
|
val index = (arrayIndexedExpression.arrayspec.index as? NumericLiteralValue)?.number?.toInt()
|
||||||
if (index != null && (index < 0 || index >= stringLen))
|
if (index != null && (index < 0 || index >= stringLen))
|
||||||
@ -969,7 +975,7 @@ internal class AstChecker(private val program: Program,
|
|||||||
checkResult.add(SyntaxError("indexing requires a variable to act upon", arrayIndexedExpression.position))
|
checkResult.add(SyntaxError("indexing requires a variable to act upon", arrayIndexedExpression.position))
|
||||||
|
|
||||||
// check index value 0..255
|
// check index value 0..255
|
||||||
val dtx = arrayIndexedExpression.arrayspec.index.inferType(program)
|
val dtx = arrayIndexedExpression.arrayspec.index.inferType(program).typeOrElse(DataType.STRUCT)
|
||||||
if(dtx!= DataType.UBYTE && dtx!= DataType.BYTE)
|
if(dtx!= DataType.UBYTE && dtx!= DataType.BYTE)
|
||||||
checkResult.add(SyntaxError("array indexing is limited to byte size 0..255", arrayIndexedExpression.position))
|
checkResult.add(SyntaxError("array indexing is limited to byte size 0..255", arrayIndexedExpression.position))
|
||||||
|
|
||||||
@ -977,7 +983,7 @@ internal class AstChecker(private val program: Program,
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(whenStatement: WhenStatement) {
|
override fun visit(whenStatement: WhenStatement) {
|
||||||
val conditionType = whenStatement.condition.inferType(program)
|
val conditionType = whenStatement.condition.inferType(program).typeOrElse(DataType.STRUCT)
|
||||||
if(conditionType !in IntegerDatatypes)
|
if(conditionType !in IntegerDatatypes)
|
||||||
checkResult.add(SyntaxError("when condition must be an integer value", whenStatement.position))
|
checkResult.add(SyntaxError("when condition must be an integer value", whenStatement.position))
|
||||||
val choiceValues = whenStatement.choiceValues(program)
|
val choiceValues = whenStatement.choiceValues(program)
|
||||||
@ -996,12 +1002,14 @@ internal class AstChecker(private val program: Program,
|
|||||||
val whenStmt = whenChoice.parent as WhenStatement
|
val whenStmt = whenChoice.parent as WhenStatement
|
||||||
if(whenChoice.values!=null) {
|
if(whenChoice.values!=null) {
|
||||||
val conditionType = whenStmt.condition.inferType(program)
|
val conditionType = whenStmt.condition.inferType(program)
|
||||||
|
if(!conditionType.isKnown)
|
||||||
|
throw FatalAstException("can't determine when choice datatype $whenChoice")
|
||||||
val constvalues = whenChoice.values!!.map { it.constValue(program) }
|
val constvalues = whenChoice.values!!.map { it.constValue(program) }
|
||||||
for(constvalue in constvalues) {
|
for(constvalue in constvalues) {
|
||||||
when {
|
when {
|
||||||
constvalue == null -> checkResult.add(SyntaxError("choice value must be a constant", whenChoice.position))
|
constvalue == null -> checkResult.add(SyntaxError("choice value must be a constant", whenChoice.position))
|
||||||
constvalue.type !in IntegerDatatypes -> checkResult.add(SyntaxError("choice value must be a byte or word", whenChoice.position))
|
constvalue.type !in IntegerDatatypes -> checkResult.add(SyntaxError("choice value must be a byte or word", whenChoice.position))
|
||||||
constvalue.type != conditionType -> checkResult.add(SyntaxError("choice value datatype differs from condition value", whenChoice.position))
|
constvalue.type != conditionType.typeOrElse(DataType.STRUCT) -> checkResult.add(SyntaxError("choice value datatype differs from condition value", whenChoice.position))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -1037,26 +1045,37 @@ internal class AstChecker(private val program: Program,
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun checkValueTypeAndRange(targetDt: DataType, struct: StructDecl?,
|
private fun checkValueTypeAndRangeString(targetDt: DataType, value: StringLiteralValue) : Boolean {
|
||||||
arrayspec: ArrayIndex, value: ReferenceLiteralValue, heap: HeapValues) : Boolean {
|
fun err(msg: String): Boolean {
|
||||||
|
checkResult.add(ExpressionError(msg, value.position))
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return when (targetDt) {
|
||||||
|
in StringDatatypes -> {
|
||||||
|
return if (value.value.length > 255)
|
||||||
|
err("string length must be 0-255")
|
||||||
|
else
|
||||||
|
true
|
||||||
|
}
|
||||||
|
else -> false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun checkValueTypeAndRangeArray(targetDt: DataType, struct: StructDecl?,
|
||||||
|
arrayspec: ArrayIndex, value: ArrayLiteralValue) : Boolean {
|
||||||
fun err(msg: String) : Boolean {
|
fun err(msg: String) : Boolean {
|
||||||
checkResult.add(ExpressionError(msg, value.position))
|
checkResult.add(ExpressionError(msg, value.position))
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
when (targetDt) {
|
when (targetDt) {
|
||||||
in StringDatatypes -> {
|
in StringDatatypes -> return err("string value expected")
|
||||||
if(!value.isString)
|
|
||||||
return err("string value expected")
|
|
||||||
if (value.str!!.length > 255)
|
|
||||||
return err("string length must be 0-255")
|
|
||||||
}
|
|
||||||
DataType.ARRAY_UB, DataType.ARRAY_B -> {
|
DataType.ARRAY_UB, DataType.ARRAY_B -> {
|
||||||
// value may be either a single byte, or a byte arraysize (of all constant values), or a range
|
// value may be either a single byte, or a byte arraysize (of all constant values), or a range
|
||||||
if(value.type==targetDt) {
|
if(value.type==targetDt) {
|
||||||
if(!checkArrayValues(value, targetDt))
|
if(!checkArrayValues(value, targetDt))
|
||||||
return false
|
return false
|
||||||
val arraySpecSize = arrayspec.size()
|
val arraySpecSize = arrayspec.size()
|
||||||
val arraySize = value.array?.size ?: heap.get(value.heapId!!).arraysize
|
val arraySize = value.value.size
|
||||||
if(arraySpecSize!=null && arraySpecSize>0) {
|
if(arraySpecSize!=null && arraySpecSize>0) {
|
||||||
if(arraySpecSize<1 || arraySpecSize>256)
|
if(arraySpecSize<1 || arraySpecSize>256)
|
||||||
return err("byte array length must be 1-256")
|
return err("byte array length must be 1-256")
|
||||||
@ -1078,7 +1097,7 @@ internal class AstChecker(private val program: Program,
|
|||||||
if(!checkArrayValues(value, targetDt))
|
if(!checkArrayValues(value, targetDt))
|
||||||
return false
|
return false
|
||||||
val arraySpecSize = arrayspec.size()
|
val arraySpecSize = arrayspec.size()
|
||||||
val arraySize = value.array?.size ?: heap.get(value.heapId!!).arraysize
|
val arraySize = value.value.size
|
||||||
if(arraySpecSize!=null && arraySpecSize>0) {
|
if(arraySpecSize!=null && arraySpecSize>0) {
|
||||||
if(arraySpecSize<1 || arraySpecSize>128)
|
if(arraySpecSize<1 || arraySpecSize>128)
|
||||||
return err("word array length must be 1-128")
|
return err("word array length must be 1-128")
|
||||||
@ -1099,7 +1118,7 @@ internal class AstChecker(private val program: Program,
|
|||||||
if(value.type==targetDt) {
|
if(value.type==targetDt) {
|
||||||
if(!checkArrayValues(value, targetDt))
|
if(!checkArrayValues(value, targetDt))
|
||||||
return false
|
return false
|
||||||
val arraySize = value.array?.size ?: heap.get(value.heapId!!).doubleArray!!.size
|
val arraySize = value.value.size
|
||||||
val arraySpecSize = arrayspec.size()
|
val arraySpecSize = arrayspec.size()
|
||||||
if(arraySpecSize!=null && arraySpecSize>0) {
|
if(arraySpecSize!=null && arraySpecSize>0) {
|
||||||
if(arraySpecSize < 1 || arraySpecSize>51)
|
if(arraySpecSize < 1 || arraySpecSize>51)
|
||||||
@ -1114,10 +1133,7 @@ internal class AstChecker(private val program: Program,
|
|||||||
return err("invalid float array size, must be 1-51")
|
return err("invalid float array size, must be 1-51")
|
||||||
|
|
||||||
// check if the floating point values are all within range
|
// check if the floating point values are all within range
|
||||||
val doubles = if(value.array!=null)
|
val doubles = value.value.map {it.constValue(program)?.number!!.toDouble()}.toDoubleArray()
|
||||||
value.array.map {it.constValue(program)?.number!!.toDouble()}.toDoubleArray()
|
|
||||||
else
|
|
||||||
heap.get(value.heapId!!).doubleArray!!
|
|
||||||
if(doubles.any { it < FLOAT_MAX_NEGATIVE || it> FLOAT_MAX_POSITIVE })
|
if(doubles.any { it < FLOAT_MAX_NEGATIVE || it> FLOAT_MAX_POSITIVE })
|
||||||
return err("floating point value overflow")
|
return err("floating point value overflow")
|
||||||
return true
|
return true
|
||||||
@ -1126,12 +1142,12 @@ internal class AstChecker(private val program: Program,
|
|||||||
}
|
}
|
||||||
DataType.STRUCT -> {
|
DataType.STRUCT -> {
|
||||||
if(value.type in ArrayDatatypes) {
|
if(value.type in ArrayDatatypes) {
|
||||||
if(value.array!!.size != struct!!.numberOfElements)
|
if(value.value.size != struct!!.numberOfElements)
|
||||||
return err("number of values is not the same as the number of members in the struct")
|
return err("number of values is not the same as the number of members in the struct")
|
||||||
for(elt in value.array.zip(struct.statements)) {
|
for(elt in value.value.zip(struct.statements)) {
|
||||||
val vardecl = elt.second as VarDecl
|
val vardecl = elt.second as VarDecl
|
||||||
val valuetype = elt.first.inferType(program)!!
|
val valuetype = elt.first.inferType(program)
|
||||||
if (!(valuetype isAssignableTo vardecl.datatype)) {
|
if (!valuetype.isKnown || !(valuetype.typeOrElse(DataType.STRUCT) isAssignableTo vardecl.datatype)) {
|
||||||
checkResult.add(ExpressionError("invalid struct member init value type $valuetype, expected ${vardecl.datatype}", elt.first.position))
|
checkResult.add(ExpressionError("invalid struct member init value type $valuetype, expected ${vardecl.datatype}", elt.first.position))
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -1142,7 +1158,6 @@ internal class AstChecker(private val program: Program,
|
|||||||
}
|
}
|
||||||
else -> return false
|
else -> return false
|
||||||
}
|
}
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun checkValueTypeAndRange(targetDt: DataType, value: NumericLiteralValue) : Boolean {
|
private fun checkValueTypeAndRange(targetDt: DataType, value: NumericLiteralValue) : Boolean {
|
||||||
@ -1189,10 +1204,10 @@ internal class AstChecker(private val program: Program,
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun checkArrayValues(value: ReferenceLiteralValue, type: DataType): Boolean {
|
private fun checkArrayValues(value: ArrayLiteralValue, type: DataType): Boolean {
|
||||||
if(value.isArray && value.heapId==null) {
|
if(value.heapId==null) {
|
||||||
// hmm weird, array literal that hasn't been moved to the heap yet?
|
// hmm weird, array literal that hasn't been moved to the heap yet?
|
||||||
val array = value.array!!.map { it.constValue(program)!! }
|
val array = value.value.map { it.constValue(program)!! }
|
||||||
val correct: Boolean
|
val correct: Boolean
|
||||||
when(type) {
|
when(type) {
|
||||||
DataType.ARRAY_UB -> {
|
DataType.ARRAY_UB -> {
|
||||||
|
@ -144,8 +144,7 @@ internal class AstIdentifiersChecker(private val program: Program) : IAstModifyi
|
|||||||
// NOTE:
|
// NOTE:
|
||||||
// - numeric types BYTE and WORD and FLOAT are passed by value;
|
// - numeric types BYTE and WORD and FLOAT are passed by value;
|
||||||
// - strings, arrays, matrices are passed by reference (their 16-bit address is passed as an uword parameter)
|
// - strings, arrays, matrices are passed by reference (their 16-bit address is passed as an uword parameter)
|
||||||
// - do NOT do this is the statement can be transformed into an asm subroutine later!
|
if(subroutine.asmAddress==null) {
|
||||||
if(subroutine.asmAddress==null && !subroutine.canBeAsmSubroutine) {
|
|
||||||
if(subroutine.asmParameterRegisters.isEmpty()) {
|
if(subroutine.asmParameterRegisters.isEmpty()) {
|
||||||
subroutine.parameters
|
subroutine.parameters
|
||||||
.filter { it.name !in namesInSub }
|
.filter { it.name !in namesInSub }
|
||||||
@ -157,6 +156,10 @@ internal class AstIdentifiersChecker(private val program: Program) : IAstModifyi
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(subroutine.isAsmSubroutine && subroutine.statements.any{it !is InlineAssembly}) {
|
||||||
|
checkResult.add(SyntaxError("asmsub can only contain inline assembly (%asm)", subroutine.position))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return super.visit(subroutine)
|
return super.visit(subroutine)
|
||||||
}
|
}
|
||||||
@ -247,75 +250,84 @@ internal class AstIdentifiersChecker(private val program: Program) : IAstModifyi
|
|||||||
return super.visit(returnStmt)
|
return super.visit(returnStmt)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(refLiteral: ReferenceLiteralValue): Expression {
|
override fun visit(arrayLiteral: ArrayLiteralValue): Expression {
|
||||||
val litval = super.visit(refLiteral)
|
val array = super.visit(arrayLiteral)
|
||||||
if(litval is ReferenceLiteralValue) {
|
if(array is ArrayLiteralValue) {
|
||||||
val vardecl = litval.parent as? VarDecl
|
val vardecl = array.parent as? VarDecl
|
||||||
if (litval.isString) {
|
return if (vardecl!=null) {
|
||||||
// intern the string; move it into the heap
|
fixupArrayDatatype(array, vardecl, program.heap)
|
||||||
if (litval.str!!.length !in 1..255)
|
} else {
|
||||||
checkResult.add(ExpressionError("string literal length must be between 1 and 255", litval.position))
|
// fix the datatype of the array (also on the heap) to the 'biggest' datatype in the array
|
||||||
else {
|
// (we don't know the desired datatype here exactly so we guess)
|
||||||
litval.addToHeap(program.heap)
|
val datatype = determineArrayDt(array.value)
|
||||||
}
|
val litval2 = array.cast(datatype)!!
|
||||||
return if(vardecl!=null)
|
litval2.parent = array.parent
|
||||||
litval
|
// finally, replace the literal array by a identifier reference.
|
||||||
else
|
makeIdentifierFromRefLv(litval2)
|
||||||
makeIdentifierFromRefLv(litval) // replace the literal string by a identifier reference.
|
|
||||||
} else if (litval.isArray) {
|
|
||||||
if (vardecl!=null) {
|
|
||||||
return fixupArrayDatatype(litval, vardecl, program.heap)
|
|
||||||
} else {
|
|
||||||
// fix the datatype of the array (also on the heap) to the 'biggest' datatype in the array
|
|
||||||
// (we don't know the desired datatype here exactly so we guess)
|
|
||||||
val datatype = determineArrayDt(litval.array!!) ?: return litval
|
|
||||||
val litval2 = litval.cast(datatype)!!
|
|
||||||
litval2.parent = litval.parent
|
|
||||||
// finally, replace the literal array by a identifier reference.
|
|
||||||
return makeIdentifierFromRefLv(litval2)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return array
|
||||||
return litval
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun determineArrayDt(array: Array<Expression>): DataType? {
|
override fun visit(stringLiteral: StringLiteralValue): Expression {
|
||||||
val datatypesInArray = array.mapNotNull { it.inferType(program) }
|
val string = super.visit(stringLiteral)
|
||||||
if(datatypesInArray.isEmpty())
|
if(string is StringLiteralValue) {
|
||||||
return null
|
val vardecl = string.parent as? VarDecl
|
||||||
if(DataType.FLOAT in datatypesInArray)
|
// intern the string; move it into the heap
|
||||||
return DataType.ARRAY_F
|
if (string.value.length !in 1..255)
|
||||||
if(DataType.WORD in datatypesInArray)
|
checkResult.add(ExpressionError("string literal length must be between 1 and 255", string.position))
|
||||||
return DataType.ARRAY_W
|
else {
|
||||||
if(DataType.UWORD in datatypesInArray)
|
string.addToHeap(program.heap)
|
||||||
return DataType.ARRAY_UW
|
}
|
||||||
if(DataType.BYTE in datatypesInArray)
|
return if (vardecl != null)
|
||||||
return DataType.ARRAY_B
|
string
|
||||||
if(DataType.UBYTE in datatypesInArray)
|
else
|
||||||
return DataType.ARRAY_UB
|
makeIdentifierFromRefLv(string) // replace the literal string by a identifier reference.
|
||||||
return null
|
}
|
||||||
|
return string
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun makeIdentifierFromRefLv(refLiteral: ReferenceLiteralValue): IdentifierReference {
|
private fun determineArrayDt(array: Array<Expression>): DataType {
|
||||||
|
val datatypesInArray = array.map { it.inferType(program) }
|
||||||
|
if(datatypesInArray.isEmpty() || datatypesInArray.any { !it.isKnown })
|
||||||
|
throw IllegalArgumentException("can't determine type of empty array")
|
||||||
|
val dts = datatypesInArray.map { it.typeOrElse(DataType.STRUCT) }
|
||||||
|
return when {
|
||||||
|
DataType.FLOAT in dts -> DataType.ARRAY_F
|
||||||
|
DataType.WORD in dts -> DataType.ARRAY_W
|
||||||
|
DataType.UWORD in dts -> DataType.ARRAY_UW
|
||||||
|
DataType.BYTE in dts -> DataType.ARRAY_B
|
||||||
|
DataType.UBYTE in dts -> DataType.ARRAY_UB
|
||||||
|
else -> throw IllegalArgumentException("can't determine type of array")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun makeIdentifierFromRefLv(array: ArrayLiteralValue): IdentifierReference {
|
||||||
// a referencetype literal value that's not declared as a variable
|
// a referencetype literal value that's not declared as a variable
|
||||||
// we need to introduce an auto-generated variable for this to be able to refer to the value
|
// we need to introduce an auto-generated variable for this to be able to refer to the value
|
||||||
// note: if the var references the same literal value, it is not yet de-duplicated here.
|
// note: if the var references the same literal value, it is not yet de-duplicated here.
|
||||||
refLiteral.addToHeap(program.heap)
|
array.addToHeap(program.heap)
|
||||||
val scope = refLiteral.definingScope()
|
val scope = array.definingScope()
|
||||||
var variable = VarDecl.createAuto(refLiteral, program.heap)
|
val variable = VarDecl.createAuto(array)
|
||||||
val existing = scope.lookup(listOf(variable.name), refLiteral)
|
return replaceWithIdentifier(variable, scope, array.parent)
|
||||||
variable = addVarDecl(scope, variable)
|
|
||||||
// replace the reference literal by a identifier reference
|
|
||||||
val identifier = IdentifierReference(listOf(variable.name), variable.position)
|
|
||||||
identifier.parent = refLiteral.parent
|
|
||||||
return identifier
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(addressOf: AddressOf): Expression {
|
private fun makeIdentifierFromRefLv(string: StringLiteralValue): IdentifierReference {
|
||||||
// register the scoped name of the referenced identifier
|
// a referencetype literal value that's not declared as a variable
|
||||||
val variable= addressOf.identifier.targetVarDecl(program.namespace) ?: return addressOf
|
// we need to introduce an auto-generated variable for this to be able to refer to the value
|
||||||
return super.visit(addressOf)
|
// note: if the var references the same literal value, it is not yet de-duplicated here.
|
||||||
|
string.addToHeap(program.heap)
|
||||||
|
val scope = string.definingScope()
|
||||||
|
val variable = VarDecl.createAuto(string)
|
||||||
|
return replaceWithIdentifier(variable, scope, string.parent)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun replaceWithIdentifier(variable: VarDecl, scope: INameScope, parent: Node): IdentifierReference {
|
||||||
|
val variable1 = addVarDecl(scope, variable)
|
||||||
|
// replace the reference literal by a identifier reference
|
||||||
|
val identifier = IdentifierReference(listOf(variable1.name), variable1.position)
|
||||||
|
identifier.parent = parent
|
||||||
|
return identifier
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(structDecl: StructDecl): Statement {
|
override fun visit(structDecl: StructDecl): Statement {
|
||||||
@ -330,33 +342,30 @@ internal class AstIdentifiersChecker(private val program: Program) : IAstModifyi
|
|||||||
|
|
||||||
override fun visit(expr: BinaryExpression): Expression {
|
override fun visit(expr: BinaryExpression): Expression {
|
||||||
return when {
|
return when {
|
||||||
expr.left is ReferenceLiteralValue ->
|
expr.left is StringLiteralValue ->
|
||||||
processBinaryExprWithReferenceVal(expr.left as ReferenceLiteralValue, expr.right, expr)
|
processBinaryExprWithString(expr.left as StringLiteralValue, expr.right, expr)
|
||||||
expr.right is ReferenceLiteralValue ->
|
expr.right is StringLiteralValue ->
|
||||||
processBinaryExprWithReferenceVal(expr.right as ReferenceLiteralValue, expr.left, expr)
|
processBinaryExprWithString(expr.right as StringLiteralValue, expr.left, expr)
|
||||||
else -> super.visit(expr)
|
else -> super.visit(expr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun processBinaryExprWithReferenceVal(refLv: ReferenceLiteralValue, operand: Expression, expr: BinaryExpression): Expression {
|
private fun processBinaryExprWithString(string: StringLiteralValue, operand: Expression, expr: BinaryExpression): Expression {
|
||||||
// expressions on strings or arrays
|
val constvalue = operand.constValue(program)
|
||||||
if(refLv.isString) {
|
if(constvalue!=null) {
|
||||||
val constvalue = operand.constValue(program)
|
if (expr.operator == "*") {
|
||||||
if(constvalue!=null) {
|
// repeat a string a number of times
|
||||||
if (expr.operator == "*") {
|
val idt = string.inferType(program)
|
||||||
// repeat a string a number of times
|
return StringLiteralValue(idt.typeOrElse(DataType.STR),
|
||||||
return ReferenceLiteralValue(refLv.inferType(program),
|
string.value.repeat(constvalue.number.toInt()), null, expr.position)
|
||||||
refLv.str!!.repeat(constvalue.number.toInt()), null, null, expr.position)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(expr.operator == "+" && operand is ReferenceLiteralValue) {
|
|
||||||
if (operand.isString) {
|
|
||||||
// concatenate two strings
|
|
||||||
return ReferenceLiteralValue(refLv.inferType(program),
|
|
||||||
"${refLv.str}${operand.str}", null, null, expr.position)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if(expr.operator == "+" && operand is StringLiteralValue) {
|
||||||
|
// concatenate two strings
|
||||||
|
val idt = string.inferType(program)
|
||||||
|
return StringLiteralValue(idt.typeOrElse(DataType.STR),
|
||||||
|
"${string.value}${operand.value}", null, expr.position)
|
||||||
|
}
|
||||||
return expr
|
return expr
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -375,7 +384,7 @@ internal class AstIdentifiersChecker(private val program: Program) : IAstModifyi
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun fixupArrayDatatype(array: ReferenceLiteralValue, vardecl: VarDecl, heap: HeapValues): ReferenceLiteralValue {
|
internal fun fixupArrayDatatype(array: ArrayLiteralValue, vardecl: VarDecl, heap: HeapValues): ArrayLiteralValue {
|
||||||
if(array.heapId!=null) {
|
if(array.heapId!=null) {
|
||||||
val arrayDt = array.type
|
val arrayDt = array.type
|
||||||
if(arrayDt!=vardecl.datatype) {
|
if(arrayDt!=vardecl.datatype) {
|
||||||
@ -386,11 +395,11 @@ internal fun fixupArrayDatatype(array: ReferenceLiteralValue, vardecl: VarDecl,
|
|||||||
} catch(x: ExpressionError) {
|
} catch(x: ExpressionError) {
|
||||||
// couldn't cast permanently.
|
// couldn't cast permanently.
|
||||||
// instead, simply adjust the array type and trust the AstChecker to report the exact error
|
// instead, simply adjust the array type and trust the AstChecker to report the exact error
|
||||||
ReferenceLiteralValue(vardecl.datatype, null, array.array, array.heapId, array.position)
|
ArrayLiteralValue(vardecl.datatype, array.value, array.heapId, array.position)
|
||||||
}
|
}
|
||||||
vardecl.value = litval2
|
vardecl.value = litval2
|
||||||
litval2.linkParents(vardecl)
|
litval2.linkParents(vardecl)
|
||||||
litval2.addToHeap(heap) // TODO is the previous array discarded from the resulting asm code?
|
litval2.addToHeap(heap)
|
||||||
return litval2
|
return litval2
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -110,14 +110,16 @@ interface IAstModifyingVisitor {
|
|||||||
return literalValue
|
return literalValue
|
||||||
}
|
}
|
||||||
|
|
||||||
fun visit(refLiteral: ReferenceLiteralValue): Expression {
|
fun visit(stringLiteral: StringLiteralValue): Expression {
|
||||||
if(refLiteral.array!=null) {
|
return stringLiteral
|
||||||
for(av in refLiteral.array.withIndex()) {
|
}
|
||||||
val newvalue = av.value.accept(this)
|
|
||||||
refLiteral.array[av.index] = newvalue
|
fun visit(arrayLiteral: ArrayLiteralValue): Expression {
|
||||||
}
|
for(av in arrayLiteral.value.withIndex()) {
|
||||||
|
val newvalue = av.value.accept(this)
|
||||||
|
arrayLiteral.value[av.index] = newvalue
|
||||||
}
|
}
|
||||||
return refLiteral
|
return arrayLiteral
|
||||||
}
|
}
|
||||||
|
|
||||||
fun visit(assignment: Assignment): Statement {
|
fun visit(assignment: Assignment): Statement {
|
||||||
|
@ -79,8 +79,11 @@ interface IAstVisitor {
|
|||||||
fun visit(numLiteral: NumericLiteralValue) {
|
fun visit(numLiteral: NumericLiteralValue) {
|
||||||
}
|
}
|
||||||
|
|
||||||
fun visit(refLiteral: ReferenceLiteralValue) {
|
fun visit(string: StringLiteralValue) {
|
||||||
refLiteral.array?.let { it.forEach { v->v.accept(this) }}
|
}
|
||||||
|
|
||||||
|
fun visit(array: ArrayLiteralValue) {
|
||||||
|
array.value.forEach { v->v.accept(this) }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun visit(assignment: Assignment) {
|
fun visit(assignment: Assignment) {
|
||||||
|
@ -1,16 +1,16 @@
|
|||||||
package prog8.ast.processing
|
package prog8.ast.processing
|
||||||
|
|
||||||
import prog8.ast.*
|
import prog8.ast.Module
|
||||||
|
import prog8.ast.Program
|
||||||
import prog8.ast.base.DataType
|
import prog8.ast.base.DataType
|
||||||
import prog8.ast.base.FatalAstException
|
import prog8.ast.base.FatalAstException
|
||||||
import prog8.ast.base.initvarsSubName
|
import prog8.ast.base.initvarsSubName
|
||||||
import prog8.ast.base.printWarning
|
|
||||||
import prog8.ast.expressions.*
|
import prog8.ast.expressions.*
|
||||||
|
import prog8.ast.mangledStructMemberName
|
||||||
import prog8.ast.statements.*
|
import prog8.ast.statements.*
|
||||||
import prog8.functions.BuiltinFunctions
|
|
||||||
|
|
||||||
|
|
||||||
fun flattenStructAssignmentFromIdentifier(structAssignment: Assignment, program: Program): List<Assignment> {
|
private fun flattenStructAssignmentFromIdentifier(structAssignment: Assignment, program: Program): List<Assignment> {
|
||||||
val identifier = structAssignment.target.identifier!!
|
val identifier = structAssignment.target.identifier!!
|
||||||
val identifierName = identifier.nameInSource.single()
|
val identifierName = identifier.nameInSource.single()
|
||||||
val targetVar = identifier.targetVarDecl(program.namespace)!!
|
val targetVar = identifier.targetVarDecl(program.namespace)!!
|
||||||
@ -61,9 +61,6 @@ internal class StatementReorderer(private val program: Program): IAstModifyingVi
|
|||||||
// - the 'start' subroutine in the 'main' block will be moved to the top immediately following the directives.
|
// - the 'start' subroutine in the 'main' block will be moved to the top immediately following the directives.
|
||||||
// - all other subroutines will be moved to the end of their block.
|
// - all other subroutines will be moved to the end of their block.
|
||||||
// - sorts the choices in when statement.
|
// - sorts the choices in when statement.
|
||||||
//
|
|
||||||
// Also, makes sure any value assignments get the proper type casts if needed to cast them into the target variable's type.
|
|
||||||
// (this includes function call arguments)
|
|
||||||
|
|
||||||
private val directivesToMove = setOf("%output", "%launcher", "%zeropage", "%zpreserved", "%address", "%option")
|
private val directivesToMove = setOf("%output", "%launcher", "%zeropage", "%zpreserved", "%address", "%option")
|
||||||
|
|
||||||
@ -186,64 +183,34 @@ internal class StatementReorderer(private val program: Program): IAstModifyingVi
|
|||||||
return subroutine
|
return subroutine
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(expr: BinaryExpression): Expression {
|
|
||||||
val expr2 = super.visit(expr)
|
|
||||||
if(expr2 !is BinaryExpression)
|
|
||||||
return expr2
|
|
||||||
val leftDt = expr2.left.inferType(program)
|
|
||||||
val rightDt = expr2.right.inferType(program)
|
|
||||||
if(leftDt!=null && rightDt!=null && leftDt!=rightDt) {
|
|
||||||
// determine common datatype and add typecast as required to make left and right equal types
|
|
||||||
val (commonDt, toFix) = BinaryExpression.commonDatatype(leftDt, rightDt, expr2.left, expr2.right)
|
|
||||||
if(toFix!=null) {
|
|
||||||
when {
|
|
||||||
toFix===expr2.left -> {
|
|
||||||
expr2.left = TypecastExpression(expr2.left, commonDt, true, expr2.left.position)
|
|
||||||
expr2.left.linkParents(expr2)
|
|
||||||
}
|
|
||||||
toFix===expr2.right -> {
|
|
||||||
expr2.right = TypecastExpression(expr2.right, commonDt, true, expr2.right.position)
|
|
||||||
expr2.right.linkParents(expr2)
|
|
||||||
}
|
|
||||||
else -> throw FatalAstException("confused binary expression side")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return expr2
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun visit(assignment: Assignment): Statement {
|
override fun visit(assignment: Assignment): Statement {
|
||||||
val assg = super.visit(assignment)
|
val assg = super.visit(assignment)
|
||||||
if(assg !is Assignment)
|
if(assg !is Assignment)
|
||||||
return assg
|
return assg
|
||||||
|
|
||||||
// see if a typecast is needed to convert the value's type into the proper target type
|
// see if a typecast is needed to convert the value's type into the proper target type
|
||||||
val valuetype = assg.value.inferType(program)
|
val valueItype = assg.value.inferType(program)
|
||||||
val targettype = assg.target.inferType(program, assg)
|
val targetItype = assg.target.inferType(program, assg)
|
||||||
if(targettype!=null && valuetype!=null) {
|
|
||||||
if(valuetype!=targettype) {
|
if(targetItype.isKnown && valueItype.isKnown) {
|
||||||
if (valuetype isAssignableTo targettype) {
|
val targettype = targetItype.typeOrElse(DataType.STRUCT)
|
||||||
assg.value = TypecastExpression(assg.value, targettype, true, assg.value.position)
|
val valuetype = valueItype.typeOrElse(DataType.STRUCT)
|
||||||
assg.value.linkParents(assg)
|
|
||||||
|
// struct assignments will be flattened (if it's not a struct literal)
|
||||||
|
if (valuetype == DataType.STRUCT && targettype == DataType.STRUCT) {
|
||||||
|
if (assg.value is StructLiteralValue)
|
||||||
|
return assg // do NOT flatten it at this point!! (the compiler will take care if it, later, if needed)
|
||||||
|
|
||||||
|
val assignments = flattenStructAssignmentFromIdentifier(assg, program) // 'structvar1 = structvar2'
|
||||||
|
return if (assignments.isEmpty()) {
|
||||||
|
// something went wrong (probably incompatible struct types)
|
||||||
|
// we'll get an error later from the AstChecker
|
||||||
|
assg
|
||||||
|
} else {
|
||||||
|
val scope = AnonymousScope(assignments.toMutableList(), assg.position)
|
||||||
|
scope.linkParents(assg.parent)
|
||||||
|
scope
|
||||||
}
|
}
|
||||||
// if they're not assignable, we'll get a proper error later from the AstChecker
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// struct assignments will be flattened (if it's not a struct literal)
|
|
||||||
if(valuetype==DataType.STRUCT && targettype==DataType.STRUCT) {
|
|
||||||
if(assg.value is StructLiteralValue)
|
|
||||||
return assg // do NOT flatten it at this point!! (the compiler will take care if it, later, if needed)
|
|
||||||
|
|
||||||
val assignments = flattenStructAssignmentFromIdentifier(assg, program) // 'structvar1 = structvar2'
|
|
||||||
return if(assignments.isEmpty()) {
|
|
||||||
// something went wrong (probably incompatible struct types)
|
|
||||||
// we'll get an error later from the AstChecker
|
|
||||||
assg
|
|
||||||
} else {
|
|
||||||
val scope = AnonymousScope(assignments.toMutableList(), assg.position)
|
|
||||||
scope.linkParents(assg.parent)
|
|
||||||
scope
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -267,134 +234,4 @@ internal class StatementReorderer(private val program: Program): IAstModifyingVi
|
|||||||
|
|
||||||
return assg
|
return assg
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(functionCallStatement: FunctionCallStatement): Statement {
|
|
||||||
checkFunctionCallArguments(functionCallStatement, functionCallStatement.definingScope())
|
|
||||||
return super.visit(functionCallStatement)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun visit(functionCall: FunctionCall): Expression {
|
|
||||||
checkFunctionCallArguments(functionCall, functionCall.definingScope())
|
|
||||||
return super.visit(functionCall)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun checkFunctionCallArguments(call: IFunctionCall, scope: INameScope) {
|
|
||||||
// see if a typecast is needed to convert the arguments into the required parameter's type
|
|
||||||
when(val sub = call.target.targetStatement(scope)) {
|
|
||||||
is Subroutine -> {
|
|
||||||
for(arg in sub.parameters.zip(call.arglist.withIndex())) {
|
|
||||||
val argtype = arg.second.value.inferType(program)
|
|
||||||
if(argtype!=null) {
|
|
||||||
val requiredType = arg.first.type
|
|
||||||
if (requiredType != argtype) {
|
|
||||||
if (argtype isAssignableTo requiredType) {
|
|
||||||
val typecasted = TypecastExpression(arg.second.value, requiredType, true, arg.second.value.position)
|
|
||||||
typecasted.linkParents(arg.second.value.parent)
|
|
||||||
call.arglist[arg.second.index] = typecasted
|
|
||||||
}
|
|
||||||
// if they're not assignable, we'll get a proper error later from the AstChecker
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
is BuiltinFunctionStatementPlaceholder -> {
|
|
||||||
val func = BuiltinFunctions.getValue(sub.name)
|
|
||||||
if(func.pure) {
|
|
||||||
// non-pure functions don't get automatic typecasts because sometimes they act directly on their parameters
|
|
||||||
for (arg in func.parameters.zip(call.arglist.withIndex())) {
|
|
||||||
val argtype = arg.second.value.inferType(program)
|
|
||||||
if (argtype != null) {
|
|
||||||
if (arg.first.possibleDatatypes.any { argtype == it })
|
|
||||||
continue
|
|
||||||
for (possibleType in arg.first.possibleDatatypes) {
|
|
||||||
if (argtype isAssignableTo possibleType) {
|
|
||||||
val typecasted = TypecastExpression(arg.second.value, possibleType, true, arg.second.value.position)
|
|
||||||
typecasted.linkParents(arg.second.value.parent)
|
|
||||||
call.arglist[arg.second.index] = typecasted
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
null -> {}
|
|
||||||
else -> throw FatalAstException("call to something weird $sub ${call.target}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun visit(typecast: TypecastExpression): Expression {
|
|
||||||
// warn about any implicit type casts to Float, because that may not be intended
|
|
||||||
if(typecast.implicit && typecast.type in setOf(DataType.FLOAT, DataType.ARRAY_F)) {
|
|
||||||
printWarning("byte or word value implicitly converted to float. Suggestion: use explicit cast as float, a float number, or revert to integer arithmetic", typecast.position)
|
|
||||||
}
|
|
||||||
return super.visit(typecast)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun visit(memread: DirectMemoryRead): Expression {
|
|
||||||
// make sure the memory address is an uword
|
|
||||||
val dt = memread.addressExpression.inferType(program)
|
|
||||||
if(dt!=DataType.UWORD) {
|
|
||||||
val literaladdr = memread.addressExpression as? NumericLiteralValue
|
|
||||||
if(literaladdr!=null) {
|
|
||||||
memread.addressExpression = literaladdr.cast(DataType.UWORD)
|
|
||||||
} else {
|
|
||||||
memread.addressExpression = TypecastExpression(memread.addressExpression, DataType.UWORD, true, memread.addressExpression.position)
|
|
||||||
memread.addressExpression.parent = memread
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return super.visit(memread)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun visit(memwrite: DirectMemoryWrite) {
|
|
||||||
val dt = memwrite.addressExpression.inferType(program)
|
|
||||||
if(dt!=DataType.UWORD) {
|
|
||||||
val literaladdr = memwrite.addressExpression as? NumericLiteralValue
|
|
||||||
if(literaladdr!=null) {
|
|
||||||
memwrite.addressExpression = literaladdr.cast(DataType.UWORD)
|
|
||||||
} else {
|
|
||||||
memwrite.addressExpression = TypecastExpression(memwrite.addressExpression, DataType.UWORD, true, memwrite.addressExpression.position)
|
|
||||||
memwrite.addressExpression.parent = memwrite
|
|
||||||
}
|
|
||||||
}
|
|
||||||
super.visit(memwrite)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun visit(structLv: StructLiteralValue): Expression {
|
|
||||||
val litval = super.visit(structLv)
|
|
||||||
if(litval !is StructLiteralValue)
|
|
||||||
return litval
|
|
||||||
|
|
||||||
val decl = litval.parent as? VarDecl
|
|
||||||
if(decl != null) {
|
|
||||||
val struct = decl.struct
|
|
||||||
if(struct != null) {
|
|
||||||
addTypecastsIfNeeded(litval, struct)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
val assign = litval.parent as? Assignment
|
|
||||||
if (assign != null) {
|
|
||||||
val decl2 = assign.target.identifier?.targetVarDecl(program.namespace)
|
|
||||||
if(decl2 != null) {
|
|
||||||
val struct = decl2.struct
|
|
||||||
if(struct != null) {
|
|
||||||
addTypecastsIfNeeded(litval, struct)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return litval
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun addTypecastsIfNeeded(structLv: StructLiteralValue, struct: StructDecl) {
|
|
||||||
structLv.values = struct.statements.zip(structLv.values).map {
|
|
||||||
val memberDt = (it.first as VarDecl).datatype
|
|
||||||
val valueDt = it.second.inferType(program)
|
|
||||||
if (valueDt != memberDt)
|
|
||||||
TypecastExpression(it.second, memberDt, true, it.second.position)
|
|
||||||
else
|
|
||||||
it.second
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
198
compiler/src/prog8/ast/processing/TypecastsAdder.kt
Normal file
198
compiler/src/prog8/ast/processing/TypecastsAdder.kt
Normal file
@ -0,0 +1,198 @@
|
|||||||
|
package prog8.ast.processing
|
||||||
|
|
||||||
|
import prog8.ast.IFunctionCall
|
||||||
|
import prog8.ast.INameScope
|
||||||
|
import prog8.ast.Program
|
||||||
|
import prog8.ast.base.DataType
|
||||||
|
import prog8.ast.base.FatalAstException
|
||||||
|
import prog8.ast.base.printWarning
|
||||||
|
import prog8.ast.expressions.*
|
||||||
|
import prog8.ast.statements.*
|
||||||
|
import prog8.functions.BuiltinFunctions
|
||||||
|
|
||||||
|
|
||||||
|
internal class TypecastsAdder(private val program: Program): IAstModifyingVisitor {
|
||||||
|
// Make sure any value assignments get the proper type casts if needed to cast them into the target variable's type.
|
||||||
|
// (this includes function call arguments)
|
||||||
|
|
||||||
|
override fun visit(expr: BinaryExpression): Expression {
|
||||||
|
val expr2 = super.visit(expr)
|
||||||
|
if(expr2 !is BinaryExpression)
|
||||||
|
return expr2
|
||||||
|
val leftDt = expr2.left.inferType(program)
|
||||||
|
val rightDt = expr2.right.inferType(program)
|
||||||
|
if(leftDt.isKnown && rightDt.isKnown && leftDt!=rightDt) {
|
||||||
|
// determine common datatype and add typecast as required to make left and right equal types
|
||||||
|
val (commonDt, toFix) = BinaryExpression.commonDatatype(leftDt.typeOrElse(DataType.STRUCT), rightDt.typeOrElse(DataType.STRUCT), expr2.left, expr2.right)
|
||||||
|
if(toFix!=null) {
|
||||||
|
when {
|
||||||
|
toFix===expr2.left -> {
|
||||||
|
expr2.left = TypecastExpression(expr2.left, commonDt, true, expr2.left.position)
|
||||||
|
expr2.left.linkParents(expr2)
|
||||||
|
}
|
||||||
|
toFix===expr2.right -> {
|
||||||
|
expr2.right = TypecastExpression(expr2.right, commonDt, true, expr2.right.position)
|
||||||
|
expr2.right.linkParents(expr2)
|
||||||
|
}
|
||||||
|
else -> throw FatalAstException("confused binary expression side")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return expr2
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visit(assignment: Assignment): Statement {
|
||||||
|
val assg = super.visit(assignment)
|
||||||
|
if(assg !is Assignment)
|
||||||
|
return assg
|
||||||
|
|
||||||
|
// see if a typecast is needed to convert the value's type into the proper target type
|
||||||
|
val valueItype = assg.value.inferType(program)
|
||||||
|
val targetItype = assg.target.inferType(program, assg)
|
||||||
|
|
||||||
|
if(targetItype.isKnown && valueItype.isKnown) {
|
||||||
|
val targettype = targetItype.typeOrElse(DataType.STRUCT)
|
||||||
|
val valuetype = valueItype.typeOrElse(DataType.STRUCT)
|
||||||
|
if (valuetype != targettype) {
|
||||||
|
if (valuetype isAssignableTo targettype) {
|
||||||
|
assg.value = TypecastExpression(assg.value, targettype, true, assg.value.position)
|
||||||
|
assg.value.linkParents(assg)
|
||||||
|
}
|
||||||
|
// if they're not assignable, we'll get a proper error later from the AstChecker
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return assg
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visit(functionCallStatement: FunctionCallStatement): Statement {
|
||||||
|
checkFunctionCallArguments(functionCallStatement, functionCallStatement.definingScope())
|
||||||
|
return super.visit(functionCallStatement)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visit(functionCall: FunctionCall): Expression {
|
||||||
|
checkFunctionCallArguments(functionCall, functionCall.definingScope())
|
||||||
|
return super.visit(functionCall)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun checkFunctionCallArguments(call: IFunctionCall, scope: INameScope) {
|
||||||
|
// see if a typecast is needed to convert the arguments into the required parameter's type
|
||||||
|
when(val sub = call.target.targetStatement(scope)) {
|
||||||
|
is Subroutine -> {
|
||||||
|
for(arg in sub.parameters.zip(call.arglist.withIndex())) {
|
||||||
|
val argItype = arg.second.value.inferType(program)
|
||||||
|
if(argItype.isKnown) {
|
||||||
|
val argtype = argItype.typeOrElse(DataType.STRUCT)
|
||||||
|
val requiredType = arg.first.type
|
||||||
|
if (requiredType != argtype) {
|
||||||
|
if (argtype isAssignableTo requiredType) {
|
||||||
|
val typecasted = TypecastExpression(arg.second.value, requiredType, true, arg.second.value.position)
|
||||||
|
typecasted.linkParents(arg.second.value.parent)
|
||||||
|
call.arglist[arg.second.index] = typecasted
|
||||||
|
}
|
||||||
|
// if they're not assignable, we'll get a proper error later from the AstChecker
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is BuiltinFunctionStatementPlaceholder -> {
|
||||||
|
val func = BuiltinFunctions.getValue(sub.name)
|
||||||
|
if(func.pure) {
|
||||||
|
// non-pure functions don't get automatic typecasts because sometimes they act directly on their parameters
|
||||||
|
for (arg in func.parameters.zip(call.arglist.withIndex())) {
|
||||||
|
val argItype = arg.second.value.inferType(program)
|
||||||
|
if (argItype.isKnown) {
|
||||||
|
val argtype = argItype.typeOrElse(DataType.STRUCT)
|
||||||
|
if (arg.first.possibleDatatypes.any { argtype == it })
|
||||||
|
continue
|
||||||
|
for (possibleType in arg.first.possibleDatatypes) {
|
||||||
|
if (argtype isAssignableTo possibleType) {
|
||||||
|
val typecasted = TypecastExpression(arg.second.value, possibleType, true, arg.second.value.position)
|
||||||
|
typecasted.linkParents(arg.second.value.parent)
|
||||||
|
call.arglist[arg.second.index] = typecasted
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
null -> {}
|
||||||
|
else -> throw FatalAstException("call to something weird $sub ${call.target}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visit(typecast: TypecastExpression): Expression {
|
||||||
|
// warn about any implicit type casts to Float, because that may not be intended
|
||||||
|
if(typecast.implicit && typecast.type in setOf(DataType.FLOAT, DataType.ARRAY_F)) {
|
||||||
|
printWarning("byte or word value implicitly converted to float. Suggestion: use explicit cast as float, a float number, or revert to integer arithmetic", typecast.position)
|
||||||
|
}
|
||||||
|
return super.visit(typecast)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visit(memread: DirectMemoryRead): Expression {
|
||||||
|
// make sure the memory address is an uword
|
||||||
|
val dt = memread.addressExpression.inferType(program)
|
||||||
|
if(dt.isKnown && dt.typeOrElse(DataType.UWORD)!=DataType.UWORD) {
|
||||||
|
val literaladdr = memread.addressExpression as? NumericLiteralValue
|
||||||
|
if(literaladdr!=null) {
|
||||||
|
memread.addressExpression = literaladdr.cast(DataType.UWORD)
|
||||||
|
} else {
|
||||||
|
memread.addressExpression = TypecastExpression(memread.addressExpression, DataType.UWORD, true, memread.addressExpression.position)
|
||||||
|
memread.addressExpression.parent = memread
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return super.visit(memread)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visit(memwrite: DirectMemoryWrite) {
|
||||||
|
val dt = memwrite.addressExpression.inferType(program)
|
||||||
|
if(dt.isKnown && dt.typeOrElse(DataType.UWORD)!=DataType.UWORD) {
|
||||||
|
val literaladdr = memwrite.addressExpression as? NumericLiteralValue
|
||||||
|
if(literaladdr!=null) {
|
||||||
|
memwrite.addressExpression = literaladdr.cast(DataType.UWORD)
|
||||||
|
} else {
|
||||||
|
memwrite.addressExpression = TypecastExpression(memwrite.addressExpression, DataType.UWORD, true, memwrite.addressExpression.position)
|
||||||
|
memwrite.addressExpression.parent = memwrite
|
||||||
|
}
|
||||||
|
}
|
||||||
|
super.visit(memwrite)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visit(structLv: StructLiteralValue): Expression {
|
||||||
|
val litval = super.visit(structLv)
|
||||||
|
if(litval !is StructLiteralValue)
|
||||||
|
return litval
|
||||||
|
|
||||||
|
val decl = litval.parent as? VarDecl
|
||||||
|
if(decl != null) {
|
||||||
|
val struct = decl.struct
|
||||||
|
if(struct != null) {
|
||||||
|
addTypecastsIfNeeded(litval, struct)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
val assign = litval.parent as? Assignment
|
||||||
|
if (assign != null) {
|
||||||
|
val decl2 = assign.target.identifier?.targetVarDecl(program.namespace)
|
||||||
|
if(decl2 != null) {
|
||||||
|
val struct = decl2.struct
|
||||||
|
if(struct != null) {
|
||||||
|
addTypecastsIfNeeded(litval, struct)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return litval
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun addTypecastsIfNeeded(structLv: StructLiteralValue, struct: StructDecl) {
|
||||||
|
structLv.values = struct.statements.zip(structLv.values).map {
|
||||||
|
val memberDt = (it.first as VarDecl).datatype
|
||||||
|
val valueDt = it.second.inferType(program)
|
||||||
|
if (valueDt.typeOrElse(memberDt) != memberDt)
|
||||||
|
TypecastExpression(it.second, memberDt, true, it.second.position)
|
||||||
|
else
|
||||||
|
it.second
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -42,7 +42,7 @@ internal class VarInitValueAndAddressOfCreator(private val program: Program): IA
|
|||||||
if(decl.isArray && decl.value==null) {
|
if(decl.isArray && decl.value==null) {
|
||||||
// array datatype without initialization value, add list of zeros
|
// array datatype without initialization value, add list of zeros
|
||||||
val arraysize = decl.arraysize!!.size()!!
|
val arraysize = decl.arraysize!!.size()!!
|
||||||
val array = ReferenceLiteralValue(decl.datatype, null,
|
val array = ArrayLiteralValue(decl.datatype,
|
||||||
Array(arraysize) { NumericLiteralValue.optimalInteger(0, decl.position) },
|
Array(arraysize) { NumericLiteralValue.optimalInteger(0, decl.position) },
|
||||||
null, decl.position)
|
null, decl.position)
|
||||||
array.addToHeap(program.heap)
|
array.addToHeap(program.heap)
|
||||||
@ -107,7 +107,7 @@ internal class VarInitValueAndAddressOfCreator(private val program: Program): IA
|
|||||||
if(argparam.second is AddressOf)
|
if(argparam.second is AddressOf)
|
||||||
continue
|
continue
|
||||||
val idref = argparam.second as? IdentifierReference
|
val idref = argparam.second as? IdentifierReference
|
||||||
val strvalue = argparam.second as? ReferenceLiteralValue
|
val strvalue = argparam.second as? StringLiteralValue
|
||||||
if(idref!=null) {
|
if(idref!=null) {
|
||||||
val variable = idref.targetVarDecl(program.namespace)
|
val variable = idref.targetVarDecl(program.namespace)
|
||||||
if(variable!=null && (variable.datatype in StringDatatypes || variable.datatype in ArrayDatatypes)) {
|
if(variable!=null && (variable.datatype in StringDatatypes || variable.datatype in ArrayDatatypes)) {
|
||||||
@ -117,16 +117,14 @@ internal class VarInitValueAndAddressOfCreator(private val program: Program): IA
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if(strvalue!=null) {
|
else if(strvalue!=null) {
|
||||||
if(strvalue.isString) {
|
// add a vardecl so that the autovar can be resolved in later lookups
|
||||||
// add a vardecl so that the autovar can be resolved in later lookups
|
val variable = VarDecl.createAuto(strvalue)
|
||||||
val variable = VarDecl.createAuto(strvalue, program.heap)
|
addVarDecl(strvalue.definingScope(), variable)
|
||||||
addVarDecl(strvalue.definingScope(), variable)
|
// replace the argument with &autovar
|
||||||
// replace the argument with &autovar
|
val autoHeapvarRef = IdentifierReference(listOf(variable.name), strvalue.position)
|
||||||
val autoHeapvarRef = IdentifierReference(listOf(variable.name), strvalue.position)
|
val pointerExpr = AddressOf(autoHeapvarRef, strvalue.position)
|
||||||
val pointerExpr = AddressOf(autoHeapvarRef, strvalue.position)
|
pointerExpr.linkParents(arglist[argparam.first.index].parent)
|
||||||
pointerExpr.linkParents(arglist[argparam.first.index].parent)
|
arglist[argparam.first.index] = pointerExpr
|
||||||
arglist[argparam.first.index] = pointerExpr
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -137,7 +135,7 @@ internal class VarInitValueAndAddressOfCreator(private val program: Program): IA
|
|||||||
for(arg in args.withIndex().zip(signature.parameters)) {
|
for(arg in args.withIndex().zip(signature.parameters)) {
|
||||||
val argvalue = arg.first.value
|
val argvalue = arg.first.value
|
||||||
val argDt = argvalue.inferType(program)
|
val argDt = argvalue.inferType(program)
|
||||||
if(argDt in PassByReferenceDatatypes && DataType.UWORD in arg.second.possibleDatatypes) {
|
if(argDt.typeOrElse(DataType.UBYTE) in PassByReferenceDatatypes && DataType.UWORD in arg.second.possibleDatatypes) {
|
||||||
if(argvalue !is IdentifierReference)
|
if(argvalue !is IdentifierReference)
|
||||||
throw CompilerException("pass-by-reference parameter isn't an identifier? $argvalue")
|
throw CompilerException("pass-by-reference parameter isn't an identifier? $argvalue")
|
||||||
val addrOf = AddressOf(argvalue, argvalue.position)
|
val addrOf = AddressOf(argvalue, argvalue.position)
|
||||||
|
@ -5,7 +5,6 @@ import prog8.ast.base.*
|
|||||||
import prog8.ast.expressions.*
|
import prog8.ast.expressions.*
|
||||||
import prog8.ast.processing.IAstModifyingVisitor
|
import prog8.ast.processing.IAstModifyingVisitor
|
||||||
import prog8.ast.processing.IAstVisitor
|
import prog8.ast.processing.IAstVisitor
|
||||||
import prog8.compiler.HeapValues
|
|
||||||
|
|
||||||
|
|
||||||
sealed class Statement : Node {
|
sealed class Statement : Node {
|
||||||
@ -111,8 +110,6 @@ data class Label(val name: String, override val position: Position) : Statement(
|
|||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
return "Label(name=$name, pos=$position)"
|
return "Label(name=$name, pos=$position)"
|
||||||
}
|
}
|
||||||
|
|
||||||
val scopedname: String by lazy { makeScopedName(name) }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
open class Return(var value: Expression?, override val position: Position) : Statement() {
|
open class Return(var value: Expression?, override val position: Position) : Statement() {
|
||||||
@ -197,20 +194,23 @@ class VarDecl(val type: VarDeclType,
|
|||||||
companion object {
|
companion object {
|
||||||
private var autoHeapValueSequenceNumber = 0
|
private var autoHeapValueSequenceNumber = 0
|
||||||
|
|
||||||
fun createAuto(refLv: ReferenceLiteralValue, heap: HeapValues): VarDecl {
|
fun createAuto(string: StringLiteralValue): VarDecl {
|
||||||
if(refLv.heapId==null)
|
if(string.heapId==null)
|
||||||
throw FatalAstException("can only create autovar for a ref lv that has a heapid $refLv")
|
throw FatalAstException("can only create autovar for a string that has a heapid $string")
|
||||||
|
val autoVarName = "auto_heap_value_${++autoHeapValueSequenceNumber}"
|
||||||
|
return VarDecl(VarDeclType.VAR, string.type, ZeropageWish.NOT_IN_ZEROPAGE, null, autoVarName, null, string,
|
||||||
|
isArray = false, autogeneratedDontRemove = true, position = string.position)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createAuto(array: ArrayLiteralValue): VarDecl {
|
||||||
|
if(array.heapId==null)
|
||||||
|
throw FatalAstException("can only create autovar for an array that has a heapid $array")
|
||||||
|
|
||||||
val autoVarName = "auto_heap_value_${++autoHeapValueSequenceNumber}"
|
val autoVarName = "auto_heap_value_${++autoHeapValueSequenceNumber}"
|
||||||
return if(refLv.isArray) {
|
val declaredType = ArrayElementTypes.getValue(array.type)
|
||||||
val declaredType = ArrayElementTypes.getValue(refLv.type)
|
val arraysize = ArrayIndex.forArray(array)
|
||||||
val arraysize = ArrayIndex.forArray(refLv, heap)
|
return VarDecl(VarDeclType.VAR, declaredType, ZeropageWish.NOT_IN_ZEROPAGE, arraysize, autoVarName, null, array,
|
||||||
VarDecl(VarDeclType.VAR, declaredType, ZeropageWish.NOT_IN_ZEROPAGE, arraysize, autoVarName, null, refLv,
|
isArray = true, autogeneratedDontRemove = true, position = array.position)
|
||||||
isArray = true, autogeneratedDontRemove = true, position = refLv.position)
|
|
||||||
} else {
|
|
||||||
VarDecl(VarDeclType.VAR, refLv.type, ZeropageWish.NOT_IN_ZEROPAGE, null, autoVarName, null, refLv,
|
|
||||||
isArray = false, autogeneratedDontRemove = true, position = refLv.position)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -284,12 +284,6 @@ class VarDecl(val type: VarDeclType,
|
|||||||
structHasBeenFlattened = true
|
structHasBeenFlattened = true
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
fun withPrefixedName(nameprefix: String): Statement {
|
|
||||||
val new = VarDecl(type, declaredDatatype, zeropage, arraysize, nameprefix+name, structName, value, isArray, autogeneratedDontRemove, position)
|
|
||||||
new.parent = parent
|
|
||||||
return new
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class ArrayIndex(var index: Expression, override val position: Position) : Node {
|
class ArrayIndex(var index: Expression, override val position: Position) : Node {
|
||||||
@ -301,9 +295,8 @@ class ArrayIndex(var index: Expression, override val position: Position) : Node
|
|||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun forArray(v: ReferenceLiteralValue, heap: HeapValues): ArrayIndex {
|
fun forArray(v: ArrayLiteralValue): ArrayIndex {
|
||||||
val arraySize = v.array?.size ?: heap.get(v.heapId!!).arraysize
|
return ArrayIndex(NumericLiteralValue.optimalNumeric(v.value.size, v.position), v.position)
|
||||||
return ArrayIndex(NumericLiteralValue.optimalNumeric(arraySize, v.position), v.position)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -375,25 +368,23 @@ data class AssignTarget(val register: Register?,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun inferType(program: Program, stmt: Statement): DataType? {
|
fun inferType(program: Program, stmt: Statement): InferredTypes.InferredType {
|
||||||
if(register!=null)
|
if(register!=null)
|
||||||
return DataType.UBYTE
|
return InferredTypes.knownFor(DataType.UBYTE)
|
||||||
|
|
||||||
if(identifier!=null) {
|
if(identifier!=null) {
|
||||||
val symbol = program.namespace.lookup(identifier!!.nameInSource, stmt) ?: return null
|
val symbol = program.namespace.lookup(identifier!!.nameInSource, stmt) ?: return InferredTypes.unknown()
|
||||||
if (symbol is VarDecl) return symbol.datatype
|
if (symbol is VarDecl) return InferredTypes.knownFor(symbol.datatype)
|
||||||
}
|
}
|
||||||
|
|
||||||
if(arrayindexed!=null) {
|
if(arrayindexed!=null) {
|
||||||
val dt = arrayindexed!!.inferType(program)
|
return arrayindexed!!.inferType(program)
|
||||||
if(dt!=null)
|
|
||||||
return dt
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(memoryAddress!=null)
|
if(memoryAddress!=null)
|
||||||
return DataType.UBYTE
|
return InferredTypes.knownFor(DataType.UBYTE)
|
||||||
|
|
||||||
return null
|
return InferredTypes.unknown()
|
||||||
}
|
}
|
||||||
|
|
||||||
infix fun isSameAs(value: Expression): Boolean {
|
infix fun isSameAs(value: Expression): Boolean {
|
||||||
@ -608,40 +599,6 @@ class Subroutine(override val name: String,
|
|||||||
.filter { it is InlineAssembly }
|
.filter { it is InlineAssembly }
|
||||||
.map { (it as InlineAssembly).assembly }
|
.map { (it as InlineAssembly).assembly }
|
||||||
.count { " rti" in it || "\trti" in it || " rts" in it || "\trts" in it || " jmp" in it || "\tjmp" in it }
|
.count { " rti" in it || "\trti" in it || " rts" in it || "\trts" in it || " jmp" in it || "\tjmp" in it }
|
||||||
|
|
||||||
val canBeAsmSubroutine =false // TODO disabled for now, see below about problem with converting to asm subroutine
|
|
||||||
// !isAsmSubroutine
|
|
||||||
// && ((parameters.size == 1 && parameters[0].type in setOf(DataType.BYTE, DataType.UBYTE, DataType.WORD, DataType.UWORD))
|
|
||||||
// || (parameters.size == 2 && parameters.map { it.type }.all { it == DataType.BYTE || it == DataType.UBYTE }))
|
|
||||||
|
|
||||||
fun intoAsmSubroutine(): Subroutine {
|
|
||||||
// TODO turn subroutine into asm calling convention. Requires rethinking of how parameters are handled (conflicts with local vardefs now, see AstIdentifierChecker...)
|
|
||||||
return this // TODO
|
|
||||||
|
|
||||||
// println("TO ASM $this") // TODO
|
|
||||||
// val paramregs = if (parameters.size == 1 && parameters[0].type in setOf(DataType.BYTE, DataType.UBYTE))
|
|
||||||
// listOf(RegisterOrStatusflag(RegisterOrPair.Y, null, null))
|
|
||||||
// else if (parameters.size == 1 && parameters[0].type in setOf(DataType.WORD, DataType.UWORD))
|
|
||||||
// listOf(RegisterOrStatusflag(RegisterOrPair.AY, null, null))
|
|
||||||
// else if (parameters.size == 2 && parameters.map { it.type }.all { it == DataType.BYTE || it == DataType.UBYTE })
|
|
||||||
// listOf(RegisterOrStatusflag(RegisterOrPair.A, null, null), RegisterOrStatusflag(RegisterOrPair.Y, null, null))
|
|
||||||
// else throw FatalAstException("cannot convert subroutine to asm parameters")
|
|
||||||
//
|
|
||||||
// val asmsub=Subroutine(
|
|
||||||
// name,
|
|
||||||
// parameters,
|
|
||||||
// returntypes,
|
|
||||||
// paramregs,
|
|
||||||
// emptyList(),
|
|
||||||
// emptySet(),
|
|
||||||
// null,
|
|
||||||
// true,
|
|
||||||
// statements,
|
|
||||||
// position
|
|
||||||
// )
|
|
||||||
// asmsub.linkParents(parent)
|
|
||||||
// return asmsub
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
open class SubroutineParameter(val name: String,
|
open class SubroutineParameter(val name: String,
|
||||||
|
@ -5,7 +5,7 @@ import prog8.ast.Program
|
|||||||
import prog8.ast.base.*
|
import prog8.ast.base.*
|
||||||
import prog8.ast.statements.Directive
|
import prog8.ast.statements.Directive
|
||||||
import prog8.compiler.target.c64.MachineDefinition
|
import prog8.compiler.target.c64.MachineDefinition
|
||||||
import prog8.compiler.target.c64.codegen2.AsmGen2
|
import prog8.compiler.target.c64.codegen.AsmGen
|
||||||
import prog8.optimizer.constantFold
|
import prog8.optimizer.constantFold
|
||||||
import prog8.optimizer.optimizeStatements
|
import prog8.optimizer.optimizeStatements
|
||||||
import prog8.optimizer.simplifyExpressions
|
import prog8.optimizer.simplifyExpressions
|
||||||
@ -69,7 +69,8 @@ fun compileProgram(filepath: Path,
|
|||||||
//println(" time2: $time2")
|
//println(" time2: $time2")
|
||||||
val time3 = measureTimeMillis {
|
val time3 = measureTimeMillis {
|
||||||
programAst.removeNopsFlattenAnonScopes()
|
programAst.removeNopsFlattenAnonScopes()
|
||||||
programAst.reorderStatements() // reorder statements and add type casts, to please the compiler later
|
programAst.reorderStatements()
|
||||||
|
programAst.addTypecasts()
|
||||||
}
|
}
|
||||||
//println(" time3: $time3")
|
//println(" time3: $time3")
|
||||||
val time4 = measureTimeMillis {
|
val time4 = measureTimeMillis {
|
||||||
@ -90,6 +91,7 @@ fun compileProgram(filepath: Path,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
programAst.addTypecasts()
|
||||||
programAst.removeNopsFlattenAnonScopes()
|
programAst.removeNopsFlattenAnonScopes()
|
||||||
programAst.checkValid(compilerOptions) // check if final tree is valid
|
programAst.checkValid(compilerOptions) // check if final tree is valid
|
||||||
programAst.checkRecursion() // check if there are recursive subroutine calls
|
programAst.checkRecursion() // check if there are recursive subroutine calls
|
||||||
@ -100,7 +102,7 @@ fun compileProgram(filepath: Path,
|
|||||||
// asm generation directly from the Ast, no need for intermediate code
|
// asm generation directly from the Ast, no need for intermediate code
|
||||||
val zeropage = MachineDefinition.C64Zeropage(compilerOptions)
|
val zeropage = MachineDefinition.C64Zeropage(compilerOptions)
|
||||||
programAst.anonscopeVarsCleanup()
|
programAst.anonscopeVarsCleanup()
|
||||||
val assembly = AsmGen2(programAst, compilerOptions, zeropage).compileToAssembly(optimize)
|
val assembly = AsmGen(programAst, compilerOptions, zeropage).compileToAssembly(optimize)
|
||||||
assembly.assemble(compilerOptions)
|
assembly.assemble(compilerOptions)
|
||||||
programName = assembly.name
|
programName = assembly.name
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,7 @@ abstract class Zeropage(protected val options: CompilationOptions) {
|
|||||||
|
|
||||||
val allowedDatatypes = NumericDatatypes
|
val allowedDatatypes = NumericDatatypes
|
||||||
|
|
||||||
fun available() = free.size
|
fun available() = if(options.zeropage==ZeropageType.DONTUSE) 0 else free.size
|
||||||
|
|
||||||
fun allocate(scopedname: String, datatype: DataType, position: Position?): Int {
|
fun allocate(scopedname: String, datatype: DataType, position: Position?): Int {
|
||||||
assert(scopedname.isEmpty() || !allocations.values.any { it.first==scopedname } ) {"isSameAs scopedname can't be allocated twice"}
|
assert(scopedname.isEmpty() || !allocations.values.any { it.first==scopedname } ) {"isSameAs scopedname can't be allocated twice"}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
package prog8.compiler.target.c64.codegen2
|
package prog8.compiler.target.c64.codegen
|
||||||
|
|
||||||
import prog8.ast.Program
|
import prog8.ast.Program
|
||||||
import prog8.ast.base.AstException
|
import prog8.ast.base.AstException
|
||||||
@ -21,7 +21,7 @@ class AnonymousScopeVarsCleanup(val program: Program): IAstModifyingVisitor {
|
|||||||
super.visit(program)
|
super.visit(program)
|
||||||
for((scope, decls) in varsToMove) {
|
for((scope, decls) in varsToMove) {
|
||||||
val sub = scope.definingSubroutine()!!
|
val sub = scope.definingSubroutine()!!
|
||||||
val existingVariables = sub.statements.filterIsInstance<VarDecl>().associate { it.name to it }
|
val existingVariables = sub.statements.filterIsInstance<VarDecl>().associateBy { it.name }
|
||||||
var conflicts = false
|
var conflicts = false
|
||||||
decls.forEach {
|
decls.forEach {
|
||||||
val existing = existingVariables[it.name]
|
val existing = existingVariables[it.name]
|
879
compiler/src/prog8/compiler/target/c64/codegen/AsmGen.kt
Normal file
879
compiler/src/prog8/compiler/target/c64/codegen/AsmGen.kt
Normal file
@ -0,0 +1,879 @@
|
|||||||
|
package prog8.compiler.target.c64.codegen
|
||||||
|
|
||||||
|
import prog8.ast.Node
|
||||||
|
import prog8.ast.Program
|
||||||
|
import prog8.ast.antlr.escape
|
||||||
|
import prog8.ast.base.*
|
||||||
|
import prog8.ast.expressions.*
|
||||||
|
import prog8.ast.statements.*
|
||||||
|
import prog8.compiler.*
|
||||||
|
import prog8.compiler.target.c64.AssemblyProgram
|
||||||
|
import prog8.compiler.target.c64.MachineDefinition
|
||||||
|
import prog8.compiler.target.c64.MachineDefinition.ESTACK_HI_HEX
|
||||||
|
import prog8.compiler.target.c64.MachineDefinition.ESTACK_LO_HEX
|
||||||
|
import prog8.compiler.target.c64.Petscii
|
||||||
|
import prog8.functions.BuiltinFunctions
|
||||||
|
import prog8.functions.FunctionSignature
|
||||||
|
import java.io.File
|
||||||
|
import java.math.RoundingMode
|
||||||
|
import java.util.*
|
||||||
|
import kotlin.math.absoluteValue
|
||||||
|
|
||||||
|
|
||||||
|
internal class AssemblyError(msg: String) : RuntimeException(msg)
|
||||||
|
|
||||||
|
|
||||||
|
internal class AsmGen(val program: Program,
|
||||||
|
val options: CompilationOptions,
|
||||||
|
val zeropage: Zeropage) {
|
||||||
|
|
||||||
|
private val assemblyLines = mutableListOf<String>()
|
||||||
|
private val globalFloatConsts = mutableMapOf<Double, String>() // all float values in the entire program (value -> varname)
|
||||||
|
private val allocatedZeropageVariables = mutableMapOf<String, Pair<Int, DataType>>()
|
||||||
|
private val breakpointLabels = mutableListOf<String>()
|
||||||
|
private val builtinFunctionsAsmGen = BuiltinFunctionsAsmGen(program, this)
|
||||||
|
private val forloopsAsmGen = ForLoopsAsmGen(program, this)
|
||||||
|
private val postincrdecrAsmGen = PostIncrDecrAsmGen(program, this)
|
||||||
|
private val functioncallAsmGen = FunctionCallAsmGen(program, this)
|
||||||
|
private val assignmentAsmGen = AssignmentAsmGen(program, this)
|
||||||
|
private val expressionsAsmGen = ExpressionsAsmGen(program, this)
|
||||||
|
internal val loopEndLabels = Stack<String>()
|
||||||
|
internal val loopContinueLabels = Stack<String>()
|
||||||
|
|
||||||
|
internal fun compileToAssembly(optimize: Boolean): AssemblyProgram {
|
||||||
|
assemblyLines.clear()
|
||||||
|
loopEndLabels.clear()
|
||||||
|
loopContinueLabels.clear()
|
||||||
|
|
||||||
|
println("Generating assembly code... ")
|
||||||
|
|
||||||
|
header()
|
||||||
|
val allBlocks = program.allBlocks()
|
||||||
|
if(allBlocks.first().name != "main")
|
||||||
|
throw AssemblyError("first block should be 'main'")
|
||||||
|
for(b in program.allBlocks())
|
||||||
|
block2asm(b)
|
||||||
|
footer()
|
||||||
|
|
||||||
|
if(optimize) {
|
||||||
|
var optimizationsDone = 1
|
||||||
|
while (optimizationsDone > 0) {
|
||||||
|
optimizationsDone = optimizeAssembly(assemblyLines)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
File("${program.name}.asm").printWriter().use {
|
||||||
|
for (line in assemblyLines) { it.println(line) }
|
||||||
|
}
|
||||||
|
|
||||||
|
return AssemblyProgram(program.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun header() {
|
||||||
|
val ourName = this.javaClass.name
|
||||||
|
out("; 6502 assembly code for '${program.name}'")
|
||||||
|
out("; generated by $ourName on ${Date()}")
|
||||||
|
out("; assembler syntax is for the 64tasm cross-assembler")
|
||||||
|
out("; output options: output=${options.output} launcher=${options.launcher} zp=${options.zeropage}")
|
||||||
|
out("\n.cpu '6502'\n.enc 'none'\n")
|
||||||
|
|
||||||
|
program.actualLoadAddress = program.definedLoadAddress
|
||||||
|
if (program.actualLoadAddress == 0) // fix load address
|
||||||
|
program.actualLoadAddress = if (options.launcher == LauncherType.BASIC)
|
||||||
|
MachineDefinition.BASIC_LOAD_ADDRESS else MachineDefinition.RAW_LOAD_ADDRESS
|
||||||
|
|
||||||
|
when {
|
||||||
|
options.launcher == LauncherType.BASIC -> {
|
||||||
|
if (program.actualLoadAddress != 0x0801)
|
||||||
|
throw AssemblyError("BASIC output must have load address $0801")
|
||||||
|
out("; ---- basic program with sys call ----")
|
||||||
|
out("* = ${program.actualLoadAddress.toHex()}")
|
||||||
|
val year = Calendar.getInstance().get(Calendar.YEAR)
|
||||||
|
out(" .word (+), $year")
|
||||||
|
out(" .null $9e, format(' %d ', _prog8_entrypoint), $3a, $8f, ' prog8 by idj'")
|
||||||
|
out("+\t.word 0")
|
||||||
|
out("_prog8_entrypoint\t; assembly code starts here\n")
|
||||||
|
out(" jsr prog8_lib.init_system")
|
||||||
|
}
|
||||||
|
options.output == OutputType.PRG -> {
|
||||||
|
out("; ---- program without basic sys call ----")
|
||||||
|
out("* = ${program.actualLoadAddress.toHex()}\n")
|
||||||
|
out(" jsr prog8_lib.init_system")
|
||||||
|
}
|
||||||
|
options.output == OutputType.RAW -> {
|
||||||
|
out("; ---- raw assembler program ----")
|
||||||
|
out("* = ${program.actualLoadAddress.toHex()}\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (zeropage.exitProgramStrategy != Zeropage.ExitProgramStrategy.CLEAN_EXIT) {
|
||||||
|
// disable shift-commodore charset switching and run/stop key
|
||||||
|
out(" lda #$80")
|
||||||
|
out(" lda #$80")
|
||||||
|
out(" sta 657\t; disable charset switching")
|
||||||
|
out(" lda #239")
|
||||||
|
out(" sta 808\t; disable run/stop key")
|
||||||
|
}
|
||||||
|
|
||||||
|
out(" ldx #\$ff\t; init estack pointer")
|
||||||
|
|
||||||
|
out(" ; initialize the variables in each block")
|
||||||
|
for (block in program.allBlocks()) {
|
||||||
|
val initVarsSub = block.statements.singleOrNull { it is Subroutine && it.name == initvarsSubName }
|
||||||
|
if(initVarsSub!=null)
|
||||||
|
out(" jsr ${block.name}.$initvarsSubName")
|
||||||
|
}
|
||||||
|
|
||||||
|
out(" clc")
|
||||||
|
when (zeropage.exitProgramStrategy) {
|
||||||
|
Zeropage.ExitProgramStrategy.CLEAN_EXIT -> {
|
||||||
|
out(" jmp main.start\t; jump to program entrypoint")
|
||||||
|
}
|
||||||
|
Zeropage.ExitProgramStrategy.SYSTEM_RESET -> {
|
||||||
|
out(" jsr main.start\t; call program entrypoint")
|
||||||
|
out(" jmp (c64.RESET_VEC)\t; cold reset")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
out("")
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun footer() {
|
||||||
|
// the global list of all floating point constants for the whole program
|
||||||
|
out("; global float constants")
|
||||||
|
for (flt in globalFloatConsts) {
|
||||||
|
val mflpt5 = MachineDefinition.Mflpt5.fromNumber(flt.key)
|
||||||
|
val floatFill = makeFloatFill(mflpt5)
|
||||||
|
val floatvalue = flt.key
|
||||||
|
out("${flt.value}\t.byte $floatFill ; float $floatvalue")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun block2asm(block: Block) {
|
||||||
|
out("\n; ---- block: '${block.name}' ----")
|
||||||
|
out("${block.name}\t" + (if("force_output" in block.options()) ".block\n" else ".proc\n"))
|
||||||
|
|
||||||
|
if(block.address!=null) {
|
||||||
|
out(".cerror * > ${block.address.toHex()}, 'block address overlaps by ', *-${block.address.toHex()},' bytes'")
|
||||||
|
out("* = ${block.address.toHex()}")
|
||||||
|
}
|
||||||
|
|
||||||
|
outputSourceLine(block)
|
||||||
|
zeropagevars2asm(block.statements)
|
||||||
|
memdefs2asm(block.statements)
|
||||||
|
vardecls2asm(block.statements)
|
||||||
|
out("\n; subroutines in this block")
|
||||||
|
|
||||||
|
// first translate regular statements, and then put the subroutines at the end.
|
||||||
|
val (subroutine, stmts) = block.statements.partition { it is Subroutine }
|
||||||
|
stmts.forEach { translate(it) }
|
||||||
|
subroutine.forEach { translateSubroutine(it as Subroutine) }
|
||||||
|
|
||||||
|
out(if("force_output" in block.options()) "\n\t.bend\n" else "\n\t.pend\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
private var generatedLabelSequenceNumber: Int = 0
|
||||||
|
|
||||||
|
internal fun makeLabel(postfix: String): String {
|
||||||
|
generatedLabelSequenceNumber++
|
||||||
|
return "_prog8_label_${generatedLabelSequenceNumber}_$postfix"
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun outputSourceLine(node: Node) {
|
||||||
|
out(" ;\tsrc line: ${node.position.file}:${node.position.line}")
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun out(str: String, splitlines: Boolean = true) {
|
||||||
|
val fragment = (if(" | " in str) str.replace("|", "\n") else str).trim('\n')
|
||||||
|
|
||||||
|
if (splitlines) {
|
||||||
|
for (line in fragment.split('\n')) {
|
||||||
|
val trimmed = if (line.startsWith(' ')) "\t" + line.trim() else line.trim()
|
||||||
|
// trimmed = trimmed.replace(Regex("^\\+\\s+"), "+\t") // sanitize local label indentation
|
||||||
|
assemblyLines.add(trimmed)
|
||||||
|
}
|
||||||
|
} else assemblyLines.add(fragment)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun makeFloatFill(flt: MachineDefinition.Mflpt5): String {
|
||||||
|
val b0 = "$" + flt.b0.toString(16).padStart(2, '0')
|
||||||
|
val b1 = "$" + flt.b1.toString(16).padStart(2, '0')
|
||||||
|
val b2 = "$" + flt.b2.toString(16).padStart(2, '0')
|
||||||
|
val b3 = "$" + flt.b3.toString(16).padStart(2, '0')
|
||||||
|
val b4 = "$" + flt.b4.toString(16).padStart(2, '0')
|
||||||
|
return "$b0, $b1, $b2, $b3, $b4"
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun encodeStr(str: String, dt: DataType): List<Short> {
|
||||||
|
return when(dt) {
|
||||||
|
DataType.STR -> {
|
||||||
|
val bytes = Petscii.encodePetscii(str, true)
|
||||||
|
bytes.plus(0)
|
||||||
|
}
|
||||||
|
DataType.STR_S -> {
|
||||||
|
val bytes = Petscii.encodeScreencode(str, true)
|
||||||
|
bytes.plus(0)
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("invalid str type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun zeropagevars2asm(statements: List<Statement>) {
|
||||||
|
out("; vars allocated on zeropage")
|
||||||
|
val variables = statements.filterIsInstance<VarDecl>().filter { it.type==VarDeclType.VAR }
|
||||||
|
for(variable in variables) {
|
||||||
|
// should NOT allocate subroutine parameters on the zero page
|
||||||
|
val fullName = variable.scopedname
|
||||||
|
val zpVar = allocatedZeropageVariables[fullName]
|
||||||
|
if(zpVar==null) {
|
||||||
|
// This var is not on the ZP yet. Attempt to move it there (if it's not a float, those take up too much space)
|
||||||
|
if(variable.zeropage != ZeropageWish.NOT_IN_ZEROPAGE &&
|
||||||
|
variable.datatype in zeropage.allowedDatatypes
|
||||||
|
&& variable.datatype != DataType.FLOAT
|
||||||
|
&& options.zeropage != ZeropageType.DONTUSE) {
|
||||||
|
try {
|
||||||
|
val address = zeropage.allocate(fullName, variable.datatype, null)
|
||||||
|
out("${variable.name} = $address\t; auto zp ${variable.datatype}")
|
||||||
|
// make sure we add the var to the set of zpvars for this block
|
||||||
|
allocatedZeropageVariables[fullName] = Pair(address, variable.datatype)
|
||||||
|
} catch (x: ZeropageDepletedError) {
|
||||||
|
// leave it as it is.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun vardecl2asm(decl: VarDecl) {
|
||||||
|
when (decl.datatype) {
|
||||||
|
DataType.UBYTE -> out("${decl.name}\t.byte 0")
|
||||||
|
DataType.BYTE -> out("${decl.name}\t.char 0")
|
||||||
|
DataType.UWORD -> out("${decl.name}\t.word 0")
|
||||||
|
DataType.WORD -> out("${decl.name}\t.sint 0")
|
||||||
|
DataType.FLOAT -> out("${decl.name}\t.byte 0,0,0,0,0 ; float")
|
||||||
|
DataType.STRUCT -> {} // is flattened
|
||||||
|
DataType.STR, DataType.STR_S -> {
|
||||||
|
val string = (decl.value as StringLiteralValue).value
|
||||||
|
val encoded = encodeStr(string, decl.datatype)
|
||||||
|
outputStringvar(decl, encoded)
|
||||||
|
}
|
||||||
|
DataType.ARRAY_UB -> {
|
||||||
|
val data = makeArrayFillDataUnsigned(decl)
|
||||||
|
if (data.size <= 16)
|
||||||
|
out("${decl.name}\t.byte ${data.joinToString()}")
|
||||||
|
else {
|
||||||
|
out(decl.name)
|
||||||
|
for (chunk in data.chunked(16))
|
||||||
|
out(" .byte " + chunk.joinToString())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DataType.ARRAY_B -> {
|
||||||
|
val data = makeArrayFillDataSigned(decl)
|
||||||
|
if (data.size <= 16)
|
||||||
|
out("${decl.name}\t.char ${data.joinToString()}")
|
||||||
|
else {
|
||||||
|
out(decl.name)
|
||||||
|
for (chunk in data.chunked(16))
|
||||||
|
out(" .char " + chunk.joinToString())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DataType.ARRAY_UW -> {
|
||||||
|
val data = makeArrayFillDataUnsigned(decl)
|
||||||
|
if (data.size <= 16)
|
||||||
|
out("${decl.name}\t.word ${data.joinToString()}")
|
||||||
|
else {
|
||||||
|
out(decl.name)
|
||||||
|
for (chunk in data.chunked(16))
|
||||||
|
out(" .word " + chunk.joinToString())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DataType.ARRAY_W -> {
|
||||||
|
val data = makeArrayFillDataSigned(decl)
|
||||||
|
if (data.size <= 16)
|
||||||
|
out("${decl.name}\t.sint ${data.joinToString()}")
|
||||||
|
else {
|
||||||
|
out(decl.name)
|
||||||
|
for (chunk in data.chunked(16))
|
||||||
|
out(" .sint " + chunk.joinToString())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DataType.ARRAY_F -> {
|
||||||
|
val array = (decl.value as ArrayLiteralValue).value
|
||||||
|
val floatFills = array.map {
|
||||||
|
val number = (it as NumericLiteralValue).number
|
||||||
|
makeFloatFill(MachineDefinition.Mflpt5.fromNumber(number))
|
||||||
|
}
|
||||||
|
out(decl.name)
|
||||||
|
for (f in array.zip(floatFills))
|
||||||
|
out(" .byte ${f.second} ; float ${f.first}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun memdefs2asm(statements: List<Statement>) {
|
||||||
|
out("\n; memdefs and kernel subroutines")
|
||||||
|
val memvars = statements.filterIsInstance<VarDecl>().filter { it.type==VarDeclType.MEMORY || it.type==VarDeclType.CONST }
|
||||||
|
for(m in memvars) {
|
||||||
|
out(" ${m.name} = ${(m.value as NumericLiteralValue).number.toHex()}")
|
||||||
|
}
|
||||||
|
val asmSubs = statements.filterIsInstance<Subroutine>().filter { it.isAsmSubroutine }
|
||||||
|
for(sub in asmSubs) {
|
||||||
|
if(sub.asmAddress!=null) {
|
||||||
|
if(sub.statements.isNotEmpty())
|
||||||
|
throw AssemblyError("kernel subroutine cannot have statements")
|
||||||
|
out(" ${sub.name} = ${sub.asmAddress.toHex()}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun vardecls2asm(statements: List<Statement>) {
|
||||||
|
out("\n; non-zeropage variables")
|
||||||
|
val vars = statements.filterIsInstance<VarDecl>().filter { it.type==VarDeclType.VAR }
|
||||||
|
|
||||||
|
// first output the flattened struct member variables *in order*
|
||||||
|
// after that, the other variables sorted by their datatype
|
||||||
|
|
||||||
|
val (structMembers, normalVars) = vars.partition { it.struct!=null }
|
||||||
|
structMembers.forEach { vardecl2asm(it) }
|
||||||
|
|
||||||
|
// special treatment for string types: merge strings that are identical
|
||||||
|
val encodedstringVars = normalVars
|
||||||
|
.filter {it.datatype in StringDatatypes }
|
||||||
|
.map { it to encodeStr((it.value as StringLiteralValue).value, it.datatype) }
|
||||||
|
.groupBy({it.second}, {it.first})
|
||||||
|
for((encoded, variables) in encodedstringVars) {
|
||||||
|
variables.dropLast(1).forEach { out(it.name) }
|
||||||
|
val lastvar = variables.last()
|
||||||
|
outputStringvar(lastvar, encoded)
|
||||||
|
}
|
||||||
|
|
||||||
|
// non-string variables
|
||||||
|
normalVars.filter{ it.datatype !in StringDatatypes}.sortedBy { it.datatype }.forEach {
|
||||||
|
if(it.scopedname !in allocatedZeropageVariables)
|
||||||
|
vardecl2asm(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun outputStringvar(lastvar: VarDecl, encoded: List<Short>) {
|
||||||
|
val string = (lastvar.value as StringLiteralValue).value
|
||||||
|
out("${lastvar.name}\t; ${lastvar.datatype} \"${escape(string).replace("\u0000", "<NULL>")}\"")
|
||||||
|
val outputBytes = encoded.map { "$" + it.toString(16).padStart(2, '0') }
|
||||||
|
for (chunk in outputBytes.chunked(16))
|
||||||
|
out(" .byte " + chunk.joinToString())
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun makeArrayFillDataUnsigned(decl: VarDecl): List<String> {
|
||||||
|
val array = (decl.value as ArrayLiteralValue).value
|
||||||
|
return when {
|
||||||
|
decl.datatype == DataType.ARRAY_UB ->
|
||||||
|
// byte array can never contain pointer-to types, so treat values as all integers
|
||||||
|
array.map {
|
||||||
|
val number = (it as NumericLiteralValue).number.toInt()
|
||||||
|
"$"+number.toString(16).padStart(2, '0')
|
||||||
|
}
|
||||||
|
decl.datatype== DataType.ARRAY_UW -> array.map {
|
||||||
|
if(it is NumericLiteralValue) {
|
||||||
|
"$" + it.number.toInt().toString(16).padStart(4, '0')
|
||||||
|
} else {
|
||||||
|
(it as AddressOf).identifier.nameInSource.joinToString(".")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("invalid arraysize type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun makeArrayFillDataSigned(decl: VarDecl): List<String> {
|
||||||
|
val array = (decl.value as ArrayLiteralValue).value
|
||||||
|
|
||||||
|
return when {
|
||||||
|
decl.datatype == DataType.ARRAY_UB ->
|
||||||
|
// byte array can never contain pointer-to types, so treat values as all integers
|
||||||
|
array.map {
|
||||||
|
val number = (it as NumericLiteralValue).number.toInt()
|
||||||
|
val hexnum = number.toString(16).padStart(2, '0')
|
||||||
|
"$$hexnum"
|
||||||
|
}
|
||||||
|
decl.datatype == DataType.ARRAY_B ->
|
||||||
|
// byte array can never contain pointer-to types, so treat values as all integers
|
||||||
|
array.map {
|
||||||
|
val number = (it as NumericLiteralValue).number.toInt()
|
||||||
|
val hexnum = number.absoluteValue.toString(16).padStart(2, '0')
|
||||||
|
if(number>=0)
|
||||||
|
"$$hexnum"
|
||||||
|
else
|
||||||
|
"-$$hexnum"
|
||||||
|
}
|
||||||
|
decl.datatype== DataType.ARRAY_UW -> array.map {
|
||||||
|
val number = (it as NumericLiteralValue).number.toInt()
|
||||||
|
val hexnum = number.toString(16).padStart(4, '0')
|
||||||
|
"$$hexnum"
|
||||||
|
}
|
||||||
|
decl.datatype== DataType.ARRAY_W -> array.map {
|
||||||
|
val number = (it as NumericLiteralValue).number.toInt()
|
||||||
|
val hexnum = number.absoluteValue.toString(16).padStart(4, '0')
|
||||||
|
if(number>=0)
|
||||||
|
"$$hexnum"
|
||||||
|
else
|
||||||
|
"-$$hexnum"
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("invalid arraysize type ${decl.datatype}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun getFloatConst(number: Double): String {
|
||||||
|
// try to match the ROM float constants to save memory
|
||||||
|
val mflpt5 = MachineDefinition.Mflpt5.fromNumber(number)
|
||||||
|
val floatbytes = shortArrayOf(mflpt5.b0, mflpt5.b1, mflpt5.b2, mflpt5.b3, mflpt5.b4)
|
||||||
|
when {
|
||||||
|
floatbytes.contentEquals(shortArrayOf(0x00, 0x00, 0x00, 0x00, 0x00)) -> return "c64flt.FL_ZERO"
|
||||||
|
floatbytes.contentEquals(shortArrayOf(0x82, 0x49, 0x0f, 0xda, 0xa1)) -> return "c64flt.FL_PIVAL"
|
||||||
|
floatbytes.contentEquals(shortArrayOf(0x90, 0x80, 0x00, 0x00, 0x00)) -> return "c64flt.FL_N32768"
|
||||||
|
floatbytes.contentEquals(shortArrayOf(0x81, 0x00, 0x00, 0x00, 0x00)) -> return "c64flt.FL_FONE"
|
||||||
|
floatbytes.contentEquals(shortArrayOf(0x80, 0x35, 0x04, 0xf3, 0x34)) -> return "c64flt.FL_SQRHLF"
|
||||||
|
floatbytes.contentEquals(shortArrayOf(0x81, 0x35, 0x04, 0xf3, 0x34)) -> return "c64flt.FL_SQRTWO"
|
||||||
|
floatbytes.contentEquals(shortArrayOf(0x80, 0x80, 0x00, 0x00, 0x00)) -> return "c64flt.FL_NEGHLF"
|
||||||
|
floatbytes.contentEquals(shortArrayOf(0x80, 0x31, 0x72, 0x17, 0xf8)) -> return "c64flt.FL_LOG2"
|
||||||
|
floatbytes.contentEquals(shortArrayOf(0x84, 0x20, 0x00, 0x00, 0x00)) -> return "c64flt.FL_TENC"
|
||||||
|
floatbytes.contentEquals(shortArrayOf(0x9e, 0x6e, 0x6b, 0x28, 0x00)) -> return "c64flt.FL_NZMIL"
|
||||||
|
floatbytes.contentEquals(shortArrayOf(0x80, 0x00, 0x00, 0x00, 0x00)) -> return "c64flt.FL_FHALF"
|
||||||
|
floatbytes.contentEquals(shortArrayOf(0x81, 0x38, 0xaa, 0x3b, 0x29)) -> return "c64flt.FL_LOGEB2"
|
||||||
|
floatbytes.contentEquals(shortArrayOf(0x81, 0x49, 0x0f, 0xda, 0xa2)) -> return "c64flt.FL_PIHALF"
|
||||||
|
floatbytes.contentEquals(shortArrayOf(0x83, 0x49, 0x0f, 0xda, 0xa2)) -> return "c64flt.FL_TWOPI"
|
||||||
|
floatbytes.contentEquals(shortArrayOf(0x7f, 0x00, 0x00, 0x00, 0x00)) -> return "c64flt.FL_FR4"
|
||||||
|
else -> {
|
||||||
|
// attempt to correct for a few rounding issues
|
||||||
|
when (number.toBigDecimal().setScale(10, RoundingMode.HALF_DOWN).toDouble()) {
|
||||||
|
3.1415926536 -> return "c64flt.FL_PIVAL"
|
||||||
|
1.4142135624 -> return "c64flt.FL_SQRTWO"
|
||||||
|
0.7071067812 -> return "c64flt.FL_SQRHLF"
|
||||||
|
0.6931471806 -> return "c64flt.FL_LOG2"
|
||||||
|
else -> {}
|
||||||
|
}
|
||||||
|
|
||||||
|
// no ROM float const for this value, create our own
|
||||||
|
val name = globalFloatConsts[number]
|
||||||
|
if(name!=null)
|
||||||
|
return name
|
||||||
|
val newName = "prog8_float_const_${globalFloatConsts.size}"
|
||||||
|
globalFloatConsts[number] = newName
|
||||||
|
return newName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun signExtendAtoMsb(destination: String) =
|
||||||
|
"""
|
||||||
|
ora #$7f
|
||||||
|
bmi +
|
||||||
|
lda #0
|
||||||
|
+ sta $destination
|
||||||
|
"""
|
||||||
|
|
||||||
|
internal fun asmIdentifierName(identifier: IdentifierReference): String {
|
||||||
|
val name = if(identifier.memberOfStruct(program.namespace)!=null) {
|
||||||
|
identifier.targetVarDecl(program.namespace)!!.name
|
||||||
|
} else {
|
||||||
|
identifier.nameInSource.joinToString(".")
|
||||||
|
}
|
||||||
|
return fixNameSymbols(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun fixNameSymbols(name: String) = name.replace("<", "prog8_").replace(">", "") // take care of the autogenerated invalid (anon) label names
|
||||||
|
|
||||||
|
private fun branchInstruction(condition: BranchCondition, complement: Boolean) =
|
||||||
|
if(complement) {
|
||||||
|
when (condition) {
|
||||||
|
BranchCondition.CS -> "bcc"
|
||||||
|
BranchCondition.CC -> "bcs"
|
||||||
|
BranchCondition.EQ, BranchCondition.Z -> "beq"
|
||||||
|
BranchCondition.NE, BranchCondition.NZ -> "bne"
|
||||||
|
BranchCondition.VS -> "bvc"
|
||||||
|
BranchCondition.VC -> "bvs"
|
||||||
|
BranchCondition.MI, BranchCondition.NEG -> "bmi"
|
||||||
|
BranchCondition.PL, BranchCondition.POS -> "bpl"
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
when (condition) {
|
||||||
|
BranchCondition.CS -> "bcs"
|
||||||
|
BranchCondition.CC -> "bcc"
|
||||||
|
BranchCondition.EQ, BranchCondition.Z -> "beq"
|
||||||
|
BranchCondition.NE, BranchCondition.NZ -> "bne"
|
||||||
|
BranchCondition.VS -> "bvs"
|
||||||
|
BranchCondition.VC -> "bvc"
|
||||||
|
BranchCondition.MI, BranchCondition.NEG -> "bmi"
|
||||||
|
BranchCondition.PL, BranchCondition.POS -> "bpl"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun readAndPushArrayvalueWithIndexA(arrayDt: DataType, variable: IdentifierReference) {
|
||||||
|
val variablename = asmIdentifierName(variable)
|
||||||
|
when (arrayDt) {
|
||||||
|
DataType.STR, DataType.STR_S, DataType.ARRAY_UB, DataType.ARRAY_B ->
|
||||||
|
out(" tay | lda $variablename,y | sta $ESTACK_LO_HEX,x | dex")
|
||||||
|
DataType.ARRAY_UW, DataType.ARRAY_W ->
|
||||||
|
out(" asl a | tay | lda $variablename,y | sta $ESTACK_LO_HEX,x | lda $variablename+1,y | sta $ESTACK_HI_HEX,x | dex")
|
||||||
|
DataType.ARRAY_F ->
|
||||||
|
// index * 5 is done in the subroutine that's called
|
||||||
|
out("""
|
||||||
|
sta $ESTACK_LO_HEX,x
|
||||||
|
dex
|
||||||
|
lda #<$variablename
|
||||||
|
ldy #>$variablename
|
||||||
|
jsr c64flt.push_float_from_indexed_var
|
||||||
|
""")
|
||||||
|
else ->
|
||||||
|
throw AssemblyError("weird array type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun saveRegister(register: Register) {
|
||||||
|
when(register) {
|
||||||
|
Register.A -> out(" pha")
|
||||||
|
Register.X -> out(" txa | pha")
|
||||||
|
Register.Y -> out(" tya | pha")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun restoreRegister(register: Register) {
|
||||||
|
when(register) {
|
||||||
|
Register.A -> out(" pla")
|
||||||
|
Register.X -> out(" pla | tax")
|
||||||
|
Register.Y -> out(" pla | tay")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun translateSubroutine(sub: Subroutine) {
|
||||||
|
out("")
|
||||||
|
outputSourceLine(sub)
|
||||||
|
|
||||||
|
if(sub.isAsmSubroutine) {
|
||||||
|
if(sub.asmAddress!=null)
|
||||||
|
return // already done at the memvars section
|
||||||
|
|
||||||
|
// asmsub with most likely just an inline asm in it
|
||||||
|
out("${sub.name}\t.proc")
|
||||||
|
sub.statements.forEach{ translate(it) }
|
||||||
|
out(" .pend\n")
|
||||||
|
} else {
|
||||||
|
// regular subroutine
|
||||||
|
out("${sub.name}\t.proc")
|
||||||
|
zeropagevars2asm(sub.statements)
|
||||||
|
memdefs2asm(sub.statements)
|
||||||
|
out("; statements")
|
||||||
|
sub.statements.forEach{ translate(it) }
|
||||||
|
out("; variables")
|
||||||
|
vardecls2asm(sub.statements)
|
||||||
|
out(" .pend\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun translate(stmt: Statement) {
|
||||||
|
outputSourceLine(stmt)
|
||||||
|
when(stmt) {
|
||||||
|
is VarDecl, is StructDecl, is NopStatement -> {}
|
||||||
|
is Directive -> translate(stmt)
|
||||||
|
is Return -> translate(stmt)
|
||||||
|
is Subroutine -> translateSubroutine(stmt)
|
||||||
|
is InlineAssembly -> translate(stmt)
|
||||||
|
is FunctionCallStatement -> {
|
||||||
|
val functionName = stmt.target.nameInSource.last()
|
||||||
|
val builtinFunc = BuiltinFunctions[functionName]
|
||||||
|
if(builtinFunc!=null) {
|
||||||
|
builtinFunctionsAsmGen.translateFunctioncallStatement(stmt, builtinFunc)
|
||||||
|
} else {
|
||||||
|
functioncallAsmGen.translateFunctionCall(stmt)
|
||||||
|
// discard any results from the stack:
|
||||||
|
val sub = stmt.target.targetSubroutine(program.namespace)!!
|
||||||
|
val returns = sub.returntypes.zip(sub.asmReturnvaluesRegisters)
|
||||||
|
for((t, reg) in returns) {
|
||||||
|
if(reg.stack) {
|
||||||
|
if (t in IntegerDatatypes || t in PassByReferenceDatatypes) out(" inx")
|
||||||
|
else if (t == DataType.FLOAT) out(" inx | inx | inx")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is Assignment -> assignmentAsmGen.translate(stmt)
|
||||||
|
is Jump -> translate(stmt)
|
||||||
|
is PostIncrDecr -> postincrdecrAsmGen.translate(stmt)
|
||||||
|
is Label -> translate(stmt)
|
||||||
|
is BranchStatement -> translate(stmt)
|
||||||
|
is IfStatement -> translate(stmt)
|
||||||
|
is ForLoop -> forloopsAsmGen.translate(stmt)
|
||||||
|
is Continue -> out(" jmp ${loopContinueLabels.peek()}")
|
||||||
|
is Break -> out(" jmp ${loopEndLabels.peek()}")
|
||||||
|
is WhileLoop -> translate(stmt)
|
||||||
|
is RepeatLoop -> translate(stmt)
|
||||||
|
is WhenStatement -> translate(stmt)
|
||||||
|
is BuiltinFunctionStatementPlaceholder -> throw AssemblyError("builtin function should not have placeholder anymore?")
|
||||||
|
is AnonymousScope -> translate(stmt)
|
||||||
|
is Block -> throw AssemblyError("block should have been handled elsewhere")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun translate(stmt: IfStatement) {
|
||||||
|
expressionsAsmGen.translateExpression(stmt.condition)
|
||||||
|
translateTestStack(stmt.condition.inferType(program).typeOrElse(DataType.STRUCT))
|
||||||
|
val elseLabel = makeLabel("if_else")
|
||||||
|
val endLabel = makeLabel("if_end")
|
||||||
|
out(" beq $elseLabel")
|
||||||
|
translate(stmt.truepart)
|
||||||
|
out(" jmp $endLabel")
|
||||||
|
out(elseLabel)
|
||||||
|
translate(stmt.elsepart)
|
||||||
|
out(endLabel)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun translateTestStack(dataType: DataType) {
|
||||||
|
when(dataType) {
|
||||||
|
in ByteDatatypes -> out(" inx | lda $ESTACK_LO_HEX,x")
|
||||||
|
in WordDatatypes -> out(" inx | lda $ESTACK_LO_HEX,x | ora $ESTACK_HI_HEX,x")
|
||||||
|
DataType.FLOAT -> throw AssemblyError("conditional value should be an integer (boolean)")
|
||||||
|
else -> throw AssemblyError("non-numerical dt")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun translate(stmt: WhileLoop) {
|
||||||
|
val whileLabel = makeLabel("while")
|
||||||
|
val endLabel = makeLabel("whileend")
|
||||||
|
loopEndLabels.push(endLabel)
|
||||||
|
loopContinueLabels.push(whileLabel)
|
||||||
|
out(whileLabel)
|
||||||
|
// TODO optimize for the simple cases, can we avoid stack use?
|
||||||
|
expressionsAsmGen.translateExpression(stmt.condition)
|
||||||
|
val conditionDt = stmt.condition.inferType(program)
|
||||||
|
if(!conditionDt.isKnown)
|
||||||
|
throw AssemblyError("unknown condition dt")
|
||||||
|
if(conditionDt.typeOrElse(DataType.BYTE) in ByteDatatypes) {
|
||||||
|
out(" inx | lda $ESTACK_LO_HEX,x | beq $endLabel")
|
||||||
|
} else {
|
||||||
|
out("""
|
||||||
|
inx
|
||||||
|
lda $ESTACK_LO_HEX,x
|
||||||
|
bne +
|
||||||
|
lda $ESTACK_HI_HEX,x
|
||||||
|
beq $endLabel
|
||||||
|
+ """)
|
||||||
|
}
|
||||||
|
translate(stmt.body)
|
||||||
|
out(" jmp $whileLabel")
|
||||||
|
out(endLabel)
|
||||||
|
loopEndLabels.pop()
|
||||||
|
loopContinueLabels.pop()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun translate(stmt: RepeatLoop) {
|
||||||
|
val repeatLabel = makeLabel("repeat")
|
||||||
|
val endLabel = makeLabel("repeatend")
|
||||||
|
loopEndLabels.push(endLabel)
|
||||||
|
loopContinueLabels.push(repeatLabel)
|
||||||
|
out(repeatLabel)
|
||||||
|
// TODO optimize this for the simple cases, can we avoid stack use?
|
||||||
|
translate(stmt.body)
|
||||||
|
expressionsAsmGen.translateExpression(stmt.untilCondition)
|
||||||
|
val conditionDt = stmt.untilCondition.inferType(program)
|
||||||
|
if(!conditionDt.isKnown)
|
||||||
|
throw AssemblyError("unknown condition dt")
|
||||||
|
if(conditionDt.typeOrElse(DataType.BYTE) in ByteDatatypes) {
|
||||||
|
out(" inx | lda $ESTACK_LO_HEX,x | beq $repeatLabel")
|
||||||
|
} else {
|
||||||
|
out("""
|
||||||
|
inx
|
||||||
|
lda $ESTACK_LO_HEX,x
|
||||||
|
bne +
|
||||||
|
lda $ESTACK_HI_HEX,x
|
||||||
|
beq $repeatLabel
|
||||||
|
+ """)
|
||||||
|
}
|
||||||
|
out(endLabel)
|
||||||
|
loopEndLabels.pop()
|
||||||
|
loopContinueLabels.pop()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun translate(stmt: WhenStatement) {
|
||||||
|
expressionsAsmGen.translateExpression(stmt.condition)
|
||||||
|
val endLabel = makeLabel("choice_end")
|
||||||
|
val choiceBlocks = mutableListOf<Pair<String, AnonymousScope>>()
|
||||||
|
val conditionDt = stmt.condition.inferType(program)
|
||||||
|
if(!conditionDt.isKnown)
|
||||||
|
throw AssemblyError("unknown condition dt")
|
||||||
|
if(conditionDt.typeOrElse(DataType.BYTE) in ByteDatatypes)
|
||||||
|
out(" inx | lda $ESTACK_LO_HEX,x")
|
||||||
|
else
|
||||||
|
out(" inx | lda $ESTACK_LO_HEX,x | ldy $ESTACK_HI_HEX,x")
|
||||||
|
for(choice in stmt.choices) {
|
||||||
|
val choiceLabel = makeLabel("choice")
|
||||||
|
if(choice.values==null) {
|
||||||
|
// the else choice
|
||||||
|
translate(choice.statements)
|
||||||
|
out(" jmp $endLabel")
|
||||||
|
} else {
|
||||||
|
choiceBlocks.add(Pair(choiceLabel, choice.statements))
|
||||||
|
for (cv in choice.values!!) {
|
||||||
|
val value = (cv as NumericLiteralValue).number.toInt()
|
||||||
|
if(conditionDt.typeOrElse(DataType.BYTE) in ByteDatatypes) {
|
||||||
|
out(" cmp #${value.toHex()} | beq $choiceLabel")
|
||||||
|
} else {
|
||||||
|
out("""
|
||||||
|
cmp #<${value.toHex()}
|
||||||
|
bne +
|
||||||
|
cpy #>${value.toHex()}
|
||||||
|
beq $choiceLabel
|
||||||
|
+
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for(choiceBlock in choiceBlocks) {
|
||||||
|
out(choiceBlock.first)
|
||||||
|
translate(choiceBlock.second)
|
||||||
|
out(" jmp $endLabel")
|
||||||
|
}
|
||||||
|
out(endLabel)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun translate(stmt: Label) {
|
||||||
|
out(stmt.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun translate(scope: AnonymousScope) {
|
||||||
|
// note: the variables defined in an anonymous scope have been moved to their defining subroutine's scope
|
||||||
|
scope.statements.forEach{ translate(it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun translate(stmt: BranchStatement) {
|
||||||
|
if(stmt.truepart.containsNoCodeNorVars() && stmt.elsepart.containsCodeOrVars())
|
||||||
|
throw AssemblyError("only else part contains code, shoud have been switched already")
|
||||||
|
|
||||||
|
val jump = stmt.truepart.statements.first() as? Jump
|
||||||
|
if(jump!=null) {
|
||||||
|
// branch with only a jump
|
||||||
|
val instruction = branchInstruction(stmt.condition, false)
|
||||||
|
out(" $instruction ${getJumpTarget(jump)}")
|
||||||
|
translate(stmt.elsepart)
|
||||||
|
} else {
|
||||||
|
if(stmt.elsepart.containsNoCodeNorVars()) {
|
||||||
|
val instruction = branchInstruction(stmt.condition, true)
|
||||||
|
val elseLabel = makeLabel("branch_else")
|
||||||
|
out(" $instruction $elseLabel")
|
||||||
|
translate(stmt.truepart)
|
||||||
|
out(elseLabel)
|
||||||
|
} else {
|
||||||
|
val instruction = branchInstruction(stmt.condition, false)
|
||||||
|
val trueLabel = makeLabel("branch_true")
|
||||||
|
val endLabel = makeLabel("branch_end")
|
||||||
|
out(" $instruction $trueLabel")
|
||||||
|
translate(stmt.elsepart)
|
||||||
|
out(" jmp $endLabel")
|
||||||
|
out(trueLabel)
|
||||||
|
translate(stmt.truepart)
|
||||||
|
out(endLabel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun translate(stmt: Directive) {
|
||||||
|
when(stmt.directive) {
|
||||||
|
"%asminclude" -> {
|
||||||
|
val sourcecode = loadAsmIncludeFile(stmt.args[0].str!!, stmt.definingModule().source)
|
||||||
|
val scopeprefix = stmt.args[1].str ?: ""
|
||||||
|
if(!scopeprefix.isBlank())
|
||||||
|
out("$scopeprefix\t.proc")
|
||||||
|
assemblyLines.add(sourcecode.trimEnd().trimStart('\n'))
|
||||||
|
if(!scopeprefix.isBlank())
|
||||||
|
out(" .pend\n")
|
||||||
|
}
|
||||||
|
"%asmbinary" -> {
|
||||||
|
val offset = if(stmt.args.size>1) ", ${stmt.args[1].int}" else ""
|
||||||
|
val length = if(stmt.args.size>2) ", ${stmt.args[2].int}" else ""
|
||||||
|
out(" .binary \"${stmt.args[0].str}\" $offset $length")
|
||||||
|
}
|
||||||
|
"%breakpoint" -> {
|
||||||
|
val label = "_prog8_breakpoint_${breakpointLabels.size+1}"
|
||||||
|
breakpointLabels.add(label)
|
||||||
|
out("$label\tnop")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun translate(jmp: Jump) {
|
||||||
|
out(" jmp ${getJumpTarget(jmp)}")
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getJumpTarget(jmp: Jump): String {
|
||||||
|
return when {
|
||||||
|
jmp.identifier!=null -> asmIdentifierName(jmp.identifier)
|
||||||
|
jmp.generatedLabel!=null -> jmp.generatedLabel
|
||||||
|
jmp.address!=null -> jmp.address.toHex()
|
||||||
|
else -> "????"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun translate(ret: Return) {
|
||||||
|
ret.value?.let { expressionsAsmGen.translateExpression(it) }
|
||||||
|
out(" rts")
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun translate(asm: InlineAssembly) {
|
||||||
|
val assembly = asm.assembly.trimEnd().trimStart('\n')
|
||||||
|
assemblyLines.add(assembly)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun translateArrayIndexIntoA(expr: ArrayIndexedExpression) {
|
||||||
|
val index = expr.arrayspec.index
|
||||||
|
when (index) {
|
||||||
|
is NumericLiteralValue -> throw AssemblyError("this should be optimized directly")
|
||||||
|
is RegisterExpr -> {
|
||||||
|
when (index.register) {
|
||||||
|
Register.A -> {}
|
||||||
|
Register.X -> out(" txa")
|
||||||
|
Register.Y -> out(" tya")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is IdentifierReference -> {
|
||||||
|
val indexName = asmIdentifierName(index)
|
||||||
|
out(" lda $indexName")
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
expressionsAsmGen.translateExpression(index)
|
||||||
|
out(" inx | lda $ESTACK_LO_HEX,x")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun translateExpression(expression: Expression) =
|
||||||
|
expressionsAsmGen.translateExpression(expression)
|
||||||
|
|
||||||
|
internal fun translateFunctioncallExpression(functionCall: FunctionCall, signature: FunctionSignature) =
|
||||||
|
builtinFunctionsAsmGen.translateFunctioncallExpression(functionCall, signature)
|
||||||
|
|
||||||
|
internal fun translateFunctionCall(functionCall: FunctionCall) =
|
||||||
|
functioncallAsmGen.translateFunctionCall(functionCall)
|
||||||
|
|
||||||
|
internal fun assignFromEvalResult(target: AssignTarget) =
|
||||||
|
assignmentAsmGen.assignFromEvalResult(target)
|
||||||
|
|
||||||
|
fun assignFromByteConstant(target: AssignTarget, value: Short) =
|
||||||
|
assignmentAsmGen.assignFromByteConstant(target, value)
|
||||||
|
|
||||||
|
fun assignFromWordConstant(target: AssignTarget, value: Int) =
|
||||||
|
assignmentAsmGen.assignFromWordConstant(target, value)
|
||||||
|
|
||||||
|
fun assignFromFloatConstant(target: AssignTarget, value: Double) =
|
||||||
|
assignmentAsmGen.assignFromFloatConstant(target, value)
|
||||||
|
|
||||||
|
fun assignFromByteVariable(target: AssignTarget, variable: IdentifierReference) =
|
||||||
|
assignmentAsmGen.assignFromByteVariable(target, variable)
|
||||||
|
|
||||||
|
fun assignFromWordVariable(target: AssignTarget, variable: IdentifierReference) =
|
||||||
|
assignmentAsmGen.assignFromWordVariable(target, variable)
|
||||||
|
|
||||||
|
fun assignFromFloatVariable(target: AssignTarget, variable: IdentifierReference) =
|
||||||
|
assignmentAsmGen.assignFromFloatVariable(target, variable)
|
||||||
|
|
||||||
|
fun assignFromRegister(target: AssignTarget, register: Register) =
|
||||||
|
assignmentAsmGen.assignFromRegister(target, register)
|
||||||
|
|
||||||
|
fun assignFromMemoryByte(target: AssignTarget, address: Int?, identifier: IdentifierReference?) =
|
||||||
|
assignmentAsmGen.assignFromMemoryByte(target, address, identifier)
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package prog8.compiler.target.c64.codegen2
|
package prog8.compiler.target.c64.codegen
|
||||||
|
|
||||||
import prog8.compiler.target.c64.MachineDefinition.ESTACK_LO_HEX
|
import prog8.compiler.target.c64.MachineDefinition.ESTACK_LO_HEX
|
||||||
import prog8.compiler.target.c64.MachineDefinition.ESTACK_LO_PLUS1_HEX
|
import prog8.compiler.target.c64.MachineDefinition.ESTACK_LO_PLUS1_HEX
|
@ -0,0 +1,745 @@
|
|||||||
|
package prog8.compiler.target.c64.codegen
|
||||||
|
|
||||||
|
import prog8.ast.Program
|
||||||
|
import prog8.ast.base.*
|
||||||
|
import prog8.ast.expressions.*
|
||||||
|
import prog8.ast.statements.AssignTarget
|
||||||
|
import prog8.ast.statements.Assignment
|
||||||
|
import prog8.ast.statements.DirectMemoryWrite
|
||||||
|
import prog8.ast.statements.VarDecl
|
||||||
|
import prog8.compiler.target.c64.MachineDefinition
|
||||||
|
import prog8.compiler.toHex
|
||||||
|
|
||||||
|
internal class AssignmentAsmGen(private val program: Program, private val asmgen: AsmGen) {
|
||||||
|
|
||||||
|
internal fun translate(assign: Assignment) {
|
||||||
|
if(assign.aug_op!=null)
|
||||||
|
throw AssemblyError("aug-op assignments should have been transformed to normal ones")
|
||||||
|
|
||||||
|
when(assign.value) {
|
||||||
|
is NumericLiteralValue -> {
|
||||||
|
val numVal = assign.value as NumericLiteralValue
|
||||||
|
when(numVal.type) {
|
||||||
|
DataType.UBYTE, DataType.BYTE -> assignFromByteConstant(assign.target, numVal.number.toShort())
|
||||||
|
DataType.UWORD, DataType.WORD -> assignFromWordConstant(assign.target, numVal.number.toInt())
|
||||||
|
DataType.FLOAT -> assignFromFloatConstant(assign.target, numVal.number.toDouble())
|
||||||
|
else -> throw AssemblyError("weird numval type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is RegisterExpr -> {
|
||||||
|
assignFromRegister(assign.target, (assign.value as RegisterExpr).register)
|
||||||
|
}
|
||||||
|
is IdentifierReference -> {
|
||||||
|
val type = assign.target.inferType(program, assign).typeOrElse(DataType.STRUCT)
|
||||||
|
when(type) {
|
||||||
|
DataType.UBYTE, DataType.BYTE -> assignFromByteVariable(assign.target, assign.value as IdentifierReference)
|
||||||
|
DataType.UWORD, DataType.WORD -> assignFromWordVariable(assign.target, assign.value as IdentifierReference)
|
||||||
|
DataType.FLOAT -> assignFromFloatVariable(assign.target, assign.value as IdentifierReference)
|
||||||
|
else -> throw AssemblyError("unsupported assignment target type $type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is AddressOf -> {
|
||||||
|
val identifier = (assign.value as AddressOf).identifier
|
||||||
|
assignFromAddressOf(assign.target, identifier)
|
||||||
|
}
|
||||||
|
is DirectMemoryRead -> {
|
||||||
|
val read = (assign.value as DirectMemoryRead)
|
||||||
|
when(read.addressExpression) {
|
||||||
|
is NumericLiteralValue -> {
|
||||||
|
val address = (read.addressExpression as NumericLiteralValue).number.toInt()
|
||||||
|
assignFromMemoryByte(assign.target, address, null)
|
||||||
|
}
|
||||||
|
is IdentifierReference -> {
|
||||||
|
assignFromMemoryByte(assign.target, null, read.addressExpression as IdentifierReference)
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
asmgen.translateExpression(read.addressExpression)
|
||||||
|
TODO("read memory byte from result and put that in ${assign.target}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is PrefixExpression -> {
|
||||||
|
// TODO optimize common cases
|
||||||
|
asmgen.translateExpression(assign.value as PrefixExpression)
|
||||||
|
assignFromEvalResult(assign.target)
|
||||||
|
}
|
||||||
|
is BinaryExpression -> {
|
||||||
|
// TODO optimize common cases
|
||||||
|
asmgen.translateExpression(assign.value as BinaryExpression)
|
||||||
|
assignFromEvalResult(assign.target)
|
||||||
|
}
|
||||||
|
is ArrayIndexedExpression -> {
|
||||||
|
// TODO optimize common cases
|
||||||
|
val arrayExpr = assign.value as ArrayIndexedExpression
|
||||||
|
val arrayDt = arrayExpr.identifier.targetVarDecl(program.namespace)!!.datatype
|
||||||
|
val index = arrayExpr.arrayspec.index
|
||||||
|
if(index is NumericLiteralValue) {
|
||||||
|
// constant array index value
|
||||||
|
val arrayVarName = asmgen.asmIdentifierName(arrayExpr.identifier)
|
||||||
|
val indexValue = index.number.toInt() * ArrayElementTypes.getValue(arrayDt).memorySize()
|
||||||
|
when (arrayDt) {
|
||||||
|
DataType.STR, DataType.STR_S, DataType.ARRAY_UB, DataType.ARRAY_B ->
|
||||||
|
asmgen.out(" lda $arrayVarName+$indexValue | sta ${MachineDefinition.ESTACK_LO_HEX},x | dex")
|
||||||
|
DataType.ARRAY_UW, DataType.ARRAY_W ->
|
||||||
|
asmgen.out(" lda $arrayVarName+$indexValue | sta ${MachineDefinition.ESTACK_LO_HEX},x | lda $arrayVarName+$indexValue+1 | sta ${MachineDefinition.ESTACK_HI_HEX},x | dex")
|
||||||
|
DataType.ARRAY_F ->
|
||||||
|
asmgen.out(" lda #<$arrayVarName+$indexValue | ldy #>$arrayVarName+$indexValue | jsr c64flt.push_float")
|
||||||
|
else ->
|
||||||
|
throw AssemblyError("weird array type")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
asmgen.translateArrayIndexIntoA(arrayExpr)
|
||||||
|
asmgen.readAndPushArrayvalueWithIndexA(arrayDt, arrayExpr.identifier)
|
||||||
|
}
|
||||||
|
assignFromEvalResult(assign.target)
|
||||||
|
}
|
||||||
|
is TypecastExpression -> {
|
||||||
|
val cast = assign.value as TypecastExpression
|
||||||
|
val sourceType = cast.expression.inferType(program)
|
||||||
|
val targetType = assign.target.inferType(program, assign)
|
||||||
|
if(sourceType.isKnown && targetType.isKnown &&
|
||||||
|
(sourceType.typeOrElse(DataType.STRUCT) in ByteDatatypes && targetType.typeOrElse(DataType.STRUCT) in ByteDatatypes) ||
|
||||||
|
(sourceType.typeOrElse(DataType.STRUCT) in WordDatatypes && targetType.typeOrElse(DataType.STRUCT) in WordDatatypes)) {
|
||||||
|
// no need for a type cast
|
||||||
|
assign.value = cast.expression
|
||||||
|
translate(assign)
|
||||||
|
} else {
|
||||||
|
asmgen.translateExpression(assign.value as TypecastExpression)
|
||||||
|
assignFromEvalResult(assign.target)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is FunctionCall -> {
|
||||||
|
asmgen.translateExpression(assign.value as FunctionCall)
|
||||||
|
assignFromEvalResult(assign.target)
|
||||||
|
}
|
||||||
|
is ArrayLiteralValue, is StringLiteralValue -> TODO("string/array/struct assignment?")
|
||||||
|
is StructLiteralValue -> throw AssemblyError("struct literal value assignment should have been flattened")
|
||||||
|
is RangeExpr -> throw AssemblyError("range expression should have been changed into array values")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun assignFromEvalResult(target: AssignTarget) {
|
||||||
|
val targetIdent = target.identifier
|
||||||
|
when {
|
||||||
|
target.register!=null -> {
|
||||||
|
if(target.register== Register.X)
|
||||||
|
throw AssemblyError("can't pop into X register - use variable instead")
|
||||||
|
asmgen.out(" inx | ld${target.register.name.toLowerCase()} ${MachineDefinition.ESTACK_LO_HEX},x ")
|
||||||
|
}
|
||||||
|
targetIdent!=null -> {
|
||||||
|
val targetName = asmgen.asmIdentifierName(targetIdent)
|
||||||
|
val targetDt = targetIdent.inferType(program).typeOrElse(DataType.STRUCT)
|
||||||
|
when(targetDt) {
|
||||||
|
DataType.UBYTE, DataType.BYTE -> {
|
||||||
|
asmgen.out(" inx | lda ${MachineDefinition.ESTACK_LO_HEX},x | sta $targetName")
|
||||||
|
}
|
||||||
|
DataType.UWORD, DataType.WORD -> {
|
||||||
|
asmgen.out("""
|
||||||
|
inx
|
||||||
|
lda ${MachineDefinition.ESTACK_LO_HEX},x
|
||||||
|
sta $targetName
|
||||||
|
lda ${MachineDefinition.ESTACK_HI_HEX},x
|
||||||
|
sta $targetName+1
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
DataType.FLOAT -> {
|
||||||
|
asmgen.out("""
|
||||||
|
lda #<$targetName
|
||||||
|
ldy #>$targetName
|
||||||
|
jsr c64flt.pop_float
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("weird target variable type $targetDt")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
target.memoryAddress!=null -> {
|
||||||
|
asmgen.out(" inx | ldy ${MachineDefinition.ESTACK_LO_HEX},x")
|
||||||
|
storeRegisterInMemoryAddress(Register.Y, target.memoryAddress)
|
||||||
|
}
|
||||||
|
target.arrayindexed!=null -> {
|
||||||
|
val arrayDt = target.arrayindexed!!.identifier.targetVarDecl(program.namespace)!!.datatype
|
||||||
|
val arrayVarName = asmgen.asmIdentifierName(target.arrayindexed!!.identifier)
|
||||||
|
asmgen.translateExpression(target.arrayindexed!!.arrayspec.index)
|
||||||
|
asmgen.out(" inx | lda ${MachineDefinition.ESTACK_LO_HEX},x")
|
||||||
|
popAndWriteArrayvalueWithIndexA(arrayDt, arrayVarName)
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("weird assignment target $target")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun assignFromAddressOf(target: AssignTarget, name: IdentifierReference) {
|
||||||
|
val targetIdent = target.identifier
|
||||||
|
val targetArrayIdx = target.arrayindexed
|
||||||
|
val struct = name.memberOfStruct(program.namespace)
|
||||||
|
val sourceName = if(struct!=null) {
|
||||||
|
// take the address of the first struct member instead
|
||||||
|
val decl = name.targetVarDecl(program.namespace)!!
|
||||||
|
val firstStructMember = struct.nameOfFirstMember()
|
||||||
|
// find the flattened var that belongs to this first struct member
|
||||||
|
val firstVarName = listOf(decl.name, firstStructMember)
|
||||||
|
val firstVar = name.definingScope().lookup(firstVarName, name) as VarDecl
|
||||||
|
firstVar.name
|
||||||
|
} else {
|
||||||
|
asmgen.fixNameSymbols(name.nameInSource.joinToString ("."))
|
||||||
|
}
|
||||||
|
|
||||||
|
when {
|
||||||
|
targetIdent!=null -> {
|
||||||
|
val targetName = asmgen.asmIdentifierName(targetIdent)
|
||||||
|
asmgen.out("""
|
||||||
|
lda #<$sourceName
|
||||||
|
ldy #>$sourceName
|
||||||
|
sta $targetName
|
||||||
|
sty $targetName+1
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
target.memoryAddress!=null -> {
|
||||||
|
TODO("assign address $sourceName to memory word $target")
|
||||||
|
}
|
||||||
|
targetArrayIdx!=null -> {
|
||||||
|
val index = targetArrayIdx.arrayspec.index
|
||||||
|
val targetName = asmgen.asmIdentifierName(targetArrayIdx.identifier)
|
||||||
|
TODO("assign address $sourceName to array $targetName [ $index ]")
|
||||||
|
}
|
||||||
|
else -> TODO("assign address $sourceName to $target")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun assignFromWordVariable(target: AssignTarget, variable: IdentifierReference) {
|
||||||
|
val sourceName = asmgen.asmIdentifierName(variable)
|
||||||
|
val targetIdent = target.identifier
|
||||||
|
val targetArrayIdx = target.arrayindexed
|
||||||
|
when {
|
||||||
|
targetIdent!=null -> {
|
||||||
|
val targetName = asmgen.asmIdentifierName(targetIdent)
|
||||||
|
asmgen.out("""
|
||||||
|
lda $sourceName
|
||||||
|
ldy $sourceName+1
|
||||||
|
sta $targetName
|
||||||
|
sty $targetName+1
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
target.memoryAddress!=null -> {
|
||||||
|
TODO("assign wordvar $sourceName to memory ${target.memoryAddress}")
|
||||||
|
}
|
||||||
|
targetArrayIdx!=null -> {
|
||||||
|
val index = targetArrayIdx.arrayspec.index
|
||||||
|
val targetName = asmgen.asmIdentifierName(targetArrayIdx.identifier)
|
||||||
|
asmgen.out(" lda $sourceName | sta ${MachineDefinition.ESTACK_LO_HEX},x | lda $sourceName+1 | sta ${MachineDefinition.ESTACK_HI_HEX},x | dex")
|
||||||
|
asmgen.translateExpression(index)
|
||||||
|
asmgen.out(" inx | lda ${MachineDefinition.ESTACK_LO_HEX},x")
|
||||||
|
val arrayDt = targetArrayIdx.identifier.inferType(program).typeOrElse(DataType.STRUCT)
|
||||||
|
popAndWriteArrayvalueWithIndexA(arrayDt, targetName)
|
||||||
|
}
|
||||||
|
else -> TODO("assign wordvar to $target")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun assignFromFloatVariable(target: AssignTarget, variable: IdentifierReference) {
|
||||||
|
val sourceName = asmgen.asmIdentifierName(variable)
|
||||||
|
val targetIdent = target.identifier
|
||||||
|
val targetArrayIdx = target.arrayindexed
|
||||||
|
when {
|
||||||
|
targetIdent!=null -> {
|
||||||
|
val targetName = asmgen.asmIdentifierName(targetIdent)
|
||||||
|
asmgen.out("""
|
||||||
|
lda $sourceName
|
||||||
|
sta $targetName
|
||||||
|
lda $sourceName+1
|
||||||
|
sta $targetName+1
|
||||||
|
lda $sourceName+2
|
||||||
|
sta $targetName+2
|
||||||
|
lda $sourceName+3
|
||||||
|
sta $targetName+3
|
||||||
|
lda $sourceName+4
|
||||||
|
sta $targetName+4
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
targetArrayIdx!=null -> {
|
||||||
|
val index = targetArrayIdx.arrayspec.index
|
||||||
|
val targetName = asmgen.asmIdentifierName(targetArrayIdx.identifier)
|
||||||
|
asmgen.out(" lda #<$sourceName | ldy #>$sourceName | jsr c64flt.push_float")
|
||||||
|
asmgen.translateExpression(index)
|
||||||
|
asmgen.out(" lda #<$targetName | ldy #>$targetName | jsr c64flt.pop_float_to_indexed_var")
|
||||||
|
}
|
||||||
|
else -> TODO("assign floatvar to $target")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun assignFromByteVariable(target: AssignTarget, variable: IdentifierReference) {
|
||||||
|
val sourceName = asmgen.asmIdentifierName(variable)
|
||||||
|
val targetIdent = target.identifier
|
||||||
|
val targetArrayIdx = target.arrayindexed
|
||||||
|
when {
|
||||||
|
target.register!=null -> {
|
||||||
|
asmgen.out(" ld${target.register.name.toLowerCase()} $sourceName")
|
||||||
|
}
|
||||||
|
targetIdent!=null -> {
|
||||||
|
val targetName = asmgen.asmIdentifierName(targetIdent)
|
||||||
|
asmgen.out("""
|
||||||
|
lda $sourceName
|
||||||
|
sta $targetName
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
targetArrayIdx!=null -> {
|
||||||
|
val index = targetArrayIdx.arrayspec.index
|
||||||
|
val targetName = asmgen.asmIdentifierName(targetArrayIdx.identifier)
|
||||||
|
val arrayDt = targetArrayIdx.identifier.inferType(program).typeOrElse(DataType.STRUCT)
|
||||||
|
asmgen.out(" lda $sourceName | sta ${MachineDefinition.ESTACK_LO_HEX},x | dex")
|
||||||
|
asmgen.translateExpression(index)
|
||||||
|
asmgen.out(" inx | lda ${MachineDefinition.ESTACK_LO_HEX},x")
|
||||||
|
popAndWriteArrayvalueWithIndexA(arrayDt, targetName)
|
||||||
|
}
|
||||||
|
target.memoryAddress != null -> {
|
||||||
|
val addressExpr = target.memoryAddress.addressExpression
|
||||||
|
val addressLv = addressExpr as? NumericLiteralValue
|
||||||
|
when {
|
||||||
|
addressLv != null -> asmgen.out(" lda $sourceName | sta ${addressLv.number.toHex()}")
|
||||||
|
addressExpr is IdentifierReference -> {
|
||||||
|
val targetName = asmgen.asmIdentifierName(addressExpr)
|
||||||
|
asmgen.out(" lda $sourceName | sta $targetName")
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
asmgen.translateExpression(addressExpr)
|
||||||
|
asmgen.out("""
|
||||||
|
inx
|
||||||
|
lda ${MachineDefinition.ESTACK_LO_HEX},x
|
||||||
|
ldy ${MachineDefinition.ESTACK_HI_HEX},x
|
||||||
|
sta (+) +1
|
||||||
|
sty (+) +2
|
||||||
|
lda $sourceName
|
||||||
|
+ sta ${65535.toHex()} ; modified
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> TODO("assign bytevar to $target")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun assignFromRegister(target: AssignTarget, register: Register) {
|
||||||
|
val targetIdent = target.identifier
|
||||||
|
val targetArrayIdx = target.arrayindexed
|
||||||
|
when {
|
||||||
|
targetIdent!=null -> {
|
||||||
|
val targetName = asmgen.asmIdentifierName(targetIdent)
|
||||||
|
asmgen.out(" st${register.name.toLowerCase()} $targetName")
|
||||||
|
}
|
||||||
|
target.register!=null -> {
|
||||||
|
when(register) {
|
||||||
|
Register.A -> when(target.register) {
|
||||||
|
Register.A -> {}
|
||||||
|
Register.X -> asmgen.out(" tax")
|
||||||
|
Register.Y -> asmgen.out(" tay")
|
||||||
|
}
|
||||||
|
Register.X -> when(target.register) {
|
||||||
|
Register.A -> asmgen.out(" txa")
|
||||||
|
Register.X -> {}
|
||||||
|
Register.Y -> asmgen.out(" txy")
|
||||||
|
}
|
||||||
|
Register.Y -> when(target.register) {
|
||||||
|
Register.A -> asmgen.out(" tya")
|
||||||
|
Register.X -> asmgen.out(" tyx")
|
||||||
|
Register.Y -> {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
target.memoryAddress!=null -> {
|
||||||
|
storeRegisterInMemoryAddress(register, target.memoryAddress)
|
||||||
|
}
|
||||||
|
targetArrayIdx!=null -> {
|
||||||
|
val index = targetArrayIdx.arrayspec.index
|
||||||
|
val targetName = asmgen.asmIdentifierName(targetArrayIdx.identifier)
|
||||||
|
when (index) {
|
||||||
|
is NumericLiteralValue -> {
|
||||||
|
val memindex = index.number.toInt()
|
||||||
|
when(register) {
|
||||||
|
Register.A -> asmgen.out(" sta $targetName+$memindex")
|
||||||
|
Register.X -> asmgen.out(" stx $targetName+$memindex")
|
||||||
|
Register.Y -> asmgen.out(" sty $targetName+$memindex")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is RegisterExpr -> {
|
||||||
|
when(register) {
|
||||||
|
Register.A -> asmgen.out(" sta ${MachineDefinition.C64Zeropage.SCRATCH_B1}")
|
||||||
|
Register.X -> asmgen.out(" stx ${MachineDefinition.C64Zeropage.SCRATCH_B1}")
|
||||||
|
Register.Y -> asmgen.out(" sty ${MachineDefinition.C64Zeropage.SCRATCH_B1}")
|
||||||
|
}
|
||||||
|
when(index.register) {
|
||||||
|
Register.A -> {}
|
||||||
|
Register.X -> asmgen.out(" txa")
|
||||||
|
Register.Y -> asmgen.out(" tya")
|
||||||
|
}
|
||||||
|
asmgen.out("""
|
||||||
|
tay
|
||||||
|
lda ${MachineDefinition.C64Zeropage.SCRATCH_B1}
|
||||||
|
sta $targetName,y
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
is IdentifierReference -> {
|
||||||
|
when(register) {
|
||||||
|
Register.A -> asmgen.out(" sta ${MachineDefinition.C64Zeropage.SCRATCH_B1}")
|
||||||
|
Register.X -> asmgen.out(" stx ${MachineDefinition.C64Zeropage.SCRATCH_B1}")
|
||||||
|
Register.Y -> asmgen.out(" sty ${MachineDefinition.C64Zeropage.SCRATCH_B1}")
|
||||||
|
}
|
||||||
|
asmgen.out("""
|
||||||
|
lda ${asmgen.asmIdentifierName(index)}
|
||||||
|
tay
|
||||||
|
lda ${MachineDefinition.C64Zeropage.SCRATCH_B1}
|
||||||
|
sta $targetName,y
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
asmgen.saveRegister(register)
|
||||||
|
asmgen.translateExpression(index)
|
||||||
|
asmgen.restoreRegister(register)
|
||||||
|
when(register) {
|
||||||
|
Register.A -> asmgen.out(" sta ${MachineDefinition.C64Zeropage.SCRATCH_B1}")
|
||||||
|
Register.X -> asmgen.out(" stx ${MachineDefinition.C64Zeropage.SCRATCH_B1}")
|
||||||
|
Register.Y -> asmgen.out(" sty ${MachineDefinition.C64Zeropage.SCRATCH_B1}")
|
||||||
|
}
|
||||||
|
asmgen.out("""
|
||||||
|
inx
|
||||||
|
lda ${MachineDefinition.ESTACK_LO_HEX},x
|
||||||
|
tay
|
||||||
|
lda ${MachineDefinition.C64Zeropage.SCRATCH_B1}
|
||||||
|
sta $targetName,y
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> TODO("assign register $register to $target")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun storeRegisterInMemoryAddress(register: Register, memoryAddress: DirectMemoryWrite) {
|
||||||
|
val addressExpr = memoryAddress.addressExpression
|
||||||
|
val addressLv = addressExpr as? NumericLiteralValue
|
||||||
|
val registerName = register.name.toLowerCase()
|
||||||
|
when {
|
||||||
|
addressLv != null -> asmgen.out(" st$registerName ${addressLv.number.toHex()}")
|
||||||
|
addressExpr is IdentifierReference -> {
|
||||||
|
val targetName = asmgen.asmIdentifierName(addressExpr)
|
||||||
|
when(register) {
|
||||||
|
Register.A -> asmgen.out("""
|
||||||
|
ldy $targetName
|
||||||
|
sty ${MachineDefinition.C64Zeropage.SCRATCH_W1}
|
||||||
|
ldy $targetName+1
|
||||||
|
sty ${MachineDefinition.C64Zeropage.SCRATCH_W1+1}
|
||||||
|
ldy #0
|
||||||
|
sta (${MachineDefinition.C64Zeropage.SCRATCH_W1}),y
|
||||||
|
""")
|
||||||
|
Register.X -> asmgen.out("""
|
||||||
|
txa
|
||||||
|
ldy $targetName
|
||||||
|
sty ${MachineDefinition.C64Zeropage.SCRATCH_W1}
|
||||||
|
ldy $targetName+1
|
||||||
|
sty ${MachineDefinition.C64Zeropage.SCRATCH_W1+1}
|
||||||
|
ldy #0
|
||||||
|
sta (${MachineDefinition.C64Zeropage.SCRATCH_W1}),y
|
||||||
|
""")
|
||||||
|
Register.Y -> asmgen.out("""
|
||||||
|
tya
|
||||||
|
ldy $targetName
|
||||||
|
sty ${MachineDefinition.C64Zeropage.SCRATCH_W1}
|
||||||
|
ldy $targetName+1
|
||||||
|
sty ${MachineDefinition.C64Zeropage.SCRATCH_W1+1}
|
||||||
|
ldy #0
|
||||||
|
sta (${MachineDefinition.C64Zeropage.SCRATCH_W1}),y
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
asmgen.saveRegister(register)
|
||||||
|
asmgen.translateExpression(addressExpr)
|
||||||
|
asmgen.restoreRegister(register)
|
||||||
|
when (register) {
|
||||||
|
Register.A -> asmgen.out(" tay")
|
||||||
|
Register.X -> throw AssemblyError("can't use X register here")
|
||||||
|
Register.Y -> {}
|
||||||
|
}
|
||||||
|
asmgen.out("""
|
||||||
|
inx
|
||||||
|
lda ${MachineDefinition.ESTACK_LO_HEX},x
|
||||||
|
sta (+) +1
|
||||||
|
lda ${MachineDefinition.ESTACK_HI_HEX},x
|
||||||
|
sta (+) +2
|
||||||
|
+ sty ${65535.toHex()} ; modified
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun assignFromWordConstant(target: AssignTarget, word: Int) {
|
||||||
|
val targetIdent = target.identifier
|
||||||
|
val targetArrayIdx = target.arrayindexed
|
||||||
|
when {
|
||||||
|
targetIdent!=null -> {
|
||||||
|
val targetName = asmgen.asmIdentifierName(targetIdent)
|
||||||
|
if(word ushr 8 == word and 255) {
|
||||||
|
// lsb=msb
|
||||||
|
asmgen.out("""
|
||||||
|
lda #${(word and 255).toHex()}
|
||||||
|
sta $targetName
|
||||||
|
sta $targetName+1
|
||||||
|
""")
|
||||||
|
} else {
|
||||||
|
asmgen.out("""
|
||||||
|
lda #<${word.toHex()}
|
||||||
|
ldy #>${word.toHex()}
|
||||||
|
sta $targetName
|
||||||
|
sty $targetName+1
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
target.memoryAddress!=null -> {
|
||||||
|
TODO("assign word $word to memory ${target.memoryAddress}")
|
||||||
|
}
|
||||||
|
targetArrayIdx!=null -> {
|
||||||
|
val index = targetArrayIdx.arrayspec.index
|
||||||
|
val targetName = asmgen.asmIdentifierName(targetArrayIdx.identifier)
|
||||||
|
// TODO optimize common cases
|
||||||
|
asmgen.translateExpression(index)
|
||||||
|
asmgen.out("""
|
||||||
|
inx
|
||||||
|
lda ${MachineDefinition.ESTACK_LO_HEX},x
|
||||||
|
asl a
|
||||||
|
tay
|
||||||
|
lda #<${word.toHex()}
|
||||||
|
sta $targetName,y
|
||||||
|
lda #>${word.toHex()}
|
||||||
|
sta $targetName+1,y
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
else -> TODO("assign word $word to $target")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun assignFromByteConstant(target: AssignTarget, byte: Short) {
|
||||||
|
val targetIdent = target.identifier
|
||||||
|
val targetArrayIdx = target.arrayindexed
|
||||||
|
when {
|
||||||
|
target.register!=null -> {
|
||||||
|
asmgen.out(" ld${target.register.name.toLowerCase()} #${byte.toHex()}")
|
||||||
|
}
|
||||||
|
targetIdent!=null -> {
|
||||||
|
val targetName = asmgen.asmIdentifierName(targetIdent)
|
||||||
|
asmgen.out(" lda #${byte.toHex()} | sta $targetName ")
|
||||||
|
}
|
||||||
|
target.memoryAddress!=null -> {
|
||||||
|
asmgen.out(" ldy #${byte.toHex()}")
|
||||||
|
storeRegisterInMemoryAddress(Register.Y, target.memoryAddress)
|
||||||
|
}
|
||||||
|
targetArrayIdx!=null -> {
|
||||||
|
val index = targetArrayIdx.arrayspec.index
|
||||||
|
val targetName = asmgen.asmIdentifierName(targetArrayIdx.identifier)
|
||||||
|
// TODO optimize common cases
|
||||||
|
asmgen.translateExpression(index)
|
||||||
|
asmgen.out("""
|
||||||
|
inx
|
||||||
|
ldy ${MachineDefinition.ESTACK_LO_HEX},x
|
||||||
|
lda #${byte.toHex()}
|
||||||
|
sta $targetName,y
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
else -> TODO("assign byte $byte to $target")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun assignFromFloatConstant(target: AssignTarget, float: Double) {
|
||||||
|
val targetIdent = target.identifier
|
||||||
|
val targetArrayIdx = target.arrayindexed
|
||||||
|
if(float==0.0) {
|
||||||
|
// optimized case for float zero
|
||||||
|
when {
|
||||||
|
targetIdent != null -> {
|
||||||
|
val targetName = asmgen.asmIdentifierName(targetIdent)
|
||||||
|
asmgen.out("""
|
||||||
|
lda #0
|
||||||
|
sta $targetName
|
||||||
|
sta $targetName+1
|
||||||
|
sta $targetName+2
|
||||||
|
sta $targetName+3
|
||||||
|
sta $targetName+4
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
targetArrayIdx!=null -> {
|
||||||
|
val index = targetArrayIdx.arrayspec.index
|
||||||
|
val targetName = asmgen.asmIdentifierName(targetArrayIdx.identifier)
|
||||||
|
if(index is NumericLiteralValue) {
|
||||||
|
val indexValue = index.number.toInt() * MachineDefinition.Mflpt5.MemorySize
|
||||||
|
asmgen.out("""
|
||||||
|
lda #0
|
||||||
|
sta $targetName+$indexValue
|
||||||
|
sta $targetName+$indexValue+1
|
||||||
|
sta $targetName+$indexValue+2
|
||||||
|
sta $targetName+$indexValue+3
|
||||||
|
sta $targetName+$indexValue+4
|
||||||
|
""")
|
||||||
|
} else {
|
||||||
|
asmgen.translateExpression(index)
|
||||||
|
asmgen.out("""
|
||||||
|
inx
|
||||||
|
lda ${MachineDefinition.ESTACK_LO_HEX},x
|
||||||
|
asl a
|
||||||
|
asl a
|
||||||
|
clc
|
||||||
|
adc ${MachineDefinition.ESTACK_LO_HEX},x
|
||||||
|
tay
|
||||||
|
lda #0
|
||||||
|
sta $targetName,y
|
||||||
|
sta $targetName+1,y
|
||||||
|
sta $targetName+2,y
|
||||||
|
sta $targetName+3,y
|
||||||
|
sta $targetName+4,y
|
||||||
|
""") // TODO use a subroutine for this
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> TODO("assign float 0.0 to $target")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// non-zero value
|
||||||
|
val constFloat = asmgen.getFloatConst(float)
|
||||||
|
when {
|
||||||
|
targetIdent != null -> {
|
||||||
|
val targetName = asmgen.asmIdentifierName(targetIdent)
|
||||||
|
asmgen.out("""
|
||||||
|
lda $constFloat
|
||||||
|
sta $targetName
|
||||||
|
lda $constFloat+1
|
||||||
|
sta $targetName+1
|
||||||
|
lda $constFloat+2
|
||||||
|
sta $targetName+2
|
||||||
|
lda $constFloat+3
|
||||||
|
sta $targetName+3
|
||||||
|
lda $constFloat+4
|
||||||
|
sta $targetName+4
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
targetArrayIdx!=null -> {
|
||||||
|
val index = targetArrayIdx.arrayspec.index
|
||||||
|
val arrayVarName = asmgen.asmIdentifierName(targetArrayIdx.identifier)
|
||||||
|
if(index is NumericLiteralValue) {
|
||||||
|
val indexValue = index.number.toInt() * MachineDefinition.Mflpt5.MemorySize
|
||||||
|
asmgen.out("""
|
||||||
|
lda $constFloat
|
||||||
|
sta $arrayVarName+$indexValue
|
||||||
|
lda $constFloat+1
|
||||||
|
sta $arrayVarName+$indexValue+1
|
||||||
|
lda $constFloat+2
|
||||||
|
sta $arrayVarName+$indexValue+2
|
||||||
|
lda $constFloat+3
|
||||||
|
sta $arrayVarName+$indexValue+3
|
||||||
|
lda $constFloat+4
|
||||||
|
sta $arrayVarName+$indexValue+4
|
||||||
|
""")
|
||||||
|
} else {
|
||||||
|
asmgen.translateArrayIndexIntoA(targetArrayIdx)
|
||||||
|
asmgen.out("""
|
||||||
|
sta ${MachineDefinition.C64Zeropage.SCRATCH_REG}
|
||||||
|
asl a
|
||||||
|
asl a
|
||||||
|
clc
|
||||||
|
adc ${MachineDefinition.C64Zeropage.SCRATCH_REG}
|
||||||
|
tay
|
||||||
|
lda $constFloat
|
||||||
|
sta $arrayVarName,y
|
||||||
|
lda $constFloat+1
|
||||||
|
sta $arrayVarName+1,y
|
||||||
|
lda $constFloat+2
|
||||||
|
sta $arrayVarName+2,y
|
||||||
|
lda $constFloat+3
|
||||||
|
sta $arrayVarName+3,y
|
||||||
|
lda $constFloat+4
|
||||||
|
sta $arrayVarName+4,y
|
||||||
|
""") // TODO use a subroutine for this
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> TODO("assign float $float to $target")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun assignFromMemoryByte(target: AssignTarget, address: Int?, identifier: IdentifierReference?) {
|
||||||
|
val targetIdent = target.identifier
|
||||||
|
val targetArrayIdx = target.arrayindexed
|
||||||
|
if(address!=null) {
|
||||||
|
when {
|
||||||
|
target.register!=null -> {
|
||||||
|
asmgen.out(" ld${target.register.name.toLowerCase()} ${address.toHex()}")
|
||||||
|
}
|
||||||
|
targetIdent!=null -> {
|
||||||
|
val targetName = asmgen.asmIdentifierName(targetIdent)
|
||||||
|
asmgen.out("""
|
||||||
|
lda ${address.toHex()}
|
||||||
|
sta $targetName
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
target.memoryAddress!=null -> {
|
||||||
|
asmgen.out(" ldy ${address.toHex()}")
|
||||||
|
storeRegisterInMemoryAddress(Register.Y, target.memoryAddress)
|
||||||
|
}
|
||||||
|
targetArrayIdx!=null -> {
|
||||||
|
val index = targetArrayIdx.arrayspec.index
|
||||||
|
val targetName = asmgen.asmIdentifierName(targetArrayIdx.identifier)
|
||||||
|
TODO("assign memory byte at $address to array $targetName [ $index ]")
|
||||||
|
}
|
||||||
|
else -> TODO("assign memory byte $target")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(identifier!=null) {
|
||||||
|
val sourceName = asmgen.asmIdentifierName(identifier)
|
||||||
|
when {
|
||||||
|
target.register!=null -> {
|
||||||
|
asmgen.out("""
|
||||||
|
ldy #0
|
||||||
|
lda ($sourceName),y
|
||||||
|
""")
|
||||||
|
when(target.register){
|
||||||
|
Register.A -> {}
|
||||||
|
Register.X -> asmgen.out(" tax")
|
||||||
|
Register.Y -> asmgen.out(" tay")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
targetIdent!=null -> {
|
||||||
|
val targetName = asmgen.asmIdentifierName(targetIdent)
|
||||||
|
asmgen.out("""
|
||||||
|
ldy #0
|
||||||
|
lda ($sourceName),y
|
||||||
|
sta $targetName
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
target.memoryAddress!=null -> {
|
||||||
|
asmgen.out(" ldy $sourceName")
|
||||||
|
storeRegisterInMemoryAddress(Register.Y, target.memoryAddress)
|
||||||
|
}
|
||||||
|
targetArrayIdx!=null -> {
|
||||||
|
val index = targetArrayIdx.arrayspec.index
|
||||||
|
val targetName = asmgen.asmIdentifierName(targetArrayIdx.identifier)
|
||||||
|
TODO("assign memory byte $sourceName to array $targetName [ $index ]")
|
||||||
|
}
|
||||||
|
else -> TODO("assign memory byte $target")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun popAndWriteArrayvalueWithIndexA(arrayDt: DataType, variablename: String) {
|
||||||
|
when (arrayDt) {
|
||||||
|
DataType.STR, DataType.STR_S, DataType.ARRAY_UB, DataType.ARRAY_B ->
|
||||||
|
asmgen.out(" tay | inx | lda ${MachineDefinition.ESTACK_LO_HEX},x | sta $variablename,y")
|
||||||
|
DataType.ARRAY_UW, DataType.ARRAY_W ->
|
||||||
|
asmgen.out(" asl a | tay | inx | lda ${MachineDefinition.ESTACK_LO_HEX},x | sta $variablename,y | lda ${MachineDefinition.ESTACK_HI_HEX},x | sta $variablename+1,y")
|
||||||
|
DataType.ARRAY_F ->
|
||||||
|
// index * 5 is done in the subroutine that's called
|
||||||
|
asmgen.out("""
|
||||||
|
sta ${MachineDefinition.ESTACK_LO_HEX},x
|
||||||
|
dex
|
||||||
|
lda #<$variablename
|
||||||
|
ldy #>$variablename
|
||||||
|
jsr c64flt.pop_float_to_indexed_var
|
||||||
|
""")
|
||||||
|
else ->
|
||||||
|
throw AssemblyError("weird array type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package prog8.compiler.target.c64.codegen2
|
package prog8.compiler.target.c64.codegen
|
||||||
|
|
||||||
import prog8.ast.IFunctionCall
|
import prog8.ast.IFunctionCall
|
||||||
import prog8.ast.Program
|
import prog8.ast.Program
|
||||||
@ -9,8 +9,6 @@ import prog8.ast.base.WordDatatypes
|
|||||||
import prog8.ast.expressions.*
|
import prog8.ast.expressions.*
|
||||||
import prog8.ast.statements.AssignTarget
|
import prog8.ast.statements.AssignTarget
|
||||||
import prog8.ast.statements.FunctionCallStatement
|
import prog8.ast.statements.FunctionCallStatement
|
||||||
import prog8.compiler.CompilationOptions
|
|
||||||
import prog8.compiler.Zeropage
|
|
||||||
import prog8.compiler.target.c64.MachineDefinition.ESTACK_HI_HEX
|
import prog8.compiler.target.c64.MachineDefinition.ESTACK_HI_HEX
|
||||||
import prog8.compiler.target.c64.MachineDefinition.ESTACK_HI_PLUS1_HEX
|
import prog8.compiler.target.c64.MachineDefinition.ESTACK_HI_PLUS1_HEX
|
||||||
import prog8.compiler.target.c64.MachineDefinition.ESTACK_LO_HEX
|
import prog8.compiler.target.c64.MachineDefinition.ESTACK_LO_HEX
|
||||||
@ -18,10 +16,7 @@ import prog8.compiler.target.c64.MachineDefinition.ESTACK_LO_PLUS1_HEX
|
|||||||
import prog8.compiler.toHex
|
import prog8.compiler.toHex
|
||||||
import prog8.functions.FunctionSignature
|
import prog8.functions.FunctionSignature
|
||||||
|
|
||||||
internal class BuiltinFunctionsAsmGen(private val program: Program,
|
internal class BuiltinFunctionsAsmGen(private val program: Program, private val asmgen: AsmGen) {
|
||||||
private val options: CompilationOptions,
|
|
||||||
private val zeropage: Zeropage,
|
|
||||||
private val asmgen: AsmGen2) {
|
|
||||||
|
|
||||||
internal fun translateFunctioncallExpression(fcall: FunctionCall, func: FunctionSignature) {
|
internal fun translateFunctioncallExpression(fcall: FunctionCall, func: FunctionSignature) {
|
||||||
translateFunctioncall(fcall, func, false)
|
translateFunctioncall(fcall, func, false)
|
||||||
@ -33,21 +28,21 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
|
|||||||
|
|
||||||
private fun translateFunctioncall(fcall: IFunctionCall, func: FunctionSignature, discardResult: Boolean) {
|
private fun translateFunctioncall(fcall: IFunctionCall, func: FunctionSignature, discardResult: Boolean) {
|
||||||
val functionName = fcall.target.nameInSource.last()
|
val functionName = fcall.target.nameInSource.last()
|
||||||
if(discardResult) {
|
if (discardResult) {
|
||||||
if(func.pure)
|
if (func.pure)
|
||||||
return // can just ignore the whole function call altogether
|
return // can just ignore the whole function call altogether
|
||||||
else if(func.returntype!=null)
|
else if (func.returntype != null)
|
||||||
throw AssemblyError("discarding result of non-pure function $fcall")
|
throw AssemblyError("discarding result of non-pure function $fcall")
|
||||||
}
|
}
|
||||||
|
|
||||||
when(functionName) {
|
when (functionName) {
|
||||||
"msb" -> {
|
"msb" -> {
|
||||||
val arg = fcall.arglist.single()
|
val arg = fcall.arglist.single()
|
||||||
if(arg.inferType(program) !in WordDatatypes)
|
if (arg.inferType(program).typeOrElse(DataType.STRUCT) !in WordDatatypes)
|
||||||
throw AssemblyError("msb required word argument")
|
throw AssemblyError("msb required word argument")
|
||||||
if(arg is NumericLiteralValue)
|
if (arg is NumericLiteralValue)
|
||||||
throw AssemblyError("should have been const-folded")
|
throw AssemblyError("should have been const-folded")
|
||||||
if(arg is IdentifierReference) {
|
if (arg is IdentifierReference) {
|
||||||
val sourceName = asmgen.asmIdentifierName(arg)
|
val sourceName = asmgen.asmIdentifierName(arg)
|
||||||
asmgen.out(" lda $sourceName+1 | sta $ESTACK_LO_HEX,x | dex")
|
asmgen.out(" lda $sourceName+1 | sta $ESTACK_LO_HEX,x | dex")
|
||||||
} else {
|
} else {
|
||||||
@ -61,8 +56,8 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
|
|||||||
}
|
}
|
||||||
"abs" -> {
|
"abs" -> {
|
||||||
translateFunctionArguments(fcall.arglist, func)
|
translateFunctionArguments(fcall.arglist, func)
|
||||||
val dt = fcall.arglist.single().inferType(program)!!
|
val dt = fcall.arglist.single().inferType(program)
|
||||||
when (dt) {
|
when (dt.typeOrElse(DataType.STRUCT)) {
|
||||||
in ByteDatatypes -> asmgen.out(" jsr prog8_lib.abs_b")
|
in ByteDatatypes -> asmgen.out(" jsr prog8_lib.abs_b")
|
||||||
in WordDatatypes -> asmgen.out(" jsr prog8_lib.abs_w")
|
in WordDatatypes -> asmgen.out(" jsr prog8_lib.abs_w")
|
||||||
DataType.FLOAT -> asmgen.out(" jsr c64flt.abs_f")
|
DataType.FLOAT -> asmgen.out(" jsr c64flt.abs_f")
|
||||||
@ -86,8 +81,8 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
|
|||||||
}
|
}
|
||||||
"min", "max", "sum" -> {
|
"min", "max", "sum" -> {
|
||||||
outputPushAddressAndLenghtOfArray(fcall.arglist[0])
|
outputPushAddressAndLenghtOfArray(fcall.arglist[0])
|
||||||
val dt = fcall.arglist.single().inferType(program)!!
|
val dt = fcall.arglist.single().inferType(program)
|
||||||
when(dt) {
|
when (dt.typeOrElse(DataType.STRUCT)) {
|
||||||
DataType.ARRAY_UB, DataType.STR_S, DataType.STR -> asmgen.out(" jsr prog8_lib.func_${functionName}_ub")
|
DataType.ARRAY_UB, DataType.STR_S, DataType.STR -> asmgen.out(" jsr prog8_lib.func_${functionName}_ub")
|
||||||
DataType.ARRAY_B -> asmgen.out(" jsr prog8_lib.func_${functionName}_b")
|
DataType.ARRAY_B -> asmgen.out(" jsr prog8_lib.func_${functionName}_b")
|
||||||
DataType.ARRAY_UW -> asmgen.out(" jsr prog8_lib.func_${functionName}_uw")
|
DataType.ARRAY_UW -> asmgen.out(" jsr prog8_lib.func_${functionName}_uw")
|
||||||
@ -98,14 +93,26 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
|
|||||||
}
|
}
|
||||||
"any", "all" -> {
|
"any", "all" -> {
|
||||||
outputPushAddressAndLenghtOfArray(fcall.arglist[0])
|
outputPushAddressAndLenghtOfArray(fcall.arglist[0])
|
||||||
val dt = fcall.arglist.single().inferType(program)!!
|
val dt = fcall.arglist.single().inferType(program)
|
||||||
when(dt) {
|
when (dt.typeOrElse(DataType.STRUCT)) {
|
||||||
DataType.ARRAY_B, DataType.ARRAY_UB, DataType.STR_S, DataType.STR -> asmgen.out(" jsr prog8_lib.func_${functionName}_b")
|
DataType.ARRAY_B, DataType.ARRAY_UB, DataType.STR_S, DataType.STR -> asmgen.out(" jsr prog8_lib.func_${functionName}_b")
|
||||||
DataType.ARRAY_UW, DataType.ARRAY_W -> asmgen.out(" jsr prog8_lib.func_${functionName}_w")
|
DataType.ARRAY_UW, DataType.ARRAY_W -> asmgen.out(" jsr prog8_lib.func_${functionName}_w")
|
||||||
DataType.ARRAY_F -> asmgen.out(" jsr c64flt.func_${functionName}_f")
|
DataType.ARRAY_F -> asmgen.out(" jsr c64flt.func_${functionName}_f")
|
||||||
else -> throw AssemblyError("weird type $dt")
|
else -> throw AssemblyError("weird type $dt")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
"sgn" -> {
|
||||||
|
translateFunctionArguments(fcall.arglist, func)
|
||||||
|
val dt = fcall.arglist.single().inferType(program)
|
||||||
|
when(dt.typeOrElse(DataType.STRUCT)) {
|
||||||
|
DataType.UBYTE -> asmgen.out(" jsr math.sign_ub")
|
||||||
|
DataType.BYTE -> asmgen.out(" jsr math.sign_b")
|
||||||
|
DataType.UWORD -> asmgen.out(" jsr math.sign_uw")
|
||||||
|
DataType.WORD -> asmgen.out(" jsr math.sign_w")
|
||||||
|
DataType.FLOAT -> asmgen.out(" jsr c64flt.sign_f")
|
||||||
|
else -> throw AssemblyError("weird type $dt")
|
||||||
|
}
|
||||||
|
}
|
||||||
"sin", "cos", "tan", "atan",
|
"sin", "cos", "tan", "atan",
|
||||||
"ln", "log2", "sqrt", "rad",
|
"ln", "log2", "sqrt", "rad",
|
||||||
"deg", "round", "floor", "ceil",
|
"deg", "round", "floor", "ceil",
|
||||||
@ -134,12 +141,12 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
|
|||||||
"lsl" -> {
|
"lsl" -> {
|
||||||
// in-place
|
// in-place
|
||||||
val what = fcall.arglist.single()
|
val what = fcall.arglist.single()
|
||||||
val dt = what.inferType(program)!!
|
val dt = what.inferType(program)
|
||||||
when(dt) {
|
when (dt.typeOrElse(DataType.STRUCT)) {
|
||||||
in ByteDatatypes -> {
|
in ByteDatatypes -> {
|
||||||
when(what) {
|
when (what) {
|
||||||
is RegisterExpr -> {
|
is RegisterExpr -> {
|
||||||
when(what.register) {
|
when (what.register) {
|
||||||
Register.A -> asmgen.out(" asl a")
|
Register.A -> asmgen.out(" asl a")
|
||||||
Register.X -> asmgen.out(" txa | asl a | tax")
|
Register.X -> asmgen.out(" txa | asl a | tax")
|
||||||
Register.Y -> asmgen.out(" tya | asl a | tay")
|
Register.Y -> asmgen.out(" tya | asl a | tay")
|
||||||
@ -147,7 +154,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
|
|||||||
}
|
}
|
||||||
is IdentifierReference -> asmgen.out(" asl ${asmgen.asmIdentifierName(what)}")
|
is IdentifierReference -> asmgen.out(" asl ${asmgen.asmIdentifierName(what)}")
|
||||||
is DirectMemoryRead -> {
|
is DirectMemoryRead -> {
|
||||||
if(what.addressExpression is NumericLiteralValue) {
|
if (what.addressExpression is NumericLiteralValue) {
|
||||||
asmgen.out(" asl ${(what.addressExpression as NumericLiteralValue).number.toHex()}")
|
asmgen.out(" asl ${(what.addressExpression as NumericLiteralValue).number.toHex()}")
|
||||||
} else {
|
} else {
|
||||||
TODO("lsl memory byte $what")
|
TODO("lsl memory byte $what")
|
||||||
@ -160,7 +167,14 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
in WordDatatypes -> {
|
in WordDatatypes -> {
|
||||||
TODO("lsl word $what")
|
when (what) {
|
||||||
|
is ArrayIndexedExpression -> TODO("lsl sbyte $what")
|
||||||
|
is IdentifierReference -> {
|
||||||
|
val variable = asmgen.asmIdentifierName(what)
|
||||||
|
asmgen.out(" asl $variable | rol $variable+1")
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("weird type")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else -> throw AssemblyError("weird type")
|
else -> throw AssemblyError("weird type")
|
||||||
}
|
}
|
||||||
@ -168,12 +182,12 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
|
|||||||
"lsr" -> {
|
"lsr" -> {
|
||||||
// in-place
|
// in-place
|
||||||
val what = fcall.arglist.single()
|
val what = fcall.arglist.single()
|
||||||
val dt = what.inferType(program)!!
|
val dt = what.inferType(program)
|
||||||
when(dt) {
|
when (dt.typeOrElse(DataType.STRUCT)) {
|
||||||
DataType.UBYTE -> {
|
DataType.UBYTE -> {
|
||||||
when(what) {
|
when (what) {
|
||||||
is RegisterExpr -> {
|
is RegisterExpr -> {
|
||||||
when(what.register) {
|
when (what.register) {
|
||||||
Register.A -> asmgen.out(" lsr a")
|
Register.A -> asmgen.out(" lsr a")
|
||||||
Register.X -> asmgen.out(" txa | lsr a | tax")
|
Register.X -> asmgen.out(" txa | lsr a | tax")
|
||||||
Register.Y -> asmgen.out(" tya | lsr a | tay")
|
Register.Y -> asmgen.out(" tya | lsr a | tay")
|
||||||
@ -181,7 +195,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
|
|||||||
}
|
}
|
||||||
is IdentifierReference -> asmgen.out(" lsr ${asmgen.asmIdentifierName(what)}")
|
is IdentifierReference -> asmgen.out(" lsr ${asmgen.asmIdentifierName(what)}")
|
||||||
is DirectMemoryRead -> {
|
is DirectMemoryRead -> {
|
||||||
if(what.addressExpression is NumericLiteralValue) {
|
if (what.addressExpression is NumericLiteralValue) {
|
||||||
asmgen.out(" lsr ${(what.addressExpression as NumericLiteralValue).number.toHex()}")
|
asmgen.out(" lsr ${(what.addressExpression as NumericLiteralValue).number.toHex()}")
|
||||||
} else {
|
} else {
|
||||||
TODO("lsr memory byte $what")
|
TODO("lsr memory byte $what")
|
||||||
@ -194,13 +208,36 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
DataType.BYTE -> {
|
DataType.BYTE -> {
|
||||||
TODO("lsr sbyte $what")
|
when (what) {
|
||||||
|
is ArrayIndexedExpression -> TODO("lsr sbyte $what")
|
||||||
|
is DirectMemoryRead -> TODO("lsr sbyte $what")
|
||||||
|
is RegisterExpr -> TODO("lsr sbyte $what")
|
||||||
|
is IdentifierReference -> {
|
||||||
|
val variable = asmgen.asmIdentifierName(what)
|
||||||
|
asmgen.out(" lda $variable | asl a | ror $variable")
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("weird type")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
DataType.UWORD -> {
|
DataType.UWORD -> {
|
||||||
TODO("lsr sword $what")
|
when (what) {
|
||||||
|
is ArrayIndexedExpression -> TODO("lsr uword $what")
|
||||||
|
is IdentifierReference -> {
|
||||||
|
val variable = asmgen.asmIdentifierName(what)
|
||||||
|
asmgen.out(" lsr $variable+1 | ror $variable")
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("weird type")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
DataType.WORD -> {
|
DataType.WORD -> {
|
||||||
TODO("lsr word $what")
|
when (what) {
|
||||||
|
is ArrayIndexedExpression -> TODO("lsr sword $what")
|
||||||
|
is IdentifierReference -> {
|
||||||
|
val variable = asmgen.asmIdentifierName(what)
|
||||||
|
asmgen.out(" lda $variable+1 | asl a | ror $variable+1 | ror $variable")
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("weird type")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else -> throw AssemblyError("weird type")
|
else -> throw AssemblyError("weird type")
|
||||||
}
|
}
|
||||||
@ -208,8 +245,8 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
|
|||||||
"rol" -> {
|
"rol" -> {
|
||||||
// in-place
|
// in-place
|
||||||
val what = fcall.arglist.single()
|
val what = fcall.arglist.single()
|
||||||
val dt = what.inferType(program)!!
|
val dt = what.inferType(program)
|
||||||
when(dt) {
|
when (dt.typeOrElse(DataType.STRUCT)) {
|
||||||
DataType.UBYTE -> {
|
DataType.UBYTE -> {
|
||||||
TODO("rol ubyte")
|
TODO("rol ubyte")
|
||||||
}
|
}
|
||||||
@ -222,8 +259,8 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
|
|||||||
"rol2" -> {
|
"rol2" -> {
|
||||||
// in-place
|
// in-place
|
||||||
val what = fcall.arglist.single()
|
val what = fcall.arglist.single()
|
||||||
val dt = what.inferType(program)!!
|
val dt = what.inferType(program)
|
||||||
when(dt) {
|
when (dt.typeOrElse(DataType.STRUCT)) {
|
||||||
DataType.UBYTE -> {
|
DataType.UBYTE -> {
|
||||||
TODO("rol2 ubyte")
|
TODO("rol2 ubyte")
|
||||||
}
|
}
|
||||||
@ -236,8 +273,8 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
|
|||||||
"ror" -> {
|
"ror" -> {
|
||||||
// in-place
|
// in-place
|
||||||
val what = fcall.arglist.single()
|
val what = fcall.arglist.single()
|
||||||
val dt = what.inferType(program)!!
|
val dt = what.inferType(program)
|
||||||
when(dt) {
|
when (dt.typeOrElse(DataType.STRUCT)) {
|
||||||
DataType.UBYTE -> {
|
DataType.UBYTE -> {
|
||||||
TODO("ror ubyte")
|
TODO("ror ubyte")
|
||||||
}
|
}
|
||||||
@ -250,8 +287,8 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
|
|||||||
"ror2" -> {
|
"ror2" -> {
|
||||||
// in-place
|
// in-place
|
||||||
val what = fcall.arglist.single()
|
val what = fcall.arglist.single()
|
||||||
val dt = what.inferType(program)!!
|
val dt = what.inferType(program)
|
||||||
when(dt) {
|
when (dt.typeOrElse(DataType.STRUCT)) {
|
||||||
DataType.UBYTE -> {
|
DataType.UBYTE -> {
|
||||||
TODO("ror2 ubyte")
|
TODO("ror2 ubyte")
|
||||||
}
|
}
|
||||||
@ -302,4 +339,3 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,430 @@
|
|||||||
|
package prog8.compiler.target.c64.codegen
|
||||||
|
|
||||||
|
import prog8.ast.Program
|
||||||
|
import prog8.ast.base.*
|
||||||
|
import prog8.ast.expressions.*
|
||||||
|
import prog8.compiler.target.c64.MachineDefinition
|
||||||
|
import prog8.compiler.toHex
|
||||||
|
import prog8.functions.BuiltinFunctions
|
||||||
|
import kotlin.math.absoluteValue
|
||||||
|
|
||||||
|
internal class ExpressionsAsmGen(private val program: Program, private val asmgen: AsmGen) {
|
||||||
|
|
||||||
|
internal fun translateExpression(expression: Expression) {
|
||||||
|
when(expression) {
|
||||||
|
is PrefixExpression -> translateExpression(expression)
|
||||||
|
is BinaryExpression -> translateExpression(expression)
|
||||||
|
is ArrayIndexedExpression -> translatePushFromArray(expression)
|
||||||
|
is TypecastExpression -> translateExpression(expression)
|
||||||
|
is AddressOf -> translateExpression(expression)
|
||||||
|
is DirectMemoryRead -> translateExpression(expression)
|
||||||
|
is NumericLiteralValue -> translateExpression(expression)
|
||||||
|
is RegisterExpr -> translateExpression(expression)
|
||||||
|
is IdentifierReference -> translateExpression(expression)
|
||||||
|
is FunctionCall -> {
|
||||||
|
val functionName = expression.target.nameInSource.last()
|
||||||
|
val builtinFunc = BuiltinFunctions[functionName]
|
||||||
|
if(builtinFunc!=null) {
|
||||||
|
asmgen.translateFunctioncallExpression(expression, builtinFunc)
|
||||||
|
} else {
|
||||||
|
asmgen.translateFunctionCall(expression)
|
||||||
|
val sub = expression.target.targetSubroutine(program.namespace)!!
|
||||||
|
val returns = sub.returntypes.zip(sub.asmReturnvaluesRegisters)
|
||||||
|
for((_, reg) in returns) {
|
||||||
|
if(!reg.stack) {
|
||||||
|
// result value in cpu or status registers, put it on the stack
|
||||||
|
if(reg.registerOrPair!=null) {
|
||||||
|
when(reg.registerOrPair) {
|
||||||
|
RegisterOrPair.A -> asmgen.out(" sta ${MachineDefinition.ESTACK_LO_HEX},x | dex")
|
||||||
|
RegisterOrPair.Y -> asmgen.out(" tya | sta ${MachineDefinition.ESTACK_LO_HEX},x | dex")
|
||||||
|
RegisterOrPair.AY -> asmgen.out(" sta ${MachineDefinition.ESTACK_LO_HEX},x | tya | sta ${MachineDefinition.ESTACK_HI_HEX},x | dex")
|
||||||
|
RegisterOrPair.X, RegisterOrPair.AX, RegisterOrPair.XY -> throw AssemblyError("can't push X register - use a variable")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// return value from a statusregister is not put on the stack, it should be acted on via a conditional branch such as if_cc
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is ArrayLiteralValue, is StringLiteralValue -> TODO("string/array/struct assignment?")
|
||||||
|
is StructLiteralValue -> throw AssemblyError("struct literal value assignment should have been flattened")
|
||||||
|
is RangeExpr -> throw AssemblyError("range expression should have been changed into array values")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun translateExpression(expr: TypecastExpression) {
|
||||||
|
translateExpression(expr.expression)
|
||||||
|
when(expr.expression.inferType(program).typeOrElse(DataType.STRUCT)) {
|
||||||
|
DataType.UBYTE -> {
|
||||||
|
when(expr.type) {
|
||||||
|
DataType.UBYTE, DataType.BYTE -> {}
|
||||||
|
DataType.UWORD, DataType.WORD -> asmgen.out(" lda #0 | sta ${MachineDefinition.ESTACK_HI_PLUS1_HEX},x")
|
||||||
|
DataType.FLOAT -> asmgen.out(" jsr c64flt.stack_ub2float")
|
||||||
|
in PassByReferenceDatatypes -> throw AssemblyError("cannot cast to a pass-by-reference datatype")
|
||||||
|
else -> throw AssemblyError("weird type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DataType.BYTE -> {
|
||||||
|
when(expr.type) {
|
||||||
|
DataType.UBYTE, DataType.BYTE -> {}
|
||||||
|
DataType.UWORD, DataType.WORD -> asmgen.out(" lda ${MachineDefinition.ESTACK_LO_PLUS1_HEX},x | ${asmgen.signExtendAtoMsb("${MachineDefinition.ESTACK_HI_PLUS1_HEX},x")}")
|
||||||
|
DataType.FLOAT -> asmgen.out(" jsr c64flt.stack_b2float")
|
||||||
|
in PassByReferenceDatatypes -> throw AssemblyError("cannot cast to a pass-by-reference datatype")
|
||||||
|
else -> throw AssemblyError("weird type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DataType.UWORD -> {
|
||||||
|
when(expr.type) {
|
||||||
|
DataType.BYTE, DataType.UBYTE -> {}
|
||||||
|
DataType.WORD, DataType.UWORD -> {}
|
||||||
|
DataType.FLOAT -> asmgen.out(" jsr c64flt.stack_uw2float")
|
||||||
|
in PassByReferenceDatatypes -> throw AssemblyError("cannot cast to a pass-by-reference datatype")
|
||||||
|
else -> throw AssemblyError("weird type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DataType.WORD -> {
|
||||||
|
when(expr.type) {
|
||||||
|
DataType.BYTE, DataType.UBYTE -> {}
|
||||||
|
DataType.WORD, DataType.UWORD -> {}
|
||||||
|
DataType.FLOAT -> asmgen.out(" jsr c64flt.stack_w2float")
|
||||||
|
in PassByReferenceDatatypes -> throw AssemblyError("cannot cast to a pass-by-reference datatype")
|
||||||
|
else -> throw AssemblyError("weird type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DataType.FLOAT -> {
|
||||||
|
when(expr.type) {
|
||||||
|
DataType.UBYTE -> asmgen.out(" jsr c64flt.stack_float2uw")
|
||||||
|
DataType.BYTE -> asmgen.out(" jsr c64flt.stack_float2w")
|
||||||
|
DataType.UWORD -> asmgen.out(" jsr c64flt.stack_float2uw")
|
||||||
|
DataType.WORD -> asmgen.out(" jsr c64flt.stack_float2w")
|
||||||
|
DataType.FLOAT -> {}
|
||||||
|
in PassByReferenceDatatypes -> throw AssemblyError("cannot cast to a pass-by-reference datatype")
|
||||||
|
else -> throw AssemblyError("weird type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
in PassByReferenceDatatypes -> throw AssemblyError("cannot case a pass-by-reference datatypes into something else")
|
||||||
|
else -> throw AssemblyError("weird type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun translateExpression(expr: AddressOf) {
|
||||||
|
val name = asmgen.asmIdentifierName(expr.identifier)
|
||||||
|
asmgen.out(" lda #<$name | sta ${MachineDefinition.ESTACK_LO_HEX},x | lda #>$name | sta ${MachineDefinition.ESTACK_HI_HEX},x | dex")
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun translateExpression(expr: DirectMemoryRead) {
|
||||||
|
when(expr.addressExpression) {
|
||||||
|
is NumericLiteralValue -> {
|
||||||
|
val address = (expr.addressExpression as NumericLiteralValue).number.toInt()
|
||||||
|
asmgen.out(" lda ${address.toHex()} | sta ${MachineDefinition.ESTACK_LO_HEX},x | dex")
|
||||||
|
}
|
||||||
|
is IdentifierReference -> {
|
||||||
|
val sourceName = asmgen.asmIdentifierName(expr.addressExpression as IdentifierReference)
|
||||||
|
asmgen.out(" lda $sourceName | sta ${MachineDefinition.ESTACK_LO_HEX},x | dex")
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
translateExpression(expr.addressExpression)
|
||||||
|
asmgen.out(" jsr prog8_lib.read_byte_from_address")
|
||||||
|
asmgen.out(" sta ${MachineDefinition.ESTACK_LO_PLUS1_HEX},x")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun translateExpression(expr: NumericLiteralValue) {
|
||||||
|
when(expr.type) {
|
||||||
|
DataType.UBYTE, DataType.BYTE -> asmgen.out(" lda #${expr.number.toHex()} | sta ${MachineDefinition.ESTACK_LO_HEX},x | dex")
|
||||||
|
DataType.UWORD, DataType.WORD -> asmgen.out("""
|
||||||
|
lda #<${expr.number.toHex()}
|
||||||
|
sta ${MachineDefinition.ESTACK_LO_HEX},x
|
||||||
|
lda #>${expr.number.toHex()}
|
||||||
|
sta ${MachineDefinition.ESTACK_HI_HEX},x
|
||||||
|
dex
|
||||||
|
""")
|
||||||
|
DataType.FLOAT -> {
|
||||||
|
val floatConst = asmgen.getFloatConst(expr.number.toDouble())
|
||||||
|
asmgen.out(" lda #<$floatConst | ldy #>$floatConst | jsr c64flt.push_float")
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("weird type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun translateExpression(expr: RegisterExpr) {
|
||||||
|
when(expr.register) {
|
||||||
|
Register.A -> asmgen.out(" sta ${MachineDefinition.ESTACK_LO_HEX},x | dex")
|
||||||
|
Register.X -> throw AssemblyError("cannot push X - use a variable instead of the X register")
|
||||||
|
Register.Y -> asmgen.out(" tya | sta ${MachineDefinition.ESTACK_LO_HEX},x | dex")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun translateExpression(expr: IdentifierReference) {
|
||||||
|
val varname = asmgen.asmIdentifierName(expr)
|
||||||
|
when(expr.inferType(program).typeOrElse(DataType.STRUCT)) {
|
||||||
|
DataType.UBYTE, DataType.BYTE -> {
|
||||||
|
asmgen.out(" lda $varname | sta ${MachineDefinition.ESTACK_LO_HEX},x | dex")
|
||||||
|
}
|
||||||
|
DataType.UWORD, DataType.WORD, in ArrayDatatypes, in StringDatatypes -> {
|
||||||
|
// (for arrays and strings, push their address)
|
||||||
|
asmgen.out(" lda $varname | sta ${MachineDefinition.ESTACK_LO_HEX},x | lda $varname+1 | sta ${MachineDefinition.ESTACK_HI_HEX},x | dex")
|
||||||
|
}
|
||||||
|
DataType.FLOAT -> {
|
||||||
|
asmgen.out(" lda #<$varname | ldy #>$varname| jsr c64flt.push_float")
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("stack push weird variable type $expr")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val optimizedByteMultiplications = setOf(3,5,6,7,9,10,11,12,13,14,15,20,25,40)
|
||||||
|
private val optimizedWordMultiplications = setOf(3,5,6,7,9,10,12,15,20,25,40)
|
||||||
|
private val powerOfTwos = setOf(0,1,2,4,8,16,32,64,128,256)
|
||||||
|
|
||||||
|
private fun translateExpression(expr: BinaryExpression) {
|
||||||
|
val leftIDt = expr.left.inferType(program)
|
||||||
|
val rightIDt = expr.right.inferType(program)
|
||||||
|
if(!leftIDt.isKnown || !rightIDt.isKnown)
|
||||||
|
throw AssemblyError("can't infer type of both expression operands")
|
||||||
|
|
||||||
|
val leftDt = leftIDt.typeOrElse(DataType.STRUCT)
|
||||||
|
val rightDt = rightIDt.typeOrElse(DataType.STRUCT)
|
||||||
|
// see if we can apply some optimized routines
|
||||||
|
when(expr.operator) {
|
||||||
|
">>" -> {
|
||||||
|
// bit-shifts are always by a constant number (for now)
|
||||||
|
translateExpression(expr.left)
|
||||||
|
val amount = expr.right.constValue(program)!!.number.toInt()
|
||||||
|
when (leftDt) {
|
||||||
|
DataType.UBYTE -> repeat(amount) { asmgen.out(" lsr ${MachineDefinition.ESTACK_LO_PLUS1_HEX},x") }
|
||||||
|
DataType.BYTE -> repeat(amount) { asmgen.out(" lda ${MachineDefinition.ESTACK_LO_PLUS1_HEX},x | asl a | ror ${MachineDefinition.ESTACK_LO_PLUS1_HEX},x") }
|
||||||
|
DataType.UWORD -> repeat(amount) { asmgen.out(" lsr ${MachineDefinition.ESTACK_HI_PLUS1_HEX},x | ror ${MachineDefinition.ESTACK_LO_PLUS1_HEX},x") }
|
||||||
|
DataType.WORD -> repeat(amount) { asmgen.out(" lda ${MachineDefinition.ESTACK_HI_PLUS1_HEX},x | asl a | ror ${MachineDefinition.ESTACK_HI_PLUS1_HEX},x | ror ${MachineDefinition.ESTACK_LO_PLUS1_HEX},x") }
|
||||||
|
else -> throw AssemblyError("weird type")
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
"<<" -> {
|
||||||
|
// bit-shifts are always by a constant number (for now)
|
||||||
|
translateExpression(expr.left)
|
||||||
|
val amount = expr.right.constValue(program)!!.number.toInt()
|
||||||
|
if (leftDt in ByteDatatypes)
|
||||||
|
repeat(amount) { asmgen.out(" asl ${MachineDefinition.ESTACK_LO_PLUS1_HEX},x") }
|
||||||
|
else
|
||||||
|
repeat(amount) { asmgen.out(" asl ${MachineDefinition.ESTACK_LO_PLUS1_HEX},x | rol ${MachineDefinition.ESTACK_HI_PLUS1_HEX},x") }
|
||||||
|
return
|
||||||
|
}
|
||||||
|
"*" -> {
|
||||||
|
val value = expr.right.constValue(program)
|
||||||
|
if(value!=null) {
|
||||||
|
if(rightDt in IntegerDatatypes) {
|
||||||
|
val amount = value.number.toInt()
|
||||||
|
if(amount in powerOfTwos)
|
||||||
|
printWarning("${expr.right.position} multiplication by power of 2 should have been optimized into a left shift instruction: $amount")
|
||||||
|
when(rightDt) {
|
||||||
|
DataType.UBYTE -> {
|
||||||
|
if(amount in optimizedByteMultiplications) {
|
||||||
|
translateExpression(expr.left)
|
||||||
|
asmgen.out(" jsr math.mul_byte_$amount")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DataType.BYTE -> {
|
||||||
|
if(amount in optimizedByteMultiplications) {
|
||||||
|
translateExpression(expr.left)
|
||||||
|
asmgen.out(" jsr math.mul_byte_$amount")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if(amount.absoluteValue in optimizedByteMultiplications) {
|
||||||
|
translateExpression(expr.left)
|
||||||
|
asmgen.out(" jsr prog8_lib.neg_b | jsr math.mul_byte_${amount.absoluteValue}")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DataType.UWORD -> {
|
||||||
|
if(amount in optimizedWordMultiplications) {
|
||||||
|
translateExpression(expr.left)
|
||||||
|
asmgen.out(" jsr math.mul_word_$amount")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DataType.WORD -> {
|
||||||
|
if(amount in optimizedWordMultiplications) {
|
||||||
|
translateExpression(expr.left)
|
||||||
|
asmgen.out(" jsr math.mul_word_$amount")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if(amount.absoluteValue in optimizedWordMultiplications) {
|
||||||
|
translateExpression(expr.left)
|
||||||
|
asmgen.out(" jsr prog8_lib.neg_w | jsr math.mul_word_${amount.absoluteValue}")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// the general, non-optimized cases
|
||||||
|
translateExpression(expr.left)
|
||||||
|
translateExpression(expr.right)
|
||||||
|
if(leftDt!=rightDt)
|
||||||
|
throw AssemblyError("binary operator ${expr.operator} left/right dt not identical") // is this strictly required always?
|
||||||
|
when (leftDt) {
|
||||||
|
in ByteDatatypes -> translateBinaryOperatorBytes(expr.operator, leftDt)
|
||||||
|
in WordDatatypes -> translateBinaryOperatorWords(expr.operator, leftDt)
|
||||||
|
DataType.FLOAT -> translateBinaryOperatorFloats(expr.operator)
|
||||||
|
else -> throw AssemblyError("non-numerical datatype")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun translateExpression(expr: PrefixExpression) {
|
||||||
|
translateExpression(expr.expression)
|
||||||
|
val type = expr.inferType(program).typeOrElse(DataType.STRUCT)
|
||||||
|
when(expr.operator) {
|
||||||
|
"+" -> {}
|
||||||
|
"-" -> {
|
||||||
|
when(type) {
|
||||||
|
in ByteDatatypes -> asmgen.out(" jsr prog8_lib.neg_b")
|
||||||
|
in WordDatatypes -> asmgen.out(" jsr prog8_lib.neg_w")
|
||||||
|
DataType.FLOAT -> asmgen.out(" jsr c64flt.neg_f")
|
||||||
|
else -> throw AssemblyError("weird type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"~" -> {
|
||||||
|
when(type) {
|
||||||
|
in ByteDatatypes ->
|
||||||
|
asmgen.out("""
|
||||||
|
lda ${MachineDefinition.ESTACK_LO_PLUS1_HEX},x
|
||||||
|
eor #255
|
||||||
|
sta ${MachineDefinition.ESTACK_LO_PLUS1_HEX},x
|
||||||
|
""")
|
||||||
|
in WordDatatypes -> asmgen.out(" jsr prog8_lib.inv_word")
|
||||||
|
else -> throw AssemblyError("weird type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"not" -> {
|
||||||
|
when(type) {
|
||||||
|
in ByteDatatypes -> asmgen.out(" jsr prog8_lib.not_byte")
|
||||||
|
in WordDatatypes -> asmgen.out(" jsr prog8_lib.not_word")
|
||||||
|
else -> throw AssemblyError("weird type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("invalid prefix operator ${expr.operator}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun translatePushFromArray(arrayExpr: ArrayIndexedExpression) {
|
||||||
|
// assume *reading* from an array
|
||||||
|
val index = arrayExpr.arrayspec.index
|
||||||
|
val arrayDt = arrayExpr.identifier.targetVarDecl(program.namespace)!!.datatype
|
||||||
|
val arrayVarName = asmgen.asmIdentifierName(arrayExpr.identifier)
|
||||||
|
if(index is NumericLiteralValue) {
|
||||||
|
val elementDt = ArrayElementTypes.getValue(arrayDt)
|
||||||
|
val indexValue = index.number.toInt() * elementDt.memorySize()
|
||||||
|
when(elementDt) {
|
||||||
|
in ByteDatatypes -> {
|
||||||
|
asmgen.out(" lda $arrayVarName+$indexValue | sta ${MachineDefinition.ESTACK_LO_HEX},x | dex")
|
||||||
|
}
|
||||||
|
in WordDatatypes -> {
|
||||||
|
asmgen.out(" lda $arrayVarName+$indexValue | sta ${MachineDefinition.ESTACK_LO_HEX},x | lda $arrayVarName+$indexValue+1 | sta ${MachineDefinition.ESTACK_HI_HEX},x | dex")
|
||||||
|
}
|
||||||
|
DataType.FLOAT -> {
|
||||||
|
asmgen.out(" lda #<$arrayVarName+$indexValue | ldy #>$arrayVarName+$indexValue | jsr c64flt.push_float")
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("weird type")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
asmgen.translateArrayIndexIntoA(arrayExpr)
|
||||||
|
asmgen.readAndPushArrayvalueWithIndexA(arrayDt, arrayExpr.identifier)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun translateBinaryOperatorBytes(operator: String, types: DataType) {
|
||||||
|
when(operator) {
|
||||||
|
"**" -> throw AssemblyError("** operator requires floats")
|
||||||
|
"*" -> asmgen.out(" jsr prog8_lib.mul_byte") // the optimized routines should have been checked earlier
|
||||||
|
"/" -> asmgen.out(if(types==DataType.UBYTE) " jsr prog8_lib.idiv_ub" else " jsr prog8_lib.idiv_b")
|
||||||
|
"%" -> {
|
||||||
|
if(types==DataType.BYTE)
|
||||||
|
throw AssemblyError("remainder of signed integers is not properly defined/implemented, use unsigned instead")
|
||||||
|
asmgen.out(" jsr prog8_lib.remainder_ub")
|
||||||
|
}
|
||||||
|
"+" -> asmgen.out("""
|
||||||
|
lda ${MachineDefinition.ESTACK_LO_PLUS2_HEX},x
|
||||||
|
clc
|
||||||
|
adc ${MachineDefinition.ESTACK_LO_PLUS1_HEX},x
|
||||||
|
inx
|
||||||
|
sta ${MachineDefinition.ESTACK_LO_PLUS1_HEX},x
|
||||||
|
""")
|
||||||
|
"-" -> asmgen.out("""
|
||||||
|
lda ${MachineDefinition.ESTACK_LO_PLUS2_HEX},x
|
||||||
|
sec
|
||||||
|
sbc ${MachineDefinition.ESTACK_LO_PLUS1_HEX},x
|
||||||
|
inx
|
||||||
|
sta ${MachineDefinition.ESTACK_LO_PLUS1_HEX},x
|
||||||
|
""")
|
||||||
|
"<<", ">>" -> throw AssemblyError("bit-shifts not via stack")
|
||||||
|
"<" -> asmgen.out(if(types==DataType.UBYTE) " jsr prog8_lib.less_ub" else " jsr prog8_lib.less_b")
|
||||||
|
">" -> asmgen.out(if(types==DataType.UBYTE) " jsr prog8_lib.greater_ub" else " jsr prog8_lib.greater_b")
|
||||||
|
"<=" -> asmgen.out(if(types==DataType.UBYTE) " jsr prog8_lib.lesseq_ub" else " jsr prog8_lib.lesseq_b")
|
||||||
|
">=" -> asmgen.out(if(types==DataType.UBYTE) " jsr prog8_lib.greatereq_ub" else " jsr prog8_lib.greatereq_b")
|
||||||
|
"==" -> asmgen.out(" jsr prog8_lib.equal_b")
|
||||||
|
"!=" -> asmgen.out(" jsr prog8_lib.notequal_b")
|
||||||
|
"&" -> asmgen.out(" jsr prog8_lib.bitand_b")
|
||||||
|
"^" -> asmgen.out(" jsr prog8_lib.bitxor_b")
|
||||||
|
"|" -> asmgen.out(" jsr prog8_lib.bitor_b")
|
||||||
|
"and" -> asmgen.out(" jsr prog8_lib.and_b")
|
||||||
|
"or" -> asmgen.out(" jsr prog8_lib.or_b")
|
||||||
|
"xor" -> asmgen.out(" jsr prog8_lib.xor_b")
|
||||||
|
else -> throw AssemblyError("invalid operator $operator")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun translateBinaryOperatorWords(operator: String, types: DataType) {
|
||||||
|
when(operator) {
|
||||||
|
"**" -> throw AssemblyError("** operator requires floats")
|
||||||
|
"*" -> asmgen.out(" jsr prog8_lib.mul_word")
|
||||||
|
"/" -> asmgen.out(if(types==DataType.UWORD) " jsr prog8_lib.idiv_uw" else " jsr prog8_lib.idiv_w")
|
||||||
|
"%" -> {
|
||||||
|
if(types==DataType.WORD)
|
||||||
|
throw AssemblyError("remainder of signed integers is not properly defined/implemented, use unsigned instead")
|
||||||
|
asmgen.out(" jsr prog8_lib.remainder_uw")
|
||||||
|
}
|
||||||
|
"+" -> asmgen.out(" jsr prog8_lib.add_w")
|
||||||
|
"-" -> asmgen.out(" jsr prog8_lib.sub_w")
|
||||||
|
"<<" -> throw AssemblyError("<< should not operate via stack")
|
||||||
|
">>" -> throw AssemblyError(">> should not operate via stack")
|
||||||
|
"<" -> asmgen.out(if(types==DataType.UWORD) " jsr prog8_lib.less_uw" else " jsr prog8_lib.less_w")
|
||||||
|
">" -> asmgen.out(if(types==DataType.UWORD) " jsr prog8_lib.greater_uw" else " jsr prog8_lib.greater_w")
|
||||||
|
"<=" -> asmgen.out(if(types==DataType.UWORD) " jsr prog8_lib.lesseq_uw" else " jsr prog8_lib.lesseq_w")
|
||||||
|
">=" -> asmgen.out(if(types==DataType.UWORD) " jsr prog8_lib.greatereq_uw" else " jsr prog8_lib.greatereq_w")
|
||||||
|
"==" -> asmgen.out(" jsr prog8_lib.equal_w")
|
||||||
|
"!=" -> asmgen.out(" jsr prog8_lib.notequal_w")
|
||||||
|
"&" -> asmgen.out(" jsr prog8_lib.bitand_w")
|
||||||
|
"^" -> asmgen.out(" jsr prog8_lib.bitxor_w")
|
||||||
|
"|" -> asmgen.out(" jsr prog8_lib.bitor_w")
|
||||||
|
"and" -> asmgen.out(" jsr prog8_lib.and_w")
|
||||||
|
"or" -> asmgen.out(" jsr prog8_lib.or_w")
|
||||||
|
"xor" -> asmgen.out(" jsr prog8_lib.xor_w")
|
||||||
|
else -> throw AssemblyError("invalid operator $operator")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun translateBinaryOperatorFloats(operator: String) {
|
||||||
|
when(operator) {
|
||||||
|
"**" -> asmgen.out(" jsr c64flt.pow_f")
|
||||||
|
"*" -> asmgen.out(" jsr c64flt.mul_f")
|
||||||
|
"/" -> asmgen.out(" jsr c64flt.div_f")
|
||||||
|
"+" -> asmgen.out(" jsr c64flt.add_f")
|
||||||
|
"-" -> asmgen.out(" jsr c64flt.sub_f")
|
||||||
|
"<" -> asmgen.out(" jsr c64flt.less_f")
|
||||||
|
">" -> asmgen.out(" jsr c64flt.greater_f")
|
||||||
|
"<=" -> asmgen.out(" jsr c64flt.lesseq_f")
|
||||||
|
">=" -> asmgen.out(" jsr c64flt.greatereq_f")
|
||||||
|
"==" -> asmgen.out(" jsr c64flt.equal_f")
|
||||||
|
"!=" -> asmgen.out(" jsr c64flt.notequal_f")
|
||||||
|
"%", "<<", ">>", "&", "^", "|", "and", "or", "xor" -> throw AssemblyError("requires integer datatype")
|
||||||
|
else -> throw AssemblyError("invalid operator $operator")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
561
compiler/src/prog8/compiler/target/c64/codegen/ForLoopsAsmGen.kt
Normal file
561
compiler/src/prog8/compiler/target/c64/codegen/ForLoopsAsmGen.kt
Normal file
@ -0,0 +1,561 @@
|
|||||||
|
package prog8.compiler.target.c64.codegen
|
||||||
|
|
||||||
|
import prog8.ast.Program
|
||||||
|
import prog8.ast.base.DataType
|
||||||
|
import prog8.ast.base.Register
|
||||||
|
import prog8.ast.expressions.IdentifierReference
|
||||||
|
import prog8.ast.expressions.RangeExpr
|
||||||
|
import prog8.ast.statements.AssignTarget
|
||||||
|
import prog8.ast.statements.Assignment
|
||||||
|
import prog8.ast.statements.ForLoop
|
||||||
|
import prog8.compiler.target.c64.MachineDefinition
|
||||||
|
import prog8.compiler.toHex
|
||||||
|
import kotlin.math.absoluteValue
|
||||||
|
|
||||||
|
|
||||||
|
internal class ForLoopsAsmGen(private val program: Program, private val asmgen: AsmGen) {
|
||||||
|
|
||||||
|
internal fun translate(stmt: ForLoop) {
|
||||||
|
val iterableDt = stmt.iterable.inferType(program)
|
||||||
|
if(!iterableDt.isKnown)
|
||||||
|
throw AssemblyError("can't determine iterable dt")
|
||||||
|
when(stmt.iterable) {
|
||||||
|
is RangeExpr -> {
|
||||||
|
val range = (stmt.iterable as RangeExpr).toConstantIntegerRange()
|
||||||
|
if(range==null) {
|
||||||
|
translateForOverNonconstRange(stmt, iterableDt.typeOrElse(DataType.STRUCT), stmt.iterable as RangeExpr)
|
||||||
|
} else {
|
||||||
|
if (range.isEmpty())
|
||||||
|
throw AssemblyError("empty range")
|
||||||
|
translateForOverConstRange(stmt, iterableDt.typeOrElse(DataType.STRUCT), range)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is IdentifierReference -> {
|
||||||
|
translateForOverIterableVar(stmt, iterableDt.typeOrElse(DataType.STRUCT), stmt.iterable as IdentifierReference)
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("can't iterate over ${stmt.iterable}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun translateForOverNonconstRange(stmt: ForLoop, iterableDt: DataType, range: RangeExpr) {
|
||||||
|
val loopLabel = asmgen.makeLabel("for_loop")
|
||||||
|
val endLabel = asmgen.makeLabel("for_end")
|
||||||
|
val continueLabel = asmgen.makeLabel("for_continue")
|
||||||
|
val counterLabel = asmgen.makeLabel("for_counter")
|
||||||
|
asmgen.loopEndLabels.push(endLabel)
|
||||||
|
asmgen.loopContinueLabels.push(continueLabel)
|
||||||
|
val stepsize=range.step.constValue(program)?.number
|
||||||
|
when (stepsize) {
|
||||||
|
1 -> {
|
||||||
|
when(iterableDt) {
|
||||||
|
DataType.ARRAY_B, DataType.ARRAY_UB -> {
|
||||||
|
if (stmt.loopRegister != null) {
|
||||||
|
// loop register over range
|
||||||
|
if(stmt.loopRegister!= Register.A)
|
||||||
|
throw AssemblyError("can only use A")
|
||||||
|
asmgen.translateExpression(range.to)
|
||||||
|
asmgen.translateExpression(range.from)
|
||||||
|
asmgen.out("""
|
||||||
|
inx
|
||||||
|
lda ${MachineDefinition.ESTACK_LO_HEX},x
|
||||||
|
sta $loopLabel+1
|
||||||
|
inx
|
||||||
|
lda ${MachineDefinition.ESTACK_LO_HEX},x
|
||||||
|
sec
|
||||||
|
sbc $loopLabel+1
|
||||||
|
adc #0
|
||||||
|
sta $counterLabel
|
||||||
|
$loopLabel lda #0 ; modified""")
|
||||||
|
asmgen.translate(stmt.body)
|
||||||
|
asmgen.out("""
|
||||||
|
$continueLabel dec $counterLabel
|
||||||
|
beq $endLabel
|
||||||
|
inc $loopLabel+1
|
||||||
|
jmp $loopLabel
|
||||||
|
$counterLabel .byte 0
|
||||||
|
$endLabel""")
|
||||||
|
} else {
|
||||||
|
// loop over byte range via loopvar
|
||||||
|
val varname = asmgen.asmIdentifierName(stmt.loopVar!!)
|
||||||
|
asmgen.translateExpression(range.to)
|
||||||
|
asmgen.translateExpression(range.from)
|
||||||
|
asmgen.out("""
|
||||||
|
inx
|
||||||
|
lda ${MachineDefinition.ESTACK_LO_HEX},x
|
||||||
|
sta $varname
|
||||||
|
inx
|
||||||
|
lda ${MachineDefinition.ESTACK_LO_HEX},x
|
||||||
|
sec
|
||||||
|
sbc $varname
|
||||||
|
adc #0
|
||||||
|
sta $counterLabel
|
||||||
|
$loopLabel""")
|
||||||
|
asmgen.translate(stmt.body)
|
||||||
|
asmgen.out("""
|
||||||
|
$continueLabel dec $counterLabel
|
||||||
|
beq $endLabel
|
||||||
|
inc $varname
|
||||||
|
jmp $loopLabel
|
||||||
|
$counterLabel .byte 0
|
||||||
|
$endLabel""")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DataType.ARRAY_UW, DataType.ARRAY_W -> {
|
||||||
|
asmgen.translateExpression(range.to)
|
||||||
|
asmgen.out(" inc ${MachineDefinition.ESTACK_LO_HEX}+1,x | bne + | inc ${MachineDefinition.ESTACK_HI_HEX}+1,x |+ ")
|
||||||
|
val varname = asmgen.asmIdentifierName(stmt.loopVar!!)
|
||||||
|
val assignLoopvar = Assignment(AssignTarget(null, stmt.loopVar, null, null, stmt.loopVar!!.position),
|
||||||
|
null, range.from, range.position)
|
||||||
|
assignLoopvar.linkParents(stmt)
|
||||||
|
asmgen.translate(assignLoopvar)
|
||||||
|
asmgen.out(loopLabel)
|
||||||
|
asmgen.translate(stmt.body)
|
||||||
|
asmgen.out("""
|
||||||
|
inc $varname
|
||||||
|
bne +
|
||||||
|
inc $varname+1
|
||||||
|
+ lda ${MachineDefinition.ESTACK_HI_HEX}+1,x
|
||||||
|
cmp $varname+1
|
||||||
|
bne +
|
||||||
|
lda ${MachineDefinition.ESTACK_LO_HEX}+1,x
|
||||||
|
cmp $varname
|
||||||
|
beq $endLabel
|
||||||
|
+ jmp $loopLabel
|
||||||
|
$endLabel inx""")
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("range expression can only be byte or word")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
-1 -> {
|
||||||
|
when(iterableDt){
|
||||||
|
DataType.ARRAY_B, DataType.ARRAY_UB -> {
|
||||||
|
if (stmt.loopRegister != null) {
|
||||||
|
// loop register over range
|
||||||
|
if(stmt.loopRegister!= Register.A)
|
||||||
|
throw AssemblyError("can only use A")
|
||||||
|
asmgen.translateExpression(range.from)
|
||||||
|
asmgen.translateExpression(range.to)
|
||||||
|
asmgen.out("""
|
||||||
|
inx
|
||||||
|
lda ${MachineDefinition.ESTACK_LO_HEX}+1,x
|
||||||
|
sta $loopLabel+1
|
||||||
|
sec
|
||||||
|
sbc ${MachineDefinition.ESTACK_LO_HEX},x
|
||||||
|
adc #0
|
||||||
|
sta $counterLabel
|
||||||
|
inx
|
||||||
|
$loopLabel lda #0 ; modified""")
|
||||||
|
asmgen.translate(stmt.body)
|
||||||
|
asmgen.out("""
|
||||||
|
$continueLabel dec $counterLabel
|
||||||
|
beq $endLabel
|
||||||
|
dec $loopLabel+1
|
||||||
|
jmp $loopLabel
|
||||||
|
$counterLabel .byte 0
|
||||||
|
$endLabel""")
|
||||||
|
} else {
|
||||||
|
// loop over byte range via loopvar
|
||||||
|
val varname = asmgen.asmIdentifierName(stmt.loopVar!!)
|
||||||
|
asmgen.translateExpression(range.from)
|
||||||
|
asmgen.translateExpression(range.to)
|
||||||
|
asmgen.out("""
|
||||||
|
inx
|
||||||
|
lda ${MachineDefinition.ESTACK_LO_HEX}+1,x
|
||||||
|
sta $varname
|
||||||
|
sec
|
||||||
|
sbc ${MachineDefinition.ESTACK_LO_HEX},x
|
||||||
|
adc #0
|
||||||
|
sta $counterLabel
|
||||||
|
inx
|
||||||
|
$loopLabel""")
|
||||||
|
asmgen.translate(stmt.body)
|
||||||
|
asmgen.out("""
|
||||||
|
$continueLabel dec $counterLabel
|
||||||
|
beq $endLabel
|
||||||
|
dec $varname
|
||||||
|
jmp $loopLabel
|
||||||
|
$counterLabel .byte 0
|
||||||
|
$endLabel""")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DataType.ARRAY_UW, DataType.ARRAY_W -> {
|
||||||
|
asmgen.translateExpression(range.to)
|
||||||
|
asmgen.out("""
|
||||||
|
lda ${MachineDefinition.ESTACK_LO_HEX}+1,x
|
||||||
|
bne +
|
||||||
|
dec ${MachineDefinition.ESTACK_HI_HEX}+1,x
|
||||||
|
+ dec ${MachineDefinition.ESTACK_LO_HEX}+1,x
|
||||||
|
""")
|
||||||
|
val varname = asmgen.asmIdentifierName(stmt.loopVar!!)
|
||||||
|
val assignLoopvar = Assignment(AssignTarget(null, stmt.loopVar, null, null, stmt.loopVar!!.position),
|
||||||
|
null, range.from, range.position)
|
||||||
|
assignLoopvar.linkParents(stmt)
|
||||||
|
asmgen.translate(assignLoopvar)
|
||||||
|
asmgen.out(loopLabel)
|
||||||
|
asmgen.translate(stmt.body)
|
||||||
|
asmgen.out("""
|
||||||
|
lda $varname
|
||||||
|
bne +
|
||||||
|
dec $varname+1
|
||||||
|
+ dec $varname
|
||||||
|
lda ${MachineDefinition.ESTACK_HI_HEX}+1,x
|
||||||
|
cmp $varname+1
|
||||||
|
bne +
|
||||||
|
lda ${MachineDefinition.ESTACK_LO_HEX}+1,x
|
||||||
|
cmp $varname
|
||||||
|
beq $endLabel
|
||||||
|
+ jmp $loopLabel
|
||||||
|
$endLabel inx""")
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("range expression can only be byte or word")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> when (iterableDt) {
|
||||||
|
DataType.ARRAY_UB, DataType.ARRAY_B -> TODO("non-const forloop bytes, step >1: $stepsize")
|
||||||
|
DataType.ARRAY_UW, DataType.ARRAY_W -> TODO("non-const forloop words, step >1: $stepsize")
|
||||||
|
else -> throw AssemblyError("range expression can only be byte or word")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
asmgen.loopEndLabels.pop()
|
||||||
|
asmgen.loopContinueLabels.pop()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun translateForOverIterableVar(stmt: ForLoop, iterableDt: DataType, ident: IdentifierReference) {
|
||||||
|
val loopLabel = asmgen.makeLabel("for_loop")
|
||||||
|
val endLabel = asmgen.makeLabel("for_end")
|
||||||
|
val continueLabel = asmgen.makeLabel("for_continue")
|
||||||
|
asmgen.loopEndLabels.push(endLabel)
|
||||||
|
asmgen.loopContinueLabels.push(continueLabel)
|
||||||
|
val iterableName = asmgen.asmIdentifierName(ident)
|
||||||
|
val decl = ident.targetVarDecl(program.namespace)!!
|
||||||
|
when(iterableDt) {
|
||||||
|
DataType.STR, DataType.STR_S -> {
|
||||||
|
if(stmt.loopRegister!=null && stmt.loopRegister!= Register.A)
|
||||||
|
throw AssemblyError("can only use A")
|
||||||
|
asmgen.out("""
|
||||||
|
lda #<$iterableName
|
||||||
|
ldy #>$iterableName
|
||||||
|
sta $loopLabel+1
|
||||||
|
sty $loopLabel+2
|
||||||
|
$loopLabel lda ${65535.toHex()} ; modified
|
||||||
|
beq $endLabel""")
|
||||||
|
if(stmt.loopVar!=null)
|
||||||
|
asmgen.out(" sta ${asmgen.asmIdentifierName(stmt.loopVar!!)}")
|
||||||
|
asmgen.translate(stmt.body)
|
||||||
|
asmgen.out("""
|
||||||
|
$continueLabel inc $loopLabel+1
|
||||||
|
bne $loopLabel
|
||||||
|
inc $loopLabel+2
|
||||||
|
bne $loopLabel
|
||||||
|
$endLabel""")
|
||||||
|
}
|
||||||
|
DataType.ARRAY_UB, DataType.ARRAY_B -> {
|
||||||
|
// TODO: optimize loop code when the length of the array is < 256, don't need a separate counter in such cases
|
||||||
|
val length = decl.arraysize!!.size()!!
|
||||||
|
if(stmt.loopRegister!=null && stmt.loopRegister!= Register.A)
|
||||||
|
throw AssemblyError("can only use A")
|
||||||
|
val counterLabel = asmgen.makeLabel("for_counter")
|
||||||
|
val modifiedLabel = asmgen.makeLabel("for_modified")
|
||||||
|
asmgen.out("""
|
||||||
|
lda #<$iterableName
|
||||||
|
ldy #>$iterableName
|
||||||
|
sta $modifiedLabel+1
|
||||||
|
sty $modifiedLabel+2
|
||||||
|
ldy #0
|
||||||
|
$loopLabel sty $counterLabel
|
||||||
|
$modifiedLabel lda ${65535.toHex()},y ; modified""")
|
||||||
|
if(stmt.loopVar!=null)
|
||||||
|
asmgen.out(" sta ${asmgen.asmIdentifierName(stmt.loopVar!!)}")
|
||||||
|
asmgen.translate(stmt.body)
|
||||||
|
asmgen.out("""
|
||||||
|
$continueLabel ldy $counterLabel
|
||||||
|
iny
|
||||||
|
cpy #${length and 255}
|
||||||
|
beq $endLabel
|
||||||
|
jmp $loopLabel
|
||||||
|
$counterLabel .byte 0
|
||||||
|
$endLabel""")
|
||||||
|
}
|
||||||
|
DataType.ARRAY_W, DataType.ARRAY_UW -> {
|
||||||
|
// TODO: optimize loop code when the length of the array is < 256, don't need a separate counter in such cases
|
||||||
|
val length = decl.arraysize!!.size()!! * 2
|
||||||
|
if(stmt.loopRegister!=null)
|
||||||
|
throw AssemblyError("can't use register to loop over words")
|
||||||
|
val counterLabel = asmgen.makeLabel("for_counter")
|
||||||
|
val modifiedLabel = asmgen.makeLabel("for_modified")
|
||||||
|
val modifiedLabel2 = asmgen.makeLabel("for_modified2")
|
||||||
|
val loopvarName = asmgen.asmIdentifierName(stmt.loopVar!!)
|
||||||
|
asmgen.out("""
|
||||||
|
lda #<$iterableName
|
||||||
|
ldy #>$iterableName
|
||||||
|
sta $modifiedLabel+1
|
||||||
|
sty $modifiedLabel+2
|
||||||
|
lda #<$iterableName+1
|
||||||
|
ldy #>$iterableName+1
|
||||||
|
sta $modifiedLabel2+1
|
||||||
|
sty $modifiedLabel2+2
|
||||||
|
ldy #0
|
||||||
|
$loopLabel sty $counterLabel
|
||||||
|
$modifiedLabel lda ${65535.toHex()},y ; modified
|
||||||
|
sta $loopvarName
|
||||||
|
$modifiedLabel2 lda ${65535.toHex()},y ; modified
|
||||||
|
sta $loopvarName+1""")
|
||||||
|
asmgen.translate(stmt.body)
|
||||||
|
asmgen.out("""
|
||||||
|
$continueLabel ldy $counterLabel
|
||||||
|
iny
|
||||||
|
iny
|
||||||
|
cpy #${length and 255}
|
||||||
|
beq $endLabel
|
||||||
|
jmp $loopLabel
|
||||||
|
$counterLabel .byte 0
|
||||||
|
$endLabel""")
|
||||||
|
}
|
||||||
|
DataType.ARRAY_F -> {
|
||||||
|
throw AssemblyError("for loop with floating point variables is not supported")
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("can't iterate over $iterableDt")
|
||||||
|
}
|
||||||
|
asmgen.loopEndLabels.pop()
|
||||||
|
asmgen.loopContinueLabels.pop()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun translateForOverConstRange(stmt: ForLoop, iterableDt: DataType, range: IntProgression) {
|
||||||
|
// TODO: optimize loop code when the range is < 256 iterations, don't need a separate counter in such cases
|
||||||
|
val loopLabel = asmgen.makeLabel("for_loop")
|
||||||
|
val endLabel = asmgen.makeLabel("for_end")
|
||||||
|
val continueLabel = asmgen.makeLabel("for_continue")
|
||||||
|
asmgen.loopEndLabels.push(endLabel)
|
||||||
|
asmgen.loopContinueLabels.push(continueLabel)
|
||||||
|
when(iterableDt) {
|
||||||
|
DataType.ARRAY_B, DataType.ARRAY_UB -> {
|
||||||
|
val counterLabel = asmgen.makeLabel("for_counter")
|
||||||
|
if(stmt.loopRegister!=null) {
|
||||||
|
|
||||||
|
// loop register over range
|
||||||
|
|
||||||
|
if(stmt.loopRegister!= Register.A)
|
||||||
|
throw AssemblyError("can only use A")
|
||||||
|
when {
|
||||||
|
range.step==1 -> {
|
||||||
|
// step = 1
|
||||||
|
asmgen.out("""
|
||||||
|
lda #${range.first}
|
||||||
|
sta $loopLabel+1
|
||||||
|
lda #${range.last-range.first+1 and 255}
|
||||||
|
sta $counterLabel
|
||||||
|
$loopLabel lda #0 ; modified""")
|
||||||
|
asmgen.translate(stmt.body)
|
||||||
|
asmgen.out("""
|
||||||
|
$continueLabel dec $counterLabel
|
||||||
|
beq $endLabel
|
||||||
|
inc $loopLabel+1
|
||||||
|
jmp $loopLabel
|
||||||
|
$counterLabel .byte 0
|
||||||
|
$endLabel""")
|
||||||
|
}
|
||||||
|
range.step==-1 -> {
|
||||||
|
// step = -1
|
||||||
|
asmgen.out("""
|
||||||
|
lda #${range.first}
|
||||||
|
sta $loopLabel+1
|
||||||
|
lda #${range.first-range.last+1 and 255}
|
||||||
|
sta $counterLabel
|
||||||
|
$loopLabel lda #0 ; modified """)
|
||||||
|
asmgen.translate(stmt.body)
|
||||||
|
asmgen.out("""
|
||||||
|
$continueLabel dec $counterLabel
|
||||||
|
beq $endLabel
|
||||||
|
dec $loopLabel+1
|
||||||
|
jmp $loopLabel
|
||||||
|
$counterLabel .byte 0
|
||||||
|
$endLabel""")
|
||||||
|
}
|
||||||
|
range.step >= 2 -> {
|
||||||
|
// step >= 2
|
||||||
|
asmgen.out("""
|
||||||
|
lda #${(range.last-range.first) / range.step + 1}
|
||||||
|
sta $counterLabel
|
||||||
|
lda #${range.first}
|
||||||
|
$loopLabel pha""")
|
||||||
|
asmgen.translate(stmt.body)
|
||||||
|
asmgen.out("""
|
||||||
|
$continueLabel pla
|
||||||
|
dec $counterLabel
|
||||||
|
beq $endLabel
|
||||||
|
clc
|
||||||
|
adc #${range.step}
|
||||||
|
jmp $loopLabel
|
||||||
|
$counterLabel .byte 0
|
||||||
|
$endLabel""")
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
// step <= -2
|
||||||
|
asmgen.out("""
|
||||||
|
lda #${(range.first-range.last) / range.step.absoluteValue + 1}
|
||||||
|
sta $counterLabel
|
||||||
|
lda #${range.first}
|
||||||
|
$loopLabel pha""")
|
||||||
|
asmgen.translate(stmt.body)
|
||||||
|
asmgen.out("""
|
||||||
|
$continueLabel pla
|
||||||
|
dec $counterLabel
|
||||||
|
beq $endLabel
|
||||||
|
sec
|
||||||
|
sbc #${range.step.absoluteValue}
|
||||||
|
jmp $loopLabel
|
||||||
|
$counterLabel .byte 0
|
||||||
|
$endLabel""")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
// loop over byte range via loopvar
|
||||||
|
val varname = asmgen.asmIdentifierName(stmt.loopVar!!)
|
||||||
|
when {
|
||||||
|
range.step==1 -> {
|
||||||
|
// step = 1
|
||||||
|
asmgen.out("""
|
||||||
|
lda #${range.first}
|
||||||
|
sta $varname
|
||||||
|
lda #${range.last-range.first+1 and 255}
|
||||||
|
sta $counterLabel
|
||||||
|
$loopLabel""")
|
||||||
|
asmgen.translate(stmt.body)
|
||||||
|
asmgen.out("""
|
||||||
|
$continueLabel dec $counterLabel
|
||||||
|
beq $endLabel
|
||||||
|
inc $varname
|
||||||
|
jmp $loopLabel
|
||||||
|
$counterLabel .byte 0
|
||||||
|
$endLabel""")
|
||||||
|
}
|
||||||
|
range.step==-1 -> {
|
||||||
|
// step = -1
|
||||||
|
asmgen.out("""
|
||||||
|
lda #${range.first}
|
||||||
|
sta $varname
|
||||||
|
lda #${range.first-range.last+1 and 255}
|
||||||
|
sta $counterLabel
|
||||||
|
$loopLabel""")
|
||||||
|
asmgen.translate(stmt.body)
|
||||||
|
asmgen.out("""
|
||||||
|
$continueLabel dec $counterLabel
|
||||||
|
beq $endLabel
|
||||||
|
dec $varname
|
||||||
|
jmp $loopLabel
|
||||||
|
$counterLabel .byte 0
|
||||||
|
$endLabel""")
|
||||||
|
}
|
||||||
|
range.step >= 2 -> {
|
||||||
|
// step >= 2
|
||||||
|
asmgen.out("""
|
||||||
|
lda #${(range.last-range.first) / range.step + 1}
|
||||||
|
sta $counterLabel
|
||||||
|
lda #${range.first}
|
||||||
|
sta $varname
|
||||||
|
$loopLabel""")
|
||||||
|
asmgen.translate(stmt.body)
|
||||||
|
asmgen.out("""
|
||||||
|
$continueLabel dec $counterLabel
|
||||||
|
beq $endLabel
|
||||||
|
lda $varname
|
||||||
|
clc
|
||||||
|
adc #${range.step}
|
||||||
|
sta $varname
|
||||||
|
jmp $loopLabel
|
||||||
|
$counterLabel .byte 0
|
||||||
|
$endLabel""")
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
// step <= -2
|
||||||
|
asmgen.out("""
|
||||||
|
lda #${(range.first-range.last) / range.step.absoluteValue + 1}
|
||||||
|
sta $counterLabel
|
||||||
|
lda #${range.first}
|
||||||
|
sta $varname
|
||||||
|
$loopLabel""")
|
||||||
|
asmgen.translate(stmt.body)
|
||||||
|
asmgen.out("""
|
||||||
|
$continueLabel dec $counterLabel
|
||||||
|
beq $endLabel
|
||||||
|
lda $varname
|
||||||
|
sec
|
||||||
|
sbc #${range.step.absoluteValue}
|
||||||
|
sta $varname
|
||||||
|
jmp $loopLabel
|
||||||
|
$counterLabel .byte 0
|
||||||
|
$endLabel""")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DataType.ARRAY_W, DataType.ARRAY_UW -> {
|
||||||
|
// loop over word range via loopvar
|
||||||
|
val varname = asmgen.asmIdentifierName(stmt.loopVar!!)
|
||||||
|
when {
|
||||||
|
range.step == 1 -> {
|
||||||
|
// word, step = 1
|
||||||
|
val lastValue = range.last+1
|
||||||
|
asmgen.out("""
|
||||||
|
lda #<${range.first}
|
||||||
|
ldy #>${range.first}
|
||||||
|
sta $varname
|
||||||
|
sty $varname+1
|
||||||
|
$loopLabel""")
|
||||||
|
asmgen.translate(stmt.body)
|
||||||
|
asmgen.out("""
|
||||||
|
$continueLabel inc $varname
|
||||||
|
bne +
|
||||||
|
inc $varname+1
|
||||||
|
+ lda $varname
|
||||||
|
cmp #<$lastValue
|
||||||
|
bne +
|
||||||
|
lda $varname+1
|
||||||
|
cmp #>$lastValue
|
||||||
|
beq $endLabel
|
||||||
|
+ jmp $loopLabel
|
||||||
|
$endLabel""")
|
||||||
|
}
|
||||||
|
range.step == -1 -> {
|
||||||
|
// word, step = 1
|
||||||
|
val lastValue = range.last-1
|
||||||
|
asmgen.out("""
|
||||||
|
lda #<${range.first}
|
||||||
|
ldy #>${range.first}
|
||||||
|
sta $varname
|
||||||
|
sty $varname+1
|
||||||
|
$loopLabel""")
|
||||||
|
asmgen.translate(stmt.body)
|
||||||
|
asmgen.out("""
|
||||||
|
$continueLabel lda $varname
|
||||||
|
bne +
|
||||||
|
dec $varname+1
|
||||||
|
+ dec $varname
|
||||||
|
lda $varname
|
||||||
|
cmp #<$lastValue
|
||||||
|
bne +
|
||||||
|
lda $varname+1
|
||||||
|
cmp #>$lastValue
|
||||||
|
beq $endLabel
|
||||||
|
+ jmp $loopLabel
|
||||||
|
$endLabel""")
|
||||||
|
}
|
||||||
|
range.step >= 2 -> {
|
||||||
|
// word, step >= 2
|
||||||
|
TODO("for, word, step>=2")
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
// step <= -2
|
||||||
|
TODO("for, word, step<=-2")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("range expression can only be byte or word")
|
||||||
|
}
|
||||||
|
asmgen.loopEndLabels.pop()
|
||||||
|
asmgen.loopContinueLabels.pop()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,235 @@
|
|||||||
|
package prog8.compiler.target.c64.codegen
|
||||||
|
|
||||||
|
import prog8.ast.IFunctionCall
|
||||||
|
import prog8.ast.Program
|
||||||
|
import prog8.ast.base.*
|
||||||
|
import prog8.ast.expressions.*
|
||||||
|
import prog8.ast.statements.AssignTarget
|
||||||
|
import prog8.ast.statements.Subroutine
|
||||||
|
import prog8.ast.statements.SubroutineParameter
|
||||||
|
import prog8.compiler.target.c64.MachineDefinition
|
||||||
|
import prog8.compiler.toHex
|
||||||
|
|
||||||
|
internal class FunctionCallAsmGen(private val program: Program, private val asmgen: AsmGen) {
|
||||||
|
|
||||||
|
internal fun translateFunctionCall(stmt: IFunctionCall) {
|
||||||
|
// output the code to setup the parameters and perform the actual call
|
||||||
|
// does NOT output the code to deal with the result values!
|
||||||
|
val sub = stmt.target.targetSubroutine(program.namespace) ?: throw AssemblyError("undefined subroutine ${stmt.target}")
|
||||||
|
if(Register.X in sub.asmClobbers)
|
||||||
|
asmgen.out(" stx c64.SCRATCH_ZPREGX") // we only save X for now (required! is the eval stack pointer), screw A and Y...
|
||||||
|
|
||||||
|
val subName = asmgen.asmIdentifierName(stmt.target)
|
||||||
|
if(stmt.arglist.isNotEmpty()) {
|
||||||
|
for(arg in sub.parameters.withIndex().zip(stmt.arglist)) {
|
||||||
|
translateFuncArguments(arg.first, arg.second, sub)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
asmgen.out(" jsr $subName")
|
||||||
|
|
||||||
|
if(Register.X in sub.asmClobbers)
|
||||||
|
asmgen.out(" ldx c64.SCRATCH_ZPREGX") // restore X again
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun translateFuncArguments(parameter: IndexedValue<SubroutineParameter>, value: Expression, sub: Subroutine) {
|
||||||
|
val sourceIDt = value.inferType(program)
|
||||||
|
if(!sourceIDt.isKnown)
|
||||||
|
throw AssemblyError("arg type unknown")
|
||||||
|
val sourceDt = sourceIDt.typeOrElse(DataType.STRUCT)
|
||||||
|
if(!argumentTypeCompatible(sourceDt, parameter.value.type))
|
||||||
|
throw AssemblyError("argument type incompatible")
|
||||||
|
if(sub.asmParameterRegisters.isEmpty()) {
|
||||||
|
// pass parameter via a variable
|
||||||
|
val paramVar = parameter.value
|
||||||
|
val scopedParamVar = (sub.scopedname+"."+paramVar.name).split(".")
|
||||||
|
val target = AssignTarget(null, IdentifierReference(scopedParamVar, sub.position), null, null, sub.position)
|
||||||
|
target.linkParents(value.parent)
|
||||||
|
when (value) {
|
||||||
|
is NumericLiteralValue -> {
|
||||||
|
// optimize when the argument is a constant literal
|
||||||
|
when(parameter.value.type) {
|
||||||
|
in ByteDatatypes -> asmgen.assignFromByteConstant(target, value.number.toShort())
|
||||||
|
in WordDatatypes -> asmgen.assignFromWordConstant(target, value.number.toInt())
|
||||||
|
DataType.FLOAT -> asmgen.assignFromFloatConstant(target, value.number.toDouble())
|
||||||
|
in PassByReferenceDatatypes -> throw AssemblyError("can't pass string/array as arguments?")
|
||||||
|
else -> throw AssemblyError("weird parameter datatype")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is IdentifierReference -> {
|
||||||
|
// optimize when the argument is a variable
|
||||||
|
when (parameter.value.type) {
|
||||||
|
in ByteDatatypes -> asmgen.assignFromByteVariable(target, value)
|
||||||
|
in WordDatatypes -> asmgen.assignFromWordVariable(target, value)
|
||||||
|
DataType.FLOAT -> asmgen.assignFromFloatVariable(target, value)
|
||||||
|
in PassByReferenceDatatypes -> throw AssemblyError("can't pass string/array as arguments?")
|
||||||
|
else -> throw AssemblyError("weird parameter datatype")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is RegisterExpr -> {
|
||||||
|
asmgen.assignFromRegister(target, value.register)
|
||||||
|
}
|
||||||
|
is DirectMemoryRead -> {
|
||||||
|
when(value.addressExpression) {
|
||||||
|
is NumericLiteralValue -> {
|
||||||
|
val address = (value.addressExpression as NumericLiteralValue).number.toInt()
|
||||||
|
asmgen.assignFromMemoryByte(target, address, null)
|
||||||
|
}
|
||||||
|
is IdentifierReference -> {
|
||||||
|
asmgen.assignFromMemoryByte(target, null, value.addressExpression as IdentifierReference)
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
asmgen.translateExpression(value.addressExpression)
|
||||||
|
asmgen.out(" jsr prog8_lib.read_byte_from_address | inx")
|
||||||
|
asmgen.assignFromRegister(target, Register.A)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
asmgen.translateExpression(value)
|
||||||
|
asmgen.assignFromEvalResult(target)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// pass parameter via a register parameter
|
||||||
|
val paramRegister = sub.asmParameterRegisters[parameter.index]
|
||||||
|
val statusflag = paramRegister.statusflag
|
||||||
|
val register = paramRegister.registerOrPair
|
||||||
|
val stack = paramRegister.stack
|
||||||
|
when {
|
||||||
|
stack -> {
|
||||||
|
// push arg onto the stack
|
||||||
|
// note: argument order is reversed (first argument will be deepest on the stack)
|
||||||
|
asmgen.translateExpression(value)
|
||||||
|
}
|
||||||
|
statusflag!=null -> {
|
||||||
|
if (statusflag == Statusflag.Pc) {
|
||||||
|
// 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
|
||||||
|
when(value) {
|
||||||
|
is NumericLiteralValue -> {
|
||||||
|
val carrySet = value.number.toInt() != 0
|
||||||
|
asmgen.out(if(carrySet) " sec" else " clc")
|
||||||
|
}
|
||||||
|
is IdentifierReference -> {
|
||||||
|
val sourceName = asmgen.asmIdentifierName(value)
|
||||||
|
asmgen.out("""
|
||||||
|
lda $sourceName
|
||||||
|
beq +
|
||||||
|
sec
|
||||||
|
bcs ++
|
||||||
|
+ clc
|
||||||
|
+
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
is RegisterExpr -> {
|
||||||
|
when(value.register) {
|
||||||
|
Register.A -> asmgen.out(" cmp #0")
|
||||||
|
Register.X -> asmgen.out(" txa")
|
||||||
|
Register.Y -> asmgen.out(" tya")
|
||||||
|
}
|
||||||
|
asmgen.out("""
|
||||||
|
beq +
|
||||||
|
sec
|
||||||
|
bcs ++
|
||||||
|
+ clc
|
||||||
|
+
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
asmgen.translateExpression(value)
|
||||||
|
asmgen.out("""
|
||||||
|
inx
|
||||||
|
lda ${MachineDefinition.ESTACK_LO_HEX},x
|
||||||
|
beq +
|
||||||
|
sec
|
||||||
|
bcs ++
|
||||||
|
+ clc
|
||||||
|
+
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else throw AssemblyError("can only use Carry as status flag parameter")
|
||||||
|
}
|
||||||
|
register!=null && register.name.length==1 -> {
|
||||||
|
when (value) {
|
||||||
|
is NumericLiteralValue -> {
|
||||||
|
val target = AssignTarget(Register.valueOf(register.name), null, null, null, sub.position)
|
||||||
|
target.linkParents(value.parent)
|
||||||
|
asmgen.assignFromByteConstant(target, value.number.toShort())
|
||||||
|
}
|
||||||
|
is IdentifierReference -> {
|
||||||
|
val target = AssignTarget(Register.valueOf(register.name), null, null, null, sub.position)
|
||||||
|
target.linkParents(value.parent)
|
||||||
|
asmgen.assignFromByteVariable(target, value)
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
asmgen.translateExpression(value)
|
||||||
|
when(register) {
|
||||||
|
RegisterOrPair.A -> asmgen.out(" inx | lda ${MachineDefinition.ESTACK_LO_HEX},x")
|
||||||
|
RegisterOrPair.X -> throw AssemblyError("can't pop into X register - use a variable instead")
|
||||||
|
RegisterOrPair.Y -> asmgen.out(" inx | ldy ${MachineDefinition.ESTACK_LO_HEX},x")
|
||||||
|
else -> throw AssemblyError("cannot assign to register pair")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
register!=null && register.name.length==2 -> {
|
||||||
|
// register pair as a 16-bit value (only possible for subroutine parameters)
|
||||||
|
when (value) {
|
||||||
|
is NumericLiteralValue -> {
|
||||||
|
// optimize when the argument is a constant literal
|
||||||
|
val hex = value.number.toHex()
|
||||||
|
when (register) {
|
||||||
|
RegisterOrPair.AX -> asmgen.out(" lda #<$hex | ldx #>$hex")
|
||||||
|
RegisterOrPair.AY -> asmgen.out(" lda #<$hex | ldy #>$hex")
|
||||||
|
RegisterOrPair.XY -> asmgen.out(" ldx #<$hex | ldy #>$hex")
|
||||||
|
else -> {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is AddressOf -> {
|
||||||
|
// optimize when the argument is an address of something
|
||||||
|
val sourceName = asmgen.asmIdentifierName(value.identifier)
|
||||||
|
when (register) {
|
||||||
|
RegisterOrPair.AX -> asmgen.out(" lda #<$sourceName | ldx #>$sourceName")
|
||||||
|
RegisterOrPair.AY -> asmgen.out(" lda #<$sourceName | ldy #>$sourceName")
|
||||||
|
RegisterOrPair.XY -> asmgen.out(" ldx #<$sourceName | ldy #>$sourceName")
|
||||||
|
else -> {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is IdentifierReference -> {
|
||||||
|
val sourceName = asmgen.asmIdentifierName(value)
|
||||||
|
when (register) {
|
||||||
|
RegisterOrPair.AX -> asmgen.out(" lda $sourceName | ldx $sourceName+1")
|
||||||
|
RegisterOrPair.AY -> asmgen.out(" lda $sourceName | ldy $sourceName+1")
|
||||||
|
RegisterOrPair.XY -> asmgen.out(" ldx $sourceName | ldy $sourceName+1")
|
||||||
|
else -> {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
asmgen.translateExpression(value)
|
||||||
|
if (register == RegisterOrPair.AX || register == RegisterOrPair.XY)
|
||||||
|
throw AssemblyError("can't use X register here - use a variable")
|
||||||
|
else if (register == RegisterOrPair.AY)
|
||||||
|
asmgen.out(" inx | lda ${MachineDefinition.ESTACK_LO_HEX},x | ldy ${MachineDefinition.ESTACK_HI_HEX},x")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun argumentTypeCompatible(argType: DataType, paramType: DataType): Boolean {
|
||||||
|
if(argType isAssignableTo paramType)
|
||||||
|
return true
|
||||||
|
|
||||||
|
// we have a special rule for some types.
|
||||||
|
// strings are assignable to UWORD, for example, and vice versa
|
||||||
|
if(argType in StringDatatypes && paramType==DataType.UWORD)
|
||||||
|
return true
|
||||||
|
if(argType==DataType.UWORD && paramType in StringDatatypes)
|
||||||
|
return true
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,149 @@
|
|||||||
|
package prog8.compiler.target.c64.codegen
|
||||||
|
|
||||||
|
import prog8.ast.Program
|
||||||
|
import prog8.ast.base.*
|
||||||
|
import prog8.ast.expressions.IdentifierReference
|
||||||
|
import prog8.ast.expressions.NumericLiteralValue
|
||||||
|
import prog8.ast.expressions.RegisterExpr
|
||||||
|
import prog8.ast.statements.PostIncrDecr
|
||||||
|
import prog8.compiler.target.c64.MachineDefinition
|
||||||
|
import prog8.compiler.toHex
|
||||||
|
|
||||||
|
internal class PostIncrDecrAsmGen(private val program: Program, private val asmgen: AsmGen) {
|
||||||
|
internal fun translate(stmt: PostIncrDecr) {
|
||||||
|
val incr = stmt.operator=="++"
|
||||||
|
val targetIdent = stmt.target.identifier
|
||||||
|
val targetMemory = stmt.target.memoryAddress
|
||||||
|
val targetArrayIdx = stmt.target.arrayindexed
|
||||||
|
val targetRegister = stmt.target.register
|
||||||
|
when {
|
||||||
|
targetRegister!=null -> {
|
||||||
|
when(targetRegister) {
|
||||||
|
Register.A -> {
|
||||||
|
if(incr)
|
||||||
|
asmgen.out(" clc | adc #1 ")
|
||||||
|
else
|
||||||
|
asmgen.out(" sec | sbc #1 ")
|
||||||
|
}
|
||||||
|
Register.X -> {
|
||||||
|
if(incr) asmgen.out(" inx") else asmgen.out(" dex")
|
||||||
|
}
|
||||||
|
Register.Y -> {
|
||||||
|
if(incr) asmgen.out(" iny") else asmgen.out(" dey")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
targetIdent!=null -> {
|
||||||
|
val what = asmgen.asmIdentifierName(targetIdent)
|
||||||
|
val dt = stmt.target.inferType(program, stmt).typeOrElse(DataType.STRUCT)
|
||||||
|
when (dt) {
|
||||||
|
in ByteDatatypes -> asmgen.out(if (incr) " inc $what" else " dec $what")
|
||||||
|
in WordDatatypes -> {
|
||||||
|
if(incr)
|
||||||
|
asmgen.out(" inc $what | bne + | inc $what+1 |+")
|
||||||
|
else
|
||||||
|
asmgen.out("""
|
||||||
|
lda $what
|
||||||
|
bne +
|
||||||
|
dec $what+1
|
||||||
|
+ dec $what
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
DataType.FLOAT -> {
|
||||||
|
asmgen.out(" lda #<$what | ldy #>$what")
|
||||||
|
asmgen.out(if(incr) " jsr c64flt.inc_var_f" else " jsr c64flt.dec_var_f")
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("need numeric type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
targetMemory!=null -> {
|
||||||
|
val addressExpr = targetMemory.addressExpression
|
||||||
|
when (addressExpr) {
|
||||||
|
is NumericLiteralValue -> {
|
||||||
|
val what = addressExpr.number.toHex()
|
||||||
|
asmgen.out(if(incr) " inc $what" else " dec $what")
|
||||||
|
}
|
||||||
|
is IdentifierReference -> {
|
||||||
|
val what = asmgen.asmIdentifierName(addressExpr)
|
||||||
|
asmgen.out(if(incr) " inc $what" else " dec $what")
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("weird target type $targetMemory")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
targetArrayIdx!=null -> {
|
||||||
|
val index = targetArrayIdx.arrayspec.index
|
||||||
|
val what = asmgen.asmIdentifierName(targetArrayIdx.identifier)
|
||||||
|
val arrayDt = targetArrayIdx.identifier.inferType(program).typeOrElse(DataType.STRUCT)
|
||||||
|
val elementDt = ArrayElementTypes.getValue(arrayDt)
|
||||||
|
when(index) {
|
||||||
|
is NumericLiteralValue -> {
|
||||||
|
val indexValue = index.number.toInt() * elementDt.memorySize()
|
||||||
|
when(elementDt) {
|
||||||
|
in ByteDatatypes -> asmgen.out(if (incr) " inc $what+$indexValue" else " dec $what+$indexValue")
|
||||||
|
in WordDatatypes -> {
|
||||||
|
if(incr)
|
||||||
|
asmgen.out(" inc $what+$indexValue | bne + | inc $what+$indexValue+1 |+")
|
||||||
|
else
|
||||||
|
asmgen.out("""
|
||||||
|
lda $what+$indexValue
|
||||||
|
bne +
|
||||||
|
dec $what+$indexValue+1
|
||||||
|
+ dec $what+$indexValue
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
DataType.FLOAT -> {
|
||||||
|
asmgen.out(" lda #<$what+$indexValue | ldy #>$what+$indexValue")
|
||||||
|
asmgen.out(if(incr) " jsr c64flt.inc_var_f" else " jsr c64flt.dec_var_f")
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("need numeric type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is RegisterExpr -> {
|
||||||
|
// TODO optimize common cases
|
||||||
|
asmgen.translateArrayIndexIntoA(targetArrayIdx)
|
||||||
|
incrDecrArrayvalueWithIndexA(incr, arrayDt, what)
|
||||||
|
}
|
||||||
|
is IdentifierReference -> {
|
||||||
|
// TODO optimize common cases
|
||||||
|
asmgen.translateArrayIndexIntoA(targetArrayIdx)
|
||||||
|
incrDecrArrayvalueWithIndexA(incr, arrayDt, what)
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
// TODO optimize common cases
|
||||||
|
asmgen.translateArrayIndexIntoA(targetArrayIdx)
|
||||||
|
incrDecrArrayvalueWithIndexA(incr, arrayDt, what)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("weird target type ${stmt.target}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun incrDecrArrayvalueWithIndexA(incr: Boolean, arrayDt: DataType, arrayVarName: String) {
|
||||||
|
asmgen.out(" stx ${MachineDefinition.C64Zeropage.SCRATCH_REG_X} | tax")
|
||||||
|
when(arrayDt) {
|
||||||
|
DataType.STR, DataType.STR_S,
|
||||||
|
DataType.ARRAY_UB, DataType.ARRAY_B -> {
|
||||||
|
asmgen.out(if(incr) " inc $arrayVarName,x" else " dec $arrayVarName,x")
|
||||||
|
}
|
||||||
|
DataType.ARRAY_UW, DataType.ARRAY_W -> {
|
||||||
|
if(incr)
|
||||||
|
asmgen.out(" inc $arrayVarName,x | bne + | inc $arrayVarName+1,x |+")
|
||||||
|
else
|
||||||
|
asmgen.out("""
|
||||||
|
lda $arrayVarName,x
|
||||||
|
bne +
|
||||||
|
dec $arrayVarName+1,x
|
||||||
|
+ dec $arrayVarName
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
DataType.ARRAY_F -> {
|
||||||
|
asmgen.out(" lda #<$arrayVarName | ldy #>$arrayVarName")
|
||||||
|
asmgen.out(if(incr) " jsr c64flt.inc_indexed_var_f" else " jsr c64flt.dec_indexed_var_f")
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("weird array dt")
|
||||||
|
}
|
||||||
|
asmgen.out(" ldx ${MachineDefinition.C64Zeropage.SCRATCH_REG_X}")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
@ -34,6 +34,7 @@ val BuiltinFunctions = mapOf(
|
|||||||
"abs" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", NumericDatatypes)), null, ::builtinAbs), // type depends on argument
|
"abs" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", NumericDatatypes)), null, ::builtinAbs), // type depends on argument
|
||||||
"len" to FunctionSignature(true, listOf(BuiltinFunctionParam("values", IterableDatatypes)), null, ::builtinLen), // type is UBYTE or UWORD depending on actual length
|
"len" to FunctionSignature(true, listOf(BuiltinFunctionParam("values", IterableDatatypes)), null, ::builtinLen), // type is UBYTE or UWORD depending on actual length
|
||||||
// normal functions follow:
|
// normal functions follow:
|
||||||
|
"sgn" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", NumericDatatypes)), DataType.BYTE, ::builtinSgn ),
|
||||||
"sin" to FunctionSignature(true, listOf(BuiltinFunctionParam("rads", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::sin) },
|
"sin" to FunctionSignature(true, listOf(BuiltinFunctionParam("rads", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::sin) },
|
||||||
"sin8" to FunctionSignature(true, listOf(BuiltinFunctionParam("angle8", setOf(DataType.UBYTE))), DataType.BYTE, ::builtinSin8 ),
|
"sin8" to FunctionSignature(true, listOf(BuiltinFunctionParam("angle8", setOf(DataType.UBYTE))), DataType.BYTE, ::builtinSin8 ),
|
||||||
"sin8u" to FunctionSignature(true, listOf(BuiltinFunctionParam("angle8", setOf(DataType.UBYTE))), DataType.UBYTE, ::builtinSin8u ),
|
"sin8u" to FunctionSignature(true, listOf(BuiltinFunctionParam("angle8", setOf(DataType.UBYTE))), DataType.UBYTE, ::builtinSin8u ),
|
||||||
@ -112,25 +113,29 @@ val BuiltinFunctions = mapOf(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
fun builtinFunctionReturnType(function: String, args: List<Expression>, program: Program): DataType? {
|
fun builtinFunctionReturnType(function: String, args: List<Expression>, program: Program): InferredTypes.InferredType {
|
||||||
|
|
||||||
fun datatypeFromIterableArg(arglist: Expression): DataType {
|
fun datatypeFromIterableArg(arglist: Expression): DataType {
|
||||||
if(arglist is ReferenceLiteralValue) {
|
if(arglist is ArrayLiteralValue) {
|
||||||
if(arglist.type== DataType.ARRAY_UB || arglist.type== DataType.ARRAY_UW || arglist.type== DataType.ARRAY_F) {
|
if(arglist.type== DataType.ARRAY_UB || arglist.type== DataType.ARRAY_UW || arglist.type== DataType.ARRAY_F) {
|
||||||
val dt = arglist.array!!.map {it.inferType(program)}
|
val dt = arglist.value.map {it.inferType(program)}
|
||||||
if(dt.any { it!= DataType.UBYTE && it!= DataType.UWORD && it!= DataType.FLOAT}) {
|
if(dt.any { !(it istype DataType.UBYTE) && !(it istype DataType.UWORD) && !(it istype DataType.FLOAT)}) {
|
||||||
throw FatalAstException("fuction $function only accepts arraysize of numeric values")
|
throw FatalAstException("fuction $function only accepts arraysize of numeric values")
|
||||||
}
|
}
|
||||||
if(dt.any { it== DataType.FLOAT }) return DataType.FLOAT
|
if(dt.any { it istype DataType.FLOAT }) return DataType.FLOAT
|
||||||
if(dt.any { it== DataType.UWORD }) return DataType.UWORD
|
if(dt.any { it istype DataType.UWORD }) return DataType.UWORD
|
||||||
return DataType.UBYTE
|
return DataType.UBYTE
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(arglist is IdentifierReference) {
|
if(arglist is IdentifierReference) {
|
||||||
return when(val dt = arglist.inferType(program)) {
|
val idt = arglist.inferType(program)
|
||||||
in NumericDatatypes -> dt!!
|
if(!idt.isKnown)
|
||||||
in StringDatatypes -> dt!!
|
throw FatalAstException("couldn't determine type of iterable $arglist")
|
||||||
in ArrayDatatypes -> ArrayElementTypes.getValue(dt!!)
|
val dt = idt.typeOrElse(DataType.STRUCT)
|
||||||
|
return when(dt) {
|
||||||
|
in NumericDatatypes -> dt
|
||||||
|
in StringDatatypes -> dt
|
||||||
|
in ArrayDatatypes -> ArrayElementTypes.getValue(dt)
|
||||||
else -> throw FatalAstException("function '$function' requires one argument which is an iterable")
|
else -> throw FatalAstException("function '$function' requires one argument which is an iterable")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -139,48 +144,48 @@ fun builtinFunctionReturnType(function: String, args: List<Expression>, program:
|
|||||||
|
|
||||||
val func = BuiltinFunctions.getValue(function)
|
val func = BuiltinFunctions.getValue(function)
|
||||||
if(func.returntype!=null)
|
if(func.returntype!=null)
|
||||||
return func.returntype
|
return InferredTypes.knownFor(func.returntype)
|
||||||
// function has return values, but the return type depends on the arguments
|
// function has return values, but the return type depends on the arguments
|
||||||
|
|
||||||
return when (function) {
|
return when (function) {
|
||||||
"abs" -> {
|
"abs" -> {
|
||||||
val dt = args.single().inferType(program)
|
val dt = args.single().inferType(program)
|
||||||
if(dt in NumericDatatypes)
|
if(dt.typeOrElse(DataType.STRUCT) in NumericDatatypes)
|
||||||
return dt
|
return dt
|
||||||
else
|
else
|
||||||
throw FatalAstException("weird datatype passed to abs $dt")
|
throw FatalAstException("weird datatype passed to abs $dt")
|
||||||
}
|
}
|
||||||
"max", "min" -> {
|
"max", "min" -> {
|
||||||
when(val dt = datatypeFromIterableArg(args.single())) {
|
when(val dt = datatypeFromIterableArg(args.single())) {
|
||||||
in NumericDatatypes -> dt
|
in NumericDatatypes -> InferredTypes.knownFor(dt)
|
||||||
in StringDatatypes -> DataType.UBYTE
|
in StringDatatypes -> InferredTypes.knownFor(DataType.UBYTE)
|
||||||
in ArrayDatatypes -> ArrayElementTypes.getValue(dt)
|
in ArrayDatatypes -> InferredTypes.knownFor(ArrayElementTypes.getValue(dt))
|
||||||
else -> null
|
else -> InferredTypes.unknown()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"sum" -> {
|
"sum" -> {
|
||||||
when(datatypeFromIterableArg(args.single())) {
|
when(datatypeFromIterableArg(args.single())) {
|
||||||
DataType.UBYTE, DataType.UWORD -> DataType.UWORD
|
DataType.UBYTE, DataType.UWORD -> InferredTypes.knownFor(DataType.UWORD)
|
||||||
DataType.BYTE, DataType.WORD -> DataType.WORD
|
DataType.BYTE, DataType.WORD -> InferredTypes.knownFor(DataType.WORD)
|
||||||
DataType.FLOAT -> DataType.FLOAT
|
DataType.FLOAT -> InferredTypes.knownFor(DataType.FLOAT)
|
||||||
DataType.ARRAY_UB, DataType.ARRAY_UW -> DataType.UWORD
|
DataType.ARRAY_UB, DataType.ARRAY_UW -> InferredTypes.knownFor(DataType.UWORD)
|
||||||
DataType.ARRAY_B, DataType.ARRAY_W -> DataType.WORD
|
DataType.ARRAY_B, DataType.ARRAY_W -> InferredTypes.knownFor(DataType.WORD)
|
||||||
DataType.ARRAY_F -> DataType.FLOAT
|
DataType.ARRAY_F -> InferredTypes.knownFor(DataType.FLOAT)
|
||||||
in StringDatatypes -> DataType.UWORD
|
in StringDatatypes -> InferredTypes.knownFor(DataType.UWORD)
|
||||||
else -> null
|
else -> InferredTypes.unknown()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"len" -> {
|
"len" -> {
|
||||||
// a length can be >255 so in that case, the result is an UWORD instead of an UBYTE
|
// a length can be >255 so in that case, the result is an UWORD instead of an UBYTE
|
||||||
// but to avoid a lot of code duplication we simply assume UWORD in all cases for now
|
// but to avoid a lot of code duplication we simply assume UWORD in all cases for now
|
||||||
return DataType.UWORD
|
return InferredTypes.knownFor(DataType.UWORD)
|
||||||
}
|
}
|
||||||
else -> return null
|
else -> return InferredTypes.unknown()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class NotConstArgumentException: AstException("not a const argument to a built-in function")
|
class NotConstArgumentException: AstException("not a const argument to a built-in function") // TODO: ugly, remove throwing exceptions for control flow
|
||||||
|
|
||||||
|
|
||||||
private fun oneDoubleArg(args: List<Expression>, position: Position, program: Program, function: (arg: Double)->Number): NumericLiteralValue {
|
private fun oneDoubleArg(args: List<Expression>, position: Position, program: Program, function: (arg: Double)->Number): NumericLiteralValue {
|
||||||
@ -271,10 +276,10 @@ private fun builtinLen(args: List<Expression>, position: Position, program: Prog
|
|||||||
NumericLiteralValue.optimalInteger(arraySize, args[0].position)
|
NumericLiteralValue.optimalInteger(arraySize, args[0].position)
|
||||||
}
|
}
|
||||||
in StringDatatypes -> {
|
in StringDatatypes -> {
|
||||||
val refLv = target.value as ReferenceLiteralValue
|
val refLv = target.value as StringLiteralValue
|
||||||
if(refLv.str!!.length>255)
|
if(refLv.value.length>255)
|
||||||
throw CompilerException("string length exceeds byte limit ${refLv.position}")
|
throw CompilerException("string length exceeds byte limit ${refLv.position}")
|
||||||
NumericLiteralValue.optimalInteger(refLv.str.length, args[0].position)
|
NumericLiteralValue.optimalInteger(refLv.value.length, args[0].position)
|
||||||
}
|
}
|
||||||
in NumericDatatypes -> throw SyntaxError("len of weird argument ${args[0]}", position)
|
in NumericDatatypes -> throw SyntaxError("len of weird argument ${args[0]}", position)
|
||||||
else -> throw CompilerException("weird datatype")
|
else -> throw CompilerException("weird datatype")
|
||||||
@ -355,6 +360,13 @@ private fun builtinCos16u(args: List<Expression>, position: Position, program: P
|
|||||||
return NumericLiteralValue(DataType.UWORD, (32768.0 + 32767.5 * cos(rad)).toInt(), position)
|
return NumericLiteralValue(DataType.UWORD, (32768.0 + 32767.5 * cos(rad)).toInt(), position)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun builtinSgn(args: List<Expression>, position: Position, program: Program): NumericLiteralValue {
|
||||||
|
if (args.size != 1)
|
||||||
|
throw SyntaxError("sgn requires one argument", position)
|
||||||
|
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
|
||||||
|
return NumericLiteralValue(DataType.BYTE, constval.number.toDouble().sign.toShort(), position)
|
||||||
|
}
|
||||||
|
|
||||||
private fun numericLiteral(value: Number, position: Position): NumericLiteralValue {
|
private fun numericLiteral(value: Number, position: Position): NumericLiteralValue {
|
||||||
val floatNum=value.toDouble()
|
val floatNum=value.toDouble()
|
||||||
val tweakedValue: Number =
|
val tweakedValue: Number =
|
||||||
|
@ -9,7 +9,7 @@ import prog8.ast.processing.fixupArrayDatatype
|
|||||||
import prog8.ast.statements.*
|
import prog8.ast.statements.*
|
||||||
import prog8.compiler.target.c64.MachineDefinition.FLOAT_MAX_NEGATIVE
|
import prog8.compiler.target.c64.MachineDefinition.FLOAT_MAX_NEGATIVE
|
||||||
import prog8.compiler.target.c64.MachineDefinition.FLOAT_MAX_POSITIVE
|
import prog8.compiler.target.c64.MachineDefinition.FLOAT_MAX_POSITIVE
|
||||||
import prog8.compiler.target.c64.codegen2.AssemblyError
|
import prog8.compiler.target.c64.codegen.AssemblyError
|
||||||
import prog8.functions.BuiltinFunctions
|
import prog8.functions.BuiltinFunctions
|
||||||
import kotlin.math.floor
|
import kotlin.math.floor
|
||||||
|
|
||||||
@ -39,11 +39,9 @@ class ConstantFolding(private val program: Program) : IAstModifyingVisitor {
|
|||||||
if(decl.isArray){
|
if(decl.isArray){
|
||||||
if(decl.arraysize==null) {
|
if(decl.arraysize==null) {
|
||||||
// for arrays that have no size specifier (or a non-constant one) attempt to deduce the size
|
// for arrays that have no size specifier (or a non-constant one) attempt to deduce the size
|
||||||
val arrayval = (decl.value as? ReferenceLiteralValue)?.array
|
val arrayval = (decl.value as ArrayLiteralValue).value
|
||||||
if(arrayval!=null) {
|
decl.arraysize = ArrayIndex(NumericLiteralValue.optimalInteger(arrayval.size, decl.position), decl.position)
|
||||||
decl.arraysize = ArrayIndex(NumericLiteralValue.optimalInteger(arrayval.size, decl.position), decl.position)
|
optimizationsDone++
|
||||||
optimizationsDone++
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if(decl.arraysize?.size()==null) {
|
else if(decl.arraysize?.size()==null) {
|
||||||
val size = decl.arraysize!!.index.accept(this)
|
val size = decl.arraysize!!.index.accept(this)
|
||||||
@ -81,15 +79,15 @@ class ConstantFolding(private val program: Program) : IAstModifyingVisitor {
|
|||||||
errors.add(ExpressionError("range expression size doesn't match declared array size", decl.value?.position!!))
|
errors.add(ExpressionError("range expression size doesn't match declared array size", decl.value?.position!!))
|
||||||
val constRange = rangeExpr.toConstantIntegerRange()
|
val constRange = rangeExpr.toConstantIntegerRange()
|
||||||
if(constRange!=null) {
|
if(constRange!=null) {
|
||||||
val eltType = rangeExpr.inferType(program)!!
|
val eltType = rangeExpr.inferType(program).typeOrElse(DataType.UBYTE)
|
||||||
if(eltType in ByteDatatypes) {
|
if(eltType in ByteDatatypes) {
|
||||||
decl.value = ReferenceLiteralValue(decl.datatype,
|
decl.value = ArrayLiteralValue(decl.datatype,
|
||||||
array = constRange.map { NumericLiteralValue(eltType, it.toShort(), decl.value!!.position) }
|
constRange.map { NumericLiteralValue(eltType, it.toShort(), decl.value!!.position) }.toTypedArray(),
|
||||||
.toTypedArray(), position = decl.value!!.position)
|
position = decl.value!!.position)
|
||||||
} else {
|
} else {
|
||||||
decl.value = ReferenceLiteralValue(decl.datatype,
|
decl.value = ArrayLiteralValue(decl.datatype,
|
||||||
array = constRange.map { NumericLiteralValue(eltType, it, decl.value!!.position) }
|
constRange.map { NumericLiteralValue(eltType, it, decl.value!!.position) }.toTypedArray(),
|
||||||
.toTypedArray(), position = decl.value!!.position)
|
position = decl.value!!.position)
|
||||||
}
|
}
|
||||||
decl.value!!.linkParents(decl)
|
decl.value!!.linkParents(decl)
|
||||||
optimizationsDone++
|
optimizationsDone++
|
||||||
@ -123,7 +121,7 @@ class ConstantFolding(private val program: Program) : IAstModifyingVisitor {
|
|||||||
}
|
}
|
||||||
// create the array itself, filled with the fillvalue.
|
// create the array itself, filled with the fillvalue.
|
||||||
val array = Array(size) {fillvalue}.map { NumericLiteralValue.optimalInteger(it, numericLv.position) as Expression}.toTypedArray()
|
val array = Array(size) {fillvalue}.map { NumericLiteralValue.optimalInteger(it, numericLv.position) as Expression}.toTypedArray()
|
||||||
val refValue = ReferenceLiteralValue(decl.datatype, array = array, position = numericLv.position)
|
val refValue = ArrayLiteralValue(decl.datatype, array, position = numericLv.position)
|
||||||
refValue.addToHeap(program.heap)
|
refValue.addToHeap(program.heap)
|
||||||
decl.value = refValue
|
decl.value = refValue
|
||||||
refValue.parent=decl
|
refValue.parent=decl
|
||||||
@ -145,7 +143,7 @@ class ConstantFolding(private val program: Program) : IAstModifyingVisitor {
|
|||||||
else {
|
else {
|
||||||
// create the array itself, filled with the fillvalue.
|
// create the array itself, filled with the fillvalue.
|
||||||
val array = Array(size) {fillvalue}.map { NumericLiteralValue(DataType.FLOAT, it, litval.position) as Expression}.toTypedArray()
|
val array = Array(size) {fillvalue}.map { NumericLiteralValue(DataType.FLOAT, it, litval.position) as Expression}.toTypedArray()
|
||||||
val refValue = ReferenceLiteralValue(DataType.ARRAY_F, array = array, position = litval.position)
|
val refValue = ArrayLiteralValue(DataType.ARRAY_F, array, position = litval.position)
|
||||||
refValue.addToHeap(program.heap)
|
refValue.addToHeap(program.heap)
|
||||||
decl.value = refValue
|
decl.value = refValue
|
||||||
refValue.parent=decl
|
refValue.parent=decl
|
||||||
@ -210,7 +208,7 @@ class ConstantFolding(private val program: Program) : IAstModifyingVisitor {
|
|||||||
val possibleDts = arg.second.possibleDatatypes
|
val possibleDts = arg.second.possibleDatatypes
|
||||||
val argConst = arg.first.value.constValue(program)
|
val argConst = arg.first.value.constValue(program)
|
||||||
if(argConst!=null && argConst.type !in possibleDts) {
|
if(argConst!=null && argConst.type !in possibleDts) {
|
||||||
val convertedValue = argConst.cast(possibleDts.first()) // TODO can throw exception
|
val convertedValue = argConst.cast(possibleDts.first())
|
||||||
functionCall.arglist[arg.first.index] = convertedValue
|
functionCall.arglist[arg.first.index] = convertedValue
|
||||||
optimizationsDone++
|
optimizationsDone++
|
||||||
}
|
}
|
||||||
@ -226,7 +224,7 @@ class ConstantFolding(private val program: Program) : IAstModifyingVisitor {
|
|||||||
val expectedDt = arg.second.type
|
val expectedDt = arg.second.type
|
||||||
val argConst = arg.first.value.constValue(program)
|
val argConst = arg.first.value.constValue(program)
|
||||||
if(argConst!=null && argConst.type!=expectedDt) {
|
if(argConst!=null && argConst.type!=expectedDt) {
|
||||||
val convertedValue = argConst.cast(expectedDt) // TODO can throw exception
|
val convertedValue = argConst.cast(expectedDt)
|
||||||
functionCall.arglist[arg.first.index] = convertedValue
|
functionCall.arglist[arg.first.index] = convertedValue
|
||||||
optimizationsDone++
|
optimizationsDone++
|
||||||
}
|
}
|
||||||
@ -311,7 +309,8 @@ class ConstantFolding(private val program: Program) : IAstModifyingVisitor {
|
|||||||
return try {
|
return try {
|
||||||
super.visit(expr)
|
super.visit(expr)
|
||||||
|
|
||||||
if(expr.left is ReferenceLiteralValue || expr.right is ReferenceLiteralValue)
|
if(expr.left is StringLiteralValue || expr.left is ArrayLiteralValue
|
||||||
|
|| expr.right is StringLiteralValue || expr.right is ArrayLiteralValue)
|
||||||
throw FatalAstException("binexpr with reference litval instead of numeric")
|
throw FatalAstException("binexpr with reference litval instead of numeric")
|
||||||
|
|
||||||
val leftconst = expr.left.constValue(program)
|
val leftconst = expr.left.constValue(program)
|
||||||
@ -356,7 +355,7 @@ class ConstantFolding(private val program: Program) : IAstModifyingVisitor {
|
|||||||
subleftIsConst: Boolean,
|
subleftIsConst: Boolean,
|
||||||
subrightIsConst: Boolean): Expression
|
subrightIsConst: Boolean): Expression
|
||||||
{
|
{
|
||||||
// @todo this implements only a small set of possible reorderings for now
|
// todo: this implements only a small set of possible reorderings at this time
|
||||||
if(expr.operator==subExpr.operator) {
|
if(expr.operator==subExpr.operator) {
|
||||||
// both operators are the isSameAs.
|
// both operators are the isSameAs.
|
||||||
// If + or *, we can simply swap the const of expr and Var in subexpr.
|
// If + or *, we can simply swap the const of expr and Var in subexpr.
|
||||||
@ -544,11 +543,15 @@ class ConstantFolding(private val program: Program) : IAstModifyingVisitor {
|
|||||||
override fun visit(forLoop: ForLoop): Statement {
|
override fun visit(forLoop: ForLoop): Statement {
|
||||||
|
|
||||||
fun adjustRangeDt(rangeFrom: NumericLiteralValue, targetDt: DataType, rangeTo: NumericLiteralValue, stepLiteral: NumericLiteralValue?, range: RangeExpr): RangeExpr {
|
fun adjustRangeDt(rangeFrom: NumericLiteralValue, targetDt: DataType, rangeTo: NumericLiteralValue, stepLiteral: NumericLiteralValue?, range: RangeExpr): RangeExpr {
|
||||||
// TODO casts can throw exception
|
val newFrom: NumericLiteralValue
|
||||||
val newFrom = rangeFrom.cast(targetDt)
|
val newTo: NumericLiteralValue
|
||||||
val newTo = rangeTo.cast(targetDt)
|
try {
|
||||||
val newStep: Expression =
|
newFrom = rangeFrom.cast(targetDt)
|
||||||
stepLiteral?.cast(targetDt) ?: range.step
|
newTo = rangeTo.cast(targetDt)
|
||||||
|
} catch (x: ExpressionError) {
|
||||||
|
return range
|
||||||
|
}
|
||||||
|
val newStep: Expression = stepLiteral?.cast(targetDt) ?: range.step
|
||||||
return RangeExpr(newFrom, newTo, newStep, range.position)
|
return RangeExpr(newFrom, newTo, newStep, range.position)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -593,17 +596,15 @@ class ConstantFolding(private val program: Program) : IAstModifyingVisitor {
|
|||||||
return resultStmt
|
return resultStmt
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(refLiteral: ReferenceLiteralValue): Expression {
|
override fun visit(arrayLiteral: ArrayLiteralValue): Expression {
|
||||||
val litval = super.visit(refLiteral)
|
val array = super.visit(arrayLiteral)
|
||||||
if(litval is ReferenceLiteralValue) {
|
if(array is ArrayLiteralValue) {
|
||||||
if (litval.isArray) {
|
val vardecl = array.parent as? VarDecl
|
||||||
val vardecl = litval.parent as? VarDecl
|
if (vardecl!=null) {
|
||||||
if (vardecl!=null) {
|
return fixupArrayDatatype(array, vardecl, program.heap)
|
||||||
return fixupArrayDatatype(litval, vardecl, program.heap)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return litval
|
return array
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(assignment: Assignment): Statement {
|
override fun visit(assignment: Assignment): Statement {
|
||||||
@ -611,7 +612,10 @@ class ConstantFolding(private val program: Program) : IAstModifyingVisitor {
|
|||||||
val lv = assignment.value as? NumericLiteralValue
|
val lv = assignment.value as? NumericLiteralValue
|
||||||
if(lv!=null) {
|
if(lv!=null) {
|
||||||
// see if we can promote/convert a literal value to the required datatype
|
// see if we can promote/convert a literal value to the required datatype
|
||||||
when(assignment.target.inferType(program, assignment)) {
|
val idt = assignment.target.inferType(program, assignment)
|
||||||
|
if(!idt.isKnown)
|
||||||
|
return assignment
|
||||||
|
when(idt.typeOrElse(DataType.STRUCT)) {
|
||||||
DataType.UWORD -> {
|
DataType.UWORD -> {
|
||||||
// we can convert to UWORD: any UBYTE, BYTE/WORD that are >=0, FLOAT that's an integer 0..65535,
|
// we can convert to UWORD: any UBYTE, BYTE/WORD that are >=0, FLOAT that's an integer 0..65535,
|
||||||
if(lv.type== DataType.UBYTE)
|
if(lv.type== DataType.UBYTE)
|
||||||
|
@ -1,16 +1,14 @@
|
|||||||
package prog8.optimizer
|
package prog8.optimizer
|
||||||
|
|
||||||
import prog8.ast.Program
|
import prog8.ast.Program
|
||||||
import prog8.ast.base.AstException
|
import prog8.ast.base.*
|
||||||
import prog8.ast.base.DataType
|
|
||||||
import prog8.ast.base.IntegerDatatypes
|
|
||||||
import prog8.ast.base.NumericDatatypes
|
|
||||||
import prog8.ast.expressions.*
|
import prog8.ast.expressions.*
|
||||||
import prog8.ast.processing.IAstModifyingVisitor
|
import prog8.ast.processing.IAstModifyingVisitor
|
||||||
import prog8.ast.statements.Assignment
|
import prog8.ast.statements.Assignment
|
||||||
import prog8.ast.statements.Statement
|
import prog8.ast.statements.Statement
|
||||||
import kotlin.math.abs
|
import kotlin.math.abs
|
||||||
import kotlin.math.log2
|
import kotlin.math.log2
|
||||||
|
import kotlin.math.pow
|
||||||
|
|
||||||
/*
|
/*
|
||||||
todo advanced expression optimization: common (sub) expression elimination (turn common expressions into single subroutine call + introduce variable to hold it)
|
todo advanced expression optimization: common (sub) expression elimination (turn common expressions into single subroutine call + introduce variable to hold it)
|
||||||
@ -136,9 +134,14 @@ internal class SimplifyExpressions(private val program: Program) : IAstModifying
|
|||||||
val constTrue = NumericLiteralValue.fromBoolean(true, expr.position)
|
val constTrue = NumericLiteralValue.fromBoolean(true, expr.position)
|
||||||
val constFalse = NumericLiteralValue.fromBoolean(false, expr.position)
|
val constFalse = NumericLiteralValue.fromBoolean(false, expr.position)
|
||||||
|
|
||||||
val leftDt = expr.left.inferType(program)
|
val leftIDt = expr.left.inferType(program)
|
||||||
val rightDt = expr.right.inferType(program)
|
val rightIDt = expr.right.inferType(program)
|
||||||
if (leftDt != null && rightDt != null && leftDt != rightDt) {
|
if(!leftIDt.isKnown || !rightIDt.isKnown)
|
||||||
|
throw FatalAstException("can't determine datatype of both expression operands $expr")
|
||||||
|
|
||||||
|
val leftDt = leftIDt.typeOrElse(DataType.STRUCT)
|
||||||
|
val rightDt = rightIDt.typeOrElse(DataType.STRUCT)
|
||||||
|
if (leftDt != rightDt) {
|
||||||
// try to convert a datatype into the other (where ddd
|
// try to convert a datatype into the other (where ddd
|
||||||
if (adjustDatatypes(expr, leftVal, leftDt, rightVal, rightDt)) {
|
if (adjustDatatypes(expr, leftVal, leftDt, rightVal, rightDt)) {
|
||||||
optimizationsDone++
|
optimizationsDone++
|
||||||
@ -226,7 +229,7 @@ internal class SimplifyExpressions(private val program: Program) : IAstModifying
|
|||||||
val x = expr.right
|
val x = expr.right
|
||||||
val y = determineY(x, leftBinExpr)
|
val y = determineY(x, leftBinExpr)
|
||||||
if(y!=null) {
|
if(y!=null) {
|
||||||
val yPlus1 = BinaryExpression(y, "+", NumericLiteralValue(leftDt!!, 1, y.position), y.position)
|
val yPlus1 = BinaryExpression(y, "+", NumericLiteralValue(leftDt, 1, y.position), y.position)
|
||||||
return BinaryExpression(x, "*", yPlus1, x.position)
|
return BinaryExpression(x, "*", yPlus1, x.position)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -235,7 +238,7 @@ internal class SimplifyExpressions(private val program: Program) : IAstModifying
|
|||||||
val x = expr.right
|
val x = expr.right
|
||||||
val y = determineY(x, leftBinExpr)
|
val y = determineY(x, leftBinExpr)
|
||||||
if(y!=null) {
|
if(y!=null) {
|
||||||
val yMinus1 = BinaryExpression(y, "-", NumericLiteralValue(leftDt!!, 1, y.position), y.position)
|
val yMinus1 = BinaryExpression(y, "-", NumericLiteralValue(leftDt, 1, y.position), y.position)
|
||||||
return BinaryExpression(x, "*", yMinus1, x.position)
|
return BinaryExpression(x, "*", yMinus1, x.position)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -338,6 +341,8 @@ internal class SimplifyExpressions(private val program: Program) : IAstModifying
|
|||||||
"-" -> return optimizeSub(expr, leftVal, rightVal)
|
"-" -> return optimizeSub(expr, leftVal, rightVal)
|
||||||
"**" -> return optimizePower(expr, leftVal, rightVal)
|
"**" -> return optimizePower(expr, leftVal, rightVal)
|
||||||
"%" -> return optimizeRemainder(expr, leftVal, rightVal)
|
"%" -> return optimizeRemainder(expr, leftVal, rightVal)
|
||||||
|
">>" -> return optimizeShiftRight(expr, rightVal)
|
||||||
|
"<<" -> return optimizeShiftLeft(expr, rightVal)
|
||||||
}
|
}
|
||||||
return expr
|
return expr
|
||||||
}
|
}
|
||||||
@ -590,7 +595,7 @@ internal class SimplifyExpressions(private val program: Program) : IAstModifying
|
|||||||
"%" -> {
|
"%" -> {
|
||||||
if (cv == 1.0) {
|
if (cv == 1.0) {
|
||||||
optimizationsDone++
|
optimizationsDone++
|
||||||
return NumericLiteralValue(expr.inferType(program)!!, 0, expr.position)
|
return NumericLiteralValue(expr.inferType(program).typeOrElse(DataType.STRUCT), 0, expr.position)
|
||||||
} else if (cv == 2.0) {
|
} else if (cv == 2.0) {
|
||||||
optimizationsDone++
|
optimizationsDone++
|
||||||
expr.operator = "&"
|
expr.operator = "&"
|
||||||
@ -603,17 +608,24 @@ internal class SimplifyExpressions(private val program: Program) : IAstModifying
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val powersOfTwo = (1 .. 16).map { (2.0).pow(it) }
|
||||||
|
private val negativePowersOfTwo = powersOfTwo.map { -it }
|
||||||
|
|
||||||
private fun optimizeDivision(expr: BinaryExpression, leftVal: NumericLiteralValue?, rightVal: NumericLiteralValue?): Expression {
|
private fun optimizeDivision(expr: BinaryExpression, leftVal: NumericLiteralValue?, rightVal: NumericLiteralValue?): Expression {
|
||||||
if(leftVal==null && rightVal==null)
|
if(leftVal==null && rightVal==null)
|
||||||
return expr
|
return expr
|
||||||
|
|
||||||
// cannot shuffle assiciativity with division!
|
// TODO fix bug in this routine!
|
||||||
|
|
||||||
|
// cannot shuffle assiciativity with division!
|
||||||
if(rightVal!=null) {
|
if(rightVal!=null) {
|
||||||
// right value is a constant, see if we can optimize
|
// right value is a constant, see if we can optimize
|
||||||
val rightConst: NumericLiteralValue = rightVal
|
val rightConst: NumericLiteralValue = rightVal
|
||||||
val cv = rightConst.number.toDouble()
|
val cv = rightConst.number.toDouble()
|
||||||
val leftDt = expr.left.inferType(program)
|
val leftIDt = expr.left.inferType(program)
|
||||||
|
if(!leftIDt.isKnown)
|
||||||
|
return expr
|
||||||
|
val leftDt = leftIDt.typeOrElse(DataType.STRUCT)
|
||||||
when(cv) {
|
when(cv) {
|
||||||
-1.0 -> {
|
-1.0 -> {
|
||||||
// '/' -> -left
|
// '/' -> -left
|
||||||
@ -629,7 +641,7 @@ internal class SimplifyExpressions(private val program: Program) : IAstModifying
|
|||||||
return expr.left
|
return expr.left
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
2.0, 4.0, 8.0, 16.0, 32.0, 64.0, 128.0, 256.0, 512.0, 1024.0, 2048.0, 4096.0, 8192.0, 16384.0, 32768.0, 65536.0 -> {
|
in powersOfTwo -> {
|
||||||
if(leftDt in IntegerDatatypes) {
|
if(leftDt in IntegerDatatypes) {
|
||||||
// divided by a power of two => shift right
|
// divided by a power of two => shift right
|
||||||
optimizationsDone++
|
optimizationsDone++
|
||||||
@ -637,7 +649,7 @@ internal class SimplifyExpressions(private val program: Program) : IAstModifying
|
|||||||
return BinaryExpression(expr.left, ">>", NumericLiteralValue.optimalInteger(numshifts, expr.position), expr.position)
|
return BinaryExpression(expr.left, ">>", NumericLiteralValue.optimalInteger(numshifts, expr.position), expr.position)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
-2.0, -4.0, -8.0, -16.0, -32.0, -64.0, -128.0, -256.0, -512.0, -1024.0, -2048.0, -4096.0, -8192.0, -16384.0, -32768.0, -65536.0 -> {
|
in negativePowersOfTwo -> {
|
||||||
if(leftDt in IntegerDatatypes) {
|
if(leftDt in IntegerDatatypes) {
|
||||||
// divided by a negative power of two => negate, then shift right
|
// divided by a negative power of two => negate, then shift right
|
||||||
optimizationsDone++
|
optimizationsDone++
|
||||||
@ -701,7 +713,7 @@ internal class SimplifyExpressions(private val program: Program) : IAstModifying
|
|||||||
return expr.left
|
return expr.left
|
||||||
}
|
}
|
||||||
2.0, 4.0, 8.0, 16.0, 32.0, 64.0, 128.0, 256.0, 512.0, 1024.0, 2048.0, 4096.0, 8192.0, 16384.0, 32768.0, 65536.0 -> {
|
2.0, 4.0, 8.0, 16.0, 32.0, 64.0, 128.0, 256.0, 512.0, 1024.0, 2048.0, 4096.0, 8192.0, 16384.0, 32768.0, 65536.0 -> {
|
||||||
if(leftValue.inferType(program) in IntegerDatatypes) {
|
if(leftValue.inferType(program).typeOrElse(DataType.STRUCT) in IntegerDatatypes) {
|
||||||
// times a power of two => shift left
|
// times a power of two => shift left
|
||||||
optimizationsDone++
|
optimizationsDone++
|
||||||
val numshifts = log2(cv).toInt()
|
val numshifts = log2(cv).toInt()
|
||||||
@ -709,7 +721,7 @@ internal class SimplifyExpressions(private val program: Program) : IAstModifying
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
-2.0, -4.0, -8.0, -16.0, -32.0, -64.0, -128.0, -256.0, -512.0, -1024.0, -2048.0, -4096.0, -8192.0, -16384.0, -32768.0, -65536.0 -> {
|
-2.0, -4.0, -8.0, -16.0, -32.0, -64.0, -128.0, -256.0, -512.0, -1024.0, -2048.0, -4096.0, -8192.0, -16384.0, -32768.0, -65536.0 -> {
|
||||||
if(leftValue.inferType(program) in IntegerDatatypes) {
|
if(leftValue.inferType(program).typeOrElse(DataType.STRUCT) in IntegerDatatypes) {
|
||||||
// times a negative power of two => negate, then shift left
|
// times a negative power of two => negate, then shift left
|
||||||
optimizationsDone++
|
optimizationsDone++
|
||||||
val numshifts = log2(-cv).toInt()
|
val numshifts = log2(-cv).toInt()
|
||||||
@ -722,4 +734,97 @@ internal class SimplifyExpressions(private val program: Program) : IAstModifying
|
|||||||
|
|
||||||
return expr
|
return expr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun optimizeShiftLeft(expr: BinaryExpression, amountLv: NumericLiteralValue?): Expression {
|
||||||
|
if(amountLv==null)
|
||||||
|
return expr
|
||||||
|
|
||||||
|
val amount=amountLv.number.toInt()
|
||||||
|
if(amount==0) {
|
||||||
|
optimizationsDone++
|
||||||
|
return expr.left
|
||||||
|
}
|
||||||
|
val targetDt = expr.left.inferType(program).typeOrElse(DataType.STRUCT)
|
||||||
|
when(targetDt) {
|
||||||
|
DataType.UBYTE, DataType.BYTE -> {
|
||||||
|
if(amount>=8) {
|
||||||
|
optimizationsDone++
|
||||||
|
return NumericLiteralValue.optimalInteger(0, expr.position)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DataType.UWORD, DataType.WORD -> {
|
||||||
|
if(amount>=16) {
|
||||||
|
optimizationsDone++
|
||||||
|
return NumericLiteralValue.optimalInteger(0, expr.position)
|
||||||
|
}
|
||||||
|
else if(amount>=8) {
|
||||||
|
optimizationsDone++
|
||||||
|
val lsb=TypecastExpression(expr.left, DataType.UBYTE, true, expr.position)
|
||||||
|
if(amount==8) {
|
||||||
|
return FunctionCall(IdentifierReference(listOf("mkword"), expr.position), mutableListOf(NumericLiteralValue.optimalInteger(0, expr.position), lsb), expr.position)
|
||||||
|
}
|
||||||
|
val shifted = BinaryExpression(lsb, "<<", NumericLiteralValue.optimalInteger(amount-8, expr.position), expr.position)
|
||||||
|
return FunctionCall(IdentifierReference(listOf("mkword"), expr.position), mutableListOf(NumericLiteralValue.optimalInteger(0, expr.position), shifted), expr.position)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> {}
|
||||||
|
}
|
||||||
|
return expr
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun optimizeShiftRight(expr: BinaryExpression, amountLv: NumericLiteralValue?): Expression {
|
||||||
|
if(amountLv==null)
|
||||||
|
return expr
|
||||||
|
|
||||||
|
val amount=amountLv.number.toInt()
|
||||||
|
if(amount==0) {
|
||||||
|
optimizationsDone++
|
||||||
|
return expr.left
|
||||||
|
}
|
||||||
|
val targetDt = expr.left.inferType(program).typeOrElse(DataType.STRUCT)
|
||||||
|
when(targetDt) {
|
||||||
|
DataType.UBYTE -> {
|
||||||
|
if(amount>=8) {
|
||||||
|
optimizationsDone++
|
||||||
|
return NumericLiteralValue.optimalInteger(0, expr.position)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DataType.BYTE -> {
|
||||||
|
if(amount>8) {
|
||||||
|
expr.right = NumericLiteralValue.optimalInteger(8, expr.right.position)
|
||||||
|
return expr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DataType.UWORD -> {
|
||||||
|
if(amount>=16) {
|
||||||
|
optimizationsDone++
|
||||||
|
return NumericLiteralValue.optimalInteger(0, expr.position)
|
||||||
|
}
|
||||||
|
else if(amount>=8) {
|
||||||
|
optimizationsDone++
|
||||||
|
val msb=FunctionCall(IdentifierReference(listOf("msb"), expr.position), mutableListOf(expr.left), expr.position)
|
||||||
|
if(amount==8)
|
||||||
|
return msb
|
||||||
|
return BinaryExpression(msb, ">>", NumericLiteralValue.optimalInteger(amount-8, expr.position), expr.position)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DataType.WORD -> {
|
||||||
|
if(amount>16) {
|
||||||
|
expr.right = NumericLiteralValue.optimalInteger(16, expr.right.position)
|
||||||
|
return expr
|
||||||
|
} else if(amount>=8) {
|
||||||
|
optimizationsDone++
|
||||||
|
val msbAsByte = TypecastExpression(
|
||||||
|
FunctionCall(IdentifierReference(listOf("msb"), expr.position), mutableListOf(expr.left), expr.position),
|
||||||
|
DataType.BYTE,
|
||||||
|
true, expr.position)
|
||||||
|
if(amount==8)
|
||||||
|
return msbAsByte
|
||||||
|
return BinaryExpression(msbAsByte, ">>", NumericLiteralValue.optimalInteger(amount-8, expr.position), expr.position)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> {}
|
||||||
|
}
|
||||||
|
return expr
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,20 +1,22 @@
|
|||||||
package prog8.optimizer
|
package prog8.optimizer
|
||||||
|
|
||||||
import prog8.ast.*
|
import prog8.ast.INameScope
|
||||||
|
import prog8.ast.Module
|
||||||
|
import prog8.ast.Node
|
||||||
|
import prog8.ast.Program
|
||||||
import prog8.ast.base.*
|
import prog8.ast.base.*
|
||||||
import prog8.ast.expressions.*
|
import prog8.ast.expressions.*
|
||||||
import prog8.ast.processing.IAstModifyingVisitor
|
import prog8.ast.processing.IAstModifyingVisitor
|
||||||
import prog8.ast.processing.IAstVisitor
|
import prog8.ast.processing.IAstVisitor
|
||||||
import prog8.ast.statements.*
|
import prog8.ast.statements.*
|
||||||
import prog8.compiler.target.c64.Petscii
|
import prog8.compiler.target.c64.Petscii
|
||||||
|
import prog8.compiler.target.c64.codegen.AssemblyError
|
||||||
import prog8.functions.BuiltinFunctions
|
import prog8.functions.BuiltinFunctions
|
||||||
import kotlin.math.floor
|
import kotlin.math.floor
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
todo: subroutines with 1 or 2 byte args or 1 word arg can be converted to asm sub calling convention (args in registers)
|
TODO: analyse for unreachable code and remove that (f.i. code after goto or return that has no label so can never be jumped to) + print warning about this
|
||||||
todo analyse for unreachable code and remove that (f.i. code after goto or return that has no label so can never be jumped to) + print warning about this
|
|
||||||
|
|
||||||
TODO: proper inlining of small subroutines (correctly renaming/relocating all variables in them and refs to those as well, or restrict to subs without variables?)
|
TODO: proper inlining of small subroutines (correctly renaming/relocating all variables in them and refs to those as well, or restrict to subs without variables?)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -104,11 +106,6 @@ internal class StatementOptimizer(private val program: Program) : IAstModifyingV
|
|||||||
linesToRemove.reversed().forEach{subroutine.statements.removeAt(it)}
|
linesToRemove.reversed().forEach{subroutine.statements.removeAt(it)}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(subroutine.canBeAsmSubroutine) {
|
|
||||||
optimizationsDone++
|
|
||||||
return subroutine.intoAsmSubroutine() // TODO this doesn't work yet due to parameter vardecl issue
|
|
||||||
}
|
|
||||||
|
|
||||||
if(subroutine !in callgraph.usedSymbols && !forceOutput) {
|
if(subroutine !in callgraph.usedSymbols && !forceOutput) {
|
||||||
printWarning("removing unused subroutine '${subroutine.name}'", subroutine.position)
|
printWarning("removing unused subroutine '${subroutine.name}'", subroutine.position)
|
||||||
optimizationsDone++
|
optimizationsDone++
|
||||||
@ -419,7 +416,10 @@ internal class StatementOptimizer(private val program: Program) : IAstModifyingV
|
|||||||
return NopStatement.insteadOf(assignment)
|
return NopStatement.insteadOf(assignment)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val targetDt = assignment.target.inferType(program, assignment)
|
val targetIDt = assignment.target.inferType(program, assignment)
|
||||||
|
if(!targetIDt.isKnown)
|
||||||
|
throw AssemblyError("can't infer type of assignment target")
|
||||||
|
val targetDt = targetIDt.typeOrElse(DataType.STRUCT)
|
||||||
val bexpr=assignment.value as? BinaryExpression
|
val bexpr=assignment.value as? BinaryExpression
|
||||||
if(bexpr!=null) {
|
if(bexpr!=null) {
|
||||||
val cv = bexpr.right.constValue(program)?.number?.toDouble()
|
val cv = bexpr.right.constValue(program)?.number?.toDouble()
|
||||||
@ -516,8 +516,7 @@ internal class StatementOptimizer(private val program: Program) : IAstModifyingV
|
|||||||
optimizationsDone++
|
optimizationsDone++
|
||||||
return NopStatement.insteadOf(assignment)
|
return NopStatement.insteadOf(assignment)
|
||||||
}
|
}
|
||||||
if (((targetDt == DataType.UWORD || targetDt == DataType.WORD) && cv > 15.0) ||
|
if ((targetDt == DataType.UWORD && cv > 15.0) || (targetDt == DataType.UBYTE && cv > 7.0)) {
|
||||||
((targetDt == DataType.UBYTE || targetDt == DataType.BYTE) && cv > 7.0)) {
|
|
||||||
assignment.value = NumericLiteralValue.optimalInteger(0, assignment.value.position)
|
assignment.value = NumericLiteralValue.optimalInteger(0, assignment.value.position)
|
||||||
assignment.value.linkParents(assignment)
|
assignment.value.linkParents(assignment)
|
||||||
optimizationsDone++
|
optimizationsDone++
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
package prog8.vm
|
package prog8.vm
|
||||||
|
|
||||||
import prog8.ast.base.*
|
import prog8.ast.base.*
|
||||||
|
import prog8.ast.expressions.ArrayLiteralValue
|
||||||
import prog8.ast.expressions.NumericLiteralValue
|
import prog8.ast.expressions.NumericLiteralValue
|
||||||
import prog8.ast.expressions.ReferenceLiteralValue
|
import prog8.ast.expressions.StringLiteralValue
|
||||||
import prog8.compiler.HeapValues
|
import prog8.compiler.HeapValues
|
||||||
import prog8.compiler.target.c64.Petscii
|
import prog8.compiler.target.c64.Petscii
|
||||||
import java.util.*
|
import java.util.*
|
||||||
@ -28,13 +29,8 @@ open class RuntimeValue(val type: DataType, num: Number?=null, val str: String?=
|
|||||||
return RuntimeValue(literalValue.type, num = literalValue.number)
|
return RuntimeValue(literalValue.type, num = literalValue.number)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun fromLv(literalValue: ReferenceLiteralValue, heap: HeapValues): RuntimeValue {
|
fun fromLv(string: StringLiteralValue, heap: HeapValues): RuntimeValue = fromHeapId(string.heapId!!, heap)
|
||||||
return when(literalValue.type) {
|
fun fromLv(array: ArrayLiteralValue, heap: HeapValues): RuntimeValue = fromHeapId(array.heapId!!, heap)
|
||||||
in StringDatatypes -> fromHeapId(literalValue.heapId!!, heap)
|
|
||||||
in ArrayDatatypes -> fromHeapId(literalValue.heapId!!, heap)
|
|
||||||
else -> throw IllegalArgumentException("weird source value $literalValue")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun fromHeapId(heapId: Int, heap: HeapValues): RuntimeValue {
|
fun fromHeapId(heapId: Int, heap: HeapValues): RuntimeValue {
|
||||||
val value = heap.get(heapId)
|
val value = heap.get(heapId)
|
||||||
|
@ -166,10 +166,10 @@ class AstVm(val program: Program) {
|
|||||||
fun memwrite(address: Int, value: Short): Short {
|
fun memwrite(address: Int, value: Short): Short {
|
||||||
if(address==0xa0 || address==0xa1 || address==0xa2) {
|
if(address==0xa0 || address==0xa1 || address==0xa2) {
|
||||||
// a write to the jiffy clock, update the clock offset for the irq
|
// a write to the jiffy clock, update the clock offset for the irq
|
||||||
val time_hi = if(address==0xa0) value else mem.getUByte_DMA(0xa0)
|
val timeHi = if(address==0xa0) value else mem.getUByte_DMA(0xa0)
|
||||||
val time_mid = if(address==0xa1) value else mem.getUByte_DMA(0xa1)
|
val timeMid = if(address==0xa1) value else mem.getUByte_DMA(0xa1)
|
||||||
val time_lo = if(address==0xa2) value else mem.getUByte_DMA(0xa2)
|
val timeLo = if(address==0xa2) value else mem.getUByte_DMA(0xa2)
|
||||||
val jiffies = (time_hi.toInt() shl 16) + (time_mid.toInt() shl 8) + time_lo
|
val jiffies = (timeHi.toInt() shl 16) + (timeMid.toInt() shl 8) + timeLo
|
||||||
rtcOffset = bootTime - (jiffies*1000/60)
|
rtcOffset = bootTime - (jiffies*1000/60)
|
||||||
}
|
}
|
||||||
if(address in 1024..2023) {
|
if(address in 1024..2023) {
|
||||||
@ -412,9 +412,11 @@ class AstVm(val program: Program) {
|
|||||||
stmt.target.arrayindexed != null -> {
|
stmt.target.arrayindexed != null -> {
|
||||||
val arrayvar = stmt.target.arrayindexed!!.identifier.targetVarDecl(program.namespace)!!
|
val arrayvar = stmt.target.arrayindexed!!.identifier.targetVarDecl(program.namespace)!!
|
||||||
val arrayvalue = runtimeVariables.get(arrayvar.definingScope(), arrayvar.name)
|
val arrayvalue = runtimeVariables.get(arrayvar.definingScope(), arrayvar.name)
|
||||||
val elementType = stmt.target.arrayindexed!!.inferType(program)!!
|
|
||||||
val index = evaluate(stmt.target.arrayindexed!!.arrayspec.index, evalCtx).integerValue()
|
val index = evaluate(stmt.target.arrayindexed!!.arrayspec.index, evalCtx).integerValue()
|
||||||
var value = RuntimeValue(elementType, arrayvalue.array!![index].toInt())
|
val elementType = stmt.target.arrayindexed!!.inferType(program)
|
||||||
|
if(!elementType.isKnown)
|
||||||
|
throw VmExecutionException("unknown/void elt type")
|
||||||
|
var value = RuntimeValue(elementType.typeOrElse(DataType.BYTE), arrayvalue.array!![index].toInt())
|
||||||
value = when {
|
value = when {
|
||||||
stmt.operator == "++" -> value.inc()
|
stmt.operator == "++" -> value.inc()
|
||||||
stmt.operator == "--" -> value.dec()
|
stmt.operator == "--" -> value.dec()
|
||||||
@ -472,7 +474,8 @@ class AstVm(val program: Program) {
|
|||||||
loopvarDt = DataType.UBYTE
|
loopvarDt = DataType.UBYTE
|
||||||
loopvar = IdentifierReference(listOf(stmt.loopRegister.name), stmt.position)
|
loopvar = IdentifierReference(listOf(stmt.loopRegister.name), stmt.position)
|
||||||
} else {
|
} else {
|
||||||
loopvarDt = stmt.loopVar!!.inferType(program)!!
|
val dt = stmt.loopVar!!.inferType(program)
|
||||||
|
loopvarDt = dt.typeOrElse(DataType.UBYTE)
|
||||||
loopvar = stmt.loopVar!!
|
loopvar = stmt.loopVar!!
|
||||||
}
|
}
|
||||||
val iterator = iterable.iterator()
|
val iterator = iterable.iterator()
|
||||||
@ -619,8 +622,10 @@ class AstVm(val program: Program) {
|
|||||||
else {
|
else {
|
||||||
val address = (vardecl.value as NumericLiteralValue).number.toInt()
|
val address = (vardecl.value as NumericLiteralValue).number.toInt()
|
||||||
val index = evaluate(targetArrayIndexed.arrayspec.index, evalCtx).integerValue()
|
val index = evaluate(targetArrayIndexed.arrayspec.index, evalCtx).integerValue()
|
||||||
val elementType = targetArrayIndexed.inferType(program)!!
|
val elementType = targetArrayIndexed.inferType(program)
|
||||||
when(elementType) {
|
if(!elementType.isKnown)
|
||||||
|
throw VmExecutionException("unknown/void array elt type $targetArrayIndexed")
|
||||||
|
when(elementType.typeOrElse(DataType.UBYTE)) {
|
||||||
DataType.UBYTE -> mem.setUByte(address+index, value.byteval!!)
|
DataType.UBYTE -> mem.setUByte(address+index, value.byteval!!)
|
||||||
DataType.BYTE -> mem.setSByte(address+index, value.byteval!!)
|
DataType.BYTE -> mem.setSByte(address+index, value.byteval!!)
|
||||||
DataType.UWORD -> mem.setUWord(address+index*2, value.wordval!!)
|
DataType.UWORD -> mem.setUWord(address+index*2, value.wordval!!)
|
||||||
|
@ -12,7 +12,6 @@ import prog8.ast.statements.Subroutine
|
|||||||
import prog8.ast.statements.VarDecl
|
import prog8.ast.statements.VarDecl
|
||||||
import prog8.vm.RuntimeValue
|
import prog8.vm.RuntimeValue
|
||||||
import prog8.vm.RuntimeValueRange
|
import prog8.vm.RuntimeValueRange
|
||||||
import kotlin.math.abs
|
|
||||||
|
|
||||||
|
|
||||||
typealias BuiltinfunctionCaller = (name: String, args: List<RuntimeValue>, flags: StatusFlags) -> RuntimeValue?
|
typealias BuiltinfunctionCaller = (name: String, args: List<RuntimeValue>, flags: StatusFlags) -> RuntimeValue?
|
||||||
@ -30,12 +29,9 @@ fun evaluate(expr: Expression, ctx: EvalContext): RuntimeValue {
|
|||||||
return RuntimeValue.fromLv(constval)
|
return RuntimeValue.fromLv(constval)
|
||||||
|
|
||||||
when(expr) {
|
when(expr) {
|
||||||
is NumericLiteralValue -> {
|
is NumericLiteralValue -> return RuntimeValue.fromLv(expr)
|
||||||
return RuntimeValue.fromLv(expr)
|
is StringLiteralValue -> return RuntimeValue.fromLv(expr, ctx.program.heap)
|
||||||
}
|
is ArrayLiteralValue -> return RuntimeValue.fromLv(expr, ctx.program.heap)
|
||||||
is ReferenceLiteralValue -> {
|
|
||||||
return RuntimeValue.fromLv(expr, ctx.program.heap)
|
|
||||||
}
|
|
||||||
is PrefixExpression -> {
|
is PrefixExpression -> {
|
||||||
return when(expr.operator) {
|
return when(expr.operator) {
|
||||||
"-" -> evaluate(expr.expression, ctx).neg()
|
"-" -> evaluate(expr.expression, ctx).neg()
|
||||||
@ -150,24 +146,22 @@ fun evaluate(expr: Expression, ctx: EvalContext): RuntimeValue {
|
|||||||
}
|
}
|
||||||
is RangeExpr -> {
|
is RangeExpr -> {
|
||||||
val cRange = expr.toConstantIntegerRange()
|
val cRange = expr.toConstantIntegerRange()
|
||||||
if(cRange!=null)
|
if(cRange!=null) {
|
||||||
return RuntimeValueRange(expr.inferType(ctx.program)!!, cRange)
|
val dt = expr.inferType(ctx.program)
|
||||||
|
if(dt.isKnown)
|
||||||
|
return RuntimeValueRange(dt.typeOrElse(DataType.UBYTE), cRange)
|
||||||
|
else
|
||||||
|
throw VmExecutionException("couldn't determine datatype")
|
||||||
|
}
|
||||||
val fromVal = evaluate(expr.from, ctx).integerValue()
|
val fromVal = evaluate(expr.from, ctx).integerValue()
|
||||||
val toVal = evaluate(expr.to, ctx).integerValue()
|
val toVal = evaluate(expr.to, ctx).integerValue()
|
||||||
val stepVal = evaluate(expr.step, ctx).integerValue()
|
val stepVal = evaluate(expr.step, ctx).integerValue()
|
||||||
val range = when {
|
val range = makeRange(fromVal, toVal, stepVal)
|
||||||
fromVal <= toVal -> when {
|
val dt = expr.inferType(ctx.program)
|
||||||
stepVal <= 0 -> IntRange.EMPTY
|
if(dt.isKnown)
|
||||||
stepVal == 1 -> fromVal..toVal
|
return RuntimeValueRange(dt.typeOrElse(DataType.UBYTE), range)
|
||||||
else -> fromVal..toVal step stepVal
|
else
|
||||||
}
|
throw VmExecutionException("couldn't determine datatype")
|
||||||
else -> when {
|
|
||||||
stepVal >= 0 -> IntRange.EMPTY
|
|
||||||
stepVal == -1 -> fromVal downTo toVal
|
|
||||||
else -> fromVal downTo toVal step abs(stepVal)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return RuntimeValueRange(expr.inferType(ctx.program)!!, range)
|
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
throw VmExecutionException("unimplemented expression node $expr")
|
throw VmExecutionException("unimplemented expression node $expr")
|
||||||
|
@ -5,8 +5,9 @@ import prog8.ast.base.DataType
|
|||||||
import prog8.ast.base.Position
|
import prog8.ast.base.Position
|
||||||
import prog8.ast.base.Register
|
import prog8.ast.base.Register
|
||||||
import prog8.ast.base.VarDeclType
|
import prog8.ast.base.VarDeclType
|
||||||
|
import prog8.ast.expressions.ArrayLiteralValue
|
||||||
import prog8.ast.expressions.NumericLiteralValue
|
import prog8.ast.expressions.NumericLiteralValue
|
||||||
import prog8.ast.expressions.ReferenceLiteralValue
|
import prog8.ast.expressions.StringLiteralValue
|
||||||
import prog8.ast.processing.IAstModifyingVisitor
|
import prog8.ast.processing.IAstModifyingVisitor
|
||||||
import prog8.ast.statements.Statement
|
import prog8.ast.statements.Statement
|
||||||
import prog8.ast.statements.StructDecl
|
import prog8.ast.statements.StructDecl
|
||||||
@ -50,8 +51,10 @@ class VariablesCreator(private val runtimeVariables: RuntimeVariables, private v
|
|||||||
val value = if(numericLv!=null) {
|
val value = if(numericLv!=null) {
|
||||||
RuntimeValue.fromLv(numericLv)
|
RuntimeValue.fromLv(numericLv)
|
||||||
} else {
|
} else {
|
||||||
val referenceLv = decl.value as ReferenceLiteralValue
|
if(decl.value is StringLiteralValue)
|
||||||
RuntimeValue.fromLv(referenceLv, heap)
|
RuntimeValue.fromLv(decl.value as StringLiteralValue, heap)
|
||||||
|
else
|
||||||
|
RuntimeValue.fromLv(decl.value as ArrayLiteralValue, heap)
|
||||||
}
|
}
|
||||||
runtimeVariables.define(decl.definingScope(), decl.name, value)
|
runtimeVariables.define(decl.definingScope(), decl.name, value)
|
||||||
}
|
}
|
||||||
|
@ -4,8 +4,9 @@ import org.junit.jupiter.api.Test
|
|||||||
import org.junit.jupiter.api.TestInstance
|
import org.junit.jupiter.api.TestInstance
|
||||||
import prog8.ast.base.DataType
|
import prog8.ast.base.DataType
|
||||||
import prog8.ast.base.Position
|
import prog8.ast.base.Position
|
||||||
|
import prog8.ast.expressions.ArrayLiteralValue
|
||||||
import prog8.ast.expressions.NumericLiteralValue
|
import prog8.ast.expressions.NumericLiteralValue
|
||||||
import prog8.ast.expressions.ReferenceLiteralValue
|
import prog8.ast.expressions.StringLiteralValue
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
import kotlin.test.assertFalse
|
import kotlin.test.assertFalse
|
||||||
import kotlin.test.assertNotEquals
|
import kotlin.test.assertNotEquals
|
||||||
@ -16,10 +17,6 @@ private fun sameValueAndType(lv1: NumericLiteralValue, lv2: NumericLiteralValue)
|
|||||||
return lv1.type==lv2.type && lv1==lv2
|
return lv1.type==lv2.type && lv1==lv2
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun sameValueAndType(rv1: ReferenceLiteralValue, rv2: ReferenceLiteralValue): Boolean {
|
|
||||||
return rv1.type==rv2.type && rv1==rv2
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||||
class TestParserNumericLiteralValue {
|
class TestParserNumericLiteralValue {
|
||||||
@ -86,8 +83,8 @@ class TestParserNumericLiteralValue {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testEqualsRef() {
|
fun testEqualsRef() {
|
||||||
assertTrue(sameValueAndType(ReferenceLiteralValue(DataType.STR, str = "hello", position = dummyPos), ReferenceLiteralValue(DataType.STR, str = "hello", position = dummyPos)))
|
assertTrue(StringLiteralValue(DataType.STR, "hello", position = dummyPos) == StringLiteralValue(DataType.STR, "hello", position = dummyPos))
|
||||||
assertFalse(sameValueAndType(ReferenceLiteralValue(DataType.STR, str = "hello", position = dummyPos), ReferenceLiteralValue(DataType.STR, str = "bye", position = dummyPos)))
|
assertFalse(StringLiteralValue(DataType.STR, "hello", position = dummyPos) == StringLiteralValue(DataType.STR, "bye", position = dummyPos))
|
||||||
|
|
||||||
val lvOne = NumericLiteralValue(DataType.UBYTE, 1, dummyPos)
|
val lvOne = NumericLiteralValue(DataType.UBYTE, 1, dummyPos)
|
||||||
val lvTwo = NumericLiteralValue(DataType.UBYTE, 2, dummyPos)
|
val lvTwo = NumericLiteralValue(DataType.UBYTE, 2, dummyPos)
|
||||||
@ -96,9 +93,9 @@ class TestParserNumericLiteralValue {
|
|||||||
val lvTwoR = NumericLiteralValue(DataType.UBYTE, 2, dummyPos)
|
val lvTwoR = NumericLiteralValue(DataType.UBYTE, 2, dummyPos)
|
||||||
val lvThreeR = NumericLiteralValue(DataType.UBYTE, 3, dummyPos)
|
val lvThreeR = NumericLiteralValue(DataType.UBYTE, 3, dummyPos)
|
||||||
val lvFour= NumericLiteralValue(DataType.UBYTE, 4, dummyPos)
|
val lvFour= NumericLiteralValue(DataType.UBYTE, 4, dummyPos)
|
||||||
val lv1 = ReferenceLiteralValue(DataType.ARRAY_UB, array = arrayOf(lvOne, lvTwo, lvThree), position = dummyPos)
|
val lv1 = ArrayLiteralValue(DataType.ARRAY_UB, arrayOf(lvOne, lvTwo, lvThree), position = dummyPos)
|
||||||
val lv2 = ReferenceLiteralValue(DataType.ARRAY_UB, array = arrayOf(lvOneR, lvTwoR, lvThreeR), position = dummyPos)
|
val lv2 = ArrayLiteralValue(DataType.ARRAY_UB, arrayOf(lvOneR, lvTwoR, lvThreeR), position = dummyPos)
|
||||||
val lv3 = ReferenceLiteralValue(DataType.ARRAY_UB, array = arrayOf(lvOneR, lvTwoR, lvFour), position = dummyPos)
|
val lv3 = ArrayLiteralValue(DataType.ARRAY_UB, arrayOf(lvOneR, lvTwoR, lvFour), position = dummyPos)
|
||||||
assertEquals(lv1, lv2)
|
assertEquals(lv1, lv2)
|
||||||
assertNotEquals(lv1, lv3)
|
assertNotEquals(lv1, lv3)
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@ import org.junit.jupiter.api.TestInstance
|
|||||||
import prog8.ast.base.DataType
|
import prog8.ast.base.DataType
|
||||||
import prog8.ast.base.Position
|
import prog8.ast.base.Position
|
||||||
import prog8.ast.expressions.NumericLiteralValue
|
import prog8.ast.expressions.NumericLiteralValue
|
||||||
import prog8.ast.expressions.ReferenceLiteralValue
|
import prog8.ast.expressions.StringLiteralValue
|
||||||
import prog8.compiler.*
|
import prog8.compiler.*
|
||||||
import prog8.compiler.target.c64.MachineDefinition.C64Zeropage
|
import prog8.compiler.target.c64.MachineDefinition.C64Zeropage
|
||||||
import prog8.compiler.target.c64.MachineDefinition.FLOAT_MAX_NEGATIVE
|
import prog8.compiler.target.c64.MachineDefinition.FLOAT_MAX_NEGATIVE
|
||||||
@ -169,7 +169,15 @@ class TestZeropage {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO test dontuse option
|
@Test
|
||||||
|
fun testZpDontuse() {
|
||||||
|
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.DONTUSE, emptyList(), false))
|
||||||
|
println(zp.free)
|
||||||
|
assertEquals(0, zp.available())
|
||||||
|
assertFailsWith<CompilerException> {
|
||||||
|
zp.allocate("", DataType.BYTE, null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testFreeSpaces() {
|
fun testFreeSpaces() {
|
||||||
@ -363,8 +371,8 @@ class TestPetscii {
|
|||||||
assertTrue(ten <= ten)
|
assertTrue(ten <= ten)
|
||||||
assertFalse(ten < ten)
|
assertFalse(ten < ten)
|
||||||
|
|
||||||
val abc = ReferenceLiteralValue(DataType.STR, str = "abc", position = Position("", 0, 0, 0))
|
val abc = StringLiteralValue(DataType.STR, "abc", position = Position("", 0, 0, 0))
|
||||||
val abd = ReferenceLiteralValue(DataType.STR, str = "abd", position = Position("", 0, 0, 0))
|
val abd = StringLiteralValue(DataType.STR, "abd", position = Position("", 0, 0, 0))
|
||||||
assertEquals(abc, abc)
|
assertEquals(abc, abc)
|
||||||
assertTrue(abc!=abd)
|
assertTrue(abc!=abd)
|
||||||
assertFalse(abc!=abc)
|
assertFalse(abc!=abc)
|
||||||
|
@ -158,6 +158,7 @@ Design principles and features
|
|||||||
- The compiler tries to optimize the program and generated code, but hand-tuning of the
|
- The compiler tries to optimize the program and generated code, but hand-tuning of the
|
||||||
performance or space-critical parts will likely still be required. This is supported by
|
performance or space-critical parts will likely still be required. This is supported by
|
||||||
the ability to easily write embedded assembly code directly in the program source code.
|
the ability to easily write embedded assembly code directly in the program source code.
|
||||||
|
- There are many built-in functions such as ``sin``, ``cos``, ``rnd``, ``abs``, ``min``, ``max``, ``sqrt``, ``msb``, ``rol``, ``ror``, ``swap``, ``memset``, ``memcopy``
|
||||||
|
|
||||||
|
|
||||||
.. _requirements:
|
.. _requirements:
|
||||||
|
@ -727,6 +727,9 @@ lsb(x)
|
|||||||
msb(x)
|
msb(x)
|
||||||
Get the most significant byte of the word x.
|
Get the most significant byte of the word x.
|
||||||
|
|
||||||
|
sgn(x)
|
||||||
|
Get the sign of the value. Result is -1, 0 or 1 (negative, zero, positive).
|
||||||
|
|
||||||
mkword(lsb, msb)
|
mkword(lsb, msb)
|
||||||
Efficiently create a word value from two bytes (the lsb and the msb). Avoids multiplication and shifting.
|
Efficiently create a word value from two bytes (the lsb and the msb). Avoids multiplication and shifting.
|
||||||
|
|
||||||
|
@ -19,17 +19,6 @@ these should call optimized pieces of assembly code, so they run as fast as poss
|
|||||||
|
|
||||||
For now, we have the ``memcopy``, ``memset`` and ``strlen`` builtin functions.
|
For now, we have the ``memcopy``, ``memset`` and ``strlen`` builtin functions.
|
||||||
|
|
||||||
Fixes
|
|
||||||
^^^^^
|
|
||||||
|
|
||||||
fix asmsub parameters so this works::
|
|
||||||
|
|
||||||
asmsub aa(byte arg @ Y) -> clobbers() -> () {
|
|
||||||
byte local = arg ; @todo fix 'undefined symbol arg' that occurs here
|
|
||||||
A=44
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
More optimizations
|
More optimizations
|
||||||
^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
@ -38,11 +27,6 @@ Add more compiler optimizations to the existing ones.
|
|||||||
- on the language AST level
|
- on the language AST level
|
||||||
- on the final assembly source level
|
- on the final assembly source level
|
||||||
- can the parameter passing to subroutines be optimized to avoid copying?
|
- can the parameter passing to subroutines be optimized to avoid copying?
|
||||||
|
|
||||||
- subroutines with 1 or 2 byte args (or 1 word arg) should be converted to asm calling convention with the args in A/Y register
|
|
||||||
this requires rethinking the way parameters are represented, simply injecting vardecls to
|
|
||||||
declare local variables for them is not always correct anymore
|
|
||||||
|
|
||||||
- working subroutine inlining (taking care of vars and identifier refs to them)
|
- working subroutine inlining (taking care of vars and identifier refs to them)
|
||||||
|
|
||||||
Also some library routines and code patterns could perhaps be optimized further
|
Also some library routines and code patterns could perhaps be optimized further
|
||||||
@ -57,11 +41,16 @@ It could then even be moved into the zeropage to greatly reduce code size and sl
|
|||||||
|
|
||||||
Or just move the LSB portion into a slab of the zeropage.
|
Or just move the LSB portion into a slab of the zeropage.
|
||||||
|
|
||||||
Allocate a fixed word in ZP that is the TOS so we can operate on TOS directly
|
Allocate a fixed word in ZP that is the TOS so we can always operate on TOS directly
|
||||||
without having to to index into the stack?
|
without having to to index into the stack?
|
||||||
|
|
||||||
|
|
||||||
Misc
|
Misc
|
||||||
^^^^
|
^^^^
|
||||||
|
|
||||||
- are there any other missing instructions in the code generator?
|
Add sort() function that can sort an array (ascending and descending)
|
||||||
|
|
||||||
|
|
||||||
|
Several ideas were discussed on my reddit post
|
||||||
|
https://www.reddit.com/r/programming/comments/alhj59/creating_a_programming_language_and_cross/
|
||||||
|
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
%import c64utils
|
%import c64utils
|
||||||
%zeropage basicsafe
|
%zeropage basicsafe
|
||||||
|
|
||||||
|
; TODO implement asm generation for all operation in here
|
||||||
|
|
||||||
main {
|
main {
|
||||||
|
|
||||||
byte bb
|
byte bb
|
||||||
|
74
examples/bdmusic-irq.p8
Normal file
74
examples/bdmusic-irq.p8
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
%zeropage basicsafe
|
||||||
|
%import c64lib
|
||||||
|
|
||||||
|
main {
|
||||||
|
|
||||||
|
sub start() {
|
||||||
|
c64scr.print("playing the music from boulderdash,\nmade in 1984 by peter liepa.\n\n")
|
||||||
|
c64utils.set_rasterirq(60) ; enable raster irq
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
irq {
|
||||||
|
const ubyte waveform = %0001 ; triangle
|
||||||
|
ubyte note_index = 0
|
||||||
|
ubyte delay = 0
|
||||||
|
|
||||||
|
sub irq() {
|
||||||
|
c64.EXTCOL++
|
||||||
|
delay++
|
||||||
|
if delay >= 8 {
|
||||||
|
delay = 0
|
||||||
|
c64.AD1 = %00011010
|
||||||
|
c64.SR1 = %00000000
|
||||||
|
c64.AD2 = %00011010
|
||||||
|
c64.SR2 = %00000000
|
||||||
|
c64.MVOL = 15
|
||||||
|
|
||||||
|
uword note = notes[note_index]
|
||||||
|
note_index++
|
||||||
|
ubyte note1 = lsb(note)
|
||||||
|
ubyte note2 = msb(note)
|
||||||
|
c64.FREQ1 = music_freq_table[note1] ; set lo+hi freq of voice 1
|
||||||
|
c64.FREQ2 = music_freq_table[note2] ; set lo+hi freq of voice 2
|
||||||
|
|
||||||
|
; retrigger voice 1 and 2 ADSR
|
||||||
|
c64.CR1 = waveform <<4 | 0
|
||||||
|
c64.CR2 = waveform <<4 | 0
|
||||||
|
c64.CR1 = waveform <<4 | 1
|
||||||
|
c64.CR2 = waveform <<4 | 1
|
||||||
|
}
|
||||||
|
|
||||||
|
c64.EXTCOL--
|
||||||
|
}
|
||||||
|
|
||||||
|
; details about the boulderdash music can be found here:
|
||||||
|
; https://www.elmerproductions.com/sp/peterb/sounds.html#Theme%20tune
|
||||||
|
|
||||||
|
uword[] notes = [
|
||||||
|
$1622, $1d26, $2229, $252e, $1424, $1f27, $2029, $2730,
|
||||||
|
$122a, $122c, $1e2e, $1231, $202c, $3337, $212d, $3135,
|
||||||
|
$1622, $162e, $161d, $1624, $1420, $1430, $1424, $1420,
|
||||||
|
$1622, $162e, $161d, $1624, $1e2a, $1e3a, $1e2e, $1e2a,
|
||||||
|
$142c, $142c, $141b, $1422, $1c28, $1c38, $1c2c, $1c28,
|
||||||
|
$111d, $292d, $111f, $292e, $0f27, $0f27, $1633, $1627,
|
||||||
|
$162e, $162e, $162e, $162e, $222e, $222e, $162e, $162e,
|
||||||
|
$142e, $142e, $142e, $142e, $202e, $202e, $142e, $142e,
|
||||||
|
$162e, $322e, $162e, $332e, $222e, $322e, $162e, $332e,
|
||||||
|
$142e, $322e, $142e, $332e, $202c, $302c, $142c, $312c,
|
||||||
|
$162e, $163a, $162e, $3538, $222e, $2237, $162e, $3135,
|
||||||
|
$142c, $1438, $142c, $1438, $202c, $2033, $142c, $1438,
|
||||||
|
$162e, $322e, $162e, $332e, $222e, $322e, $162e, $332e,
|
||||||
|
$142e, $322e, $142e, $332e, $202c, $302c, $142c, $312c,
|
||||||
|
$2e32, $292e, $2629, $2226, $2c30, $272c, $2427, $1420,
|
||||||
|
$3532, $322e, $2e29, $2926, $2730, $242c, $2027, $1420
|
||||||
|
]
|
||||||
|
|
||||||
|
uword[] music_freq_table = [
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
732, 778, 826, 876, 928, 978, 1042, 1100, 1170, 1238, 1312, 1390, 1464, 1556,
|
||||||
|
1652, 1752, 1856, 1956, 2084, 2200, 2340, 2476, 2624, 2780, 2928, 3112, 3304,
|
||||||
|
3504, 3712, 3912, 4168, 4400, 4680, 4952, 5248, 5560, 5856, 6224, 6608, 7008,
|
||||||
|
7424, 7824, 8336, 8800, 9360, 9904, 10496, 11120, 11712
|
||||||
|
]
|
||||||
|
}
|
@ -1,6 +1,8 @@
|
|||||||
%import c64lib
|
%import c64lib
|
||||||
%import c64utils
|
%import c64utils
|
||||||
|
|
||||||
|
; TODO: some optimizer breaks this.. runs fine without optimization
|
||||||
|
|
||||||
spritedata $2000 {
|
spritedata $2000 {
|
||||||
; this memory block contains the sprite data
|
; this memory block contains the sprite data
|
||||||
; it must start on an address aligned to 64 bytes.
|
; it must start on an address aligned to 64 bytes.
|
||||||
@ -100,12 +102,12 @@ main {
|
|||||||
; rotate around origin (0,0,0)
|
; rotate around origin (0,0,0)
|
||||||
|
|
||||||
; set up the 3d rotation matrix values
|
; set up the 3d rotation matrix values
|
||||||
word wcosa = cos8(ax) as word
|
word wcosa = cos8(ax)
|
||||||
word wsina = sin8(ax) as word
|
word wsina = sin8(ax)
|
||||||
word wcosb = cos8(ay) as word
|
word wcosb = cos8(ay)
|
||||||
word wsinb = sin8(ay) as word
|
word wsinb = sin8(ay)
|
||||||
word wcosc = cos8(az) as word
|
word wcosc = cos8(az)
|
||||||
word wsinc = sin8(az) as word
|
word wsinc = sin8(az)
|
||||||
|
|
||||||
word wcosa_sinb = wcosa*wsinb / 128
|
word wcosa_sinb = wcosa*wsinb / 128
|
||||||
word wsina_sinb = wsina*wsinb / 128
|
word wsina_sinb = wsina*wsinb / 128
|
||||||
@ -134,7 +136,8 @@ main {
|
|||||||
; set each of the 8 sprites to the correct vertex of the cube
|
; set each of the 8 sprites to the correct vertex of the cube
|
||||||
|
|
||||||
; first sort vertices to sprite order so the back/front order is correct as well
|
; first sort vertices to sprite order so the back/front order is correct as well
|
||||||
; (chose to do a simple bubble sort it's only 8 items to sort)
|
; (simple bubble sort as it's only 8 items to sort)
|
||||||
|
; TODO make a builtin function sort()
|
||||||
for ubyte sorti in 6 to 0 step -1 {
|
for ubyte sorti in 6 to 0 step -1 {
|
||||||
for ubyte i1 in 0 to sorti {
|
for ubyte i1 in 0 to sorti {
|
||||||
ubyte i2 = i1+1
|
ubyte i2 = i1+1
|
||||||
@ -161,7 +164,7 @@ main {
|
|||||||
else
|
else
|
||||||
c64.SPRPTR[i] = $2000/64 ; small ball
|
c64.SPRPTR[i] = $2000/64 ; small ball
|
||||||
|
|
||||||
c64.SPCOL[i] = spritecolors[zc>>13 as byte + 4] ; further away=darker color
|
c64.SPCOL[i] = spritecolors[(zc>>13) as byte + 4] ; further away=darker color
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -41,12 +41,12 @@ main {
|
|||||||
; rotate around origin (0,0,0)
|
; rotate around origin (0,0,0)
|
||||||
|
|
||||||
; set up the 3d rotation matrix values
|
; set up the 3d rotation matrix values
|
||||||
word wcosa = cos8(ax) as word
|
word wcosa = cos8(ax)
|
||||||
word wsina = sin8(ax) as word
|
word wsina = sin8(ax)
|
||||||
word wcosb = cos8(ay) as word
|
word wcosb = cos8(ay)
|
||||||
word wsinb = sin8(ay) as word
|
word wsinb = sin8(ay)
|
||||||
word wcosc = cos8(az) as word
|
word wcosc = cos8(az)
|
||||||
word wsinc = sin8(az) as word
|
word wsinc = sin8(az)
|
||||||
|
|
||||||
word wcosa_sinb = wcosa*wsinb / 128
|
word wcosa_sinb = wcosa*wsinb / 128
|
||||||
word wsina_sinb = wsina*wsinb / 128
|
word wsina_sinb = wsina*wsinb / 128
|
||||||
@ -62,14 +62,13 @@ main {
|
|||||||
word Azz = wcosb*wcosc / 128
|
word Azz = wcosb*wcosc / 128
|
||||||
|
|
||||||
for ubyte i in 0 to len(xcoor)-1 {
|
for ubyte i in 0 to len(xcoor)-1 {
|
||||||
rotatedx[i] = (Axx*xcoor[i] + Axy*ycoor[i] + Axz*zcoor[i]) / 128
|
; don't normalize by dividing by 128, instead keep some precision for perspective calc later
|
||||||
rotatedy[i] = (Ayx*xcoor[i] + Ayy*ycoor[i] + Ayz*zcoor[i]) / 128
|
rotatedx[i] = (Axx*xcoor[i] + Axy*ycoor[i] + Axz*zcoor[i])
|
||||||
rotatedz[i] = (Azx*xcoor[i] + Azy*ycoor[i] + Azz*zcoor[i]) / 128
|
rotatedy[i] = (Ayx*xcoor[i] + Ayy*ycoor[i] + Ayz*zcoor[i])
|
||||||
|
rotatedz[i] = (Azx*xcoor[i] + Azy*ycoor[i] + Azz*zcoor[i])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ubyte[] vertexcolors = [1,7,7,12,11,6]
|
|
||||||
|
|
||||||
sub draw_edges() {
|
sub draw_edges() {
|
||||||
|
|
||||||
; plot the points of the 3d cube
|
; plot the points of the 3d cube
|
||||||
@ -80,24 +79,25 @@ main {
|
|||||||
word persp
|
word persp
|
||||||
byte sx
|
byte sx
|
||||||
byte sy
|
byte sy
|
||||||
|
ubyte color
|
||||||
|
|
||||||
for i in 0 to len(xcoor)-1 {
|
for i in 0 to len(xcoor)-1 {
|
||||||
rz = rotatedz[i]
|
rz = rotatedz[i]
|
||||||
if rz >= 10 {
|
if rz >= 10 {
|
||||||
persp = (rz+200) / height
|
persp = 900 + rz/32
|
||||||
sx = rotatedx[i] / persp as byte + width/2
|
sx = rotatedx[i] / persp as byte + width/2
|
||||||
sy = rotatedy[i] / persp as byte + height/2
|
sy = rotatedy[i] / persp as byte + height/2
|
||||||
c64scr.setcc(sx as ubyte, sy as ubyte, 46, vertexcolors[(rz as byte >>5) + 3])
|
c64scr.setcc(sx as ubyte, sy as ubyte, 46, 7)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for i in 0 to len(xcoor)-1 {
|
for i in 0 to len(xcoor)-1 {
|
||||||
rz = rotatedz[i]
|
rz = rotatedz[i]
|
||||||
if rz < 10 {
|
if rz < 10 {
|
||||||
persp = (rz+200) / height
|
persp = 900 + rz/32
|
||||||
sx = rotatedx[i] / persp as byte + width/2
|
sx = rotatedx[i] / persp as byte + width/2
|
||||||
sy = rotatedy[i] / persp as byte + height/2
|
sy = rotatedy[i] / persp as byte + height/2
|
||||||
c64scr.setcc(sx as ubyte, sy as ubyte, 81, vertexcolors[(rz as byte >>5) + 3])
|
c64scr.setcc(sx as ubyte, sy as ubyte, 81, 7)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
102
examples/test.p8
102
examples/test.p8
@ -6,42 +6,78 @@
|
|||||||
main {
|
main {
|
||||||
|
|
||||||
sub start() {
|
sub start() {
|
||||||
byte[] barr = [-100, 0, 99, -122, 22]
|
byte ub = 100
|
||||||
ubyte[] ubarr = [100, 0, 99, 199, 22]
|
byte ub2
|
||||||
word[] warr = [-1000, 0, 999, -4444, 222]
|
word uw = 22222
|
||||||
uword[] uwarr = [1000, 0, 222, 4444, 999]
|
word uw2
|
||||||
float[] farr = [-1000.1, 0, 999.9, -4444.4, 222.2]
|
|
||||||
str name = "irmen"
|
|
||||||
ubyte ub
|
|
||||||
byte bb
|
|
||||||
word ww
|
|
||||||
uword uw
|
|
||||||
float ff
|
|
||||||
|
|
||||||
; LEN/STRLEN
|
ub = -100
|
||||||
ubyte length = len(name)
|
c64scr.print_b(ub >> 1)
|
||||||
if(length!=5) c64scr.print("error len1\n")
|
c64.CHROUT('\n')
|
||||||
length = len(uwarr)
|
c64scr.print_b(ub >> 2)
|
||||||
if(length!=5) c64scr.print("error len2\n")
|
c64.CHROUT('\n')
|
||||||
length=strlen(name)
|
c64scr.print_b(ub >> 7)
|
||||||
if(length!=5) c64scr.print("error strlen1\n")
|
c64.CHROUT('\n')
|
||||||
name[3] = 0
|
c64scr.print_b(ub >> 8)
|
||||||
length=strlen(name)
|
c64.CHROUT('\n')
|
||||||
if(length!=3) c64scr.print("error strlen2\n")
|
c64scr.print_b(ub >> 9)
|
||||||
|
c64.CHROUT('\n')
|
||||||
|
c64scr.print_b(ub >> 16)
|
||||||
|
c64.CHROUT('\n')
|
||||||
|
c64scr.print_b(ub >> 26)
|
||||||
|
c64.CHROUT('\n')
|
||||||
|
c64.CHROUT('\n')
|
||||||
|
|
||||||
; MAX
|
ub = 100
|
||||||
; ub = max(ubarr)
|
c64scr.print_b(ub >> 1)
|
||||||
; bb = max(barr)
|
c64.CHROUT('\n')
|
||||||
; ww = max(warr)
|
c64scr.print_b(ub >> 2)
|
||||||
; uw = max(uwarr)
|
c64.CHROUT('\n')
|
||||||
; ff = max(farr)
|
c64scr.print_b(ub >> 7)
|
||||||
|
c64.CHROUT('\n')
|
||||||
|
c64scr.print_b(ub >> 8)
|
||||||
|
c64.CHROUT('\n')
|
||||||
|
c64scr.print_b(ub >> 9)
|
||||||
|
c64.CHROUT('\n')
|
||||||
|
c64scr.print_b(ub >> 16)
|
||||||
|
c64.CHROUT('\n')
|
||||||
|
c64scr.print_b(ub >> 26)
|
||||||
|
c64.CHROUT('\n')
|
||||||
|
c64.CHROUT('\n')
|
||||||
|
|
||||||
; word ww = sum(barr)
|
|
||||||
; uword uw = sum(ubarr)
|
|
||||||
; ww = sum(warr)
|
|
||||||
; uw = sum(uwarr)
|
|
||||||
; float ff = sum(farr)
|
|
||||||
|
|
||||||
c64scr.print("\nyou should see no errors above.")
|
uw = -22222
|
||||||
|
c64scr.print_w(uw >> 1)
|
||||||
|
c64.CHROUT('\n')
|
||||||
|
c64scr.print_w(uw >> 7)
|
||||||
|
c64.CHROUT('\n')
|
||||||
|
c64scr.print_w(uw >> 8)
|
||||||
|
c64.CHROUT('\n')
|
||||||
|
c64scr.print_w(uw >> 9)
|
||||||
|
c64.CHROUT('\n')
|
||||||
|
c64scr.print_w(uw >> 15)
|
||||||
|
c64.CHROUT('\n')
|
||||||
|
c64scr.print_w(uw >> 16)
|
||||||
|
c64.CHROUT('\n')
|
||||||
|
c64scr.print_w(uw >> 26)
|
||||||
|
c64.CHROUT('\n')
|
||||||
|
c64.CHROUT('\n')
|
||||||
|
|
||||||
|
uw = 22222
|
||||||
|
c64scr.print_w(uw >> 1)
|
||||||
|
c64.CHROUT('\n')
|
||||||
|
c64scr.print_w(uw >> 7)
|
||||||
|
c64.CHROUT('\n')
|
||||||
|
c64scr.print_w(uw >> 8)
|
||||||
|
c64.CHROUT('\n')
|
||||||
|
c64scr.print_w(uw >> 9)
|
||||||
|
c64.CHROUT('\n')
|
||||||
|
c64scr.print_w(uw >> 15)
|
||||||
|
c64.CHROUT('\n')
|
||||||
|
c64scr.print_w(uw >> 16)
|
||||||
|
c64.CHROUT('\n')
|
||||||
|
c64scr.print_w(uw >> 26)
|
||||||
|
c64.CHROUT('\n')
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
%zeropage basicsafe
|
%zeropage basicsafe
|
||||||
|
|
||||||
|
; TODO implement asm generation for all loops here
|
||||||
|
|
||||||
main {
|
main {
|
||||||
|
|
||||||
sub start() {
|
sub start() {
|
||||||
|
@ -7,6 +7,7 @@ repositories {
|
|||||||
mavenCentral()
|
mavenCentral()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
configurations {
|
configurations {
|
||||||
// strange antlr plugin issue, see https://github.com/gradle/gradle/issues/820
|
// strange antlr plugin issue, see https://github.com/gradle/gradle/issues/820
|
||||||
// this avoids linking in the complete antlr binary jar
|
// this avoids linking in the complete antlr binary jar
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
||||||
<excludeFolder url="file://$MODULE_DIR$/build" />
|
<excludeFolder url="file://$MODULE_DIR$/build" />
|
||||||
</content>
|
</content>
|
||||||
<orderEntry type="jdk" jdkName="1.8" jdkType="JavaSDK" />
|
<orderEntry type="jdk" jdkName="openjdk-11" jdkType="JavaSDK" />
|
||||||
<orderEntry type="sourceFolder" forTests="false" />
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
<orderEntry type="library" name="antlr-4.7.2-complete" level="project" />
|
<orderEntry type="library" name="antlr-4.7.2-complete" level="project" />
|
||||||
</component>
|
</component>
|
||||||
|
Reference in New Issue
Block a user