Declaracion de segmentos en TASM

Iniciado por Usuario887, 2 Mayo 2020, 17:53 PM

0 Miembros y 1 Visitante están viendo este tema.

RayR

Los segmentos siempre están alineados a un párrafo. Lo que ponemos en CS, DS, etc. no son direcciones de memoria, sino números de segmento, y su dirección se calcula multiplicándolos por 16, (o desplazándolos 4 bits a la izquerda). Por ejemplo, cuando el valor de CS es 0x0020, la dirección real del segmento es 0x00200. Al especificar BYTE, WORD, etc. al definir un segmento, estamos indicando cómo queremos que esté alineada la dirección donde comienzan las instrucciones o datos contenidos en él, pero el segmento en sí siempre estará en dirección múltiplo de 16.

En el ejemplo que pusiste, vemos que ref fue colocado en la dirección 0x07100 (segmento 0x0710) y contiene un sólo byte, por lo que termina en 0x07101. Luego tienes seg1, con alineación WORD, por lo que se busca la primera dirección disponible que sea múltiplo de 2 (en este caso, 0x07102) y ahí se coloca su contenido. Todo esto hace que casi no se desperdicie memoria, pero ¿que pasa con seg1 en sí? Como 0x07102 no es múltiplo de 16, lo que se hace es tomar la dirección múltiplo de 16 inmediatamente anterior. En este ejemplo será 0x07100, lo cual hará que ref y seg1 estén en el mismo segmento "real", pero para que no se superpongan sus datos, el byte de seg1 no tendrá un offset de 0 sino de 2. Y lo mismo pasa con el resto de segmentos que pusiste. Así que esto que habías hecho era correcto:

Código (asm) [Seleccionar]
MOV AX, ref
MOV AX, seg1
MOV AX, seg2


simplemente sucedió que los 3 segmentos, así como cseg, y por ende, CS, apuntaban a la misma dirección. Prueba a alinearlos todos con PARA y verás que AX tendrá valores distintos. O bien, déjalo como está, pero cambia el DB ? de ref por DB 15 DUP(?) y verás que debería quedar en un segmento distinto al resto.

Eternal Idol

#11
Cierto, aunque es necesario contar tanto con el selector de segmento como con el desplazamiento (offset) para para obtener una direccion segmentada con la que poder trabajar.




The segment address is always added to a 16-bit offset in the instruction to yield a linear address, which is the same as physical address in this mode. For instance, the segmented address 06EFh:1234h (here the suffix "h" means hexadecimal) has a segment selector of 06EFh, representing a segment address of 06EF0h, to which the offset is added, yielding the linear address 06EF0h + 1234h = 08124h.

Because of the way the segment address and offset are added, a single linear address can be mapped to up to 212 = 4096 distinct segment:offset pairs. For example, the linear address 08124h can have the segmented addresses 06EFh:1234h, 0812h:0004h, 0000h:8124h, etc.

https://en.wikipedia.org/wiki/X86_memory_segmentation




Volviendo al ejemplo original los segmentos declarados por TASM arrancan en (entre otras direcciones segmentadas):
ref 0710h:0000h
seg 0710h:0002h
seg2 0710h:0004h
cseg 0710h:0006h

Poner el selector de segmento en AX no es suficiente para acceder a los datos (AX no sirve como indice tampoco), ni saber donde arranca el segmento declarado en TASM.

Para poder acceder a los datos con los segmentos declarados en mi ejemplo:
Código (asm) [Seleccionar]
entry:
mov bx, offset seg2
mov al, byte ptr [bx] ;esto no lee de 0:[bx] o [bx]:0, lee de ds:[bx] es decir ds:[0x8]
mov al, byte ptr cs:[bx] ;cs:[bx] es decir cs:[0x8] es 0x66
mov al, byte ptr cs:[2] ;es 0x3

mov bx, seg1
mov ds, bx ;ponemos en dx el selector de segmento
mov bx, offset seg1 ;ponemos en bx el desplazamiento
mov al, byte ptr [bx + 1] ;ds:[0x5] es 0x5
mov al, byte ptr [bx + 2] ;ds:[0x6] es 0x7


Si le damos nombre a nuestros datos como por ejemplo cambiando uno por despues podemos acceder a los mismos:
Código (asm) [Seleccionar]
var db 7
...
entry:
mov bx, seg1
mov ds, bx
mov bx, offset var
mov al, byte ptr [bx] ;ds:[0x6] es 0x7
La economía nunca ha sido libre: o la controla el Estado en beneficio del Pueblo o lo hacen los grandes consorcios en perjuicio de éste.
Juan Domingo Perón

