You are on page 1of 14

Lenguaje Ensamblador 1

EL ENSAMBLADOR Y EL LENGUAJE C
El lenguaje C es sin duda el ms apropiado para la programacin de sistemas, pudiendo sustituir al
ensamblador en muchos casos. Sin embargo, hay ocasiones en que es necesario acceder a un nivel ms bajo
por raones de operatividad e incluso de necesidad !programas residentes que economicen memoria,
algoritmos rpidos para operaciones cr"ticas, etc.#. Es entonces cuando resulta evidente la necesidad de
poder emplear el ensamblador y el C a la ve.
$ara comprender este cap"tulo, basta tener unos conocimientos raonables de C estndar. %qu" se
e&plicarn las 'unciones de librer"a necesarias para acceder al ms bajo nivel, as" como la manera de
integrar el ensamblador y el C.
USO DEL TURBO C y BORLAND C A BAJO NIVEL.
% continuacin veremos algunas 'unciones, macros y estructuras de la librer"a ()S.* del +urbo C.
%CCES) % L)S $,E-+)S (E E.S.
int inp !int puerto#/ .0 leer del puerto E.S una palabra !11 bits# 0.
int inport !int puerto#/ .0 leer del puerto E.S una palabra !11 bits# 0.
unsigned char inportb !int puerto#/ .0 leer del puerto E.S un byte !2 bits# 0.
int outp !int puerto, int valor#/ .0 enviar al puerto E.S una palabra !11 bits# 0.
void outport !int puerto, int valor#/ .0 enviar al puerto E.S una palabra !11 bits# 0.
void outportb !int puerto, unsigned char valor#/ .0 enviar al puerto E.S un byte !2 bits# 0.
%unque pueden parecer demasiadas, algunas son id3nticas !caso de inp!# e inport!## y otras se
di'erencian slo ligeramente en el tipo de los datos devueltos, lo cual es irrelevante si se tiene en cuenta que
el dato devuelto es descartado !caso de outp!# y outport!##. En general, lo normal es emplear inport!# e
inportb!# para la entrada, as" como outport!# y outportb!# para la salida. $or ejemplo, para enviar el E)4 al
'inal de una interrupcin hard5are se puede ejecutar6 outportb!7&87, 7&87#/
%CCES) % L% 9E9)-4%.
int pee: !unsigned seg, unsigned o''#/ .0 leer la palabra !11 bits# en seg6o'' 0.
char pee:b !unsigned seg, unsigned o''#/ .0 leer el byte !2 bits# en seg6o'' 0.
void po:e !unsigned seg, unsigned o'', int valor#/ .0 poner palabra valor !11 bits# en seg6o'' 0.
void po:eb !unsigned seg, unsigned o'', char valor#/ .0 poner byte valor !2 bits# en seg6o'' 0.
unsigned ;$<);; !void 'ar 0puntero#/ .0 obtener o''set de variable tipo 'ar 0.
unsigned ;$<SE= !void 'ar 0puntero#/ .0 obtener segmento de variable tipo 'ar 0.
void 'ar 09><;$ !unsigned seg, unsigned o''#/ .0 convertir seg6o'' en puntero tipo 'ar 0.
Las 'unciones pee:!#, pee:b!#, po:e!# y po:eb!# tienen una utilidad evidente de cara a consultar y
modi'icar las posiciones de memoria. Cuando se necesita saber el segmento y.o el o''set de una variable del
programa, las macros ;$<);; y ;$<SE= devuelven dicha in'ormacin. $or ?ltimo, con 9><;$ es posible
asignar una direccin de memoria absoluta a un puntero 'ar.
$or ejemplo, si se declara una variable6
char 'ar 0pantalla<color/
se puede hacer que apunte a la memoria de v"deo del modo te&to de los adaptadores de color con6
pantalla<color @ 9><;$ !7&A277, 7#/
Lenguaje Ensamblador 8
y despu3s se podr"a limpiar la pantalla con un bucle6 'or !i@7/ iBC777/ iDD# 0pantalla<colorDD@7/
C)E+-)L (E 4E+E--,$C4)EES.
void enable!void#/ .0 habilitar interrupciones hard5are, equivalente a S+4 0.
void disable!void#/ .0 inhibir interrupciones hard5are, equivalente a CL4 0.
LL%9%(% % 4E+E--,$C4)EES.
$ara llamar a las interrupciones es conveniente conocer antes ciertas estructuras y uniones.
struct F)-(-E=S G
unsigned int a&, b&, c&, d&, si, di, c'lag, 'lags/
H/
struct AI+E-E=S G
unsigned char al, ah, bl, bh, cl, ch, dl, dh/
H/
union -E=S G
struct F)-(-E=S &/
struct AI+E-E=S h/
H/
struct S-E=S G
unsigned int es/ unsigned int cs/ unsigned int ss/ unsigned int ds/
H/
struct -E=$%C> G
unsigned r<a&, r<b&, r<c&, r<d&/
unsigned r<bp, r<si, r<di, r<ds, r<es, r<'lags/
H/
% continuacin, se listan las 'unciones que permiten invocar las interrupciones6
int int21!int interrupcin, union -E=S 0entrada, union -E=S 0salida#/
int int21&!int interrupcin, union -E=S 0entrada, union -E=S 0salida, struct -E=S 0rsegmento#/
void intr!int interrupcin, struct -E=$%C> 0registros#/
Las dos primeras 'unciones se basan en la declaracin de dos uniones6 una para entrada y otra para
salida, que simbolian los valores iniciales !antes de llamar a la interrupcin# y 'inales !tras la llamada# en
los registros. Si se desea que la misma unin que indica los valores iniciales devuelva los 'inales, se puede
indicar por duplicado6
union -E=S regs/
regs.h.ah @ 7/
regs.h.al @ 7&1J/ .0 K=% J87&877 L 8M1 colores 0.
int21 !7&17, Ns, Ns#/ .0 cambiar modo de v"deo 0.
La di'erencia entre int21!# e int21&!# reside en que la ?ltima permite trabajar con los registros de
segmento !la estructura S-E=S se puede inicialiar con los valores que tienen que tener los registros de
segmento antes de llamar a la interrupcin/ a la vuelta, dicha estructura habr sido modi'icada para indicar
el valor devuelto en los registros de segmento tras la interrupcin#.
*ay quien pre'iere trabajar con REGPACK, que con una sola estructura permite tambi3n operar
con los registros de segmento y la emplea tanto para enviar como para recibir los resultados. El
Lenguaje Ensamblador J
inconveniente, poco relevante, es que slo admite registros de 11 bits, lo que suele obligar a hacer
desplaamientos y 'orar el empleo de mscaras para trabajar con las mitades necesarias6
struct -E=$%C> bios/
bios.r<a& @ 7&1J/ .0 K=% J87&877 L 8M1 colores 0.
intr !7&17, Obios#/ .0 cambiar modo de v"deo 0.
C%9A4) (E KEC+)-ES (E 4E+E--,$C4PE.
void interrupt !0getvect!int interrupcin##!#/ .0 obtener vector de interrupcin 0.
void setvect !int interrupcin, void interrupt !0rutina#!##/ .0 establecer vector de interrupcin 0.
La 'uncin getvect!# devuelve un puntero con la direccin del vector de interrupcin indicado. La
'uncin setvect!# permite desviar un vector hacia la rutina de tipo interrupt que se indica. 4nterrupt es una
palabra clave del +urbo C que ser e&plicada en el 'uturo.
$or ahora, baste el siguiente programa de ejemplo6
void interrupt nueva<rutina!#/ .0 nuestra 'uncin de interrupcin 0.
void interrupt !0vieja<rutina#!#/ .0 variable para almacenar el vector inicial 0.
int main!#
G
vieja<rutina @ getvect !M#/ .0 almacenar direccin de 4E+ M !activada con $rint Screen# 0.
setvect !M, nueva<rutina#/ .0 desviar 4E+ M a nuestra propia rutina de control 0.
. . .
. . . .0 resto del programa 0.
. . .
setvect !M, vieja<rutina#/ .0 restaurar rutina inicial de 4E+ M 0.
H
void interrupt nueva<rutina!# .0 rutina de control de 4E+ M 0.
G
. . .
H
$-)=-%9%S -ES4(EE+ES.
void :eep !unsigned char errorlevel, unsigned tamaQo#/
La 'uncin anterior, basada en el servicio J1h del ()S, permite a un programa realiado en C quedar
residente en la memoria. %dems del cdigo de retorno, es preciso indicar el tamaQo del rea residente !en
prra'os#. Es di'"cil determinar con precisin la memoria que ocupa un programa en C. Sin embargo, en
muchos casos la siguiente 'rmula puede ser vlida6
:eep !7, !<SS D !!<S$ D area<de<seguridad#.11# L <psp##/
En los casos en que no lo sea, se le puede hacer que vuelva a serlo aumentando el tamaQo del rea de
seguridad !que en los programas menos con'lictivos ser 7#. +anto <psp como <SS y <S$ estn de'inidas ya
por el compilador, por lo que la l"nea anterior es per'ectamente vlida !sin ms# al 'inal de un programa.
K%-4%ALES =L)A%LES $-E(E;4E4(%S 4E+E-ES%E+ES.
<version .0 devuelve la versin del ()S de manera completa 0.
<osmajor .0 devuelve el n?mero principal de versin del ()S6 ej., M en el ()S M.7 0.
<osminor .0 devuelve el n?mero secundario de versin del ()S6 ej., 7 en el ()S M.7 0.
<psp .0 segmento del $S$ 0.
<st:len .0 contiene el tamaQo de la pila, en bytes 0.
Lenguaje Ensamblador C
<heaplen .0 almacena el tamaQo inicial del heap, en bytes !7 para ma&imiarlo# 0.
(e estas variables prede'inidas, las ms ?tiles son qui las que devuelven la versin del ()S, lo
que ahorra el es'uero que supone averiguarlo llamando al ()S o empleando la 'uncin de librer"a
correspondiente. +ambi3n es ?til <psp, que permite un acceso a este rea del programa de manera inmediata.
4ESE-C4PE (E CP(4=) EE LREE%
.
void < <emit< < !argumento,...#/
void geninterrupt !int interrupcin#/
$or medio de < <emit< <!# se puede colocar cdigo mquina de manera directa dentro del programa
en C. Eo es conveniente hacerlo as" porque as", ya que alterar directamente los registros de la C$, acabar
alterando el 'uncionamiento esperado del compilador y haciendo 'allar el programa. Sin embargo, en un
procedimiento dedicado e&clusivamente a almacenar cdigo inline !en l"nea#, es seguro este m3todo, sobre
todo si se tiene cuidado de no alterar los registros S4 y (4 !empleados muy a menudo por el compilador como
variables de tipo register#. $or medio de geninterrupt!# se puede llamar directamente a una interrupcin6
geninterrupt !interr# es e&actamente lo mismo que < <emit< <!7&C(, interr# ya que 7&C( es el cdigo de
operacin de 4E+. $or ejemplo, para volcar la pantalla por impresora se puede ejecutar geninterrupt!M#. Con
los s"mbolos <%S, <%L, <%*, <AS, <AL, <A*, <CS, <CL, <C*, <(S, <(L, <(*, <S4, <(4, <A$, <S$, <CS,
<(S, <ES, <SS y <;L%=S se puede acceder directamente a los registros de la C$,. *ay que tomar tambi3n
precauciones para evitar e'ectos laterales !una asignacin tipo <(S@7&C7 no a'ectar slo a (S#.
L%S $%L%A-%S CL%KE 4E+E--,$+ I %S9.
Con interrupt <e!"#r#!i$n%e%&un!i$n'/ se declara una determinada 'uncin como de tipo
interrupcin. En estas 'unciones, el compilador preserva y restaura todos los registros al comieno y 'inal de
las mismas/ 'inalmente, retorna con 4-E+. $or tanto, es ?til para 'unciones que controlan interrupciones.
$ara emplear esto, se deber"a compilar el programa con la opcin test stac: over'lo5 y las variables tipo
registro desactivadas. Con #() se pueden insertar instrucciones en ensamblador, como se ver ms adelante.
INTER*A+ C ,BORLAND-MICROSO*T. / ENSAMBLADOR.
9)(EL)S (E 9E9)-4%.
Los modelos de memoria constituyen las diversas maneras de acceder a la memoria por parte de los
compiladores de C. En el caso del +urbo C se pueden distinguir los siguientes6
TINY0 Se emplea en los programas donde es preciso apurar el consumo de memoria hasta el ?ltimo
byte. Los C registros de segmento !CS, (S, ES, SS# estn asignados a la misma direccin, por lo que e&iste un
total de 1C >b donde se meclan cdigo, datos y pila. Los programas de este tipo pueden convertirse a
'ormato C)9.
SMALL0 Se utilia en aplicaciones pequeQas. Los segmentos de cdigo y datos son di'erentes y no
se solapan. $or ello, hay 1C :b para cdigo y otros 1C >b a repartir entre datos y pila.
Segmentos $unteros
9odelo Cdigo (atos $ila Cdigo (atos
+iny 1C >b near near
Small 1C >b 1C >b near near
9edium 1 9b 1C >b 'ar near
Lenguaje Ensamblador M
Compact 1C >b 1 9b near 'ar
Large 1 9b 1 9b 'ar 'ar
*uge 1 9b
1 9b
!Aloques T 1C >b#
'ar 'ar
MEDIUM0 Este modelo es ideal para programas largos que no manejan demasiados datos. Se utilian
punteros largos para el cdigo !que puede e&tenderse hasta 1 9b# y cortos para los datos6 la pila y los datos
juntos no pueden e&ceder de 1C >b.
COMPACT0 %l contrario que el anterior, este modelo es el apropiado para los programas pequeQos que
emplean muchos datos. $or ello, el programa no puede e&ceder de 1C >b aunque los datos que controla
pueden alcanar el 9b, ya que los punteros de datos son de tipo 'ar por de'ecto.
LARGE0 Empleado en las aplicaciones grandes y tambi3n por los programadores de sistemas que no tienen
paciencia para andar 'orando continuamente el tipo de los punteros !para rebasar el l"mite de 1C >b#. +anto
los datos como el cdigo pueden alcanar el 9b, aunque no se admite que los datos estticos ocupen ms de
1C >b. Este modo es el que menos problemas da para manejar la memoria, no siendo qui tan lento y
pesado como indica el 'abricante.
1UGE0 Similar al anterior, pero con algunas ventajas6 por un lado, todos los punteros son normaliados
automticamente y se admiten datos estticos de ms de 1C >b. $or otro, y gracias a esto ?ltimo, es 'actible
manipular bloques de datos de ms de 1C >b cada uno, ya que los segmentos de los punteros se actualian
correctamente. Sin embargo, este modelo es el ms costoso en tiempo de ejecucin de los programas.
4E+E=-%C4PE (E 9P(,L)S EE EES%9AL%()-.
LA SENTENCIA ASM
La sentencia #() permite incluir cdigo ensamblador dentro del programa C, utiliando los mnemnicos
normales del ensamblador. Sin embargo, el uso de esta posibilidad est ms o menos limitado seg?n la
versin del compilador. En +urbo C 8.7, los programas que utilian este m3todo es necesario salir a la l"nea
de comandos para compilarlos con el tradicional compilador de l"nea, lo cual resulta poco atractivo. En
+urbo CDD 1.7, se puede con'igurar adecuadamente el compilador para que localice el +urbo %ssembler y lo
utilice automticamente para ensamblar, sin necesidad de salir del entorno integrado. Sin embargo, es a
partir del Aorland CDD cuando se puede trabajar a gusto6 en concreto, la versin Aorland CDD 8.7 permite
ensamblar sin rodeos cdigo ensamblador incluido dentro del listado C. El ?nico inconveniente es la
limitacin del hard5are disponible6 para un $C.S+, el +urbo C 8.7 es el ?nico compilador aceptablemente
rpido. Sin embargo, en un 821 es ms recomendable el +urbo CDD, mientras que en un J21 modesto !o
incluso en un 821 potente# resulta ms interesante emplear el Aorland CDD 8.76 las versiones J.S de este
compilador son las ms adecuadas para un C21 o superior !bajo ()S#.
La sinta&is de #() se puede entender 'cilmente con un ejemplo6
main!#
G
int dato1, dato8, resultado/
print'!U(ame dos n?meros6 U#/ scan'!UVd VdU, Odato1, Odato8#/

asm push a&/ push c&/
asm mov c&,dato1
asm mov a&,7h
Lenguaje Ensamblador 1
mult6
asm add a&,dato8
asm loop mult
asm mov resultado,a&
asm pop c&/ pop a&/
print'!USu producto por el peor m3todo da6 VdU, resultado#/
H
Como se ve en el ejemplo, los registros utiliados son convenientemente preservados para no alterar el
valor que puedan tener en ese momento !importante para el compilador#. +ambi3n puede observarse lo 'cil
que resulta acceder a las variables. %h, cuidado con A$6 el registro A$ es empleado mucho por el
compilador y no conviene tocarlo !ni siquiera guardndolo en la pila#. (e hecho, la instruccin 9)K
CS,(%+)1 ser compilada como 9)K CS,WA$LalgoX al ser una variable local de main!#.
Esta es la ?nica sinta&is soportada por el +urbo C 8.7/ sin embargo, en las versiones ms modernas del
compilador se admiten las llaves YGY y YHY para agrupar varias sentencias #()6
asm G
push a&/ push c&/
mov c&,dato1
mov a&,7h H
mult6 asm G
add a&,dato8
loop mult
mov resultado,a&
pop c&/ pop a&/
H
SUBRUTINAS EN ENSAMBLADOR
Cuando las rutinas a incluir son e&cesivamente largas, resulta ms conveniente escribirlas como
'icheros independientes y ensamblarlas por separado, incluy3ndolas en un 'ichero de proyecto !0.$-Z#
seleccionable en los men?s del compilador.
$ara escribir este tipo de rutinas hay que respetar las mismas de'iniciones de segmentos que realia
el compilador. *oy en d"a e&iste algo ms de 'le&ibilidad/ sin embargo, aqu" se e&pone el m3todo general
para meclar cdigo de ensamblador con C.
Keamos el siguiente programa en C6
int variable/
e&tern dato/
e&tern 'uncion!#/
main!#
G
int a@81[J7/ char b@Y\Y/
variable @ 'uncion !a, b, 7&18JCM1]2#/
H
La variable variable es una variable global del programa a la que no se asigna valor alguno en el
momento de de'inirla. +anto a como b son variables locales del procedimiento main!# y son asignadas con un
cierto valor inicial/ 'uncion!# no aparece por ning?n sitio, ya que ser codi'icada en ensamblador en un
Lenguaje Ensamblador ]
'ichero independiente. % dicha 'uncin se le pasan J parmetros. La manera de hacerlo es colocndolos en la
pila !empeando por el ?ltimo y acabando por el primero#. $or ello, el compilador meter primero en la pila
el valor 18JCh y luego el M1]2h !necesita dos palabras de pila porque es un dato de tipo long#. Luego coloca
en la pila el carcter almacenado en la variable b6 como los valores que se apilan son siempre de 11 bits, la
parte alta est a 7. ;inalmente, deposita el dato entero a. Seguidamente, llama a la 'uncin 'uncion!# con un
C%LL que puede ser de dos tipos6 corto !C%LL.-E+ en el mismo segmento# o largo !C%LL.-E+; entre
distintos segmentos#. Esta llamada a la 'uncin, por tanto, provoca un almacenamiento adicional de 8 bytes
!modelos +4EI, S9%LL y C)9$%C+# o C !en los restantes modelos de memoria, que podr"amos llamar
largos#.
El esqueleto de la subrutina en ensamblador que ha de recibir esos datos y, tras procesarlos,
devolver un resultado de tipo int es el siguiente6
(=-),$ =-),$ <(%+%, <ASS
<(%+% SE=9EE+ F)-( $,AL4C Y(%+%Y
$,AL4C <dato / <dato ser accesible desde el programa C
<dato (F 7 / valor inicial a 7
<(%+% EE(S
<ASS SE=9EE+ F)-( $,AL4C YASSY
ES+-E <variable6F)-( / variable e&terna
<in'o (F ^ / sin valor inicial
<ASS EE(S
<+ES+ SE=9EE+ AI+E $,AL4C YC)(EY
%SS,9E CS6<+ES+,(S6(=-),$,SS6(=-),$
$,AL4C <'uncion / <'uncion ser accesible desde el programa C
<'uncion $-)C EE%- / 'uncion!# del C
$,S* A$
9)K A$,S$
9)K AS,WA$DCX / recuperar variable YaY
9)K CS,WA$D1X / recuperar variable YbY
9)K %S,WA$D2X / %S @ M1]2h
9)K (S,WA$D17X / (S @ 18JCh LT (S6%S @ 18JCM1]2h
/ ...
/ ...
%(( CS,AS / cuerpo de la 'uncin
%(( CS,%S
S,A CS,(S
/ ...
/ ...
9)K %S,CS / resultado !tipo int#
9)K S$,A$
$)$ A$
-E+
<'uncion EE($
<+ES+ EE(S
EE(
Lenguaje Ensamblador 2
Como se puede observar, se respetan ciertas convenciones en cuanto a los nombres de los segmentos y
grupos. En el segmento %DATA se de'inen las variables inicialiadas !las que tienen un valor inicial#6 <dato
podr"a haber sido accedida per'ectamente desde el programa en C, ya que es declarada como p?blica. $or
otro lado, en el segmento %BSS se de'inen o declaran las variables que no son inicialiadas con un valor
inicial !como es el caso de la variable <variable del programa C, que 'ue de'inida simplemente como int
variable6 en el listado ensamblador se la declara como e&terna ya que est de'inida en el programa C#. El
compilador de C precede siempre de un subrayado a todas las variables y 'unciones cuando compila, motivo
por el cual hay que hacer lo propio en el listado ensamblador. %l tratarse de un modelo de memoria pequeQo,
<ASS y <(%+% estn agrupados. En el segmento %TE2T se almacena el cdigo, es decir, las 'unciones
de'inidas6 en nuestro caso, slo una !el procedimiento <'uncion#. Como es de tipo EE%-, slo se podr
emplear con programas C compilados en un modelo de memoria +4EI, S9%LL o C)9$%C+ !para los dems
modelos hay que poner ;%- en lugar de EE%-#. Esta 'uncin de ejemplo en ensamblador no utilia ninguna
variable, pero tanto <variable !la variable del programa C# como, por supuesto, <in'o o <dato son
plenamente accesibles.
% la hora de acceder a las variables, hay que tener en cuenta el modelo de memoria6 como no emplea ms
de 1C >b para cdigo !modelos +4EI, S9%LL o C)9$%C+#, el compilador slo ha colocado en la pila el
o''set de la direccin de retorno !registro 4$#. Eosotros apilamos despu3s A$ !ya que lo vamos a manchar#
por lo que el ?ltimo dato que apil el programa C antes de llamar a la rutina en ensamblador habr de ser
accedido en WA$DCX. La ventaja de inicialiar A$ es que luego se pueden introducir datos en la pila sin
perder la posibilidad de acceder a los parmetros de la rutina que llama. Si el procedimiento 'uera de tipo
;%- !modelos 9E(4,9, L%-=E y *,=E#, todos los accesos inde&ados sobre la pila se incrementar"an en
dos unidades !por ejemplo, WA$D1X en ve de WA$DCX para acceder a la variable a# debido a que tambi3n se
habr"a almacenado CS en la llamada. Como se puede observar, la rutina no preserva ni restaura todos los
registros que va a emplear6 slo es necesario devolver intactos (S, SS, A$ y !por si se emplean variables
register# S4 y (4/ los dems registros pueden ser libremente alterados. Como la 'uncin es de tipo entero,
devuelve el resultado en %S/ si 'uera de tipo long lo devolver"a en (S6%S.
El modelo de memoria tambi3n cuenta en los parmetros que son pasados a la rutina en ensamblador
cuando no son pasados por valor !es decir, cuando se pasan punteros#. En el ejemplo, podr"amos haber
pasado un puntero que podr"a ser de tipo corto !para cargarlo en AS, por ejemplo, y e'ectuar operaciones
tipo WASX#. Sin embargo, si se pasan punteros a variables de tipo 'ar !o si se emplea un modelo de memoria
C)9$%C+, L%-=E o *,=E# es necesario cargar la direccin con una instruccin LES de J8 bits.
Esta rutina de ejemplo en ensamblador es slo demostrativa, por lo que no debe el lector intentar
encontrar alguna utilidad prctica, de ah" que incluso ni siquiera emplee todas las variables que de'ine.
Evidentemente, cuando el programa C retome el control, habr de equilibrar la pila sumando 2 unidades
a S$ !para compensar las C palabras que apil antes de llamar a la 'uncin en ensamblador#. En general, el
'uncionamiento general del C en las llamadas a procedimientos se basa en apilar los parmetros empeando
por el ?ltimo y llamar al procedimiento6 3ste, a su ve, preserva A$ y lo hace apuntar a dichos parmetros !a
los que acceder con WA$DdespX#/ a continuacin, le resta a S$ una cantidad su'iciente para que quepan en
la pila todas las variables locales !a las que acceder con WA$LdespX#/ antes de retornar restaura el valor
inicial de S$ y recupera A$ de la pila. Es entonces cuando el procedimiento que llam, al recuperar el
control, se encarga de sumar el valor adecuado a S$ para equilibrar la pila !devolverla al estado previo a la
introduccin de los parmetros#.
(esde las rutinas en ensamblador tambi3n se puede llamar a las 'unciones del compilador, apilando
adecuadamente los parmetros en la pila !empeando por el ?ltimo# y haciendo un C%LL al nombre de la
'uncin precedido de un subrayado6 no olvidar nunca al 'inal sumar a S$ la cantidad necesaria para
reequilibrar la pila.
AVISO IMPORTANTE6 %lgo a tener en cuenta es que el compilador de C es sensible a las may?sculas6
'uncion!# no es lo mismo que ;,EC4)E!#. $or ello, al ensamblar, es 34"i5#t3ri3 emplear como m"nimo el
Lenguaje Ensamblador [
parmetro -)6 del ensamblador con objeto de que no ponga todos los s"mbolos automticamente en
may?sculas !con .m& se respetan las min?sculas en los s"mbolos globales y con .ml en todos los s"mbolos#. En
9%S9 1.7, el equivalente a .m& es .C& y la opcin .Cp se corresponde con .ml.
Lenguaje Ensamblador 17
P#(!#" 7 Len5u#8e En(#)4"#3r
Opti)i9#!i$n e" !$i53

En esta parte trataremos de hacer nuestro cdigo un poco ms e'iciente, es decir, que teniendo la
misma 'uncionalidad, sea mucho ms rpido. *ar3 una seleccin de las cosas ms importantes que hemos de
optimiar6

C$i53 en en(#)4"#3r
+urbo $ascal ].7 permite incluir sentencias en lenguaje mquina en nuestro programa mediante la
directiva %S9. Las ventajas de hacer esto son bsicamente la rapide y el trabajo directo con el hard5are, y
las desventajas son la ilegibilidad del cdigo, lo 'acil que es cometer errores y la cantidad de intrucciones
que hay que escribir en equivalencia con una sentencia de alto nivel, como un simple 5rite!UholaU#. Esto
?ltimo, lo hariamos en pascal, claro est.
Cuando escribimos en un leguaje de alto nivel !en nuestro caso $%SC%L, pero ocurre con todos#, el
compilador lo que hace en esencia es convertir las sentencias de alto nivel en leguaje mquina. Entonces te
preguntars6 _y por qu3 escribir ensamblador puro, si al 'inal el compilador lo va a hacer por nosotros^
$ues muy buena pregunta, pero desgraciadamente no siempre se genera un cdigo mquina lo ms optimo
posible. Esto ya es algo propio de cada compilador, es decir, que si e&istiera !de hecho los hay# ms
compiladores de $%SC%L, cada uno generar"a un ejecutable di'erente !con la misma 'uncionalidad, por
supuesto#.
$or ejemplo, el compilador +urbo $ascal ].7 no es capa de utiliar los registros propios de los
procesadores J21 y superiores !los registros e&tendidos#. Esto es una gran desventaja, evidentemente, al no
poder aprovechar todas la ventajas de la operaciones en J8 bits !ms rpidas que 11 bits#.
Es precisamente en este campo donde C da mil vueltas a $ascal, y a todos los lenguajes de alto
nivel6 los compiladores de C son, en general, muy e'icientes/ una pequeQa anecdota en este sentido es una
re'erente a Zava6 siempre se ha dicho que, a pesar de su 'acilidad de programacin, sinta&is simple y
portabilidad a otras plata'ormas, era muy ine'iciente. Esto es cierto a medias, lo que ocurre es que siempre
ha tenido la mala 'ortuna de ser comparado con C, y claro, aqu" es donde C gana por mucho a cualquier
lenguaje. Lo cierto es que Zava es tan bueno como cualquier otro.
$or contra, al haber tantos compiladores de C, este lenguaje ha ido su'riendo pequeQas variaciones
con cada uno de ellos !di'erencias insigni'icantes, pero cambios, al 'in y al cabo#. Eos podemos encontrar
con cdigo en C que compila per'ectamente en +urbo C, y no lo hace en (Z=$$.
EL ENSAMBLADOR INCORPORADO
Esta es la mejor y ms cmoda manera de incorporar ensamblador a tus programas pascal.
Cuando quieras poner algo de cdigo en ensamblador pon la palabra reservada %S9, y aQade
detrs todas las rdenes ensamblador que quieras y cuando hayas acabado pon un EE( y un punto
y coma, as"6
AE=4E
%S9
9)K %S,1Jh
4E+ 17h
EE(/
Frite!Y`9odo 1JhaY#/
-eadLn/
Lenguaje Ensamblador 11
%S9
9)K %S,Jh
4E+ 17h
EE(/
EE(.
$ues bien, antes de seguir, hay que entender algo de cdigo binario !base 8#6 si tenemos el n?mero
1711 y hacemos un desplaamiento hacia la iquierda, es decir 1711 LT 17117, lo que hemos hecho es
multiplicar ese n?mero por 8. !1711 @ 11 !en decimal#, y 17117 @ 88#. Esto es semejante cuando, trabajando
en base 17, si tenemos el n?mero J8 y ponemos un cero, obtenemos J87 ! &17, base 17#.
Aien, una ve aclarado esto, podemos descomponer6
J870yD& @ !8M1D1C#0yD& @ !8b2D8b1#0yD& @ y08b2 D y08b1 D &
Entonces, lo que hemos de hacer es desplaar a la iquierda UyU ocho veces e UyU seis veces, que es
mucho ms rpido que hacer multiplicaciones. Con todo esto, obtenemos el siguiente cdigo en
ensamblador6 !shl @ shi't le't @ desplaamiento a la iquierda#
PROCEDURE Ponpixel (X,Y : Integer; Color : Byte; segmento:word;!""E#B$ER;
!"#
mo% &x,segmento
mo% es,&x
mo% 'x,X
mo% dx,y
mo% di,'x
mo% 'x, dx
s(l dx, )
s(l 'x, *
&dd dx, 'x
&dd di, dx
mo% &l, Color
mo% es:+di,,&l
E-D;
%qu", por ejemplo, es donde vemos la e'icacia de poner cdigo ensamblador directamente6 nuestro
compilador de $ascal, aunque es muy listo, no es mgico, y lo que har"a es generar el cdigo mquina
necesario para hacer la multiplicacin por J87, y no lo que hemos hecho nosotros.


T#4"#( pre!#"!u"##(
Esta parte es e&tremadamente importante. Como todos sabremos a estas alturas !si no, mal
andamos#, las rotaciones del objeto son el 'undamento de nuestro motor Jd. Ia vimos en la seccin rotacin
del objeto debemos hacer un motn de operaciones trigonom3tricas por v3rtice. Cada operacin de este tipo
requiere mucho clculo !aunque no lo pareca#, y echando unas pequeQas cuentas, tenemos que por vertice
hacen 'alta unos 17 senos y cosenos, y suponiendo que tenemos un objeto de 177 v3rtices, nos queda que
hemos de hacer 1777 por 'rame de animacin. %dems en un segundo deben haber unos M7 'rames, con lo
que hay que calcular M7.777 relaciones trigonometricas por segundo. Como vers, la cosa empiea a
adquirir dimensiones preocupantes.
$ero no pasa nadaaa Es aqu" donde entram en juego las 'amosas tablas precalculadas !en ingl3s se
traducen como loo:up tables#. La idea consiste en calcular todos los senos y cosenos desde 7 hasta JM[
grados, y guardar los resultados en un array. %s", en lugar de escribir resultado6@sin!8M# haremos
resultado6@senoW8MX
Lenguaje Ensamblador 18
Como siempre, ah" va el cdigo de la inicialiacin. !ojo6 este cdigo est en la unidad trigon.pas en
la parte 'inal/ este parte se ejecuta simplemente con llamar a la unidad, de manera que ni siquiera hace 'alta
UinvocarU a este cdigo#

U-I. trigon;

I-.ER/!CE
.YPE t&'l&0&rr&y+122344, o5 s(ortint;
6!R seno,7ose:t&'l&;

222
BE8I- 9: se ll&m& siempre ;<e se <s& est& <nid&d :=
97&l7<lo de t&'l&s pre7&l7<l&d&s=
5or i:01 to 344 do 'egin
seno+i,:0ro<nd(sin(i:Pi>?3):?3);
i5 (i:?)1>?3)@A1 !-D (i:?)1>?3)B??1 !-D (seno+i,0C?3) t(en
seno+i,:0?3A;
9p&r& ;<e no se p&se del limite de ?3A C@ C?3)=
7ose+i,:0ro<nd(7os(i:Pi>?3):?3);
i5 (i:?)1>?3)@DE1 OR (i:?)1>?3)B31 !-D (7ose+i,0C?3) t(en 7ose+i,:0?3A;
end;
E-D2
Punter3( en e" p#(3 e p#r:)etr3( # &un!i3ne(
Esta es una de las cosas de la que no te das cuenta si no conoces en pro'undidad la traduccin que
hace el compilador del cdigo en alto nivel.
Cuando invocamos a una 'uncin con una serie de parmetros, el cdigo mquina generado es
similar a una llamada a una 'uncin, pero a nivel de lenguaje ensamblador. %ntes de ser invocada, todos los
parmetros son metidos en la pila mediante la intruccin $,S*. (espu3s de llama a la 'uncin, y 3sta saca
todos los parmetros mediante $)$. $ero veamoslo con un ejemplo
/<n7tion s<m& (&,',7,d,e,5,g,(:integer:integer;
BE8I-
s<m&:0&F'F7FdFeF5FgF(;
E-D
Que se traducira
PU"G &
PU"G '
PU"G 7
PU"G d
PU"G e
PU"G 5
PU"G g
PU"G (
C!$$ s<m&
Lenguaje Ensamblador 1J
POP (
POP g
POP 5
POP d
POP 7
POP '
POP &
222
Evidentemente este ejemplo es un poco e&agerado, pero ilustra muy bien el problema de velocidad
que supone el pasar muchos parmetros a una 'uncin. Eo lo he comentado, pero las intrucciones $,S* y
$)$ consumen muchos ciclos de reloj, y es algo intoloreble en una aplicacin cuya m&ima es la velocidad
!casualmente, nuestro programa es uno de esos 6) #. I ms aun si esta 'uncin ha de llamarse veinte veces
por 'rame/ es bastante UdolorosoU que la mitad del tiempo de ejecucin se emplee ?nicamente en invocar a
una 'uncin !no ejecutarla#
La solucin de esto es el empleo de punteros. El ejemplo anterior se puede resecribir de la siguiente
manera

.YPE
#iHtipo 0 RECORD
&,',7,d,e,5,g,(:integer;
end;
p<ntero 0 I#iHtipo;
222
5<n7tion s<m&(p:p<ntero;
BE8I-
s<m&:0pI2&FpI2'F222pI2(;
E-D;
222
Con esto vemos que solo hacemos un $,S* y un solo $)$
RE*ERENCIAS
Lenguaje Ensamblador 1C
;ttp0--e!##.;yper)#rt.net-pr35-p#(!#"-tp#().;t)
http://ditec.um.es/interno/GDAC/ECI-ii.pdf

You might also like