Capítulo 8 - Invocando Programas en Lenguaje Ensamblador desde el BASIC

A veces es difícil decidir si un programa debe ser escrito en BASIC o en lenguaje ensamblador. Pero no siempre es necesario tomar esta decisión. En muchos casos, puede combinar la simplicidad del BASIC con la velocidad y la versatilidad del lenguaje ensamblador simplemente escribiendo rutinas de lenguaje ensamblador que se pueden llamar desde el BASIC, eso es lo que vamos a aprender en este capítulo.


Los dos primeros programas con los que vamos a trabajar son los que se introdujeron en el Capítulo 7: los programas llamados The Visitor y Response. Sin embargo, antes de que podamos llamar a estos dos programas desde el BASIC, tendremos que hacer un par de cambios en la forma en que fueron escritos. En primer lugar, vamos a tener que eliminar el bucle infinito al final de cada programa, y sustituirlo por la instrucción RTS, de modo que cada programa vuelva a BASIC cuando haya terminado, en vez de quedar en un bucle infinito. También tendrá que añadir una instrucción PLA en el comienzo de cada programa para que no estropeen la pila cuando se les llama desde el BASIC. Más adelante en este capítulo vamos a explicar exactamente por qué son necesarias estas instrucciones PLA. Si acaba de terminar el capítulo 7 y todavía tienen su computador encendido, entonces puede cargar los programas The Visitor y Response y hacer los cambios necesarios en ellos.


Comenzando con The Visitor


Vamos a empezar con el programa llamado The Visitor. Con su ensamblador funcionando y en modo EDIT, cargue el código fuente
de The Visitor en el computador y teclee LIST. Esto es lo que debería ver:


THE VISITOR

10   ;
20   ; THE VISITOR
30   ;
35   TXTBUF=$5041
40   OPNSCR=$5003
50   PRNTLN=$5031
55   BUFLEN=40
60   FILLCH=$20
70   ;
80           *=$0600
90   ;
0100 TEXT    .BYTE $4C,$4C,$45,$56,$41,$4D,$45,$20
0110         .BYTE $43,$4F,$4E,$20,$54,$55,$20
0120         .BYTE $4C,$49,$44,$45,$52,$21
0130 ;
0140 VIZTOR
0150         JSR FILL
0160         LDX #0
0170 LOOP    LDA TEXT,X
0180         STA TXTBUF,X
0190         INX
0200         CPX #23
0210         BNE LOOP
0220         JSR OPNSCR
0230         JSR PRNTLN
0240 INFIN   JMP INFIN
1300 FILL
1310         LDA #FILLCH
1320         LDX #BUFLEN
1330 START
1340         DEX
1350         STA TXTBUF,X
1360         BNE START
1370 RTS

Ahora puede modificar el programa para que pueda ser llamado desde el BASIC. En primer lugar, inserte una línea que contenga una instrucción PLA digitando

145 PLA

A continuación, cambie el ciclo infinito en la línea 240 por una instrucción RTS escribiendo

240 RTS

Cuando haya terminado, le sugiero que cambie el nombre del programa por VISITOR.SR2 para diferenciarlo del programa original. Para ello, escriba

20 ;VISITOR.SR2

y presione la tecla RETURN.
Listo.


Cuando haya hecho todos los cambios en el programa VISITOR.SRC, escriba LIST de nuevo y esto es lo que debería ver:


THE VISITOR

10 ;
20 ; VISITOR.SRC2
30 ;
35 TXTBUF=$5041
40 OPNSCR=$5003
50 PRNTLN=$5031
55 BUFLEN=40
60 FILLCH=$20
70 ;
80           *=$0600
90 ;
0100 TEXT    .BYTE $4C,$4C,$45,$56,$41,$4D,$45,$20
0110         .BYTE $43,$4F,$4E,$20,$54,$55,$20
0120         .BYTE $4C,$49,$44,$45,$52,$21
0130 ;
0140 VIZTOR
0145         PLA
0150         JSR FILL
0160         LDX #0
0170 LOOP    LDA TEXT,X
0180         STA TXTBUF,X
0190         INX
0200         CPX #23
0210         BNE LOOP
0220         JSR OPNSCR
0230         JSR PRNTLN
0240         RTS
1300 FILL
1310         LDA #FILLCH
1320         LDX #BUFLEN
1330 START
1340         DEX
1350         STA TXTBUF,X
1360         BNE START
1370         RTS

