Primero y ante todo decir que el código que voy a implementar no usa muchas directivas ya creadas en MASM y TASM. Debido a que yo daba clases de Ensamblador y a que comencé con ASM, preferí inicialmente crear directivas propias a usar las que tenía el compilado TASM 2.0. (rutina en vez de proc, modelo en vez de model, _data en vez de .data... etc). De esta forma podía demostrar que realmente las directivas eran definibles.
Por eso, aunque muchos digan que lo que estoy haciendo ya está construido en el compilador, quiero que vean que muchas de las directivas del TASM 2.0 son prescindibles, y que un buen programador puede usar un pequeño compilador y realizar cualquier tarea.
Esto es importante para los que hemos tenido que compilar con otros compiladores mucho más simples, por ejemplo para otros microprocesadores, y en los que hemos querido aplicar las mismas reglas de programación que con el 8086.
Por eso ruego paciencia. Primero aplicaré mis directivas, y veremos como resulta un código casi idéntico al del ensamblador TASM 2.0. Así veremos, sin necesidad de desensamblar, lo que realmente hacen esas directivas, que simplifican el código, pero que ocultan cosas, al programador, que se deben conocer y entender.
Sin embargo, si pensamos usar standares y acoplarnos a códigos de alto nivel, nos conviene usar las directivas incorporadas en el compilador TASM, para compatibilizar de forma sencilla el código con otros lenguajes de alto nivel.
Por eso a partir de la entrada 50 del blog haré una comparativa de las dos formas y comenzaré a aplicar directamente el código TASM 2.0.
FUNCIONES ACOPLADAS EN MAIN.MAC
Todo lo que iré poniendo ahora será para generar ficheros .exe. Más adelante, Cuando hayamos implementado programas ya con las directivas del TAM completas, lo generaremos en .com.
Como siempre habrá una cabecera ".mac", bueno ".inc" si se desea, con las macros y definiciones de las funciones. Cuando simplifique los programas a versión completa las llamaré ".inc" para distinguirlas.
En esta cabecera, como cabecera de inicialización de programa incluyo algunas sentencias de precompilación, que servirán para asignar memoria, segmentos, pila y por supuesto la estructura de lista de argumentos.
Rutina (equivale a proc+ arg en TASM)
Permite crear estructura para el paso de parámetros través de pila, de esta manera será muy sencillo declarar una función y pasarla hasta 10 variables . Declara al tiempo la función como publica, al modo que lo hace "C", pero sin apenas darnos cuenta. Se verá mejor cuando coloque la función Graphics.
Permite crear estructura para el paso de parámetros través de pila, de esta manera será muy sencillo declarar una función y pasarla hasta 10 variables . Declara al tiempo la función como publica, al modo que lo hace "C", pero sin apenas darnos cuenta. Se verá mejor cuando coloque la función Graphics.
_modelo (equivale a model en TASM)
Sirve para definir el tipo de programa .com o .exe. si no se usa es .exe. Esto se usará paa definr los segmetos
EN esta macro creamos una constante que podemos identificar en la macro _data, para asignar la zona de datos a DS o CS, según el programa sea . com o .exe. Esto no evitará tener que utilizar dos librerías (una para como y otra para exe) , si no queremos tener que compilar todas las librerías cada vez que hagamos un programa.
Pero sí nos permitirá tener un programa único para cada librería y una de muy forma simple compilarlo para uno y otro modo.
_code (equivale a .code en TASM)
Nos define el comienzo de la zona de código.
_data (equivale a .data en TASM)
Nos define el comienzo de la zona de datos. Puede estar antes o después del code, aunque en las funciones es recomendable que esté detrás.
_end (equivalente a end en TASM)
Cierra el programa en caso un programa se debe indicar el nombre de la rutina principal
_stack (equivale a .stack en TASM)
Nos inicializa la pila al tamaño indicado en NUMERO
Todas estas macros crean un entorno de trabajo equivalente al estandar de TASM y MASM, pero sin usar su directivas. Como solo usa if y macro con ellas puede crearse el entrono equivalente a TASM pero en el compilador inicial ASM. Todas estas macros pueden ser eliminadas y substituidas por las directivas equivalentes del TASM 2.0. Cosa que haremos más adelante.
Con estas macros podemos ver como se pueden crear no solo llamadas a funciones sino nuevas directivas de compilación. A partir de la entrada 50 del blog veremos como crear con las macros directivas de PROGRAMACIÓN ESTRUCTURADA tales como FOR o WHILE.
Otras macros más interesantes aún pues no las tiene el TASM son:
Sirve para definir el tipo de programa .com o .exe. si no se usa es .exe. Esto se usará paa definr los segmetos
EN esta macro creamos una constante que podemos identificar en la macro _data, para asignar la zona de datos a DS o CS, según el programa sea . com o .exe. Esto no evitará tener que utilizar dos librerías (una para como y otra para exe) , si no queremos tener que compilar todas las librerías cada vez que hagamos un programa.
Pero sí nos permitirá tener un programa único para cada librería y una de muy forma simple compilarlo para uno y otro modo.
_code (equivale a .code en TASM)
Nos define el comienzo de la zona de código.
_data (equivale a .data en TASM)
Nos define el comienzo de la zona de datos. Puede estar antes o después del code, aunque en las funciones es recomendable que esté detrás.
_end (equivalente a end en TASM)
Cierra el programa en caso un programa se debe indicar el nombre de la rutina principal
_stack (equivale a .stack en TASM)
Nos inicializa la pila al tamaño indicado en NUMERO
Todas estas macros crean un entorno de trabajo equivalente al estandar de TASM y MASM, pero sin usar su directivas. Como solo usa if y macro con ellas puede crearse el entrono equivalente a TASM pero en el compilador inicial ASM. Todas estas macros pueden ser eliminadas y substituidas por las directivas equivalentes del TASM 2.0. Cosa que haremos más adelante.
Con estas macros podemos ver como se pueden crear no solo llamadas a funciones sino nuevas directivas de compilación. A partir de la entrada 50 del blog veremos como crear con las macros directivas de PROGRAMACIÓN ESTRUCTURADA tales como FOR o WHILE.
Otras macros más interesantes aún pues no las tiene el TASM son:
dim - Permite crear una variable compleja tomando una estructura como plantilla.
asigdata - Nos permitirá reasignar una zona de memoria al segmento de datos.
of_reg- devuelve la dirección del registro indicado de una tabla que se encuentre en memoria, creada bajo una estructura.
exit- Fin de programa con salida al sistema.
encriptar y desencriptar- Funciones especiales que permitirán encriptar parte de los datos del programa, de manera que no sean legibles mediante un Dump del código, pero si sean legibles una vez inicializado el programa.
string, char, integer Definición de los tres tipos de variables más importantes. Serán usadas en la inicialización de los datos, por lo que están incluidas en el main como definición de variables.
main - comienzo del programa. Es la encargada de llamar a la linea de comandos si se definieron parámetros y la encargada de asignar la zona de datos si se definió con la macro _data. Así mismo prepara el PSP para que la salvar la dirección de vuelta al sistema.
Dentro del main existe una directiva por la cual si no se ha añadido la cabecera argum.mac, y por lo tanto no se ha definido la variable externa _line_comand, no se llamará a la función de la librería correspondiente. De esta forma, si no necesitamos argumentos no consumiremos código innecesario.
Main parece una forma de iniciar el programa pero es mucho más, en realidad inicializa el programa., reserva la memoria, recoge los argumentos de la línea de comando, y prepara la ejecución. Por ello tomé este nombre para crear una rutina especial, a la que llamaré siempre al inicio del programa ejecutable. Se encargará de inicializar la memoria, la pila del sistema y recoger los argumentos de la linea de comandos, para que posteriormente mediante argv y argc tratarlos.
En el caso del main no incluimos la función directamente en la cabecera, pues en este caso el tamaño de la función supera al código de llamada.
En el caso del main no incluimos la función directamente en la cabecera, pues en este caso el tamaño de la función supera al código de llamada.
Esta rutina no tendrá argumentos, y será llamada simplemente main.
Con esta cabecera estamos creando un entorno de trabajo equivalente al TASM completo, pero sin usar sus directivas más complejas.
MAIN.MAC
; Copyright (C) 2013 José Ángel Moneo Fernández ; This program is free software: you can redistribute it and/or modify ; it under the terms of the GNU General Public License as published by ; the Free Software Foundation, either version 3 of the License, or ; (at your option) any later version. ; This program is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ; GNU General Public License for more details. ; You should have received a copy of the GNU General Public License ; along with this program. If not, see <http://www.gnu.org/licenses/>.
;Declaracion de una rutina (función) con paso de variables
Esta macro crea una estructura de compilación para ser usada posteriormente en la recogida de los datos pasados a la función.
;define modelo .com o .exe para definir forma de colocar assume en _data
_modelo macro tipo
tipo equ 1
endm
Esta macro crea una estructura de compilación para ser usada posteriormente en la recogida de los datos pasados a la función.
;define modelo .com o .exe para definir forma de colocar assume en _data
_modelo macro tipo
tipo equ 1
endm
rutina macro nombre,arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8,arg9,arg10
nombre&p struc
dd ?
irp arg,<arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8,arg9,arg10>
ifnb <arg>
arg dw ?
endif
endm
nombre&p ends
public nombre
nombre proc near
push bp
mov bp,sp
push bp
mov bp,sp
endm
_end macro nombre
ends
end nombre
endm
;genera la cabecera de datos
_data macro
ifndef com
ends ;asegura el cierre del segmento anterior
data segment byte public 'data'
endif
endm
_code macro
code segment byte public 'code'
assume cs:code
ifdef com
assume ds:code
else
assume ds:data ; esto nos obliga a definir siempre un data aunque sea vacío
endif
endm
_end macro nombre
ends
end nombre
endm
;genera la cabecera de datos
_data macro
ifndef com
ends ;asegura el cierre del segmento anterior
data segment byte public 'data'
endif
endm
_code macro
code segment byte public 'code'
assume cs:code
ifdef com
assume ds:code
else
assume ds:data ; esto nos obliga a definir siempre un data aunque sea vacío
endif
endm
;genera una pila con el tama¤o indicado
_stack macro NUMERO
pila segment byte stack 'stack'
db NUMERO dup (0)
pila ends
assume ss:pila
endm
;coloca en pila de direccion del PSP para salir al dos y llama
;a la gesti¢n de la linea de comandos si se ha usado parametros
main macro
push ds
xor ax, ax
push ax
ifdef _lin_comand
call _lin_comand
endif
ifdef data
asigdata data
endif
endm
;asigna un nuevo segmento de datos de trabajo
asigdata macro etiq
assume ds:etiq
mov ax, etiq
mov ds, ax
endm
;dimensiona una tabla de numero de elementos con una estructura regis
dim macro tablas,regis,numero
tablas regis numero dup (<>)
endm
; devuelve la direccion de un registro en una tabla en memoria
of_reg macro dest,lista,numero
mov dest,offset lista[numero*type(lista)]
endm
;Sale al sistema devolviendo un codigo
exit macro codigo
mov ah,4ch
ifdif <codigo>,<al>
mov al,codigo
endif
int 21h
endm
;encripta una zona de datos con un codigo dado
encriptar macro inicio,bytes,codigo
ifndef _encriptar
extrn _encriptar:near
endif
xor ax,ax
push ax
mov ax,codigo
push ax
mov ax,bytes
push ax
lea ax,inicio
push ax
call _encriptar
add sp,8
endm
;desencripta una zona de datos con un codigo dado
desencriptar macro inicio,bytes,codigo
ifndef _encriptar
extrn _encriptar:near
endif
mov ax,28h
push ax
mov ax,codigo
push ax
mov ax,bytes
push ax
lea ax,inicio
push ax
call _encriptar
add sp,8
endm
;define una cadena de entrada de texto para el scanf
string macro nombre,long
nombre db long+1,0,long+2 dup (0)
endm
char macro var,ini
ifb <ini>
var db ?
else
var db ini
endif
endm
integer macro var,ini
ifb <ini>
var dd ?
else
var dd ini
endif
endm
; devuelve la direccion de un registro en una tabla en memoria
of_reg macro dest,lista,numero
mov dest,offset lista[numero*type(lista)]
endm
;Sale al sistema devolviendo un codigo
exit macro codigo
mov ah,4ch
ifdif <codigo>,<al>
mov al,codigo
endif
int 21h
endm
;encripta una zona de datos con un codigo dado
encriptar macro inicio,bytes,codigo
ifndef _encriptar
extrn _encriptar:near
endif
xor ax,ax
push ax
mov ax,codigo
push ax
mov ax,bytes
push ax
lea ax,inicio
push ax
call _encriptar
add sp,8
endm
;desencripta una zona de datos con un codigo dado
desencriptar macro inicio,bytes,codigo
ifndef _encriptar
extrn _encriptar:near
endif
mov ax,28h
push ax
mov ax,codigo
push ax
mov ax,bytes
push ax
lea ax,inicio
push ax
call _encriptar
add sp,8
endm
;define una cadena de entrada de texto para el scanf
string macro nombre,long
nombre db long+1,0,long+2 dup (0)
endm
char macro var,ini
ifb <ini>
var db ?
else
var db ini
endif
endm
integer macro var,ini
ifb <ini>
var dd ?
else
var dd ini
endif
endm
No hay comentarios:
Publicar un comentario
Si tienes algún comentario, duda o sugerencia, o si quieres aportar algún código creado a partir de las librerías expuestas aquí, por favor, indícamelo.