跳转至

Chapter 10 | Calculator

Data Type Conversion

  • Binary to ASCII
  • ASCII to Binary

Arithmetic Using a Stack

Postfix Expression

The Calculator

It allows a user to enter positive integers consisting of not more than 3 decimal digits, perform basic arithmetic (addition, subtraction and multiplication) on these integers, and display the decimal result

the overview of calculator
.ORIG   x3000

;   Main Routine                
                LEA     R6, STACK_BASE
                ADD     R6, R6, #1

NEW_COMMAND     LEA     R0, PROMPT_MSG
                PUTS
                GETC
                OUT

; Check the command

; X for exit
TEST_X          LD      R1, NEG_X
                ADD     R1, R1, R0
                BRnp    TEST_C
                HALT
; C for clearing the stack
TEST_C          LD      R1, NEG_C
                ADD     R1, R1, R0
                BRnp    TEST_ADD
                JSR     OP_CLEAR
                BRnzp   NEW_COMMAND
; + for addition
TEST_ADD        LD      R1, NEG_PLUS
                ADD     R1, R1, R0
                BRnp    TEST_MINUS
                JSR     OP_ADD
                BRnzp   NEW_COMMAND
; - for subtraction
TEST_MINUS      LD      R1, NEG_MINUS
                ADD     R1, R1, R0
                Brnp    TEST_MUL
                JSR     OP_MINUS
                BRnzp   NEW_COMMAND
; * for multiplication
TEST_MUL        LD      R1, NEG_MUL
                ADD     R1, R1, R0
                BRnp    TEST_D
                JSR     OP_MUL
                BRnzp   NEW_COMMAND
; D for Display the value at the top of the stack
TEST_D          LD      R1, NEG_D
                ADD     R1, R1, R0
                BRnp    ENTER_NUMBER
                JSR     OP_DISPLAY
                BRnzp   NEW_COMMAND
; Input a number, LF for end
ENTER_NUMBER    JSR     PUSH_VALUE
                BRnzp   NEW_COMMAND

PROMPT_MSG      .FILL   x000A
                .STRINGZ    "Enter a command: "
NEG_X           .FILL   xFFA8
NEG_C           .FILL   xFFBD
NEG_PLUS        .FILL   xFFD5
NEG_MINUS       .FILL   xFFD3
NEG_MUL         .FILL   xFFD6
NEG_D           .FILL   xFFBC

; Globals
STACK_MAX       .BLKW   #9
STACK_BASE      .BLKW   #1
ASCIIBUFF       .BLKW   #4
                .FILL   x0000                           ; ASCIIBUFF sentinel



; Stack Operations

; Push the value in R0 on the stack
; R5 is used to indicate success (R5 = 0) or failure (R5 = 1)
PUSH            ST      R1, PUSH_SAVE1

                LD      R1, PUSH_STACK_MAX
                NOT     R1, R1
                ADD     R1, R1, #1
                ADD     R1, R1, R6
                BRz     OVERFLOW

                ADD     R6, R6, #-1
                STR     R0, R6, #0
                AND     R5, R5, #0

                LD      R1, PUSH_SAVE1

                RET

OVERFLOW        LEA     R0, OVERFLOW_MSG
                PUTS
                AND     R5, R5, #0
                ADD     R5, R5, #1

                LD      R1, PUSH_SAVE1

                RET

PUSH_SAVE1      .BLKW   #1
OVERFLOW_MSG    .FILL   x000A
                .STRINGZ    "Error: Stack is Full."
PUSH_STACK_MAX  .FILL   STACK_MAX


; Pop a value from the stack into R0
; R5 is used to indicate success (R5 = 0) or failure (R5 = 1)
POP             LD      R0, POP_STACK_BASE
                NOT     R0, R0
                ADD     R0, R0, R6
                BRz     UNDERFLOW

                LDR     R0, R6, #0
                ADD     R6, R6, #1
                AND     R5, R5, #0

                RET

UNDERFLOW       LEA     R0, UNDERFLOW_MSG
                PUTS

                AND     R5, R5, #0
                ADD     R5, R5, #1

                RET

UNDERFLOW_MSG   .FILL   x000A
                .STRINGZ    "Error: Too Few Values on the Stack."
POP_STACK_BASE  .FILL   STACK_BASE


; Clear the stack by resetting the stack pointer R6
OP_CLEAR        LD      R6, OP_CLEAR_STACK_BASE
                ADD     R6, R6, #1

                RET

OP_CLEAR_STACK_BASE     .FILL   STACK_BASE


