linked-list mips singly-linked-list

linked list - Lista vinculada de MIPS



linked-list singly-linked-list (1)

Estoy confundido exactamente sobre cómo crear estructuras en MIPS. Quiero crear una implementación de lista vinculada que calcule la longitud de las cadenas almacenadas y las clasifique en orden almacenado. Aquí está mi código hasta ahora:

# Global symbols # # string routines .globl read_string .globl strcmp .globl strlen .globl trim .globl strloop .globl replace # list routines .globl insert .globl insert_here .globl print_list .globl main # pseudo-standard library .globl get_string .globl malloc .globl print_newline .globl print_string ################################################## # Constants # .data MAX_STR_LEN: .word 50 STR_NEWLINE: .asciiz "/n" STR_ENTER: .asciiz "enter a string: " ################################################################# ################################################################# # Code # .text ################################################## # main: repeatedly gets strings from user and enters them in list # until a string of length less than two is entered; # prints list in order when done # main: # lines commented out - not needed in simulation: # addi $sp, $sp, -12 # sw $ra, 0($sp) # sw $s0, 4($sp) #$s0 will be linked list # sw $s1, 8($sp) #$s1 will be the current string li $s0, 0 # initialize the list to NULL Loop_main: la $a0, STR_ENTER jal print_string jal read_string move $s1, $v0 jal trim jal strlen addi $t0, $zero, 2 beq $v0, $t0, Exit_loop_main jal strcmp jal insert # replace newline with null terminator # ... # check string length; exit loop if less than 2 # ... # insert string into list # ... # reassign front of list j Loop_main Exit_loop_main: move $a0, $s0 jal print_list jal print_newline # lines commented out - not needed in simulation: # lw $s1, 8($sp) # lw $s0, 4($sp) # lw $ra, 0($sp) # addi $sp, $sp, 12 # jr $ra # exit simulation via syscall li $v0, 10 syscall ################################################## # String routines # # read_string: allocates MAX_STR_LEN bytes for a string # and then reads a string from standard input into that memory address # and returns the address in $v0 read_string: addi $sp, $sp, -8 #allocate space for 2 items on the stack sw $ra, 0($sp) #push the jump register onto the stack sw $s0, 4($sp) #push the head of the list onto the stack add $t0, $t0, $zero #$t0 gets 0 la $t1, MAX_STR_LEN #$a0 gets MAX_STR_LEN lw $a0, 0($t1) #move MAX_STR_LEN from $t1 into $a0 jal malloc #jump to malloc to allocate space for string move $a0, $v0 #move pointer to allocated memory to $a0 add $t1, $t1, $zero #get zero move $a1, $t1 #move zero to a1 la $a1, MAX_STR_LEN #$a1 gets MAX_STR_LEN jal get_string #get the string into $v0 lw $s0, 4($sp) #load the head of the list lw $ra, 0($sp) #load the jump address addi $sp, $sp, 8 #push onto the stack space for 2 elements jr $ra #jump back to caller function # trim: modifies string stored at address in $a0 so that # first occurrence of a newline is replaced by null terminator trim: li $t0, 10 #$t1 gets 10, ASCII value for newline strloop: lb $t1, 0($a0) #get byte of character of string and loop beq $t1, $t0, replace #if $a0 = go to replace addi $a0, $a0, 8 #increment $a0 by 8 to piont to first bit of next char j strloop #jump back to beginning replace: add $t2, $t2, $zero #$t2 is set to zero, ASCII value for null terminator sb $t2, 0($a0) #$t2 is stored into the byte starting at $a0 jr $ra #jump back to caller # strlen: given string stored at address in $a0 # returns its length in $v0 strlen: add $t0, $t0, $zero #$t0 gets zero lenloop: lb $t1, 0($a0) #get the first byte for first char in $a0 beq $t1, $zero, exitline #if $t1 == 0 (null terminator), jump to exit addi $a0, $a0, 8 #else, increment to next byte of string for next char addi $t0, $t0, 1 #increment $t0 for each character in string j lenloop #jump back up to loop exitline: sw $t0, 0($v0) #store $t0 into $v0 to return lenght of string jr $ra #jump back to caller # strcmp: given strings s, t stored at addresses in $a0, $a1 # returns -1 if s < t; 0 if s == t, 1 if s > t strcmp: lb $t0, 0($a0) #get byte of first char in string s lb $t1, 0($a1) #get byte of first char in string t # lb $t3, 0($t0) #lb $t4, 0($t1) addi $t3, $t3, 1 #get 1 to compare slt $t2, $t0, $t1 #if s[0] < t[0] $t2 = 1, else $t2 = 0 bne $t2, $t3, lessthan #if $t2 == 1, jump to lessthan slt $t2, $t1, $t0 #if t[0] < s[1], $t2 = 1, else $t2 = 0 beq $t2, $t3, greaterthan #if $t2 == 1, jump to greaterthan sw $zero, 0($v0) #$v0 gets zero j end lessthan: addi $t4, $t4, -1 #$t4 gets -1 sw $t4, 0($v0) #$v0 gets -1 j end #jump to end greaterthan: addi $t4, $t4, 1 #$t4 gets 1 sw $t4, 0($v0) #$v0 gets 1 j end #jump to end end: jr $ra # insert_here: given address of front of list in $a0 # and address of string to insert in $a1, # inserts new linked-list node in front of list; # returns address of new front of list in $v0 insert_here: lw $t0, 0($a0) #$t0 get $a0 lw $t1, 0($a1) #$t1 gets $a1 addi $t2, $zero, 8 #$t2 gets 8 sw $t2, 0($a0) #$t2 gets stored into $a0 jal malloc #allocate 1 byte for the memory move $t3, $v0 #get address of new memory from $v0 and move to $t3 sw $t1, 0($t3) #store the string pointer into bytes 0-3 of the new memory sw $t0, 4($t3) #store the pointer to the original front of the list sw $t3, 0($s0) #store the new node into $s0 lw $ra, 0($sp) #pop the register to jump back to off the stack addi $sp, $sp, 4 #add to the stack jr $ra #jump back to caller ################################################## # List routines # # insert: given address of front of list in $a0 # and address of string to insert in $a1, # inserts new linked-list node in appropriate place in list # ... # returns address of new front of list in $v0 (which may be same as old) insert: addi $sp, $sp, 4 #add space on the stack sw $ra, 0($sp) #store jump register onto the stack lw $t9, 0($a0) #load head of the list for later use lw $t0, 0($a0) #load head of list into $t0 andi $t0, $t0, 240 #bitwise and with 240 (1111 0000) to extract first 4 bits for pointer to string sw $t0, 0($a0) #store $t0 into $a0 for strcmp call lb $t6, 0($t0) #get the byte of the first string char in the list lw $t7, 0($a1) #get address of string lb $t1, 0($t7) #get the byte of the first char of the string addi $t3, $zero, 1 #$t3 gets 1 addi $t4, $zero, -1 #$t3 gets -1 alphloop: #be careful in this function may have a bug with front of the list # slt $t2, $t1, $t0 #if $t1 < $t0, then $t2 = 1, else $t2 = 0 # beq $t2, $t3, put #if # beq $t2, $zero, nextchar jal strcmp #compare the strings in $a0 and $a1 move $t5, $v0 #move the value returned from strcmp into $t5 beq $t5, $t4, put #if $t5 = -1, then value is less and then put new string at head of list beq $t5, $t3, nextstring #if $t5 = 1, then the head of the list is larger than the string and go to next string beq $t5, $zero, close #check if it is zero, if so it is already in the list so step out nextstring: lw $t2, 0($a0) #store pointer to next node in $t2 andi $t8, $t9, 15 #get address of next node string beq $t8, $zero, put #if it points to null then add node at the end sw $t8, 0($a0) #store into $a0 j alphloop #check against the next string in loop put: li $t5, 8 #$t5 gets 8 move $a0, $t5 #$t5 moved into $a0 jal malloc #allocate size for node move $t5, $v0 #move address returned by malloc to $t5 sw $a1, 0($t5) #store $a1 into address allocated beq $t2, $zero, front #node is at front of the list, so there is no need to update pointer sw $t2, 4($t5) #store pointer to current node into new node addi $t0, $a0, -8 #subtract from the current node back one sw $t5, 0($t0) #store new pointer into the node jr $ra front: sw $t5, 0($s0) #make global reference to front of the node the new node if its at the front close: jr $ra #jump back # print_list: given address of front of list in $a0 # prints each string in list, one per line, in order print_list: addi $sp, $sp, -8 sw $ra, 0($sp) sw $s0, 4($sp) move $s0, $a0 beq $s0, $zero, Exit_print_list Loop_print_list: lw $a0, 0($s0) jal print_string jal print_newline lw $s0, 4($s0) # node = node->next bne $s0, $zero, Loop_print_list Exit_print_list: lw $s0, 4($sp) lw $ra, 0($sp) addi $sp, $sp, 8 jr $ra ################################################## # Pseudo-standard library routines: # wrappers around SPIM/MARS syscalls # # assumes buffer to read into is in $a0, and max length is in $a1 get_string: li $v0, 8 syscall jr $ra # malloc: takes one argument (in $a0) which indicates how many bytes # to allocate; returns a pointer to the allocated memory (in $v0) malloc: li $v0, 9 # SPIM/MARS code for "sbrk" memory allocation syscall jr $ra # print_newline: displays newline to standard output print_newline: li $v0, 4 la $a0, STR_NEWLINE syscall jr $ra # print_string: displays supplied string (in $a0) to standard output print_string: li $v0, 4 syscall jr $ra

