OK, I've been working on a cursor routine and have a problem...the routine works perfectly, but leaves random copies of the arrow all over the screen. I don't think there's anything wrong with the code; maybe it's the LCD driver? While most of the routine is in ASM, there is some BASIC ASM stuff, so you'll need BZC to compile it.
Code: #include <asmlib.h>
FUNCTION slowdown
FUNCTION CURSOR
FUNCTION waitforLCD
FUNCTION curwrite ARG 1
FUNCTION curread
FUNCTION curor
FUNCTION getkey
INT coord = $0000
MEM curbuf 16
MEM savebuf 16
FUNCTION curspr
START
ASM
LD A, $01 ; Make sure LCD is set to 8-bit word mode (otherwise goofy things happen...)
OUT ($10), A
ENDASM
FORCE curspr
FORCE curbuf
FORCE savebuf
FORCE coord
FORCE getkey
getkey
END
FSTART CURSOR ; Wrapper for cursor display routine; calls curread, curor, and curwrite
curread
curor
curwrite curbuf
FEND
FSTART curspr ; Do-nothing function containing cursor images
ASM
curspr1:
.DB %11111110
.DB %01111100
.DB %00111000
.DB %00010000
.DB %00111000
.DB %01000100
.DB %11111110
.DB %00000000
.DB %00000001
.DB %10000011
.DB %11000111
.DB %11101111
.DB %11000111
.DB %10000011
.DB %00000001
.DB %11111111
curspr2:
.DB %10000000
.DB %11000000
.DB %10100000
.DB %10010000
.DB %10001000
.DB %11111100
.DB %00110000
.DB %00011000
.DB %01111111
.DB %00111111
.DB %00011111
.DB %00001111
.DB %00000111
.DB %00000011
.DB %11001111
.DB %11100111
ENDASM
FEND
FSTART curread ; Read data from LCD
ASM
LD DE, (coord) ; DE holds coordinates of cursor (upper left corner)
SRL D
SRL D
SRL D ; Column of display obtained
LD IX, curbuf ; IX points to cursor buffer
LD HL, savebuf ; HL points to screen save buffer
LD B, -1 ; B is our offset to tell us which half of the 2-byte wide data column we're on
read_loop_main:
INC B
ENDASM
waitforLCD
ASM
LD A, $20 ; Let's set the column
ADD A, B
ADD A, D ; Correct column obtained
OUT ($10), A
ENDASM
waitforLCD
ASM
LD A, E ; Get the row directly from the original coordinates
ADD A, $80 ; Correct row obtained
OUT ($10), A
PUSH BC ; Save BC (since it is our offset)
LD B, 8 ; Data block we need is 8 rows long
ENDASM
waitforLCD
ASM
IN A, ($11) ; Dummy read
read_loop:
ENDASM
waitforLCD
ASM
IN A, ($11) ; Real read
LD (HL), A ; Put screen byte into save buffer...
LD (IX), A ; ...And cursor buffer
INC HL
INC IX
DJNZ read_loop ; If we don't have 8 rows, loop again
POP BC ; Restore BC
LD A, B
CP 1 ; See if we've copied 2 columns of data
JP NZ, read_loop_main ; If not, do the whole thing again
ENDASM
FEND
FSTART curwrite IX ; Writes data to LCD (takes address of data location through IX)
ASM
LD DE, (coord) ; DE holds coordinates of cursor
LD A, D ; See if the cursor goes off the left side of the screen; not used at this point
CP 0
JP C, no_left
CP 88 ; See if cursor goes off right side of screen; if so, jump!
JP NC, no_right
LD C, 1 ; C = 1 indicates that we want both columns of data written
LD B, -1 ; Set B (will be incremented at the start of the write loop)
JP write_continue
no_left: ; Appropriately sets B and C to result in only the right column of data being written
LD B, 0
LD C, 1
JP write_continue
no_right: ; Appropriately sets B and C to result in only the left column of data being written
LD B, -1
LD C, 0
write_continue:
SRA D
SRA D
SRA D ; Column of display obtained
LD A, E ; Now we check to see if the cursor is going off the bottom of the screen
LD H, 8 ; H will hold the number of rows of data to be copied, to reinitialize B for each column
CP 56 ; See if cursor goes off screen
JP C, write_loop_main ; If not, jump!
LD A, 63
SUB E
LD H, A ; H is now adjusted to hold only the number of rows of the cursor that remain on the screen
write_loop_main:
INC B
ENDASM
waitforLCD
ASM
LD A, $20 ; Time to set the column
ADD A, B
ADD A, D ; Correct column obtained
OUT ($10), A
ENDASM
waitforLCD
ASM
LD A, E ; Time to set the row
ADD A, $80
OUT ($10), A
PUSH BC ; Save our offset
continue_write:
LD B, H ; Initialize B with H
write_loop:
ENDASM
waitforLCD
ASM
LD A, (IX) ; Write the data
OUT ($11), A
INC IX
DJNZ write_loop
POP BC ; Restore the offset in B
LD A, 8 ; Now for some unavoidably messy code
SUB H ; We have to account for the fact that we've skipped writing rows of data in the cursor
PUSH DE ; buffer if the cursor goes off the bottom of the screen
LD D, 0 ; So, adjust IX so it points to the top of the next column of data
LD E, A
ADD IX, DE
POP DE
LD A, B ; See if we've written all the data necessary
CP C
JP NZ, write_loop_main ; If not, loop again
ENDASM
FEND
FSTART curor ; Integrates cursor into LCD data with OR logic
ASM
DI ; Note that IY is being used in this routine, necessitating DI and EI, and restoring IY
LD IY, curspr2 ; We want the arrow (see curspr function above, that contains the sprite images)
LD IX, curbuf ; IX points to the cursor buffer
LD A, 8 ; 8 rows of data must be integrated into the cursor buffer
copy_loop:
PUSH AF ; Save our counter
LD A, (coord+1) ; Get the row coordinate
AND $07 ; Get the shift amount (0 if image is aligned on LCD byte)
LD B, (IY) ; Copy left half of image into B
LD C, 0 ; C = 0 since we will be shifting bits of B into C (and we're using OR logic)
LD D, (IY+8) ; Copy left half of bitmask into D
LD E, $FF ; E = $FF since we will be shifting bits of D into E (and we're using AND logic)
CP 0 ; See if shift amount is 0
JP Z, copy_loop_continue ; If yes, no shifting required, so jump!
shift_loop:
SRL B ; Shift left part of sprite image (original bit 0 sent to carry)
RR C ; Build right part of sprite image (carry sent to bit 7; convenient!)
SRL D ; Shift left part of sprite image
RR E ; Build right part of sprite image
LD H, A ; Problem: there is no shift/rotate instruction to put a 1 into bit 7
LD A, D ; Solution: OR A with %10000000 ($80)...This necessitates moving stuff around...
OR $80
LD D, A
LD A, H
DEC A
CP 0 ; Finally! See if we've shifted enough
JP NZ, shift_loop ; If not, loop again
copy_loop_continue: ; Time to put the cursor image into the cursor buffer, bitmask and all
LD A, (IX) ; A holds left cursor buffer byte
AND D ; Clear the area occupied by the cursor image with AND logic (0 resets, 1 preserves)
OR B ; Put cursor image into cleared area with OR logic (1 sets, 0 ignores)
LD (IX), A ; Put modified byte back into cursor buffer
LD A, (IX+8) ; Repeat process, this time with the right side; this is where index registers come in handy
AND E
OR C
LD (IX+8), A ; You gotta love offsets; makes code very elegant...
; Now both parts of sprite are in the cursor buffer!
INC IY ; Do that incrementing
INC IX ; Ditto
POP AF ; Restore the counter saved so long ago
DEC A ; Decrement it
CP 0 ; See if we've gone through the entire cursor image
JP NZ, copy_loop ; If not, loop again
LD IY, Flags ; Restore the state of IY
EI ; Enable Interrupts
ENDASM
FEND
FSTART waitforLCD ; Wait for the *slow* LCD driver
ASM
waitforLCD_again:
IN A, ($10)
BIT 7, A ; Bit 7 tells us the driver's status (1 = busy, 0 = ready)
JR NZ, waitforLCD_again
ENDASM
FEND
FSTART getkey ; Direct key input routine
ASM
LD A, $FF
OUT (1), A ; Reset keyport
return:
ENDASM
CURSOR ; Call the full cursor display routine
slowdown ; Direct input is so fast, we have to slow it down (just a short pause)
ASM
key_loop:
LD A, $FE ; Set the arrow keys group
OUT (1), A
NOP ; Wait
NOP ; Wait some more
IN A, (1)
BIT 0, A ; Check for the arrow keys being pressed; if any are pressed we jump
JP Z, arrow ; Exactly which ones are pressed will be handled later
BIT 1, A
JP Z, arrow
BIT 2, A
JP Z, arrow
BIT 3, A
JP Z, arrow
LD A, $FD ; Set key group that includes [CLEAR]
OUT (1), A
NOP ; Twiddle one's thumbs
NOP ; Twiddle some more
IN A, (1)
CP $BF ; See if [CLEAR] was pressed
JP Z, exit ; If yes, exit!
JP key_loop
arrow: ; Now we figure out just which arrow key was pressed
LD D, A ; D holds key press value (trust me, this way is better)
LD BC, (coord) ; Save coordinates of cursor in BC
BIT 0, D ; See if down was pressed
CALL Z, down ; If yes, call that routine
BIT 3, D ; See if up was pressed
CALL Z, up ; If yes, call that routine
BIT 1, D ; See if left was pressed
CALL Z, left ; If yes, call that routine
BIT 2, D ; See if right was pressed
CALL Z, right ; If yes, call that routine
LD HL, (coord) ; Get the new modified coordinates
LD A, H ; Get ready to compare with that saved in BC
CP B
JP NZ, continue_arrow ; If there is a difference, the cursor will need to be erased and redisplayed
LD A, L
CP C
JP NZ, continue_arrow
JP key_loop ; Cursor position didn't change
continue_arrow:
LD (coord), BC ; Replace old coordinates
PUSH HL ; Save new coordinates
ENDASM
curwrite savebuf ; Erase cursor (by copying saved screen data back to the screen
ASM
POP HL ; Restore HL
LD (coord), HL ; Store new coordinates
JP return ; Back to the top of the routine!
down: ; Checks to see if top of cursor off bottom of screen, otherwise moves it down one pixel
LD A, (coord) ; (coord) holds row coordinate
CP 62
RET Z
INC A
LD (coord), A
RET
up: ; Checks to see if top of cursor at top of screen, otherwise moves it up one pixel
LD A, (coord)
CP 0
RET Z
DEC A
LD (coord), A
RET
left: ; Checks to see if left side of cursor at left of screen, otherwise moves it left one pixel
LD A, (coord+1) ; (coord+1) holds column coordinate
CP 0
RET Z
DEC A
LD (coord+1), A
RET
right: ; Checks to see if right of cursor off right of screen, otherwise moves it right one pixel
LD A, (coord+1)
CP 95
RET Z
INC A
LD (coord+1), A
RET
exit:
ENDASM
FEND
FSTART slowdown ; The slowdown routine, basically just wastes time; nice example of a 16-bit counter, though
ASM
LD BC, $0800
loop:
DEC BC
LD A, B
OR C
JP NZ, loop
ENDASM
FEND