Thought this would be simple... Z80

13»

• Hi Folks,

Found the following in the Tips section:

```;H=Y,L=X
LD A,H
AND 7
RRCA
RRCA
RRCA
LD L,A
LD A,H
RRA
RRA
RRA
AND 3
OR #58
LD H,A
```

I read this as calculating the address in the bitmap of X,Y but when I tested it is actually the attriubute file. Is there a similar routine for the bitmap address?

Many thanks

• I read this as calculating the address in the bitmap of X,Y but when I tested it is actually the attriubute file. Is there a similar routine for the bitmap address?

Can I cheat a little?

Heavy on the disasm
• edited September 2017
Thanks Bedazzle. I thoughjt I would test this but it does not seem to work unless I am being thick. I must be as this is ROM code so must work. :-)

```org 63000
ld b,\$08
call \$0E9B
ld (hl),\$FF
ret
```

I was expecting this to place a bar on the 8th row?

Manyt thanks

Post edited by Paddy Coleman on
;H =010ssppp, ss=segment 0-2, ppp=pixrow 0-7;
;L =rrrccccc, rrr=char.row 0-7, ccccc=char.col 0-31
;H =010ssppp, ss=segment 0-2, ppp=pixrow 0-7;
;L =rrrccccc, rrr=char.row 0-7, ccccc=char.col 0-31
To make this useful, we also need to deconstruct the actual coordinates:

Y coord = ssrrrppp
X coord = cccccxxx, where xxx is the pixel position within a cell.

(Note: Unlike in BASIC, the coordinates originate in the top left corner and span the entire height of the screen—192 pixels.)

So, for example, any pixel at coordinates 0,0–7,0 will be at address 01000000 00000000, i.e. 16384 (\$4000).
Every man should plant a tree, build a house, and write a ZX Spectrum game.

Author of A Yankee in Iraq, a 50 fps shoot-’em-up—the first game to utilize the floating bus on the +2A/+3,
and zasm Z80 Assembler syntax highlighter.
Member of the team that discovered, analyzed, and detailed the floating bus behavior on the ZX Spectrum +2A/+3.

A few Spectrum game fixes.
• edited September 2017
Hi Folks,

Looking for some advice on best practice. The following wee routine simply stores the 8 bytes of a character square, replaces them with a black box and finally restores the original contents.

The code works but seems rather long winded compared to a high level language e.g. using 8 variables to store the bytes rather than an array/loop. Is this the correct approach or am I missing a Z80 trick?

```100 CLS
110 PRINT AT 12,16;"X"
120 POKE 63000,12
130 POKE 63001,16
140 LET A=USR 63010
150 STOP
```
```; ------------------------------------------------------------------------------
; ------------------------------------------------------------------------------
; Variables

ROW                defb   \$00               ; Character row
COL                defb   \$00               ; Character column

STO_B1             defb   \$00               ; 8 bytes used to store current
STO_B2             defb   \$00               ; contents of the display file
STO_B3             defb   \$00               ; at row/column
STO_B4             defb   \$00
STO_B5             defb   \$00
STO_B6             defb   \$00
STO_B7             defb   \$00
STO_B8             defb   \$00
; ------------------------------------------------------------------------------

MAIN               ld     a,(ROW)           ; Calculate display file address
ld     b,a               ; from the character row/column
ld     a,(COL)
ld     c,a

push   hl                ; Store display file address
ld     a,(hl)            ; Store the current 8 bytes at
ld     (STO_B1),a        ; the character row/column
inc    h
ld     a,(hl)
ld     (STO_B2),a
inc    h
ld     a,(hl)
ld     (STO_B3),a
inc    h
ld     a,(hl)
ld     (STO_B4),a
inc    h
ld     a,(hl)
ld     (STO_B5),a
inc    h
ld     a,(hl)
ld     (STO_B6),a
inc    h
ld     a,(hl)
ld     (STO_B7),a
inc    h
ld     a,(hl)
ld     (STO_B8),a
pop    hl                ; Restore display file address

call   WAIT_KEY          ; Wait for a key to be pressed

push   hl                ; Store display file address
ld     (hl),\$FF          ; Draw a black box at the
inc    h                 ; character row/column
ld     (hl),\$FF
inc    h
ld     (hl),\$FF
inc    h
ld     (hl),\$FF
inc    h
ld     (hl),\$FF
inc    h
ld     (hl),\$FF
inc    h
ld     (hl),\$FF
inc    h
ld     (hl),\$FF
pop    hl                ; Restore display file address

call   WAIT_KEY          ; Wait for a key to be pressed

push   hl                ; Store display file address
ld     a,(STO_B1)        ; Restore the original 8 bytes
ld     (hl),a            ; to the character row/column
inc    h
ld     a,(STO_B2)
ld     (hl),a
inc    h
ld     a,(STO_B3)
ld     (hl),a
inc    h
ld     a,(STO_B4)
ld     (hl),a
inc    h
ld     a,(STO_B5)
ld     (hl),a
inc    h
ld     a,(STO_B6)
ld     (hl),a
inc    h
ld     a,(STO_B7)
ld     (hl),a
inc    h
ld     a,(STO_B8)
ld     (hl),a
pop    hl                ; Restore display file address

; ------------------------------------------------------------------------------
; Calculate display file address from character row and column

and    \$0F8
ld     h,a
ld     a,b
and    \$07
rrca
rrca
rrca
ld     l,a
ret
; ------------------------------------------------------------------------------
; Waits until a key is pressed

WAIT_KEY           push   hl                ; Store HL as used elsewhere
ld     hl,23560          ; LAST K system variable
ld     (hl),\$00          ; Put null value there
WKLOOP             ld     a,(hl)            ; New value of LAST K
cp     \$00               ; Is it still zero?
jr     z,WKLOOP          ; Yes, so no key pressed
pop    hl                ; Restore HL registers
ret                      ; No, key was pressed
; ------------------------------------------------------------------------------
```

