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)

This commit is contained in:
Irmen de Jong 2021-03-12 20:12:33 +01:00
parent a00c39e9cf
commit 6922333755
6 changed files with 164 additions and 16 deletions

View File

@ -206,4 +206,26 @@ internal class BeforeAsmGenerationAstChanger(val program: Program, val errors: I
}
return noModifications
}
override fun after(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification> {
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
}
}

View File

@ -97,6 +97,7 @@ private val functionSignatures: List<FSignature> = 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

View File

@ -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")

View File

@ -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".

View File

@ -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

View File

@ -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)
}
}