Wednesday, December 05, 2007

65816: Cool bitmap stuff...

Yeah, I know I know.... I should be doing XeO3, but the lure of 65816 code is too much just now! :)

Anyway... After doing my block transfer code for copying a bitmap, I started wondering how expensive it would be to physically draw the screen everyframe from a tileset. This then got me thinking about making a software character map. If you could do a 16bit character map, you could extend the characters available, and even do some pretend hardware flipping! So I did some basic code (no visuals yet) to see how bad it was, and was pretty pleased. It worked out about 1.5 to x2 the raw block copy. Not bad at all! After playing with the hardware FLIP (on Y) idea, it came out to about x2 to x2.5 which is still pretty good, and actually BETTER than my current character screen copy! (code shown below)
     ldx #8000-8
BlitLoop2
lda $9000+1000 ; get character
dec BlitLoop2+1
dec BlitLoop2+1
and #$3ff
asl
asl
asl
tay ; X=Character address

lda $8000,y ; copy character
sta $4000,x
lda $8002,y
sta $4002,x
lda $8004,y
sta $4004,x
lda $8006,y
sta $4006,x
txa
sbc #7 ; -8 coz carry is clear
tax
bpl BlitLoop2


Imagine it.... a character map screen that has proper bitmap colouring, a colour screen that is double buffered, and around 1024 (at least) characters. Very cool. On top of all that, your software sprites DONT steal any of them, and DONT have to draw into chars before going on screen.

Then I started thinking... On the 65816 Direct page is moveable.... So why not point it at the bitmap screen. This could save 2 cycles per STA - a LOT when dealing with a bitmap. The results are amazing to say the least!! Believe it or not, its now about the SAME time as the raw block transfer instruction!!! How amazing is that!! (basic code below)
DoChar     macro 
lda charScr+\0 ; get character
and #$3ff
asl
asl
asl
tay ; X=Character address
lda $8000,y ; copy character
sta $00+\1
lda $8002,y
sta $02+\1
lda $8004,y
sta $04+\1
lda $8006,y
sta $06+\1
endm

; Actual loop....

phd
lda #31
sta LoopCounter+1
lda #$4000
tcd
ldx #0
BlitLoop:
DoChar 0,0
DoChar 2,(8*1)
DoChar 4,(8*2)
DoChar 6,(8*3)
DoChar 8,(8*4)
;
; etc...
;
DoChar 60,(8*30)
DoChar 62,(8*31)

txa
clc
adc #64
tax

tdc
clc
adc #$100
tcd

dec LoopCounter+1

LoopCounter:
lda #$0101
beq AllDone
jmp BlitLoop
AllDone:
; 8 more DoChar's to finish off....



So I can now not ONLY copy a bitmap, but create a whole screen in realtime from a 16bit charactermap. This means you can do all the old tricks of animating a couple of characters and the whole screen could change (as I do for my turrets in XeO3), and if you really wanted you could draw huge character style sprites/baddies again.

I need to verify all this by actually drawing a screen (probably get the XeO3 scrolling going), but it looks really REALLY cool.....

1 comment:

Anonymous said...

If you want the maximum possible speed out of your blitting loop, you can remap both the direct page and stack pointer and use the PEI instructions to move 16-bits worth of data in only 6 cycles. I don't know the details of your machine, but it may be implemented like this:

DoChar macro
lda charScr+\0 ; get character
and #$3ff
asl
asl
asl
ora #$8006 ; OK, since bottom 3 bits are zero
tcd ; DP=Character address
pei $06
pei $04
pei $02
pei $00
endm

; Actual loop....

phd
lda #31
sei ; disable interrupts!
tsc
sta stkSave

lda #$4000+8*32
tcs
ldx #loopCount
BlitLoop:
DoChar 62
DoChar 60
;
; etc..., must draw in reverse order
; because of stack mapping
DoChar 2
DoChar 0

tsc
clc
adc #$200
tcs

dex
bne BlipLoop

lda stkSave
tcs
cli