FASM (Flat assembler), es un programa compilador y ensamblador de código fuente en lenguaje ensamblador. La sintaxis aceptada por el mismo depende de la versión del mismo, "1.68" es la versión que utiliza la versión actual del editor TEIMSI.
Compilar un archivo en lenguaje ensamblador es relativamente sencillo, algunos ejemplos de programas se hallan en la carpeta "EXAMPLES" dentro de la carpeta de "FASM".
El ensamblador permite crear también archivos ejecutables con formato .ELF utilizados en sistemas operativos Linux (específicamente Debian y Ubuntu). La principal diferencia entre programas de Linux y Win2000/XP/7/... es que en Linux no se cuenta con la sección de importación de librerías "API", sino que hay que incluir funciones de la librería de "C" (se espera dar mejor soporte a la creación de programas en sistemas Linux en las siguientes versiones del editor para compilar archivos .ELF usando la librería "xlib" para ventanas y controles).
Volviendo al título del tema, un ejemplo de programa sencillo se muestra a continuación.
; //############################################################ format PE GUI 4.0 include 'include\win32a.inc' section '.text' code readable executable invoke MessageBoxA,0,_message,_caption,MB_ICONINFORMATION invoke ExitProcess,0 section '.data' data readable writeable _message db 'Hola mundo!',0 _caption db 'Mensaje',0 section '.idata' import data readable writeable library kernel32,'KERNEL32.DLL',\ user32,'USER32.DLL' import kernel32,\ ExitProcess,'ExitProcess' import user32,\ MessageBoxA,'MessageBoxA' ; //############################################################
Para compilarlo, luego de colocar el texto en un archivo llamado "ejemplo.asm" se debe ejecutar el comando de línea: "fasm.exe ejemplo.asm". El editor nos simplifica la tarea pues basta con abrir con el editor un archivo llamado "ejemplo.tsi", ponerle la instrucción alert("Hola mundo") y compilar con tecla F5.
El ensamblador nos permite crear un programa ejecutable (con extensión .exe) o librería dinámica a partir del contenido creado por TEIMSI.
El contenido creado por TEIMSI incluye una carpeta cuyo nombre empieza con el mismo nombre del archivo de código TEIMSI con extensión .tsi, la cual será puesta en la misma carpeta de tal archivo tsi y su nombre se sigue de la cadena "_asm_files"; por ejemplo si el archivo del proyecto es "UltraProgri.tsi" la carpeta que el compilador creará será "UtraProgri_asm_files", en ella pondrá los archivos necesarios para que el ensamblador haga su creación de salida. El archivo del proyecto puede tratarse de un archivo con otra extensión como por ejemplo ".thp", o ".php", o .".tco" o ."asm" porque son aceptadas por el editor de TEIMSI. Pero como convención se ha preferido utilizar la extensión .tsi para indicar el archivo principal de un proyecto TEIMSI, así como la extensión .thp para archivos con código TEIMSI adicional a un proyecto, y la extensión ".tco" para los archivos "objeto" compilados por TEIMSI. La extensión ".php" se aplica para texto en lenguaje P.h.p o bien para texto de contenido personalizado que incluya por ejemplo comentarios y ejemplos de código, mientras que los archivos ".asm" tienen código en lenguaje ensamblador, para ser incluidos mediante la instrucción: include "nombre_de_archivo"; disponible en el conjunto de instrucciones del ensamblador.
Para comenzar a crear un proyecto TEIMSI, basta con crear una carpeta con el nombre del proyecto y crear adentro un archivo con la extensión ".tsi", que contendrá el código TEIMSI principal así como posibles instrucciones includec() y include_tco() para incluir otros archivos al proyecto, archivos con extensión ".thp" o ".tco". (Por más información ver Inclusión de archivos y Carpetas. Constantes predefinidas de sendas).
Luego de creados la carpeta y el archivo, abrimos el archivo tsi con el editor de TEIMSI haciéndole doble clic, y ponemos (como ejemplo) el siguiente texto:
; //######################################################################################################## var mensaje1="Hola mundo! número77="; function mostrar(msg){ alert(msg) } var numero77=0; // crea una variable de tipo número entero. _direct{ mov eax, 11 mov ebx, 7 mul ebx mov [numero77+reg.vo], eax } mostrar(mensaje1+numero77) @eof_file ; //########################################################################################################
Luego presionamos la tecla F5 (o bien hacemos clic en el menú en Compilar y luego "Crear y ejecutar .exe ..."), el programa ".exe" que será creado tendrá el nombre de archivo .tsi del proyecto.
Hagamos un análisis del código escrito. Distinguimos el modulo raíz como el código que excluye al código escrito las funciones, por ello para el compilador entiende que luego de la instrucción "var mensaje1=..." viene la instrucción "var numero77=0". En la primer línea se escribió:
var mensaje1="Hola mundo! número77=";
Esta instrucción declara en el módulo raíz la variable "mensaje1", que contiene un texto estático (de longitud 21 bytes). Incluyendo variables de cadena estáticas, hay en TEIMSI tres tipos de modalidades para variables (ver Variables de TEIMSI).
En las siguientes líneas se escribió:
function mostrar(msg){ alert(msg) }
Se trata de una función que no devuelve ningún valor, en efecto la instrucción alert(mostrar("a")==undefined) mostrará luego de mostrar el texto "a" un "true" (verdadero). La función "mostrar" sólo muestra una cadena y luego termina. En la siguiente línea se escribió:
var numero77=0;
Ello declara que "numero77" es una variable que contendrá un número entero largo, pudo tratarse de un booleano poniendo "var numero77=false" o bien de un número de precisión doble de coma flotante poniendo "var numero77=0.0" (nótese el ".0" adicional), pero se ha elegido un entero porque representa la forma más sencilla de acceder (leer y escribir) a su información por medio de ensamblador. En las siguientes líneas se escribió:
_direct{ mov eax, 11 mov ebx, 7 mul ebx mov [numero77+reg.vo], eax }
La instrucción "_direct{" manda las siguientes líneas directo al texto de código en ensamblador hasta que encuentra un carácter "}" solitario. En efecto si se comenta esa linea poniendo " } // Comentando", se producirá un error de sintaxis porque el compilador, no percibe el final del bloque de líneas de ensamblador. Tampoco sería bien visto declarar " _direct{ // Comentario".
El lenguaje válido para dicho bloque es el lenguaje ensamblador de FASM, para el ejemplo se pone primero el entero 11 en el registro eax, luego 7 en el registro ebx, y luego se hace un "mul ebx" que en efecto pone en el registro edx "la parte alta" de la multiplicación y en eax "la parte baja", por ahora nos interesa sólo eax, porque el resultado no excederá el valor de los 32 bits menos uno; eso es 4294967295. Luego pone el resultado de eax en el espacio que determina el valor de la variable "numero77" cuyo desplazamiento es indicado por "+reg.vo". Todas las variables en TEIMSI son de tipo "variant" y para determinar su modalidad, tipo de datos, valor y longitud se hace uso de una estructura de 16 bytes o bien 4 dwords. La estructura se ha llamado "reg" o "regvar" la cual equivale a la estructura siguiente:
reg
struct reg mo dd ? ; Contiene la modalidad ty dd ? ; Contiene el tipo de datos (booleano, entero, doble, cadena, etc) vo dd ? ; Contiene el valor para booleanos, enteros y dobles; o bien el manejador de la cadena. nu dd ? ; Contiene la longitud de la cadena o bien la parte del número flotante de precisión doble (de 8 bytes). ends
La estructura "regvar" es equivalente a "reg":
struct regvar vmode dd ? vtype dd ? vofix dd ? vnull dd ? ends
Entonces luego de ejecutar "mov [numero77+reg.vo], eax", la variable numero77 tiene el valor del entero que tenía el registro eax el cual es 11*7=77.
En la siguiente línea se escribió:
mostrar(mensaje1+numero77)
Nos preguntaríamos que pasó con el carácter ";" a la derecha de la instrucción, pero con TEIMSI tal indicativo es opcional (incluso al compilarse se considera como el inicio de una nueva línea por lo cual carece de interés práctico a excepción de expresar múltiples instrucciones por renglón).
Con la llamada a la función "mostrar" se mostrará la salida de la suma de "mensaje1" con "numero77", numero77 es la variable con el entero 77. Cabe indicar que así como en otros lenguajes conocidos como "Jscript", puesto que la variable "mensaje1" es de tipo cadena cualquier cosa a la cual se sume a su derecha (o bien cualquier cosa que se sume a su izquierda) resultará en una cadena. El valor de numero77 es automáticamente convertido en cadena, siendo el resultado una cadena.
Y en la última instrucción se escribió:
@eof_file
Es similar a @eof_module (que indica el final del módulo raíz) y se utiliza así como está (sin espacios a la izquierda y sin nada a la derecha). Para TEIMSI ésta línea, la línea existente que le sigue; indica una sola cosa y es el final del texto programado del proyecto. De esta manera el compilador desprende el texto ajeno a TEIMSI para crear el ejecutable, librería u objeto compilado TEIMSI de salida.
Esta instrucción es de gran utilidad para situar luego de ella textos programados, comentarios o bien texto que el programador desee incorporar en el archivo del proyecto.
FASM es una herramienta potencial y sencilla cuyo conocimiento es recomendado para programadores profesionales de código TEIMSI, aplicaciones sencillas pueden encontrarse en la carpeta "EXAMPLES" de FASM. Es posible crear un Script que haga uso del lenguaje ensamblador solamente. El siguiente es un ejemplo "sample.tsi":
;// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Lo que hace este ejemplo en ensamblador es sumar los inversos cuadrados desde 1 hasta 1000 (suma que tiende a pi*pi/6) y luego muestra el entero que es la suma multiplicada por 10000. place_idata{ numsum dq 0.0 numdd dd 0 } _direct{ mov ecx, 1000 @@: mov [numdd], ecx fild [numdd] fld st fmulp st1, st fld1 fdivrp st1, st ; Que en otras palabras es hallar 1/(ecx*ecx) y sumarlo a la variable numsum. fadd [numsum] fstp [numsum] dec ecx jnz @b mov [numdd], 10000 fld [numsum] fimul [numdd] ; Multiplica la suma por 10000. fistp [numdd] MOSTRAR2 [numdd] ; Esta macro "MOSTRAR2" muestra valor/registro de 32 bits, en este caso resulta 16439. } ;Observación importante: ; La instrucción "fistp [numdd]" guarda el contenido del registro st del coprocesador pero ; el redondeo de un número a entero está determinado por las banderas del registro ; del coprocesador "de control" que se acceden por la instrucción "fldcw" y "fstcw". Las ; aplicaciones TEIMSI cambian en el código base (el que antecede al del usuario) la ; modalidad de redondeo de enteros de coprocesador: en forma predeterminada cuando inicia ; un programa se redondea el número a un entero con "fistp [numdd]" pero entonces se ; redondea hacia el entero inferior en valor, por ejemplo 1.6 pasa a ser 1. Para ajustar ; ese comportamiento se debe cargar el modo predeterminado segun indican las siguientes ; tres instrucciones: ; ; fldcw [public_old_fpu_cw] ; Carga el registro de control al predeterminado, para redondear al entero cercano. ; fistp [numdd] ; fldcw [public_new_fpu_cw] ; Restaura el registro de control al modificado (que es utilizado por las funciones de TEIMSI) para redondear al menor entero cercano. ; ; El resultado es un entero que equivale al que muestra la siguiente instrucción en TEIMSI: ; ; alert(parseint(10000*numsum)) // El siguiente es un ejemplo "sample2.tsi", tiene código equivalente en TEIMSI: var numsum=0.0 for(var t=1000;t>=1;t--){ numsum+=1/(t*t) } alert(floor(10000*numsum)) // Muestra "16439". En otras palabras: alert_ex(floor(10000*numsum), "La suma es:", _mb_ok) // Obsérvese que en este caso con sólo 5 líneas de código TEIMSI se hace lo que aproximadamente 23 líneas de ensamblador, la ventaja del ensamblador es ser más veloz. ;// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -