Hex, dec, binary converter for 4 bit LCD


; latest 6502 board
; 
PORTB = $6000	  ; 8 bits PB0-PB7
PORTA = $6001     ; 8 bits PA0-PA7
DDRB = $6002      ; data direction B bits
DDRA = $6003      ; data direction A bits
E =  %00001000    ; PB3 connected
RW = %00000100    ; PB2 connected
RS = %00000010    ; PB1 connected

value  = $20	  ; from input str to 1 byte value
Hx     = $21	  ; store hex result
Cx     = $22	  ; store hunderds
Xx     = $23      ; store tens
Rx     = $24      ; store ones
keep   = $25      ; zero page, store A in lcd_instruction 

endz   = $0201    ; end of string
numeral = $0202   ; hex,dec,bin = 0,1,2
xmax   = $0203	  ; maximum xpos = 2,3,8
xpos   = $0206    ; position cursor
ypos   = $0207	  ; 0-7  (8 bytes max)
ymax   = $020f	  ; 16,10,2 (h,d,b)
binstr = $0212    ; store binary string (8 bytes)

	.org $8000

setup:			; setup io reset
	ldx #$ff
	txs      	; init stack pointer
 
	lda #%11111110  ; 7 outputs PB1 -PB7, PB0 free
	sta DDRB

	lda #%00000001  ; 7 pins input, 1 output (PA0 led)
	sta DDRA
	lda #%00000001
	sta PORTA       ; turn off led (sink)

	lda #%00000010	; 4-bit mode
	jsr lcd_instruction
	lda #%00101000  ; 4-bit mode, 2-lines, 5x8 font
	jsr lcd_instruction
	lda #%00001110  ; display on, blink on, cursor off
	jsr lcd_instruction
	jsr short_delay 
	lda #%00000110  ; increment and shift cursor, no shift display
	jsr lcd_instruction

init:
        lda #%00000001  ; clear screen
        jsr lcd_instruction
        jsr short_delay ; necessary delay for lcd
        lda #%10000000  ; cursor (0,0) line 1, pos 1
        jsr lcd_instruction
        jsr short_delay

        ; init variables
        lda #$0
        sta keep        ; keep = 0
        sta xpos        ; xpos = 0
        sta numeral     ; numeral = 0
        sta Hx
	sta Cx
	sta Xx
	sta Rx

        ldx #$0         ; index
        lda #$2
        sta xmax,x      ; xmax[0] = 2
        lda #$10
        sta ymax,x      ; ymax[0] = 16
        inx
        lda #$3
        sta xmax,x      ; xmax[1] = 3
        lda #$0a
        sta ymax,x      ; ymax[1] = 10
        inx
        lda #$8
        sta xmax,x      ; xmax[2] = 8
        lda #$2
        sta ymax,x      ; ymax[2] = 2

        ldx #$0
        lda #$0
lp0:                    ;  ypos[0-7]=0
        sta ypos,x
        inx
        cpx #$8
        bne lp0

start:
	; print "Press Up"
	lda #$0c         ; cursor off
	jsr lcd_instruction
	lda #$80         ; cursor 0,0
	jsr lcd_instruction
        ldx #$0          ; start of msg
        ldy #$8
        sty endz         ; end point of msg + 1 
	jsr print_msg
	jsr short_delay 
key_up_enter:		; to select input hex,dec,bin
        lda PORTA       ; 1111 111x
        and #$fe        ; 1111 1110  
        cmp #$fe        ; 1111 1110
        beq key_up_enter ; zero = no press, loop back
        jsr short_delay ; debounce delay
        lda PORTA       ; 1111 110x 
        and #$02        ; 0000 0010
        beq choice      ; up pressed
        lda PORTA
        and #$20        ; 0010 0000
	beq stage1	; enter pressed
	jmp key_up_enter
choice:			; print hex,dec or bin
	jsr blink_off
	ldx numeral	; read numeral 0
	inx		; x+1
	stx numeral	; store 1
	cpx #$3		; x==3
	beq set_b	; set back 0
	cpx #$2
	beq set_2
	cpx #$1
	beq set_1
	cpx #$0
	beq set_0
	jmp key_up_enter
set_b:
	ldx #$0
	stx numeral
	jmp set_0	 
set_2:
	lda #$80        ; cursor 0,0
	jsr lcd_instruction
  	ldx #$1b 	; start string
	ldy #$23	; end string
	sty endz
	jsr print_msg
	jmp key_up_enter
set_1:
	lda #$80	; cursor 0,0
	jsr lcd_instruction
  	ldx #$12 	; start string
	ldy #$1a	; end string
	sty endz
	jsr print_msg
	jmp key_up_enter
set_0:
	lda #$80	; cursor 0,0
	jsr lcd_instruction
  	ldx #$9 	; start string
	ldy #$11	; end string
	sty endz
	jsr print_msg
	jmp key_up_enter

