* lcd.asm - Code for working with the LCD (Liquid-Crystal Display) *--------------------------------------- * LCD-related equates *--------------------------------------- * LCD I/O registers LCD_BASE EQU $B5F0 ; LCD port, base address LCD_CTL EQU LCD_BASE|0 ; LCD control address = base address + 0 LCD_DAT EQU LCD_BASE|1 ; LCD data address = base address + 1 * LCD instruction codes CLR_HOM EQU BIT0 ; Clear display and home the cursor RET_HOM EQU BIT1 ; Return cursor home & reset display shift ENT_MOD EQU BIT2 ; Entry mode set ID_INC EQU BIT1 ; Increment/Decrement = Increment (else Decrement) S_shift EQU BIT0 ; With display shift (else not) DISP_ONOFF EQU BIT3 ; Display On/Off Control D_disp EQU BIT2 ; Turn display on (else off) C_cursor EQU BIT1 ; Turn cursor on (else off) B_blink EQU BIT0 ; Turn blink on (else off) CD_shift EQU BIT4 ; Cursor/Display Shift SC_shift EQU BIT3 ; Shift/Cursor = display shift (else cursor movement) RL_right EQU BIT2 ; Right/Left = shift right (else shift left) FUNC_SET EQU BIT5 ; Function Set DL_8bit EQU BIT4 ; Interface data length = 8 bits (else 4 bits) N_2lines EQU BIT3 ; Number of display lines = 2 (else 1 lines) F_5x10dots EQU BIT2 ; Character font = 5x10 dots (else 5x7 dots) CGRAM_addrset EQU BIT6 ; Character Generator RAM address set DDRAM_addrset EQU BIT7 ; Display Data RAM address set BF EQU BIT7 ; Busy Flag: LCD is busy processing instruction * LCD Display Data Ram (DDRAM) location equates. Row_length EQU 20 Row1_start EQU $00 Row1_end EQU Row1_start+Row_length-1 Row3_start EQU Row1_end+1 Row3_end EQU Row3_start+Row_length-1 Row2_start EQU $40 Row2_end EQU Row2_start+Row_length-1 Row4_start EQU Row2_end+1 Row4_end EQU Row4_start+Row_length-1 *------------------------------------------------------------------------- * Initialize the LCD, for 2 virtual 40-character lines * (which in reality are broken into 4 rows of 20 characters each) * and a blinking cursor that moves the right. *------------------------------------------------------------------------- init_LCD: * Wait more than 15 milliseconds after power-up. LDX #5000 ; 5000 iterations = 15 ms JSR dloop ; Wait this long before sending 1st cmd * Send a Function Set 8-bit data length command LDAA #FUNC_SET|DL_8bit ; Function set, data length 8-bit STAA LCD_CTL ; Store 8-bit command to LCD control register * Wait more than 4.1 milliseconds. LDX #1368 ; 1368 iterations = 4.1 ms. JSR dloop ; Wait that long after first function set. * Send the 8-bit data length command again. STAA LCD_CTL ; Store 8-bit command, second time * Wait more than 100 microseconds. LDX #34 ; 34 iterations = 100 us. JSR dloop ; Wait that long after 2nd function set * Send the 8-bit data length command a third time. STAA LCD_CTL ; Store 8-bit command, third time. BF now works. * At this point, the LCD's Busy Flag bit should now be responding properly. * Set the display to have two 40-character lines, with 5x7-dot characters. LDAA #FUNC_SET|DL_8bit|N_2lines ; Set to 8 bit data length, 2 virtual 40-char "lines", 5x7 dot font JSR send_cmd ; Send that command. * Clear the LCD display contents, return to home position, unshift display. JSR clear_disp ; Clear display LDAA #RET_HOM JSR send_cmd * Set the entry mode to increment the cursor position, and not shift the display. * (Actually this is the default behavior for our LCD anyway.) LDAA #ENT_MOD|ID_INC ; Entry mode: increment, no shift JSR send_cmd ; Send that command. * Turn on the display, turn on the cursor, and turn on cursor blinking. LDAA #DISP_ONOFF|D_disp|B_blink|C_cursor ; Turn on display/cursor/blink. JSR send_cmd ; Send that command. * The display is now initialized! RTS ; End of init_LCD subroutine. *------------------------------------------------------------- * Read the present display location (DDRAM address) into accB. *------------------------------------------------------------- read_loc: JSR wait_LCD ; Wait till the LCD is ready. LDAB LCD_CTL ; Get current cursor location (DDRAM address) into accB. RTS ; Return from read_loc subroutine. * Map of locations locmap: fcb $00,$01,$02,$03,$04,$05,$06,$07,$08,$09,$0a,$0b,$0c,$0d,$0e,$0f,$10,$11,$12,$13 fcb $40,$41,$42,$43,$44,$45,$46,$47,$48,$49,$4a,$4b,$4c,$4d,$4e,$4f,$50,$51,$52,$53 fcb $14,$15,$16,$17,$18,$19,$1a,$1b,$1c,$1d,$1e,$1f,$20,$21,$22,$23,$24,$25,$26,$27 fcb $54,$55,$56,$57,$58,$59,$5a,$5b,$5c,$5d,$5e,$5f,$60,$61,$62,$63,$64,$65,$66,$67 * If A,B contain (x,y) coordinates, go to that display location tmpx: rmb 1 gotoxy: psha pshb pshx jsr printA jsr printB staa tmpx ldaa #20 mul addb tmpx addd #locmap xgdx ldab 0,x jsr printB jsr set_loc jsr OUTCRLF pulx pulb pula rts *------------------------------------------------------------------------- * Write character in accA to current location on LCD display screen, and * advance the cursor location appropriately. * This version adjusts for 4x20 keypad row addressing if you're expecting * continuous rows 1,2,3,4 *------------------------------------------------------------------------- lcd_out: JSR read_loc ; Read the current LCD cursor location into accB. * Check to see if we're at the end of one row and we need to jump to the start of the next. CMPB #Row1_end ; Are we at the end of the 1st row of characters? BEQ lcd_ad1 ; Make adjustment for end of row 1. CMPB #Row2_end ; Are we at the end of the 2nd row of characters? BEQ lcd_ad2 ; Make adjustment for end of row 2. CMPB #Row3_end ; Are we at the end of the 3rd row of characters? BEQ lcd_ad3 ; Make adjustment for end of row 3. CMPB #Row4_end ; End of 4th row? BEQ lcd_ad4 ; Adjust. * Normal case: Just write the character. JSR write_lcd ; Write the character in accA to the LCD screen. RTS ; Return from the lcd_out subroutine * Adjustment cases: Figure out the new location, then write the character, * then jump to the new location. lcd_ad1: ldab #Row2_start ; Location $40 = start of 2nd row. bra lcd_fix ; Continue fix lcd_ad2: ldab #Row3_start ; Location $14 = start of 3rd row. bra lcd_fix ; Continue fix lcd_ad3: ldab #Row4_start ; Location $54 = start of 4th row. bra lcd_fix ; Continue fix lcd_ad4: ldab #Row1_start ; Location $00 = start of 1st row. lcd_fix: JSR write_lcd ; Write the character in accA to the LCD screen. JSR set_loc ; Set the new LCD screen location (DDRAM address) to accB. RTS ; Return from the lcd_out subroutine. ENDSTR equ $ff ; Use non-ASCII byte code $FF to denote end of string * Print ENDSTR-terminated string pointed to by X lcdstr: psha pshx lstrl: ldaa 0,x jsr printA cmpa #ENDSTR beq nomore pshx jsr lcd_out ldx #1000 jsr dloop pulx inx bra lstrl nomore: jsr OUTCRLF pulx pula rts *------------------------------------------------------ * Set the LCD Display Data RAM (DDRAM) address to accB. *------------------------------------------------------ set_loc: PSHA ; Preserve accA. TBA ; Copy address from B to A. ORAA #DDRAM_addrset ; Turn it into a "DDRAM address set" command. JSR send_cmd ; Send that command to the LCD. PULA ; Restore accA. RTS ; Return from the set_loc subroutine. *-------------------------------------------------- * Clear the LCD display contents. *-------------------------------------------------- clear_disp: PSHA LDAA #CLR_HOM ; Load the clear-display-and-home command JSR send_cmd ; Send it to the LCD PULA *-------------------------------------------------------------- * Shift the LCD display contents to the left. *-------------------------------------------------------------- shift_left: PSHA LDAA #CD_shift|SC_shift ; Display shift to the left JSR send_cmd PULA *------------------------------------------------------------------ * Send the LCD command (instruction) in accA to the LCD. *------------------------------------------------------------------ send_cmd: JSR wait_LCD ; Wait until the LCD is ready. STAA LCD_CTL ; Store the instruction at the LCD control register. RTS ; Return from send_cmd subroutine. *----------------------------------------------------------------- * Write the character whose code is in accA to the LCD. *----------------------------------------------------------------- write_lcd: JSR wait_LCD ; Wait until the LCD is ready. STAA LCD_DAT ; Store the character at the LCD data address. RTS ; Return from the write_lcd subroutine. *--------------------------------------------------------- * Wait for the LCD to finish processing the last command. * Use this before sending a new command or data value. *---------------------------------------------------------- wait_LCD: PSHX LDX #LCD_CTL ; Let X point to the LCD control register. wait_loop: BRSET 0,X BF wait_loop ; Wait for BF bit of LCD control register to be 0. PULX RTS ; Return from wait_LCD subroutine. *----------------------------------------------------------- * Delay for 6*X clock cycles (3*X microseconds). Stomps X. *----------------------------------------------------------- dloop: DEX ; Decrement X (3 cycles) BNE dloop ; Repeat if not 0 (3 cycles) RTS ; Return from dloop subroutine. *----------------------------------------------- * Store the eight glyphs in glyph_table in the CGRAM. * Stomps A,B,X,Y *----------------------------------------------- store_glyphs: CLRA ; Clear accA (select character #0). LDX #glyph_table ; X will point to the table of glyph pointers. nextgl: LDY 0,X ; Load Y with pointer to next glyph. PSHA ; Preserve A. PSHX ; Preserve X. JSR store_glyph ; Store the glyph at the selected CGRAM location. PULX ; Restore X. PULA ; Restore A. INCA ; Go to the next CGRAM location. INX ; Go to the next entry in the table of glyph pointers. INX ; . CMPA #8 ; Did we fill all 8 characters yet? BLO nextgl ; If not, do the next one. RTS ; Return from store_glyphs subroutine. *-------------------------------------------------------------------- * Store the 8-byte graphic pointed to by Y as character code A (0-7). * Stomps A,B,X *--------------------------------------------------------------------- store_glyph: JSR read_loc ; Read current cursor loc into accB. LSLA ; Shift A's value over into bits 3-5. LSLA LSLA ORAA #CGRAM_addrset ; Turn A into command "set CGRAM address to first row of that character" JSR send_cmd ; Send that command to the LCD. PSHB ; Preserve present value of B (cursor loc). LDAB #8 ; Load B with row count: 8. nextrow: LDAA 0,Y ; Load next row of font graphics. JSR write_lcd ; Write it to the LCD. INY ; Increment graphics data pointer. DECB ; Decrement number of rows left to process. BNE nextrow ; Continue if there are rows left. PULB ; Restore the old value of B (DDRAM address). JSR set_loc ; Go back into DDRAM-writing mode. RTS ; Return from store_glyph subroutine. *------------------------------------------------- * Show all 8 glyphs in the CGRAM on the display. *------------------------------------------------- show_glyphs: CLRA ; For A=0 to 7 printit: JSR lcd_out ; Output character A to the LCD INCA ; Next A (A=A+1) CMPA #7 ; . (Compare A with 7) BLS printit ; . (If lower or same, repeat) RTS ; Return from show_glyphs subrotuine.