lunes, 14 de abril de 2008

LENGUAJES DE MAQUINA Y ALTO NIVEL

Lenguaje de maquina:
de máquina de una computadora consta de cadenas de números binarios (ceros y unos) y es el único que "entienden" directamente los procesadores. Todas las instrucciones preparadas en cualquier lenguaje de máquina tienen por lo menos dos partes. La primera es el comando u operación, que dice a la computadora cuál es la función que va a realizar. Todas las computadoras tiene un código de operación para cada una de sus funciones. La segunda parte de la instrucción es el operando, que indica a la computadora donde hallar o almacenar los datos y otras instrucciones que se van a manipular; el número de operandos de una instrucción varía en las distintas computadoras.

En el principio de la computación este era el lenguaje que tenía que "hablar" el ser humano con la computadora y consistía en insertar en un tablero miles de conexiones y alambres y encender y apagar interruptores.

Aunque en la actualidad ya no se emplea, es importante reconocer que ya no es necesario que nos comuniquemos en este lenguaje de "unos" y "ceros", pero es el que internamente una computadora reconoce o "habla".
Lenguajes de alto nivel
Los lenguajes de alto nivel logran la independencia del tipo de máquina y se aproximan al lenguaje natural. Se puede decir que el principal problema que presentan los lenguajes de alto nivel es la gran cantidad de ellos que existen actualmente en uso.

Los lenguajes de alto nivel, también denominados lenguajes evolucionados, surgen con posterioridad a los anteriores, con los siguientes objetivos, entre otros:

Lograr independencia de la máquina, pudiendo utilizar un mismo programa en diferentes equipos con la única condición de disponer de un programa traductor o compilador, que lo suministra el fabricante, para obtener el programa ejecutable en lenguaje binario de la máquina que se trate. Además, no se necesita conocer el hardware específico de dicha máquina.

Aproximarse al lenguaje natural, para que el programa se pueda escribir y leer de una forma más sencilla, eliminando muchas de las posibilidades de cometer errores que se daban en el lenguaje máquina, ya que se utilizan palabras (en inglés) en lugar de cadenas de símbolos sin ningún significado aparente.

Incluir rutinas de uso frecuente como son las de entrada/salida, funciones matemáticas, manejo de tablas, etc, que figuran en una especie de librería del lenguaje, de tal manera que se pueden utilizar siempre que se quieran sin necesidad de programarlas cada vez.

Se puede decir que el principal problema que presentan los lenguajes de alto nivel es la gran cantidad de ellos que existen actualmente en uso (FORTRAN, LISP, ALGOL, COBOL, APL, SNOBOL, PROLOG, MODULA2, ALGOL68, PASCAL, SIMULA67, ADA, C++, LIS, EUCLID, BASIC), además de las diferentes versiones o dialectos que se han desarrollado de algunos de ellos.

SISTEMAS DE NUMERACION

El sistema numérico binario





usa solamente dos símbolos diferentes, 0 y 1, que significan “ninguna unidad” y “una unidad” respectivamente. A diferencia del sistema decimal, el valor relativo de los dígitos binarios a la izquierda del digito menos significativo aumente en una potencia de dos cada vez, en lugar de hacerlo en potencias de diez.



Específicamente, los valores de posición de la parte entera de un numero binario son las potencias no negativas de dos: 2º 2¹ 2² 2³ (de derecha a izquierda).



Y los valores de posición de la parte fraccionaria de un numero binario son las potencias negativas de dos: 2 ¹ 2 ² 2 ³ (de izquierda a derecha).



Por ejemplo, el numero binario 1101,11 significa:



1101,11 = 1 " 2³ + 1 " 2² + 0 " 2¹ + 1 " 2º + 1 " 2 ¹ + 1 " 2 ²
= 1 " 8 + 1 " 4 + 0 + 1 " 1 + 1 " 0,5 + 1 " 0,25
= 13,75 (en el sistema decimal)



Este es además el sistema de numeración que utiliza internamente el hardware de las computadoras actuales. Cada digito de un numero representado en este sistema se denomina bit.





Se suelen utilizar con nombre propio determinados conjuntos de dígitos binarios:



o Cuatro bits se denominan cuarteto o nibble (ej. 1001).
o Ocho bits octeto o byte (ej. 10010110)
o Al conjunto de 1024 bytes se lo llama Kilobyte o simplemente KB.
o 1024 Kilobytes forman el llamado Megabyte (MB).
o 1024 Megabytes se denomina Gigabyte (GB).
o 1024 Gigabytes se denomina Terabyte (TB).