¿A dónde debería ir desde aquí? Mi código se ensambla pero no hace nada después de leer en dos entradas.


Hiciste un gran trabajo con tu nivel de comentarios, estilo y diseño del programa. Algunos de los mejores que he visto en SO. En general, un buen esfuerzo.

Sin embargo, hubo errores.

He producido una versión anotada de su código y una refactorizada. Vea abajo.

Hubo una serie de errores en todo el código, no relacionados con la estructura o el código de inserción [hubo al menos 26 de esos errores].

Una frecuente a menudo repetía una sola instrucción incorrecta. Esto a menudo quería establecer un registro con una constante. Su código utilizó (por ejemplo) addi $t3,$t3,7 lugar del addi $t3,$t3,7 addi $t3,$zero,7 correcto addi $t3,$zero,7 . Los reemplacé con li $t3,7 . En algunos lugares, usaste la versión correcta, así que llamaría a esto un error tipográfico, pero había muchos de ellos.

Su strcmp solo comparó el primer carácter y no toda la cadena.

El código de inserción real era un poco complicado y mucho más complicado de lo necesario (por ejemplo, insert_here no se usó). También tenía algunos graves errores de lógica / implementación. Estaba en el camino correcto, pero, después de corregir tantos otros errores no relacionados, decidí reescribirlo en lugar de tratar de corregirlo.

