miércoles, 28 de septiembre de 2016

Tecnologías para la construcción de Memorias

DRAM

La Memoria de Acceso Aleatorio, mas frecuentemente conocida como RAM, es un componente común que cada PC necesita. Los usuarios que construyan una nueva computadora siempre necesitan comprarla, y aquellos con hardware desactualizado usualmente necesitan actualizarlo, como una manera sencilla de mejorar el rendimiento. La RAM puede ser también encontrada  en una variedad de productos como smartphones, tabletas, tarjetas de vídeo y mas, aunque en esos casos, esta usualmente soldada a una tarjeta madre y puede ser reemplazada.

La RAM, como la mayoría de las cosas, esta hecha en un fabrica, pero no cualquier fabrica. Equipos extremadamente complejos son requeridos para fabricar los chips y debido a que impurezas dentro o sobre el silicon puede causar defectos, el ambiente debe estar extremadamente limpio. También esta el problema de programar las maquinas, una tarea la cual por si misma requiere habilidades significativas de ingeniería.

Y hay mas que solo la RAM por si misma. La memoria debe estar adherida a ya sea una tarjeta de circuito impreso o a la tarjeta madre del producto que se enviara. La tarjeta de circuito impreso debe estar también producida en un ambiente limpio por medio de una maquina de extrema precisión, que a su vez necesita de entrenamiento y ser operada apropiadamente, ya que un pequeño error de soldadura podría resultar en miles de chips defectuosos. Y finalmente una vez ensamblado, el producto final debe ser probado y luego puesto en el producto que se enviara.

Lo que tenemos entonces, es la receta para costos de inicio extremadamente altos. El equipo de producción opera desde cientos de miles a cientos de millones por maquina, y una fabrica podría incluir docenas o cientos de ellos. Luego estos deben ser puestos un ambiente seguro y limpio, lo que significa que solo las mejores industrias harán este trabajo.  Y finalmente, un equipo de ingenieros debe ser contratado para supervisar la producción, y su valioso conocimiento no es barato.

Si alguna vez te has preguntado porque muchas de las compañías mundiales de diseño de hardware (como AMD, ARM y NVDIA) no producen sus propios chips, bueno, este es el porque. El costo extremo de comenzar la producción significa que no hay una tienda de producción independiente de RAMs, y a diferencia de otras industrias, la linea de operación de producción puede solo durar de tres a cinco años. Un cambio en el estándar o una nueva tecnología de producción usualmente fuerza a los manofacturadores a cambiar su equipo o comprar maquinas totalmente nuevas.

SRAM

Conocida como memoria cache, es un componente que almacena información para futuras referencias y que esta pueda ser servida rápidamente. La información almacenada en la cache puede ser el resultado de un computo anterior, o el duplicado de información almacenada en otra parte. Un hit de cache ocurre cuando la información que se pide puede ser encontrada en la cache, mientras que un miss de cache ocurre cuando no. Los hits de cache puede ser servidos mediante la lectura de datos desde la cache, lo cual es mas rápido que re computar un resultado o leer desde un almacenamiento mas lento, por lo tanto, mientras mas peticiones puedan ser servidas desde la cache, el rendimiento del sistema sera mejor.

Pare ser costo-efectivas y hacer uso eficiente de los datos, las caches son relativamente pequeñas. Sin embargo, las caches se han probado por si mismas en muchas áreas de la computación porque los patrones de accesos en aplicaciones típicas de computadoras exhiben la referencia de ubicación. Mas aun, los patrones de accesos exhiben localidad temporal si los datos se piden de nuevo después de que se pidieron recientemente, mientras localidad espacial se refiere a peticiones de datos almacenados físicamente cerca a los datos que ya han sido pedidos. 

Una celda tipica de SRAM esta hecha de seis MOSFETs. Cada bit en una SRAM esta almacenado en cuatro transistores que forman dos parejas de inversores cruzados. Esta celda de almacenamiento tiene dos estados estables los cuales son usados para denotar 0 y 1. Dos transistores de accesos adicionales sirven para controlar el acceso a una celda de almacenamiento durante operaciones de lectura o escritura. En adición a dichos seis transistores, otros tipos de chips de SRAM usan 4, 8, 10 o mas transistores por bit. SRAM de cuatro transistores es lo mas común en dispositivos que utilizan una sola SRAM,  implementado en un proceso especial con una capa extra de polysilicon, permitiendo la instalación de resistores de alta resistencia. La principal desventaja de utilizar 4T SRAM es el incremento de poder estático debido al constante flujo de corriente a través de uno de los transistores instalados.

