Documenti di Didattica
Documenti di Professioni
Documenti di Cultura
Hola Gente!
Por haberse prendido de entrada en esta idea, y por haber hecho sugerencias,
y por haberme hecho decidirme. !!!
Bueno, Mmmm...
Vamos a empezar por algo que quizás muchos ya sepan, pero como es
indispensable, igual se lo tengo que contar:
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...
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 -> 006C
DS -> + 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.
000C 046C
+0046 +0000
------- --------
0046C = 0046:000C 0046C=0000:046C
023C 005C
+0023 +003F
-------- --------
0046C = 0023:023C 0046C = 003F:005C
If Mem[$0040:$0049]=7 Then { Modo Texto } para leer un byte (el '$' significa
Hexa)
Mem[$B800:(Columna+File*80)*2]:='A' Para la letra
Mem[$B800:(Columna+File*80)*2+1]:=White; Para el atributo
MemW[$0040:0000] (o MemW[$40:0]) para un Word
MemL[$40:$6C] para un LongInt (o DWord Signado)
Desde C:
ahora si:
if (peekpokel(0x40,0x6C)==0x0F0342034) {} y
peekpokel(0x40,0x6C)=0
para un DWord
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 tan
fá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 80286
vamos 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:
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
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.
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.
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:
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).
Por ultimo:
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:
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.
En TASM, hacer un .COM se simplifica mucho, se limita a poner al principio del
programa:
EJEMPLO.ASM:
Por otro lado, están las variables y labels, que son ayudas que nos da el
compilador. Si esto no existiera tendríamos que recordar, en vez del nombre de
una variable, su posición, y además tendríamos que calcularla a mano. De
todo esto se ocupa el compilador (ensamblador). Este programa, funciona y
hace algo, pero, claro, no todo es tan simple. Traten de analizarlo antes de
ejecutarlo, para eso van a necesitar una lista de interrupciones. Si no la tienen,
vayan consiguiéndola, casi todo programa en assembler se aprovecha de las
interrupciones. Tengan en cuenta que en un .COM, al comienzo, DS, ES, CS y
SS tienen el mismo valor, y claro que lo conservan a nos ser que se lo
cambiemos nosotros.
Con estos básicos conocimientos ya se pueden ir dando maña para hacer otras
cosas, traten de ver si les sale algo. Piensen que quieren hacer y como, si hay
algo que no se les ocurre, traten otra vez, que esa es la mejor forma de
aprender, tratar hasta que salga, si no les sale, pregunten, que por esta vez
puede pasar... :)
Acá esta lo que van a necesitar, sobre las interrupciones que usa el programa.
.MODEL TINY
.CODE
ORG 100h
NomProg:
END NomProg
Entre el Label "NomProg:" y el fin del archivo "END NomProg", hay que poner
todo el código del programa en si. Mas adelante vamos a ver que significa cada
una de estas
cosas, y como cambiarlas para crear EXEs de distintos tipos, por ahora,
recuérdenlas para poder probar sus cositas.
Como en todo lenguaje, además de saber como programar hay que saber las
órdenes, para poder decirle que hacer a la máquina. Yo voy a ir explicando de
a poco todas las
órdenes que se me ocurran, pero como buena guía de consulta sugiero la
Norton Guide de Assembler, (Salió una versión nueva que esta en Kansas
City). Esta NG es una buena guía y explica bastante bien que hace cada orden,
igualmente, me ofrezco para que si alguien quiere hacer algo en Assembler, y
le surge alguna duda, me lo pregunte; ahora tenemos el área Assembler.
Donde:
Mnemónico es la orden en si misma, es llamada mnemónico porque se utilizan
estas letras, para no tener que recordar el número de la orden. Son letras que
parecen
no tener significado, pero si lo tienen, y una vez que se lo conoce, no solo se
recuerdan, también se aciertan algunos que no se conocían antes...
MOV Param1,Param2
MOV DS:[1200],ES:[3020]
NO es una orden valida. Si se desea hacer esto, hay que desdoblarlo en dos
ordenes:
MOV AX,ES:[3020]
MOV DS:[1200],AX
Si lo que se quería mover era un word. O cambiando por AL (en vez de AX) si
se quería mover un byte. (AX y AL son ejemplos, podría haber sido cualquier
registro). Hay otras formas de hacerlo sin destruir el contenido de AX, ya lo
vamos a ver. Hay otra limitación mas, a un registro de segmento, no se le
puede asignar directamente un valor, sólo esta permitido asignarle el contenido
de otro registro, pero que tampoco sea de segmento, es decir, para hacer:
DS=CS
INT IntNum
Bueno, entonces tenemos que las Interrupciones son rutinas que nos facilitan
la programación, son rutinas ya instaladas en la máquina, que hacen tareas
complejas por nosotros. Y como tales rutinas, llevan parámetros, pero ¿donde?
si la orden es: INT IntNum Las Interrupciones toman los parámetros de los
registros, y su resultado también vuelve en registros...
Las Interrupciones BIOS, por ejemplo son las 13h, 10h, 11h, 12h, 16h, 17h,
19h, etc...
Las DOS, son las 20h, 22h, 23h, 24h, 25h, 26h, 27h, 28h, 29h.
Pero la fundamental es la INT 21h, por medio de ella es posible hacer casi todo
lo necesario para llevar un programa a la marcha... Pero ¿como puede ser que
con una sola INT sea posible hacer tantas cosas? Muy simple, muchas INTs
tienen funciones y sub-funciones, las cuales se identifican según el valor de los
registros, normalmente AX o AH son utilizados para decir a que número de
función/sub-función queremos acceder. Así, por ejemplo, la INT 21h si AH =
09h, sirve para mostrar un String en pantalla, pero si AH = 39h sirve para hacer
un directorio. Como ven, la utilidad de una INT no solo esta limitada a una
función, si no que puede tener múltiples usos.
Nombre XX Valor
Nombre es el nombre con el que nos vamos a referir, mas tarde a esa posición
de memoria, es el nombre de la variable. XX, es la definición del tipo de la
variable:
etc...
La vez pasada me pase con el programa, quizás era un poco complicado, pero
era para que tengan una idea de como es un programa en Assembler, quizás
muchos le encontraron la vuelta y animaron a largarse a programar. Sugiero,
que traten de entender programitas en Assembler, no es ninguna ciencia este
lenguaje, es uno de los mas simples, pero lleva su tiempo encontrarle la
vuelta... Acá va otro programita, también rebuscado, pero, yo se que si no
entienden van a preguntar... O no? (:
.model tiny
.code
org 100h
Ejemplo_2:
jmp Comienzo ; Salteo el área de datos, si se
; ejecutara (no hay diferencia entre datos y
; programas) seria una colgada mas...
Matching DB '*.*',0
; Array: Una cadena mas un número.
Comienzo:
mov ah,4eh
mov cx,0
mov dx,offset Matching ; DX = Offset de Matching
; DS, al entrar a un .COM es igual a CS.
int 21h ; Que hace?
jc Error ; Si esta prendido el Carry Flag, salto
mov ah,2fh
int 21h ; Y esta?
mov byte ptr es:[bx+1eh+13],'$'
; Byte ptr para decirle que a lo que apunta
; [bx+13] es un byte, podría ser un word ptr, o
; un dword ptr. El '$' es el fin de una cadena
; a imprimir por el DOS.
mov dx,es
mov ds,dx
lea dx,[bx+1eh]
; Pone en DX el Offset al que apunta
; BX+13, es decir DX = BX+13
mov ah,9
int 21h ; Imprime una cadena.
Error:
mov ax,4c00h
int 21h
END Ejemplo_2
* Fuera de tema, pero interesante quizás, es el hecho de ampliar cada vez mas
este concepto de reutilización de código hasta llegar al Windows, donde ningún
programa
trae las rutinas para hacer menús, botones o ventanas, pero todos las utilizan.
También crearon las DLL (Dinamic Link Libraries) que pueden ser reutilizadas
por varios programas, pero claro, hay que saber aprovecharlo...
Bueno, espero que entiendan algo, porque la verdad, que aunque lo leí varias
veces, tengo mis dudas, todavía...
Devuelve:
AX = Codigo de Error, si El Carry Flag (CF) esta en uno.
DTA = Contiene la informacion de retorno.
--------------------------------------------------------------------------
[...]
Devuelve:
ES:BX -> Puntero con la dirección de la DTA actual.
--------------------------------------------------------------------------