Screen memory
Screen memory
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.
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.
Re: Screen memory
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).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.
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
Re: Screen memory
Thanks [mention]TomD[/mention]!
Re: Screen memory
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
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
Re: Screen memory
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
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
- Ast A. Moore
- Rick Dangerous
- Posts: 2641
- Joined: Mon Nov 13, 2017 3:16 pm
Re: Screen memory
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.
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.
Re: Screen memory
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
Re: Screen memory
This addressing actually makes more sense if you swap some address lines
Re: Screen memory
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.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
You'd end up with:
add a,a
ld l,a
ld h,15
add hl,hl
add hl,hl
Re: Screen memory
yeah, if you don't need A to be preserved (which is usually the case at this stage), then your version is even better.
- Ast A. Moore
- Rick Dangerous
- Posts: 2641
- Joined: Mon Nov 13, 2017 3:16 pm
Re: Screen memory
Very clever, indeed! Unfortunately, that would be difficult to maintain for a custom font, especially if it was loaded at an arbitrary address.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
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.
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.
Re: Screen memory
yeah. but your routine seem to use hard-coded ROM address anyway, so in this special case it fits. ;-)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.
Re: Screen memory
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.
I have pixel scrolling working in a horizontal direction now.
- Ast A. Moore
- Rick Dangerous
- Posts: 2641
- Joined: Mon Nov 13, 2017 3:16 pm
Re: Screen memory
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):ketmar wrote: ↑Tue Aug 25, 2020 8:22 amyeah. but your routine seem to use hard-coded ROM address anyway, so in this special case it fits.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.
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.
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.
Re: Screen memory
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.
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
Re: Screen memory
@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
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
Re: Screen memory
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:
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:
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!
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!