1. Introducción
El ensamblador es el lenguaje mas directo con el ordenador, aunque también se le ha llamado Código Máquina, en realidad no es así, esto es debido a la mala utilización del término Lenguaje Máquina o Código Máquina, éste lenguaje en realidad está formado por unos y ceros, que son los datos que realmente utilizan los ordenadores.
Lenguaje Ensamblador, es en realidad el lenguaje propio del microprocesador del ordenador, el microprocesador , también llamado uP, es en realidad el centro del ordenador, normalmente los conocemos por sus nombre, 286, 386, 486 , Pentium, etc.. Estos son los nombres que les dan las fábricas de dichos microprocesadores.
El lenguaje que utilizan los microprocesadores es el lenguaje ensamblador, éste lenguaje está formado por una serie de palabras o instrucciones las cuales son las más directas con el microprocesador, es por ello , por lo que es el lenguaje más rápido que hay, dado que es el lenguaje directo y no necesita ninguna conversión, y es por ello , por lo que se recurre a éste lenguaje para hacer determinadas funciones o programas que requieren mucha velocidad , como los juegos , inteligencia artificial, etc...
Éste curso pretende que una vez terminado, tenga usted los conocimientos necesarios para adentrarse profundamente en la utilización de éste lenguaje para cuando le sea necesario hacer ritunas, funcione, etc. que le requieran más velocidad.
2. El Microprocesador
La parte principal de todo ordenador es el microprocesador , que es el encargado de ejecutar las instrucciones que le especificamos en un programa y realizar las comunicaciones con los demas perifèricos para realizar la tarea que se le ha encomendado.
Aunque el curso es integramente de ensamblador, quiero que tengas una base de conocimiento de usos que realiza el microprocesador con el resto de componentes de tu ordenador.
Para que te sirva de referencia , te dire que el microprocesador para realizar las tareas que se le encomienden, debe realizar primero la puesta en memoria del programa que le decimos que debe ejecutar y seguir una serie de pasos antes de realizar dicha ejecuciòn.
Para ello, el programa se alojara en la memoria a partir de una direcciòn determinada, a èsta direcciòn a partir de ahora la vamos a llamar "direcciòn de inicio" o "direcciòn de comienzo".
Por ello, en donde estè èsta direcciòn comenzarà a guardarse nuestro programa hasta que estè completamente alojado en la memoria, desde la direcciòn de inicio hasta la ùltima posiciòn de memoria que ocupe nuestro programa, en esas direcciones no se alojarà ningun otro programa hasta que el nuestro termine sus funciones.
Toda la gestiòn de memoria y alojamiento de programas en la memoria la realiza el microprocesador a travès del DMA o Acceso Directo A memoria, el cual le proporciona el control de la memoria tanto para lectura como para escritura en la misma.
Para realizar las escrituras y lecturas de memoria, utiliza una serie de patillas que el microprocesador tiene conectadas con el entorno de trabajo, osea con el resto de componentes del ordenador.
Una vez que pulsas con el raton sobre el icono, el microprocesador aloja el programa que has pulsado en la memoria, le asigna una direcciòn de inicio y espera a que se carge todo el programa en la memoria para saber donde termina y asi saber donde debe ir el siguiente programa para no usar la misma zona de memoria para dos programas distintos.
Una vez cargado en la memoria, el microprocesador va a la direcciòn de inicio y empieza la ejecuciòn de las instrucciones que tiene en dicho programa y hace las funciones que se le ha encomendado.
No quiero entrar en màs epecificaciones tècnicas sobre la electrònica del propio microprocesador , pues no lo considero necesario y solo queria que supieras que es lo que realiza el micro con tus programas y cuales son los pasos que sigue cada vez que pulsamos con el ratòn sobre un icono de un programa.
3. Los registros y registros de segmentos
Hace tiempo, cuando los microprocesadores eran basados en tegnologia de 16 bits, los registros del microprocesador, debian de ser tambien de 16 bits, probablemente a la falta de tegnologia de la epoca.
Hoy en dia los registros son de amplio margen en bits, y esto se lo explico, por que para poder acceder a una posicion de memoria, dado que la memoria de los ordenadores puede ser de megas, los regitros de 16 no permitian poder superar la barrera de los 64K osea que como maximo podian acceder a 65535 posiciones de memoria, quedando la restante inutil.
Hoy en dia con la aparicion de la memoria Extendida y Expandida, esto es algo que ya ha pasado a la historia, pero cuando tenian que acceder a los 64K que se tenian por registro, para poder acceder a una direccion de memoria superior se creo los llamados registros de segmento.
Un registro de segmento lo que hace en realidad es dividir la memoria en zonas de 64K, y cada segmento podia trabajar con dicha memoria, con esto y en conjuncion con los registros normales se podia acceder a la totalidad de la memoria disponible, con la simple instruccion de cambio de segmento y del correspondiente registro de segmento.
A partir desde ahora cuando hablemos de regitros de segmentos lo haremos con las palabra CS, DS y ES.
Aunque los registros sean conocidos y usados por sus nombres AX, BX, CX , etc.., en realidad cada uno de estos registros, podemos decir que se dividen en dos, su parte alta o AH y su parte baja o AL, esto se debe a que con ello podemos acceder de forma independiente al mismo registro pero teniendo dos valores posibles.
Esto es algo que debemos tener en cuenta , pues si lo vamos a usar, el valor del registro AX no sera el mismo que los valores por separado de su parte alta AH y su parte baja AL.
Esto se debio a que cuando se crearon los microprocesadores 80X86 se penso en la necesidad de acceder de forma independiente a las dos zonas de que esta compuesto todo registro, aunque ahora no le vea la utilidad , con el transcurso del tiempo ira viendo como ademas de ser util ses acostumbra a usar este sistema.
Esto es lo que por el momento quiero que sepa de los registros, conforme vayamos avanzando ire aplicandole nuevos conceptos de los registros, pues al ya tener una base de ellos no le sera dificil asimilar los nuevos conceptos que le exponga.
4. Primer contacto , la instrucciòn Move
Aunque la entrada en el lenguaje la iremos haciendo despacio para que no le cueste aimilar los conceptos , he preferido introducirle ya en esta instruccion del lenguaje ensamblador, pues sera una de las instrucciones que mas usara en sus programas y la que ya seguro que conoce o ha visto en algun programa.
Con la instruccion Mov se puede decir que asignamos valores a los registros, bien es cierto que como hemos visto
anteriormente en el capitulo de registros, tenemos dos tipos de registros, los normales y los de segmento, a cada
uno de ellos la asignacion de valores por medio del mov se produce de forma distinta.
La forma estandar de la instruccion Mov seria:
Mov AX,16
Con esto hemos introducido el valor 16 en el registro AX, pero esto no es lo mismo que:
Mov AX[16]
Esto aunque nos parezca parecido o igual , en realidad es completamente diferente, mientras que con Mov AX,16, introducimos el valor 16 en el registro AX, con Mov AX[16] , en realidad introducimos en el registro AX el valor que se encuentre en la posicion de memoria numero 16.
Esto es algo que debe fijarse por ahora, pues con el tiempo vera la diferencia rapidamente y notara enseguida como es cada instruccion y como se comporta cada una de ellas, que como ha podido ver sus efectos son totalmente distintos.
Otra asignacion con los registros normales, son las que se producen entre ellos mismos:
Mov AX,BX
Esto carga el valor del registro BX en el AX, osea que ahora tendrian tanto AX como BX el mismo valor, tambien hemos de decir que con esto, el valor que tuviera AX se perderia al asignarle el valor que tiene BX.
Como hemos dicho , los registros de segmento tienen una asignacion distinta, y esto se debe a que a un registro
de segmento no le podemos asignar el valor directamente, osea:
Mov DS, 10
esto no se nos permite y daria un error, para hacer esto debemos utilizar un registro intermedio:
Mov AX,16
Mov DS,AX
con esto ponemos el valor 16 en DS que es lo que queriamos , pero con el registro AX como intermediario, primero se lo asignamos a AX y despues a DS le aignamos el valor de AX.
Otra funcion de Mov es la asignacion de un registro a una determinada posicion de memoria, osea:
Mov AX,[10] digimos que ponia en AX el valor que tuviera la posicion 10 de memoria, pues
Mov [10],AX pone en la posicion de memoria lo que contenga el registro AX
Aunque al principio le parezca un poco lioso, con el tiempo se acostumbrarà a ver conchetes a uno y otro lado de la intruccion Mov y no le costarà tanto trabajo asimilar el concepto de cada caso.
5. Sistemas Numèricos
Aunque no parezca razonable pasar de la instruccion Move a los sistemas numèricos , en realidad el capitulo anterior cumple la funcion de darle a conocer el manejo de determinados registros y determinadas formas de hacerlo.
El presente capitulo pretende darle a conocer los tipos de sistemas numèricos que normalmente va a usar, como se pasa o realiza la conversion de un sistema a otro.
Sistema decimal
El sistema decimal es el que practicamente todos usamos, osea de 1 al infinito, dicho sistema de basa en la unidad , osea el uno, todos son multiplos o producto de la suma de unidades (2 = 1 + 1) y asi sucesivamente.
Sistema Hexadecimal
El sistema hexadecimal esta basado en los valores del 1 al 15 , compuesto por los numeros del 0 al 9 y por las letras A,B,C,D,E,F quedando la tabla de valore de la siguiente forma:
0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F
Sistema Binario
El sistema se basa en valores, el 0 y el 1, aunque con la union de ambos es puede crear cualquier numero. Para cada valor posee una determinada caracteristica , siendo su tabla de valores del 0 al 15 asi:
0000 0
0001 1
0010 2
0011 3
0100 4
0101 5
0110 6
0111 7
1000 8
1001 9
1010 10
1011 11
1100 12
1101 13
1110 14
1111 15
Esto se denomina numeracion binaria en base de 4 bits, el significado de la palabras bit , identifica a cada uno de los valores de que esta compuesto, puesto que esta compuesto de 4 posible valores de 0 o 1 , por ejemplo el 1 e 0001 , osea 3 ceros y un 1, osea 4 bit.
Bit tambien es la unidad màs pequeña de informacion que utilizan los ordenadores, se dice la mas pequeña puesto que esta ya no se puede dividir en mas valores que el 0 el 1.
Conversion del sistema binario al sistema decimal
Trabajando en el lenguaje ensamblador nos encontramos con la necesidad de convertir números del sistema binario, que es el empleado por los ordenadore en sus operaciones mas elementales, al sistema decimal.
El sistema binario está basado en 1 o 0, siendo 1 como valor activo y el 0 como inactivo.
Para la conversión se utiliza la siguiente formula:
Por ejemplo, si tenemos el numero binario 10011, tomamos de derecha a izquierda cada dígito
y lo multiplicamos por la base elevada a la posición que ocupan (comenzando por la pocisión cero):
Binario: 1 0 0 1 lo separamos de uno en uno de derecha a
izquierda:
el 1*(2^0)= 0
el 0*(2^1)= 0
el 0*(2^2)= 0
el 1*(2^3)= 8
y sumamos los valores de los resultados 0 + 0 + 0 + 8 = 16 en decimal.
El caracter ^ significa de potencia del numero es pecificado, en este caso el 2, y el caracter * se usa para la multiplicación.
Como habra podido observar el 2 va desde su potencia de 0 osea 2^0 hasta en este caso el 2^3, pero si el numero en vez de ser de 4 bits fuera de mas (8,16,32..), el dos seguiria elevando su pontencia, de modo el el quinto si fuera un 1, multiplicaria el 1 por 2^4 y asi sucesivamente.
Conversion del sistema decimal al sistema binario
Ahora es la operaciòn inversa a la anterior, pasar de decimal a binario.
Para ello la operaciòn , aunque hay varias, consiste en dividir el numero entre dos de la siguiente manera:
Supongamos el numero 33 en decimal:
33 lo dividimos entre 2 y da 16 con resto de 1
16 lo dividimos entre 2 y da 8 con resto de 0
8 lo dividimos entre 2 y da 4 con resto de 0
4 lo dividimos entre 2 y da 2 con resto de 0
2 lo dividimos entre 2 y da 1 con resto de 0
1 lo dividimos entre 2 y da 0 con resto de 1 aqui siempre es resto 1, pues no se puede utilizar 0.5
Pues ahora solo tenemos que coger los restos de cada division y ponerlos de derecha a izquierda: 100001
Conversion del sistema hexadecimal
En este sistema para pasar del decimal al hexadecimal , primero debera pasarlo a binario y la operacion de binario a hexadecimal es:
Se coge el numero binario en grupos de 4 bits de derecha a izquierda, sirvamonos del numero en binario 111101
lo cogemos de derecha a izquierda en grupo de 4 , quedando asi: 11 y 1101, como el primer grupo se nos queda con solo 2 cifras (11) , le añadimos al pricipio los ceros que necesite para completarse a cuatro bits, en este caso 2 ceros, quedando entonces asi 0011 y por lo tanto el numero quedaria asi: 0011 y 1101
Cogemos el primer grupo de 4 bits osea el 0011 y cogemos la tabla de valores del sistema binario
0000 0
0001 1
0010 2
0011 3
0100 4
0101 5
0110 6
0111 7
1000 8
1001 9
1010 10
1011 11
1100 12
1101 13
1110 14
1111 15
Como puede observar
El 0011 corresponde al 2
El 1101 corresponde al 13
Ahora cogemos los valores que antes nos pusimos en la tabla para el sistema hexadecimal y deciamos
que teniamos:
0 1 2 3 4 5 6 7 8 9 A B C D E F
El 2 que teniamos de la tabla de binario al buscarlo en la hexadecimal corresponde tambien al 2
El 13 que teniamos de la tabala de binario corresponde en la hexadecimal a la D, puesto que a partir del 9, la A es el 10, la B es el 11, la C es el 12, la D es el 13, la E es el 14 y la F es el 15.
Por lo tanto
El numero 111101 en hexadecimal es el 2D, tenga cuidado por que los numeros hexadecimales se representan siempre terminados en H osea 2DH seria nuestro numero, pero la H no tiene ningun valor mas que el de informar que se trata de un numero hexadecimal.
6. El ASCII y el BCD
Introducción
El ASCII
El ASCII o American Standard Code for Information Interchange (Codigo estandar americano para el intercambio de informacion) es una serie completa de los caractere y numeros que forman las palabras, a los cuale se les da un determinado numero que va desde el 0 al 255 y con los cuales se realizan intercambio de informacion independientemente del ordenador y tipo de sistema operativo que utilizemos.
Este sistema es utilizado normalmente para texto , y su almacenamiento en memoria o fichero es de un byte por cada letra/numero/simbolo de forma que la palabra "hola" ocuparia 4 bytes, uno por cada una de las letras que la componen.
Tabla ASCII
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33 !
34 "
35 #
36 $
37 %
38 &
39 '
40 (
41 )
42 *
43 +
44 ,
45 -
46 .
47 /
48 0
49 1
50 2
51 3
52 4
53 5
54 6
55 7
56 8
57 9
58 :
59 ;
60 <
61 =
62 >
63 ?
64 @
65 A
66 B
67 C
68 D
69 E
70 F
71 G
72 H
73 I
74 J
75 K
76 L
77 M
78 N
79 O
80 P
81 Q
82 R
83 S
84 T
85 U
86 V
87 W
88 X
89 Y
90 Z
91 [
92
93 ]
94 ^
95 _
96 `
97 a
98 b
99 c
100 d
101 e
102 f
103 g
104 h
105 i
106 j
107 k
108 l
109 m
110 n
111 o
112 p
113 q
114 r
115 s
116 t
117 u
118 v
119 w
120 x
121 y
122 z
123 {
124 |
125 }
126 ~
127
128 €
129
130 ‚
131 ƒ
132 „
133 …
134 †
135 ‡
136 ˆ
137 ‰
138 Š
139 ‹
140 Œ
141
142 Ž
143
144
145 ‘
146 ’
147 “
148 ”
149 •
150 –
151 —
152 ˜
153 ™
154 š
155 ›
156 œ
157
158 ž
159 Ÿ
160
161 ¡
162 ¢
163 £
164 ¤
165 ¥
166 ¦
167 §
168 ¨
169 ©
170 ª
171 «
172 ¬
173
174 ®
175 ¯
176 °
177 ±
178 ²
179 ³
180 ´
181 µ
182 ¶
183 ·
184 ¸
185 ¹
186 º
187 »
188 ¼
189 ½
190 ¾
191 ¿
192 À
193 Á
194 Â
195 Ã
196 Ä
197 Å
198 Æ
199 Ç
200 È
201 É
202 Ê
203 Ë
204 Ì
205 Í
206 Î
207 Ï
208 Ð
209 Ñ
210 Ò
211 Ó
212 Ô
213 Õ
214 Ö
215 ×
216 Ø
217 Ù
218 Ú
219 Û
220 Ü
221 Ý
222 Þ
223 ß
224 à
225 á
226 â
227 ã
228 ä
229 å
230 æ
231 ç
232 è
233 é
234 ê
235 ë
236 ì
237 í
238 î
239 ï
240 ð
241 ñ
242 ò
243 ó
244 ô
245 õ
246 ö
247 ÷
248 ø
249 ù
250 ú
251 û
252 ü
253 ý
254 þ
255 ÿ
Esta es la tabla de codigos de ASCII, aunque del 1 al 31 no se vea nada, en realidad si lo hay pues son codigos de control del propio sistema, el 32 no se ve pero es el codigo del espacio.
No se debe confundir la taba ASCII con la ANSI que es otra tabla tambien de caracteres parecida al ASCII pero con codigos distintos a partir del 128, desde el 1 al 128 son las dos tablas iguales pero a partir del 128 cada una de ellas es distinta.
El BCD
El BCD o Binary Coded Decimal (Codigo Binario-decimal) es un sistema que se utiliza para representar numeros de forma que cada numero ocupe 4 bits , osea que en un byte podriamos almacenar dos numeros en vez de uno como el sistema ASCII, para ello se utiliza el siguiente sistema:
Un byte son 8 bits y por lo tanto si lo dividimos en grupos de 4 obtenemos dos grupos de 4 bits por byte.
Pues ahora recordemos el sistema binario de la leccion anterior y tenemos la siguiente tabla:
0000 0
0001 1
0010 2
0011 3
0100 4
0101 5
0110 6
0111 7
1000 8
1001 9
Son los numeros del 0 al 9 en binario, solo necesitamos del 0 al 9 pues los demas son combiaciones de ellos, osea, el 10 es el 1 y el 0 y asi cualquier numero.
Pues como podemos observar con tan solo 4 bits podemos reprentar cualquier numero del 0 al 9, y entonces tenemos que si tenemos 2 grupos de 4 bits por caba byte, entonces podemos almacenar dos numeros en byte en vez de uno como con el ASCII, para ello supongamos que queremos al macenar el numero 90.
Sacamos el 9 que es 1001 y el 0 que es 0000, entonces los unimos y tenemos:
10010000 al contar tenemos 8 bits y hemos almacenado dos numeros dentro del byte.
Evidentemente por cada byte podemos almacenar del 0 al 99, si quieremos almacenar un numero mayor tendriamos que usar los bits de otro byte y juntarlos, pero por ejemplo para almacenar el numero 356.999 osea que tenemos 6 numeros, necesitariamos 6 bytes, mientras que con el sistema de bits tan solo necesitariamos 3 bytes (2 numeros por cada byte).
7. Los registros de la CPU
La CPU tiene 14 registros internos, cada uno de 16 bits.
Los primeros cuatro, AX, BX, CX, y DX son registros de uso general y tambien pueden ser utilizados como registros de 8 bits, para utilizarlos como tales es necesario referirse a ellos como parte superior y parte inferior del regitro, mediante AH y AL, que son alto (high) y bajo (low) del registro AX. De la misma forma se hace referencia a los registros de proposito general (BX, CX y DX.).
Los registros son conocidos por sus nombres específicos:
Cada uno de estos registros cumple una determinada funcion, aun cuando alguno de ellos lo podamos usar para dos tareas determinadas, cada uno de ellos ya tiene asignada una determinada funcion o tarea.
8. Las Interrupciones
Las interrupciones ocurren muy seguido, sencillamente la interrupción que actualiza la hora del día ocurre aproximadamente 18 veces por segundo. Para lograr administrar todas estas interrupciones, el ordenador cuenta con un espacio de memoria, llamado memoria baja, donde se almacenan las direcciones de cierta localidad de memoria donde se encuentran un juego de instrucciones que la CPU ejecutará para despues regresar a la aplicación en proceso.
En los programas anteriores hicimos uso de la interrupcion número 20H para terminar la ejecución de nuestros programas, ahora utilizaremos otra interrupción para mostrar información en pantalla:
El manejo directo de interrupciones es una de las partes mas importantes del lenguaje ensamblador, ya que con ellas es posible controlar eficientemente todos los dispositivos internos y externos de un ordenador gracias al completo control que se tiene sobre operaciones de entrada y salida.
9. Los segmentos de memoria
La arquitectura de los procesadores x86 obliga al uso de segmentos de memoria para manejar la información, el tamaño de estos segmentos es de 64kb.
El motivo es que teniendo en cuenta que el tamaño máximo de un número que puede manejar el procesador esta dado por una palabra de 16 bits o registro, no sería posible accesar a más de 65536 localidades de memoria utilizando uno solo de estos registros, ahora, si se divide la memoria de la pc en grupos o segmentos, cada uno de 65536 localidades, y utilizamos una dirección en un registro exclusivo para localizar cada segmento, y entonces cada dirección de una casilla específica la formamos con dos registros, nos es posible accesar a una cantidad de 4294967296 bytes de memoria, lo cual es, en la actualidad, más memoria de la que veremos instalada en una PC.
Aunque pueda parecer que se puedan producir errores, no es asi, el sistema de funcionamiento es muy sencillo:
Tenemos 3 segmentos de memoria, por que teniamos 170 Kb de memoria y entonces los hemos dividido de forma que podamos acceder a todos ellos, para esto hemos creado 2 segmentos de 64 Kb que son 128 y un tercer segmento de 42 Kb.
Y ahora tenemos que accedemos a la posicion 40000, pero tenemos tres posiciones
una en el segmento uno, en el dos y en el tres, para diferenciar cual de las tres es, tenemos los registros de
segmento que me indican que segmento contiene la direccion correcta.
Con esta operacion tan sencilla, solo necesitamos saber cual es la direccion y de que segmento para poder guardar o sacar un dato , pero con esto podemos acceder a toda la memoria que tengamos.
Para que el ensamblador pueda manejar los datos es necesario que cada dato o instrucción se encuentren localizados en el área que corresponde a sus respectivos segmentos. El ensamblador accesa a esta información tomando en cuenta la localización del segmento, dada por los registros DS, ES, SS y CS, y dentro de dicho registro la dirección del dato específico. Es por ello que cuando creamos un programa empleando el Debug en cada linea que vamos ensamblando aparce algo parecido a lo siguiente:
1CB0:0102 MOV AX,BX
En donde el primer número, 1CB0, corresponde al segmento de memoria que se está utilizando, el segundo se refiere la la dirección dentro de dicho segmento, y a continuación aparecen las instrucciones que se almacenaran a partir de esa dirección.
La forma de indicarle al ensamblador con cuales de los segmentos se va a trabajar es por medio de las directivas .CODE, .DATA y .STACK.
El ensamblador se encarga de ajustar el tamaño de los segmentos tomando
como base el número de bytes que necesita cada instrucción que va ensamblando, ya que sería
un desperdicio de memoria utilizar los segmentos completos. Por ejemplo, si un programa unicamente necesita 10kb
para almacenar los datos, el segmento de datos unicamente sera de 10kb y no de los 64kb que puede manejar. 10. Instrucciones de asignacion Instrucción MOV Estas instrucciones se utilizan para asignar datos entre celdas de memoria, registros
y la pila(stack). MOV Destino,Fuente Este es el modelo estandar de declaracion de la instruccion, Destino hace referencia
al lugar donde se moverán los datos y fuente es el lugar de donde se obtendran los datos. Ejemplos: Con esta instruccion se mueven cadenas de bytes o palabras desde el fuente, direccionada
por SI, hasta el destino direccionado por DI. MOVS Este comando no necesita parametros pues toma la dirección fuente del contenido
del registro SI y como destino el contenido de DI.
Se inicializan los valores de SI y DI con las direcciones de las variables VAR1
y VAR2 asignadas a cada caso, despues se ejecutar MOVS y se copia el contenido de VAR1 a VAR2. Los comandos MOVSB y MOVSW se utilizan de la misma forma que MOVS, solo que mientras
que MOVSB mueve un byte MOVSW mueve una palabra.
Instrucción MOVS (MOVSB) (MOVSW)