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
Nomad
Manic Miner
Posts: 600
Joined: Thu Dec 28, 2017 12:38 pm

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

Post by Nomad »

Appreciate that there are some great tutorials about animation of sprites, but I can't find a simple example of how to load a static sprite 16x16, 32x32, 64x64 etc.. on the screen using assembly.

I must have missed it, but nearly all of these tutorials start from the animation stage.

The books... ah the books... well I have started to browse through them but to be honest the quality of these things is so low its much better use of my time to ask you fellows for help. Ok its not the books fault they were written before pasmo but the time it takes me to convert the example in the book from whatever archaic listing they supply its not worth it. Hope ya all don't think I am being lazy but the articles I found on the internet are discussing sprite animation, so it pre-supposes the first step..

So any help would be greatly appreciated. Sorry for all the questions.
AndyC
Dynamite Dan
Posts: 1403
Joined: Mon Nov 13, 2017 5:12 am

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

Post by AndyC »

There's really no difference between animating a sprite and just drawing it, an animation is just the result of drawing multiple different versions of a sprite in sequence. In the most simple case (unmasked sprites and plain backgrounds) it's literally just a case of copying the correct number of bytes from one location to the screen - the complexity basically arising from calculating the correct addresses within screen memory to copy bytes to.
dfzx
Manic Miner
Posts: 677
Joined: Mon Nov 13, 2017 6:55 pm
Location: New Forest, UK
Contact:

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

Post by dfzx »

I'm at work so can't work out something specific, but all you're trying to do with, say, a 16x16 static sprite image is copy a bunch of data bytes into screen memory. 16 pixels wide is 2 8-bit bytes side by side. Times 16 pixels deep, that's 32 bytes of graphic data copied into 32 bytes of screen memory.

This gets complicated if you want to put the 16x16 pixel image on any pixel location, but it's much easier if you're OK putting it on a horizontal pixel coordinate which is divisible by 8 because that will have it sitting on an exact screen memory byte.

Assuming you're OK with the 8 pixel boundary restriction for the time being, the gist, ignoring even obvious optimisations for clarity of the example:

* find the first byte of screen memory for the location on screen
* copy the first graphical data byte to that location
* move right one byte on screen
* copy the next graphical data byte to that location
* go back to the first screen memory location (i.e. move back left one byte), then calculate the screen memory address of the byte below it
* go back to the top and repeat 15 more times

So if you want to put the sprite at location 0,0, that's starting screen address 0x4000 (16384). First data byte goes into 0x4000, second goes into 0x4001. The screen line below 0x4000 is 0x4100 (16640) so the next data byte goes there, and the next goes into 0x4101. And so on.

You'll find that working out the initial screen address might be a little tricky, and working out the next screen address down is definitely a little tricky. But you can get help with those issues too.
Derek Fountain, author of the ZX Spectrum C Programmer's Getting Started Guide and various open source games, hardware and other projects, including an IF1 and ZX Microdrive emulator.
User avatar
Ast A. Moore
Rick Dangerous
Posts: 2641
Joined: Mon Nov 13, 2017 3:16 pm

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

Post by Ast A. Moore »

What the guys above said. Plus the following.

I know you’ve expressed frustration with tutorials and how-to books of the past. I think I know where it’s coming from. The key to your frustration (and that of, I’m sure, thousands of others) lies in the fact that either by being misled by someone, or by misleading yourself (by making incorrect assumptions), you convinced yourself that the Spectrum is capable of displaying graphics. The truth is, it’s not. There are no sprites, no pixels. You don’t “draw” or “erase” anything on the screen because there is no screen. It’s all a hoax, a trick, sleight of hand. Thinking that the Spectrum does have all those things is akin to thinking there’s a tiny orchestra inside of a record player. Because that’s what the Spectrum is—a sophisticated (or not so sophisticated, from the contemporary point of view) record player.

Thus, your goal as a programmer is to create an illusion of “sprites” “moving” on a “screen.” Much like carving grooves in a record creates the illusion of a huge orchestra playing a piece of music when the record is being played back.

Once you’ve become comfortable with this concept, you’ll be able to answer many of the questions that arise in your head yourself. Moreover, I think you’ll also be able to appreciate—and understand—at least some of the books you previously dismissed.

I can only fault the authors of those books for not explaining this basic concept from the get go. They themselves often assume that you, the reader, know this already.

Perhaps at some point I’ll expand on this idea and write my own tutorial, but gist of the concept is as follows.

Imagine the Spectrum as a special record player with two cartridges each holding a stylus. One is a read-only cartridge—you, the programmer, have absolutely no control over it. You cannot lift it nor stop it. It is limited to a small portion of the record, which it will read, and, when it reaches the end of that portion, it’ll skip to the beginning of the same portion and start anew.

The other cartridge is capable of reading the entire surface of record, but it is also capable of changing it, i.e. modifying the grooves. And, yes, it can modify the portion that the read-only cartridge can traverse.

The read-only cartridge is the portion of the Spectrum’s ULA that is responsible for forming the video signal; the read-and-modify cartridge is the CPU; and the record itself is—well, the Spectrum’s ROM and RAM.

Remember, you only have indirect control over the read-and-modify cartridge, and it knows nothing about the existence of the read-only cartridge. It doesn’t even know the differences between the ROM and the RAM. As far as it’s concerned, it’s all RAM—that is it “thinks” it can read or modify the entire record from start to finish. That is all it can do—read and modify the grooves on the record. (This is a special kind of record, which doesn’t suffer from wear and tear. You can carve and recarve it infinitely many times.)