stage1:			; hex 00 dec 000 bin 00000000
	jsr blink_on
	lda #$0e	; cursor on
	jsr lcd_instruction
	lda #$84	; cursor 0,4
	jsr lcd_instruction
	ldx numeral	; read numeral 0
	cpx #$2
	beq print_8
	cpx #$1
	beq print_3
	cpx #$0
	beq print_2
	jmp stage1      ; just in case
print_8:
  	ldx #$24 	; start string
	ldy #$2c	; end string
	sty endz
	jsr print_msg
	lda #$84	; cursor 0,4
	jsr lcd_instruction
	jmp keypress
print_3:
  	ldx #$24 	; start string
	ldy #$27	; end string
	sty endz
	jsr print_msg
	lda #$84	; cursor 0,4
	jsr lcd_instruction
	jmp keypress
print_2:
  	ldx #$24 	; start string
	ldy #$26	; end string
	sty endz
	jsr print_msg
	lda #$84	; cursor 0,4
	jsr lcd_instruction

keypress:               ; up,down,left,right,enter or clear
        lda PORTA       ; 1111 111x
        and #$fe        ; 1111 1110  
        cmp #$fe        ; 1111 1110
        beq keypress    ; zero = no press, loop back
        jsr short_delay ; debounce delay

        lda PORTA       ; 1111 110x 
        and #$02        ; 0000 0010
        beq b_up        
   
        lda PORTA       ; 1111 101x
        and #$04        ; 0000 0100
        beq b_down      
   
        lda PORTA       ; 1111 011x
        and #$08        ; 0000 1000
        beq b_left     
   
        lda PORTA       ; 1110 111x
        and #$10        ; 0001 0000
        beq b_right      
   
        lda PORTA       ; 1101 111x
        and #$20        ; 0010 0000
        beq b_enter     
   
        lda PORTA       ; 1011 111x
        and #$40        ; 0100 0000
        beq b_clear     
	
	jmp keypress

b_up:
	jmp jmp_up 	; not necessary but consistent
b_down:
	jmp jmp_down
b_left:			; branch can jump max 128 bytes
	jmp jmp_left
b_right:
	jmp jmp_right
b_enter:
	jmp jmp_enter   
b_clear:
	jmp jmp_clear   

clearpress:		; only clear button to start again
	lda PORTA       ; 1011 111x
        and #$40        ; 0100 0000
        beq b_clear
	jmp clearpress

jmp_up:			; get numeral (h,d,b); get xpos; get ypos,xpos; 
        		; incr ypos; > #$0; print char
	ldx xpos	; relative cursor postion 
	lda ypos,x	; index number of y in relation to xpos
	adc #$1		; ypos + 1= 1
	sta ypos,x	; store ypos
	ldx numeral	; check maximum allowed for numeral
	cmp ymax,x 	; ymax,0 = 16
	beq zero_ypos 	; make ypos zero
b_up_back:
	tax		; copy a to x
	lda str,x	; get print char using value of ypos,x
	jsr print_char
	lda xpos 	; set cursor back
	clc
	adc #$84	; add $84 to set cursor position  
	jsr lcd_instruction
	jmp keypress	
zero_ypos:		; ypos reached max+1
	clc
	lda #$0
	ldx xpos
	sta ypos,x	; store ypos, either +1 or 0
	jmp b_up_back

jmp_down:			; get numeral (h,d,b); get xpos; get ypos,xpos; 
			;dec ypos; print char
	ldx xpos   	; relative cursor postion 
	lda ypos,x 	; index number of y in relation to xpos
	sec        	; clear borrow
	sbc #$1    	; ypos - 1
	bcc max_ypos	; borrow set (carry clear)
        ldx xpos
	sta ypos,x	; store ypos
b_down_back:
	tax		; copy a to x 15,9,1
	lda str,x	; get print char using value of ypos,x
	jsr print_char
	lda xpos	; set cursor back
	clc
	adc #$84	; add $84 to set cursor position  
	jsr lcd_instruction
	jmp keypress	

max_ypos:		; ypos reached -1 set back to maximum -1
	ldx numeral 	; 0,1,2
	lda ymax,x 	; 16,10,2
	sec        	; clear borrow
	sbc #$1    	; 15,9,1
	ldx xpos
	sta ypos,x   	; store ypos, 15,9,1
	jmp b_down_back

jmp_left:
	sec	    	; clear borrow
	lda xpos    	; 0
	sbc #$1     	; xpos-1  
	bcc max_xpos
b_left_back:
	clc
	sta xpos	
	adc #$84    	; def location
	jsr lcd_instruction
	jmp keypress
max_xpos:
	ldx numeral
	sec
        lda xmax,x
	sbc #$1
	jmp b_left_back	

