[2022][Febrero][Problema 2]

[2022][Febrero][Problema 2]

de Edison Steven Estramil Moreira -
Número de respuestas: 13

Buenas tardes, quisiera saber si esta bien la solución de la parte de assembler, cuando se hace la llamada recursiva estoy entendiendo que lo que se haciendo es pushear la  rama izquierda y la rama derecha del mismo arbol cuando deberia pushear la rama izquierda de ambos arboles, ademas creo que no se estaría pusehando correctamente ninguno de los dos segmentos de los arboles.


en [si + 4] y [si + 2] se encuentran el hijo izquierdo y el hijo derecho del arbol1 respectivamente, y en ES:[bx+4] y ES[bx+2] los hijos izquierdo y derecho del arbol2.

otra duda, que los punteros sean de tipo FAR significa que los hijos no pueden estar en un segmento diferente al padre no es asi?

muchas gracias!



En respuesta a Edison Steven Estramil Moreira

Re: [2022][Febrero][Problema 2]

de Federico Rivero -
Estimado,

No, que los punteros sean FAR significa que se almacenan como segmento y desplazamiento, es decir, usando 4 bytes. De este modo, la estructura nodo se compila como:

0xNNNNN -> dato
0xNNNNN+2 -> desplazamiento izq
0xNNNNN+4 -> segmento izq
0xNNNNN+6 -> desplazamiento der
0xNNNNN+8 -> segmento der

Las llamadas son correctas. Avisame si quedan dudas aún teniendo en cuenta lo de arriba!

Saludos,
Federico
En respuesta a Federico Rivero

Re: [2022][Febrero][Problema 2]

de Alexis Alfonso -
Buenas Federico, este ejercicio, en comparación con otros ejer de assembler de otros años, me dejó muchas dudas...Las voy tirando:

1- Decir que "los punteros son FAR" ¿es distinto a decir que el procedimiento o la función está en un segmento diferente (aquello de que cuando se hace 'CALL' se pushea segmento+ip de retorno)?

2- Cuando quiere saber si el árbol1 es NULL, veo que hace un OR entre seg1 y desp1. ¿Para que un puntero sea null, ambos valores deben ser 0? ¿Por qué entonces, en otras soluciones, cuando en la pila se pushea el árbol y se quiere chequear lo mismo se consulta simplemente por el desplazamiento ( cmp [ BP + x ], 0 )

3- Tal vez un poco tiene que ver con la pregunta 1 pero, cuando va a hacer las llamadas recursivas y pushea seg1,des1,seg2,des2 ¿por qué el valor de los segmentos son DS : [ SI + 4 ] y ES : [ BX + 4 ] y DS : [ SI + 8 ] y ES : [ BX + 8 ] ? ¿Los segmentos no son siempre los mismos que se pushearon en la primer llamada, y los que van cambiando son los desplazamientos dentro de ellos?

4- El haber elegido DS y ES como segmentos de cada árbol, ¿tiene que ver con que son las únicas 2 posibilidades, dado que el proc está en CS y la pila en SS? ¿No podrían haber estado ambos árboles en DS (o ES) y respetar de igual manera la restricción de ser FAR?

Disculpas por tanta pregunta, pero este ejercicio me resultó bastante diferente a lo que venía acostumbrado.


Saludos

En respuesta a Alexis Alfonso

Re: [2022][Febrero][Problema 2]

de Gustavo Brown -

Alexis,

1) El procedimiento puede estar en un segmento diferente, por ello para determinar la dirección de comienzo se debe especificar el segmento y el desplazamiento (el puntero FAR a la rutina). Cuando se trabaja con punteros NEAR sólamente se debe especificar el desplazamiento dado que el segmento está implícito.
Hay 2 variantes de la instrucción CALL, una a una rutina NEAR y otra a una FAR. La variante del CALL que llama a la rutina NEAR solamente guarda en el stack el IP actual y modifica el IP al desplazamiento asociado a la rutina a llamar. La variante FAR debe almacenar en el stack también el segmento CS actual dado que al realizar la llamada también debe setear en ese registro el segmento asociado a la rutina a llamar.
De la misma manera hay 2 versiones de la instrucción RET, una que es para rutinas NEAR y otra para rutinas FAR. El ensamblador elije la versión adecuada de los CALL y RETs automáticamente ya que el programador indica para cada rutina si es FAR.

