Ir al contenido

Unidad 2

En esta unidad descubrirás cómo se traducen algunos conceptos fundamentales de la programación en alto nivel, como condicionales, ciclos, punteros, funciones y arreglos, a lenguaje ensamblador. Utilizarás el lenguaje Hack de la unidad anterior para implementar estos conceptos. Esta unidad servirá como puente para la siguiente unidad, donde exploraremos algunos conceptos fundamentales utilizando el lenguaje de programación C++.


Criterio (peso)Cumple plenamente (5.0)Se cumple medianamente (4.0)Problemas importantes (3.0)Falta comprensión básica (2.0)No hay evidencia (0.0)
1. Aplicación + bitácora (40%)La app se ejecuta sin fallos en el entorno acordado. Evidencia completa y verificable en bitácora. Todo consistente con lo mostrado en la demo.La app funciona y cumple lo esencial. La bitácora permite verificar, pero hay 1–2 vacíos menoresLa app funciona parcialmente o depende de condiciones no declaradas. Bitácora con vacíos importantes o incompleta.La app no corre o no demuestra lo requerido. La bitácora no permite verificación de la app.No se entregaron evidencias o no se puede acceder a ellas
Evaluación
2. Sustentación (60%)Responde a las preguntas con precisión, conectando: (a) lo que se ve, (b) cómo está hecho, y (c) por qué. Usa su bitácora para justificar decisiones. Reconoce límites/errores y propone cómo probar/mejorar.Respuestas correctas pero con imprecisiones menores o justificación superficial. Usa parcialmente la bitácora para sustentar.Responde solo “qué hizo” pero le cuesta explicar “cómo” o “por qué”. Necesita guía para conectar con su propia evidencia/bitácora.No logra responder de forma coherente o responde sin relación con lo presentado/documentado. Evidencia falta de comprensión básica del trabajo entregado.No se entregaron evidencias o no se puede acceder a ellas
Evaluación


Set: ¿Qué aprenderás en esta unidad? 💡

Sección titulada «Set: ¿Qué aprenderás en esta unidad? 💡»

En esta unidad aprenderás la relación entre el lenguaje ensamblador y un lenguaje de alto nivel como C++. Aprenderás a traducir conceptos de alto nivel a bajo nivel, y viceversa. Además, desarrollarás programas que implementan estos conceptos en el lenguaje ensamblador del computador Hack. Vas a explorar estos conceptos usando el simulador del Hack. Y por favor, SIEMPRE SIMULA y aplica la metodología de predice, ejecuta, observa y reflexiona.

Vamos a resolver juntos este problema:

La pantalla del computador Hack se controla a través de un mapa de memoria que comienza en la dirección 16384 (SCREEN). Cada bit en este mapa de memoria representa un pixel en la pantalla (1 = negro, 0 = blanco). Escribe un programa que dibuje un punto negro en la esquina superior izquierda de la pantalla. (Recuerda que la esquina superior izquierda corresponde al primer bit del primer word en la dirección SCREEN).

Vamos a resolver juntos este problema:

Modifica el programa anterior para que dibuje una línea horizontal negra de 16 pixeles de largo en la esquina superior izquierda de la pantalla. (Recuerda que cada word en la memoria representa 16 pixeles).

Modifica el programa de la actividad anterior de tal manera que puedas mover la línea horizontal de derecha a izquierda usando las teclas d y i respectivamente. Tu programa no tiene que verificar si la línea se sale de la pantalla.

Enunciado: considera el siguiente programa:

//Adds 1+...+100.
int i=1;
int sum=0;
while(i <=100){
sum+= i;
i++;
}
Una posible traducción a ensamblador
// Adds1+...+100.
@i // i refers to some memory location.
M=1 // i=1
@sum // sum refers to some memory location.
M=0 // sum=0
(LOOP)
@i
D=M // D=i
@100
D=D-A // D=i-100
@END
D;JGT // If(i-100)>0 gotoEND
@i
D=M // D=i
@sum
M=D+M // sum=sum+i
@i
M=M+1 // i=i+1
@LOOP
0;JMP // GotoLOOP
(END)
@END
0;JMP // Infinite loop

Vamos a transformar este programa a su equivalente usando un ciclo for:

//Adds 1+...+100.
int sum=0;
for(int i = 1; i <=100; i++){
sum+= i;
}

Un puntero es una variable que almacena la dirección de memoria de otra variable. Observa el siguiente programa escrito en C++:

int a = 10;
int* p;
p = &a;
*p = 20;

El programa anterior modifica el contenido de la variable a por medio de la variable p. p es un puntero porque almacena la dirección de memoria de la variable a. En este caso el valor de la variable a será 20 luego de ejecutar *p = 20;.

