BATCH 3 | LAB 3 - Working Code for Addition and Subtraction Calculator
Lab 3 - Addition and Subtraction
Hi everyone! If you've been following along, you will notice in my list of blog posts that I previously wrote an assembly code for a simple addition and subtraction calculator designed for the 6502 Emulator. Unfortunately, my first attempt did not work as intended. So now, here's an improved and fully testable version that performs much better.
Before we jump into testing the code, let's break down each part of the code to understand exactly what's happening.
1. Predefined ROM Routines and Constants
To use the ROM routines, we were provided with define directives to paste into our code. These directives were taken from Chris Tyler's Wiki.
define SCINIT $ff81
define CHRIN $ffcf
define CHROUT $ffd2
define SCREEN $ffed
define PLOT $fff0
2. Zero Page Memory Locations
These define directives reserve addresses in the zero page for storing data including INPUT1 and INPUT2 that store the numeric values of the first and second digits, RESULT that holds the result of the operation. OPERATOR that stores the operator character for + and - operations and RESULT_TEMP that is used during the process of converting the number into a readable form.
define INPUT1 $0010
define INPUT2 $0011
define RESULT $0012
define OPERATOR $0013
define RESULT_TEMP $0014
3. ASCII Character Define Directives
These define directives provide readable names for ASCII codes and are used in input routines or screen output to compare or print characters.
define BACKSPACE $08
define ENTER $0D
define SPACE $20
define BLACK $A0
define LEFT $83
4. Jump for Execution
The first executable instruction is a jump so that the CPU starts executing at START. Prevents the CPU from mistakenly interpreting data as code..
JMP START
5. Prompt Messages
Using DCB to prompt messages to be displayed. Each string is defined character by character in hexadecimal. For instance, PROMPTFIRST spells out "ENTER FIRST DIGIT (0-9): " and ends with a 0. Code handles all newlines for formatting and easy readability.
PROMPTFIRST:
dcb $45,$4E,$54,$45,$52,$20,$46,$49,$52,$53,$54,$20,$44,$49,$47,$49,$54,$20,$28,$30,$2D,$39,$29,$3A,$20,$00
...
PROMPTOPERATOR:
dcb $45,$4E,$54,$45,$52,$20,$4F,$50,$45,$52,$41,$54,$4F,$52,$20,$28,$2B,$20,$2D,$29,$3A,$20,$00
...
PROMPTSECOND:
dcb $45,$4E,$54,$45,$52,$20,$53,$45,$43,$4F,$4E,$44,$20,$44,$49,$47,$49,$54,$20,$28,$30,$2D,$39,$29,$3A,$20,$00
...
PROMPTRESULT:
dcb $52,$45,$53,$55,$4C,$54,$3A,$20,$00
6. Storage for Echoing Input
These DCB declarations temporarily hold the character input for the first digit, second digit, and operator. They are used by the input routines so that the entered digits can be stored and later converted from ASCII to numeric form and then echoes input on screen.
INPUTFIRST_STORAGE:
dcb $00
INPUTSECOND_STORAGE:
dcb $00
OPERATOR_STORAGE:
dcb $00
7. Main Code
This is where the main execution of the calculator begins. The logic will be explained down below after the code:
START:
JSR SCINIT
LDY #$00
LOOP_PROMPT_FIRST:
LDA PROMPTFIRST, Y
BEQ GET_FIRST_DIGIT
JSR CHROUT
INY
BNE LOOP_PROMPT_FIRST
GET_FIRST_DIGIT:
JSR INPUT_FIRST
JSR PRINT_NEWLINE
LDY #$00
LOOP_PROMPT_OPERATOR:
LDA PROMPTOPERATOR, Y
BEQ GET_OPERATOR
JSR CHROUT
INY
BNE LOOP_PROMPT_OPERATOR
GET_OPERATOR:
JSR CHRIN
CMP #$3D
BEQ TREAT_AS_PLUS
CMP #$2B
BEQ STORE_OPERATOR
CMP #$2D
BEQ STORE_OPERATOR
JMP GET_OPERATOR
TREAT_AS_PLUS:
LDA #$2B
STORE_OPERATOR:
STA OPERATOR
JSR CHROUT
JSR PRINT_NEWLINE
LDY #$00
LOOP_PROMPT_SECOND:
LDA PROMPTSECOND, Y
BEQ GET_SECOND_DIGIT
JSR CHROUT
INY
BNE LOOP_PROMPT_SECOND
GET_SECOND_DIGIT:
JSR INPUT_SECOND
JSR PRINT_NEWLINE
LDA INPUTFIRST_STORAGE
SEC
SBC #$30
STA INPUT1
LDA INPUTSECOND_STORAGE
SEC
SBC #$30
STA INPUT2
LDA OPERATOR
CMP #$2B
BEQ DO_ADDITION
CMP #$2D
BEQ DO_SUBTRACTION
JMP ERROR
What It Does:
- SCINIT is used to set up a clear display.
- PROMPT and GET input print the prompt messages for the first digit, the operator and the second digit by looping through each DCB-defined string and using CHROUT. It then calls the input subroutines such as INPUT_FIRST and INPUT_SECOND to get valid digits.
- Operator reads the operator, treating "=" as "+" since it requires a shift + = to get "+".
- After the input is echoed, it converts the ASCII digits to numeric values by subtracting $30.
- Depending on the chose operator, whether it is '+' or '-', it will perform addition or subtraction and stores the result.
- It prints the "RESULT: " prompt and then calls the PRINT_NUMBER routine to display the result.
- If an error occurs such as an invalid operator, it prints an error character and restarts. Otherwise, after printing the result, the program ends with BRK.
8. DO_ADDITION and DO_SUBTRACTION
This part of the code performs the basic arithmetic operations depending on the chosen operator. If user inputs "+", it will perform DO_ADDITION which clears the carry, adds the two numeric values, and stores the result. Otherwise, DO_SUBTRACTION will be called which sets the carry then subtracts the second value from the first, and stores the result which will eventually display on screen.
DO_ADDITION:
CLC
LDA INPUT1
ADC INPUT2
STA RESULT
JMP PRINT_RESULT
DO_SUBTRACTION:
SEC
LDA INPUT1
SBC INPUT2
STA RESULT
JMP PRINT_RESULT
9. Result Printing
This part prints the "RESULT: " prompt and then prints the calculated result. It checks if the result is negative. If it is negative, it then prints a '-' sign, computes the absolute value and then prints the positive number. For numbers less than 10, it prints one digit but for numbers 10 or greater, it calculates tens and ones using a loop that prints two digits.
PRINT_RESULT:
LDY #$00
// (print PROMPTRESULT)
PRINT_NUMBER:
LDA RESULT
CMP #$80
BCC PRINT_POSITIVE
10. PRINT_NEWLINE Subroutine
This part of the code is used to have that results appear on a new line for easy readability. It outputs a carry return and line using CHROUT.
PRINT_NEWLINE:
LDA #$0D
JSR CHROUT
LDA #$0A
JSR CHROUT
RTS
11. INPUT_FIRST and INPUT_SECOND
These subroutines wait for the user to enter a key, validate that it’s a digit between 0 and 9, store the ASCII character in a temporary storage, and echoes the digit on the screen. They loop until a valid digit is being inputted.
INPUT_FIRST:
LDY #$00
GETKEY1:
JSR CHRIN
CMP #$00
BEQ GETKEY1
CMP #$30
BMI GETKEY1
CMP #$3A
BPL GETKEY1
STA INPUTFIRST_STORAGE
JSR CHROUT
RTS
LET'S TEST IT OUT!
Try it out on 6502 Emulator!
define SCINIT $ff81define CHRIN $ffcfdefine CHROUT $ffd2define SCREEN $ffeddefine PLOT $fff0define INPUT1 $0010define INPUT2 $0011define RESULT $0012define OPERATOR $0013define RESULT_TEMP $0014define BACKSPACE $08define ENTER $0Ddefine SPACE $20define BLACK $A0define LEFT $83JMP STARTPROMPTFIRST:dcb $45,$4E,$54,$45,$52,$20,$46,$49,$52,$53,$54,$20,$44,$49,$47,$49,$54,$20,$28,$30,$2D,$39,$29,$3A,$20,$00; "ENTER FIRST DIGIT (0-9): "PROMPTOPERATOR:dcb $45,$4E,$54,$45,$52,$20,$4F,$50,$45,$52,$41,$54,$4F,$52,$20,$28,$2B,$20,$2D,$29,$3A,$20,$00; "ENTER OPERATOR (+ -): "PROMPTSECOND:dcb $45,$4E,$54,$45,$52,$20,$53,$45,$43,$4F,$4E,$44,$20,$44,$49,$47,$49,$54,$20,$28,$30,$2D,$39,$29,$3A,$20,$00; "ENTER SECOND DIGIT (0-9): "PROMPTRESULT:dcb $52,$45,$53,$55,$4C,$54,$3A,$20,$00; "RESULT: "INPUTFIRST_STORAGE:dcb $00INPUTSECOND_STORAGE:dcb $00OPERATOR_STORAGE:dcb $00START:JSR SCINITLDY #$00LOOP_PROMPT_FIRST:LDA PROMPTFIRST, YBEQ GET_FIRST_DIGITJSR CHROUTINYBNE LOOP_PROMPT_FIRSTGET_FIRST_DIGIT:JSR INPUT_FIRSTJSR PRINT_NEWLINELDY #$00LOOP_PROMPT_OPERATOR:LDA PROMPTOPERATOR, YBEQ GET_OPERATORJSR CHROUTINYBNE LOOP_PROMPT_OPERATORGET_OPERATOR:JSR CHRINCMP #$3DBEQ TREAT_AS_PLUSCMP #$2BBEQ STORE_OPERATORCMP #$2DBEQ STORE_OPERATORJMP GET_OPERATORTREAT_AS_PLUS:LDA #$2BSTORE_OPERATOR:STA OPERATORJSR CHROUTJSR PRINT_NEWLINELDY #$00LOOP_PROMPT_SECOND:LDA PROMPTSECOND, YBEQ GET_SECOND_DIGITJSR CHROUTINYBNE LOOP_PROMPT_SECONDGET_SECOND_DIGIT:JSR INPUT_SECONDJSR PRINT_NEWLINELDA INPUTFIRST_STORAGESECSBC #$30STA INPUT1LDA INPUTSECOND_STORAGESECSBC #$30STA INPUT2LDA OPERATORCMP #$2BBEQ DO_ADDITIONCMP #$2DBEQ DO_SUBTRACTIONJMP ERRORDO_ADDITION:CLCLDA INPUT1ADC INPUT2STA RESULTJMP PRINT_RESULTDO_SUBTRACTION:SECLDA INPUT1SBC INPUT2STA RESULTJMP PRINT_RESULTERROR:LDA #$45JSR CHROUTJMP STARTPRINT_RESULT:LDY #$00LOOP_PROMPT_RESULT:LDA PROMPTRESULT, YBEQ PRINT_NUMBERJSR CHROUTINYBNE LOOP_PROMPT_RESULTPRINT_NUMBER:LDA RESULTCMP #$80BCC PRINT_POSITIVELDA #$2DJSR CHROUTLDA RESULTEOR #$FFCLCADC #$01STA RESULT_TEMPJMP CONVERT_NUMBERPRINT_POSITIVE:STA RESULT_TEMPCONVERT_NUMBER:LDA RESULT_TEMPCMP #$0ABCC PRINT_ONE_DIGITLDX #$00LDA RESULT_TEMPDIV_LOOP:CMP #$0ABCC DIV_DONESECSBC #$0AINXSTA RESULT_TEMPJMP DIV_LOOPDIV_DONE:TXACLCADC #$30JSR CHROUTLDA RESULT_TEMPCLCADC #$30JSR CHROUTRTSPRINT_ONE_DIGIT:CLCADC #$30JSR CHROUTRTSPRINT_NEWLINE:LDA #$0DJSR CHROUTLDA #$0AJSR CHROUTRTSINPUT_FIRST:LDY #$00GETKEY1:JSR CHRINBEQ GETKEY1CMP #$30BMI GETKEY1CMP #$3ABPL GETKEY1STA INPUTFIRST_STORAGEJSR CHROUTRTSINPUT_SECOND:LDY #$00GETKEY2:JSR CHRINCMP #$00BEQ GETKEY2CMP #$30BMI GETKEY2CMP #$3ABPL GETKEY2STA INPUTSECOND_STORAGEJSR CHROUTRTS
Reflection:
While working on getting this code to run on the 6502 Emulator, I realized just how much I still have to learn. Using the provided define directives helped me get started. The learning curve has been steep, but after several attempts, I finally saw the prompts and inputs appear on the screen which was something I was not able to achieve in my first Lab 3 attempt. If you want to see my first process, check out my blog post under the title Lab 3 - Addition and Subtraction Calculator. Although there is still a lot for me to learn, this lab definitely helped me understand assembly language more clearly and was helpful in preparing me to write more complex code.
Comments
Post a Comment