Un lenguaje de macros es un metalenguaje que generalmente se usa en otro lenguaje para generar código; en lugar de escribir una función, se escribe un miniprograma (”macro”) que escribe la función. En lenguajes como Lisp es un mecanismo realmente poderoso (y no demasiado difícil de usar, aunque tampoco trivial) que permite integrar compiladores a la medida dentro de cualquier programa, porque el lenguaje de macro es el mismo Lisp. En C es simplemente el preprocesador que ejecuta instrucciones #define, #include, etc.
En ensamblador, si bien el sistema de macros no es tan poderoso como en Lisp, se aprecia más que en C, principalmente por las limitaciones del lenguaje. Un ejemplo:
Supongamos que queremos dibujar una línea. En DOS x86 una forma burda de hacerlo es, para dibujar un pixel, guardar la columna en el registro CX, la fila en DX, y llamar una interrupción 10H. Hay más detalles, pero por ahora eso es suficiente. Para trazar la línea hay cuatro posibilidades:
Para hacer una línea horizontal de izquierda a derecha, se incrementa CX.
Para hacer una línea horizontal de derecha a izquierda, se decrementa CX.
Para hacer una línea vertical de arriba abajo, se incrementa DX.
Para hacer una línea vertical de abajo arriba, se decrementa DX.
Se podrían escribir cuatro funciones “línea”, una para cada caso. Pero es muy poquito código, se va a repetir muy seguido, y las llamadas frecuentes a CALL que esencialmente es un salto cercano van a afectar mucho el desempeño. Otra posibilidad es escribir un pedazo de código que se vea como
CICLO: INT 10H INC CX DEC SI ; longitud de la línea JNZ CICLO
y cortar y pegar, por ejemplo, si se quiere hacer una función rectángulo:
RECTANGULO PROC NEAR PUSH DI PUSH SI ; a la derecha CICLO1: INT 10H INC CX DEC SI ; longitud de la línea JNZ CICLO1 ; abajo CICLO2: INT 10H INC DX DEC DI ; longitud de la línea JNZ CICLO2 POP SI POP DI ; izquierda CICL3: INT 10H DEC CX DEC SI ; longitud de la línea JNZ CICLO3 ; arriba CICLO4: INT 10H DEC DX DEC DI ; longitud de la línea JNZ CICLO4 RET RECTANGULO ENDP
Eso es un fastidio. Escribí cuatro veces código que es casi exactamente el mismo, y me equivoqué un par de veces. Pero cumple con la plantilla:
CICLO: INT 10H OP REGISTRO DEC CONTADOR ; longitud de la línea JNZ CICLO
y por lo tanto se puede hacer con una macro (formato TASM):
MLINEA MACRO OP,REGISTRO,CONTADOR LOCAL @@CICLO @@CICLO: INT 10H OP REGISTRO DEC CONTADOR ; longitud de la línea JNZ @@CICLO
El “LOCAL” es para simular CICLO1, CICLO2, etc. Cada vez que llame la macro va a hacer lo mismo que el preprocesador de C, reemplazar REGISTRO por lo que haya puesto, etc. y escribir código nuevo, que es el que se va a ensamblar. La función RECTANGULO va a quedar entonces
RECTANGULO PROC NEAR PUSH DI PUSH SI MLINEA CX,INC,SI ; derecha MLINEA DX,INC,DI ; abajo POP SI POP DI MLINEA CX,DEC,SI ; izquierda MLINEA DX,DEC,DI ; arriba RET RECTANGULO ENDP
que definitivamente ya no es un fastidio :D aunque el ensamblador va a ver exactamente (bueno, casi) el mismo código al final.
Hay que destacar que esto no se puede hacer con procedimientos porque las “variables” son elementos del lenguaje, instrucciones INC, DEC y registros; en realidad estoy creando un mini lenguaje nuevo y el compilador más rudimentario posible. De usar procedimientos haría falta crear cuatro, uno por tipo de línea (o escribir cuatro funciones y unirlas con un disyuntor, que es exactamente lo mismo :P) pero solamente hizo falta una macro.
Relacionados:
No user responded in this post
Leave A Reply
Nota: La moderación de comentarios está activada; no hace falta volver a enviar los comentarios.