Lab 3 addition/subtraction calculator

 In this blog post I will write and actually implement the subtracting portion of my previous blog post about lab 3. Below is the code for the subtraction and addition calculator in the 6502 emulator.

; Adding calculator with addition and subtraction capabilities


; ROM routine entry points

define          SCINIT          $ff81 ; initialize/clear screen

define          CHRIN           $ffcf ; input character from keyboard

define          CHROUT          $ffd2 ; output character to screen

define          SCREEN          $ffed ; get screen size

define          PLOT            $fff0 ; get/set cursor coordinates


; zeropage variables

define          PRINT_PTR       $10

define          PRINT_PTR_H     $11

define          value           $14

define          value_h         $15

define          second_value    $16

define          second_value_h  $17

define          OPERATOR        $18


; absolute variables

define          GETNUM_1        $0080

define          GETNUM_2        $0081


; constants


; --------------------------------------------------------


                jsr SCINIT

                jsr CHRIN


                jsr PRINT


dcb "A","d","d","i","n","g",32,"o","r",32,"s","u","b","t","r","a","c","t","i","n","g",32

dcb "c","a","l","c","u","l","a","t","o","r",00


start:          jsr PRINT

                

dcb $0d,$0d,"E","n","t","e","r",32,"a",32,"n","u","m","b","e","r"

dcb "(","0","-","9","9",")",":",32,00


                lda #$00

                sta value_h


                jsr GETNUM

                sta value


                jsr CHOOSE_OP  ; Choose addition or subtraction


                jsr PRINT


dcb "E","n","t","e","r",32,"a","n","o","t","h","e","r"

dcb 32,"n","u","m","b","e","r",32,"(","0","-","9","9",")",":",32,00


                lda #$00

                sta second_value_h


                jsr GETNUM

                sta second_value


                lda OPERATOR

                beq do_add   


do_sub:         sed

                sec          

                lda value

                sbc second_value

                sta value

                lda value_h

                sbc second_value_h

                sta value_h

                cld

                jmp result


do_add:         sed

                clc          

                lda value

                adc second_value

                sta value

                lda value_h

                adc second_value_h

                sta value_h

                cld


result:         pha

                jsr PRINT


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


                pla

                tax

                ldy #$00


print_result:   ldx value_h

                cpx #$00

                beq low_digits_result

                lda value_h

                and #$0f       

                ora #$30       

                jsr CHROUT


low_digits_result:

                lda value

                and #$f0

                beq ones_digit_result


                lda value

                lsr

                lsr

                lsr

                lsr

                and #$0f       

                ora #$30       

                jsr CHROUT


ones_digit_result:

                lda value

                and #$0f       

                ora #$30       

                jsr CHROUT


                jsr start


; --------------------------------------------------------

; Print a message

;

; Prints the message in memory immediately after the

; JSR PRINT. The message must be null-terminated and

; 255 characters maximum in length.


PRINT:          pla

                clc

                adc #$01

                sta PRINT_PTR

                pla

                sta PRINT_PTR_H


                tya

                pha


                ldy #$00

print_next:     lda (PRINT_PTR),y

                beq print_done

                

                jsr CHROUT

                iny

                jmp print_next


print_done:     tya

                clc

                adc PRINT_PTR

                sta PRINT_PTR


                lda PRINT_PTR_H

                adc #$00

                sta PRINT_PTR_H


                pla

                tay


                lda PRINT_PTR_H

                pha

                lda PRINT_PTR

                pha


                rts


; ---------------------------------------------------

; GETNUM - get a 2-digit decimal number

;

; Returns A containing 2-digit BCD value


GETNUM:         txa

                pha

                tya

                pha


                ldx #$00        ; count of digits received

                stx GETNUM_1

                stx GETNUM_2


getnum_cursor:  lda #$a0        ; black square

                jsr CHROUT

                lda #$83        ; left cursor

                jsr CHROUT


getnum_key:     jsr CHRIN

                cmp #$00

                beq getnum_key


                cmp #$08        ; BACKSPACE

                beq getnum_bs


                cmp #$0d        ; ENTER

                beq getnum_enter


                cmp #$30        ; "0"

                bmi getnum_key


                cmp #$3a        ; "9" + 1

                bmi getnum_digit


                jmp getnum_key


getnum_enter:   cpx #$00

                beq getnum_key


                lda #$20

                jsr CHROUT

                lda #$0d

                jsr CHROUT


                lda GETNUM_1


                cpx #$01

                beq getnum_done


                asl

                asl

                asl

                asl

                ora GETNUM_2


getnum_done:    sta GETNUM_1

                pla

                tay

                pla

                tax

                lda GETNUM_1


                rts


getnum_digit:   cpx #$02

                bpl getnum_key

                pha

                jsr CHROUT

                pla

                and #$0f

                sta GETNUM_1,x

                inx

                jmp getnum_cursor


getnum_bs:      cpx #$00

                beq getnum_key

                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


; ---------------------------------------------------

; Operator Selection Subroutine

;

CHOOSE_OP:      jsr PRINT

dcb $0d,$0d,"C","h","o","o","s","e",32,"o","p","e","r","a","t","o","r",32,"(","+","-",")",":",32,00


get_op:         jsr CHRIN

                cmp #$2b       ; "+"

                beq op_add

                cmp #$2d       ; "-"

                beq op_sub

                jmp get_op


op_add:         lda #$00       ; Addition

                sta OPERATOR

                rts


op_sub:         lda #$01       ; Subtraction

                sta OPERATOR

                rts


In my previous post I already discussed how the addition portion of this program works so I will talk more about the new subroutines that I added, more specifically the subtraction and operation choice routines.

First, a prompt is given to the user to choose whether they want to add or subtract, this user input is read using CHRIN. If the input matches +/- (#2b, or #2d in ASCII) then the program will jump to its respective operation.

I added a do_add or sub routines to handle each arithmetic operation. I will explain the do_sub. The value of the operator is determined first to see which arithmetic operation it should do, then sed sets the decimal mode for BCD arithmetic. sec instruction (set carry flag) is then used to start subtracting. I learned that sec must be set before using the sbc instruction to indicate that no borrow has occurred at the start, and so the processor will know that the full value is available for subtracting. lda is then used to load the low byte of the first number in the accumulator, then sbc second_value is used to subtract second_value from the value in the accumulator. Then the result is stored using sta. The same thing is done for the decimal high byte , if a borrow occurs during the low byte subtraction, then it is carried over and considered in this section to ensure the result is correct. Finally, we cld to clear the decimal mode and revert the processor to the regular binary mode.

To summarize the result displaying section, it first pushes and then pulls the accumulator value to save the result temporarily when printing the result message. It also determines if result is a multi-byte value, and if the high byte is not zero then it prints to 3 digits.






The calculator works fine for all 2 digit numbers and can handle 3 digit addition results but when it comes to subtracting and getting a negative number it will not work due to how the numbers are represented. We use binary here to represent decimals in our code, and there is no way to show a negative like that. Furthermore, our result printing logic always assumes a non negative value as a result. So many alterations would be needed to implement negative numbers. I know that we can use two's complement rule to allow the accumulator to hold negative and positive values but we would need to switch up our whole arithmetic logic. Not only that but we would have to change our display logic too.


Comments

Popular posts from this blog

Early stages of project

Building GCC again on aarch64-002

Project Stage 2: Testing