Использование стеков в сборке LC-3
У меня проблема с калькулятором в LC-3, которую я обнаружил на веб-сайте. Я делаю это поэтапно и вижу кое-что о том, что я делаю сейчас, используя стеки. Мне было интересно, может ли кто-нибудь помочь мне с тем, что я до сих пор конвертировал в использование стеков, пока я только что использовал регистры! Приведенный ниже код, очевидно, является неполным, это именно то, что у меня есть, когда я остановился и начал исследовать, как делать некоторые вещи. Вот мой код LC-3 для того, что я имею до сих пор:
;get first string from user
START LEA R0, PROMPT1 ; Display the prompt
PUTS
LEA R0, BUFFER
JSR READLINE
;convert string to number
LEA R0, BUFFER
JSR ATOI
;save the number
ST R0, FIRST_ARG
;get second string from user
LEA R0, PROMPT2 ; Display the prompt
PUTS
LEA R0, BUFFER
JSR READLINE
;convert string to number
LEA R0, BUFFER
JSR ATOI
;save the number
ST R0, SECOND_ARG
;load the arguments into R0 and R1
LD R0, FIRST_ARG
LD R1, SECOND_ARG
JSR SUM
PRINT_OUTPUT
;convert R0 into a string
LEA R1, BUFFER
JSR ITOA ;not made yet, also will need division and subtraction subroutines
;print the sum of the two digits entered
LEA R0, ANSWER ; Display the prompt
PUTS
LEA R0, BUFFER
PUTS
;print a new line character.
LD R0, ENTER
OUT
STOP HALT ;
;subroutine SUM : calculates the sum of two numbers
;input: R0,R1
;output: R0 = R0 + R1
SUM
ADD R0, R0, R1
RET
;subroutine PROD : calculates the product of two numbers
;input: R0,R1
;output: R0 = R0 * R1
MUL
ST R2, MUL_SAVE_R2
AND R2, R2, #0
AND R1, R1, R1
MUL_START
BRz MUL_END
ADD R2, R2, R0
ADD R1, R1, #-1
BR MUL_START
MUL_END
ADD R0, R2, #0
LD R2, MUL_SAVE_R2
RET
MUL_SAVE_R2 .FILL x0000
;subroutine READLINE : Reads a line of input from keyboard.
;input: R0. contains the address of the memory location where the
; string must be placed.
READLINE
ST R7, RL_RETURN
AND R1, R0, R0
RL_START
;get a character and echo it
GETC
OUT
;compare the character with ENTER which has ascii value 10
ADD R2, R0, #-10
BRz RL_END ;the user typed ENTER, stop the loop
STR R0, R1, #0 ;store whatever the user typed
ADD R1, R1, #1 ;increment the pointer
BR RL_START
RL_END
AND R0, R0, #0
STR R0, R1, #0 ;write the null character
LD R7, RL_RETURN
RET
RL_RETURN .FILL x0000
;subroutine ATOI : Converts an ASCII string to an integer
;input: R0, contains the address of the string
;output: R0, should contain the integer value
ATOI
ST R7, ATOI_RETURN
AND R2, R0, R0 ;R2 <- R0;
AND R0, R0, #0 ;R0 <- 0
ADD R1, R0, #10 ;R1 <- 10
LD R4, ASCIIZERO
NOT R4, R4
ADD R4, R4, #1 ;This is to convert ascii character to integer
ATOI_START
LDR R5, R2, #0
BRz ATOI_END ;we've reached the end of the string
AND R1, R1, #0
ADD R1, R1, #10 ;R1 <- 10
JSR MUL ;multiply current number by 10
ADD R5, R5, R4 ;subtract ASCIIZERO from R5
BRn INVALID_INPUT ;user typed something less than '0'
ADD R6, R5, #-9
BRp INVALID_INPUT ;user typed something more than '9'
ADD R0, R0, R5
ADD R2, R2, #1 ;next character
BR ATOI_START
INVALID_INPUT
AND R0, R0, #0 ;make R0 <- -1
ADD R0, R0, #-1
ATOI_END
LD R7, ATOI_RETURN
RET
ATOI_RETURN .FILL x0000
;allocate memory for the input
FIRST_ARG .FILL x0000
SECOND_ARG .FILL x0000
BUFFER .BLKW #15 ; allocating memory for storing user input.
POP
LDR R0, R6, #0
ADD R6, R6, #1
RET
PUSH
ADD R6, R6, #-1
STR R0, R6, #0
RET
;constants
MINUS .FILL x002D; '-'
ENTER .FILL x000A; newline character
PROMPT1 .STRINGZ "Please input the first digit > "
PROMPT2 .STRINGZ "Please input the second digit > "
ANSWER .STRINGZ "Sum of the two digits entered = "
ASCIIZERO .FILL x0030; '0'
.END
2 ответа
Простой способ сделать калькулятор - создать калькулятор с обратной польской нотацией (RPN).
Когда вы используете RPN, вы читаете входной токен токеном и выполняете действие после каждого чтения. Если токен является числом, поместите его в стек. Если это оператор, вытолкните элементы из стека и обработайте их. Например, если оператор +
поместите два верхних элемента в стек и добавьте их, поместив сумму в стек. Когда вы закончите читать токены, верхний элемент в стеке - ваш ответ.
Чтобы было проще получить токены, возможно, потребуется, чтобы токены были разделены одним пробелом (по крайней мере, для начала).
Так, например, пользователь может ввести:
7 5 + 12 /
Ты бы:
- Читать токен
7
а такжеpush
это (стек сейчас7
) - Читать токен
5
а такжеpush
это (стек сейчас5
,7
) - Читать токен
+
, поп7
а также5
в регистры, добавить их,push
результат (стек сейчас12
) - Читать токен
12
а такжеpush
это (стек сейчас12
,12
) - Читать токен
/
,pop
12
а также12
в регистры, разделить их,push
результат (стек сейчас1
). Pop
окончательный ответ в регистр и распечатать результат (1
).
Утилита Unix dc
работает так (или работает так, как будто это так, по крайней мере).
Использование других видов обозначений, в том числе стандартного обозначения "инфикс", будет более сложным. Я думаю, что самый превосходный язык программирования C++ имеет (или имел в более раннем издании, если он не делает сейчас) игрушечный пример калькулятора инфиксов, чтобы дать вам представление о том, что будет происходить.
Если вы спрашиваете о передаче переменных в функции в стеке, то, с одной стороны, вам это не нужно. Использование регистров - это то, к чему стремятся оптимизаторы.
Однако механизм заключается в том, чтобы поместить их в стек (R13), вызвать функцию, а затем добавить возврат, добавить размер переменных в указатель стека (2 x 32-разрядных числа = 8 байт).
Я верю в ARM, указатель кадра (R11) автоматически указывает на стек перед вызовом функции. Таким образом, если бы 2 x 32-разрядных чисел были помещены в стек, первое было бы в [R11, #-8], а второе в [R11, #-4] (помните, что стеки растут с конца).