2. Para que un puntero FAR sea NULL ambos componentes (segmento y desplazamiento) deben ser NULL. Para que un puntero NEAR sea NULL solamente hay que chequear el desplazamiento (el segmento está implícito).
En otras soluciones se chequea únicamente una palabra porque son punteros NEAR. Cada problema debe indicar de alguna manera de se trabajará con punteros FAR. Si no se dice nada se considera NEAR.


3. Lo que se pushea son los punteros de cada nodo. Como el problema usa punteros FAR debe pushear segmento y desplazamiento y cada nodo puede tener distinto segmento y desplazamiento. Para cada problema que use punteros FAR hay que ver de donde se obtiene el segmento de cada dato FAR de la misma manera que hacer con los desplazamientos.

4. El uso de los registros de segmento DS y ES es a solo efecto de acceder a memoria a leer datos y se pueden ver como registros auxiliares en este contexto. Podrías perfectamente utilizar DS para ambos accesos siempre y cuando actualices el valor de DS al segmento adecuado antes de realizar el acceso a memoria.


Saludos,
  Gustavo

En respuesta a Gustavo Brown

Re: [2022][Febrero][Problema 2]

de Gianfranco Caton Stefanoli Ortiz -
Buenas, respecto a este ejercicio también surge la duda que en la solución se hace un push con memoria
pero en las diapositivas la lógica de la instrucción implicaría hacer un MOV de memoria a memoria
¿Esto se puede hacer o es un error?
En respuesta a Gianfranco Caton Stefanoli Ortiz

Re: [2022][Febrero][Problema 2]

de Gustavo Brown -
Lo que no permite el formato de instrucción del 8086 son instrucciones con dos parámetros que sean a memoria (no se puede codificar). Pero sí permite instrucciones que realizan más de un acceso a memoria a partir de un parámetro.
Por ejemplo los PUSH de la solución del examen se pueden hacer (solo tienen un parámetro a memoria). También se podría por ejemplo incrementar una variable que esté en memoria (ej: INC WORD PTR DS:[BX] ).
Un ejemplo de instruccion que no se puede codificar es MOV con 2 parámetros a memoria.

No se confundan con las notas de las diapositivas, donde dice "lógica" tiene pseudocódigo que intenta explicar el funcionamiento de la instrucción.

Saludos,
Gustavo
En respuesta a Gustavo Brown

Re: [2022][Febrero][Problema 2]

de Joel Cabrera Dechia -
Hola! 

Espero anden bien.

No me quedan muy claros los punteros apuntando a NULL.
 
La primera duda, que es media básica pero me la quiero sacar de arriba; NULL es 0 en 8086? O sea para chequear si apunta NULL comparamos con 0?

Luego algo que me hace ruido, en el caso de los punteros NEAR y que NULL sea 0, para saber si apunta a NULL debería ver si el desplazamiento es 0, pero no podría pasar que se apunte al inicio del segmento? O esa no sería la interpretación?

Saludos,
Joel
En respuesta a Joel Cabrera Dechia

Re: [2022][Febrero][Problema 2]

de Gonzalo Tejera -

Hola Joel, espero estés bien.

El concepto de "NULL" está definido en los lenguajes de alto nivel, en asembler no está definido. En el marco de este curso se usa 8086 como arquitectura objetivo por lo que desde el punto de vista del programador todas las direcciones de memoria son segmentas. En cada letra de ejercicio se indica cómo se deben interpretar los punteros, y a partir de ello como se genera la dirección segmentada para acceder a la estructura.

Sobre la pregunta puntual:

  • "en el caso de los punteros NEAR y que NULL sea 0, para saber si apunta a NULL debería ver si el desplazamiento es 0" Es correcto.
  • "no podría pasar que se apunte al inicio del segmento" Sí, si usas segmento:[0] apuntas al inicio del segmento
