How to load a 16x16, 32x32, 64x64 sprite on the screen

The place for codemasters or beginners to talk about programming any language for the Spectrum.
Post Reply
User avatar
R-Tape
Site Admin
Posts: 6353
Joined: Thu Nov 09, 2017 11:46 am

Re: How to load a 16x16, 32x32, 64x64 sprite on the screen

Post by R-Tape »

You may know most of this already Nomad, but just in case:

For starters this routine will print a letter 'A' at the top left of the screen. If you're not already, it's a really good idea to get familiar with the speccy's screen layout. There are examples out there but none of them are a substitute (for me anyway) for investigating the relationships between the screen positions yourself.

A good place to start is to know that the screen starts at memory address 16384, is made of 3 x 2048 byte (so the pixels are a total 6144) repeating segments (which can be seen appear in order in a standard loading screen).

If you RUN this BASIC program you'll see how the screen is organised if you increase by 1 byte each time.

FOR A=16384 TO 16384+6144:POKE A,255:NEXT A

If you observe it fill up the first line of 32 bytes you'll see it then makes a jump, it doesn't move down to the next line directly below. Within a segment you add 256 to move down a line, and 32 to move down a row. I plan to write something more detailed on this sometime.

Code: Select all

	ld hl,letter_a_graphic	;8 bytes of graphic
	ld de,16384	;start of screen memory
printchar:		;print a character graphic addressed by HL at screen address DE
	ld b,8		;loop counter for 8 bytes
printloop:
	ld a,(hl)	;get byte of graphic
	ld (de),a	;put it in screen memory
	inc hl		;next byte of graphic
	inc d		;move DE down one line, inc d is the same as add 256 to DE
	djnz printloop	;repeat B times
	ret
	;
letter_a_graphic:
	db	00000000b
	db	00111100b
	db	01000010b
	db	01000010b
	db	01111110b
	db	01000010b
	db	01000010b
	db	00000000b
	;
And an example of a tile draw routine, nextlinedown etc need more explanation another time.

Code: Select all

	ld b,8
	ld c,8
	call yx2cell
	ld hl,tilegraphic
	call drawtile
	ret
	;
drawtile:	;arrive HL pointing at tile graphic, DE at screen memory
	ld b,16		;loop counter for 16 px high tile
drawtileloop:
	ld a,(hl)	;get byte of graphic
	ld (de),a	;plonk it in screen memory
	inc hl		;next byte of graphic
	inc e		;next column of screen
	ld a,(hl)	;right hand side of tile
	ld (de),a
	inc hl		;next byte of graphic
	dec e		;back to left hand side of tile
	call nextlinedown	;move DE down one screen line
	djnz drawtileloop
	ret
	;
nextlinedown:	;arrive DE at screen address, move it down one pixel line
	inc d
	ld a,d
	and 7
	ret nz
	ld a,e
	add a,32
	ld e,a
	ret c
	ld a,d
	sub 8
	ld d,a
	ret
	;
yx2cell:	;arrive with b=y column 0-23, c=x column 0-31, point DE at corresponding screen address
	ld a,b
	rrca
	rrca
	rrca
	and 224
	or c
	ld e,a
	ld a,b
	and 24
	or 64
	ld d,a
	ret
	;
tilegraphic:
	db	255,255
	db	255,255
	db	255,255
	db	255,255
	db	255,255
	db	255,255
	db	255,255
	db	255,255
	db	255,255
	db	255,255
	db	255,255
	db	255,255
	db	255,255
	db	255,255
	db	255,255
	db	255,255
	;
Nomad
Manic Miner
Posts: 600
Joined: Thu Dec 28, 2017 12:38 pm

Re: How to load a 16x16, 32x32, 64x64 sprite on the screen

Post by Nomad »

Thanks R-tape, I had a play with the example. I agree its good to play around and get things straight. It's one thing to read something - but for me anyway until I make a few mistakes and break stuff it does not really sink in.


Image


Image
Nomad
Manic Miner
Posts: 600
Joined: Thu Dec 28, 2017 12:38 pm

Re: How to load a 16x16, 32x32, 64x64 sprite on the screen

Post by Nomad »

I am being a proper potato..

I tried to figure out the tile draw routine above, to get it to print a 32x32 graphic but I seem to have gone all adrift. :lol: Could a kind soul help a brother out and get a face on the screen? :D It must be something I am missing about the draw tile loop.

Image

Code: Select all

ORG 50000

	ld b,8
	ld c,8
	call yx2cell
	ld hl,tilegraphic
	call drawtile
	ret
	;
drawtile:	;arrive HL pointing at tile graphic, DE at screen memory
	ld b,32		;loop counter for 32 px high tile
drawtileloop:
	ld a,(hl)	;get byte of graphic
	ld (de),a	;plonk it in screen memory
	inc hl		;next byte of graphic
	inc e		;next column of screen
	ld a,(hl)	;right hand side of tile
	ld (de),a
	inc hl		;next byte of graphic
	dec e		;back to left hand side of tile
	call nextlinedown	;move DE down one screen line
	djnz drawtileloop
	ret
	;
nextlinedown:	;arrive DE at screen address, move it down one pixel line
	inc d
	ld a,d
	and 7
	ret nz
	ld a,e
	add a,32
	ld e,a
	ret c
	ld a,d
	sub 8
	ld d,a
	ret
	;
yx2cell:	;arrive with b=y column 0-23, c=x column 0-31, point DE at corresponding screen address
	ld a,b
	rrca
	rrca
	rrca
	and 224
	or c
	ld e,a
	ld a,b
	and 24
	or 64
	ld d,a
	ret
	;
