Im sure most people will be use to the idea of sprites and masks, but just incase ill quickly recap on the details of them before going into the trick im using for Dizzy. A sprite is usually a chunk of memory representing the pixels for the graphic, you need to know the width and height of the sprite so you know how to deal with the chunk of memory, but thats about it. Below you can see the sprite for the Dizzy character, its 25 pixels wide and 22 pixels high. The magenta colours represent the areas that are not actual pixels we want to see when we draw the sprite to the screen.
If we just copied this sprite to the screen using LDIR or similar instructions we would end up with something that looked like the following screen shot.
It should be fairly obvious this often isnt the effect we are looking for. Normally to get around this we would have a sprite mask. A sprite mask is a graphic where we have a pixel value of 15 where we want to keep the background and a pixel value of 0 where we want our sprite to be. You can see a mask for the Dizzy character below.
This mask allows us to ‘cookie cut’ the background out for our sprite. To do this we simply AND the mask data with the screen data. If the mask is 15 (White in the example) we keep the background, if its 0 then we remove the background. After we run this over the area of the sprite we end up with the following.
Once we have cut out the area for our sprite we can then simply OR the sprite data to the screen to put our sprite back in.
While you can do this in two steps, normally the sprite and mask data are interleaved so we have 1 byte of sprite and 1 byte of mask data. This means we only need to store a single pointer to the sprite data. The actual code for a single pixel would look something like the following, for this example HL is the screen pointer and DE is the mask/graphic pointer.
; Read the sprite LD A,(DE) INC DE LD C, A ; Get the mask LD A,(DE) INC DE ; And it with the screen data AND (HL) ; Or in the graphics OR C ; Write it back to the screen LD (HL), A
This isnt the fastest way to achieve this routine. The fastest way that springs to mind is to use the stack to read both the mask and sprite data at once. This would give a routine that looks something like this
; Read mask and sprite data POP DE ; Read the screen LD A, (HL) ; Mask and OR AND D OR E ; Store and move on LD (HL), A INC HL
This is off the top of my head, there may be a quicker way still without going to compiled sprites, maybe ill cover them in another blog entry. For now in Dizzy I decided to go a slightly different way.
Due to the size of the graphic data I have currently decided to generate the mask at run time while drawing. This may seem crazy, and I may yet change my mind. For now I use pixel colour 0 as transparent, i read the pixel data in and use that to look up the mask from a 256 byte table. Aligning the table on a 256 byte boundary means I can preload the high byte of a register pair with the address of the table and read the pixel data into the low byte, this gives me the address to look up the mask from.
The routine im currently using looks something like this.
; Get the high byte of the table address LD B, MaskTable / 256 ; Read the pixel data into the low byte LD A, (DE) LD C, A ; Read the mask from the table LD A, (BC) ; Mask and Or AND (HL) OR C ; Store and move on LD (HL), A INC DE INC L
Its not pretty but it works, The INC L rather than INC HL is deliberate as on the Sam Coupe the screen line is at most 128 bytes so INC L will do and its a bit faster.
I may yet decided to go with the traditional mask method for speed, time will tell.