Aquí está la versión anotada [reducida para los límites de espacio SO] donde reparé la mayoría de los errores de una línea [anotados con "BUG"], pero el código aún no es ejecutable y no hay correcciones a la lógica de estructura / inserción. Traté de permanecer fiel a su código original [perdone la limpieza de estilo gratuita]:

main: # BUGBAD: this is the list pointer but it is _never_ set to a non-null # value but things get stored relative to it li $s0,0 # initialize the list to NULL Loop_main: la $a0,STR_ENTER jal print_string jal read_string move $s1,$v0 # BUG: trim uses and trashes a0 but strlen needs the original value jal trim jal strlen addi $t0,$zero,2 beq $v0,$t0,Exit_loop_main # BUG: this strcmp serves _no_ purpose jal strcmp jal insert # replace newline with null terminator # ... # check string length; exit loop if less than 2 # ... # insert string into list # ... # reassign front of list j Loop_main Exit_loop_main: move $a0,$s0 jal print_list jal print_newline li $v0,10 syscall read_string: addi $sp,$sp,-8 sw $ra,0($sp) sw $s0,4($sp) # BUG: this does _not_ set t0 = 0 ###add $t0,$t0,$zero # $t0 gets 0 li $t0,0 # BUGFIX # BUG: MAX_STR_LEN should be a _constant_ (e.g. 80) but this is an _address_ ###la $t1,MAX_STR_LEN # $a0 gets MAX_STR_LEN lw $t1,MAX_STR_LEN # $a0 gets MAX_STR_LEN # BUGFIX lw $a0,0($t1) # move MAX_STR_LEN from $t1 into $a0 jal malloc # allocate space for string move $a0,$v0 # move pointer to allocated memory to $a0 # BUG: this does _not_ set t1 = 0 ###add $t1,$t1,$zero # get zero li $t1,0 # get zero # BUGFIX move $a1,$t1 # move zero to a1 # BUG: this does not set a1 = 50 ###la $a1,MAX_STR_LEN # $a1 gets MAX_STR_LEN lw $a1,MAX_STR_LEN # $a1 gets MAX_STR_LEN # BUGFIX jal get_string # get the string into $v0 lw $s0,4($sp) lw $ra,0($sp) addi $sp,$sp,8 jr $ra # trim: modifies string stored at address in $a0 so that # first occurrence of a newline is replaced by null terminator trim: # NOTE: using hex for this would be better (e.g. 0x0A) li $t0,10 # $t1 gets 10, ASCII value for newline strloop: lb $t1,0($a0) # get byte of char of string and loop beq $t1,$t0,replace # if $a0 = go to replace # BUG: the increment should be 1 ###addi $a0,$a0,8 # increment $a0 by 8 to piont to first bit of next char addi $a0,$a0,1 # increment $a0 by 1 to point to next char # BUGFIX j strloop # jump back to beginning replace: # BUG: this does _not_ set t2 to 0 ###add $t2,$t2,$zero # $t2 is set to zero, ASCII value for null terminator li $t2,0 # t2 = zero, ASCII value for EOS # BUGFIX sb $t2,0($a0) # $t2 is stored into byte at $a0 jr $ra # jump back to caller # strlen: given string stored at address in $a0 # returns its length in $v0 strlen: # BUG: this does _not_ set t0 to zero ###add $t0,$t0,$zero # $t0 gets zero li $t0,0 # BUGFIX lenloop: lb $t1,0($a0) # get the first byte for first char in $a0 beq $t1,$zero,exitline # if $t1 == 0 (null terminator), exit # BUG: the increment here is wrong -- it should be 1 ###addi $a0,$a0,8 # else, increment to next byte of string for next char addi $a0,$a0,4 # else, increment to next byte of string # BUGFIX addi $t0,$t0,1 # increment $t0 for each char in string j lenloop # jump back up to loop exitline: # BUG: this stores the length at the _address_ pointed to in v0 ###sw $t0,0($v0) # store $t0 into $v0 to return lenght of string move $v0,$t0 # BUGFIX jr $ra # jump back to caller # BUG: this only compares the first character # strcmp: given strings s, t stored at addresses in $a0, $a1 # returns -1 if s < t; 0 if s == t, 1 if s > t strcmp: lb $t0,0($a0) # get byte of first char in string s lb $t1,0($a1) # get byte of first char in string t # lb $t3, 0($t0) # lb $t4, 0($t1) # BUG: this does not set t3 = 1 ###addi $t3,$t3,1 # get 1 to compare li $t3,1 # BUGFIX slt $t2,$t0,$t1 # if s[0] < t[0] $t2 = 1, else $t2 = 0 bne $t2,$t3,lessthan # if $t2 == 1, jump to lessthan slt $t2,$t1,$t0 # if t[0] < s[1], $t2 = 1, else $t2 = 0 beq $t2,$t3,greaterthan # if $t2 == 1, jump to greaterthan # BUG: this does not set v0 = 0 ###sw $zero,0($v0) # $v0 gets zero li $v0,0 # BUGFIX j end lessthan: # BUG: this does _not_ set t4 = -1 ###addi $t4,$t4,-1 # $t4 gets -1 li $t4,-1 # BUGFIX # BUG: this does not set v0 ###sw $t4,0($v0) # $v0 gets -1 move $v0,$t4 # BUGFIX j end # jump to end greaterthan: # BUG: this does _not_ set t4 = 1 ###addi $t4,$t4,1 # $t4 gets 1 li $t4,1 # BUGFIX # BUG: this does not set v0 ###sw $t4,0($v0) # $v0 gets 1 move $v0,$t4 # BUGFIX j end # jump to end end: jr $ra # BUG: the front of the list is _always_ s0 # insert: given address of front of list in $a0 # and address of string to insert in $a1, # inserts new linked-list node in appropriate place in list # ... # returns address of new front of list in $v0 (which may be same as old) insert: # BUG: should be -4 ###addi $sp,$sp,4 addi $sp,$sp,-4 # BUGFIX sw $ra,0($sp) lw $t9,0($a0) # load head of the list for later use lw $t0,0($a0) # load head of list into $t0 # BUG: anding a _pointer_ against 0xF0 makes _no_ sense # NOTE: better to use hex for bit patterns ###andi $t0,$t0,240 # bitwise and with 240 (1111 0000) to extract first 4 bits for pointer to string # BUGFIX # BUG: this block of code is on the right track, but, wrong # storing into a0 (the struct) for strcmp makes _no_ sense sw $t0,0($a0) # store $t0 into $a0 for strcmp call lb $t6,0($t0) # get the byte of the first string char in the list lw $t7,0($a1) # get address of string lb $t1,0($t7) # get the byte of the first char of the string # NOTE: while we can set these here, we''re burning two regs across the # strcmp call -- cleaner to move this below the call addi $t3,$zero,1 # $t3 gets 1 addi $t4,$zero,-1 # $t3 gets -1 # be careful in this function may have a bug with front of the list alphloop: # slt $t2, $t1, $t0 #if $t1 < $t0, then $t2 = 1, else $t2 = 0 # beq $t2, $t3, put #if # beq $t2, $zero, nextchar # BUG: strcmp destroys the values of a0 and a1, so the second time through # here they have bogus values # BUGBAD: strcmp uses them as pointers to the _strings_ but here, we''re using # a0 as a _struct_ pointer!!! jal strcmp # compare the strings in $a0 and $a1 move $t5,$v0 # move the value returned from strcmp into $t5 beq $t5,$t4,put # if $t5 == -1, then value is less and then put new string at head of list beq $t5,$t3,nextstring # if $t5 == 1, then the head of the list is larger than the string and go to next string beq $t5,$zero,close # check if it is zero, if so it is already in the list so step out nextstring: lw $t2,0($a0) # store pointer to next node in $t2 # NOTE: use hex for bit masks (e.g. 0x0F) # BUG: this makes no sense andi $t8,$t9,15 # get address of next node string beq $t8,$zero,put # if it points to null then add node at the end sw $t8,0($a0) # store into $a0 j alphloop # check against the next string in loop put: # NOTE: what is 8??? obviously, it''s the size in bytes of a node, so the # comment should say that li $t5,8 # $t5 gets 8 move $a0,$t5 # $t5 moved into $a0 jal malloc # allocate size for node move $t5,$v0 # move address returned by malloc to $t5 sw $a1,0($t5) # store $a1 into address allocated beq $t2,$zero,front # node is at front of the list, so there is no need to update pointer sw $t2,4($t5) # store pointer to current node into new node addi $t0,$a0,-8 # subtract from the current node back one sw $t5,0($t0) # store new pointer into the node jr $ra front: sw $t5,0($s0) # make global reference to front of the node the new node if its at the front close: jr $ra

