Compare commits

...

56 Commits
v4.3 ... v4.4

Author SHA1 Message Date
22031f39b0 update compiled examples 2020-10-02 23:39:20 +02:00
c4673d3a67 v4.4 2020-10-02 23:32:45 +02:00
e83e021541 doc 2020-10-02 23:31:49 +02:00
c1f2ecd413 struct assignment from array value now checks number of elements 2020-10-02 22:48:39 +02:00
46fbe01df9 added codengeration for assigment of array of values to a struct variable (all members at once) 2020-10-02 22:37:52 +02:00
8647a8290e fix code generation for using struct vars in arrays and such 2020-10-02 22:21:18 +02:00
bac51f4b31 fix subtraction error for bytes 2020-10-02 21:30:32 +02:00
582aab180a oops 2020-10-02 02:39:19 +02:00
5fb714fcb2 expression splitter integrated into expression simplifier 2020-10-02 01:54:37 +02:00
3994de77d0 fix expression splitter handling related to code ballooning 2020-10-02 01:49:55 +02:00
24c8d1f1f4 expression splitter for vardecls with binexpr init expression 2020-10-02 00:34:12 +02:00
110f877dcc binexpr expression splitter for assignments 2020-10-02 00:04:21 +02:00
9cd3a9f8e8 fix isSameAs for ArrayIndexed expressions, and by extension, assignment.isAugmentable() 2020-10-01 23:26:43 +02:00
1464050bf5 expression splitter moved to separate optimizer 2020-10-01 02:58:12 +02:00
95e9e1b550 avoid adding unneeded variable initalization assignments. Improved removal of useless double assignments. 2020-10-01 00:39:49 +02:00
bda1c1c1eb reduce slow estack usage by splitting up simple binary expressions 2020-09-30 19:57:16 +02:00
d020a7974a reduce slow estack usage by splitting up simple binary expressions 2020-09-30 17:51:35 +02:00
a51fad3aab parentheses around binary exprs in source output 2020-09-30 16:38:54 +02:00
3cd32778bb don't split expressions referencing the target variable wrongly 2020-09-30 01:11:33 +02:00
8d67056f84 fixed estack corruption caused by c64 print_f 2020-09-29 21:12:16 +02:00
e986973b5e wrong floats 2020-09-29 04:05:45 +02:00
448c934cba optimized neg(x) and abs(x) 2020-09-29 03:58:17 +02:00
96ef7ba55d fixed ast to source for structs 2020-09-29 00:28:11 +02:00
4372de1e7e allow creating arrays of pointers to other arrays. Usefullness is very limited though... 2020-09-29 00:03:47 +02:00
af0fb88adf allow creating string arrays. Fixed array index scaling for word arrays. 2020-09-28 02:23:36 +02:00
066233eee8 todos 2020-09-27 22:05:44 +02:00
b6f85d10b0 reintroduced system reset at program exit if zeropage is clobbered 2020-09-27 22:00:36 +02:00
6f75413c09 some more optimizations in expressions with memreads 2020-09-27 21:43:40 +02:00
d45fe4ce74 fixed invalid eval stack ptr issue 2020-09-27 20:55:34 +02:00
e828c013e6 fix word+/-byte errors if byte was unsigned 2020-09-27 20:23:42 +02:00
988459f744 don't generate a byte storage for every single time a register needs saving 2020-09-27 16:26:02 +02:00
7c701bdf3f corrections 2020-09-27 14:14:45 +02:00
446fc35d5c avoid excessive comparisons for certain comparison expressions against zero 2020-09-27 03:55:59 +02:00
bec9cc7047 asm store/load same optimizer back.... 2020-09-27 02:45:59 +02:00
961380acb6 optimized float ==0 or 1 comparisons 2020-09-27 01:56:08 +02:00
84c0685a60 fix faulty comparison optimization 2020-09-27 01:40:12 +02:00
629222f103 larger 2020-09-26 19:59:57 +02:00
8c448e5bc2 finished optimized comparison asm generation 2020-09-26 19:55:04 +02:00
b5fa6c2d0a library modules imported from embedded resource now contain proper file path (useful for error messages) 2020-09-26 19:30:17 +02:00
680b2df08a just call the asmsub 2020-09-26 19:14:06 +02:00
09bd47f98b > 2020-09-26 19:02:29 +02:00
7f69f9ce4f <= 2020-09-26 18:04:43 +02:00
4179b4e543 all unsigned comparisons 2020-09-26 17:45:35 +02:00
66364554c4 new comparisons testprog 2020-09-26 16:11:47 +02:00
43f2448789 added (u)byte and (u)word '>' 2020-09-26 13:15:03 +02:00
130cee1e70 tweak '<' code 2020-09-26 12:47:40 +02:00
b976360248 fix fallthrough problem with 'when'. Fix too greedy asm optimization that caused conditional jumps to fail sometimes because the condition value wasn't loaded. 2020-09-26 00:22:55 +02:00
225bfc4164 fix 16+8 bit add and sub sign extensions 2020-09-25 22:51:59 +02:00
d7ceda4d82 removed the automatic system reset at program exit, this did't work with the new init code 2020-09-25 22:12:14 +02:00
14d091e60a crashes :( 2020-09-24 23:50:20 +02:00
2809668ef4 new asm code for (u)word and (u)byte < 2020-09-24 23:08:36 +02:00
bafb86e00b new asm code for (n)equals 2020-09-24 22:28:24 +02:00
f5db31b8ff do..until condition can now refer to variables defined in the loop's inner scope. 2020-09-24 19:26:07 +02:00
e1d0dbed0c do..until condition can now refer to variables defined in the loop's inner scope. 2020-09-23 23:24:32 +02:00
1d1fe364d0 added %option no_sysinit to avoid having the system re-initialization code executed at the start of the program 2020-09-23 23:01:47 +02:00
2b9316c4ff reworked program init logic so that it is included as the first thing inside main.start itself, to allow better stand alone asm 2020-09-23 22:29:21 +02:00
82 changed files with 3334 additions and 1188 deletions

View File

@ -355,19 +355,19 @@ mul_f .proc
.pend .pend
neg_f .proc neg_f .proc
; -- push -flt back on stack ; -- toggle the sign bit on the stack
jsr pop_float_fac1 lda P8ESTACK_HI+3,x
stx P8ZP_SCRATCH_REG eor #$80
jsr NEGOP sta P8ESTACK_HI+3,x
jmp push_fac1_as_result rts
.pend .pend
abs_f .proc abs_f .proc
; -- push abs(float) on stack (as float) ; -- strip the sign bit on the stack
jsr pop_float_fac1 lda P8ESTACK_HI+3,x
stx P8ZP_SCRATCH_REG and #$7f
jsr ABS sta P8ESTACK_HI+3,x
jmp push_fac1_as_result rts
.pend .pend
equal_f .proc equal_f .proc

View File

@ -207,8 +207,8 @@ sub print_f (float value) {
jsr c64.CHROUT jsr c64.CHROUT
iny iny
bne - bne -
ldx floats_store_reg + ldx floats_store_reg
+ rts rts
}} }}
} }

View File

@ -33,8 +33,8 @@ graphics {
} }
word @zp d = 0 word @zp d = 0
ubyte positive_ix = true ubyte positive_ix = true
word @zp dx = x2 - x1 as word word @zp dx = x2-x1
word @zp dy = y2 as word - y1 as word word @zp dy = y2-y1
if dx < 0 { if dx < 0 {
dx = -dx dx = -dx
positive_ix = false positive_ix = false

View File

@ -251,8 +251,7 @@ asmsub init_system() {
sta c64.COLOR sta c64.COLOR
lda #0 lda #0
sta c64.BGCOL0 sta c64.BGCOL0
tax jsr disable_runstop_and_charsetswitch
tay
clc clc
clv clv
cli cli
@ -270,6 +269,16 @@ asmsub reset_system() {
}} }}
} }
asmsub disable_runstop_and_charsetswitch() {
%asm {{
lda #$80
sta 657 ; disable charset switching
lda #239
sta 808 ; disable run/stop key
rts
}}
}
asmsub set_irqvec_excl() clobbers(A) { asmsub set_irqvec_excl() clobbers(A) {
%asm {{ %asm {{
sei sei

View File

@ -169,20 +169,18 @@ cx16 {
; ---- Commander X-16 additions on top of C64 kernal routines ---- ; ---- Commander X-16 additions on top of C64 kernal routines ----
; spelling of the names is taken from the Commander X-16 rom sources ; spelling of the names is taken from the Commander X-16 rom sources
; TODO specify the correct clobbers for alle these functions, for simplicity all 3 regs are marked clobbered atm
; supported C128 additions ; supported C128 additions
romsub $ff4a = close_all() clobbers(A,X,Y) romsub $ff4a = close_all(ubyte device @A) clobbers(A,X,Y)
romsub $ff59 = lkupla() clobbers(A,X,Y) romsub $ff59 = lkupla(ubyte la @A) clobbers(A,X,Y)
romsub $ff5c = lkupsa() clobbers(A,X,Y) romsub $ff5c = lkupsa(ubyte sa @Y) clobbers(A,X,Y)
romsub $ff5f = screen_set_mode(ubyte mode @A) clobbers(A, X, Y) -> ubyte @Pc romsub $ff5f = screen_set_mode(ubyte mode @A) clobbers(A, X, Y) -> ubyte @Pc
romsub $ff62 = screen_set_charset(ubyte charset @A, uword charsetptr @XY) clobbers(A,X,Y) ; incompatible with C128 dlchr() romsub $ff62 = screen_set_charset(ubyte charset @A, uword charsetptr @XY) clobbers(A,X,Y) ; incompatible with C128 dlchr()
romsub $ff65 = pfkey() clobbers(A,X,Y) ; not yet supported: romsub $ff65 = pfkey() clobbers(A,X,Y)
romsub $ff6e = jsrfar() clobbers(A,X,Y) romsub $ff6e = jsrfar()
romsub $ff74 = fetch() clobbers(A,X,Y) romsub $ff74 = fetch(ubyte bank @X, ubyte index @Y) clobbers(X) -> ubyte @A
romsub $ff77 = stash() clobbers(A,X,Y) romsub $ff77 = stash(ubyte data @A, ubyte bank @X, ubyte index @Y) clobbers(X)
romsub $ff7a = cmpare() clobbers(A,X,Y) romsub $ff7a = cmpare(ubyte data @A, ubyte bank @X, ubyte index @Y) clobbers(X)
romsub $ff7d = primm() clobbers(A,X,Y) romsub $ff7d = primm()
; X16 additions ; X16 additions
romsub $ff44 = macptr() clobbers(A,X,Y) romsub $ff44 = macptr() clobbers(A,X,Y)
@ -193,7 +191,9 @@ romsub $ff71 = mouse_scan() clobbers(A, X, Y)
romsub $ff53 = joystick_scan() clobbers(A, X, Y) romsub $ff53 = joystick_scan() clobbers(A, X, Y)
romsub $ff56 = joystick_get(ubyte joynr @A) -> ubyte @A, ubyte @X, ubyte @Y romsub $ff56 = joystick_get(ubyte joynr @A) -> ubyte @A, ubyte @X, ubyte @Y
romsub $ff4d = clock_set_date_time() clobbers(A, X, Y) ; args: r0, r1, r2, r3L romsub $ff4d = clock_set_date_time() clobbers(A, X, Y) ; args: r0, r1, r2, r3L
romsub $ff50 = clock_get_date_time() clobbers(A) ; outout args: r0, r1, r2, r3L romsub $ff50 = clock_get_date_time() clobbers(A, X, Y) ; outout args: r0, r1, r2, r3L
; TODO specify the correct clobbers for alle these functions below, we now assume all 3 regs are clobbered
; high level graphics & fonts ; high level graphics & fonts
romsub $ff20 = GRAPH_init() clobbers(A,X,Y) ; uses vectors=r0 romsub $ff20 = GRAPH_init() clobbers(A,X,Y) ; uses vectors=r0

View File

@ -1 +1 @@
4.3 4.4

View File

@ -53,12 +53,14 @@ class AstToSourceCode(val output: (text: String) -> Unit, val program: Program):
} }
override fun visit(expr: BinaryExpression) { override fun visit(expr: BinaryExpression) {
output("(")
expr.left.accept(this) expr.left.accept(this)
if(expr.operator.any { it.isLetter() }) if(expr.operator.any { it.isLetter() })
output(" ${expr.operator} ") output(" ${expr.operator} ")
else else
output(expr.operator) output(expr.operator)
expr.right.accept(this) expr.right.accept(this)
output(")")
} }
override fun visit(directive: Directive) { override fun visit(directive: Directive) {
@ -85,7 +87,7 @@ class AstToSourceCode(val output: (text: String) -> Unit, val program: Program):
DataType.ARRAY_W -> "word[" DataType.ARRAY_W -> "word["
DataType.ARRAY_F -> "float[" DataType.ARRAY_F -> "float["
DataType.STRUCT -> "" // the name of the struct is enough DataType.STRUCT -> "" // the name of the struct is enough
else -> "?????2" else -> "?????"
} }
} }
@ -113,7 +115,10 @@ class AstToSourceCode(val output: (text: String) -> Unit, val program: Program):
VarDeclType.CONST -> output("const ") VarDeclType.CONST -> output("const ")
VarDeclType.MEMORY -> output("&") VarDeclType.MEMORY -> output("&")
} }
output(decl.struct?.name ?: "")
if(decl.datatype==DataType.STRUCT && decl.struct!=null)
output(decl.struct!!.name)
output(datatypeString(decl.datatype)) output(datatypeString(decl.datatype))
if(decl.arraysize!=null) { if(decl.arraysize!=null) {
decl.arraysize!!.index.accept(this) decl.arraysize!!.index.accept(this)
@ -338,7 +343,7 @@ class AstToSourceCode(val output: (text: String) -> Unit, val program: Program):
output("do ") output("do ")
untilLoop.body.accept(this) untilLoop.body.accept(this)
output(" until ") output(" until ")
untilLoop.untilCondition.accept(this) untilLoop.condition.accept(this)
} }
override fun visit(returnStmt: Return) { override fun visit(returnStmt: Return) {

View File

@ -137,7 +137,15 @@ interface INameScope {
} }
return null return null
} else { } else {
// unqualified name, find the scope the localContext is in, look in that first // unqualified name
// special case: the do....until statement can also look INSIDE the anonymous scope
if(localContext.parent.parent is UntilLoop) {
val symbolFromInnerScope = (localContext.parent.parent as UntilLoop).body.getLabelOrVariable(scopedName[0])
if(symbolFromInnerScope!=null)
return symbolFromInnerScope
}
// find the scope the localContext is in, look in that first
var statementScope = localContext var statementScope = localContext
while(statementScope !is ParentSentinel) { while(statementScope !is ParentSentinel) {
val localScope = statementScope.definingScope() val localScope = statementScope.definingScope()
@ -232,7 +240,7 @@ class Program(val name: String, val modules: MutableList<Module>): Node {
override fun linkParents(parent: Node) { override fun linkParents(parent: Node) {
modules.forEach { modules.forEach {
it.linkParents(this) it.linkParents(namespace)
} }
} }
@ -312,6 +320,14 @@ class GlobalNamespace(val modules: List<Module>): Node, INameScope {
} }
} }
} }
// special case: the do....until statement can also look INSIDE the anonymous scope
if(localContext.parent.parent is UntilLoop) {
val symbolFromInnerScope = (localContext.parent.parent as UntilLoop).body.lookup(scopedName, localContext)
if(symbolFromInnerScope!=null)
return symbolFromInnerScope
}
// lookup something from the module. // lookup something from the module.
return when (val stmt = localContext.definingModule().lookup(scopedName, localContext)) { return when (val stmt = localContext.definingModule().lookup(scopedName, localContext)) {
is Label, is VarDecl, is Block, is Subroutine, is StructDecl -> stmt is Label, is VarDecl, is Block, is Subroutine, is StructDecl -> stmt

View File

@ -17,13 +17,13 @@ import kotlin.math.abs
val associativeOperators = setOf("+", "*", "&", "|", "^", "or", "and", "xor", "==", "!=") val associativeOperators = setOf("+", "*", "&", "|", "^", "or", "and", "xor", "==", "!=")
val comparisonOperators = setOf("==", "!=", "<", ">", "<=", ">=") val comparisonOperators = setOf("==", "!=", "<", ">", "<=", ">=")
val augmentAssignmentOperators = setOf("+", "-", "/", "*", "**", "&", "|", "^", "<<", ">>")
sealed class Expression: Node { sealed class Expression: Node {
abstract fun constValue(program: Program): NumericLiteralValue? abstract fun constValue(program: Program): NumericLiteralValue?
abstract fun accept(visitor: IAstVisitor) abstract fun accept(visitor: IAstVisitor)
abstract fun accept(visitor: AstWalker, parent: Node) abstract fun accept(visitor: AstWalker, parent: Node)
abstract fun referencesIdentifiers(vararg name: String): Boolean abstract fun referencesIdentifier(vararg scopedName: String): Boolean
abstract fun inferType(program: Program): InferredTypes.InferredType abstract fun inferType(program: Program): InferredTypes.InferredType
infix fun isSameAs(assigntarget: AssignTarget) = assigntarget.isSameAs(this) infix fun isSameAs(assigntarget: AssignTarget) = assigntarget.isSameAs(this)
@ -85,7 +85,7 @@ class PrefixExpression(val operator: String, var expression: Expression, overrid
override fun accept(visitor: IAstVisitor) = visitor.visit(this) override fun accept(visitor: IAstVisitor) = visitor.visit(this)
override fun accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent) override fun accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent)
override fun referencesIdentifiers(vararg name: String) = expression.referencesIdentifiers(*name) override fun referencesIdentifier(vararg scopedName: String) = expression.referencesIdentifier(*scopedName)
override fun inferType(program: Program): InferredTypes.InferredType { override fun inferType(program: Program): InferredTypes.InferredType {
val inferred = expression.inferType(program) val inferred = expression.inferType(program)
return when(operator) { return when(operator) {
@ -142,7 +142,7 @@ class BinaryExpression(var left: Expression, var operator: String, var right: Ex
override fun accept(visitor: IAstVisitor) = visitor.visit(this) override fun accept(visitor: IAstVisitor) = visitor.visit(this)
override fun accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent) override fun accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent)
override fun referencesIdentifiers(vararg name: String) = left.referencesIdentifiers(*name) || right.referencesIdentifiers(*name) override fun referencesIdentifier(vararg scopedName: String) = left.referencesIdentifier(*scopedName) || right.referencesIdentifier(*scopedName)
override fun inferType(program: Program): InferredTypes.InferredType { 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)
@ -255,7 +255,7 @@ class ArrayIndexedExpression(var identifier: IdentifierReference,
override fun accept(visitor: IAstVisitor) = visitor.visit(this) override fun accept(visitor: IAstVisitor) = visitor.visit(this)
override fun accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent) override fun accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent)
override fun referencesIdentifiers(vararg name: String) = identifier.referencesIdentifiers(*name) override fun referencesIdentifier(vararg scopedName: String) = identifier.referencesIdentifier(*scopedName)
override fun inferType(program: Program): InferredTypes.InferredType { override fun inferType(program: Program): InferredTypes.InferredType {
val target = identifier.targetStatement(program.namespace) val target = identifier.targetStatement(program.namespace)
@ -291,7 +291,7 @@ class TypecastExpression(var expression: Expression, var type: DataType, val imp
override fun accept(visitor: IAstVisitor) = visitor.visit(this) override fun accept(visitor: IAstVisitor) = visitor.visit(this)
override fun accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent) override fun accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent)
override fun referencesIdentifiers(vararg name: String) = expression.referencesIdentifiers(*name) override fun referencesIdentifier(vararg scopedName: String) = expression.referencesIdentifier(*scopedName)
override fun inferType(program: Program): InferredTypes.InferredType = InferredTypes.knownFor(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
@ -322,7 +322,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 referencesIdentifier(vararg scopedName: String) = false
override fun inferType(program: Program): InferredTypes.InferredType = InferredTypes.knownFor(DataType.UWORD) override fun inferType(program: Program): InferredTypes.InferredType = InferredTypes.knownFor(DataType.UWORD)
override fun accept(visitor: IAstVisitor) = visitor.visit(this) override fun accept(visitor: IAstVisitor) = visitor.visit(this)
override fun accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent) override fun accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent)
@ -345,7 +345,7 @@ class DirectMemoryRead(var addressExpression: Expression, override val position:
override fun accept(visitor: IAstVisitor) = visitor.visit(this) override fun accept(visitor: IAstVisitor) = visitor.visit(this)
override fun accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent) override fun accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent)
override fun referencesIdentifiers(vararg name: String) = false override fun referencesIdentifier(vararg scopedName: String) = false
override fun inferType(program: Program): InferredTypes.InferredType = InferredTypes.knownFor(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
@ -398,7 +398,7 @@ class NumericLiteralValue(val type: DataType, // only numerical types allowed
throw FatalAstException("can't replace here") throw FatalAstException("can't replace here")
} }
override fun referencesIdentifiers(vararg name: String) = false override fun referencesIdentifier(vararg scopedName: String) = false
override fun constValue(program: Program) = this override fun constValue(program: Program) = this
override fun accept(visitor: IAstVisitor) = visitor.visit(this) override fun accept(visitor: IAstVisitor) = visitor.visit(this)
@ -498,7 +498,7 @@ class StringLiteralValue(val value: String,
throw FatalAstException("can't replace here") throw FatalAstException("can't replace here")
} }
override fun referencesIdentifiers(vararg name: String) = false override fun referencesIdentifier(vararg scopedName: String) = false
override fun constValue(program: Program): NumericLiteralValue? = null override fun constValue(program: Program): NumericLiteralValue? = null
override fun accept(visitor: IAstVisitor) = visitor.visit(this) override fun accept(visitor: IAstVisitor) = visitor.visit(this)
override fun accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent) override fun accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent)
@ -533,7 +533,7 @@ class ArrayLiteralValue(val type: InferredTypes.InferredType, // inferred be
replacement.parent = this replacement.parent = this
} }
override fun referencesIdentifiers(vararg name: String) = value.any { it.referencesIdentifiers(*name) } override fun referencesIdentifier(vararg scopedName: String) = value.any { it.referencesIdentifier(*scopedName) }
override fun constValue(program: Program): NumericLiteralValue? = null override fun constValue(program: Program): NumericLiteralValue? = null
override fun accept(visitor: IAstVisitor) = visitor.visit(this) override fun accept(visitor: IAstVisitor) = visitor.visit(this)
override fun accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent) override fun accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent)
@ -569,10 +569,17 @@ class ArrayLiteralValue(val type: InferredTypes.InferredType, // inferred be
val dts = datatypesInArray.map { it.typeOrElse(DataType.STRUCT) } val dts = datatypesInArray.map { it.typeOrElse(DataType.STRUCT) }
return when { return when {
DataType.FLOAT in dts -> InferredTypes.InferredType.known(DataType.ARRAY_F) DataType.FLOAT in dts -> InferredTypes.InferredType.known(DataType.ARRAY_F)
DataType.STR in dts -> InferredTypes.InferredType.known(DataType.ARRAY_UW)
DataType.WORD in dts -> InferredTypes.InferredType.known(DataType.ARRAY_W) DataType.WORD in dts -> InferredTypes.InferredType.known(DataType.ARRAY_W)
DataType.UWORD in dts -> InferredTypes.InferredType.known(DataType.ARRAY_UW) DataType.UWORD in dts -> InferredTypes.InferredType.known(DataType.ARRAY_UW)
DataType.BYTE in dts -> InferredTypes.InferredType.known(DataType.ARRAY_B) DataType.BYTE in dts -> InferredTypes.InferredType.known(DataType.ARRAY_B)
DataType.UBYTE in dts -> InferredTypes.InferredType.known(DataType.ARRAY_UB) DataType.UBYTE in dts -> InferredTypes.InferredType.known(DataType.ARRAY_UB)
DataType.ARRAY_UW in dts ||
DataType.ARRAY_W in dts ||
DataType.ARRAY_UB in dts ||
DataType.ARRAY_B in dts ||
DataType.ARRAY_F in dts ||
DataType.STRUCT in dts -> InferredTypes.InferredType.known(DataType.ARRAY_UW)
else -> InferredTypes.InferredType.unknown() else -> InferredTypes.InferredType.unknown()
} }
} }
@ -631,7 +638,7 @@ class RangeExpr(var from: Expression,
override fun accept(visitor: IAstVisitor) = visitor.visit(this) override fun accept(visitor: IAstVisitor) = visitor.visit(this)
override fun accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent) override fun accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent)
override fun referencesIdentifiers(vararg name: String): Boolean = from.referencesIdentifiers(*name) || to.referencesIdentifiers(*name) override fun referencesIdentifier(vararg scopedName: String): Boolean = from.referencesIdentifier(*scopedName) || to.referencesIdentifier(*scopedName)
override fun inferType(program: Program): InferredTypes.InferredType { 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)
@ -744,8 +751,8 @@ data class IdentifierReference(val nameInSource: List<String>, override val posi
override fun accept(visitor: IAstVisitor) = visitor.visit(this) override fun accept(visitor: IAstVisitor) = visitor.visit(this)
override fun accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent) override fun accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent)
override fun referencesIdentifiers(vararg name: String): Boolean = override fun referencesIdentifier(vararg scopedName: String): Boolean =
nameInSource.size==name.size && nameInSource.toTypedArray().contentEquals(name) nameInSource.size==scopedName.size && nameInSource.toTypedArray().contentEquals(scopedName)
override fun inferType(program: Program): InferredTypes.InferredType { override fun inferType(program: Program): InferredTypes.InferredType {
return when (val targetStmt = targetStatement(program.namespace)) { return when (val targetStmt = targetStatement(program.namespace)) {
@ -767,6 +774,18 @@ data class IdentifierReference(val nameInSource: List<String>, override val posi
else -> throw FatalAstException("requires a reference value") else -> throw FatalAstException("requires a reference value")
} }
} }
fun firstStructVarName(namespace: INameScope): String? {
// take the name of the first struct member of the structvariable instead
// if it's just a regular variable, return null.
val struct = memberOfStruct(namespace) ?: return null
val decl = targetVarDecl(namespace)!!
val firstStructMember = struct.nameOfFirstMember()
// find the flattened var that belongs to this first struct member
val firstVarName = listOf(decl.name, firstStructMember)
val firstVar = definingScope().lookup(firstVarName, this) as VarDecl
return firstVar.name
}
} }
class FunctionCall(override var target: IdentifierReference, class FunctionCall(override var target: IdentifierReference,
@ -833,7 +852,7 @@ class FunctionCall(override var target: IdentifierReference,
override fun accept(visitor: IAstVisitor) = visitor.visit(this) override fun accept(visitor: IAstVisitor) = visitor.visit(this)
override fun accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent) override fun accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent)
override fun referencesIdentifiers(vararg name: String): Boolean = target.referencesIdentifiers(*name) || args.any{it.referencesIdentifiers(*name)} override fun referencesIdentifier(vararg scopedName: String): Boolean = target.referencesIdentifier(*scopedName) || args.any{it.referencesIdentifier(*scopedName)}
override fun inferType(program: Program): InferredTypes.InferredType { override fun inferType(program: Program): InferredTypes.InferredType {
val constVal = constValue(program ,false) val constVal = constValue(program ,false)

View File

@ -334,8 +334,8 @@ internal class AstChecker(private val program: Program,
} }
override fun visit(untilLoop: UntilLoop) { override fun visit(untilLoop: UntilLoop) {
if(untilLoop.untilCondition.inferType(program).typeOrElse(DataType.STRUCT) !in IntegerDatatypes) if(untilLoop.condition.inferType(program).typeOrElse(DataType.STRUCT) !in IntegerDatatypes)
errors.err("condition value should be an integer type", untilLoop.untilCondition.position) errors.err("condition value should be an integer type", untilLoop.condition.position)
super.visit(untilLoop) super.visit(untilLoop)
} }
@ -376,6 +376,9 @@ internal class AstChecker(private val program: Program,
if (sourceVar?.struct != null) { if (sourceVar?.struct != null) {
if (sourceVar.struct !== targetVar.struct) if (sourceVar.struct !== targetVar.struct)
errors.err("assignment of different struct types", assignment.position) errors.err("assignment of different struct types", assignment.position)
} else if(sourceVar?.isArray==true) {
if((sourceVar.value as ArrayLiteralValue).value.size != targetVar.struct?.numberOfElements)
errors.err("number of elements doesn't match struct definition", sourceVar.position)
} }
} }
} }
@ -476,7 +479,7 @@ internal class AstChecker(private val program: Program,
fun err(msg: String, position: Position?=null) = errors.err(msg, position ?: decl.position) fun err(msg: String, position: Position?=null) = errors.err(msg, position ?: decl.position)
// the initializer value can't refer to the variable itself (recursive definition) // the initializer value can't refer to the variable itself (recursive definition)
if(decl.value?.referencesIdentifiers(decl.name) == true || decl.arraysize?.index?.referencesIdentifiers(decl.name) == true) if(decl.value?.referencesIdentifier(decl.name) == true || decl.arraysize?.index?.referencesIdentifier(decl.name) == true)
err("recursive var declaration") err("recursive var declaration")
// CONST can only occur on simple types (byte, word, float) // CONST can only occur on simple types (byte, word, float)
@ -709,7 +712,7 @@ internal class AstChecker(private val program: Program,
err("this directive may only occur in a block or at module level") err("this directive may only occur in a block or at module level")
if(directive.args.isEmpty()) if(directive.args.isEmpty())
err("missing option directive argument(s)") err("missing option directive argument(s)")
else if(directive.args.map{it.name in setOf("enable_floats", "force_output")}.any { !it }) else if(directive.args.map{it.name in setOf("enable_floats", "force_output", "no_sysinit")}.any { !it })
err("invalid option directive argument(s)") err("invalid option directive argument(s)")
} }
"%target" -> { "%target" -> {
@ -742,11 +745,17 @@ internal class AstChecker(private val program: Program,
checkValueTypeAndRangeArray(array.type.typeOrElse(DataType.STRUCT), null, arrayspec, array) checkValueTypeAndRangeArray(array.type.typeOrElse(DataType.STRUCT), null, arrayspec, array)
} }
if(!array.value.all { it is NumericLiteralValue || it is AddressOf || it is StringLiteralValue }) { fun isPassByReferenceElement(e: Expression): Boolean {
// TODO for now, array literals have to consist of all compile time constant values... if(e is IdentifierReference) {
errors.err("array literal doesn't consist of only compile time constant values", array.position) val decl = e.targetVarDecl(program.namespace)!!
return decl.datatype in PassByReferenceDatatypes
}
return e is StringLiteralValue
} }
if(!array.value.all { it is NumericLiteralValue || it is AddressOf || isPassByReferenceElement(it) })
errors.err("array literal contains invalid types", array.position)
super.visit(array) super.visit(array)
} }

