Una expresión es falsa si su valor es cero; verdadera si es diferente de cero.

Texto completo

(1)

5. Abstracción de bifurcaciones y saltos. 5.1. Operaciones lógicas.

En el lenguaje C:

• Una expresión es falsa si su valor es cero; verdadera si es diferente de cero.

• Si el resultado de una operación lógica es falso, entonces la expresión toma valor cero; uno en caso contrario.

• El and debe estar implementado con cortocircuito. Es decir si el primer operando es falso el resultado debe ser falso.

Para la implementación en assembler, se requieren instrucciones de bifurcación: La instrucción de máquina:

beq <reg1>,<reg2>, rótulo Se interpreta según:

if ( reg1 == reg2) PC = rótulo; else PC = PC +4;

Donde PC es un registro interno del procesador que apunta a la palabra donde se encuentra la instrucción beq. Entonces si los registros tienen contenidos iguales, la próxima instrucción que se ejecuta es la ubicada en la dirección simbólica rótulo. Se bifurca la secuencia de acciones del programa.

También se requiere un salto incondicional a un rótulo. La macro instrucción:

b rótulo se traduce a la de máquina:

j rótulo

que se interpreta como un salto incondicional a la dirección simbólica de una instrucción. Veamos algunos ejemplos:

a) v8 = v7&&v6; se traduce en: beq $v7,$0,false beq $v6,$0,false la $t8,1

(2)

false: la $t8,0 set: move $v8,$t8

(3)

b) v7 = v6||v5; se traduce en: bne $v6,$0,true beq $v5,$0,false true: la $t8,1 b set false: la $t8,0 set: move $v7,$t8 c) v7=!v6; se traduce en: bne $v6,$0,clear la $t8,1 b set clear: la $t8,0 set: move $23,$t8

El lcc genera código empleando el stack para la evaluación de expresiones, el programador assembler puede optimizar el código mediante el empleo de registros temporales, que es lo que se ha empleado en los ejemplos anteriores.

5.2. Alternativa.

La alternativa escrita en C:

if (v8>v7) v6++; else v5--; se traduce, empleando macros, en:

ble $v8,$v7,else

la $v6,1($v6)

b endif

else: subu $v5,$v5,1

endif:

La traducción a lenguaje de máquina es:

slt $t8, $v7, $v8 ; ble $v8,$v7,else beq $t8, $zero, else

addi $v6, $v6, 1

j endif ; bgez $0, endif else: addiu $v5, $v5, -1

(4)

endif:

La instrucción de máquina: bifurca a rótulo si el registro es mayor o igual a cero (bgez) puede implementar un salto incondicional si se elige el registro con la constante cero, de este modo siempre se cumplirá la condición.

Programación de condiciones. Empleando Bifurcaciones y Saltos. Basados en slt y beq.

a) slt r1, r2, r3

Cuya interpretación es: if (r2<r3) r1=1; else r1=0; Y también, la lógicamente equivalente: if ( r2>=r3) r1=0; else r1=1; b) beq r2, r3, rótulo

Usar sltu si los números son naturales. ( sin signo) Usar slti para comparar con constantes con signo.

Usar sltiu para comparar con constantes sin signo (naturales).

Obviamente las comparaciones de igualdad o desigualdad de dos registros se traducen directamente, empleando beq y bne. Veremos a continuación la implementación de comparaciones menor y mayor o igual, en base a slt y beq.

Veremos la generación de otras condiciones, pero sólo describiremos la instrucción inicial, ya que el resto del código es idéntico.

Para la siguiente alternativa:

if (v8 >= v7) v6++; else v5--; La evaluación de la condición v8 >= v7 se logra con la macro:

blt $v8, $v7, else

Que es traducida a instrucciones de máquina según: slt $t8, $v8, $v7 bne $t8, $0, else

Para la siguiente alternativa:

if (v8 < v7) v6++; else v5--; La evaluación de la condición v8 < v7 se logra con la macro:

bge $v8, $v7, else

Que es traducida a instrucciones de máquina según: slt $t8, $v8, $v7

(5)

beq $t8, $0, else Para la siguiente alternativa:

if (v8 <= v7) v6++; else v5--; La evaluación de la condición v8 <= v7 se logra con la macro:

bgt $v8, $v7, else

Que es traducida a instrucciones de máquina según: slt $t8, $v7, $v8 bne $t8, $0, else