Esto es a veces usado para implementar mas de un puerto de lectura o escritura, el cual puede ser útil en ciertos tipos de memoria de vídeo y archivos de registros implementados con circuiteria SRAM de multi puerto.

Generalmente, mientras menos transistores sean necesitados por celda, mas pequeña puede ser la celda. Dado que el costo de procesar una oblea de silicon es relativamente fijo, usar celdas mas pequeñas y por lo tanto empacar mas bits en una oblea reduce el costo por bit de memoria.

El acceso a la celda esta permitido por la linea de palabra, el cual controla los dos transistores de acceso, los cuales, en turno, controlan ya sea que la celda deba ser conectada a las lineas de bit. Ellos están acostumbrados a transferir datos para tanto operaciones de lectura o escritura. Aunque no es estrictamente necesario el tener dos lineas de bit, tanto la señal y su inverso son tipicamente proporcionados en orden para mejorar los margenes de ruidos.

Durante el proceso de lectura, las lineas de bit son activamente llevadas a alto o bajo por los inversiones en la celda de SRAM. Esto mejora el ancho de banda de la SRAM en comparación a las DRAMs. En una DRAM, la linea de bit esta conectada a los capacitores de almacenamiento y el compartimiento de carga causa que el bit de linea oscilar hacia arriba o hacia abajo. La estructura simétrica de las SRAMs también permite el señalamiento diferencial, el cual hace que pequeñas oscilaciones de voltajes sean detectables con mayor facilidad. Otra diferencia con DRAM que contribuye a hacer SRAM mas rápido es que los chips comerciales aceptan todas los bits de dirección al mismo tiempo. En comparación, DRAMs de mercancía mantiene la dirección multiplicada en dos mitades, por ejemplo, bits mas altos seguidos por bits mas bajos, sobre el mismo paquete de pins con el fin de mantener su tamaño y costo bajo.


jueves, 22 de septiembre de 2016

Jerarquia de Memoria

El ordenamiento de almacenamiento en las arquitecturas de las computadoras actuales es llamado "jerarquía de memoria". Esta diseñado para tomar ventaja de las ubicaciones de memoria para los programas de computadoras. Cada nivel de la jerarquía es de una velocidad más alta, latencia mas pequeña y de tamaño menor, que los niveles más bajos.

La mayoría de los CPUs modernos son tan rápidos que para la mayoría de los programas, sobrecargan las referencias de localidades de los accesos de memoria, y la eficiencia del cacheo y transferencia  de memoria entre los diferentes niveles de la jerarquía , es la limitación práctica en la velocidad de procesamiento. Como resultado, el CPU pasa mucho de su tiempo, esperando a que las entradas y salidas se completen.



La jerarquía de memoria en la mayoría de las computadoras se compone de la siguiente manera:

Registros: Los accesos más rápidos (usualmente un ciclo de CPU), solo algunos cientos de bytes en tamaño.


Nivel 1 (L1) cache – usualmente accedida en solo unos cuantos ciclos, usualmente miles de kilobytes. 


Nivel 2 (L2) cache – latencia mas alta que L1, usualmente 512KB o más.

Nivel 3 (L3) cache – latencia mas alta que L2, usualmente múltiples megabytes.

Memoria principal (DRAM) – puede tomar cientos de ciclos, pero muchos gigabytes.


Almacenamiento de disco – cientos de miles de ciclos de latencia, pero muy grande.





En el nivel mas alto de la jerarquía, se encuentran los registros de propósito general del CPU. Estos proveen el acceso mas rápido posible a datos. Estos registros a su vez son el objeto más pequeño de la jerarquía de memoria. También son las ubicaciones de memoria más caras. 

Un poco mas abajo, se encuentra el nivel uno del sistema caché, y es el siguiente subsistema de mayor rendimiento en la jerarquía. El tamaño es usualmente muy pequeño, típicamente entre 4k bytes y 32k bytes, aun asi, mucho mas grandes que los registros disponibles en el chip CPU. Aunque el tamaño del nivel uno de cache esta fijado en el CPU y no se puede expandir, el costo por byte de la memoria cache es mucho mas pequeño que el de los registros, porque la cache contiene por mucho mas almacenamiento que es disponible en todos los registros combinados.

