mirror of
https://github.com/irmen/prog8.git
synced 2025-08-16 05:27:31 +00:00
for loops no longer execute when from var already reached beyond the end
This commit is contained in:
@@ -54,19 +54,28 @@ internal class ForLoopsAsmGen(private val program: PtProgram,
|
|||||||
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.A, false)
|
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.A, false)
|
||||||
// pre-check for end already reached
|
// pre-check for end already reached
|
||||||
if(iterableDt==DataType.ARRAY_B) {
|
if(iterableDt==DataType.ARRAY_B) {
|
||||||
if(stepsize<0) {
|
asmgen.out(" sta $modifiedLabel+1")
|
||||||
println("signed byte check to<=from") // TODO
|
if(stepsize<0)
|
||||||
} else {
|
asmgen.out("""
|
||||||
println("signed byte check from<=to") // TODO
|
clc
|
||||||
}
|
sbc $varname
|
||||||
|
bvc +
|
||||||
|
eor #${'$'}80
|
||||||
|
+ bpl $endLabel""")
|
||||||
|
else
|
||||||
|
asmgen.out("""
|
||||||
|
clc
|
||||||
|
sbc $varname
|
||||||
|
bvc +
|
||||||
|
eor #${'$'}80
|
||||||
|
+ bmi $endLabel""")
|
||||||
} else {
|
} else {
|
||||||
if(stepsize<0) {
|
if(stepsize<0)
|
||||||
println("unsigned byte check to<=from") // TODO
|
asmgen.out(" cmp $varname | bcs $endLabel")
|
||||||
} else {
|
else
|
||||||
println("unsigned byte check from<=to") // TODO
|
asmgen.out(" cmp $varname | bcc $endLabel | beq $endLabel")
|
||||||
}
|
asmgen.out(" sta $modifiedLabel+1")
|
||||||
}
|
}
|
||||||
asmgen.out(" sta $modifiedLabel+1")
|
|
||||||
asmgen.out(loopLabel)
|
asmgen.out(loopLabel)
|
||||||
asmgen.translate(stmt.statements)
|
asmgen.translate(stmt.statements)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
@@ -84,20 +93,30 @@ $modifiedLabel cmp #0 ; modified
|
|||||||
// loop over byte range via loopvar
|
// loop over byte range via loopvar
|
||||||
val varname = asmgen.asmVariableName(stmt.variable)
|
val varname = asmgen.asmVariableName(stmt.variable)
|
||||||
asmgen.assignExpressionToVariable(range.from, varname, ArrayToElementTypes.getValue(iterableDt))
|
asmgen.assignExpressionToVariable(range.from, varname, ArrayToElementTypes.getValue(iterableDt))
|
||||||
asmgen.assignExpressionToVariable(range.to, "$modifiedLabel+1", ArrayToElementTypes.getValue(iterableDt))
|
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.A, false)
|
||||||
// pre-check for end already reached
|
// pre-check for end already reached
|
||||||
if(iterableDt==DataType.ARRAY_B) {
|
if(iterableDt==DataType.ARRAY_B) {
|
||||||
if(stepsize<0) {
|
asmgen.out(" sta $modifiedLabel+1")
|
||||||
println("signed byte check to<=from") // TODO
|
if(stepsize<0)
|
||||||
} else {
|
asmgen.out("""
|
||||||
println("signed byte check from<=to") // TODO
|
clc
|
||||||
}
|
sbc $varname
|
||||||
|
bvc +
|
||||||
|
eor #${'$'}80
|
||||||
|
+ bpl $endLabel""")
|
||||||
|
else
|
||||||
|
asmgen.out("""
|
||||||
|
clc
|
||||||
|
sbc $varname
|
||||||
|
bvc +
|
||||||
|
eor #${'$'}80
|
||||||
|
+ bmi $endLabel""")
|
||||||
} else {
|
} else {
|
||||||
if(stepsize<0) {
|
if(stepsize<0)
|
||||||
println("unsigned byte check to<=from") // TODO
|
asmgen.out(" cmp $varname | bcs $endLabel")
|
||||||
} else {
|
else
|
||||||
println("unsigned byte check from<=to") // TODO
|
asmgen.out(" cmp $varname | bcc $endLabel | beq $endLabel")
|
||||||
}
|
asmgen.out(" sta $modifiedLabel+1")
|
||||||
}
|
}
|
||||||
asmgen.out(loopLabel)
|
asmgen.out(loopLabel)
|
||||||
asmgen.translate(stmt.statements)
|
asmgen.translate(stmt.statements)
|
||||||
@@ -129,22 +148,9 @@ $modifiedLabel cmp #0 ; modified
|
|||||||
|
|
||||||
stepsize == 1 || stepsize == -1 -> {
|
stepsize == 1 || stepsize == -1 -> {
|
||||||
val varname = asmgen.asmVariableName(stmt.variable)
|
val varname = asmgen.asmVariableName(stmt.variable)
|
||||||
assignLoopvar(stmt, range)
|
assignLoopvarWord(stmt, range)
|
||||||
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.AY)
|
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.AY)
|
||||||
// pre-check for end already reached
|
precheckFromToWord(iterableDt, stepsize, varname, endLabel)
|
||||||
if(iterableDt==DataType.ARRAY_W) {
|
|
||||||
if(stepsize<0) {
|
|
||||||
println("signed word check to<=from") // TODO
|
|
||||||
} else {
|
|
||||||
println("signed word check from<=to") // TODO
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if(stepsize<0) {
|
|
||||||
println("unsigned word check to<=from") // TODO
|
|
||||||
} else {
|
|
||||||
println("unsigned word check from<=to") // TODO
|
|
||||||
}
|
|
||||||
}
|
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
sty $modifiedLabel+1
|
sty $modifiedLabel+1
|
||||||
sta $modifiedLabel2+1
|
sta $modifiedLabel2+1
|
||||||
@@ -177,14 +183,9 @@ $modifiedLabel2 cmp #0 ; modified
|
|||||||
|
|
||||||
// (u)words, step >= 2
|
// (u)words, step >= 2
|
||||||
val varname = asmgen.asmVariableName(stmt.variable)
|
val varname = asmgen.asmVariableName(stmt.variable)
|
||||||
assignLoopvar(stmt, range)
|
assignLoopvarWord(stmt, range)
|
||||||
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.AY)
|
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.AY)
|
||||||
// pre-check for end already reached
|
precheckFromToWord(iterableDt, stepsize, varname, endLabel)
|
||||||
if(iterableDt==DataType.ARRAY_W) {
|
|
||||||
println("signed word check from<=to") // TODO
|
|
||||||
} else {
|
|
||||||
println("unsigned word check from<=to") // TODO
|
|
||||||
}
|
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
sty $modifiedLabel+1
|
sty $modifiedLabel+1
|
||||||
sta $modifiedLabel2+1
|
sta $modifiedLabel2+1
|
||||||
@@ -231,14 +232,9 @@ $endLabel""")
|
|||||||
|
|
||||||
// (u)words, step <= -2
|
// (u)words, step <= -2
|
||||||
val varname = asmgen.asmVariableName(stmt.variable)
|
val varname = asmgen.asmVariableName(stmt.variable)
|
||||||
assignLoopvar(stmt, range)
|
assignLoopvarWord(stmt, range)
|
||||||
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.AY)
|
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.AY)
|
||||||
// pre-check for end already reached
|
precheckFromToWord(iterableDt, stepsize, varname, endLabel)
|
||||||
if(iterableDt==DataType.ARRAY_W) {
|
|
||||||
println("signed word check to<=from") // TODO
|
|
||||||
} else {
|
|
||||||
println("unsigned word check to<=from") // TODO
|
|
||||||
}
|
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
sty $modifiedLabel+1
|
sty $modifiedLabel+1
|
||||||
sta $modifiedLabel2+1
|
sta $modifiedLabel2+1
|
||||||
@@ -289,6 +285,53 @@ $endLabel""")
|
|||||||
asmgen.loopEndLabels.pop()
|
asmgen.loopEndLabels.pop()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun precheckFromToWord(iterableDt: DataType, stepsize: Int, fromVar: String, endLabel: String) {
|
||||||
|
// pre-check for end already reached.
|
||||||
|
// 'to' is in AY, do NOT clobber this!
|
||||||
|
if(iterableDt==DataType.ARRAY_W) {
|
||||||
|
if(stepsize<0)
|
||||||
|
asmgen.out("""
|
||||||
|
sta P8ZP_SCRATCH_REG
|
||||||
|
cmp $fromVar
|
||||||
|
tya
|
||||||
|
sbc $fromVar+1
|
||||||
|
bvc +
|
||||||
|
eor #${'$'}80
|
||||||
|
+ bpl $endLabel
|
||||||
|
lda P8ZP_SCRATCH_REG""")
|
||||||
|
else
|
||||||
|
asmgen.out("""
|
||||||
|
sta P8ZP_SCRATCH_REG
|
||||||
|
cmp $fromVar
|
||||||
|
tya
|
||||||
|
sbc $fromVar+1
|
||||||
|
bvc +
|
||||||
|
eor #${'$'}80
|
||||||
|
+ bmi $endLabel
|
||||||
|
lda P8ZP_SCRATCH_REG""")
|
||||||
|
} else {
|
||||||
|
if(stepsize<0)
|
||||||
|
asmgen.out("""
|
||||||
|
cpy $fromVar+1
|
||||||
|
beq +
|
||||||
|
bcc ++
|
||||||
|
bcs $endLabel
|
||||||
|
+ cmp $fromVar
|
||||||
|
bcc +
|
||||||
|
beq +
|
||||||
|
bne $endLabel
|
||||||
|
+""")
|
||||||
|
else
|
||||||
|
asmgen.out("""
|
||||||
|
cpy $fromVar+1
|
||||||
|
bcc $endLabel
|
||||||
|
bne +
|
||||||
|
cmp $fromVar
|
||||||
|
bcc $endLabel
|
||||||
|
+""")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun translateForOverIterableVar(stmt: PtForLoop, iterableDt: DataType, ident: PtIdentifier) {
|
private fun translateForOverIterableVar(stmt: PtForLoop, iterableDt: DataType, ident: PtIdentifier) {
|
||||||
val loopLabel = asmgen.makeLabel("for_loop")
|
val loopLabel = asmgen.makeLabel("for_loop")
|
||||||
val endLabel = asmgen.makeLabel("for_end")
|
val endLabel = asmgen.makeLabel("for_end")
|
||||||
@@ -645,7 +688,7 @@ $loopLabel""")
|
|||||||
asmgen.loopEndLabels.pop()
|
asmgen.loopEndLabels.pop()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun assignLoopvar(stmt: PtForLoop, range: PtRange) =
|
private fun assignLoopvarWord(stmt: PtForLoop, range: PtRange) =
|
||||||
asmgen.assignExpressionToVariable(
|
asmgen.assignExpressionToVariable(
|
||||||
range.from,
|
range.from,
|
||||||
asmgen.asmVariableName(stmt.variable),
|
asmgen.asmVariableName(stmt.variable),
|
||||||
|
@@ -473,6 +473,7 @@ Loops
|
|||||||
The *for*-loop is used to let a variable iterate over a range of values. Iteration is done in steps of 1, but you can change this.
|
The *for*-loop is used to let a variable iterate over a range of values. Iteration is done in steps of 1, but you can change this.
|
||||||
The loop variable must be declared separately as byte or word earlier, so that you can reuse it for multiple occasions.
|
The loop variable must be declared separately as byte or word earlier, so that you can reuse it for multiple occasions.
|
||||||
Iterating with a floating point variable is not supported. If you want to loop over a floating-point array, use a loop with an integer index variable instead.
|
Iterating with a floating point variable is not supported. If you want to loop over a floating-point array, use a loop with an integer index variable instead.
|
||||||
|
If the from value is already outside of the loop range, the whole for loop is skipped.
|
||||||
|
|
||||||
The *while*-loop is used to repeat a piece of code while a certain condition is still true.
|
The *while*-loop is used to repeat a piece of code while a certain condition is still true.
|
||||||
The *do--until* loop is used to repeat a piece of code until a certain condition is true.
|
The *do--until* loop is used to repeat a piece of code until a certain condition is true.
|
||||||
@@ -492,16 +493,6 @@ Only simple statements are allowed to be inside an unroll loop (assignments, fun
|
|||||||
on it to be the last value in the range for instance! The value of the variable should only be used inside the for loop body.
|
on it to be the last value in the range for instance! The value of the variable should only be used inside the for loop body.
|
||||||
(this is an optimization issue to avoid having to deal with mostly useless post-loop logic to adjust the loop variable's value)
|
(this is an optimization issue to avoid having to deal with mostly useless post-loop logic to adjust the loop variable's value)
|
||||||
|
|
||||||
.. warning::
|
|
||||||
For efficiency reasons, it is assumed that the ending value of the for loop is actually >= the starting value
|
|
||||||
(or <= if the step is negative). This means that for loops in prog8 behave differently than in other
|
|
||||||
languages if this is *not* the case! A for loop from ubyte 10 to ubyte 2, for example, will iterate through
|
|
||||||
all values 10, 11, 12, 13, .... 254, 255, 0 (wrapped), 1, 2. In other languages the entire loop will
|
|
||||||
be skipped in such cases. But prog8 omits the overhead of an extra loop range check and/or branch for every for loop
|
|
||||||
because the most common case is that it is not needed.
|
|
||||||
You should add an explicit range check yourself if the ending value can be less than the start value and
|
|
||||||
a full wrap-around loop is not what you want!
|
|
||||||
|
|
||||||
|
|
||||||
Conditional Execution
|
Conditional Execution
|
||||||
---------------------
|
---------------------
|
||||||
|
@@ -10,12 +10,8 @@ For 9.0 major changes
|
|||||||
- DONE: divmod() now supports multiple datatypes. divmodw() has been removed.
|
- DONE: divmod() now supports multiple datatypes. divmodw() has been removed.
|
||||||
- DONE: cx16diskio module merged into diskio (which got specialized for commander x16 target). load() and load_raw() with extra ram bank parameter are gone.
|
- DONE: cx16diskio module merged into diskio (which got specialized for commander x16 target). load() and load_raw() with extra ram bank parameter are gone.
|
||||||
- DONE: drivenumber parameter removed from all routines in diskio module. The drive to work on is now simply stored as a diskio.drivenumber variable, which defaults to 8.
|
- DONE: drivenumber parameter removed from all routines in diskio module. The drive to work on is now simply stored as a diskio.drivenumber variable, which defaults to 8.
|
||||||
|
- DONE: for loops now skip the whole loop if from value already outside the loop range (this is what all other programming languages also do)
|
||||||
|
|
||||||
- VM codegen: fix for loop pre-check for negative stepsizes.
|
|
||||||
- 6502 codegen: see if we can let for loops skip the loop if startvar>endvar, without adding a lot of code size/duplicating the loop condition.
|
|
||||||
It is documented behavior to now loop 'around' $00 but it's too easy to forget about!
|
|
||||||
Lot of work because of many special cases in ForLoopsAsmgen.translateForOverNonconstRange()
|
|
||||||
(vm codegen already behaves like this partly! Except for negative step sizes right now)
|
|
||||||
- once 9.0 is stable, upgrade other programs (assem, shell, etc) to it. + add migration guide to the manual.
|
- once 9.0 is stable, upgrade other programs (assem, shell, etc) to it. + add migration guide to the manual.
|
||||||
- [much work:] add special (u)word array type (or modifier such as @fast? ) that puts the array into memory as 2 separate byte-arrays 1 for LSB 1 for MSB -> allows for word arrays of length 256 and faster indexing
|
- [much work:] add special (u)word array type (or modifier such as @fast? ) that puts the array into memory as 2 separate byte-arrays 1 for LSB 1 for MSB -> allows for word arrays of length 256 and faster indexing
|
||||||
this is an enormous amout of work, if this type is to be treated equally as existing (u)word , because all expression / lookup / assignment routines need to know about the distinction....
|
this is an enormous amout of work, if this type is to be treated equally as existing (u)word , because all expression / lookup / assignment routines need to know about the distinction....
|
||||||
|
@@ -5,67 +5,107 @@
|
|||||||
|
|
||||||
main {
|
main {
|
||||||
sub start() {
|
sub start() {
|
||||||
|
ubyte from = 20
|
||||||
ubyte target = 10
|
ubyte target = 10
|
||||||
ubyte from = 250
|
|
||||||
|
|
||||||
ubyte xx
|
ubyte xx
|
||||||
for xx in from to target {
|
for xx in from to target {
|
||||||
txt.print_ub(xx)
|
txt.print_ub(xx)
|
||||||
txt.spc()
|
txt.spc()
|
||||||
}
|
}
|
||||||
txt.print("done\n")
|
txt.print("done\n\n")
|
||||||
for xx in target downto from {
|
for xx in target downto from {
|
||||||
txt.print_ub(xx)
|
txt.print_ub(xx)
|
||||||
txt.spc()
|
txt.spc()
|
||||||
}
|
}
|
||||||
txt.print("done\n")
|
txt.print("done\n\n")
|
||||||
|
|
||||||
byte starget = -120
|
byte sfrom = -10
|
||||||
byte sfrom = 120
|
byte starget = -20
|
||||||
|
|
||||||
byte sxx
|
byte sxx
|
||||||
for sxx in sfrom to starget {
|
for sxx in sfrom to starget {
|
||||||
txt.print_b(sxx)
|
txt.print_b(sxx)
|
||||||
txt.spc()
|
txt.spc()
|
||||||
}
|
}
|
||||||
txt.print("done\n")
|
txt.print("done\n\n")
|
||||||
for sxx in starget downto sfrom {
|
for sxx in starget downto sfrom {
|
||||||
txt.print_b(sxx)
|
txt.print_b(sxx)
|
||||||
txt.spc()
|
txt.spc()
|
||||||
}
|
}
|
||||||
txt.print("done\n")
|
txt.print("done\n\n")
|
||||||
|
|
||||||
uword wtarget = 10
|
uword wfrom = 1020
|
||||||
uword wfrom = 65530
|
uword wtarget = 1010
|
||||||
|
|
||||||
uword ww
|
uword ww
|
||||||
for ww in wfrom to wtarget {
|
for ww in wfrom to wtarget {
|
||||||
txt.print_uw(ww)
|
txt.print_uw(ww)
|
||||||
txt.spc()
|
txt.spc()
|
||||||
}
|
}
|
||||||
txt.print("done\n")
|
txt.print("done\n\n")
|
||||||
for ww in wtarget downto wfrom {
|
for ww in wtarget downto wfrom {
|
||||||
txt.print_uw(ww)
|
txt.print_uw(ww)
|
||||||
txt.spc()
|
txt.spc()
|
||||||
}
|
}
|
||||||
txt.print("done\n")
|
txt.print("done\n\n")
|
||||||
|
|
||||||
word swtarget = -32760
|
word swfrom = -1010
|
||||||
word swfrom = 32760
|
word swtarget = -1020
|
||||||
word sww
|
word sww
|
||||||
for sww in swfrom to swtarget {
|
for sww in swfrom to swtarget {
|
||||||
txt.print_w(sww)
|
txt.print_w(sww)
|
||||||
txt.spc()
|
txt.spc()
|
||||||
}
|
}
|
||||||
txt.print("done\n")
|
txt.print("done\n\n")
|
||||||
for sww in swtarget downto swfrom {
|
for sww in swtarget downto swfrom {
|
||||||
txt.print_w(sww)
|
txt.print_w(sww)
|
||||||
txt.spc()
|
txt.spc()
|
||||||
}
|
}
|
||||||
txt.print("done\n")
|
txt.print("done\n\n")
|
||||||
|
|
||||||
|
; all of the above with stepsize 2 / -2
|
||||||
|
for xx in from to target step 2{
|
||||||
|
txt.print_ub(xx)
|
||||||
|
txt.spc()
|
||||||
|
}
|
||||||
|
txt.print("done\n\n")
|
||||||
|
for xx in target downto from step -2 {
|
||||||
|
txt.print_ub(xx)
|
||||||
|
txt.spc()
|
||||||
|
}
|
||||||
|
txt.print("done\n\n")
|
||||||
|
for sxx in sfrom to starget step 2 {
|
||||||
|
txt.print_b(sxx)
|
||||||
|
txt.spc()
|
||||||
|
}
|
||||||
|
txt.print("done\n\n")
|
||||||
|
for sxx in starget downto sfrom step -2 {
|
||||||
|
txt.print_b(sxx)
|
||||||
|
txt.spc()
|
||||||
|
}
|
||||||
|
txt.print("done\n\n")
|
||||||
|
for ww in wfrom to wtarget step 2 {
|
||||||
|
txt.print_uw(ww)
|
||||||
|
txt.spc()
|
||||||
|
}
|
||||||
|
txt.print("done\n\n")
|
||||||
|
for ww in wtarget downto wfrom step -2 {
|
||||||
|
txt.print_uw(ww)
|
||||||
|
txt.spc()
|
||||||
|
}
|
||||||
|
txt.print("done\n\n")
|
||||||
|
for sww in swfrom to swtarget step 2 {
|
||||||
|
txt.print_w(sww)
|
||||||
|
txt.spc()
|
||||||
|
}
|
||||||
|
txt.print("done\n\n")
|
||||||
|
for sww in swtarget downto swfrom step -2 {
|
||||||
|
txt.print_w(sww)
|
||||||
|
txt.spc()
|
||||||
|
}
|
||||||
|
txt.print("done\n\n")
|
||||||
|
|
||||||
; TODO all of the above with stepsize 2 / -2
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user