La razón por la que se utiliza el factor multiplicador 1024 en lugar de 1000, como sucede en otras magnitudes físicas, es por ser la potencia de 2 mas próxima a 1000, cuestión muy importante desde el punto de vista electrónico.



El byte es considerado como la unidad básica de medida de la información representada en este sistema.



· Sistemas en base 16 y 8
Estos sistemas permiten:



· Representar un mismo numero con menos símbolos que en binario, lo cual a su vez redunda en una manipulación más veloz y con menos errores de lectura y escritura para el hombre.



· Pasar en forma directa y sencilla de cualquiera de ellas a binario y viceversa por se una base potencia de la otra.



Sistema Hexadecimal



La notación hexadecimal requiere el uso de 16 símbolos para representar 16 valores numéricos. Dado que el sistema decimal proporciona solamente 10 símbolos numéricos ( de 0 a 9), se necesitan 6 símbolos adicionales para representar los valores restantes. Se han adoptado para este fin las letras A, B, C, D, E y F, aunque podrían haberse utilizado cualesquiera otros símbolos.





La lista completa de símbolos hexadecimales consta, por lo tanto, del 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E y F, en orden ascendente de valor. Como muestra la tabla al final de la enumeración de todos los sistemas de numeración, los números decimales, hexadecimales, binarios y octales equivalentes (hasta el numero 25).



El significado de los números hexadecimales se hace evidente con el desarrollo en potencias de 16.



Sistema Octal



Emplea los símbolos del 0 al 7, de igual significado que los similares decimales para formar los números, de acuerdo a la tabla. En este caso se tendrían hasta 7 recipiente de cada tipo, siendo los de un tipo 8 veces mayores que el anterior: (1), (8), (64), (512), (4096),..., o sea se halla multiplicando por 8 el precedente.



BIT Y BYTE

Bit

Significa la unión de dos significados de dos palabras que en ingles Binary digit (Digito Binario) y representa un digito de enumeración binario.

Mientras que en el sistema de enumeración decimal se utilizan 10 dígitos en el binario se utilizan 2, el 0 o 1, es la unidad mínima de información empleada en informática, dispositivos digitales o en teoría.

Para utilizarlo solo basta con dar a significado a los valores 0 o 1 como true o false y esto nos permite hablar de 4 distintas combinaciones de valores

Cuatro forman un nibble y representan 16 valores diferentes.

Ocho forman un octeto y representan 256 valores.

Byte

Bueno, representa a octeto, osea a 8 bits y que puede ser considerado como la secuencia de bits contiguos.

Aclaro:

Pero no siempre se puede decir que fue un octeto pues en los computadores antiguos podría estar conformado por 6,7,8 o 9 bits

domingo, 13 de abril de 2008

EJEMPLO SENCILLO ASSEMBLER

EJEMPLO.ASM:

.MODEL SMALL ; Tipo .COM, este modelo de memoria

; significa que SS, DS, ES y CS son iguales al

;comenzar el programa. Y que los datos y el stack

;están sobre el mismo segmento pero claro, en

; distinto offset.

.CODE ; Para decirle al Assembler que aquí

; comienza el código. En otros modelos, como .HUGE

; hay que definir la parte de datos, con .DATA y

; la parte de Stack, con .STACK.ORG

100h; Esta orden, para el compilador también

; le dice que comience el programa en el offset

; 100h. Todos los .COMs deben comenzar en el

; offset 100h. Mas adelante voy a explicar bien el; funcionamiento de esta orden.



; Y luego completar el programa con lo que sea.

EJEMPLO:



; Esta es una etiqueta, es un nombre que

; se le da a una posición para, luego, poder

; referirnos a ella por su nombre. Esta es una

; ayuda que nos da el Assembler para no tener que

; referirnos a números todo el tiempo. Acá hace

; falta para que el compilador sepa que todo

; comienza allí. No es necesario que sea el nombre

; del programa, puede ser cualquier otra cosa pero

; yo acostumbro esto.



; La orden MOV (MOVE), lleva dos parámetros. Lo

; que hace es copiar el contenido del segundo

; parámetro al primero



mov di,offset mensaje;

Esta orden, significa poner en DI el offset de

; la posición que representa mensaje. (mirar mas

; abajo para ver mensaje).

; DI = Offset Mensaje



Repetir: ; Pongo un 'label' o etiqueta, para

; usarlo luego.



mov al,byte ptr [di]

; Pongo en AL el contenido de la posición que