jmp_right:
	clc	    	; clear carry
	lda xpos    	; 0
	adc #$1     	; xpos+1 
	ldx numeral    	; check xmax
	cmp xmax,x  	; xmax[numeral]
	beq zero_xpos
b_right_back:
	sta xpos	
	clc
	lda xpos    	; 0
	adc #$84    	; def location
	jsr lcd_instruction
	jmp keypress
zero_xpos:
	lda #$0
	jmp b_right_back	

jmp_enter:
	lda #$0c         ; cursor off
	jsr lcd_instruction
	; if dec check input
	lda numeral  ; 0=hex
	cmp #$0
	beq hex_ok
	cmp #$1
	beq check_dec
	cmp #$2
	beq bin_ok
	jmp jmp_enter    ; not necessary

check_dec:   		; check input > 255
        ldx #$0
        lda ypos,x
        cmp #$3     	; a>=3?
        bcs error
        cmp #$2     	; a>=2
        bcs check2
        jmp dec_ok
check2:
        inx
        lda ypos,x
        clc
        cmp #$6    	; a >= 6
        bcs error
        cmp #$5    	; a >= 5
        bcs check3
        jmp dec_ok
check3:
        inx
        lda ypos,x
        clc
        cmp #$6    	; a >= 6
        bcs error
        jmp dec_ok
error:
        jmp stage1	; back to enter values


bin_ok:			; start of binary conversion 
	lda #$c0        ; set cursor line 2,1 1100 0000
	jsr lcd_instruction
	jsr bin_to_val
	jsr val_to_hex
	lda #$c4        ; set cursor line 2,5 1100 0100 
	jsr lcd_instruction
	jsr val_to_dec
	jsr short_delay
	jmp clearpress  ; clear as only option	

hex_ok:			; start of hex conversion
	lda #$c0        ; set cursor line 2,1 1100 0000
	jsr lcd_instruction
	jsr hex_to_val
	jsr val_to_dec
	lda #$c4        ; set cursor line 2,5 1100 0100 
	jsr lcd_instruction
	jsr val_to_bin
	jsr short_delay
	jmp clearpress   ; clear as only option	
	
dec_ok:			; start of decimal conversion
	lda #$c0        ; set cursor line 2,1 1100 0000
	jsr lcd_instruction
	jsr dec_to_val
        jsr val_to_hex
	lda #$c4        ; set cursor line 2,5 1100 0100 
	jsr lcd_instruction
	jsr val_to_bin
	jsr short_delay
	jmp clearpress  ; clear as only option	

jmp_clear:
	jmp init
;-----------------------------
bin_to_val:		; binary input to hex output
	ldx #$0
	stx value
bv_loop:
	lda ypos,x   ; ypos[0]
	ora value   ; 0000 0000
	inx
	cpx #$8
	beq bv_done
	asl
	sta value
	jmp bv_loop
bv_done:
	sta value
	rts
;-------------------------------
dec_to_val:
	lda #$0
        sta value	; value=0
dv_100:
        ldx #$0
        lda ypos,x      ; 0,1 or 2
        cmp #$01        ; >= 1?
        bcs dv_hunderd  ; carry set
dv_10:  
        ldx #$1
        lda ypos,x      
        cmp #$01        ; >= 1?
        bcs dv_ten
        ; add the one position
        lda value
        ldx #$02
        adc ypos,x
        sta value
	rts
dv_ten:
        sbc #$01   	; a-1
        ldx #$1
        sta ypos,x 	; ypos-1
        lda value   
        clc
        adc #$0a 	; 10
        sta value 	; store Hx+10
        jmp dv_10	

dv_hunderd:
        sbc #$01    	; minus 1
        ldx #$0
        sta ypos,x	; ypos-1
        lda value       ; get value
        clc
        adc #$64   	; 100
        sta value       ; store value+100
        jmp dv_100

;-------------------------------
hex_to_val:
        ldx #$0
        stx value	; value=0
        lda ypos,x      ; number first xpos
        asl
        asl
        asl
        asl             ; 4x shift left  xxxx 0000
        inx
        adc ypos,x      ; add number second xpos
        sta value       
	rts

;-------------------------------
val_to_hex:
        lda value       ; D7 1101 0111
        lsr             ; high nibble -> low
        lsr
        lsr
        lsr             ; 0000 1101
        tax             ; copy number a->x
        lda str,x       ; get char from str, 15=F
        jsr print_char
        lda value       ; 1101 0111
        and #$0f        ; 0000 1111 = 0111
        tax
        lda str,x
        jsr print_char
        rts
;-------------------------------
val_to_dec:
	lda value
vd_100:
        cmp #$64        ; >=100 ?
        bcs vd_hunderd  ; c=1, no borrow

vd_10:
        cmp #$0a        ; >=10 ?
        bcs vd_ten