Para la siguiente alternativa:

if (v8 == v7) v6++; else v5--;

La evaluación de la condición v8 == v7 se logra directamente con la instrucción de máquina:

bne $v8, $v7, else Para la siguiente alternativa:

if (v8 != v7) v6++; else v5--;

La evaluación de la condición v8 != v7 se logra directamente con la instrucción de máquina:

beq $v8,$v7,else

Para programar empleando instrucciones de máquina, la estrategia entonces es bifurcar al else.

Más adelante se implementarán algunas instrucciones de máquina, para realizar un diseño de un procesador simple. Si se decide implementar sólo el beq, las instrucciones bne pueden ser reemplazadas por beq, como se ilustra a continuación:

Por ejemplo:

bne $t8, $0, else puede reemplazarse por la instrucción:

beq $t8, $t7, else

siempre que en $t7 se haya depositado un 1 previamente. La instrucción:

(6)

debe ser reemplazada por: beq $v8, $v7, then

cambiando el orden de las acciones alternativas.

Veamos otro ejemplo:

El código: if (R5 < R6) R4++; else R2= R1-1; R3--;

Se asume que los nombres de las variables en C, se tendrán en registros temporales, de igual número que la variable.

Se usará el registro $t9 como de almacenamiento temporal, para formar la condición. Puede trasladarse según:

slt $t9, $t5, $t6 # t9 = 1 si t5 < t6 y 0 si t5 >= t6 beq $t9, $zero, else # bifurca a else si t5 >= t6 addi $t4, $t4, 1 # acción del then

j endif # salta al fin del if then else. else: addi $t2, $t1, -1 # acción del else

endif: addi $t3, $t3, -1 # acción siguiente a la alternativa if then else.

Se han empleado los símbolos: else y endif para hacer referencia a las direcciones donde se debe saltar o bifurcar. Estos nombres debe elegirlos el programador, y ojalá sean de ayuda al elaborar el código.

Debido a que se dispone de bifurcación por alguna condición y también la bifurcación por el complemento de la condición; y que además puede efectuarse diferentes elecciones para los operandos en la instrucción de comparación (slt), el segmento de código anterior puede escribirse de diferentes maneras.

En este caso no conviene cambiar el orden de los operandos en la comparación, ya que se desea evaluar t5 < t6, pero puede emplearse un bne, lo cual se ilustra a continuación: slt $t9, $t5, $t6 # t9 = 1 si t5 < t6 y 0 si t5 >= t6

bne $t9, $zero, if # bifurca a segmento del if si t5 < t6 addi $t2, $t1, -1 # acción del else

j endif # salta al fin del if then else. if: addi $t4, $t4, 1 # acción del then

(7)

Direccionamiento. Referencias en saltos y bifurcaciones.

La directiva (instrucción hacia el assembler) .text le comunica al ensamblador que lo que viene a continuación son instrucciones.

La directiva .globl establece una dirección como global, ésta podrá ser accesada desde otros módulos assembler. En el lenguaje C, se emplea el calificador extern, para especificar que una función (dirección) ha sido definida como global en otro módulo.

Main, arriba y abajo son direcciones simbólicas.

El programador emplea símbolos para las direcciones (rótulos) de memoria donde se encuentran almacenadas las instrucciones.

.text

.globl main

main: beq $t1, $t2, abajo arriba: add $t1, $t1, $t1

addi $t2, $t3, 5 abajo: beq $t1, $t2, arriba j arriba

j main # para repetir la secuencia

El ejemplo muestra referencias hacia delante (salto hacia instrucciones que están cargadas en direcciones mayores de la memoria que la que se está ejecutando) y hacia atrás (el caso del salto hacia el rótulo arriba).

El assembler provee mecanismos para completar los campos binarios de los saltos y bifurcaciones.

Para tal fin va incorporando los símbolos que aparecen en una tabla, en la cual se asocia la calidad del símbolo (si está en el módulo, o si es externo), con la dirección que le corresponde (si es que la conoce, en caso de referencias hacia delante no la conoce; y si no la conoce lo incorpora en la tabla como una referencia no satisfecha).

Para ir colocando direcciones en la tabla, es preciso conocer la dirección inicial de la primera instrucción (esto puede establecerse mediante la directiva de origen), y el largo de la instrucción que se está ensamblando.

