From d97da3bb7b932820670642e5cbc78706cd98f1e8 Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Thu, 27 Aug 2020 20:47:22 +0200 Subject: [PATCH] implemented almost all math operations --- compiler/res/prog8lib/math.asm | 3 +- compiler/res/prog8lib/prog8lib.asm | 6 +- .../assignment/AugmentableAssignmentAsmGen.kt | 1527 +++++++++-------- examples/arithmetic/div.p8 | 14 +- examples/arithmetic/mult.p8 | 2 - examples/arithmetic/remainder.p8 | 2 - examples/test.p8 | 17 +- 7 files changed, 845 insertions(+), 726 deletions(-) diff --git a/compiler/res/prog8lib/math.asm b/compiler/res/prog8lib/math.asm index 3bf166a61..0ea72e68e 100644 --- a/compiler/res/prog8lib/math.asm +++ b/compiler/res/prog8lib/math.asm @@ -86,7 +86,8 @@ result .byte 0,0,0,0 .pend -divmod_ub .proc +divmod_ub_asm .proc + ; TODO divmod_ub_asm doesn't work correctly. (remainder = ok, quotient = FAULTY) ; -- divide A by Y, result quotient in Y, remainder in A (unsigned) ; division by zero will result in quotient = 255 and remainder = original number sty P8ZP_SCRATCH_REG diff --git a/compiler/res/prog8lib/prog8lib.asm b/compiler/res/prog8lib/prog8lib.asm index 24a53e546..e098bb36d 100644 --- a/compiler/res/prog8lib/prog8lib.asm +++ b/compiler/res/prog8lib/prog8lib.asm @@ -339,7 +339,7 @@ idiv_b .proc eor #$ff sec adc #0 ; make num2 positive -+ jsr math.divmod_ub ++ jsr math.divmod_ub_asm sta _remainder tya plp ; get sign of result @@ -357,7 +357,7 @@ idiv_ub .proc inx ldy P8ESTACK_LO,x lda P8ESTACK_LO+1,x - jsr math.divmod_ub + jsr math.divmod_ub_asm tya sta P8ESTACK_LO+1,x rts @@ -410,7 +410,7 @@ remainder_ub .proc inx ldy P8ESTACK_LO,x ; right operand lda P8ESTACK_LO+1,x ; left operand - jsr math.divmod_ub + jsr math.divmod_ub_asm sta P8ESTACK_LO+1,x rts .pend diff --git a/compiler/src/prog8/compiler/target/c64/codegen/assignment/AugmentableAssignmentAsmGen.kt b/compiler/src/prog8/compiler/target/c64/codegen/assignment/AugmentableAssignmentAsmGen.kt index 7a29e3b23..070b9b286 100644 --- a/compiler/src/prog8/compiler/target/c64/codegen/assignment/AugmentableAssignmentAsmGen.kt +++ b/compiler/src/prog8/compiler/target/c64/codegen/assignment/AugmentableAssignmentAsmGen.kt @@ -212,8 +212,8 @@ internal class AugmentableAssignmentAsmGen(private val program: Program, println("*** TODO optimize simple inplace array assignment ${target.array} $operator= $value") assignmentAsmGen.translateNormalAssignment(target.origAssign) // TODO get rid of this fallback for the most common cases here } - TargetStorageKind.REGISTER -> TODO("reg") - TargetStorageKind.STACK -> TODO("stack") + TargetStorageKind.REGISTER -> TODO("reg in-place modification") + TargetStorageKind.STACK -> TODO("stack in-place modification") } } @@ -229,6 +229,813 @@ internal class AugmentableAssignmentAsmGen(private val program: Program, return false } + private fun inplaceModification_byte_value_to_memory(pointervar: IdentifierReference, operator: String, value: Expression) { + println("warning: slow stack evaluation used (3): @(${pointervar.nameInSource.last()}) $operator= ${value::class.simpleName} at ${value.position}") // TODO + asmgen.translateExpression(value) + when (operator) { + // note: ** (power) operator requires floats. + "+" -> { + val (ptrOnZp, sourceName) = asmgen.loadByteFromPointerIntoA(pointervar) + asmgen.out(" clc | adc P8ESTACK_LO+1,x") + if(ptrOnZp) + asmgen.out(" sta ($sourceName),y") + else + asmgen.out(" sta (P8ZP_SCRATCH_W1),y") + } + "-" -> { + val (ptrOnZp, sourceName) = asmgen.loadByteFromPointerIntoA(pointervar) + asmgen.out(" sec | sbc P8ESTACK_LO+1,x") + if(ptrOnZp) + asmgen.out(" sta ($sourceName),y") + else + asmgen.out(" sta (P8ZP_SCRATCH_W1),y") + } + "*" -> TODO("mul")// asmgen.out(" jsr prog8_lib.mul_byte") // the optimized routines should have been checked earlier + "/" -> TODO("div")// asmgen.out(if(types==DataType.UBYTE) " jsr prog8_lib.idiv_ub" else " jsr prog8_lib.idiv_b") + "%" -> { + TODO("byte remainder") +// if(types==DataType.BYTE) +// throw AssemblyError("remainder of signed integers is not properly defined/implemented, use unsigned instead") +// asmgen.out(" jsr prog8_lib.remainder_ub") + } + "<<" -> TODO("ubyte asl") + ">>" -> TODO("ubyte lsr") + "&" -> { + val (ptrOnZp, sourceName) = asmgen.loadByteFromPointerIntoA(pointervar) + asmgen.out(" and P8ESTACK_LO+1,x") + if(ptrOnZp) + asmgen.out(" sta ($sourceName),y") + else + asmgen.out(" sta (P8ZP_SCRATCH_W1),y") + } + "^" -> { + val (ptrOnZp, sourceName) = asmgen.loadByteFromPointerIntoA(pointervar) + asmgen.out(" eor P8ESTACK_LO+1,x") + if(ptrOnZp) + asmgen.out(" sta ($sourceName),y") + else + asmgen.out(" sta (P8ZP_SCRATCH_W1),y") + } + "|" -> { + val (ptrOnZp, sourceName) = asmgen.loadByteFromPointerIntoA(pointervar) + asmgen.out(" ora P8ESTACK_LO+1,x") + if(ptrOnZp) + asmgen.out(" sta ($sourceName),y") + else + asmgen.out(" sta (P8ZP_SCRATCH_W1),y") + } + else -> throw AssemblyError("invalid operator for in-place modification $operator") + } + asmgen.out(" inx") + } + + private fun inplaceModification_byte_variable_to_memory(pointervar: IdentifierReference, operator: String, value: IdentifierReference) { + val otherName = asmgen.asmVariableName(value) + when (operator) { + // note: ** (power) operator requires floats. + "+" -> { + val (ptrOnZp, sourceName) = asmgen.loadByteFromPointerIntoA(pointervar) + asmgen.out(" clc | adc $otherName") + if(ptrOnZp) + asmgen.out(" sta ($sourceName),y") + else + asmgen.out(" sta (P8ZP_SCRATCH_W1),y") + } + "-" -> { + val (ptrOnZp, sourceName) = asmgen.loadByteFromPointerIntoA(pointervar) + asmgen.out(" sec | sbc $otherName") + if(ptrOnZp) + asmgen.out(" sta ($sourceName),y") + else + asmgen.out(" sta (P8ZP_SCRATCH_W1),y") + } + "*" -> TODO("mul")// asmgen.out(" jsr prog8_lib.mul_byte") // the optimized routines should have been checked earlier + "/" -> TODO("div")// asmgen.out(if(types==DataType.UBYTE) " jsr prog8_lib.idiv_ub" else " jsr prog8_lib.idiv_b") + "%" -> { + TODO("byte remainder") +// if(types==DataType.BYTE) +// throw AssemblyError("remainder of signed integers is not properly defined/implemented, use unsigned instead") +// asmgen.out(" jsr prog8_lib.remainder_ub") + } + "<<" -> TODO("ubyte asl") + ">>" -> TODO("ubyte lsr") + "&" -> { + val (ptrOnZp, sourceName) = asmgen.loadByteFromPointerIntoA(pointervar) + asmgen.out(" and $otherName") + if(ptrOnZp) + asmgen.out(" sta ($sourceName),y") + else + asmgen.out(" sta (P8ZP_SCRATCH_W1),y") + } + "^" -> { + val (ptrOnZp, sourceName) = asmgen.loadByteFromPointerIntoA(pointervar) + asmgen.out(" eor $otherName") + if(ptrOnZp) + asmgen.out(" sta ($sourceName),y") + else + asmgen.out(" sta (P8ZP_SCRATCH_W1),y") + } + "|" -> { + val (ptrOnZp, sourceName) = asmgen.loadByteFromPointerIntoA(pointervar) + asmgen.out(" ora $otherName") + if(ptrOnZp) + asmgen.out(" sta ($sourceName),y") + else + asmgen.out(" sta (P8ZP_SCRATCH_W1),y") + } + else -> throw AssemblyError("invalid operator for in-place modification $operator") + } + } + + private fun inplaceModification_byte_litval_to_memory(pointervar: IdentifierReference, operator: String, value: Int) { + when (operator) { + // note: ** (power) operator requires floats. + "+" -> { + val (ptrOnZp, sourceName) = asmgen.loadByteFromPointerIntoA(pointervar) + asmgen.out(" clc | adc #$value") + if(ptrOnZp) + asmgen.out(" sta ($sourceName),y") + else + asmgen.out(" sta (P8ZP_SCRATCH_W1),y") + } + "-" -> { + val (ptrOnZp, sourceName) = asmgen.loadByteFromPointerIntoA(pointervar) + asmgen.out(" sec | sbc #$value") + if(ptrOnZp) + asmgen.out(" sta ($sourceName),y") + else + asmgen.out(" sta (P8ZP_SCRATCH_W1),y") + } + "*" -> { + TODO("mul byte litval") + // asmgen.out(" jsr prog8_lib.mul_byte") // the optimized routines should have been checked earlier + } + "/" -> { + if(value==0) + throw AssemblyError("division by zero") + TODO("div byte litval") + // asmgen.out(if(types==DataType.UBYTE) " jsr prog8_lib.idiv_ub" else " jsr prog8_lib.idiv_b") + } + "%" -> { + if(value==0) + throw AssemblyError("division by zero") + TODO("byte remainder litval") +// if(types==DataType.BYTE) +// throw AssemblyError("remainder of signed integers is not properly defined/implemented, use unsigned instead") +// asmgen.out(" jsr prog8_lib.remainder_ub") + } + "<<" -> { + if (value > 0) { + val (ptrOnZp, sourceName) = asmgen.loadByteFromPointerIntoA(pointervar) + repeat(value) { asmgen.out(" asl a") } + if(ptrOnZp) + asmgen.out(" sta ($sourceName),y") + else + asmgen.out(" sta (P8ZP_SCRATCH_W1),y") + } + } + ">>" -> { + if (value > 0) { + val (ptrOnZp, sourceName) = asmgen.loadByteFromPointerIntoA(pointervar) + repeat(value) { asmgen.out(" lsr a") } + if(ptrOnZp) + asmgen.out(" sta ($sourceName),y") + else + asmgen.out(" sta (P8ZP_SCRATCH_W1),y") + } + } + "&" -> { + val (ptrOnZp, sourceName) = asmgen.loadByteFromPointerIntoA(pointervar) + asmgen.out(" and #$value") + if(ptrOnZp) + asmgen.out(" sta ($sourceName),y") + else + asmgen.out(" sta (P8ZP_SCRATCH_W1),y") + } + "^" -> { + val (ptrOnZp, sourceName) = asmgen.loadByteFromPointerIntoA(pointervar) + asmgen.out(" eor #$value") + if(ptrOnZp) + asmgen.out(" sta ($sourceName),y") + else + asmgen.out(" sta (P8ZP_SCRATCH_W1),y") + } + "|" -> { + val (ptrOnZp, sourceName) = asmgen.loadByteFromPointerIntoA(pointervar) + asmgen.out(" ora #$value") + if(ptrOnZp) + asmgen.out(" sta ($sourceName),y") + else + asmgen.out(" sta (P8ZP_SCRATCH_W1),y") + } + else -> throw AssemblyError("invalid operator for in-place modification $operator") + } + } + + private fun inplaceModification_byte_value_to_variable(name: String, dt: DataType, operator: String, value: Expression) { + // this should be the last resort for code generation for this, + // because the value is evaluated onto the eval stack (=slow). + println("warning: slow stack evaluation used (5): $name $operator= ${value::class.simpleName} at ${value.position}") // TODO + asmgen.translateExpression(value) + when (operator) { + // note: ** (power) operator requires floats. + "+" -> asmgen.out(" lda $name | clc | adc P8ESTACK_LO+1,x | sta $name") + "-" -> asmgen.out(" lda $name | sec | sbc P8ESTACK_LO+1,x | sta $name") + "*" -> { + TODO("mul byte expr") + // asmgen.out(" jsr prog8_lib.mul_byte") // the optimized routines should have been checked earlier + } + "/" -> { + TODO("div byte expr")// asmgen.out(if(types==DataType.UBYTE) " jsr prog8_lib.idiv_ub" else " jsr prog8_lib.idiv_b") + } + "%" -> { + TODO("byte remainder expr") +// if(types==DataType.BYTE) +// throw AssemblyError("remainder of signed integers is not properly defined/implemented, use unsigned instead") +// asmgen.out(" jsr prog8_lib.remainder_ub") + } + "<<" -> { + asmgen.translateExpression(value) + asmgen.out(""" + inx + ldy P8ESTACK_LO,x + beq + +- asl $name + dey + bne - ++""") + } + ">>" -> { + asmgen.translateExpression(value) + if(dt==DataType.UBYTE) { + asmgen.out(""" + inx + ldy P8ESTACK_LO,x + beq + +- lsr $name + dey + bne - ++""") + } else { + asmgen.out(""" + inx + ldy P8ESTACK_LO,x + beq + +- lda $name + asl a + ror $name + dey + bne - ++""") + } + } + "&" -> asmgen.out(" lda $name | and P8ESTACK_LO+1,x | sta $name") + "^" -> asmgen.out(" lda $name | eor P8ESTACK_LO+1,x | sta $name") + "|" -> asmgen.out(" lda $name | ora P8ESTACK_LO+1,x | sta $name") + else -> throw AssemblyError("invalid operator for in-place modification $operator") + } + asmgen.out(" inx") + } + + private fun inplaceModification_byte_variable_to_variable(name: String, dt: DataType, operator: String, ident: IdentifierReference) { + val otherName = asmgen.asmVariableName(ident) + when (operator) { + // note: ** (power) operator requires floats. + "+" -> asmgen.out(" lda $name | clc | adc $otherName | sta $name") + "-" -> asmgen.out(" lda $name | sec | sbc $otherName | sta $name") + "*" -> asmgen.out(" lda $name | ldy $otherName | jsr math.multiply_bytes | sta $name") + "/" -> { + if(dt==DataType.BYTE) { + TODO("signed byte divide see prog8lib.idiv_b") + } + else { + asmgen.out(" lda $name | ldy $otherName | jsr math.divmod_ub_asm | sty $name") + } + } + "%" -> { + if(dt==DataType.BYTE) + throw AssemblyError("remainder of signed integers is not properly defined/implemented, use unsigned instead") + asmgen.out(" lda $name | ldy $otherName | jsr math.divmod_ub_asm | sta $name") + } + "<<" -> { + asmgen.out(""" + ldy $otherName +- asl $name + dey + bne -""") + } + ">>" -> { + if(dt==DataType.UBYTE) { + asmgen.out(""" + ldy $otherName +- lsr $name + dey + bne -""") + } else { + asmgen.out(""" + ldy $otherName +- lda $name + asl a + ror $name + dey + bne -""") + } + } + "&" -> asmgen.out(" lda $name | and $otherName | sta $name") + "^" -> asmgen.out(" lda $name | eor $otherName | sta $name") + "|" -> asmgen.out(" lda $name | ora $otherName | sta $name") + "and" -> asmgen.out(""" + lda $name + and $otherName + beq + + lda #1 ++ sta $name""") + "or" -> asmgen.out(""" + lda $name + ora $otherName + beq + + lda #1 ++ sta $name""") + "xor" -> asmgen.out(""" + lda $name + eor $otherName + beq + + lda #1 ++ sta $name""") + else -> throw AssemblyError("invalid operator for in-place modification $operator") + } + } + + private fun inplaceModification_byte_litval_to_variable(name: String, dt: DataType, operator: String, value: Int) { + when (operator) { + // note: ** (power) operator requires floats. + "+" -> asmgen.out(" lda $name | clc | adc #$value | sta $name") + "-" -> asmgen.out(" lda $name | sec | sbc #$value | sta $name") + "*" -> { + // TODO what about the optimized mul_5 etc routines? + TODO("byte mul litval") + // asmgen.out(" jsr prog8_lib.mul_byte") // the optimized routines should have been checked earlier + } + "/" -> { + if (dt == DataType.UBYTE) { + asmgen.out(""" + lda $name + ldy #$value + jsr math.divmod_ub_asm + sty $name + """) + } else { + TODO("BYTE div litval") + } + } + "%" -> { + if(dt==DataType.BYTE) + throw AssemblyError("remainder of signed integers is not properly defined/implemented, use unsigned instead") + asmgen.out(""" + lda $name + ldy #$value + jsr math.divmod_ub_asm + sta $name""") + } + "<<" -> { + repeat(value) { asmgen.out(" asl $name") } + } + ">>" -> { + if(value>0) { + if (dt == DataType.UBYTE) { + repeat(value) { asmgen.out(" lsr $name") } + } else { + repeat(value) { asmgen.out(" lda $name | asl a | ror $name") } + } + } + } + "&" -> asmgen.out(" lda $name | and #$value | sta $name") + "^" -> asmgen.out(" lda $name | eor #$value | sta $name") + "|" -> asmgen.out(" lda $name | ora #$value | sta $name") + else -> throw AssemblyError("invalid operator for in-place modification $operator") + } + } + + private fun inplaceModification_word_litval_to_variable(name: String, dt: DataType, operator: String, value: Int) { + when (operator) { + // note: ** (power) operator requires floats. + // TODO use these + and - optimizations in the expressionAsmGenerator as well. + "+" -> { + when { + value<0x0100 -> asmgen.out(""" + lda $name + clc + adc #$value + sta $name + bcc + + inc $name+1 ++ """) + value==0x0100 -> asmgen.out(" 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 and 255==0 -> asmgen.out(" lda $name+1 | clc | adc #>$value | sta $name+1") + else -> asmgen.out(""" + lda $name + clc + adc #<$value + sta $name + lda $name+1 + adc #>$value + sta $name+1""") + } + } + "-" -> { + when { + value<0x0100 -> asmgen.out(""" + lda $name + sec + sbc #$value + sta $name + bcs + + dec $name+1 ++ """) + value==0x0100 -> asmgen.out(" 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 and 255==0 -> asmgen.out(" lda $name+1 | sec | sbc #>$value | sta $name+1") + else -> asmgen.out(""" + lda $name + sec + sbc #<$value + sta $name + lda $name+1 + sbc #>$value + sta $name+1""") + } + } + "*" -> { + // TODO what about the optimized mul_5 etc routines? + asmgen.out(""" + lda $name + sta P8ZP_SCRATCH_W1 + lda $name+1 + sta P8ZP_SCRATCH_W1+1 + lda #<$value + ldy #>$value + jsr math.multiply_words + lda math.multiply_words.result + sta $name + lda math.multiply_words.result+1 + sta $name+1""") + } + "/" -> { + if(value==0) + throw AssemblyError("division by zero") + if(dt==DataType.WORD) { + TODO("signed word divide see prog8lib.idiv_w") + } + else { + asmgen.out(""" + lda $name + ldy $name+1 + sta P8ZP_SCRATCH_W1 + sty P8ZP_SCRATCH_W1+1 + lda #<$value + ldy #>$value + jsr math.divmod_uw_asm + sta $name + sty $name+1 + """) + } + } + "%" -> { + if(value==0) + throw AssemblyError("division by zero") + if(dt==DataType.WORD) + throw AssemblyError("remainder of signed integers is not properly defined/implemented, use unsigned instead") + asmgen.out(""" + lda $name + ldy $name+1 + sta P8ZP_SCRATCH_W1 + sty P8ZP_SCRATCH_W1+1 + lda #<$value + ldy #>$value + jsr math.divmod_uw_asm + lda P8ZP_SCRATCH_W2 + sta $name + lda P8ZP_SCRATCH_W2+2 + sty $name+1 + """) + } + "<<" -> { + repeat(value) { asmgen.out(" asl $name | rol $name+1") } + } + ">>" -> { + if (value > 0) { + if(dt==DataType.UWORD) { + repeat(value) { asmgen.out(" lsr $name+1 | ror $name")} + } else { + repeat(value) { asmgen.out(" lda $name+1 | asl a | ror $name+1 | ror $name") } + } + } + } + "&" -> { + when { + value == 0 -> asmgen.out(" lda #0 | sta $name | sta $name+1") + value and 255 == 0 -> asmgen.out(" lda #0 | sta $name | lda $name+1 | and #>$value | sta $name+1") + value < 0x0100 -> asmgen.out(" lda $name | and #$value | sta $name | lda #0 | sta $name+1") + else -> asmgen.out(" lda $name | and #<$value | sta $name | lda $name+1 | and #>$value | sta $name+1") + } + } + "^" -> { + when { + value == 0 -> {} + value and 255 == 0 -> asmgen.out(" lda $name+1 | eor #>$value | sta $name+1") + value < 0x0100 -> asmgen.out(" lda $name | eor #$value | sta $name") + else -> asmgen.out(" lda $name | eor #<$value | sta $name | lda $name+1 | eor #>$value | sta $name+1") + } + } + "|" -> { + when { + value == 0 -> {} + value and 255 == 0 -> asmgen.out(" lda $name+1 | ora #>$value | sta $name+1") + value < 0x0100 -> asmgen.out(" lda $name | ora #$value | sta $name") + else -> asmgen.out(" lda $name | ora #<$value | sta $name | lda $name+1 | ora #>$value | sta $name+1") + } + } + else -> throw AssemblyError("invalid operator for in-place modification $operator") + } + } + + private fun inplaceModification_word_variable_to_variable(name: String, dt: DataType, operator: String, ident: IdentifierReference) { + val otherName = asmgen.asmVariableName(ident) + val valueDt = ident.targetVarDecl(program.namespace)!!.datatype + when (valueDt) { + in ByteDatatypes -> { + // the other variable is a BYTE type so optimize for that TODO does this even occur? + when (operator) { + // note: ** (power) operator requires floats. + "+" -> asmgen.out(""" + lda $name + clc + adc $otherName + sta $name + bcc + + inc $name+1 ++ """) + "-" -> asmgen.out(""" + lda $name + sec + sbc $otherName + sta $name + bcs + + dec $name+1 ++ """) + "*" -> TODO("mul word*byte") + "/" -> TODO("div word/byte") + "%" -> TODO("word remainder byte") + "<<" -> { + asmgen.out(""" + ldy $otherName +- asl $name + rol $name+1 + dey + bne -""") + } + ">>" -> { + if(dt==DataType.UWORD) { + asmgen.out(""" + ldy $otherName +- lsr $name+1 + ror $name + dey + bne -""") + } else { + asmgen.out(""" + ldy $otherName +- lda $name+1 + asl a + ror $name+1 + ror $name + dey + bne -""") + } + } + "&" -> TODO("bitand word byte") + "^" -> TODO("bitxor word byte") + "|" -> TODO("bitor word byte") + else -> throw AssemblyError("invalid operator for in-place modification $operator") + } + } + in WordDatatypes -> { + // the value is a proper 16-bit word, so use both bytes of it. + when (operator) { + // note: ** (power) operator requires floats. + "+" -> asmgen.out(" lda $name | clc | adc $otherName | sta $name | lda $name+1 | adc $otherName+1 | sta $name+1") + "-" -> asmgen.out(" lda $name | sec | sbc $otherName | sta $name | lda $name+1 | sbc $otherName+1 | sta $name+1") + "*" -> { + asmgen.out(""" + lda $otherName + ldy $otherName+1 + sta P8ZP_SCRATCH_W1 + sty P8ZP_SCRATCH_W1+1 + lda $name + ldy $name+1 + jsr math.multiply_words + lda math.multiply_words.result + sta $name + lda math.multiply_words.result+1 + sta $name+1 + """) + } + "/" -> { + if(dt==DataType.WORD) { + TODO("signed word divide see prog8lib.idiv_w") + } + else { + asmgen.out(""" + lda $name + ldy $name+1 + sta P8ZP_SCRATCH_W1 + sty P8ZP_SCRATCH_W1+1 + lda $otherName + ldy $otherName+1 + jsr math.divmod_uw_asm + sta $name + sty $name+1 + """) + } + } + "%" -> { + if(dt==DataType.WORD) + throw AssemblyError("remainder of signed integers is not properly defined/implemented, use unsigned instead") + asmgen.out(""" + lda $name + ldy $name+1 + sta P8ZP_SCRATCH_W1 + sty P8ZP_SCRATCH_W1+1 + lda $otherName + ldy $otherName+1 + jsr math.divmod_uw_asm + lda P8ZP_SCRATCH_W2 + sta $name + lda P8ZP_SCRATCH_W2+1 + sta $name+1 + """) + } + "<<", ">>" -> throw AssemblyError("shift by a word value not supported, max is a byte") + "&" -> asmgen.out(" lda $name | and $otherName | sta $name | lda $name+1 | and $otherName+1 | sta $name+1") + "^" -> asmgen.out(" lda $name | eor $otherName | sta $name | lda $name+1 | eor $otherName+1 | sta $name+1") + "|" -> asmgen.out(" lda $name | ora $otherName | sta $name | lda $name+1 | ora $otherName+1 | sta $name+1") + else -> throw AssemblyError("invalid operator for in-place modification $operator") + } + } + else -> { + throw AssemblyError("can only use integer datatypes here") + } + } + } + + private fun inplaceModification_word_value_to_variable(name: String, dt: DataType, operator: String, value: Expression) { + // this should be the last resort for code generation for this, + // because the value is evaluated onto the eval stack (=slow). + println("warning: slow stack evaluation used (4): $name $operator= ${value::class.simpleName} at ${value.position}") // TODO + asmgen.translateExpression(value) + val valueDt = value.inferType(program).typeOrElse(DataType.STRUCT) + + when(valueDt) { + in ByteDatatypes -> { + // the other variable is a BYTE type so optimize for that TODO does this even occur? + when (operator) { + // note: ** (power) operator requires floats. + "+" -> asmgen.out(""" + lda $name + clc + adc P8ESTACK_LO+1,x + sta $name + bcc + + inc $name+1 ++ """) + "-" -> asmgen.out(""" + lda $name + sec + sbc P8ESTACK_LO+1,x + sta $name + bcs + + dec $name+1 ++ """) + "*" -> TODO("mul word byte") + "/" -> TODO("div word byte") + "%" -> TODO("word remainder byte") + "<<" -> { + asmgen.translateExpression(value) + asmgen.out(""" + inx + ldy P8ESTACK_LO,x + beq + +- asl $name + rol $name+1 + dey + bne - ++""") + } + ">>" -> { + asmgen.translateExpression(value) + if(dt==DataType.UWORD) { + asmgen.out(""" + inx + ldy P8ESTACK_LO,x + beq + +- lsr $name+1 + ror $name + dey + bne - ++""") } + else { + asmgen.out(""" + inx + ldy P8ESTACK_LO,x + beq + +- lda $name+1 + asl a + ror $name+1 + ror $name + dey + bne - ++""") + } + } + "&" -> TODO("bitand word byte") + "^" -> TODO("bitxor word byte") + "|" -> TODO("bitor word byte") + else -> throw AssemblyError("invalid operator for in-place modification $operator") + } + } + in WordDatatypes -> { + // the value is a proper 16-bit word, so use both bytes of it. + when (operator) { + // note: ** (power) operator requires floats. + "+" -> asmgen.out(" lda $name | clc | adc P8ESTACK_LO+1,x | sta $name | lda $name+1 | adc P8ESTACK_HI+1,x | sta $name+1") + "-" -> asmgen.out(" lda $name | sec | sbc P8ESTACK_LO+1,x | sta $name | lda $name+1 | sbc P8ESTACK_HI+1,x | sta $name+1") + "*" -> { + asmgen.out(""" + lda P8ESTACK_LO+1,x + ldy P8ESTACK_HI+1,x + sta P8ZP_SCRATCH_W1 + sty P8ZP_SCRATCH_W1+1 + lda $name + ldy $name+1 + jsr math.multiply_words + lda math.multiply_words.result + sta $name + lda math.multiply_words.result+1 + sta $name+1 + """) + } + "/" -> { + if (dt == DataType.WORD) { + TODO("signed word divide see prog8lib.idiv_w") + } else { + asmgen.out(""" + lda $name + ldy $name+1 + sta P8ZP_SCRATCH_W1 + sty P8ZP_SCRATCH_W1+1 + lda P8ESTACK_LO+1,x + ldy P8ESTACK_HI+1,x + jsr math.divmod_uw_asm + sta $name + sty $name+1 + """) + } + } + "%" -> { + if(dt==DataType.WORD) + throw AssemblyError("remainder of signed integers is not properly defined/implemented, use unsigned instead") + asmgen.out(""" + lda $name + ldy $name+1 + sta P8ZP_SCRATCH_W1 + sty P8ZP_SCRATCH_W1+1 + lda P8ESTACK_LO+1,x + ldy P8ESTACK_HI+1,x + jsr math.divmod_uw_asm + lda P8ZP_SCRATCH_W2 + sta $name + lda P8ZP_SCRATCH_W2+1 + sta $name+1 + """) + } + "<<", ">>" -> throw AssemblyError("shift by a word value not supported, max is a byte") + "&" -> asmgen.out(" lda $name | and P8ESTACK_LO+1,x | sta $name | lda $name+1 | and P8ESTACK_HI+1,x | sta $name+1") + "^" -> asmgen.out(" lda $name | eor P8ESTACK_LO+1,x | sta $name | lda $name+1 | eor P8ESTACK_HI+1,x | sta $name+1") + "|" -> asmgen.out(" lda $name | ora P8ESTACK_LO+1,x | sta $name | lda $name+1 | ora P8ESTACK_HI+1,x | sta $name+1") + else -> throw AssemblyError("invalid operator for in-place modification $operator") + } + } + else -> { + throw AssemblyError("can only use integer datatypes here") + } + } + + asmgen.out(" inx") + } + private fun inplaceModification_float_value_to_variable(name: String, operator: String, value: Expression) { // this should be the last resort for code generation for this, // because the value is evaluated onto the eval stack (=slow). @@ -440,693 +1247,6 @@ internal class AugmentableAssignmentAsmGen(private val program: Program, """) } - private fun inplaceModification_byte_value_to_memory(pointervar: IdentifierReference, operator: String, value: Expression) { - println("warning: slow stack evaluation used (3): @(${pointervar.nameInSource.last()}) $operator= ${value::class.simpleName} at ${value.position}") // TODO - asmgen.translateExpression(value) - when (operator) { - // note: ** (power) operator requires floats. - "+" -> { - val (ptrOnZp, sourceName) = asmgen.loadByteFromPointerIntoA(pointervar) - asmgen.out(" clc | adc P8ESTACK_LO+1,x") - if(ptrOnZp) - asmgen.out(" sta ($sourceName),y") - else - asmgen.out(" sta (P8ZP_SCRATCH_W1),y") - } - "-" -> { - val (ptrOnZp, sourceName) = asmgen.loadByteFromPointerIntoA(pointervar) - asmgen.out(" sec | sbc P8ESTACK_LO+1,x") - if(ptrOnZp) - asmgen.out(" sta ($sourceName),y") - else - asmgen.out(" sta (P8ZP_SCRATCH_W1),y") - } - "*" -> TODO("mul")// asmgen.out(" jsr prog8_lib.mul_byte") // the optimized routines should have been checked earlier - "/" -> TODO("div")// asmgen.out(if(types==DataType.UBYTE) " jsr prog8_lib.idiv_ub" else " jsr prog8_lib.idiv_b") - "%" -> { - TODO("byte remainder") -// if(types==DataType.BYTE) -// throw AssemblyError("remainder of signed integers is not properly defined/implemented, use unsigned instead") -// asmgen.out(" jsr prog8_lib.remainder_ub") - } - "<<" -> TODO("ubyte asl") - ">>" -> TODO("ubyte lsr") - "&" -> { - val (ptrOnZp, sourceName) = asmgen.loadByteFromPointerIntoA(pointervar) - asmgen.out(" and P8ESTACK_LO+1,x") - if(ptrOnZp) - asmgen.out(" sta ($sourceName),y") - else - asmgen.out(" sta (P8ZP_SCRATCH_W1),y") - } - "^" -> { - val (ptrOnZp, sourceName) = asmgen.loadByteFromPointerIntoA(pointervar) - asmgen.out(" eor P8ESTACK_LO+1,x") - if(ptrOnZp) - asmgen.out(" sta ($sourceName),y") - else - asmgen.out(" sta (P8ZP_SCRATCH_W1),y") - } - "|" -> { - val (ptrOnZp, sourceName) = asmgen.loadByteFromPointerIntoA(pointervar) - asmgen.out(" ora P8ESTACK_LO+1,x") - if(ptrOnZp) - asmgen.out(" sta ($sourceName),y") - else - asmgen.out(" sta (P8ZP_SCRATCH_W1),y") - } - else -> throw AssemblyError("invalid operator for in-place modification $operator") - } - asmgen.out(" inx") - } - - private fun inplaceModification_byte_variable_to_memory(pointervar: IdentifierReference, operator: String, value: IdentifierReference) { - val otherName = asmgen.asmVariableName(value) - when (operator) { - // note: ** (power) operator requires floats. - "+" -> { - val (ptrOnZp, sourceName) = asmgen.loadByteFromPointerIntoA(pointervar) - asmgen.out(" clc | adc $otherName") - if(ptrOnZp) - asmgen.out(" sta ($sourceName),y") - else - asmgen.out(" sta (P8ZP_SCRATCH_W1),y") - } - "-" -> { - val (ptrOnZp, sourceName) = asmgen.loadByteFromPointerIntoA(pointervar) - asmgen.out(" sec | sbc $otherName") - if(ptrOnZp) - asmgen.out(" sta ($sourceName),y") - else - asmgen.out(" sta (P8ZP_SCRATCH_W1),y") - } - "*" -> TODO("mul")// asmgen.out(" jsr prog8_lib.mul_byte") // the optimized routines should have been checked earlier - "/" -> TODO("div")// asmgen.out(if(types==DataType.UBYTE) " jsr prog8_lib.idiv_ub" else " jsr prog8_lib.idiv_b") - "%" -> { - TODO("byte remainder") -// if(types==DataType.BYTE) -// throw AssemblyError("remainder of signed integers is not properly defined/implemented, use unsigned instead") -// asmgen.out(" jsr prog8_lib.remainder_ub") - } - "<<" -> TODO("ubyte asl") - ">>" -> TODO("ubyte lsr") - "&" -> { - val (ptrOnZp, sourceName) = asmgen.loadByteFromPointerIntoA(pointervar) - asmgen.out(" and $otherName") - if(ptrOnZp) - asmgen.out(" sta ($sourceName),y") - else - asmgen.out(" sta (P8ZP_SCRATCH_W1),y") - } - "^" -> { - val (ptrOnZp, sourceName) = asmgen.loadByteFromPointerIntoA(pointervar) - asmgen.out(" eor $otherName") - if(ptrOnZp) - asmgen.out(" sta ($sourceName),y") - else - asmgen.out(" sta (P8ZP_SCRATCH_W1),y") - } - "|" -> { - val (ptrOnZp, sourceName) = asmgen.loadByteFromPointerIntoA(pointervar) - asmgen.out(" ora $otherName") - if(ptrOnZp) - asmgen.out(" sta ($sourceName),y") - else - asmgen.out(" sta (P8ZP_SCRATCH_W1),y") - } - else -> throw AssemblyError("invalid operator for in-place modification $operator") - } - } - - private fun inplaceModification_byte_litval_to_memory(pointervar: IdentifierReference, operator: String, value: Int) { - when (operator) { - // note: ** (power) operator requires floats. - "+" -> { - val (ptrOnZp, sourceName) = asmgen.loadByteFromPointerIntoA(pointervar) - asmgen.out(" clc | adc #$value") - if(ptrOnZp) - asmgen.out(" sta ($sourceName),y") - else - asmgen.out(" sta (P8ZP_SCRATCH_W1),y") - } - "-" -> { - val (ptrOnZp, sourceName) = asmgen.loadByteFromPointerIntoA(pointervar) - asmgen.out(" sec | sbc #$value") - if(ptrOnZp) - asmgen.out(" sta ($sourceName),y") - else - asmgen.out(" sta (P8ZP_SCRATCH_W1),y") - } - "*" -> TODO("mul")// asmgen.out(" jsr prog8_lib.mul_byte") // the optimized routines should have been checked earlier - "/" -> { - if(value==0) - throw AssemblyError("division by zero") - TODO("div") - // asmgen.out(if(types==DataType.UBYTE) " jsr prog8_lib.idiv_ub" else " jsr prog8_lib.idiv_b") - } - "%" -> { - if(value==0) - throw AssemblyError("division by zero") - TODO("byte remainder") -// if(types==DataType.BYTE) -// throw AssemblyError("remainder of signed integers is not properly defined/implemented, use unsigned instead") -// asmgen.out(" jsr prog8_lib.remainder_ub") - } - "<<" -> { - if (value > 0) { - val (ptrOnZp, sourceName) = asmgen.loadByteFromPointerIntoA(pointervar) - repeat(value) { asmgen.out(" asl a") } - if(ptrOnZp) - asmgen.out(" sta ($sourceName),y") - else - asmgen.out(" sta (P8ZP_SCRATCH_W1),y") - } - } - ">>" -> { - if (value > 0) { - val (ptrOnZp, sourceName) = asmgen.loadByteFromPointerIntoA(pointervar) - repeat(value) { asmgen.out(" lsr a") } - if(ptrOnZp) - asmgen.out(" sta ($sourceName),y") - else - asmgen.out(" sta (P8ZP_SCRATCH_W1),y") - } - } - "&" -> { - val (ptrOnZp, sourceName) = asmgen.loadByteFromPointerIntoA(pointervar) - asmgen.out(" and #$value") - if(ptrOnZp) - asmgen.out(" sta ($sourceName),y") - else - asmgen.out(" sta (P8ZP_SCRATCH_W1),y") - } - "^" -> { - val (ptrOnZp, sourceName) = asmgen.loadByteFromPointerIntoA(pointervar) - asmgen.out(" eor #$value") - if(ptrOnZp) - asmgen.out(" sta ($sourceName),y") - else - asmgen.out(" sta (P8ZP_SCRATCH_W1),y") - } - "|" -> { - val (ptrOnZp, sourceName) = asmgen.loadByteFromPointerIntoA(pointervar) - asmgen.out(" ora #$value") - if(ptrOnZp) - asmgen.out(" sta ($sourceName),y") - else - asmgen.out(" sta (P8ZP_SCRATCH_W1),y") - } - else -> throw AssemblyError("invalid operator for in-place modification $operator") - } - } - - private fun inplaceModification_word_litval_to_variable(name: String, dt: DataType, operator: String, value: Int) { - when (operator) { - // note: ** (power) operator requires floats. - // TODO use these + and - optimizations in the expressionAsmGenerator as well. - "+" -> { - when { - value<0x0100 -> asmgen.out(""" - lda $name - clc - adc #$value - sta $name - bcc + - inc $name+1 -+ """) - value==0x0100 -> asmgen.out(" 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 and 255==0 -> asmgen.out(" lda $name+1 | clc | adc #>$value | sta $name+1") - else -> asmgen.out(""" - lda $name - clc - adc #<$value - sta $name - lda $name+1 - adc #>$value - sta $name+1""") - } - } - "-" -> { - when { - value<0x0100 -> asmgen.out(""" - lda $name - sec - sbc #$value - sta $name - bcs + - dec $name+1 -+ """) - value==0x0100 -> asmgen.out(" 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 and 255==0 -> asmgen.out(" lda $name+1 | sec | sbc #>$value | sta $name+1") - else -> asmgen.out(""" - lda $name - sec - sbc #<$value - sta $name - lda $name+1 - sbc #>$value - sta $name+1""") - } - } - "*" -> { - // TODO what about the optimized mul_5 etc routines? - asmgen.out(""" - lda $name - sta P8ZP_SCRATCH_W1 - lda $name+1 - sta P8ZP_SCRATCH_W1+1 - lda #<$value - ldy #>$value - jsr math.multiply_words - lda math.multiply_words.result - sta $name - lda math.multiply_words.result+1 - sta $name+1""") - } - "/" -> { - if(value==0) - throw AssemblyError("division by zero") - TODO("word $name /= $value") - // asmgen.out(if(types==DataType.UBYTE) " jsr prog8_lib.idiv_ub" else " jsr prog8_lib.idiv_b") - } - "%" -> { - if(value==0) - throw AssemblyError("division by zero") - TODO("word remainder $value") -// if(types==DataType.BYTE) -// throw AssemblyError("remainder of signed integers is not properly defined/implemented, use unsigned instead") -// asmgen.out(" jsr prog8_lib.remainder_ub") - } - "<<" -> { - repeat(value) { asmgen.out(" asl $name | rol $name+1") } - } - ">>" -> { - if (value > 0) { - if(dt==DataType.UWORD) { - repeat(value) { asmgen.out(" lsr $name+1 | ror $name")} - } else { - repeat(value) { asmgen.out(" lda $name+1 | asl a | ror $name+1 | ror $name") } - } - } - } - "&" -> { - when { - value == 0 -> asmgen.out(" lda #0 | sta $name | sta $name+1") - value and 255 == 0 -> asmgen.out(" lda #0 | sta $name | lda $name+1 | and #>$value | sta $name+1") - value < 0x0100 -> asmgen.out(" lda $name | and #$value | sta $name | lda #0 | sta $name+1") - else -> asmgen.out(" lda $name | and #<$value | sta $name | lda $name+1 | and #>$value | sta $name+1") - } - } - "^" -> { - when { - value == 0 -> {} - value and 255 == 0 -> asmgen.out(" lda $name+1 | eor #>$value | sta $name+1") - value < 0x0100 -> asmgen.out(" lda $name | eor #$value | sta $name") - else -> asmgen.out(" lda $name | eor #<$value | sta $name | lda $name+1 | eor #>$value | sta $name+1") - } - } - "|" -> { - when { - value == 0 -> {} - value and 255 == 0 -> asmgen.out(" lda $name+1 | ora #>$value | sta $name+1") - value < 0x0100 -> asmgen.out(" lda $name | ora #$value | sta $name") - else -> asmgen.out(" lda $name | ora #<$value | sta $name | lda $name+1 | ora #>$value | sta $name+1") - } - } - else -> throw AssemblyError("invalid operator for in-place modification $operator") - } - } - - private fun inplaceModification_word_variable_to_variable(name: String, dt: DataType, operator: String, ident: IdentifierReference) { - val otherName = asmgen.asmVariableName(ident) - val valueDt = ident.targetVarDecl(program.namespace)!!.datatype - when (valueDt) { - in ByteDatatypes -> { - // the other variable is a BYTE type so optimize for that - when (operator) { - // note: ** (power) operator requires floats. - "+" -> asmgen.out(""" - lda $name - clc - adc $otherName - sta $name - bcc + - inc $name+1 -+ """) - "-" -> asmgen.out(""" - lda $name - sec - sbc $otherName - sta $name - bcs + - dec $name+1 -+ """) - "*" -> TODO("mul") - "/" -> TODO("div") - "%" -> TODO("word remainder") - "<<" -> { - asmgen.out(""" - ldy $otherName -- asl $name - rol $name+1 - dey - bne -""") - } - ">>" -> { - if(dt==DataType.UWORD) { - asmgen.out(""" - ldy $otherName -- lsr $name+1 - ror $name - dey - bne -""") - } else { - asmgen.out(""" - ldy $otherName -- lda $name+1 - asl a - ror $name+1 - ror $name - dey - bne -""") - } - } - "&" -> TODO("bitand") - "^" -> TODO("bitxor") - "|" -> TODO("bitor") - else -> throw AssemblyError("invalid operator for in-place modification $operator") - } - } - in WordDatatypes -> { - // the value is a proper 16-bit word, so use both bytes of it. - when (operator) { - // note: ** (power) operator requires floats. - "+" -> asmgen.out(" lda $name | clc | adc $otherName | sta $name | lda $name+1 | adc $otherName+1 | sta $name+1") - "-" -> asmgen.out(" lda $name | sec | sbc $otherName | sta $name | lda $name+1 | sbc $otherName+1 | sta $name+1") - "*" -> TODO("mul")// asmgen.out(" jsr prog8_lib.mul_byte") // the optimized routines should have been checked earlier - "/" -> TODO("div")// asmgen.out(if(types==DataType.UBYTE) " jsr prog8_lib.idiv_ub" else " jsr prog8_lib.idiv_b") - "%" -> { - TODO("word remainder") -// if(types==DataType.BYTE) -// throw AssemblyError("remainder of signed integers is not properly defined/implemented, use unsigned instead") -// asmgen.out(" jsr prog8_lib.remainder_ub") - } - "<<", ">>" -> throw AssemblyError("shift by a word value not supported, max is a byte") - "&" -> asmgen.out(" lda $name | and $otherName | sta $name | lda $name+1 | and $otherName+1 | sta $name+1") - "^" -> asmgen.out(" lda $name | eor $otherName | sta $name | lda $name+1 | eor $otherName+1 | sta $name+1") - "|" -> asmgen.out(" lda $name | ora $otherName | sta $name | lda $name+1 | ora $otherName+1 | sta $name+1") - else -> throw AssemblyError("invalid operator for in-place modification $operator") - } - } - else -> { - throw AssemblyError("can only use integer datatypes here") - } - } - } - - private fun inplaceModification_word_value_to_variable(name: String, dt: DataType, operator: String, value: Expression) { - // this should be the last resort for code generation for this, - // because the value is evaluated onto the eval stack (=slow). - println("warning: slow stack evaluation used (4): $name $operator= ${value::class.simpleName} at ${value.position}") // TODO - asmgen.translateExpression(value) - val valueDt = value.inferType(program).typeOrElse(DataType.STRUCT) - - when(valueDt) { - in ByteDatatypes -> { - // the other variable is a BYTE type so optimize for that - when (operator) { - // note: ** (power) operator requires floats. - "+" -> asmgen.out(""" - lda $name - clc - adc P8ESTACK_LO+1,x - sta $name - bcc + - inc $name+1 -+ """) - "-" -> asmgen.out(""" - lda $name - sec - sbc P8ESTACK_LO+1,x - sta $name - bcs + - dec $name+1 -+ """) - "*" -> TODO("mul") - "/" -> TODO("div") - "%" -> TODO("word remainder") - "<<" -> { - asmgen.translateExpression(value) - asmgen.out(""" - inx - ldy P8ESTACK_LO,x - beq + -- asl $name - rol $name+1 - dey - bne - -+""") - } - ">>" -> { - asmgen.translateExpression(value) - if(dt==DataType.UWORD) { - asmgen.out(""" - inx - ldy P8ESTACK_LO,x - beq + -- lsr $name+1 - ror $name - dey - bne - -+""") } - else { - asmgen.out(""" - inx - ldy P8ESTACK_LO,x - beq + -- lda $name+1 - asl a - ror $name+1 - ror $name - dey - bne - -+""") - } - } - "&" -> TODO("bitand") - "^" -> TODO("bitxor") - "|" -> TODO("bitor") - else -> throw AssemblyError("invalid operator for in-place modification $operator") - } - } - in WordDatatypes -> { - // the value is a proper 16-bit word, so use both bytes of it. - when (operator) { - // note: ** (power) operator requires floats. - "+" -> asmgen.out(" lda $name | clc | adc P8ESTACK_LO+1,x | sta $name | lda $name+1 | adc P8ESTACK_HI+1,x | sta $name+1") - "-" -> asmgen.out(" lda $name | sec | sbc P8ESTACK_LO+1,x | sta $name | lda $name+1 | sbc P8ESTACK_HI+1,x | sta $name+1") - "*" -> TODO("mul")// asmgen.out(" jsr prog8_lib.mul_byte") // the optimized routines should have been checked earlier - "/" -> TODO("div")// asmgen.out(if(types==DataType.UBYTE) " jsr prog8_lib.idiv_ub" else " jsr prog8_lib.idiv_b") - "%" -> { - TODO("word remainder") -// if(types==DataType.BYTE) -// throw AssemblyError("remainder of signed integers is not properly defined/implemented, use unsigned instead") -// asmgen.out(" jsr prog8_lib.remainder_ub") - } - "<<", ">>" -> throw AssemblyError("shift by a word value not supported, max is a byte") - "&" -> asmgen.out(" lda $name | and P8ESTACK_LO+1,x | sta $name | lda $name+1 | and P8ESTACK_HI+1,x | sta $name+1") - "^" -> asmgen.out(" lda $name | eor P8ESTACK_LO+1,x | sta $name | lda $name+1 | eor P8ESTACK_HI+1,x | sta $name+1") - "|" -> asmgen.out(" lda $name | ora P8ESTACK_LO+1,x | sta $name | lda $name+1 | ora P8ESTACK_HI+1,x | sta $name+1") - else -> throw AssemblyError("invalid operator for in-place modification $operator") - } - } - else -> { - throw AssemblyError("can only use integer datatypes here") - } - } - - asmgen.out(" inx") - } - - private fun inplaceModification_byte_value_to_variable(name: String, dt: DataType, operator: String, value: Expression) { - // this should be the last resort for code generation for this, - // because the value is evaluated onto the eval stack (=slow). - println("warning: slow stack evaluation used (5): $name $operator= ${value::class.simpleName} at ${value.position}") // TODO - asmgen.translateExpression(value) - when (operator) { - // note: ** (power) operator requires floats. - "+" -> asmgen.out(" lda $name | clc | adc P8ESTACK_LO+1,x | sta $name") - "-" -> asmgen.out(" lda $name | sec | sbc P8ESTACK_LO+1,x | sta $name") - "*" -> TODO("mul")// asmgen.out(" jsr prog8_lib.mul_byte") // the optimized routines should have been checked earlier - "/" -> TODO("div")// asmgen.out(if(types==DataType.UBYTE) " jsr prog8_lib.idiv_ub" else " jsr prog8_lib.idiv_b") - "%" -> { - TODO("byte remainder") -// if(types==DataType.BYTE) -// throw AssemblyError("remainder of signed integers is not properly defined/implemented, use unsigned instead") -// asmgen.out(" jsr prog8_lib.remainder_ub") - } - "<<" -> { - asmgen.translateExpression(value) - asmgen.out(""" - inx - ldy P8ESTACK_LO,x - beq + -- asl $name - dey - bne - -+""") - } - ">>" -> { - asmgen.translateExpression(value) - if(dt==DataType.UBYTE) { - asmgen.out(""" - inx - ldy P8ESTACK_LO,x - beq + -- lsr $name - dey - bne - -+""") - } else { - asmgen.out(""" - inx - ldy P8ESTACK_LO,x - beq + -- lda $name - asl a - ror $name - dey - bne - -+""") - } - } - "&" -> asmgen.out(" lda $name | and P8ESTACK_LO+1,x | sta $name") - "^" -> asmgen.out(" lda $name | eor P8ESTACK_LO+1,x | sta $name") - "|" -> asmgen.out(" lda $name | ora P8ESTACK_LO+1,x | sta $name") - else -> throw AssemblyError("invalid operator for in-place modification $operator") - } - asmgen.out(" inx") - } - - private fun inplaceModification_byte_variable_to_variable(name: String, dt: DataType, operator: String, ident: IdentifierReference) { - val otherName = asmgen.asmVariableName(ident) - when (operator) { - // note: ** (power) operator requires floats. - "+" -> asmgen.out(" lda $name | clc | adc $otherName | sta $name") - "-" -> asmgen.out(" lda $name | sec | sbc $otherName | sta $name") - "*" -> TODO("mul")// asmgen.out(" jsr prog8_lib.mul_byte") // the optimized routines should have been checked earlier - "/" -> TODO("div")// asmgen.out(if(types==DataType.UBYTE) " jsr prog8_lib.idiv_ub" else " jsr prog8_lib.idiv_b") - "%" -> { - TODO("byte remainder") -// if(types==DataType.BYTE) -// throw AssemblyError("remainder of signed integers is not properly defined/implemented, use unsigned instead") -// asmgen.out(" jsr prog8_lib.remainder_ub") - } - "<<" -> { - asmgen.out(""" - ldy $otherName -- asl $name - dey - bne -""") - } - ">>" -> { - if(dt==DataType.UBYTE) { - asmgen.out(""" - ldy $otherName -- lsr $name - dey - bne -""") - } else { - asmgen.out(""" - ldy $otherName -- lda $name - asl a - ror $name - dey - bne -""") - } - } - "&" -> asmgen.out(" lda $name | and $otherName | sta $name") - "^" -> asmgen.out(" lda $name | eor $otherName | sta $name") - "|" -> asmgen.out(" lda $name | ora $otherName | sta $name") - "and" -> asmgen.out(""" - lda $name - and $otherName - beq + - lda #1 -+ sta $name""") - "or" -> asmgen.out(""" - lda $name - ora $otherName - beq + - lda #1 -+ sta $name""") - "xor" -> asmgen.out(""" - lda $name - eor $otherName - beq + - lda #1 -+ sta $name""") - else -> throw AssemblyError("invalid operator for in-place modification $operator") - } - } - - private fun inplaceModification_byte_litval_to_variable(name: String, dt: DataType, operator: String, value: Int) { - when (operator) { - // note: ** (power) operator requires floats. - "+" -> asmgen.out(" lda $name | clc | adc #$value | sta $name") - "-" -> asmgen.out(" lda $name | sec | sbc #$value | sta $name") - "*" -> { - // TODO what about the optimized mul_5 etc routines? - TODO("$dt mul $name *= $value") - // asmgen.out(" jsr prog8_lib.mul_byte") // the optimized routines should have been checked earlier - } - "/" -> { - if (dt == DataType.UBYTE) { - asmgen.out(""" - lda $name - ldy #$value - jsr math.divmod_ub - sty $name - """) - } else { - // BYTE - // requires to use unsigned division and fix sign afterwards, see idiv_b in prog8lib - TODO("BYTE div $name /= $value") - } - } - "%" -> { - if(dt==DataType.BYTE) - throw AssemblyError("remainder of signed integers is not properly defined/implemented, use unsigned instead") - asmgen.out(""" - lda $name - ldy #$value - jsr math.divmod_ub - sta $name""") - } - "<<" -> { - repeat(value) { asmgen.out(" asl $name") } - } - ">>" -> { - if(value>0) { - if (dt == DataType.UBYTE) { - repeat(value) { asmgen.out(" lsr $name") } - } else { - repeat(value) { asmgen.out(" lda $name | asl a | ror $name") } - } - } - } - "&" -> asmgen.out(" lda $name | and #$value | sta $name") - "^" -> asmgen.out(" lda $name | eor #$value | sta $name") - "|" -> asmgen.out(" lda $name | ora #$value | sta $name") - else -> throw AssemblyError("invalid operator for in-place modification $operator") - } - } - private fun inplaceCast(target: AsmAssignTarget, cast: TypecastExpression, position: Position) { val outerCastDt = cast.type val innerCastDt = (cast.expression as? TypecastExpression)?.type @@ -1218,8 +1338,8 @@ internal class AugmentableAssignmentAsmGen(private val program: Program, } } TargetStorageKind.ARRAY -> TODO("in-place not of ubyte array") - TargetStorageKind.REGISTER -> TODO("reg") - TargetStorageKind.STACK -> TODO("stack") + TargetStorageKind.REGISTER -> TODO("reg not") + TargetStorageKind.STACK -> TODO("stack not") } } DataType.UWORD -> { @@ -1237,8 +1357,8 @@ internal class AugmentableAssignmentAsmGen(private val program: Program, } TargetStorageKind.MEMORY -> throw AssemblyError("no asm gen for uword-memory not") TargetStorageKind.ARRAY -> TODO("in-place not of uword array") - TargetStorageKind.REGISTER -> TODO("reg") - TargetStorageKind.STACK -> TODO("stack") + TargetStorageKind.REGISTER -> TODO("reg not") + TargetStorageKind.STACK -> TODO("stack not") } } else -> throw AssemblyError("boolean-not of invalid type") @@ -1285,8 +1405,8 @@ internal class AugmentableAssignmentAsmGen(private val program: Program, } } TargetStorageKind.ARRAY -> TODO("in-place invert ubyte array") - TargetStorageKind.REGISTER -> TODO("reg") - TargetStorageKind.STACK -> TODO("stack") + TargetStorageKind.REGISTER -> TODO("reg invert") + TargetStorageKind.STACK -> TODO("stack invert") } } DataType.UWORD -> { @@ -1302,8 +1422,8 @@ internal class AugmentableAssignmentAsmGen(private val program: Program, } TargetStorageKind.MEMORY -> throw AssemblyError("no asm gen for uword-memory invert") TargetStorageKind.ARRAY -> TODO("in-place invert uword array") - TargetStorageKind.REGISTER -> TODO("reg") - TargetStorageKind.STACK -> TODO("stack") + TargetStorageKind.REGISTER -> TODO("reg invert") + TargetStorageKind.STACK -> TODO("stack invert") } } else -> throw AssemblyError("invert of invalid type") @@ -1323,8 +1443,8 @@ internal class AugmentableAssignmentAsmGen(private val program: Program, } TargetStorageKind.MEMORY -> throw AssemblyError("can't in-place negate memory ubyte") TargetStorageKind.ARRAY -> TODO("in-place negate byte array") - TargetStorageKind.REGISTER -> TODO("reg") - TargetStorageKind.STACK -> TODO("stack") + TargetStorageKind.REGISTER -> TODO("reg negate") + TargetStorageKind.STACK -> TODO("stack negate") } } DataType.WORD -> { @@ -1341,8 +1461,8 @@ internal class AugmentableAssignmentAsmGen(private val program: Program, } TargetStorageKind.ARRAY -> TODO("in-place negate word array") TargetStorageKind.MEMORY -> throw AssemblyError("no asm gen for word memory negate") - TargetStorageKind.REGISTER -> TODO("reg") - TargetStorageKind.STACK -> TODO("stack") + TargetStorageKind.REGISTER -> TODO("reg negate") + TargetStorageKind.STACK -> TODO("stack negate") } } DataType.FLOAT -> { @@ -1361,9 +1481,8 @@ internal class AugmentableAssignmentAsmGen(private val program: Program, """) } TargetStorageKind.ARRAY -> TODO("in-place negate float array") - TargetStorageKind.MEMORY -> throw AssemblyError("no asm gen for float memory negate") - TargetStorageKind.REGISTER -> TODO("reg") - TargetStorageKind.STACK -> TODO("stack") + TargetStorageKind.STACK -> TODO("stack float negate") + else -> throw AssemblyError("weird target kind for float") } } else -> throw AssemblyError("negate of invalid type") diff --git a/examples/arithmetic/div.p8 b/examples/arithmetic/div.p8 index 1827b4023..b1ea60ccd 100644 --- a/examples/arithmetic/div.p8 +++ b/examples/arithmetic/div.p8 @@ -2,7 +2,7 @@ %import c64textio %zeropage basicsafe -; TODO implement DIV asm generation +; TODO implement signed byte/word DIV asm generation, fix unsigned DIV asm generation (for in-place) main { @@ -11,17 +11,17 @@ main { div_ubyte(100, 6, 16) div_ubyte(255, 2, 127) - div_byte(0, 1, 0) - div_byte(100, -6, -16) - div_byte(127, -2, -63) + ;div_byte(0, 1, 0) ; TODO implement + ;div_byte(100, -6, -16) ; TODO implement + ;div_byte(127, -2, -63) ; TODO implement div_uword(0,1,0) div_uword(40000,500,80) div_uword(43211,2,21605) - div_word(0,1,0) - div_word(-20000,500,-40) - div_word(-2222,2,-1111) + ;div_word(0,1,0) ; TODO implement + ;div_word(-20000,500,-40) ; TODO implement + ;div_word(-2222,2,-1111) ; TODO implement div_float(0,1,0) div_float(999.9,111.0,9.008108108108107) diff --git a/examples/arithmetic/mult.p8 b/examples/arithmetic/mult.p8 index 211f4b145..2a34e691f 100644 --- a/examples/arithmetic/mult.p8 +++ b/examples/arithmetic/mult.p8 @@ -2,8 +2,6 @@ %import c64textio %zeropage basicsafe -; TODO implement MUL asm generation - main { sub start() { diff --git a/examples/arithmetic/remainder.p8 b/examples/arithmetic/remainder.p8 index a8c5d69e5..c37a1c262 100644 --- a/examples/arithmetic/remainder.p8 +++ b/examples/arithmetic/remainder.p8 @@ -1,8 +1,6 @@ %import c64textio %zeropage basicsafe -; TODO implement REMAINDER asmgeneration - main { sub start() { diff --git a/examples/test.p8 b/examples/test.p8 index 83ba84883..a286fa242 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -1,15 +1,18 @@ -%import c64flt +%import c64textio %zeropage basicsafe main { sub start() { - float f1 = 2.2 - float f2 = 1.0 - float f4 = 4.0 - float f5 = 5.0 + ubyte b1 = 2 + ubyte b2 = 13 + ubyte b3 = 100 - f1 /= f2+f4 - c64flt.print_f(f1) + uword w1 = 2222 + uword w2 = 11 + uword w3 = 33 + + w1 %= (w2+w3) + txt.print_uw(w1) c64.CHROUT('\n') } }