El nivel dos de cache esta presente en algunos CPUs, en otros, es tarea del diseñador del sistema el incorporar esta cache. Por ejemplo, la mayoría de procesadores Pentium II, III y IV tienen un nivel dos de cache como parte del paquete de CPU, pero muchos de los procesadores Intel Celeron no lo tienen. El nivel dos de cache es generalmente mucho mas grande que el nivel uno. En CPUs donde Intel incluye el nivel dos de caché como parte del paquete del CPU, la caché no es expandible. Aun asi es de costo mas pequeño que el nivel uno de cache porque se amortigua el costo del CPU a través de todos los bytes en el nivel dos de caché . En sistemas donde el nivel dos, es externo, muchos diseñadores de sistemas dejan al usuario final seleccionar el tamaño de la cache y aumentarlo. Por razones económicas, caches externas son en realidad más caras que las caches que son parte del paquete de CPU.

Bajo el nivel dos de cache, se encuentra el subsistema de la memoria principal. Esta es la memoria de propósito general y de relativamente bajo costo encontrada en la mayoría de sistemas de computadoras. Típicamente esta es la DRAM o algun otra tecnología de memoria similar de bajo costo.

Bajo la memoria principal, está la categoría NUMA. NUMA significa que diferentes tipos de memoria tienen diferentes tiempos de accesos. Por lo tanto, el término NUMA es justamente descriptiva de toda la jerarquía de memoria. Sin embargo, usaremos el término NUMA para describir bloques de memoria que sos electrónicamente similar a la memoria principal pero por alguno razón y otra operan significativamente más lento que la memoria principal. Un buen ejemplo es la memoria en una tarjeta de video. Acceder a la memoria de una tarjeta de video es usualmente mas lento que el acceso a la memoria principal. Otros dispositivos periféricos que proveen un bloque de memoria compartida entre el CPU y el periférico, probablemente ofrezcan un tiempo de acceso similar al de las tarjetas de video. Otros ejemplos de NUMA incluyen ciertas tecnologías de memoria mas lenta como las memorias FLASH, que tienen un acceso significativamente mas lento y tiempos de transferencia mas largos que los del semiconductor estándar de RAM.

La mayoría de los sistemas de las computadoras modernas implementan un esquema de Memoria Virtual que les permite simular memoria principal usando espacio de memoria de un disco duro. Aunque los discos son significativamente mucho mas lentos que la memoria principal, el costo por bit también es significativamente mucho menor. Por lo tanto, es mucho más barato mantener algunos datos en almacenaje magnético que en la memoria principal. Un subsistema de Memoria Virtual es responsable de copiar transparentemente datos entre el disco y la memoria principal tanto como lo necesite un programa.

El Almacenaje de Archivos también usa medios de disco para almacenar datos de programa. Sin embargo, es responsabilidad del programa almacenar y recuperar datos de archivo. En muchas instancias, esto es un poco más lento que usar Memoria Virtual, de ahí que se encuentre en una posición de la jerarquía más baja.

Bajo el Almacenaje de Archivos se encuentra el Almacenaje de Red. En este nivel, un programa esta manteniendo datos en un sistema diferente que conecta el sistema del programa vía una red. Con el Almacenaje de Red se puede implementar Memoria Virtual, Almacenaje de Datos y un sistema conocido como Memoria Compartida Distribuida (donde procesos ejecutándose en diferentes sistemas de computadoras comparten datos en un bloque común de memoria y comunican cambios a ese bloque a través de la red).

La Memoria Virtual, Almacenaje de Archivos y Almacenaje de Red son ejemplos de los llamados subsistemas de memoria en linea. Los accesos a memoria por medio de estos mecanismos son mas lentos que accesos a memoria principal, pero cuando un programa pide datos de algunos de estos dispositivos de memoria, el dispositivo esta listo para responder a la petición tan rápido como sea físicamente posible. Esto ultimo no es cierto para los restantes niveles en la jerarquía de memoria.

