Screen Memory - Moving screen address up one line

The place for codemasters or beginners to talk about programming any language for the Spectrum.
Post Reply
User avatar
MonkZy
Manic Miner
Posts: 279
Joined: Thu Feb 08, 2018 1:01 pm

Screen Memory - Moving screen address up one line

Post by MonkZy »

The Spectrums screen address layout seems very confusing at first. After some study the arrangement makes sense. If you are writing software with text output, having a display organised into character rows of 8 pixel lines is useful.

When I started z80 assembly I used routines written by other people to determine screen addresses. A commonly used routine is 'Move register pair down one pixel line'.

Code: Select all

;;; Move HL down one pixel line

Pixel_Address_Down:     INC H		; Go down onto the next pixel line
                        LD A,H		; Check first three bits of high byte (Y0-Y2)
                        AND 7
                        RET NZ		; If any bits are set (we are inside a char line), we are done
;next char line
                        LD A,L		; we crossed the char line ((Y0-Y2)=0 i.e Y6=1)
                        ADD A,32	; (move down 1 char line) add the extra bit into (Y3-Y5)
                        LD L,A
                        RET C		; Check for a carry bit (crossed a third), if C is set we are done
;did not cross a third
                        LD A,H		; We better clean up the bit from (Y6)
                        SUB 8
                        LD H,A
        
	                RET		; we are done
User avatar
MonkZy
Manic Miner
Posts: 279
Joined: Thu Feb 08, 2018 1:01 pm

Re: Screen Memory - Moving screen address up one line

Post by MonkZy »

Sorry , nasty double post...clicked submit rather than preview


I needed a routine for 'Move register pair up one line'. At first I thought it would be simple, just a reverse of moving down. Here is my code :

Code: Select all

;;; Move HL up one pixel line

HL_Up_4:
	ld a,h			; get the high byte into A
	AND %00011000		
	ld c,a			; strip out (Y6-Y7), store in C

	ld a,h
	AND %11100111		; 
	add 8			; put in a 'stop bit'
	dec a			; Go up one pixel line

	AND %11100111		;
	add c			; restore (Y6-Y7)
	ld h,a
							
	and %00000111
	cp 4			; if (bits Y0-Y2)=4 we have crossed a character line
	ret nz			; A<>7, we are inside the char line, we are done
	
	ld a,l			; get the low byte into A
	AND %00011111
	ld b,a			; keep the first 5 bits (X0-X4) in B

	ld a,l
	sub a,32

	AND %11100000
	add b			; restore the first five bytes
	ld l,a	

	AND %11100000
	cp 224
	ret nz			;A<>224, we have not crossed a third, we are done

	ld a,h	
	sub 8			;we have crossed a third , subtract 8 from the high byte
	ld h,a

	ret
The move down code is very elegant. My code seems less so..but it works. Can this be optimised, or is there optimised code for this calculation already?
User avatar
Ast A. Moore
Rick Dangerous
Posts: 2641
Joined: Mon Nov 13, 2017 3:16 pm

Re: Screen Memory - Moving screen address up one line

Post by Ast A. Moore »

I do it like this:

Code: Select all

	ld a,h			;check if we’re within 7 bottom lines of a char row
	and 7			;one of the three lowest bits will not be 0
	jr nz,done		;if so, it is safe to decrement H and move one line up
	ld a,l			;else, load A with L
	sub $20			;subtract 32 from it,
	ld l,a			;and put it back in L
	jr c,done		;if SUB $20 results in an overflow, we’re on the next line up
				;of char row
	ld a,h
	add a,8			;now add 8 to H
	ld h,a			;to move to the next char row

done	dec h			;move up one line

	ret
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.
User avatar
MonkZy
Manic Miner
Posts: 279
Joined: Thu Feb 08, 2018 1:01 pm

Re: Screen Memory - Moving screen address up one line

Post by MonkZy »

Wow...Beautiful. Thank you so much [mention]Ast A. Moore[/mention].

I was keen to get some of my own code working, as an excercise in trying to understand the screen memory. I now understand the memory arrangement, I am still a million miles away from turning out clean, orderly code.
User avatar
MonkZy
Manic Miner
Posts: 279
Joined: Thu Feb 08, 2018 1:01 pm

Re: Screen Memory - Moving screen address up one line

Post by MonkZy »

I do have one question..When the sub 32 creates an overflow (carry bit set) does it not also destroy the first five bits containing the X location...I should really read up about how the z80 handles negative numbers.
User avatar
Ast A. Moore
Rick Dangerous
Posts: 2641
Joined: Mon Nov 13, 2017 3:16 pm

Re: Screen Memory - Moving screen address up one line

Post by Ast A. Moore »

MonkZy wrote: Sun Apr 26, 2020 5:08 pm I am still a million miles away from turning out clean, orderly code.
Well, clean and orderly code is almost always a just few hundred iterations away. ;)
MonkZy wrote: Sun Apr 26, 2020 5:24 pm When the sub 32 creates an overflow (carry bit set) does it not also destroy the first five bits containing the X location...I should really read up about how the z80 handles negative numbers.
It doesn’t destroy them per se. It just moves us into the wrong row, so we compensate by adding 8 to the hight byte. There are no “wasted” bits in a screen address. As long as you are within the $4000–$5aff range, you’ll always end up in some position on the screen.