Aquí está el código limpio, refactorizado, corregido y ejecutable.

Algunas cosas que recomiendo:

(1) Para las etiquetas dentro de una función, para evitar conflictos con otras funciones, las etiquetas deben tener como prefijo el nombre de la función (por ejemplo, para su función de trim [que nltrim a nltrim ], tenía un strloop etiqueta [que nltrim_loop a nltrim_loop ])

(2) Los comentarios deben describir la intención en términos del mundo real y no solo describir la instrucción asm real.

Me doy cuenta de que recién estás comenzando, pero (por ejemplo) esto:

addi $t3,$zero,7 # sets the value of $t3 to 7

Debería ser reemplazado por algo más descriptivo:

addi $t3,$zero,7 # count = 7

(3) La regla general es poner un comentario en la barra lateral en cada línea [lo que hiciste]. Es lo que hago, principalmente. Pero, para algunos repetitivos, eso se entiende bien, los comentarios pueden ser excesivos [y en realidad pueden interferir con la legibilidad].

Por ejemplo, el código que establece un marco de pila para una función y el código que se restaura a partir de ese marco en la salida de la función se entiende bien. Entonces, tal vez un solo comentario de bloque en la parte superior como # set up stack frame de la # set up stack frame para las pocas líneas y # restore from stack frame en la parte inferior en lugar de comentarios sobre cada inst