; Display the value on the screen
OP_DISPLAY      ST      R0, OP_DISPLAY_SAVE0
                ST      R5, OP_DISPLAY_SAVE5
                ST      R7, OP_DISPLAY_SAVE7

                JSR     POP
                ADD     R5, R5, #0
                BRp     OP_DISPAY_DONE

                JSR     BINARY_TO_ASCII
                LD      R0, NEWLINE_CHAR
                OUT
                LD      R0, OP_DISPLAY_ASCIIBUFF
                PUTS
                ADD     R6, R6, #-1
OP_DISPAY_DONE  LD      R0, OP_DISPLAY_SAVE0
                LD      R5, OP_DISPLAY_SAVE5
                LD      R7, OP_DISPLAY_SAVE7

                RET

NEWLINE_CHAR             .FILL   x000A
OP_DISPLAY_ASCIIBUFF    .FILL   ASCIIBUFF
OP_DISPLAY_SAVE0        .BLKW   #1
OP_DISPLAY_SAVE5        .BLKW   #1
OP_DISPLAY_SAVE7        .BLKW   #1

; Push the value into stack
; R0 is each input char
; R1 points to the string
PUSH_VALUE      ST      R0, PUSH_VALUE_SAVE0
                ST      R1, PUSH_VALUE_SAVE1
                ST      R2, PUSH_VALUE_SAVE2
                ST      R7, PUSH_VALUE_SAVE7

                LD      R1, PUSH_VALUE_ASCIIBUFF
                LD      R2, MAX_DIGITS

VALUE_LOOP      ADD     R3, R0, x-0A                    ; Test for LF
                BRz     INPUT_DONE

                ADD     R2, R2, #0
                BRz     DIGIT_OVERFLOW

                LD      R3, NEG_ASCII_0
                ADD     R3, R0, R3
                BRn     NOT_INTEGER
                LD      R3, NEG_ASCII_9
                ADD     R3, R0, R3
                BRp     NOT_INTEGER

                ADD     R2, R2, #-1
                STR     R0, R1, #0
                ADD     R1, R1, #1
                GETC
                OUT
                BRnzp   VALUE_LOOP

INPUT_DONE      LD      R2, PUSH_VALUE_ASCIIBUFF        ; Test whether no input
                NOT     R2, R2
                ADD     R2, R2, #1
                ADD     R1, R1, R2
                BRz     NO_DIGIT

                JSR     ASCII_TO_BINARY
                JSR     PUSH
                BRnzp   PUSH_VALUE_DONE

NO_DIGIT        LEA     R0, NO_DIGIT_MSG
                PUTS
                BRnzp   PUSH_VALUE_DONE

NOT_INTEGER     GETC
                OUT
                ADD     R3, R0, x-0A
                BRnp    NOT_INTEGER

                LEA     R0, NOT_INTEGER_MSG
                PUTS
                BRnzp   PUSH_VALUE_DONE

DIGIT_OVERFLOW  GETC
                OUT
                ADD     R3, R0, x-0A
                BRnp    DIGIT_OVERFLOW

                LEA     R0, DIGIT_OVERFLOW_MSG
                PUTS
                BRnzp   PUSH_VALUE_DONE

PUSH_VALUE_DONE LD      R0, PUSH_VALUE_SAVE0
                LD      R1, PUSH_VALUE_SAVE1
                LD      R2, PUSH_VALUE_SAVE2
                LD      R7, PUSH_VALUE_SAVE7

                RET

DIGIT_OVERFLOW_MSG      .FILL   x000A
                        .STRINGZ    "Too many digits"
NO_DIGIT_MSG            .FILL   x000A
                        .STRINGZ    "No number entered"
NOT_INTEGER_MSG         .FILL   x000A
                        .STRINGZ    "Not an integer"

MAX_DIGITS              .FILL   x0003
NEG_ASCII_0             .FILL   x-30
NEG_ASCII_9             .FILL   x-39
PUSH_VALUE_ASCIIBUFF    .FILL   ASCIIBUFF

PUSH_VALUE_SAVE0        .BLKW   #1
PUSH_VALUE_SAVE1        .BLKW   #1
PUSH_VALUE_SAVE2        .BLKW   #1
PUSH_VALUE_SAVE7        .BLKW   #1



; Convertion