Sometimes it’s easier to figure out the logic of the Spectrum’s display file with a pen and paper (quite literally), rather than rack your brain staring at the code and raw numbers.
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.
dfzx
Manic Miner
Posts: 681
Joined: Mon Nov 13, 2017 6:55 pm
Location: New Forest, UK
Contact:

Re: Screen Memory - Moving screen address up one line

Post by dfzx »

Since this is a bite sized piece of assembly language which I can deal with, I had a look at the function which does this in Z88Dk. It's this:

Code: Select all

asm_zx_saddrpup:

   ; enter : hl = screen address
   ;
   ; exit  : hl = screen address moved up one pixel
   ;         carry set if new screen address is off screen
   ;
   ; uses  : af, hl

   ld a,h
   dec h
   and $07
   ret nz
   
   ld a,$08
   add a,h
   ld h,a
   
   ld a,l
   sub $20
   ld l,a
   ret nc
   
   ld a,h
   sub $08
   ld h,a

   and $18
   cp $18

   ccf
   ret
This appears to be 7 bytes larger than Ast's code but I think it's quicker in most cases. I'm not sure what that 'and/cp' at the end is for though.
Derek Fountain, author of the ZX Spectrum C Programmer's Getting Started Guide and various open source games, hardware and other projects, including an IF1 and ZX Microdrive emulator.
User avatar
arkannoyed
Manic Miner
Posts: 436
Joined: Mon Feb 05, 2018 9:56 am
Location: Northamptonshire

Re: Screen Memory - Moving screen address up one line

Post by arkannoyed »

Not sure if any of these might help. Found these in my archives.

Code: Select all

Next Line down WRT HL
---------------------

24		inc h
7C		ld a,h
E607		and $07
200A		jr nz,end
7D		ld a,l
C620		add a,$20
6F		ld l,a
3805		jr c,end
7C		ld a,h
D608		sub $08
67		ld h,a

16 bytes

Next Line Down Circa 1998 Spencer Winset
----------------------------------------

24		inc h
7C		ld a,h
E607		and $07
2009		jr nz,end
7D		ld a,l
D6E0		sub $E0
6F		ld l,a
9F		sbc a,a
E6F8		and $F8
84		add a,h
67		ld h,a

15 bytes

Next Line Up Spencer Winset
---------------------------------------

     (UP_HL)                  (UP_HL+)
 
    HEX  |  OP CODE          LINE    HEX  |  OP CODE
    _____|________________          ______|_______________
    7C   |  LD   A,H          01     7C   |  LD   A,H
    25   |  DEC  H            02     25   |  DEC  H
    E607 |  AND  #07          03     E607 |  AND  #07
    200A |  JR   NZ,BREAK     04     2009 |  JR   NZ,BREAK
    7D   |  LD   A,L          05     7D   |  LD   A,L
    D620 |  SUB  #20          06     C6E0 |  ADD  A,#E0
    6F   |  LD   L,A          07     6F   |  LD   L,A
    3804 |  JR   C,BREAK      08     9F   |  SBC  A,A
    7C   |  LD   A,H          09     E608 |  AND  #08
    D608 |  SUB  #08          10     84   |  ADD  A,H
    67   |  LD   H,A          11     67   |  LD   H,A
          BREAK  ...                       BREAK  ...
    _____________________          _____________________
     16b - 27/49/59t                15b - 27/56t
 
 
	
User avatar
Joefish
Rick Dangerous
Posts: 2058
Joined: Tue Nov 14, 2017 10:26 am

Re: Screen Memory - Moving screen address up one line

Post by Joefish »

I always remember the diagram of how a 16-bit number holds the screen address (I so hope I get this right):

Code: Select all

0 1 0 [b b] [p p p] // [r r r] [c c c c c]
Where '010----- // --------' represents the start address, 16384
'b b' - is the screen one-third block (two bits, 0,1,2)
'p p p' - is the in-character pixel-row (three bits 0..7)
'r r r' - is the character row within the 1/3 block (three bits 0..7)
'c c c c c' - is the character column number (five bits, 0..31)

So technically the eight-bit pixel line number Y (0..191) is defined as b b // r r r // p p p, which needs to be chopped up into three parts and inserted into the above. Even just the character row number (0..23) is 'b b // r r r', which still needs chopping up. (Though note that the 'b b' part is already in the right position to go straight into the high word).

As described above, adding or subtracting one from Y is complicated once it's dismembered and spread around the address word.

Personally I tend to go for a table, or character placement, as avoidance strategies!

