From 0c64d7ffe5b2748d08069bc97ef70ad0c57b58f2 Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Tue, 4 Dec 2018 22:30:35 +0100 Subject: [PATCH] more asm, constants now also outputted in asm source --- compiler/examples/numbergame-c64.p8 | 5 +- compiler/examples/numbergame-novm.p8 | 8 +- compiler/examples/test.p8 | 66 ++----------- compiler/src/prog8/ast/AstChecker.kt | 43 +++++---- compiler/src/prog8/compiler/Zeropage.kt | 2 +- .../intermediate/IntermediateProgram.kt | 4 +- .../src/prog8/compiler/target/c64/AsmGen.kt | 69 ++++++++++++-- prog8lib/c64utils.p8 | 30 +++--- prog8lib/prog8lib.p8 | 94 +++++++++++++++++-- 9 files changed, 200 insertions(+), 121 deletions(-) diff --git a/compiler/examples/numbergame-c64.p8 b/compiler/examples/numbergame-c64.p8 index 293f42367..2c322d56c 100644 --- a/compiler/examples/numbergame-c64.p8 +++ b/compiler/examples/numbergame-c64.p8 @@ -1,7 +1,6 @@ %output prg %import c64lib %import c64utils -%import mathlib ~ main { sub start() { @@ -28,7 +27,7 @@ c64.MUL10() ; .. and now *100 c64.FADDH() ; add 0.5.. c64.FADDH() ; and again, so +1 total - A, Y = c64flt.GETADRAY() ; @todo fix return value type check "cannot assign word to byte, use msb() or lsb()" + A, Y = c64flt.GETADRAY() secretnumber = A c64scr.print_string("I am thinking of a number from 1 to 100!You'll have to guess it!\n") @@ -44,7 +43,7 @@ ask_guess: c64.CHROUT('\n') freadstr_arg = guess c64.FREADSTR(A) - A, Y = c64flt.GETADRAY() ; @todo fix return value type check "cannot assign word to byte, use msb() or lsb()" + A, Y = c64flt.GETADRAY() if(A==secretnumber) { c64scr.print_string("\nThat's my number, impressive!\n") goto goodbye diff --git a/compiler/examples/numbergame-novm.p8 b/compiler/examples/numbergame-novm.p8 index ff08f2807..fd6f3fe6a 100644 --- a/compiler/examples/numbergame-novm.p8 +++ b/compiler/examples/numbergame-novm.p8 @@ -8,18 +8,18 @@ c64scr.print_string("Let's play a number guessing game!\n") c64scr.print_string("Enter your name: ") - vm_input_str(name) + Y=c64scr.input_chars(name) c64scr.print_string("\nHello, ") c64scr.print_string(name) c64scr.print_string(".\nI am thinking of a number from 1 to 100! You'll have to guess it!\n") for ubyte attempts_left in 10 to 1 step -1 { c64scr.print_string("\nYou have ") - vm_write_num(attempts_left) + c64scr.print_byte_decimal(attempts_left) c64scr.print_string(" guess") if attempts_left>1 c64scr.print_string("es") c64scr.print_string(" left. What is your next guess? ") - vm_input_str(guess) + Y=c64scr.input_chars(guess) ubyte guessednumber = str2ubyte(guess) if guessednumber==secretnumber { c64scr.print_string("\nYou guessed it, impressive!\n") @@ -35,7 +35,7 @@ } c64scr.print_string("\nToo bad! My number was: ") - vm_write_num(secretnumber) + c64scr.print_byte_decimal(secretnumber) c64scr.print_string(".\n") return } diff --git a/compiler/examples/test.p8 b/compiler/examples/test.p8 index 3c9fefdbf..2beaa8756 100644 --- a/compiler/examples/test.p8 +++ b/compiler/examples/test.p8 @@ -1,63 +1,11 @@ +%output raw +%launcher none %import c64utils -%option enable_floats - -~ main { - -sub start() { - -; memory ubyte[40] firstScreenLine = $0400 -; word ww=333 -; -; Y=33 -; -; if(Y>3) { -; A=99 -; } else { -; A=100 -; } -; -; if(ww>33) { -; A=99 -; } else { -; A=100 -; } -; -; for ubyte c in 10 to 30 { -; firstScreenLine[c] = c -; } - - - ubyte bv = 99 - word wv = 14443 - float fv = 3.14 - memory ubyte mb = $c000 - memory word mw = $c100 - memory float mf = $c200 - memory ubyte[10] mba = $d000 - memory word[10] mwa = $d100 - memory float[10] mfa = $d200 - memory str mstr = $d300 - memory str_p mstrp = $d300 - memory str_s mstrs = $d300 - memory str_ps mstrps = $d300 - - mb = 5 - mb = Y - mb = bv - mw = 22334 - mw = wv - mf = 4.45 - mf = fv - mba[9] = 5 - mba[9] = Y - mba[9] = bv - mwa[9] = 22334 - mwa[9] = wv - mfa[9] = 5.667 - mfa[9] = fv - - return -} +~ main { + memory ubyte[40*25] Screen = $0400 + sub start() { + A, Y = c64flt.GETADRAY() + } } diff --git a/compiler/src/prog8/ast/AstChecker.kt b/compiler/src/prog8/ast/AstChecker.kt index f6b328f6e..546bff02c 100644 --- a/compiler/src/prog8/ast/AstChecker.kt +++ b/compiler/src/prog8/ast/AstChecker.kt @@ -413,7 +413,7 @@ class AstChecker(private val namespace: INameScope, } } else - checkAssignmentCompatible(targetDatatype, sourceDatatype, assignment.value, assignment.position) + checkAssignmentCompatible(targetDatatype, sourceDatatype, assignment.value, assignment.targets, assignment.position) } } return assignment @@ -482,13 +482,13 @@ class AstChecker(private val namespace: INameScope, when(decl.datatype) { DataType.ARRAY_B, DataType.ARRAY_UB -> if(arraySize > 256) - err("byte arrayspec length must be 1-256") + err("byte array length must be 1-256") DataType.ARRAY_W, DataType.ARRAY_UW -> if(arraySize > 128) - err("word arrayspec length must be 1-128") + err("word array length must be 1-128") DataType.ARRAY_F -> if(arraySize > 51) - err("float arrayspec length must be 1-51") + err("float array length must be 1-51") else -> {} } } @@ -891,13 +891,13 @@ class AstChecker(private val namespace: INameScope, val arraySize = value.arrayvalue?.size ?: heap.get(value.heapId!!).arraysize if(arraySpecSize!=null && arraySpecSize>0) { if(arraySpecSize<1 || arraySpecSize>256) - return err("byte arrayspec length must be 1-256") + return err("byte array length must be 1-256") val constX = arrayspec.x.constValue(namespace, heap) if(constX?.asIntegerValue==null) - return err("arrayspec size specifier must be constant integer value") + return err("array size specifier must be constant integer value") val expectedSize = constX.asIntegerValue if (arraySize != expectedSize) - return err("initializer arrayspec size mismatch (expecting $expectedSize, got $arraySize)") + return err("initializer array size mismatch (expecting $expectedSize, got $arraySize)") return true } return err("invalid byte array size, must be 1-256") @@ -911,18 +911,18 @@ class AstChecker(private val namespace: INameScope, val arraySize = value.arrayvalue?.size ?: heap.get(value.heapId!!).arraysize if(arraySpecSize!=null && arraySpecSize>0) { if(arraySpecSize<1 || arraySpecSize>128) - return err("word arrayspec length must be 1-128") + return err("word array length must be 1-128") val constX = arrayspec.x.constValue(namespace, heap) if(constX?.asIntegerValue==null) - return err("arrayspec size specifier must be constant integer value") + return err("array size specifier must be constant integer value") val expectedSize = constX.asIntegerValue if (arraySize != expectedSize) - return err("initializer arrayspec size mismatch (expecting $expectedSize, got $arraySize)") + return err("initializer array size mismatch (expecting $expectedSize, got $arraySize)") return true } - return err("invalid word arrayspec size, must be 1-128") + return err("invalid word array size, must be 1-128") } - return err("invalid word arrayspec initialization value ${value.type}, expected $targetDt") + return err("invalid word array initialization value ${value.type}, expected $targetDt") } DataType.ARRAY_F -> { // value may be either a single float, or a float arrayspec @@ -931,15 +931,15 @@ class AstChecker(private val namespace: INameScope, val arraySpecSize = arrayspec.size() if(arraySpecSize!=null && arraySpecSize>0) { if(arraySpecSize < 1 || arraySpecSize>51) - return err("float arrayspec length must be 1-51") + return err("float array length must be 1-51") val constX = arrayspec.x.constValue(namespace, heap) if(constX?.asIntegerValue==null) - return err("arrayspec size specifier must be constant integer value") + return err("array size specifier must be constant integer value") val expectedSize = constX.asIntegerValue if (arraySize != expectedSize) - return err("initializer arrayspec size mismatch (expecting $expectedSize, got $arraySize)") + return err("initializer array size mismatch (expecting $expectedSize, got $arraySize)") } else - return err("invalid float arrayspec size, must be 1-51") + return err("invalid float array size, must be 1-51") // check if the floating point values are all within range val doubles = if(value.arrayvalue!=null) @@ -950,7 +950,7 @@ class AstChecker(private val namespace: INameScope, return err("floating point value overflow") return true } - return err("invalid float arrayspec initialization value ${value.type}, expected $targetDt") + return err("invalid float array initialization value ${value.type}, expected $targetDt") } } return true @@ -959,6 +959,7 @@ class AstChecker(private val namespace: INameScope, private fun checkAssignmentCompatible(targetDatatype: DataType, sourceDatatype: DataType, sourceValue: IExpression, + assignTargets: List, position: Position) : Boolean { if(sourceValue is RangeExpr) @@ -980,8 +981,12 @@ class AstChecker(private val namespace: INameScope, if(result) return true - if((sourceDatatype==DataType.UWORD || sourceDatatype==DataType.WORD) && (targetDatatype==DataType.UBYTE || targetDatatype==DataType.BYTE)) - checkResult.add(ExpressionError("cannot assign word to byte, use msb() or lsb()?", position)) + if((sourceDatatype==DataType.UWORD || sourceDatatype==DataType.WORD) && (targetDatatype==DataType.UBYTE || targetDatatype==DataType.BYTE)) { + if(assignTargets.size==2 && assignTargets[0].register!=null && assignTargets[1].register!=null) + return true // for asm subroutine calls that return a (U)WORD that's going to be stored into two BYTES (registers), we make an exception. + else + checkResult.add(ExpressionError("cannot assign word to byte, use msb() or lsb()?", position)) + } else if(sourceDatatype==DataType.FLOAT && targetDatatype in IntegerDatatypes) checkResult.add(ExpressionError("cannot assign float to ${targetDatatype.toString().toLowerCase()}; possible loss of precision. Suggestion: round the value or revert to integer arithmetic", position)) else diff --git a/compiler/src/prog8/compiler/Zeropage.kt b/compiler/src/prog8/compiler/Zeropage.kt index b898b262e..e54321bdf 100644 --- a/compiler/src/prog8/compiler/Zeropage.kt +++ b/compiler/src/prog8/compiler/Zeropage.kt @@ -21,7 +21,7 @@ abstract class Zeropage(private val options: CompilationOptions) { DataType.UBYTE -> (vardecl.arrayspec.x as LiteralValue).asIntegerValue!! DataType.UWORD -> (vardecl.arrayspec.x as LiteralValue).asIntegerValue!! * 2 DataType.FLOAT -> (vardecl.arrayspec.x as LiteralValue).asIntegerValue!! * 5 - else -> throw CompilerException("arrayspec can only be of byte, word, float") + else -> throw CompilerException("array can only be of byte, word, float") } } else { when (vardecl.datatype) { diff --git a/compiler/src/prog8/compiler/intermediate/IntermediateProgram.kt b/compiler/src/prog8/compiler/intermediate/IntermediateProgram.kt index 67aa70a44..d3e16dba7 100644 --- a/compiler/src/prog8/compiler/intermediate/IntermediateProgram.kt +++ b/compiler/src/prog8/compiler/intermediate/IntermediateProgram.kt @@ -274,8 +274,8 @@ class IntermediateProgram(val name: String, var loadAddress: Int, val heap: Heap } currentBlock.variables[scopedname] = value } - VarDeclType.CONST -> {} // constants are all folded away - VarDeclType.MEMORY -> { + VarDeclType.MEMORY, VarDeclType.CONST -> { + // note that constants are all folded away, but assembly code may still refer to them val lv = decl.value as LiteralValue if(lv.type!=DataType.UWORD && lv.type!=DataType.UBYTE) throw CompilerException("expected integer memory address $lv") diff --git a/compiler/src/prog8/compiler/target/c64/AsmGen.kt b/compiler/src/prog8/compiler/target/c64/AsmGen.kt index 47a63b9be..523efb36b 100644 --- a/compiler/src/prog8/compiler/target/c64/AsmGen.kt +++ b/compiler/src/prog8/compiler/target/c64/AsmGen.kt @@ -619,10 +619,10 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram, } Opcode.INV_WORD -> { """ - lda ${(ESTACK_LO + 1).toHex()},x + lda ${(ESTACK_LO+1).toHex()},x eor #255 sta ${(ESTACK_LO+1).toHex()},x - lda ${(ESTACK_HI + 1).toHex()},x + lda ${(ESTACK_HI+1).toHex()},x eor #255 sta ${(ESTACK_HI+1).toHex()},x """ @@ -732,11 +732,6 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram, Opcode.MUL_W -> " jsr prog8_lib.mul_w" Opcode.MUL_UW -> " jsr prog8_lib.mul_uw" Opcode.MUL_F -> " jsr prog8_lib.mul_f" - Opcode.LESS_UB -> " jsr prog8_lib.less_ub" - Opcode.LESS_B -> " jsr prog8_lib.less_b" - Opcode.LESS_UW -> " jsr prog8_lib.less_uw" - Opcode.LESS_W -> " jsr prog8_lib.less_w" - Opcode.LESS_F -> " jsr prog8_lib.less_f" Opcode.AND_BYTE -> { """ @@ -754,6 +749,39 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram, sta ${(ESTACK_LO + 1).toHex()},x """ } + Opcode.REMAINDER_B -> " jsr prog8_lib.remainder_b" + Opcode.REMAINDER_UB -> " jsr prog8_lib.remainder_ub" + Opcode.REMAINDER_W -> " jsr prog8_lib.remainder_w" + Opcode.REMAINDER_UW -> " jsr prog8_lib.remainder_uw" + Opcode.REMAINDER_F -> " jsr prog8_lib.remainder_f" + + Opcode.GREATER_B -> " jsr prog8_lib.greater_b" + Opcode.GREATER_UB -> " jsr prog8_lib.greater_ub" + Opcode.GREATER_W -> " jsr prog8_lib.greater_w" + Opcode.GREATER_UW -> " jsr prog8_lib.greater_uw" + Opcode.GREATER_F -> " jsr prog8_lib.greater_f" + + Opcode.GREATEREQ_B -> " jsr prog8_lib.greatereq_b" + Opcode.GREATEREQ_UB -> " jsr prog8_lib.greatereq_ub" + Opcode.GREATEREQ_W -> " jsr prog8_lib.greatereq_w" + Opcode.GREATEREQ_UW -> " jsr prog8_lib.greatereq_uw" + Opcode.GREATEREQ_F -> " jsr prog8_lib.greatereq_f" + + Opcode.EQUAL_BYTE -> " jsr prog8_lib.equal_b" + Opcode.EQUAL_WORD -> " jsr prog8_lib.equal_w" + Opcode.EQUAL_F -> " jsr prog8_lib.equal_f" + + Opcode.LESS_UB -> " jsr prog8_lib.less_ub" + Opcode.LESS_B -> " jsr prog8_lib.less_b" + Opcode.LESS_UW -> " jsr prog8_lib.less_uw" + Opcode.LESS_W -> " jsr prog8_lib.less_w" + Opcode.LESS_F -> " jsr prog8_lib.less_f" + + Opcode.LESSEQ_UB -> " jsr prog8_lib.lesseq_ub" + Opcode.LESSEQ_B -> " jsr prog8_lib.lesseq_b" + Opcode.LESSEQ_UW -> " jsr prog8_lib.lesseq_uw" + Opcode.LESSEQ_W -> " jsr prog8_lib.lesseq_w" + Opcode.LESSEQ_F -> " jsr prog8_lib.lesseq_f" else -> null } @@ -830,6 +858,7 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram, } private fun sameConstantIndexedVarOperation(variable: String, index: Int, ins: Instruction): AsmFragment? { + // an in place operation that consists of a push-value / op / push-index-value / pop-into-indexed-var return when(ins.opcode) { Opcode.SHL_BYTE -> AsmFragment(" asl $variable+$index", 8) Opcode.SHR_BYTE -> AsmFragment(" lsr $variable+$index", 8) @@ -848,6 +877,7 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram, } private fun sameIndexedVarOperation(variable: String, indexVar: String, ins: Instruction): AsmFragment? { + // an in place operation that consists of a push-value / op / push-index-var / pop-into-indexed-var val saveX = " stx ${C64Zeropage.SCRATCH_B1} |" val restoreX = " | ldx ${C64Zeropage.SCRATCH_B1}" val loadXWord: String @@ -891,6 +921,7 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram, } private fun sameMemOperation(address: Int, ins: Instruction): AsmFragment? { + // an in place operation that consists of a push-mem / op / pop-mem sequence val addr = address.toHex() val addrHi = (address+1).toHex() return when(ins.opcode) { @@ -911,6 +942,7 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram, } private fun sameVarOperation(variable: String, ins: Instruction): AsmFragment? { + // an in place operation that consists of a push-var / op / pop-var return when(ins.opcode) { Opcode.SHL_BYTE -> { when (variable) { @@ -2705,9 +2737,28 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram, // push msb(word var) AsmPattern(listOf(Opcode.PUSH_VAR_WORD, Opcode.MSB)) { segment -> " lda #>${segment[0].callLabel} | sta ${ESTACK_LO.toHex()},x | dex " + }, + + // set a register pair to a certain memory address (of a variable) + AsmPattern(listOf(Opcode.PUSH_ADDR_HEAPVAR, Opcode.POP_REGAX_WORD)) { segment -> + " lda #<${segment[0].callLabel} | ldx #>${segment[0].callLabel} " + }, + AsmPattern(listOf(Opcode.PUSH_ADDR_HEAPVAR, Opcode.POP_REGAY_WORD)) { segment -> + " lda #<${segment[0].callLabel} | ldy #>${segment[0].callLabel} " + }, + AsmPattern(listOf(Opcode.PUSH_ADDR_HEAPVAR, Opcode.POP_REGXY_WORD)) { segment -> + " ldx #<${segment[0].callLabel} | ldy #>${segment[0].callLabel} " + }, + // set a register pair to a certain memory address (of a literal string value) + AsmPattern(listOf(Opcode.PUSH_ADDR_STR, Opcode.POP_REGAX_WORD)) { segment -> + TODO("$segment") + }, + AsmPattern(listOf(Opcode.PUSH_ADDR_STR, Opcode.POP_REGAY_WORD)) { segment -> + TODO("$segment") + }, + AsmPattern(listOf(Opcode.PUSH_ADDR_STR, Opcode.POP_REGXY_WORD)) { segment -> + TODO("$segment") } - - ) } diff --git a/prog8lib/c64utils.p8 b/prog8lib/c64utils.p8 index ae410c88d..74a828541 100644 --- a/prog8lib/c64utils.p8 +++ b/prog8lib/c64utils.p8 @@ -648,7 +648,7 @@ asmsub print_pstring (text: str_p @ XY) -> clobbers(A,X) -> (ubyte @ Y) { asmsub print_byte_decimal0 (value: ubyte @ A) -> clobbers(A,X,Y) -> () { ; ---- print the byte in A in decimal form, with left padding 0s (3 positions total) %asm {{ - jsr byte2decimal + jsr c64utils.byte2decimal pha tya jsr c64.CHROUT @@ -663,7 +663,7 @@ asmsub print_byte_decimal0 (value: ubyte @ A) -> clobbers(A,X,Y) -> () { asmsub print_byte_decimal (value: ubyte @ A) -> clobbers(A,X,Y) -> () { ; ---- print the byte in A in decimal form, without left padding 0s %asm {{ - jsr byte2decimal + jsr c64utils.byte2decimal pha cpy #'0' bne _print_hundreds @@ -689,7 +689,7 @@ asmsub print_byte_hex (prefix: ubyte @ Pc, value: ubyte @ A) -> clobbers(A,X,Y lda #'$' jsr c64.CHROUT pla -+ jsr byte2hex ++ jsr c64utils.byte2hex txa jsr c64.CHROUT tya @@ -716,16 +716,16 @@ asmsub print_word_decimal0 (dataword: uword @ XY) -> clobbers(A,X,Y) -> () { ; ---- print the (unsigned) word in X/Y in decimal form, with left padding 0s (5 positions total) ; @todo shorter in loop form? %asm {{ - jsr word2decimal - lda word2decimal_output + jsr c64utils.word2decimal + lda c64utils.word2decimal_output jsr c64.CHROUT - lda word2decimal_output+1 + lda c64utils.word2decimal_output+1 jsr c64.CHROUT - lda word2decimal_output+2 + lda c64utils.word2decimal_output+2 jsr c64.CHROUT - lda word2decimal_output+3 + lda c64utils.word2decimal_output+3 jsr c64.CHROUT - lda word2decimal_output+4 + lda c64utils.word2decimal_output+4 jmp c64.CHROUT }} } @@ -734,27 +734,27 @@ asmsub print_word_decimal0 (dataword: uword @ XY) -> clobbers(A,X,Y) -> () { asmsub print_word_decimal (dataword: uword @ XY) -> clobbers(A,X,Y) -> () { ; ---- print the word in X/Y in decimal form, without left padding 0s %asm {{ - jsr word2decimal + jsr c64utils.word2decimal ldy #0 - lda word2decimal_output + lda c64utils.word2decimal_output cmp #'0' bne _pr_decimal iny - lda word2decimal_output+1 + lda c64utils.word2decimal_output+1 cmp #'0' bne _pr_decimal iny - lda word2decimal_output+2 + lda c64utils.word2decimal_output+2 cmp #'0' bne _pr_decimal iny - lda word2decimal_output+3 + lda c64utils.word2decimal_output+3 cmp #'0' bne _pr_decimal iny _pr_decimal - lda word2decimal_output,y + lda c64utils.word2decimal_output,y jsr c64.CHROUT iny cpy #5 diff --git a/prog8lib/prog8lib.p8 b/prog8lib/prog8lib.p8 index b8e3c003d..c3e98fc8a 100644 --- a/prog8lib/prog8lib.p8 +++ b/prog8lib/prog8lib.p8 @@ -82,15 +82,6 @@ mul_f neg_f rts -less_ub - rts - -less_b - rts - -less_f - rts - add_w rts ; @todo inline? @@ -120,7 +111,92 @@ div_w div_uw rts +remainder_b + rts +remainder_ub + rts +remainder_w + rts +remainder_uw + rts +remainder_f + rts +equal_ub + rts + +equal_b + rts + +equal_w + rts + +equal_uw + rts + +equal_f + rts + +less_ub + rts + +less_b + rts + +less_w + rts + +less_uw + rts + +less_f + rts + +lesseq_ub + rts + +lesseq_b + rts + +lesseq_w + rts + +lesseq_uw + rts + +lesseq_f + rts + +greater_ub + rts + +greater_b + rts + +greater_w + rts + +greater_uw + rts + +greater_f + rts + +greatereq_ub + rts + +greatereq_b + rts + +greatereq_w + rts + +greatereq_uw + rts + +greatereq_f + rts + func_sin rts func_cos