;;--------------------------------------------------
;; 2BPP PACKED SPRITES
;;--------------------------------------------------
NOTE: This was written with cpc "dual-playfield mode 0" in mind so should be checked for validity for the Enterprise (because of fixbias)...
Here's a method for 2-bits-per-pixel-packing of 16-color mode sprites. Saves half of the memory needed for the sprites, but (as always the case) at the cost of some speed...
I don't think it will be much useful for any of you, but here goes...
1. Configure the palette so that bits 0,1 or 2,3 of the ink number will be used for the background inks.
These would be the "SSBB" or the standard "BBSS" configuration [see next post].
I'll use the standard "BBSS" configuration.
2. Store sprite data in a 2-bits-per-pixel-packed and pre-rotated (one rlca/rrca) form.
This means storing 2 pixel pairs in one byte, so the memory needed for the sprite data is halved.
We use 0,4,8,c for background inks, so for sprite data pixels, bits 2 and 3 are always zero.
So if we have two sprite pixel bytes XY and ST of the bit form x0-y0-x2-y2-x1-y1-x3-y3 and s0-t0-s2-t2-s1-t1-s3-t3, then, after zeroing the redundant bits they become x0-y0-00-00-x1-y1-00-00 and s0-y0-00-00-s1-t1-00-00.
We combine them into one byte and get x0-y0-s0-t0-x1-y1-s1-t1.
Then we rotate all the bytes by one bit right (like rrca) or one bit left (like rlca).
Let's assume that we'll be using right rotation so our packed byte pair becomes t1-x0-y0-s0-t0-x1-y1-s1.
To get the left byte use rrca + and %11001100.
To get the right byte use rlca + and %11001100.
Storing sprites in such form saves us half the memory needed for the data and we can unpack the byte pairs during drawing.
Here's an example of a sprite routine doing this:
;; de=^sprite
;; hl=^screen
ld b,%11001100 ;; preload b with bitmask
ld iyh,sprite_height
draw_looph:
ld iyl,sprite_height*sprite_width
draw_loopw:
ld a,(de) ;; get the byte pair in byte-packed form
ld c,a ;; save for later
rrca ;; rotate to get them in place
and b ;; get left pixel bit pairs
or (hl) ;; combine with background
ld (hl),a ;; save to screen memory
inc hl ;; go to next screen position
ld a,c ;; restore the packed byte
rlca ;; rotate to get them in place
and b ;; get right pixel bit pairs
or (hl) ;; combine with background
ld (hl),a ;; save to screen memory
inc hl ;; go to next screen position
inc de ;; next sprite data byte
dec iyl
jp nz,draw_loopw
;;
;; get next line address code
;;
dec iyh
jp nz,draw_looph
ret
Storing the data in a prerotated state isn't necassary but makes the code somewhat more elegant - one rlca/rrca for one half-byte
You could store the data normally and have no rlca/rrca for one half-byte and have two rlca/rrca for the other half.
However for using this method for flipping or shifting the prerotated storage is better because it evens the times of drawing the two versions of the sprite.
We could use a similar approach for flipping/shifting dual-playfield mode 0 sprites.
Instead of storing every two consecutive byte pairs in one byte, store half-byte of the normal form and half for the flipped/shifted form.
So the XY above would be a normal form byte and ST could be a byte of the flipped/shifted form.
Note that you wouldn't even have to change the direction of storing/loading data.
draw_normal:
;; de=^sprite
;; hl=^screen
ld c,%11001100 ;; preload c with the bit mask
ld iyh,sprite_height
dnorm_looph:
ld b,sprite_width ;; load b with loop count
dnorm_loopw:
ld a,(de)
rrca
and c ;; get left pixel pair bits - the normal sprite version bits
or (hl)
ld (hl),a
inc hl
inc de
djnz dnorm_loopw
;;
;; get next line address code
;;
dec iyh
jp nz,dnorm_looph
ret
draw_flipped: ;; or shifted
;; de=^sprite
;; hl=^screen
ld c,%11001100 ;; preload c with the bit mask
ld iyh,sprite_height
dflip_looph:
ld b,sprite_height*sprite_width
dflip_loopw:
ld a,(de)
rlca
and c ;; get right pixel pair bits - the flipped (shifted) version bits
or (hl)
ld (hl),a
inc hl
inc de
djnz dflip_loopw
;;
;; get next line address code
;;
dec iyh
jp nz,dflip_looph
ret
Note that the only difference between the two routines is the direction of rotation (rlca/rrca)
The possibility to pack two versions of the same sprite (for flipping/shifting) in one seems especially interesting...
-------------------
List of possible palette configurations:
legend:
"BBSS" means that bits 3 and 2 are background ink bits and bits 0,1 are sprite ink bits
LBn - left pixel background ink bit n
RBn - right pixel background ink bit n
LSn - left pixel sprite ink bit n
RSn - right pixel sprite ink bit n
n=0,1
1. BBSS
- sprite data bytes of the form LS0 - RS0 - LB0 - RB0 - LS1 - RS1 - LB1 - RB1
- inks 0, 4, 8, c for background inks
- inks 1 = 5 = 9 = d, 2 = 6 = a = e and 3 = 7 = b = f for sprite (0 as transparent)
- use and %00110011 for erasing sprite bytes
ink: 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f
B0 S1 S2 S3 B1 S1 S2 S3 B2 S1 S2 S3 B3 S1 S2 S3
2. BSBS
- screen data bytes of the form LS0 - RS0 - LS1 - RS1 - LB0 - RB0 - LB1 - RB1
- inks 0, 2, 8, a for background
- inks 1 = 3 = 9 = b, 4 = 6 = c = e and 5 = 7 = d = f for sprite
- use and %00001111 for erasing sprite bytes
ink: 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f
B0 S1 B1 S1 S2 S3 S2 S3 B2 S1 B3 S1 S2 S3 S2 S3
3. BSSB
- screen data bytes of the form LB0 - RB0 - LS1 - RS1 - LS0 - RS0 - LB1 - RB1
- inks 0, 1, 8, 9 for background
- inks 2 = 3 = a = b, 4 = 5 = c = d and 6 = 7 = e = f for sprite
- use and %11000011 for erasing sprite bytes
ink: 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f
B0 B1 S0 S0 S1 S1 S3 S3 B2 B3 S0 S0 S1 S1 S3 S3
4. SBBS
- screen data bytes of the form LS0 - RS0 - LB1 - RB1 - LB0 - RB0 - LS1 - RS1
- inks 0, 2, 4, 6 for background
- inks 1 = 3 = 5 = d, 8 = a = c = e and 9 = b = d = f for sprites
- use and %00111100 for erasing sprite bytes
ink: 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f
B0 S1 B1 S1 B2 S1 B3 S3 S2 S3 S2 S3 S2 S3 S2 S3
5. SSBB
- screen data bytes of the form LB0 - RB0 - LS0 - RS0 - LB1 - RB1 - LS1 - RS1
- inks 0, 1, 2, 3 for background
- inks 4 = 5 = 6 = 7, 8 = 9 = a = b and 9 = b = d = f for sprites
- use and %11001100 for erasing sprite bytes
ink: 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f
B0 B1 B2 B3 S1 S1 S1 S1 S2 S2 S2 S2 S3 S3 S3 S3
6. SBSB
- screen data bytes of the form LB0 - RB0 - LB1 - RB1 - LS0 - RS0 - LS1 - RS1
- inks 0, 1, 4, 5 for background
- inks 2 = 3 = 6 = 7, 8 = 9 = c = d and a = b = e = f for sprites
- use and %11110000 for erasing sprite bytes
ink: 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f
B0 B1 S1 S1 B2 B3 S1 S1 S2 S2 S3 S3 S2 S2 S3 S3
The remaining possibilities:
The following four give you 2 colors for background (you could use xor for drawing/erasing) and 8 colors for sprites:
7. BSSS
[this and SBBB seem to be the only "cpc dual-playfield-like" modes possible on the enterprise computers... (because of the fixbias)]
8. SBSS
9. SSBS
10. SSSB
and
11. SBBB
12. BSBB
13. BBSB
14. BBBS
Once more - note that this can be impossible on the enterprise...
I know I should check it before posting but...I don't want to sacrifice more time on it. I know it's not very useful...so sorry...
However maybe someone has other ideas on packing a sprite for unpacking during the drawing process?