Managing the Screen
How to manipulate the Spectrum’s screen
Table of Contents
1 - Clear the Screen
Example code to clear the Spectrum’s screen
This code snippet will clear the standard screen on all versions of the Spectrum.
It's broken down into three sections:
- Clear's the pixel data
- Reset the screen's colour data, in this instance to White text on Black Background
- Sets the screen border to black
; ***************************************************************************
; Clear the Spectrum Screen
;
; Example 1
;
; Author: Peter Mount, Area51.dev & Contributors
; URL: https://area51.dev/sinclair/asm/screen/getcharaddr/
; ***************************************************************************
;
; Clear the Spectrum screen, setting the border to black and white text on
; a black background
;
; On Exit:
; A Undefined
; BC Undefined
; DE Undefined
; HL Undefined
;
clearScreen:
; Clear the pixel data
ld hl,DISPLAYFILE ; Start of screen memory
ld (hl),&00 ; Set first byte to 0
ld de,DISPLAYFILE+1 ; Destination as next byte
ld bc, 6144 ; Number of bytes to copy
ldir ; Copy from hl to de bc times
; Clear the colour attributes
ld (hl),&07 ; White text black Background
ld bc, 767 ; Number of bytes to copy, attr size -1
ldir ; Clear the attributes
ld a, 0 ; Set border to black
ld (BORDCR),a ; set OS copy of colour
out (&fe),a ; set hardware colour
ret
Important note here:
We copy 6144 bytes in the first LDIR
not 6143 which we would normally do if we want to erase just the
DISPLAYFILE
.
Using 6144 bytes will cause the first byte of DISPLAYATTR
to be set to 0 which is fine here as we want
HL
and DE
to be pointing to DISPLAYATTR
ready for the second
LDIR
operation.
This could, in theory cause the top left character to flicker briefly as it's been set to 0
(BLACK foreground and background).
If that's an issue then set the BC
to 6143 and increment HL
and DE
after the
LDIR
as in the next example:
; ***************************************************************************
; Clear the Spectrum Screen
;
; Example 2 - does not corrupt the top left characters colour attributes
;
; Author: Peter Mount, Area51.dev & Contributors
; URL: https://area51.dev/sinclair/asm/screen/getcharaddr/
; ***************************************************************************
;
; Clear the Spectrum screen, setting the border to black and white text on
; a black background
;
; On Exit:
; A Undefined
; BC Undefined
; DE Undefined
; HL Undefined
;
clearScreen:
; Clear the pixel data
ld hl,DISPLAYFILE ; Start of screen memory
ld (hl),&00 ; Set first byte to 0
ld de,DISPLAYFILE+1 ; Destination as next byte
ld bc, 6143 ; Number of bytes to copy
ldir ; Copy from hl to de bc times
inc hl ; Move HL and DE forward 1 byte
inc de ; so they point to DISPLAYATTR
; Clear the colour attributes
ld (hl),&07 ; White text black Background
ld bc, 767 ; Number of bytes to copy, attr size -1
ldir ; Clear the attributes
ld a, 0 ; Set border to black
ld (BORDCR),a ; set OS copy of colour
out (&fe),a ; set hardware colour
ret
There are faster but longer methods of clearing the screen which I won't go into here.
For most purposes the first example above will work for most purposes other than time critical games.
2 - Get address of character on screen
Calculate the true address on screen of a character
Calculating the start address for a character might sound complicated due to its weird memory layout, but it is
actually pretty simple.
The way the address is calculated is described with the Display File,
however the following code allows you to calculate the address in Z80 machine code.
A link to download this routine is available under Resources at the top right of the page:
; ***************************************************************************
; Calculates the actual screen address for a character in BC (row,col)
; returning that address in HL.
;
; Author: Peter Mount, Area51.dev & Contributors
; URL: https://area51.dev/sinclair/asm/screen/getcharaddr/
; ***************************************************************************
;
; Although the memory layout for the spectrum screen seems weird,
; it's actually pretty logical. You can tell this weirdness is down to how
; the ULA works internally with the way the addressing is mapped, as shown
; at https://area51.dev/sinclair/spectrum/screen/file/
;
; Address format:
; H L
; 010rrnnn rrrccccc where r=row,
; c=column,
; n=row in the character definition.
;
; To calculate the address of a character:
; High byte = &40 or (row and &18)
; Low byte = (row<<5) or column
;
; That would then be the top byte of the character.
;
; To get the next byte in the character just increment the high byte
; (e.g. n=1) & so on for the next 7 bytes.
;
; To get the next character's address then simply increment the low byte.
; Be careful however when wrapping around the end of the line as, after
; the first 8 lines the address will fail.
;
; Ideally you should calculate the address again at the start of each line.
;
; This might explain why the original spectrum editor was 8 lines long, as you
; only needed to set the high byte to the 8-line block in memory and set the
; low byte to the offset in the line being edited to get that characters
; screen address.
;
; On Entry:
; BC b=row, c=col in Spectrum 32x24 characters
;
; On Exit:
; HL address of top row if character definition in screen
; BC unchanged
; A undefined
;
getSpectrumCharPos: ; Get Spectrum char pos (bc) into HL for physical screen address
ld a, b ; first calculate high byte - get row
and &18 ; Mask bits 4 & 5 from row
or &40 ; set bit 6, this gives us the upper memory address
ld h, a ; Set H
ld a, b ; get row
add a ; Shift left 5 to form low address
add a ; use add not srl as this saves 1 byte and half the t-states
add a ; per shift, especially as we don't need to worry about carry flag
add a
add a ; HA is now the address of the start of line
or c ; Add column value
ld l, a ; HL now address of top row of character
ret
Note: This code is actually from my Teletext Mode 7 emulator for the Spectrum
3 - Set the border colour
Example code to set the border colour on the Spectrum’s screen
The border colour is managed by the ULA.
Changing the border colour is simply writing the required colour to the ULA whilst
storing a copy in the BORDCR
system variable at 0x5C48 so that the OS also knows what the colour is.
In this example we set the Border to black
Set the Spectrum Screen Border:
setBorder
LDA,0x00
LD(BORDCR),A
OUT(&FE),A
RET