tilegraphic:
	DEFB	  0,  0,  0,  0,  0,  0,  0,  0
	DEFB	  0,  7,255,240,  0, 31,255,224
	DEFB	  0, 63,255,192,  0, 48, 15,128
	DEFB	  0, 32,  7,  0,  0, 32,  4,  0
	DEFB	  0, 46,116,  0,  0, 32,  4,  0
	DEFB	  0, 36, 36,  0,  1,234, 87,128
	DEFB	  1, 46,116,128,  1, 42, 84,128
	DEFB	  1, 36, 36,128,  1, 32,  4,128
	DEFB	  7, 66, 66,224,  4,142,113, 32
	DEFB	  5, 10, 80,160,  3, 10, 80,192
	DEFB	  1, 12, 48,128,  1,  7,224,128
	DEFB	  1,  0,  0,128,  0,128,  1,  0
	DEFB	  0, 99,198,  0,  0,144,  9,  0
	DEFB	  1, 17,136,128, 15,  8, 17,240
	DEFB	 17,199,227,  8, 16, 51,198,  8
	DEFB	 32, 27,220,  4, 32,  7,240,  4
	DEFB	 56, 56, 56, 56, 56, 56, 56, 56
	DEFB	 56, 56, 56, 56, 56, 56, 56, 56
	;

END 50000
What's worse is I think I will need to use a 64x64 pixel tile in the end anyway as the face will be too small.

What I am planning to do is have a large face in the centre of the screen. The thing is I am planning to divide all of the faces into 4 parts so they can be swapped in and out as needed to generate different people. (my way of saving space.


This way you can a guy with a bunch of different hats/hair, eyes, chins,noses.

Thing is the tile is not going to move, but I don't want to waste the space of using a screen just to display a head. even if I just include the rows that the head will appear its still would have 192 pixel width for each row. That is just wasteful.
User avatar
RMartins
Manic Miner
Posts: 776
Joined: Thu Nov 16, 2017 3:26 pm

Re: How to load a 16x16, 32x32, 64x64 sprite on the screen

Post by RMartins »

If your tile is 32x32 px this code is wrong:

Code: Select all

drawtileloop:
	ld a,(hl)	;get byte of graphic
	ld (de),a	;plonk it in screen memory
	inc hl		;next byte of graphic
	inc e		;next column of screen
	ld a,(hl)	;right hand side of tile
	ld (de),a
	inc hl		;next byte of graphic
	dec e		;back to left hand side of tile
	call nextlinedown	;move DE down one screen line
You are only copying 2 bytes, and you need to copy 4 bytes per line.
32 pixels / 8 pixels/byte = 4 byte(s)
Nomad
Manic Miner
Posts: 600
Joined: Thu Dec 28, 2017 12:38 pm

Re: How to load a 16x16, 32x32, 64x64 sprite on the screen

Post by Nomad »

Code: Select all

	ld a,(hl)	;get byte of graphic
	ld (de),a	;plonk it in screen memory
	inc hl		;next byte of graphic
	inc e		;next column of screen
	ld a,(hl)	;right hand side of tile
	ld (de),a
	inc hl		;next byte of graphic
	dec e		;back to left hand side of tile
	ld a,(hl)	;get byte of graphic
	ld (de),a	;plonk it in screen memory
	inc hl		;next byte of graphic
	inc e		;next column of screen	
	ld a,(hl)	;right hand side of tile
	ld (de),a
	inc hl		;next byte of graphic
	dec e		;back to left hand side of tile
I got half the fellas face on the screen now... lol time for the other bytes.. I realise what I did.

So I need 4 on each line as you say, but in what order do I need to be inc / dec ?

I tried inc / dec / inc / dec then next line

but it seems to have screwed everything up...
User avatar
RMartins
Manic Miner
Posts: 776
Joined: Thu Nov 16, 2017 3:26 pm

Re: How to load a 16x16, 32x32, 64x64 sprite on the screen

Post by RMartins »

Big confusion in your head.

In a single pixel line, all 32 bytes that exist in a line, the DE address increment by one, to reach the next right neighbour.
So DE (or just E) just needs to increment (if you reach the screen right margin, you might get into trouble with just inc E).

Depending on how you have your sprite organized, it should also just increment to get the next byte for the right neighbour.

So resuming, for a single line it's always increment.

Code: Select all

	ld a,(hl)	;get byte of graphic
	ld (de),a	;plonk it in screen memory
	inc hl	;next byte of graphic
	inc e		;next column of screen

	ld a,(hl)	;right hand side of tile
	ld (de),a	;plonk it in screen memory
	inc hl	;next byte of graphic
	inc e	;back to left hand side of tile

	ld a,(hl)	;get byte of graphic
	ld (de),a	;plonk it in screen memory
	inc hl	;next byte of graphic
	inc e		;next column of screen	

	ld a,(hl)	;right hand side of tile
	ld (de),a	;plonk it in screen memory
	inc hl	;next byte of graphic
	inc e		;back to left hand side of tile
If you look closely it's the same code repeated 4 times.
I would suggest you use this to simplify

Code: Select all

push DE
... place the code above, in here
pop DE
The idea, is to get to the line start, so that we can calculate the next line in the correct X position (address).

P.S.
I would suggest that you try to understand the screen layout, instead of trying stuff randomly.
Nomad
Manic Miner
Posts: 600
Joined: Thu Dec 28, 2017 12:38 pm

Re: How to load a 16x16, 32x32, 64x64 sprite on the screen

Post by Nomad »

Yes you are correct its a case of me being a dummy and not understanding what's going on.


Image
User avatar
RMartins
Manic Miner
Posts: 776
Joined: Thu Nov 16, 2017 3:26 pm

Re: How to load a 16x16, 32x32, 64x64 sprite on the screen

Post by RMartins »

You are welcome.

If you understand the screen layout, then you can come up with your own routines, and tricks to output whatever you want.
Post Reply