Ahora analiza con detenimiento:

  • ¿Cómo se declara un puntero en C++?
int* p;

p es una variable que almacenará la dirección de otra variable. Dicha variable almacenará número enteros.

  • ¿Cómo se define (nota que antes preguntamos cómo se declara) un puntero en C++?
p = &a;.

Definir el puntero es inicializar el valor del puntero, es decir, guardar la dirección de una variable. En este caso p contendrá la dirección de a o podemos decir que p apunta a a

  • ¿Cómo se almacena en C++ la dirección de memoria de una variable? Con el operador &.
p = &a;
  • ¿Cómo se escribe el contenido de la variable a la que apunta un puntero? Con el operador *.
*p = 20;

En este caso como p contiene la dirección de a. Por tanto, se está modificando el valor de la variable a por medio de p.

Ahora vas a usar un puntero para leer la posición de memoria a la que este apunta, es decir, vas a leer por medio del puntero la variable cuya dirección está almacenada en él.

int a = 10;
int b = 5;
int *p;
p = &a;
b = *p;

En este caso:

b = *p;

el código anterior hace que el valor de b cambie de 5 a 10 porque p apunta a a y con *p a la derecha del igual estás leyendo el contenido de la variable apuntada.

Los arreglos son colecciones de datos en la memoria.

Considera el siguiente programa

int arr[] = {1,20,13,24,55,96,87,83,98,102};
int sum = 0;
for (int j = 0; j < 10; j++) {
sum = sum + arr[j];
}

Considera el siguiente programa en C++ que utiliza funciones:

#include <iostream>
int result = 0;
int sum(int a, int b)
{
return a + b;
}
int main()
{
result = sum(3, 4);
std::cout << "The sum: " << result << std::endl;
}
Hack code
(start)
//int result = 0;
@result
M=0
/*
int main()
{
result = sum(3, 4);
std::cout << "The sum: " << result << std::endl;
}
*/
// Load sum arguments
@3
D=A
@R0
M=D
@4
D=A
@R1
M=D
// Save return address
@returnFromSum
D=A
@R15
M=D
// call sum
@sum
0;JMP
// return after sum
// and store result
(returnFromSum)
@R0
D=M
@result
M=D
@fin
(fin)
0;JMP
/*
int sum(int a, int b)
{
return a + b;
}
*/
(sum)
@R0
D=M
@R1
D=D+M
@R0
M=D
@R15
A=M
0;JMP

En este puntos ya has aprendido a manipular direcciones de memoria por medio de punteros, a usar arreglos y a implementar funciones en lenguaje ensamblador.

En esta actividad de aplicación te pediré que integres todo lo que sabes.

Vas a resolver dos problemas de traducción de C++ a ensamblador. Ten presente que en ambos casos la expresión std::cout no la vas a implementar en ensamblador. Esta expresión solo está para que entiendas el flujo del programa en C++.

El protocolo de llamado a función será: los argumentos de una función se pasan en los registros R0, R1, R2, etc. El valor de retorno se pasa en R0. La dirección de retorno se guarda en R15. Si necesitas variables temporales adicionales, puedes usarlas en R13 y R14.

Problema 1:

#include <iostream>
void swap(int* pa, int* pb){
int tmp = *pa;
*pa = *pb;
*pb = tmp;
}
int main()
{
int a = 10;
int b = 20;
std::cout<<"a: " << a <<" b: "<< b<< std::endl;
swap(&a,&b);
std::cout<<"a: " << a <<" b: "<< b<< std::endl;
return 0;
}

Problema 2:

#include <iostream>
int calSum(int* parr,int arrSize){
int sum = 0;
for(int i= 0; i < arrSize;i++){
sum = sum + *(parr+i);
}
return sum;
}
int main()
{
int arr[] = {10,15,2,3,50};
int sum = calSum(arr,5);
std::cout<<"Sum: " << sum << std::endl;
return 0;
}

Reflect: Consolidación y metacognición 🤔

Sección titulada «Reflect: Consolidación y metacognición 🤔»

En esta actividad de reflexión te voy a pedir que consolides lo aprendido en estas dos unidades que llevamos del curso mediante un RETO final. Se trata de implementar un programa en lenguaje ensamblador que DIBUJE un mapa de bits en la pantalla del computador Hack.

Para realizar tu diseño o el dibujar el mapa de bits usarás la herramienta Bitmap Editor. Esta herramienta te permitirá generar el código en lenguaje ensamblador. El código generado será una función, que dibujará tu diseño y que deberás llamar desde el programa principal. El programa principal deberá leer las teclas d y e que usará para dibujar y borrar respectivamente el mapa de bits.