Screen memory

The place for codemasters or beginners to talk about programming any language for the Spectrum.
Post Reply
User avatar
PeterJ
Site Admin
Posts: 6873
Joined: Thu Nov 09, 2017 7:19 pm
Location: Surrey, UK

Screen memory

Post by PeterJ »

I'm continuing my adventures with assembly, and after many failed attempts over the years I'm finally progressing.

I'm looking at the screen memory layout, and I know starting on the first line of a cell I incremental the h byte from the original 16384 to get the next line down from lines 0-7.

What's the calculation for moving between cells. So the first line of first cell is 16384, next is 16385, until we get to column 31. What do I add for the top row line of the second row?

Are these addresses stored in ROM for the printing routine or can the be worked out? Thanks.
User avatar
TomD
Manic Miner
Posts: 374
Joined: Tue Nov 13, 2018 9:47 am
Location: Leeds UK
Contact:

Re: Screen memory

Post by TomD »

PeterJ wrote: Mon Aug 24, 2020 7:35 pm I'm continuing my adventures with assembly, and after many failed attempts over the years I'm finally progressing.

I'm looking at the screen memory layout, and I know starting on the first line of a cell I incremental the h byte from the original 16384 to get the next line down from lines 0-7.

What's the calculation for moving between cells. So the first line of first cell is 16384, next is 16385, until we get to column 31. What do I add for the top row line of the second row?

Are these addresses stored in ROM for the printing routine or can the be worked out? Thanks.
The next cell across after 31 (one row down and back at the start) is simply 32 and this continues until you get to 256. You then need to jump to 16384+2048bytes (256*8).

It takes a little getting used to at first.

TomD
Last edited by TomD on Mon Aug 24, 2020 8:19 pm, edited 2 times in total.
Retro enthusiast and author of Flynn's Adventure in Bombland, The Order of Mazes & Maze Death Rally-X. Check them out at http://tomdalby.com
User avatar
PeterJ
Site Admin
Posts: 6873
Joined: Thu Nov 09, 2017 7:19 pm
Location: Surrey, UK

Re: Screen memory

Post by PeterJ »

Thanks [mention]TomD[/mention]!
User avatar
TomD
Manic Miner
Posts: 374
Joined: Tue Nov 13, 2018 9:47 am
Location: Leeds UK
Contact:

Re: Screen memory

Post by TomD »

Bit more info:

0x4000, 0x4001, 0x4002... 0x401F
0x4020
0x4040
0x4060
0x4080
0x40A0
0x40C0
0x40E0
now you would think 0x4100 but it is actually 0x4800, 0x4100 is the first cell second line down.

As far as I know these memory positions are not stored in ROM so for my games so I usually create a lookup table. I pass it a y value and it gives me the memory position of the left most memory location, so for y=0 that would be 0x4000 (16384) and for y=7 it would be 0x4700 (18176).

TomD
Last edited by TomD on Mon Aug 24, 2020 8:23 pm, edited 2 times in total.
Retro enthusiast and author of Flynn's Adventure in Bombland, The Order of Mazes & Maze Death Rally-X. Check them out at http://tomdalby.com
User avatar
TomD
Manic Miner
Posts: 374
Joined: Tue Nov 13, 2018 9:47 am
Location: Leeds UK
Contact:

Re: Screen memory

Post by TomD »

When I was starting out in machine code I heavily used Toni Baker's excellent book MASTERING MACHINE CODE ON YOUR ZX SPECTRUM

https://spectrumcomputing.co.uk/entry/2 ... X_Spectrum

Chapter 7, page 86 has a great description of the screen layout.

TomD
Retro enthusiast and author of Flynn's Adventure in Bombland, The Order of Mazes & Maze Death Rally-X. Check them out at http://tomdalby.com
User avatar
Ast A. Moore
Rick Dangerous
Posts: 2641
Joined: Mon Nov 13, 2017 3:16 pm

