Sprite Graphics Tutorial

This tutorial is by Derek M. Smith © 2005.

Sprite graphics are the backbone of arcade games. Put simply, a sprite is a moveable screen object, such as a spaceship, alien, or anything else you can imagine. Some computers come with built in sprites, such as the Commodore and Atari ranges. These machines could generate sprites without the need for complex programming, and on top of this, they used an additional processor, which left the main CPU free to get on with the rest of the program. The Spectrum sadly was never adorned with such luxuries and so programmers had to write their own sprite routines from scratch (a daunting task for the amateur).

Sloppy programming in this area could really spoil a game, either making it too slow, or through flickering graphics.

A simple sprite routine is often one of the first things a new programmer, after having mastered the basics of machine code, will have a go at.

At first it seems straight forward, but soon all sorts of hurdles appear. The first usually being the Spectrum’s unusual screen arrangement. More experienced programmers who have mastered the basic sprite handling routine will then seek ways to optimise it.

This tutorial is aimed at both beginners and the more experienced programmers. Beginners will learn the principles behind sprite programming and the experienced machine coders will learn to find ways of improving their routines. For simplicity, this tutorial assumes that the reader has a decent grasp of assembly language.

ZX Spectrum screen arrangement
“The display file stores the television picture. It is rather curiously laid out…” -Spectrum Manual ch24 p164

On the face of it the Spectrum’s screen arrangement is pretty strange. Just watch the screen of a game loading, and you’ll understand what I mean. Why does it skip lines like that? Sure it may be interesting to watch the picture being gradually built up, but it can be a real pain when it comes to writing a sprite routine (at least until you understand the principles behind its layout).

First off, the Spectrum screen has a resolution of 256 pixels across by 192 down, not including the border around it. It can display 8 colours (including black and white) with two levels of intensity (brightness).

The Memory Map for the screen starts at address 16384 and is 6912 bytes long.
It is split in to two halves with the first 6144 bytes containing the bit-map (or pixel map, if you wish) and the remaining 768 bytes containing the attribute-map.

Consider the bit-map first:-

Each line of 256 pixels is stored as 32 bytes: 32 x 8 = 256. So far so good.

Now you would think that each line would follow on from the one before in the pixel map, and most people (myself included) write their first sprite routine thinking this to be the case – only to find that when they execute the routine, with a shudder of anticipation, their sprite is spread all over the screen. At this point some give up (or decide to write adventure games instead) baffled by the Spectrum’s idiosyncrasies.

Let me say now that when you do grasp the screen layout and the techniques used for addressing it, I think you will be glad that the designers done it that way.

Type in the following and run it:

	10 FOR S = 16384 TO 22527
	20 POKE S, 255
	30 NEXT S

This short BASIC program fills the screen. Although it is moving through memory sequentially, POKING 255 into each memory location, the screen fills up in a rather more esoteric manner. Run the program a few times and watch the pattern it traces. You’ll notice two things: One is the way it skips lines, another is that the screen is divided into three parts.

I said earlier that each line is stored as 32 bytes, with the first line beginning at 16384. Where does the second line start? Most of us assume it would start at 16384+32. In fact it starts at 16384+256 and 16384+32 takes us down to line 8. Remember that the screen is used to display text (characters) as well as graphics. So adding 32 actually takes us down one character row (8 screen lines).

In machine code 16bit addresses, such as 16384, are stored in high byte / low byte format. The high byte is equal to ADDRESS / 256 and the low byte is the remainder from the division, so 16384 in high/low byte format would be 64, 0 (16384 / 256 = 64 with no remainder). This means that when we increase the high byte of a 16 bit address by one, it is equivalent to adding 256. So when it comes to screen addressing all we need to do to move down a line is increase the high byte of the address. This can be done quite easily and quickly in machine code:

	ld hl,16384	;load the hl register pair with the address of the start of the display file
	inc h		;increment the high byte (4 tstates)

This is in fact much quicker than adding 32 where we would have to do the following:

	ld hl,16384
	ld a,l		;load the accumulator with the l register as we cannot add 
			;directly to the l register
	add a,32	;add 32
	ld l,a		;load the result back into the l register (total time 15 tstates)

The above works within a group of 8 lines, ie. 1 char line.

Now, as mentioned before, the other peculiar thing about the screen layout is that it is divided in to three parts – top, middle and bottom. Each third of the screen has 64 lines (or 8 character rows) and takes up 2048 bytes of memory. All that has been said so far applies only so long as we don’t cross from one third into another.

The whole matter becomes at good deal clearer if we look at the screen address in binary.

           High Byte                |               Low Byte

0   1   0   T   T   L   L   L          Cr Cr Cr Cc Cc Cc Cc Cc

I have used some abbreviations to make things a bit clearer:

T – these two bits refer to which third of the screen is being addressed: 00 – Top, 01 – Middle, 10 – Bottom

L – these three bits indicate which line is being addressed: from 0 – 7, or 000 – 111 in binary

Cr – these three bits indicate which character row is being addressed: from 0 – 7

Cc – these five bits refer to which character column is being addressed: from 0 – 31

The top three bits ( 010 ) of the high byte don’t change.

Calculating the screen address

The first task in putting a sprite on the screen is to translate the X,Y coords into a screen address.
There are two ways of doing this. One is to set up a look-up table which contains 192 addresses corresponding to each screen line. The other way is a bit more interesting and involves distilling the appropriate line, column and row bits that make up the address from the X and Y coords. Lets examine this way first.

Taking the Y coord first: This will be in the range 0 – 191, with 0 corresponding to the top of the screen. The lowest three bits indicate which line (within a character row) 0 – 7 we are dealing with. This is the same as the high byte of the screen address. The top two bits refer to which third of the screen we are dealing with:

			   Y Coord

		T   T   -   -   -   L   L   L

Let us assume that the B & C registers contain our X & Y coords. First we need to isolate the lowest three bits of the Y coord (C reg.), as follows:

	ld a,c
	and %00000111		;% indicates that the number following it is in binary format

We will use the HL register pair for the screen address. So next we transfer these three bits to the high byte (H reg.)

	ld h,a

In order to get the top two bits into the correct position we must shift them right three times, as follows:

	ld a,c
	rra			;rotate right accumulator
	rra
	rra

This shifts all the bits to the right three times, with the highest bit being reset after each shift. All that’s left to do at this stage is to clear the bits we don’t need, as follows:

	and %00011000

This must now be ORd with the H register.

	or h

Then the top three bits, which remain constant, are set as appropriate:

	or %01000000
	ld h,a			;load the result back into the H reg.

So now we have the high byte of the screen address.

The low byte of the address is composed of the following bits of both the X and Y coords:

   	  	 X Coord              |               Y Coord

  Cc Cc Cc Cc Cc   -   -   -         -   -   Cr Cr Cr   -   -   -

		Low Byte

  Cr Cr Cr Cc Cc Cc Cc Cc

The character column bits need to be shifted to the right three times.

	ld a,b			;B reg. holds the X coord
	rra
	rra
	rra
	and %00011111		; ensure the top 3 bits are clear
	ld l,a

Then the character row bits must be shifted left twice so that they correspond to the highest three bits of the low byte of the address (see earlier).

	ld a,c			;C reg. holds the Y coord
	rla			;rotate left accumulator
	rla
	and %11100000		;isolate the character row bits
	or l			;OR the result with the low byte
	ld l,a			;and place in L register

Right, that’s the difficult part done. We now have the screen address in HL. All that is left is to get the pixel position (0 – 7 from left to right) from the X coord (lowest three bits).

	ld a,b
	and %00000111

So the complete routine is as follows:

On Entry: B reg = X coord,  C reg = Y coord
On Exit: HL = screen address, A = pixel position

; Calculate the high byte of the screen addressand store in H reg.

	ld a,c
	and %00000111
	ld h,a
	ld a,c
	rra
	rra
	rra
	and %00011000
	or h
	or %01000000
	ld h,a

; Calculate the low byte of the screen address and store in L reg.

	ld a,b
	rra
	rra
	rra
	and %00011111
	ld l,a
	ld a,c
	rla
	rla
	and %11100000
	or l
	ld l,a

; Calculate pixel postion and store in A reg.

	ld a,b
	and %00000111

Using a Look-up Table

The other method of calculating the screen address is to use a table of pre-calculated addresses, and then use the Y coord to pick out the right one. This is a good deal quicker than the above method, albeit at the cost of 384 bytes of memory for the table. The look-up table contains the address of each line in the display file, 192 lines.

Routine to generate a Screen Address Table:

scradtab	equ 64000

gentab		ld de,16384
		ld hl,scradtab
		ld b,192

lineloop	ld (hl),e
		inc l
		ld (hl),d
		inc hl

		inc d
		ld a,d
		and 7
		jr nz,nextline
		ld a,e
		add a,32
		ld e,a
		jr c,nextline
		ld a,d
		sub 8
		ld d,a

nextline	djnz lineloop

		ret

Assuming again that on entry the B and C registers contain our X & Y coords.

	ld de,scradtab		;address of look-up table
	ld l,c			;Y coord in L register
	ld h,0
	add hl,hl		;multiply by two, as each address is 2 bytes
	add hl,de		;add to the start address of the table. HL is now at the
				;appropriate point in the table
	ld a,(hl)		;get low byte of screen address; store in A temporarily
				;so as not to corrupt HL
	inc l
	ld h,(hl)		;get high byte
	ld l,a			;HL now contains the address of the start of the line

	ld a,b			;calculate character column 0 - 31 from X coord
	and %11111000		;isolate appropriate bits
	rrca			;rotate right circular accumulator (faster than srl a)
	rrca
	rrca			;shifting three times is the same as dividing by eight
	add a,l			;add to low byte of address
	ld l,a			;put result back in L reg.

On exit HL will contain the screen address.

An even quicker way is to arrange the lookup table so that it is aligned with a 256 byte page boundary. This method separates the low byte and the high byte of the screen addresses, so in effect there are two tables one containing the low bytes of the address of each screen line and one containing the high bytes. Each must be aligned with a page boundary, ie. the start address of the table must be cleanly divisible by 256.

The above routine for generating the screen address table needs the following changes made to it:

lineloop	ld (hl),e
		inc h
		ld (hl),d
		dec h
		inc l

The table would be filled as follows:

	64000 - 64191		0....... 32....... 64....... 96....... 128.......		Low bytes of screen addresses

	64256 - 64447		64,65,66....71, 64,65,66....71, 64,65,66....71....	High bytes of screen addresses

Finding the start address of a particular line is then done as follows:

	ld h,scradtab/256	; high byte of start of table
	ld l,c			; C reg. contains the Y coord
	ld a,(hl)		; already we have found the low byte, so store in A temporarily
	inc h			; increasing high byte moves forward 256 bytes, and to the
	ld h,(hl)		; corresponding high byte of the screen address		
	ld l,a			; HL now contains the screen address for the start of the line

All that is then needed is to add on the column position, which is calculated in the same way as the previous routine.

Basic considerations

The main factors which need consideration when writing a sprite routine are:

1. Whether or not to use masked sprites
2. Whether or not to use pre-shifted sprites
3. What sizes of sprite to use and whether to use a different routine tailored for each size or a generic
routine which will produce various sizes of sprite

Writing a generic sprite routine which can handle many sizes and types of sprite, is generally shunned by most programmers, because the complexity of the routine has a negative impact on performance. It is perferable to write several routines tailored to specific sizes of sprite.

As per usual there is a trade-off between memory and speed, with the fastest routines using the most memory.

Masked sprites on average will take about 30% more time to process.

Pre-shifted sprites, which are sprites stored in 2, 4 or 8 different pixel positions, are by far the fastest type, but consume lots of memory.

An alternative to using pre-shifted sprites favored by some programmers, is to have a look-up table containing each bit pattern from 0 – 255 shifted into eight positions. In practice this takes up 4K of memory but this is usually less than is required when pre-shifting a large number of sprites. More about this later.

A Worked Example

It’s now time to take a look at how to code a basic sprite routine. The example below is for an 8 x 8 non-masked sprite. The sprite data (a small arrow pointer) is located at USR “A” – 65368. The routine shifts the data in real time. The sprite is XORed with the contents of the screen. You could also OR the sprite with the screen contents (see below for explanation of OR, XOR etc.) Real time rotation of sprite data is easiest acheived with small sprites (16 pixels or less wide), as the data can be stored in registers while being rotated.

The steps in the algorithm to draw the sprite could be stated as follows:

