diff --git a/compiler/res/prog8lib/c64utils.p8 b/compiler/res/prog8lib/c64utils.p8 index e6d486c8e..ebcd9488d 100644 --- a/compiler/res/prog8lib/c64utils.p8 +++ b/compiler/res/prog8lib/c64utils.p8 @@ -47,9 +47,10 @@ asmsub byte2decimal (ubyte value @ A) -> clobbers() -> (ubyte @ Y, ubyte @ X, }} } -asmsub ubyte2hex (ubyte value @ A) -> clobbers(X) -> (ubyte @ A, ubyte @ Y) { +asmsub ubyte2hex (ubyte value @ A) -> clobbers() -> (ubyte @ A, ubyte @ Y) { ; ---- A to hex string in AY (first hex char in A, second hex char in Y) %asm {{ + stx c64.SCRATCH_ZPREGX pha and #$0f tax @@ -61,6 +62,7 @@ asmsub ubyte2hex (ubyte value @ A) -> clobbers(X) -> (ubyte @ A, ubyte @ Y) { lsr a tax lda hex_digits,x + ldx c64.SCRATCH_ZPREGX rts hex_digits .text "0123456789abcdef" ; can probably be reused for other stuff as well @@ -69,13 +71,13 @@ hex_digits .text "0123456789abcdef" ; can probably be reused for other stuff as str word2hex_output = "1234" ; 0-terminated, to make printing easier -asmsub uword2hex (uword value @ AY) -> clobbers(A,X,Y) -> () { +asmsub uword2hex (uword value @ AY) -> clobbers(A,Y) -> () { ; ---- convert 16 bit uword in A/Y into 4-character hexadecimal string into memory 'word2hex_output' %asm {{ sta c64.SCRATCH_ZPREG tya jsr ubyte2hex - stx word2hex_output + sta word2hex_output sty word2hex_output+1 lda c64.SCRATCH_ZPREG jsr ubyte2hex @@ -86,7 +88,7 @@ asmsub uword2hex (uword value @ AY) -> clobbers(A,X,Y) -> () { } ubyte[3] word2bcd_bcdbuff = [0, 0, 0] -asmsub uword2bcd (uword value @ AY) -> clobbers(A,X) -> () { +asmsub uword2bcd (uword value @ AY) -> clobbers(A,Y) -> () { ; Convert an 16 bit binary value to BCD ; ; This function converts a 16 bit binary value in A/Y into a 24 bit BCD. It @@ -103,7 +105,7 @@ asmsub uword2bcd (uword value @ AY) -> clobbers(A,X) -> () { sta word2bcd_bcdbuff+0 sta word2bcd_bcdbuff+1 sta word2bcd_bcdbuff+2 - ldx #16 ; the number of source bits + ldy #16 ; the number of source bits - asl c64.SCRATCH_ZPB1 ; shift out one bit rol c64.SCRATCH_ZPREG @@ -116,7 +118,7 @@ asmsub uword2bcd (uword value @ AY) -> clobbers(A,X) -> () { lda word2bcd_bcdbuff+2 ; ... thru whole result adc word2bcd_bcdbuff+2 sta word2bcd_bcdbuff+2 - dex ; and repeat for next bit + dey ; and repeat for next bit bne - cld ; back to binary cli ; enable interrupts again @@ -126,7 +128,7 @@ asmsub uword2bcd (uword value @ AY) -> clobbers(A,X) -> () { ubyte[5] word2decimal_output = 0 -asmsub uword2decimal (uword value @ AY) -> clobbers(A,X,Y) -> () { +asmsub uword2decimal (uword value @ AY) -> clobbers(A,Y) -> () { ; ---- convert 16 bit uword in A/Y into decimal string into memory 'word2decimal_output' %asm {{ jsr uword2bcd @@ -471,11 +473,12 @@ _loop sta c64.Colors,y } -asmsub scroll_left_full (ubyte alsocolors @ Pc) -> clobbers(A, X, Y) -> () { +asmsub scroll_left_full (ubyte alsocolors @ Pc) -> clobbers(A, Y) -> () { ; ---- scroll the whole screen 1 character to the left ; contents of the rightmost column are unchanged, you should clear/refill this yourself ; Carry flag determines if screen color data must be scrolled too %asm {{ + stx c64.SCRATCH_ZPREGX bcs + jmp _scroll_screen @@ -525,16 +528,18 @@ _scroll_screen ; scroll the screen memory dey bpl - + ldx c64.SCRATCH_ZPREGX rts }} } -asmsub scroll_right_full (ubyte alsocolors @ Pc) -> clobbers(A,X) -> () { +asmsub scroll_right_full (ubyte alsocolors @ Pc) -> clobbers(A) -> () { ; ---- scroll the whole screen 1 character to the right ; contents of the leftmost column are unchanged, you should clear/refill this yourself ; Carry flag determines if screen color data must be scrolled too %asm {{ + stx c64.SCRATCH_ZPREGX bcs + jmp _scroll_screen @@ -576,16 +581,18 @@ _scroll_screen ; scroll the screen memory dex bpl - + ldx c64.SCRATCH_ZPREGX rts }} } -asmsub scroll_up_full (ubyte alsocolors @ Pc) -> clobbers(A,X) -> () { +asmsub scroll_up_full (ubyte alsocolors @ Pc) -> clobbers(A) -> () { ; ---- scroll the whole screen 1 character up ; contents of the bottom row are unchanged, you should refill/clear this yourself ; Carry flag determines if screen color data must be scrolled too %asm {{ + stx c64.SCRATCH_ZPREGX bcs + jmp _scroll_screen @@ -627,16 +634,18 @@ _scroll_screen ; scroll the screen memory dex bpl - + ldx c64.SCRATCH_ZPREGX rts }} } -asmsub scroll_down_full (ubyte alsocolors @ Pc) -> clobbers(A,X) -> () { +asmsub scroll_down_full (ubyte alsocolors @ Pc) -> clobbers(A) -> () { ; ---- scroll the whole screen 1 character down ; contents of the top row are unchanged, you should refill/clear this yourself ; Carry flag determines if screen color data must be scrolled too %asm {{ + stx c64.SCRATCH_ZPREGX bcs + jmp _scroll_screen @@ -678,6 +687,7 @@ _scroll_screen ; scroll the screen memory dex bpl - + ldx c64.SCRATCH_ZPREGX rts }} } @@ -693,7 +703,7 @@ asmsub print (str text @ AY) -> clobbers(A,Y) -> () { sta c64.SCRATCH_ZPB1 sty c64.SCRATCH_ZPREG ldy #0 -- lda (c64.SCRATCH_ZPB1),y +- lda (c64.SCRATCH_ZPB1),y beq + jsr c64.CHROUT iny @@ -703,11 +713,12 @@ asmsub print (str text @ AY) -> clobbers(A,Y) -> () { } -asmsub print_p (str_p text @ AY) -> clobbers(A,X) -> (ubyte @ Y) { +asmsub print_p (str_p text @ AY) -> clobbers(A) -> (ubyte @ Y) { ; ---- print pstring (length as first byte) from A/Y, returns str len in Y %asm {{ sta c64.SCRATCH_ZPB1 sty c64.SCRATCH_ZPREG + stx c64.SCRATCH_ZPREGX ldy #0 lda (c64.SCRATCH_ZPB1),y beq + @@ -717,14 +728,16 @@ asmsub print_p (str_p text @ AY) -> clobbers(A,X) -> (ubyte @ Y) { jsr c64.CHROUT dex bne - -+ rts ; output string length is in Y ++ ldx c64.SCRATCH_ZPREGX + rts ; output string length is in Y }} } -asmsub print_ub0 (ubyte value @ A) -> clobbers(A,X,Y) -> () { +asmsub print_ub0 (ubyte value @ A) -> clobbers(A,Y) -> () { ; ---- print the ubyte in A in decimal form, with left padding 0s (3 positions total) %asm {{ + stx c64.SCRATCH_ZPREGX jsr c64utils.ubyte2decimal pha tya @@ -732,14 +745,17 @@ asmsub print_ub0 (ubyte value @ A) -> clobbers(A,X,Y) -> () { txa jsr c64.CHROUT pla - jmp c64.CHROUT + jsr c64.CHROUT + ldx c64.SCRATCH_ZPREGX + rts }} } -asmsub print_ub (ubyte value @ A) -> clobbers(A,X,Y) -> () { +asmsub print_ub (ubyte value @ A) -> clobbers(A,Y) -> () { ; ---- print the ubyte in A in decimal form, without left padding 0s %asm {{ + stx c64.SCRATCH_ZPREGX jsr c64utils.ubyte2decimal _print_byte_digits pha @@ -754,13 +770,16 @@ _print_hundreds tya _print_tens txa jsr c64.CHROUT pla - jmp c64.CHROUT + jsr c64.CHROUT + ldx c64.SCRATCH_ZPREGX + rts }} } -asmsub print_b (byte value @ A) -> clobbers(A,X,Y) -> () { +asmsub print_b (byte value @ A) -> clobbers(A,Y) -> () { ; ---- print the byte in A in decimal form, without left padding 0s %asm {{ + stx c64.SCRATCH_ZPREGX pha cmp #0 bpl + @@ -768,14 +787,17 @@ asmsub print_b (byte value @ A) -> clobbers(A,X,Y) -> () { jsr c64.CHROUT + pla jsr c64utils.byte2decimal - jmp print_ub._print_byte_digits + jsr print_ub._print_byte_digits + ldx c64.SCRATCH_ZPREGX + rts }} } -asmsub print_ubhex (ubyte prefix @ Pc, ubyte value @ A) -> clobbers(A,X,Y) -> () { +asmsub print_ubhex (ubyte prefix @ Pc, ubyte value @ A) -> clobbers(A,Y) -> () { ; ---- print the ubyte in A in hex form (if Carry is set, a radix prefix '$' is printed as well) %asm {{ + stx c64.SCRATCH_ZPREGX bcc + pha lda #'$' @@ -784,14 +806,17 @@ asmsub print_ubhex (ubyte prefix @ Pc, ubyte value @ A) -> clobbers(A,X,Y) -> + jsr c64utils.ubyte2hex jsr c64.CHROUT tya - jmp c64.CHROUT + jsr c64.CHROUT + ldx c64.SCRATCH_ZPREGX + rts }} } -asmsub print_ubbin (ubyte prefix @ Pc, ubyte value @ A) -> clobbers(A,X,Y) ->() { +asmsub print_ubbin (ubyte prefix @ Pc, ubyte value @ A) -> clobbers(A,Y) ->() { ; ---- print the ubyte in A in binary form (if Carry is set, a radix prefix '%' is printed as well) %asm {{ + stx c64.SCRATCH_ZPREGX sta c64.SCRATCH_ZPB1 bcc + lda #'%' @@ -804,12 +829,13 @@ asmsub print_ubbin (ubyte prefix @ Pc, ubyte value @ A) -> clobbers(A,X,Y) ->( + jsr c64.CHROUT dey bne - + ldx c64.SCRATCH_ZPREGX rts }} } -asmsub print_uwbin (ubyte prefix @ Pc, uword value @ AY) -> clobbers(A,X,Y) ->() { +asmsub print_uwbin (ubyte prefix @ Pc, uword value @ AY) -> clobbers(A,Y) ->() { ; ---- print the uword in A/Y in binary form (if Carry is set, a radix prefix '%' is printed as well) %asm {{ pha @@ -822,7 +848,7 @@ asmsub print_uwbin (ubyte prefix @ Pc, uword value @ AY) -> clobbers(A,X,Y) -> } -asmsub print_uwhex (ubyte prefix @ Pc, uword value @ AY) -> clobbers(A,X,Y) -> () { +asmsub print_uwhex (ubyte prefix @ Pc, uword value @ AY) -> clobbers(A,Y) -> () { ; ---- print the uword in A/Y in hexadecimal form (4 digits) ; (if Carry is set, a radix prefix '$' is printed as well) %asm {{ @@ -836,7 +862,7 @@ asmsub print_uwhex (ubyte prefix @ Pc, uword value @ AY) -> clobbers(A,X,Y) -> } -asmsub print_uw0 (uword value @ AY) -> clobbers(A,X,Y) -> () { +asmsub print_uw0 (uword value @ AY) -> clobbers(A,Y) -> () { ; ---- print the uword in A/Y in decimal form, with left padding 0s (5 positions total) %asm {{ jsr c64utils.uword2decimal @@ -851,7 +877,7 @@ asmsub print_uw0 (uword value @ AY) -> clobbers(A,X,Y) -> () { } -asmsub print_uw (uword value @ AY) -> clobbers(A,X,Y) -> () { +asmsub print_uw (uword value @ AY) -> clobbers(A,Y) -> () { ; ---- print the uword in A/Y in decimal form, without left padding 0s %asm {{ jsr c64utils.uword2decimal @@ -883,7 +909,7 @@ _pr_decimal }} } -asmsub print_w (word value @ AY) -> clobbers(A,X,Y) -> () { +asmsub print_w (word value @ AY) -> clobbers(A,Y) -> () { ; ---- print the (signed) word in A/Y in decimal form, without left padding 0s %asm {{ cpy #0 @@ -904,7 +930,7 @@ asmsub print_w (word value @ AY) -> clobbers(A,X,Y) -> () { }} } -asmsub input_chars (uword buffer @ AY) -> clobbers(A, X) -> (ubyte @ Y) { +asmsub input_chars (uword buffer @ AY) -> clobbers(A) -> (ubyte @ Y) { ; ---- Input a string (max. 80 chars) from the keyboard. Returns length in Y. ; It assumes the keyboard is selected as I/O channel! diff --git a/compiler/src/prog8/CompilerMain.kt b/compiler/src/prog8/CompilerMain.kt index fbe65454d..c043f3ad8 100644 --- a/compiler/src/prog8/CompilerMain.kt +++ b/compiler/src/prog8/CompilerMain.kt @@ -88,7 +88,7 @@ private fun compileMain(args: Array) { } //println(" time2: $time2") val time3 = measureTimeMillis { - StatementReorderer(namespace, heap).process(moduleAst) // reorder statements to please the compiler later + moduleAst.reorderStatements(namespace,heap) // reorder statements to please the compiler later } //println(" time3: $time3") val time4 = measureTimeMillis { diff --git a/compiler/src/prog8/ast/AST.kt b/compiler/src/prog8/ast/AST.kt index f84a06e0d..5cb90b904 100644 --- a/compiler/src/prog8/ast/AST.kt +++ b/compiler/src/prog8/ast/AST.kt @@ -692,7 +692,7 @@ class VarDecl(val type: VarDeclType, return "VarDecl(name=$name, vartype=$type, datatype=$datatype, value=$value, pos=$position)" } - fun asDefaultValueDecl(): VarDecl { + fun asDefaultValueDecl(parent: Node?): VarDecl { val constValue = when(declaredDatatype) { DataType.UBYTE -> LiteralValue(DataType.UBYTE, 0, position=position) DataType.BYTE -> LiteralValue(DataType.BYTE, 0, position=position) @@ -701,7 +701,10 @@ class VarDecl(val type: VarDeclType, DataType.FLOAT -> LiteralValue(DataType.FLOAT, floatvalue=0.0, position=position) else -> throw FatalAstException("can only set a default value for a numeric type") } - return VarDecl(type, declaredDatatype, arrayspec, name, constValue, position) + val decl = VarDecl(type, declaredDatatype, arrayspec, name, constValue, position) + if(parent!=null) + decl.linkParents(parent) + return decl } } diff --git a/compiler/src/prog8/ast/AstChecker.kt b/compiler/src/prog8/ast/AstChecker.kt index 8c7465569..cd91bcb2a 100644 --- a/compiler/src/prog8/ast/AstChecker.kt +++ b/compiler/src/prog8/ast/AstChecker.kt @@ -46,7 +46,7 @@ fun printWarning(msg: String, position: Position, detailInfo: String?=null) { } -class AstChecker(private val namespace: INameScope, +private class AstChecker(private val namespace: INameScope, private val compilerOptions: CompilationOptions, private val heap: HeapValues) : IAstProcessor { private val checkResult: MutableList = mutableListOf() diff --git a/compiler/src/prog8/ast/AstIdentifiersChecker.kt b/compiler/src/prog8/ast/AstIdentifiersChecker.kt index 8f2acfb85..2bb6b1d66 100644 --- a/compiler/src/prog8/ast/AstIdentifiersChecker.kt +++ b/compiler/src/prog8/ast/AstIdentifiersChecker.kt @@ -40,7 +40,7 @@ fun Module.checkIdentifiers(heap: HeapValues): MutableMap { } -class AstIdentifiersChecker(val heap: HeapValues) : IAstProcessor { +private class AstIdentifiersChecker(val heap: HeapValues) : IAstProcessor { private val checkResult: MutableList = mutableListOf() var symbols: MutableMap = mutableMapOf() diff --git a/compiler/src/prog8/ast/AstRecursionChecker.kt b/compiler/src/prog8/ast/AstRecursionChecker.kt index 5623bb3ac..c839f9566 100644 --- a/compiler/src/prog8/ast/AstRecursionChecker.kt +++ b/compiler/src/prog8/ast/AstRecursionChecker.kt @@ -11,7 +11,7 @@ fun Module.checkRecursion(namespace: INameScope) { } -class DirectedGraph { +private class DirectedGraph { private val graph = mutableMapOf>() private var uniqueVertices = mutableSetOf() val numVertices : Int @@ -81,7 +81,7 @@ class DirectedGraph { } -class AstRecursionChecker(private val namespace: INameScope) : IAstProcessor { +private class AstRecursionChecker(private val namespace: INameScope) : IAstProcessor { private val callGraph = DirectedGraph() fun result(): List { diff --git a/compiler/src/prog8/ast/ImportedAstChecker.kt b/compiler/src/prog8/ast/ImportedAstChecker.kt index 6280c63d8..877bd2b17 100644 --- a/compiler/src/prog8/ast/ImportedAstChecker.kt +++ b/compiler/src/prog8/ast/ImportedAstChecker.kt @@ -13,7 +13,7 @@ fun Module.checkImportedValid() { } -class ImportedAstChecker : IAstProcessor { +private class ImportedAstChecker : IAstProcessor { private val checkResult: MutableList = mutableListOf() fun result(): List { diff --git a/compiler/src/prog8/ast/StmtReorderer.kt b/compiler/src/prog8/ast/StmtReorderer.kt index 9add6427b..fc87e6227 100644 --- a/compiler/src/prog8/ast/StmtReorderer.kt +++ b/compiler/src/prog8/ast/StmtReorderer.kt @@ -2,7 +2,15 @@ package prog8.ast import prog8.compiler.HeapValues -class StatementReorderer(private val namespace: INameScope, private val heap: HeapValues): IAstProcessor { +fun Module.reorderStatements(namespace: INameScope, heap: HeapValues) { + val initvalueCreator = VarInitValueCreator() + this.process(initvalueCreator) + + val checker = StatementReorderer(namespace, heap) + this.process(checker) +} + +private class StatementReorderer(private val namespace: INameScope, private val heap: HeapValues): IAstProcessor { // Reorders the statements in a way the compiler needs. // - 'main' block must be the very first statement UNLESS it has an address set. // - blocks are ordered by address, where blocks without address are put at the end. @@ -14,9 +22,7 @@ class StatementReorderer(private val namespace: INameScope, private val heap: He // - the 'start' subroutine in the 'main' block will be moved to the top immediately following the directives. // - all other subroutines will be moved to the end of their block. - private val directivesToMove = setOf("%output", "%launcher", "%zeropage", "%zpreserved", "%address", "%option") - private val vardeclsToAdd = mutableMapOf>() override fun process(module: Module) { super.process(module) @@ -37,14 +43,6 @@ class StatementReorderer(private val namespace: INameScope, private val heap: He module.statements.removeAll(directives) module.statements.addAll(0, directives) - // add any new vardecls - // @todo doing it this late causes problems with namespace lookups elsewhere in sortConstantAssignments - for(decl in vardeclsToAdd) - for(d in decl.value) { - d.linkParents(decl.key as Node) - decl.key.statements.add(0, d) - } - sortConstantAssignments(module.statements) } @@ -155,7 +153,7 @@ class StatementReorderer(private val namespace: INameScope, private val heap: He } private fun sortConstantAssignmentSequence(first: Assignment, stmtIter: MutableIterator): Pair, IStatement?> { - val sequence= mutableListOf(first) + val sequence= mutableListOf(first) var trailing: IStatement? = null while(stmtIter.hasNext()) { val next = stmtIter.next() @@ -176,20 +174,40 @@ class StatementReorderer(private val namespace: INameScope, private val heap: He return Pair(sorted, trailing) } +} + + +private class VarInitValueCreator: IAstProcessor { + // Replace the var decl with an assignment and add a new vardecl with the default constant value. + // This makes sure the variables get reset to the intended value on a next run of the program. + // Variable decls without a value don't get this treatment, which means they retain the last + // value they had when restarting the program. + // This is done in a separate step because it interferes with the namespace lookup of symbols + // in other ast processors. + + private val vardeclsToAdd = mutableMapOf>() + + override fun process(module: Module) { + super.process(module) + + // add any new vardecls to the various scopes + for(decl in vardeclsToAdd) + for(d in decl.value) { + d.linkParents(decl.key as Node) + decl.key.statements.add(0, d) + } + } + override fun process(decl: VarDecl): IStatement { super.process(decl) if(decl.type!=VarDeclType.VAR || decl.value==null) return decl - // Replace the var decl with an assignment and add a new vardecl with the default constant value. - // This makes sure the variables get reset to the intended value on a next run of the program. - // Variable decls without a value don't get this treatment, which means they retain the last - // value they had when restarting the program. if(decl.datatype in NumericDatatypes) { val scope = decl.definingScope() if(scope !in vardeclsToAdd) vardeclsToAdd[scope] = mutableListOf() - vardeclsToAdd[scope]!!.add(decl.asDefaultValueDecl()) + vardeclsToAdd[scope]!!.add(decl.asDefaultValueDecl(null)) val declvalue = decl.value!! val value = if(declvalue is LiteralValue) { @@ -207,4 +225,5 @@ class StatementReorderer(private val namespace: INameScope, private val heap: He } return decl } + }