Re: Screen memory

Post by Ast A. Moore »

PeterJ wrote: Mon Aug 24, 2020 7:35 pm Are these addresses stored in ROM for the printing routine or can the be worked out? Thanks.
For a printing routine, you can take advantage of another property of the screen layout—the low byte of the attribute is the same as the low byte of the topmost line of pixels of the bitmap address; you only need to convert the high byte.

Here’s an example of a fairly quick print routine I wrote some time ago. It has a few more features than you might be interested in at the moment, but they might come in handy in the future. The code is decently commented (he said modesty), but feel free to ask me question if you find any parts difficult to follow.

Code: Select all

		halt			;delay for border coloring
		ld bc,$14d
wait		dec bc
		ld a,b
		or c
		jp nz,wait
		dec bc
		nop
		nop

		ld de,$5800		;initial print position
		ld a,%00111001		;attributes
		ld (char_attr+1),a	;inject into SMC below

		xor a			;set border to black
		out (254),a
	
		call printer
string		defm "String of text for testing spee",$e4	;Last char has Bit 7 set
	
		ld a,7			;set border to white
		out (254),a

		ret

printer
		pop hl			;grab stack address (after CALL; beginning of string)
		ld a,(hl)		;value at address into A for testing
		inc hl			;next address
		push hl			;back onto the stack
		push af			;remember A

		and $7f			;reset Bit 7

		ld h,0			;make sure H=0
		add a,a			;			\
		ld l,a			;load L with chr code
		add hl,hl		; 			  multiply HL by 8
		add hl,hl		;			/
		ld bc,$3c00		;add $3c00 to get the base address of character
		add hl,bc		;bitmap in ROM (chr code*8+$3c00), load it into HL

		ld c,d			;save D in C

char_attr	ld a,0			;SMC
		ld (de),a
		ld a,d

		add a,a			;convert attr addr
		add a,a			;to bitmap addr
		add a,a
		and d
		ld d,a

		ld b,8			;eight lines of char bitmap to draw
char_pr		ld a,(hl)
		ld (de),a
		inc l
		inc d
		djnz char_pr

		ld d,c			;restore D
		inc e			;next cell to the right

		pop af			;recall character code
		or a
		jp p,printer		;continue, if it's positive (Bit 7 reset)
		ret			;RETurn to the address right after EOS
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
ketmar
Manic Miner
Posts: 697
Joined: Tue Jun 16, 2020 5:25 pm
Location: Ukraine

Re: Screen memory

Post by ketmar »

smaller and faster ROM character address calculation, BC is not used. ;-)

Code: Select all

  ld   l,a
  add  hl,hl
  ld   h,15
  add  hl,hl
  add  hl,hl
zx64
Manic Miner
Posts: 211
Joined: Sat Jul 11, 2020 3:25 am
Location: Australia

Re: Screen memory

Post by zx64 »

This addressing actually makes more sense if you swap some address lines
User avatar
cmal
Manic Miner
Posts: 629
Joined: Fri Jun 05, 2020 1:05 am
Location: California

Re: Screen memory

Post by cmal »

ketmar wrote: Tue Aug 25, 2020 3:44 am smaller and faster ROM character address calculation, BC is not used. ;-)

Code: Select all

  ld   l,a
  add  hl,hl
  ld   h,15
  add  hl,hl
  add  hl,hl
This is neat! Thanks @ketmar! I never thought of doing it this way. Would it help to first do an add a,a then ld l,a, and do without the first add hl,hl? I think add a,a is a tad faster than Add hl,hl. That's if you don't mind disrupting the contents of A.
You'd end up with:
add a,a
ld l,a
ld h,15
add hl,hl
add hl,hl
User avatar
ketmar
Manic Miner
Posts: 697
Joined: Tue Jun 16, 2020 5:25 pm
Location: Ukraine

Re: Screen memory

Post by ketmar »