Many thanks

Post edited by Paddy Coleman on
• a quick starter...
```push   hl                ; Store display file address
ld     a,(hl)            ; Store the current 8 bytes at
ld     (STO_B1),a        ; the character row/column
ld     (hl),FF        ; <--- put the cursor byte in at the same time
inc    h
ld     a,(hl)
ld     (STO_B2),a
ld     (hl),FF
inc    h
ld     a,(hl)
ld     (STO_B3),a
ld     (hl),FF

```

and so on....

use DE for STO_B1 and then INC DE it as you go along, will probably need a PUSH DE/POP DE
• A simpler keypress check:
```key_w	call \$28e	;poll keyboard
inc e		;if E=FFh (no key is pressed),
;INC E will result in 0
jr z,key_w
```

Or, without a ROM call and using only the accumulator:
```	xor a		;zeroize A to test all address lines of keyboard
key_w	in a,(\$fe)	;read port 254
cpl		;invert A
and \$1f		;mask the lowest 5 bits
jr z,key_w	;try again if no key is pressed

```
Every man should plant a tree, build a house, and write a ZX Spectrum game.

Author of A Yankee in Iraq, a 50 fps shoot-’em-up—the first game to utilize the floating bus on the +2A/+3,
and zasm Z80 Assembler syntax highlighter.
Member of the team that discovered, analyzed, and detailed the floating bus behavior on the ZX Spectrum +2A/+3.

A few Spectrum game fixes.
• If you're finished with the A register, you can shave a little bit of time off by doing this instead...

```                   push   hl                ; Store display file address

ld     a,\$FF

ld     (hl),a            ; Draw a black box at the
inc    h                 ; character row/column
ld     (hl),a
inc    h
ld     (hl),a
inc    h
ld     (hl),a
inc    h
ld     (hl),a
inc    h
ld     (hl),a
inc    h
ld     (hl),a
inc    h
ld     (hl),a
pop    hl                ; Restore display file address
```

You can also use the stack pointer to speed up restoring the 8 bytes of the original character a touch, too.

Something for you to ponder, perhaps. ;)

M

• edited September 2017
A simpler keypress check:
```key_w	call \$28e	;poll keyboard
inc e		;if E=FFh (no key is pressed),
;INC E will result in 0
jr z,key_w
```

I tried the above but it does not seem to work... I have the following and it simply seems to loop forever.
```; ------------------------------------------------------------------------------
; Waits until a key is pressed
; Provided by Ast_A_Moore on WOS

WAIT_KEY           call   \$028E             ; ROM poll keyboard
inc    e                 ; If E=\$FF (no key pressed)
; INC E will result in 0
jr     z,WAIT_KEY
ret
; ------------------------------------------------------------------------------
```

Post edited by Paddy Coleman on
• I tried the above but it does not seem to work... I have the following and it simply seems to loop forever.
Have you tried it within your code or on its own? Also, have you tried the other version (that only uses the A register)? I have a feeling something else is at play here. The code I posted is solid.
Every man should plant a tree, build a house, and write a ZX Spectrum game.