Saludos, Gonzalo


En respuesta a Joel Cabrera Dechia

Re: [2022][Febrero][Problema 2]

de Gustavo Brown -

Joel,

  NULL es la constante 0.

Cuando se asigna un puntero a NULL en 8086:

  • Si se trabaja con punteros NEAR entonces el OFFSET vale 0
  • Si se trabaja con punteros FAR entonces el SEGMENT y el OFFSET ambos valen 0

Cuando se trabaja con punteros que manejan el valor NULL hay un valor que no se puede utilizar como puntero válido. Si es un puntero NEAR es el que tiene desplazamiento 0. Si es un puntero FAR es el que tiene tanto segmento como desplazamiento en 0.

Para chequear si un puntero es NULL en 8086:

  • Si se trabaja con punteros NEAR se compara el offset con 0
  • Si se trabaja con punteros FAR se compara tanto el segmento como el offset con 0. En este caso ambos tienen que valer 0 para considerar el puntero como NULL

Saludos,
   Gustavo

En respuesta a Gustavo Brown

Re: [2022][Febrero][Problema 2]

de Juan Martin Nuñez Pena -

Buenas, una consulta sobre la explicación de como ver si un puntero es NULL en 8086.

Si se trabaja con punteros NEAR, lo que yo debo comparar con 0 seria, suponiendo que en BX tengo cargado el desplazamiento:

CMP BX, 0x0 

o seria 

CMP [BX], 0x0

Y por otro lado, si estoy trabajando con punteros FAR debo chequear que el segmento es igual a 0 o que en, suponiendo que el segmento lo tengo en ES:

CMP ES:[BX], 0x0?

Gracias.

En respuesta a Juan Martin Nuñez Pena

Re: [2022][Febrero][Problema 2]

de Federico Rivero -

Hola Juan Martín!

CMP BX, 0x0 compara el valor de BX con 0, mientras que CMP (byte ptr/word ptr) [BX], 0x0 compara el contenido de la dirección de memoria en la dirección DS:[BX] con 0. Entendiendo esas dos sentencias, la pregunta ya no es de arquitectura sino de programación. Verificar que un puntero es NULL implica verificar que la variable puntero es NULL, en este caso: BX. La otra sentencia (CMP (byte ptr/word ptr) [BX], 0x0) lo que compara es el valor apuntado por el puntero, que no es lo que querés.

En el caso de trabajar con un puntero FAR, tenés que verificar que tanto el segmento como el desplazamiento sean 0. Es decir, la comparación sería:

CMP BX, 0

JNZ noesnull

CMP ES, 0

JZ esnull       

       

Nuevamente, la sentencia  CMP (byte ptr/word ptr) ES:[BX], 0x0, lo que compara es el contenido de lo apuntado por el puntero, con 0.

Por último, quiero aclarar que cuando escribís CMP word ptr [BX], 0, , el segmento DS está implícito en la instrucción, es decir que la instrucción es equivalente a CMP word ptr DS:[BX], 0. Lo aclaro porque me dio la impresión de que quizás pensabas que omitir el segmento implicaba que no había segmento y que eso lo volvía un puntero NEAR.

Si quedan dudas por favor preguntá otra vez.

Saludos!
Federico

En respuesta a Federico Rivero

Re: [2022][Febrero][Problema 2]

de Facundo Martin Barboza Fernandez -
Buenas,
Retomo el hilo ya que me surgieron dudas en el ejercicio.
No me estaría quedando claro porque antes de hacer los call a la funcion se hace push de esos elementos.
Este es el codigo de la solucion:
push [SI+4]
push [SI+2]
push ES:[BX+4]
push ES:[BX+2]
call arboles_iguales
pop AX ; AX=arboles_iguales(arbol1->izq, arbol2->izq)
cmp AX, 0
je ret0
push [SI+8]
push [SI+6]
push ES:[BX+8]
push ES:[BX+6]
call arboles_iguales
pop AX ; AX=arboles_iguales(arbol1->der, arbol2->der)
jmp fin

