From fba3cb7301acc1a04604251270301eca85c1ca7e Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Wed, 24 Oct 2018 01:39:52 +0200 Subject: [PATCH] more asm output --- compiler/examples/mandelbrot-novm.p8 | 39 ++ compiler/examples/test.p8 | 36 +- compiler/src/prog8/compiler/Compiler.kt | 2 + .../src/prog8/compiler/intermediate/Opcode.kt | 25 ++ .../src/prog8/compiler/target/c64/AsmGen.kt | 382 ++++++++++++++---- .../compiler/target/c64/AssemblyProgram.kt | 4 +- prog8lib/prog8lib.p8 | 46 +++ 7 files changed, 438 insertions(+), 96 deletions(-) create mode 100644 compiler/examples/mandelbrot-novm.p8 diff --git a/compiler/examples/mandelbrot-novm.p8 b/compiler/examples/mandelbrot-novm.p8 new file mode 100644 index 000000000..6224c021c --- /dev/null +++ b/compiler/examples/mandelbrot-novm.p8 @@ -0,0 +1,39 @@ +%import c64utils +%option enable_floats + +~ main { + const uword width = 320 // 2 + const uword height = 256 // 2 + const uword xoffset = 40 + const uword yoffset = 30 + + sub start() { + ;vm_gfx_clearscr(11) + ;vm_gfx_text(2, 1, 1, "Calculating Mandelbrot Fractal...") + + for ubyte pixely in yoffset to yoffset+height-1 { + float yy = flt((pixely-yoffset))/height/3.6+0.4 + + for uword pixelx in xoffset to xoffset+width-1 { + float xx = flt((pixelx-xoffset))/width/3.0+0.2 + + float xsquared = 0.0 + float ysquared = 0.0 + float x = 0.0 + float y = 0.0 + ubyte iter = 0 + + while (iter<32 and xsquared+ysquared<4.0) { + y = x*y*2.0 + yy + x = xsquared - ysquared + xx + xsquared = x*x + ysquared = y*y + iter++ + } + + ;vm_gfx_pixel(pixelx, pixely, iter) + } + } + ;vm_gfx_text(11, 21, 1, "Finished!") + } +} diff --git a/compiler/examples/test.p8 b/compiler/examples/test.p8 index c70c05a02..2f0635ed0 100644 --- a/compiler/examples/test.p8 +++ b/compiler/examples/test.p8 @@ -7,26 +7,26 @@ sub start() { - const uword width = 160 - const uword height = 128 ubyte pixely = 255 - const ubyte yoffset = 50 + ubyte derp = 0 + byte b = 99 + byte b2 = 100 + word w = 999 + word w2 = 3 + uword uw = 40000 + uword uw2 = 3434 + float fl1 = 1.1 + float fl2 = 2.2 + + uw2 = uw + w2 = w + b2 = b + derp=pixely + fl2 = fl1 + fl2++ + + - float y11a = (pixely-30) - float y11b = (pixely-30)/128 - float y11c = (pixely-30)/128/3.6 - float y11d = (pixely-30)/500+0.4 - float y11e = (pixely-30)/128/3.6+0.4 - float y11f = flt(pixely-30)/128/3.6+0.4 - float y111 = (((pixely-30)))/128/3.6+0.4 - float y1 = (pixely-yoffset)/128/3.6+0.4 - float y1a = flt(pixely-yoffset)/128/3.6+0.4 - float y1a2 = (flt(pixely-yoffset))/128/3.6+0.4 - float y1b = (pixely-40)/128/3.6+0.4 - float y1c = (pixely-40.0)/128/3.6+0.4 - float y2 = flt((pixely-yoffset))/128.0/3.6+0.4 - float y3 = flt((pixely-yoffset))/height/3.6+0.4 - float y4 = flt((pixely-yoffset))/height/3.6+0.4 return } diff --git a/compiler/src/prog8/compiler/Compiler.kt b/compiler/src/prog8/compiler/Compiler.kt index 2fbef780d..db04bdc1e 100644 --- a/compiler/src/prog8/compiler/Compiler.kt +++ b/compiler/src/prog8/compiler/Compiler.kt @@ -381,6 +381,7 @@ private class StatementTranslator(private val prog: IntermediateProgram, DataType.BYTE -> Opcode.DEC_VAR_B DataType.UWORD -> Opcode.DEC_VAR_UW DataType.WORD -> Opcode.DEC_VAR_W + DataType.FLOAT -> Opcode.DEC_VAR_F else -> throw CompilerException("can't dec type $dt") } } @@ -391,6 +392,7 @@ private class StatementTranslator(private val prog: IntermediateProgram, DataType.BYTE -> Opcode.INC_VAR_B DataType.UWORD -> Opcode.INC_VAR_UW DataType.WORD -> Opcode.INC_VAR_W + DataType.FLOAT -> Opcode.INC_VAR_F else -> throw CompilerException("can't inc type $dt") } } diff --git a/compiler/src/prog8/compiler/intermediate/Opcode.kt b/compiler/src/prog8/compiler/intermediate/Opcode.kt index 49b83a0aa..f975310b3 100644 --- a/compiler/src/prog8/compiler/intermediate/Opcode.kt +++ b/compiler/src/prog8/compiler/intermediate/Opcode.kt @@ -232,3 +232,28 @@ val opcodesWithVarArgument = setOf( Opcode.READ_INDEXED_VAR_BYTE, Opcode.READ_INDEXED_VAR_WORD, Opcode.READ_INDEXED_VAR_FLOAT, Opcode.WRITE_INDEXED_VAR_BYTE, Opcode.WRITE_INDEXED_VAR_WORD, Opcode.WRITE_INDEXED_VAR_FLOAT ) + +val pushOpcodes = setOf( + Opcode.PUSH_BYTE, + Opcode.PUSH_WORD, + Opcode.PUSH_FLOAT, + Opcode.PUSH_MEM_B, + Opcode.PUSH_MEM_UB, + Opcode.PUSH_MEM_W, + Opcode.PUSH_MEM_UW, + Opcode.PUSH_MEM_FLOAT, + Opcode.PUSH_VAR_BYTE, + Opcode.PUSH_VAR_WORD, + Opcode.PUSH_VAR_FLOAT +) + +val popOpcodes = setOf( + Opcode.POP_MEM_B, + Opcode.POP_MEM_UB, + Opcode.POP_MEM_W, + Opcode.POP_MEM_UW, + Opcode.POP_MEM_FLOAT, + Opcode.POP_VAR_BYTE, + Opcode.POP_VAR_WORD, + Opcode.POP_VAR_FLOAT +) \ No newline at end of file diff --git a/compiler/src/prog8/compiler/target/c64/AsmGen.kt b/compiler/src/prog8/compiler/target/c64/AsmGen.kt index f90e58e69..064277e63 100644 --- a/compiler/src/prog8/compiler/target/c64/AsmGen.kt +++ b/compiler/src/prog8/compiler/target/c64/AsmGen.kt @@ -320,27 +320,36 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram, // find best patterns (matching the most of the lines, then with the smallest weight) val fragments = findPatterns(ins).sortedWith(compareBy({it.segmentSize}, {it.prio})) if(fragments.isEmpty()) { + // we didn't find any matching patterns (complex multi-instruction fragments), try simple ones val firstIns = ins[0] val singleAsm = simpleInstr2Asm(firstIns) if(singleAsm != null) { - if(singleAsm.isNotEmpty()) { - for(line in singleAsm.split('|')) { - val trimmed = if(line.startsWith(' ')) "\t"+line.trim() else line.trim() - out(trimmed) - } - } + outputAsmFragment(singleAsm) return 1 } return 0 } val best = fragments[0] - if(best.asm.isNotEmpty()) { - for (line in best.asm.split('|')) { - val trimmed = if (line.startsWith(' ')) "\t" + line.trim() else line.trim() - out(trimmed) + outputAsmFragment(best.asm) + return best.segmentSize + } + + private fun outputAsmFragment(singleAsm: String) { + if (singleAsm.isNotEmpty()) { + when { + singleAsm.startsWith("@inline@") -> out(singleAsm.substring(8)) + '\n' in singleAsm -> for (line in singleAsm.split('\n')) { + if (line.isNotEmpty()) { + val trimmed = if (line.startsWith(' ')) "\t" + line.trim() else line.trim() + out(trimmed) + } + } + else -> for (line in singleAsm.split('|')) { + val trimmed = if (line.startsWith(' ')) "\t" + line.trim() else line.trim() + out(trimmed) + } } } - return best.segmentSize } private fun simpleInstr2Asm(ins: Instruction): String? { @@ -373,14 +382,98 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram, Opcode.DISCARD_BYTE -> " inx" Opcode.DISCARD_WORD -> " inx" Opcode.DISCARD_FLOAT -> " inx | inx | inx" - Opcode.INLINE_ASSEMBLY -> ins.callLabel ?: "" // All of the inline assembly is stored in the calllabel property. + Opcode.INLINE_ASSEMBLY -> "@inline@" + (ins.callLabel ?: "") // All of the inline assembly is stored in the calllabel property. + Opcode.SYSCALL -> { + if (ins.arg!!.numericValue() in syscallsForStackVm.map { it.callNr }) + throw CompilerException("cannot translate vm syscalls to real assembly calls - use *real* subroutine calls instead. Syscall ${ins.arg.numericValue()}") + TODO("syscall $ins") + } + Opcode.BREAKPOINT -> { + breakpointCounter++ + "_prog8_breakpoint_$breakpointCounter\tnop" + } + Opcode.PUSH_BYTE -> { - " lda #${ins.arg!!.integerValue().toHex()} | sta $ESTACK_LO,x | dex" + " lda #${ins.arg!!.integerValue().toHex()} | sta ${ESTACK_LO.toHex()},x | dex" } Opcode.PUSH_WORD -> { val value = ins.arg!!.integerValue().toHex() - " lda #<$value | sta $ESTACK_LO,x | lda #>$value | sta $ESTACK_HI,x | dex" + " lda #<$value | sta ${ESTACK_LO.toHex()},x | lda #>$value | sta ${ESTACK_HI.toHex()},x | dex" } + Opcode.PUSH_FLOAT -> { + val floatConst = globalFloatConsts[ins.arg!!.numericValue().toDouble()] ?: throw AssemblyError("should have a global float const for number ${ins.arg}") + " lda #<$floatConst | ldy #>$floatConst | jsr prog8_lib.push_float" + } + Opcode.PUSH_VAR_BYTE -> { + when(ins.callLabel) { + "X" -> throw CompilerException("makes no sense to push X, it's used as a stack pointer itself") + "A" -> " sta ${ESTACK_LO.toHex()},x | dex" + "Y" -> " tya | sta ${ESTACK_LO.toHex()},x | dex" + else -> " lda ${ins.callLabel} | sta ${ESTACK_LO.toHex()},x | dex" + } + } + Opcode.PUSH_VAR_WORD -> { + when (ins.callLabel) { + "AX" -> throw CompilerException("makes no sense to push X, it's used as a stack pointer itself") + "XY" -> throw CompilerException("makes no sense to push X, it's used as a stack pointer itself") + "AY" -> " sta ${ESTACK_LO.toHex()},x | pha | tya | sta ${ESTACK_HI.toHex()},x | pla | dex" + else -> " lda ${ins.callLabel} | ldy ${ins.callLabel}+1 | sta ${ESTACK_LO.toHex()},x | pha | tya | sta ${ESTACK_HI.toHex()},x | pla | dex" + } + } + Opcode.PUSH_VAR_FLOAT -> " lda #<${ins.callLabel} | ldy #>${ins.callLabel}| jsr prog8_lib.push_float" + Opcode.PUSH_MEM_B, Opcode.PUSH_MEM_UB -> { + """ + lda ${ins.arg!!.integerValue().toHex()} + sta ${ESTACK_LO.toHex()},x + dex + """ + } + Opcode.PUSH_MEM_W, Opcode.PUSH_MEM_UW -> { + """ + lda ${ins.arg!!.integerValue().toHex()} + sta ${ESTACK_LO.toHex()},x + lda ${(ins.arg.integerValue()+1).toHex()} + sta ${ESTACK_HI.toHex()},x + dex + """ + } + + Opcode.POP_MEM_B, Opcode.POP_MEM_UB -> { + """ + inx + lda ${ESTACK_LO.toHex()},x + sta ${ins.arg!!.integerValue().toHex()} + """ + } + Opcode.POP_MEM_W, Opcode.POP_MEM_UW -> { + """ + inx + lda ${ESTACK_LO.toHex()},x + sta ${ins.arg!!.integerValue().toHex()} + lda ${ESTACK_HI.toHex()},x + sta ${(ins.arg.integerValue()+1).toHex()} + """ + } + Opcode.POP_VAR_BYTE -> { + when (ins.callLabel) { + "X" -> throw CompilerException("makes no sense to pop X, it's used as a stack pointer itself") + "A" -> " inx | lda ${ESTACK_LO.toHex()},x" + "Y" -> " inx | ldy ${ESTACK_LO.toHex()},x" + else -> " inx | lda ${ESTACK_LO.toHex()},x | sta ${ins.callLabel}" + } + } + Opcode.POP_VAR_WORD -> { + when (ins.callLabel) { + "AX" -> throw CompilerException("makes no sense to pop X, it's used as a stack pointer itself") + "XY" -> throw CompilerException("makes no sense to pop X, it's used as a stack pointer itself") + "AY" -> " inx | lda ${ESTACK_LO.toHex()},x | ldy ${ESTACK_HI.toHex()},x" + else -> " inx | lda ${ESTACK_LO.toHex()},x | ldy ${ESTACK_HI.toHex()},x | sta ${ins.callLabel} | sty ${ins.callLabel}+1" + } + } + Opcode.POP_VAR_FLOAT -> { + " lda #<${ins.callLabel} | ldy #>${ins.callLabel} | jsr prog8_lib.pop_var_float" + } + Opcode.COPY_VAR_BYTE -> { when { ins.callLabel2 in registerStrings -> { @@ -444,6 +537,20 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram, } } } + Opcode.COPY_VAR_FLOAT -> { + """ + lda #<${ins.callLabel} + ldy #>${ins.callLabel} + sta ${C64Zeropage.SCRATCH_W1} + sty ${C64Zeropage.SCRATCH_W1+1} + lda #<${ins.callLabel2} + ldy #>${ins.callLabel2} + sta ${C64Zeropage.SCRATCH_W2} + sty ${C64Zeropage.SCRATCH_W2+1} + jsr prog8_lib.copy_float + """ + } + Opcode.INC_VAR_UB, Opcode.INC_VAR_B -> { when (ins.callLabel) { "A" -> " clc | adc #1" @@ -457,9 +564,16 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram, "AX" -> " clc | adc #1 | bne + | inx |+" "AY" -> " clc | adc #1 | bne + | iny |+" "XY" -> " inx | bne + | iny |+" - else -> TODO("inc_var_uw $ins") + else -> " inc ${ins.callLabel} | bne + | inc ${ins.callLabel}+1 |+" } } + Opcode.INC_VAR_F -> { + """ + lda #<${ins.callLabel} + ldy #>${ins.callLabel} + jsr prog8_lib.inc_var_f + """ + } Opcode.DEC_VAR_UB, Opcode.DEC_VAR_B -> { when (ins.callLabel) { "A" -> " sec | sbc #1" @@ -468,87 +582,137 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram, else -> " dec ${ins.callLabel}" } } - Opcode.ADD_UB, Opcode.ADD_B -> { - " lda ${(ESTACK_LO + 2).toHex()},x | " + - " clc | adc ${(ESTACK_LO + 1).toHex()},x | inx" + - " sta ${(ESTACK_LO + 1).toHex()},x" - } - Opcode.SUB_UB, Opcode.SUB_B -> { - " lda ${(ESTACK_LO + 2).toHex()},x | " + - " sec | sbc ${(ESTACK_LO + 1).toHex()},x | inx" + - " sta ${(ESTACK_LO + 1).toHex()},x" - } - Opcode.POP_MEM_UB, Opcode.POP_MEM_B -> { - " inx | lda ${ESTACK_LO.toHex()},x " + - " sta ${ins.arg!!.integerValue().toHex()}" - } - Opcode.POP_MEM_W, Opcode.POP_MEM_UW -> { - " inx | lda ${ESTACK_LO.toHex()},x | ldy ${ESTACK_HI.toHex()},x | " + - " sta ${ins.callLabel} | sty ${ins.callLabel}+1" - } - Opcode.POP_VAR_BYTE -> { - when (ins.callLabel) { - "X" -> throw CompilerException("makes no sense to pop X it's used as a stack pointer itself") - "A" -> " inx | lda ${ESTACK_LO.toHex()},x" - "Y" -> " inx | ldy ${ESTACK_LO.toHex()},x" - else -> " inx | lda ${ESTACK_LO.toHex()},x | sta ${ins.callLabel}" - } - } - Opcode.POP_VAR_WORD -> { - when (ins.callLabel) { - "AX" -> throw CompilerException("makes no sense to pop X it's used as a stack pointer itself") - "XY" -> throw CompilerException("makes no sense to pop X it's used as a stack pointer itself") - "AY" -> " inx | lda ${ESTACK_LO.toHex()},x | ldy ${ESTACK_HI.toHex()},x" - else -> " inx | lda ${ESTACK_LO.toHex()},x | ldy ${ESTACK_HI.toHex()},x | sta ${ins.callLabel} | sty ${ins.callLabel}+1" - } - } Opcode.DEC_VAR_UW -> { when (ins.callLabel) { "AX" -> " cmp #0 | bne + | dex |+ | sec | sbc #1" "AY" -> " cmp #0 | bne + | dey |+ | sec | sbc #1" "XY" -> " txa | bne + | dey |+ | dex" - else -> TODO("dec_var_uw $ins") + else -> " lda ${ins.callLabel} | bne + | dec ${ins.callLabel}+1 |+ | dec ${ins.callLabel}" } } + Opcode.DEC_VAR_F -> { + """ + lda #<${ins.callLabel} + ldy #>${ins.callLabel} + jsr prog8_lib.dec_var_f + """ + } Opcode.NEG_B -> { - " lda ${(ESTACK_LO+1).toHex()},x |" + - " eor #\$ff | sec | adc #0 | " + - " sta ${(ESTACK_LO+1).toHex()},x" + """ + lda ${(ESTACK_LO+1).toHex()},x + eor #255 + sec + adc #0 + sta ${(ESTACK_LO+1).toHex()},x + """ } Opcode.INV_BYTE -> { - " lda ${(ESTACK_LO+1).toHex()},x | " + - " eor #\$ff | " + - " sta ${(ESTACK_LO+1).toHex()},x" + """ + lda ${(ESTACK_LO + 1).toHex()},x + eor #255 + sta ${(ESTACK_LO + 1).toHex()},x + """ } Opcode.INV_WORD -> { - " lda ${(ESTACK_LO + 1).toHex()},x | eor #\$ff | sta ${(ESTACK_LO+1).toHex()},x | " + - " lda ${(ESTACK_HI + 1).toHex()},x | eor #\$ff | sta ${(ESTACK_HI+1).toHex()},x | " + """ + lda ${(ESTACK_LO + 1).toHex()},x + eor #255 + sta ${(ESTACK_LO+1).toHex()},x + lda ${(ESTACK_HI + 1).toHex()},x + eor #255 + sta ${(ESTACK_HI+1).toHex()},x + """ } Opcode.NOT_BYTE -> { - " lda ${(ESTACK_LO+1)},x " + - " beq + | lda #0 | beq ++ |+ | lda #1 |+ | " + - " sta ${(ESTACK_LO+1)},x" + """ + lda ${(ESTACK_LO+1).toHex()},x + beq + + lda #0 + beq ++ ++ lda #1 ++ sta ${(ESTACK_LO+1).toHex()},x + """ } Opcode.NOT_WORD -> { - " lda ${(ESTACK_LO + 1).toHex()},x | ora ${(ESTACK_HI + 1).toHex()},x | " + - " beq + | lda #0 | beq ++ |+ | lda #1 |+ | "+ - " sta ${(ESTACK_LO + 1).toHex()},x | sta ${(ESTACK_HI + 1).toHex()},x" - } - Opcode.SYSCALL -> { - if (ins.arg!!.numericValue() in syscallsForStackVm.map { it.callNr }) - throw CompilerException("cannot translate vm syscalls to real assembly calls - use *real* subroutine calls instead. Syscall ${ins.arg.numericValue()}") - TODO("syscall $ins") - } - Opcode.BREAKPOINT -> { - breakpointCounter++ - "_prog8_breakpoint_$breakpointCounter\tnop" + """ + lda ${(ESTACK_LO + 1).toHex()},x + ora ${(ESTACK_HI + 1).toHex()},x + beq + + lda #0 + beq ++ ++ lda #1 ++ sta ${(ESTACK_LO + 1).toHex()},x | sta ${(ESTACK_HI + 1).toHex()},x + """ } + Opcode.BCS -> " bcs ${ins.callLabel}" Opcode.BCC -> " bcc ${ins.callLabel}" Opcode.BZ -> " beq ${ins.callLabel}" Opcode.BNZ -> " bne ${ins.callLabel}" Opcode.BNEG -> " bmi ${ins.callLabel}" Opcode.BPOS -> " bpl ${ins.callLabel}" + Opcode.UB2FLOAT -> " jsr prog8_lib.ub2float" + Opcode.B2FLOAT -> " jsr prog8_lib.b2float" + Opcode.UW2FLOAT -> " jsr prog8_lib.uw2float" + Opcode.W2FLOAT -> " jsr prog8_lib.w2float" + + Opcode.DIV_UB -> " jsr prog8_lib.div_ub" + Opcode.DIV_B -> " jsr prog8_lib.div_b" + Opcode.DIV_F -> " jsr prog8_lib.div_f" + Opcode.DIV_W -> " jsr prog8_lib.div_w" + Opcode.DIV_UW -> " jsr prog8_lib.div_uw" + Opcode.ADD_UB, Opcode.ADD_B -> { + """ + lda ${(ESTACK_LO + 2).toHex()},x + clc + adc ${(ESTACK_LO + 1).toHex()},x + inx + sta ${(ESTACK_LO + 1).toHex()},x + """ + } + Opcode.SUB_UB, Opcode.SUB_B -> { + """ + lda ${(ESTACK_LO + 2).toHex()},x + sec + sbc ${(ESTACK_LO + 1).toHex()},x + inx + sta ${(ESTACK_LO + 1).toHex()},x + """ + } + Opcode.ADD_F -> " jsr prog8_lib.add_f" + Opcode.ADD_W -> " jsr prog8_lib.add_w" // todo or inline rather + Opcode.ADD_UW -> " jsr prog8_lib.add_uw" // todo or inline rather + Opcode.SUB_F -> " jsr prog8_lib.sub_f" + Opcode.SUB_W -> " jsr prog8_lib.sub_w" // todo or inline rather + Opcode.SUB_UW -> " jsr prog8_lib.sub_uw" // todo or inline rather + Opcode.MUL_F -> " jsr prog8_lib.mul_f" + Opcode.MUL_B -> " jsr prog8_lib.mul_b" + Opcode.MUL_UB -> " jsr prog8_lib.mul_ub" + Opcode.MUL_W -> " jsr prog8_lib.mul_w" + Opcode.MUL_UW -> " jsr prog8_lib.mul_uw" + 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 -> { + """ + lda ${(ESTACK_LO + 2).toHex()},x + and ${(ESTACK_LO + 1).toHex()},x + inx + sta ${(ESTACK_LO + 1).toHex()},x + """ + } + Opcode.OR_BYTE -> { + """ + lda ${(ESTACK_LO + 2).toHex()},x + ora ${(ESTACK_LO + 1).toHex()},x + inx + sta ${(ESTACK_LO + 1).toHex()},x + """ + } + else -> null } } @@ -557,7 +721,72 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram, val opcodes = segment.map { it.opcode } val result = mutableListOf() - if((opcodes[0]==Opcode.PUSH_VAR_BYTE && opcodes[2]==Opcode.POP_VAR_BYTE) || + // check for regular 'assignments' (a push immediately followed by a pop) + if(opcodes[0] in pushOpcodes && opcodes[1] in popOpcodes) { + when(opcodes[0]) { + Opcode.PUSH_BYTE -> when(opcodes[1]) { + Opcode.POP_VAR_BYTE -> { + result.add(AsmFragment( + " lda #${segment[0].arg!!.integerValue().toHex()} | sta ${segment[1].callLabel}", + 10, 2)) + } + else -> TODO("pop byte ${segment[1]}") + } + Opcode.PUSH_WORD -> when(opcodes[1]) { + Opcode.POP_VAR_WORD -> { + result.add(AsmFragment( + """ + lda #<${segment[0].arg!!.integerValue().toHex()} + sta ${segment[1].callLabel} + lda #>${segment[0].arg!!.integerValue().toHex()} + sta ${segment[1].callLabel}+1 + """, + 10, 2)) + } + else -> TODO("pop word ${segment[1]}") + } + Opcode.PUSH_FLOAT -> { + val floatConst = globalFloatConsts[segment[0].arg!!.numericValue().toDouble()] ?: throw AssemblyError("should have a global float const for number ${segment[0].arg}") + result.add(AsmFragment( + """ + lda #<$floatConst + ldy #>$floatConst + sta ${C64Zeropage.SCRATCH_W1} + sty ${C64Zeropage.SCRATCH_W1+1} + lda #<${segment[1].callLabel} + ldy #>${segment[1].callLabel} + sta ${C64Zeropage.SCRATCH_W2} + sty ${C64Zeropage.SCRATCH_W2+1} + jsr prog8_lib.copy_float + """, 10,2)) + } + Opcode.PUSH_MEM_B -> TODO("assignment ${segment[0]} --> ${segment[1]}") + Opcode.PUSH_MEM_UB -> TODO("assignment ${segment[0]} --> ${segment[1]}") + Opcode.PUSH_MEM_W -> TODO("assignment ${segment[0]} --> ${segment[1]}") + Opcode.PUSH_MEM_UW -> TODO("assignment ${segment[0]} --> ${segment[1]}") + Opcode.PUSH_MEM_FLOAT -> TODO("assignment ${segment[0]} --> ${segment[1]}") + Opcode.PUSH_VAR_BYTE -> { + if(opcodes[1] == Opcode.POP_VAR_BYTE) + throw AssemblyError("push+pop var byte should have been changed into COPY_VAR_BYTE opcode") + else TODO("assignment ${segment[0]} --> ${segment[1]}") + } + Opcode.PUSH_VAR_WORD -> { + if(opcodes[1] == Opcode.POP_VAR_WORD) + throw AssemblyError("push+pop var word should have been changed into COPY_VAR_WORD opcode") + else TODO("assignment ${segment[0]} --> ${segment[1]}") + } + Opcode.PUSH_VAR_FLOAT -> { + if(opcodes[1] == Opcode.POP_VAR_FLOAT) + throw AssemblyError("push+pop var float should have been changed into COPY_VAR_FLOAT opcode") + else TODO("assignment ${segment[0]} --> ${segment[1]}") + } + else -> throw AssemblyError("strange push opcode ${segment[0]}") + } + } + + // check for operations that modify a single value, by putting it on the stack (and popping it afterwards) + + else if((opcodes[0]==Opcode.PUSH_VAR_BYTE && opcodes[2]==Opcode.POP_VAR_BYTE) || (opcodes[0]==Opcode.PUSH_VAR_WORD && opcodes[2]==Opcode.POP_VAR_WORD)) { if (segment[0].callLabel == segment[2].callLabel) { val fragment = sameVarOperation(segment[0].callLabel!!, segment[1]) @@ -634,11 +863,12 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram, private fun sameIndexedVarOperation(variable: String, indexVar: String, ins: Instruction): AsmFragment? { val saveX = " stx ${C64Zeropage.SCRATCH_B1} |" // todo optimize to TXA when possible val restoreX = " | ldx ${C64Zeropage.SCRATCH_B1}" - var loadX = "" - var loadXWord = "" + val loadXWord: String + val loadX: String when(indexVar) { "X" -> { + loadX = "" loadXWord = " txa | asl a | tax |" } "Y" -> { diff --git a/compiler/src/prog8/compiler/target/c64/AssemblyProgram.kt b/compiler/src/prog8/compiler/target/c64/AssemblyProgram.kt index dde437607..83569b14b 100644 --- a/compiler/src/prog8/compiler/target/c64/AssemblyProgram.kt +++ b/compiler/src/prog8/compiler/target/c64/AssemblyProgram.kt @@ -12,8 +12,8 @@ class AssemblyProgram(val name: String) { fun assemble(options: CompilationOptions) { println("Generating machine code program...") - val command = mutableListOf("64tass", "--ascii", "--case-sensitive", "-Wall", "-Wno-strict-bool", - "-Werror", "--dump-labels", "--vice-labels", "-l", viceMonListFile, "--no-monitor") + val command = mutableListOf("64tass", "--ascii", "--case-sensitive", "--long-branch", "-Wall", "-Wno-strict-bool", "-Wlong-branch", + "-Werror", "-Wno-error=long-branch", "--dump-labels", "--vice-labels", "-l", viceMonListFile, "--no-monitor") val outFile = when(options.output) { OutputType.PRG -> { diff --git a/prog8lib/prog8lib.p8 b/prog8lib/prog8lib.p8 index 74fbca799..d32fca148 100644 --- a/prog8lib/prog8lib.p8 +++ b/prog8lib/prog8lib.p8 @@ -26,5 +26,51 @@ ror2_word sta SCRATCH_ZPWORD1+1 + rts + + +; @todo: stubs for now + +ub2float + rts + +uw2float + rts + +push_float + rts + +pop_var_float + rts + +copy_float + rts + +inc_var_f + rts + +dec_var_f + rts + +div_f + rts + +add_f + rts + +sub_f + rts + +mul_f + rts + +sub_uw + rts + +less_ub + rts + +less_f + rts + }} }