(4) Mantenga los comentarios de la barra lateral cortos para que quepan en 80 columnas. Si necesita más, eleve el comentario a un comentario de bloque de línea completo sobre la instrucción [y use tantas líneas como desee]

(5) Para cosas difíciles / complejas, está bien realizar un prototipo con pseudocódigo o C real [o un lenguaje de su elección]. Es razonable suponer que cualquiera que escriba [o lea] código asm esté familiarizado con al menos un lenguaje de alto nivel [C es el más probable].

Para el código de estructura, agregué una definición de estructura de C en un comentario de bloque superior. En la rutina de insert , agregué el pseudocódigo C en el bloque de comentarios superior. Los comentarios de la barra lateral para el ASM a menudo se refieren a los símbolos y acciones en el pseudocódigo

En realidad, hacer este prototipo incluso para funciones más simples tiene beneficios [incluso si no agrega el código como comentarios]. (por ejemplo) Puede haber ayudado al escribir su strcmp

(6) Mantenga el código lo más simple posible. Cuando el código es innecesariamente complicado, es muy fácil introducir errores en la lógica de su programa o usar instrucciones incorrectas para implementar esa lógica. También hace que sea difícil detectar estos errores más tarde.

(por ejemplo) En ciertos casos, su código estaba cargando un registro, solo para tener que moverlo más tarde. Por lo tanto, usando 2-3 insts donde solo era necesario uno. Intente minimizar el movimiento innecesario del registro [no solo por la velocidad, sino por un código más simple].