One thing you might do is Joffa Smith's trick for drawing sprites (e.g. Cobra) where they only appear with 2-pixel precision. That means you always know you can copy at least two lines of your sprite image with just a simple INC Hi-Byte between rows, before needing to resort to such fiddly maths. And he'd even store every second line of his sprites in reverse byte order, so he could copy one row with INC L between bytes, INC H to move down a row, then copy another line of bytes in reverse order with DEC L, then be ready to do this fiddly add-one-line-to-the-screen-addess function, now being back at the left-hand edge of the sprite. (That is assuming the screen address is in HL, and you're doing some sort of masked copy rather than a simple LDI).
User avatar
Pobulous
Dynamite Dan
Posts: 1358
Joined: Wed Nov 15, 2017 12:51 pm

Re: Screen Memory - Moving screen address up one line

Post by Pobulous »

Took me a while to follow the code.

Firstly I didn't understand the comment: ;if SUB $20 results in an overflow, we’re on the next line up of char row
It makes more sense to say : ;if we get an overflow, then we are crossing a 1/3 screen boundary

The other part I couldn't follow was why you were adding 8 to H, when crossing into a new character (instead of 7), but then I realised you follow on into the DEC H, which makes it equivalent whilst saving 1 byte of RAM (there's a speed/storage optimisation available there)
Firefox

Re: Screen Memory - Moving screen address up one line

Post by Firefox »

Joefish wrote: Mon Apr 27, 2020 2:58 pm One thing you might do is Joffa Smith's trick for drawing sprites (e.g. Cobra) where they only appear with 2-pixel precision. That means you always know you can copy at least two lines of your sprite image with just a simple INC Hi-Byte between rows, before needing to resort to such fiddly maths. And he'd even store every second line of his sprites in reverse byte order, so he could copy one row with INC L between bytes, INC H to move down a row, then copy another line of bytes in reverse order with DEC L, then be ready to do this fiddly add-one-line-to-the-screen-addess function, now being back at the left-hand edge of the sprite. (That is assuming the screen address is in HL, and you're doing some sort of masked copy rather than a simple LDI).
That's a pretty cool bit of thinking! 8-)
User avatar
Ast A. Moore
Rick Dangerous
Posts: 2641
Joined: Mon Nov 13, 2017 3:16 pm

Re: Screen Memory - Moving screen address up one line

Post by Ast A. Moore »

arkannoyed wrote: Mon Apr 27, 2020 2:57 pm Not sure if any of these might help. Found these in my archives.

Code: Select all

     (UP_HL)              
 
    HEX  |  OP CODE       
    _____|________________
    7C   |  LD   A,H      
    25   |  DEC  H        
    E607 |  AND  #07      
    200A |  JR   NZ,BREAK 
    7D   |  LD   A,L      
    D620 |  SUB  #20      
    6F   |  LD   L,A      
    3804 |  JR   C,BREAK  
    7C   |  LD   A,H      
    D608 |  SUB  #08      
    67   |  LD   H,A
          BREAK  ...      
    _____________________
     16b - 27/49/59t
	
Uh, I don’t think SUB $08 is going to work. You really need to add 8 to H, not subtract it from it.
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.
User avatar
Joefish
Rick Dangerous
Posts: 2058
Joined: Tue Nov 14, 2017 10:26 am

Re: Screen Memory - Moving screen address up one line

Post by Joefish »

Another option is to use a linear back-buffer where the next line address is simply the current line +32. And you re-order it when copying the whole thing to the main screen.

There's an even more advanced (and mind-boggling) trick, mainly for doing plotting points and lines for 3D stuff, where you have a 256 x 256 pixel back-buffer, which allows you to define your screen in columns, not rows. So the first 256 bytes is the first 8-pixel wide column of the screen (plus a few extra rows), the next 256 bytes is the next 8-pixel-wide column, and so on. That way if your address is HL then DEC L and INC L move you one pixel row up and down, and DEC H and INC H move you one screen byte left and right. Again, you resolve it all when copying from the buffer to the display.
User avatar
1bvl109
Dizzy
Posts: 98
Joined: Tue Jun 04, 2019 9:00 pm

Re: Screen Memory - Moving screen address up one line

Post by 1bvl109 »

MonkZy wrote: Sun Apr 26, 2020 2:34 pm After some study the arrangement makes sense. If you are writing software with text output, having a display organised into character rows of 8 pixel lines is useful.
But is this really the reason? Has anyone ever asked any designer of the machine? I mean, it seems to be a question which urges itself on you, once you see, what's going on, doesn't it? According to this https://retrocomputing.stackexchange.co ... map-layout it might be a problem caused by cheapness of the hardware in the first place and might have been solved otherwise
even with a linear layout, the ULA could have used page mode to optimize accesses, but it would have needed to read 16 pixels ahead rather than just 8 each time, which would have made the required circuitry larger and the ULA potentially more expensive. Alternatively, the RAM chips could have been organised with their address lines mapped into a different order than the one the processor expects; this would have worked (and, I believe, was the solution used in the Apple II for refresh, which is a similar problem).
"Truth would quickly cease to be stranger than fiction, once we got used to it." - H.L. Mencken
AndyC
Dynamite Dan
Posts: 1406
Joined: Mon Nov 13, 2017 5:12 am

Re: Screen Memory - Moving screen address up one line

Post by AndyC »

I suspect that whatever made the hardware cheaper would have been the key driver. Getting the cost down as low as possible was a big deal for Sinclair and it's fairly obvious from the ROM that concerns about optimal speed were far from the top priority.
Post Reply