1. Retrieve X and Y coords.
2. Calculate Screen Address based on X,Y coords, using a look-up table
3. Calculate Bit Position from X coord (X coord AND 7 = bit position)
4. Retrieve a line of the sprite graphics data
5. Check if Bit Position is zero
6. If so there is no need to shift the sprite data, so skip the code which shifts the data (jump to step 8 )
7. Shift the sprite data according to its bit position
8. Put the line of sprite data on the screen
9. Adjust screen address for next line
10. Perform steps 4-9 until all lines have been drawn

		ORG 50000

SPRITE	DI			;Disable Interupts. Not strictly necessary in this
					;example as we are not redirecting the Stack.

		LD BC,(XPOS)		; First off, get the X & Y coords
					; and place them in B & C registers

		LD H,SCRADTAB/256	; this next section calculates the
		LD L,B			; screen address using a lookup table
		LD A,(HL)		; as explained earlier in the tutorial
		INC H
		LD H,(HL)
		LD L,A
		LD A,C
		AND 248
		RRCA
		RRCA
		RRCA
		ADD A,L
		LD L,A
		LD (SCRADD),HL	; store screen address for later
					; as HL is needed again

		LD A,C			; find which pixel position the sprite
		AND 7			; will be at and store it. We need this to
		LD (BITPOS),A		; know how many times to shift the sprite data

		LD HL,SPRGFX	; start address for the sprite graphic

		LD C,8			; The sprite is 8 lines tall

LINELOOP	LD E,(HL)		; load the E reg with the first line of
		INC L			; sprite data, and move forward

		PUSH HL		; preserve this address for later

		LD A,(BITPOS)		; Retrieve the pixel position
		OR A			; Quick way of testing if A is zero - Note: A is unaffected
		JR Z,SKIPROTATE	; If zero then no shifting is needed

		LD B,A			; loop counter for number of times to
					; rotate (shift) sprite data
		XOR A			; Quick way setting A to zero and
					; clearing the Carry Flag. The Carry Flag must be reset
					; as the rotate instructions will shift its contents
					; into the sprite data.

ROTATELOOP	RR E			; An extra register is needed for the
		RRA			; shifted sprite data. After the RR E is executed
					; rightmost bit of sprite data is shifted out to the Carry Flag.
					; It is then shifted into the A reg by RRA. So no data is lost.
		DJNZ ROTATELOOP	; Loop back until shifting is complete

SKIPROTATE	LD D,A			; Store in D register, as A will be
					; needed for another purpose. E & D regs now contain the
					; shifted sprite data.

		LD HL,(SCRADD)	; Get back the screen address

		LD A,(HL)		; Actually put a line of the sprite on the screen
		XOR E			
		LD (HL),A
		INC L
		LD A,(HL)
		XOR D
		LD (HL),A

		DEC L			; move back and down one line
		INC H

		LD A,H			; It is not necessary to recalculate
		AND 7			; the screen address for each line of
		JR NZ,A1		; the sprite. All that is needed is to
		LD A,L			; check if a char. or segment boundary
		ADD A,32		; has been crossed and adjust address
		LD L,A			; accordingly.
		JR C,A1
		LD A,H
		SUB 8
		LD H,A

A1		LD (SCRADD),HL	; store it again

		POP HL		; retrieve the address of the next line
					; of sprite data
		DEC C		
		JP NZ,LINELOOP	; loop back until all lines are drawn.

		EI			; Enable Interupts again
		RET

XPOS		DEFB 0
YPOS		DEFB 0
SCRADD	DEFW 0
BITPOS	DEFB 0

SCRADTAB	EQU 64000

On Exit : A, BC, DE, HL corrupt

As can be seen from the above routine it is not necessary to re-calculate the screen address for every line of a sprite. A couple of checks can be made to test if the next line to be drawn is in a new character row or screen third and adjust the address accordingly:

	LD A,H				;HL contains screen address
	AND 7
	JR NZ, REST_OF_PROGRAM
	LD A,L
	ADD A,32
	LD L,A
	JR C, REST_OF PROGRAM
	LD A,H
	SUB 8
	LD H,A

Rest Of Program continues here...

Routine to generate a table of screen addresses

GENTAB	LD DE,16384
		LD HL,SCRADTAB
		LD B,192

LINELOOP	LD (HL),E
		INC H
		LD (HL),D
		DEC H
		INC L

		INC D
		LD A,D
		AND 7
		JR NZ,NEXTLINE
		LD A,E
		ADD A,32
		LD E,A
		JR C,NEXTLINE
		LD A,D
		SUB 8
		LD D,A

NEXTLINE	DJNZ LINELOOP

		RET

Sprite Graphics Data

		ORG 65368

SPRGFX	DEFB %00000000
		DEFB %01111000
		DEFB %01110000
		DEFB %01111000
		DEFB %01011100
		DEFB %00001110
		DEFB %00000100
		DEFB %00000000

OR, XOR, AND

00111100  OR 	00011000  = 00111100

00111100  XOR	00011000  = 00100100

00111100  AND	00011000  = 00011000

Masked Sprites

Masking is a technique used to blank out the area behind a sprite before drawing the sprite to the display. It’s slower than planar (non-masked) sprites, and uses twice as much memory but the final result is more pleasing to the eye. If your sprites are going to be moving over a patterned background its the best technique to use.

The way to draw a masked sprite is to AND the mask with background and then OR the actual sprite with the result. When you AND two bitmaps, bits in the BACKGROUND are reset by zeros in the MASK, and left as they are by ones. That’s probably not very easy to grasp so look at the following examples:

background	            mask

  00111100     and      11111111 	=  00111100	; each bit in the data byte remains  the same after the mask is applied

  00111100     and      00000000 	=  00000000	; all the bits in the data byte are reset by the mask

  11111111     and      00111100 	=  00111100	; the middle four bits remain unchanged and the rest are reset

The previous example showed how to code a very simple sprite routine. Lets now look at a more complex example. This routine will draw a 16 x 16 Masked Sprite, storing the background underneath in a buffer.

This is probably the largest size that can be conveniently shifted in real-time. Any larger and you will want to use a different technique.

The following routine uses a couple of non-standard programming techniques. These tricks help to speed up execution but make the code a bit more difficult to follow. One technique involves using the Stack to retrieve the sprite data, the other is self-modifying code, where the program pokes values into instructions that will be executed later on in the routine.

		ORG 50000,50000

SPRITE	DI				; Disable Interupts.
		LD (SPTEMP),SP		; Store Stack Pointer as it will be redirected
						; by the routine
		LD BC,(XPOS)
		LD A,C
		AND 7
		LD (BITPOS),A

		LD H,SCRADTAB/256		; Calculate screen address as before
		LD L,B
		LD A,(HL)
		INC H
		LD H,(HL)
		LD L,A
		LD A,C
		AND 248
		RRCA
		RRCA
		RRCA
		ADD A,L
		LD L,A

		LD (SCRADD),HL

		LD IX,BKGRNDBUF		; IX is used to point to a temporary buffer where the contents
						; of the screen under the sprite are stored.

		LD SP,SPRGFX		; start address for the sprite graphic

		LD A,16			; The sprite is 16 lines tall
		LD (LINECOUNT),A