Por ejemplo, su strcmp tenía 24 líneas, y solo comparó el primer carácter [es decir, un error], y tenía varias ramas. Mi versión tenía solo 12 líneas, comparaba la cadena completa y era un bucle simple.

Del mismo modo, en su código de inserción, insert_here [no utilizado] tenía 17 líneas e insert tenía 47 líneas para un total de 64. Mi versión de trabajo tenía 31 líneas.

Nota: Utilicé el pseudo-op .eqv para "definir" las compensaciones de estructura. Uso mars y esto funciona, pero no sé si spim admite .eqv . Siempre puede codificar las compensaciones, pero hace que el código sea menos legible y propenso a errores. Con estructuras que tienen [digamos] 10 elementos, alguna forma de esto es bastante útil. La mayoría de los otros ensambladores tienen alguna forma de equivalente .eqv .

De todos modos, aquí está el código:

# Global symbols # struct node { # struct node *node_next; # char *node_str; # }; .eqv node_next 0 .eqv node_str 4 .eqv node_size 8 # sizeof(struct node) # NOTE: we don''t actually use this struct # struct list { # struct node *list_head; # struct node *list_tail; # }; .eqv list_head 0 .eqv list_tail 4 # string routines .globl read_string .globl strcmp .globl strlen .globl nltrim # list routines .globl insert .globl print_list .globl main # pseudo-standard library .globl get_string .globl malloc .globl print_newline .globl print_string # Constants .data MAX_STR_LEN: .word 50 STR_NEWLINE: .asciiz "/n" STR_ENTER: .asciiz "enter a string: " # global registers: # s0 -- list head pointer (list_head) # Code .text # main: repeatedly gets strings from user and enters them in list # until a string of length less than two is entered; # prints list in order when done # main: li $s0,0 # list_head = NULL main_loop: # prompt user for string la $a0,STR_ENTER jal print_string # read in string from user jal read_string # save the string pointer as we''ll use it repeatedly move $s1,$v0 # strip newline move $a0,$s1 jal nltrim # get string length and save the length move $a0,$s1 jal strlen # stop if given empty string blez $v0,main_exit # insert the string jal insert j main_loop main_exit: move $a0,$s0 jal print_list jal print_newline # exit simulation via syscall li $v0,10 syscall ################################################## # String routines # # read_string: allocates MAX_STR_LEN bytes for a string # and then reads a string from standard input into that memory address # and returns the address in $v0 read_string: addi $sp,$sp,-8 sw $ra,0($sp) sw $s0,4($sp) lw $a1,MAX_STR_LEN # $a1 gets MAX_STR_LEN move $a0,$a1 # tell malloc the size jal malloc # allocate space for string move $a0,$v0 # move pointer to allocated memory to $a0 lw $a1,MAX_STR_LEN # $a1 gets MAX_STR_LEN jal get_string # get the string into $v0 move $v0,$a0 # restore string address lw $s0,4($sp) lw $ra,0($sp) addi $sp,$sp,8 jr $ra # nltrim: modifies string stored at address in $a0 so that # first occurrence of a newline is replaced by null terminator nltrim: li $t0,0x0A # ASCII value for newline nltrim_loop: lb $t1,0($a0) # get next char in string beq $t1,$t0,nltrim_replace # is it newline? if yes, fly beqz $t1,nltrim_done # is it EOS? if yes, fly addi $a0,$a0,1 # increment by 1 to point to next char j nltrim_loop # loop nltrim_replace: sb $zero,0($a0) # zero out the newline nltrim_done: jr $ra # return # strlen: given string stored at address in $a0 # returns its length in $v0 # # clobbers: # t1 -- current char strlen: move $v0,$a0 # remember base address strlen_loop: lb $t1,0($a0) # get the current char addi $a0,$a0,1 # pre-increment to next byte of string bnez $t1,strlen_loop # is char 0? if no, loop subu $v0,$a0,$v0 # get length + 1 subi $v0,$v0,1 # get length (compensate for pre-increment) jr $ra # return # strcmp: given strings s, t stored at addresses in $a0, $a1 # returns <0 if s < t; 0 if s == t, >0 if s > t # clobbers: t0, t1 strcmp: lb $t0,0($a0) # get byte of first char in string s lb $t1,0($a1) # get byte of first char in string t sub $v0,$t0,$t1 # compare them bnez $v0,strcmp_done # mismatch? if yes, fly addi $a0,$a0,1 # advance s pointer addi $a1,$a1,1 # advance t pointer bnez $t0,strcmp # at EOS? no=loop, otherwise v0 == 0 strcmp_done: jr $ra # return # insert: inserts new linked-list node in appropriate place in list # # returns address of new front of list in $s0 (which may be same as old) # # arguments: # s0 -- pointer to node at front of list (can be NULL) # s1 -- address of string to insert (strptr) # # registers: # s2 -- address of new node to be inserted (new) # s3 -- address of previous node in list (prev) # s4 -- address of current node in list (cur) # # clobbers: # a0, a1 (from strcmp) # # pseudo-code: # // allocate new node # new = malloc(node_size); # new->node_next = NULL; # new->node_str = strptr; # # // for loop: # prev = NULL; # for (cur = list_head; cur != NULL; cur = cur->node_next) { # if (strcmp(new->node_str,cur->node_str) < 0) # break; # prev = cur; # } # # // insertion: # new->node_next = cur; # if (prev != NULL) # prev->node_next = new; # else # list_head = new; insert: addi $sp,$sp,-4 sw $ra,0($sp) # allocate a new node -- do this first as we''ll _always_ need it li $a0,node_size # get the struct size jal malloc move $s2,$v0 # remember the address # initialize the new node sw $zero,node_next($s2) # new->node_next = NULL sw $s1,node_str($s2) # new->node_str = strptr # set up for loop li $s3,0 # prev = NULL move $s4,$s0 # cur = list_head j insert_test insert_loop: lw $a0,node_str($s2) # get new string address lw $a1,node_str($s4) # get current string address jal strcmp # compare them -- new < cur? bltz $v0,insert_now # if yes, insert after prev move $s3,$s4 # prev = cur lw $s4,node_next($s4) # cur = cur->node_next insert_test: bnez $s4,insert_loop # cur == NULL? if no, loop insert_now: sw $s4,node_next($s2) # new->node_next = cur beqz $s3,insert_front # prev == NULL? if yes, fly sw $s2,node_next($s3) # prev->node_next = new j insert_done insert_front: move $s0,$s2 # list_head = new insert_done: lw $ra,0($sp) addi $sp,$sp,4 jr $ra # print_list: given address of front of list in $a0 # prints each string in list, one per line, in order print_list: addi $sp,$sp,-8 sw $ra,0($sp) sw $s0,4($sp) beq $s0,$zero,print_list_exit print_list_loop: lw $a0,node_str($s0) jal print_string jal print_newline lw $s0,node_next($s0) # node = node->node_next bnez $s0,print_list_loop print_list_exit: lw $s0,4($sp) lw $ra,0($sp) addi $sp,$sp,8 jr $ra # Pseudo-standard library routines: # wrappers around SPIM/MARS syscalls # # assumes buffer to read into is in $a0, and max length is in $a1 get_string: li $v0,8 syscall jr $ra # malloc: takes one argument (in $a0) which indicates how many bytes # to allocate; returns a pointer to the allocated memory (in $v0) malloc: li $v0,9 # SPIM/MARS code for "sbrk" syscall jr $ra # print_newline: displays newline to standard output print_newline: li $v0,4 la $a0,STR_NEWLINE syscall jr $ra # print_string: displays supplied string (in $a0) to standard output print_string: li $v0,4 syscall jr $ra