cmal wrote: Tue Aug 25, 2020 6:50 am Would it help to first do an add a,a then ld l,a, and do without the first add hl,hl? I think add a,a is a tad faster than Add hl,hl. That's if you don't mind disrupting the contents of A.
yeah, if you don't need A to be preserved (which is usually the case at this stage), then your version is even better.
User avatar
Ast A. Moore
Rick Dangerous
Posts: 2641
Joined: Mon Nov 13, 2017 3:16 pm

Re: Screen memory

Post by Ast A. Moore »

ketmar wrote: Tue Aug 25, 2020 3:44 am smaller and faster ROM character address calculation, BC is not used. ;-)

Code: Select all

  ld   l,a
  add  hl,hl
  ld   h,15
  add  hl,hl
  add  hl,hl
Very clever, indeed! Unfortunately, that would be difficult to maintain for a custom font, especially if it was loaded at an arbitrary address.
Last edited by Ast A. Moore on Tue Aug 25, 2020 9:41 am, edited 1 time in total.
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
ketmar
Manic Miner
Posts: 697
Joined: Tue Jun 16, 2020 5:25 pm
Location: Ukraine

Re: Screen memory

Post by ketmar »

Ast A. Moore wrote: Tue Aug 25, 2020 7:58 am Very clever, indeed! Unfortunately, that would be difficult to maintain for a custom font, especially if it loaded at an arbitrary address.
yeah. but your routine seem to use hard-coded ROM address anyway, so in this special case it fits. ;-)
User avatar
PeterJ
Site Admin
Posts: 6873
Joined: Thu Nov 09, 2017 7:19 pm
Location: Surrey, UK

Re: Screen memory

Post by PeterJ »

Thanks for all the replies everyone. I'm printing off chapter 7 of the Toni Baker book too.

I have pixel scrolling working in a horizontal direction now.
User avatar
Ast A. Moore
Rick Dangerous
Posts: 2641
Joined: Mon Nov 13, 2017 3:16 pm

Re: Screen memory

Post by Ast A. Moore »

ketmar wrote: Tue Aug 25, 2020 8:22 am
Ast A. Moore wrote: Tue Aug 25, 2020 7:58 am Very clever, indeed! Unfortunately, that would be difficult to maintain for a custom font, especially if it loaded at an arbitrary address.
yeah. but your routine seem to use hard-coded ROM address anyway, so in this special case it fits. ;-)
That was for Peter’s benefit. In my code, I use a label for the font address and then include a BIN font file in the assembly listing (8-byte aligned):

Code: Select all

	ld h,0		
	add a,a		
	ld l,a		
	add hl,hl	
	add hl,hl	
	ld bc,font1-256	
	add hl,bc

. . .

 align 8

font1
#insert "Assets/Font 1.4.bin"
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
PeterJ
Site Admin
Posts: 6873
Joined: Thu Nov 09, 2017 7:19 pm
Location: Surrey, UK

Re: Screen memory

Post by PeterJ »

Thanks again for all the information. Lots to read over the weekend!

In the meantime I was flicking through my copy of Spectrum Machine Code by Ian Stewart and Robin Jones and found this routine which helped me along my way too.

So my understand is that we start at 16384, poke the location with 255 and continue this 256 times which fills the top line of the first 8 rows. Then we increase HL by 7 x 256 which jumps us down to section 2 of the display, then again to section 3.

[mention]TomD[/mention] Thanks for the tip about the Toni Baker book - I had forgotten about that one. I sneakily printed off chapter 7 today. I noticed that Toni clears the TV-Flag system variable before doing displaying. I have not seen that done before in any of the other books I have read. Is it strictly necessary?

https://skoolkid.github.io/rom/asm/5C3C.html

The book seems as rare as hens' teeth. Its a pity there is not a better scan around. The copy which has been around for ages seems to be OCRd so is not perfect. If anyone has a spare copy they want to sell please get in touch.