Guardando su programa corregido


Cuando haya hecho todos los cambios necesarios en el código de su programa The Visitor, guárdelo nuevamente en un disco – tanto en sus versiones fuente como objeto – usando los nombres de archivo VISITOR.SR2 y VISITOR.OB2. Entonces estará listo para modificar el otro programa que le presentamos en el Capítulo 7 – Response – de modo que también pueda ser llamado desde el BASIC.


Modificando el programa Response


Para arreglar el programa RESPONSE.SRC de modo que se pueda acceder desde el BASIC, sólo tiene que cargar el código fuente en el computador y hacer que los mismos tres cambios que hizo en el programa The Visitor. Cuando los haya realizado, Response debería verse así:


RESPONSE.SRC

10 ;
20 ; RESPONSE
30 ;
40 TXTBUF=$5041
50 OPNSCR=$5003
60 PRNTLN=$5031
65 BUFLEN=40
70 FILLCH=$20
75 ;
80 EOL=$9B
90 ;
0100         *=$0650
0110 ;
0120 TEXT    .BYTE " YO SOY vuestro líder, tonto!",EOL##
0130 ;
0140 RSPONS
0145         PLA
0150         JSR FILL
0160         LDX #0
0170 LOOP
0180         LDA TEXT,X
0190         STA TXTBUF,X
0200         CMP #$9B
0210         BEQ FINI
0220         INX
0230         JMP LOOP
0240 FINI
0250         JSR OPNSCR
0260         JSR PRNTLN
0270         RTS
1300 FILL
1310         LDA #FILLCH
1320         LDX #BUFLEN
1330 START
1340         DEX
1350         STA TXTBUF,X
1360         BNE START
1370         RTS

Ejecutándolo


Cuando haya realizado los cambios necesarios en el programa Response, puede guardar sus nuevas versiones bajo los nombres de archivo RESPONSE.SR2 y RESPONSE.OB2. Entonces estaremos listos para llamar a los programas The Visitor y Response desde el BASIC. Para ello, necesitará un cartucho de BASIC en su computador, si éste es un Atari 400, 800 o un 1200XL. (No necesita un cartucho de BASIC si tiene uno de los modelos actualizados de computador Atari, ya que éstos traen BASIC incorporado.) Cuando tenga su computador funcionando nuevamente, ejecutando el BASIC, y con el disco de datos en la unidad de disco, lo primero que tendrá que hacer es llamar al menú del DOS de Atari.


Cuando éste haya cargado, seleccione la opción L del menú (carga binaria) y cargue en la memoria de su computador los programas VISITOR.OB2, RESPONSE.OB2 y PRNTSC.OB2.


Cuando haya cargado los tres programas, seleccione la opción B para devolver el control al intérprete BASIC de computador. Luego, cuando en su pantalla aparezca el mensaje READY del intérprete BASIC, escriba el siguiente comando BASIC:

X=USR(1559)

Si ha escrito y ensamblado su programa VISITOR.SR2 de la misma manera en que lo hicimos anteriormente, y si su código objeto está almacenado en la memoria de su computadora, entonces éste debería ejecutarse tan pronto como escriba la orden X = USR (1559) y presione la tecla RETURN, ya que su dirección de comienzo es $0617, o 1559 en decimal.


Del mismo modo, puede ejecutar RESPONSE.OB2 simplemente escribiendo

X=USR(1643)

Y presionando la tecla RETURN.


Ahora que ya ha invocado dos programas desde el BASIC, estamos listos para ver cómo lo hizo: en concreto, cómo él trabaja la función USR del BASIC de Atari, y cómo se utiliza en la programación en lenguaje ensamblador.


La función USR