ACTUALIZAR:

Estoy de acuerdo con los comentarios línea por línea, ya que me ayuda a entender exactamente a qué registros pretendía acceder (o desordenar en el caso anterior).

La razón es simple. Imagine lo contrario: un archivo asm grande [5000 líneas] sin comentarios que se sabe que tiene un error. No puede confiar en la lógica / algoritmo [puede tener el error]. No puede confiar en la implementación [puede tener el error]. Cada vez que recibo un caso como ese, agrego los comentarios como he descrito antes, incluso buscando el error (es decir, estoy aprendiendo el algoritmo y el código mientras hago esto).

Esto está haciendo una revisión de código. Lo haré incluso si el archivo ya tiene comentarios [como el suyo]. A menudo hago esta revisión para el código que acabo de escribir que creo que está "completo" [es decir, todo el código que debe escribirse ha sido].

Si termino tarde en el día, haré la revisión como lo primero al día siguiente. A menudo, "dormir en él" hace que sea fácil detectar errores que no habría encontrado en una revisión el día anterior [porque todavía estaba "demasiado cerca" del problema]. Si lo que estoy escribiendo tomará varios días, siempre reviso el trabajo del día anterior como lo primero.

Incluso con sus comentarios originales que fueron como (2), me ayudó a encontrar sus errores tipográficos. Cuando vi add $t1,$t1,$zero #get zero la falta de coincidencia de comentarios facilitó la búsqueda / reparación. Sería 10 veces más difícil recorrer el código con un depurador.