; indica [DI].

; Recordemos que [DI] solo, significa DS:[DI],

; porque el segmento por omisión para DI es DS.

; Con 'byte ptr' indico que DS:[DI] es un

; puntero a un BYTE. AL = DS:[DI]



add al,40h ; le sumo a AL 40h.

; AL+=40h (AL=AL+40h)



mov byte ptr [di],al

; Pongo en DS:[DI] (lo que apunta DS:DI) el

; contenido de AL. DS:[DI]=AL



inc di ; Incrementa DI en 1.

; DI++ (DI=DI+1)



cmp al,'$' ; !!! Compara AL con '$', es decir

; con el valor ASCII de '$'. El resultado de la

; comparación es guardado en los flags del micro.

; AL??'$'jne Repetir



; Salta si el resultado de la última

; comparación fue que son iguales.

; Jump if Not Equal. Si no eran iguales salta a

; Repetir, el label que habíamos puesto al

; principio. AL=='$' ? Go Repetir



mov ah,09h ; Pongo en AH un 09h; AH=09hmov dx,offset mensaje



; DX = Offset Mensajeint 21h



; !!! Ejecuto la Interrupción 21hmov ax,4c00h



; AX=4C00hint 21h ; INT 21h



Mensaje db 08h, 2Fh, 2Ch, 21h, 0E1h, 0E1h, 0E1h

; Aca estoy definiendo una lista de números,

; cada uno es un byte. (DB = Define Byte). El

; primer byte de la lista esta bajo el nombre

; mensaje. También podría haber puesto:

; Mensaje db 'El día esta lindo!'

; Se toma cada caracter como un byte.

db 0CDh, 0CAh, 0E4h
; Esta línea es la continuación de la anterior.
; Es necesario volver a poner

DB.END EJEMPLO
; Con esta orden, le digo a
; assembler que mi programa termina acá.
; El nombre después de END tiene que ser el
; mismo del principio.

EXPLICANDO ASSEMBLER 2

Bueno, acá esta la cuestión:


CS, Current Segment: Es el Segmento actual de ejecución, juntos CS:IP apuntan a la próxima orden que el Micro debe leer para ejecutar. Veamos algo:

CS:IP = 1000:FFFA hay una orden entonces IP se incrementa:

CS:IP = 1000:FFFE Que pasa si ahora hay otra orden, e IP se incrementa de nuevo?

Fácil: FFFE+1 = FFFF pero FFFF+1 = 10000 !!!

Que pasa en este caso? CS no se incrementa, e IP no puede contener un numero de 5 cifras! Lo que pasa es que IP vuelve a 0000 y el programa sigue en CS:IP o sea 1000:0000, lo que es una posición 64k mas baja de memoria, o sea cualquier lado... Este es el problema por lo que los .COMs no pueden tener mas de 64k de largo sin hacer nada raro (cambiar CS cuando sea necesario).

DS, Data Segment: Es el segmento destinado a ser usado junto con BX, SI y DI para apuntar a una dirección de memoria. También puede ser usado con BP y SP, pero hace falta expresarlo concretamente. (Mas adelante aclaro esto)

ES, Extra Segment: Es un segmento Extra para ser utilizado cuando haga falta. También tiene una función propia: Junto con DI indican el destino en las "órdenes de cadena" (el dato leído de DS:SI es puesto en ES:DI, en las ordenes de movimiento).

SS, Stack Segment: El segmento de Stack; junto con SP apuntan a la última posición que fue utilizada para "depositar" datos en el Stack.

Por ultimo:

Flags: Es un registro especial en el cual se guardan los resultados de la última operación matemática o de comparación. No se guardan los resultados numéricos, solo los de estado, es decir:

Hasta aquí la explicación de los registros internos del Micro.

Para apuntar a una posición, hace falta indicar su Offset y su Segment, esto se hace mediante un registro de segmento (DS, CS, SS, ES) y un Offset, que puede ser un número o un registro como BS, SI, DI, BP, SP:DS:[3304h] Apunta al Segment DS y al offset 3304h.

Los corchetes indican que loque se quiere es el contenido de esa posición, no la posición en si.ES:[BX] Indica a la posición que esta en el Segmento DS y el Offset BX.SS:[SP] Indica la posición de arriba de todo del Stack, apuntada por el Segmento SS y el Offset SP.No siempre hace falta indicar el Segment al que se apunta.

