top of page
Writer's picturerehsd

Driving a 1602 LCD with a 286 Processor!

Updated: Oct 19, 2022

I have been working on a breadboard-based 80286 system. Recently, I made it to a nice milestone -- I can now drive a 1602 LCD!


This is a continuation of Start to an 80286 System, if you are interested in my journey so far (and more of the design details).



Schematic


Decode Logic

Code Used in Video

; *physical memory map*
;-----------------------
;-         ROM         -
;-        0.5 MB       -
;-   0x80000-0xFFFFF   -
;-----------------------
;-         RAM         -
;-        0.5 MB       -
;-   0x00000-0x7FFFF   -
;-----------------------

CPU	286
BITS 	16				

TIMES 0x80000-($-$$) DB 0	;Fill bottom half of ROM with zeros.
					;Bottom half of address space used by RAM. 
					;Controlled with A19 decode.

ORG 0x8000:0x0000			;Usable ROM starts at physical address 0x80000
;ORG 0x0

TOP:					;physically at 0x80000 in ROM
CLI					;disable interrupts

;*** SETUP REGISTERS **********************************
MOV	AX,	0X0
;					;code segment
MOV	DS,	AX			;data segment
MOV	ES,	AX			;extra segment
MOV	SS,	AX			;stack segment
MOV	SP,	AX			;Start at 0, will wrap around to FFFE
;*** /SETUP REGISTERS *********************************

MOV AL, 0b00111000		;Set 8-bit mode; 2-line display; 5x8 font
CALL lcd_instruction
MOV AL, 0b00001110 		;Display on; cursor on; blink off
CALL lcd_instruction
MOV AL, 0b00000110 		;Increment and shift cursor; don't shift display
CALL lcd_instruction
MOV AL, 0b00000001 		;Clear display
CALL lcd_instruction

MOV AL, 'H'
CALL print_char
MOV AL, 'E'
CALL print_char
MOV AL, 'L'
CALL print_char
MOV AL, 'L'
CALL print_char
MOV AL, 'O'
CALL print_char
MOV AL, ' '
CALL print_char
MOV AL, '2'
CALL print_char
MOV AL, '8'
CALL print_char
MOV AL, '6'
CALL print_char
MOV AL, '!'
CALL print_char

HLT

lcd_instruction:
	OUT 0x02, AL			;sta PORTB
	
	MOV AL, 0x0
	OUT 0x04, AL			;lda #0         ;Clear RS/RW/E bits
						;sta PORTA
	
	MOV AL, 0b10000000
	OUT 0x04, AL			;lda #E         ;Set E bit to send instruction
						;sta PORTA

	MOV AL, 0x0
	OUT 0x04, AL			;lda #0         ;Clear RS/RW/E bits
						;sta PORTA
	RET

print_char:
	OUT 0x02, AL			;sta PORTB

	MOV AL, 0b00100000
	OUT 0x04, AL			;lda #RS         ; Set RS; Clear RW/E bits
						;sta PORTA

	MOV AL, 0b10100000		
	OUT 0x04, AL			;lda #(RS | E)   ; Set E bit to send instruction
						;sta PORTA

	MOV AL, 0b00100000		
	OUT 0x04, AL			;lda #RS         ; Clear E bits
						;sta PORTA

	RET

TIMES 0xFFFF0-($-$$) NOP	;Fill ROM with NOPs up to startup address
					;(upper portion of 1 MB addr space)
					;This will get to 0xFFFF0 

RESET:				;at 0xFFFF0			Processor starts reading here
JMP 0x8000:0x0			;EA	00 00 00 80		Jump to TOP: label


TIMES 0x100000-($-$$) DB 1	;Fill the rest of ROM with bytes of 0x01



Updated Decode Logic

I updated the decode logic diagram in PSoC Creator to make it more readable for myself. I also added an I/O read for port 0x02 so that I can read the LCD busy status.



Next Steps

In the coming months, I plan to update the system with the following:

  • 82C284 Clock Generator

  • 82C288 Bus Controller

  • D8255 Programmable Peripheral Interface

  • Keyboard

  • D80287 Math Coprocessor

Further out, I plan to work on:

  • Mouse

  • SPI

  • ISA Bus

  • ISA VGA Card

  • ISA Sound Card


In the above video, I was running a processor clock of 2 MHz. With a little minor tweaking, I now have the processor clock running at 5 MHz.

Latest Code

;Assembler: NASM
;
; *physical memory map*
;-----------------------
;-    ROM  (0.5 MB)    -
;-   0x80000-0xFFFFF   -
;-----------------------
;-    RAM  (0.5 MB)    -
;-   0x00000-0x7FFFF   -
;-----------------------