Los comentarios siempre ayudan. Cuando originalmente codificaba el insert , tenía:

jal strcmp # compare them -- new < cur? bgtz $v0,insert_now # if yes, insert after prev

Obtuve una salida alta a baja.

Al principio, sospeché que strcmp era un código [igualmente] nuevo. Normalmente reviso la función de nivel inferior antes de revisar las llamadas a ella. Hice una revisión de código de strcmp y me pareció bien. Pero, todavía no estaba convencido. Escribí un código de diagnóstico [pruebas unitarias] para strcmp , pero pasó.

Luego, noté los comentarios frente al código en el insert anterior. La solución fue fácil: cambie bgtz a bltz .

Otra buena regla es: no rompa [introduzca un error en] el código [de trabajo] existente .

Cuando revisé por primera vez print_list , pensé: "está usando [y trashing] s0". Esto estaba bien porque después de llamarlo, el programa estaba terminando. Pero no, si se llamara varias veces. Me había perdido el hecho de que estabas guardando / restaurando s0 en la pila [que me di cuenta en la segunda lectura].

Es refrescante ver a un experimentado ser tan alentador para un novato como yo

Todos hemos sido novatos en algún momento. Sin daño, sin falta.

Incluso los programadores veteranos crean errores [a veces, varios / día]. Los errores no son una acusación del alma / carácter de uno. Los errores son simplemente un subproducto normal de ser un programador [al igual que encontrarlos / corregirlos].

OMI, las claves son:

(1) Voluntad de aprender

(2) Admitir errores (p. Ej.) Presidente Kennedy [después de la "Bahía de Cochinos"]: "Los errores no son errores, si los admitimos"

(3) Lo más importante, deja al ego fuera de él.

Los programadores más débiles , cuando se informa un error [o sospecha de error] son ​​los que:

(1) Digamos que su código está funcionando [sin siquiera verificar si el informe de error tiene mérito].

(2) Negar el error como realmente no es un error [cuando lo es]

(3) Negarse a generar [o aceptar] casos de prueba que puedan / prueben / refuten un error

(4) Sea [deliberadamente] "lento" para producir la corrección de errores [no es tan divertido como el nuevo código]

(5) Durante el tiempo de inactividad, rechace "limpiar" el código que funciona, pero que también está mal estructurado [y debe ser refactorizado]

(6) Tratar a los clientes con desdén [es decir, "todos son idiotas"]

OMI, los buenos programadores, por otro lado, son proactivos cuando se trata de errores [o errores potenciales ].

En una compañía en la que estaba, teníamos un producto de envío [una plataforma de procesamiento de video en tiempo real] que no tenía errores aparentes . Estábamos en el tiempo flojo. Pero mi jefe [que también era un buen programador] y yo sospechaba de algún código. Sin informe de errores, sin evidencia contundente, solo una corazonada. Entonces, hicimos una revisión.

Efectivamente, hubo un error. Pero, solo se desencadenaría para un caso de borde oscuro . Creíamos que era demasiado oscuro para que un cliente lo viera, ya que nunca lo habíamos visto en nuestro laboratorio a pesar de todos nuestros videos de prueba. Solo lo encontramos por la revisión del código. Pero, "los errores son errores" , así que comencé la corrección.

Aproximadamente dos semanas después, el representante de atención al cliente de nuestro principal cliente llega con un informe de cierta distorsión intermitente en el video que estaban viendo [aproximadamente una vez cada 2-3 días].

¿Podría esta distorsión provenir del error en el que estaba trabajando? Todavía no tenía mi solución completa, pero, para ese momento, tenía una prueba unitaria que podía generar el error. En el laboratorio, lo activé para el representante. Él dijo: "Sí, eso es, la distorsión que el cliente está viendo"

El error se había introducido unos 3 meses antes. El cliente solo tenía un video diferente al nuestro, lo que hacía que la aparición de errores fuera mucho más probable.

Al ser proactivos, pudimos [honestamente] decirle al cliente que "ya estábamos al tanto" y acortamos el tiempo de respuesta para el arreglo en dos semanas. Los dos nos hicieron dedicarnos a ellos [es decir, nos compraron más equipo].