Los programas de lenguaje de máquina, como acabamos de señalar, son llamados desde el BASIC por medio de una función especial llamada USR. La función USR puede ser escrita de dos maneras: sin argumentos, o con uno o más argumentos opcionales. Una llamada que no incluye argumentos se escribe con el formato que utilizamos para llamar a nuestros programas The Visitor y Response:

X=USR(1643)

Cuando se escribe una llamada utilizando este formato, el número entre paréntesis equivale a la dirección inicial (expresada como un número decimal) del programa de lenguaje de máquina que va a ser llamado. Cuando el programa en lenguaje de máquina termine su ejecución, el control del equipo será devuelto al BASIC. Si un programa está en ejecución y éste utiliza la función USR, el programa se reanudará en la primera instrucción que viene a continuación de la función USR, una vez que el control sea devuelto al BASIC.


Funciona como "GOSUB"


En este sentido, la orden USR funciona como una instrucción GOSUB común. Como una subrutina escrita en BASIC, un programa en lenguaje de máquina llamado desde el BASIC debe terminar con una instrucción de retorno. En BASIC, la instrucción de retorno es, lógicamente, RETURN. En el lenguaje ensamblador, la instrucción de retorno es RTS, que significa "ReTurn from Subroutine – Retorno de la subrutina". Una llamada en la que se utilizan argumentos se ve más o menos así:

X=USR(1536,ADR(A$),ADR(B$))

O así:

X=USR(1536,X,Y)

Cuando se usan argumentos en la función USR, cada argumento puede ser cualquier valor que equivalga a un número de 16 bits. Se pueden realizar operaciones de lenguaje de máquina sobre los valores referidos en los argumentos, y los resultados de esas operaciones se pueden pasar de vuelta al BASIC cuando se terminen las operaciones en lenguaje de máquina. De esta manera, se pueden usar en programas BASIC operaciones que requieren ejecutarse en velocidad lenguaje de máquina. Ya sea si se usan o no argumentos en una función USR, un programa en lenguaje de máquina llamado por la función USR también puede devolver un valor de 16 bits al BASIC, cuando le pase el control a éste.


Volviendo al BASIC


Para devolver un valor al BASIC cuando un programa en lenguaje de máquina haya terminado, todo lo que tiene que hacer es almacenar el valor que va a ser devuelto en dos posiciones de memoria especiales de 8-bits, antes de retornar el control al BASIC. Estas dos posiciones son $D4 y $D5 (212 y 213 en notación decimal). Cuando un valor que va a ser devuelto al BASIC se almacena en estas dos posiciones, debe ser almacenado con el byte menor en $D4 y el byte mayor en $D5. Cuando se devuelva el control al BASIC, el valor de 16 bits en las dos posiciones será convertido automáticamente a un valor decimal entre 0 y 65535. Luego ese valor será devuelto al BASIC como el valor de la variable en la orden USR, por ejemplo, la "X" en la orden X = USR (1536, X, Y).


Cómo trabaja la función USR


Cuando un programa BASIC encuentra una orden USR, se coloca en la parte superior de la pila la dirección de memoria de la instrucción actual del programa en ejecución. Luego, se pone en la pila un número de 8 bits correspondiente al número de argumentos que aparecen en la orden USR. Si no hay argumentos en la orden USR, se coloca un cero en la parte superior de la pila. Cuando una orden USR llama a una subrutina en lenguaje de máquina, la dirección de retorno de la subrutina en lenguaje de máquina siempre es "tapada" en la pila por otro número y, por lo tanto, éste debe ser eliminado de la pila (incluso si es un cero) antes de que el control sea devuelto al BASIC.


Limpiando la Pila


Y es por eso que el primer mnemotécnico en la mayoría de los programas de lenguaje de máquina diseñados para ser llamados desde el BASIC es una instrucción para "limpiar la pila": la instrucción PLA.


Más operaciones de la Pila


