CI-1322 Autómatas y compiladores
Realizado por :
Roberto Hernández M. A11727
Jorge Villalta A. A13959
Contenido
Comparación
y Análisis de las dos versiones de código ensamblador obtenidas.
A partir
del bloque de código asignado para la tarea ubicado en la dirección http://profesores.ecci.ucr.ac.cr/Luis%20Quesada/CI1322/Tareas/CI1322_Tarea_1.pdf se obtuvieron 2 programas, uno escrito en
Pascal y otro en C++. Se escogió 2 compiladores (Visual C++ 6.0 y Free Pascal)
para obtener el lenguaje ensamblador para cada versión del programa. Se
le quitaron las opciones de optimización de código (en los dos casos se le
quitaban estas opciones en el menú de opciones del compilador) a cada
compilador antes de correrlo y obtener resultados.
A el código
ensamblador obtenido para cada versión del programa se le incluyeron
comentarios explicativos que muestran la correspondencia entre el programa fuente
y el que produce cada compilador y se señaló claramente el inicio y fin de cada
instrucción de lenguaje de alto nivel en lenguaje ensamblador. Finalmente se
compararon los resultados obtenidos, y se destacan las diferencias importantes
que se encontraron, realizando un análisis sobre los resultados obtenidos.
A
continuación se hará una breve descripción de las diferencias entre los códigos
ensamblador producidos por el compilador de Visual C++ y por el compilador de
Free Pascal, así como un análisis de los mismos para determinar si es alguno de
los dos más eficiente, y que particularidades tiene cada uno.
Entrando
de lleno en materia, se puede observar que el código ensamblador creado en el
compilador de C++ comienza declarando las variables utilizadas, mientras que el
realizado en el compilador de pascal las define en el segmento de datos ubicado
después del código ensamblador.
Antes de
iniciar a traducir el lenguaje de alto nivel a ensamblador, los dos
compiladores guardan ebp en la pila y mueven el
contenido de esp a ebp,
pero apartir de aquí se nota otra diferencia, el Free
Pascal, llama a un procedimiento llamado FPC_INITIALIZEUNITS
mientras que el ensamblador creado por visual C++ realiza todas las operaciones
sin hacer ningún llamado a procedimientos.
Es
importante recalcar que n en los dos programas fue declarada como constante,
pero a la hora de ver el código ensamblador, podemos observar que Visual C++ sí
define un n y le asigna el valor de 3000 (definido en el programa fuente) pero
más adelante en el código donde hay n lo sustituye por 3000, mientras que el
compilador de pascal declara la variable, pero nunca la asigna si no que
simplemente sustituye las n’s por 3000.
En este
punto se puede observar una gran diferencia en los dos códigos, ya que mientras
el código producido por visual C++ logra imprimir un mensaje con 4 líneas de
lenguaje ensamblador, el código creado por Free Pascal imprime el mismo mensaje
con 37 líneas.
Luego,
para representar el for en los dos casos se inicia
asignando 1 a la variable numero (que es _numero$ en
el ensamblador creado por el compilador de C++ y _NUMERO en el creado por el
compilador de pascal.) y en el caso de C++, se hace un salto corto a un una
etiqueta $L583 en donde se hace una comparación del valor de numero con
3000 y si es mayor salta al final de todas las instrucciones del for a la etiqueta $L585 y termina así los ciclos del for. En el caso del compilador de Pascal, se hacen un poco
diferentes las cosas ya que después de asignar a numero el valor 1, inicia
directamente con las instrucciones dentro del for y
se deja al final de estas, la comparación de la variable numero con 3000 para
ver si realiza otro ciclo del for o termina el for y continua con la instrucción siguiente.
Una vez
dentro del for se pueden ver diferencias todavía más
grandes en la forma en que se traducen los códigos en C++ y Pascal a
ensamblador, como se verá a continuación:
En las
primera línea después del for simplemente se asigna a
la variable suma el valor 0 por lo que tanto el compilador de C++ como el de
Pascal simplemente asignan ese valor al espacio de memoria asignado a la
variable suma.
En la
siguiente línea se asigna a la variable temp el valor
de la variable numero, que en los dos casos se realiza de la misma forma, en
dos instrucciones una que asigna la dirección del valor de numero a un registro
y otra que almacena esa dirección de ese registro en el espacio de memoria de
la variable temp. La diferencia está en que el código
generado por el compilador de C++ utiliza el registro ecx
mientras que el creado por el compilador de pascal utiliza el registro di.
En la
siguiente línea de código, se inicia el ciclo while.
En el caso de C++, se inicia comparando el valor de la variable temp con 0 y si es diferente continúa con la siguiente
instrucción dentro del ciclo while, y si no hace un
salto hacia el final del ciclo while. Por otro lado,
el compilador de pascal nuevamente deja esta comparación para después de todas
las instrucciones del while, pero sin embargo hace
algo que a mi parecer no es muy eficiente, salta hasta el final de las
instrucciones del while para hacer la primera
comparación de temp con 0, y luego se devuelve a la
primera instrucción del while para continuar con la
ejecución de este (en caso de que temp sea diferente
de 0, si no termina el while).
En la
primera instrucción del while se encuentra una
diferencia importante, ya que en el caso de C++ se realiza la operación de
asignar a la variable digito el valor de temp modulo
10 en solamente 5 instrucciones de código ensamblador, mientras que el código
ensamblador del compilador de pascal realiza la misma instrucción por medio de
8 instrucciones ensamblador dentro de las cuales se incluye una llamada a un
procedimiento llamado FPC_BOUNDCHECK que no se
especifica de cuantas instrucciones consiste, pero ya se puede notar que el
código ensamblador creado por Visual C++ por lo menos para esta instrucción es
más eficiente que el creado por Free Pascal.
En la
instrucción suma = suma + (digito * digito * digito) se puede observar más
claramente que el código ensamblador del compilador de C++ es más eficiente que
el de Pascal, ya que esta instrucción consiste simplemente en 6 instrucciones
en ensamblador para el código ensamblador de C++, que consisten 3 de ellas en
multiplicar el valor de la variable dígito 3 veces, y las otras 3 realizan la
suma de este resultado más el valor de la variable suma y almacena el resultado
en la variable suma. En cambio en el caso del código generado por Free Pascal,
esta misma instrucción en lenguaje de alto nivel se traduce a 17 instrucciones
de código ensamblador más 3 posibles llamadas al procedimiento llamado FPC_OVERFLOW (por si las multiplicaciones producen overflow) y una llamada más al procedimiento FPC_BOUNDCHECK. Aquí se observa claramente que debido al
menor número de instrucciones en ensamblador, el código emsamblador
generado por Visual C++ es más eficiente que el creado por Free Pascal para
esta instrucción.
En la
instrucción temp = temp /
10 nuevamente vemos que el código ensamblador creado por Visual C++ traduce
dicha instrucción a 5 instrucciones en ensamblador, mientras que el
código generado por Free Pascal traduce la instrucción a 8 instrucciones en ensamblador
y una llamada a un procedimiento llamado FPC_BOUNDCHECK.
En cuanto
al IF, el compilador de C++ asigna _suma$ a un
registro y lo compara con _numero$ y si no son
iguales se salta la escritura del mensaje. Es aquí donde vemos una pequeña
diferencia entre los dos compiladores, ya que Free Pascal asigna _SUMA y
_NUMERO a dos registros y compara dichos
registros, utilizando una instrucción más para hacer la comparación.
Luego de
estas instrucciones el programa termina, en el caso de el código generado por
Visual C++ termina el programa sacando los registros guardados en la pila,
mientras que el código ensamblador creado por Free Pascal llama al
procedimiento FPC_DO_EXIT para terminar el programa y
a continuación se ubica el segmento de datos utilizado por el programa, donde
se ubica el espacio de memoria asignado para las variables utilizadas en el
programa.
Conclusión
En
conclusión, se puede observar claramente que el código ensamblador producido por
el compilador de C++ es más eficiente que el creado por Free Pascal, ya que
como se observó anteriormente algunas instrucciones en código de alto nivel son
traducidas en muchas menos instrucciones en código ensamblador por parte del
compilador de C++ que por el compilador de Pascal. Además la manera en
que al compilador de Pascal traduce los ciclos while
y for es un poco más ineficiente ya que consta de más
saltos, que en algunos casos son innecesarios, por lo tanto considero que el
compilador de Visual C++ es un poco más eficiente que el de Free Pascal.
Programa en C++
/*programa de prueba para la
tarea # 1 de autómatas y compiladores*/
#include <stdio.h>
int main(){
const int n = 3000;
/*Declaro las variables*/
int numero, suma, temp,
digito;
/*imprimo
titulo*/
printf("Numeros encontrados
( 1 .. %i ) que son de amstrong\n",
n);
/*inicia el
programa*/
/*inicio del for*/
for(numero = 1; numero <= n; numero++)
{
suma = 0;
temp = numero;
/*inicio del while*/
while( temp
!= 0)
{
digito = temp % 10;
suma = suma +
(digito * digito * digito);
temp = temp / 10;
}
/*fin del while
*/
/*inicio del if*/
if (suma == numero){
printf("%i, suma de sus digitos al cubo, %i\n",
numero, suma);
}
/*fin del if*/
}
/*fin del for*/
/*finaliza el programa*/
return 0;
}
Código ensamblador
obtenido en Visual C++
TITLE D:\Tarea #1\Amstrong\Amstrong.cpp
.386P
include listing.inc
if @Version gt 510
.model FLAT
else
_TEXT SEGMENT
_TEXT ENDS
_DATA SEGMENT DWORD USE32 PUBLIC 'DATA'
_DATA ENDS
CONST SEGMENT DWORD USE32 PUBLIC 'CONST'
CONST ENDS
_BSS SEGMENT DWORD USE32 PUBLIC 'BSS'
_BSS ENDS
$$SYMBOLS SEGMENT BYTE USE32 'DEBSYM'
$$SYMBOLS ENDS
$$TYPES SEGMENT BYTE USE32 'DEBTYP'
$$TYPES ENDS
_TLS SEGMENT DWORD USE32 PUBLIC 'TLS'
_TLS ENDS
; COMDAT ??_C@_0DF@JJPP@Numeros?5encontrados?5?$CI?51?5?4?4?5?$CFi?5?$CJ?5@
CONST SEGMENT DWORD USE32 PUBLIC 'CONST'
CONST ENDS
; COMDAT ??_C@_0CF@MMOH@?$CFi?0?5suma?5de?5sus?5digitos?5al?5cubo?0@
CONST SEGMENT DWORD USE32 PUBLIC 'CONST'
CONST ENDS
; COMDAT _main
_TEXT SEGMENT
_TEXT ENDS
FLAT GROUP _DATA, CONST, _BSS
ASSUME CS: FLAT, DS: FLAT, SS: FLAT
endif
PUBLIC _main
PUBLIC ??_C@_0DF@JJPP@Numeros?5encontrados?5?$CI?51?5?4?4?5?$CFi?5?$CJ?5@
; `string'
PUBLIC ??_C@_0CF@MMOH@?$CFi?0?5suma?5de?5sus?5digitos?5al?5cubo?0@
; `string'
EXTRN _printf:NEAR
EXTRN __chkesp:NEAR
; COMDAT ??_C@_0DF@JJPP@Numeros?5encontrados?5?$CI?51?5?4?4?5?$CFi?5?$CJ?5@
; File D:\Tarea
#1\Amstrong\Amstrong.cpp
CONST SEGMENT
??_C@_0DF@JJPP@Numeros?5encontrados?5?$CI?51?5?4?4?5?$CFi?5?$CJ?5@ DB 'Nu'
DB 'meros encontrados ( 1 .. %i ) que son de amstrong', 0aH, 00H ; `string'
CONST ENDS
; COMDAT ??_C@_0CF@MMOH@?$CFi?0?5suma?5de?5sus?5digitos?5al?5cubo?0@
CONST SEGMENT
??_C@_0CF@MMOH@?$CFi?0?5suma?5de?5sus?5digitos?5al?5cubo?0@
DB '%i, suma '
DB 'de sus digitos al cubo, %i', 0aH, 00H ; `string'
CONST ENDS
; COMDAT
_main
; Se declaran las variables del programa
_TEXT SEGMENT
_n$ = -4
_numero$ = -8
_suma$ = -12
_temp$ = -16
_digito$ = -20
_main PROC NEAR ;
COMDAT
; l¡nea
4: main() inicio del main
en el programa de codigo de m s alto nivel
push ebp
mov ebp, esp
sub esp, 84 ;
00000054H
push ebx
push esi
push edi
lea edi, DWORD
PTR [ebp-84]
mov ecx, 21 ;
00000015H
mov eax, -858993460 ;
ccccccccH
rep stosd
; linea
6: const int n = 3000; se
asigna 3000 a la variable n
; en caso de el ensamblador
la variable n es _n$
mov DWORD PTR _n$[ebp],
3000 ; 00000bb8H
; Se declaran las variables en el lenguaje de alto
nivel, en el ensamblador
; son declaradas en el segmento de datos
; linea
10: int numero, suma, temp,
digito;
; se imprime mensaje
mediante el procedimiento externo _printf
; linea
13: printf("Numeros
encontrados ( 1 .. %i ) que son de amstrong\n",
n);
push 3000 ;
00000bb8H
push OFFSET
FLAT:??_C@_0DF@JJPP@Numeros?5encontrados?5?$CI?51?5?4?4?5?$CFi?5?$CJ?5@ ;
`string'
call _printf
add esp, 8
; ******************************inicio
del for****************************
; linea
17: for(numero = 1; numero <= n; numero++)
mov DWORD PTR _numero$[ebp], 1 ;asigna 1 a la variable _numero$
jmp SHORT $L583
$L584:
mov eax, DWORD PTR _numero$[ebp]
add eax, 1
;incrementa en 1 la variable _numero$
mov DWORD PTR _numero$[ebp], eax
$L583:
cmp DWORD PTR _numero$[ebp], 3000 ;se compara numero con 3000
jg SHORT
$L585 ;si es mayor salta a $L585 donde termina el for
; linea
20: suma = 0; se le asigna a
_suma$ el valor de 0
mov DWORD PTR _suma$[ebp], 0
; linea
21: temp
= numero; se le asigna a _temp$ el valor de ecx que
;
es el mismo de _numero$
mov ecx, DWORD PTR _numero$[ebp]
mov DWORD PTR _temp$[ebp], ecx
$L587:
;*********************inicio
del while************************************
; linea
23: while(
temp != 0)
cmp DWORD PTR _temp$[ebp], 0 ; se compara _temp$ con 0
y si es
je SHORT
$L588 ; igual salta a
$L588
; linea
25: digito = temp
% 10;
mov eax, DWORD PTR _temp$[ebp] ; se divide el valor de _temp$
cdq
mov ecx, 10 ;
0000000aH
idiv ecx
mov DWORD PTR _digito$[ebp], edx ; se guarda el residuo
en _digito$
; linea
26: suma = suma + (digito * digito * digito);
mov edx, DWORD PTR _digito$[ebp]
imul edx, DWORD PTR _digito$[ebp] ;se multiplica digito 2 veces
imul edx, DWORD PTR _digito$[ebp] ;por su propio valor
mov eax, DWORD PTR _suma$[ebp]
add eax, edx ;se suma al resultado el
valor de _suma$
mov DWORD PTR _suma$[ebp], eax ;se guarda en suma
; linea 27: temp = temp /
10;
mov eax, DWORD PTR _temp$[ebp]
cdq
mov ecx, 10 ;
0000000aH
idiv ecx ;se divide _temp$ entre 10
mov DWORD PTR _temp$[ebp], eax ;se guarda el resultado en _temp$
jmp SHORT $L587 ;salta
al inicio del while
$L588:
; **************************fin
del while***********************************
; **************************inicio
del if***********************************
; linea
32: if
(suma == numero){
mov edx, DWORD PTR _suma$[ebp]
cmp edx, DWORD PTR _numero$[ebp] ;se compara _numero$ con _suma
jne SHORT $L589 ;si
no son iguales salta a $L589
; linea
33: printf("%i,
suma de sus digitos al cubo, %i\n",
numero, suma);
mov eax, DWORD PTR _suma$[ebp]
push eax
mov ecx, DWORD PTR _numero$[ebp]
push ecx
push OFFSET
FLAT:??_C@_0CF@MMOH@?$CFi?0?5suma?5de?5sus?5digitos?5al?5cubo?0@ ; `string'
call _printf ;se imprime
el mensaje
add esp, 12 ;
0000000cH
$L589:
;****************************fin
del if*************************************
jmp SHORT $L584 ; salta
al inicio del for
$L585:
;****************************fin
del for************************************
; linea 39: return 0;
xor eax, eax
pop edi
pop esi
pop ebx
add esp, 84 ;
00000054H
cmp ebp, esp
call __chkesp
mov esp, ebp
pop ebp
ret 0 ; se liberan los registros y
retorna 0
_main ENDP
_TEXT ENDS
END
;****************************finaliza
el programa***************************
Código Ensamblador Obtenido en Free
Pascal
.386p
LOCALS
@@
DGROUP GROUP _BSS,_DATA
ASSUME CS:_CODE,ES:DGROUP,DS:DGROUP,SS:DGROUP
EXTRN U_SYSWIN32_OUTPUT
EXTRN FPC_WRITELN_END
EXTRN FPC_WRITE_TEXT_SINT
EXTRN FPC_WRITE_END
EXTRN INIT$$SYSWIN32
EXTRN FPC_WRITE_TEXT_SHORTSTR
EXTRN FPC_INITIALIZEUNITS
EXTRN FPC_IOCHECK
EXTRN FPC_OVERFLOW
EXTRN FPC_DO_EXIT
EXTRN U_SYSWIN32_ISCONSOLE
EXTRN FPC_BOUNDCHECK
_CODE SEGMENT PARA PUBLIC USE32 'CODE'
ALIGN 16
; [amstrong.pas]
; [15] BEGIN {
Armstrong }
PUBLIC _main
_main:
PUBLIC PASCALMAIN
PASCALMAIN:
PUBLIC program_init
program_init:
push ebp
mov ebp,esp
sub esp,4
mov byte ptr [dword
ptr U_SYSWIN32_ISCONSOLE],1
call FPC_INITIALIZEUNITS
@@4:
; ************************inicio de escritura de mensaje ******************
; [16] Write('N.meros encontrados (1 .. ');
lea edi,dword ptr [dword ptr U_SYSWIN32_OUTPUT]
mov dword ptr
[ebp-4],edi
push offset
_$ARMSTRONG$_L7
mov edi,dword ptr
[ebp-4]
push edi
xor edi,edi
push edi
call FPC_WRITE_TEXT_SHORTSTR
mov edi,dword ptr
[ebp-4]
push edi
call FPC_WRITE_END
push offset @@4
call FPC_IOCHECK
@@8:
; [17] WriteLn(N:1, ') que son de Armstrong:');
lea edi,dword ptr [dword ptr U_SYSWIN32_OUTPUT]
mov dword ptr
[ebp-4],edi
push 3000
mov edi,dword ptr
[ebp-4]
push edi
push 1
call FPC_WRITE_TEXT_SINT
push offset
_$ARMSTRONG$_L15
mov edi,dword ptr
[ebp-4]
push edi
xor edi,edi
push edi
call FPC_WRITE_TEXT_SHORTSTR
mov edi,dword ptr
[ebp-4]
push edi
call FPC_WRITELN_END
push offset @@8
call FPC_IOCHECK
@@16:
; [18] WriteLn;
lea edi,dword ptr [dword ptr U_SYSWIN32_OUTPUT]
mov dword ptr
[ebp-4],edi
push edi
call FPC_WRITELN_END
push offset @@16
call FPC_IOCHECK
;************************inicio de escritura de mensaje *******************
; [19] FOR numero := 1 TO N DO BEGIN
;*************************inicio del for***********************************
mov word ptr [dword ptr _NUMERO],1 ; asigna 1 a _NUMERO
ALIGN 4
@@19:
; [20] suma := 0;
mov word ptr [dword ptr _SUMA],0 ; asigna 0 a _SUMA
; [21] temp := numero;
mov di,word ptr [dword ptr _NUMERO]
mov word
ptr [dword ptr _TEMP],di ;asigna _NUMERO a
_TEMP
;************************ inicio del while*********************************
; [22] WHILE temp <> 0 DO BEGIN { suma de d.gitos }
jmp @@27 ;salta a @@27 para hacer la comparaci¢n
@@26:
; [23] digito := temp MOD 10; { al cubo }
movsx eax,word ptr [dword
ptr _TEMP]
mov edi,10
cdq
idiv edi
mov ecx,edx
mov edi,offset R_31
call FPC_BOUNDCHECK
mov word ptr [dword ptr _DIGITO],dx; se guarda en _DIGITO
;el valor del residuo
; [24] suma := suma + (digito * digito * digito);
movsx eax,word ptr [dword
ptr _DIGITO]
movsx edx,word ptr [dword ptr _DIGITO];se multiplica DIGITO por
imul edx,eax ;su propio valor
jno @@34
call FPC_OVERFLOW
@@34:
movsx eax,word ptr [dword ptr _DIGITO];se vuelve a multiplicar
imul eax,edx ;digito por el mismo
jno @@35
call FPC_OVERFLOW
@@35:
movsx edx,word ptr
[dword ptr _SUMA]
add edx,eax
jno @@36
call FPC_OVERFLOW
@@36:
mov ecx,edx
mov edi,offset R_31
call FPC_BOUNDCHECK
mov word ptr [dword ptr _SUMA],dx ; se suma _SUMA a el resultado
; anterior y se guarda en _SUMA
; [25] temp := temp DIV
10;
movsx eax,word ptr
[dword ptr _TEMP]
mov edi,10
cdq
idiv edi
mov ecx,eax
mov edi,offset R_31
call FPC_BOUNDCHECK
mov word ptr [dword ptr _TEMP],ax ;se guarda en _TEMP el
;valor de temp / 10
@@27:
movsx eax,word ptr
[dword ptr _TEMP]
test eax,eax
;comparacion del while
jne @@26 ;si no es igual repite el while
;***************************fin del while*************************************
@@28:
;***************************inicio del if*************************************
; [27] IF suma
= numero THEN BEGIN
movsx eax,word ptr
[dword ptr _SUMA]
movsx edx,word ptr
[dword ptr _NUMERO]
cmp edx,eax ;se compara _SUMA con _NUMERO
jne @@40 ;si no es igual salta a @@40
@@41:
; [28] WriteLn(numero,' Suma de sus d.gitos al cubo ', suma);
;***************************inicio de escritura de mensaje********************
lea edi,dword ptr [dword ptr U_SYSWIN32_OUTPUT]
mov dword ptr
[ebp-4],edi
movsx eax,word ptr
[dword ptr _NUMERO]
push eax
mov edi,dword ptr
[ebp-4]
push edi
xor edi,edi
push edi
call FPC_WRITE_TEXT_SINT
push offset
_$ARMSTRONG$_L46
mov edi,dword ptr
[ebp-4]
push edi
xor edi,edi
push edi
call FPC_WRITE_TEXT_SHORTSTR
movsx eax,word ptr
[dword ptr _SUMA]
push eax
mov edi,dword ptr
[ebp-4]
push edi
xor edi,edi
push edi
call FPC_WRITE_TEXT_SINT
mov edi,dword ptr
[ebp-4]
push edi
call FPC_WRITELN_END
push offset @@41
call FPC_IOCHECK
;**************************fin de escritura de mensaje************************
@@40: ;comparaci¢n del for
cmp word ptr [dword ptr _NUMERO],3000 ;se compara _NUMERO
jge @@18 ;con 3000, si es mayor
inc word ptr [dword ptr _NUMERO] ;salta a @@18 y termina
jmp @@19 ;si no salta a @@19 y
@@18: ;repite el for
;*************************termina el for**************************************
; [31] END. {
Armstrong }
call FPC_DO_EXIT
@@49:
leave
ret
ALIGN
16
_CODE ENDS
_DATA SEGMENT PARA
PUBLIC USE32 'DATA'
DB "FPC 1.0.10 [2003/06/27] for i386
- WIN32"
ALIGN
4
R_31 DD -32768,32767
PUBLIC FPC_RESOURCESTRINGTABLES
FPC_RESOURCESTRINGTABLES DD 0
PUBLIC INITFINAL
INITFINAL DD 1,0
DD offset INIT$$SYSWIN32
DD 0
PUBLIC HEAPSIZE
HEAPSIZE DD 2097152
PUBLIC __stklen
__stklen DD 131072
_DATA ENDS
_DATA SEGMENT PARA
PUBLIC USE32 'DATA'
_$ARMSTRONG$_L7 DB 26,"N",250,"meros encontrados (1 .. ",0
_$ARMSTRONG$_L15 DB 23,") que son de Armstrong:",0
_$ARMSTRONG$_L46 DB 29," Suma de sus d",237,"gitos al cubo ",0
_DATA ENDS
_DATA SEGMENT
_DATA ENDS
;declaracion de las variables usadas
_BSS SEGMENT
_NUMERO DB 2
DUP(?)
_SUMA DB 2 DUP(?)
_DIGITO DB 2
DUP(?)
_TEMP DB 2 DUP(?)
PUBLIC HEAP
HEAP DB 2097152 DUP(?)
_BSS ENDS
END