Entiendo que se esta pasando el segmento y el desplazamiento, o eso creo. Pero me deja dudas en como es que hace referencia al izq o der.
Agradecería si me pueden aclarar esa idea.
Muchas gracias! saludos.
En respuesta a Facundo Martin Barboza Fernandez

Re: [2022][Febrero][Problema 2]

de Belen Brandino -
hola,
como dice la letra, los parámetros se reciben por stack. Entonces, cuando se llama la función arboles_iguales, uno debe asumir que los parámetros de la función (nodo* arbol1, nodo* arbol2) se encuentran en el stack. Como en este caso la función la estás llamando vos (recursivamente) tenés que pasarle los parámetros correspondientes. Observar que la primera llamada a la función arboles_iguales, alguien te va a dar los parámetros, como dice la letra (El programa que va a invocar esta función lo hace con el siguiente pseudocódigo: ...).

Entonces, en cada llamada recursiva de árboles iguales es necesario pushear en el stack los punteros a los subárboles correspondientes (nodo* arbol1, nodo* arbol2). En este caso sería
return (arboles_iguales(arbol1->izq, arbol2->izq) && arboles_iguales(arbol1->der, arbol2->der));
donde primero tenes que pushear las ramas izquierdas de ambos árboles y hacer la llamada de la función, y luego pushear las dos ramas derechas y llamar la función nuevamente.

Luego, sabemos que el nodo tiene la siguiente estructura:
typedef struct {
short valor;
nodo* izq, der;
} nodo;
Esto en memoria se vería así:
Representación gráfica de un nodo en memoria
Donde podes ver el valor, el puntero izq y luego el der, representados como punteros FAR, que significa que se encuentran en otro segmento y por ende necesitamos el segmento y el desplazamiento (cuando el puntero es NEAR el puntero está en el mismo segmento y solo se necesita el desplazamiento para representarlo). 
En la llamada inicial al código, y después de pushear BP, tenemos los dos punteros a los árboles originales en el stack, así:
Imagen del stack
y con el código:
mov BX, [BP+4] ;        BX = desplazamiento arbol 2
mov AX, [BP+6] ;        AX = segmento arbol 2
mov SI, [BP+8] ;          SI = desplazamiento arbol 1
mov CX, [BP+10] ;      CX = segmento arbol 1
mov DS, CX ;               DS = segm arbol1
mov ES, AX;                 ES = segm arbol2
se guardan los punteros a estos árboles.

En la llamada recursiva arboles_iguales(arbol1->izq, arbol2->izq) debemos pushear en el stack los parámetros necesarios (los punteros a los dos árboles) de forma que queden como en la última imagen, que es como los espera nuestra función. Primero se pushea la rama izquierda del árbol1, pusheando primero el segmento y luego el desplazamiento. Sabemos que en DS:[SI] se encuentra árbol 1 (DS=segm arb1, SI=desp arb1). Por la imagen 1, sabemos como se dispondrá en memoria este árbol, y por ende sabemos que en DS:[SI+2] se encuentra el desplazamiento del árbol izquierdo y en  DS:[SI+4] el segmento, entonces se hace:
PUSH [SI+4];        segmento arb1-> izq (DS segmento implícito)
PUSH [SI + 2];      desplazamiento arb1->izq

Para el arbol 2 es lo mismo, sabemos que en ES:[BX] se encuentra árbol 2 (ES=segm arb2, BX=desp arb2). Sabemos que en ES:[BX+2] se encuentra el desplazamiento del árbol izquierdo y en  ES:[BX+4] el segmento, entonces se hace:
PUSH ES:[BX+4];        segmento arb2-> izq (DS segmento implícito)
PUSH ES:[BX + 2];      desplazamiento arb2->izq

la llamada a arboles_iguales(arbol1->der, arbol2->der) es análoga, mirando el dibujo en la memoria (en vez de ser +4 y +2 va a ser +8 y +6) por que acceso a las ramas derechas

espero que quede claro, sino preguntá de nuevo
saludos!