Si se incluyen los argumentos en la función USR, entonces se requieren más operaciones sobre la pila, cuando se tenga que llamar a un programa de lenguaje de máquina. Antes de que el número de argumentos se coloque en la pila, los argumentos mismos son insertados en la pila. Luego, cuando el programa en lenguaje de máquina comienza su ejecución, los argumentos pueden ser eliminados de la pila y ser procesados por el programa en lenguaje de máquina de cualquier forma que se desee. Por último, cuando termina el programa en lenguaje de máquina y el control del computador pasa de nuevo al BASIC, los resultados de las operaciones en lenguaje de máquina que se hayan realizado con los argumentos de la orden USR pueden ser almacenados en las posiciones memoria $D4 y $D5. Los valores que se hayan almacenado en este par de posiciones pueden ser devueltos al BASIC como el valor de la variable usada en la orden USR original.


Un programa de ejemplo


Esto puede sonar un poco complicado, pero un programa de ejemplo que pueda escribir en su computador y ejecutarlo desde el BASIC aclarará las cosas. Escriba el siguiente programa en su computador.


UN PROGRAMA DE SUMAS DE 16 bits

10   ;
20   NUM1 = $CB
30   NUM2 = $CE
40   SUM = $D4
50   ;
60        *=$0600
70   ;
90        CLD
0100      PLA ;LIMPIAR # DE ARGUMENTOS DEL ACUMULADOR
0105      PLA
0110      STA NUM1+1 ;BYTE MAYOR DEL PRIMER ARGUMENTO
0120      PLA
0130      STA NUM1 ;BYTE MENOR DEL PRIMER ARGUMENTO
0140      PLA 0150 STA NUM2+1 ;BYTE MAYOR DEL SEGUNDO ARGUMENTO
0160      PLA 0170 STA NUM2 ;BYTE MENOR DEL SEGUNDO ARGUMENTO
0180 ;
0190      CLC
0210      LDA NUM1 ;BYTE MENOR DE NUM1
0220      ADC NUM2 ;BYTE MENOR DE NUM2
0230      STA SUM ;BYTE MENOR DE SUM
0240      LDA NUM1+1 ;BYTE MAYOR DE NUM1
0250      ADC NUM2+1 ;BYTE MAYOR DE NUM2
0260      STA SUM+1 ;BYTE MAYOR DE SUM
0270      RTS

Cuando haya terminado de digitar el programa, ensámblelo y guarde el código en sus versiones fuente y objeto. Los nombres de archivo sugeridos son "ADD16B.SRC" y "ADD16B.OBJ".


Una Máquina de Sumas


Cuando haya guardado sus programas, saque el ensamblador de su computador, y digite el siguiente programa BASIC:


LA MAQUINA DE SUMAR MAS CARA DEL MUNDO

10 GRAPHICS 0:PRINT:PRINT "LA MAQUINA DE SUMAR MAS CARA DEL MUNDO"
20 PRINT:PRINT "X=";
30 INPUT X
40 PRINT "Y=";
50 INPUT Y
60 SUM=USR(1536,X,Y)
70 PRINT "X+Y=";SUM
80 PRINT
90 GOTO 20

Cuando haya terminado de digitar el programa, guárdelo usando el nombre de archivo "ADD16B.BAS". Esto completa nuestros preparativos para llamar desde el BASIC a nuestro nuevo programa de sumas. Ahora puede llamar desde el BASIC a su programa ADD16B.OBJ de la misma manera que llamó a su programa Response. Sólo tiene que llamar a su menú de DOS y cargar el archivo binario ADD16B.OBJ. Presione la tecla "B" para volver al modo de edición del BASIC, cargue su archivo ADD16B.BAS, y digite "RUN". A continuación, puede comenzar a usar "LA MAQUINA DE SUMAR MAS CARA DEL MUNDO".


Al ingresar algunos números en su nueva máquina de sumar, se dará cuenta de que se pueden realizar sumas de 16 bits. Aunque no hemos cubierto todavía en el libro las sumas de 16-bits, probablemente entenderá cómo funciona el programa sin problemas.


En primer lugar, en las líneas de la 20 a 40, el programa reserva espacio de memoria para tres números de 16 bits: los dos números que se sumarán, y la suma de ambos. Los dos números que se sumarán están etiquetados como num1 y num2, y la dirección donde se almacenará la suma tiene la etiqueta SUM. En la línea 100, el programa borra la pila hardware con una instrucción PLA. Esta orden elimina el valor de 8 bits que representa el "número de argumentos" que nuestro programa BASIC ha colocado en la parte superior de la pila – los números de 16 bits que se han incluido en la orden USR de nuestro programa BASIC. Estos son los dos números que se sumarán, y nuestro programa de lenguaje de máquina ahora los coloca en los dos pares de posiciones de memoria de 8 bits cuyas etiquetas son NUM1 y NUM2.


