LCD port driver by Lee Davison
[Back]
; suprdupr LCD driver ver$ 0.1 ; Some basic routines to drive an LCD graphic module in both graphic and ; alpha modes. This code is mostly a translation of my Z80 LCD drivers ; for the MPF ; The display plugs into the LCD port on the SuprDupr board. Port6 = $0C ; LCD control port data register PDir6 = $0D ; LCD control port direction register Port8 = $10 ; LCD port data register PDir8 = $11 ; LCD port direction register LCD_c = Port6 ; LCD control port data register LCD_d = PDir6 ; LCD control port direction register LCD_E = 2 ; LCD E pin LCD_WR = 6 ; LCD /WR line LCD_RS = 7 ; LCD register select line LCD_DA = Port8 ; LCD port data register LCD_DIR = PDir8 ; LCD port direction register DispMode = $52 ; display mode byte off/master/noblink/nocsr/cgr/intcg) ChrHigh = $53 ; current character height (8 default) ChrWidt = $54 ; current character width (8 default) LineLen = $55 ; current visible line length (32 default) CursorAt = $56 ; cursor address in display StrtOff = $58 ; default display start offset DispOff = $5A ; offset for display ; Display attribute equates DispOn = $20 ; display on bit DispBlnk = $08 ; cursor blink bit DispCrsr = $04 ; cursor on bit DispGrap = $02 ; display graph mode bit DispExCg = $01 ; external GC bit *= $8000 ; code origin ; set default LCD parameters LCD_default: LDM #$20,DispMode ; display mode byte (on/master/noblink/nocsr/cgr/intcg) LDM #$70,ChrHigh ; current character height (8 default) LDM #$07,ChrWidt ; current character width (8 default) LDM #$20,LineLen ; current visible line length (32 default) LDM #$00,CursorAt ; cursor address in display LDM #$00,CursorAt+1 ; cursor address in display LDM #$E0,StrtOff ; default display start offset LDM #$FF,StrtOff+1 ; default display start offset LDM #$00,DispOff ; offset for display LDM #$00,DispOff+1 ; offset for display RTS ; initialise 240x40 LCD display. Sets line length to 64 and time divisions ; to 40 LCD_init: CLB LCD_E,LCD_c ; disable LCD's port SEB LCD_E,LCD_d ; set control bit to output SEB LCD_WR,LCD_d ; set control bit to output SEB LCD_RS,LCD_d ; set control bit to output LDA #$03 ; set time divisions instruction JSR LCD_instr ; send display control instruction LDA #$28 ; set refresh to 40 lines JMP LCD_data ; go write data byte & return ; output instruction. We need to wait until the busy flag is clear before ; attempting to write a new instruction. LCD_instr: JSR LCD_busy ; wait until ready SEB LCD_RS,LCD_c ; register select line high BRA LCD_inst ; go write data byte ; output data. We don't need to wait until the busy flag is clear before ; attempting to write data. LCD_data: CLB LCD_RS,LCD_c ; register select line low LCD_inst: STA LCD_DA ; byte out to port CLB LCD_WR,LCD_c ; control line to write LDM #$FF,LCD_DIR ; data port to output SEB LCD_E,LCD_c ; LCD E high CLB LCD_E,LCD_c ; LCD E low RTS ; wait until the display is not busy. LCD_busy: PHA ; save data or instruction LDM #$00,LCD_DIR ; data port to input SEB LCD_WR,LCD_c ; control line to read SEB LCD_RS,LCD_c ; register select line high LCD_busy1: SEB LCD_E,LCD_c ; enable LCD LDA LCD_DA ; in from display port CLB LCD_E,LCD_c ; disable LCD BBS 7,A,LCD_busy1 ; loop while LCD busy PLA ; restore data or instruction RTS ; Clear LCD display. Turns off display, checks mode and then fills 2048 display bytes ; with either $00 (graph mode) or $20 (chr mode). It also homes the cursor. ; If the commented out instructions are restored then the display will blank during ; clearing. LCD_clr: ; LDA DispMode ; get mode ; AND #$1F ; display off ; JSR LCD_mode ; set display mode LDA #$00 ; space chr to clear graph mode display STA CursorAt ; clear byte STA CursorAt+1 ; clear word BBS 1,DispMode,ClGraph ; if graph go clear graph mode LDA #$20 ; space chr to clear character mode display ClGraph: PHA ; save blank character LDX $00 ; reset cursor low address LDY $00 ; reset cursor high address JSR Set_cursor ; Set cursor address. 16 bit value in XY (low/high) LDA #$0C ; write display data JSR LCD_instr ; set instruction LDY #$08 ; 8 x 256 = 2048 bytes PLA ; get blank character back Clear_l: JSR LCD_busy ; wait until ready JSR LCD_data ; go write data byte DEX ; decrement byte count BNE Clear_l ; loop until all chrs done DEY ; inner loop BNE Clear_l ; loop until all blocks done JSR Set_cursor ; set cursor position (XY will be $0000) LDX DispMode ; get mode JSR LCD_Xmode ; set display mode from X RTS ; Set cursor address. 16 bit value in XY (low/high) Set_cursor: STX CursorAt ; save cursor position low byte STY CursorAt+1 ; save cursor position low byte LDA #$0A ; Set cursor low addr instruction JSR LCD_instr ; send display control instruction TXA ; set low address JSR LCD_data ; go write data byte LDA #$0B ; Set cursor high addr instruction JSR LCD_instr ; send display control instruction TYA ; set high address JSR LCD_data ; go write data byte RTS ; set display start address to XY (low/high) Set_start: STX StrtOff ; save start offset low byte STY StrtOff+1 ; save start offset high byte LDA #$08 ; Set display start low addr instruction JSR LCD_instr ; send display control instruction TXA ; set low byte JSR LCD_data ; go write data byte LDA #$09 ; Set display start high addr instruction JSR LCD_instr ; send display control instruction TYA ; set high byte JSR LCD_data ; go write data byte RTS ; Turn on the LCD. without changing the mode parameters. LCD_on: LDA DispMode ; get the display mode ORA #DispOn ; turn on bit STA DispMode ; save mode JMP LCD_mode ; set display mode & return ; Turn off the LCD. without changing the mode parameters. LCD_off: LDA DispMode ; get the display mode AND #$FF-DispOn ; turn off bit STA DispMode ; save mode ; Set the LCD mode. mode byte should be in A LCD_mode: TAX ; save mode byte LCD_Xmode: LDA #$00 ; clear A JSR LCD_instr ; set display control instruction TXA ; get mode byte back JMP LCD_data ; go write data byte & return ; Set graph mode. This will set the width to eight and the display to 32 ; chrs/line. If you want the display on when graph mode is set call this ; routine with A<>0 LCD_setgramode: LDX #$02 ; display off/graph mode ORA #$00 ; set the flags BEQ GsetOff ; jump if display is to be off LDX #$22 ; display on/graph mode GsetOff: STX DispMode ; save mode JSR LCD_Xmode ; set display mode from X LDA #$01 ; set chr pitch instruction JSR LCD_instr ; send display control instruction LDA #$07 ; chr width 8 bits/chr JSR LCD_data ; go write data byte LDA #$02 ; set no. of chrs instruction JSR LCD_instr ; send display control instruction LDA #$1E ; 32 chr mode JSR LCD_data ; go write data byte LDX #$00 ; set start of display low byte LDY #$00 ; set start of display high byte BRA Set_start ; set start address & return ; RTS ; Set character mode. This will read the default values and use those for ; the chr width and height, it will also set the display to 64 chrs/line. ; If you want the display on when chr mode is set call this routine with ; A<>0 . LCD_setchrmode: ; LDX #$00 ; display off/chr mode ; ORA #$00 ; set the flags ; BEQ CsetOff ; jump if display is to be off LDX #$20 ; display on/chr mode CsetOff: STX DispMode ; save mode JSR LCD_Xmode ; set display mode from X LDA #$02 ; Set no. of chrs instruction JSR LCD_instr ; send display control instruction LDA #$3E ; 64 chr mode JSR LCD_data ; go write data byte LDA ChrWidt ; get chr width CLC ; clear carry for add ADC #$01 ; +1 for routine ; JSR LCD_chrw ; go set it & return ; RTS ; Set the character width for the chr mode display. Width is in A, valid ; widths are 6,7 and 8 bits. After the width is set the new visible length ; and the new start offset are set. LCD_chrw: SEC ; set carry for subtract SBC #$06 ; convert to 0,1,2 BCC LCD_make ; if < 6 force to 8 bits wide CMP #$03 ; compare with 9 BCC LCD_wsok ; branch if was ok (range 6 to 8) LCD_make: LDA #$02 ; default is 8 bits wide LCD_wsok: ASL A ; *2 ASL A ; *4 TAX ; copy offset LDA DispTbl,X ; get width byte STA ChrWidt ; save new width ORA ChrHigh ; or in chr height TAY ; save size byte LDA #$01 ; Set chr pitch instruction JSR LCD_instr ; send display control instruction TYA ; get size byte back JSR LCD_data ; go write data byte ; now set the other values INX ; point to visible line length LDA DispTbl,X ; get visible line length STA LineLen ; save it INX ; point to offset low byte LDA DispTbl,X ; get low byte INX ; point to offset high byte LDY DispTbl,X ; get high byte TAX ; copy low byte JMP Set_start ; set start address & return ; RTS ; Write chr to display. This routine will emulate a TTY display with auto ; scroll H (ON/OFF) and auto scroll V (ON/OFF) and a virtual display size ; of 64H x 32V characters. ; Control codes include.... ; $0A Line Feed ; $0D Carrage Return ; $08 Backspace ; $09 Tab LCD_print: BBS 1,DispMode,CtrlDone ; exit if graph mode AND #$7F ; clear bit 7 CMP #$20 ; compare with ' ' BCC ctrl_chr ; jump if control character ; normal chr so out to display TAX ; copy character LDA #$0C ; write display data JSR LCD_instr ; set instruction TXA ; get chr back JSR LCD_data ; go write data byte ; now increment the cursor position LDX CursorAt ; get cursor position low byte LDY CursorAt+1 ; get cursor position high byte INX ; increment low byte BNE SetExit ; skip high byte increment INY ; increment high byte BRA SetExit ; set cursor and exit ;WasBELL: ; put your code to go 'BEEP' here ; RTS ctrl_chr: ; control characters get here.... ; CMP #$07 ; was it BELL ; BEQ WasBELL ; jump if so LDX CursorAt ; get cursor position low byte LDY CursorAt+1 ; get cursor position high byte CMP #$0D ; was it CR BEQ WasCR ; jump if so CMP #$0A ; was it LF BEQ WasLF ; jump if so CMP #$09 ; was it TAB BEQ WasTA ; jump if so CMP #$08 ; was it Backspace BEQ WasBA ; jump if so BRA CtrlDone ; else exit SetExit: JSR Set_cursor ; Set cursor address. 16 bit value in XY (low/high) CtrlDone: ; gets here if not valid ctrl chr RTS ; all these routines are entered with the current cursor position in XY ; was CARRIAGE RETURN WasCR: TXA ; copy cursor low byte AND #$C0 ; clear cursor x bits TAX ; save new low byte BRA SetExit ; set cursor and exit ; was LINE FEED WasLF: TXA ; copy cursor low byte ADC #$3F ; add linefeed offset+carry (carry set by CMP #$0A) TAX ; save new low byte BCC SetExit ; branch if no overflow INY ; increment high byte BRA SetExit ; set cursor and exit ; was TAB - need to do something cleverer here! WasTA: CLC ; clear carry for add TXA ; copy cursor low byte AND #$F8 ; mask out TAB bits ADC #$08 ; add TAB step TAX ; save new low byte BCC SetExit ; branch if no overflow INY ; increment high byte BRA SetExit ; set cursor and exit ; was BACKSPACE WasBA: TXA ; copy cursor low byte BNE WasBB ; branch if not zero DEY ; decrement high byte WasBB: DEX ; step back one BRA SetExit ; set cursor and exit .END |