RayR

#12
Sí, aunque creo que puede ser bastante engañoso y confuso mezclar el segmento y su contenido.

Aquí estamos poniendo ejemplos sencillos para ilustrar ciertos conceptos, pero obviamente, en una situación mas realista, es incorrecto suponer que todos los segmentos van a coincidir. Antes de intentar hacer cualquier cosa con un dato, siempre tenemos que hacer apuntar DS (o ES y usar ASSUME o segment overrides) al segmento que lo contiene. Naturalmente, en el ejemplo anterior, a poco que se toque el código (por ejemplo, haciendo que seg2 sea más grande), el programa puede dar resultados incorrectos. Esto lo aclaro por si alguien que empieza lee esto. En los ejemplos que pondré a continuación, siempre supongo que DS apunta al segmento correcto.

Si tenemos esto:

Código (asm) [Seleccionar]
SEGMENT seg2 WORD
var1 DW ?
ENDS seg2


y var1 está en 0910:0002, decir que seg2 también inicia en esa posición es, como decía al principio, engañoso. Siempre que hagamos referencia a seg2 en el código, el valor que se sustituirá será 0910, nada más. Luego, dando por sentado que hemos hecho, como deberíamos, que DS apunte a seg2, si queremos ver el offset de var, veremos que es 2. Pero si se supone que seg2 (el segmento declarado por TASM) inicia en 0910:0002, la pregunta que surgiría es ¿por qué el ensamblador desperdicia memoria metiendo ese hueco de 2 bytes? Más aún, y siguiendo con ese razonamiento, si seg2 inicia en 0910:0002, y var1 tiene un offset de 2, eso implicaría que la dirección de var1 es 0910:0004, lo cual es incorrecto. La realidad simplemente es que seg2 inicia en el segmento 0910 o, si se quiere, en 0910:0000 (no necesitamos offset, porque, por definición, siempre va a ser 0000) y se superpone con un segmento anterior, y de ahí que var1 se coloque en una posición más alejada, es decir, los datos que contiene, sí que inician en el offset 2. Por eso, como decía al principio, mezclar seg2 con sus datos que contiene puede ser útil como abstracción y para simplificar las cosas, pero es algo inexacto y cuando queremos profundizar un poco, la abstracción se rompe; de ahí que yo prefiera separar los conceptos.

Eternal Idol

¿Que es un segmento sin su contenido? ¿Un rango de memoria? Separar un segmento de sus datos no tiene sentido, un segmento es un bloque de memoria, un conjunto de bytes alineado a un parrafo y esos bytes que son sus datos tienen valores. Sin ir mas lejos al declarar un segmento en TASM nos permite darle valor a su contenido.

"The segment alignment attribute tells the linker to ensure that a segment begins on a
specified boundary. This is important because data can be loaded faster on the 80x86
processors if it's properly aligned."

De la misma manera equiparar los segmentos de memoria y los segmentos declarados por TASM me parece errado como concepto y por lo explicado en este mismo hilo es imposible que sean exactamente lo mismo. Si seg2 es 0x910 y antes esta seg1 que es 0x910 quiere decir que seg2 no inicia en la misma posicion, por pura logica; es cierto que el segmento de memoria 0x910 siempre inicia en la misma direccion pero si podemos tener 3 segmentos declarados por TASM que inician en un mismo selector de segmento no podemos seguir diciendo que son lo mismo que los segmentos de memoria.

Hacer referencia a seg2 en el codigo es solo una parte - inconclusa - del trabajo necesario para acceder a los datos que el segmento declarado por TASM contiene, se necesita tambien un desplazamiento, el cual tambien se puede conseguir haciendo referencia al mismo seg2 con offset.

No se desperdicia nada, si inicia el segmento TASM con un offset de 2 bytes es que hay otro segmento TASM anteriormente ocupando 1 o 2 bytes (lo cual lo hace intrinsicamente diferente a un segmento de memoria que solo puede iniciar alineado a un parrafo). No es como lo planteas, offset de seg2 y offset de var1 son lo mismo, no hay nada que ocupe otros 2 bytes entre medias, el primer elemento de ese segmento no inicia en otro lugar que no sea el principio del mismo. Es cierto que offset es con respecto al segmento de memoria, es la unica forma de que funcione.