Code: Select all

org $6000
	ld a,3
	ld b,0
	ld hl,4000h
LOOP
	ld (hl),255
	inc hl
	djnz LOOP
	dec a
	cp b
	jr z, SKIP
	push af
	ld a,7
	add a,h
	ld h,a
	pop af
	jr LOOP
SKIP	ret
	END 24576
Image
User avatar
TomD
Manic Miner
Posts: 374
Joined: Tue Nov 13, 2018 9:47 am
Location: Leeds UK
Contact:

Re: Screen memory

Post by TomD »

@PeterJ the TVFLAG is for using the ROM routines to print characters, so only need if you do that. Sometimes it can be useful to use these ROM routines especially if you want to save space for mini-game challenges, but mostly best to write your own and if you do it is not needed.

TomD
Retro enthusiast and author of Flynn's Adventure in Bombland, The Order of Mazes & Maze Death Rally-X. Check them out at http://tomdalby.com
User avatar
ketmar
Manic Miner
Posts: 697
Joined: Tue Jun 16, 2020 5:25 pm
Location: Ukraine

Re: Screen memory

Post by ketmar »

as it was said somewhere else ;-), screen address pattern is actually very simple. in binary it looks like this:

010hhzzz yyyxxxxx

here:
xxxxx -- 5-bit x coordinate. this gives us [0..#1F] in hex, or [0..31] in dec.
zzz -- vertical coordinate in character cell (that's what you're changing by `inc h`). 3 bits, [0..7].
hh -- spectrum screen is divided to 3 parts by 8 character rows, and this is the number of that part. note that despite using 2 bits, this is [0..2], not [0..3].
yyy -- finally, this is row number in the corresponding part, selected by hh.

just remember this bit pattern, and then you will be able to easily decipher all magic code that calculates various addresses. basically, what that code does is rearranging bits from linear pattern to "screen" pattern. for example, to calculate character address:
we have 5 bits of "linear" character coord in A, [0..23]:
000nnnnn
we need to put low 3 bits into "yyy" above, and high 2 bits into "hh" above. as we want character cell address, "zzz" is always 0. so:

Code: Select all

; note that two highest "nn" bits are already where we want them to be
; so, store A to L (we'll need it later)
ld l,a
; mask all unneded bits in A
and #18  ; this is %00011000 in binary, i.e. only "hh" bits are left
or #40 ; we need it to go from 16384, or #4000 ;-) set required bits after hh (see our "diagram" above)
ld h,a ; we're done with the high part, let's build the low part
; restore our A
; we need to move 3 lowest bits to 3 highest bits
; this can be done with 5 shifts to the left (see our "diagram" again)
ld a,l
and #07  ; remove unneeded bits; this is %00000111 in binary, i.e. we left only 3 last bits
; 5 shifts, to put it in place
rla
rla
rla
rla
rla
ld l,a
; here we have our screen address in HL!
that's it. of course, this code is far from optimal, but this is the basic thing which you can use to build your optimised versions. as you'll learn about cyclic shifts and other asm tricks, you will be able to make this code much smaller and much faster. yet the basic idea will always remain the same: shuffling bits around until they end up in the places we want them to be.

if you want pixel position, i.e. from [0..191], you will need to do slightly more bit shuffling, but once you understand which bits should go where, it is quite easy.

moving from screen address to attribute address is easy too. attributes are linear, so:
010110HH YYYxxxxxx
basically, screen "yyy" bits are going to attribute "YYY", and screen "hh" bits are going to attribute "HH" bits. our "yyy" bits are already where we need them, so we don't even have to touch the low byte. so, the only thing we have to do is move "hh" to HH, with three shifts:

Code: Select all

ld a,h
; move hh to HH
rra
rra
rra
; remove unneded bits
and #03
; and fix the address by setting required bits
or #58
ld h,a
; we're done!
Post Reply