Programming:Convert CPC sprites to Plus hardware sprites

From CPCWiki - THE Amstrad CPC encyclopedia!
Jump to: navigation, search

The Plus ASIC hardware sprite format is different to the standard CPC sprite format, so sprites have to be converted if you want to use them as hardware sprites on the Plus.

The formats

Plus hardware sprites are 16x16 pixels in size and have 16 colours, with PEN 0 always being transparent. Therefore, when converting standard CPC sprites it is easiest to convert them from MODE 0 because this mode supports a colour depth of 16 and two pixels are stored in one byte.

The pixel data in MODE 0 on the standard CPC is stored in an awkward manner and a byte is organised as follows:

Pixels.png

The top row shows the two pixels and the bottom row shows how they are arranged in one byte on the standard CPC.

The pixel data on the Plus is organised in a straight forward manner being that each byte is a pixel and its value is the PEN colour. For example, the first two lines of a Plus sprite could be:

<geshi lang=Z80> 00,01,02,03,04,05,06,07,08,09,10,11,12,13,14,15 01,02,03,04,05,06,07,08,09,10,11,12,13,14,15,00 </geshi>

Conversion

To convert a sprite, you can download the DSK image below which contains a converter utility (use RUN"DISC") or you can use the source code provided on this page.

To use the converter utility, you must have first captured your standard CPC sprite (you can use the SPRITER utility on the disc to do this) which must be 16 x 16 in size (8 bytes wide by 16 lines high). The converter utility will then display the original and converted sprite before saving the converted sprite to the disk.

Download

Media:Convspr.dsk - DSK image containing the converter utility

Source code

The source code for the converter (which will assemble in WinApe and Maxam) is as follows:

<geshi lang=Z80>
;Convert CPC sprite to Plus sprite
;by Redbox, February 2010
;Public Domain

		org &8000

		ld b,16			;loop counter, sprite is 16 lines high

mainloop:	push bc			;preserve loop counter

		call doline
		ld hl,(scraddr)		;load HL with screen address
		call nextline		;work out next screen line down
		ld (scraddr),hl		;and store the screen address

		pop bc			;restore loop counter
		djnz mainloop		;decrease B and jump back to loop if not 0

		ret			;exit

doline:		ld b,8			;loop counter, sprite is 8 bytes wide = 16 pixels

lineloop:	push bc			;store counter

		ld hl,(scraddr)		;get screen location
		ld a,(hl)		;and load data from it into A

		bit 7,a			;does bit equal 1? if so, reset Z
		ld b,&1			;pixel 0, bit 0 = &1
		call nz,addbit		;add the bit to our pixel

		bit 3,a			;does bit equal 1? if so, reset Z
		ld b,&2			;pixel 0, bit 1 (%0010) = &2
		call nz,addbit		;add the bit to our pixel

		bit 5,a			;does bit equal 1? if so, reset Z
		ld b,&4			;pixel 0, bit 2 (%0100) = &4
		call nz,addbit		;add the bit to our pixel

		bit 1,a			;does bit equal 1? if so, reset Z
		ld b,&8			;pixel 0, bit 3 (%1000) = &8
		call nz,addbit		;add the bit to our pixel

		call storeit		;store pixel at sprite address

		ld a,&0			;reset pixel
		ld (pixel),a		;and store reset

		ld hl,(scraddr)		;get data from current screen location
		ld a,(hl)		;and load it into A

		bit 6,a			;does bit equal 1? if so, reset Z
		ld b,&1			;pixel 1, bit 0 (%0001) = &1
		call nz,addbit		;add the bit to our pixel

		bit 2,a			;does bit equal 1? if so, reset Z
		ld b,&2			;pixel 1, bit 1 (%0010) = &2
		call nz,addbit		;add the bit to our pixel

		bit 4,a			;does bit equal 1? if so, reset Z
		ld b,&4			;pixel 1, bit 2 (%0100) = &4
		call nz,addbit		;add the bit to our pixel

		bit 0,a			;does bit equal 1? if so, reset Z
		ld b,&8			;pixel 1, bit 3 (%1000) = &8
		call nz,addbit		;add the bit to our pixel

		call storeit		;store pixel at sprite address

		ld a,&0			;reset pixel
		ld (pixel),a		;and store reset

		ld hl,(scraddr)		;get screen address
		inc hl			;increase it
		ld (scraddr),hl		;store it for next time

		pop bc			;restore loop counter
		djnz lineloop		;decrease B and jump back to loop if not 0

		ret			;else return

addbit:		push af			;preserve screen data
		ld a,(pixel)		;load A with pixel 
		add b			;add what we've found to it
		ld (pixel),a		;store it again
		pop af			;restore screen data
		ret

storeit:	ld hl,(spraddr)		;load HL with sprite address
		ld a,(pixel)		;load pixel into A
		ld (hl),a		;load sprite address with pixel value
		inc hl			;increase sprite address
		ld (spraddr),hl		;and store it for next time
		ret			

nextline:	ld a,l			;load A with L (low byte of screen address)
		sub 8			;subtract the 8 bytes we've already done
		ld l,a			;and load it back into L
		ld a,8			;load A with 8
	        add a,h         	;which means we add &800 for the next pixel line down
	        ld h,a			;write new address back to H
	        ret nc          	;and return if no overflow

	        ld de,&C050		;else loop back round to top of screen plus one pixel line
	        add hl,de       	;by adding &C000+&50 to HL
	        ret

scraddr:	dw	&C000		;screen address to grab CPC sprite from

pixel:		db	&0		;pixel store

spraddr:	dw	&9000		;address Plus sprite is stored (&100 bytes in length)
</geshi>

See also