; ASCII to Binary convertion
; R0 is used to collect the result.
; R1 keeps track of how many digits are left to process
ASCII_TO_BINARY ST      R1, ATOB_SAVE1
                ST      R2, ATOB_SAVE2
                ST      R3, ATOB_SAVE3
                ST      R4, ATOB_SAVE4

                AND     R0, R0, #0
                ADD     R1, R1, #0                  ; Test number of digit
                BRz     ATOB_DONE                   ; No digit, i.e 0

                LD      R2, ATOB_ASCIIBUFF          ; R2 is the pointer
                ADD     R2, R2, R1

                ADD     R2, R2, #-1
                LDR     R4, R2, #0                  ; R4 <- "ones" digit
                AND     R4, R4, x000F               ; Strip off the ASCII template, i.e. R4 <- R4 - x0030
                ADD     R0, R0, R4

                ADD     R1, R1, #-1
                BRz     ATOB_DONE                   ; If it is only one digit

                ADD     R2, R2, #-1
                LDR     R4, R2, #0                  ; R4 <- "tens" digit
                AND     R4, R4, x000F
                LEA     R3, LOOK_UP_10
                ADD     R3, R3, R4
                LDR     R4, R3, #0
                ADD     R0, R0, R4

                ADD     R1, R1, #-1
                BRz     ATOB_DONE                   ; If it is only two digits

                ADD     R2, R2, #-1
                LDR     R4, R2, #0                  ; R4 <- "hundreds" digit
                AND     R4, R4, x000F
                LEA     R3, LOOK_UP_100
                ADD     R3, R3, R4
                LDR     R4, R3, #0
                ADD     R0, R0, R4

ATOB_DONE       LD      R1, ATOB_SAVE1
                LD      R2, ATOB_SAVE2
                LD      R3, ATOB_SAVE3
                LD      R4, ATOB_SAVE4

                RET

ATOB_ASCIIBUFF  .FILL   ASCIIBUFF
ATOB_SAVE1      .BLKW   #1
ATOB_SAVE2      .BLKW   #1
ATOB_SAVE3      .BLKW   #1
ATOB_SAVE4      .BLKW   #1
LOOK_UP_10      .FILL   #0
                .FILL   #10
                .FILL   #20
                .FILL   #30
                .FILL   #40
                .FILL   #50
                .FILL   #60
                .FILL   #70
                .FILL   #80
                .FILL   #90
LOOK_UP_100     .FILL   #0
                .FILL   #100
                .FILL   #200
                .FILL   #300
                .FILL   #400
                .FILL   #500
                .FILL   #600
                .FILL   #700
                .FILL   #800
                .FILL   #900


; Binary to ASCII convertion
; R0 contains the binary value
; R1 keeps track of the output string
BINARY_TO_ASCII ST      R0, BTOA_SAVE0
                ST      R1, BTOA_SAVE1
                ST      R2, BTOA_SAVE2
                ST      R3, BTOA_SAVE3

                LD      R1, BTOA_ASCIIBUFF
                ADD     R0, R0, #0
                BRn     NEG_SIGN

                LD      R2, ASCII_POS                   ; Store the positive sign
                STR     R2, R1, #0
                BRnzp   SKIP

NEG_SIGN        LD      R2, ASCII_NEG                   ; Store the negtive sign
                STR     R2, R1, #0
                NOT     R0, R0
                ADD     R0, R0, #1

; Process the "hundreds" digit
SKIP            LD      R2, ASCII_OFFSET
                LD      R3, NEG_100                     ; Since 100 > 2^5, immediate mode can't be used
LOOP_100        ADD     R0, R0, R3
                BRn     END_100
                ADD     R2, R2, #1
                BRnzp   LOOP_100

END_100         STR     R2, R1, #1
                LD      R3, POS_100
                ADD     R0, R0, R3                      ; R0 <- R0 + 100

; Process the "tens" digit
                LD      R2, ASCII_OFFSET
LOOP_10         ADD     R0, R0, #-10
                BRn     END_10
                ADD     R2, R2, #1
                BRnzp   LOOP_10

END_10          STR     R2, R1, #2
                ADD     R0, R0, #10

; Process the "ones" digit
                LD      R2, ASCII_OFFSET
                ADD     R2, R2, R0
                STR     R2, R1, #3

                LD      R0, BTOA_SAVE0
                LD      R1, BTOA_SAVE1
                LD      R2, BTOA_SAVE2
                LD      R3, BTOA_SAVE3

                RET

ASCII_POS       .FILL   x002B
ASCII_NEG       .FILL   x002D
ASCII_OFFSET    .FILL   x0030
NEG_100         .FILL   #-100
POS_100         .FILL   #100
BTOA_ASCIIBUFF  .FILL   ASCIIBUFF
BTOA_SAVE0      .BLKW   #1
BTOA_SAVE1      .BLKW   #1
BTOA_SAVE2      .BLKW   #1
BTOA_SAVE3      .BLKW   #1


; Check

RANGE_CHECK     LD      R5, NEG_999
                ADD     R5, R0, R5
                BRp     RANGE_OVERFLOW
                LD      R5, POS_999
                ADD     R5, R0, R5
                BRn     RANGE_OVERFLOW
                AND     R5, R5, #0

                RET

RANGE_OVERFLOW        ST      R0, RANGE_CHECK_SAVE0
                LEA     R0, RANGE_EROOR_MSG
                PUTS

                AND     R5, R5, #0
                ADD     R5, R5, #1

                LD      R0, RANGE_CHECK_SAVE0

                RET