LINELOOP	POP DE			; Using the stack to retrieve a line of sprite graphics
		POP HL			; E and D will contain the MASK as it is stored first
						; L and H hold the actual sprite image
		LD A,255			; When shifting, the A reg will be the extra register that the MASK
						; is shifted into. It is loaded with 255 as that means all the bits are
						; initially transparent.
		SCF				; The carry flag is set by this instruction so that bits shifted into the
						; left side of the mask are set (transparent)
		EX AF,AF'			; We also need an extra register for the sprite image, so we will use
						; the alternate or shadow A register.

		LD A,(BITPOS)			; Retrieve the pixel position
		OR A				; Quick way of testing if A is zero
		JR Z,SKIPROTATE		; If zero then no shifting is needed

		LD B,A				; loop counter for number of times to shift

		XOR A				; clear carry flag and set A reg. to zero,
						; as the rotate instructions will shift its contents (carry flag's)
						; into the sprite image data. This is the opposite to the MASK.

ROTATELOOP	EX AF,AF'			; Shift the Sprite Mask data
		RR E				
		RR D
		RRA

		EX AF,AF'			; Shift the Sprite Image data
		RR L
		RR H
		RRA

		DJNZ ROTATELOOP		; Loop back until shifting is complete

SKIPROTATE	LD (DATA+1),A			; This is the piece of self-modifying code mentioned earlier.
		EX AF,AF'			; As the A reg will be needed and there are no other registers
		LD (MASK+1),A			; free, we will poke their data (the rightmost bytes of the sprite mask
						; and image) into instructions further on in the routine.

		LD BC,(SCRADD)		; Get back the screen address

		LD A,(BC)			; Actually put a line of sprite data on the screen
		LD (IX),A			; Store what is under the sprite so that it can be erased
						; without corrupting the background
		AND E				; This instruction applies the MASK
		OR L				; The sprite image is then ORed with the background
		LD (BC),A			; and the result copied back to the screen
		INC C
		INC IXL

		LD A,(BC)			; Put the second byte on the screen
		LD (IX),A			; As we are dealing with a 16 x 16 sprite
		AND D				; each line will be three bytes wide when shifted
		OR H
		LD (BC),A
		INC C
		INC IXL

		LD A,(BC)			; Put the third byte on the screen
		LD (IX),A
MASK		AND 255			; These are the two instructions that were modified
DATA		OR 0				; earlier. The data stored in the A reg. and its shadow
						; were poked into the AND & OR instructions.
		LD (BC),A
		INC IXL

		DEC C
		DEC C				; move back and down one line
		INC B

		LD A,B				; It is not necessary to recalculate
		AND 7				; the screen address for each line of
		JR NZ,A1			; the sprite. All that is needed is to
		LD A,C				; check if a char. or segment (screen third) boundary
		ADD A,32			; has been crossed and adjust the address
		LD C,A				; accordingly.
		JR C,A1
		LD A,B
		SUB 8
		LD B,A

A1		LD (SCRADD),BC		; store it again

		LD HL,LINECOUNT
		DEC (HL)
		JP NZ,LINELOOP		; loop back until all lines are drawn.

		LD SP,(SPTEMP)		; Restore the Stack Pointer
		EI				; Enable Interupts again
		RET

In order to clear the sprite we need to copy back to the screen the Buffer holding the contents
of the screen under the sprite. The following routine will do this.

CLEARSPRITE	DI

		LD BC,(OLDXPOS)

		LD H,SCRADTAB/256
		LD L,B
		LD A,(HL)
		INC H
		LD H,(HL)
		LD L,A
		LD A,C
		AND 248
		RRCA
		RRCA
		RRCA
		ADD A,L
		LD L,A

		LD DE, BKGRNDBUF		

		LD C,16	

CLOOP		LD A,(DE)
		LD (HL),A
		INC L
		INC E

		LD A,(DE)
		LD (HL),A
		INC L
		INC E

		LD A,(DE)
		LD (HL),A
		INC E

		DEC L
		DEC L
		INC H

		LD A,H
		AND 7
		JR NZ,A2
		LD A,L
		ADD A,32
		LD L,A
		JR C,A2
		LD A,H
		SUB 8
		LD H,A

A2		DEC C
		JP NZ,CLOOP

		EI
		RET

XPOS		DEFB 0
YPOS		DEFB 0
OLDXPOS	DEFB 0
OLDYPOS	DEFB 0
LINECOUNT	DEFB 0
BITPOS	DEFB 0
SCRADD	DEFW 0
SPTEMP	DEFW 0

SCRADTAB	EQU 64000
BKGRNDBUF	EQU 64512
; ASM source file created by SevenuP v1.12
; SevenuP (C) Copyright 2002-2004 by Jaime Tejedor G¢mez, aka Metalbrain

;GRAPHIC DATA:
;Pixel Size:      ( 16,  16)
;Char Size:       (  2,   2)
;Sort Priorities: X char, Char line, Y char, Mask
;Attributes:      No attributes
;Mask:            Yes, mask before

SPRGFX	DEFB	248, 31,  0,  0
		DEFB	224,  7,  3,192
		DEFB	192,  3, 15,240
		DEFB	128,  1, 25,248
		DEFB	128,  1, 51,252
		DEFB	  0,  0, 39,252
		DEFB	  0,  0,111,250
		DEFB	  0,  0,127,252

		DEFB	  0,  0,127,250
		DEFB	  0,  0,127,252
		DEFB	  0,  0, 63,248
		DEFB	128,  1, 63,244
		DEFB	128,  1, 31,232
		DEFB	192,  3, 13, 80
		DEFB	224,  7,  2,128
		DEFB	248, 31,  0,  0

Addendum: There appears to be a minor bug in the original code by Derek Smith. Commentator uglifruit posted a fix in the comments section, which has now been integrated into the above code.

Introduction to Z88DK – Part 1: Installation & Compilation

This article was originally published on www.speccy.org/wiki, in Spanish, and is reproduced with permission from Santiago Romero. Translation to English courtesy Google and some re-write from my end. Please do let me know of any errors in the text.


In this series of articles we’ll take a look at Z88DK and learn to make programs with it for our Spectrum.

Basically it is a C cross compiler, which allows us to make our programs in this language on our desktop computer (PC running Windows or Linux, SPARC) and create a file that can be run in an emulator. Using the right tools it can be moved to tape to run on our Spectrum. While these articles will focus on the target machine of this publication (the Sinclair ZX Spectrum), the z88dk allows develop applications for a wide range of platforms including Sam Coupe, MSX1, Jupiter ACE, ZX81, and a large number of computers based on the Z80 processor.

The package includes a set of libraries that allow us to easily add functionality to the programs, including a versatile graphics library, which will enable us to even write a game. The C compiler included with z88dk is only a small compiler accepts many features of ANSI C and perform basic optimizations. Finally, we clarify that the purpose of these articles is not to explain the syntax or the operation of the C language and, the reader is assumed to have a basic knowledge about it.

In this issue we focus on how to install the package for different platforms, as well as learn how to compile and run the examples.

Installation

To download the latest stable version we will go to the download page of z88dk, where we can get the Linux version. Binaries for Windows can be obtained from Dennis Grönign’s page (the link can also be found on the same page z88dk).

Installing on Linux

The first step after downloading the file z88dk-src-xytar.gz (where xy is the version of the package at the time of writing – the latest stable version was 1.5), is unpacking in the directory where you want to have it installed:

tar-xvzf z88dk-src-xytar.gz

Once this is done we must compile the various binaries and libraries that make z88dk:

cd z88dk
. / build.sh

To use z88dk we should declare a number of environment variables. We can do this by creating a file setpath.sh (which we will execute permissions with chmod a + x setpath.sh) with the following contents:

Export Z88DK = "(z88dk) / z88dk"
Export PATH = "$ PATH: $ Z88DK/bin"
Export Z80_OZFILES = "$ Z88DK/lib /"
Export ZCCCFG = "$ Z88DK/lib/config /"

where (z88dk) is the directory where you have unpacked the tar.gz. Every time we use z88dk, must execute this file by using the following command:

setpath.sh source

With this we define the environment variables for the current shell.

One tool that we use in creating our programs is bin2tap. This program transforms the binary produced by the compiler to TAP files that can be run in an emulator. Fortunately, this program comes bundled with z88dk. Unfortunately, this is included without compiling, and also not compiled to run the. / Build.sh previously indicated.

To build it, we move to the $ Z88DK/support/zx /, and execute:

cc-o bin2tap bin2tap.c

That way we will create the executable program. To use it comfortably, we copy the bin directory:

mv $ Z88DK/support/zx/bin2tap $ Z88DK/bin /

Compiling and Running on Linux


Once the package is installed, and prepared our system, we explain how we can compile our programs and run them on any emulator. We shall do this in the examples located in the $ Z88DK/examples/spectrum.

In the above directory are a number of files containing C programs that use z88dk librarie. In the README file have a description for each one of them. Let’s compile and test the gfx.c, which incorporates a series of graphic effects that look spectacular. To do so, according to the README file, we can run:

CCZ + zx-or gfx.bin gfx.c-lndos

Below is a brief description of the syntax:
• Just after the ZCC command specify the target machine, ie the machine which will produce the binary. In our case we want to create a binary for the ZX Spectrum, so we use the modifier + zx (the switches for each of the machines supported can be found at $ Z88DK/doc/compile.txt).

• With the-o parameter specified the resulting binary file name (in our case, gfx.bin).

• Then name the source file.

• Finally, we con-lndos to bind to the bookstore NDOS (in next installments we go into detail about the most useful libraries and functionalities offered). The libraries are in $ Z88DK/libsrc /.

As you can see, the syntax is very similar to Linux gcc compiler, so if we are used to programming in C on this platform, we should not have any problem. The resulting file, gfx.bin, is a binary file (which a text editor can’t understand). Keep in mind that CCZ is not the compiler itself, but it is a frontend that is responsible for implementing the compiler and assembler / linker (all these applications can be found in the $ Z88DK/bin /).

This binary may not yet be executed in any emulator, we must first convert it to a suitable format. To do this we can use the application bin2tap, we compiled in the previous paragraph:

bin2tap gfx.bin gfx.tap

And now we can test the program in our favorite emulator!

Installation on Windows (thanks to the collaboration of Miguel)

This installation has been completed and tested on a PC with Windows 98 Second Edition. Attempt on a Windows XP system files and executables in the bin directory gave the error “xxxxxxx.exe is not a valid win32 file” (This may be due more to the PC on which the author has installed the operating system incompatibility problems between himself and z88dk Windows XP).
The first step we must carry out is to download the file z88dk-win32-xyzip (where xy indicates the version of z88dk) we can find on the project website. Once downloaded, unzip the zip file in the directory c: z88dk, which will be the default directory that we use in this article. We can switch to one that suits us more by changing the path in all explanations given below.

The following adds the path of the executables and libraries to the autoexec.bat file. For this we have two options:

• 1. Add the path manually by editing the autoexec.bat file. To do this simply edit the autoexec.bat file located in c: / with the Windows Notepad, or from a MS-DOS “edit autoexec.bat” and add the following lines:

SET Z80_OZFILES = C:  z88dk  Lib 
SET ZCCCFG = C:  z88dk  Lib  Config 
SET PATH =% PATH% C:  z88dk  Bin

• 2. When you uncompress the zip files containing z88dk, there is a file called SETZ88-W95.inf, we place the mouse pointer over it, right-click and select install, which automatically adds the file path autoexec.bat.
After one of the above, reboot the PC to update the paths.
Compile and run on Windows

When writing code with the z88dk, the code is written in C language in plain text files with your preferred editor (can be even Windows Notepad). These files are usually saved with a .c extension.
To show briefly how we create a file to run on a ZX Spectrum emulator, or upload to a real computer, we’ll use one of the examples that come with z88dk.

• The first thing we do is open a MS-DOS command window and move to the folder where the examples are present:
To put ourselves in the root directory of C:

CD c:
CD z88dk  examples  spectrum

With that we are in the examples folder.
Now we have to compile the program. To do this we will use the executable ZCC.EXE, which is located in the BIN folder, but we do not have to enter it at all, because we added its path earlier. Compile by typing:

CCZ + zx-or gfx.bin gfx.c-lndos

When you run it, it creates a file.bin in which we will have our program. We need to convert it to a format that the emulator can understand, or move to tape and load on a real ZX Spectrum.

• To convert the file from .bin to .tap, we have a program called BIN2TAP.EXE that is included in z88dk and located in the support zx folder.

Usage is very simple: we have to be in the same folder as the. BIN2TAP.exe and provide it the .bin source and specify the target .tap. Execute:

BIN2TAP gfx.bin gfx.tap

And now we have our program ready to load into our favorite emulator.

Tutorial: ZX Spectrum Machine Code Game in 30 Minutes!

This tutorial by Jon Kingsman (bigjon) originally appeared in a thread on WoSF. Reproduced with permission.

Roadrace game by bigjon, incorporating suggestions from Dr Beep, skoolkid and Matt B

Hi folks, I’m a machine code novice who coded a very small roadrace game to help me learn.

I reckon you can learn the basics of machine code in half an hour by coding this game step by step.

This tutorial assumes you have a working knowledge of ZX Spectrum basic, and the ZX Spin emulator.

Make yourself a large cup of tea – by the time you’ve drunk it, you be able to program in machine code!

CHAPTER 1 – Create a machine code function that returns the score to BASIC
Machine code programs are a series of bytes in the Spectrum’s memory.
In this chapter we will

  • – Use the Spin assembler to write a few bytes into the memory generating a score for our game.
  • – Write a BASIC program to run the machine code and print the score we have obtained from it.

Open ZX Spin.  Select Tools -> Z80 Assembler.
To run our roadrace game, we need to execute the following steps:

MAIN 	;label for main section of program as opposed to graphics data etc	

	;arrange to put our machine code at free, fast-running (over 32768) and memorable address in RAM
	org 33000  

	;initialise score
	;initialise road, car

PRINCIPALLOOP ;label for the loop in the game that will execute over and over
	;read keyboard
	;set new carposition
	;crash? if so, go to GAMEOVER.
	;print car
	;scroll road
	;random road left or right
	;jump back to PRINCIPALLOOP

GAMEOVER ;label for the cleaning up that needs to be done before returning to BASIC
	;return score to BASIC

Copy and paste the paragraph above into the Spin Assembler.It will appear as 15 lines of mainly grey text.

The text is grey because text after a ; is a comment. The assembler ignores it but it’s there for our benefit.

You can TAB comments over towards the right-hand side of the assembler page to make your code more readable.

The labels are in pink.The assembler won’t put anything in RAM for them but will use them as entry points to jump to.

In the assembler, do File -> Save as and type something like mc30mintut.asm into the save box.
We’ll do the first and last of these steps in this chapter, starting with the last one.

The assembly language instruction for ‘return to calling program’ (in our case a BASIC routine) is ‘ret’.

Click on the end of line 15, press enter to create line 16 and type ret
The word ret‘ appears in blue. This is Spin’s colour code for an instruction.

When the Spin assembler gets to the instruction ret it writes the byte 201 into the memory at an address we choose.

The computer knows that the first byte it meets will be an instruction byte.

It does something different for each byte from 0 to 255. There’s a list in Appendix A of the Spectrum Manual.

Some instruction bytes, like 201 for ret, are complete as is – no further info is needed to complete the action.

Some instruction bytes need one or two bytes of data afterwards for the computer to know what to do.

Some other instruction bytes need a further instruction byte to clarify the action required.

Now we’ll initialise the score, ready for the mc program to report it back to BASIC at the end of the game.

The computer does most of its work using 7 temporary byte-sized addresses called registers.

The first register deals with single bytes only (numbers 0 to 255), the other six are in pairs to deal with 0 to 65535.

The first (single-byte) register is called the A register, sometimes also referred to as the accumulator.

The other three register pairs are called BC, DE, and HL (H is for High byte, L is for Low byte)

Any machine code function called from basic will return the value from 0 to 65535 in the BC register.

We will write the value 0 into the BC register, ready to increase it by 1 each time the game goes round its principal loop.

At the beginning of line 4, type ld bc,0. ld is the instruction for load a value into a register or register pair.

The instruction byte for ld is different for each register or register pair that is loaded.

The instruction byte for ld bc is 1. The computer then expects 2 data bytes to give it a number from 0 to 65535.

In our case the two data bytes will be 0,0.  So the assembler will write 1,0,0,201 at address 33000 in RAM.

We’ll assemble this code now. Do File -> Save, then File -> Assemble. Type 33000 into the Start Address box and click OK.

At the bottom window of the assembler you should see a report that says “No errors in 16 lines. 4 bytes generated”.

You can see the four bytes are now at memory address 33000 by clicking in the main Spin display window on Tools -> Debugger.

To run these four bytes of machine code, enter this one-line program in the main Spin display window:
10 PRINT AT 0,0; “Your score was “; USR 33000

Now RUN the program. Did you get “Your score was 0”? Congratulations – you have coded your first machine code program!

Do File -> Save in the main Spin display window and save as something like mc30mintut.sna. Here ends Chapter 1!

CHAPTER 2 – Display material on the screen.
There are two areas of the Spectrum’s memory which have a direct effect on the screen display.
The complicated way is the display file, from addresses 16384 to 22527, which stores a dash of 8 pixels per byte.

Try POKE-ing 255 into bytes within this range to see the funny order in which this memory area is mapped onto the screen.

The simple way is the attribute file, from 22528 to 23296, which affects an 8×8 pixel block per byte, in logical order.

In this chapter we will

  • Draw our ‘car’ by changing the paper colour of one character square to blue.
  • Draw our ‘road’ by using a loop to create two vertical stripes of black paper colour down the screen.

In the spin assembler line 5, delete the word ‘road’ in the comments.

At the beginning of line 5, type ld hl,23278. This points HL to the middle of the bottom row in the display file.

Insert line 6, ld a,8. This puts a blue PAPER colour into the A register. Why 8? See the BASIC manual chapter 16.

Insert line 7, ld (hl),a. The brackets round hl mean the load will be to the address in RAM that hl is pointing to.

Insert line 8, ld (32900),hl ;save car posn. We’ll store the attribute file address of the ‘car’ in some free bytes in RAM.

Now for the road. Insert line 4, ld hl,22537 ;initialise road. This points to a third of the way along the top line.

To save the road position, which we’ll need frequently, we’ll let the computer choose where to store it, on its ‘stack’.

Chapter 24 of the manual has a diagram showing where the machine stack is in the RAM.
To write to the stack we use push. To write from the stack we use pop. What goes on the stack first will come off last.

Insert line 5, push hl ;save road posn. Insert line 21, pop hl ;empty stack

To print a black road we need 0 in the accumulator (ch16 of the BASIC manual).

We could do ld a, 0 but this takes 2 bytes whereas xor a takes only one. Insert line 6, xor a.
xor compares the chosen register to the A register and puts a 1 in the A register for each bit that is different.

We’ll print the top line of the road. Two double squares of black with a 9-square gap between them.

Insert line 7, then copy and paste the following code:

	ld (hl),a
	inc hl ;inc increases the register by one, dec decreases it by one.
	ld (hl),a
	ld de,9 ;for a 9-space gap in the road.
	add hl,de ;add adds the registers together, so hl points to the right hand side of the road.
	ld (hl),a
	inc hl
	ld (hl),a

To get hl to point to the left hand verge on the next line, we need to move 21 bytes further in the attribute file.

Insert line 15, ld de, 21 ;point to left verge on next line
Insert line 16, add hl,de

To fill the screen with the road we will use machine code’s equivalent of a FOR-NEXT loop, djnz.
djnz stands for Decrement then Jump if Not Zero. We load the b register with the number of times we want to loop.

Insert line 7, ld b,24 ;print road verge on 24 lines.
Insert line 8, fillscreen – this is the label for our loop to jump back to.
Insert line 19, djnz fillscreen.

Because our routine will continue from the loop when b=0, we no longer need to initialise b as well as c to 0 in the next line.

Change line 20 ld bc, 0 to ld c,b.  This is one byte shorter.

Assemble and save. If you want to see the blue ‘car’, you’ll need to add something like 20 PAUSE 0 to your basic program.

CHAPTER 3 – move the car, test for collision.
Time to start playing the game! First we need to erase the car ready to move it if the player wants to.

Insert line 26, then copy and paste the following code:

	ld hl,(32900) ;retrieve car posn
	ld a,56 ;erase car
	ld (hl),a

Before we read the keyboard we will lock the keyboard for most of the game, and unlock it only when we want to read the keys.

The instruction to lock the keyboard is di = ‘disable interrupts. Its opposite is ei = ‘enable interrupts’.

Replace line 3 with di. Insert line 29, ei. Insert line 31, di. Insert line 41, ei

To read the keys we use the IN ports – see ch23 of the BASIC manual – to read the left and right half of the bottom row.

We load bc with the port number and use the instruction cp (compare) to see if the number has dropped to show a keypress.

Delete line30 and replace with the following code:

	ld bc,65278 ;read keyboard caps to v
	in a,(c)
	cp 191
	jr nz, moveright
	inc l
moveright
	ld bc,32766 ;read keyboard space to b
	in a,(c)
	cp 191
	jr nz, dontmove
	dec l
dontmove

jr nz stands for jump relative if not zero. It skips over the instruction to increment / decrement the car position.

Replace line 43 with the following to see if we bump into the oncoming road 32 bytes (1 screen) down the attribute file:

	ld (32900),hl ;store car posn
	ld de, 32 ;new carposn
	xor a  ;set carry flag to 0
	sbc hl,de
	ld a,(hl) ;crash?
	or a
	jr z,gameover
	ld a,8  ;print car
	ld (hl),a

We’d like to sub hl,de but there’s no such instruction so we use sbc, subtract with carry, and set the carry flag to zero.

or compares the register to the a register bit by bit and leaves a 1 in the a register for each bit that is 1 in either.

If all the digits are zero, then the zero flag will be set, so we can use or a to test for a black paper colour.

Delete line 53. Delete line 53 again!

To clean up the score at GAMEOVER insert line21, push bc; save score. Replace line 57 with pop bc;retrieve score

To cycle round the game before GAMEOVER change line 55 to jp PRINCIPALLOOP.

Assemble, save, and run. You’ll need to deliberately crash to get out!

CHAPTER 4 – scroll and move the road, keep score, adjust speed.
To scroll the road down the screen we copy the screen attribute bytes to the line beneath 736 times.

We use the instruction lddr, which stand for LoaD ((hl) to (de)),Decrement (hl and de) and Repeat (until bc is zero).

Replace line 53 with the following:

	ld hl,23263 ;scroll road
	ld de,23295
	ld bc,736
	lddr
	pop bc  ;retrieve score

To add 1 to the score and save it ready for GAMEOVER, insert the following into line 59:

inc bc ;add 1 to score
push bc ;save score

To move the road randomly left or right on the top line we use the following algorithm –
Choose a location in ROM where the are 256 random looking bytes and add the low byte of the score in bc to it.

If it is odd, lower the road position in hl by one. If it is even, increase by one.

(To test the last bit for odd and even we use ‘and 1’ which “masks” the last bit and sets the zero flag if it is 0).

Check to see if the road has reached the edge of the screen and bump it away if it has.
Print the new road top line like we did in chapter 2.

Replace line 58 with the following hefty chunk of code:

	pop hl  ;retrieve road posn
	push hl  ;save road posn
	ld a,56  ;delete old road
	ld (hl),a
	inc hl
	ld (hl),a
	ld de,9
	add hl,de
	ld (hl),a
	inc hl
	ld (hl),a
	;random road left or right
	ld hl,14000 ;source of random bytes in ROM
	ld d,0
	ld e,c
	add hl, de
	ld a,(hl)
	pop hl  ;retrieve road posn
	dec hl  ;move road posn 1 left
	and 1
	jr z, roadleft
	inc hl
	inc hl
roadleft
	ld a,l  ;check left
	cp 255
	jr nz, checkright
	inc hl
	inc hl
checkright
	ld a,l
	cp 21
	jr nz, newroadposn
	dec hl
	dec hl
newroadposn
	push hl  ;save road posn
	xor a  ;print new road
	ld (hl),a
	inc hl
	ld (hl),a
	ld de,9
	add hl,de
	ld (hl),a
	inc hl
	ld (hl),a

The last thing we need to do to have a playable game is slow down our blindingly fast machine code.

Insert the following into line 106 (as extension material, you could adjust the figure in bc with a keypress to ‘brake’):

;wait routine
	ld bc,$1fff ;max waiting time
wait
	dec bc
	ld a,b
	or c
	jr nz, wait

Save, assemble, and run – and that’s it! Has your tea gone cold yet?

A full listing follows, with my email address at the end for your comments and suggestions:

main
	org 33000
	di
	ld hl, 22537 ;initialise road
	push hl  ;save road posn
	xor a
	ld b,24
fillscreen
	ld (hl),a
	inc hl
	ld (hl),a
	ld de,9
	add hl,de
	ld (hl),a
	inc hl
	ld (hl),a
	ld de,21
	add hl,de
	djnz fillscreen
	ld c,b  ;initialise score
	push bc  ;save score
	ld hl,23278 ;initialise car
	ld a,8
	ld (hl),a
	ld (32900),hl ;save car posn
principalloop
	ld hl,(32900) ;retrieve car posn
	ld a,56  ;erase car
	ld (hl),a
	ei
	ld bc,65278 ;read keyboard caps to v
	in a,(c)
	cp 191
	jr nz, moveright
	inc l
moveright
	ld bc,32766 ;read keyboard space to b
	in a,(c)
	cp 191
	jr nz, dontmove
	dec l
dontmove
	di
	ld (32900),hl ;store car posn
	ld de, 32 ;new carposn
	xor a  ;set carry flag to 0
	sbc hl,de
	ld a,(hl) ;crash?
	or a
	jr z,gameover
	ld a,8  ;print car
	ld (hl),a
	ld hl,23263 ;scroll road
	ld de,23295
	ld bc,736
	lddr
	pop bc  ;retrieve score
	pop hl  ;retrieve road posn
	push hl  ;save road posn
	ld a,56  ;delete old road
	ld (hl),a
	inc hl
	ld (hl),a
	ld de,9
	add hl,de
	ld (hl),a
	inc hl
	ld (hl),a
	;random road left or right
	ld hl,14000 ;source of random bytes in ROM
	ld d,0
	ld e,c
	add hl, de
	ld a,(hl)
	pop hl  ;retrieve road posn
	dec hl  ;move road posn 1 left
	and 1
	jr z, roadleft
	inc hl
	inc hl
roadleft
	ld a,l  ;check left
	cp 255
	jr nz, checkright
	inc hl
	inc hl
checkright
	ld a,l
	cp 21
	jr nz, newroadposn
	dec hl
	dec hl
newroadposn
	push hl  ;save road posn
	xor a  ;print new road
	ld (hl),a
	inc hl
	ld (hl),a
	ld de,9
	add hl,de
	ld (hl),a
	inc hl
	ld (hl),a
	inc bc  ;add 1 to score
	push bc  ;save score
	;wait routine
	ld bc,$1fff ;max waiting time
wait
	dec bc
	ld a,b
	or c
	jr nz, wait
	jp principalloop
gameover
	pop bc  ;retrieve score
	pop hl  ;empty stack
	ei
	ret; game and tutorial written by Jon Kingsman ('bigjon', 'bj'). electronic mail gmail.com - atsign - jon.kingsman (reversed)

Beginners guide to BASIC – 3

Welcome to the third and final edition of this guide to BASIC!

Now that we’ve created the graphics for our game and have the key handling routines in place, we can now finally move on to the fun part of animating stuff and watch the screen come alive with millions of tiny coloured pixels (okay, maybe not millions but what the heck, who’s counting?)!

Firstly, if you remember the plot of our game, our intrepid hero of the game Krapz has to survive as long as he can by collecting little quanta particles until he gains enough power to jump out the warp into… yet another more difficult screen. He also has to avoid touching the “ST fluctuation trails” he leaves behind in his wake plus he must stay clear of the deadly boundaries of the warp. In order to pull this fancy (oh sure! – Ed) stuff off, we’ll break it down to a set of tasks.

Task #1: Set up the playing area. This involves drawing the boundaries of the warp and populating the playing field with randomly distributed set of quanta particles. Since every new screen that Krapz jumps into involves the above steps, it makes sense to package it as a sub-routine. Which is what we’ll do:

115 LET quanta=5*level: LET time=seedtime+(level*30): CLS

120 PRINT AT 1,0; INK 3;"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb";AT 20,0; INK 3;"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"

130 FOR f=1 TO 19

140 PRINT AT f,0; INK 3;"b";AT f,31; INK 3;"b"

150 NEXT f

160 LET x=10: LET y=10

250 FOR f=1 TO 5*level

260 LET p=INT (RND*17)+2

270 LET q=INT (RND*28)+2

280 IF ((p=y AND q=x) OR (ATTR (p,q)=5)) THEN GO TO 260: REM don't overwrite player or another quanta!

290 PRINT AT p,q; INK 5;"g"

300 NEXT f

999 RETURN

The first line looks a bit complex but is actually a rather simple way of increasing the difficulty level as Krapz progresses through the game. The first LET statement simply increases the number of quanta particle in a screen by a factor of 5 depending on which level (screen) Krapz is playing on. For the first screen (level = 1), there will be only 5 quanta particles to collect. On the 2nd screen (level = 2) there will be 10, and so on and so forth. The second LET statement controls how much time Krapz has to collect all the quanta particles. This is again a factor of the level Krapz is on – 30 times the level as a matter of fact, which compensates for the fact that there are more quantas on each screen as we progress. You’ll notice that a seedtime has been tacked on in the equation to ensure that the player has a basic minimum time to start with on each level.

After having cleared the screen, it’s time to draw the boundaries of the warp – in our case it’s simply a rectangular arena to delineate the playing field. First we draw the top and bottom edges, which is simply a matter of printing 32 characters of the UDG “b” (incorrectly reproduced as b  in the listing above) that we created earlier on. Line 120 prints the top edge and the bottom edge in lurid purple ink.

To draw the vertical left and right edges we employ a FOR-NEXT loop from line 130 to 150 that print UDG “b” 19 times vertically.

It’s now time to randomly populate the playing area with quanta particles. Line 160 first sets the initial co-ordinates of Krapz on each screen – he always starts on row 10, column 10 on every screen.

We then employ a FOR-NEXT loop to print the actual quanta. The number of particles is simply a factor of the level the player is on, and is in fact exactly the same as the variable quanta we calculated on line 115. In fact we can substitute the 5*level with quanta and the loop will work just as well. Line 260 and 270 calculate the row and column (p,q) to print the quanta on. INT (RND* 17) gives us a random number from 0 to 16. We add a factor of 2 to ensure we don’t print anything on row 0 and 1 – the top edge of the playing arena. Note that if got 16 and added 2 we get 18, which still within the bottom edge (column 20) of the arena. Similarl, we keep the quanta within the left and right edges of the playing arena in line 270.

Line 280 introduces a necessary check that ensures that we aren’t printing a quanta on top of another already existing quanta on screen (we do want all our quantas visible individually on the screen!). Plus we don’t want to print at the position Krapz is already in (10,10). So, what we are checking for is “whether the quanta co-ordinates are the same as Krapz’s co-ordinates OR whether the quanta particle is being printed on top of another quanta” state. This is achieved by the IF statement, where the two expressions are separated by an OR (as in the above statement) condition which returns true if any one of the expression is true (either printing on Krapz or printing on a quanta).

To check for whether the quanta co-ordinates are same as Krapz’s co-ordinates, we simply have to check if the column and row co-ordinates for both match up. If they are the same, then we are at the same location on screen. This is calculated using the expression in the IF statement p=y AND q=x. The AND condition returns true if and only if both the sub-expressions are true.

The next thing we have to check for is the case of a quanta overwrite condition. This is done by using the ATTR command, which given a x & y co-ordinate returns the colour attribute at that co-ordinate. The BASIC manual explains how to interpret this value but suffice to say that for our purpose if ATTR returns a value of 5 it means that a cyan coloured character (INK 5) is present at that position. As you will see on line 290, where we print our quanta particles, they are printed with INK 5. In essence, what we are saying is that if ATTR returns 5, we assume that we have a quanta particle there regardless of what actually might be there. In order to not trip up our assumptions we ensure that we do not print any other stuff in INK 5 anywhere the screen. In our program only the quanta particles can have the cyan colour when playing the game.Period.

If we are overwriting Krapz or another quanta, we simply go back to line 160 and re-calculate a new quanta position until we are satisfied we aren’t overwriting anything we shouldn’t be overwriting! If all is well, we proceed to actually print a quanta particle on the screen at the position we calculated (line 290). Once we’ve printed all the quanta particles required for a level we RETURN from the sub-routine via line 999.

There! We’re all set to actually do some gameplay and stuff! Lets move on to the task of moving Krapz around on the playing area. Remember we set up our control keys in Code Shed Guide 2, so it’s time to use those keys to move Krapz around.The control scheme we’ll follow is a simple one. Pressing one of the four direction keys changes Krapz’s direction instantly (inertia? What’s that?).  The key needn’t be held down if one wishes Krapz to continue in that direction though- his inertia (that word again!) will keep him going in that direction until a different direction key is pressed.

Have a look at this code which begins our main game loop (so called because we’ll be executing it repeatedly till something happens to break proceedings) proper:

1010 PRINT AT 21,0;"Lives: ";lives;TAB 20;"Score: ";score

1020 PRINT AT 0,0;"Time: ";time;"      "

1030 PRINT AT 0,20;"Level: ";level

2000 LET a$=INKEY$

2010 IF a$=k$(1) THEN LET dir=1: REM right

2020 IF a$=k$(2) THEN LET dir=2: REM left

2030 IF a$=k$(3) THEN LET dir=3: REM up

2040 IF a$=k$(4) THEN LET dir=4: REM down

2045 IF dir<>0 THEN PRINT AT y,x; INK 1;CHR$ (143)

2050 IF dir=1 THEN LET x=x+1

2060 IF dir=2 THEN LET x=x-1

2070 IF dir=3 THEN LET y=y-1

2080 IF dir=4 THEN LET y=y+1

Lines 1010 to 1030 give information regarding the status of the number of lives left, the current score, the time left and the current level being played. It’s our HUD if you will. Since it’s within the main game loop, it will be updated continously.

Lines 2000-2080 are responsible for our inertial control mechanism. The logic is quite simple. We sample the keyboard for any keypress. If it’s a direction key we change Krapz’s direction of movement. If no directional key is pressed, Krapz continues to move along in the same direction. Here’s how. Line 2000 reads in a key from the keyboard (null if no key is pressed). Lines 20101 to 2040 compare the value in a$ with the value in k$ (our desired control keys). Depending on which key is pressed, a variable called dir is set to a particular value that signifies a direction.

Line 2045 prints the trail behind Krapz. Basically, all it does is see if Krapz is moving (Krapz doesn’t start moving until you press a key initially), we don’t bother printing a trail. If he is moving (dir will have some non-zero value then), we will print a solid block of blue at the current Krapz co-ordinates. CHR$ (143) will print the solid graphic block you see on the numeric 8 key on the Speccy keyboard (Graphic mode + shift + 8). What’s this CHR$ you ask? Well it’s one way of printing a character on the screen. For example,if you do PRINT CHR$(65) it will print the letter “A” on the screen. This is because CHR$ converts a number to its string equivalent from the character set. If you take a look at the character set table in the Speccy manual you’ll see that numbers from 32 to 127 represent characters from the ASCII standard. Which is why 65 corresponds to letter “A”. From 128 to 255, the character set is unique to the Spectrum with some special characters like the Graphic Blocks taking up positions 129 to 143. You can print any of these characters by passing the code number to CHR$. Some unprintable characters (no, not the sort you’re thinking of) can do some fancy print formatting tricks – refer to the manual for more on that.

Coming back to the code, so we print the blue block at the current Krapz co-ordinates to signify a trail. “Hang on!” you say. “Won’t we be overwriting Krapz in the process?”. But of course! But since we’ll be repositioning Krapz at new co-ordinates anyway, it doesn’t matter. In fact, the code to calculate Krapz’s new co-ordinates follows:

2050 IF dir=1 THEN LET x=x+1

2060 IF dir=2 THEN LET x=x-1

2070 IF dir=3 THEN LET y=y-1

2080 IF dir=4 THEN LET y=y+1

2090 IF (ATTR (y,x)=3 OR ATTR (y,x)=1) THEN FOR f=0 TO 4: PRINT AT y,x; PAPER 2; INK 6; FLASH 1;CHR$ (145+f); FLASH 0: BEEP 0.4,RND*f: NEXT f: PAUSE 10: GO TO 6000

Lines 2050 to 2080 recalculate the new x or y position depending on the direction of travel. Simple, eh?

In line 2090 we check whether Krapz has crashed into the boundary walls or walked into his own trail (ST fluctuations are bad remember?). This is handled by a single IF statement that checks whether the attribute colour at the newly calculated Krapz co-ordinate matches INK 3 (the magenta colour with which the walls are drawn) or INK 1 (blue colour of Krapz’s trail). If it is, it’s time to play a sad ditty expressing our condolences and create a nifty looking explosion. Since I’m not musically inclined I’m just using a simple BEEP statement that randomly plays some stuff in a low pitch. Creating the explosion is simplicity in itself – I’ve created 4 explosion UDG’s using BASin’s UDG creator tool. To simulate the explosion effect, I just print the four characters (with full FLASH and stuff for added effect) one atop the other with a small time gap inbetween to slow things down a bit. It’s not the greatest explosion effect you’ll ever see but it’s good enough for our purpose. After the dust has settled down, we send the program off to line 6000 where the last recitals are performed. More on that later.

2095 IF ATTR (y,x)=5 THEN LET score=score+(level*10): LET quanta=quanta-1: BEEP 0.01,0.1: IF quanta=0 THEN PRINT AT 10,3; PAPER 1; INK 5; FLASH 1;"Space-Time Jump! Get Ready!"; FLASH 0: FOR f=1 TO 10: BEEP 0.03,RND*f: NEXT f: PAUSE 50: LET level=level+1: GO TO 6020

3000 PRINT AT y,x; INK 4;"a"

Line 2095 determines what happens when Krapz picks up a quanta particle (we simply perform an attribute check for that). What happens is this: first the score is increased by 10 times the level we are on (as a measure of difficulty).The number of active quantas on the screen is decreased by one. If there are no more quantas left, it’s time to trigger a Space-Time jump and warp to the next level which is easily done with a simple message, a few random beeps and a jump to Line 6020 that sets a few variables back to initial values and then re-draws the level screen by jumping to line 1010.

If a space-time warp hasn’t been triggered (because there are quantas still left on the screen), we go on to print Krapz at the new locatio in line 3000.

We come to a final bit of code that deals with the player running out of time.

3005 LET time=time-1

3010 IF time=0 THEN PRINT AT 10,5; PAPER 2; INK 6; FLASH 1;"S-T Field Collapsing!"; FLASH 0: FOR f=10 TO 1 STEP -1: BEEP 0.1,f/2: NEXT f: PAUSE 50: FOR f=2 TO 19: PRINT AT f,1; PAPER 1; INK 2;"::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::": BEEP 0.05,RND*f/2: NEXT f: GO TO 6000

4000 GO TO 1010

6000 LET lives=lives-1

6010 IF lives=0 THEN PRINT AT 10,10; PAPER 4; INK 1;"  GAME OVER!  ": PAUSE 100

6012 IF lives=0 THEN IF score>hiscore THEN LET hiscore=score: PRINT AT 12,10; PAPER 5; INK 1; FLASH 1;"New High Score!"; FLASH 0: PAUSE 100

6015 IF lives=0 THEN RETURN

Line 3005 simply decreases the available time by 1 unit.

Line 3010 determines what happens next. If we’ve run out of time it’s time for another dramatic message with annoying random beeps and appropriately dangerous looking flashing text. We up the ante by printing a line of solid block (the odd :: is the same GRAPHIC MODE + SHIFT + 8 friend we met a while back) that slowly fills up the entire screen to signify a field collapse. We then jump to line 6000 to finish off the formalities.

If we haven’t run out of time we simply loop back to line 1010 via Line 4000 to continue playing.

Line 6000 is where the case of death is handled. First the number of available lives is decreased by 1. Line 6010 checks if we’ve used up all our lives, in which case it’s really Game Over! Line 6012 next checks if we created a new high score by simply comparing the current score with the high score. If our current score is higher, we save it as the high score and congratulate the player on achieving the same.

Line 6015 proceeds to return from the main game loop routine and dumps us back at the main menu screen from where we’d first come from thus bringing us back full circle to square one.

And that my friends brings us to an end to this guide! Hopefully, you’ll go on to write better and bigger stuff than the Dash-it game described here. If not, don’t forget to send your entry to the annual Crap Games Competition! Cheers!

64 Column print

The source code for 64 column printing was originally provided by Andrew Owen in a thread on WoSF. Reproduced with permission.

BASIC normally provides only 32 columns for printing text. This machine code routine, which can be called from BASIC, effectively doubles the number of columns horizontally available for printing.

In BASIC, do a CLEAR 49999 so that BASIC won’t overrun the machine code in memory.

Assemble or load the following routine at address 50000. Do a RANDOMIZE USR 50000, to run the routine and perform the magic for 64 column printing.

To print in 64 columns, use stream 4, for eg: PRINT #4;AT 0,53;”Hello World”

; ---------------
; 4x8 font driver, (c) 2007 Andrew Owen
; ---------------

	org	50000		;

; --------------------------------
; CREATE CHANNEL AND ATTACH STREAM
; --------------------------------
;
; Based on code by Ian Beardsmore from Your Spectrum issue 7, September 1984.

c_chan:	ld	hl,($5c53)	; a channel must be created below basic
				; so look at the system variable PROG
	dec	hl		; move hl down one address

	ld	bc,$0005	; the new channel takes 5 bytes
	call	$1655		; call the MAKE_ROOM routine
	inc	hl		; move HL up one address

	ld	bc,chan_4	; could write the bytes directly but
				; then code would be non-relocatable

	ld	(hl),c		; low byte of the output routine
	inc	hl		; move HL up one address
	push	hl		; save this address for later

	ld	(hl),b		; high byte of the output routine
	inc	hl		; move HL up one address

	ld	bc,$15c4	; address of input routine

	ld	(hl),c		; low byte of the input routine
	inc	hl		; move HL up one address

	ld	(hl),b		; high byte of the input routine
	inc	hl		; move HL up one address 

	ld	(hl),'P'	; channel type; 'K', 'S', 'R' or 'P'

; attach stream

	pop	hl		; the first address plus one of the
				; extra space stored earlier
	ld	de,($5c4f)	; store the contents of CHANS in DE
	and	a		; clear the carry flag before
				; calculation
	sbc	hl,de		; the difference between the start of
				; the channels area and the start of the
				; extra space becomes the offset, stored
				; in HL
	ex	de,hl		; store the offset in DE

	ld	hl,$5c10	; store the contents of STRMS in HL
	ld	a,$04		; stream number 4
	add	a,$03		; take account of streams -3 to -1
	add	a,a		; each of the seven default streams has
				; two bytes of offset data
				; the total number of bytes occupied,
				; held in a, forms the offset for the
				; new stream
	ld	b,$00		; set b to hold $00
	ld	c,a		; set the low byte of the offset
	add	hl,bc		; the offset is added to the base
				; address to give the correct location
				; in the streams table to store the
				; offset
	ld	(hl),e		; the low byte of the offset
	inc	hl		; move HL up one address
	ld	(hl),d		; the high byte of the offset
	ret			; all done

; -----------------
; CHANNEL #4 OUTPUT
; -----------------
;
; Based on code by Tony Samuels from Your Spectrum issue 13, April 1985.
; A channel wrapper for the 64-column display driver.

chan_4:	ld	b,a		; save character
	ld	a,(atflg)	; value of AT flag
	and	a		; test against zero
	jr	nz,getrow	; jump if not
	ld	a,b		; restore character

atchk:	cp	$16		; test for AT
	jr	nz,crchk	; if not test for CR
	ld	a,$ff		; set the AT flag
	ld	(atflg),a	; next character will be row
	ret			; return

getrow:	cp	$fe		; test AT flag
	jr	z,getcol	; jump if setting col
	ld	a,b		; restore character
	cp	$18		; greater than 23?
	jr	nc,err_b	; error if so

	ld	(row),a		; store it in row
	ld	hl,atflg	; AT flag
	dec	(hl)		; indicates next character is col
	ret			; return

getcol:	ld	a,b		; restore character
	cp	$40		; greater than 63?
	jr	nc,err_b	; error if so
	ld	(col),a		; store it in col
	xor	a		; set a to zero
	ld	(atflg),a	; store in AT flag
	ret			; return

err_b:	xor	a		; set a to zero
	ld	(atflg),a	; clear AT flag
	rst	08h		;
	defb	$0a		;

crchk:	cp	$0d		; check for return
	jr	z,do_cr		; to carriage return if so
	call	pr_64		; print it

	ld	hl,col		; increment
	inc	(hl)		; the column
	ld	a,(hl)		;

	cp	$40		; column 64?
	ret	nz		;

do_cr:	xor	a		; set A to zero
	ld	(col),a		; reset column
	ld	a,(row)		; get the row
	inc	a		; increment it
	cp	$18		; row 24?
	jr	z,wrap		;

zend:	ld	(row),a		; write it back
	ret

wrap:	xor	a		;
	jr	zend		;

; ------------------------
; 64 COLUMN DISPLAY DRIVER
; ------------------------

pr_64:	rra			; divide by two with remainder in carry flag

	ld	h,$00		; clear H
	ld	l,a		; CHAR to low byte of HL

	ex	af,af'		; save the carry flag

	add	hl,hl		; multiply
	add	hl,hl		; by
	add	hl,hl		; eight
	ld	de,font-$80	; offset to FONT
	add	hl,de		; HL holds address of first byte of
				; character map in FONT
	push	hl		; save font address

; convert the row to the base screen address

	ld	a,(row)		; get the row
	ld	b,a		; save it
	and	$18		; mask off bit 3-4
	ld	d,a		; store high byte of offset in D
	ld	a,b		; retrieve it
	and	$07		; mask off bit 0-2
	rlca			; shift
	rlca			; five
	rlca			; bits
	rlca			; to the
	rlca			; left
	ld	e,a		; store low byte of offset in E

; add the column

	ld	a,(col)		; get the column
	rra			; divide by two with remainder in carry flag
	push	af		; store the carry flag

	ld	h,$40		; base location
	ld	l,a		; plus column offset

	add	hl,de		; add the offset

	ex	de,hl		; put the result back in DE

; HL now points to the location of the first byte of char data in FONT_1
; DE points to the first screen byte in SCREEN_1
; C holds the offset to the routine

	pop	af		; restore column carry flag
	pop	hl		; restore the font address

	jr	nc,odd_col	; jump if odd column

even_col:
	ex	af,af'		; restore char position carry flag
	jr	c,l_on_l	; left char on left col
	jr	r_on_l		; right char on left col

odd_col:
	ex	af,af'		; restore char position carry flag
	jr	nc,r_on_r	; right char on right col
	jr	l_on_r		; left char on right col

; -------------------------------
; WRITE A CHARACTER TO THE SCREEN
; -------------------------------
;
; There are four separate routines

; HL points to the first byte of a character in FONT
; DE points to the first byte of the screen address

; left nibble on left hand side

l_on_l:	ld	c,$08		; 8 bytes to write
ll_lp:	ld	a,(de)		; read byte at destination
	and	$f0		; mask area used by new character
	ld	b,a		; store in b
	ld	a,(hl)		; get byte of font
	and	$0f		; mask off unused half
	or	b		; combine with background
	ld	(de),a		; write it back
	inc	d		; point to next screen location
	inc	hl		; point to next font data
	dec	c		; adjust counter
	jr	nz,ll_lp	; loop 8 times
	ret			; done

; right nibble on right hand side

r_on_r:	ld	c,$08		; 8 bytes to write
rr_lp:	ld	a,(de)		; read byte at destination
	and	$0f		; mask area used by new character
	ld	b,a		; store in b
	ld	a,(hl)		; get byte of font
	and	$f0		; mask off unused half
	or	b		; combine with background
	ld	(de),a		; write it back
	inc	d		; point to next screen location
	inc	hl		; point to next font data
	dec	c		; adjust counter
	jr	nz,rr_lp	; loop 8 times
	ret			; done

; left nibble on right hand side

l_on_r:	ld	c,$08		; 8 bytes to write
lr_lp:	ld	a,(de)		; read byte at destination
	and	$0f		; mask area used by new character
	ld	b,a		; store in b
	ld	a,(hl)		; get byte of font
	rrca			; shift right
	rrca			; four bits
	rrca			; leaving 7-4
	rrca			; empty
	and	$f0		;
	or	b		; combine with background
	ld	(de),a		; write it back
	inc	d		; point to next screen location
	inc	hl		; point to next font data
	dec	c		; adjust counter
	jr	nz,lr_lp	; loop 8 times
	ret			; done

; right nibble on left hand side

r_on_l:	ld	c,$08		; 8 bytes to write
rl_lp:	ld	a,(de)		; read byte at destination
	and	$f0		; mask area used by new character
	ld	b,a		; store in b
	ld	a,(hl)		; get byte of font
	rlca			; shift left
	rlca			; four bits
	rlca			; leaving 3-0
	rlca			; empty
	and	$0f		;
	or	b		; combine with background
	ld	(de),a		; write it back
	inc	d		; point to next screen location
	inc	hl		; point to next font data
	dec	c		; adjust counter
	jr	nz,rl_lp	; loop 8 times
	ret			; done

; --------------
; TEXT VARIABLES
; --------------
;
; Used by the 64 column driver

atflg:	defb	$00		; AT flag
row:	defb	$00		; row
col:	defb	$00		; col

; -------------------
; half width 4x8 font
; -------------------
;
; 384 bytes

font:
	defb	$00,$02,$02,$02,$02,$00,$02,$00,$00,$52,$57,$02,$02,$07,$02,$00;
	defb	$00,$25,$71,$62,$32,$74,$25,$00,$00,$22,$42,$30,$50,$50,$30,$00;
	defb	$00,$14,$22,$41,$41,$41,$22,$14,$00,$20,$70,$22,$57,$02,$00,$00;
	defb	$00,$00,$00,$00,$07,$00,$20,$20,$00,$01,$01,$02,$02,$04,$14,$00;
	defb	$00,$22,$56,$52,$52,$52,$27,$00,$00,$27,$51,$12,$21,$45,$72,$00;
	defb	$00,$57,$54,$56,$71,$15,$12,$00,$00,$17,$21,$61,$52,$52,$22,$00;
	defb	$00,$22,$55,$25,$53,$52,$24,$00,$00,$00,$00,$22,$00,$00,$22,$02;
	defb	$00,$00,$10,$27,$40,$27,$10,$00,$00,$02,$45,$21,$12,$20,$42,$00;
	defb	$00,$23,$55,$75,$77,$45,$35,$00,$00,$63,$54,$64,$54,$54,$63,$00;
	defb	$00,$67,$54,$56,$54,$54,$67,$00,$00,$73,$44,$64,$45,$45,$43,$00;
	defb	$00,$57,$52,$72,$52,$52,$57,$00,$00,$35,$15,$16,$55,$55,$25,$00;
	defb	$00,$45,$47,$45,$45,$45,$75,$00,$00,$62,$55,$55,$55,$55,$52,$00;
	defb	$00,$62,$55,$55,$65,$45,$43,$00,$00,$63,$54,$52,$61,$55,$52,$00;
	defb	$00,$75,$25,$25,$25,$25,$22,$00,$00,$55,$55,$55,$55,$27,$25,$00;
	defb	$00,$55,$55,$25,$22,$52,$52,$00,$00,$73,$12,$22,$22,$42,$72,$03;
	defb	$00,$46,$42,$22,$22,$12,$12,$06,$00,$20,$50,$00,$00,$00,$00,$0F;
	defb	$00,$20,$10,$03,$05,$05,$03,$00,$00,$40,$40,$63,$54,$54,$63,$00;
	defb	$00,$10,$10,$32,$55,$56,$33,$00,$00,$10,$20,$73,$25,$25,$43,$06;
	defb	$00,$42,$40,$66,$52,$52,$57,$00,$00,$14,$04,$35,$16,$15,$55,$20;
	defb	$00,$60,$20,$25,$27,$25,$75,$00,$00,$00,$00,$62,$55,$55,$52,$00;
	defb	$00,$00,$00,$63,$55,$55,$63,$41,$00,$00,$00,$53,$66,$43,$46,$00;
	defb	$00,$00,$20,$75,$25,$25,$12,$00,$00,$00,$00,$55,$55,$27,$25,$00;
	defb	$00,$00,$00,$55,$25,$25,$53,$06,$00,$01,$02,$72,$34,$62,$72,$01;
	defb	$00,$24,$22,$22,$21,$22,$22,$04,$00,$56,$A9,$06,$04,$06,$09,$06;

Adding extra string manipulation techniques to BASIC

By default Sinclair BASIC provides very limited string manipulation techniques. However, you can easily add more functionality via the following methods.

The following methods are taken from an original World of Spectrum Forum thread,  started by Andrew Owen. However, I’ve added some details for better explanation of each method.

The earlier versions of UPPER$ and LOWER$ didn’t work as expected and Alvin Albrecht was kind enough to provide the corrected versions.

LEFT$(string, number)

DEF FN L$(s$, x) = s$( to x)

Given a string, the L$ function will return a string starting from the leftmost character (hence called LEFT$ in some BASIC implementations) and up to x characters to the right. Therefore, if you have a string, say “Hello” and wish to extract just “Hell” from it, you can do FN L$(a$, 4), where a$ is the string holding “Hello”. A typical example in BASIC would be:

05 DEF FN L$(s$, x) = s$( to x)
10 LET a$ = "Hello"
20 LET b$ = FN L$(a$, 4)
30 PRINT b$

The above BASIC example is easily modified for the rest of the functions that follow.

RIGHT$(string, number)

DEF FN R$(s$, x) = s$(LEN s$ + 1 – x TO LEN s$)

Given a string, the R$ function will return a string starting from the rightmost character (hence called RIGHT$ in some BASIC implementations) and up to x characters to the left. Therefore, if you have a string, say “Hello” and wish to extract just “ello” from it, you can do FN R$(a$, 4), where a$ is the string holding “Hello”.

MID$(string, number1, number2)

DEF FN M$(s$, x,y) = s$(x TO x – 1 + y)

Given a string, the M$ function will return a string starting from anywhere in the string x and up to y characters to the right (hence called MID$ in some BASIC implementations). Therefore, if you have a string, say “Hello” and wish to extract just “ell” from it, you can do FN M$(a$, 2,4), where a$ is the string holding “Hello”.

LOWER$(string)

DEF FN D$(s$) = VAL$ “CHR$ ((CODE s$)+(32 AND CODE s$ > 64 AND CODE s$ < 92))+FN D$(s$(2 TO ))”( TO 27+12*(LEN s> 1))

Given a string, the D$ function will return a string with all characters converted to lower case (hence called LOWER$ in some BASIC implementations). Therefore, if you have a string, say “Hello” and wish to convert all of it to lower case (“hello”), you can do FN D$(a$), where a$ is the string holding “Hello”.

UPPER$(string)

DEF FN U$(s$) = VAL$ “CHR$ ((CODE s$)-(32 AND CODE s$ > 96 AND CODE s$ < 124))+FN u$(s$(2 TO ))”( TO 28+12*(LEN s$> 1))

Given a string, the D$ function will return a string with all characters converted to upper case (hence called UPPER$ in some BASIC implementations). Therefore, if you have a string, say “Hello” and wish to convert all of it to upper case (“HELLO”), you can do FN U$(a$), where a$ is the string holding “Hello”.

Using the Kempston Joystick in your own BASIC programs

Traditionally, games in BASIC use the keyboard for player input and act accordingly. For eg, the keys Q, A, O and P may be used to move the player character around and M may be used to fire or other action. However, as anyone who has played ‘professional’ games (i.e written using machine code) on the speccy would know, using a joystick is a far better alternative especially in fast action games.

This article will tell you how to use the popular Kempston Joystick in your games for that ‘professional’ touch. 😉

To begin with, inputs from the Kempston Joystick are read via port 31. The Spectrum manual has more information on what a ‘port’ is and so I won’t be covering it here. Suffice to say, any external device connected to the speccy reads through a specific port, numbered 1 to 255. The Kempston Joystick uses port 31.

To read the ‘value’ in port 31 you simply do: LET kj = IN 31. Depending on what state the joystick is in, kj will hold a very specific value that can be interpreted as desired. Now then, the Kempston joystick  has only 5 states to deal with, namely ‘Fire’ (any of the fire buttons pressed), ‘Up’ (joystick pulled back), ‘Down’ (joystick pushed forward), ‘Left’ (joystick to the left) and ‘Right’ (joystick to the right).

This information is packed into a single byte in the following format: 000FUDLR. As you can see only the lower 5 bits are of interest to us, which means we can ignore any value of kj above 31. Depending on which bit is set (value of 1) we can assume the joystick is in that state. Multiple states are also possible – firing and moving for example, or moving diagonally for example. Obviously you can’t have states of ‘Up’ & ‘Down’ or ‘Left’ & ‘Right’ occurring simultaneously!

The table of basic values that we need to test is given below:

kj (IN 31) Bit pattern (lower 5 bits only) State
1 00001 Right
2 00010 Left
4 00100 Down
8 01000 Up
16 10000 Fire

For the purpose of this tutorial I’m going to ignore multiple directional states because BASIC doesn’t support the kind of bit level manipulation we would like to achieve detection of that. However, it will be useful to detect if we’re firing and moving so that will be on the agenda.

Let’s get the simple detection out of the way first!

10 LET x=10: LET y=10
20 LET kj=IN 31: REM get the state of joystick
30 PRINT AT y,x;"*"
40 IF (kj>31) OR (kj=0) THEN GO TO 20: REM ignore spurious inputs
45 BORDER 1: PRINT AT y,x;" ": REM overwrite at old position
50 IF kj=1 THEN LET x=x+1: REM left
60 IF kj=2 THEN LET x=x-1: REM right
70 IF kj=4 THEN LET y=y+1: REM Down
80 IF kj=8 THEN LET y=y-1: REM Up
90 IF kj=16 THEN BORDER 2: REM Fire
100 GO TO 20

The code is pretty simple. To see it in action we will print an asterisk on the screen that can be moved about by manipulating the joystick. Screen boundaries aren’t checked though to keep the code simple. We’ll also set the border to Red if the fire button is pressed. That’s it really.

To check for the event of fire and moving we’ll have to perform a simple trick. When firing and moving, kj will have bit 5 (for fire) and the bits for movement (any of them from 1 to 4) to be set, leading to a number  that is greater than 16 (since fire by alone is itself 16). So all we do then is check for this fact (kj > 16) and if so, note that the fire is being pressed and then subtract 16 from kj so that we can continue to check for the movement keys as normal.

The above code can be modified so (changed lines in blue):

10 LET x=10: LET y=10
20 LET fire=0: LET kj=IN 31: REM note: reset fire event every time!
30 PRINT AT y,x;"*"
40 IF (kj>31) OR (kj=0) THEN GO TO 20
45 BORDER 1: PRINT AT y,x;" "
46 IF kj>16 THEN LET kj=kj-16: LET fire=1: REM Check if firing AND moving
50 IF kj=1 THEN LET x=x+1
60 IF kj=2 THEN LET x=x-1
70 IF kj=4 THEN LET y=y+1
80 IF kj=8 THEN LET y=y-1
90 IF kj=16 OR fire THEN BORDER 2: REM fire by itself or firing and moving? If so red border
100 GO TO 20

That’s about it really. Doing the above in machine code is, in fact, easier because it has all the necessary operators to perform low level bit manipulation which you really need if you want to check for multiple states of the joystick. However, I believe the above will do very nicely for those BASIC games that do not need diagonal movement!

Experiment and enjoy!

Review: The ZX Spectrum Book – 1982 to 199x

Publisher: Hiive Books

Author: Andrew Rollings

Some wise person once said,  “Never judge a book by its cover.” Sane advice when it comes to books, no doubt, but when it comes to “The ZX Spectrum Book” the cover pretty much tells you what you can expect from the book – polish, style, quality and staggering attention to detail. Why, even the envelope that the book ships in has a customised Spectrum labeling on it! Beat that!

But a bit of background on the book before I give out more details on it. It was in mid-2005 (I think, well it was some time ago at any rate!) that Andrew Rollings announced he was doing a memorabilia book that covered a fair bit of Spectrum gaming history. He figured that he would finish the book by September 2005 (yes, that’s not a spelling mistake). *snigger*

Of course, he didn’t! In fact, it took more than a year to finish the book. And now that we have the book, we know why!

The book chronicles more than 230 games covering the lifespan of the Speccy gaming industry from 1982 to 1992. Each year is colour coded to help you quickly find a relevant game. Apart from a Contents section that lists the games by their year of release, there is a handy Index section that lists the game in alphabetical order.

The entire meaty 250 odd paged book is printed in full-colour (Spectrum colours in fact) and the paper, colour reproduction and print quality is outstanding.

To cap it the foreword is written by Sir Clive Sinclair himself.

If that doesn’t impress you the contents of the book certainly will.

Effectively, there are 9 chapters in the book each representing the year of interest from 1982 to 199x. Nick Humphries (of YSRNR fame) introduces each chapter with an excellent summation of the important milestones in that year. This sets a nice background and tone for the games being presented in the chapter.

Each write-up of a game is broken up into three sections that give you a brief background about the game, some trivia and a succinct overview of the game itself. The write-ups are accompanied by two in-game screenshots, the loading screen and the inlay of the game. In addition, where possible, the ratings given by the Big Three (Crash, Your Sinclair, and Sinclair User) are reproduced to give you a fair idea of the overall perception of the game (by the magazines that is) when it came out.

If you’re wondering what kind of games are chronicled, it’s interesting to note that Andrew has been rather eclectic in his selection. He writes about the good games, the bad games and even the downright ugly ones. It’s a fair representation of the gaming scene that existed during that particular period. More importantly, the chosen games either have some sort of historical significance or interesting trivia that helped set them apart from the others. That’s not to say he’s managed to write about every interesting game that existed – that would have been well near impossible considering the number of games that were released – but lets say every game included in the book has a story to tell and make a very interesting read.

There’s nothing much else to say really. This book deserves to be on every specchums coffee table – it’s a piece of Spectrum computing history (the book that is, not the coffee table). If you haven’t got it already, go on spoil yourself – you know you want to!

Review: Exolon DX

Game: Exolon DX

Publisher: Retrospec

Authors: Graham Goring, John Blythe & Infamous


When I first played Exolon by Raffaele Cecco on the speccy I was blown away by the colourful graphics and the super smooth animation of the main character – Vitorc. To date it remains as a reminder as to what can be done on the speccy when a competent coder and graphic artist design a game with the limitations and abilities of the speccy in mind.

So when retrospec announced a Exolon remake in the offing way back in 2005, I was hugely interested in knowing how the talented folks at retrospec would remake this classic. The answer is ExolonDX, the second ever remake done by Graham Goring for retrospec.

The starting intro sets the mood for the remake nicely. Colourful, well drawn graphics with all sorts of embellishments greet you before whisking you away to the main menu. The first thing you should do is go to the options screen and switch the display to 16 or 32 bit full screen mode (unless you don’t want to of course!) to get the maximum pleasure out of the game. You will need to quit and restart the game for the setting to take effect though, which is a bit annoying but easily done.

The first screen in the game shows off the graphics nicely. Cool looking spaceship, nifty status panel, smooth parallax-scrolling starfield and ,whey-hey, the Cybernoid ship zooming off screen in the background! Okay enough sight-seeing. Time to lay into that gun emplacement with a quick rocket from the rocket launcher. Woohoo! Watch that sucker explode! Well then, not a bad start eh? Wonder what lies beyond that crazy looking alien artifact…

And with that you’re on your way. The first thing to note in the remake is that the number of lives has reduced from the gracious (but much needed) nine lives to a mere two. Yep, that’s right – two. That’s probably because the remake doesn’t require you to be pixel perfect as in the original speccy version so things are indeed a bit easier. Plus the remake is pretty much faithful to the original version so there aren’t any nasty surprises in store to upset your march through the game.

Amidst the graphical extravaganza the only thing I found a bit amiss was the graphic for Vitorc himself. He looks like a spaceman in a spacesuit – as he should – but lacks the character that Vitorc had in the original version. Plus the upgrade suit doesn’t alter Vitorc’s appearance as dramatically. I’m probably nit-picking but it’s these two points that caught my eye early on in the game.

Apart from that Exolon is a visual feast with extremely colourful well defined graphics and really cool visual effects that bring the game to life. In the sound department, Exolon boasts of a thumpingly good  soundtrack that plays in the background and competent but varied sound effects that accompany most of the actions in the game.

I must say I thoroughly enjoyed playing the remake for the simple reason that it’s a well crafted remake that stays to the original but significantly improves on all the aspects of the original game. Highly recommended!

Interestingly, ExolonDX credits Hugh Binns as the creator of the original graphics. However, I can’t find any reference to Hugh Binn as the author of the graphics for the speccy version (on which ExolonDX is based). Indeed, he isn’t mentioned anywhere in the credits for the speccy version of Exolon. Hugh Binns, however, did do the graphics for the Amstrad CPC version, so there’s probably been a mix-up somewhere. Either that or Raffaele cheekily took all the credit!

Beginners guide to BASIC – 2

In the previous installment we left off at the GO SUB command and how it can be used to invoke a sub-routine. We shall continue that discussion and then some more by examining another bit of code first:

1 REM Dash it!
2 REM by Arjun Nair 2004
3 REM ***********************
5 CLEAR : RANDOMIZE
10 CLS : LET hiscore=0
11 DIM k$(4): LET k$(1) = "p": LET k$(2) = "o": LET k$(3) = "q": LET k$(4) = "s"
15 GO SUB 6060: REM init graphics
20 GO SUB 8000: REM main menu
30 GO SUB 1000: REM game loop
50 GO TO 20

In line 5, we see the first bits of useful code. CLEAR simply wipes out the Spectrum memory and provides you with a clean slate to work on.

NOTE: Actually that’s a simplistic explanation. The CLEAR command actually does a bit more than that. It not only clears all the variables but also clears the display screen, resets the PLOT position and clears the GO SUB stack.

The following statement is a RANDOMIZE keyword, which initialises the random number generator and makes it as random as possible (since computers can’t actually generate truly random numbers for various reasons). Sometimes the RANDOMIZE keyword is followed by a number, which is called the seed. Specifying a seed of zero (using RANDOMIZE on its own is equivalent of saying RANDOMIZE 0) makes the Speccy use an internal timer to seed the generator, which makes things very random indeed.

NOTE: Again, I have not gone in depth into the workings of the RANDOMIZE keyword. But suffice to say that using a number other than zero will create a sequence of random numbers that will repeat every time the program is run. Using zero ensures that RANDOMIZE will be as random as possible.

Coming to line 10 we have the CLS keyword that simply clears the screen, and nothing more. If you have specified any foreground and background colours and/or a border colour, the CLS command will clear the screen to them. We’ll come to that shortly when we start dabbling in colours.

The next statement is a LET statement that assigns the value zero to a variable called highscore. Effectively, a variable is a named placeholder that serves to hold some value. They are of two types – numeric variables and string variables. Numeric variables hold only numeric values (ranging from 0 to 255), while string variables hold alphanumeric data in string format (anything within quotation marks). In our case, highscore is simply a numeric variable that initially holds zero.

Things get a bit more involved in the next line. Before I get into the technical explanation, let me explain what I’m trying to do here. What I want is the player to be able to redefine the control keys from within the game if he so chooses. Therefore, I save the information on control keys in a string array (I’ll come to arrays shortly), which can be manipulated anytime (which we’ll do when we implement the Re-define keys option).

As for arrays, well they are nothing but a collection of similar stuff (or objects as we call them in programming parlance). You can have a collection of months (January, February, et al) or collection of music albums, and they can all be thought of as arrays of months and albums respectively. Like plain variables, an array can be of numeric or string type. In fact, they are called array variables and individual elements of the array behave just like plain variables.

In the code above, we would like to have an array of control keys (an array since they are just a collection of keys). The way to go about is that we first tell the Speccy that we would like to set aside space for so many number of array elements. In our case, we would like to create an array of four control keys (Up, Down, Left & Right). The statement DIM k$(4) does precisely this. DIM is short for DIMension (size of array), k$ is the name of the array variable and 4 signifies the number of elements that we want.

Once you have created an array, you can fill it in using the LET statement like we did for highscore. The difference here is that we access individual elements of the array using the syntax k$(1), k$(2), …. k$(n)  – where n is any element number (an index). You cannot do LET k$ = something. You MUST specify the element within the array that you wish to change by giving an index number. So we have stored the default letters that will correspond to our control keys in k$ from 1 to 4.

NOTE: In Sinclair BASIC, all string variables must consist of only one letter followed by the $ sign, which is why we have called our control keys array k$ instead of something meaningful like key. Numeric variables don’t suffer from such limitations, which is why our high-score variable is sensibly named highscore.

Lines 15 to 30 use a bunch of GO SUB’s to transfer control to various part of the program. GO SUB is short for GO to SUBroutine. As we saw earlier, a subroutine is a short piece of code within your program that performs a specific function. Once it is finished, the sub routine RETURNS control to the point in the program from which it was called.

In our case, we have separate sub-routines for initialising the graphics for the game, for presenting a menu to the player and finally for running the actual game. As I explained earlier, by using sub routines I have “compartmentalised” the code in my program so that if I later decide to change the way a certain sub routine is implemented (say, if I change how the menu looks) I don’t have to worry about how it will affect the whole game. Here, line 15 first transfers control to subroutine that starts from line 6060 that sets up the game graphics.

Once the subroutine is done, it RETURNs to control to the point from where it was called, in our case this is back to line 15. The next statement is the REM statement (which I have used to remind me which subroutine the GO SUB is referring to), so the Speccy ignores it and goes to the next line instead. Similarly for the main menu and for running the game (the game loop).

Finally we have line 50 with a GO TO keyword that transfers control to line 20. This is so that once the game loop is done (the player has finished all his lives) we want to go back to the main menu option screen. The difference between GO TO and GO SUB is that the former simply transfers control to the line specified in the program and doesn’t expect any RETURNs to it (we don’t want to come back here from the main menu for instance), while the latter is a call to an actual sub-routine.

NOTE: If you’ve typed the code in and wish to save it, choose the Save-As option from the BASin File option. Late on, I’ll show you how to create an actual tape image so that you can load it in to your Speccy!

Okay, let’s have a look at the main menu sub-routine then. This sub-routine will present 4 options to the user and the user can select any one of the options by pressing the key corresponding to the item of interest. The code then:

8000 REM *** Main Menu ***
8010 BORDER 0: PAPER 0: INK 6: CLS : LET a$="":
8020 PRINT "ffffffffffffDash it!ffffffffffff"
8030 PRINT AT 5,7; INK 3;"1) Play game"
8040 PRINT AT 7,7; INK 4;"2) Redefine Keys"
8050 PRINT AT 9,7; INK 5;"3) Instructions"
8060 PRINT AT 11,7; INK 6;"4) Credits"
8070 PRINT AT 15,7; INK 7;" High Score: ";hiscore
8080 PRINT #0; INK 2;TAB 5;"(c) 2004 Arjun Nair"
8090 LET a$=INKEY$:
8095 IF a$="" OR a$>"4" THEN GO TO 8090
8096 BEEP 0.3,10
8100 IF a$="0" THEN PRINT #0;"For my parents and Lina, Shreya & Hazel": PAUSE 200: CLS
8110 IF a$="1" THEN RETURN
8120 IF a$="2" THEN GO SUB 8400
8130 IF a$="3" THEN GO SUB 9000
8140 IF a$="4" THEN GO SUB 8300
8150 GO TO 8010

All right! So what do we have here? Line 8000 is our friendly REM statement again informing us that this is the Main Menu sub-routine.

Line 8010 sets up the display screen by setting the background (PAPER) and border (BORDER) colour to black (0) and the foreground (INK) to yellow (6). Doing a CLS then quickly clears the screen of any previous junk and enables our colour settings. We then initialise a string variable called a$ to a null value. I’ll tell you why shortly.

The funny characters you see in line 8020 in the PRINT command are, unfortunately, junk copied over from BASin thanks to some sneaky embedded control characters in the PRINT command. Instead of using the usual INK and PAPER commands to change the colours we can actually use a BASIC trick to embed these colours within the PRINT command itself. You can see that they are embedded because they will then turn up even in the listing of the program on the Speccy. In BASin, the editor replaces these control characters with some symbols, which is why they don’t turn up in the listings. However, do a LIST command and see the results in the emulator window. You will see what I mean. So how do we embed control characters for colours in the PRINT statement? I’m not aware of how to do it in BASin, since the editor doesn’t allow you to do such things. On a Speccy however, you can do this: After the first quotation mark in the PRINT statement, embed the colour information by going into extended mode (flashing E cursor) and pressing the key that corresponds to the required paper colour or holding down the SHIFT key (while still in extended mode) and selecting the required INK colour. For example, if you want to print something in red on a yellow background, you will first enable the yellow background for the text by going in to extended mode and pressing 6. Then enable the red ink by again going into extended mode, hold down shift and press 2. Now type in whatever you want and it will be in the colours you selected (even in listings). When the required text is done, you will have to revert back to the normal colours or your entire program listing will be in this colour! To do this, simply repeat the process after the coloured text but this time choosing white paper and black ink. Apart from colour control characters, there are other control characters that you can embed but I won’t be examining them here.

If you aren’t comfortable with using control characters (and I’m not) stick to normal INK and PAPER commands to get things done.

Lines 8030 to 8060 print out the options for the user. Again note the versatility of the PRINT command. I’m adding an AT qualifier to the PRINT command to tell it where exactly to print text on the screen. The PRINT AT form takes two parameters: one for the column number and the second for the row number that you want to print to. So the PRINT AT command in line 8030 will print the text “1) Play game” at column 5, row 7 on the screen. Similarly we list the other options on the screen.

