From 692233375581ab0c99a2aa2ae6636c9223a596a1 Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Fri, 12 Mar 2021 20:12:33 +0100 Subject: [PATCH] add a cmp(x,y) function that returns no value but only sets the status bits based off the comparison (can be used with a conditional jump afterwards) --- .../compiler/BeforeAsmGenerationAstChanger.kt | 22 +++++ .../compiler/functions/BuiltinFunctions.kt | 1 + .../cpu6502/codegen/BuiltinFunctionsAsmGen.kt | 70 ++++++++++++++++ docs/source/programming.rst | 6 ++ docs/source/todo.rst | 1 - examples/test.p8 | 80 +++++++++++++++---- 6 files changed, 164 insertions(+), 16 deletions(-) diff --git a/compiler/src/prog8/compiler/BeforeAsmGenerationAstChanger.kt b/compiler/src/prog8/compiler/BeforeAsmGenerationAstChanger.kt index 17492bd1a..e7160c226 100644 --- a/compiler/src/prog8/compiler/BeforeAsmGenerationAstChanger.kt +++ b/compiler/src/prog8/compiler/BeforeAsmGenerationAstChanger.kt @@ -206,4 +206,26 @@ internal class BeforeAsmGenerationAstChanger(val program: Program, val errors: I } return noModifications } + + override fun after(functionCallStatement: FunctionCallStatement, parent: Node): Iterable { + if(functionCallStatement.target.nameInSource==listOf("cmp")) { + // if the datatype of the arguments of cmp() are different, cast the byte one to word. + val arg1 = functionCallStatement.args[0] + val arg2 = functionCallStatement.args[1] + val dt1 = arg1.inferType(program).typeOrElse(DataType.STRUCT) + val dt2 = arg2.inferType(program).typeOrElse(DataType.STRUCT) + if(dt1 in ByteDatatypes) { + if(dt2 in ByteDatatypes) + return noModifications + val cast1 = TypecastExpression(arg1, if(dt1==DataType.UBYTE) DataType.UWORD else DataType.WORD, true, functionCallStatement.position) + return listOf(IAstModification.ReplaceNode(arg1, cast1, functionCallStatement)) + } else { + if(dt2 in WordDatatypes) + return noModifications + val cast2 = TypecastExpression(arg2, if(dt2==DataType.UBYTE) DataType.UWORD else DataType.WORD, true, functionCallStatement.position) + return listOf(IAstModification.ReplaceNode(arg2, cast2, functionCallStatement)) + } + } + return noModifications + } } diff --git a/compiler/src/prog8/compiler/functions/BuiltinFunctions.kt b/compiler/src/prog8/compiler/functions/BuiltinFunctions.kt index 797224d66..9c32fa9f0 100644 --- a/compiler/src/prog8/compiler/functions/BuiltinFunctions.kt +++ b/compiler/src/prog8/compiler/functions/BuiltinFunctions.kt @@ -97,6 +97,7 @@ private val functionSignatures: List = listOf( FSignature("ror2" , false, listOf(FParam("item", setOf(DataType.UBYTE, DataType.UWORD))), null), FSignature("sort" , false, listOf(FParam("array", ArrayDatatypes)), null), FSignature("reverse" , false, listOf(FParam("array", ArrayDatatypes)), null), + FSignature("cmp" , false, listOf(FParam("value1", IntegerDatatypes), FParam("value2", NumericDatatypes)), null), // these few have a return value depending on the argument(s): FSignature("max" , true, listOf(FParam("values", ArrayDatatypes)), null) { a, p, prg, ct -> collectionArg(a, p, prg, ::builtinMax) }, // type depends on args FSignature("min" , true, listOf(FParam("values", ArrayDatatypes)), null) { a, p, prg, ct -> collectionArg(a, p, prg, ::builtinMin) }, // type depends on args diff --git a/compiler/src/prog8/compiler/target/cpu6502/codegen/BuiltinFunctionsAsmGen.kt b/compiler/src/prog8/compiler/target/cpu6502/codegen/BuiltinFunctionsAsmGen.kt index 69a634f47..2fb50002f 100644 --- a/compiler/src/prog8/compiler/target/cpu6502/codegen/BuiltinFunctionsAsmGen.kt +++ b/compiler/src/prog8/compiler/target/cpu6502/codegen/BuiltinFunctionsAsmGen.kt @@ -69,10 +69,80 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val "peek" -> throw AssemblyError("peek() should have been replaced by @()") "pokew" -> funcPokeW(fcall) "poke" -> throw AssemblyError("poke() should have been replaced by @()") + "cmp" -> funcCmp(fcall) else -> TODO("missing asmgen for builtin func ${func.name}") } } + private fun funcCmp(fcall: IFunctionCall) { + val arg1 = fcall.args[0] + val arg2 = fcall.args[1] + val dt1 = arg1.inferType(program).typeOrElse(DataType.STRUCT) + val dt2 = arg2.inferType(program).typeOrElse(DataType.STRUCT) + if(dt1 in ByteDatatypes) { + if(dt2 in ByteDatatypes) { + when (arg2) { + is IdentifierReference -> { + asmgen.assignExpressionToRegister(arg1, RegisterOrPair.A) + asmgen.out(" cmp ${asmgen.asmVariableName(arg2)}") + } + is NumericLiteralValue -> { + asmgen.assignExpressionToRegister(arg1, RegisterOrPair.A) + asmgen.out(" cmp #${arg2.number}") + } + is DirectMemoryRead -> { + if(arg2.addressExpression is NumericLiteralValue) { + asmgen.assignExpressionToRegister(arg1, RegisterOrPair.A) + asmgen.out(" cmp ${arg2.addressExpression.constValue(program)!!.number.toHex()}") + } else { + asmgen.assignExpressionToVariable(arg2, "P8ZP_SCRATCH_B1", DataType.UBYTE, (fcall as Node).definingSubroutine()) + asmgen.assignExpressionToRegister(arg1, RegisterOrPair.A) + asmgen.out(" cmp P8ZP_SCRATCH_B1") + } + } + else -> { + asmgen.assignExpressionToVariable(arg2, "P8ZP_SCRATCH_B1", DataType.UBYTE, (fcall as Node).definingSubroutine()) + asmgen.assignExpressionToRegister(arg1, RegisterOrPair.A) + asmgen.out(" cmp P8ZP_SCRATCH_B1") + } + } + } else + throw AssemblyError("args for cmp() should have same dt") + } else { + // dt1 is a word + if(dt2 in WordDatatypes) { + when (arg2) { + is IdentifierReference -> { + asmgen.assignExpressionToRegister(arg1, RegisterOrPair.AY) + asmgen.out(""" + cpy ${asmgen.asmVariableName(arg2)}+1 + bne + + cmp ${asmgen.asmVariableName(arg2)} ++""") + } + is NumericLiteralValue -> { + asmgen.assignExpressionToRegister(arg1, RegisterOrPair.AY) + asmgen.out(""" + cpy #>${arg2.number} + bne + + cmp #<${arg2.number} ++""") + } + else -> { + asmgen.assignExpressionToVariable(arg2, "P8ZP_SCRATCH_W1", DataType.UWORD, (fcall as Node).definingSubroutine()) + asmgen.assignExpressionToRegister(arg1, RegisterOrPair.AY) + asmgen.out(""" + cpy P8ZP_SCRATCH_W1+1 + bne + + cmp P8ZP_SCRATCH_W1 ++""") + } + } + } else + throw AssemblyError("args for cmp() should have same dt") + } + } + private fun funcMemory(fcall: IFunctionCall, discardResult: Boolean, resultToStack: Boolean, resultRegister: RegisterOrPair?) { if(discardResult || fcall !is FunctionCall) throw AssemblyError("should not discard result of memory allocation at $fcall") diff --git a/docs/source/programming.rst b/docs/source/programming.rst index bf9c38635..c1384e5dc 100644 --- a/docs/source/programming.rst +++ b/docs/source/programming.rst @@ -791,6 +791,12 @@ sort(array) Miscellaneous ^^^^^^^^^^^^^ + +cmp(x,y) + Compare the integer value x to integer value y. Doesn't return a value or boolean result, only sets the processor's status bits! + You can use a conditional jumps (``if_cc`` etcetera) to act on this. + Normally you should just use a comparison expression (``x < y``) + lsb(x) Get the least significant byte of the word x. Equivalent to the cast "x as ubyte". diff --git a/docs/source/todo.rst b/docs/source/todo.rst index aa049b336..d6bd0e539 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -2,7 +2,6 @@ TODO ==== -- add a cmp(x,y) function that returns no value but only sets the status bits based off the comparison (can be used with a conditional jump afterwards) - optimize comparisons followed by a conditional jump ; try to not have to jsr to the comparison routines. (so if/while/do-until are faster) - optimize several inner loops in gfx2 diff --git a/examples/test.p8 b/examples/test.p8 index f6c59b717..09990dd4e 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -1,31 +1,81 @@ -%import floats -%import gfx2 -%import test_stack -%zeropage floatsafe +%import graphics +%zeropage basicsafe main { sub start() { - gfx2.screen_mode(1) - ;graphics.enable_bitmap_mode() + byte xx + word yy + + uword addr = $3000 + cmp(xx, @($2000)) + cmp(@($2000), xx) + cmp(yy, @($2000)) + cmp(@($2000), yy) + } + sub start3() { + + byte xx + byte yy + + ; all comparisons with constant values are already optimized + + byte value = 100 + + while xx==value { + yy++ + } + + while xx!=value { + yy++ + } + + while xx>value { + yy++ + } + + do { + yy++ + } until xx==value + + do { + yy++ + } until xx!=value + + do { + yy++ + } until xx>value + + if xx==value + yy++ + + if xx!=value + yy++ + + if xx>value + yy++ + } + + + sub start2() { + + graphics.enable_bitmap_mode() uword xx ubyte yy - gfx2.line(160,100,160,80 ,1) - gfx2.line(160,80,180,81 ,1) - gfx2.line(180,81,177,103 ,1) + graphics.line(150,50,150,50) for yy in 0 to 199-60 step 16 { for xx in 0 to 319-50 step 16 { - gfx2.line(30+xx, 10+yy, 50+xx, 30+yy ,1) - gfx2.line(49+xx, 30+yy, 10+xx, 30+yy ,1) - gfx2.line(11+xx, 29+yy, 29+xx, 11+yy ,1) + graphics.line(30+xx, 10+yy, 50+xx, 30+yy) + graphics.line(49+xx, 30+yy, 10+xx, 30+yy) + graphics.line(11+xx, 29+yy, 29+xx, 11+yy) ; triangle 2, counter-clockwise - gfx2.line(30+xx, 40+yy, 10+xx, 60+yy ,1) - gfx2.line(11+xx, 60+yy, 50+xx, 60+yy ,1) - gfx2.line(49+xx, 59+yy, 31+xx,41+yy ,1) + graphics.line(30+xx, 40+yy, 10+xx, 60+yy) + graphics.line(11+xx, 60+yy, 50+xx, 60+yy) + graphics.line(49+xx, 59+yy, 31+xx,41+yy) } }