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
; 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 ;'
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
Top comments (0)