Recently I issued the third Z80 programming challenge for the ZX Spectrum:
The deadline is Monday 30th March, midday (GMT).
Target: under 125 bytes.
- The X and Y coordinates are in pixels with 0,0 at the top left.
- The sprite needs to be clipped if it goes over the screen edge.
- Sprite data can be formatted however you like within 64 bytes.
- Programs must return. The
RET
instruction is included in the size. - So everyone has a fair chance comment with the code size not code.
- There are no prizes, just the chance to show off your coding skills.
Solutions can be emailed to digital.wilderness@googlemail.com or posted here after the deadline.
Final Results
Congratulations to everyone who rose to the challenge, this was a tough one. Adrian claimed an impressive victory with a neat piece of self-modifying code. Here are the final results:
Coder | Size |
---|---|
Adrian Brown | 68 |
John Metcalf | 88 |
Ralph Becket | 97 |
Arcadiy Gobuzov | 99 |
Winning Entry
Adrian Brown submitted an ingenious solution in only 68 byte. The code displays a sprite pixel by pixel in approx 18ms. The instruction at DS_SetResOp
is modified to set, reset or leave the appropriate bit.
DrawSprite:
; At most we want to draw 16 lines (lets store
; the 4 onto c as well as its saves a byte)
ld bc, 01004h
DS_YLoop:
; Gotta be able to stop doing all this push/pop
; with exx at some point - but hey ho
push bc
push de
; Splitting is actually helpful as it gives us
; the byte increase on clipping :D
DS_XLoop1:
; Lets get that data byte
ld b, 4
DS_XLoop2:
; Bit cheaty, roll the actual data, it will end
; up back as it started so thats fine
ld a, (hl)
rlca
rlca
ld (hl), a
; Store the data pointer
push hl
; See if we want to draw or not, bit sneaky
; because of data layout
or %10011111
ld l,a
; Now calculate the screen address, start it
; here so carry is clear
ld a,e
rra
; Lets use the check to set the C flag
cp 96
jr nc, DS_SkipPixel
rra
or a
rra
push af
xor e
and %11111000
xor e
ld h,a
; Now work out the opcode for set/res bit (we need
; 01 for bit, 10 for res and 11 for set - so data
; needs to be 10 for bit, 01 for res and 00 for set)
ld a,d
and %00000111
rlca
rlca
; Thats nice, this will do the cpl for us on the
; bit number ;)
xor l
rla
ld (DS_SetResOp + 1),a
pop af
xor d
and %00000111
xor d
; Move across - check for clipping, do it here so
; we can use a as a value > 192
inc d
jr nz, DS_NoClipX
; Stick Y off the bottom so the rest of the line is clipped
; we can use a at this point as its got to be > 192
ld e, a
DS_NoClipX:
rrca
rrca
rrca
ld l,a
; Go set/res the bit
DS_SetResOp:
set 0, (hl)
DS_SkipPixel:
; Store the data pointer
pop hl
; Go do the byte of data
djnz DS_XLoop2
; Now we need to move to the next bytes
inc hl
dec c
jr nz, DS_XLoop1
pop de
pop bc
; Just increase down
inc e
djnz DS_YLoop
ret
;***********************************************************
; Sprite Data twiddled a bit, Mask/Data/Mask/Data
; Mask = 0 we want the screen, set data to 1 means we convert
; the set/res into a bit which is fine, All rolled right
; three bit to get the pixel data i want in bits 3+4
;***********************************************************
SpriteData:
db %10101010, %01001011, %10110100, %10101010
db %10101010, %11110101, %01011111, %10101010
db %11001010, %01011111, %11110101, %10110010
db %01101011, %01010101, %01010101, %10111100
db %11001101, %11010101, %01010101, %00110111
db %11001101, %11110111, %01010101, %00110111
db %01010111, %11010101, %01110101, %11010101
db %01010111, %01010101, %01010101, %11011101
db %01010111, %01010101, %01110101, %11010101
db %01010111, %01010101, %11010101, %11011101
db %11001101, %01010101, %01110111, %00110111
db %11001101, %11010101, %11011101, %00110111
db %01101011, %01110101, %01010111, %10111100
db %11001010, %01011111, %11110101, %10110010
db %10101010, %11110101, %01011111, %10101010
db %10101010, %01001011, %10110100, %10101010
Here my own solution in 88 bytes. This displays the sprite row by row and is slightly faster, taking approx 5ms.
; called with hl = address of sprite, de = position on screen
putsprite:
ld c,16
nextline:
ld a,d
and 7
inc a
ld b,a
ld a,e
rra
cp 96
ret nc
rra
or a
rra
push de
push hl
ld l,a
xor e
and 248
xor e
ld h,a
ld a,l
xor d
and 7
xor d
rrca
rrca
rrca
ld l,a
ld e,255
spd:
ex (sp),hl
ld a,(hl)
inc hl
ld d,(hl)
inc hl
ex (sp),hl
push bc
rrc e
jr noshift
shiftspr:
rra
rr d
rr e
noshift:
djnz shiftspr
push hl
ld b,3
mask:
bit 0,e
jr z,bm1
and (hl)
db 254 ; jr bm2
bm1:
xor (hl)
bm2:
ld (hl),a
inc l
ld a,l
and 31
ld a,d
ld d,e
jr z,clip
djnz mask
clip:
bit 0,e
ld e,0
pop hl
pop bc
jr nz,spd
pop hl
pop de
inc e
dec c
jr nz,nextline
ret
sprite:
db %11111100, %00111111, %00000000, %00000000
db %11110000, %00001111, %00000011, %11000000
db %11100000, %00000111, %00001100, %00110000
db %11000000, %00000011, %00010000, %00001000
db %10000000, %00000001, %00100010, %00000100
db %10000000, %00000001, %00100111, %00000100
db %00000000, %00000000, %01000010, %00010010
db %00000000, %00000000, %01000000, %00001010
db %00000000, %00000000, %01000000, %00010010
db %00000000, %00000000, %01000000, %00101010
db %10000000, %00000001, %00100000, %01010100
db %10000000, %00000001, %00100010, %10100100
db %11000000, %00000011, %00010001, %01001000
db %11100000, %00000111, %00001100, %00110000
db %11110000, %00001111, %00000011, %11000000
db %11111100, %00111111, %00000000, %00000000
I guess this is for the ZX Spectrum? Just checking it's not for Amstrad or MSX ;-p zxchris
ReplyDeleteHi Chris, I've just added "for the ZX Spectrum" to the post, thanks :-)
DeleteHi John, I'm glad I've finally found an example online with some example sprite data. Your version mentions that it starts with de as the position on screen. I assume that's a 16bit screen address (e.g. 16384)
ReplyDeleteAlso, hl is the 'address' of the sprite. What exactly so you mean by that? An example would be great!
Apologies, I've only started with z80 recently!
Andrew
Hi Andrew, sorry for the slow reply. In the second routine D is the x-coordinate to display the sprite in pixels (0 to 255). E is the y-coordinate (0 to 191) and HL is a pointer to the data for the sprite to display.
DeleteIf you use the following before calling the routine it will display the sprite somewhere near the middle of the screen:
LD HL, SPRITE
LD D, 120
LD E, 88
Good luck with Z80 :-)
Many thanks for confirming John. While I can make it work, I don't suppose you have an annotated version? There are several commands in this I haven;t used before, and would love to understand the inner workings myself.
ReplyDeleteAndrew