Author of A Yankee in Iraq, a 50 fps shoot-’em-up—the first game to utilize the floating bus on the +2A/+3,
and zasm Z80 Assembler syntax highlighter.
Member of the team that discovered, analyzed, and detailed the floating bus behavior on the ZX Spectrum +2A/+3.

A few Spectrum game fixes.
• edited September 2017
Hi Folks,

The code works but seems rather long winded compared to a high level language e.g. using 8 variables to store the bytes rather than an array/loop. Is this the correct approach or am I missing a Z80 trick?
Hi Paddy, you want some of the repetative commands
eg DJNZ , decrement reg B ,jump on NZ of B to adres -127 to +128
```   ld B, 8
start:
ld a,(hl)
inc hl
djnz start
```
this results in DECrementin B as a counter
when B = 0, the loop will stop
succes,
Post edited by Crisis on
my old website http://home.hccnet.nl/c.born/ has changed to http://www.cborn.nl/zxfiles/ so just click it and select a file
• ld a,\$FF
ld b, 8
start:
ld (hl),a ; Draw a black box at the
inc h ; character row/column
djnz start
my old website http://home.hccnet.nl/c.born/ has changed to http://www.cborn.nl/zxfiles/ so just click it and select a file
• The code works but seems rather long winded compared to a high level language e.g. using 8 variables to store the bytes rather than an array/loop.

You don’t need to define a variable for each stored byte. Just label the beginning of the buffer (and its length), then copy your screen to it:
```		   ld de,STO_B1
ld b,8
to_buffer	   ld a,(hl)
ld (de),a
inc h
inc de
djnz to_buffer
```

To draw your black square, use the same loop method:
```		   ld b,8
ld a,\$ff
box		   ld (hl),a
inc h
djnz box
```

Finally, reuse the screen-to-buffer loop, but reverse the source/destination:
```		   ld de,STO_B1
ld b,8
from_buffer	   ld a,(de)
ld (hl),a
inc h
inc de
djnz from_buffer

```

The complete section of the code will then look like this:
```                   push hl
ld de,STO_B1
ld b,8
to_buffer	   ld a,(hl)
ld (de),a
inc h
inc de
djnz to_buffer
pop hl

call WAIT_KEY

push hl
ld b,8
ld a,\$ff
box		   ld (hl),a
inc h
djnz box
pop hl

call WAIT_KEY

push hl
ld de,STO_B1
ld b,8
from_buffer	   ld a,(de)
ld (hl),a
inc h
inc de
djnz from_buffer
pop hl

ret

```

If you align the buffer address to a 256-byte boundary, you can speed up the code a little by replacing INC DE with INC E.
Every man should plant a tree, build a house, and write a ZX Spectrum game.

Author of A Yankee in Iraq, a 50 fps shoot-’em-up—the first game to utilize the floating bus on the +2A/+3,
and zasm Z80 Assembler syntax highlighter.
Member of the team that discovered, analyzed, and detailed the floating bus behavior on the ZX Spectrum +2A/+3.

A few Spectrum game fixes.
• I tried the above but it does not seem to work... I have the following and it simply seems to loop forever.
Have you tried it within your code or on its own? Also, have you tried the other version (that only uses the A register)? I have a feeling something else is at play here. The code I posted is solid.

Thanks. I am sure it is me and my code. :-). I will test your routine on its own tomorrow and hopefully the reason will become apparent.

• edited September 2017
You don’t need to define a variable for each stored byte. Just label the beginning of the buffer (and its length), then copy your screen to it:

How do you define the variable and it's length? Not seen that yet.

Many thanks

Post edited by Paddy Coleman on
• You don’t need to define a variable for each stored byte. Just label the beginning of the buffer (and its length), then copy your screen to it:

How do you define the variable and it's length? Not seen that yet.

Note, I didn’t say “variable,” I said “buffer.”
Well, it depends on the assembler. For example, the piece of code I posted above will work with what you have—the remaining labels (STO_B2, 3, etc.) will simply remain unused as labels. Many assemblers allow you to define a memory area by size with a DEFS pseudoinstruction. So in your case, you could replace your eight bytes allocated by eight DEFB statements with a single
```label	defs 8
```

This should reserve eight bytes starting at memory location pointed to by the label label.