Un procedimiento habitual es efectuar una primera pasada de lectura del código, en la cual no se ensambla, sólo se calculan direcciones y se va llenando la tabla, de este modo todos

(8)

los símbolos locales al módulo quedan con sus direcciones, excepto los externos. Luego se efectúa una segunda pasada, en la que va generando el código binario de las instrucciones. Cuando se emplean bibliotecas o diferentes módulos, se traduce el texto assembler a un formato objeto, que en forma binaria contiene los códigos de operación y todos los campos que han podido calcularse, estableciendo algún tipo de marcas en campos que aún no pueden llenarse porque corresponden a símbolos externos; además de esto le agrega la tabla de símbolos del módulo. El ligador (linker) carga todos los módulos objeto, le adiciona los segmentos de código de funciones de biblioteca, y genera una sola tabla de símbolos, con las tablas que vienen en cada módulo objeto, de esta forma puede llenar las direcciones que le faltan en la tabla y luego reescribir los campos incompletos de las instrucciones.

Cuando aparece el concepto multitarea, es decir tener varios programas en ejecución, tanto el assembler como el ligador se adaptaron para generar un código final que es relocalizable; es decir, no hay direcciones absolutas en el código binario. De este modo, el cargador (loader) puede depositar el programa el cualquier lugar de la memoria, es el loader el que establece las direcciones físicas que tendrán finalmente las instrucciones de un programa. Esto, a su vez demandó disponer de repertorios con modos de direccionamiento que permitan generar códigos relocalizables. El modo de direccionamiento relativo a PC, para saltos y bifurcaciones tiene esta capacidad.

Si se agrega una instrucción assembler, todo el módulo debe ser reensamblado; ya que cambian los campos de direccionamiento de las instrucciones de saltos y bifurcaciones. Así también ocurre si se agregan datos, o se cambia su tamaño; todas las referencias a datos, a través de las instrucciones de carga y almacenamiento se modifican.

5.3. Repeticiones. 5.3.1. While.

La sentencia básica de iteración en C es:

mientras se cumpla la condición, realice acción. while (v8 > v0) v0++;

Se traduce saltando al código que efectúa la evaluación de la condición; y si el test resulta verdadero, se bifurca al bloque que realiza las acciones que deben repetirse.

j test

(9)

test: bgt $v8, $v0, bloque

Las traducciones de los macros a secuencias de instrucciones de máquina es similar al caso analizado antes.

5.3.2. Do while.

La repetición de a lo menos una vez:

repita acción mientras condición; do {v1++;} while (v8>v1); Se traduce en:

bloque: addi $v1, $v1, 1 L.6: bgt $v8, $v1, bloque Veamos otros ejemplos:

El código: while ( R2 >= R4 ) {R2--; R3 = R3 +1;} R5--;

En su implementación, lo que importa es formar la condición pedida; esto es R2 >=R4, o R2 < R4.

El orden de los operandos en la comparación (slt), depende de la condición. Puede trasladarse según:

While: slt $t9, $t2, $t4 # t9 =1 si t2 < t4 y 0 si t2 >= t4 bne $t9, $zero, Endwh # bifurca si es condición de término. addi $t2, $t2,-1

addi $t3, $t3, 1 j While Endwh:addi $t5, $t5, -1 También, sólo usando beq:

While: slt $t9, $t2, $t4 # t9 =1 si t2 < t4 y 0 si t2 >= t4 beq $t9, $zero, bloque

j Endwh bloque:addi $t2, $t2, -1

addi $t3, $t3, 1 j While Endwh:addi $t5, $t5, -1

(10)

Si se forma R2-R4 >= 0, puede emplearse la bifurcación bgez, para implementar la condición, como se muestra a continuación.

sub $t2, $t2, $t4 j test While: addi $t2, $t2,-1 addi $t3, $t3, 1

test : bgez $t2, while # bifurca si se repite el bloque. add $t2, $t2, $t4

Endwh:addi $t5, $t5, -1

Código que es más eficiente, en velocidad, en caso que el bloque se repita un número elevado de veces (ejecuta tres instrucciones por bloque). Además sólo una bifurcación por bloque, lo cual mejora la ejecución en un procesador segmentado (pipeline) como se verá más adelante. Ver ejemplo 3.9 en el texto guía.