Los subsistemas Cerca y Fuera de Linea no están inmediatamente listos para responder a las peticiones de datos de un programa Un sistema de Almacenamiento Fuera de Linea mantiene la información en forma electrónica (usualmente magnética y óptica) pero en medios que no están necesariamente conectados al sistema de la computadora cuando el programa que necesite la información este ejecutándose. Ejemplos de Almacenamiento Fuera de Linea incluyen cintas magnéticas, cartuchos de disco, discos ópticos, y disquetes. Cuando un programa necesita información desde un medio fuera de linea, el programa se detiene y espera a que alguien o algo monte el medio apropiado en la computadora. El Almacenamiento Cerca de Linea usa los mismos medios que los de fuera de línea, la diferencia es que el sistema mantiene el medio en un dispositivo que puede montar automáticamente el medio deseado cuando un programa lo requiera. Las cintas y dispositivos removibles están entre los formatos de almacenamiento mas baratos que existen. Por lo tanto, estos medios son efectivos para almacenar grandes cantidades de datos por largos periodos de tiempo.

El Almacenamiento de Copia Robusta es simplemente el imprimir algunos datos. Si un programa pide alguna información, y la información esta presente únicamente en forma de copia robusta, alguien tendrá que ingresar la información de manera manual a la computadora. El papel es probablemente la forma mas barata de memoria, al menos para ciertos tipos de datos.




lunes, 5 de septiembre de 2016

Ejercicios: Arquitectura MIPS

Ejercicios

Para la realización de los siguientes ejercicios, use el simulador asignado para la arquitectura MIPS (cuando el ejercicio lo permita). Cuando sea conveniente o necesario, haga las suposiciones correspondientes. También, se permite el uso de pseudoinstrucciones.

1.      Para el siguiente fragmento de código C, ¿Cuál es el correspondiente código en ensamblador MIPS?
f = g + (h - 5)
Solución:

f = $s0             g = $s1                        h = $s2
add $s0, $s0, $s1
addi $t0, $s2, -5
add $s0, $s0, $t0

2.      Para el siguiente fragmento de código en ensamblador MIPS, ¿Cuál es el correspondiente código en lenguaje C?
add f, g, h
add d, i, f
sub b, f, d

Solución:

Procedimiento:
f = (g+h)
d = (i+g+h)
Resultado:
b = f – d     ó     b = (g+h) - (i+g+h)

3.      Para el siguiente fragmento de código C, ¿Cuál es el correspondiente código en ensamblador MIPS?
B[8] = A[i-j]
Solución:

B[0] = $s0                   A[0] = $s1                   i = $s2             j = $s3

sub $t0, $s2, $s3
add $t0, $t0, $t0
add $t0, $t0, $t0
add $t0, $s1, $t0
lw $t1, 0($t0)
sw $t1, 32($s0)

4.      Para el siguiente fragmento de código en ensamblador MIPS, ¿Cuál es el correspondiente código en lenguaje C?

sll  $t0, $s0, 2          # St0 = f * 4
add $t0, $s6, $t0        # $t0 = &A[f]
sll  $t1, $s1, 2          # $t1 = g * 4
add $t1, $s7, $t1        # $t1 = &B[g]
lw   $s0, 0($t0)          # f = A[f]
addi $t2, $t0, 4
lw   $t0, 0($t2)
add $t0, $t0, $s0
sw   $t0, 0($t1)

 Solución:

Procedimiento:
f = A[f]
t2 = dA[f + 1]
t1 = dB[g]
 t0 ó A[f + 1] = A[f + 1] + A[f]
Resultado:
B[g] = A[f + 1] + A[f]

5.      La siguiente tabla muestra un arreglo de 32 bits almacenado en memoria:


Address
Data
24
2
28
4
32
3
36
6
40
1

a.    Implemente código C que ordene el arreglo.
b.    Escriba el código MIPS correspondiente al algoritmo en código C del apartado a.

Solución:

a.

Suponiendo que el arreglo fue declarado como: A[].
Y declaramos cuatro variables: a,b,c,d.

a = A[1]           b = A[2]           c = A[4]           d = A[5]          

Entonces…

A[1] = d           A[2] = a           A[4] = b           A[5] = c

b.

a = $s0                        b = $s1                        c = $s2            d = $s3                        A[] = $s4

sw $s0 , 0($s4)
sw $s1 , 4($s4)
sw $s2 , 12($s4)
sw $s3 , 16($s4)
lw $s3  , 0($s4)
lw $s0  , 4($s4)
lw $s1  , 12($s4)
lw $s2  , 16($s4)