Si seg2 iniciara en 0910:000 siempre entonces offset daria siempre 0 para un segmento declarado por TASM y no es el caso, devuelve el desplazamiento necesario para poder hacer estos segmentos de TASM funcionales.

Código (asm) [Seleccionar]
SEGMENT seg1 WORD
var db 7
var2 db 8
ENDS seg1

SEGMENT seg2 WORD
var1 DW 666h
ENDS seg2

SEGMENT cseg WORD
entry:
mov ax, seg1
mov ax, seg2
mov ax, offset seg1 ;0x0
mov ax, offset seg2 ;0x2
mov ax, offset var1 ;0x2
La economía nunca ha sido libre: o la controla el Estado en beneficio del Pueblo o lo hacen los grandes consorcios en perjuicio de éste.
Juan Domingo Perón

Usuario887

#14
Cita de: Eternal Idol en  6 Mayo 2020, 17:17 PM
Nada mejor que probar para averiguar.

Si, en verdad.

Gracias por tu ayuda.




Cita de: RayR en  7 Mayo 2020, 02:54 AM
Al especificar BYTE, WORD, etc. al definir un segmento, estamos indicando cómo queremos que esté alineada la dirección donde comienzan las instrucciones o datos contenidos en él, pero el segmento en sí siempre estará en dirección múltiplo de 16.
Me alegra que comentes esto porque supuse que a algo parecido tenia que referirse el manual con aligned.
Y ¿se especifica de esta manera para que el direccionamiento comienze en la proxima direccion, o mas precisamente para que la forma de acceso por defecto al segmento sea BYTE, WORD, etc.?

Cita de: RayR en  7 Mayo 2020, 02:54 AM
se busca la primera dirección disponible que sea múltiplo de 2
Esto como una implicacion de lo anterior, ¿no?.

Cita de: RayR en  7 Mayo 2020, 02:54 AM
para que no se superpongan sus datos, el byte de seg1 no tendrá un offset de 0 sino de 2.
¿Y como el procesador deduce este offset al momento de acceder al segmento?
¿O debe ser especificado explicitamente en el programa?.  
Gracias por la ayuda.

Cita de: Eternal Idol en  7 Mayo 2020, 12:52 PM
es necesario contar tanto con el selector de segmento como con el desplazamiento (offset) para para obtener una direccion segmentada con la que poder trabajar.




The segment address is always added to a 16-bit offset in the instruction to yield a linear address, which is the same as physical address in this mode. For instance, the segmented address 06EFh:1234h (here the suffix "h" means hexadecimal) has a segment selector of 06EFh, representing a segment address of 06EF0h, to which the offset is added, yielding the linear address 06EF0h + 1234h = 08124h.

Because of the way the segment address and offset are added, a single linear address can be mapped to up to 212 = 4096 distinct segment:offset pairs. For example, the linear address 08124h can have the segmented addresses 06EFh:1234h, 0812h:0004h, 0000h:8124h, etc.

https://en.wikipedia.org/wiki/X86_memory_segmentation



Esto responde mi pregunta.

Ahora solo me pregunto que quiere decir 'code' en esa expresion. Sin embargo probablemente consiga la respuesta en seguida en el manual de TASM 5.
Respecto a lo que comentas, Eternal Idol, acerca de ello, no estoy seguro... claramente tiene que ver con codigo pero me pregunto mas bien como es precisamente esta relacion. Ya que, si en vez de 'code', lo expresas asi:

Código (asm) [Seleccionar]

SEGMENT cseg 'code'


Resulta en lo mismo que:

Código (asm) [Seleccionar]

SEGMENT cseg 'qiwnef' ;(una cadena random)


Esto concluye claramente en la poca importancia de ello, sin embargo me sigue intrigando.

Saludos.

Eternal Idol

#15
"The segment alignment attribute tells the linker to ensure that a segment begins on a specified boundary."

En este punto el manual es explicito (y tambien habla de la generacion de segmentos, los segmentos de memoria son pre-existentes al programa), el alineamiento le dice al enlazador que se asegure que un segmento comienza en un limite especifico, este puede ser en bytes: 1, 2, 4, 16, 256 y 4096.