La codificación correcta de la sentencia while implica el verificar que a medida que se efectúa el bloque repetitivo se vayan modificando algunas variables, de tal modo que la condición deje de ser cierta, en algún momento. Esto evita los lazos infinitos; si por mala codificación se producen, el procesador queda repitiendo permanentemente una secuencia de instrucciones. Esto se denomina hang-up (colgado).

En el caso del ejemplo, debe verificarse que R2 disminuye o que R4 aumente, con lo cual deja de ser verdadera la condición (R2 >= R4), dando fin a la repetición.

5.3.3. For.

Una operación frecuente es la repetición de un bloque de instrucciones un número conocido de veces, en los lenguajes de alto nivel suele denominarse for. El lenguaje C, posee una construcción especial para el for, que la hace muy poderosa. Se expresa según:

for (inicio; condición; reinicio) bloque; Donde inicio, condición y reinicio son expresiones.

La sentencia for tiene la siguiente interpretación: inicio;

while (condición) {

bloque; reinicio;

(11)

Es la sentencia más poderosa del lenguaje C para las repeticiones. Veamos un ejemplo: for(v8=0; v8<10; v8++) v0--; Se traduce en la secuencia: inicio: move $v8, $0 j test bloque: subu $v0,$v0,1 reinicio: addi $v8, $v8, 1 test: la $t8,10 blt $v8,$t8, bloque

Nótese que en el caso del ejemplo (en el cual se compara la variable de control del for con una constante), si el compilador puede determinar que el bloque se realiza al inicio, puede omitirse el salto a test, después del inicio.

Un ejemplo típico de for es: for ( i=0 ; i<N ; i++ ){ bloque de repetición };

Se evalúa la condición, luego se realiza el bloque y después se modifica la variable de control, y se vuelve a evaluar la condición. De esta forma, si se inicia la variable de control del for en cero (caso del ejemplo), después de realizado una vez el bloque, la variable se incrementa en uno; y así sucesivamente. Es decir, en el momento de evaluar la condición la variable de control registra el número de veces que se ha realizado el bloque.

Si uno forma la expresión: (veces que se ha realizado el bloque)- (valor de la variable de control al evaluar la condición); podrá verificar, mediante razonamiento inductivo, que el valor de ésta es cero. El hecho de que dicha expresión no cambie su valor durante la repetición permite denominarla: invariante del lazo. Concepto que es útil para determinar el número exacto de veces que se desea repetir el bloque, y no una vez más o una vez menos, si se programa descuidadamente.

La correcta construcción, implica verificar que desde la expresión de inicio y a medida que se ejecuta la expresión de reinicio se llegue a producir que la condición sea falsa. Si nunca la condición del for se hace falsa, tendremos un lazo infinito.

Veremos cómo se compila un lazo regresivo, con las variables en registros: for ( R1=7 ; R1>0 ; R1-- ) R3 = R2+R1;

(12)

Si se analiza la siguiente tabla:

Veces que se ha realizado el bloque

Valor variable de control al evaluar condición Valor condición 0 7 V 1 6 V 2 5 V 3 4 V 4 3 V 5 2 V 6 1 V 7 0 F

Invariante del lazo = (veces que se ha realizado el bloque) + (valor de la variable de control al evaluar la condición) = 7

li $t1, 7 # inicio

for: slt $t9, $zero, $t1 # t9 = 0 si 0 >= t1 beq $t9, $zero, endfor # condición add $t3, $t2, $t1 # bloque addi $t9, $t9, -1 # reinicio j for

Las instrucciones slt y beq pueden reemplazarse por: blez $t1, endfor

Los programas de alto nivel, le permiten al programador abstraerse de los saltos y bifurcaciones (algunos incluso le impiden, en prácticas de buena programación, emplear instrucciones de saltos o bifurcaciones). Además, en estos lenguajes, se restringen las posibilidades de saltos a mecanismos de programación estructurada.

El compilador genera automáticamente el flujo de ejecución a través de saltos y bifurcaciones.

El compilador lcc genera los lazos for con la siguiente codificación: Repasando, la construcción: for(inicio; condición; reinicio)

ejecuta la expresión de inicio; luego evalúa la condición, y si es verdadera(valor diferente de cero) ejecuta el bloque asociado; finalmente evalúa la expresión de reinicio y vuelve a

(13)

evaluar la condición y así sucesivamente hasta que ésta es falsa(valor cero), situación en que termina el for.

Sean las variables locales i y j, y luego una sentencia for: int i, j;

for(i=0 ;i>10; i++) j++;

La traducción a assembler queda como sigue: #asume i en s8, j en s7, las considera locales. inicio: add $s8, $zero, $zero

j condicion

bloque: addi $s7, $s7, 1

reinicio: addi $s8, $s8, 1 #i++ condicion: addi $t8, $zero,10

slt $t7, $t8, $s8 # t7=1 si 10<i bne $t7, $zero, bloque endfor:

Ejemplo de construcción de sentencia for.

La siguiente sentencia for, escribe el valor de i, para los valores que toma i, que son controlados por el for.

int main(void) {

int i;

for(i=0; i < 10; i++) printf(" %d \n",i); return(0);

}

Si se compila empleando lcc, se obtiene: .set reorder .globl main Condición F V inicio bloque reinicio

(14)

.text .text .align 2 .ent main main: .frame $sp,24,$31

addu $sp,$sp,-24 #arma el frame de 6 palabras( 6*4 = 24 Bytes) .mask 0xc0000000,-4

sw $30,16($sp) #salva $30 ya que escribe en $30 en la rutina

sw $31,20($sp) #salva $31 ya que existe un llamado dentro de la rutina

move $30,$0 L.2: la $4,L.6 move $5,$30 jal printf L.3: la $30,1($30) la $24,10 blt $30,$24,L.2

move $2,$0 #valor de retorno L.1: lw $30,16($sp) #restaura $30

lw $31,20($sp) #restaura $30 addu $sp,$sp,24 #desarma el frame

j $31 .end main .rdata .align 0 L.6: .byte 32 .byte 37 .byte 100 .byte 32 .byte 10 .byte 0

Si el programa hubiera sido escrito en assembler, respetando las reglas del compilador lcc, y no empleando macros, y adicionalmente generando las condiciones empleando slt y beq, quedaría como sigue:

.globl main .text

.align 2 #almacenar con alineamiento de palabras. main: addu $sp,$sp,-24

sw $s8,16($sp)

$30 $31

sp Tope del frame

Fondo del frame

(15)

sw $ra,20($sp) inicio: add $s8,$zero,$zero j test

bloque:la $a0, format #pasa argumentos en registros ai

move $a1,$s8

jal printf

reinit: addi $s8,$s8,1 #escribe en $s8 test: addi $t8,$zero,10

addi $t7,$zero,1

slt $t6,$s8,$t8 # t6=1 si i<10 beq $t6,$t7,bloque

endfor: add $v0,$zero,$zero #retorna resultados en registros vi lw $s8,16($sp) #restaura $s8 lw $ra,20($sp) # addu $sp,$sp,24 j $ra .end main .data

.align 0 #almacenar con alineamiento de bytes. format: .asciiz " %d \n"

Nótese el uso de nombres simbólicos para los registros, para los nombres de los rótulos, el empleo de la directiva .asciiz, que permite describir string en forma legible. Se han agregado comentarios.

Los cuatro espacios de palabra en el tope del stack, se reservan para que la rutina que es invocada(en este caso printf) guarde los argumentos a0, a1, a2 y a3.

Si se observa el uso de los registros temporales $t8 y $t7, se advierte que se cargan con constantes. Y que estas instrucciones están dentro del bloque que se repite. El programador assembler(y hoy en día casi todos los compiladores) extraen de los bloques de repetición estas recargas de constantes y las dejan fuera de éstos. Se decide dejarlas como variables locales, y para esto se emplean registros s, los que deben salvarse si son modificados por la función. Se logra un programa que se ejecuta más velozmente.

Analizando las condiciones del for, éste siempre se realiza la primera vez, lo cual permite eliminar el primer salto a test.

(16)

Nótese que no pueden emplearse registros t, ya que no se sabe si la rutina printf los emplea. Pero si se sabe que no son empleados en printf, podrían emplearse; con lo cual se ahorraría salvar y restaurar registros s en el stack.

.globl main .text

.align 2 #almacenar con alineamiento de palabras. main: addu $sp,$sp,-32

sw $s6,16($sp)

sw $s7,20($sp)

sw $s8,24($sp)

sw $ra,28($sp)

inicio: add $s8,$zero,$zero #escribe en $s8

addi $s7,$zero,10

addi $s6,$zero,1

bloque:la $a0, format #pasa argumentos en registros ai move $a1, $s8

jal printf

reinit: addi $s8,$s8,1

test: slt $t8,$s8,$s7 # t8=1 si i<10 beq $t8,$s6,bloque

endfor: add $v0,$zero,$zero #retorna resultados en registros vi

lw $s6,16($sp) lw $s7,20($sp) lw $s8,24($sp) #restaura $s8 lw $ra,28($sp) # addu $sp,$sp,32 j $ra .end main .data

.align 0 #almacenar con alineamiento de bytes. format: .asciiz " %d \n"

Si el programador (de main) sabe que tiene disponibles los registros temporales t0, t1, t2 y t4, puede reorganizar aún más el código. Ahora no se requiere disponer de un frame, todas las variables quedan en registros temporales, que no requieren ser salvados.

(17)

.globl main

.text

.align 2 #almacenar instrucciones con alineamiento de palabras. main: add $t3, $zero, $ra #salva $ra en registro temporal $t3 inicio: add $t2, $zero, $zero #$t2 es i

addi $t1, $zero, 10 addi $t0, $zero, 1

bloque:la $a0, format #pasa argumentos en registros ai

add $a1,$zero,$t2

jal printf

reinit: addi $t2, $t2, 1

test: slt $t8, $t2, $t1 # t8=1 si i<10 beq $t8, $t0, bloque

endfor: add $v0, $zero, $zero #retorna resultados en registros vi add $ra, $zero, $t3 #recupera $ra.

j $ra

.end main

.data

.align 0 #almacenar string con alineamiento de bytes. format: .asciiz " %d \n" 5.4. Expresión condicional. La construcción: v8= (v7>v6) ? v7 : v6; Se traduce en: ble $v7, $v6, else move $t8, $v7 b asigna else: move $t8, $v6 asigna: move $v8, $t8

(18)

En lcc no se emplea registros temporales. Las expresiones almacenan temporales en el stack.

5.5 Decisión múltiple.

La proposición switch siguiente: switch (v7) { case 1: case 2: v2++; break; case 3: v3--; break; default: v3=++v5; } Se traduce según: la $t8, 1 beq $v7, $t8, case1 la $t8, 2 beq $v7, $t8, case1 la $t8, 3 beq $v7, $t8, case3 b defecto case1: addi $v2, $v2, 1 b salida case3: subu $v3, $v3, 1 b salida defecto: addi $t8, $v5, 1 move $v5, $t8 move $v3, $t8 salida:

Se generan bifurcaciones a las acciones de cada caso, de acuerdo al valor de las etiquetas. El caso por defecto, si está presente es un caso más, al cual se salta incondicionalmente al final de los casos presentes.

Nótese que el break se traduce como un salto incondicional a la salida. Si no están presentes, se ejecutan las acciones de los casos siguientes.

(19)

5.6. Break. Continue.

Cuando se encuentra la sentencia break, en las iteraciones: for, while o do, se termina la iteración; tal como lo hace en el switch.

Es un salto incondicional a la siguiente sentencia.

En assembler esto es más evidente que en C, debido a los saltos explícitos del assembler. Cuando se encuentra la sentencia continue, en las iteraciones: for, while o do, se inicia la siguiente iteración. En el caso del while y do, se salta incondicionalmente al test de la condición; en el caso del for se salta incondicionalmente al texto de reinicio.

for(v8=0 ;v8<10 ;v8++ ) { v6++; if(v6>8) break;v5++; } move $30, $0 bloque: addi $v6, $v6, 1 la $t8, 8 ble $v6, $t8, endblk b salida # break endblk: addi $v5, $v5, 1 reinicio: addi $v8, $v8, 1 test: la $t8, 10 blt $v8, $t8, bloque salida:

El caso del continue sólo difiere que salta a reinicio, en lugar del salto a la salida del break. for(v8=0 ;v8<10 ;v8++ ) { v6++; if(v6>8) continue;v5++; }

move $30, $0 bloque: addi $v6, $v6,1 la $t8, 8 ble $v6, $t8, endblk b reinicio # continue endblk: addi $v5, $v5, 1 reinicio: addi $v8, $v8, 1 test: la $t8, 10 blt $v8, $t8, bloque salida:

Figure

Actualización...

Referencias

Actualización...

Related subjects : Cero Y Uno