PORTB EQU 0x02
PORTA EQU 0x04

E 	EQU 0b10000000
RW 	EQU 0b01000000
RS	EQU 0b00100000

ORG 	0x0
CPU	286
BITS 	16	

STRING_HELLO DB 'Hello at 4 MHz!', 0x0			

TIMES 0x80000-($-$$) DB 0	;Fill bottom half of ROM with zeros.
					;Bottom half of address space used by RAM. 
					;Controlled with A19 decode.

;ORG 0x8000:0x0000

TOP:					;physically at 0x80000 in ROM
CLI					;disable interrupts

;*** SETUP REGISTERS **********************************
MOV	AX,	0X0
MOV	DS,	AX			;data segment
MOV	ES,	AX			;extra segment
MOV	SS,	AX			;stack segment
MOV	SP,	AX			;Start at 0, will wrap around to FFFE
;*** /SETUP REGISTERS *********************************

MOV AL, 0b00111000		;Set 8-bit mode; 2-line display; 5x8 font
CALL lcd_instruction
MOV AL, 0b00001110 		;Display on; cursor on; blink off
CALL lcd_instruction
MOV AL, 0b00000110 		;Increment and shift cursor; don't shift display
CALL lcd_instruction
MOV AL, 0b00000001 		;Clear display
CALL lcd_instruction

CALL delay

CALL print_message

HLT

print_message:
	PUSHA

	MOV AL, 'H'
	CALL print_char
	MOV AL, 'E'
	CALL print_char
	MOV AL, 'L'
	CALL print_char
	MOV AL, 'L'
	CALL print_char
	MOV AL, 'O'
	CALL print_char
	MOV AL, ' '
	CALL print_char
	MOV AL, '2'
	CALL print_char
	MOV AL, '8'
	CALL print_char
	MOV AL, '6'
	CALL print_char
	MOV AL, ' '
	CALL print_char
	MOV AL, ' '
	CALL print_char
	MOV AL, ' '
	CALL print_char
	MOV AL, '5'
	CALL print_char
	MOV AL, 'M'
	CALL print_char
	MOV AL, 'H'
	CALL print_char
	MOV AL, 'z'
	CALL print_char

	;MOV AL, 0b10101000 		;move cursor to line 2  **not working  ??!
	;CALL lcd_instruction
	
	POPA
	RET

lcd_wait:
	PUSHA
.lcd_busy:
	MOV AL, RW				;lda #RW
	OUT PORTA, AL			;sta PORTA
	MOV AL, (RW|E)			;lda #(RW | E)
	OUT PORTA, AL			;sta PORTA
	IN AL, PORTB			;lda PORTB
	AND AL, 0b10000000		;and #%10000000
	JNZ .lcd_busy			;bne lcdbusy

	MOV AL, RW				;lda #RW
	OUT PORTA, AL			;sta PORTA
	
	POPA
	RET

lcd_instruction:
	PUSHA
	CALL lcd_wait

	OUT PORTB, AL			;sta PORTB
	
	MOV AL, 0x0
	OUT PORTA, AL			;lda #0         ;Clear RS/RW/E bits
						;sta PORTA
	
	MOV AL, E				;0b10000000
	OUT PORTA, AL			;lda #E         ;Set E bit to send instruction
						;sta PORTA

	MOV AL, 0x0
	OUT PORTA, AL			;lda #0         ;Clear RS/RW/E bits
						;sta PORTA
	POPA
	RET

print_char:
	PUSHA
	CALL lcd_wait

	OUT PORTB, AL			;sta PORTB

	MOV AL, RS				;0b00100000
	OUT PORTA, AL			;lda #RS         ; Set RS; Clear RW/E bits
						;sta PORTA

	MOV AL, (E|RS)			;0b10100000		
	OUT PORTA, AL			;lda #(RS | E)   ; Set E bit to send instruction
						;sta PORTA

	MOV AL, 0b00100000		
	OUT PORTA, AL			;lda #RS         ; Clear E bits
						;sta PORTA
	POPA
	RET

delay:
	PUSHA
	mov bp, 0x0000
	mov si, 0x0001
	.delay2:
		dec bp
		nop
		jnz .delay2
		dec si
		cmp si,0    
		jnz .delay2
	POPA
	RET

TIMES 0xFFFF0-($-$$) NOP	;Fill ROM with NOPs up to startup address
					;(upper portion of 1 MB addr space)
					;This will get to 0xFFFF0 

RESET:				;at 0xFFFF0			Processor starts reading here
JMP 0x8000:0x0			;EA	00 00 00 80		Jump to TOP: label


TIMES 0x100000-($-$$) DB 1	;Fill the rest of ROM with bytes of 0x01



243 views0 comments

Comments


bottom of page