mirror of
https://github.com/irmen/prog8.git
synced 2025-01-14 01:29:55 +00:00
added sprites library module (cx16 only)
This commit is contained in:
parent
09a7a4bbe5
commit
9cc0cda0fb
102
compiler/res/prog8lib/cx16/sprites.p8
Normal file
102
compiler/res/prog8lib/cx16/sprites.p8
Normal file
@ -0,0 +1,102 @@
|
||||
; Simple routines to control sprites.
|
||||
; They're not written for high performance, but for simplicity.
|
||||
; That's why they control 1 sprite at a time. The exception is pos_batch().
|
||||
; which is quite efficient to update sprite positions of multiple sprites in one go.
|
||||
|
||||
; note: sprites z-order will be in front of all layers.
|
||||
; note: collision mask is not supported here yet.
|
||||
|
||||
sprites {
|
||||
const uword VERA_SPRITEREGS = $fc00 ; $1fc00
|
||||
const ubyte PALETTE_OFFSET = 16 ; color palette indices 16-31
|
||||
const ubyte SIZE_8 = 0
|
||||
const ubyte SIZE_16 = 1
|
||||
const ubyte SIZE_32 = 2
|
||||
const ubyte SIZE_64 = 3
|
||||
const ubyte COLORS_16 = 0
|
||||
const ubyte COLORS_256 = 128
|
||||
uword @zp sprite_reg
|
||||
|
||||
sub init(ubyte spritenum,
|
||||
ubyte databank, uword dataaddr,
|
||||
ubyte width_flag, ubyte height_flag,
|
||||
ubyte colors_flag) {
|
||||
hide(spritenum)
|
||||
cx16.VERA_DC_VIDEO |= %01000000 ; enable sprites globally
|
||||
dataaddr >>= 5
|
||||
dataaddr |= (databank as uword)<<11
|
||||
sprite_reg = VERA_SPRITEREGS + spritenum*$0008
|
||||
cx16.vpoke(1, sprite_reg, lsb(dataaddr)) ; address 12:5
|
||||
cx16.vpoke(1, sprite_reg+1, colors_flag | msb(dataaddr)) ; 4 bpp + address 16:13
|
||||
cx16.vpoke(1, sprite_reg+6, %00001100) ; z depth %11 = in front of both layers, no flips
|
||||
cx16.vpoke(1, sprite_reg+7, height_flag<<6 | width_flag<<4 | PALETTE_OFFSET>>4) ; 64x64 pixels, palette offset
|
||||
}
|
||||
|
||||
sub pos(ubyte spritenum, word xpos, word ypos) {
|
||||
sprite_reg = VERA_SPRITEREGS + 2 + spritenum*$0008
|
||||
cx16.vpoke(1, sprite_reg, lsb(xpos))
|
||||
cx16.vpoke(1, sprite_reg+1, msb(xpos))
|
||||
cx16.vpoke(1, sprite_reg+2, lsb(ypos))
|
||||
cx16.vpoke(1, sprite_reg+3, msb(ypos))
|
||||
}
|
||||
|
||||
sub pos_batch(ubyte first_spritenum, ubyte num_sprites, uword xpositions_ptr, uword ypositions_ptr) {
|
||||
sprite_reg = VERA_SPRITEREGS + 2 + first_spritenum*$0008
|
||||
cx16.vaddr_autoincr(1, sprite_reg, 0, 8)
|
||||
cx16.vaddr_autoincr(1, sprite_reg+1, 1, 8)
|
||||
repeat num_sprites {
|
||||
cx16.VERA_DATA0 = @(xpositions_ptr)
|
||||
xpositions_ptr ++
|
||||
cx16.VERA_DATA1 = @(xpositions_ptr)
|
||||
xpositions_ptr ++
|
||||
}
|
||||
sprite_reg += 2
|
||||
cx16.vaddr_autoincr(1, sprite_reg, 0, 8)
|
||||
cx16.vaddr_autoincr(1, sprite_reg+1, 1, 8)
|
||||
repeat num_sprites {
|
||||
cx16.VERA_DATA0 = @(ypositions_ptr)
|
||||
ypositions_ptr ++
|
||||
cx16.VERA_DATA1 = @(ypositions_ptr)
|
||||
ypositions_ptr ++
|
||||
}
|
||||
}
|
||||
|
||||
sub setx(ubyte spritenum, word xpos) {
|
||||
sprite_reg = VERA_SPRITEREGS + 2 + spritenum*$0008
|
||||
cx16.vpoke(1, sprite_reg, lsb(xpos))
|
||||
cx16.vpoke(1, sprite_reg+1, msb(xpos))
|
||||
}
|
||||
|
||||
sub sety(ubyte spritenum, word ypos) {
|
||||
sprite_reg = VERA_SPRITEREGS + 4 + spritenum*$0008
|
||||
cx16.vpoke(1, sprite_reg, lsb(ypos))
|
||||
cx16.vpoke(1, sprite_reg+1, msb(ypos))
|
||||
}
|
||||
|
||||
sub getx(ubyte spritenum) -> word {
|
||||
sprite_reg = VERA_SPRITEREGS + 2 + spritenum*$0008
|
||||
return mkword(cx16.vpeek(1, sprite_reg+1), cx16.vpeek(1, sprite_reg)) as word
|
||||
}
|
||||
|
||||
sub gety(ubyte spritenum) -> word {
|
||||
sprite_reg = VERA_SPRITEREGS + 4 + spritenum*$0008
|
||||
return mkword(cx16.vpeek(1, sprite_reg+1), cx16.vpeek(1, sprite_reg)) as word
|
||||
}
|
||||
|
||||
sub hide(ubyte spritenum) {
|
||||
pos(spritenum, -64, -64)
|
||||
}
|
||||
|
||||
sub flipx(ubyte spritenum, bool flipped) {
|
||||
cx16.vpoke_mask(1, VERA_SPRITEREGS + 6 + spritenum*$0008, %11111110, flipped)
|
||||
}
|
||||
|
||||
sub flipy(ubyte spritenum, bool flipped) {
|
||||
cx16.vpoke_mask(1, VERA_SPRITEREGS + 6 + spritenum*$0008, %11111101, flipped<<1)
|
||||
}
|
||||
|
||||
sub set_palette_offset(ubyte spritenum, ubyte offset) {
|
||||
sprite_reg = VERA_SPRITEREGS + 7 + spritenum*$0008
|
||||
cx16.vpoke_mask(1, sprite_reg, %11110000, offset>>4)
|
||||
}
|
||||
}
|
@ -98,6 +98,8 @@ class TestCompilerOnExamplesCx16: FunSpec({
|
||||
"vtui/testvtui",
|
||||
"pcmaudio/play-adpcm",
|
||||
"pcmaudio/stream-wav",
|
||||
"sprites/dragon",
|
||||
"sprites/dragons",
|
||||
"amiga",
|
||||
"bdmusic",
|
||||
"bobs",
|
||||
|
@ -472,5 +472,17 @@ It includes an interrupt routine to handle simple Attack/Release envelopes as we
|
||||
See the examples/cx16/bdmusic.p8 program for ideas how to use it.
|
||||
|
||||
Read the `source code <https://github.com/irmen/prog8/tree/master/compiler/res/prog8lib/cx16/psg.p8>`_
|
||||
to see what's in there. (Note: slight variations for different compiler targets)
|
||||
to see what's in there.
|
||||
|
||||
|
||||
sprites (cx16 only)
|
||||
--------------------
|
||||
Available for the Cx16 target. Simple routines to manipulate sprites.
|
||||
They're not written for high performance, but for simplicity.
|
||||
That's why they control one sprite at a time. The exception is the ``pos_batch`` routine,
|
||||
which is quite efficient to update sprite positions of multiple sprites in one go.
|
||||
See the examples/cx16/sprites/dragon.p8 and dragons.p8 programs for ideas how to use it.
|
||||
|
||||
|
||||
Read the `source code <https://github.com/irmen/prog8/tree/master/compiler/res/prog8lib/cx16/sprites.p8>`_
|
||||
to see what's in there.
|
||||
|
@ -2,6 +2,8 @@ TODO
|
||||
====
|
||||
- optimize assembly output for ( word1 & word2 ==0) ... (no need for stack pushes)
|
||||
|
||||
- opimize assembly where pha/pla can be converted into tax/txa (saves 2 cycles, clobbers X)
|
||||
|
||||
- prefix prog8 subroutines with p8s_ instead of p8_ to not let them clash with variables in the asm?
|
||||
|
||||
- allow 'chained' array indexing for expressions: value = ptrarray[0][0]
|
||||
|
@ -1,20 +1,27 @@
|
||||
%import diskio
|
||||
%import textio
|
||||
%import sprites
|
||||
%zeropage basicsafe
|
||||
%option no_sysinit
|
||||
|
||||
; an example that displays and moves a single dragon (actually 2 sprites).
|
||||
|
||||
main {
|
||||
; we choose arbitrary unused vram location for sprite data: $12000
|
||||
const ubyte SPRITE_DATA_BANK = 1
|
||||
const uword SPRITE_DATA_ADDR = $2000
|
||||
|
||||
sub start() {
|
||||
txt.plot(32,30)
|
||||
txt.print("there be dragons!")
|
||||
|
||||
; load the sprite data and color palette directly into Vera ram
|
||||
void diskio.vload_raw("dragonsprite.bin", sprites.DATA_BANK, sprites.DATA_ADDR)
|
||||
void diskio.vload_raw("dragonsprite.bin", SPRITE_DATA_BANK, SPRITE_DATA_ADDR)
|
||||
void diskio.vload_raw("dragonsprite.pal", 1, $fa00 + sprites.PALETTE_OFFSET*2)
|
||||
|
||||
; initialize the dragon sprites
|
||||
sprites.init(1, sprites.DATA_BANK, sprites.DATA_ADDR, sprites.SIZE_64, sprites.SIZE_64) ; top half of dragon
|
||||
sprites.init(2, sprites.DATA_BANK, sprites.DATA_ADDR + 64*64/2, sprites.SIZE_64, sprites.SIZE_64) ; bottom half of dragon
|
||||
sprites.init(1, SPRITE_DATA_BANK, SPRITE_DATA_ADDR, sprites.SIZE_64, sprites.SIZE_64, sprites.COLORS_16) ; top half of dragon
|
||||
sprites.init(2, SPRITE_DATA_BANK, SPRITE_DATA_ADDR + 64*64/2, sprites.SIZE_64, sprites.SIZE_64, sprites.COLORS_16) ; bottom half of dragon
|
||||
|
||||
ubyte tt = 0
|
||||
word xpos = -64
|
||||
@ -48,78 +55,3 @@ main {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
sprites {
|
||||
; sprite registers base in VRAM: $1fc00
|
||||
; Sprite 0: $1FC00 - $1FC07 ; used by the kernal for mouse pointer
|
||||
; Sprite 1: $1FC08 - $1FC0F
|
||||
; Sprite 2: $1FC10 - $1FC17
|
||||
; …
|
||||
; Sprite 127: $1FFF8 - $1FFFF
|
||||
const uword VERA_SPRITEREGS = $fc00 ; $1fc00
|
||||
const ubyte PALETTE_OFFSET = 16 ; color palette indices 16-31
|
||||
const ubyte SIZE_8 = 0
|
||||
const ubyte SIZE_16 = 1
|
||||
const ubyte SIZE_32 = 2
|
||||
const ubyte SIZE_64 = 3
|
||||
; we choose arbitrary unused vram location for sprite data: $12000
|
||||
const ubyte DATA_BANK = 1
|
||||
const uword DATA_ADDR = $2000
|
||||
|
||||
uword @zp sprite_reg
|
||||
|
||||
sub init(ubyte sprite_num, ubyte data_bank, uword data_addr, ubyte width_flag, ubyte height_flag) {
|
||||
hide(sprite_num)
|
||||
cx16.VERA_DC_VIDEO |= %01000000 ; enable sprites globally
|
||||
data_addr >>= 5
|
||||
data_addr |= (data_bank as uword)<<11
|
||||
sprite_reg = VERA_SPRITEREGS + sprite_num*$0008
|
||||
cx16.vpoke(1, sprite_reg, lsb(data_addr)) ; address 12:5
|
||||
cx16.vpoke(1, sprite_reg+1, %00000000 | msb(data_addr)) ; 4 bpp + address 16:13
|
||||
cx16.vpoke(1, sprite_reg+6, %00001100) ; z depth %11 = in front of both layers, no flips
|
||||
cx16.vpoke(1, sprite_reg+7, height_flag<<6 | width_flag<<4 | PALETTE_OFFSET>>4) ; 64x64 pixels, palette offset
|
||||
}
|
||||
|
||||
sub hide(ubyte sprite_num) {
|
||||
pos(sprite_num, -64, -64)
|
||||
}
|
||||
|
||||
sub flipx(ubyte sprite_num, bool flipped) {
|
||||
cx16.vpoke_mask(1, VERA_SPRITEREGS + 6 + sprite_num*$0008, %11111110, flipped)
|
||||
}
|
||||
|
||||
sub flipy(ubyte sprite_num, bool flipped) {
|
||||
cx16.vpoke_mask(1, VERA_SPRITEREGS + 6 + sprite_num*$0008, %11111101, flipped<<1)
|
||||
}
|
||||
|
||||
sub pos(ubyte sprite_num, word xpos, word ypos) {
|
||||
sprite_reg = VERA_SPRITEREGS + 2 + sprite_num*$0008
|
||||
cx16.vpoke(1, sprite_reg, lsb(xpos))
|
||||
cx16.vpoke(1, sprite_reg+1, msb(xpos))
|
||||
cx16.vpoke(1, sprite_reg+2, lsb(ypos))
|
||||
cx16.vpoke(1, sprite_reg+3, msb(ypos))
|
||||
}
|
||||
|
||||
sub setx(ubyte sprite_num, word xpos) {
|
||||
sprite_reg = VERA_SPRITEREGS + 2 + sprite_num*$0008
|
||||
cx16.vpoke(1, sprite_reg, lsb(xpos))
|
||||
cx16.vpoke(1, sprite_reg+1, msb(xpos))
|
||||
}
|
||||
|
||||
sub sety(ubyte sprite_num, word ypos) {
|
||||
sprite_reg = VERA_SPRITEREGS + 4 + sprite_num*$0008
|
||||
cx16.vpoke(1, sprite_reg, lsb(ypos))
|
||||
cx16.vpoke(1, sprite_reg+1, msb(ypos))
|
||||
}
|
||||
|
||||
sub getx(ubyte sprite_num) -> word {
|
||||
sprite_reg = VERA_SPRITEREGS + 2 + sprite_num*$0008
|
||||
return mkword(cx16.vpeek(1, sprite_reg+1), cx16.vpeek(1, sprite_reg)) as word
|
||||
}
|
||||
|
||||
sub gety(ubyte sprite_num) -> word {
|
||||
sprite_reg = VERA_SPRITEREGS + 4 + sprite_num*$0008
|
||||
return mkword(cx16.vpeek(1, sprite_reg+1), cx16.vpeek(1, sprite_reg)) as word
|
||||
}
|
||||
}
|
||||
|
57
examples/cx16/sprites/dragons.p8
Normal file
57
examples/cx16/sprites/dragons.p8
Normal file
@ -0,0 +1,57 @@
|
||||
%import diskio
|
||||
%import textio
|
||||
%import sprites
|
||||
%zeropage basicsafe
|
||||
%option no_sysinit
|
||||
|
||||
; an example that displays and then moves many sprites at once.
|
||||
|
||||
main {
|
||||
; we choose arbitrary unused vram location for sprite data: $12000
|
||||
const ubyte SPRITE_DATA_BANK = 1
|
||||
const uword SPRITE_DATA_ADDR = $2000
|
||||
|
||||
const ubyte NUM_DRAGONS = 25
|
||||
word[NUM_DRAGONS*2] xpositions
|
||||
word[NUM_DRAGONS*2] ypositions
|
||||
|
||||
sub start() {
|
||||
txt.plot(30,30)
|
||||
txt.print("there be many dragons!")
|
||||
|
||||
; load the sprite data and color palette directly into Vera ram
|
||||
void diskio.vload_raw("dragonsprite.bin", SPRITE_DATA_BANK, SPRITE_DATA_ADDR)
|
||||
void diskio.vload_raw("dragonsprite.pal", 1, $fa00 + sprites.PALETTE_OFFSET*2)
|
||||
|
||||
; initialize the dragon sprites (every dragon needs 2 sprites)
|
||||
ubyte sprite_num
|
||||
for sprite_num in 0 to NUM_DRAGONS*2-2 step 2 {
|
||||
sprites.init(sprite_num+1, SPRITE_DATA_BANK, SPRITE_DATA_ADDR, sprites.SIZE_64, sprites.SIZE_64, sprites.COLORS_16) ; top half of dragon
|
||||
sprites.init(sprite_num+2, SPRITE_DATA_BANK, SPRITE_DATA_ADDR + 64*64/2, sprites.SIZE_64, sprites.SIZE_64, sprites.COLORS_16) ; bottom half of dragon
|
||||
|
||||
xpositions[sprite_num] = math.rndw() % (640-64) as word
|
||||
xpositions[sprite_num+1] = xpositions[sprite_num]
|
||||
ypositions[sprite_num] = sprite_num * $0008 as word
|
||||
ypositions[sprite_num+1] = ypositions[sprite_num]+64
|
||||
}
|
||||
|
||||
repeat {
|
||||
; move all dragons (remember each one consists of a top and a bottom sprite)
|
||||
for sprite_num in 0 to NUM_DRAGONS*2-2 step 2 {
|
||||
xpositions[sprite_num]++
|
||||
xpositions[sprite_num+1]++
|
||||
if sprite_num & 2 {
|
||||
xpositions[sprite_num]++
|
||||
xpositions[sprite_num+1]++
|
||||
}
|
||||
if xpositions[sprite_num] >= 640
|
||||
xpositions[sprite_num] = -64
|
||||
if xpositions[sprite_num+1] >= 640
|
||||
xpositions[sprite_num+1] = -64
|
||||
}
|
||||
|
||||
sys.waitvsync()
|
||||
sprites.pos_batch(1, NUM_DRAGONS*2, &xpositions, &ypositions)
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user