4

I originally posted this question on stackoverflow, but I wrote a little program that converts coordinates stored in the b and c registers of the z80 into their corresponding memory address for the ZX Spectrum screen bitmap. Here it is:

org $8000 start: ld b, $0f ;0-32 X ld c, $08 ;0-255 Y call convert_xy ld (hl), $ff ;this shows where the pixels are supposed to end up ld a, $ff ld ($402f), a ;the subroutine is supposed to produce this value in hl ret convert_xy: ;convert (x,y) locations to bitmap memory locations, answer is stored in hl ld a,c ;y first and %00111000 ;let's look at y3-y5 first and a ;clear carry flag rla ;move over to end of low byte rla ld l,a ;move results to low byte storage register ld a,c ;look at the rest of y and %11000000 ;look at y7 and 6 and a rra ;move over rra rra ld d,a ;temporary storage ld a,c or %01000000 ;base address ($4000) or d ;done ld h,a ;move to results register ld a,b ;x now and %00011111 or l ld l,a ;store result in l ret 

The portion of the code containing the x coordinate works just fine but the part of the subroutine concerning the y coordinate acts strange when any value over 7 is converted.

For example, when I try to convert y = $08, the byte loaded onto the screen suddenly skips and ends up in the middle of the screen (while 0-7 neatly moved down the pixels on screen one pixel at a time). Values over $10 disappear from the screen entirely. What is going on? Thank you.

EDIT: Here is the way that the coordinates are encoded into the memory address:

enter image description here

2
  • Could you add some sort of pseudo-code/formula of what calculation you're trying to perform on y? Use Edit link to edit your question. Commented Jul 17, 2021 at 18:18
  • 2
    You don't need the and a after and #imm because the carry flag is already cleared. Commented Jul 18, 2021 at 12:48

1 Answer 1

7

The code could look like this:

0000 06 0F LD b,$0f ; 0-31 X 0002 0E 08 LD c,$08 ; 0-191 Y 0004 CD 0A 00 CALL convert_xy 0007 36 FF LD (hl),$ff ; write the pixels 0009 76 HALT 000A convert_xy: ; convert (x,y) in BC to memory address in HL 000A ; assuming arguments are always in valid range 000A 79 LD a,c ; getting Y position bits [5:3] ... 000B 07 RLCA ; adjust it for the address lower byte's 000C 07 RLCA ; bits [7:5] 000D E6 E0 AND $e0 ; mask out the garbage 000F B0 OR b ; mix in the X coordinate 0010 6F LD l,a ; this is final result for address low byte 0011 79 LD a,c ; back to Y 0012 E6 07 AND $7 ; bits Y[2:0] are already in place 0014 F6 40 OR $40 ; make it $4000 base 0016 67 LD h,a ; intermediate state is set to H 0017 79 LD a,c 0018 0F RRCA ; moving bits Y[7:6] to close the gap 0019 0F RRCA ; that was taken by bits Y[5:3] before 001A 0F RRCA 001B E6 18 AND $18 ; mask out the garbage 001D B4 OR h ; join with temporary in H 001E 67 LD h,a ; this is final address high byte 001F C9 RET 

The result looks ok in online simulator.

Your mistake is to stash the moved bits [7:6] into register D, and then loading A with the original Y coordinate and or'ing it all together with previously masked and moved bits.

1
  • 1
    Yep, that was my mistake, it seems that adding an AND to target the first three bits after reloading c into a fixed it. Commented Jul 17, 2021 at 20:45

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.