vd_ones:
        sta Rx
        ldx Cx
        lda str,x
        jsr print_char
        ldx Xx
        lda str,x
        jsr print_char
        ldx Rx
        lda str,x
        jsr print_char
        rts

vd_hunderd:
        sec
        sbc #$64        ; subtract 100
        ldx Cx
        inx
        stx Cx          ;  Cx+1
        jmp vd_100

vd_ten:
        sec
        sbc #$0a        ; subtract 10
        ldx Xx
        inx
        stx Xx          ; Xx + 1
        jmp vd_10

;-------------------------------
val_to_bin:
        ldx #$0
        lda #$30      	; letter "0"
vb_loop1:    		; init binstr
        sta binstr,x  	; "0"->binstr[0-7]
        inx
        cpx #$8
        bne vb_loop1

        ldx #$7         ; from 7 to 0
vb_loop2:
        lda value       ; 0101 0101 &
        and #$1         ; 0000 0001
        beq vb_zero     ; and results in 0
        lda #$31        ; not zero
        sta binstr,x    ; order 7->0
        lsr value       ; shift right to check next bit
        dex             ; x-1
        cpx #$ff        ; (x)0-1
        beq vb_done
        jmp vb_loop2

vb_zero:
        lsr value	; shift right ->  0010 1010
        dex            ; x-1
        cpx #$ff
        beq vb_done
        jmp vb_loop2

vb_done:                ; print binstr
        ldx #$0
vb_loop3:
        lda binstr,x    ; 0->7
        jsr print_char
        inx
        cpx #$8
        bne vb_loop3
        rts
;-------------------------------
print_msg:
        lda msg,x
        jsr print_char
        inx
	cpx endz
	bne print_msg
	rts

        ;   0-8/9-11/12-1a/1b-23/24-2c/
msg: .byte "Press up hex      dec      bin      00000000 "
str: .byte "0123456789ABCDEF"

short_delay:
	ldx  #$ff   	; (2 cycles)
        ldy  #$3f   	; (2 cycles)
short_loop:
        dex          	; (2 cycles)
        bne  short_loop ; (3 cycles in loop, 2 cycles at end)
        dey         
        bne  short_loop 
	rts

delay:              	;  delay for 1MHz
	ldx  #$ff   	; (2 cycles)
        ldy  #$ff   	; (2 cycles)
	lda  #$02
delay_loop:
	nop
        dex          	; (2 cycles)
        bne  delay_loop ; (3 cycles in loop, 2 cycles at end)
	nop
        dey         
        bne  delay_loop 
	nop
	sbc #$1
        bne  delay_loop 
  	rts

lcd_instruction:        ; 1. remove low nibble ; 2. move lnibble to high bit
	sta keep        ; save A in keep
	and #$F0        ; lda & 1111 0000 
	sta PORTB
  	                ; Clear RS/RW/E bits no necessary
                        ; Set E bit to send instruction
  	ora #E          ; lda or E  xxxx 0000 or 0000 1000
        sta PORTB
  	                ; Clear RS/RW/E bits
        and #$F0        ; lda & 1111 0000
  	sta PORTB

	lda keep	; load lda from memory
	asl	        ; shift left 4x    0000 xxxx -> xxxx 0000
	asl	
	asl
	asl
	sta PORTB
  	                ; Set E bit to send instruction
  	ora #E          ; lda or E xxxx 0000 or 0000 1000
        sta PORTB
  	                ; Clear RS/RW/E bits
	and #$F0        ; lda & 1111 0000
  	sta PORTB
	rts

print_char:
	pha             ; push A on stack
	and #$F0        ; lda & 1111 0000 
	sta PORTB
	        	; clear E, set RS
	and #$f0        ; lda & 1111 0000
	ora #RS         ; lda or 0000 0010
	sta PORTB			
	                ; set E, set RS	
	ora #E          ; lda or 0000 1000
	ora #RS         ; lda or 0000 0010
	sta PORTB
	        	; clear E, set RS
	and #$f0        ; lda & 1111 0000
	ora #RS         ; lda or 0000 0010
	sta PORTB

	pla 		; pull A from stack
	asl	        ; shift left 4x
	asl	
	asl
	asl
	sta PORTB
	        	; clear E, set RS
	and #$f0        ; lda & 1111 0000
	ora #RS         ; lda or 0000 0010
	sta PORTB
	                ; set E, set RS	
	ora #E          ; lda or 0000 1000
	ora #RS         ; lda or 0000 0010
	sta PORTB
	        	; clear E, set RS
	and #$f0        ; lda & 1111 0000
	ora #RS         ; lda or 0000 0010
	sta PORTB
	rts

blink_on:  ; sink=on
	ldy #$0
	sty PORTA      
	rts

blink_off:  ; source=off
	ldy #$1
	sty PORTA
	rts

	
	.org $fffc
	.word setup
	.word $0000