6502-OS/system/lcd.s65

219 lines
5.6 KiB
Plaintext
Raw Normal View History

2023-10-26 19:51:20 +02:00
;********************************************************************************
; @module LCD-W164B
; @type driver
; @device ELECTRONIC ASSEMBLY - W164B-NLW
; @details
2023-10-28 03:48:27 +02:00
; The LCD must be connected to a W65C22N Interface Chip:
2023-10-26 19:51:20 +02:00
; - 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
2023-10-28 03:48:27 +02:00
; @optparam LCD_MEM: Memory address for a runtime variable. Default = $300
2023-10-26 19:51:20 +02:00
; @depends IO-W65C22N
;********************************************************************************
.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
2023-10-27 16:50:58 +02:00
.include "utility.h65"
2023-10-26 19:51:20 +02:00
; 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
2023-10-28 03:48:27 +02:00
; LCD Instructions (see datasheet for more)
2023-10-26 19:51:20 +02:00
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
2023-10-28 03:48:27 +02:00
LCD_LINE1 = $00
2023-10-26 19:51:20 +02: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
2023-10-28 03:48:27 +02:00
; 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
2023-10-26 19:51:20 +02:00
; 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
;********************************************************************************
;********************************************************************************
2023-10-28 03:48:27 +02:00
; @function Clear the display
2023-10-26 19:51:20 +02:00
; @see lcd_print
;********************************************************************************
2023-10-28 03:48:27 +02:00
.proc lcd_clear
2023-10-26 19:51:20 +02:00
stz LCD_CHARCOUNT
lda #LCD_CLEAR
jsr _lcd_cmd
lda #(LCD_CMD_SET_ADDRESS | LCD_LINE1)
jsr _lcd_cmd
2023-10-28 03:48:27 +02:00
rts
.endproc
2023-10-26 19:51:20 +02:00
;********************************************************************************
; @function Print a null-terminated string
; @param ARG0-1 Address of the string to print
;********************************************************************************
2023-10-28 03:48:27 +02:00
.proc lcd_print
ldy #$00
@lcd_print_loop:
lda ARG0,y
beq @lcd_print_end
2023-10-26 19:51:20 +02:00
jsr _lcd_char
2023-10-28 03:48:27 +02:00
iny
bra @lcd_print_loop
@lcd_print_end:
2023-10-26 19:51:20 +02:00
rts
2023-10-28 03:48:27 +02:00
.endproc
.macro Print message
jsr lcd_clear
lda #.LOBYTE(message)
sta ARG0
lda #.HIBYTE(message)
sta ARG1
jsr lcd_print
.endmacro
2023-10-26 19:51:20 +02:00
;********************************************************************************
; LCD Commands
;********************************************************************************
; read busy flag
_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
2023-10-28 03:48:27 +02:00
.proc _lcd_cmd ; send cmd in acc
2023-10-26 19:51:20 +02:00
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
2023-10-28 03:48:27 +02:00
.endproc
2023-10-26 19:51:20 +02:00
_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
;********************************************************************************
; Set the LCD DD-RAM Address so that text linebreaks after 16 chars
;********************************************************************************
2023-10-28 03:48:27 +02:00
.proc _lcd_set_address
2023-10-26 19:51:20 +02:00
; 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
2023-10-28 03:48:27 +02:00
.endproc