Como el segment DS es el destinado para poner los datos, la gente de Intel decidió que si no se especifica segmento para un offset absoluto (un número como el primer caso), o para los registros BS, SI y DI, se asume DS. Para los registros SP y BP, se asume SS como Segmento por defecto. Entonces:

[1304h] Indica el contenido de DS:[1304h]

[BX] Apunta a DS:[BX], pero

ES:[BX] Apunta a ES:[BX], porque se especifica ES

ES:[0017h] -> a ES:[0017h]

[SP] -> a SS:[SP]

CS:[SP] -> a CS:[SP]

CS:[DI] -> a CS:[DI]

SS:[SI] -> a SS:[SI]

[SI] -> a DS:[SI]

[DI] -> a DS:[DI]

[BP] -> a SS:[BP]

ES:[BP] -> a ES:[BP]

Es decir, cualquier combinación de Segmento y Registro Índice o Base o Offset Absoluto puede ser formada, pero no siempre hace falta aclarar el Segmento.

Hasta ahora vimos que en los microprocesadores hay variables llamadas REGISTROS, que cada uno esta destinado a una función especial, pero que en general se dividen en dos grandes grupos, los registros de uso general, por decirles de alguna forma, y los exclusivamente para ser usados como segmentos. Vamos a ver como es un programa en Assembler, pero antes, quiero aclarar que es un Assembler.

Assembler, propiamente dicho, es el programa (TASM, MASM, A86, etc.) que se ocupa de transformar lo que nosotros escribimos, en palabras, se podría decir, a número, que es lo que realmente entiende la máquina. En realidad, lo convierte, actualmente, en un código objeto, .OBJ, que es una herramienta que se creo para poder unir o Linkear varias rutinas hechas en distintos momentos y/o lenguajes. Lo que completa el proceso es el Linker, TLINK, LINK, OPTLINK, BLINKER, etc. Lo que yo uso, particularmente, son el Assembler y Linker de Borland, el TASM y el TLINK, pero bueno, si alguien quiere usar alguno mas, es cosa de darse maña, o preguntar que hay que cambiar.

Primero, para comenzar por algo fácil, y para que por fin, de una vez por todas, puedan comenzar a hacer algo, vamos a hacer un .COM, es lo mas simple, mas rápido, y mas fácil de recordar.

EXPLICANDO ASSEMBLER

Vamos a empezar por algo que quizás muchos ya sepan, pero como es indispensable, igual se lo tengo que contar:


La Memoria de Las PC:


Como ya sabrán, de haber escuchado por ahí, hay varios tipos de memoria, la Memoria Base, la Memoria Expandida (EMS), Memoria Extendida (XMS), Upper Memory Blocks (UMB), High Memory Area (HMA) y un montón mas de referencias a distintos tipos de memoria, lo que les voy a contar es que dirección de memoria le corresponde a un Byte dentro del primer MegaByte, es decir, dentro de la memoria base (0-640K) y en la zona donde están las plaquetas de expansión, Monitor, BIOS ROM, área de la controladora de Disco, etc...


Tratemos de acostumbrarnos a ver los números en Hexadecimal (base 16). Los dígitos que pueden formar un numero en esta base, son: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E y F, tomando valores en ese mismo orden, el 0 vale 0 y la F vale 15.


De esta forma un numero, por ejemplo 0FA3h (la 'h' final indica que esta en 'Hexa'), expresado en decimal valdría (3)+(Ah*16)+(Fh*16*16)+(0*16*16*16), o lo que es lo mismo 3+160+3840+0=4003, pongámonos de acuerdo que cuando un número lo escribimos en Haxa le ponemos la 'h' al final, y si comienza con una letra, le agregamos un '0' adelante para no confundirnos con un nombre de variable.


Esto fue una breve pero necesaria introducción al sistema Hexadecimal de numeración.Ahora si, veamos como se direcciona la memoria de una PC.