El corazón del programa


Ahora hemos llegado a la parte principal de nuestro programa en lenguaje ensamblador. En la línea 200, el programa desactiva la bandera de acarreo, tal como debería hacerlo todo buen programa de sumas. Entonces comienza la suma propiamente tal.
El programa suma los bytes menores de NUM1 y NUM2, y almacena el resultado de este cálculo en el byte menor de SUM. A continuación, suma los bytes mayores de NUM1 y NUM2 (junto con un acarreo, si es que lo hay) y almacena el resultado de este cálculo en el byte mayor de SUM.


Eso es todo lo que hay que hacer. Como resultado, y esto no es un accidente, la dirección de memoria que se ha reservado para el valor SUM son $D4 y $D5, las dos posiciones especiales de memoria que siempre se devuelven al BASIC como el valor de la variable en una orden USR. Es un poco complicado, pero en realidad no es difícil de entender. Significa que nuestro programa en lenguaje de máquina ha resuelto la ecuación que se le presentó cuando se le entregó a la instrucción USR en nuestro programa BASIC. Esa orden USR, como usted recordará, era así:

SUM=USR(1536,X,Y)

Ahora los valores se han asignado a todas sus variables.


A la "X" y a la "Y" en las ecuaciones se les asignaron valores cuando fueron digitados durante la ejecución de la parte BASIC del programa. Luego, cuando el control de su equipo fue trasladado al lenguaje de máquina, los valores de X e Y fueron convertidos a números de 8 bits y colocados en la pila hardware. En el programa de lenguaje de máquina que usted escribió, los valores de X e Y se sacan de la pila. Luego se almacena en dos pares de posiciones de memoria de 8 bits (que tienen la etiqueta NUM1 y NUM2, para evitar cualquier confusión con los registros X e Y del 6502). Luego, los valores de X y Y (ahora llamados NUM1 y NUM2) han sido sumados. El resultado es almacenado en las direcciones $D4 y $D5. Cuando el control vuelve al BASIC, el intérprete convierte el valor en $D4 y $D5 en un valor de 16-bits y asigna ese valor a la variable SUM del BASIC, la variable utilizada en la orden USR que invocó al programa en lenguaje de máquina. Entonces, tal como lo ordena el programa BASIC que escribió, el intérprete imprime el valor almacenado en la variable SUM del BASIC en la pantalla.


Esta fue una serie compleja de operaciones, como los son la mayoría de las secuencias de operaciones en lenguaje de máquina. Lamentablemente, todavía no hemos escrito un programa de sumas muy útil. Si bien es cierto que la máquina de sumar que acabamos de crear es muy cara, no es muy útil en las aplicaciones del mundo real. Puede sumar números de 16 bits e imprimir los resultados de 16-bits. Esa es una clara mejora con respecto al programa de 8 bits que creamos unos capítulos atrás, pero todavía tiene graves deficiencias. No puede manejar números o resultados que sean más largos que 16 bits. No puede trabajar con números decimales de punto flotante, o con números con signo. Si escribe un número que es demasiado grande para que lo pueda manejar, no se lo hará saber, simplemente "rodará" después del cero y sumará números sin ningún tipo de acarreo, y entregará resultados incorrectos.


Obviamente, todavía no hemos conseguido escribir un programa que trabaje como un buen programa de sumas. Ni siquiera hemos mirado programas de restas, multiplicación o división. Sin embargo, muy pronto los veremos. También vamos a discutir muchos otros temas, como números con signo, números BCD, manipulación de bits y mucho más en el capítulo 10, Matemáticas del Lenguaje Ensamblador. Antes de entrar en el capítulo 10, sin embargo, veremos que queda un poco de terreno por recorrer en Programando Bit a Bit, el tema del capítulo 9.