En la practica podemos ver como eso sucede con los ejemplos ya publicados, y no estamos hablando de segmentos superpuestos, estos no inician en la misma direccion de memoria segmentada (de hacerlo hablariamos siempre del mismo segmento de memoria, parrafo X, direccion de memoria lineal X con igual o diferente tamaño, el cual el procesador desconoce), solo puede haber segmentos superpuestos cuando involucramos un selector de segmento + un desplazamiento - el ejemplo seria 0710h:0006h y 0709h:0076h ambas direcciones segmentadas apuntan al mismo lugar con dos segmentos diferentes - de otra manera tratamos exclusivamente con valores consecutivos de 0 a 0xFFFF. Aca hablamos de varios segmentos de TASM contenidos en un solo segmento de memoria por la forma de declararlos.

No se puede acceder de ninguna manera a 256 o 4096 bytes atomicamente (en modo real creo que a 16 tampoco, sinceramente esto me esta haciendo releer material precambrico), el alineamiento - que por defecto es 16 en TASM - no tiene que ver con eso.

El procesador no sabe nada de estos segmentos declarados en TASM, MS-DOS despues de cargar el ejecutable solo sabe que tiene que hacer relocalizaciones y gracias.

SEGMENT cseg 'lalala' << le dice a TASM genera un segmento de codigo (cseg) llamado lalala, esto (el nombre) no se ve reflejado en el binario, en el manual hay una muy breve explicacion.
La economía nunca ha sido libre: o la controla el Estado en beneficio del Pueblo o lo hacen los grandes consorcios en perjuicio de éste.
Juan Domingo Perón

RayR

Sé que no hay desperdicio; eso lo puse como ejemplo de una conclusión errónea a la que se podía llegar. Pero tienes razón, aquí yo estaba mezclando selectores de segmento con los segmentos que el ensamblador declara. Y es que hace demasiado tiempo que no toco nada de esto, y si ya de por sí el tema de la segmentación en x86 es complejo...

Cita de: marax en  8 Mayo 2020, 11:15 AM

Código (asm) [Seleccionar]

SEGMENT cseg 'code'


Eso último entre comillas simples es el nombre de "clase". El ensamblador pone juntos en memoria los segmentos pertenecientes a una clase, independientemente del orden en que hayan sido declarados o si están en diferentes archivos. Hay algunos nombres predefinidos y "code" es uno de ellos. Ése es básicamente su uso.

Eternal Idol

Cita de: RayR en  8 Mayo 2020, 17:29 PMY es que hace demasiado tiempo que no toco nada de esto, y si ya de por sí el tema de la segmentación en x86 es complejo...

Yo ya lo habia olvidado por completo, por suerte, pero al menos ahora tengo en la mente mas razones para agradecer estar trabajando con un modelo de memoria plano ;-) ;-) ;-)
La economía nunca ha sido libre: o la controla el Estado en beneficio del Pueblo o lo hacen los grandes consorcios en perjuicio de éste.
Juan Domingo Perón

Usuario887

#18
Cita de: RayR en  8 Mayo 2020, 17:29 PM
Eso último entre comillas simples es el nombre de "clase". El ensamblador pone juntos en memoria los segmentos pertenecientes a una clase, independientemente del orden en que hayan sido declarados o si están en diferentes archivos. Hay algunos nombres predefinidos y "code" es uno de ellos. Ése es básicamente su uso.
Lo intuia... aunque me considero demasiado poco experto aun para permitirme confiar en mi intuicion.
Gracias, ahora comprendo la expresion correctamente.

Cita de: Eternal Idol en  8 Mayo 2020, 17:36 PM
por suerte
Me parece demasiado arriesgado hablar de suerte y de ensamblador en el mismo contexto...  :-X
Saludos.

Eternal Idol

Cita de: marax en  9 Mayo 2020, 17:16 PMMe parece demasiado arriesgado hablar de suerte y de ensamblador en el mismo contexto...  :-X

¿Cual seria el riesgo exactamente? ¿Tener que lidiar con codigo de los años 80s/90s? Las probabilidades de que tenga la necesidad (el foro no cuenta, hablo profesionalmente) de depurar (debug.com/.exe desaparecio de mi SO hace rato largo cuando aparecieron a su vez los procesadores de 64 bits) y/o programar en ensamblador x86 de 16 bits son infimas por no decir nulas y ya lo eran incluso la decada pasada.
La economía nunca ha sido libre: o la controla el Estado en beneficio del Pueblo o lo hacen los grandes consorcios en perjuicio de éste.
Juan Domingo Perón