If you’re still a little confused, let me copy your code and replace the relevant sections with mine.
```; ------------------------------------------------------------------------------
; ------------------------------------------------------------------------------
; Variables

ROW_COL		defw \$0000               ; Character row/column
STO_B1		defs 8		;Buffer (8 bytes)
; ------------------------------------------------------------------------------

MAIN               ld     hl,(ROW_COL)
ld     b,h
ld     c,l

push hl
ld de,STO_B1
ld b,8
to_buffer	   ld a,(hl)
ld (de),a
inc h
inc de
djnz to_buffer
pop hl

call WAIT_KEY

push hl
ld b,8
ld a,\$ff
box		   ld (hl),a
inc h
djnz box
pop hl

call WAIT_KEY

push hl
ld de,STO_B1
ld b,8
from_buffer	   ld a,(de)
ld (hl),a
inc h
inc de
djnz from_buffer
pop hl

ret
; ------------------------------------------------------------------------------
; Calculate display file address from character row and column

and    \$0F8
ld     h,a
ld     a,b
and    \$07
rrca
rrca
rrca
ld     l,a
ret
; ------------------------------------------------------------------------------
; Waits until a key is pressed

WAIT_KEY           push   hl                ; Store HL as used elsewhere
ld     hl,23560          ; LAST K system variable
ld     (hl),\$00          ; Put null value there
WKLOOP             ld     a,(hl)            ; New value of LAST K
cp     \$00               ; Is it still zero?
jr     z,WKLOOP          ; Yes, so no key pressed
pop    hl                ; Restore HL registers
ret                      ; No, key was pressed
; ------------------------------------------------------------------------------
```

Notice I also slightly optimized the bit where you pass the row/column values into B and C. Instead of multiple memory reads into the accumulator, I used a single LD HL,(**) instruction. DEFW means “define word” (a word is two bytes).
Every man should plant a tree, build a house, and write a ZX Spectrum game.

Author of A Yankee in Iraq, a 50 fps shoot-’em-up—the first game to utilize the floating bus on the +2A/+3,
and zasm Z80 Assembler syntax highlighter.
Member of the team that discovered, analyzed, and detailed the floating bus behavior on the ZX Spectrum +2A/+3.

A few Spectrum game fixes.
• Looking for some advice on best practice. The following wee routine simply stores the 8 bytes of a character square, replaces them with a black box and finally restores the original contents.

You don't really say why you're doing this. Is it an self-education thing? I'm trying to learn Spectrum C programming with Z88DK so I'm motivated in a similar way. So, how about a challenge?

Take your original concept, but loop to walk the screen column by column and row by row, blanking and restoring each character cell at a time. Here's a first attempt, with no optimisation so it's hopefully clear what it's doing:
```/*
* zcc +zx -vn -m -startup=31 -clib=sdcc_iy -O3 --max-allocs-per-node200000 blank_chars.c -o blank_chars -create-app
*/

#include <z80.h>
#include <arch/zx.h>

int main()
{
unsigned char col;
unsigned char row;

/* Copy the ROM contents into the screen. This is just so it's easier to see it working. */
unsigned char* rom_ptr;
unsigned char* screen_ptr;
for( rom_ptr=(unsigned char*)0,
screen_ptr=(unsigned char*)0x4000; rom_ptr < (unsigned char*)6144; rom_ptr++,
screen_ptr++ )
{
*screen_ptr = *rom_ptr;
}

/* Loop, top to bottom, left to right... */
for ( row=0; row < 24; row++ )
{
for ( col=0; col < 32; col++ )
{
unsigned char  char_line;
unsigned char  char_lines_store[8];

/* ...find the screen address of the character.*/

/* Store away the data at each line and replace with 0xFF. */

for ( char_line=0; char_line < 8; char_line++ )
{

z80_delay_ms(1);
}

/* Now go back to the top of the character cell... */

/* ...and restore the cell's lines from the saved data. */

for ( char_line=0; char_line < 8; char_line++ )
{

z80_delay_ms(1);
}

}
}

return 0;
}
```

I haven't timed this. It's got little delays in so you can see the blank/restore cell sort of winking its way down the screen.

Try it in assembly language?
• Hi Folks,

The challenge continues... I now have the following which displays a hatched desktop followed by a masked sprite (8x8). You press a key between stages. I am sure to you experts this is basic stuff but I am learning a lot by going through this. Also, I now understand how you can find yourself needing more registers! :-)