Now, which grooves it reads and modifies actually depends on what it reads from the grooves. That’s right. Remember I said you control this cartridge indirectly? That’s because you manually precarve the grooves in the record. Another analogy is the player piano. There’s a roll of paper with holes in it, which is pulled through a sophisticated system of levers. The levers then strike the keys and pedals in a predetermined manner that corresponds to the positions of the holes on the paper roll. Now, it would be trivial to modify the mechanism to punch new holes in the same paper roll, thus changing the data on it. That’s programming in a nutshell for you.

Thus, you “cut the record” in such a way that when the read-and-modify cartridge starts reading it, it executes commands that eventually modify a small portion of the record which is read by the read-only cartridge in such a way that it produces the sound that was never prerecorded on that portion. But it is still and illusion, because it’s all a bunch of squiggly grooves on the surface of the record (or holes in a paper roll). The same squiggles (or hole patterns) have a different effect on each cartridge. The read-only cartridge converts them into sound, while the read-and-modify cartridge treats them as commands to read or modify some more grooves.

With a bit of imagination, I can explain such relatively esoteric concepts as memory contention (two cartridges fighting for the same groove), the ROM (the beginning of the record is made from a special material that the read-and-modify cartridge cannot actually change, no matter how hard it tries—the material is just too tough), and even video screen memory layout (the entire record is just a single continuous groove, but looking at any section of the record, you see several “stacked” grooves—again, an illusion), etc.

Ahem. Okay, digression over. As you were, ensign. We’re back to our original programming (see what I did there?)
Every man should plant a tree, build a house, and write a ZX Spectrum game.

Author of A Yankee in Iraq, a 50 fps shoot-’em-up—the first game to utilize the floating bus on the +2A/+3,
and zasm Z80 Assembler syntax highlighter.
C.Born
Manic Miner
Posts: 209
Joined: Sat Dec 09, 2017 4:09 pm

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

Post by C.Born »

i have never done it myself.
push and pop are the fastest aslong as you remind the stack works top-down

if i try i would start with trying to make a loop from

Code: Select all


        ld hl,16384+1   ; mind topdown!!
adres   ld sp,hl       ; stack has MOVED so MIND RET adresses

adrpx   ld hl,pix
        ld e,(hl)
        inc hl
        ld d,(hl)

        push de   ; 16 bits to 16384 +16385

        ld hl,(adres+2)
        inc (hl)               ; should be like "inc h" to move to next pixel line which is 16384+256

        ld hl,(adrpx+2)
        inc hl
        inc hl 
        ld (adrpx),hl

       jr adres

User avatar
R-Tape
Site Admin
Posts: 6394
Joined: Thu Nov 09, 2017 11:46 am

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

Post by R-Tape »

[mention]Ast A. Moore[/mention] You are Eric Cantona and I claim my 5 pounds!

[mention]C.Born[/mention] nice routine, but using the stack sounds like overkill for this project IMO :)

I've got some tile routines I can add explanations to later.
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 »

Cheers for all the input fellas, it is most appreciated.

Image

That feel when you realise nothing is what it seems, there is no spoon.. no hardware sprites, you get nothing.. everything is in your hands...
Last edited by Nomad on Fri Jan 05, 2018 3:30 pm, edited 1 time in total.
User avatar
Ast A. Moore
Rick Dangerous
Posts: 2641
Joined: Mon Nov 13, 2017 3:16 pm

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

Post by Ast A. Moore »

R-Tape wrote: Fri Jan 05, 2018 1:17 pm @Ast A. Moore You are Eric Cantona and I claim my 5 pounds!
Ah, oui. Vood monsieur vont heez five pounds framed?
Every man should plant a tree, build a house, and write a ZX Spectrum game.

Author of A Yankee in Iraq, a 50 fps shoot-’em-up—the first game to utilize the floating bus on the +2A/+3,
and zasm Z80 Assembler syntax highlighter.
User avatar
Joefish
Rick Dangerous
Posts: 2058
Joined: Tue Nov 14, 2017 10:26 am

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

Post by Joefish »

R-Tape wrote: Fri Jan 05, 2018 1:17 pm Ast A. Moore You are Eric Cantona and I claim my 5 pounds!
Ze spectrum is ze man oo 'ears ze record, and weeps for knowing zat ze orchestra is playing no more. :lol:
User avatar
Ast A. Moore
Rick Dangerous
Posts: 2641
Joined: Mon Nov 13, 2017 3:16 pm

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

Post by Ast A. Moore »

Joefish wrote: Fri Jan 05, 2018 3:16 pm
R-Tape wrote: Fri Jan 05, 2018 1:17 pm Ast A. Moore You are Eric Cantona and I claim my 5 pounds!
Ze spectrum is ze man oo 'ears ze record, and weeps for knowing zat ze orchestra is playing no more. :lol:
Ah, mon chéri, zat ouoz so beautifool.
Every man should plant a tree, build a house, and write a ZX Spectrum game.

Author of A Yankee in Iraq, a 50 fps shoot-’em-up—the first game to utilize the floating bus on the +2A/+3,
and zasm Z80 Assembler syntax highlighter.
User avatar
R-Tape
Site Admin
Posts: 6394
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