home *** CD-ROM | disk | FTP | other *** search
/ PCMania 64 / PCMania CD64_1.iso / phy / phy003 / files / articulo.006 < prev    next >
Encoding:
Text File  |  1997-04-12  |  23.0 KB  |  620 lines

  1. *cX Curso de programación en assembler (y III)
  2.  
  3. *cR              El primer programa
  4. *c^
  5.   Hoy, por fin, abordaremos el primer ejemplo
  6. práctico en assembler. El primer programa que
  7. haremos será (como no) el *c hola mundo*c^. Pero
  8. antes de poder darle caña al ensamblador ne-
  9. cesitamos tener alguno... la verdad es que
  10. todos teneis uno en vuestro PC, el Debug, pe-
  11. ro cuando hay que hacer cosas complicadas se
  12. hace imposible y por eso a lo largo del curso
  13. pasaremos un poco de él (excepto en este
  14. artículo) y nos centraremos en el Turbo
  15. Assembler y el A86 que al ser shareware y
  16. tener una sintaxis simplificada os ayudará
  17. a empezar.
  18.   El Debug del DOS se invoca escribiendo
  19. *cvDEBUG*c^ en la linea de comandos y si quieres
  20. puedes poner detrás el nombre del programa a
  21. debuggear o a crear. Para hacer el *c hola
  22. mundo*c^ entraremos escribiendo:
  23. *cH
  24.     DEBUG hola.com
  25. *c^
  26. lo primero que el programa nos dirá es que
  27. hemos cometido un error porque el fichero
  28. *c hola.com*c^ no existe (si no nos da el error
  29. es que si existe y lo sobre-escribiremos).
  30. Ahora nos encontramos con un prompt que es
  31. una rallita *cj-*c^ y ahí es donde deberemos
  32. entrar los comandos, por ejemplo *cj?*c^ te
  33. mostrará todos los posibles. El que más nos
  34. va a interesar de momento es *cja*c^ que nos
  35. permite ensamblar directamente las intruc-
  36. ciones que entremos.
  37.   La primera instrucción que veremos hoy (y
  38. la que es más usada) será MOV. Con ella con-
  39. seguimos copiar el contenido de una variable,
  40. registro, constante en otro. Su prototipo es
  41. bastante sencillo:
  42. *cH
  43.         MOV     <destino>, <origen>
  44. *c^
  45.  
  46. donde <origen> será el valor que copiaremos
  47. en <destino>. En general, en el ensamblador
  48. estandard de los 80X86 se especifica primero
  49. el destino y luego el origen como veremos en
  50. siguientes instrucciones. Un registro es una
  51. especie de variable que se guarda muy cer-
  52. quita del procesador y por ello se accede a
  53. ella de forma muy rápida. Una variable la
  54. consideraremos una porción de memoria de
  55. tamaño BYTE, WORD... que podrá tomar distin-
  56. tos valores según lo que queramos hacer. Una
  57. constante será un valor que definiremos cuan-
  58. do creemos el código fuente y que no se podrá
  59. cambiar bajo ningún concepto.
  60.   Los registros son una parte fundamental de
  61. la arquitectura de un ordenador que ayudan al
  62. programador a acelerar los programas. Existen
  63. 4 registros llamados "de uso general" que son
  64. AX, BX, CX y DX de 16bits cada uno y dividi-
  65. dos en dos partes de 8bits (parte alta-high
  66. y parte baja-low):
  67. *cH
  68.            AX --> AH - AL
  69.            BX --> BH - BL
  70.            CX --> CH - CL
  71.            DX --> DH - DL
  72. *c^
  73. Existen tambien otros registros no impor-
  74. tantes de momento como son CS, SS, SP, DI...
  75. Simplemente mencionar el registro DS que es
  76. un registro de segmento que lo que hace es
  77. apuntar al segmento de memoria sobre el que
  78. se opera (recordad lo que deciamos de la me-
  79. moria segmentada del PC). Por ejemplo, si
  80. decimos que DS:DX apunta a una cadena de
  81. texto, lo que queremos decir es que en el
  82. segmento DS si nos desplazamos DX posiciones
  83. nos encontraremos con una ristra de bytes
  84. que definen una cadena de texto; a DS se le
  85. llamaria segmento y a DX se le llamaria off-
  86. set o desplazamiento.
  87.   Bueno veamos como queda el programa y se-
  88. guimos explicando cosas a partir del mismo:
  89. *cH
  90.         mov ax, cs       *cO       [1]
  91. *cH        mov ds, ax       *cO       [2]
  92. *cH        mov dx, 110      *cO       [3]
  93. *cH        mov ah, 9        *cO       [4]
  94. *cH        int 21           *cO       [5]
  95. *cH        mov ax, 4c00     *cO       [6]
  96. *cH        int 21           *cO       [7]
  97. *cH        db 'Hola mundo!!$'*cO      [8]
  98. *c^
  99. para guardarlo debemos presionar ENTER, es-
  100. cribir *c RCX*c^, pulsar ENTER, escribir la lon-
  101. gitud del programa a guardar y ENTER y escri-
  102. bir *c w*c^ (y ENTER :). Si te dice que hay algún
  103. error es que no has hecho algo bien. En este
  104. caso el tamaño del programa a almacenar es 1D
  105. (habrás observado que en el Debug todos los
  106. números se deben escribir en hexadecimal.
  107.   Vamos con el comentario línea por línea del
  108. programa (supongo que te darás cuenta de que
  109. no debes escribir los numeros entre corche-
  110. tes ;). En la primera linea vemos una de las
  111. asignaciones MOV de las que hablabamos antes,
  112. el registro CS (que apunta al trozo de có-
  113. digo) se cópia en AX. La segunda instrucción
  114. hace algo parecido, pero esta vez cópia en
  115. un registro de segmento lo que habia en AX
  116. (que mira tu por donde es lo mismo que lo
  117. que hay en CS). En la linea 3 se asigna una
  118. constante a un registro, DX pasará a valer
  119. 110h. Lo mismo que antes se hace en la cuarta
  120. línea solo que ahora se trata de un registro
  121. de 8bits (la mitad de arriba de AX). Las ins-
  122. trucciones 5 y 7 son llamadas a interrupcio-
  123. nes y las veremos ahora enseguida. La ins-
  124. trucción 6 es otra vez una asignación de una
  125. constante a un registro (observa lo frecuen-
  126. tes que son las instrucciones MOV). Y por úl-
  127. timo, la palabra clave *c db*c^ no se trata de
  128. una instrucción própiamente dicha, sino que
  129. sirve para definir una espécie de matriz o
  130. array de BYTES (como en los lenguajes de alto
  131. nivel).
  132.   Todo esto está muy bien, ¿pero donde está
  133. el análisis semántico? Pues aquí mismo... Si
  134. miramos lo que hace el programa en las 2 pri-
  135. meras líneas es meter en *c ds*c^ el contenido de
  136. *c cs*c^, entonces ¿por que no hacer *c MOV DS,CS*c^
  137. directamente? Sencillo, porque no se puede:
  138. los registros de segmento no pueden asignarse
  139. a otros registros de segmento, por tanto,
  140. cada vez que queramos hacer algo así, tendre-
  141. mos que copiar a un registro auxiliar el con-
  142. tenido del origen. Las dos siguientes líneas
  143. (3 y 4) son una simple preparación para la
  144. interrupción que se lanzará en la línea 5
  145. (tambien forman parte de esa "preparación"
  146. las instrucciones 1 y 2). Las interrupciones
  147. las veremos más a fondo ahora mismo, pero
  148. baste decir que la interrupción que llamamos
  149. primero nos permite ver una cadena en panta-
  150. lla y la segunda hace que regresemos al
  151. sistema operativo.
  152. *cR
  153.            Las interrupciones
  154. *c^
  155.   Las interrupciones son un método de sim-
  156. plificar la tarea al programador ya son como
  157. pequeñas funciones o procedimientos que de-
  158. vuelven valores valiosos o simplemente hacen
  159. algo que resultaria difícil de programar ma-
  160. nualmente. Los "parámetros" se le pasan en
  161. los registros y los valores nos los devuelve
  162. en registros tambien. En el PC hay exacta-
  163. mente 256 servicios de interrupción (del 00h
  164. al FFh) que se dividen en centenares de sub-
  165. servicios por lo que para programar en assem-
  166. bler se hace necesaria una lista suficiente-
  167. mente completa de todas las interrupciones
  168. como la de *cRalf Brown*c^ (que podeis encontrar
  169. en muchos CD-ROMs de share).
  170.   Si comprendemos apróximadamente este
  171. funcionamiento ya estamos en disposición de
  172. ver que es exactamente lo que se hacia en el
  173. programa de ejemplo. La INT 21h es la inter-
  174. rupción que lleva el grueso de los servicios
  175. del sistema operativo (DOS), tiene unos 200
  176. subservicios y debe identificar a cual de
  177. ellos se va a llamar en cada momento. Para
  178. ello, en el registro AH debemos guardar el
  179. número de subservicio (en nuestro caso el 09)
  180. y llamar a los servicios con los parámetros
  181. correspondientes. La lista de Ralf Brown para
  182. este caso indica lo siguiente:
  183. *c9
  184. --------D-2109------------------------------
  185. INT 21 - DOS 1+ - WRITE STRING TO STANDARD
  186.                   OUTPUT
  187.         AH = 09h
  188.         DS:DX -> '$'-terminated string
  189. Return: AL = 24h (the '$' terminating the
  190.              string, despite official docs
  191.              which state that nothing is
  192.              returned) (at least DOS 3.3-5.0)
  193. Notes:  ^C/^Break are checked, and INT 23 is
  194.         called if either pressed standard
  195.         output is always the screen under DOS
  196.         1.x, but may be redirected under DOS
  197.         2+ under the FlashTek X-32 DOS exten-
  198.         der, the pointer is in DS:EDX
  199. SeeAlso: AH=02h,AH=06h"OUTPUT"
  200. *c^
  201.   Lo primero que se indica es el número, ser-
  202. vicio y nombre de la interrupción para poder
  203. localizarla. Luego están los parámetros de
  204. entrada que en nuestro caso son AH=09h (lógi-
  205. camente!!) y DS:DX=<Cadena terminada con '$'>
  206. Observa el código fuente del programa y verás
  207. como comienzas a ligar cabos sueltos. Según
  208. esto, en AL devolverá el valor 24h que cor-
  209. responde al valor ASCII de *c $*c^ (hace tambien
  210. una observación de que esto no está documen-
  211. tado). Como observaciones nos dice que pul-
  212. sando CTRL-C o CTRL-PAUSA haremos saltar la
  213. interrupción 23h (esto lo veremos en un ejem-
  214. plo de otro número) que hace que se interrum-
  215. pa el programa. Tambien nos dice que es con-
  216. veniente ver los subservicios 02h y 06h.
  217.   Veamos nuestro programa como asigna a DS:DX
  218. la cadena terminada en *c $*c^ que aparece al final
  219. del código: primero, como ya habiamos visto,
  220. cópia el segmento de código en DS y luego en-
  221. tra el valor constante 110h en DX. Si en el
  222. Debug escribimos:
  223. *cH
  224.      u 100
  225. *c^
  226. veremos lo que hemos introducido y podremos
  227. observar que al principio de cada línea
  228. aparece una cosa así:
  229. *cH
  230.    XXXX:YYYY
  231. *c^
  232. eso no es ni más ni menos que la dirección
  233. segmentada donde estamos guardando el código
  234. del programa; *cHXXXX*c^ es el segmento e *cHYYYY*c^ es
  235. el desplazamiento u offset dentro de ese
  236. segmento. Por tanto, la dirección que yo he
  237. de pasar es el segmento donde reside la
  238. cadena (que resulta ser el del código) y el
  239. offset dentro de ese segmento que es 110h y
  240. que si miras el programa desensamblado
  241. (despues de usar el comando 'u' del debug)
  242. es 0110 precisamente.
  243.   Un posible pero a todo esto es: pero, ¿por-
  244. que has entrado el offset como constante y el
  245. segmento lo has sacado del registro CS? La
  246. respuesta a esto la entenderás mejor cuando
  247. veamos los programas COM y EXE, aunque es
  248. sencilla (a priori); cuando el sistema opera-
  249. tivo carga un programa (COM en este caso)
  250. lo que hace es buscar un segmento de memoria
  251. libre (los TSR ocupan memoria y el própio SO)
  252. y lo carga a partir de la dirección 0100h,
  253. por tanto podemos saber el offset pero no el
  254. segmento donde se cargará.
  255. *cR
  256.                   El Debug
  257. *c^
  258.   A parte de ensamblar y desensamblar pro-
  259. gramas, el debug (y esa es su razón de ser)
  260. se usa para ejecutar programas paso por paso
  261. como se hace en los IDEs de los lenguajes de
  262. alto nivel. Para ejecutar un paso (step) de
  263. un trozo de código usaremos *c p*c^ y para tra-
  264. zar usaremos *c t*c^. La diferencia entre una
  265. instrucción y otra es simplemente que con
  266. *c t*c^ si encontramos una INT (o algunas
  267. instrucciones que veremos para hacer llamadas
  268. a subprocedimientos) entraremos dentro de
  269. ella, mientras que con *c p*c^ ejecutaremos el
  270. código linealmente. Para lo que sabemos
  271. ahora es suficiente con *c p*c^ (no usar *c t*c^
  272. delante de INT!!).
  273.   La utilidad de todo esto cuando se está
  274. comenzando es increible, así podrás ver como
  275. las instrucciones e interrupciones modifican
  276. los registros. Hablando de registros, el
  277. registro IP es un registro que apunta a la
  278. instrucción que está siendo ejecutada en cada
  279. momento dentro del segmento CS del que ya
  280. sabiamos algo. Vamos a ver un ejemplo de
  281. trazeado de un programa (el "hola mundo")
  282. y observaremos como cambian los registros:
  283. *cA
  284. AX=109FBX=0000CX=001DDX=0000SP=FFFE
  285.    -->BP=0000SI=0000DI=0000
  286. DS=109FES=109FSS=109FCS=109FIP=0102
  287.    -->NV UP EI PL NZ NA PO NC
  288. 109F:0102 8ED8          MOV     DS,AX
  289. *c^
  290. Al ejecutarse la primera instrucción AX
  291. ha adquirido el valor de CS. Podemos observar
  292. tambien que DS es igual a CS y que no es ne-
  293. cesario copiarlo, pero siempre es mejor ase-
  294. gurarse de hacer las cosas bien. La instruc-
  295. ción 'p' nos muestra tambien cual será la si-
  296. guiente instrucción a ejecutar (MOV DS,AX).
  297. Observa el valor de CS:IP y la dirección de
  298. la siguiente instrucción a ser
  299. ejecutada y fíjate que es el mismo.
  300. *cA
  301. AX=109FBX=0000CX=001DDX=0000SP=FFFE
  302.   -->BP=0000SI=0000DI=0000
  303. DS=109FES=109FSS=109FCS=109FIP=0104
  304.   -->NV UP EI PL NZ NA PO NC
  305. 109F:0104 BA1001        MOV     DX,0110
  306. *c^
  307. Trás esta instrucción no podemos observar
  308. nada nuevo en DS ya que el valor era
  309. el mismo que tenia anteriormente. Lo que
  310. si podemos ver es que CS:IP sigue
  311. apuntando a la siguiente instrucción a
  312. ejecutar y esto será así siempre.
  313. *cA
  314. AX=109FBX=0000CX=001DDX=0110SP=FFFE
  315.   -->BP=0000SI=0000DI=0000
  316. DS=109FES=109FSS=109FCS=109FIP=0107
  317.   -->NV UP EI PL NZ NA PO NC
  318. 109F:0107 B409          MOV     AH,09
  319. *c^
  320. Podemos ver como ha cambiado DX, pasando
  321. del valor 0000h al 0110h que es el
  322. offset de la cadena.
  323. *cA
  324. AX=099FBX=0000CX=001DDX=0110SP=FFFE
  325.   -->BP=0000SI=0000DI=0000
  326. DS=109FES=109FSS=109FCS=109FIP=0109
  327.   -->NV UP EI PL NZ NA PO NC
  328. 109F:0109 CD21          INT     21
  329. *c^
  330. Observa la parte alta de AX que ha pasado
  331. a valer 09h. Por si todavia no tenias
  332. claro eso de la parte alta y la baja mira
  333. el valor que tenia AX antes (AX=109F)
  334. y mira el que tiene ahora (AX=099F) lo que
  335. ha cambiado es la parte de mayor peso del
  336. registro. Si hubiesemos actuado sobre AL en
  337. lugar de AH, el 9Fh de AX hubiese pasado a
  338. 09h.
  339. *cR
  340. Hola mundo!!*cA
  341. AX=0924BX=0000CX=001DDX=0110SP=FFFE
  342.   -->BP=0000SI=0000DI=0000
  343. DS=109FES=109FSS=109FCS=109FIP=010B
  344.   -->NV UP EI PL NZ NA PO NC
  345. 109F:010B B8004C        MOV     AX,4C00
  346. *c^
  347. En este paso se ejecuta la interrupción y
  348. por tanto se muestra el mensaje en pantalla.
  349. Como se decia en la lista de Ralf Brown, AL
  350. tiene ahora el valor 24h y los demás regis-
  351. tros permanecen inalterados (a excepción de
  352. IP, claro!!).
  353. *cA
  354. AX=4C00BX=0000CX=001DDX=0110SP=FFFE
  355.   -->BP=0000SI=0000DI=0000
  356. DS=109FES=109FSS=109FCS=109FIP=010E
  357.   -->NV UP EI PL NZ NA PO NC
  358. 109F:010E CD21          INT     21
  359. *c^
  360. Vemos como AX ha adquirido el valor 4C00h
  361. para llamar a la interrupción 21h. Este sub-
  362. servicio es el que nos permite regresar al
  363. prompt del DOS pasandole en AH el valor 4Ch
  364. (que indica el número de subservicio) y en
  365. AL el llamado *cferrorlevel*c^ que devuelve el
  366. programa (en este caso 00h). Si volvemos a
  367. teclear el comando 'p' saldremos al DOS ya
  368. que se ejecutará la interrución (como podemos
  369. ver en el campo "siguiente instrucción").
  370.   Tal vez, si escribes *cvu 100*c^ para ver el
  371. programa que has hecho, te sorprenda ver que
  372. la cadena *cvhola mundo!!$*c^ no aparece por nin-
  373. gún sítio. Ello es porque cuando le dices al
  374. debug que desensamble un trozo de código,
  375. interpreta que ese trozo es de intrucciones
  376. y por ello trata de dar un significado a la
  377. ristra de bytes que componen la cadena. Para
  378. ver las cadenas que definamos en nuestro
  379. programa, deberemos escribir la instrucción
  380. *cvd 100*c^ que significa "dump" (volcar) y que
  381. hace un caracteristico listado hexadecimal.
  382.   Si todavia no te has dado cuenta de que
  383. significado tiene ese 100 que ponemos detrás
  384. de las instrucciones *c d*c^ y *c u*c^, deberias de
  385. haberte dado cuenta de que se trata de la
  386. dirección (sólo offset, se supone que el seg-
  387. mento es CS) donde el debug debe empezar a
  388. volcar o desensamblar. Opcionalmente tambien
  389. puedes poner detrás otro offset para decirle
  390. cuando debe acabar, aunque si no lo haces no
  391. pasa nada ya que entiende que quieres un
  392. trozito.
  393.   La instrucción *c w*c^ (write) sirve para alma-
  394. cenar en disco el programa (recuerda que ya
  395. la has usado para salvar el programa) desde
  396. la posición CS:0100h hasta el offset dentro
  397. de CS almacenado en CX más 100h. En realidad
  398. en CX lo que debes guardar es la longitud del
  399. programa (en el "hola mundo" era 1Dh porque
  400. despues de introducir la última instrucción
  401. [la del mensaje] apareció como siguiente
  402. dirección para ensamblar la XXXX:01D1h;
  403. vuelve a reescribir el programa para obser-
  404. varlo). Para poner un valor en un registro,
  405. debemos usar la instrucción *c r*c^ que usado sin
  406. parámetros devuelve el valor de los registros
  407. y con un registro como parámetro permite su
  408. modificación (*c rcx*c^ en este caso).
  409.   Otra instrucción interesante es *c g*c^ (go)
  410. que permitirá que un programa que estamos
  411. trazando con *c t*c^ o *c p*c^ pueda ser ejecutado
  412. hasta su finalización. Si entramos al pro-
  413. grama sin especificar un nombre para el ar-
  414. chivo que queremos almacenar en disco o que-
  415. remos poner otro usaremos la intrucción *c n*c^
  416. pasandole como parámetro el nombre del fi-
  417. chero de salida con extensión COM (no se pue-
  418. den hacer EXEs con el debug).
  419. *cR
  420.      Instrucciones aritméticas
  421. *c^
  422.   Las instrucciones aritméticas son las que
  423. permiten operar con los registros y hacer
  424. sencillos cálculo en complemento a dos (ver
  425. anteriores artículos). Podemos sumar, restar,
  426. multiplicar y dividir directamente y podemos
  427. realizar operaciones más complicadas a base
  428. de estas más simples. Los prototipos de las
  429. instrucciones son estos:
  430. *cH
  431.             ADD  <destino>, <origen>
  432.             SUB  <destino>, <origen>
  433.             MUL  <multipl>
  434.             IMUL <multipl>
  435.             DIV  <divisor>
  436.             IDIV <divisor>
  437. *c^
  438.   Lo primero que se observa es que hay 2
  439. instrucciones para multiplicar y 2 más para
  440. dividir. Las instrucciones MUL y DIV consi-
  441. deran números sin signo, mientras IMUL e IDIV
  442. los consideran con signo. Para sumar 2 regis-
  443. tros lo que hacemos es poner el destino de-
  444. lante y lo que le sumaremos detrás. Es equi-
  445. valente a la instrucción de C "+=". Veamos
  446. un par de ejemplos:
  447. *cH
  448.         MOV     AX, 25*cO
  449.                   ;  AX pasa a valer 25*cH
  450.         MOV     BX, 35*cO
  451.                   ;  BX pasa a valer 35*cH
  452.         ADD     AX, BX*cO
  453.                   ;  BX se mantiene y AX
  454.                   ; valdrá 25+35=60*cH
  455.  
  456.         MOV     AX, 100*cO
  457.                   ;  AX = 100*cH
  458.         MOV     BX, 50*cO
  459.                   ;  BX = 50*c├
  460.         ·····
  461. *cO                  ;  Queremos que AX y BX
  462.                   ; no cambien, usamos*cH
  463.         MOV     CX, AX*cO
  464.                   ; otro registro para
  465.                   ; almacenar el resultado.*cH
  466.         ADD     CX, BX*cO
  467.                   ;  CX = 150, AX = 100,
  468.                   ; BX = 50*c^
  469.  
  470.   Tambien podemos operar con memoria, pero
  471. recordando siempre que el 80x86 no nos per-
  472. mite operaciones de memoria a memoria (es
  473. incorrecto MOV [v1], [v2]). Para la resta
  474. lo mismo, se restará el "origen" al "des-
  475. tino", es decir, como si hiciesemos en un
  476. HLL "destino=destino-origen". Algunos
  477. ejemplos:
  478. *cH
  479.     MOV     AX, 100   *cO      ;  AX vale 100
  480. *cH    SUB     AX, 10  *cO        ;  AX valdrá 90
  481. *cH    MOV     BX, 90  *cO        ;  BX = 90
  482. *cH    SUB     AX, BX  *cO        ;  AX se hace 0
  483.  
  484. *cH    MOV     AX, 25  *cO        ;  AX = 25
  485. *cH    MOV     BX, 15  *cO        ;  BX = 15
  486. *c├    ·····           *cO        ;  Supongamos que
  487.                 ; queremos guardar en memoria
  488. *cH    MOV     DS:[130h], AX*cO   ; AX - BX
  489. *cH    SUB     DS:[130h], BX*cO   ;  En memoria 10
  490. *c^
  491.   Despues de ver este último ejemplo tengo
  492. que aclarar un par de cosas. Lo primero,
  493. cuando no escriba una *c h*c^ o una *c o*c^ al final
  494. del número, estaré escribiendo los números
  495. en decimal (a no ser que el código este dise-
  496. ñado explícitamente para ser usado en el
  497. debug). Lo segundo, que cuando queramos
  498. almacenar en memoria un valor, deberemos
  499. haber definido una zona para el almacena-
  500. miento de memoria (con las instrucciones
  501. DB, DW o DD que hemos visto superficial-
  502. mente) y luego recordar la dirección donde
  503. están definidas para acceder a ellas escri-
  504. biendo su dirección entre corchetes. Este
  505. sistema tan arcaico es sólo necesario con
  506. el debug, ya que todos los ensambladores
  507. modernos incorporan etiquetas (en el pró-
  508. ximo número). Por ejemplo, tomemos del debug
  509. un trozo de código que haga uso de esta
  510. caracteristica:
  511.  
  512. *cH10BB:0100 8CC8          MOV     AX,CS
  513. *cO           ;  Primero metemos el segmento
  514. *cH10BB:0102 8ED8          MOV     DS,AX
  515. *cO         ; correcto.
  516. *cH10BB:0104 B85000        MOV     AX,0050
  517. *cH10BB:0107 BB3000        MOV     BX,0030
  518. *cO         ;  Los datos a restar.
  519. *cH10BB:010A A31601        MOV     [0116],AX
  520. *cO       ;  Escribimos a memoria.
  521. *cH10BB:010D 291E1601      SUB     [0116],BX
  522. *cO       ;  Restamos de memoria.
  523. *cH10BB:0111 B8004C        MOV     AX,4C00
  524. *cO       ;  Función de salir al DOS
  525. *cH10BB:0114 CD21          INT     21
  526. *cO         ;  Hasta luego lucas!
  527. *cH10BB:0116 0000          DW      0
  528. *cO      ;  Aquí es donde definimos la WORD que
  529.       ; almacenará los numeritos.
  530. *c^
  531.   Las instrucciones multiplicativas tienen un
  532. aspecto más complicado, sólo tienen 1 pará-
  533. metro. Para hacer una multiplicación se coge
  534. el acumulador y se opera con el registro o
  535. posición de memoria que le pasemos como pa-
  536. rámetro. Pero, ¿que es eso del acumulador?
  537. Pues depende, puede ser AL o AX. Como sabrás
  538. (si prestaste atención en los anteriores
  539. números) cuando multiplicamos dos números
  540. de n-bits, la mayor de las cifrás que ten-
  541. dremos será de 2n-bits. Así, pues, se estan-
  542. dariza la operación de multiplicación y si
  543. se quiere multiplicar 2 números deberán te-
  544. ner los mismos bits (8 o 16) y el resultado
  545. se almacenará en un registro de 2n-bits. En
  546. definitiva, si queremos multiplicar un nú-
  547. mero de 8-bits el acumulador será AL y si
  548. es de 16 será AX. Para almacenar el resul-
  549. tado de los de 8-bits usaremos AX y para
  550. los de 16 usaremos el par DX-AX donde DX
  551. serán los 16-bits de mayor peso y AX los
  552. 16 de menor. Ejemplos:
  553.  
  554. *cH MOV     AX, 10    *cO      ;  AX = 10
  555. *cH MOV     BX, 50    *cO      ;  BX = 50
  556. *cH MUL     BX        *cO      ;  DX-AX = AX x BX,
  557.                          ; y como 500 cabe en
  558.                          ; 16 bits
  559.                          ; AX valdrá 500 y DX
  560.                          ; será 0.
  561.  
  562. *cH MOV     AL, 120      *cO   ;  AL = 120
  563. *cH MOV     CL, 40       *cO   ;  CL = 40
  564. *cH MUL     CL           *cO   ;  AX = AL x CL =
  565.                          ; 120 x 40 = 4800
  566. *c^
  567.   La división es lo contrario de la multipli-
  568. cación, cuando uso el divisor de 8-bits, el
  569. acumulador es de 16 y cuando lo uso de
  570. 16-bits, el acumulador será el de 32 (DX-AX).
  571. Vamos directamente con un ejemplo:
  572.  
  573. *cH MOV     CL, 100    *cO     ;  CL = 100
  574. *cH MOV     BL, 10     *cO     ;  BL = 10.
  575.            ; Queremos dividir CL/BL:
  576. *cH MOV     AL, CL     *cO     ;  Tenemos un
  577.            ; problema, AH puede contener un
  578. *cH MOV     AH, 0      *cO     ; valor distinto de
  579.            ; 0, pues lo "borramos".
  580. *cH DIV     BL         *cO   ;  AL = 100 / 10 = 10
  581. *c^
  582.   El cociente de esta operación de división
  583. se almacena en AL y el resto lo hace en AH.
  584. Como el acumulador para divisores de 8-bits
  585. es de 16, hemos copiado a AX el dividendo,
  586. haciendo 0 la parte de arriba (más adelante
  587. veremos como hacer esto en una sóla instruc-
  588. ción). Si usasemos un registro de 16-bits
  589. tendriamos que hacer DX=0.
  590.  
  591.   Creo que ya ha habido bastante por hoy,
  592. ¿no? En el próximo número veremos las ins-
  593. trucciones lógicas, los saltos, comparacio-
  594. nes y si hay tiempo las entradas y salidas,
  595. además de un especial interrupciones. Os
  596. dejo unos deberes para casa: determinad que
  597. valores contendrán los registros de uso ge-
  598. neral (AX, BX, CX y DX) al terminar el pro-
  599. grama o decid si no es posible ensamblarlos
  600. por contener algún error (se deben hacer sin
  601. usar el PC):
  602. *cH
  603. 1) MOV     AX, 100
  604.    MOV     BX, 200
  605.    ADD     AX, BX
  606.    MUL     BX
  607.  
  608. 2) MOV     AL, 500
  609.    MOV     BL, 100
  610.    SUB     AL, BL
  611.    MUL     10
  612.  
  613. 3) MOV     AX, 30
  614.    MOV     BX, 10
  615.    MUL     BX    
  616.    DIV     BX    
  617.    ADD     AL, BX
  618.  
  619.                             *cG  Navi Dj.
  620.