```100 CLS
110 POKE 63000,12
120 POKE 63001,16
130 LET A=USR 63002
140 STOP
```
```; ------------------------------------------------------------------------------
; ------------------------------------------------------------------------------
; Parameters

ROW                defb   \$00               ; Character row
COL                defb   \$00               ; Character column
; ------------------------------------------------------------------------------

MAIN               call   DT_DRAW           ; Create desktop pattern

ld     a,(ROW)           ; Calculate display file address
ld     b,a               ; from the character row/column
ld     a,(COL)
ld     c,a

push   hl                ; Store display file address

call   WAIT_KEY          ; Wait for a key to be pressed

; Copy old display file data to temporary storage (OLD_Bn)

ld     de,OLD_B1         ; Address of 1st old storage byte
ld     b,8               ; Loop for 8 bytes
MAIN_LOOP1         ld     a,(hl)            ; Get byte from display file
ld     (de),a            ; Store dislay file byte
inc    h                 ; Move down 1 pixel row (256
inc    de                ; bytes) and next storage byte
djnz   MAIN_LOOP1

; Copy old data to new data

ld     bc,8              ; Number of bytes to copy
ldi                      ; INC DE/HL and DEC BC x 8
ldi
ldi
ldi
ldi
ldi
ldi
ldi

; Create new data by ANDing old data with the mask

ld     b,8               ; Loop for 8 bytes
MAIN_LOOP2         ld     a,(de)            ; Get mask byte
ld     c,a               ; Store in C
ld     a,(hl)            ; Get sprite data
and    c                 ; AND A with C
ld     (hl),a            ; Store modified sprite byte
inc    hl                ; Next masked sprite byte
inc    de                ; Next mask byte
djnz   MAIN_LOOP2

; Create new data by ORing old data with the pointer

ld     de,PNT_B1         ; Address of pointer data
ld     b,8               ; Loop for 8 bytes
MAIN_LOOP3         ld     a,(de)            ; Get pointer byte
ld     c,a               ; Store in C
ld     a,(hl)            ; Get sprite data
or     c                 ; OR A with C
ld     (hl),a            ; Store modified sprite byte
inc    hl                ; Next masked sprite byte
inc    de                ; Next pointer byte
djnz   MAIN_LOOP3

; Display new sprite data

pop    hl                ; Restore display file address
push   hl                ; Store display file address

ld     de,NEW_B1         ; New sprite data (AND/OR)
ld     b,8               ; Loop for 8 bytes
MAIN_LOOP4         ld     a,(de)            ; Get masked sprite data
ld     (hl),a            ; Replace display file byte
inc    de                ; Next masked sprite byte
inc    h                 ; Add 256 i.e. next pixel row
djnz   MAIN_LOOP4

call   WAIT_KEY          ; Wait for a key to be pressed

; Restore old screen data

pop    hl                ; Restore display file address

ld     de,OLD_B1         ; Restore the old 8 bytes
ld     b,8               ; Loop for 8 bytes
MAIN_LOOP5         ld     a,(de)            ; Get old display file byte
ld     (hl),a            ; Replace display file byte
inc    de                ; Next old display file byte
inc    h                 ; Add 256 i.e. next pixel row
djnz   MAIN_LOOP5

; ------------------------------------------------------------------------------
; Draw the Desktop

DT_DRAW            ld     hl,16384          ; Start of display file
ld     a,170             ; Hatch pattern 10101010
ld     c,24              ; Loop for 24 character rows
halt
DT_LOOP1           ld     b,0               ; Loop for 256 bytes
DT_LOOP2           ld     (hl),a            ; Load byte to display file
inc    hl                ; Next display file byte
djnz   DT_LOOP2
cpl                      ; NOT hatch pattern
dec    c
jr     nz,DT_LOOP1
ret
; ------------------------------------------------------------------------------
; Calculate display file address from character row and column

and    \$0F8
ld     h,a
ld     a,b
and    \$07
rrca
rrca
rrca
ld     l,a
ret
; ------------------------------------------------------------------------------
; Waits until a key is pressed
; Provided by Ast_A_Moore on WOS

WAIT_KEY           push   hl                ; Store HL as used elsewhere
ld     hl,23560          ; LAST K system variable
ld     (hl),\$00          ; Put null value there
WK_LOOP            ld     a,(hl)            ; New value of LAST K
cp     \$00               ; Is it still zero?
jr     z,WK_LOOP         ; Yes, so no key pressed
pop    hl                ; Restore HL registers
ret
; ------------------------------------------------------------------------------
; Current Data

OLD_B1             defb   \$00               ; 8 bytes used to store current
OLD_B2             defb   \$00               ; contents of the display file
OLD_B3             defb   \$00               ; at row/column
OLD_B4             defb   \$00
OLD_B5             defb   \$00
OLD_B6             defb   \$00
OLD_B7             defb   \$00
OLD_B8             defb   \$00

; New Data

NEW_B1             defb   \$00               ; 8 bytes used to store the
NEW_B2             defb   \$00               ; masked sprite data
NEW_B3             defb   \$00               ; AND/OR
NEW_B4             defb   \$00
NEW_B5             defb   \$00
NEW_B6             defb   \$00
NEW_B7             defb   \$00
NEW_B8             defb   \$00

MSK_B1             defb   0
MSK_B2             defb   126
MSK_B3             defb   126
MSK_B4             defb   126
MSK_B5             defb   126
MSK_B6             defb   126
MSK_B7             defb   126
MSK_B8             defb   0

; Sprite Pointer Data

PNT_B1             defb   0
PNT_B2             defb   126
PNT_B3             defb   126
PNT_B4             defb   126
PNT_B5             defb   126
PNT_B6             defb   126
PNT_B7             defb   126
PNT_B8             defb   0
; ------------------------------------------------------------------------------
```