View File

@ -82,6 +82,7 @@ interface IAstModification {
class SwapOperands(val expr: BinaryExpression): IAstModification { class SwapOperands(val expr: BinaryExpression): IAstModification {
override fun perform() { override fun perform() {
require(expr.operator in associativeOperators)
val tmp = expr.left val tmp = expr.left
expr.left = expr.right expr.left = expr.right
expr.right = tmp expr.right = tmp
@ -228,6 +229,7 @@ abstract class AstWalker {
track(before(decl, parent), decl, parent) track(before(decl, parent), decl, parent)
decl.value?.accept(this, decl) decl.value?.accept(this, decl)
decl.arraysize?.accept(this, decl) decl.arraysize?.accept(this, decl)
decl.struct?.accept(this, decl)
track(after(decl, parent), decl, parent) track(after(decl, parent), decl, parent)
} }
@ -348,7 +350,7 @@ abstract class AstWalker {
fun visit(untilLoop: UntilLoop, parent: Node) { fun visit(untilLoop: UntilLoop, parent: Node) {
track(before(untilLoop, parent), untilLoop, parent) track(before(untilLoop, parent), untilLoop, parent)
untilLoop.untilCondition.accept(this, untilLoop) untilLoop.condition.accept(this, untilLoop)
untilLoop.body.accept(this, untilLoop) untilLoop.body.accept(this, untilLoop)
track(after(untilLoop, parent), untilLoop, parent) track(after(untilLoop, parent), untilLoop, parent)
} }

View File

@ -33,6 +33,7 @@ interface IAstVisitor {
fun visit(decl: VarDecl) { fun visit(decl: VarDecl) {
decl.value?.accept(this) decl.value?.accept(this)
decl.arraysize?.accept(this) decl.arraysize?.accept(this)
decl.struct?.accept(this)
} }
fun visit(subroutine: Subroutine) { fun visit(subroutine: Subroutine) {
@ -115,7 +116,7 @@ interface IAstVisitor {
} }
fun visit(untilLoop: UntilLoop) { fun visit(untilLoop: UntilLoop) {
untilLoop.untilCondition.accept(this) untilLoop.condition.accept(this)
untilLoop.body.accept(this) untilLoop.body.accept(this)
} }

View File

@ -84,7 +84,7 @@ internal class StatementReorderer(val program: Program) : AstWalker() {
override fun before(assignment: Assignment, parent: Node): Iterable<IAstModification> { override fun before(assignment: Assignment, parent: Node): Iterable<IAstModification> {
val valueType = assignment.value.inferType(program) val valueType = assignment.value.inferType(program)
val targetType = assignment.target.inferType(program, assignment) val targetType = assignment.target.inferType(program, assignment)
if(valueType.istype(DataType.STRUCT) && targetType.istype(DataType.STRUCT)) { if(targetType.istype(DataType.STRUCT) && (valueType.istype(DataType.STRUCT) || valueType.typeOrElse(DataType.STRUCT) in ArrayDatatypes )) {
val assignments = if (assignment.value is ArrayLiteralValue) { val assignments = if (assignment.value is ArrayLiteralValue) {
flattenStructAssignmentFromStructLiteral(assignment, program) // 'structvar = [ ..... ] ' flattenStructAssignmentFromStructLiteral(assignment, program) // 'structvar = [ ..... ] '
} else { } else {
@ -179,26 +179,47 @@ internal class StatementReorderer(val program: Program) : AstWalker() {
when (structAssignment.value) { when (structAssignment.value) {
is IdentifierReference -> { is IdentifierReference -> {
val sourceVar = (structAssignment.value as IdentifierReference).targetVarDecl(program.namespace)!! val sourceVar = (structAssignment.value as IdentifierReference).targetVarDecl(program.namespace)!!
if (sourceVar.struct == null) when {
throw FatalAstException("can only assign arrays or structs to structs") sourceVar.struct!=null -> {
// struct memberwise copy // struct memberwise copy
val sourceStruct = sourceVar.struct!! val sourceStruct = sourceVar.struct!!
if(sourceStruct!==targetVar.struct) { if(sourceStruct!==targetVar.struct) {
// structs are not the same in assignment // structs are not the same in assignment
return listOf() // error will be printed elsewhere return listOf() // error will be printed elsewhere
} }
return struct.statements.zip(sourceStruct.statements).map { member -> if(struct.statements.size!=sourceStruct.statements.size)
val targetDecl = member.first as VarDecl return listOf() // error will be printed elsewhere
val sourceDecl = member.second as VarDecl return struct.statements.zip(sourceStruct.statements).map { member ->
if(targetDecl.name != sourceDecl.name) val targetDecl = member.first as VarDecl
throw FatalAstException("struct member mismatch") val sourceDecl = member.second as VarDecl
val mangled = mangledStructMemberName(identifierName, targetDecl.name) if(targetDecl.name != sourceDecl.name)
val idref = IdentifierReference(listOf(mangled), structAssignment.position) throw FatalAstException("struct member mismatch")
val sourcemangled = mangledStructMemberName(sourceVar.name, sourceDecl.name) val mangled = mangledStructMemberName(identifierName, targetDecl.name)
val sourceIdref = IdentifierReference(listOf(sourcemangled), structAssignment.position) val idref = IdentifierReference(listOf(mangled), structAssignment.position)
val assign = Assignment(AssignTarget(idref, null, null, structAssignment.position), sourceIdref, member.second.position) val sourcemangled = mangledStructMemberName(sourceVar.name, sourceDecl.name)
assign.linkParents(structAssignment) val sourceIdref = IdentifierReference(listOf(sourcemangled), structAssignment.position)
assign val assign = Assignment(AssignTarget(idref, null, null, structAssignment.position), sourceIdref, member.second.position)
assign.linkParents(structAssignment)
assign
}
}
sourceVar.isArray -> {
val array = (sourceVar.value as ArrayLiteralValue).value
if(struct.statements.size!=array.size)
return listOf() // error will be printed elsewhere
return struct.statements.zip(array).map {
val decl = it.first as VarDecl
val mangled = mangledStructMemberName(identifierName, decl.name)
val targetName = IdentifierReference(listOf(mangled), structAssignment.position)
val target = AssignTarget(targetName, null, null, structAssignment.position)
val assign = Assignment(target, it.second, structAssignment.position)
assign.linkParents(structAssignment)
assign
}
}
else -> {
throw FatalAstException("can only assign arrays or structs to structs")
}
} }
} }
is ArrayLiteralValue -> { is ArrayLiteralValue -> {

View File

@ -26,6 +26,19 @@ class VerifyFunctionArgTypes(val program: Program) : IAstVisitor {
} }
companion object { companion object {
private fun argTypeCompatible(argDt: DataType, paramDt: DataType): Boolean {
if(argDt==paramDt)
return true
// there are some exceptions that are considered compatible, such as STR <> UWORD
if(argDt==DataType.STR && paramDt==DataType.UWORD ||
argDt==DataType.UWORD && paramDt==DataType.STR)
return true
return false
}
fun checkTypes(call: IFunctionCall, scope: INameScope, program: Program): String? { fun checkTypes(call: IFunctionCall, scope: INameScope, program: Program): String? {
val argtypes = call.args.map { it.inferType(program).typeOrElse(DataType.STRUCT) } val argtypes = call.args.map { it.inferType(program).typeOrElse(DataType.STRUCT) }
val target = call.target.targetStatement(scope) val target = call.target.targetStatement(scope)
@ -34,7 +47,7 @@ class VerifyFunctionArgTypes(val program: Program) : IAstVisitor {
if(call.args.size != target.parameters.size) if(call.args.size != target.parameters.size)
return "invalid number of arguments" return "invalid number of arguments"
val paramtypes = target.parameters.map { it.type } val paramtypes = target.parameters.map { it.type }
val mismatch = argtypes.zip(paramtypes).indexOfFirst { it.first != it.second} val mismatch = argtypes.zip(paramtypes).indexOfFirst { !argTypeCompatible(it.first, it.second) }
if(mismatch>=0) { if(mismatch>=0) {
val actual = argtypes[mismatch].toString() val actual = argtypes[mismatch].toString()
val expected = paramtypes[mismatch].toString() val expected = paramtypes[mismatch].toString()
@ -47,7 +60,8 @@ class VerifyFunctionArgTypes(val program: Program) : IAstVisitor {
return "invalid number of arguments" return "invalid number of arguments"
val paramtypes = func.parameters.map { it.possibleDatatypes } val paramtypes = func.parameters.map { it.possibleDatatypes }
for (x in argtypes.zip(paramtypes).withIndex()) { for (x in argtypes.zip(paramtypes).withIndex()) {
if (x.value.first !in x.value.second) { val anyCompatible = x.value.second.any { argTypeCompatible(x.value.first, it) }
if (!anyCompatible) {
val actual = x.value.first.toString() val actual = x.value.first.toString()
val expected = x.value.second.toString() val expected = x.value.second.toString()
return "argument ${x.index + 1} type mismatch, was: $actual expected: $expected" return "argument ${x.index + 1} type mismatch, was: $actual expected: $expected"

View File

@ -5,6 +5,7 @@ import prog8.ast.base.*
import prog8.ast.expressions.* import prog8.ast.expressions.*
import prog8.ast.processing.AstWalker import prog8.ast.processing.AstWalker
import prog8.ast.processing.IAstVisitor import prog8.ast.processing.IAstVisitor
import prog8.compiler.target.CompilationTarget
sealed class Statement : Node { sealed class Statement : Node {
@ -214,8 +215,9 @@ open class VarDecl(val type: VarDeclType,
DataType.UWORD -> DataType.ARRAY_UW DataType.UWORD -> DataType.ARRAY_UW
DataType.WORD -> DataType.ARRAY_W DataType.WORD -> DataType.ARRAY_W
DataType.FLOAT -> DataType.ARRAY_F DataType.FLOAT -> DataType.ARRAY_F
DataType.STR -> DataType.ARRAY_UW // use memory address of the string instead
else -> { else -> {
datatypeErrors.add(SyntaxError("array can only contain bytes/words/floats", position)) datatypeErrors.add(SyntaxError("array can only contain bytes/words/floats/strings(ptrs)", position))
DataType.ARRAY_UB DataType.ARRAY_UB
} }
} }
@ -301,6 +303,8 @@ class ArrayIndex(var index: Expression, override val position: Position) : Node
} }
fun constIndex() = (index as? NumericLiteralValue)?.number?.toInt() fun constIndex() = (index as? NumericLiteralValue)?.number?.toInt()
infix fun isSameAs(other: ArrayIndex) = index.isSameAs(other.index)
} }
open class Assignment(var target: AssignTarget, var value: Expression, override val position: Position) : Statement() { open class Assignment(var target: AssignTarget, var value: Expression, override val position: Position) : Statement() {
@ -391,8 +395,8 @@ data class AssignTarget(var identifier: IdentifierReference?,
override fun replaceChildNode(node: Node, replacement: Node) { override fun replaceChildNode(node: Node, replacement: Node) {
when { when {
node===identifier -> identifier = replacement as IdentifierReference node === identifier -> identifier = replacement as IdentifierReference
node===arrayindexed -> arrayindexed = replacement as ArrayIndexedExpression node === arrayindexed -> arrayindexed = replacement as ArrayIndexedExpression
else -> throw FatalAstException("invalid replace") else -> throw FatalAstException("invalid replace")
} }
replacement.parent = this replacement.parent = this
@ -413,16 +417,16 @@ data class AssignTarget(var identifier: IdentifierReference?,
} }
fun inferType(program: Program, stmt: Statement): InferredTypes.InferredType { fun inferType(program: Program, stmt: Statement): InferredTypes.InferredType {
if(identifier!=null) { if (identifier != null) {
val symbol = program.namespace.lookup(identifier!!.nameInSource, stmt) ?: return InferredTypes.unknown() val symbol = program.namespace.lookup(identifier!!.nameInSource, stmt) ?: return InferredTypes.unknown()
if (symbol is VarDecl) return InferredTypes.knownFor(symbol.datatype) if (symbol is VarDecl) return InferredTypes.knownFor(symbol.datatype)
} }
if(arrayindexed!=null) { if (arrayindexed != null) {
return arrayindexed!!.inferType(program) return arrayindexed!!.inferType(program)
} }
if(memoryAddress!=null) if (memoryAddress != null)
return InferredTypes.knownFor(DataType.UBYTE) return InferredTypes.knownFor(DataType.UBYTE)
return InferredTypes.unknown() return InferredTypes.unknown()
@ -430,69 +434,93 @@ data class AssignTarget(var identifier: IdentifierReference?,
fun toExpression(): Expression { fun toExpression(): Expression {
return when { return when {
identifier!=null -> identifier!! identifier != null -> identifier!!
arrayindexed!=null -> arrayindexed!! arrayindexed != null -> arrayindexed!!
memoryAddress!=null -> DirectMemoryRead(memoryAddress.addressExpression, memoryAddress.position) memoryAddress != null -> DirectMemoryRead(memoryAddress.addressExpression, memoryAddress.position)
else -> throw FatalAstException("invalid assignmenttarget $this") else -> throw FatalAstException("invalid assignmenttarget $this")
} }
} }
infix fun isSameAs(value: Expression): Boolean { infix fun isSameAs(value: Expression): Boolean {
return when { return when {
this.memoryAddress!=null -> { memoryAddress != null -> {
// if the target is a memory write, and the value is a memory read, they're the same if the address matches // if the target is a memory write, and the value is a memory read, they're the same if the address matches
if(value is DirectMemoryRead) if (value is DirectMemoryRead)
this.memoryAddress.addressExpression isSameAs value.addressExpression this.memoryAddress.addressExpression isSameAs value.addressExpression
else else
false false
} }
this.identifier!=null -> value is IdentifierReference && value.nameInSource==identifier!!.nameInSource identifier != null -> value is IdentifierReference && value.nameInSource == identifier!!.nameInSource
this.arrayindexed!=null -> value is ArrayIndexedExpression && arrayindexed != null -> {
value.identifier.nameInSource==arrayindexed!!.identifier.nameInSource && if(value is ArrayIndexedExpression && value.identifier.nameInSource == arrayindexed!!.identifier.nameInSource)
value.arrayspec.constIndex()!=null && arrayindexed!!.arrayspec isSameAs value.arrayspec
arrayindexed!!.arrayspec.constIndex()!=null && else
value.arrayspec.constIndex()==arrayindexed!!.arrayspec.constIndex() false
}
else -> false else -> false
} }
} }
fun isSameAs(other: AssignTarget, program: Program): Boolean { fun isSameAs(other: AssignTarget, program: Program): Boolean {
if(this===other) if (this === other)
return true return true
if(this.identifier!=null && other.identifier!=null) if (this.identifier != null && other.identifier != null)
return this.identifier!!.nameInSource==other.identifier!!.nameInSource return this.identifier!!.nameInSource == other.identifier!!.nameInSource
if(this.memoryAddress!=null && other.memoryAddress!=null) { if (this.memoryAddress != null && other.memoryAddress != null) {
val addr1 = this.memoryAddress.addressExpression.constValue(program) val addr1 = this.memoryAddress.addressExpression.constValue(program)
val addr2 = other.memoryAddress.addressExpression.constValue(program) val addr2 = other.memoryAddress.addressExpression.constValue(program)
return addr1!=null && addr2!=null && addr1==addr2 return addr1 != null && addr2 != null && addr1 == addr2
} }
if(this.arrayindexed!=null && other.arrayindexed!=null) { if (this.arrayindexed != null && other.arrayindexed != null) {
if(this.arrayindexed!!.identifier.nameInSource == other.arrayindexed!!.identifier.nameInSource) { if (this.arrayindexed!!.identifier.nameInSource == other.arrayindexed!!.identifier.nameInSource) {
val x1 = this.arrayindexed!!.arrayspec.index.constValue(program) val x1 = this.arrayindexed!!.arrayspec.index.constValue(program)
val x2 = other.arrayindexed!!.arrayspec.index.constValue(program) val x2 = other.arrayindexed!!.arrayspec.index.constValue(program)
return x1!=null && x2!=null && x1==x2 return x1 != null && x2 != null && x1 == x2
} }
} }
return false return false
} }
fun isNotMemory(namespace: INameScope): Boolean { fun isInRegularRAM(namespace: INameScope): Boolean {
if(this.memoryAddress!=null) when {
return false this.memoryAddress != null -> {
if(this.arrayindexed!=null) { return when (this.memoryAddress.addressExpression) {
val targetStmt = this.arrayindexed!!.identifier.targetVarDecl(namespace) is NumericLiteralValue -> {
if(targetStmt!=null) CompilationTarget.instance.machine.isRegularRAMaddress((this.memoryAddress.addressExpression as NumericLiteralValue).number.toInt())
return targetStmt.type!= VarDeclType.MEMORY }
is IdentifierReference -> {
val decl = (this.memoryAddress.addressExpression as IdentifierReference).targetVarDecl(namespace)
if ((decl?.type == VarDeclType.VAR || decl?.type == VarDeclType.CONST) && decl.value is NumericLiteralValue)
CompilationTarget.instance.machine.isRegularRAMaddress((decl.value as NumericLiteralValue).number.toInt())
else
false
}
else -> false
}
}
this.arrayindexed != null -> {
val targetStmt = this.arrayindexed!!.identifier.targetVarDecl(namespace)
return if (targetStmt?.type == VarDeclType.MEMORY) {
val addr = targetStmt.value as? NumericLiteralValue
if (addr != null)
CompilationTarget.instance.machine.isRegularRAMaddress(addr.number.toInt())
else
false
} else true
}
this.identifier != null -> {
val decl = this.identifier!!.targetVarDecl(namespace)!!
return if (decl.type == VarDeclType.MEMORY && decl.value is NumericLiteralValue)
CompilationTarget.instance.machine.isRegularRAMaddress((decl.value as NumericLiteralValue).number.toInt())
else
true
}
else -> return true
} }
if(this.identifier!=null) {
val targetStmt = this.identifier!!.targetVarDecl(namespace)
if(targetStmt!=null)
return targetStmt.type!= VarDeclType.MEMORY
}
return false
} }
} }
class PostIncrDecr(var target: AssignTarget, val operator: String, override val position: Position) : Statement() { class PostIncrDecr(var target: AssignTarget, val operator: String, override val position: Position) : Statement() {
override lateinit var parent: Node override lateinit var parent: Node
@ -815,19 +843,19 @@ class RepeatLoop(var iterations: Expression?, var body: AnonymousScope, override
} }
class UntilLoop(var body: AnonymousScope, class UntilLoop(var body: AnonymousScope,
var untilCondition: Expression, var condition: Expression,
override val position: Position) : Statement() { override val position: Position) : Statement() {
override lateinit var parent: Node override lateinit var parent: Node
override fun linkParents(parent: Node) { override fun linkParents(parent: Node) {
this.parent = parent this.parent = parent
untilCondition.linkParents(this) condition.linkParents(this)
body.linkParents(this) body.linkParents(this)
} }
override fun replaceChildNode(node: Node, replacement: Node) { override fun replaceChildNode(node: Node, replacement: Node) {
when { when {
node===untilCondition -> untilCondition = replacement as Expression node===condition -> condition = replacement as Expression
node===body -> body = replacement as AnonymousScope node===body -> body = replacement as AnonymousScope
else -> throw FatalAstException("invalid replace") else -> throw FatalAstException("invalid replace")
} }

View File

@ -17,8 +17,13 @@ internal class BeforeAsmGenerationAstChanger(val program: Program, val errors: E
override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> { override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> {
subroutineVariables.add(Pair(decl.name, decl)) subroutineVariables.add(Pair(decl.name, decl))
if (decl.value == null && !decl.autogeneratedDontRemove && decl.type == VarDeclType.VAR && decl.datatype in NumericDatatypes) { if (decl.value == null && !decl.autogeneratedDontRemove && decl.type == VarDeclType.VAR && decl.datatype in NumericDatatypes) {
// a numeric vardecl without an initial value is initialized with zero. // a numeric vardecl without an initial value is initialized with zero,
decl.value = decl.zeroElementValue() // unless there's already an assignment below, that initializes the value
val nextAssign = decl.definingScope().nextSibling(decl) as? Assignment
if(nextAssign!=null && nextAssign.target.isSameAs(IdentifierReference(listOf(decl.name), Position.DUMMY)))
decl.value = null
else
decl.value = decl.zeroElementValue()
} }
return noModifications return noModifications
} }
@ -26,16 +31,31 @@ internal class BeforeAsmGenerationAstChanger(val program: Program, val errors: E
override fun after(assignment: Assignment, parent: Node): Iterable<IAstModification> { override fun after(assignment: Assignment, parent: Node): Iterable<IAstModification> {
// Try to replace A = B <operator> Something by A= B, A = A <operator> Something // Try to replace A = B <operator> Something by A= B, A = A <operator> Something
// this triggers the more efficent augmented assignment code generation more often. // this triggers the more efficent augmented assignment code generation more often.
// But it can only be done if the target variable IS NOT OCCURRING AS AN OPERAND ITSELF.
if(!assignment.isAugmentable if(!assignment.isAugmentable
&& assignment.target.identifier != null && assignment.target.identifier != null
&& assignment.target.isNotMemory(program.namespace)) { && assignment.target.isInRegularRAM(program.namespace)) {
val binExpr = assignment.value as? BinaryExpression val binExpr = assignment.value as? BinaryExpression
if(binExpr!=null && binExpr.operator !in comparisonOperators) { if (binExpr != null && binExpr.operator !in comparisonOperators) {
if(binExpr.left !is BinaryExpression) { if (binExpr.left !is BinaryExpression) {
val assignLeft = Assignment(assignment.target, binExpr.left, assignment.position) if (binExpr.right.referencesIdentifier(*assignment.target.identifier!!.nameInSource.toTypedArray())) {
return listOf( // the right part of the expression contains the target variable itself.
IAstModification.InsertBefore(assignment, assignLeft, parent), // we can't 'split' it trivially because the variable will be changed halfway through.
IAstModification.ReplaceNode(binExpr.left, assignment.target.toExpression(), binExpr)) if(binExpr.operator in associativeOperators) {
// A = <something-without-A> <associativeoperator> <otherthing-with-A>
// use the other part of the expression to split.
val assignRight = Assignment(assignment.target, binExpr.right, assignment.position)
return listOf(
IAstModification.InsertBefore(assignment, assignRight, parent),
IAstModification.ReplaceNode(binExpr.right, binExpr.left, binExpr),
IAstModification.ReplaceNode(binExpr.left, assignment.target.toExpression(), binExpr))
}
} else {
val assignLeft = Assignment(assignment.target, binExpr.left, assignment.position)
return listOf(
IAstModification.InsertBefore(assignment, assignLeft, parent),
IAstModification.ReplaceNode(binExpr.left, assignment.target.toExpression(), binExpr))
}
} }
} }
} }
@ -106,7 +126,6 @@ internal class BeforeAsmGenerationAstChanger(val program: Program, val errors: E
&& outerScope !is Block) { && outerScope !is Block) {
mods += IAstModification.InsertAfter(outerStatements[subroutineStmtIdx - 1], returnStmt, outerScope as Node) mods += IAstModification.InsertAfter(outerStatements[subroutineStmtIdx - 1], returnStmt, outerScope as Node)
} }
return mods return mods
} }
@ -154,4 +173,34 @@ internal class BeforeAsmGenerationAstChanger(val program: Program, val errors: E
return noModifications return noModifications
} }
override fun after(ifStatement: IfStatement, parent: Node): Iterable<IAstModification> {
val binExpr = ifStatement.condition as? BinaryExpression
if(binExpr==null || binExpr.operator !in comparisonOperators) {
// if x -> if x!=0, if x+5 -> if x+5 != 0
val booleanExpr = BinaryExpression(ifStatement.condition, "!=", NumericLiteralValue.optimalInteger(0, ifStatement.condition.position), ifStatement.condition.position)
return listOf(IAstModification.ReplaceNode(ifStatement.condition, booleanExpr, ifStatement))
}
return noModifications
}
override fun after(untilLoop: UntilLoop, parent: Node): Iterable<IAstModification> {
val binExpr = untilLoop.condition as? BinaryExpression
if(binExpr==null || binExpr.operator !in comparisonOperators) {
// until x -> until x!=0, until x+5 -> until x+5 != 0
val booleanExpr = BinaryExpression(untilLoop.condition, "!=", NumericLiteralValue.optimalInteger(0, untilLoop.condition.position), untilLoop.condition.position)
return listOf(IAstModification.ReplaceNode(untilLoop.condition, booleanExpr, untilLoop))
}
return noModifications
}
override fun after(whileLoop: WhileLoop, parent: Node): Iterable<IAstModification> {
val binExpr = whileLoop.condition as? BinaryExpression
if(binExpr==null || binExpr.operator !in comparisonOperators) {
// while x -> while x!=0, while x+5 -> while x+5 != 0
val booleanExpr = BinaryExpression(whileLoop.condition, "!=", NumericLiteralValue.optimalInteger(0, whileLoop.condition.position), whileLoop.condition.position)
return listOf(IAstModification.ReplaceNode(whileLoop.condition, booleanExpr, whileLoop))
}
return noModifications
}
} }

View File

@ -27,7 +27,8 @@ data class CompilationOptions(val output: OutputType,
val launcher: LauncherType, val launcher: LauncherType,
val zeropage: ZeropageType, val zeropage: ZeropageType,
val zpReserved: List<IntRange>, val zpReserved: List<IntRange>,
val floats: Boolean) val floats: Boolean,
val noSysInit: Boolean)
class CompilerException(message: String?) : Exception(message) class CompilerException(message: String?) : Exception(message)

View File

@ -7,6 +7,7 @@ import prog8.ast.statements.Directive
import prog8.compiler.target.C64Target import prog8.compiler.target.C64Target
import prog8.compiler.target.CompilationTarget import prog8.compiler.target.CompilationTarget
import prog8.compiler.target.Cx16Target import prog8.compiler.target.Cx16Target
import prog8.optimizer.*
import prog8.optimizer.UnusedCodeRemover import prog8.optimizer.UnusedCodeRemover
import prog8.optimizer.constantFold import prog8.optimizer.constantFold
import prog8.optimizer.optimizeStatements import prog8.optimizer.optimizeStatements
@ -125,6 +126,7 @@ private fun determineCompilationOptions(program: Program): CompilationOptions {
as? Directive)?.args?.single()?.name?.toUpperCase() as? Directive)?.args?.single()?.name?.toUpperCase()
val allOptions = program.modules.flatMap { it.statements }.filter { it is Directive && it.directive == "%option" }.flatMap { (it as Directive).args }.toSet() val allOptions = program.modules.flatMap { it.statements }.filter { it is Directive && it.directive == "%option" }.flatMap { (it as Directive).args }.toSet()
val floatsEnabled = allOptions.any { it.name == "enable_floats" } val floatsEnabled = allOptions.any { it.name == "enable_floats" }
val noSysInit = allOptions.any { it.name == "no_sysinit" }
var zpType: ZeropageType = var zpType: ZeropageType =
if (zpoption == null) if (zpoption == null)
if(floatsEnabled) ZeropageType.FLOATSAFE else ZeropageType.KERNALSAFE if(floatsEnabled) ZeropageType.FLOATSAFE else ZeropageType.KERNALSAFE
@ -160,7 +162,7 @@ private fun determineCompilationOptions(program: Program): CompilationOptions {
return CompilationOptions( return CompilationOptions(
if (outputType == null) OutputType.PRG else OutputType.valueOf(outputType), if (outputType == null) OutputType.PRG else OutputType.valueOf(outputType),
if (launcherType == null) LauncherType.BASIC else LauncherType.valueOf(launcherType), if (launcherType == null) LauncherType.BASIC else LauncherType.valueOf(launcherType),
zpType, zpReserved, floatsEnabled zpType, zpReserved, floatsEnabled, noSysInit
) )
} }
@ -188,13 +190,13 @@ private fun optimizeAst(programAst: Program, errors: ErrorReporter) {
// keep optimizing expressions and statements until no more steps remain // keep optimizing expressions and statements until no more steps remain
val optsDone1 = programAst.simplifyExpressions() val optsDone1 = programAst.simplifyExpressions()
val optsDone2 = programAst.optimizeStatements(errors) val optsDone2 = programAst.optimizeStatements(errors)
programAst.constantFold(errors) // because simplified statements and expressions could give rise to more constants that can be folded away programAst.constantFold(errors) // because simplified statements and expressions can result in more constants that can be folded away
errors.handle() errors.handle()
if (optsDone1 + optsDone2 == 0) if (optsDone1 + optsDone2 == 0)
break break
} }
val remover = UnusedCodeRemover(errors) val remover = UnusedCodeRemover(programAst, errors)
remover.visit(programAst) remover.visit(programAst)
remover.applyModifications() remover.applyModifications()
errors.handle() errors.handle()

View File

@ -70,12 +70,4 @@ abstract class Zeropage(protected val options: CompilationOptions) {
private fun loneByte(address: Int) = address in free && address-1 !in free && address+1 !in free private fun loneByte(address: Int) = address in free && address-1 !in free && address+1 !in free
private fun sequentialFree(address: Int, size: Int) = free.containsAll((address until address+size).toList()) private fun sequentialFree(address: Int, size: Int) = free.containsAll((address until address+size).toList())
enum class ExitProgramStrategy {
CLEAN_EXIT,
SYSTEM_RESET
}
abstract val exitProgramStrategy: ExitProgramStrategy
} }

View File

@ -17,8 +17,6 @@ internal interface CompilationTarget {
fun encodeString(str: String, altEncoding: Boolean): List<Short> fun encodeString(str: String, altEncoding: Boolean): List<Short>
fun decodeString(bytes: List<Short>, altEncoding: Boolean): String fun decodeString(bytes: List<Short>, altEncoding: Boolean): String
fun asmGenerator(program: Program, errors: ErrorReporter, zp: Zeropage, options: CompilationOptions, path: Path): IAssemblyGenerator fun asmGenerator(program: Program, errors: ErrorReporter, zp: Zeropage, options: CompilationOptions, path: Path): IAssemblyGenerator
val initProcName: String?
val resetProcName: String?
companion object { companion object {
lateinit var instance: CompilationTarget lateinit var instance: CompilationTarget
@ -35,8 +33,6 @@ internal object C64Target: CompilationTarget {
if(altEncoding) Petscii.decodeScreencode(bytes, true) else Petscii.decodePetscii(bytes, true) if(altEncoding) Petscii.decodeScreencode(bytes, true) else Petscii.decodePetscii(bytes, true)
override fun asmGenerator(program: Program, errors: ErrorReporter, zp: Zeropage, options: CompilationOptions, path: Path) = override fun asmGenerator(program: Program, errors: ErrorReporter, zp: Zeropage, options: CompilationOptions, path: Path) =
AsmGen(program, errors, zp, options, path) AsmGen(program, errors, zp, options, path)
override val initProcName = "c64.init_system"
override val resetProcName = "c64.reset_system"
} }
internal object Cx16Target: CompilationTarget { internal object Cx16Target: CompilationTarget {
@ -48,6 +44,4 @@ internal object Cx16Target: CompilationTarget {
if(altEncoding) Petscii.decodeScreencode(bytes, true) else Petscii.decodePetscii(bytes, true) if(altEncoding) Petscii.decodeScreencode(bytes, true) else Petscii.decodePetscii(bytes, true)
override fun asmGenerator(program: Program, errors: ErrorReporter, zp: Zeropage, options: CompilationOptions, path: Path) = override fun asmGenerator(program: Program, errors: ErrorReporter, zp: Zeropage, options: CompilationOptions, path: Path) =
AsmGen(program, errors, zp, options, path) AsmGen(program, errors, zp, options, path)
override val initProcName = "cx16.init_system"
override val resetProcName = "cx16.reset_system"
} }

View File

@ -35,4 +35,5 @@ internal interface IMachineDefinition {
fun getFloatRomConst(number: Double): String? fun getFloatRomConst(number: Double): String?
fun importLibs(compilerOptions: CompilationOptions, importer: ModuleImporter, program: Program) fun importLibs(compilerOptions: CompilationOptions, importer: ModuleImporter, program: Program)
fun launchEmulator(programName: String) fun launchEmulator(programName: String)
fun isRegularRAMaddress(address: Int): Boolean
} }

View File

@ -37,7 +37,8 @@ internal object C64MachineDefinition: IMachineDefinition {
val mflpt5 = Mflpt5.fromNumber(number) val mflpt5 = Mflpt5.fromNumber(number)
val floatbytes = shortArrayOf(mflpt5.b0, mflpt5.b1, mflpt5.b2, mflpt5.b3, mflpt5.b4) val floatbytes = shortArrayOf(mflpt5.b0, mflpt5.b1, mflpt5.b2, mflpt5.b3, mflpt5.b4)
when { when {
floatbytes.contentEquals(shortArrayOf(0x00, 0x00, 0x00, 0x00, 0x00)) -> return "floats.ZERO" // not a ROM const floatbytes.contentEquals(shortArrayOf(0x00, 0x00, 0x00, 0x00, 0x00)) -> return "floats.FL_ZERO_const" // not a ROM const
floatbytes.contentEquals(shortArrayOf(0x81, 0x00, 0x00, 0x00, 0x00)) -> return "floats.FL_ONE_const" // not a ROM const
floatbytes.contentEquals(shortArrayOf(0x82, 0x49, 0x0f, 0xda, 0xa1)) -> return "floats.FL_PIVAL" floatbytes.contentEquals(shortArrayOf(0x82, 0x49, 0x0f, 0xda, 0xa1)) -> return "floats.FL_PIVAL"
floatbytes.contentEquals(shortArrayOf(0x90, 0x80, 0x00, 0x00, 0x00)) -> return "floats.FL_N32768" floatbytes.contentEquals(shortArrayOf(0x90, 0x80, 0x00, 0x00, 0x00)) -> return "floats.FL_N32768"
floatbytes.contentEquals(shortArrayOf(0x81, 0x00, 0x00, 0x00, 0x00)) -> return "floats.FL_FONE" floatbytes.contentEquals(shortArrayOf(0x81, 0x00, 0x00, 0x00, 0x00)) -> return "floats.FL_FONE"
@ -88,6 +89,8 @@ internal object C64MachineDefinition: IMachineDefinition {
} }
} }
override fun isRegularRAMaddress(address: Int): Boolean = address<0xa000 || address in 0xc000..0xcfff
override fun initializeZeropage(compilerOptions: CompilationOptions) { override fun initializeZeropage(compilerOptions: CompilationOptions) {
zeropage = C64Zeropage(compilerOptions) zeropage = C64Zeropage(compilerOptions)
} }
@ -112,12 +115,6 @@ internal object C64MachineDefinition: IMachineDefinition {
override val SCRATCH_W2 = 0xfd // temp storage 2 for a word $fb+$fc override val SCRATCH_W2 = 0xfd // temp storage 2 for a word $fb+$fc
override val exitProgramStrategy: ExitProgramStrategy = when (options.zeropage) {
ZeropageType.BASICSAFE, ZeropageType.DONTUSE -> ExitProgramStrategy.CLEAN_EXIT
ZeropageType.FLOATSAFE, ZeropageType.KERNALSAFE, ZeropageType.FULL -> ExitProgramStrategy.SYSTEM_RESET
}
init { init {
if (options.floats && options.zeropage !in setOf(ZeropageType.FLOATSAFE, ZeropageType.BASICSAFE, ZeropageType.DONTUSE )) if (options.floats && options.zeropage !in setOf(ZeropageType.FLOATSAFE, ZeropageType.BASICSAFE, ZeropageType.DONTUSE ))
throw CompilerException("when floats are enabled, zero page type should be 'floatsafe' or 'basicsafe' or 'dontuse'") throw CompilerException("when floats are enabled, zero page type should be 'floatsafe' or 'basicsafe' or 'dontuse'")

View File

@ -47,10 +47,10 @@ internal class AsmGen(private val program: Program,
private val forloopsAsmGen = ForLoopsAsmGen(program, this) private val forloopsAsmGen = ForLoopsAsmGen(program, this)
private val postincrdecrAsmGen = PostIncrDecrAsmGen(program, this) private val postincrdecrAsmGen = PostIncrDecrAsmGen(program, this)
private val functioncallAsmGen = FunctionCallAsmGen(program, this) private val functioncallAsmGen = FunctionCallAsmGen(program, this)
private val assignmentAsmGen = AssignmentAsmGen(program, this)
private val expressionsAsmGen = ExpressionsAsmGen(program, this) private val expressionsAsmGen = ExpressionsAsmGen(program, this)
private val assignmentAsmGen = AssignmentAsmGen(program, this, expressionsAsmGen)
internal val loopEndLabels = ArrayDeque<String>() internal val loopEndLabels = ArrayDeque<String>()
internal val blockLevelVarInits = mutableMapOf<Block, MutableSet<VarDecl>>() private val blockLevelVarInits = mutableMapOf<Block, MutableSet<VarDecl>>()
override fun compileToAssembly(optimize: Boolean): IAssemblyProgram { override fun compileToAssembly(optimize: Boolean): IAssemblyProgram {
assemblyLines.clear() assemblyLines.clear()
@ -81,6 +81,7 @@ internal class AsmGen(private val program: Program,
return AssemblyProgram(program.name, outputDir) return AssemblyProgram(program.name, outputDir)
} }
private fun header() { private fun header() {
val ourName = this.javaClass.name val ourName = this.javaClass.name
val cpu = when(CompilationTarget.instance.machine.cpu) { val cpu = when(CompilationTarget.instance.machine.cpu) {
@ -120,18 +121,14 @@ internal class AsmGen(private val program: Program,
out(" .null $9e, format(' %d ', _prog8_entrypoint), $3a, $8f, ' prog8 by idj'") out(" .null $9e, format(' %d ', _prog8_entrypoint), $3a, $8f, ' prog8 by idj'")
out("+\t.word 0") out("+\t.word 0")
out("_prog8_entrypoint\t; assembly code starts here\n") out("_prog8_entrypoint\t; assembly code starts here\n")
out(" tsx") if(!options.noSysInit)
out(" stx prog8_lib.orig_stackpointer") out(" jsr ${CompilationTarget.instance.name}.init_system")
if(!CompilationTarget.instance.initProcName.isNullOrEmpty())
out(" jsr ${CompilationTarget.instance.initProcName}")
} }
options.output == OutputType.PRG -> { options.output == OutputType.PRG -> {
out("; ---- program without basic sys call ----") out("; ---- program without basic sys call ----")
out("* = ${program.actualLoadAddress.toHex()}\n") out("* = ${program.actualLoadAddress.toHex()}\n")
out(" tsx") if(!options.noSysInit)
out(" stx prog8_lib.orig_stackpointer") out(" jsr ${CompilationTarget.instance.name}.init_system")
if(!CompilationTarget.instance.initProcName.isNullOrEmpty())
out(" jsr ${CompilationTarget.instance.initProcName}")
} }
options.output == OutputType.RAW -> { options.output == OutputType.RAW -> {
out("; ---- raw assembler program ----") out("; ---- raw assembler program ----")
@ -139,33 +136,16 @@ internal class AsmGen(private val program: Program,
} }
} }
if (zeropage.exitProgramStrategy != Zeropage.ExitProgramStrategy.CLEAN_EXIT) { if(options.zeropage !in setOf(ZeropageType.BASICSAFE, ZeropageType.DONTUSE)) {
// disable shift-commodore charset switching and run/stop key out("""
out(" lda #$80") ; zeropage is clobbered so we need to reset the machine at exit
out(" lda #$80") lda #>${CompilationTarget.instance.name}.reset_system
out(" sta 657\t; disable charset switching") pha
out(" lda #239") lda #<${CompilationTarget.instance.name}.reset_system
out(" sta 808\t; disable run/stop key") pha""")
} }
out(" ldx #\$ff\t; init estack pointer") out(" jmp main.start ; start program / force start proc to be included")
out(" ; initialize the variables in each block that has globals")
program.allBlocks().forEach {
if(it.statements.filterIsInstance<VarDecl>().any { vd->vd.value!=null && vd.type==VarDeclType.VAR && vd.datatype in NumericDatatypes})
out(" jsr ${it.name}.prog8_init_vars")
}
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 ${CompilationTarget.instance.resetProcName}")
}
}
} }
private fun footer() { private fun footer() {
@ -426,10 +406,17 @@ internal class AsmGen(private val program: Program,
"$"+number.toString(16).padStart(2, '0') "$"+number.toString(16).padStart(2, '0')
} }
DataType.ARRAY_UW -> array.map { DataType.ARRAY_UW -> array.map {
if(it is NumericLiteralValue) { when (it) {
"$" + it.number.toInt().toString(16).padStart(4, '0') is NumericLiteralValue -> {
} else { "$" + it.number.toInt().toString(16).padStart(4, '0')
(it as AddressOf).identifier.nameInSource.joinToString(".") }
is AddressOf -> {
it.identifier.firstStructVarName(program.namespace) ?: asmSymbolName(it.identifier)
}
is IdentifierReference -> {
it.firstStructVarName(program.namespace) ?: asmSymbolName(it)
}
else -> throw AssemblyError("weird array elt dt")
} }
} }
else -> throw AssemblyError("invalid arraysize type") else -> throw AssemblyError("invalid arraysize type")
@ -561,26 +548,11 @@ internal class AsmGen(private val program: Program,
CpuRegister.A -> out(" pha") CpuRegister.A -> out(" pha")
CpuRegister.X -> { CpuRegister.X -> {
if (CompilationTarget.instance.machine.cpu == CpuType.CPU65c02) out(" phx") if (CompilationTarget.instance.machine.cpu == CpuType.CPU65c02) out(" phx")
else { else out(" stx _prog8_regsave${register.name}")
val save = makeLabel("saveX")
saveRegisterLabels.push(save)
out("""
stx $save
jmp +
$save .byte 0
+""")
}
} }
CpuRegister.Y -> { CpuRegister.Y -> {
if (CompilationTarget.instance.machine.cpu == CpuType.CPU65c02) out(" phy") if (CompilationTarget.instance.machine.cpu == CpuType.CPU65c02) out(" phy")
else { else out(" sty _prog8_regsave${register.name}")
val save = makeLabel("saveY")
out("""
sty $save
jmp +
$save .byte 0
+""")
}
} }
} }
} }
@ -590,17 +562,11 @@ $save .byte 0
CpuRegister.A -> out(" pla") CpuRegister.A -> out(" pla")
CpuRegister.X -> { CpuRegister.X -> {
if (CompilationTarget.instance.machine.cpu == CpuType.CPU65c02) out(" plx") if (CompilationTarget.instance.machine.cpu == CpuType.CPU65c02) out(" plx")
else { else out(" ldx _prog8_regsave${register.name}")
val save = saveRegisterLabels.pop()
out(" ldx $save")
}
} }
CpuRegister.Y -> { CpuRegister.Y -> {
if (CompilationTarget.instance.machine.cpu == CpuType.CPU65c02) out(" ply") if (CompilationTarget.instance.machine.cpu == CpuType.CPU65c02) out(" ply")
else { else out(" ldy _prog8_regsave${register.name}")
val save = saveRegisterLabels.pop()
out(" ldy $save")
}
} }
} }
} }
@ -619,15 +585,15 @@ $save .byte 0
is FunctionCallStatement -> { is FunctionCallStatement -> {
val functionName = stmt.target.nameInSource.last() val functionName = stmt.target.nameInSource.last()
val builtinFunc = BuiltinFunctions[functionName] val builtinFunc = BuiltinFunctions[functionName]
if(builtinFunc!=null) { if (builtinFunc != null) {
builtinFunctionsAsmGen.translateFunctioncallStatement(stmt, builtinFunc) builtinFunctionsAsmGen.translateFunctioncallStatement(stmt, builtinFunc)
} else { } else {
functioncallAsmGen.translateFunctionCall(stmt) functioncallAsmGen.translateFunctionCall(stmt)
// discard any results from the stack: // discard any results from the stack:
val sub = stmt.target.targetSubroutine(program.namespace)!! val sub = stmt.target.targetSubroutine(program.namespace)!!
val returns = sub.returntypes.zip(sub.asmReturnvaluesRegisters) val returns = sub.returntypes.zip(sub.asmReturnvaluesRegisters)
for((t, reg) in returns) { for ((t, reg) in returns) {
if(reg.stack) { if (reg.stack) {
if (t in IntegerDatatypes || t in PassByReferenceDatatypes) out(" inx") if (t in IntegerDatatypes || t in PassByReferenceDatatypes) out(" inx")
else if (t == DataType.FLOAT) out(" inx | inx | inx") else if (t == DataType.FLOAT) out(" inx | inx | inx")
} }
@ -752,10 +718,40 @@ $save .byte 0
} }
else { else {
expressionsAsmGen.translateExpression(index) expressionsAsmGen.translateExpression(index)
when(register) { when(elementDt) {
CpuRegister.A -> out(" inx | lda P8ESTACK_LO,x") in ByteDatatypes -> {
CpuRegister.X -> out(" inx | lda P8ESTACK_LO,x | tax") when (register) {
CpuRegister.Y -> out(" inx | ldy P8ESTACK_LO,x") CpuRegister.A -> out(" inx | lda P8ESTACK_LO,x")
CpuRegister.X -> out(" inx | lda P8ESTACK_LO,x | tax")
CpuRegister.Y -> out(" inx | ldy P8ESTACK_LO,x")
}
}
in WordDatatypes -> {
out("""
inx
lda P8ESTACK_LO,x
asl a""")
when (register) {
CpuRegister.A -> {}
CpuRegister.X -> out(" tax")
CpuRegister.Y -> out(" tay")
}
}
DataType.FLOAT -> {
require(DataType.FLOAT.memorySize()==5)
out("""
inx
lda P8ESTACK_LO,x
asl a
asl a
clc
adc P8ESTACK_LO,x""")
when (register) {
CpuRegister.A -> {}
CpuRegister.X -> out(" tax")
CpuRegister.Y -> out(" tay")
}
}
} }
} }
} }
@ -790,9 +786,30 @@ $save .byte 0
out("${sub.name}\t.proc") out("${sub.name}\t.proc")
zeropagevars2asm(sub.statements) zeropagevars2asm(sub.statements)
memdefs2asm(sub.statements) memdefs2asm(sub.statements)
// the main.start subroutine is the program's entrypoint and should perform some initialization logic
if(sub.name=="start" && sub.definingBlock().name=="main") {
out("; program startup initialization")
out(" cld")
program.allBlocks().forEach {
if(it.statements.filterIsInstance<VarDecl>().any { vd->vd.value!=null && vd.type==VarDeclType.VAR && vd.datatype in NumericDatatypes})
out(" jsr ${it.name}.prog8_init_vars")
}
out("""
tsx
stx prog8_lib.orig_stackpointer ; required for func_exit
ldx #255 ; init estack ptr
clv
clc""")
}
out("; statements") out("; statements")
sub.statements.forEach{ translate(it) } sub.statements.forEach{ translate(it) }
out("; variables") out("; variables")
out("""
; register saves
_prog8_regsaveX .byte 0
_prog8_regsaveY .byte 0""") // TODO only generate these bytes if they're actually used by saveRegister()
vardecls2asm(sub.statements) vardecls2asm(sub.statements)
out(" .pend\n") out(" .pend\n")
} }
@ -824,47 +841,32 @@ $save .byte 0
} }
private fun translate(stmt: IfStatement) { private fun translate(stmt: IfStatement) {
when { checkBooleanExpression(stmt.condition) // we require the condition to be of the form 'x <comparison> <value>'
stmt.elsepart.containsNoCodeNorVars() -> { val booleanCondition = stmt.condition as BinaryExpression
// empty else
expressionsAsmGen.translateExpression(stmt.condition) if (stmt.elsepart.containsNoCodeNorVars()) {
translateTestStack(stmt.condition.inferType(program).typeOrElse(DataType.STRUCT)) // empty else
val endLabel = makeLabel("if_end") val endLabel = makeLabel("if_end")
out(" beq $endLabel") expressionsAsmGen.translateComparisonExpressionWithJumpIfFalse(booleanCondition, endLabel)
translate(stmt.truepart) translate(stmt.truepart)
out(endLabel) out(endLabel)
} }
stmt.truepart.containsNoCodeNorVars() -> { else {
// empty true part // both true and else parts
expressionsAsmGen.translateExpression(stmt.condition) val elseLabel = makeLabel("if_else")
translateTestStack(stmt.condition.inferType(program).typeOrElse(DataType.STRUCT)) val endLabel = makeLabel("if_end")
val endLabel = makeLabel("if_end") expressionsAsmGen.translateComparisonExpressionWithJumpIfFalse(booleanCondition, elseLabel)
out(" bne $endLabel") translate(stmt.truepart)
translate(stmt.elsepart) out(" jmp $endLabel")
out(endLabel) out(elseLabel)
} translate(stmt.elsepart)
else -> { out(endLabel)
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) { private fun checkBooleanExpression(condition: Expression) {
when(dataType) { if(condition !is BinaryExpression || condition.operator !in comparisonOperators)
in ByteDatatypes -> out(" inx | lda P8ESTACK_LO,x") throw AssemblyError("expected boolean expression $condition")
in WordDatatypes -> out(" inx | lda P8ESTACK_LO,x | ora P8ESTACK_HI,x")
DataType.FLOAT -> throw AssemblyError("conditional value should be an integer (boolean)")
else -> throw AssemblyError("non-numerical dt")
}
} }
private fun translate(stmt: RepeatLoop) { private fun translate(stmt: RepeatLoop) {
@ -983,25 +985,13 @@ $counterVar .byte 0""")
} }
private fun translate(stmt: WhileLoop) { private fun translate(stmt: WhileLoop) {
checkBooleanExpression(stmt.condition) // we require the condition to be of the form 'x <comparison> <value>'
val booleanCondition = stmt.condition as BinaryExpression
val whileLabel = makeLabel("while") val whileLabel = makeLabel("while")
val endLabel = makeLabel("whileend") val endLabel = makeLabel("whileend")
loopEndLabels.push(endLabel) loopEndLabels.push(endLabel)
out(whileLabel) out(whileLabel)
expressionsAsmGen.translateExpression(stmt.condition) expressionsAsmGen.translateComparisonExpressionWithJumpIfFalse(booleanCondition, endLabel)
val conditionDt = stmt.condition.inferType(program)
if(!conditionDt.isKnown)
throw AssemblyError("unknown condition dt")
if(conditionDt.typeOrElse(DataType.BYTE) in ByteDatatypes) {
out(" inx | lda P8ESTACK_LO,x | beq $endLabel")
} else {
out("""
inx
lda P8ESTACK_LO,x
bne +
lda P8ESTACK_HI,x
beq $endLabel
+ """)
}
translate(stmt.body) translate(stmt.body)
out(" jmp $whileLabel") out(" jmp $whileLabel")
out(endLabel) out(endLabel)
@ -1009,26 +999,14 @@ $counterVar .byte 0""")
} }
private fun translate(stmt: UntilLoop) { private fun translate(stmt: UntilLoop) {
checkBooleanExpression(stmt.condition) // we require the condition to be of the form 'x <comparison> <value>'
val booleanCondition = stmt.condition as BinaryExpression
val repeatLabel = makeLabel("repeat") val repeatLabel = makeLabel("repeat")
val endLabel = makeLabel("repeatend") val endLabel = makeLabel("repeatend")
loopEndLabels.push(endLabel) loopEndLabels.push(endLabel)
out(repeatLabel) out(repeatLabel)
translate(stmt.body) translate(stmt.body)
expressionsAsmGen.translateExpression(stmt.untilCondition) expressionsAsmGen.translateComparisonExpressionWithJumpIfFalse(booleanCondition, repeatLabel)
val conditionDt = stmt.untilCondition.inferType(program)
if(!conditionDt.isKnown)
throw AssemblyError("unknown condition dt")
if(conditionDt.typeOrElse(DataType.BYTE) in ByteDatatypes) {
out(" inx | lda P8ESTACK_LO,x | beq $repeatLabel")
} else {
out("""
inx
lda P8ESTACK_LO,x
bne +
lda P8ESTACK_HI,x
beq $repeatLabel
+ """)
}
out(endLabel) out(endLabel)
loopEndLabels.pop() loopEndLabels.pop()
} }
@ -1068,6 +1046,7 @@ $counterVar .byte 0""")
} }
} }
} }
out(" jmp $endLabel")
for(choiceBlock in choiceBlocks) { for(choiceBlock in choiceBlocks) {
out(choiceBlock.first) out(choiceBlock.first)
translate(choiceBlock.second) translate(choiceBlock.second)

View File

@ -179,6 +179,7 @@ private fun optimizeSameAssignments(linesByFourteen: List<List<IndexedValue<Stri
} }
private fun optimizeStoreLoadSame(linesByFour: List<List<IndexedValue<String>>>): List<Modification> { private fun optimizeStoreLoadSame(linesByFour: List<List<IndexedValue<String>>>): List<Modification> {
// TODO not sure if this is correct in all situations....:
// sta X + lda X, sty X + ldy X, stx X + ldx X -> the second instruction can be eliminated // sta X + lda X, sty X + ldy X, stx X + ldx X -> the second instruction can be eliminated
val mods = mutableListOf<Modification>() val mods = mutableListOf<Modification>()
for (pair in linesByFour) { for (pair in linesByFour) {

View File

@ -19,7 +19,7 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
is ArrayIndexedExpression -> translateExpression(expression) is ArrayIndexedExpression -> translateExpression(expression)
is TypecastExpression -> translateExpression(expression) is TypecastExpression -> translateExpression(expression)
is AddressOf -> translateExpression(expression) is AddressOf -> translateExpression(expression)
is DirectMemoryRead -> translateExpression(expression) is DirectMemoryRead -> translateDirectMemReadExpression(expression, true)
is NumericLiteralValue -> translateExpression(expression) is NumericLiteralValue -> translateExpression(expression)
is IdentifierReference -> translateExpression(expression) is IdentifierReference -> translateExpression(expression)
is FunctionCall -> translateExpression(expression) is FunctionCall -> translateExpression(expression)
@ -28,6 +28,920 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
} }
} }
internal fun translateComparisonExpressionWithJumpIfFalse(expr: BinaryExpression, jumpIfFalseLabel: String) {
// first, if it is of the form: <constvalue> <comparison> X , swap the operands around
var left = expr.left
var right = expr.right
var operator = expr.operator
var leftConstVal = left.constValue(program)
var rightConstVal = right.constValue(program)
if(leftConstVal!=null) {
val tmp = left
left = right
right = tmp
val tmp2 = leftConstVal
leftConstVal = rightConstVal
rightConstVal = tmp2
when(expr.operator) {
"<" -> operator = ">"
"<=" -> operator = ">="
">" -> operator = "<"
">=" -> operator = "<="
}
}
val dt = left.inferType(program).typeOrElse(DataType.STRUCT)
when (operator) {
"==" -> {
// if the left operand is an expression, and the right is 0, we can just evaluate that expression.
// (the extra comparison is not required as the result of the expression is already the required boolean value)
if(rightConstVal?.number?.toDouble() == 0.0) {
when(left) {
is PrefixExpression,
is BinaryExpression,
is ArrayIndexedExpression,
is TypecastExpression,
is AddressOf,
is RangeExpr,
is FunctionCall -> {
translateExpression(left)
if(dt in ByteDatatypes) {
asmgen.out("""
inx
lda P8ESTACK_LO,x
bne $jumpIfFalseLabel""")
return
}
else if(dt in WordDatatypes) {
asmgen.out("""
inx
lda P8ESTACK_LO,x
clc
adc P8ESTACK_HI,x
bne $jumpIfFalseLabel""")
return
}
}
else -> {}
}
}
when (dt) {
in ByteDatatypes -> translateByteEquals(left, right, leftConstVal, rightConstVal, jumpIfFalseLabel)
in WordDatatypes -> translateWordEquals(left, right, leftConstVal, rightConstVal, jumpIfFalseLabel)
DataType.FLOAT -> translateFloatEquals(left, right, leftConstVal, rightConstVal, jumpIfFalseLabel)
else -> throw AssemblyError("weird operand datatype")
}
}
"!=" -> {
// if the left operand is an expression, and the right is 0, we can just evaluate that expression.
// (the extra comparison is not required as the result of the expression is already the required boolean value)
if(rightConstVal?.number?.toDouble() == 0.0) {
when(left) {
is PrefixExpression,
is BinaryExpression,
is ArrayIndexedExpression,
is TypecastExpression,
is AddressOf,
is RangeExpr,
is FunctionCall -> {
translateExpression(left)
if(dt in ByteDatatypes) {
asmgen.out("""
inx
lda P8ESTACK_LO,x
beq $jumpIfFalseLabel""")
return
}
else if(dt in WordDatatypes) {
asmgen.out("""
inx
lda P8ESTACK_LO,x
clc
adc P8ESTACK_HI,x
beq $jumpIfFalseLabel""")
return
}
}
else -> {}
}
}
when (dt) {
in ByteDatatypes -> translateByteNotEquals(left, right, leftConstVal, rightConstVal, jumpIfFalseLabel)
in WordDatatypes -> translateWordNotEquals(left, right, leftConstVal, rightConstVal, jumpIfFalseLabel)
DataType.FLOAT -> translateFloatNotEquals(left, right, leftConstVal, rightConstVal, jumpIfFalseLabel)
else -> throw AssemblyError("weird operand datatype")
}
}
"<" -> {
when(dt) {
DataType.UBYTE -> translateUbyteLess(left, right, leftConstVal,rightConstVal, jumpIfFalseLabel)
DataType.BYTE -> translateByteLess(left, right, leftConstVal,rightConstVal, jumpIfFalseLabel)
DataType.UWORD -> translateUwordLess(left, right, leftConstVal,rightConstVal, jumpIfFalseLabel)
DataType.WORD -> translateWordLess(left, right, leftConstVal,rightConstVal, jumpIfFalseLabel)
DataType.FLOAT -> {
translateExpression(left)
translateExpression(right)
asmgen.out(" jsr floats.less_f | inx | lda P8ESTACK_LO,x | beq $jumpIfFalseLabel")
}
else -> throw AssemblyError("weird operand datatype")
}
}
"<=" -> {
when(dt) {
DataType.UBYTE -> translateUbyteLessOrEqual(left, right, leftConstVal,rightConstVal, jumpIfFalseLabel)
DataType.BYTE -> translateByteLessOrEqual(left, right, leftConstVal,rightConstVal, jumpIfFalseLabel)
DataType.UWORD -> translateUwordLessOrEqual(left, right, leftConstVal,rightConstVal, jumpIfFalseLabel)
DataType.WORD -> translateWordLessOrEqual(left, right, leftConstVal,rightConstVal, jumpIfFalseLabel)
DataType.FLOAT -> {
translateExpression(left)
translateExpression(right)
asmgen.out(" jsr floats.lesseq_f | inx | lda P8ESTACK_LO,x | beq $jumpIfFalseLabel")
}
else -> throw AssemblyError("weird operand datatype")
}
}
">" -> {
when(dt) {
DataType.UBYTE -> translateUbyteGreater(left, right, leftConstVal,rightConstVal, jumpIfFalseLabel)
DataType.BYTE -> translateByteGreater(left, right, leftConstVal,rightConstVal, jumpIfFalseLabel)
DataType.UWORD -> translateUwordGreater(left, right, leftConstVal,rightConstVal, jumpIfFalseLabel)
DataType.WORD -> translateWordGreater(left, right, leftConstVal,rightConstVal, jumpIfFalseLabel)
DataType.FLOAT -> {
translateExpression(left)
translateExpression(right)
asmgen.out(" jsr floats.greater_f | inx | lda P8ESTACK_LO,x | beq $jumpIfFalseLabel")
}
else -> throw AssemblyError("weird operand datatype")
}
}
">=" -> {
when(dt) {
DataType.UBYTE -> translateUbyteGreaterOrEqual(left, right, leftConstVal,rightConstVal, jumpIfFalseLabel)
DataType.BYTE -> translateByteGreaterOrEqual(left, right, leftConstVal,rightConstVal, jumpIfFalseLabel)
DataType.UWORD -> translateUwordGreaterOrEqual(left, right, leftConstVal,rightConstVal, jumpIfFalseLabel)
DataType.WORD -> translateWordGreaterOrEqual(left, right, leftConstVal,rightConstVal, jumpIfFalseLabel)
DataType.FLOAT -> {
translateExpression(left)
translateExpression(right)
asmgen.out(" jsr floats.greatereq_f | inx | lda P8ESTACK_LO,x | beq $jumpIfFalseLabel")
}
else -> throw AssemblyError("weird operand datatype")
}
}
}
}
private fun translateUbyteLess(left: Expression, right: Expression, leftConstVal: NumericLiteralValue?, rightConstVal: NumericLiteralValue?, jumpIfFalseLabel: String) {
if(rightConstVal!=null) {
if(leftConstVal!=null) {
if(rightConstVal>=leftConstVal)
asmgen.out(" jmp $jumpIfFalseLabel")
return
} else {
if (left is IdentifierReference) {
val name = asmgen.asmVariableName(left)
if(rightConstVal.number.toInt()!=0)
asmgen.out("""
lda $name
cmp #${rightConstVal.number}
bcs $jumpIfFalseLabel""")
else
asmgen.out(" jmp $jumpIfFalseLabel")
return
}
else if (left is DirectMemoryRead) {
if(rightConstVal.number.toInt()!=0) {
translateDirectMemReadExpression(left, false)
asmgen.out(" cmp #${rightConstVal.number} | bcs $jumpIfFalseLabel")
}
else
asmgen.out(" jmp $jumpIfFalseLabel")
return
}
}
}
asmgen.translateExpression(left)
asmgen.translateExpression(right)
asmgen.out(" jsr prog8_lib.less_ub | inx | lda P8ESTACK_LO,x | beq $jumpIfFalseLabel")
}
private fun translateByteLess(left: Expression, right: Expression, leftConstVal: NumericLiteralValue?, rightConstVal: NumericLiteralValue?, jumpIfFalseLabel: String) {
if(rightConstVal!=null) {
if(leftConstVal!=null) {
if(rightConstVal>=leftConstVal)
asmgen.out(" jmp $jumpIfFalseLabel")
return
} else {
if (left is IdentifierReference) {
val name = asmgen.asmVariableName(left)
if(rightConstVal.number.toInt()!=0)
asmgen.out("""
lda $name
sec
sbc #${rightConstVal.number}
bvc +
eor #$80
+ bpl $jumpIfFalseLabel""")
else
asmgen.out(" lda $name | bpl $jumpIfFalseLabel")
return
}
}
}
asmgen.translateExpression(left)
asmgen.translateExpression(right)
asmgen.out(" jsr prog8_lib.less_b | inx | lda P8ESTACK_LO,x | beq $jumpIfFalseLabel")
}
private fun translateUwordLess(left: Expression, right: Expression, leftConstVal: NumericLiteralValue?, rightConstVal: NumericLiteralValue?, jumpIfFalseLabel: String) {
if(rightConstVal!=null) {
if(leftConstVal!=null) {
if(rightConstVal>=leftConstVal)
asmgen.out(" jmp $jumpIfFalseLabel")
return
} else {
if (left is IdentifierReference) {
val name = asmgen.asmVariableName(left)
if(rightConstVal.number.toInt()!=0)
asmgen.out("""
lda $name+1
cmp #>${rightConstVal.number}
bcc +
bne $jumpIfFalseLabel
lda $name
cmp #<${rightConstVal.number}
bcs $jumpIfFalseLabel
+""")
else
asmgen.out(" jmp $jumpIfFalseLabel")
return
}
}
}
asmgen.translateExpression(left)
asmgen.translateExpression(right)
asmgen.out(" jsr prog8_lib.less_uw | inx | lda P8ESTACK_LO,x | beq $jumpIfFalseLabel")
}
private fun translateWordLess(left: Expression, right: Expression, leftConstVal: NumericLiteralValue?, rightConstVal: NumericLiteralValue?, jumpIfFalseLabel: String) {
if(rightConstVal!=null) {
if(leftConstVal!=null) {
if(rightConstVal>=leftConstVal)
asmgen.out(" jmp $jumpIfFalseLabel")
return
} else {
if (left is IdentifierReference) {
val name = asmgen.asmVariableName(left)
if(rightConstVal.number.toInt()!=0)
asmgen.out("""
lda $name
cmp #<${rightConstVal.number}
lda $name+1
sbc #>${rightConstVal.number}
bvc +
eor #$80
+ bpl $jumpIfFalseLabel""")
else
asmgen.out(" lda $name+1 | bpl $jumpIfFalseLabel")
return
}
}
}
asmgen.translateExpression(left)
asmgen.translateExpression(right)
asmgen.out(" jsr prog8_lib.less_w | inx | lda P8ESTACK_LO,x | beq $jumpIfFalseLabel")
}
private fun translateUbyteGreater(left: Expression, right: Expression, leftConstVal: NumericLiteralValue?, rightConstVal: NumericLiteralValue?, jumpIfFalseLabel: String) {
if(rightConstVal!=null) {
if(leftConstVal!=null) {
if(rightConstVal<=leftConstVal)
asmgen.out(" jmp $jumpIfFalseLabel")
return
} else {
if (left is IdentifierReference) {
val name = asmgen.asmVariableName(left)
if(rightConstVal.number.toInt()!=0)
asmgen.out("""
lda $name
cmp #${rightConstVal.number}
bcc $jumpIfFalseLabel
beq $jumpIfFalseLabel""")
else
asmgen.out(" lda $name | beq $jumpIfFalseLabel")
return
}
else if (left is DirectMemoryRead) {
translateDirectMemReadExpression(left, false)
if(rightConstVal.number.toInt()!=0)
asmgen.out(" cmp #${rightConstVal.number} | bcc $jumpIfFalseLabel | beq $jumpIfFalseLabel")
else
asmgen.out(" beq $jumpIfFalseLabel")
return
}
}
}
asmgen.translateExpression(left)
asmgen.translateExpression(right)
asmgen.out(" jsr prog8_lib.greater_ub | inx | lda P8ESTACK_LO,x | beq $jumpIfFalseLabel")
}
private fun translateByteGreater(left: Expression, right: Expression, leftConstVal: NumericLiteralValue?, rightConstVal: NumericLiteralValue?, jumpIfFalseLabel: String) {
if(rightConstVal!=null) {
if(leftConstVal!=null) {
if(rightConstVal<=leftConstVal)
asmgen.out(" jmp $jumpIfFalseLabel")
return
} else {
if (left is IdentifierReference) {
val name = asmgen.asmVariableName(left)
if(rightConstVal.number.toInt()!=0)
asmgen.out("""
lda $name
clc
sbc #${rightConstVal.number}
bvc +
eor #$80
+ bpl +
bmi $jumpIfFalseLabel
+""")
else
asmgen.out(" lda $name | bmi $jumpIfFalseLabel | beq $jumpIfFalseLabel")
return
}
}
}
asmgen.translateExpression(left)
asmgen.translateExpression(right)
asmgen.out(" jsr prog8_lib.greater_b | inx | lda P8ESTACK_LO,x | beq $jumpIfFalseLabel")
}
private fun translateUwordGreater(left: Expression, right: Expression, leftConstVal: NumericLiteralValue?, rightConstVal: NumericLiteralValue?, jumpIfFalseLabel: String) {
if(rightConstVal!=null) {
if(leftConstVal!=null) {
if(rightConstVal<=leftConstVal)
asmgen.out(" jmp $jumpIfFalseLabel")
return
} else {
if (left is IdentifierReference) {
val name = asmgen.asmVariableName(left)
if(rightConstVal.number.toInt()!=0)
asmgen.out("""
lda $name+1
cmp #>${rightConstVal.number}
bcc $jumpIfFalseLabel
bne +
lda $name
cmp #<${rightConstVal.number}
bcc $jumpIfFalseLabel
beq $jumpIfFalseLabel
+""")
else
asmgen.out("""
lda $name
ora $name+1
beq $jumpIfFalseLabel""")
return
}
}
}
asmgen.translateExpression(left)
asmgen.translateExpression(right)
asmgen.out(" jsr prog8_lib.greater_uw | inx | lda P8ESTACK_LO,x | beq $jumpIfFalseLabel")
}
private fun translateWordGreater(left: Expression, right: Expression, leftConstVal: NumericLiteralValue?, rightConstVal: NumericLiteralValue?, jumpIfFalseLabel: String) {
if(rightConstVal!=null) {
if(leftConstVal!=null) {
if(rightConstVal<=leftConstVal)
asmgen.out(" jmp $jumpIfFalseLabel")
return
} else {
if (left is IdentifierReference) {
val name = asmgen.asmVariableName(left)
if(rightConstVal.number.toInt()!=0)
asmgen.out("""
lda #<${rightConstVal.number}
cmp $name
lda #>${rightConstVal.number}
sbc $name+1
bvc +
eor #$80
+ bpl $jumpIfFalseLabel""")
else
asmgen.out("""
lda #0
cmp $name
sbc $name+1
bvc +
eor #$80
+ bpl $jumpIfFalseLabel""")
return
}
}
}
asmgen.translateExpression(left)
asmgen.translateExpression(right)
asmgen.out(" jsr prog8_lib.greater_w | inx | lda P8ESTACK_LO,x | beq $jumpIfFalseLabel")
}
private fun translateUbyteLessOrEqual(left: Expression, right: Expression, leftConstVal: NumericLiteralValue?, rightConstVal: NumericLiteralValue?, jumpIfFalseLabel: String) {
if(rightConstVal!=null) {
if(leftConstVal!=null) {
if(rightConstVal>leftConstVal)
asmgen.out(" jmp $jumpIfFalseLabel")
return
} else {
if (left is IdentifierReference) {
val name = asmgen.asmVariableName(left)
if(rightConstVal.number.toInt()!=0)
asmgen.out("""
lda $name
cmp #${rightConstVal.number}
beq +
bcs $jumpIfFalseLabel
+""")
else
asmgen.out("""
lda $name
bne $jumpIfFalseLabel""")
return
}
else if (left is DirectMemoryRead) {
translateDirectMemReadExpression(left, false)
if(rightConstVal.number.toInt()!=0)
asmgen.out("""
cmp #${rightConstVal.number}
beq +
bcs $jumpIfFalseLabel
+""")
else
asmgen.out(" bne $jumpIfFalseLabel")
return
}
}
}
asmgen.translateExpression(left)
asmgen.translateExpression(right)
asmgen.out(" jsr prog8_lib.lesseq_ub | inx | lda P8ESTACK_LO,x | beq $jumpIfFalseLabel")
}
private fun translateByteLessOrEqual(left: Expression, right: Expression, leftConstVal: NumericLiteralValue?, rightConstVal: NumericLiteralValue?, jumpIfFalseLabel: String) {
if(rightConstVal!=null) {
if(leftConstVal!=null) {
if(rightConstVal>leftConstVal)
asmgen.out(" jmp $jumpIfFalseLabel")
return
} else {
if (left is IdentifierReference) {
val name = asmgen.asmVariableName(left)
if(rightConstVal.number.toInt()!=0)
asmgen.out("""
lda $name
clc
sbc #${rightConstVal.number}
bvc +
eor #$80
+ bpl $jumpIfFalseLabel""")
else
asmgen.out("""
lda $name
beq +
bpl $jumpIfFalseLabel
+""")
return
}
}
}
asmgen.translateExpression(left)
asmgen.translateExpression(right)
asmgen.out(" jsr prog8_lib.lesseq_b | inx | lda P8ESTACK_LO,x | beq $jumpIfFalseLabel")
}
private fun translateUwordLessOrEqual(left: Expression, right: Expression, leftConstVal: NumericLiteralValue?, rightConstVal: NumericLiteralValue?, jumpIfFalseLabel: String) {
if(rightConstVal!=null) {
if(leftConstVal!=null) {
if(rightConstVal>leftConstVal)
asmgen.out(" jmp $jumpIfFalseLabel")
return
} else {
if (left is IdentifierReference) {
val name = asmgen.asmVariableName(left)
if(rightConstVal.number.toInt()!=0)
asmgen.out("""
lda #>${rightConstVal.number}
cmp $name+1
bcc $jumpIfFalseLabel
bne +
lda #<${rightConstVal.number}
cmp $name
bcc $jumpIfFalseLabel
+""")
else
asmgen.out("""
lda $name
ora $name+1
bne $jumpIfFalseLabel""")
return
}
}
}
asmgen.translateExpression(left)
asmgen.translateExpression(right)
asmgen.out(" jsr prog8_lib.lesseq_uw | inx | lda P8ESTACK_LO,x | beq $jumpIfFalseLabel")
}
private fun translateWordLessOrEqual(left: Expression, right: Expression, leftConstVal: NumericLiteralValue?, rightConstVal: NumericLiteralValue?, jumpIfFalseLabel: String) {
if(rightConstVal!=null) {
if(leftConstVal!=null) {
if(rightConstVal>leftConstVal)
asmgen.out(" jmp $jumpIfFalseLabel")
return
} else {
if (left is IdentifierReference) {
val name = asmgen.asmVariableName(left)
if(rightConstVal.number.toInt()!=0)
asmgen.out("""
lda #<${rightConstVal.number}
cmp $name
lda #>${rightConstVal.number}
sbc $name+1
bvc +
eor #$80
+ bmi $jumpIfFalseLabel""")
else
asmgen.out("""
lda #0
cmp $name
sbc $name+1
bvc +
eor #$80
+ bmi $jumpIfFalseLabel""")
return
}
}
}
asmgen.translateExpression(left)
asmgen.translateExpression(right)
asmgen.out(" jsr prog8_lib.lesseq_w | inx | lda P8ESTACK_LO,x | beq $jumpIfFalseLabel")
}
private fun translateUbyteGreaterOrEqual(left: Expression, right: Expression, leftConstVal: NumericLiteralValue?, rightConstVal: NumericLiteralValue?, jumpIfFalseLabel: String) {
if(rightConstVal!=null) {
if(leftConstVal!=null) {
if(rightConstVal<leftConstVal)
asmgen.out(" jmp $jumpIfFalseLabel")
return
} else {
if (left is IdentifierReference) {
val name = asmgen.asmVariableName(left)
if(rightConstVal.number.toInt()!=0)
asmgen.out("""
lda $name
cmp #${rightConstVal.number}
bcc $jumpIfFalseLabel""")
return
}
else if (left is DirectMemoryRead) {
translateDirectMemReadExpression(left, false)
if(rightConstVal.number.toInt()!=0)
asmgen.out(" cmp #${rightConstVal.number} | bcc $jumpIfFalseLabel")
return
}
}
}
asmgen.translateExpression(left)
asmgen.translateExpression(right)
asmgen.out(" jsr prog8_lib.greatereq_ub | inx | lda P8ESTACK_LO,x | beq $jumpIfFalseLabel")
}
private fun translateByteGreaterOrEqual(left: Expression, right: Expression, leftConstVal: NumericLiteralValue?, rightConstVal: NumericLiteralValue?, jumpIfFalseLabel: String) {
if(rightConstVal!=null) {
if(leftConstVal!=null) {
if(rightConstVal<leftConstVal)
asmgen.out(" jmp $jumpIfFalseLabel")
return
} else {
if (left is IdentifierReference) {
val name = asmgen.asmVariableName(left)
if(rightConstVal.number.toInt()!=0) {
asmgen.out("""
lda $name
sec
sbc #${rightConstVal.number}
bvc +
eor #$80
+ bmi $jumpIfFalseLabel""")
return
}
else {
asmgen.out(" lda $name | bmi $jumpIfFalseLabel")
return
}
}
}
}
asmgen.translateExpression(left)
asmgen.translateExpression(right)
asmgen.out(" jsr prog8_lib.greatereq_b | inx | lda P8ESTACK_LO,x | beq $jumpIfFalseLabel")
}
private fun translateUwordGreaterOrEqual(left: Expression, right: Expression, leftConstVal: NumericLiteralValue?, rightConstVal: NumericLiteralValue?, jumpIfFalseLabel: String) {
if(rightConstVal!=null) {
if(leftConstVal!=null) {
if(rightConstVal<leftConstVal)
asmgen.out(" jmp $jumpIfFalseLabel")
return
} else {
if (left is IdentifierReference) {
val name = asmgen.asmVariableName(left)
if(rightConstVal.number.toInt()!=0)
asmgen.out("""
lda $name+1
cmp #>${rightConstVal.number}
bcc $jumpIfFalseLabel
bne +
lda $name
cmp #<${rightConstVal.number}
bcc $jumpIfFalseLabel
+""")
return
}
}
}
asmgen.translateExpression(left)
asmgen.translateExpression(right)
asmgen.out(" jsr prog8_lib.greatereq_uw | inx | lda P8ESTACK_LO,x | beq $jumpIfFalseLabel")
}
private fun translateWordGreaterOrEqual(left: Expression, right: Expression, leftConstVal: NumericLiteralValue?, rightConstVal: NumericLiteralValue?, jumpIfFalseLabel: String) {
if(rightConstVal!=null) {
if(leftConstVal!=null) {
if(rightConstVal<leftConstVal)
asmgen.out(" jmp $jumpIfFalseLabel")
return
} else {
if (left is IdentifierReference) {
val name = asmgen.asmVariableName(left)
if(rightConstVal.number.toInt()!=0)
asmgen.out("""
lda $name
cmp #<${rightConstVal.number}
lda $name+1
sbc #>${rightConstVal.number}
bvc +
eor #$80
+ bmi $jumpIfFalseLabel""")
else {
asmgen.out(" lda $name+1 | bmi $jumpIfFalseLabel")
}
return
}
}
}
asmgen.translateExpression(left)
asmgen.translateExpression(right)
asmgen.out(" jsr prog8_lib.greatereq_w | inx | lda P8ESTACK_LO,x | beq $jumpIfFalseLabel")
}
private fun translateByteEquals(left: Expression, right: Expression, leftConstVal: NumericLiteralValue?, rightConstVal: NumericLiteralValue?, jumpIfFalseLabel: String) {
if(rightConstVal!=null) {
if(leftConstVal!=null) {
if(rightConstVal!=leftConstVal)
asmgen.out(" jmp $jumpIfFalseLabel")
return
} else {
if (left is IdentifierReference) {
val name = asmgen.asmVariableName(left)
if(rightConstVal.number.toInt()!=0)
asmgen.out(" lda $name | cmp #${rightConstVal.number} | bne $jumpIfFalseLabel")
else
asmgen.out(" lda $name | bne $jumpIfFalseLabel")
return
}
else if (left is DirectMemoryRead) {
translateDirectMemReadExpression(left, false)
if(rightConstVal.number.toInt()!=0)
asmgen.out(" cmp #${rightConstVal.number} | bne $jumpIfFalseLabel")
else
asmgen.out(" bne $jumpIfFalseLabel")
return
}
}
}
asmgen.translateExpression(left)
asmgen.translateExpression(right)
asmgen.out(" jsr prog8_lib.equal_b | inx | lda P8ESTACK_LO,x | beq $jumpIfFalseLabel")
}
private fun translateByteNotEquals(left: Expression, right: Expression, leftConstVal: NumericLiteralValue?, rightConstVal: NumericLiteralValue?, jumpIfFalseLabel: String) {
if(rightConstVal!=null) {
if(leftConstVal!=null) {
if(rightConstVal==leftConstVal)
asmgen.out(" jmp $jumpIfFalseLabel")
return
} else {
if (left is IdentifierReference) {
val name = asmgen.asmVariableName(left)
if(rightConstVal.number.toInt()!=0)
asmgen.out(" lda $name | cmp #${rightConstVal.number} | beq $jumpIfFalseLabel")
else
asmgen.out(" lda $name | beq $jumpIfFalseLabel")
return
}
else if (left is DirectMemoryRead) {
translateDirectMemReadExpression(left, false)
if(rightConstVal.number.toInt()!=0)
asmgen.out(" cmp #${rightConstVal.number} | beq $jumpIfFalseLabel")
else
asmgen.out(" beq $jumpIfFalseLabel")
return
}
}
}
asmgen.translateExpression(left)
asmgen.translateExpression(right)
asmgen.out(" jsr prog8_lib.notequal_b | inx | lda P8ESTACK_LO,x | beq $jumpIfFalseLabel")
}
private fun translateWordEquals(left: Expression, right: Expression, leftConstVal: NumericLiteralValue?, rightConstVal: NumericLiteralValue?, jumpIfFalseLabel: String) {
if(rightConstVal!=null) {
if(leftConstVal!=null) {
if(rightConstVal!=leftConstVal)
asmgen.out(" jmp $jumpIfFalseLabel")
return
} else {
if (left is IdentifierReference) {
val name = asmgen.asmVariableName(left)
if(rightConstVal.number.toInt()!=0)
asmgen.out("""
lda $name
cmp #<${rightConstVal.number}
bne $jumpIfFalseLabel
lda $name+1
cmp #>${rightConstVal.number}
bne $jumpIfFalseLabel""")
else
asmgen.out("""
lda $name
bne $jumpIfFalseLabel
lda $name+1
bne $jumpIfFalseLabel""")
return
}
}
}
asmgen.translateExpression(left)
asmgen.translateExpression(right)
asmgen.out(" jsr prog8_lib.equal_w | inx | lda P8ESTACK_LO,x | beq $jumpIfFalseLabel")
}
private fun translateWordNotEquals(left: Expression, right: Expression, leftConstVal: NumericLiteralValue?, rightConstVal: NumericLiteralValue?, jumpIfFalseLabel: String) {
if(rightConstVal!=null) {
if(leftConstVal!=null) {
if(rightConstVal==leftConstVal)
asmgen.out(" jmp $jumpIfFalseLabel")
return
} else {
if (left is IdentifierReference) {
val name = asmgen.asmVariableName(left)
if(rightConstVal.number.toInt()!=0)
asmgen.out("""
lda $name
cmp #<${rightConstVal.number}
bne +
lda $name+1
cmp #>${rightConstVal.number}
beq $jumpIfFalseLabel
+""")
else
asmgen.out("""
lda $name
bne +
lda $name+1
beq $jumpIfFalseLabel
+""")
return
}
}
}
asmgen.translateExpression(left)
asmgen.translateExpression(right)
asmgen.out(" jsr prog8_lib.notequal_w | inx | lda P8ESTACK_LO,x | beq $jumpIfFalseLabel")
}
private fun translateFloatEquals(left: Expression, right: Expression, leftConstVal: NumericLiteralValue?, rightConstVal: NumericLiteralValue?, jumpIfFalseLabel: String) {
if(rightConstVal!=null) {
if(leftConstVal!=null) {
if(rightConstVal!=leftConstVal)
asmgen.out(" jmp $jumpIfFalseLabel")
return
} else {
if (left is IdentifierReference) {
val name = asmgen.asmVariableName(left)
when(rightConstVal.number.toDouble())
{
0.0 -> {
asmgen.out("""
lda $name
clc
adc $name+1
adc $name+2
adc $name+3
adc $name+4
bne $jumpIfFalseLabel""")
return
}
1.0 -> {
asmgen.out("""
lda $name
cmp #129
bne $jumpIfFalseLabel
lda $name+1
clc
adc $name+2
adc $name+3
adc $name+4
bne $jumpIfFalseLabel""")
return
}
}
}
}
}
translateExpression(left)
translateExpression(right)
asmgen.out(" jsr floats.equal_f | inx | lda P8ESTACK_LO,x | beq $jumpIfFalseLabel")
}
private fun translateFloatNotEquals(left: Expression, right: Expression, leftConstVal: NumericLiteralValue?, rightConstVal: NumericLiteralValue?, jumpIfFalseLabel: String) {
if(rightConstVal!=null) {
if(leftConstVal!=null) {
if(rightConstVal==leftConstVal)
asmgen.out(" jmp $jumpIfFalseLabel")
return
} else {
if (left is IdentifierReference) {
val name = asmgen.asmVariableName(left)
when(rightConstVal.number.toDouble())
{
0.0 -> {
asmgen.out("""
lda $name
clc
adc $name+1
adc $name+2
adc $name+3
adc $name+4
beq $jumpIfFalseLabel""")
return
}
1.0 -> {
asmgen.out("""
lda $name
cmp #129
bne +
lda $name+1
clc
adc $name+2
adc $name+3
adc $name+4
beq $jumpIfFalseLabel
+""")
return
}
}
}
}
}
translateExpression(left)
translateExpression(right)
asmgen.out(" jsr floats.notequal_f | inx | lda P8ESTACK_LO,x | beq $jumpIfFalseLabel")
}
private fun translateExpression(expression: FunctionCall) { private fun translateExpression(expression: FunctionCall) {
val functionName = expression.target.nameInSource.last() val functionName = expression.target.nameInSource.last()
val builtinFunc = BuiltinFunctions[functionName] val builtinFunc = BuiltinFunctions[functionName]
@ -100,7 +1014,7 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
DataType.UBYTE, DataType.BYTE -> {} DataType.UBYTE, DataType.BYTE -> {}
DataType.UWORD, DataType.WORD -> { DataType.UWORD, DataType.WORD -> {
// sign extend // sign extend
asmgen.out(""" asmgen.out("""
lda P8ESTACK_LO+1,x lda P8ESTACK_LO+1,x
ora #$7f ora #$7f
bmi + bmi +
@ -151,21 +1065,27 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
asmgen.out(" lda #<$name | sta P8ESTACK_LO,x | lda #>$name | sta P8ESTACK_HI,x | dex") asmgen.out(" lda #<$name | sta P8ESTACK_LO,x | lda #>$name | sta P8ESTACK_HI,x | dex")
} }
private fun translateExpression(expr: DirectMemoryRead) { internal fun translateDirectMemReadExpression(expr: DirectMemoryRead, pushResultOnEstack: Boolean) {
when(expr.addressExpression) { when(expr.addressExpression) {
is NumericLiteralValue -> { is NumericLiteralValue -> {
val address = (expr.addressExpression as NumericLiteralValue).number.toInt() val address = (expr.addressExpression as NumericLiteralValue).number.toInt()
asmgen.out(" lda ${address.toHex()} | sta P8ESTACK_LO,x | dex") asmgen.out(" lda ${address.toHex()}")
if(pushResultOnEstack)
asmgen.out(" sta P8ESTACK_LO,x | dex")
} }
is IdentifierReference -> { is IdentifierReference -> {
// the identifier is a pointer variable, so read the value from the address in it // the identifier is a pointer variable, so read the value from the address in it
asmgen.loadByteFromPointerIntoA(expr.addressExpression as IdentifierReference) asmgen.loadByteFromPointerIntoA(expr.addressExpression as IdentifierReference)
asmgen.out(" sta P8ESTACK_LO,x | dex") if(pushResultOnEstack)
asmgen.out(" sta P8ESTACK_LO,x | dex")
} }
else -> { else -> {
translateExpression(expr.addressExpression) translateExpression(expr.addressExpression)
asmgen.out(" jsr prog8_lib.read_byte_from_address_on_stack") asmgen.out(" jsr prog8_lib.read_byte_from_address_on_stack")
asmgen.out(" sta P8ESTACK_LO+1,x") if(pushResultOnEstack)
asmgen.out(" sta P8ESTACK_LO+1,x")
else
asmgen.out(" php | inx | plp")
} }
} }
} }
@ -500,8 +1420,7 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
"<=" -> 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.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(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.equal_w")
"!=" -> asmgen.out(" jsr prog8_lib.notequal_w") "!=" -> asmgen.out(" jsr prog8_lib.notequal_w") "&" -> asmgen.out(" jsr prog8_lib.bitand_w")
"&" -> asmgen.out(" jsr prog8_lib.bitand_w")
"^" -> asmgen.out(" jsr prog8_lib.bitxor_w") "^" -> asmgen.out(" jsr prog8_lib.bitxor_w")
"|" -> asmgen.out(" jsr prog8_lib.bitor_w") "|" -> asmgen.out(" jsr prog8_lib.bitor_w")
"and" -> asmgen.out(" jsr prog8_lib.and_w") "and" -> asmgen.out(" jsr prog8_lib.and_w")

View File

@ -41,7 +41,7 @@ internal class AsmAssignTarget(val kind: TargetStorageKind,
{ {
val constMemoryAddress by lazy { memory?.addressExpression?.constValue(program)?.number?.toInt() ?: 0} val constMemoryAddress by lazy { memory?.addressExpression?.constValue(program)?.number?.toInt() ?: 0}
val constArrayIndexValue by lazy { array?.arrayspec?.constIndex() } val constArrayIndexValue by lazy { array?.arrayspec?.constIndex() }
val vardecl by lazy { variable?.targetVarDecl(program.namespace)!! } val vardecl by lazy { variable!!.targetVarDecl(program.namespace)!! }
val asmVarname by lazy { val asmVarname by lazy {
if(variable!=null) if(variable!=null)
asmgen.asmVariableName(variable) asmgen.asmVariableName(variable)

View File

@ -8,12 +8,13 @@ import prog8.compiler.AssemblyError
import prog8.compiler.target.CompilationTarget import prog8.compiler.target.CompilationTarget
import prog8.compiler.target.CpuType import prog8.compiler.target.CpuType
import prog8.compiler.target.c64.codegen.AsmGen import prog8.compiler.target.c64.codegen.AsmGen
import prog8.compiler.target.c64.codegen.ExpressionsAsmGen
import prog8.compiler.toHex import prog8.compiler.toHex
internal class AssignmentAsmGen(private val program: Program, private val asmgen: AsmGen) { internal class AssignmentAsmGen(private val program: Program, private val asmgen: AsmGen, private val exprAsmgen: ExpressionsAsmGen) {
private val augmentableAsmGen = AugmentableAssignmentAsmGen(program, this, asmgen) private val augmentableAsmGen = AugmentableAssignmentAsmGen(program, this, exprAsmgen, asmgen)
fun translate(assignment: Assignment) { fun translate(assignment: Assignment) {
val target = AsmAssignTarget.fromAstAssignment(assignment, program, asmgen) val target = AsmAssignTarget.fromAstAssignment(assignment, program, asmgen)
@ -319,18 +320,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
} }
private fun assignAddressOf(target: AsmAssignTarget, name: IdentifierReference) { private fun assignAddressOf(target: AsmAssignTarget, name: IdentifierReference) {
val struct = name.memberOfStruct(program.namespace) val sourceName = name.firstStructVarName(program.namespace) ?: asmgen.fixNameSymbols(name.nameInSource.joinToString("."))
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(target.kind) { when(target.kind) {
TargetStorageKind.VARIABLE -> { TargetStorageKind.VARIABLE -> {

View File

@ -7,11 +7,13 @@ import prog8.compiler.AssemblyError
import prog8.compiler.target.CompilationTarget import prog8.compiler.target.CompilationTarget
import prog8.compiler.target.CpuType import prog8.compiler.target.CpuType
import prog8.compiler.target.c64.codegen.AsmGen import prog8.compiler.target.c64.codegen.AsmGen
import prog8.compiler.target.c64.codegen.ExpressionsAsmGen
import prog8.compiler.toHex import prog8.compiler.toHex
import kotlin.math.absoluteValue import kotlin.math.absoluteValue
internal class AugmentableAssignmentAsmGen(private val program: Program, internal class AugmentableAssignmentAsmGen(private val program: Program,
private val assignmentAsmGen: AssignmentAsmGen, private val assignmentAsmGen: AssignmentAsmGen,
private val exprAsmGen: ExpressionsAsmGen,
private val asmgen: AsmGen) { private val asmgen: AsmGen) {
fun translate(assign: AsmAssignment) { fun translate(assign: AsmAssignment) {
require(assign.isAugmentable) require(assign.isAugmentable)
@ -104,6 +106,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
private fun inplaceModification(target: AsmAssignTarget, operator: String, value: Expression) { private fun inplaceModification(target: AsmAssignTarget, operator: String, value: Expression) {
val valueLv = (value as? NumericLiteralValue)?.number val valueLv = (value as? NumericLiteralValue)?.number
val ident = value as? IdentifierReference val ident = value as? IdentifierReference
val memread = value as? DirectMemoryRead
when(target.kind) { when(target.kind) {
TargetStorageKind.VARIABLE -> { TargetStorageKind.VARIABLE -> {
@ -112,7 +115,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
when { when {
valueLv != null -> inplaceModification_byte_litval_to_variable(target.asmVarname, target.datatype, operator, valueLv.toInt()) valueLv != null -> inplaceModification_byte_litval_to_variable(target.asmVarname, target.datatype, operator, valueLv.toInt())
ident != null -> inplaceModification_byte_variable_to_variable(target.asmVarname, target.datatype, operator, ident) ident != null -> inplaceModification_byte_variable_to_variable(target.asmVarname, target.datatype, operator, ident)
// TODO more specialized code for types such as memory read etc. memread != null -> inplaceModification_byte_memread_to_variable(target.asmVarname, target.datatype, operator, memread)
value is TypecastExpression -> { value is TypecastExpression -> {
if (tryRemoveRedundantCast(value, target, operator)) return if (tryRemoveRedundantCast(value, target, operator)) return
inplaceModification_byte_value_to_variable(target.asmVarname, target.datatype, operator, value) inplaceModification_byte_value_to_variable(target.asmVarname, target.datatype, operator, value)
@ -124,18 +127,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
when { when {
valueLv != null -> inplaceModification_word_litval_to_variable(target.asmVarname, target.datatype, operator, valueLv.toInt()) valueLv != null -> inplaceModification_word_litval_to_variable(target.asmVarname, target.datatype, operator, valueLv.toInt())
ident != null -> inplaceModification_word_variable_to_variable(target.asmVarname, target.datatype, operator, ident) ident != null -> inplaceModification_word_variable_to_variable(target.asmVarname, target.datatype, operator, ident)
// TODO more specialized code for types such as memory read etc. memread != null -> inplaceModification_word_memread_to_variable(target.asmVarname, target.datatype, operator, memread)
// value is DirectMemoryRead -> {
// println("warning: slow stack evaluation used (8): $name $operator= ${value::class.simpleName} at ${value.position}")
// // assignmentAsmGen.translateOtherAssignment(origAssign)
// asmgen.translateExpression(value.addressExpression)
// asmgen.out("""
// jsr prog8_lib.read_byte_from_address_on_stack
// sta ...
// inx
// """)
// inplaceModification_word_value_to_variable(name, operator, )
// }
value is TypecastExpression -> { value is TypecastExpression -> {
if (tryRemoveRedundantCast(value, target, operator)) return if (tryRemoveRedundantCast(value, target, operator)) return
inplaceModification_word_value_to_variable(target.asmVarname, target.datatype, operator, value) inplaceModification_word_value_to_variable(target.asmVarname, target.datatype, operator, value)
@ -147,7 +139,6 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
when { when {
valueLv != null -> inplaceModification_float_litval_to_variable(target.asmVarname, operator, valueLv.toDouble()) valueLv != null -> inplaceModification_float_litval_to_variable(target.asmVarname, operator, valueLv.toDouble())
ident != null -> inplaceModification_float_variable_to_variable(target.asmVarname, operator, ident) ident != null -> inplaceModification_float_variable_to_variable(target.asmVarname, operator, ident)
// TODO more specialized code for types such as memory read etc.
value is TypecastExpression -> { value is TypecastExpression -> {
if (tryRemoveRedundantCast(value, target, operator)) return if (tryRemoveRedundantCast(value, target, operator)) return
inplaceModification_float_value_to_variable(target.asmVarname, operator, value) inplaceModification_float_value_to_variable(target.asmVarname, operator, value)
@ -196,7 +187,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
when { when {
valueLv != null -> inplaceModification_byte_litval_to_variable(zp.SCRATCH_B1.toHex(), DataType.UBYTE, operator, valueLv.toInt()) valueLv != null -> inplaceModification_byte_litval_to_variable(zp.SCRATCH_B1.toHex(), DataType.UBYTE, operator, valueLv.toInt())
ident != null -> inplaceModification_byte_variable_to_variable(zp.SCRATCH_B1.toHex(), DataType.UBYTE, operator, ident) ident != null -> inplaceModification_byte_variable_to_variable(zp.SCRATCH_B1.toHex(), DataType.UBYTE, operator, ident)
// TODO more specialized code for types such as memory read etc. memread != null -> inplaceModification_byte_memread_to_variable(zp.SCRATCH_B1.toHex(), DataType.UBYTE, operator, memread)
value is TypecastExpression -> { value is TypecastExpression -> {
if (tryRemoveRedundantCast(value, target, operator)) return if (tryRemoveRedundantCast(value, target, operator)) return
inplaceModification_byte_value_to_variable(zp.SCRATCH_B1.toHex(), DataType.UBYTE, operator, value) inplaceModification_byte_value_to_variable(zp.SCRATCH_B1.toHex(), DataType.UBYTE, operator, value)
@ -636,20 +627,77 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
} }
} }
private fun inplaceModification_byte_memread_to_variable(name: String, dt: DataType, operator: String, memread: DirectMemoryRead) {
when(operator) {
"+" -> {
exprAsmGen.translateDirectMemReadExpression(memread, false)
asmgen.out("""
clc
adc $name
sta $name""")
}
"-" -> {
exprAsmGen.translateDirectMemReadExpression(memread, false)
asmgen.out("""
sta P8ZP_SCRATCH_B1
lda $name
sec
sbc P8ZP_SCRATCH_B1
sta $name""")
// TODO: more operators
}
else -> {
inplaceModification_byte_value_to_variable(name, dt, operator, memread);
}
}
}
private fun inplaceModification_word_memread_to_variable(name: String, dt: DataType, operator: String, memread: DirectMemoryRead) {
when(operator) {
"+" -> {
exprAsmGen.translateDirectMemReadExpression(memread, false)
asmgen.out("""
clc
adc $name
sta $name
bcc +
inc $name+1
+""")
}
"-" -> {
exprAsmGen.translateDirectMemReadExpression(memread, false)
asmgen.out("""
sta P8ZP_SCRATCH_B1
lda $name
sec
sbc P8ZP_SCRATCH_B1
sta $name
bcc +
dec $name+1
+""")
// TODO: more operators
}
else -> {
inplaceModification_word_value_to_variable(name, dt, operator, memread);
}
}
}
private fun inplaceModification_word_litval_to_variable(name: String, dt: DataType, operator: String, value: Int) { private fun inplaceModification_word_litval_to_variable(name: String, dt: DataType, operator: String, value: Int) {
when (operator) { when (operator) {
// note: ** (power) operator requires floats. // note: ** (power) operator requires floats.
// TODO use these + and - optimizations in the expressionAsmGenerator as well. // TODO use these + and - optimizations in the expressionAsmGenerator as well.
"+" -> { "+" -> {
when { when {
value<0x0100 -> asmgen.out(""" value==0 -> {}
value in 1..0xff -> asmgen.out("""
lda $name lda $name
clc clc
adc #$value adc #$value
sta $name sta $name
bcc + bcc +
inc $name+1 inc $name+1
+ """) +""")
value==0x0100 -> asmgen.out(" inc $name+1") value==0x0100 -> asmgen.out(" inc $name+1")
value==0x0200 -> asmgen.out(" inc $name+1 | inc $name+1") value==0x0200 -> asmgen.out(" inc $name+1 | inc $name+1")
value==0x0300 -> asmgen.out(" inc $name+1 | inc $name+1 | inc $name+1") value==0x0300 -> asmgen.out(" inc $name+1 | inc $name+1 | inc $name+1")
@ -666,14 +714,15 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
} }
"-" -> { "-" -> {
when { when {
value<0x0100 -> asmgen.out(""" value==0 -> {}
value in 1..0xff -> asmgen.out("""
lda $name lda $name
sec sec
sbc #$value sbc #$value
sta $name sta $name
bcs + bcs +
dec $name+1 dec $name+1
+ """) +""")
value==0x0100 -> asmgen.out(" dec $name+1") value==0x0100 -> asmgen.out(" dec $name+1")
value==0x0200 -> asmgen.out(" dec $name+1 | dec $name+1") value==0x0200 -> asmgen.out(" dec $name+1 | dec $name+1")
value==0x0300 -> asmgen.out(" dec $name+1 | dec $name+1 | dec $name+1") value==0x0300 -> asmgen.out(" dec $name+1 | dec $name+1 | dec $name+1")
@ -840,22 +889,54 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
// the other variable is a BYTE type so optimize for that // the other variable is a BYTE type so optimize for that
when (operator) { when (operator) {
// note: ** (power) operator requires floats. // note: ** (power) operator requires floats.
"+" -> asmgen.out(""" "+" -> {
lda $name if(valueDt==DataType.UBYTE)
clc asmgen.out("""
adc $otherName lda $name
sta $name clc
bcc + adc $otherName
inc $name+1 sta $name
+ """) bcc +
"-" -> asmgen.out(""" inc $name+1
lda $name +""")
sec else
sbc $otherName asmgen.out("""
sta $name ldy #0
bcs + lda $otherName
dec $name+1 bpl +
+ """) dey ; sign extend
+ clc
adc $name
sta $name
tya
adc $name+1
sta $name+1""")
}
"-" -> {
if(valueDt==DataType.UBYTE)
asmgen.out("""
lda $name
sec
sbc $otherName
sta $name
bcs +
dec $name+1
+""")
else
asmgen.out("""
ldy #0
lda $otherName
bpl +
dey ; sign extend
+ sty P8ZP_SCRATCH_B1
lda $name
sec
sbc $otherName
sta $name
lda $name+1
sbc P8ZP_SCRATCH_B1
sta $name+1""")
}
"*" -> { "*" -> {
asmgen.out(""" asmgen.out("""
lda $otherName lda $otherName
@ -870,8 +951,8 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
lda math.multiply_words.result+1 lda math.multiply_words.result+1
sta $name+1""") sta $name+1""")
} }
"/" -> TODO("div wordvar/bytevar") "/" -> TODO("div (u)wordvar/bytevar")
"%" -> TODO("word remainder bytevar") "%" -> TODO("(u)word remainder bytevar")
"<<" -> { "<<" -> {
asmgen.out(""" asmgen.out("""
ldy $otherName ldy $otherName
@ -899,9 +980,9 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
bne -""") bne -""")
} }
} }
"&" -> TODO("bitand wordvar bytevar") "&" -> TODO("bitand (u)wordvar bytevar")
"^" -> TODO("bitxor wordvar bytevar") "^" -> TODO("bitxor (u)wordvar bytevar")
"|" -> TODO("bitor wordvar bytevar") "|" -> TODO("bitor (u)wordvar bytevar")
else -> throw AssemblyError("invalid operator for in-place modification $operator") else -> throw AssemblyError("invalid operator for in-place modification $operator")
} }
} }
@ -993,28 +1074,60 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
when(valueDt) { when(valueDt) {
in ByteDatatypes -> { in ByteDatatypes -> {
// the other variable is a BYTE type so optimize for that TODO does this even occur? // the other variable is a BYTE type so optimize for that
when (operator) { when (operator) {
// note: ** (power) operator requires floats. // note: ** (power) operator requires floats.
"+" -> asmgen.out(""" "+" -> {
lda $name if(valueDt==DataType.UBYTE)
clc asmgen.out("""
adc P8ESTACK_LO+1,x lda $name
sta $name clc
bcc + adc P8ESTACK_LO+1,x
inc $name+1 sta $name
+ """) bcc +
"-" -> asmgen.out(""" inc $name+1
lda $name +""")
sec else
sbc P8ESTACK_LO+1,x asmgen.out("""
sta $name ldy #0
bcs + lda P8ESTACK_LO+1,x
dec $name+1 bpl +
+ """) dey ; sign extend
"*" -> TODO("mul word byte") + clc
"/" -> TODO("div word byte") adc $name
"%" -> TODO("word remainder byte") sta $name
tya
adc $name+1
sta $name+1""")
}
"-" -> {
if(valueDt==DataType.UBYTE)
asmgen.out("""
lda $name
sec
sbc P8ESTACK_LO+1,x
sta $name
bcc +
dec $name+1
+""")
else
asmgen.out("""
ldy #0
lda P8ESTACK_LO+1,x
bpl +
dey ; sign extend
+ sty P8ZP_SCRATCH_B1
lda $name
sec
sbc P8ESTACK_LO+1,x
sta $name
lda $name+1
sbc P8ZP_SCRATCH_B1
sta $name+1""")
}
"*" -> TODO("mul (u)word (u)byte")
"/" -> TODO("div (u)word (u)byte")
"%" -> TODO("(u)word remainder (u)byte")
"<<" -> { "<<" -> {
asmgen.translateExpression(value) asmgen.translateExpression(value)
asmgen.out(""" asmgen.out("""
@ -1053,9 +1166,9 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
+""") +""")
} }
} }
"&" -> TODO("bitand word byte") "&" -> TODO("bitand (u)word (u)byte")
"^" -> TODO("bitxor word byte") "^" -> TODO("bitxor (u)word (u)byte")
"|" -> TODO("bitor word byte") "|" -> TODO("bitor (u)word (u)byte")
else -> throw AssemblyError("invalid operator for in-place modification $operator") else -> throw AssemblyError("invalid operator for in-place modification $operator")
} }
} }
@ -1567,17 +1680,12 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
DataType.FLOAT -> { DataType.FLOAT -> {
when(target.kind) { when(target.kind) {
TargetStorageKind.VARIABLE -> { TargetStorageKind.VARIABLE -> {
asmgen.saveRegister(CpuRegister.X) // simply flip the sign bit in the float
asmgen.out(""" asmgen.out("""
lda #<${target.asmVarname} lda ${target.asmVarname}+1
ldy #>${target.asmVarname} eor #$80
jsr floats.MOVFM sta ${target.asmVarname}+1
jsr floats.NEGOP
ldx #<${target.asmVarname}
ldy #>${target.asmVarname}
jsr floats.MOVMF
""") """)
asmgen.restoreRegister(CpuRegister.X)
} }
TargetStorageKind.ARRAY -> TODO("in-place negate float array") TargetStorageKind.ARRAY -> TODO("in-place negate float array")
TargetStorageKind.STACK -> TODO("stack float negate") TargetStorageKind.STACK -> TODO("stack float negate")

View File

@ -52,6 +52,8 @@ internal object CX16MachineDefinition: IMachineDefinition {
} }
} }
override fun isRegularRAMaddress(address: Int): Boolean = address < 0x9f00 || address in 0xa000..0xbfff
override fun initializeZeropage(compilerOptions: CompilationOptions) { override fun initializeZeropage(compilerOptions: CompilationOptions) {
zeropage = CX16Zeropage(compilerOptions) zeropage = CX16Zeropage(compilerOptions)
} }
@ -78,13 +80,6 @@ internal object CX16MachineDefinition: IMachineDefinition {
override val SCRATCH_W2 = 0x7e // temp storage 2 for a word $7e+$7f override val SCRATCH_W2 = 0x7e // temp storage 2 for a word $7e+$7f
override val exitProgramStrategy: ExitProgramStrategy = when (options.zeropage) {
ZeropageType.BASICSAFE, ZeropageType.DONTUSE -> ExitProgramStrategy.CLEAN_EXIT
ZeropageType.KERNALSAFE, ZeropageType.FULL -> ExitProgramStrategy.SYSTEM_RESET
else -> ExitProgramStrategy.SYSTEM_RESET
}
init { init {
if (options.floats && options.zeropage !in setOf(ZeropageType.BASICSAFE, ZeropageType.DONTUSE )) if (options.floats && options.zeropage !in setOf(ZeropageType.BASICSAFE, ZeropageType.DONTUSE ))
throw CompilerException("when floats are enabled, zero page type should be 'basicsafe' or 'dontuse'") throw CompilerException("when floats are enabled, zero page type should be 'basicsafe' or 'dontuse'")

View File

@ -58,7 +58,7 @@ internal class ConstantIdentifierReplacer(private val program: Program, private
override fun before(decl: VarDecl, parent: Node): Iterable<IAstModification> { override fun before(decl: VarDecl, parent: Node): Iterable<IAstModification> {
// the initializer value can't refer to the variable itself (recursive definition) // the initializer value can't refer to the variable itself (recursive definition)
// TODO: use call graph for this? // TODO: use call graph for this?
if(decl.value?.referencesIdentifiers(decl.name) == true || decl.arraysize?.index?.referencesIdentifiers(decl.name) == true) { if(decl.value?.referencesIdentifier(decl.name) == true || decl.arraysize?.index?.referencesIdentifier(decl.name) == true) {
errors.err("recursive var declaration", decl.position) errors.err("recursive var declaration", decl.position)
return noModifications return noModifications
} }

View File

@ -1,11 +1,15 @@
package prog8.optimizer package prog8.optimizer
import prog8.ast.INameScope
import prog8.ast.Node import prog8.ast.Node
import prog8.ast.Program import prog8.ast.Program
import prog8.ast.base.* import prog8.ast.base.*
import prog8.ast.expressions.* import prog8.ast.expressions.*
import prog8.ast.processing.AstWalker import prog8.ast.processing.AstWalker
import prog8.ast.processing.IAstModification import prog8.ast.processing.IAstModification
import prog8.ast.statements.AssignTarget
import prog8.ast.statements.Assignment
import prog8.ast.statements.VarDecl
import kotlin.math.abs import kotlin.math.abs
import kotlin.math.log2 import kotlin.math.log2
import kotlin.math.pow import kotlin.math.pow
@ -175,28 +179,6 @@ internal class ExpressionSimplifier(private val program: Program) : AstWalker()
// unsigned >= 0 --> true // unsigned >= 0 --> true
return listOf(IAstModification.ReplaceNode(expr, NumericLiteralValue.fromBoolean(true, expr.position), parent)) return listOf(IAstModification.ReplaceNode(expr, NumericLiteralValue.fromBoolean(true, expr.position), parent))
} }
when(leftDt) {
DataType.BYTE -> {
// signed >=0 --> signed ^ $80
return listOf(IAstModification.ReplaceNode(
expr,
BinaryExpression(expr.left, "^", NumericLiteralValue.optimalInteger(0x80, expr.position), expr.position),
parent
))
}
DataType.WORD -> {
// signedw >=0 --> msb(signedw) ^ $80
return listOf(IAstModification.ReplaceNode(
expr,
BinaryExpression(FunctionCall(IdentifierReference(listOf("msb"), expr.position),
mutableListOf(expr.left),
expr.position
), "^", NumericLiteralValue.optimalInteger(0x80, expr.position), expr.position),
parent
))
}
else -> {}
}
} }
if(expr.operator == "<" && rightVal?.number == 0) { if(expr.operator == "<" && rightVal?.number == 0) {
@ -297,6 +279,80 @@ internal class ExpressionSimplifier(private val program: Program) : AstWalker()
return noModifications return noModifications
} }
// override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> {
// TODO somehow if we do this, the resulting code for some programs (cube3d.p8) gets hundreds of bytes larger...:
// if(decl.type==VarDeclType.VAR ) {
// val binExpr = decl.value as? BinaryExpression
// if (binExpr != null && binExpr.operator in augmentAssignmentOperators) {
// // split into a vardecl with just the left expression, and an aug. assignment with the right expression.
// val augExpr = BinaryExpression(IdentifierReference(listOf(decl.name), decl.position), binExpr.operator, binExpr.right, binExpr.position)
// val target = AssignTarget(IdentifierReference(listOf(decl.name), decl.position), null, null, decl.position)
// val assign = Assignment(target, augExpr, binExpr.position)
// println("SPLIT VARDECL $decl")
// return listOf(
// IAstModification.SetExpression({ decl.value = it }, binExpr.left, decl),
// IAstModification.InsertAfter(decl, assign, parent)
// )
// }
// }
// return noModifications
// }
override fun after(assignment: Assignment, parent: Node): Iterable<IAstModification> {
val binExpr = assignment.value as? BinaryExpression
if (binExpr != null) {
/*
reduce the complexity of a (binary) expression that has to be evaluated on the eval stack,
by attempting to splitting it up into individual simple steps:
X = BinExpr X = LeftExpr
<operator> followed by
/ \ IF 'X' not used X = BinExpr
/ \ IN LEFTEXPR ==> <operator>
/ \ / \
LeftExpr. RightExpr. / \
/ \ / \ X RightExpr.
.. .. .. ..
*/
if(binExpr.operator in augmentAssignmentOperators && isSimpleTarget(assignment.target, program.namespace)) {
if (!assignment.isAugmentable) {
val firstAssign = Assignment(assignment.target, binExpr.left, binExpr.left.position)
val targetExpr = assignment.target.toExpression()
val augExpr = BinaryExpression(targetExpr, binExpr.operator, binExpr.right, binExpr.right.position)
return listOf(
IAstModification.InsertBefore(assignment, firstAssign, parent),
IAstModification.ReplaceNode(assignment.value, augExpr, assignment))
}
}
// TODO further unraveling of binary expression trees into flat statements.
// however this should probably be done in a more generic way to also service
// the expressiontrees that are not used in an assignment statement...
}
return noModifications
}
private fun isSimpleTarget(target: AssignTarget, namespace: INameScope): Boolean {
return when {
target.identifier!=null -> target.isInRegularRAM(namespace)
target.memoryAddress!=null -> target.isInRegularRAM(namespace)
target.arrayindexed!=null -> {
val index = target.arrayindexed!!.arrayspec.index
if(index is NumericLiteralValue)
target.isInRegularRAM(namespace)
else
false
}
else -> false
}
}
override fun after(functionCall: FunctionCall, parent: Node): Iterable<IAstModification> { override fun after(functionCall: FunctionCall, parent: Node): Iterable<IAstModification> {
if(functionCall.target.nameInSource == listOf("lsb")) { if(functionCall.target.nameInSource == listOf("lsb")) {
val arg = functionCall.args[0] val arg = functionCall.args[0]

View File

@ -49,11 +49,6 @@ internal class StatementOptimizer(private val program: Program,
} }
} }
val linesToRemove = deduplicateAssignments(subroutine.statements)
if(linesToRemove.isNotEmpty()) {
linesToRemove.reversed().forEach{subroutine.statements.removeAt(it)}
}
if(subroutine !in callgraph.usedSymbols && !forceOutput) { if(subroutine !in callgraph.usedSymbols && !forceOutput) {
errors.warn("removing unused subroutine '${subroutine.name}'", subroutine.position) errors.warn("removing unused subroutine '${subroutine.name}'", subroutine.position)
return listOf(IAstModification.Remove(subroutine, parent)) return listOf(IAstModification.Remove(subroutine, parent))
@ -62,11 +57,6 @@ internal class StatementOptimizer(private val program: Program,
return noModifications return noModifications
} }
override fun after(scope: AnonymousScope, parent: Node): Iterable<IAstModification> {
val linesToRemove = deduplicateAssignments(scope.statements)
return linesToRemove.reversed().map { IAstModification.Remove(scope.statements[it], scope) }
}
override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> { override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> {
val forceOutput = "force_output" in decl.definingBlock().options() val forceOutput = "force_output" in decl.definingBlock().options()
if(decl !in callgraph.usedSymbols && !forceOutput) { if(decl !in callgraph.usedSymbols && !forceOutput) {
@ -249,11 +239,11 @@ internal class StatementOptimizer(private val program: Program,
} }
override fun before(untilLoop: UntilLoop, parent: Node): Iterable<IAstModification> { override fun before(untilLoop: UntilLoop, parent: Node): Iterable<IAstModification> {
val constvalue = untilLoop.untilCondition.constValue(program) val constvalue = untilLoop.condition.constValue(program)
if(constvalue!=null) { if(constvalue!=null) {
if(constvalue.asBooleanValue) { if(constvalue.asBooleanValue) {
// always true -> keep only the statement block (if there are no break statements) // always true -> keep only the statement block (if there are no break statements)
errors.warn("condition is always true", untilLoop.untilCondition.position) errors.warn("condition is always true", untilLoop.condition.position)
if(!hasBreak(untilLoop.body)) if(!hasBreak(untilLoop.body))
return listOf(IAstModification.ReplaceNode(untilLoop, untilLoop.body, parent)) return listOf(IAstModification.ReplaceNode(untilLoop, untilLoop.body, parent))
} else { } else {
@ -395,21 +385,21 @@ internal class StatementOptimizer(private val program: Program,
val targetDt = targetIDt.typeOrElse(DataType.STRUCT) 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 rightCv = bexpr.right.constValue(program)?.number?.toDouble()
if (cv != null && assignment.target isSameAs bexpr.left) { if (rightCv != null && assignment.target isSameAs bexpr.left) {
// assignments of the form: X = X <operator> <expr> // assignments of the form: X = X <operator> <expr>
// remove assignments that have no effect (such as X=X+0) // remove assignments that have no effect (such as X=X+0)
// optimize/rewrite some other expressions // optimize/rewrite some other expressions
val vardeclDt = (assignment.target.identifier?.targetVarDecl(program.namespace))?.type val vardeclDt = (assignment.target.identifier?.targetVarDecl(program.namespace))?.type
when (bexpr.operator) { when (bexpr.operator) {
"+" -> { "+" -> {
if (cv == 0.0) { if (rightCv == 0.0) {
return listOf(IAstModification.Remove(assignment, parent)) return listOf(IAstModification.Remove(assignment, parent))
} else if (targetDt in IntegerDatatypes && floor(cv) == cv) { } else if (targetDt in IntegerDatatypes && floor(rightCv) == rightCv) {
if (vardeclDt != VarDeclType.MEMORY && cv in 1.0..4.0) { if (vardeclDt != VarDeclType.MEMORY && rightCv in 1.0..4.0) {
// replace by several INCs if it's not a memory address (inc on a memory mapped register doesn't work very well) // replace by several INCs if it's not a memory address (inc on a memory mapped register doesn't work very well)
val incs = AnonymousScope(mutableListOf(), assignment.position) val incs = AnonymousScope(mutableListOf(), assignment.position)
repeat(cv.toInt()) { repeat(rightCv.toInt()) {
incs.statements.add(PostIncrDecr(assignment.target, "++", assignment.position)) incs.statements.add(PostIncrDecr(assignment.target, "++", assignment.position))
} }
return listOf(IAstModification.ReplaceNode(assignment, incs, parent)) return listOf(IAstModification.ReplaceNode(assignment, incs, parent))
@ -417,62 +407,38 @@ internal class StatementOptimizer(private val program: Program,
} }
} }
"-" -> { "-" -> {
if (cv == 0.0) { if (rightCv == 0.0) {
return listOf(IAstModification.Remove(assignment, parent)) return listOf(IAstModification.Remove(assignment, parent))
} else if (targetDt in IntegerDatatypes && floor(cv) == cv) { } else if (targetDt in IntegerDatatypes && floor(rightCv) == rightCv) {
if (vardeclDt != VarDeclType.MEMORY && cv in 1.0..4.0) { if (vardeclDt != VarDeclType.MEMORY && rightCv in 1.0..4.0) {
// replace by several DECs if it's not a memory address (dec on a memory mapped register doesn't work very well) // replace by several DECs if it's not a memory address (dec on a memory mapped register doesn't work very well)
val decs = AnonymousScope(mutableListOf(), assignment.position) val decs = AnonymousScope(mutableListOf(), assignment.position)
repeat(cv.toInt()) { repeat(rightCv.toInt()) {
decs.statements.add(PostIncrDecr(assignment.target, "--", assignment.position)) decs.statements.add(PostIncrDecr(assignment.target, "--", assignment.position))
} }
return listOf(IAstModification.ReplaceNode(assignment, decs, parent)) return listOf(IAstModification.ReplaceNode(assignment, decs, parent))
} }
} }
} }
"*" -> if (cv == 1.0) return listOf(IAstModification.Remove(assignment, parent)) "*" -> if (rightCv == 1.0) return listOf(IAstModification.Remove(assignment, parent))
"/" -> if (cv == 1.0) return listOf(IAstModification.Remove(assignment, parent)) "/" -> if (rightCv == 1.0) return listOf(IAstModification.Remove(assignment, parent))
"**" -> if (cv == 1.0) return listOf(IAstModification.Remove(assignment, parent)) "**" -> if (rightCv == 1.0) return listOf(IAstModification.Remove(assignment, parent))
"|" -> if (cv == 0.0) return listOf(IAstModification.Remove(assignment, parent)) "|" -> if (rightCv == 0.0) return listOf(IAstModification.Remove(assignment, parent))
"^" -> if (cv == 0.0) return listOf(IAstModification.Remove(assignment, parent)) "^" -> if (rightCv == 0.0) return listOf(IAstModification.Remove(assignment, parent))
"<<" -> { "<<" -> {
if (cv == 0.0) if (rightCv == 0.0)
return listOf(IAstModification.Remove(assignment, parent)) return listOf(IAstModification.Remove(assignment, parent))
} }
">>" -> { ">>" -> {
if (cv == 0.0) if (rightCv == 0.0)
return listOf(IAstModification.Remove(assignment, parent)) return listOf(IAstModification.Remove(assignment, parent))
} }
} }
} }
} }
return noModifications
}
private fun deduplicateAssignments(statements: List<Statement>): MutableList<Int> { return noModifications
// removes 'duplicate' assignments that assign the isSameAs target
val linesToRemove = mutableListOf<Int>()
var previousAssignmentLine: Int? = null
for (i in statements.indices) {
val stmt = statements[i] as? Assignment
if (stmt != null && stmt.value is NumericLiteralValue) {
if (previousAssignmentLine == null) {
previousAssignmentLine = i
continue
} else {
val prev = statements[previousAssignmentLine] as Assignment
if (prev.target.isSameAs(stmt.target, program)) {
// get rid of the previous assignment, if the target is not MEMORY
if (prev.target.isNotMemory(program.namespace))
linesToRemove.add(previousAssignmentLine)
}
previousAssignmentLine = i
}
} else
previousAssignmentLine = null
}
return linesToRemove
} }
private fun hasBreak(scope: INameScope): Boolean { private fun hasBreak(scope: INameScope): Boolean {

View File

@ -8,7 +8,8 @@ import prog8.ast.processing.AstWalker
import prog8.ast.processing.IAstModification import prog8.ast.processing.IAstModification
import prog8.ast.statements.* import prog8.ast.statements.*
internal class UnusedCodeRemover(private val errors: ErrorReporter): AstWalker() {
internal class UnusedCodeRemover(private val program: Program, private val errors: ErrorReporter): AstWalker() {
override fun before(program: Program, parent: Node): Iterable<IAstModification> { override fun before(program: Program, parent: Node): Iterable<IAstModification> {
val callgraph = CallGraph(program) val callgraph = CallGraph(program)
@ -66,4 +67,35 @@ internal class UnusedCodeRemover(private val errors: ErrorReporter): AstWalker()
else -> errors.warn("unreachable code", next.position) else -> errors.warn("unreachable code", next.position)
} }
} }
override fun after(scope: AnonymousScope, parent: Node): Iterable<IAstModification> {
val removeDoubleAssignments = deduplicateAssignments(scope.statements)
return removeDoubleAssignments.map { IAstModification.Remove(it, scope) }
}
override fun after(block: Block, parent: Node): Iterable<IAstModification> {
val removeDoubleAssignments = deduplicateAssignments(block.statements)
return removeDoubleAssignments.map { IAstModification.Remove(it, block) }
}
override fun after(subroutine: Subroutine, parent: Node): Iterable<IAstModification> {
val removeDoubleAssignments = deduplicateAssignments(subroutine.statements)
return removeDoubleAssignments.map { IAstModification.Remove(it, subroutine) }
}
private fun deduplicateAssignments(statements: List<Statement>): List<Assignment> {
// removes 'duplicate' assignments that assign the same target directly after another
val linesToRemove = mutableListOf<Assignment>()
for (stmtPairs in statements.windowed(2, step = 1)) {
val assign1 = stmtPairs[0] as? Assignment
val assign2 = stmtPairs[1] as? Assignment
if (assign1 != null && assign2 != null && !assign2.isAugmentable) {
if (assign1.target.isSameAs(assign2.target, program) && assign1.target.isInRegularRAM(program.namespace))
linesToRemove.add(assign1)
}
}
return linesToRemove
}
} }

View File

@ -131,13 +131,14 @@ internal class ModuleImporter {
if(existing!=null) if(existing!=null)
return null return null
val resource = tryGetEmbeddedResource("$moduleName.p8") val rsc = tryGetEmbeddedResource("$moduleName.p8")
val importedModule = val importedModule =
if(resource!=null) { if(rsc!=null) {
// load the module from the embedded resource // load the module from the embedded resource
val (resource, resourcePath) = rsc
resource.use { resource.use {
println("importing '$moduleName' (library)") println("importing '$moduleName' (library)")
importModule(program, CharStreams.fromStream(it), Paths.get("@embedded@/$moduleName"), true) importModule(program, CharStreams.fromStream(it), Paths.get("@embedded@/$resourcePath"), true)
} }
} else { } else {
val modulePath = discoverImportedModuleFile(moduleName, source, import.position) val modulePath = discoverImportedModuleFile(moduleName, source, import.position)
@ -148,11 +149,18 @@ internal class ModuleImporter {
return importedModule return importedModule
} }
private fun tryGetEmbeddedResource(name: String): InputStream? { private fun tryGetEmbeddedResource(name: String): Pair<InputStream, String>? {
val target = CompilationTarget.instance.name val target = CompilationTarget.instance.name
val targetSpecific = object{}.javaClass.getResourceAsStream("/prog8lib/$target/$name") val targetSpecificPath = "/prog8lib/$target/$name"
if(targetSpecific!=null) val targetSpecificResource = object{}.javaClass.getResourceAsStream(targetSpecificPath)
return targetSpecific if(targetSpecificResource!=null)
return object{}.javaClass.getResourceAsStream("/prog8lib/$name") return Pair(targetSpecificResource, targetSpecificPath)
val generalPath = "/prog8lib/$name"
val generalResource = object{}.javaClass.getResourceAsStream(generalPath)
if(generalResource!=null)
return Pair(generalResource, generalPath)
return null
} }
} }

View File

@ -5,18 +5,21 @@ import org.hamcrest.Matchers.closeTo
import org.hamcrest.Matchers.equalTo import org.hamcrest.Matchers.equalTo
import org.junit.jupiter.api.Test 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.Module
import prog8.ast.base.ErrorReporter import prog8.ast.Program
import prog8.ast.base.Position import prog8.ast.base.*
import prog8.ast.expressions.NumericLiteralValue import prog8.ast.expressions.*
import prog8.ast.expressions.StringLiteralValue import prog8.ast.statements.*
import prog8.compiler.* import prog8.compiler.*
import prog8.compiler.target.C64Target
import prog8.compiler.target.CompilationTarget
import prog8.compiler.target.c64.C64MachineDefinition.C64Zeropage import prog8.compiler.target.c64.C64MachineDefinition.C64Zeropage
import prog8.compiler.target.c64.C64MachineDefinition.FLOAT_MAX_NEGATIVE import prog8.compiler.target.c64.C64MachineDefinition.FLOAT_MAX_NEGATIVE
import prog8.compiler.target.c64.C64MachineDefinition.FLOAT_MAX_POSITIVE import prog8.compiler.target.c64.C64MachineDefinition.FLOAT_MAX_POSITIVE
import prog8.compiler.target.c64.C64MachineDefinition.Mflpt5 import prog8.compiler.target.c64.C64MachineDefinition.Mflpt5
import prog8.compiler.target.c64.Petscii import prog8.compiler.target.c64.Petscii
import java.io.CharConversionException import java.io.CharConversionException
import java.nio.file.Path
import kotlin.test.* import kotlin.test.*
@TestInstance(TestInstance.Lifecycle.PER_CLASS) @TestInstance(TestInstance.Lifecycle.PER_CLASS)
@ -129,7 +132,7 @@ class TestC64Zeropage {
@Test @Test
fun testNames() { fun testNames() {
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), false)) val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), false, false))
zp.allocate("", DataType.UBYTE, null, errors) zp.allocate("", DataType.UBYTE, null, errors)
zp.allocate("", DataType.UBYTE, null, errors) zp.allocate("", DataType.UBYTE, null, errors)
@ -142,37 +145,37 @@ class TestC64Zeropage {
@Test @Test
fun testZpFloatEnable() { fun testZpFloatEnable() {
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false)) val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false, false))
assertFailsWith<CompilerException> { assertFailsWith<CompilerException> {
zp.allocate("", DataType.FLOAT, null, errors) zp.allocate("", DataType.FLOAT, null, errors)
} }
val zp2 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.DONTUSE, emptyList(), true)) val zp2 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.DONTUSE, emptyList(), true, false))
assertFailsWith<CompilerException> { assertFailsWith<CompilerException> {
zp2.allocate("", DataType.FLOAT, null, errors) zp2.allocate("", DataType.FLOAT, null, errors)
} }
val zp3 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), true)) val zp3 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), true, false))
zp3.allocate("", DataType.FLOAT, null, errors) zp3.allocate("", DataType.FLOAT, null, errors)
} }
@Test @Test
fun testZpModesWithFloats() { fun testZpModesWithFloats() {
C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false)) C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false, false))
C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.KERNALSAFE, emptyList(), false)) C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.KERNALSAFE, emptyList(), false, false))
C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), false)) C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), false, false))
C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), false)) C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), false, false))
C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true)) C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true, false))
C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), true)) C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), true, false))
assertFailsWith<CompilerException> { assertFailsWith<CompilerException> {
C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), true)) C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), true, false))
} }
assertFailsWith<CompilerException> { assertFailsWith<CompilerException> {
C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.KERNALSAFE, emptyList(), true)) C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.KERNALSAFE, emptyList(), true, false))
} }
} }
@Test @Test
fun testZpDontuse() { fun testZpDontuse() {
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.DONTUSE, emptyList(), false)) val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.DONTUSE, emptyList(), false, false))
println(zp.free) println(zp.free)
assertEquals(0, zp.available()) assertEquals(0, zp.available())
assertFailsWith<CompilerException> { assertFailsWith<CompilerException> {
@ -182,19 +185,19 @@ class TestC64Zeropage {
@Test @Test
fun testFreeSpaces() { fun testFreeSpaces() {
val zp1 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true)) val zp1 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true, false))
assertEquals(16, zp1.available()) assertEquals(16, zp1.available())
val zp2 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), false)) val zp2 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), false, false))
assertEquals(89, zp2.available()) assertEquals(89, zp2.available())
val zp3 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.KERNALSAFE, emptyList(), false)) val zp3 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.KERNALSAFE, emptyList(), false, false))
assertEquals(125, zp3.available()) assertEquals(125, zp3.available())
val zp4 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false)) val zp4 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false, false))
assertEquals(238, zp4.available()) assertEquals(238, zp4.available())
} }
@Test @Test
fun testReservedSpace() { fun testReservedSpace() {
val zp1 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false)) val zp1 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false, false))
assertEquals(238, zp1.available()) assertEquals(238, zp1.available())
assertTrue(50 in zp1.free) assertTrue(50 in zp1.free)
assertTrue(100 in zp1.free) assertTrue(100 in zp1.free)
@ -203,7 +206,7 @@ class TestC64Zeropage {
assertTrue(200 in zp1.free) assertTrue(200 in zp1.free)
assertTrue(255 in zp1.free) assertTrue(255 in zp1.free)
assertTrue(199 in zp1.free) assertTrue(199 in zp1.free)
val zp2 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, listOf(50 .. 100, 200..255), false)) val zp2 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, listOf(50 .. 100, 200..255), false, false))
assertEquals(139, zp2.available()) assertEquals(139, zp2.available())
assertFalse(50 in zp2.free) assertFalse(50 in zp2.free)
assertFalse(100 in zp2.free) assertFalse(100 in zp2.free)
@ -216,7 +219,7 @@ class TestC64Zeropage {
@Test @Test
fun testBasicsafeAllocation() { fun testBasicsafeAllocation() {
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true)) val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true, false))
assertEquals(16, zp.available()) assertEquals(16, zp.available())
assertFailsWith<ZeropageDepletedError> { assertFailsWith<ZeropageDepletedError> {
@ -239,7 +242,7 @@ class TestC64Zeropage {
@Test @Test
fun testFullAllocation() { fun testFullAllocation() {
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false)) val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false, false))
assertEquals(238, zp.available()) assertEquals(238, zp.available())
val loc = zp.allocate("", DataType.UWORD, null, errors) val loc = zp.allocate("", DataType.UWORD, null, errors)
assertTrue(loc > 3) assertTrue(loc > 3)
@ -269,7 +272,7 @@ class TestC64Zeropage {
@Test @Test
fun testEfficientAllocation() { fun testEfficientAllocation() {
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true)) val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true, false))
assertEquals(16, zp.available()) assertEquals(16, zp.available())
assertEquals(0x04, zp.allocate("", DataType.WORD, null, errors)) assertEquals(0x04, zp.allocate("", DataType.WORD, null, errors))
assertEquals(0x06, zp.allocate("", DataType.UBYTE, null, errors)) assertEquals(0x06, zp.allocate("", DataType.UBYTE, null, errors))
@ -379,3 +382,169 @@ class TestPetscii {
assertFalse(abc!=abc) assertFalse(abc!=abc)
} }
} }
class TestMemory {
@Test
fun testInValidRamC64_memory_addresses() {
CompilationTarget.instance = C64Target
var memexpr = NumericLiteralValue.optimalInteger(0x0000, Position.DUMMY)
var target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY)
var scope = AnonymousScope(mutableListOf(), Position.DUMMY)
assertTrue(target.isInRegularRAM(scope))
memexpr = NumericLiteralValue.optimalInteger(0x1000, Position.DUMMY)
target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY)
scope = AnonymousScope(mutableListOf(), Position.DUMMY)
assertTrue(target.isInRegularRAM(scope))
memexpr = NumericLiteralValue.optimalInteger(0x9fff, Position.DUMMY)
target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY)
scope = AnonymousScope(mutableListOf(), Position.DUMMY)
assertTrue(target.isInRegularRAM(scope))
memexpr = NumericLiteralValue.optimalInteger(0xc000, Position.DUMMY)
target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY)
scope = AnonymousScope(mutableListOf(), Position.DUMMY)
assertTrue(target.isInRegularRAM(scope))
memexpr = NumericLiteralValue.optimalInteger(0xcfff, Position.DUMMY)
target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY)
scope = AnonymousScope(mutableListOf(), Position.DUMMY)
assertTrue(target.isInRegularRAM(scope))
}
@Test
fun testNotInValidRamC64_memory_addresses() {
CompilationTarget.instance = C64Target
var memexpr = NumericLiteralValue.optimalInteger(0xa000, Position.DUMMY)
var target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY)
var scope = AnonymousScope(mutableListOf(), Position.DUMMY)
assertFalse(target.isInRegularRAM(scope))
memexpr = NumericLiteralValue.optimalInteger(0xafff, Position.DUMMY)
target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY)
scope = AnonymousScope(mutableListOf(), Position.DUMMY)
assertFalse(target.isInRegularRAM(scope))
memexpr = NumericLiteralValue.optimalInteger(0xd000, Position.DUMMY)
target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY)
scope = AnonymousScope(mutableListOf(), Position.DUMMY)
assertFalse(target.isInRegularRAM(scope))
memexpr = NumericLiteralValue.optimalInteger(0xffff, Position.DUMMY)
target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY)
scope = AnonymousScope(mutableListOf(), Position.DUMMY)
assertFalse(target.isInRegularRAM(scope))
}
@Test
fun testInValidRamC64_memory_identifiers() {
CompilationTarget.instance = C64Target
var target = createTestProgramForMemoryRefViaVar(0x1000, VarDeclType.VAR)
assertTrue(target.isInRegularRAM(target.definingScope()))
target = createTestProgramForMemoryRefViaVar(0xd020, VarDeclType.VAR)
assertFalse(target.isInRegularRAM(target.definingScope()))
target = createTestProgramForMemoryRefViaVar(0x1000, VarDeclType.CONST)
assertTrue(target.isInRegularRAM(target.definingScope()))
target = createTestProgramForMemoryRefViaVar(0xd020, VarDeclType.CONST)
assertFalse(target.isInRegularRAM(target.definingScope()))
target = createTestProgramForMemoryRefViaVar(0x1000, VarDeclType.MEMORY)
assertFalse(target.isInRegularRAM(target.definingScope()))
}
@Test
private fun createTestProgramForMemoryRefViaVar(address: Int, vartype: VarDeclType): AssignTarget {
val decl = VarDecl(vartype, DataType.BYTE, ZeropageWish.DONTCARE, null, "address", null, NumericLiteralValue.optimalInteger(address, Position.DUMMY), false, false, Position.DUMMY)
val memexpr = IdentifierReference(listOf("address"), Position.DUMMY)
val target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY)
val assignment = Assignment(target, NumericLiteralValue.optimalInteger(0, Position.DUMMY), Position.DUMMY)
val subroutine = Subroutine("test", emptyList(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, mutableListOf(decl, assignment), Position.DUMMY)
subroutine.linkParents(ParentSentinel)
return target
}
@Test
fun testInValidRamC64_memory_expression() {
CompilationTarget.instance = C64Target
val memexpr = PrefixExpression("+", NumericLiteralValue.optimalInteger(0x1000, Position.DUMMY), Position.DUMMY)
val target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY)
val scope = AnonymousScope(mutableListOf(), Position.DUMMY)
assertFalse(target.isInRegularRAM(scope))
}
@Test
fun testInValidRamC64_variable() {
CompilationTarget.instance = C64Target
val decl = VarDecl(VarDeclType.VAR, DataType.BYTE, ZeropageWish.DONTCARE, null, "address", null, null, false, false, Position.DUMMY)
val target = AssignTarget(IdentifierReference(listOf("address"), Position.DUMMY), null, null, Position.DUMMY)
val assignment = Assignment(target, NumericLiteralValue.optimalInteger(0, Position.DUMMY), Position.DUMMY)
val subroutine = Subroutine("test", emptyList(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, mutableListOf(decl, assignment), Position.DUMMY)
subroutine.linkParents(ParentSentinel)
assertTrue(target.isInRegularRAM(target.definingScope()))
}
@Test
fun testInValidRamC64_memmap_variable() {
CompilationTarget.instance = C64Target
val address = 0x1000
val decl = VarDecl(VarDeclType.MEMORY, DataType.UBYTE, ZeropageWish.DONTCARE, null, "address", null, NumericLiteralValue.optimalInteger(address, Position.DUMMY), false, false, Position.DUMMY)
val target = AssignTarget(IdentifierReference(listOf("address"), Position.DUMMY), null, null, Position.DUMMY)
val assignment = Assignment(target, NumericLiteralValue.optimalInteger(0, Position.DUMMY), Position.DUMMY)
val subroutine = Subroutine("test", emptyList(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, mutableListOf(decl, assignment), Position.DUMMY)
subroutine.linkParents(ParentSentinel)
assertTrue(target.isInRegularRAM(target.definingScope()))
}
@Test
fun testNotInValidRamC64_memmap_variable() {
CompilationTarget.instance = C64Target
val address = 0xd020
val decl = VarDecl(VarDeclType.MEMORY, DataType.UBYTE, ZeropageWish.DONTCARE, null, "address", null, NumericLiteralValue.optimalInteger(address, Position.DUMMY), false, false, Position.DUMMY)
val target = AssignTarget(IdentifierReference(listOf("address"), Position.DUMMY), null, null, Position.DUMMY)
val assignment = Assignment(target, NumericLiteralValue.optimalInteger(0, Position.DUMMY), Position.DUMMY)
val subroutine = Subroutine("test", emptyList(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, mutableListOf(decl, assignment), Position.DUMMY)
subroutine.linkParents(ParentSentinel)
assertFalse(target.isInRegularRAM(target.definingScope()))
}
@Test
fun testInValidRamC64_array() {
CompilationTarget.instance = C64Target
val decl = VarDecl(VarDeclType.VAR, DataType.ARRAY_UB, ZeropageWish.DONTCARE, null, "address", null, null, false, false, Position.DUMMY)
val arrayindexed = ArrayIndexedExpression(IdentifierReference(listOf("address"), Position.DUMMY), ArrayIndex(NumericLiteralValue.optimalInteger(1, Position.DUMMY), Position.DUMMY), Position.DUMMY)
val target = AssignTarget(null, arrayindexed, null, Position.DUMMY)
val assignment = Assignment(target, NumericLiteralValue.optimalInteger(0, Position.DUMMY), Position.DUMMY)
val subroutine = Subroutine("test", emptyList(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, mutableListOf(decl, assignment), Position.DUMMY)
subroutine.linkParents(ParentSentinel)
assertTrue(target.isInRegularRAM(target.definingScope()))
}
@Test
fun testInValidRamC64_array_memmapped() {
CompilationTarget.instance = C64Target
val address = 0x1000
val decl = VarDecl(VarDeclType.MEMORY, DataType.ARRAY_UB, ZeropageWish.DONTCARE, null, "address", null, NumericLiteralValue.optimalInteger(address, Position.DUMMY), false, false, Position.DUMMY)
val arrayindexed = ArrayIndexedExpression(IdentifierReference(listOf("address"), Position.DUMMY), ArrayIndex(NumericLiteralValue.optimalInteger(1, Position.DUMMY), Position.DUMMY), Position.DUMMY)
val target = AssignTarget(null, arrayindexed, null, Position.DUMMY)
val assignment = Assignment(target, NumericLiteralValue.optimalInteger(0, Position.DUMMY), Position.DUMMY)
val subroutine = Subroutine("test", emptyList(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, mutableListOf(decl, assignment), Position.DUMMY)
subroutine.linkParents(ParentSentinel)
assertTrue(target.isInRegularRAM(target.definingScope()))
}
@Test
fun testNotValidRamC64_array_memmapped() {
CompilationTarget.instance = C64Target
val address = 0xe000
val decl = VarDecl(VarDeclType.MEMORY, DataType.ARRAY_UB, ZeropageWish.DONTCARE, null, "address", null, NumericLiteralValue.optimalInteger(address, Position.DUMMY), false, false, Position.DUMMY)
val arrayindexed = ArrayIndexedExpression(IdentifierReference(listOf("address"), Position.DUMMY), ArrayIndex(NumericLiteralValue.optimalInteger(1, Position.DUMMY), Position.DUMMY), Position.DUMMY)
val target = AssignTarget(null, arrayindexed, null, Position.DUMMY)
val assignment = Assignment(target, NumericLiteralValue.optimalInteger(0, Position.DUMMY), Position.DUMMY)
val subroutine = Subroutine("test", emptyList(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, mutableListOf(decl, assignment), Position.DUMMY)
subroutine.linkParents(ParentSentinel)
assertFalse(target.isInRegularRAM(target.definingScope()))
}
}

View File

@ -236,12 +236,15 @@ The largest 5-byte MFLPT float that can be stored is: **1.7014118345e+38** (ne
Arrays Arrays
^^^^^^ ^^^^^^
Array types are also supported. They can be made of bytes, words or floats:: Array types are also supported. They can be made of bytes, words or floats, strings, and other arrays
(although the usefulness of the latter is very limited for now)::
byte[10] array ; array of 10 bytes, initially set to 0 byte[10] array ; array of 10 bytes, initially set to 0
byte[] array = [1, 2, 3, 4] ; initialize the array, size taken from value byte[] array = [1, 2, 3, 4] ; initialize the array, size taken from value
byte[99] array = 255 ; initialize array with 99 times 255 [255, 255, 255, 255, ...] byte[99] array = 255 ; initialize array with 99 times 255 [255, 255, 255, 255, ...]
byte[] array = 100 to 199 ; initialize array with [100, 101, ..., 198, 199] byte[] array = 100 to 199 ; initialize array with [100, 101, ..., 198, 199]
str[] names = ["ally", "pete"] ; array of string pointers/addresses (equivalent to uword)
uword[] others = [names, array] ; array of pointers/addresses to other arrays
value = array[3] ; the fourth value in the array (index is 0-based) value = array[3] ; the fourth value in the array (index is 0-based)
char = string[4] ; the fifth character (=byte) in the string char = string[4] ; the fifth character (=byte) in the string
@ -293,6 +296,12 @@ large enough to contain the new value::
string1 = "new value" string1 = "new value"
.. info::
Strings and uwords (=memory address) can often be interchanged.
An array of strings is actually an array of uwords where every element is the memory
address of the string. You can pass a memory address to assembly functions
that require a string as an argument.
.. caution:: .. caution::
It's probably best to avoid changing strings after they've been created. This It's probably best to avoid changing strings after they've been created. This
includes changing certain letters by index, or by assigning a new value, or by includes changing certain letters by index, or by assigning a new value, or by
@ -327,7 +336,7 @@ and then create a variable with it::
ubyte blue ubyte blue
} }
Color rgb = {255,122,0} ; note the curly braces here instead of brackets Color rgb = [255,122,0] ; note that struct initializer value is same as an array
Color another ; the init value is optional, like arrays Color another ; the init value is optional, like arrays
another = rgb ; assign all of the values of rgb to another another = rgb ; assign all of the values of rgb to another
@ -718,7 +727,7 @@ sort(array)
floating point values. floating point values.
reverse(array) reverse(array)
Reverse the values in the array (in-place). Supports all data types including floats. Reverse the values in the array (in-place).
Can be used after sort() to sort an array in descending order. Can be used after sort() to sort an array in descending order.
len(x) len(x)

View File

@ -67,7 +67,8 @@ Directives
- style ``kernalsafe`` -- use the part of the ZP that is 'free' or only used by BASIC routines, - style ``kernalsafe`` -- use the part of the ZP that is 'free' or only used by BASIC routines,
and don't change anything else. This allows full use of KERNAL ROM routines (but not BASIC routines), and don't change anything else. This allows full use of KERNAL ROM routines (but not BASIC routines),
including default IRQs during normal system operation. including default IRQs during normal system operation.
When the program exits, a system reset is performed (because BASIC will be in a corrupt state). It's not possible to return cleanly to BASIC when the program exits. The only choice is
to perform a system reset. (A ``system_reset`` subroutine is available in the syslib to help you do this)
- style ``floatsafe`` -- like the previous one but also reserves the addresses that - style ``floatsafe`` -- like the previous one but also reserves the addresses that
are required to perform floating point operations (from the BASIC kernel). No clean exit is possible. are required to perform floating point operations (from the BASIC kernel). No clean exit is possible.
- style ``basicsafe`` -- the most restricted mode; only use the handful 'free' addresses in the ZP, and don't - style ``basicsafe`` -- the most restricted mode; only use the handful 'free' addresses in the ZP, and don't
@ -78,9 +79,10 @@ Directives
except the few addresses mentioned above that are used by the system's IRQ routine. except the few addresses mentioned above that are used by the system's IRQ routine.
Even though the default IRQ routine is still active, it is impossible to use most BASIC and KERNAL ROM routines. Even though the default IRQ routine is still active, it is impossible to use most BASIC and KERNAL ROM routines.
This includes many floating point operations and several utility routines that do I/O, such as ``print_string``. This includes many floating point operations and several utility routines that do I/O, such as ``print_string``.
As with ``kernalsafe``, it is not possible to cleanly exit the program, other than to reset the machine.
This option makes programs smaller and faster because even more variables can This option makes programs smaller and faster because even more variables can
be stored in the ZP (which allows for more efficient assembly code). be stored in the ZP (which allows for more efficient assembly code).
It's not possible to return cleanly to BASIC when the program exits. The only choice is
to perform a system reset. (A ``system_reset`` subroutine is available in the syslib to help you do this)
- style ``dontuse`` -- don't use *any* location in the zeropage. - style ``dontuse`` -- don't use *any* location in the zeropage.
Also read :ref:`zeropage`. Also read :ref:`zeropage`.
@ -117,33 +119,38 @@ Directives
Level: module, block. Level: module, block.
Sets special compiler options. Sets special compiler options.
For a module option, only the ``enable_floats`` option is recognised, which will tell the compiler
to deal with floating point numbers (by using various subroutines from the Commodore-64 kernal). - For a module option, there is ``enable_floats``, which will tell the compiler
Otherwise, floating point support is not enabled. to deal with floating point numbers (by using various subroutines from the Commodore-64 kernal).
When used in a block with the ``force_output`` option, it will force the block to be outputted Otherwise, floating point support is not enabled.
in the final program. Can be useful to make sure some - There's also ``no_sysinit`` which cause the resulting program to *not* include
data is generated that would otherwise be discarded because it's not referenced (such as sprite data). the system re-initialization logic of clearing the screen, resetting I/O config etc. You'll have to
take care of that yourself. The program will just start running from whatever state the machine is in when the
program was launched.
- When used in a block with the ``force_output`` option, it will force the block to be outputted
in the final program. Can be useful to make sure some
data is generated that would otherwise be discarded because it's not referenced (such as sprite data).
.. data:: %asmbinary "<filename>" [, <offset>[, <length>]] .. data:: %asmbinary "<filename>" [, <offset>[, <length>]]
Level: block. Level: block.
This directive can only be used inside a block. This directive can only be used inside a block.
The assembler will include the file as binary bytes at this point, prog8 will not process this at all. The assembler will include the file as binary bytes at this point, prog8 will not process this at all.
The optional offset and length can be used to select a particular piece of the file. The optional offset and length can be used to select a particular piece of the file.
The file is located relative to the current working directory! The file is located relative to the current working directory!
.. data:: %asminclude "<filename>", "scopelabel" .. data:: %asminclude "<filename>", "scopelabel"
Level: block. Level: block.
This directive can only be used inside a block. This directive can only be used inside a block.
The assembler will include the file as raw assembly source text at this point, The assembler will include the file as raw assembly source text at this point,
prog8 will not process this at all, with one exception: the labels. prog8 will not process this at all, with one exception: the labels.
The scopelabel argument will be used as a prefix to access the labels from the included source code, The scopelabel argument will be used as a prefix to access the labels from the included source code,
otherwise you would risk symbol redefinitions or duplications. otherwise you would risk symbol redefinitions or duplications.
If you know what you are doing you can leave it as an empty string to not have a scope prefix. If you know what you are doing you can leave it as an empty string to not have a scope prefix.
The compiler first looks for the file relative to the same directory as the module containing this statement is in, The compiler first looks for the file relative to the same directory as the module containing this statement is in,
if the file can't be found there it is searched relative to the current directory. if the file can't be found there it is searched relative to the current directory.
.. data:: %breakpoint .. data:: %breakpoint
@ -274,6 +281,7 @@ type identifier type storage size example var declara
``word[]`` signed word array depends on value ``word[] myvar = [1, 2, 3, 4]`` ``word[]`` signed word array depends on value ``word[] myvar = [1, 2, 3, 4]``
``uword[]`` unsigned word array depends on value ``uword[] myvar = [1, 2, 3, 4]`` ``uword[]`` unsigned word array depends on value ``uword[] myvar = [1, 2, 3, 4]``
``float[]`` floating-point array depends on value ``float[] myvar = [1.1, 2.2, 3.3, 4.4]`` ``float[]`` floating-point array depends on value ``float[] myvar = [1.1, 2.2, 3.3, 4.4]``
``str[]`` array with string ptrs 2*x bytes + strs ``str[] names = ["ally", "pete"]``
``str`` string (petscii) varies ``str myvar = "hello."`` ``str`` string (petscii) varies ``str myvar = "hello."``
implicitly terminated by a 0-byte implicitly terminated by a 0-byte
=============== ======================= ================= ========================================= =============== ======================= ================= =========================================
@ -391,7 +399,7 @@ After defining a struct you can use the name of the struct as a data type to dec
Struct variables can be assigned a struct literal value (also in their declaration as initial value):: Struct variables can be assigned a struct literal value (also in their declaration as initial value)::
Color rgb = {255, 100, 0} ; curly braces instead of brackets Color rgb = [255, 100, 0] ; note that the value is an array
Operators Operators

View File

@ -3,31 +3,24 @@ TODO
==== ====
- get rid of all other TODO's in the code ;-) - get rid of all other TODO's in the code ;-)
- move the ldx #$ff | clc | cld from the startup logic into the start() function as first instructions - make it possible for array literals to not only contain compile time constants?
- add an %option that omits the 'system-init' code at the start. Useful to create separate standalone routines that shouldn't re-init the whole machine every time they're called
- line-circle-gfx examples are now a few hundred bytes larger than before. Why is that, can it be fixed?
- until condition should be able to refer to variables defined IN the do-until block itself.
- add support? example? for processing arguments to a sys call : sys 999, 1, 2, "aaa"
- make it possible for array literals to not only contain compile time constants
- further optimize assignment codegeneration
- implement @stack for asmsub parameters - implement @stack for asmsub parameters
- make it possible to use cpu opcodes such as 'nop' as variable names by prefixing all asm vars with something such as '_' - make it possible to use cpu opcodes such as 'nop' as variable names by prefixing all asm vars with something such as '_'
- option to load the built-in library files from a directory instead of the embedded ones (for easier library development/debugging) - option to load the built-in library files from a directory instead of the embedded ones (for easier library development/debugging)
- see if we can group some errors together for instance the (now single) errors about unidentified symbols - see if we can group some errors together for instance the (now single) errors about unidentified symbols
- use VIC banking to move up the graphics bitmap memory location. Don't move it under the ROM though as that would require IRQ disabling and memory bank swapping for every bitmap manipulation - use VIC banking to move up the graphics bitmap memory location. Don't move it under the ROM though as that would require IRQ disabling and memory bank swapping for every bitmap manipulation
- add some primitives/support/examples for using custom char sets, copying the default charset. - add some primitives/subroutines/examples for using custom char sets, copying the default charset.
- recursive subroutines? via %option recursive, allocate all params and local vars on estack, don't allow nested subroutines, can begin by first not allowing any local variables just fixing the parameters
More optimizations More optimizations
^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^
Add more compiler optimizations to the existing ones. Add more compiler optimizations to the existing ones.
- more targeted optimizations for assigment asm code, such as the following: - further optimize assignment codegeneration, such as the following:
- binexpr splitting (beware self-referencing expressions and asm code ballooning though)
- subroutine calling convention? like: 1 byte arg -> pass in A, 2 bytes -> pass in A+Y, return value likewise. - subroutine calling convention? like: 1 byte arg -> pass in A, 2 bytes -> pass in A+Y, return value likewise.
- can such parameter passing to subroutines be optimized to avoid copying? - can such parameter passing to subroutines be optimized to avoid copying?
- add a compiler option to not include variable initialization code (useful if the program is expected to run only once, such as a game)
the program will then rely solely on the values as they are in memory at the time of program startup.
- Also some library routines and code patterns could perhaps be optimized further
- more optimizations on the language AST level - more optimizations on the language AST level
- more optimizations on the final assembly source level - more optimizations on the final assembly source level
- note: subroutine inlining is abandoned because of problems referencing non-local stuff. Can't move everything around. - note: subroutine inlining is abandoned because of problems referencing non-local stuff. Can't move everything around.

View File

@ -0,0 +1,315 @@
%import textio
%import syslib
%zeropage basicsafe
main {
sub start() {
repeat 25 {
txt.chrout('\n')
}
ubyte ub
byte bb
uword uwsum
word wsum
uwsum = 50000
ub=50
uwsum += ub
ub=250
uwsum += ub
if uwsum==50300
txt.print("1 ok\n")
else {
txt.print("1 fail:")
txt.print_uw(uwsum)
txt.chrout('\n')
}
wsum = -30000
bb = 100
wsum += bb
bb = -50
wsum += bb
if wsum==-29950
txt.print("2 ok\n")
else {
txt.print("2 fail:")
txt.print_w(wsum)
txt.chrout('\n')
}
uwsum = 50000
ub=50
uwsum -= ub
ub=250
uwsum -= ub
if uwsum==49700
txt.print("3 ok\n")
else {
txt.print("3 fail:")
txt.print_uw(uwsum)
txt.chrout('\n')
}
wsum = -30000
bb = 100
wsum -= bb
bb = -50
wsum -= bb
if wsum==-30050
txt.print("4 ok\n")
else
txt.print("4 fail\n")
uwsum = 50000
bb=50
uwsum += bb as uword
bb=-100
uwsum += bb as uword
if uwsum==49950
txt.print("5 ok\n")
else
txt.print("5 fail\n")
uwsum = 50000
bb=50
uwsum -= bb as uword
bb=100
uwsum -= bb as uword
if uwsum==49850
txt.print("6 ok\n")
else {
txt.print("6 fail:")
txt.print_uw(uwsum)
txt.chrout('\n')
}
wsum = -30000
ub = 50
wsum += ub
ub = 250
wsum += ub
if wsum==-29700
txt.print("7 ok\n")
else {
txt.print("7 fail:")
txt.print_w(wsum)
txt.chrout('\n')
}
wsum = -30000
ub = 50
wsum -= ub
ub = 250
wsum -= ub
if wsum==-30300
txt.print("8 ok\n")
else {
txt.print("8 fail:")
txt.print_w(wsum)
txt.chrout('\n')
}
txt.chrout('\n')
uwsum = 50000
ub=0
uwsum += (50+ub)
uwsum += (250+ub)
if uwsum==50300
txt.print("1b ok\n")
else {
txt.print("1b fail:")
txt.print_uw(uwsum)
txt.chrout('\n')
}
bb = 0
wsum = -30000
wsum += (100+bb)
wsum += (-50+bb)
if wsum==-29950
txt.print("2b ok\n")
else {
txt.print("2b fail:")
txt.print_w(wsum)
txt.chrout('\n')
}
uwsum = 50000
uwsum -= (50+ub)
uwsum -= (250+ub)
if uwsum==49700
txt.print("3b ok\n")
else {
txt.print("3b fail:")
txt.print_uw(uwsum)
txt.chrout('\n')
}
wsum = -30000
wsum -= (100+bb)
wsum -= (-50+bb)
if wsum==-30050
txt.print("4b ok\n")
else
txt.print("4b fail\n")
uwsum = 50000
uwsum += (50+bb) as uword
uwsum += (-100+bb) as uword
if uwsum==49950
txt.print("5b ok\n")
else
txt.print("5b fail\n")
uwsum = 50000
uwsum -= (50+bb) as uword
uwsum -= (100+bb) as uword
if uwsum==49850
txt.print("6b ok\n")
else {
txt.print("6b fail:")
txt.print_uw(uwsum)
txt.chrout('\n')
}
wsum = -30000
wsum += (50+ub)
wsum += (250+ub)
if wsum==-29700
txt.print("7b ok\n")
else {
txt.print("7b fail:")
txt.print_w(wsum)
txt.chrout('\n')
}
wsum = -30000
wsum -= (50+ub)
wsum -= (250+ub)
if wsum==-30300
txt.print("8b ok\n")
else {
txt.print("8b fail:")
txt.print_w(wsum)
txt.chrout('\n')
}
txt.chrout('\n')
uwsum = 50000
uwsum += 50
uwsum += 250
if uwsum==50300
txt.print("1c ok\n")
else {
txt.print("1c fail:")
txt.print_uw(uwsum)
txt.chrout('\n')
}
wsum = -30000
wsum += 100
wsum += -50
if wsum==-29950
txt.print("2c ok\n")
else {
txt.print("2c fail:")
txt.print_w(wsum)
txt.chrout('\n')
}
uwsum = 50000
uwsum -= 50
uwsum -= 250
if uwsum==49700
txt.print("3c ok\n")
else {
txt.print("3c fail:")
txt.print_uw(uwsum)
txt.chrout('\n')
}
wsum = -30000
wsum -= 100
wsum -= -50
if wsum==-30050
txt.print("4c ok\n")
else
txt.print("4c fail\n")
uwsum = 50000
uwsum += 50 as uword
uwsum += -100 as uword
if uwsum==49950
txt.print("5c ok\n")
else
txt.print("5c fail\n")
uwsum = 50000
uwsum -= 50 as uword
uwsum -= 100 as uword
if uwsum==49850
txt.print("6c ok\n")
else {
txt.print("6c fail:")
txt.print_uw(uwsum)
txt.chrout('\n')
}
wsum = -30000
wsum += 50
wsum += 250
if wsum==-29700
txt.print("7c ok\n")
else {
txt.print("7c fail:")
txt.print_w(wsum)
txt.chrout('\n')
}
wsum = -30000
wsum -= 50
wsum -= 250
if wsum==-30300
txt.print("8c ok\n")
else {
txt.print("8c fail:")
txt.print_w(wsum)
txt.chrout('\n')
}
}
}

View File

@ -45,7 +45,9 @@ main {
perform_scroll = false perform_scroll = false
txt.scroll_left(true) txt.scroll_left(true)
if c64.RASTER & 1
; float the balloon
if rnd() & %10000
c64.SPXY[1] ++ c64.SPXY[1] ++
else else
c64.SPXY[1] -- c64.SPXY[1] --

1008
examples/cmp/comparisons.p8 Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,95 +0,0 @@
%import textio
%zeropage basicsafe
; Note: this program is compatible with C64 and CX16.
main {
sub start() {
byte v1
byte v2
ubyte cr
txt.print("signed byte ")
cr=v1==v2
cr=v1==v2
cr=v1==v2
cr=v1!=v2
cr=v1!=v2
cr=v1!=v2
cr=v1<v2
cr=v1<v2
cr=v1<v2
cr=v1<v2
cr=v1>v2
cr=v1>v2
cr=v1>v2
cr=v1>v2
cr=v1>v2
cr=v1<=v2
cr=v1<=v2
cr=v1<=v2
cr=v1<=v2
cr=v1>=v2
cr=v1>=v2
cr=v1>=v2
cr=v1>=v2
cr=v1>=v2
; comparisons:
v1=-20
v2=125
txt.print("v1=-20, v2=125\n")
compare()
v1=80
v2=80
txt.print("v1 = v2 = 80\n")
compare()
v1=20
v2=-111
txt.print("v1=20, v2=-111\n")
compare()
return
sub compare() {
txt.print(" == != < > <= >=\n")
if v1==v2
txt.print(" Q ")
else
txt.print(" . ")
if v1!=v2
txt.print(" Q ")
else
txt.print(" . ")
if v1<v2
txt.print(" Q ")
else
txt.print(" . ")
if v1>v2
txt.print(" Q ")
else
txt.print(" . ")
if v1<=v2
txt.print(" Q ")
else
txt.print(" . ")
if v1>=v2
txt.print(" Q ")
else
txt.print(" . ")
c64.CHROUT('\n')
}
}
}

View File

@ -1,112 +0,0 @@
%import textio
%import floats
%zeropage basicsafe
; Note: this program is compatible with C64 and CX16.
main {
sub start() {
float v1
float v2
ubyte cr
txt.print("floating point ")
cr=v1==v2
cr=v1==v2
cr=v1==v2
cr=v1!=v2
cr=v1!=v2
cr=v1!=v2
cr=v1<v2
cr=v1<v2
cr=v1<v2
cr=v1<v2
cr=v1>v2
cr=v1>v2
cr=v1>v2
cr=v1>v2
cr=v1>v2
cr=v1<=v2
cr=v1<=v2
cr=v1<=v2
cr=v1<=v2
cr=v1>=v2
cr=v1>=v2
cr=v1>=v2
cr=v1>=v2
cr=v1>=v2
; comparisons:
v1=20
v2=666.66
txt.print("v1=20, v2=666.66\n")
compare()
v1=-20
v2=666.66
txt.print("v1=-20, v2=666.66\n")
compare()
v1=666.66
v2=555.55
txt.print("v1=666.66, v2=555.55\n")
compare()
v1=3.1415
v2=-3.1415
txt.print("v1 = 3.1415, v2 = -3.1415\n")
compare()
v1=3.1415
v2=3.1415
txt.print("v1 = v2 = 3.1415\n")
compare()
v1=0
v2=0
txt.print("v1 = v2 = 0\n")
compare()
return
sub compare() {
txt.print(" == != < > <= >=\n")
if v1==v2
txt.print(" Q ")
else
txt.print(" . ")
if v1!=v2
txt.print(" Q ")
else
txt.print(" . ")
if v1<v2
txt.print(" Q ")
else
txt.print(" . ")
if v1>v2
txt.print(" Q ")
else
txt.print(" . ")
if v1<=v2
txt.print(" Q ")
else
txt.print(" . ")
if v1>=v2
txt.print(" Q ")
else
txt.print(" . ")
c64.CHROUT('\n')
}
}
}

View File

@ -1,96 +0,0 @@
%import textio
%zeropage basicsafe
; Note: this program is compatible with C64 and CX16.
main {
sub start() {
ubyte v1
ubyte v2
ubyte cr
txt.print("unsigned byte ")
cr=v1==v2
cr=v1==v2
cr=v1==v2
cr=v1!=v2
cr=v1!=v2
cr=v1!=v2
cr=v1<v2
cr=v1<v2
cr=v1<v2
cr=v1<v2
cr=v1>v2
cr=v1>v2
cr=v1>v2
cr=v1>v2
cr=v1>v2
cr=v1<=v2
cr=v1<=v2
cr=v1<=v2
cr=v1<=v2
cr=v1>=v2
cr=v1>=v2
cr=v1>=v2
cr=v1>=v2
cr=v1>=v2
; comparisons:
v1=20
v2=199
txt.print("v1=20, v2=199\n")
compare()
v1=80
v2=80
txt.print("v1 = v2 = 80\n")
compare()
v1=220
v2=10
txt.print("v1=220, v2=10\n")
compare()
return
sub compare() {
txt.print(" == != < > <= >=\n")
if v1==v2
txt.print(" Q ")
else
txt.print(" . ")
if v1!=v2
txt.print(" Q ")
else
txt.print(" . ")
if v1<v2
txt.print(" Q ")
else
txt.print(" . ")
if v1>v2
txt.print(" Q ")
else
txt.print(" . ")
if v1<=v2
txt.print(" Q ")
else
txt.print(" . ")
if v1>=v2
txt.print(" Q ")
else
txt.print(" . ")
c64.CHROUT('\n')
}
}
}

View File

@ -1,125 +0,0 @@
%import textio
%zeropage basicsafe
; Note: this program is compatible with C64 and CX16.
main {
sub start() {
uword v1
uword v2
ubyte cr
txt.print("unsigned word ")
cr=v1==v2
cr=v1==v2
cr=v1==v2
cr=v1!=v2
cr=v1!=v2
cr=v1!=v2
cr=v1<v2
cr=v1<v2
cr=v1<v2
cr=v1<v2
cr=v1>v2
cr=v1>v2
cr=v1>v2
cr=v1>v2
cr=v1>v2
cr=v1<=v2
cr=v1<=v2
cr=v1<=v2
cr=v1<=v2
cr=v1>=v2
cr=v1>=v2
cr=v1>=v2
cr=v1>=v2
cr=v1>=v2
; comparisons:
v1=20
v2=$00aa
txt.print("v1=20, v2=$00aa\n")
compare()
v1=20
v2=$ea00
txt.print("v1=20, v2=$ea00\n")
compare()
v1=$c400
v2=$22
txt.print("v1=$c400, v2=$22\n")
compare()
v1=$c400
v2=$2a00
txt.print("v1=$c400, v2=$2a00\n")
compare()
v1=$c433
v2=$2a00
txt.print("v1=$c433, v2=$2a00\n")
compare()
v1=$c433
v2=$2aff
txt.print("v1=$c433, v2=$2aff\n")
compare()
v1=$aabb
v2=$aabb
txt.print("v1 = v2 = aabb\n")
compare()
v1=$aa00
v2=$aa00
txt.print("v1 = v2 = aa00\n")
compare()
v1=$aa
v2=$aa
txt.print("v1 = v2 = aa\n")
compare()
return
sub compare() {
txt.print(" == != < > <= >=\n")
if v1==v2
txt.print(" Q ")
else
txt.print(" . ")
if v1!=v2
txt.print(" Q ")
else
txt.print(" . ")
if v1<v2
txt.print(" Q ")
else
txt.print(" . ")
if v1>v2
txt.print(" Q ")
else
txt.print(" . ")
if v1<=v2
txt.print(" Q ")
else
txt.print(" . ")
if v1>=v2
txt.print(" Q ")
else
txt.print(" . ")
c64.CHROUT('\n')
}
}
}

View File

@ -1,161 +0,0 @@
%import textio
%zeropage basicsafe
; Note: this program is compatible with C64 and CX16.
main {
sub start() {
word v1
word v2
ubyte cr
txt.print("signed word ")
cr=v1==v2
cr=v1==v2
cr=v1==v2
cr=v1!=v2
cr=v1!=v2
cr=v1!=v2
cr=v1<v2
cr=v1<v2
cr=v1<v2
cr=v1<v2
cr=v1>v2
cr=v1>v2
cr=v1>v2
cr=v1>v2
cr=v1>v2
cr=v1<=v2
cr=v1<=v2
cr=v1<=v2
cr=v1<=v2
cr=v1>=v2
cr=v1>=v2
cr=v1>=v2
cr=v1>=v2
cr=v1>=v2
; comparisons:
v1=20
v2=$00aa
txt.print("v1=20, v2=$00aa\n")
compare()
v1=20
v2=$7a00
txt.print("v1=20, v2=$7a00\n")
compare()
v1=$7400
v2=$22
txt.print("v1=$7400, v2=$22\n")
compare()
v1=$7400
v2=$2a00
txt.print("v1=$7400, v2=$2a00\n")
compare()
v1=$7433
v2=$2a00
txt.print("v1=$7433, v2=$2a00\n")
compare()
v1=$7433
v2=$2aff
txt.print("v1=$7433, v2=$2aff\n")
compare()
; with negative numbers:
v1=-512
v2=$00aa
txt.print("v1=-512, v2=$00aa\n")
compare()
v1=-512
v2=$7a00
txt.print("v1=-512, v2=$7a00\n")
compare()
v1=$7400
v2=-512
txt.print("v1=$7400, v2=-512\n")
compare()
v1=-20000
v2=-1000
txt.print("v1=-20000, v2=-1000\n")
compare()
v1=-1000
v2=-20000
txt.print("v1=-1000, v2=-20000\n")
compare()
v1=-1
v2=32767
txt.print("v1=-1, v2=32767\n")
compare()
v1=32767
v2=-1
txt.print("v1=32767, v2=-1\n")
compare()
v1=$7abb
v2=$7abb
txt.print("v1 = v2 = 7abb\n")
compare()
v1=$7a00
v2=$7a00
txt.print("v1 = v2 = 7a00\n")
compare()
v1=$aa
v2=$aa
txt.print("v1 = v2 = aa\n")
compare()
return
sub compare() {
txt.print(" == != < > <= >=\n")
if v1==v2
txt.print(" Q ")
else
txt.print(" . ")
if v1!=v2
txt.print(" Q ")
else
txt.print(" . ")
if v1<v2
txt.print(" Q ")
else
txt.print(" . ")
if v1>v2
txt.print(" Q ")
else
txt.print(" . ")
if v1<=v2
txt.print(" Q ")
else
txt.print(" . ")
if v1>=v2
txt.print(" Q ")
else
txt.print(" . ")
c64.CHROUT('\n')
}
}
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -2,7 +2,6 @@
%import syslib %import syslib
%import textio %import textio
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.

View File

@ -2,6 +2,7 @@
%import textio %import textio
%import syslib %import syslib
%zeropage basicsafe %zeropage basicsafe
%option no_sysinit
%launcher none %launcher none
%address 50000 %address 50000

View File

@ -1,6 +1,7 @@
%target c64 %target c64
%import textio %import textio
%import syslib %import syslib
%option no_sysinit
%zeropage basicsafe %zeropage basicsafe
; This example shows the directory contents of disk drive 8. ; This example shows the directory contents of disk drive 8.

View File

@ -11,16 +11,7 @@ main {
sub start() { sub start() {
txt.print("calculating mandelbrot fractal...") txt.print("calculating mandelbrot fractal...")
c64.SETTIM(0, 0, 0)
%asm {{
stx P8ZP_SCRATCH_REG
; reset the jiffy clock
ldx #0
ldy #0
lda #0
jsr c64.SETTIM
ldx P8ZP_SCRATCH_REG
}}
ubyte pixelx ubyte pixelx
ubyte pixely ubyte pixely

View File

@ -2,16 +2,12 @@
%import syslib %import syslib
%import textio %import textio
; converted from plasma test program for cc65.
; which is (w)2001 by groepaz/hitmen
;
; Cleanup and porting to C by Ullrich von Bassewitz.
; Converted to prog8 by Irmen de Jong.
;/*****************************************************************************\
;** plasma test program for cc65. **
;** **
;** (w)2001 by groepaz/hitmen **
;** **
;** Cleanup and porting by Ullrich von Bassewitz. **
;** Converted to prog8 by Irmen de Jong **
;** **
;\*****************************************************************************/
main { main {
const uword SCREEN1 = $E000 const uword SCREEN1 = $E000
@ -73,13 +69,17 @@ main {
} }
c2A += 2 c2A += 2
c2B -= 3 c2B -= 3
for y in 24 downto 0 { for y in 24 downto 0 {
for x in 39 downto 0 { for x in 39 downto 0 {
@(screen) = xbuf[x] + ybuf[y] ; using a temp var here to enable expression optimization that can't be done on a 'problematic' ROM/RAM memory location
ubyte cc = xbuf[x] + ybuf[y]
@(screen) = cc
; this is the fastest way to do this inner part:
; %asm {{ ; %asm {{
; ldy x ; ldy i
; lda xbuf,y ; lda xbuf,y
; ldy y ; ldy ii
; clc ; clc
; adc ybuf,y ; adc ybuf,y
; ldy #0 ; ldy #0

View File

@ -31,7 +31,8 @@ main {
sub start() { sub start() {
@(650) = 128 ; set all keys to repeat c64.disable_runstop_and_charsetswitch()
;@(650) = 128 ; set all keys to repeat
sound.init() sound.init()
newGame() newGame()
drawBoard() drawBoard()

View File

@ -1,32 +1,57 @@
%import syslib
; %import graphics
%import textio %import textio
%import syslib
%import floats
%zeropage basicsafe %zeropage basicsafe
main { main {
sub start() { struct Color {
ubyte red
ubyte v = 1 ubyte green
@($c000+v) = 10 ubyte blue
txt.print_ub(@($c001))
txt.chrout('\n')
@($c000+v) ++
txt.print_ub(@($c001))
txt.chrout('\n')
@($c000+v) += 10
txt.print_ub(@($c001))
txt.chrout('\n')
@($c000+v) *= 10
txt.print_ub(@($c001))
txt.chrout('\n')
; @($c000) *= 99 ; TODO implement
} }
Color c1 = [11,22,33]
Color c2 = [11,22,33]
Color c3 = [11,22,33]
uword[] colors = [ c1, c2, c3]
sub start() {
txt.print_ub(c1.red)
txt.chrout('\n')
txt.print_ub(c1.green)
txt.chrout('\n')
txt.print_ub(c1.blue)
txt.chrout('\n')
txt.chrout('\n')
c1 = [99,88,77]
txt.print_ub(c1.red)
txt.chrout('\n')
txt.print_ub(c1.green)
txt.chrout('\n')
txt.print_ub(c1.blue)
txt.chrout('\n')
testX()
}
asmsub testX() {
%asm {{
stx _saveX
lda #13
jsr txt.chrout
lda _saveX
jsr txt.print_ub
lda #13
jsr txt.chrout
ldx _saveX
rts
_saveX .byte 0
}}
}
} }

View File

@ -2,7 +2,6 @@
%import syslib %import syslib
%zeropage basicsafe %zeropage basicsafe
spritedata $0a00 { spritedata $0a00 {
; 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.