6.      Muestre cómo el valor 0xabcdef12 es almacenado en memoria.
a.      En un esquema de memoria Little Endian.
b.      En un esquema de memoria Big Endian.

Solución:

a.

En Little Endian el valor menos significante en este caso, 12, se almacena en la posición más pequeña:

Dirección
0000
0001
0010
0011
Byte
12
ef
cd
ab

b.

En Big Endian el valor más significante en este caso, ab, se almacena en la posición más pequeña:

Dirección
0000
0001
0010
0011
Byte
Ab
Cd
Ef
12

7.      Traslade el valor 0xabcdef12 a decimal.

Solución:

2 * 16^0 = 2
1 * 16^1 = 16
f (15) * 16^2 = 3840
e (14) * 16^3 = 57,344
d (13) * 16^4 = 851,968
c(12) * 16^5 = 12,582,912
b(11) * 16^6 = 184,549,376
a(10) * 16^7 = 2,684,354,560

Sumando todos los resultados: 2,882,400,018

8.      Traslade el siguiente código MIPS a código C.

addi      $t0, $s6, 4
add $t1, $s6, $zero
sw   $t1, 0($t0)
lw   $t0, 0($t0)    
add  $s0, $t1, $t0

Solución:

Procedimiento:

t0 = dA[1]
t1 = A[0]
A[1] = A[0]
t0 = A[1]
s0 = A[0] + A[1]

Resultado:

A[1] = A[0]
b =  A[0] + A[1]
9.      Traduzca el código MIPS del ejercicio anterior en su correspondiente versión de código máquina, identificando cada uno de sus campos.

Solución:

Op
rs
rt
rd
add/sha/con
funct
001000
10110
01000
0000000000000100
000000
10110
00000
01001
n.a
100000
101011
01000
01001
0000000000000000
100011
01000
01000
0000000000000000
000000
01001
01000
10000
n.a
100000


10.  Asuma que los registros $s0 y $s1 contienen respectivamente los valores 0x80000000 y 0xD0000000:

a.      ¿Cual es el valor de $t0 para el siguiente código ensamblador?

                    add $t0, $s0, $s1

b.      ¿El contenido de $t0 es el resultado de la operación, o hay overflow?

c.       ¿Cuál es el contenido de $t0 para el siguiente código MIPS?

                    add $t0, $s0, $s1
                    add  $t0, $t0, $s0
Solución:

a.
Convertimos los valores hexadecimales a decimales:

0 = 0000
0 = 0000
0 = 0000
0 = 0000
0 = 0000
0 = 0000
0 = 0000
0 = 0000
8 = 1000                     Resultado: = 1000 0000 0000 0000 0000 0000 0000 0000

0 = 0000
0 = 0000
0 = 0000
0 = 0000
0 = 0000
0 = 0000
0 = 0000
0 = 0000
D = 1011                     Resultado: = 1101 0000 0000 0000 0000 0000 0000 0000

Debido a que estamos usando una operación add y el tamaño de los valores supera el limite, de los registros de 32bits, se da el caso de un overflow, asi que no hay ningún valore en el registro $t0.

b.

Se da el caso de un overflow.

c.

Sucede los mismo que el inciso a, se da el caso de un overflow y no hay valor para $t0.

11.  Traduzca la siguiente instrucción máquina a ensamblador ¿De qué tipo es?

                       
0000 0010 0001 0000 1000 0000 0010 00002

Solución:

Dado que los primeros 6 bits son 0s, solo puede ser una instrucción de tipo R.

Por lo tanto, podemos analizar libremente los últimos  6 bits para identificar su función:

100000 – Equivale al número de función de la instrucción add.

000000
10000
10000
10000
00000
100000
add
$s0
$s0
$s0
n.a
32

add $s0, $s0, $s0

12.  Proporcione la representación hexadecimal de la siguiente instrucción:

sw $t1,32($t2)

Solución:

Primero lo convertimos a binario:

sw – 101011
$t2 – 01010
$t1 – 01001
32 - 0000000000100000

Agrupamos en grupos de cuatro:

1010 1101 0100 1001 0000 0000 0010 0000

Convertimos la representación binaria a hexadecimal: 0xAD490020

13.  Proporcione el tipo, instrucción en lenguaje ensamblador, y la representación binaria de la instrucción descrita por los siguientes campos de instrucción MIPS:

op = 0, rs = 3, rt = 2, rd = 3, shamt = 0, funct = 34