As always, comments and suggestions welcome.

Many thanks to everyone for their help and encouragement so far.

• A simpler keypress check:
```key_w	call \$28e	;poll keyboard
inc e		;if E=FFh (no key is pressed),
;INC E will result in 0
jr z,key_w
```

I tried the above but it does not seem to work... I have the following and it simply seems to loop forever.
```; ------------------------------------------------------------------------------
; Waits until a key is pressed
; Provided by Ast_A_Moore on WOS

WAIT_KEY           call   \$028E             ; ROM poll keyboard
inc    e                 ; If E=\$FF (no key pressed)
; INC E will result in 0
jr     z,WAIT_KEY
ret
; ------------------------------------------------------------------------------
```

Well tried this on its own in Basin but it just seems to ignore the key press. Wonder if this is a problem with Basin?

• you can try this, it will wait for a keypress
```keyloop        RST   56                            ; checks for keypress
LD    A,(23557)
OR    A         ; CP 0
JP    Z,keyloop

; the rest of your program
```
• Well tried this on its own in Basin but it just seems to ignore the key press. Wonder if this is a problem with Basin?
I don’t think it ignores it. If you tried it on its own, it might execute too quickly, and register the key that you keep holding. For example, if you run it with RANDOMIZE USR XXXXX, then when you press ENTER, the program might execute way before you release the key.
Every man should plant a tree, build a house, and write a ZX Spectrum game.

Author of A Yankee in Iraq, a 50 fps shoot-’em-up—the first game to utilize the floating bus on the +2A/+3,
and zasm Z80 Assembler syntax highlighter.
Member of the team that discovered, analyzed, and detailed the floating bus behavior on the ZX Spectrum +2A/+3.

A few Spectrum game fixes.
• MatGubbins wrote: »
Thanks for doing that thricenightly, I was about to ask if you could post the C --> asm output to compare the code, but you've provided it - cheers. It does make great reading too.

Heh, this fun!

I had a brainwave: unwinding the loop by one allows me to use an 8 bit count down instead of a 16 bit one:
```      unsigned char scans;

for( scans = 255; scans != 0; scans-- )
{
*screen = 0xAA;
screen++;
}
*screen = 0xAA;
screen++;
```

Now the scans variable will fit in a single 8 bit register.

The compiler now produces:
```ld	a,0xff
l_main_00104:
ld	(hl),0xaa
inc	hl
dec	a
or	a, a
jr	NZ,l_main_00104
ld	(hl),0xaa
inc	hl
```

So that's better, since it's not messing with D and E, but it's got the OR A,A in there which your code avoided by using B and DJNZ. This runs 100 times in 7.6 seconds, so halfway between the first C version and the hand crafted version.

i dont know if it was mentioned but ' DEC A ' will interfere with the flags so ommitting 'or a,a' will save 4t per loop
the compiler should know the flags are influenced already so that 'or' is not needed.
```ld	a,0xff
l_main_00104:
ld	(hl),0xaa
inc	hl
dec	a
jr	NZ,l_main_00104
ld	(hl),0xaa
inc	hl
```

my old website http://home.hccnet.nl/c.born/ has changed to http://www.cborn.nl/zxfiles/ so just click it and select a file