6502-OS/system/lcd.s65
matthias@rpi 60f42f2d90 add guard
2023-11-09 12:08:26 +01:00

234 lines
6.2 KiB
Plaintext

;********************************************************************************
; @module LCD-W164B
; @type driver
; @device ELECTRONIC ASSEMBLY - W164B-NLW
; @details
; The LCD must be connected to a W65C22N Interface Chip:
; - IO.RB0-7 -> LCD.D0-7 data lines
; - IO.RA5 -> LCD.RS register select
; - IO.RA6 -> LCD.R/W read/write
; - IO.RA7 -> LCD.E enable
;
; @requires LCD_IO: Base Address of IO Chip
; @optparam LCD_MEM: Memory address for a runtime variable. Default = $300
; @depends IO-W65C22N
;********************************************************************************
.ifndef INCLUDE_LCD
INCLUDE_LCD = 1
.ifndef LCD_IO
.fatal "LCD_IO is not defined: set it to the base address of the IO chip of the LCD"
.endif
.ifndef INCLUDE_IOW65C22
.error "IO-W65C22 module is not included"
.endif
.include "utility.h65"
; RAM VARIABLES
.ifndef LCD_MEM
LCD_MEM = $300
.endif
LCD_CHARCOUNT = LCD_MEM
; PIN MASKS
LCD_E = %10000000
LCD_RW = %01000000
LCD_RS = %00100000
; .local LCD_E,LCD_RW,LCD_RS
; LCD Instructions (see datasheet for more)
LCD_CMD_CLEAR = %00000001 ; clear display
LCD_CMD_ENTRY_MODE = %00000110 ; auto-shift cursor
LCD_CMD_DISPLAY_ON = %00001111 ; everything on, with blinking cursor
LCD_CMD_FUNCTION_SET = %00111000 ; 8-bit, 4 lines, 5x7 font
LCD_CMD_SET_ADDRESS = %10000000 ; or with the address
; LCD Constants
LCD_LINE1 = $00
LCD_LINE2 = $40
LCD_LINE3 = $10
LCD_LINE4 = $50
LCD_CLEAR = %00000000
;********************************************************************************
; @function Initialize the lcd module
; @details call before doing anything else
;********************************************************************************
.proc lcd_init
; init IO
lda #$ff ; RB 0-7 output
sta LCD_IO+IO::DDRB
; UT_update_with_mask LCD_IO + IO::DDRA, (LCD_RS | LCD_RW | LCD_E), (LCD_RS | LCD_RW | LCD_E)
lda #(LCD_RS | LCD_RW | LCD_E) ; RA 5-7 output
sta LCD_IO+IO::DDRA
; init lcd
lda #LCD_CMD_FUNCTION_SET
jsr _lcd_cmd
lda #LCD_CMD_DISPLAY_ON
jsr _lcd_cmd
lda #LCD_CMD_CLEAR
jsr _lcd_cmd
lda #LCD_CMD_ENTRY_MODE
jsr _lcd_cmd
stz LCD_CHARCOUNT
rts
.endproc
;********************************************************************************
; PRINTING TO LCD
;********************************************************************************
;********************************************************************************
; @function Clear the display
; @see lcd_print
;********************************************************************************
.proc lcd_clear
stz LCD_CHARCOUNT
lda #LCD_CLEAR
jsr _lcd_cmd
lda #(LCD_CMD_SET_ADDRESS | LCD_LINE1)
jsr _lcd_cmd
rts
.endproc
;********************************************************************************
; @function Print a null-terminated string
; @param ARG0-1 Address of the string to print
;********************************************************************************
.proc lcd_print
ldy #$00
@lcd_print_loop:
lda (ARG0),y
beq @lcd_print_end
jsr lcd_char
iny
bra @lcd_print_loop
@lcd_print_end:
rts
.endproc
;********************************************************************************
; @macro Print a null-terminated string
; @param message: Address of the message
;********************************************************************************
.macro Print message
jsr lcd_clear
lda #.LOBYTE(message)
sta ARG0
lda #.HIBYTE(message)
sta ARG1
jsr lcd_print
.endmacro
;********************************************************************************
; @macro Print a single character
; @param A: Character to print
;********************************************************************************
.proc lcd_char
pha
pha
; TODO use UT_update_with_mask?
jsr _lcd_wait_nbusy
pla
sta LCD_IO + IO::RB
lda #LCD_RS
sta LCD_IO + IO::RA
lda #(LCD_RS | LCD_E)
sta LCD_IO + IO::RA
lda #LCD_RS
sta LCD_IO + IO::RA
inc LCD_CHARCOUNT
jsr _lcd_set_address
pla ; put char back in a
.endproc
;********************************************************************************
; Internal LCD Commands
;********************************************************************************
; read busy flag
.proc _lcd_wait_nbusy
stz LCD_IO + IO::DDRB ; set IO1-LCD_IO + IO::RB to input
@lcd_wait_nbusy_loop: ; read the busy flag
lda #LCD_RW
sta LCD_IO + IO::RA
lda #(LCD_RW | LCD_E)
sta LCD_IO + IO::RA
lda LCD_IO + IO::RB
and #%10000000 ; and updates zero flag, if not set retry
bne @lcd_wait_nbusy_loop
lda #%00000000 ; TODO dont overwrite 0-4
sta LCD_IO + IO::RA
lda #%11111111 ; set IO1-LCD_IO + IO::RB to output
sta LCD_IO + IO::DDRB
rts
.endproc
.proc _lcd_cmd ; send cmd in acc
pha
jsr _lcd_wait_nbusy
pla
; TODO use UT_update_with_mask?
sta LCD_IO + IO::RB
lda #LCD_CLEAR
sta LCD_IO + IO::RA
lda #LCD_E
sta LCD_IO + IO::RA
lda #LCD_CLEAR
sta LCD_IO + IO::RA
rts
.endproc
;********************************************************************************
; Set the LCD DD-RAM Address so that text linebreaks after 16 chars
;********************************************************************************
.proc _lcd_set_address
; check if checks are necessary
lda LCD_CHARCOUNT
beq @lcd_line1
and #%10001111 ; ($10 | $20 | $30 | $40) = %01110000
bne @lcd_set_address_done
; checks necessary
lda LCD_CHARCOUNT
beq @lcd_line1
cmp #$10
beq @lcd_line2
cmp #$20
beq @lcd_line3
cmp #$30
beq @lcd_line4
cmp #$40 ; set to line1 when full ; set to line1 when full
beq @lcd_line1
@lcd_set_address_done:
rts
@lcd_line1:
stz LCD_CHARCOUNT
lda #(LCD_CMD_SET_ADDRESS | LCD_LINE1)
jsr _lcd_cmd
rts
@lcd_line2:
lda #(LCD_CMD_SET_ADDRESS | LCD_LINE2)
jsr _lcd_cmd
rts
@lcd_line3:
lda #(LCD_CMD_SET_ADDRESS | LCD_LINE3)
jsr _lcd_cmd
rts
@lcd_line4:
lda #(LCD_CMD_SET_ADDRESS | LCD_LINE4)
jsr _lcd_cmd
rts
.endproc
.endif