Преобразование строки цифр ASCII в int в MIPS/Assembler

Я пишу код MIPS, чтобы взять строку цифр ASCII и преобразовать строку в целое число. Строка вводится пользователем и может содержать не более 10 цифр. Мой код работает нормально и использует очевидный метод выполнения циклического сложения после умножения наименее значимого числа в строке на степень десяти, определяемую индексом массива, начиная с последней введенной цифры (10 ^ 0) до первой цифры введено (10^n, n=количество цифр в массиве).

Мне было интересно, есть ли альтернативный метод, который был бы быстрее или короче для написания. В частности, я хотел знать, может ли использование логического сдвига битов сократить этот процесс. Мы будем очень признательны за любые идеи по оптимизации или улучшению этого кода!

Кроме того, в качестве примечания, я хотел бы вызывать подпрограммы gets и readInt с помощью jal, но поскольку и gets, и readInt вызывают подпрограммы, использование jal в основном методе для вызова gets или readInt вызывает проблемы. Любые идеи, как обойти это? Еще раз спасибо

PS: извините за форматирование комментариев в этом коде, копирование и вставка из симулятора MARS в текстовое поле переполнения стека привело к отключению выравнивания:/

#IO
#Prompts user to input 10 ascii digits into an array
#Converts the string of digits into a single int
#Also handles any number of digits between 1 and 10 
#Returns 0 if non-digit chars are entered into the string

.data           #declaration of vars follows
array: .space 11    #reserves space for a 10 elem array
char: .space 2
prompt: .asciiz "Please enter 10 numbers, then press ENTER:  \n"
null: .asciiz ""
space: .ascii " "
newline: .asciiz "\n"
.text           #instructions follow

main:
la $a0, prompt      #load prompt message into $a0 for syscall
li $v0, 4               #load syscall to print string
syscall         #print prompt message
j readInt               #call readInt function to get user input string         

gets:           #read multiple chars from keyboard buffer until ENTER key,
                            #add NULL char and store into buffer pointed to by *array
                            #passed to the subroutine
la $s1, array       #set base address of array to s1
loop:           #start of read loop
jal getc        #jump to getc subroutine
lb $t0, char        #load the char from char buffer into t0, stripping null
sb $t0, 0($s1)      #store the char into the nth elem of array
lb $t1, newline     #load newline char into t1
beq $t0, $t1, done  #end of string?  jump to done
addi $s1, $s1, 1    #increments base address of array
j loop          #jump to start of read loop

getc:           #read char from keyboard buffer and return ascii value
li $v0, 8       #call code for read string
la $a0, char        #load address of char for read
li $a1, 2       #length of string is 1byte char and 1byte for null
syscall         #store the char byte from input buffer into char
jr $ra          #jump-register to calling function

readInt:        #read string of ascii digits, store into a local variable and  
                    #convert into integer, return that int unless string contains 
                    #non-integers 
j gets          #let s1 be top address of array, let s0 be the digitcounter
done:           #let s2 be the sum total
addi $s1, $s1, -1   #reposition array pointer to last char before newline char
la $s0, array       #set base address of array to s0 for use as counter
addi $s0, $s0, -1   #reposition base array to read leftmost char in string
add $s2, $zero, $zero   #initialize sum to 0
li $t0, 10      #set t0 to be 10, used for decimal conversion
li $t3, 1
lb $t1, 0($s1)      #load char from array into t1
blt $t1, 48, error  #check if char is not a digit (ascii<'0')
bgt $t1, 57, error  #check if char is not a digit (ascii>'9')
addi $t1, $t1, -48  #converts t1's ascii value to dec value
add $s2, $s2, $t1   #add dec value of t1 to sumtotal
addi $s1, $s1, -1   #decrement array address
lp:         #loop for all digits preceeding the LSB
mul $t3, $t3, $t0   #multiply power by 10
beq $s1, $s0, FIN   #exit if beginning of string is reached
lb $t1, ($s1)       #load char from array into t1
blt $t1, 48, error  #check if char is not a digit (ascii<'0')
bgt $t1, 57, error  #check if char is not a digit (ascii>'9')
addi $t1, $t1, -48  #converts t1's ascii value to dec value
mul $t1, $t1, $t3   #t1*10^(counter)
add $s2, $s2, $t1   #sumtotal=sumtotal+t1
addi $s1, $s1, -1   #decrement array address
j lp            #jump to start of loop

error:          #if non digit chars are entered, readInt returns 0
add $s2, $zero, $zero
j FIN

FIN:
li $v0, 1
add $a0, $s2, $zero
syscall 
li $v0, 10      #ends program
syscall

person user1730099    schedule 11.04.2013    source источник


Ответы (3)


Замаскируйте первые четыре бита, и добавив в строку 0x0F, как показано ниже.

andi $t0,$t0,0x0F # where $t0 contains the ascii digit .

теперь у $t0 есть int.

person sampath    schedule 10.08.2013
comment
Не могли бы вы использовать это с двузначным символом? Пример: «10» - person LukeDev; 29.11.2020
comment
@LukeDev нет, двузначных символов не бывает. addi $t0 $t0 -48 работает так же, поэтому идея заключается в преобразовании одного символа ASCII (где 48 -> 0, 49 -> 1 и поэтому вперед) для представления цифры. Является ли байт цифрой или символом ASCII, зависит от контекста. - person ggorlen; 03.12.2020

Предполагается, что $s1 указывает на начало строки, заканчивающейся NULL (т. е. на старшую значащую цифру), $t0 содержит 10, а $s2 содержит 0:

lp:         
  lbu $t1, ($s1)       #load unsigned char from array into t1
  beq $t1, $0, FIN     #NULL terminator found
  blt $t1, 48, error   #check if char is not a digit (ascii<'0')
  bgt $t1, 57, error   #check if char is not a digit (ascii>'9')
  addi $t1, $t1, -48   #converts t1's ascii value to dec value
  mul $s2, $s2, $t0    #sum *= 10
  add $s2, $s2, $t1    #sum += array[s1]-'0'
  addi $s1, $s1, 1     #increment array address
  j lp                 #jump to start of loop

Это на mul меньше за итерацию, и нет необходимости знать длину строки перед входом в цикл.

person Michael    schedule 11.04.2013

В ответ на вопросы об использовании jal во вложенных вызовах вы можете просто сохранить регистр из $ra (это регистр, используемый для хранения обратного адреса при вызове jal) при первом вводе первого вызова; когда вы достигаете вызова alst, можно просто использовать $ra как обычно.

main:
    jal call1

call1:
    add $s0, $ra, 0
    ...
    jal call2
    ...
    jr $s0 # will be where $ra was when call 1 was implemented

call2:
    ...
    jr $ra

Использование регистров s, вероятно, наиболее безопасно, но я также смог использовать регистры t.

person LukeDev    schedule 28.11.2020