NEG_999             .FILL   #-999
POS_999             .FILL   #999
RANGE_EROOR_MSG     .FILL   x000A
                    .STRINGZ    "Error: Number is out of range."
RANGE_CHECK_SAVE0   .BLKW   #1



; Arithmetic Operations

OP_ADD          ST      R0, OP_ADD_SAVE0
                ST      R5, OP_ADD_SAVE5
                ST      R7, OP_ADD_SAVE7

                JSR     POP
                ADD     R5, R5, #0
                BRp     OP_ADD_EXIT
                ADD     R1, R0, #0

                JSR     POP
                ADD     R5, R5, #0
                BRp     OP_ADD_RESTORE1

                ADD     R0, R0, R1                      ; Addition routine

                JSR     RANGE_CHECK
                ADD     R5, R5, #0
                BRp     OP_ADD_RESTORE2

                JSR     PUSH
                BRnzp   OP_ADD_EXIT

OP_ADD_RESTORE2 ADD     R6, R6, #-1
OP_ADD_RESTORE1 ADD     R6, R6, #-1
OP_ADD_EXIT     LD      R0, OP_ADD_SAVE0
                LD      R1, OP_ADD_SAVE1
                LD      R7, OP_ADD_SAVE7

                RET

OP_ADD_SAVE0    .BLKW   #1
OP_ADD_SAVE1    .BLKW   #1
OP_ADD_SAVE5    .BLKW   #1
OP_ADD_SAVE7    .BLKW   #1


OP_MINUS        ST      R0, OP_MINUS_SAVE0
                ST      R5, OP_MINUS_SAVE5
                ST      R7, OP_MINUS_SAVE7

                JSR     POP
                ADD     R5, R5, #0
                BRp     OP_MINUS_EXIT

                ADD     R0, R0, #1
                JSR     PUSH

                JSR     OP_ADD

OP_MINUS_EXIT   LD      R0, OP_MINUS_SAVE0
                LD      R5, OP_MINUS_SAVE5
                LD      R7, OP_MINUS_SAVE7

                RET

OP_MINUS_SAVE0  .BLKW   #1
OP_MINUS_SAVE5  .BLKW   #1
OP_MINUS_SAVE7  .BLKW   #1


OP_MUL          ST      R0, OP_MUL_SAVE0
                ST      R1, OP_MUL_SAVE1
                ST      R2, OP_MUL_SAVE2
                ST      R3, OP_MUL_SAVE3
                ST      R5, OP_MUL_SAVE5
                ST      R7, OP_MUL_SAVE7

                AND     R3, R3, #0                      ; R3 holds sign of multiplier
                JSR     POP
                ADD     R5, R5, #0
                BRp     OP_MUL_EXIT
                ADD     R1, R0, #0

                JSR     POP
                ADD     R5, R5, #0
                BRp     OP_MUL_RESTORE1
                ADD     R2, R0, #0                      ; Moves multiplier to test sign
                BRzp    POS_MULTIPLIER

                ADD     R3, R3, #1
                NOT     R2, R2
                ADD     R2, R2, #1

POS_MULTIPLIER  AND     R0, R0, #0
                ADD     R2, R2, #0
                BRz     PUSH_MULT                       ; If multiplier = 0, then DONE

MUL_LOOP        ADD     R0, R0, R1                      ; Multiply routine
                ADD     R2, R2, #-1
                BRp     MUL_LOOP

                JSR     RANGE_CHECK
                ADD     R5, R5, #0
                BRp     OP_MUL_RESTORE2

                ADD     R3, R3, #0                      ; Test for negative multiplier
                BRz     PUSH_MULT
                NOT     R0, R0
                ADD     R0, R0, #1

PUSH_MULT       JSR     PUSH
                BRnzp   OP_MUL_EXIT

OP_MUL_RESTORE2 ADD     R6, R6, #-1
OP_MUL_RESTORE1 ADD     R6, R6, #-1    
OP_MUL_EXIT     LD      R0, OP_MUL_SAVE0
                LD      R1, OP_MUL_SAVE1
                LD      R3, OP_MUL_SAVE3
                LD      R5, OP_MUL_SAVE5
                LD      R7, OP_MUL_SAVE7

                RET

OP_MUL_SAVE0   .BLKW   #1
OP_MUL_SAVE1   .BLKW   #1
OP_MUL_SAVE2   .BLKW   #1
OP_MUL_SAVE3   .BLKW   #1
OP_MUL_SAVE5   .BLKW   #1
OP_MUL_SAVE7   .BLKW   #1


.END

最后更新: 2024.02.21 00:56:50 CST
创建日期: 2024.02.21 00:56:50 CST