Solución:

Que sea la función número 34 y traducido a hexadecimal Ox22, si consultamos con la tabla encontramos que es la operación “sub”.

Traduciendo a lenguaje ensamblador:

rs = 3, equivale al registro $v1
rt = 2, equivale al registro $v0
rd = 3, equivale al registro $v1

Por lo tanto la operación en leguaje ensamblador es: sub $v1, $v1, $v0

Traduciendo a lenguaje binario:

op  = 0 a binario: 000000
rs = 3 a binario : 00101
rt = 2 a binario : 00010
rd = 3 a binario : 00101
shamt = 0 a binario: 000000
funct = 34 a binario: 100010

Ordenando: 000000001010001000101000000100010

14.  Proporcione el tipo, instrucción en lenguaje ensamblador, y la representación binaria de la instrucción descrita por los siguientes campos de instrucción MIPS:

op = 0x23, rs = 1, rt = 2, const = 0x4

Solución:

Que sea la opcode número 0x23, si consultamos con la tabla encontramos que es la operación “lw”.

Traduciendo a lenguaje ensamblador:

rs = 1, equivale al registro $at
rt = 2, equivale al registro $v0
const = 0x4, equivale a la constante decimal 4

Por lo tanto la operación en leguaje ensamblador es: lw $v0, 4($at)

Traduciendo a lenguaje binario:

op  = 0x23 a binario: 100011
rs = 1 a binario : 00001
rt = 2 a binario : 00010
const = 0 a binario: 00000000000000000000000000000100

Ordenando: 10001100001000100000000000000011

15.  Asuma que n un procesador MIPS, se expande la cantidad de registros de 32 a 128. ¿Cómo afectaría este cambio el tamaño de los campos de una instrucción MIPS  de tipo Registro?

Solución:

Los 32 registros, numerados desde 0 a 31, pueden ser escritos en binario desde 00000 a 11111, respectivamente. Si se ampliara la cantidad de registros a 128, los cinco dígitos binarios no serian suficientes para enumerar los registros a partir del numero 32. Por lo tanto seria necesario añadir dos bits, de manera que el numero binario mas alto que podríamos escribir seria 1111111, cuyo equivalente decimal es 127.

16.  Asuma los siguientes contenidos de registros:

  $t0 = 0xAAAAAAAA, $t1 = 0x12345678

a.      ¿Cuál es el contenido de $t2 después de operar las siguientes instrucciones?

               sll $t2, $t0, 8
or $t2, $t2, $t1

b.      ¿Cuál es el contenido de $t2 después de operar las siguientes instrucciones?

sll $t2, $t0, 4
andi      $t2, $t2, −1

17.  Asuma que  $t0 contiene el valor 0x00101000 ¿Cuál es el valor de $t2 después de ejecutar las siguientes instrucciones?

...
slt $t2, $0, $t0
bne $t2, $0, ELSE
j    DONE
ELSE:
addi      $t2, $t2, 2
DONE:
               ...
Solución:

Como el valor de $t0 es claramente mayor que el valor de $0 (zero) el comando slt hará que $t2 tome el valor de 1.
Luego el comando bne intenta verificar si $t2 y $0 son distintos. Como esta condición se cumple entonces la instrucción salta hacia el ELSE, donde el comando addi le suma 2 al valor de $t2.
Por lo tanto el valor final de $t2 es 3.

18.  Considere el siguiente ciclo MIPS:

...
LOOP:
slt $t2, $0, $t1
beq $t2, $0, DONE
subi      $t1, $t1, 1
addi $s2, $s2, 2
j    LOOP
DONE:
               ...
                                  
a.      Asuma que el registro $t1 es inicializado a 10. ¿Cuál es el valor del registro $s2 asumiendo que $s2 es inicializado a cero?

Solución:

Asumiendo que subi existe, ya que de lo contrario esto causaría un error de compilación:

Hacemos una tabla donde anotamos los valores de cada registro al FINAL de un ciclo.

Numero de Ciclo
Valor de $t1
Valor de $t2
Valor de $s2
1
9
1
2
2
8
1
4
3
7
1
6
4
6
1
8
5
5
1
10
6
4
1
12
7
3
1
14
8
2
1
16
9
1
1
18
10
0
1
20
11
0
0
20

El valor final del registro $s2.

19.  Implemente en código ensamblador el siguiente código escrito en C: