- Instrucciones
- Atascos
- Punto Flotante
- Subrutinas
- E/S
- Fases de una instrucción
- Codigos
- Set de instrucciones
Veamos cómo nos manejamos con variables.
- Para empezar, todas las operaciones aritméticas-lógicas deben hacerse con registros
- Si quiero usar variables, debo cargarlas antes en un registro
- Las variables se definen en un bloque .data y arrancan en la dirección 0
- Para tomar una variable se usa LD "DESTINO", "VARIABLE" (DESPLAZAMIENTO).
.data ; LD R1, A (R0)
A: .word 5 ; R1 = variable en dir. de A + 0
B: .word 8
.code
LD R1, A(R0)
- Para guardar un valor en memoria se utiliza el mismo mecanismo de desplazamiento
- La sintaxis es SD "REGISTRO ORIGEN", "VARIABLE" (DESPLAZAMIENTO)
.data ; SD R1, A (R0)
A: .word 0 ; RES = VALOR DE R1 EN VARIABLE EN DIR. DE A + 0
.code
DADDI R1, R0, 5
SD R1, A(R0)
Se pueden dar en situaciones que impiden a la siguiente instruccion que se ejecute en el ciclo que le corresponde.
Los queremos evitar a toda costa. Si tenemos atascos significa que una instruccion esta estancada esperando algo de una instruccion anterior.
Provocados por conflicto de recursos.
En el MIPS sucede cuando dos instrucciones intentan acceder a la etapa MEM simultáneamente.
Dos instrucciones listas para pasar a la etapa de memoria. Se produce un atasco estructural y solo pasa una de ellas Tiene prioridad la primera instruccion que entró en el cause |
Significa: Read After Write.
- Se produce cuando una instrucción necesita leer un dato que todavía no esta disponible.
- Si no nos queda de otra, podemos usar un NOP
Por software | Con NOPS | Ordenando Sentencias |
.data
NUM1: .word 15
.code
DADDI R1, R0, 8
DADDI R2, R1, 10
LD R7, NUM1 (R0)
HALT |
.data
NUM1: .word 15
.code
DADDI R1, R0, 8
NOP
NOP
DADDI R2, R1, 10
LD R7, NUM1 (R0)
HALT
|
.data
NUM1: .word 15
.code
DADDI R1, R0, 8
LD R7, NUM1 (R0)
DADDI R2, R1, 10
HALT
|
- WAR Write After Read y WAW Write After Write (estan pero tengo idea del para que)
POR HARDWARE Si ya tenemos los valores necesarios, podemos "adelantarlos"
En estos buffers se almacena los valores para que los puedan usar en las próximas instrucciones. De esta manera no hace falta esperar a las etapas MEM y WB para usar los valores!. Este adelantamiento de operando lo llamamos Forwarding. Los buffers no se pueden ver en el simulador. Una ventaja que tiene esta técnica es que nos permite postergar la "necesidad" de los operandos.
¿Cuando conviente tenerlo activado? Siempre es conveniente tenerlo activado (en la mayoria de los casos)
Se producen cuando:
- Hay dependencia de datos entre dos instrucciones (igual que RAW)
- Una instrucción puede sobrepasar a una instrucción anterior, queriendo escribir un registro pendiente de lectura
(WAR)
o escritura(WAW)
- El simulador produce atascos cuando detecta una situación potencial (Puede que realmente no suceda) de dependencia WAR o WAW.
Ejemplo 1 | Ejemplo 2 |
.data
n1: .double 9.13
n2: .double 6.58
res1: .double 0.0
res2: .double 0.0
.code
L.D F1, n1 (R0) ; F1 = n1
L.D F2, n2 (R0) ; F2 = n2
ADD.D F3, F2, F1 ; F3 = F2 + F1
MUL.D F4, F2, F1 ; F4 = F2 * F1
S.D F3, res1 (R0) ; Guarda la suma en res1
S.D F4, res2 (R0) ; Guarda la mult en res2
HALT |
.data
n1: .double 9.13
n2: .double 6.58
res1: .double 0.0
res2: .double 0.0
.code
L.D F1, n1 (R0) ; F1 = n1
L.D F2, n2 (R0) ; F2 = n2
ADD.D F3, F2, F1 ; F3 = F2 + F1
MUL.D F2, F2, F1 ; F2 = F2 * F1
MUL.D F4, F2, F1 ; F4 = F2 * F1
S.D F3, res1 (R0) ; Guarda la suma en res1
S.D F4, res2 (R0) ; Guarda la mult en res2
HALT |
Provocados al esperar la decisión de otra instrucción anterior (por ejemplo: si se realiza o no un salto).
Tenemos dos tipos de saltos
- Incondicionales: Salta siempre.
- Condicionales: Salta dependiendo de que se cumpla una condición.
Definicion: Atasco de salto, es un salto que tuvimos que haber hecho y no lo hicimos. (Empezamos a ejecutar una instrucción que no debiamos ejecutar). El programa se da cuenta, corta la ejecución y abajo comienza la sentencia que tendria que haber ejecutado.
Ejemplo:
Ejemplo | Estadisticas |
.data
B: .word 5
.code
DADDI R1, R0, 1
LD R2, B (R0)
LOOP: DSLL R1, R1, 1 ; Desplazo a la izquierda
DADDI R2, R2, -1 ; Cant. de desplazamientos que faltan
BNEZ R2, LOOP ; Si no es 0 salto a LOOP
HALT
|
Al igual que los atascos de dependencia de datos, tenemos diferentes técnicas para evitar los atascos por saltos.
La primera se denomina Branch Target Buffer que consiste en tener un flag que indica que si se debe saltar incondicionalmente o no dependiendo de qué hizo antes (es decir, predice).
Cada vez que ese flag/buffer se actualiza cuenta como un atasco de salto! Cada vez que le erramos a la predicción cuenta como atasco.
Misprediction (nuevo atasco): Es un atasco que me va a contabilizar cuando el BTB este equivocado
Ejemplo:
¿Cuando conviene activar el BTB?
Como solo fallamos al principio y al final de loop, conviene usarlo cuando tenemos bucles muy grandes. (sin condiciones de por medio)
Buenoooo | Maloooo |
for i:=1 to 100000000 do
begin
// Hace algo
end; Vamos a tener dos atascos al principio y dos atascos al final |
for i:=1 to 100000000 do
begin
if i es par then
// Hace algo
else
// Hace otro algo
end; Vamos a tener dos atascos en cada interación (porque vamos a fallar la predicción) |
Es la otra técnica. Simplemente consiste en ejecutar SIEMPRE la siguiente instrucción a un salto
Programa | Definicion |
DADDI R2, R0, 3
LOOP: DADDI R2, R2, -1
BNEZ R2, LOOP
HALT Se terminaría el programa ya que se ejecuta el HALT en la primera interación! |
|
Ejemplo Mal | Delay Slot Activado |
.data
cant: .word 8
datos: .word 1, 2, 3, 4, 5, 6, 7, 8
res: .word 0
.code
DADD R1, R0, R0 ; Inicializa R1 = 0
LD R2, cant (R0) ; R2 = cant
LOOP: LD R3, datos (R1) ; R3 = elemento de datos en la posición R1
DADDI R2, R2, -1 ; Resta 1 a la cantidad de elementos a procesar
DSLL R3, R3, 1 ; Multiplica por dos el elemento actual
SD R3, res (R1) ; Almacena el resultado en la tabla de resultados
DADDI R1, R1, 8 ; Avanza a la siguiente posición
BNEZ R2, LOOP ; Si quedan elementos sigo iterando
NOP
HALT
|
|
Ejemplo Bien | Delay Slot Activado |
.data
cant: .word 8
datos: .word 1, 2, 3, 4, 5, 6, 7, 8
res: .word 0
.code
DADD R1, R0, R0 ; Inicializa R1 = 0
LD R2, cant (R0) ; R2 = ca
LOOP: LD R3, datos (R1) ; R3 = elemento de datos en la posición R1
DADDI R2, R2, -1 ; Resta 1 a la cantidad de elementos a procesar
DSLL R3, R3, 1 ; Multiplica por dos el elemento actual
SD R3, res (R1) ; Almacena el resultado en la tabla de resultados
BNEZ R2, LOOP ; Si quedan elementos sigo iterando
DADDI R1, R1, 8 ; Avanza a la siguiente posición
HALT
|
MIPS utiliza IEE 754 para números en punto flotante.
Contamos con 32 registros: desde F0 (simpre vale 0) hasta F31
Tiene un único tipo de dato que es el .double
NO todas las etapas tardan lo mismo.
- Generales = 1 ciclo
- Multiplicar en Pto. F. = 7 ciclos
- Sumar en Pto. F. = 4 ciclos.
- Dividir en Pto. F. = 24 Ciclos
Esta arquitectura nos permite tener múltiples instrucciones en la etapa EX
Podemos ejecutar múltiples instrucciones en menos tiempo!
No todo es color de rosas. Introduce los siguientes atascos:
- Dependencia Estructural
- Dependencia de datos WAR
- Dependencia de datos WAW
IMPORTANTE
- No hay manejo implícito de la pila!
- La dirección de retorno siempre estará en R31
- Esto si se hace implícitamente en el JAL
- Los registros se pueden sobrescribir , incluído R31
- Vamos a tener que salvarlos en la pila
CONVENCIONES
Las subrutinas deben garantizar el guardado de los registros que correspondan. De esta manera, una subrutina podrá llamar a otra sabiendo que esta no modificará el valor de estos registros, para poder mantener esta garantía, es necesario guardar los registros en la pila. (PERO MIPS NO TIENE PILA 😧). Pero existe un registro que por convención todas las subrutinas usarán como puntero al tope de la pila. Y ese registro es.........
El registro $sp
PUSH $t1 | POP $t1 |
daddi $sp, $sp, -8 ; "Subo" una celda de memoria
sd $t1, 0 ($sp) ; Almaceno el dato |
ld $t1, 0 ($sp) ; Extraigo el dato
daddi $sp, $sp, 8 ; "Bajo" una celda de memoria |
Como ejemplo | Como lo vamos a usar |
.data
NUM1: .word 5
NUM2: .word 8
RES: .word 0
.code
LD R1, NUM1 (R0)
LD R2, NUM2 (R0)
JAL SUMAR ; Llama a la subrutina
SD R3, RES (R0)
HALT
SUMAR: DADD R3, R1, R2
JR R31 ; Retorna al punto donde fue llamado |
.data
NUM1: .word 5
NUM2: .word 8
RES: .word 0
.code
LD $a0, NUM1 (R0)
LD $a1, NUM2 (R0)
JAL SUMAR ; Llama a la subrutina
SD $v0, RES (R0)
HALT
SUMAR: DADD $v0, $a0, $a1
JR $ra ; Retorna al punto donde fue llamado |
- En verde aquellos que, en caso de usarse, deben ser salvados
- En azul aquellos que podemos sobrescribir sin ningún problema
Existen dos "registros" (es decir, dos celdas de memoria comunes)
- CONTROL sirve para enviar códigos de operaciones. (0x10000)
- DATA sirve para enviar o recibir datos. (0x10008)
Como son celdas de memoria se leen y escriben con instrucciones de memoria: LD/L.D/LBU/SD/S.D....
IMPRIMIR UN STRING
- DATA --> Direccion del string
- CONTROL --> El valor 4
IMPRIMIR UN NÚMERO
- DATA --> El Dato
- CONTROL
- 1 Imprime un entero sin signo
- 2 Imprime un entero con signo
- 3 Imprime un flotante
LIMPIAR LA PANTALLA
- CONTROL -> El valor 6
Imprimir Entero | Imprimir Double | Imprimir String |
.data
CONTROL: .word 0x10000
DATA: .word 0x10008
NUM: .word 2
.code
LD $s0, CONTROL ($0)
LD $s1, DATA ($0)
LD $t0, NUM ($0)
SD $t0, 0 ($s1)
; CONTROL = 2
DADDI $t0, $0, 2
SD $t0, 0 ($s0)
; LIMPIA LA PANTALLA
;DADDI $t0, $0, 6
;SD $t0, 0 ($s0)
HALT |
.data
CONTROL: .word 0x10000
DATA: .word 0x10008
NUM: .double 19.5
.code
LD $s0, CONTROL ($0)
LD $s1, DATA ($0)
L.D f1, NUM ($0)
S.D f1, 0 ($s1)
; Control 3
DADDI $t0, $0, 3
SD $t0, 0 ($s0)
;DADDI $t0, $0, 6
;SD $t0, 0 ($s0)
HALT |
.data
CONTROL: .word 0x10000
DATA: .word 0x10008
TEXTO: .asciiz "Hola, Mundo!"
.code
LD $s0, CONTROL($0)
LD $s1, DATA($0)
DADDI $t0, $0, TEXTO
SD $t0, 0($s1)
DADDI $t0, $0, 4
SD $t0, 0($s0)
HALT
;(1) $t0 = 4 -> función 4: salida de una cadena ASCII
;(2) CONTROL recibe 4 y produce la salida del mensaje
|
Pantalla de 50x50px
PINTAR UN PÍXEL
- DATA --> Color y coordenadas
- CONTROL --> El valor 5
LIMPIAR LA PANTALLA
- CONTROL -> El valor 7
.data ;Imprimir un pixel
CONTROL: .word 0x10000
DATA: .word 0x10008
PIXEL: .byte 0, 185, 135, 0, 23, 10, 0, 0
.code
LD $s0, CONTROL ($0) ; $s0 = CONTROL
LD $s1, DATA ($0) ; $s1 = DATA
LD $t0, PIXEL ($0)
SD $t0, 0 ($s1) ; Mando el dato a DATA
DADDI $t0, $0, 5
SD $t0, 0 ($s0) ; CONTROL = 5
HALT |
.data
coorX: .byte 24 ; X
coorY: .byte 24 ; Y
color: .byte 255, 0, 255, 0
CONTROL: .word 0x10000
DATA: .word 0x10008
.code
ld $s0, CONTROL ($zero) ; $s0 = dir de CONTROL
ld $s1, DATA ($zero) ; $s1 = dir de DATA
; limpia la pantalla
daddi $t0, $zero, 7
sd $t0, 0 ($s0)
lbu $t0, coorX ($zero)
sb $t0, 5 ($s1)
lbu $t1, coorY ($zero) ; $t1 = valor de coordenada Y
sb $t1, 4 ($s1) ; DATA + 4 recibe el valor de coordenada Y
lwu $t2, color ($zero) ; $t2 = color
sw $t2, 0 ($s1) ; Pongo color en DATA
daddi $t0, $zero, 5
sd $t0, 0 ($s0) ; Pinta el píxel
HALT |
LEER UN NÚMERO (ENTERO O FLOTANTE)
- CONTROL --> El valor 8
- DATA -->
- Muestra el caracter presionado
- Termina de leer cuando presiona ENTER
- Si el dato ingresado no es un número se guarda 0. Tomar el valor (HEXADECIMAL) con LD o L.D desde DATA
LEER UN CARACTER
- CONTROL -> El valor 9
- DATA ->
- NO muestra el caracter presionado
- No espera al ENTER
- Tomar el valor (ASCII) con LBU desde DATA
Leer Double | Leer Infinito |
.data ;leer desde teclado
CONTROL: .word 0x10000
DATA: .word 0x10008
NUM: .double 0.0
CARACTER: .byte 0
.code
LWU $s0, CONTROL ($zero) ; $s0 = CONTROL
LWU $s1, DATA ($zero) ; $s1 = DATA
DADDI $t0, $zero, 8
SD $t0, 0 ($s0) ; CONTROL = 8
L.D f1, 0 ($s1) ; Tomo número en f1
S.D f1, NUM ($zero) ; Guardo en variable
DADDI $t1, $zero, 9
SD $t1, 0 ($s0) ; CONTROL = 9
LBU $t1, 0 ($s1) ; Tomo caracter en $t1
SB $t1, CARACTER ($zero) ; Guardo en variable
HALT |
.data
CONTROL: .word 0x10000
DATA: .word 0x10008
CARACTER: .byte 0
.code
LWU $s0, CONTROL($0)
LWU $s1, DATA($0)
DADDI $s4, $0, 13 ; 13 el ascii del enter pá
LOOP: DADDI $t1, $0, 9
SD $t1,0 ($s0) ; CONTROL = 9
; *PRESIONA UNA TECLA*
; LA CPU GUARDA EL CARACTER EN DATA
LBU $t1,0 ($s1)
; COMPARO CON EL ENTER
BEQ $t1, $s4, FIN
; GUARDO LA VARIABLE
SB $t1, CARACTER($0)
;TOMO LA DIR DE CARACTER
DADDI $s3, $0, CARACTER
; MANDO DIR DE CARACTER
SD $s3, 0 ($s1)
; IMPRIMIR CARACTER
DADDI $t1, $0, 4
SD $t1, 0 ($s0) ; CONTROL 4
J LOOP
FIN: HALT |
Las instrucciones se organizan en fases de manera que esto sea posible
Fases | Funcion |
|
|
|
|
|
|
|
|
|
.data
A: .word 1
B: .word 2
.code
ld r1, A(r0)
ld r2, B(r0)
sd r2, A(r0)
sd r1, B(r0)
halt
.data
NUM1: .word 5
NUM2: .word 8
RES: .word 0
.code
LD R1, NUM1 (R0)
LD R2, NUM2 (R0)
;MULTIPLICAMOS
DMUL R3, R1, R2
;ALMACENAMOS EL RESULTADO EN RES
SD R3, RES (R0)
HALT
.data
B: .word 5
.code
DADDI R1, R0, 1
LD R2, B (R0)
LOOP: DSLL R1, R1, 1 ; Desplazo a la izquierda
DADDI R2, R2, -1 ; Cant. de desplazamientos que faltan
BNEZ R2, LOOP ; Si no es 0 salto a LOOP
HALT
.data
cadena: .asciiz "Caza"
.code
; La pila comienza en el tope de la memoria de datos
DADDI $sp, $0, 0x400 ; bus 10 bits 🡪 2^10 = 1024 = 0x400
; Guarda como primer argumento para upcaseStr
; la dirección de cadena
DADDI $a0, $0, cadena
JAL upcaseStr
HALT
upcaseStr: DADDI $sp, $sp, -16 ; Reserva lugar en pila -> 2 x 8
SD $ra, 0 ($sp) ; Guarda en pila $ra
SD $s0, 8 ($sp) ; Guarda en pila $s0
DADD $s0, $a0, $zero ; Copia la dirección de inicio de la cadena
LOOP: LBU $a0, 0 ($s0) ; Toma car. actual
BEQ $a0, $zero, FIN ; Si es el fin, termina
JAL upcase
SB $v0, 0 ($s0) ; Guarda el caracter procesado en la cadena
DADDI $s0, $s0, 1 ; Avanza al siguiente caracter
J LOOP
; Recupera los datos salvados en la pila
FIN: LD $ra, 0 ($sp)
LD $s0, 8 ($sp)
DADDI $sp, $sp, 16
JR $ra
; Pasa un caracter a mayúscula.
; Parámetros: $a0 -> caracter
; Retorna $v0 -> caracter en mayúscula
; No se utiliza la pila porque no se usan registros que deban ser salvados
upcase: DADD $v0, $a0, $zero
SLTI $t0, $v0, 0x61 ; Compara con ‘a’ minúscula
BNEZ $t0, salir ; No es un caracter en minúscula
SLTI $t0, $v0, 0x7B ; Compara con el car sig a 'z' minúscula (z=7AH)
BEQZ $t0, salir ; No es un caracter en minúscula
DADDI $v0, $v0, -0x20 ; Pasa a minúscula
salir: JR $ra ; Retorna
.data
BASE: .word 2
EXPONENTE: .word 3
.code
LD $t0, BASE($0)
LD $t1, EXPONENTE($0)
DADD $t2, $t2 , $t0
LOOP: BEQZ $t1, TERMINO
dmul $t2, $t2, $t0
DADDI $t1, $t1, -1
J LOOP
TERMINO: HALT