1

this kind of stems of gbz80 - IF statements

I have a code that loops 0x167 times, writing to the screen. What I'd like to happen is for the script to loop three times.

My script:

    d322 21A0C3 LD HL,C3A0h
    d323 016801 LD BC,0168h
    d324 110300 LD DE,0003h
    d325 00     NOP
    .
    .
    .
    d330 0B     DEC BC
    d331 75     LD (HL),L
    d332 00     NOP
    d333 23     INC HL
    d334 00     NOP
    .
    .
    .
    d33f 78     LD A,B
    d340 B1     OR C
    d341 C22BD3 JP NZ,D32Bh
    d344 7A     LD A,D
    d345 B3     OR E
    d346 1B     DEC DE
    d347 00     NOP
    d348 00     NOP
    d349 C22BD3 JP NZ,D32B
    d34c C9     RET

Now, instead of stopping where it should, it goes on to corrupt memory further until it reaches d325 and causes the script to crash. As you might be able to tell, I attempted to use DE as the counter for loops.

Please explain your answer, I'm still quite dull towards this.

Community
  • 1
  • 1
  • my z80 is a little rusty, but does DEC DE set the flags? And why the NOPs after it, they're not doing anything useful. – Anonymouse Mar 02 '17 at 22:26
  • As far as I can tell, DEC does not set flags, but LD does when setting things to 0. And the NOP are for space in case I want to add any code. – Deccadeo Youtube Mar 03 '17 at 00:09
  • 1
    Just re: NOPs, is that because you're assembling by hand for educational purposes? So the jump addresses are calculated manually? In an assembler you can usually place a label, e.g. `loop_start: DEC HL` and then later just write `JP NZ, loop_start`. The assembler will figure out the correct address after it has assembled your code. No need to do anything that assumes a certain amount of space. – Tommy Mar 03 '17 at 17:19
  • 1
    DEC of a 16 bit register (BC, DE, HL, IX, IY, SP) does not set any flags. DEC of an 8 bit register does set flags. LD does not set any flags. `LD A,D; OR E;` tests if DE = 0 because it logically OR's the top 8 bits with the bottom 8 bits. "OR" sets flags and after a bit of thought we can see that A will be zero if and only if D and E were 0. – George Phillips Mar 04 '17 at 01:59

2 Answers2

2

My reading is that you:

Set HL = 0xC3A0, BC = 0x0168, DE = 0x0003

do {
  ... something you've omitted ...
  do {
    bc--
    [l] = hl
    hl++
  } while(bc)

  was_positive = (DE > 0)
  DE--
} while(was_positive)

So the outer loop will occur four times, as the pattern will be:

  • perform inner loop (* 1)
  • compare 3 to 0
  • perform inner loop (* 2)
  • compare 2 to 0
  • perform inner loop (* 3)
  • compare 1 to 0
  • perform inner loop (* 4)
  • compare 0 to 0, no longer greater, exit

Possibly you want to do the DEC before the comparison? It's the LD A, D / OR E that compares DE to zero — you're asserting that it is zero if exactly zero of its bits are set — the subsequent decrement doesn't change the result you already calculated.

That being the case you'll get 0x0168 + 4 * 0x10000 iterations = 0x40168 iterations. Which even then is probably not what you want? That means you'll overwrite every address in memory, several times. Even if your code is safe in ROM, that's a lot of wasted code.

If you intended to loop 3 * 0x0168 times then also remember to reload BC. You just let it underflow to 0xffff and resume. Consider switching the order of your initial loads so that BC is last, and change the outer branch to jump back to where you load it.

Tommy
  • 99,986
  • 12
  • 185
  • 204
1

The problem is that you decrement DE after setting the flags. The critical bit of code reads:

LD A,D
OR E
DEC DE
JP NZ,D32B

Change that to:

DEC DE
LD A,D
OR E
JP NZ,D32B

And the outer loop will run 3 times instead of 4. Note that the BC loop is handled correctly and will execute 0x168 times, not 0x167 so you may want to change that.

16 bit counters on the Z-80 are awkward as the 16 bit decrements don't set any flags. That's why you need the code to OR the two registers together as a way to set the Z flag when the 16 bit pair is equal to zero.

Since you only need 3 iterations you can use a single 8 bit register and write:

LD E,3
...
DEC E
JP NZ,D32B

As noted in Tommy's response, you also need to have BC reloaded in the inner loop say by moving the LD BC,0x168 to D32B.

George Phillips
  • 4,564
  • 27
  • 25