Line 8070 offers something of more interest. We are printing out the text “High Score: ” followed by the value in the hiscore variable. This way you can print the value of any variable including string variables (we’ll see an example of that in the Key Redefine sub-routine).

Next we come to the ego-feeding copyright message of line 8080. The only thing of interest here is the TAB keyword that inserts blank spaces when printing to screen. The number after the TAB keywords indicates how many blank spaces to insert. Note that this number ranges from 0 to 31 inclusive. So to insert only one blank space, use TAB 0, not TAB 1, which will insert 2 spaces!

Now we come to the interesting part of actually finding out which option the user has selected and then acting on it. The first step is to read in the key pressed by the player when choosing an option.

To find out which key has been pressed is achieved by using the INKEY$ command. When the Speccy encounters this command it immediately (well almost) scans the keyboard to see if a key has been pressed, and if so, it returns the value of that key. If no key has been pressed, it returns a null value. In line 8090, we ask the Speccy to do just that and save that value in a variable called a$ (which you will remember we initialised to a null value in line 8010), so that we can act upon the information.

Line 8095 does a simple check to see if the user has pressed a key that we are interested in. If not we simply GO TO line 8090 to again wait for a key press.

This requires an introduction of our first conditional statement via the IF-THEN statement. It’s simplicity itself, really. The IF statement takes the form, IF <condition> is true, THEN do this otherwise skip this entire line. You can string multiple conditions together in an IF statement by using the OR and AND operators. ORing two conditions together will evaluate to true if any one of the conditions is true. ANDing any two conditions together will evaluate to true only if both conditions are true. It’s pretty similar to how things work in English. For example: IF it’s raining AND I don’t have an umbrella THEN I’ll stay in. Here you will stay in only if both the conditions – rain and lack of umbrella – are true. Another example: IF it’s raining OR it’s windy THEN I’ll stay in. In this case you will stay in if either of the conditions – rain or wind – are true. The BASIC form of IF is similar except that the conditions aren’t in English like above. They take a slightly different form which will become apparent as we examine our code in detail.

