Adding Calculator in 6502 Emulator
Tags: spo600
Published: 2023-09-22 18:21:56

**Features**
- Simple adding calculator program for a computer system using 6502 assembly language.
- It includes routines for initializing the screen, obtaining user input, displaying the result on the character screen, and presenting a green colour on the graphics screen.

**Source Code**

```sh
; Adding Calculator

; ROM routine entry points
define		SCINIT		$ff81 ; initialize/clear screen
define		CHRIN		$ffcf ; input character from keyboard
define		CHROUT		$ffd2 ; output character to screen

; zeropage variables
define		PRINT_PTR	$10
define		PRINT_PTR_H	$11
define		firstInput	$14
define		secInput	$15

; absolute variables
define		GETNUM_1	$0080
define		GETNUM_2	$0081
define 		ENTER  		$0d	; for the ENTER key
define 		BACKSPACE 	$08	; for the BACKSPACE key

; --------------------------------------------------------
; Main loop
		jsr SCINIT	; initialize/clean screen

main:		
; get first input'
		ldy #$00        
		jsr firstInputQs
; '
		jsr GETNUM	; get firstInput
		sta firstInput	; store firstInput		

; get second input'
		ldy #$00
		jsr secondInputQs
; '
		jsr GETNUM	; get secondInput
	
		sed		; sets decimal model flag in processor status register	
		clc		; clear carry flag in status register
		adc firstInput	; add with carry
		cld		; clear decimal mode flag

		sta firstInput	; store result
		bcc result	; if carry flag=0 -> branch to result
		inc secInput	; increment

; get sum result
result:		
        pha		; push current value onto the stack'
		ldy #$00
		jsr showResultMsg		
; '
		lda secInput	
		beq low_digits	; if result=0 -> branch to low-digits
        lda #$31	
		jsr CHROUT	
		jmp draw_100s

low_digits:	
        lda firstInput	
		and #$f0	; keep high nibble, masking out low nibble
		beq ones_digit	; if high nibble=0 -> branch to ones_digit

draw_100s:	
        lda firstInput	
		lsr		; logical shift right
		lsr
		lsr
		lsr
		ora #$30	; add ASCII value for 0 for conversion to char
		jsr CHROUT


ones_digit:	
        lda firstInput
		and #$0f
		ora #$30
		jsr CHROUT
; initialize green pointer'
	    ldx #$00
    	 stx $10  
	    stx $11
; ' 
		jsr display_green
		jsr main

; --------------------------------------------------------
; Print a message

PRINT:		
        pla			; pull the value from stack -> accumulator 
		clc			; clear carry flag 
		adc #$01		; add with carry
		sta PRINT_PTR	
		pla			; retrieves a byte from stack -> accum
		sta PRINT_PTR_H

		tya			; transfer y to accumulator
		pha			; store value in accumulator -> stack

		ldy #$00		; load y register with 0

print_next:	
        lda (PRINT_PTR),y	; load PRINT_PTR, Y into accumulator
		beq print_done		
		
		jsr CHROUT		
		iny			
		jmp print_next

print_done:	
        tya			
		clc			
		adc PRINT_PTR		
		sta PRINT_PTR		; store the result in PRINT_PTR

		lda PRINT_PTR_H		; load value at PRINT_PTR_H -> accumulator
		adc #$00		; add zero
		sta PRINT_PTR_H		; store the result in PRINT_PTR_H

		pla			
		tay			; transfer accumulator to Y

		lda PRINT_PTR_H		
		pha			; push accumulator to stack
		lda PRINT_PTR		
		pha			

		rts			; return

; ---------------------------------------------------
; GETNUM - get a 2-digit decimal number
;
; Returns A containing 2-digit BCD value

GETNUM:		
        txa			; transfer x register -> accumulator
		pha			; push accumulator value -> stack
		tya			; transfer y register -> accumulator
		pha			

		ldx #$00		; count of digits received
		stx GETNUM_1		; store value of x into $0080
		stx GETNUM_2		; store value of x into $0081


getnum_cursor:	
        lda #$a0		; black square
		jsr CHROUT
		lda #$83		; left cursor
		jsr CHROUT

key_check:	
                jsr CHRIN
		cmp #$00
		beq key_check		; if 0 -> loops back

		cmp #BACKSPACE		; comps value w/BACKSPACE
		beq getnum_bs

		cmp #ENTER		; comps value w/ENTER
		beq getnum_enter

		cmp #$30		; "0"
		bmi key_check		; if result of comps=negative 

		cmp #$3a		; "9" + 1
		bmi getnum_digit

		jmp key_check

getnum_enter:	
        cpx #$00		; compare x with 0
		beq key_check		; if 0 -> branch

		lda #$20		
		jsr CHROUT
		lda #$0d
		jsr CHROUT

		lda GETNUM_1

		cpx #$01		; comps x register with 1
		beq getnum_done		 

		asl			; multiples value * 2
		asl
		asl
		asl
		ora GETNUM_2		; logical OR ops

getnum_done:	
        sta GETNUM_1		
		pla			; pull top value from stack -> accumulator
		tay			; transfer accumulator -> y
		pla
		tax			; transfer accum -> x
		lda GETNUM_1

		rts			; return

getnum_digit:	
        cpx #$02		; comps x value with 2
		bpl key_check		; if positive -> branch
		pha			; push accu -> stack	
		jsr CHROUT
		pla			
		and #$0f		; bitwise AND ops
		sta GETNUM_1,x	
		inx
		jmp getnum_cursor

getnum_bs:	
        cpx #$00
		beq key_check
		lda #$20
		jsr CHROUT
		lda #$83
		jsr CHROUT
		jsr CHROUT
		lda #$20
		jsr CHROUT
		lda #$83
		jsr CHROUT
		dex			
		lda #$00
		sta GETNUM_1,x
		jmp getnum_cursor

; ---------------------------------------------------
; print questions'

fistInput:
dcb $0d,$0d,"E","n","t","e","r",32,"a",32,"F","I","R","S","T",32,"N","U","M","B","E","R"
dcb "(","0","-","9","9",")",":"
dcb 32,32,32,32,32,32,32,00

secondInput:
dcb "E","N","T","E","R",32,"S","E","C","O","N","D",32,"N","U","M","B","E","R"
dcb "(","0","-","9","9",")",":"
dcb 32,32,32,32,32,32,32,32,00

sumResult:
dcb "S","u","m",32,"R","e","s","u","l","t",":",32,32
dcb 32,32,32,32,32,32,32
dcb 32,32,32,32,32,32,32
dcb 32,32,32,32,32,32,32
dcb 00

firstInputQs:
 	lda fistInput,y
        beq backToMain
        jsr CHROUT
        iny
        bne firstInputQs

secondInputQs:
	lda secondInput,y
        beq backToMain
        jsr CHROUT
        iny
        bne secondInputQs

showResultMsg:
	lda sumResult,y
        beq backToMain
        jsr CHROUT
        iny
        bne showResultMsg

backToMain:
	rts

; ---------------------------------------------------
; display green color when result is available

display_green: 
	lda #$0D 	; Load a with colour code
	ldy #$00 	; load the Y register with 0

color_loop:
	sta ($10), y 	; add y to pointer at $10 and store the accumulator there
	iny 	     	; increment the y register y++
	bne color_loop     	; continue until page 2 is done

	inc $11 	; increment high byte (page) of pointer
	cpx $11 	; compare value in X register with page number 6
	bne color_loop 	; continue as long as we have not hit the page 6.
	rts         ;'
```

**Demo**
![Result on the screen and text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hbtm0d5gj1j7btzeabfk.png)

**Main Sections**

1.Definitions:
- Various **ROM** routine entry points and memory addresses are defined.
- Zeropage and absolute variables are allocated.

2.Initialization:
- The program starts by initializing/clearing the screen and getting user input.
- The **PRINT** subroutine is called to display the initial message.

3.Message Definitions:
- Constant byte strings are defined and stored in memory.

4.Input and Addition:
- The program prompts the user to enter two numbers.
- The **GETNUM** subroutine is used to receive and process the input.
- The numbers are added together using ADC arithmetic.

5.Result Display:
- The result is displayed on the screen, handling cases where the result might be more than one digit.

6.Printing Subroutine:
- The **PRINT** subroutine prints a null-terminated message immediately after it's called.

7.**GETNUM** Subroutine:
- This subroutine reads a 2-digit decimal number from the user.

8.Keyboard Input Handling:
- The program handles various keyboard inputs including backspace, enter, and numeric characters.

9.Printing Cursor and Feedback:
- The program displays a cursor and updates the screen based on user input.

10.Backspace Handling:
- Handles the backspace key to erase the previous input.

**Reflection**
It was a lot harder than I thought to write a new program from scratch using a 6502 emulator. Since I am a super beginner, I had to look up every assembly language instruction used in 6502 programming. So, I decided to use existing code and build upon it, which was not easy either. First of all, I needed to understand what was going on in the existing code, which took a lot of time. Secondly, it took even more time to implement the new code on top of that. I think I need to become more familiar with the 6502 emulator through lots of practice and revision, so that I can do better next time.

**Ideas for further enhancement of the program**
- Subtraction Operation: Add functionality to perform subtraction in addition to addition.
- Multiplication and Division: Extend the calculator to handle multiplication and division operations.
- Error Messages: Provide clear error messages to the user in case of invalid input or operations.

**Limitations or remaining minor bugs**
Using various colour displays that change based on user input can enhance the program's user-friendliness and overall quality.

---------------------------------------------
Reference: https://github.com/ctyler/6502js-code/blob/master/adding-calculator.6502