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
Comments