Convierta una cadena de dígitos ASCII a int en MIPS/Assembler

Estoy escribiendo un código MIPS para tomar una cadena de dígitos ASCII y convertir la cadena en un número entero. El usuario ingresa la cadena y puede tener una longitud máxima de 10 dígitos. Mi código funciona bien y usa el método obvio de realizar una suma en bucle después de multiplicar el número menos significativo en la cadena por una potencia de diez determinada por el índice de la matriz, comenzando desde el último dígito ingresado (10 ^ 0) hasta el primer dígito ingresado (10^n, n=número de dígitos en la matriz).

Me preguntaba si había un método alternativo que sería más rápido o más corto de escribir. En particular, quería saber si el uso de un cambio de bit lógico podría acortar este proceso. ¡Cualquier idea para optimizar o mejorar este código sería muy apreciada!

Además, como nota al margen, me gustaría llamar a las subrutinas gets y readInt usando jal, pero como tanto gets como readInt llaman subrutinas, usar jal en el método principal para llamar a gets o readInt causa problemas. ¿Alguna idea de cómo evitar esto? Gracias de nuevo Saludos

PD: perdón por el formato de los comentarios en este código, copiar y pegar desde el simulador MARS en el cuadro de texto de desbordamiento de la pila hizo que la alineación estuviera desactivada:/

#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

preguntado el 11 de abril de 13 a las 04:04

3 Respuestas

Enmascarar los primeros cuatro bits por ying la cuerda con 0x0F como se muestra a continuación

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

ahora mismo $t0 tiene el int de la misma.

Respondido 15 Feb 21, 06:02

¿Podrías usar esto con un carácter de dos dígitos? Ej: '10' - lucasdev

@LukeDev no, no existe un carácter de dos dígitos. addi $t0 $t0 -48 funciona de la misma manera, por lo que la idea es convertir un solo carácter ASCII (donde 48 -> 0, 49 -> 1 y etc.) para representar un dígito. Si un byte es un dígito o un carácter ASCII es una cuestión de contexto. - ggorlen

Se asume que $s1 apunta al comienzo de una cadena terminada en NULL (es decir, al dígito más significativo), $t0 contiene 10, y $s2 contiene 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

esto tiene uno mul menos por iteración, y no hay necesidad de conocer la longitud de la cadena antes de ingresar al bucle.

Respondido 11 Abr '13, 07:04

En respuesta a las preguntas sobre el uso jal en llamadas anidadas, puede simplemente guardar el registro de $ra (este es el registro utilizado para almacenar la dirección de retorno cuando jal se llama) cuando ingresa por primera vez a la primera llamada; cuando llegas a la última llamada que uno puede usar $ra como normal.

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

Usar s los registros son probablemente los más seguros, pero he podido usar t registros también.

respondido 28 nov., 20:19

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.