Tengamos en cuenta que si queremos direccionar 1 Mb nos hacen falta 5 dígitos hexadecimales, es decir, poder escribir, de alguna forma 0FFFFFh o 100000h-1 que es 1*16^5=1 Mb (1 que se resta, corresponde al byte #0). Como los 80x86 tienen registros de 16 bits (8 bits = 1 Byte, 16 bits=1 Word) o 4 dígitos hexa, solo podríamos llegar a direccionar 0FFFFh bytes, lo que significan 64 Kb solamente (de aquí la limitación de los .COMs).


Lo que se hace es utilizar dos registros a la vez para apuntar a un solo byte, lo que nos amplia el campo a poder diferenciar 0FFFFh:0FFFFh bytes o 1 Mg de Mb muchísimo! Entonces, ¿Que pasa que no se puede tener tanta memoria sin hacer cosas extrañas? Claro, lo que pasa es que para direccionar se usan dos registros, pero no ubicados uno al lado del otro, sino desplazados un dígito. ¿Que? Si, ya se que esta confuso, ahora te cuento:En un Registro, digamos que se llama DS, esta 0040h, y en otro, digamos BS, hay 006Ch.


La dirección a la que apunta DS:BS (así se escribe) es 0040:006C, pero esta dirección, en números de 5 cifras se escribe 0046C, la conversión es simplísima, lo que hay que hacer es sumar los registros de la siguiente forma:BS -> 006CDS -> + 0040---------------DS:BS -> 0046Ch De esta suma, uno se puede dar cuenta que para referirse a una misma posición de memoria puede haber muchas formas distintas. Por ejemplo, para referirse a 0046C, se puede hacer: 000C 046C+0046 +0000------- --------0046C = 0046:000C 0046C=0000:046C 023C 005C+0023 +003F-------- --------0046C = 0023:023C 0046C = 003F:005CEs decir, puede haber muchas formas de nombrar el mismo byte, y tenemos que tratar de acostumbrarnos a darnos cuenta que, por ejemplo, 0F000:1234 y 0F123:0004 son equivalentes.


Tenemos que una dirección de memoria se separa en dos partes, la de la izquierda del : es el famoso SEGMENT o segmento y lo de la derecha es el OFFSET o desplazamiento, de una dirección.Conclusión: Para referirnos a cualquier Posición de Memoria es necesario conocer su Segment y su Offset, ya que sabiendo uno solo, nos podemos estar refiriendo a muchos bytes distintos.


Muy bien, ahora ya sabemos que la máquina tiene memoria y también sabemos como apuntar a una posición en esta memoria, pero bueno, este curso es de Assembler, no de memorias, por lo tanto, lo interesante y apropiado sería que les comentara, así como si de pasada fuera, como se hace en Assembler para cargar o grabar algo desde o hacia la memoria. Pero, primero hay que saber otras cositas... No es tanfácil la cosa... Al "hablar" en Assembler, estamos diciéndole a la máquina en lo mas cercano a su idioma posible (ya que su verdadero idioma es de "1" y "0" totalmente) lo que tiene que hacer.


Estamos hablando directamente, nosotros mismos, sin la ayuda de un traductor (compilador), con el cerebro propiamente dicho de la máquina. Estamos hablando con el vendito 80x86. Entonces para decirle algo tenemos que conocerlo un poco mas. Como es muy probable que muchos tengan 8086/88 o 80286vamos a ver especialmente, la estructura interna de estos "micros" (microprocesadores), por arriba, solo lo necesario para programar en assembler, obviamente también sirve para los iluminados que tengan 386 o 486, o quizás 586 o P-5. Internamente todos los micros (entiendo que todos), tiene unas pocas variables, llamadas REGISTROS que son en donde se procesan los datos, se hacen operaciones; son por las cuales se puede acceder a memoria, etc. Sirven para todo. Pero no todas sirven para lo mismo, y aunque muchas si sean intercambiables, todas están destinadas para algo en especial, y tienen una que otra función propia. Los registros de la familia 80x86 son:

Flags Señalizadores de Estado:

Todos estos registros son de 16 bits, es decir de un Word. Pero los registros que terminan en X (AX, BX, CX, DX) pueden ser manejados, también, como si fueran dos Bytes (Que lo son), por separado, sus nombres son, AH para el Byte mas significativo de AX y AL para el menos significativo, BH y BL para BX, CH y CL para CX y DH y DL para DX (la H y la L hacen referencia a "Hi" y "Lo", es decir Alto y Bajo, alto es sinónimo de mas significativo y bajo de menos. Si por ejemplo:

AX = 437Ah Entonces AH = 43h y AL = 7Ah

BX = 0145h BH = 01h y BL = 45h

CX = 0AABBh CH = 0AAh y CL = 0BBh

DX = 1h DH = 00h y DL = 01h

De igual manera, si:

AL = 10h y AH = 32h AX = 3210h

BL = 08h y BH = 0CAh BX = 0CA08h

CL = 1h y CH=1h CX = 0101h

DL = 00 y DX=00 DX = 0000h

Estos cuatro son los únicos segmentos que se pueden separar en byte alto y byte bajo.

Funciones especificas:

AX, Acumulator: Sirve para hacer todas las operaciones aritméticas, y algunas, como Multiplicar y Dividir, le son exclusivas. AX (y AL por su versión de un solo byte) son los únicos registros que pueden ser multiplicados y divididos por otro registro. La resta de AX y AL, por ejemplo, ocupan un byte menos que la de cualquier otro registro, pero esto no es para preocuparse, un byte, realmente no es nada, por mas que se acumule.

BX, Base Index: Sirve para ser usado como registro de base para un índice o array, es decir, una posición de memoria puede ser apuntada por BX (su offset), igualmente también se lo puede usar para hacer sumas restas y todo tipo de operaciones lógicas. AX, CX y DX no sirven para apuntar a memoria.

CX, Counter: Es el registro reservado para contar, como su nombre lo indica. Para este propósito hay órdenes especiales que lo decrementan o incrementan y hacen algo según el resultado. También hay ciertas órdenes repetitivas que necesitan saber cuanto repetirse, por medio de CX (o CL en su versión Byte) se les indica.

DX, Data: Este registro no tiene definido un uso, en general es utilizado para pasar ciertos parámetros, pero si cumple una función, por ejemplo en la multiplicación, si se multiplica AX=1000h (un Word) por, simplemente 10h (un Byte) el resultado es 00010000h (Un DWord), entonces, el Word Alto del resultado de la multiplicación se deposita en DX, y el Bajo en AX.

SI, Source Index: Puede ser utilizado como índice a posiciones de memoria, es decir se puede poner un número en SI (Offset) y leer el dato de esta posición. Pero a la vez tiene una función específica, la de Registro Fuente para las órdenes de tratamiento de cadenas. Hay ciertas órdenes en Assembler que son para, por ejemplo mover toda una cadena de bytes, de un lugar a otro, la dirección de la cual se leen los bytes se pone en SI antes de decir que lea.

DI, Destination Index: Como SI, puede ser usado como índice. Pero su función específica es la de Registro de Destino para las operaciones de cadena, lo que se lee de el contenido de SI (no de SI mismo, sino de la posición de memoria a la que apunta SI) es depositado en la posición de memoria a la que apunta DI, expresada por [DI]. Al la vez, igual que con SI, se pueden hacer operaciones aritméticas simples (suma y resta), y también todo tipo de operaciones lógicas (AND, OR, XOR).

BP, Base Pointer: Puntero a una posición de memoria, muy parecido a BX, pero generalmente usado para facilitar el pasaje de parámetros en funciones hechas con lenguajes de alto nivel, por una característica propia que ya voy a explicar.

SP, Stack Pointer: Puntero que indica en que Offset termina el Stack, o pila. El Stack, es un área de la memoria principal de la máquina, (no esta dentro del Micro, ni tampoco es fija) que sirve para preservar cosas, la estructura del Stack, que ya explicare mas a fondo, es simple, esta estructura es llamada LIFO (Last In First Out) o lo que es lo mismo, lo último que entra, es lo primero que sale, es como si tuviéramos una pila de cosas, lo último que apoyamos arriba va a ser lo primero que podamos sacar después. Si no esta claro, no se preocupen, ya voy a explicarlo bien, y voy a decir para que se usa, y cuando.

IP, Instruction Pointer: El puntero de instrucción es el que le indica al Micro cual va a ser la próxima instrucción que debe ejecutar (Solo el Offset). El programa en Assembler tiene una estructura lógica, la cual se puede seguir. IP comienza al principio del programa (la próxima orden que se debe ejecutar es la primera del programa), se ejecuta esa orden e IP es incrementado tanto como Bytes ocupe la orden recién ejecutada (no todas las órdenes ocupan un byte) luego sigue con la próxima y así sucesivamente. Si pudiéramos de alguna forma cambiar el contenido de IP lo que estaríamos haciendo seria una desviación, o un Jump (salto) a otro lado del programa, y efectivamente se puede hacer esto, pero no diciendo IP = 1234h, sino haciendo un salto, que es equivalente a esto último: JMP 1234h. Ya lo voy a explicar, esto también.

Se habrán dado cuenta, que siempre que dije que apuntaba a una posición de memoria, hice notar que solo era el Offset lo que estaba comprendido, por ejemplo en DI o SI, BX o SP. Pero entonces, como es posible que con solamente el offset alcance para identificar una posición de memoria? si yo mismo dije:

"Para referirnos a cualquier posición de memoria es necesario conocer su Segment y su Offset, ya que sabiendo uno solo, nos podemos estar refiriendo a muchos bytes distintos."