Right, so what does line 8095 do? It will GO TO line 8090 IF the value in a$ is null (no key pressed) OR if a$ has a value greater than 4 (we are interested only in keys from 0 to 4 that correspond to the options on the screen). Simple, eh?

In line 8096 we take our first stab at sound effects on the Speccy. This is achieved by using the BEEP command that takes two parameters. The first one is the duration in seconds (so 0.3 here indicates that we want a short note) and the second one is the pitch of the note (in semi-tones above or below middle C depending on whether it’s positive or negative). As I’m not musically inclined all I can say is that lower values of second parameters give you a low guttural sound while higher values give high-pitched “shrieky” sounds. Here we want to emit a short low pitched sound to indicate that a valid key has been pressed by the user and that an option has been selected.

Line 8100 is an interesting one ‘cos it handles the event for key 0 being pressed. If you notice there is no option that corresponds to key 0. It’s just a hidden Easter-egg that pops up with some irrelevant information (to the user), waits for 4 seconds and then clears the screen. In case you are wondering who the other people are, apart from my parents, that I’ve mentioned, they are my cousin-sister, my niece and my ex-crush (don’t ask).

Right ho! Back to the code and we come to line 8110 which checks if the user chose to play the game, in which case it simply RETURNs control to line 20 from where it was called originally, where it just finds the REM statement lurking after the colon symbol and so skips on next to line 30. You can clearly see the sub-routine process in action here. Line 20 called the main menu sub-routine, which after it was done, simply returns back to line 20. You can call this sub-routine from anywhere in the program and after it’s done, it will RETURN control to from wherever it was called from.

Line 8120 calls the Key Redefine sub-routine if the user has selected 2. So once that sub-routine is done it will return here, back to the main menu. Similarly, line 8130 calls the Instructions sub-routine and line 8140 calls the Credits sub-routine.

The last line in the sub-routine, 8150, does a GO TO to the beginning of the sub-routine (line 8010), so that the main menu is displayed again. This is done because the sub-routines may have displayed something else on the screen (instructions for example) and we want to re-display the main menu once control returns from the sub-routine.

And with that we come to an end to this installment. I hope you’ve understood most of the stuff in here if not all. Experiment with the BASIC stuff introduced here and you will get a good hang of things. The one good thing about Sinclair BASIC is that it’s pretty intuitive for the most part and is quite easy to get to grips with.

In the next edition of this guide, we’ll introduce the key-redefinition sub-routine, graphics and the actual game loop where all the action resides. So, until next time, cheerio!

Comments and feedback are most welcome and appreciated! Please note that the article is not meant to replace the Spectrum BASIC manual as a reference tome and as such I would encourage you to look up any keywords that you come across in the article for further insights. Also, please address your queries regarding BASin to the author of the program Paul Dunn, especially regarding bugs, features and operation of the program (the article will only cover parts that are essential to the discussion).