diff --git a/codeCore/src/prog8/code/ast/AstBase.kt b/codeCore/src/prog8/code/ast/AstBase.kt index 5ba34a920..91245efca 100644 --- a/codeCore/src/prog8/code/ast/AstBase.kt +++ b/codeCore/src/prog8/code/ast/AstBase.kt @@ -75,6 +75,7 @@ class PtBlock(name: String, val library: Boolean, val forceOutput: Boolean, val noSymbolPrefixing: Boolean, + val veraFxMuls: Boolean, val alignment: BlockAlignment, val source: SourceCode, // taken from the module the block is defined in. position: Position diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AssignmentAsmGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AssignmentAsmGen.kt index 109a3a171..ce3743ebf 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AssignmentAsmGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AssignmentAsmGen.kt @@ -544,9 +544,28 @@ internal class AssignmentAsmGen(private val program: PtProgram, return true } in WordDatatypes -> { - asmgen.assignWordOperandsToAYAndVar(expr.right, expr.left, "math.multiply_words.multiplier") - asmgen.out(" jsr math.multiply_words") - assignRegisterpairWord(target, RegisterOrPair.AY) + if(expr.definingBlock()!!.veraFxMuls) { + // cx16 verafx hardware mul + if(expr.right.isSimple()) { + asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.R0, expr.left.type in SignedDatatypes) + asmgen.assignExpressionToRegister(expr.right, RegisterOrPair.R1, expr.left.type in SignedDatatypes) + } else { + asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.AY, expr.left.type in SignedDatatypes) + asmgen.saveRegisterStack(CpuRegister.A, false) + asmgen.saveRegisterStack(CpuRegister.Y, false) + asmgen.assignExpressionToRegister(expr.right, RegisterOrPair.R1, expr.left.type in SignedDatatypes) + asmgen.restoreRegisterStack(CpuRegister.Y, false) + asmgen.restoreRegisterStack(CpuRegister.A, false) + asmgen.out(" sta cx16.r0 | sty cx16.r0+1") + } + asmgen.out(" jsr verafx.muls") + assignRegisterpairWord(target, RegisterOrPair.AY) + return true + } else { + asmgen.assignWordOperandsToAYAndVar(expr.right, expr.left, "math.multiply_words.multiplier") + asmgen.out(" jsr math.multiply_words") + assignRegisterpairWord(target, RegisterOrPair.AY) + } return true } else -> return false @@ -568,8 +587,17 @@ internal class AssignmentAsmGen(private val program: PtProgram, asmgen.out(" jsr math.mul_word_${value}") } else { - asmgen.assignWordOperandsToAYAndVar(expr.right, expr.left, "math.multiply_words.multiplier") - asmgen.out(" jsr math.multiply_words") + if(expr.definingBlock()!!.veraFxMuls){ + // cx16 verafx hardware mul + asmgen.assignWordOperandsToAYAndVar(expr.right, expr.left, "cx16.r1") + asmgen.out(""" + sta cx16.r0 + sty cx16.r0+1 + jsr verafx.muls""") + } else { + asmgen.assignWordOperandsToAYAndVar(expr.right, expr.left, "math.multiply_words.multiplier") + asmgen.out(" jsr math.multiply_words") + } } assignRegisterpairWord(target, RegisterOrPair.AY) return true diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AugmentableAssignmentAsmGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AugmentableAssignmentAsmGen.kt index bbc9dd5c1..25fba5f2a 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AugmentableAssignmentAsmGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AugmentableAssignmentAsmGen.kt @@ -83,19 +83,20 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram, } } in WordDatatypes -> { + val block = target.origAstTarget?.definingBlock() when(value.kind) { - SourceStorageKind.LITERALNUMBER -> inplacemodificationWordWithLiteralval(target.asmVarname, target.datatype, operator, value.number!!.number.toInt()) - SourceStorageKind.VARIABLE -> inplacemodificationWordWithVariable(target.asmVarname, target.datatype, operator, value.asmVarname, value.datatype) - SourceStorageKind.REGISTER -> inplacemodificationWordWithVariable(target.asmVarname, target.datatype, operator, regName(value), value.datatype) + SourceStorageKind.LITERALNUMBER -> inplacemodificationWordWithLiteralval(target.asmVarname, target.datatype, operator, value.number!!.number.toInt(), block) + SourceStorageKind.VARIABLE -> inplacemodificationWordWithVariable(target.asmVarname, target.datatype, operator, value.asmVarname, value.datatype, block) + SourceStorageKind.REGISTER -> inplacemodificationWordWithVariable(target.asmVarname, target.datatype, operator, regName(value), value.datatype, block) SourceStorageKind.MEMORY -> inplacemodificationWordWithMemread(target.asmVarname, target.datatype, operator, value.memory!!) - SourceStorageKind.ARRAY -> inplacemodificationWordWithValue(target.asmVarname, target.datatype, operator, value.array!!) + SourceStorageKind.ARRAY -> inplacemodificationWordWithValue(target.asmVarname, target.datatype, operator, value.array!!, block) SourceStorageKind.EXPRESSION -> { if(value.expression is PtTypeCast) { if (tryInplaceModifyWithRemovedRedundantCast(value.expression, target, operator)) return - inplacemodificationWordWithValue(target.asmVarname, target.datatype, operator, value.expression) + inplacemodificationWordWithValue(target.asmVarname, target.datatype, operator, value.expression, block) } else { - inplacemodificationWordWithValue(target.asmVarname, target.datatype, operator, value.expression!!) + inplacemodificationWordWithValue(target.asmVarname, target.datatype, operator, value.expression!!, block) } } } @@ -230,18 +231,19 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram, } } in WordDatatypes -> { + val block = target.origAstTarget?.definingBlock() when(value.kind) { - SourceStorageKind.LITERALNUMBER -> inplacemodificationWordWithLiteralval(targetVarName, target.datatype, operator, value.number!!.number.toInt()) - SourceStorageKind.VARIABLE -> inplacemodificationWordWithVariable(targetVarName, target.datatype, operator, value.asmVarname, value.datatype) - SourceStorageKind.REGISTER -> inplacemodificationWordWithVariable(targetVarName, target.datatype, operator, regName(value), value.datatype) + SourceStorageKind.LITERALNUMBER -> inplacemodificationWordWithLiteralval(targetVarName, target.datatype, operator, value.number!!.number.toInt(), block) + SourceStorageKind.VARIABLE -> inplacemodificationWordWithVariable(targetVarName, target.datatype, operator, value.asmVarname, value.datatype, block) + SourceStorageKind.REGISTER -> inplacemodificationWordWithVariable(targetVarName, target.datatype, operator, regName(value), value.datatype, block) SourceStorageKind.MEMORY -> inplacemodificationWordWithMemread(targetVarName, target.datatype, operator, value.memory!!) - SourceStorageKind.ARRAY -> inplacemodificationWordWithValue(targetVarName, target.datatype, operator, value.array!!) + SourceStorageKind.ARRAY -> inplacemodificationWordWithValue(targetVarName, target.datatype, operator, value.array!!, block) SourceStorageKind.EXPRESSION -> { if(value.expression is PtTypeCast) { if (tryInplaceModifyWithRemovedRedundantCast(value.expression, target, operator)) return - inplacemodificationWordWithValue(targetVarName, target.datatype, operator, value.expression) + inplacemodificationWordWithValue(targetVarName, target.datatype, operator, value.expression, block) } else { - inplacemodificationWordWithValue(targetVarName, target.datatype, operator, value.expression!!) + inplacemodificationWordWithValue(targetVarName, target.datatype, operator, value.expression!!, block) } } } @@ -327,12 +329,13 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram, asmgen.out(" lda ${target.array.variable.name},y") asmgen.out(" ldx ${target.array.variable.name}+1,y") } + val block = target.origAstTarget?.definingBlock() when(value.kind) { SourceStorageKind.LITERALNUMBER -> { val number = value.number!!.number.toInt() if(!inplacemodificationRegisterAXwithLiteralval(operator, number)) { asmgen.out(" sta P8ZP_SCRATCH_W1 | stx P8ZP_SCRATCH_W1+1") - inplacemodificationWordWithLiteralval("P8ZP_SCRATCH_W1", target.datatype, operator, number) + inplacemodificationWordWithLiteralval("P8ZP_SCRATCH_W1", target.datatype, operator, number, block) asmgen.out(" lda P8ZP_SCRATCH_W1 | ldx P8ZP_SCRATCH_W1+1") } } @@ -343,7 +346,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram, value.datatype )) { asmgen.out(" sta P8ZP_SCRATCH_W1 | stx P8ZP_SCRATCH_W1+1") - inplacemodificationWordWithVariable("P8ZP_SCRATCH_W1", target.datatype, operator, value.asmVarname, value.datatype) + inplacemodificationWordWithVariable("P8ZP_SCRATCH_W1", target.datatype, operator, value.asmVarname, value.datatype, block) asmgen.out(" lda P8ZP_SCRATCH_W1 | ldx P8ZP_SCRATCH_W1+1") } } @@ -354,7 +357,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram, value.datatype )) { asmgen.out(" sta P8ZP_SCRATCH_W1 | stx P8ZP_SCRATCH_W1+1") - inplacemodificationWordWithVariable("P8ZP_SCRATCH_W1", target.datatype, operator, regName(value), value.datatype) + inplacemodificationWordWithVariable("P8ZP_SCRATCH_W1", target.datatype, operator, regName(value), value.datatype, block) asmgen.out(" lda P8ZP_SCRATCH_W1 | ldx P8ZP_SCRATCH_W1+1") } } @@ -365,15 +368,15 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram, } SourceStorageKind.ARRAY -> { asmgen.out(" sta P8ZP_SCRATCH_W1 | stx P8ZP_SCRATCH_W1+1") - inplacemodificationWordWithValue("P8ZP_SCRATCH_W1", target.datatype, operator, value.array!!) + inplacemodificationWordWithValue("P8ZP_SCRATCH_W1", target.datatype, operator, value.array!!, block) asmgen.out(" lda P8ZP_SCRATCH_W1 | ldx P8ZP_SCRATCH_W1+1") } SourceStorageKind.EXPRESSION -> { asmgen.out(" sta P8ZP_SCRATCH_W1 | stx P8ZP_SCRATCH_W1+1") if(value.expression is PtTypeCast) - inplacemodificationWordWithValue("P8ZP_SCRATCH_W1", target.datatype, operator, value.expression) + inplacemodificationWordWithValue("P8ZP_SCRATCH_W1", target.datatype, operator, value.expression, block) else - inplacemodificationWordWithValue("P8ZP_SCRATCH_W1", target.datatype, operator, value.expression!!) + inplacemodificationWordWithValue("P8ZP_SCRATCH_W1", target.datatype, operator, value.expression!!, block) asmgen.out(" lda P8ZP_SCRATCH_W1 | ldx P8ZP_SCRATCH_W1+1") } } @@ -1277,12 +1280,12 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram, asmgen.out(" eor $name | sta $name") } else -> { - inplacemodificationWordWithValue(name, dt, operator, memread) + inplacemodificationWordWithValue(name, dt, operator, memread, memread.definingBlock()) } } } - private fun inplacemodificationWordWithLiteralval(name: String, dt: DataType, operator: String, value: Int) { + private fun inplacemodificationWordWithLiteralval(name: String, dt: DataType, operator: String, value: Int, block: PtBlock?) { // note: this contains special optimized cases because we know the exact value. Don't replace this with another routine. when (operator) { "+" -> { @@ -1342,16 +1345,31 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram, if(value in asmgen.optimizedWordMultiplications) { asmgen.out(" lda $name | ldy $name+1 | jsr math.mul_word_$value | sta $name | sty $name+1") } else { - asmgen.out(""" - lda $name - sta math.multiply_words.multiplier - lda $name+1 - sta math.multiply_words.multiplier+1 - lda #<$value - ldy #>$value - jsr math.multiply_words - sta $name - sty $name+1""") + if(block?.veraFxMuls==true) + // cx16 verafx hardware mul + asmgen.out(""" + lda $name + ldy $name+1 + sta cx16.r0 + sty cx16.r0+1 + lda #<$value + ldy #>$value + sta cx16.r1 + sty cx16.r1+1 + jsr verafx.muls + sta $name + sty $name+1""") + else + asmgen.out(""" + lda $name + sta math.multiply_words.multiplier + lda $name+1 + sta math.multiply_words.multiplier+1 + lda #<$value + ldy #>$value + jsr math.multiply_words + sta $name + sty $name+1""") } } "/" -> { @@ -1741,7 +1759,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram, } } - private fun inplacemodificationWordWithVariable(name: String, dt: DataType, operator: String, otherName: String, valueDt: DataType) { + private fun inplacemodificationWordWithVariable(name: String, dt: DataType, operator: String, otherName: String, valueDt: DataType, block: PtBlock?) { when (valueDt) { in ByteDatatypes -> { // the other variable is a BYTE type so optimize for that @@ -1794,23 +1812,46 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram, sta $name+1""") } "*" -> { - if(valueDt==DataType.UBYTE) { - asmgen.out(" lda $otherName | sta math.multiply_words.multiplier") - if(asmgen.isTargetCpu(CpuType.CPU65c02)) - asmgen.out(" stz math.multiply_words.multiplier+1") - else - asmgen.out(" lda #0 | sta math.multiply_words.multiplier+1") - } else { - asmgen.out(" lda $otherName") - asmgen.signExtendAYlsb(valueDt) - asmgen.out(" sta math.multiply_words.multiplier | sty math.multiply_words.multiplier+1") - } - asmgen.out(""" + if(block?.veraFxMuls==true) { + // cx16 verafx hardware muls + if(valueDt==DataType.UBYTE) { + asmgen.out(" lda $otherName | sta cx16.r1") + if(asmgen.isTargetCpu(CpuType.CPU65c02)) + asmgen.out(" stz cx16.r1+1") + else + asmgen.out(" lda #0 | sta cx16.r1+1") + } else { + asmgen.out(" lda $otherName") + asmgen.signExtendAYlsb(valueDt) + asmgen.out(" sta cx16.r1 | sty cx16.r1+1") + } + asmgen.out(""" lda $name ldy $name+1 - jsr math.multiply_words + sta cx16.r0 + sty cx16.r0+1 + jsr verafx.muls sta $name sty $name+1""") + } else { + if(valueDt==DataType.UBYTE) { + asmgen.out(" lda $otherName | sta math.multiply_words.multiplier") + if(asmgen.isTargetCpu(CpuType.CPU65c02)) + asmgen.out(" stz math.multiply_words.multiplier+1") + else + asmgen.out(" lda #0 | sta math.multiply_words.multiplier+1") + } else { + asmgen.out(" lda $otherName") + asmgen.signExtendAYlsb(valueDt) + asmgen.out(" sta math.multiply_words.multiplier | sty math.multiply_words.multiplier+1") + } + asmgen.out(""" + lda $name + ldy $name+1 + jsr math.multiply_words + sta $name + sty $name+1""") + } } "/" -> { if(dt==DataType.UWORD) { @@ -1939,16 +1980,31 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram, "+" -> 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 math.multiply_words.multiplier - sty math.multiply_words.multiplier+1 - lda $name - ldy $name+1 - jsr math.multiply_words - sta $name - sty $name+1""") + if(block?.veraFxMuls==true) + // cx16 verafx hardware muls + asmgen.out(""" + lda $name + ldy $name+1 + sta cx16.r0 + sty cx16.r0+1 + lda $otherName + ldy $otherName+1 + sta cx16.r1 + sty cx16.r1+1 + jsr verafx.muls + sta $name + sty $name+1""") + else + asmgen.out(""" + lda $otherName + ldy $otherName+1 + sta math.multiply_words.multiplier + sty math.multiply_words.multiplier+1 + lda $name + ldy $name+1 + jsr math.multiply_words + sta $name + sty $name+1""") } "/" -> { if(dt==DataType.WORD) { @@ -2126,17 +2182,31 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram, } } - private fun inplacemodificationWordWithValue(name: String, dt: DataType, operator: String, value: PtExpression) { + private fun inplacemodificationWordWithValue(name: String, dt: DataType, operator: String, value: PtExpression, block: PtBlock?) { fun multiplyVarByWordInAY() { - asmgen.out(""" - sta math.multiply_words.multiplier - sty math.multiply_words.multiplier+1 - lda $name - ldy $name+1 - jsr math.multiply_words - sta $name - sty $name+1 - """) + if(block?.veraFxMuls==true) + // cx16 verafx hardware muls + asmgen.out(""" + sta cx16.r1 + sty cx16.r1+1 + lda $name + ldy $name+1 + sta cx16.r0 + sty cx16.r0+1 + jsr verafx.muls + sta $name + sty $name+1 + """) + else + asmgen.out(""" + sta math.multiply_words.multiplier + sty math.multiply_words.multiplier+1 + lda $name + ldy $name+1 + jsr math.multiply_words + sta $name + sty $name+1 + """) } fun divideVarByWordInAY() { diff --git a/codeGenCpu6502/test/TestCodegen.kt b/codeGenCpu6502/test/TestCodegen.kt index 8372d1eef..f0d90ca12 100644 --- a/codeGenCpu6502/test/TestCodegen.kt +++ b/codeGenCpu6502/test/TestCodegen.kt @@ -44,7 +44,7 @@ class TestCodegen: FunSpec({ //} val codegen = AsmGen6502(prefixSymbols = false) val program = PtProgram("test", DummyMemsizer, DummyStringEncoder) - val block = PtBlock("main", null, false, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY) + val block = PtBlock("main", null, false, false, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY) val sub = PtSub("start", emptyList(), null, Position.DUMMY) sub.add(PtVariable("pi", DataType.UBYTE, ZeropageWish.DONTCARE, PtNumber(DataType.UBYTE, 0.0, Position.DUMMY), null, Position.DUMMY)) sub.add(PtVariable("particleX", DataType.ARRAY_UB, ZeropageWish.DONTCARE, null, 3u, Position.DUMMY)) @@ -94,7 +94,7 @@ class TestCodegen: FunSpec({ program.add(block) // define the "cx16.r0" virtual register - val cx16block = PtBlock("cx16", null, false, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY) + val cx16block = PtBlock("cx16", null, false, false, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY) cx16block.add(PtMemMapped("r0", DataType.UWORD, 100u, null, Position.DUMMY)) program.add(cx16block) diff --git a/codeGenIntermediate/test/TestVmCodeGen.kt b/codeGenIntermediate/test/TestVmCodeGen.kt index 36a2efd91..83e607651 100644 --- a/codeGenIntermediate/test/TestVmCodeGen.kt +++ b/codeGenIntermediate/test/TestVmCodeGen.kt @@ -41,7 +41,7 @@ class TestVmCodeGen: FunSpec({ //} val codegen = VmCodeGen() val program = PtProgram("test", DummyMemsizer, DummyStringEncoder) - val block = PtBlock("main", null, false, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY) + val block = PtBlock("main", null, false, false, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY) val sub = PtSub("start", emptyList(), null, Position.DUMMY) sub.add(PtVariable("pi", DataType.UBYTE, ZeropageWish.DONTCARE, PtNumber(DataType.UBYTE, 0.0, Position.DUMMY), null, Position.DUMMY)) sub.add(PtVariable("particleX", DataType.ARRAY_UB, ZeropageWish.DONTCARE, null, 3u, Position.DUMMY)) @@ -91,7 +91,7 @@ class TestVmCodeGen: FunSpec({ program.add(block) // define the "cx16.r0" virtual register - val cx16block = PtBlock("cx16", null, false, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY) + val cx16block = PtBlock("cx16", null, false, false, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY) cx16block.add(PtMemMapped("r0", DataType.UWORD, 100u, null, Position.DUMMY)) program.add(cx16block) @@ -120,7 +120,7 @@ class TestVmCodeGen: FunSpec({ //} val codegen = VmCodeGen() val program = PtProgram("test", DummyMemsizer, DummyStringEncoder) - val block = PtBlock("main", null, false, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY) + val block = PtBlock("main", null, false, false, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY) val sub = PtSub("start", emptyList(), null, Position.DUMMY) sub.add(PtVariable("f1", DataType.FLOAT, ZeropageWish.DONTCARE, null, null, Position.DUMMY)) val if1 = PtIfElse(Position.DUMMY) @@ -183,7 +183,7 @@ class TestVmCodeGen: FunSpec({ //} val codegen = VmCodeGen() val program = PtProgram("test", DummyMemsizer, DummyStringEncoder) - val block = PtBlock("main", null, false, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY) + val block = PtBlock("main", null, false, false, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY) val sub = PtSub("start", emptyList(), null, Position.DUMMY) sub.add(PtVariable("f1", DataType.FLOAT, ZeropageWish.DONTCARE, null, null, Position.DUMMY)) val if1 = PtIfElse(Position.DUMMY) @@ -242,7 +242,7 @@ class TestVmCodeGen: FunSpec({ //} val codegen = VmCodeGen() val program = PtProgram("test", DummyMemsizer, DummyStringEncoder) - val block = PtBlock("main", null, false, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY) + val block = PtBlock("main", null, false, false, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY) val sub = PtSub("start", emptyList(), null, Position.DUMMY) sub.add(PtVariable("f1", DataType.FLOAT, ZeropageWish.DONTCARE, null, null, Position.DUMMY)) val if1 = PtIfElse(Position.DUMMY) @@ -289,7 +289,7 @@ class TestVmCodeGen: FunSpec({ //} val codegen = VmCodeGen() val program = PtProgram("test", DummyMemsizer, DummyStringEncoder) - val block = PtBlock("main", null, false, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY) + val block = PtBlock("main", null, false, false, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY) val sub = PtSub("start", emptyList(), null, Position.DUMMY) sub.add(PtVariable("sb1", DataType.BYTE, ZeropageWish.DONTCARE, null, null, Position.DUMMY)) val if1 = PtIfElse(Position.DUMMY) @@ -352,7 +352,7 @@ class TestVmCodeGen: FunSpec({ //} val codegen = VmCodeGen() val program = PtProgram("test", DummyMemsizer, DummyStringEncoder) - val block = PtBlock("main", null, false, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY) + val block = PtBlock("main", null, false, false, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY) val sub = PtSub("start", emptyList(), null, Position.DUMMY) sub.add(PtVariable("sb1", DataType.BYTE, ZeropageWish.DONTCARE, null, null, Position.DUMMY)) val if1 = PtIfElse(Position.DUMMY) @@ -411,7 +411,7 @@ class TestVmCodeGen: FunSpec({ //} val codegen = VmCodeGen() val program = PtProgram("test", DummyMemsizer, DummyStringEncoder) - val block = PtBlock("main", null, false, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY) + val block = PtBlock("main", null, false, false, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY) val sub = PtSub("start", emptyList(), null, Position.DUMMY) sub.add(PtVariable("ub1", DataType.UBYTE, ZeropageWish.DONTCARE, null, null, Position.DUMMY)) val if1 = PtIfElse(Position.DUMMY) @@ -451,7 +451,7 @@ class TestVmCodeGen: FunSpec({ //} val codegen = VmCodeGen() val program = PtProgram("test", DummyMemsizer, DummyStringEncoder) - val block = PtBlock("main", null, false, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY) + val block = PtBlock("main", null, false, false, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY) val romsub = PtAsmSub("routine", 0x5000u, setOf(CpuRegister.Y), emptyList(), emptyList(), false, Position.DUMMY) block.add(romsub) val sub = PtSub("start", emptyList(), null, Position.DUMMY) diff --git a/compiler/res/prog8lib/cx16/verafx.p8 b/compiler/res/prog8lib/cx16/verafx.p8 index 9422df231..ae5039a93 100644 --- a/compiler/res/prog8lib/cx16/verafx.p8 +++ b/compiler/res/prog8lib/cx16/verafx.p8 @@ -4,6 +4,7 @@ ; https://docs.google.com/document/d/1q34uWOiM3Be2pnaHRVgSdHySI-qsiQWPTo_gfE54PTg/edit verafx { + %option no_symbol_prefixing sub clear(ubyte vbank, uword vaddr, ubyte data, uword amountof32bits) { ; use cached 4-byte write to quickly clear a portion of the video memory to a given byte value diff --git a/compiler/src/prog8/compiler/Compiler.kt b/compiler/src/prog8/compiler/Compiler.kt index ad078ff6f..54ab99b45 100644 --- a/compiler/src/prog8/compiler/Compiler.kt +++ b/compiler/src/prog8/compiler/Compiler.kt @@ -254,6 +254,10 @@ fun parseMainModule(filepath: Path, importer.importImplicitLibraryModule("math") } importer.importImplicitLibraryModule("prog8_lib") + if(program.allBlocks.any { it.options().any { option->option=="verafxmuls" } }) { + if(compTarget.name==Cx16Target.NAME) + importer.importImplicitLibraryModule("verafx") + } if (compilerOptions.launcher == CbmPrgLauncherType.BASIC && compilerOptions.output != OutputType.PRG) errors.err("BASIC launcher requires output type PRG", program.toplevelModule.position) diff --git a/compiler/src/prog8/compiler/astprocessing/AstChecker.kt b/compiler/src/prog8/compiler/astprocessing/AstChecker.kt index 6a5cd929f..960980757 100644 --- a/compiler/src/prog8/compiler/astprocessing/AstChecker.kt +++ b/compiler/src/prog8/compiler/astprocessing/AstChecker.kt @@ -7,6 +7,8 @@ import prog8.ast.expressions.* import prog8.ast.statements.* import prog8.ast.walk.IAstVisitor import prog8.code.core.* +import prog8.code.target.C64Target +import prog8.code.target.Cx16Target import prog8.code.target.VMTarget import prog8.compiler.builtinFunctionReturnType import java.io.CharConversionException @@ -822,10 +824,16 @@ internal class AstChecker(private val program: Program, err("this directive may only occur in a block or at module level") if(directive.args.isEmpty()) err("missing option directive argument(s)") - else if(directive.args.map{it.name in arrayOf("enable_floats", "force_output", "no_sysinit", "align_word", "align_page", "merge", "splitarrays", "no_symbol_prefixing")}.any { !it }) + else if(directive.args.map{it.name in arrayOf("enable_floats", "force_output", "no_sysinit", "align_word", "align_page", "merge", "splitarrays", "no_symbol_prefixing", "verafxmuls")}.any { !it }) err("invalid option directive argument(s)") if(directive.args.any {it.name=="align_word"} && directive.args.any { it.name=="align_page"}) err("conflicting alignment options") + if(directive.parent is Block) { + if(directive.args.any {it.name !in arrayOf("align_word", "align_page", "no_symbol_prefixing", "force_output", "merge", "splitarrays", "verafxmuls")}) + err("using an option that is not valid for blocks") + } + if(directive.args.any { it.name=="verafxmuls" } && compilerOptions.compTarget.name != Cx16Target.NAME) + err("verafx option is only valid on cx16 target") } else -> throw SyntaxError("invalid directive ${directive.directive}", directive.position) } diff --git a/compiler/src/prog8/compiler/astprocessing/IntermediateAstMaker.kt b/compiler/src/prog8/compiler/astprocessing/IntermediateAstMaker.kt index 5572c106e..b554ea835 100644 --- a/compiler/src/prog8/compiler/astprocessing/IntermediateAstMaker.kt +++ b/compiler/src/prog8/compiler/astprocessing/IntermediateAstMaker.kt @@ -161,6 +161,7 @@ class IntermediateAstMaker(private val program: Program, private val errors: IEr private fun transform(srcBlock: Block): PtBlock { var alignment = PtBlock.BlockAlignment.NONE var forceOutput = false + var veraFxMuls = false var noSymbolPrefixing = false val directives = srcBlock.statements.filterIsInstance() for (directive in directives.filter { it.directive == "%option" }) { @@ -169,15 +170,16 @@ class IntermediateAstMaker(private val program: Program, private val errors: IEr "align_word" -> alignment = PtBlock.BlockAlignment.WORD "align_page" -> alignment = PtBlock.BlockAlignment.PAGE "no_symbol_prefixing" -> noSymbolPrefixing = true - "force_output" -> forceOutput=true + "force_output" -> forceOutput = true "merge", "splitarrays" -> { /* ignore this one */ } + "verafxmuls" -> veraFxMuls = true else -> throw FatalAstException("weird directive option: ${arg.name}") } } } val (vardecls, statements) = srcBlock.statements.partition { it is VarDecl } val src = srcBlock.definingModule.source - val block = PtBlock(srcBlock.name, srcBlock.address, srcBlock.isInLibrary, forceOutput, noSymbolPrefixing, alignment, src, srcBlock.position) + val block = PtBlock(srcBlock.name, srcBlock.address, srcBlock.isInLibrary, forceOutput, noSymbolPrefixing, veraFxMuls, alignment, src, srcBlock.position) makeScopeVarsDecls(vardecls).forEach { block.add(it) } for (stmt in statements) block.add(transformStatement(stmt)) diff --git a/compiler/test/TestSymbolTable.kt b/compiler/test/TestSymbolTable.kt index 592ff9a38..2c2321a6f 100644 --- a/compiler/test/TestSymbolTable.kt +++ b/compiler/test/TestSymbolTable.kt @@ -111,7 +111,7 @@ private fun makeSt(): SymbolTable { // first build the AST val astProgram = PtProgram("test", DummyMemsizer, DummyStringEncoder) - val astBlock1 = PtBlock("block1", null, false, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("block1"), Position.DUMMY) + val astBlock1 = PtBlock("block1", null, false, false, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("block1"), Position.DUMMY) val astConstant1 = PtConstant("c1", DataType.UWORD, 12345.0, Position.DUMMY) val astConstant2 = PtConstant("blockc", DataType.UWORD, 999.0, Position.DUMMY) astBlock1.add(astConstant1) @@ -134,7 +134,7 @@ private fun makeSt(): SymbolTable { astBlock1.add(astSub2) val astBfunc = PtIdentifier("msb", DataType.UBYTE, Position.DUMMY) astBlock1.add(astBfunc) - val astBlock2 = PtBlock("block2", null, false, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("block2"), Position.DUMMY) + val astBlock2 = PtBlock("block2", null, false, false, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("block2"), Position.DUMMY) val astSub21 = PtSub("sub1", emptyList(), null, Position.DUMMY) val astSub22 = PtSub("sub2", emptyList(), null, Position.DUMMY) val astSub221 = PtSub("subsub", emptyList(), null, Position.DUMMY) diff --git a/docs/source/libraries.rst b/docs/source/libraries.rst index 56c8f3068..a4425489f 100644 --- a/docs/source/libraries.rst +++ b/docs/source/libraries.rst @@ -525,6 +525,8 @@ the emulators already support it). For now, the hardware 16*16 multiplier is exposed via ``mult`` and ``muls`` routines (unsigned and signed respectively). They are about 4 to 5 times faster as the default 6502 cpu routine for word multiplication. But they depend on some Vera manipulation and 4 bytes in vram just below the PSG registers for storage. + Note: there is a block level %option "verafxmuls" that automatically replaces all word multiplications in that block + by calls to verafx.muls/mult, but be careful with it because it may interfere with other Vera operations or IRQs. ``clear`` There's also a ``clear`` routine here to very quickly clear a piece of vram to a given byte value (it writes 4 bytes at a time). diff --git a/docs/source/syntaxreference.rst b/docs/source/syntaxreference.rst index cc644eec0..7996b4555 100644 --- a/docs/source/syntaxreference.rst +++ b/docs/source/syntaxreference.rst @@ -141,7 +141,7 @@ Directives - ``splitarrays`` (block or module) makes all word-arrays in this scope lsb/msb split arrays (as if they all have the @split tag). See Arrays. - ``no_symbol_prefixing`` (block) makes the compiler *not* use symbol-prefixing when translating prog8 code into assembly. Only use this if you know what you're doing because it could result in invalid assembly code being generated. - + - ``verafxmuls`` (block, cx16 target only) uses Vera FX hardware word multiplication on the CommanderX16 for all word multiplications in this block. Warning: this may interfere with IRQs and other Vera operations, so use this only when you know what you're doing. It's safer to explicitly use ``verafx.muls()``. .. data:: %asmbinary "" [, [, ]] diff --git a/docs/source/todo.rst b/docs/source/todo.rst index 1ce3ab041..e270e981e 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -1,8 +1,6 @@ TODO ==== -- add %option verafxmuls in block to enable transparent verafx muls use for that block only + add warning message to docs to not use it it in prg AND irq code - - [on branch: shortcircuit] investigate McCarthy evaluation again? this may also reduce code size perhaps for things like if a>4 or a<2 .... - [on branch: ir-less-branch-opcodes] IR: reduce the number of branch instructions such as BEQ, BEQR, etc (gradually), replace with CMP(I) + status branch instruction - IR: reduce amount of CMP/CMPI after instructions that set the status bits correctly (LOADs? INC? etc), but only after setting the status bits is verified! diff --git a/examples/test.p8 b/examples/test.p8 index c04e8aadf..3f3146773 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -1,76 +1,83 @@ -%import monogfx +%import diskio +%import textio +;%import math +;%import verafx +%zeropage basicsafe +%option no_sysinit main { + %option verafxmuls sub start() { - monogfx.lores() - draw() - repeat { - } - } + word w1 = -123 + word w2 = 222 + ubyte b2 = 222 + byte sb2 = 111 + txt.print_w(w1*w2) + txt.nl() + txt.print_w(w1*222) + txt.nl() + w1 = -123 + w1 *= 222 + txt.print_w(w1) + txt.nl() + w1 = -123 + w1 *= w2 + txt.print_w(w1) + txt.nl() + w1 = -123 + w1 *= (w2-1) + txt.print_w(w1) + txt.nl() + w1 = -123 + w1 *= b2 + txt.print_w(w1) + txt.nl() + w1 = -123 + w1 *= sb2 + txt.print_w(w1) + txt.nl() - sub draw() { - uword xx - monogfx.stipple(true) - monogfx.fillrect(0,0,200,200,true) - monogfx.stipple(false) - for xx in 0 to 255 { - monogfx.rect(xx, xx/2, 6, 16, 1) - sys.waitvsync() - monogfx.rect(xx, xx/2, 6, 16, 0) - } +; txt.print_uw(math.mul16_last_upper()) +; txt.nl() +; uword value1=5678 +; uword value2=9999 +; uword result = value1*value2 +; uword upper16 = math.mul16_last_upper() +; txt.print_uw(result) +; txt.spc() +; txt.print_uw(upper16) +; txt.nl() - monogfx.rect(100, 100, 16, 16, 1) - monogfx.rect(101, 101, 15, 15, 1) - monogfx.rect(102, 102, 14, 14, 1) - monogfx.rect(103, 103, 13, 13, 1) - monogfx.rect(104, 104, 12, 12, 1) -; monogfx.rect(10,10, 1, 1, 1) -; monogfx.rect(20,10, 2, 1, 1) -; monogfx.rect(30,10, 3, 1, 1) -; monogfx.rect(40,10, 1, 2, 1) -; monogfx.rect(50,10, 1, 3, 1) -; monogfx.rect(60,10, 2, 2, 1) -; monogfx.rect(70,10, 3, 3, 1) -; monogfx.rect(80,10, 4, 4, 1) -; monogfx.rect(120,220, 5, 5, 1) -; monogfx.rect(90,10, 5, 5, 1) -; monogfx.rect(100,10, 8, 8, 1) -; monogfx.rect(110,10, 20, 5, 1) -; monogfx.fillrect(10,40, 1, 1, 1) -; monogfx.fillrect(20,40, 2, 1, 1) -; monogfx.fillrect(30,40, 3, 1, 1) -; monogfx.fillrect(40,40, 1, 2, 1) -; monogfx.fillrect(50,40, 1, 3, 1) -; monogfx.fillrect(60,40, 2, 2, 1) -; monogfx.fillrect(70,40, 3, 3, 1) -; monogfx.fillrect(80,40, 4, 4, 1) -; monogfx.fillrect(90,40, 5, 5, 1) -; monogfx.fillrect(100,40, 8, 8, 1) -; monogfx.fillrect(110,40, 20, 5, 1) +; const word MULTIPLIER = 431 +; +; ; verify results: +; for value in -50 to 50 { +; if value*MULTIPLIER != verafx.muls(value, MULTIPLIER) { +; txt.print("verafx muls error\n") +; sys.exit(1) +; } +; } +; +; +; word value +; txt.print("verafx muls...") +; cbm.SETTIM(0,0,0) +; for value in -50 to 50 { +; repeat 250 void verafx.muls(value, MULTIPLIER) +; } +; txt.print_uw(cbm.RDTIM16()) +; txt.nl() +; +; txt.print("6502 muls...") +; cbm.SETTIM(0,0,0) +; for value in -50 to 50 { +; repeat 250 cx16.r0s = value*MULTIPLIER +; } +; txt.print_uw(cbm.RDTIM16()) +; txt.nl() -; monogfx.rect(160, 10, 1, 1, 1) -; monogfx.rect(160, 20, 2, 1, 1) -; monogfx.rect(160, 30, 3, 1, 1) -; monogfx.rect(160, 40, 1, 2, 1) -; monogfx.rect(160, 50, 1, 3, 1) -; monogfx.rect(160, 60, 2, 2, 1) -; monogfx.rect(160, 70, 3, 3, 1) -; monogfx.rect(160, 80, 4, 4, 1) -; monogfx.rect(160, 90, 5, 5, 1) -; monogfx.rect(160, 100, 8, 8, 1) -; monogfx.rect(160, 110, 20, 5, 1) -; monogfx.fillrect(160, 120, 1, 1, 1) -; monogfx.fillrect(160, 130, 2, 1, 1) -; monogfx.fillrect(160, 140, 3, 1, 1) -; monogfx.fillrect(160, 150, 1, 2, 1) -; monogfx.fillrect(160, 160, 1, 3, 1) -; monogfx.fillrect(160, 170, 2, 2, 1) -; monogfx.fillrect(160, 180, 3, 3, 1) -; monogfx.fillrect(160, 190, 4, 4, 1) -; monogfx.fillrect(160, 200, 5, 5, 1) -; monogfx.fillrect(160, 210, 8, 8, 1) -; monogfx.fillrect(160, 220, 20, 5, 1) } } +