drawtile:
;DE points to the sprite data
;BC = (y,x)
; draw an 8x8 tile where X is on [0,11] and Y is on [0,7]
ld a,b
add a,a
add a,b
add a,a
add a,a
add a,a
ld h,0
ld b,h
ld l,a
add hl,hl
add hl,hl
add hl,bc
ld bc,(DrawBufPtr)
add hl,bc
ld bc,12
ld a,8
ex de,hl ; 32
ldi ;128
ex de,hl ; 32
add hl,bc ; 88
inc c ; 32
dec a ; 32
jr nz,$-7 ; 91
ret
And here is how it looks the other way:drawtile:
;DE points to the sprite
;BC = (y,x)
;X*64
or a
ld a,c
ld l,0
rra
rr l
rra
rr l
ld h,a
;y*8
ld a,b
add a,a
add a,a
add a,a
add a,l
ld l,a
ld bc,(DrawBufPtr)
add hl,bc
ex de,hl
ld bc,8
ldir
ret
The former is 565 t-states 33 bytes, the latter is 281 t-states 28 bytes. There are ways to optimise both routines for speed. PutSprite8x8:
;Note: No clipping.
;Inputs:
; BC = (x,y)
; IX points to the sprite
; 1871 worst-case
ld a,b
and $F8
ld h,0
rla \ rl h
rla \ rl h
rla \ rl h
ld l,a
ld a,b
ld b,0
add hl,bc
ld bc,9340h
add hl,bc
;HL points to the first byte to draw at
and 7
jr nz,crossedbound
push ix \ pop de
ld b,8
ld a,(de)
xor (hl)
ld (hl),a
inc hl
inc de
djnz $-5
ret
crossedbound:
ld b,a
dec a
ld (smc_jump1),a
ld (smc_jump2),a
ld a,1
rrca
djnz $-1
dec a
ld e,a
ld c,8
;E is the mask
;IX points to the sprite
;HL points to where to draw
drawloop1:
ld a,(ix)
.db 18h ;start of jr *
smc_jump1:
.db 0
rlca
rlca
rlca
rlca
rlca
rlca
rlca
and e
xor (hl)
ld (hl),a
inc ix
inc hl
dec c
jr nz,drawloop1
ld c,56
add hl,bc
ld a,e
cpl
ld e,a
ld c,8
drawloop2:
ld a,(ix-8)
.db 18h ;start of jr *
smc_jump2:
.db 0
rlca
rlca
rlca
rlca
rlca
rlca
rlca
and e
xor (hl)
ld (hl),a
inc ix
inc hl
dec c
jr nz,drawloop2
ret
That actually turns out to be pretty fast, so if you need to draw sprites, this is still a viable buffer setup.#define lcddelay() in a,(16) \ rlca \ jr c,$-3
ld a,5
out (16),a
lcddelay()
ld a,80h
out (16),a
ld hl,9340h
lcddelay()
ld a,20h
col:
out (16),a
push af
ld bc,4011h
row:
lcddelay()
outi
jr nz,row
lcddelay()
pop af
inc a
cp 2Ch
jr nz,col
ret
.org 9D93h
.db $BB,$6D
Start:
ld a,5 ;set the increment mode, only needs to be done once
out (16),a
lcddelay()
ld a,80h ;set the row pointer, only needs to be done once, since the LCD update routine leaves it where it started.
out (16),a
Main:
<code>
UpdateLCD:
ld hl,9340h
ld a,20h
col:
out (16),a
push af
ld bc,4011h
row:
lcddelay()
outi
jr nz,row
lcddelay()
pop af
inc a
cp 2Ch
jr nz,col
ret
;GetPixelLoc
;Inputs:
; BC =(x,y)
; DE is the buffer on which to draw
;Outputs:
; Returns HL pointing to the byte where the pixel gets plotted
; Returns A as a mask
; NC returned if out of bounds, else C if in bounds
ld a,c \ cp 64 \ ret nc
ld a,b \ cp 96 \ ret nc
and $F8
ld h,0
rla \ rl h
rla \ rl h
rla \ rl h
ld l,a
ld a,b
ld b,0
add hl,bc
add hl,de
;HL points to the first byte to draw at
and 7
ld b,a
ld a,1
inc b
rrca \ djnz $-1
scf
ret
Now to set the pixel, use or (hl) \ ld (hl),a or use xor to invert, and to erase, cpl \ and (hl) \ ld (hl),a. ld hl,9340h ;gbuf
ld de,64
ld c,e
loop:
or a
ld b,12
rr (hl)
push af \ add hl,de \ pop af
djnz $-5
dec h \ dec h \ dec h
inc l
dec c
jr nz,loop
ret
That is now half the speed of what it is for the current gbuf setup. We can cut out 9828 t-states if interrupts are off, though, but that is still a huge hit to speed.;RectangleErase can be optimised to be much faster if you make it its own routine using cpl \ and (hl) for logic. Currently, it uses RectangleOR \ RectangleXOR
;Included Routines
;======================
;Inputs:
; B = Height
; C = Width
; D = X (leftmost)
; E = Y (upper)
;RectangleBoxEraseFill
; Draws a box with a black border, interior is erased.
; Returns B-2, C-2, D+1, E+1
;RectArrowXOR
; Draws an arrow pointing right with XOR logic
; Returns BC,DE unchanged
;RectangleMenu
; Draws a box with rounded edges
; Returns D+1,C+2, B and E unchanged
;RectangleOR
; Draws a black rectangle
; Returns BC,DE unchanged
;RectangleXOR
; Inverts the region of the screen
; Returns BC,DE unchanged
;RectangleErase
; Draws a white rectangle
; Returns BC,DE unchanged
RectangleBoxEraseFill:
call RectangleOR
inc d
inc e
dec b
dec b
dec c
dec c
jp RectangleXOR
RectArrowXOR:
push de
push bc
ld a,b
cp c
jr c,$+3
ld b,c
ld c,1
;B is now the smaller of the two
;C is 1
call RectangleXOR
inc d \ inc e
djnz $+5
pop bc
pop de
ret
djnz $-10
jr $-5
RectangleMenu:
call RectangleErase
push bc \ push de
inc e \ dec b \ dec b
call RectangleXOR
pop de \ pop bc
inc d \ dec c \ dec c
jr RectangleXOR
RectangleErase:
call RectangleOR
RectangleXOR:
;Inputs:
; DE is (x,y)
; BC is (h,w)
;Outputs:
push de
push bc
call RectMain
jr nz,$+11
ld a,c
xor (hl) ;logic
ld (hl),a
inc hl
djnz $-4
jp EndRect
ld d,a ;number of columns before last col
ld e,b
ld a,(rect_FirstByte)
ld c,a
xorrectcol:
ld b,e ;height
push hl
xorrectloop:
ld a,(hl)
xor c ;logic
ld (hl),a
inc hl
djnz xorrectloop
pop hl
ld c,64
add hl,bc
ld c,-1
dec d
jp m,endrect
jr nz,xorrectcol
ld bc,(rect_LastByte)
jp xorrectcol
RectangleOR:
;Inputs:
; DE is (x,y)
; BC is (h,w)
;Outputs:
push de
push bc
call RectMain
jr nz,$+11
ld a,c
or (hl) ;logic
ld (hl),a
inc hl
djnz $-4
jp EndRect
ld d,a ;number of columns before last col
ld e,b
ld a,(rect_FirstByte)
ld c,a
orrectcol:
ld b,e ;height
push hl
orrectloop:
ld a,(hl)
or c ;logic
ld (hl),a
inc hl
djnz orrectloop
pop hl
ld c,64
add hl,bc
ld c,-1
dec d
jp m,endrect
jr nz,orrectcol
ld bc,(rect_LastByte)
jp orrectcol
EndRect:
pop bc
pop de
ret
RectMain:
;Inputs:
; DE is (x,y)
; BC is (h,w)
;Outputs:
; returns z if it is all in one column, else it returns the number of columns
; HL points to the start byte
; (rect_FirstByte), (rect_LastByte)
; A is negative the number of columns
; BC is (h,w)
;
;If it is a single column wide, C is the mask, B is the height
ld a,d
and %11111000
ld h,0
rla \ rl h
rla \ rl h
rla \ rl h
ld l,a
ld a,d
ld d,0
add hl,de
;HL points to the byte it will start on
push hl
ld d,a
push bc
call ComputeByte
ld (rect_FirstByte),a
ex (sp),hl
ld a,d
cpl
and 7
inc a
ld b,a
ld a,l
sub b
ex (sp),hl
call ComputeByte
cpl
ld (rect_LastByte),a
;last and first byte are computed
ld a,d
and %11111000
ld e,a
ld a,d
add a,c
and %11111000
pop hl
ex (sp),hl
ld bc,(DrawBufPtr)
add hl,bc
pop bc
sub e
rrca
rrca
rrca
and %00011111
ret nz
ld de,(rect_FirstByte)
ld a,d \ and e
ld c,a
xor a
ret
ComputeByte:
and 7
ld b,a
ld a,80h
jr z,$+5
rrca
djnz $-1
add a,a
dec a
ret
I also made some text display routines, but they are pretty complicated, so I will have to organise them before posting.
Yeah, in my app the shadow registers are free game (the interrupt routine preserves them). As for using IX, it depends on where it is used, because something like ld a,(ix+n) is 19 t-states, versus 7 for ld a,(hl).I was talking more for the rr (hl) bit, something like:
ld ix,gBuf+128
ld b,64
shiftrightloop:
srl (ix-128) \ rr (ix-64) \ rr (ix) \ rr (ix+64) \ inc ixh ;100
rr (ix-128) \ rr (ix-64) \ rr (ix) \ rr (ix+64) \ inc ixh ;100
rr (ix-128) \ rr (ix-64) \ rr (ix) \ rr (ix+64) ;92
dec ixh \ dec ixh \ inc ix ; 26
djnz shiftrightloop
ret
That is 21210 t-states, which is much nicer!
interrupt: ;19
ex af, af' ;4
exx ;4
out ($31), a ;11 a = 3
outi ;16
jr z, nextColumm ;7
exx ;4
ex af, af' ;4
ei ;4
ret ;10
;83 total
;RAM required:
;fmt_leftmost 1 byte , define the left edge of where text can be drawn
;fmt_rightmost 1 byte , define the right edge of where text can be drawn
;fmt_lower 1 byte , define the lower edge of where text can be drawn
;fmt_upper 1 byte , define the upper edge of where text can be drawn
;bit wordrawp,(iy+textflags) ;define your own location and whatnot
;textcol 1 byte
;textrow 1 byte
;fontptr 2 bytes points to the nibble packed 4x6 fontset
;DrawBufPtr 2 bytes, points to the buffer where text gets drawn
;lFont_record is defined by the ti83plus.inc. 6 bytes are used
;
;Sample Fontset included
;Inlcuded Routines, Input:
; IGPutSFmt null-terminated string, directly following the call
; GPutSFmt HL points to the null terminated string
; GPutCFmt A is the character to display (all formatting applied)
; GPutC B is the character to display (some formatting applied)
;
;All text is written to the buffer with OR logic.
;These routines are modified for vertically aligned buffers, not the way the OS does it.
IGPutSFmt:
;Inputs:
; The null-terminated string to display immediately follows the call.
;Example:
; call IGPutSFmt
; .db "Hello World!",0
pop hl
call GPutSFmt
jp (hl)
GPutSFmt:
;Inputs:
; HL points to the zero-terminated string to display
;The following define a rectangular region for where text is allowed:
; (fmt_leftmost)
; (fmt_rightmost)
; (fmt_lower)
; (fmt_upper)
; bit wordwrap,(iy+textflags)
ld a,(hl)
inc hl
or a
ret z
push de
push hl
call GPutCFmt
pop hl
pop de
jr GPutSFmt
GPutCFmt:
;Input:
; A is the char to display
ld b,a
call GPutC
;perform formatting based on indents, wordwrap
bit wordwrap,(iy+textflags)
ret z
ld hl,(textcol)
ld de,(fmt_rightmost)
ld a,e
sub l
ret nc
ld a,d
sub h
ret c
ld a,(fmt_leftmost)
ld l,a
ld a,h
add a,6
ld h,a
ld (textcol),hl
ret
GPutC:
;Inputs:
; (fontptr)
; (textcol)
; (textrow)
; B is the char to display
ld a,$D6
cp b
jr nz,charreg
nextline:
ld a,(fmt_leftmost)
ld (textcol),a
ld a,(textrow)
add a,6
ld (textrow),a
ret
charreg:
ld hl,(fontptr)
ld a,b
ld c,b
ld b,0
add hl,bc
add hl,bc
add hl,bc
ld de,lFont_record
ld b,3
ld a,(hl)
and $F0
ld (de),a
inc e
ld a,(hl)
rrca \ rrca
rrca \ rrca
and $F0
ld (de),a
inc e
inc hl
djnz $-15
ld ix,lFont_record
ld de,0406h
ld bc,(textcol)
ld hl,fmt_lower
ld a,b
cp (hl) \ ret nc
;e is height
add a,e
sub (hl)
jr c,$+6
neg
add a,e
ld e,a
ld a,c
dec l
cp (hl) \ ret nc
ld a,c
add a,4
ld (textcol),a
ld a,b
ld b,c
ld c,a
ORSprite8xY:
;Note: No clipping.
;Inputs:
; BC = (x,y)
; IX points to the sprite
; E is the height
ld a,b
and $F8
ld h,0
rla \ rl h
rla \ rl h
rla \ rl h
ld l,a
ld a,b
ld b,0
add hl,bc
ld bc,(DrawBufPtr)
add hl,bc
;HL points to the first byte to draw at
and 7
jr nz,crossedbound
ld b,e
push ix \ pop de
ld a,(de)
xor (hl)
ld (hl),a
inc hl
inc de
djnz $-5
ret
crossedbound:
ld b,a
ld d,a
ld a,1
rrca
djnz $-1
dec a
ld c,e
push bc
ld e,a
;E is the mask
;IX points to the sprite
;HL points to where to draw
drawloop1:
ld a,(ix)
ld b,d \ rrca \ djnz $-1
and e
or (hl)
ld (hl),a
inc ix
inc hl
dec c
jr nz,drawloop1
ld c,64
add hl,bc
ld a,e
cpl
ld e,a
pop bc
drawloop2:
dec ix
dec hl
ld a,(ix)
ld b,d \ rrca \ djnz $-1
and e
or (hl)
ld (hl),a
dec c
jr nz,drawloop2
ret
;===============================================================
FontSet:
;===============================================================
;00~7F
.db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$8C,$EC,$80,$00,$00,$00,$00,$00,$00
.db $24,$44,$20,$0A,$4A,$00,$00,$EA,$E0,$00,$4E,$40,$00,$04,$00,$00,$E4,$44,$E4,$2C,$00,$EC,$EC,$C0
.db $74,$4C,$40,$62,$A2,$00,$C2,$4E,$00,$02,$48,$E0,$69,$96,$00,$AC,$88,$00,$E4,$40,$00,$68,$60,$E0
.db $2E,$4E,$80,$C2,$C0,$E0,$06,$00,$00,$E8,$C8,$E0,$42,$F2,$40,$AD,$DD,$A0,$4E,$44,$40,$44,$4E,$40
.db $00,$00,$00,$44,$40,$40,$AA,$A0,$00,$00,$00,$00,$00,$00,$00,$A2,$48,$A0,$4A,$4A,$50,$88,$80,$00
.db $24,$44,$20,$84,$44,$80,$00,$40,$00,$04,$E4,$00,$00,$44,$80,$00,$E0,$00,$00,$00,$80,$22,$48,$80
;FontNumbers
.db $4A,$AA,$40,$4C,$44,$E0,$C2,$48,$E0,$C2,$42,$C0,$AA,$E2,$20,$E8,$C2,$C0,$68,$EA,$E0,$E2,$44,$40,$EA,$EA,$E0,$EA,$E2,$20
;3Ah~3Fh
.db $04,$04,$00,$04,$04,$80,$24,$84,$20,$0E,$0E,$00,$84,$24,$80,$C2,$40,$40
.db $00,$00,$00,$4A,$EA,$A0,$CA,$CA,$C0,$68,$88,$60,$CA,$AA,$C0,$E8,$C8,$E0,$E8,$C8,$80,$68,$AA,$60
.db $AA,$EA,$A0,$E4,$44,$E0,$62,$2A,$40,$AA,$CA,$A0,$88,$88,$E0,$AE,$AA,$A0,$CA,$AA,$A0,$EA,$AA,$E0
.db $CA,$C8,$80,$EA,$AE,$60,$CA,$CA,$A0,$68,$42,$C0,$E4,$44,$40,$AA,$AA,$E0,$AA,$AA,$40,$AA,$AE,$A0
.db $AA,$4A,$A0,$AA,$44,$40,$E2,$48,$E0,$4A,$EA,$40,$88,$42,$20,$C4,$44,$C0,$4A,$00,$00,$00,$00,$E0
.db $84,$00,$00,$06,$AA,$60,$88,$CA,$C0,$06,$88,$60,$22,$6A,$60,$04,$AC,$60,$48,$C8,$80,$06,$A6,$2C
.db $88,$CA,$A0,$40,$44,$40,$20,$22,$A4,$8A,$CA,$A0,$88,$88,$40,$0A,$EA,$A0,$0C,$AA,$A0,$04,$AA,$40
.db $0C,$AC,$80,$06,$A6,$22,$0A,$C8,$80,$0C,$84,$C0,$4E,$44,$20,$0A,$AA,$E0,$0A,$AA,$40,$0A,$AE,$A0
.db $0A,$44,$A0,$0A,$A6,$24,$0E,$24,$E0,$64,$84,$60,$44,$44,$40,$C4,$24,$C0,$05,$A0,$00,$E0,$E0,$E0
;FontNumbers2
.db $04,$AA,$A4,$04,$C4,$4E,$0C,$24,$8E,$0C,$24,$2C,$0A,$AE,$22,$0E,$8C,$2C,$06,$8E,$AE,$0E,$24,$44
.db $0E,$AE,$AE,$0E,$AE,$22
;Accented A
.db $24,$AE,$A0,$84,$AE,$A0,$00,$00,$00,$A4,$AE,$A0
;Accented a
.db $24,$06,$A5,$42,$06,$A5,$4A,$06,$A5,$A0,$6A,$60
;Accented E
.db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
;Accented e
.db $48,$4A,$C6,$42,$4A,$C6,$4A,$4A,$C6,$A0,$4A,$C6
;Accented I
.db $24,$0E,$4E,$84,$0E,$4E,$4A,$0E,$4E,$A0,$E4,$E0
;Accented i
.db $24,$04,$44,$84,$04,$44,$4A,$04,$44,$A0,$44,$40
;Bombs... er, accented O
.db $24,$69,$96,$84,$69,$96,$4A,$69,$96,$A0,$69,$96
;Lowercase bombs
.db $24,$06,$96,$84,$06,$96,$4A,$06,$96,$A0,$06,$96
;Accented U
.db $24,$AA,$A6,$84,$AA,$A6,$4A,$AA,$A6,$A0,$AA,$A6
;Accented u
.db $24,$0A,$A6,$84,$0A,$A6,$4A,$0A,$A6,$A0,$0A,$A6
;Accented C,c,N,n
.db $4A,$8A,$48,$06,$88,$6C,$5A,$0C,$AA,$5A,$0C,$AA
;Other Puntuation
.db $24,$00,$00,$84,$00,$00,$A0,$00,$00,$40,$48,$60
;Upside-Down Exclamation Point Identical to lowercase i
;Change to something else?
.db $00,$00,$00
;Greek
.db $05,$AA,$50,$25,$65,$A0,$05,$A2,$20,$00,$4A,$E0,$34,$27,$96,$68,$E8,$60
;[
.db $64,$44,$60
;Greek (continued)
.db $84,$25,$90,$0A,$AD,$80,$0F,$55,$90,$25,$56,$48,$F4,$24,$F0
.db $07,$55,$40,$07,$A2,$10
;Idunno howta do these
.db $4E,$AE,$40,$69,$99,$69
;CC~CF
.db $E0,$A4,$A0,$E0,$A6,$24,$52,$50,$00,$00,$00,$A0,$26,$E6,$20
;D0~D5
.db $44,$40,$00,$22,$48,$80,$00,$60,$00,$C4,$8C,$00,$EA,$E0,$00,$E4,$2C,$00
;D6
.db $00,$00,$00
;D7~DF
.db $40,$44,$20,$04,$CA,$C8,$8A,$4A,$20,$E9,$AE,$A8,$69,$E8,$60,$00,$44,$60,$9D,$FB,$90,$A5,$55,$A0,$4E,$FE,$40
;Overwrite Cursor
.db $FF,$FF,$FF,$FB,$1B,$BF,$FB,$51,$5F,$FF,$95,$9F
;Insert Cursor
.db $00,$00,$0F,$4E,$EE,$0F,$4A,$EA,$0F,$06,$A6,$0F
;E8~EF
.db $00,$84,$20,$00,$C6,$20,$00,$E6,$20,$00,$8C,$E0,$25,$D5,$20,$4A,$AA,$40,$4E,$44,$40
;F0~F4
.db $44,$4E,$40,$5A,$5A,$5A,$27,$A6,$3E,$4E,$44,$00,$69,$A9,$A0
;male/female
.db $73,$5E,$AE,$EA,$E4,$E4
;BlockEater Down $F7
.db $6F,$96,$90
;BlockEater Left $F8
.db $6F,$16,$90
;BlockEater Right $F9
.db $6F,$86,$90
;BlockEater Up $FA
.db $69,$96,$90
;FB~FE
.db $09,$AC,$E0,$08,$53,$70,$EC,$A1,$00,$73,$58,$00
;FF
.db $A5,$A5,$A5
Those use a nibble-packed 4 wide by 6 tall font (3 bytes per char for a total of 768 bytes).
#define DWAIT in a, ($10) \ or a \ jp m, $-3
copySetup:
di
ld hl, $9900
ld de, $9901
ld bc, $100
ld (hl), $98
ldir
ld a, $C3
ld ($9898), a
ld hl, interrupt
ld ($9899), hl
ld a, $99
ld i, a
im 2
xor a
out (03), a ;don't use halt
ld a, $A0 ;64 t-state timer
out ($30), a
ret
; hl is the buffer to use
interruptCopy:
nop
ld a, $C9
ld (interruptCopy), a ;safeguard against running twice
ld e, $20
nextColumn:
DWAIT
ld a, e
out ($10), a
cp $2E
jr nz, notDoneYet
xor a
ld (interruptCopy), a
ex af, af'
exx
ret ;returns with interrupts disabled
notDoneYet:
inc e
DWAIT
ld a, $80
out ($10), a
ld bc, 64*256+$11
ld a, 3
out ($31), a ;this is 3*64 = 192 t-states per write
out ($32), a ; you can refine this, but 192 is probably good
ex af, af'
exx
ei
ret
interrupt: ;19 + 10
ex af, af' ;4
exx ;4
out ($31), a ;11 a = 3
outi ;16
jr z, nextColumn ;7
exx ;4
ex af, af' ;4
ei ;4
ret ;10
;93 total